Blender V4.5
lineart_chain.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2019 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLI_listbase.h"
10#include "BLI_math_geom.h"
11
12#include "MOD_lineart.hh"
13
14#include "lineart_intern.hh"
15
16#include <algorithm> /* For `min/max`. */
17#include <cmath>
18
19#define LRT_OTHER_VERT(e, vt) ((vt) == (e)->v1 ? (e)->v2 : ((vt) == (e)->v2 ? (e)->v1 : nullptr))
20
21struct Object;
22
23/* Get a connected line, only for lines who has the exact given vert, or (in the case of
24 * intersection lines) who has a vert that has the exact same position. */
26 LineartVert *vt,
27 LineartVert **new_vt,
28 int match_flag,
29 uint8_t match_isec_mask,
30 Object *match_isec_object)
31{
32 for (int i = 0; i < ba->line_count; i++) {
33 LineartEdge *n_e = ba->linked_lines[i];
34
37 {
38 continue;
39 }
40
41 if (match_flag && ((n_e->flags & MOD_LINEART_EDGE_FLAG_ALL_TYPE) & match_flag) == 0) {
42 continue;
43 }
44
45 if (n_e->intersection_mask != match_isec_mask) {
46 continue;
47 }
48
49 *new_vt = LRT_OTHER_VERT(n_e, vt);
50 if (*new_vt) {
51 return n_e;
52 }
53
55 if (n_e->object_ref != match_isec_object) {
56 continue;
57 }
58 if (vt->fbcoord[0] == n_e->v1->fbcoord[0] && vt->fbcoord[1] == n_e->v1->fbcoord[1]) {
59 *new_vt = LRT_OTHER_VERT(n_e, n_e->v1);
60 return n_e;
61 }
62 if (vt->fbcoord[0] == n_e->v2->fbcoord[0] && vt->fbcoord[1] == n_e->v2->fbcoord[1]) {
63 *new_vt = LRT_OTHER_VERT(n_e, n_e->v2);
64 return n_e;
65 }
66 }
67 }
68
69 return nullptr;
70}
71
73{
75 ec = static_cast<LineartEdgeChain *>(
77
78 BLI_addtail(&ld->chains, ec);
79
80 return ec;
81}
82
84 float x,
85 float y,
86 double threshold)
87{
88 if (!eci) {
89 return false;
90 }
91 if (((eci->pos[0] + threshold) >= x) && ((eci->pos[0] - threshold) <= x) &&
92 ((eci->pos[1] + threshold) >= y) && ((eci->pos[1] - threshold) <= y))
93 {
94 return true;
95 }
96 return false;
97}
98
101 float fbcoord[4],
102 float gpos[3],
103 float normal[3],
104 uint8_t type,
105 int level,
106 uint8_t material_mask_bits,
107 uint32_t shadow_mask_bits,
108 size_t index)
109{
111
113 static_cast<LineartEdgeChainItem *>(ec->chain.last), fbcoord[0], fbcoord[1], 1e-5))
114 {
115 /* Because the new chain point is overlapping, just replace the type and occlusion level of the
116 * current point. This makes it so that the line to the point after this one has the correct
117 * type and level. */
118 LineartEdgeChainItem *old_eci = static_cast<LineartEdgeChainItem *>(ec->chain.last);
119 old_eci->line_type = type;
120 old_eci->occlusion = level;
121 old_eci->material_mask_bits = material_mask_bits;
122 old_eci->shadow_mask_bits = shadow_mask_bits;
123 return old_eci;
124 }
125
126 eci = static_cast<LineartEdgeChainItem *>(
128
129 copy_v4_v4(eci->pos, fbcoord);
130 copy_v3_v3(eci->gpos, gpos);
131 eci->index = index;
132 copy_v3_v3(eci->normal, normal);
134 eci->occlusion = level;
135 eci->material_mask_bits = material_mask_bits;
136 eci->shadow_mask_bits = shadow_mask_bits;
137 BLI_addtail(&ec->chain, eci);
138
139 return eci;
140}
141
144 float fbcoord[4],
145 float gpos[3],
146 float normal[3],
147 uint8_t type,
148 int level,
149 uint8_t material_mask_bits,
150 uint32_t shadow_mask_bits,
151 size_t index)
152{
154
156 static_cast<LineartEdgeChainItem *>(ec->chain.first), fbcoord[0], fbcoord[1], 1e-5))
157 {
158 return static_cast<LineartEdgeChainItem *>(ec->chain.first);
159 }
160
161 eci = static_cast<LineartEdgeChainItem *>(
163
164 copy_v4_v4(eci->pos, fbcoord);
165 copy_v3_v3(eci->gpos, gpos);
166 eci->index = index;
167 copy_v3_v3(eci->normal, normal);
169 eci->occlusion = level;
170 eci->material_mask_bits = material_mask_bits;
171 eci->shadow_mask_bits = shadow_mask_bits;
172 BLI_addhead(&ec->chain, eci);
173
174 return eci;
175}
176
178{
183 int last_occlusion;
184 uint8_t last_transparency;
185 uint32_t last_shadow;
186 /* Used when converting from double. */
187 float use_fbcoord[4];
188 float use_gpos[3];
189
190#define VERT_COORD_TO_FLOAT(a) \
191 copy_v4fl_v4db(use_fbcoord, (a)->fbcoord); \
192 copy_v3fl_v3db(use_gpos, (a)->gloc);
193
194#define POS_TO_FLOAT(lpos, gpos) \
195 copy_v3fl_v3db(use_fbcoord, lpos); \
196 copy_v3fl_v3db(use_gpos, gpos);
197
199 {
200 if (!(e->flags & MOD_LINEART_EDGE_FLAG_ALL_TYPE) ||
202 {
204 continue;
205 }
206
208
209 ec = lineart_chain_create(ld);
210
211 /* One chain can only have one object_ref and intersection_mask,
212 * so we assign them based on the first segment we found. */
213 ec->object_ref = e->object_ref;
214 ec->intersection_mask = e->intersection_mask;
215
216 LineartEdge *new_e;
217 LineartVert *new_vt;
218 float N[3] = {0};
219
221 /* We do not have actual target triangle reference for projected shadow lines, so the normal
222 * is unavailable at this point. In which case we set that to (0,0,1). */
223 N[0] = 1.0f;
224 }
225 else {
226 if (e->t1) {
227 N[0] += e->t1->gn[0];
228 N[1] += e->t1->gn[1];
229 N[2] += e->t1->gn[2];
230 }
231 if (e->t2) {
232 N[0] += e->t2->gn[0];
233 N[1] += e->t2->gn[1];
234 N[2] += e->t2->gn[2];
235 }
236 if (e->t1 || e->t2) {
238 }
239 }
240
241 /* Step 1: grow left. */
242 ba = MOD_lineart_get_bounding_area(ld, e->v1->fbcoord[0], e->v1->fbcoord[1]);
243 new_vt = e->v1;
244 es = static_cast<LineartEdgeSegment *>(e->segments.first);
245 VERT_COORD_TO_FLOAT(new_vt);
247 ec,
248 use_fbcoord,
249 use_gpos,
250 N,
251 e->flags,
252 es->occlusion,
255 e->v1->index);
256 while (ba && (new_e = lineart_line_get_connected(
257 ba, new_vt, &new_vt, e->flags, ec->intersection_mask, ec->object_ref)))
258 {
260
261 if (new_e->t1 || new_e->t2) {
262 zero_v3(N);
263 if (new_e->t1) {
264 N[0] += new_e->t1->gn[0];
265 N[1] += new_e->t1->gn[1];
266 N[2] += new_e->t1->gn[2];
267 }
268 if (new_e->t2) {
269 N[0] += new_e->t2->gn[0];
270 N[1] += new_e->t2->gn[1];
271 N[2] += new_e->t2->gn[2];
272 }
274 }
275
276 if (new_vt == new_e->v1) {
278 double gpos[3], lpos[3];
279 double *lfb = new_e->v1->fbcoord, *rfb = new_e->v2->fbcoord;
280 double global_at = lfb[3] * es->ratio / (es->ratio * lfb[3] + (1 - es->ratio) * rfb[3]);
281 interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->ratio);
282 interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at);
283 use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at);
284 POS_TO_FLOAT(lpos, gpos)
286 ec,
287 use_fbcoord,
288 use_gpos,
289 N,
290 new_e->flags,
291 es->occlusion,
294 new_e->v1->index);
295 last_occlusion = es->occlusion;
296 last_transparency = es->material_mask_bits;
297 last_shadow = es->shadow_mask_bits;
298 }
299 }
300 else if (new_vt == new_e->v2) {
301 es = static_cast<LineartEdgeSegment *>(new_e->segments.first);
302 last_occlusion = es->occlusion;
303 last_transparency = es->material_mask_bits;
304 last_shadow = es->shadow_mask_bits;
305 es = es->next;
306 for (; es; es = es->next) {
307 double gpos[3], lpos[3];
308 double *lfb = new_e->v1->fbcoord, *rfb = new_e->v2->fbcoord;
309 double global_at = lfb[3] * es->ratio / (es->ratio * lfb[3] + (1 - es->ratio) * rfb[3]);
310 interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->ratio);
311 interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at);
312 use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at);
313 POS_TO_FLOAT(lpos, gpos)
315 ec,
316 use_fbcoord,
317 use_gpos,
318 N,
319 new_e->flags,
320 last_occlusion,
321 last_transparency,
322 last_shadow,
323 new_e->v2->index);
324 last_occlusion = es->occlusion;
325 last_transparency = es->material_mask_bits;
326 last_shadow = es->shadow_mask_bits;
327 }
328 VERT_COORD_TO_FLOAT(new_e->v2);
330 ec,
331 use_fbcoord,
332 use_gpos,
333 N,
334 new_e->flags,
335 last_occlusion,
336 last_transparency,
337 last_shadow,
338 new_e->v2->index);
339 }
340 ba = MOD_lineart_get_bounding_area(ld, new_vt->fbcoord[0], new_vt->fbcoord[1]);
341 }
342
343 /* Restore normal value. */
344 if (e->t1 || e->t2) {
345 zero_v3(N);
346 if (e->t1) {
347 N[0] += e->t1->gn[0];
348 N[1] += e->t1->gn[1];
349 N[2] += e->t1->gn[2];
350 }
351 if (e->t2) {
352 N[0] += e->t2->gn[0];
353 N[1] += e->t2->gn[1];
354 N[2] += e->t2->gn[2];
355 }
357 }
358 /* Step 2: Adding all cuts from the given line, so we can continue connecting the right side
359 * of the line. */
360 es = static_cast<LineartEdgeSegment *>(e->segments.first);
361 last_occlusion = es->occlusion;
362 last_transparency = es->material_mask_bits;
363 last_shadow = es->shadow_mask_bits;
364 for (es = es->next; es; es = es->next) {
365 double gpos[3], lpos[3];
366 double *lfb = e->v1->fbcoord, *rfb = e->v2->fbcoord;
367 double global_at = lfb[3] * es->ratio / (es->ratio * lfb[3] + (1 - es->ratio) * rfb[3]);
368 interp_v3_v3v3_db(lpos, e->v1->fbcoord, e->v2->fbcoord, es->ratio);
369 interp_v3_v3v3_db(gpos, e->v1->gloc, e->v2->gloc, global_at);
370 use_fbcoord[3] = interpf(e->v2->fbcoord[3], e->v1->fbcoord[3], global_at);
371 POS_TO_FLOAT(lpos, gpos)
373 ec,
374 use_fbcoord,
375 use_gpos,
376 N,
377 e->flags,
378 es->occlusion,
381 e->v1->index);
382 last_occlusion = es->occlusion;
383 last_transparency = es->material_mask_bits;
384 last_shadow = es->shadow_mask_bits;
385 }
388 ec,
389 use_fbcoord,
390 use_gpos,
391 N,
392 e->flags,
393 last_occlusion,
394 last_transparency,
395 last_shadow,
396 e->v2->index);
397
398 /* Step 3: grow right. */
399 ba = MOD_lineart_get_bounding_area(ld, e->v2->fbcoord[0], e->v2->fbcoord[1]);
400 new_vt = e->v2;
401 while (ba && (new_e = lineart_line_get_connected(
402 ba, new_vt, &new_vt, e->flags, ec->intersection_mask, ec->object_ref)))
403 {
405
406 if (new_e->t1 || new_e->t2) {
407 zero_v3(N);
408 if (new_e->t1) {
409 N[0] += new_e->t1->gn[0];
410 N[1] += new_e->t1->gn[1];
411 N[2] += new_e->t1->gn[2];
412 }
413 if (new_e->t2) {
414 N[0] += new_e->t2->gn[0];
415 N[1] += new_e->t2->gn[1];
416 N[2] += new_e->t2->gn[2];
417 }
419 }
420
421 /* Fix leading vertex type. */
422 eci = static_cast<LineartEdgeChainItem *>(ec->chain.last);
424
425 if (new_vt == new_e->v1) {
426 es = static_cast<LineartEdgeSegment *>(new_e->segments.last);
427 last_occlusion = es->occlusion;
428 last_transparency = es->material_mask_bits;
429 last_shadow = es->shadow_mask_bits;
430 /* Fix leading vertex occlusion. */
431 eci->occlusion = last_occlusion;
432 eci->material_mask_bits = last_transparency;
433 eci->shadow_mask_bits = last_shadow;
435 double gpos[3], lpos[3];
436 double *lfb = new_e->v1->fbcoord, *rfb = new_e->v2->fbcoord;
437 double global_at = lfb[3] * es->ratio / (es->ratio * lfb[3] + (1 - es->ratio) * rfb[3]);
438 interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->ratio);
439 interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at);
440 use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at);
441 last_occlusion = es->prev ? es->prev->occlusion : last_occlusion;
442 last_transparency = es->prev ? es->prev->material_mask_bits : last_transparency;
443 last_shadow = es->prev ? es->prev->shadow_mask_bits : last_shadow;
444 POS_TO_FLOAT(lpos, gpos)
446 ec,
447 use_fbcoord,
448 use_gpos,
449 N,
450 new_e->flags,
451 last_occlusion,
452 last_transparency,
453 last_shadow,
454 new_e->v1->index);
455 }
456 }
457 else if (new_vt == new_e->v2) {
458 es = static_cast<LineartEdgeSegment *>(new_e->segments.first);
459 last_occlusion = es->occlusion;
460 last_transparency = es->material_mask_bits;
461 last_shadow = es->shadow_mask_bits;
462 eci->occlusion = last_occlusion;
463 eci->material_mask_bits = last_transparency;
464 eci->shadow_mask_bits = last_shadow;
465 es = es->next;
466 for (; es; es = es->next) {
467 double gpos[3], lpos[3];
468 double *lfb = new_e->v1->fbcoord, *rfb = new_e->v2->fbcoord;
469 double global_at = lfb[3] * es->ratio / (es->ratio * lfb[3] + (1 - es->ratio) * rfb[3]);
470 interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->ratio);
471 interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at);
472 use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at);
473 POS_TO_FLOAT(lpos, gpos)
475 ec,
476 use_fbcoord,
477 use_gpos,
478 N,
479 new_e->flags,
480 es->occlusion,
483 new_e->v2->index);
484 last_occlusion = es->occlusion;
485 last_transparency = es->material_mask_bits;
486 last_shadow = es->shadow_mask_bits;
487 }
488 VERT_COORD_TO_FLOAT(new_e->v2)
490 ec,
491 use_fbcoord,
492 use_gpos,
493 N,
494 new_e->flags,
495 last_occlusion,
496 last_transparency,
497 last_shadow,
498 new_e->v2->index);
499 }
500 ba = MOD_lineart_get_bounding_area(ld, new_vt->fbcoord[0], new_vt->fbcoord[1]);
501 }
502 if (ld->conf.fuzzy_everything) {
504 }
505 else {
506 ec->type = (e->flags & MOD_LINEART_EDGE_FLAG_ALL_TYPE);
507 }
508 }
510}
511
515{
516 if (root->child == nullptr) {
517 return root;
518 }
519
520 LineartBoundingArea *ch = root->child;
521#define IN_BOUND(ba, eci) \
522 ba.l <= eci->pos[0] && ba.r >= eci->pos[0] && ba.b <= eci->pos[1] && ba.u >= eci->pos[1]
523
524 if (IN_BOUND(ch[0], eci)) {
525 return lineart_bounding_area_get_eci_recursive(ld, &ch[0], eci);
526 }
527 if (IN_BOUND(ch[1], eci)) {
528 return lineart_bounding_area_get_eci_recursive(ld, &ch[1], eci);
529 }
530 if (IN_BOUND(ch[2], eci)) {
531 return lineart_bounding_area_get_eci_recursive(ld, &ch[2], eci);
532 }
533 if (IN_BOUND(ch[3], eci)) {
534 return lineart_bounding_area_get_eci_recursive(ld, &ch[3], eci);
535 }
536#undef IN_BOUND
537 return nullptr;
538}
539
542{
543 if (!eci) {
544 return nullptr;
545 }
547 if (root == nullptr) {
548 return nullptr;
549 }
550 return lineart_bounding_area_get_eci_recursive(ld, root, eci);
551}
552
563{
564 if (root->child == nullptr) {
568
569 cre->eci = eci;
570
571 if (eci == ec->chain.first) {
572 cre->is_left = 1;
573 }
574 }
575 else {
576 LineartBoundingArea *ch = root->child;
577
578#define IN_BOUND(ba, eci) \
579 ba.l <= eci->pos[0] && ba.r >= eci->pos[0] && ba.b <= eci->pos[1] && ba.u >= eci->pos[1]
580
581 if (IN_BOUND(ch[0], eci)) {
583 }
584 else if (IN_BOUND(ch[1], eci)) {
586 }
587 else if (IN_BOUND(ch[2], eci)) {
589 }
590 else if (IN_BOUND(ch[3], eci)) {
592 }
593
594#undef IN_BOUND
595 }
596}
597
599{
600 LineartEdgeChainItem *pl = static_cast<LineartEdgeChainItem *>(ec->chain.first);
601 LineartEdgeChainItem *pr = static_cast<LineartEdgeChainItem *>(ec->chain.last);
604
605 if (ba1) {
607 }
608 if (ba2) {
610 }
611}
612
614 LineartEdgeChainItem *last_matching_eci,
615 float distance_threshold,
616 bool preserve_details,
617 LineartEdgeChainItem **r_next_eci)
618{
619 float dist_accum = 0;
620
621 int fixed_occ = last_matching_eci->occlusion;
622 uint8_t fixed_mask = last_matching_eci->material_mask_bits;
623 uint32_t fixed_shadow = last_matching_eci->shadow_mask_bits;
624
625 LineartEdgeChainItem *can_skip_to = nullptr;
626 LineartEdgeChainItem *last_eci = last_matching_eci;
627 for (LineartEdgeChainItem *eci = last_matching_eci->next; eci; eci = eci->next) {
628 dist_accum += len_v2v2(last_eci->pos, eci->pos);
629 if (dist_accum > distance_threshold) {
630 break;
631 }
632 last_eci = eci;
633 /* The reason for this is because we don't want visible segments to be "skipped" into
634 * connecting with invisible segments. */
635 if (eci->occlusion < fixed_occ) {
636 break;
637 }
638 if (eci->material_mask_bits == fixed_mask && eci->occlusion == fixed_occ &&
639 eci->shadow_mask_bits == fixed_shadow)
640 {
641 can_skip_to = eci;
642 }
643 }
644 if (can_skip_to) {
645 /* Either mark all in-between segments with the same occlusion and mask or delete those
646 * different ones. */
647 LineartEdgeChainItem *next_eci;
648 for (LineartEdgeChainItem *eci = last_matching_eci->next; eci != can_skip_to; eci = next_eci) {
649 next_eci = eci->next;
650 if (eci->material_mask_bits == fixed_mask && eci->occlusion == fixed_occ &&
651 eci->shadow_mask_bits == fixed_shadow)
652 {
653 continue;
654 }
655 if (preserve_details) {
656 eci->material_mask_bits = fixed_mask;
657 eci->occlusion = fixed_occ;
658 eci->shadow_mask_bits = fixed_shadow;
659 }
660 else {
661 BLI_remlink(&ec->chain, eci);
662 }
663 }
664 *r_next_eci = can_skip_to;
665 return true;
666 }
667 return false;
668}
669
671{
672 LineartEdgeChainItem *eci, *next_eci;
673 ListBase swap = {nullptr};
674
675 swap.first = ld->chains.first;
676 swap.last = ld->chains.last;
677
678 ld->chains.last = ld->chains.first = nullptr;
679
680 int loop_id = 0;
681 while (LineartEdgeChain *ec = static_cast<LineartEdgeChain *>(BLI_pophead(&swap))) {
682 ec->next = ec->prev = nullptr;
683 BLI_addtail(&ld->chains, ec);
684
685 ec->loop_id = loop_id;
686 loop_id++;
687
688 LineartEdgeChainItem *first_eci = (LineartEdgeChainItem *)ec->chain.first;
689 int fixed_occ = first_eci->occlusion;
690 uint8_t fixed_mask = first_eci->material_mask_bits;
691 uint32_t fixed_shadow = first_eci->shadow_mask_bits;
692 ec->level = fixed_occ;
693 ec->material_mask_bits = fixed_mask;
694 ec->shadow_mask_bits = fixed_shadow;
695 for (eci = first_eci->next; eci; eci = next_eci) {
696 next_eci = eci->next;
697 if (eci->occlusion != fixed_occ || eci->material_mask_bits != fixed_mask ||
698 eci->shadow_mask_bits != fixed_shadow)
699 {
700 if (next_eci) {
701 if (lineart_point_overlapping(next_eci, eci->pos[0], eci->pos[1], 1e-5)) {
702 continue;
703 }
705 eci->prev,
708 &next_eci))
709 {
710 continue;
711 }
712 }
713 else {
714 /* Set the same occlusion level for the end vertex, so when further connection is needed
715 * the backwards occlusion info is also correct. */
716 eci->occlusion = fixed_occ;
717 eci->shadow_mask_bits = fixed_shadow;
718 eci->material_mask_bits = fixed_mask;
719 /* No need to split at the last point anyway. */
720 break;
721 }
723 new_ec->chain.first = eci;
724 new_ec->chain.last = ec->chain.last;
725 new_ec->loop_id = loop_id;
726 ec->chain.last = eci->prev;
727 ((LineartEdgeChainItem *)ec->chain.last)->next = nullptr;
728 eci->prev = nullptr;
729
730 /* End the previous one. */
732 ec,
733 eci->pos,
734 eci->gpos,
735 eci->normal,
736 eci->line_type,
737 fixed_occ,
738 fixed_mask,
739 fixed_shadow,
740 eci->index);
741 new_ec->object_ref = ec->object_ref;
742 new_ec->type = ec->type;
743 new_ec->intersection_mask = ec->intersection_mask;
744 ec = new_ec;
745 fixed_occ = eci->occlusion;
746 fixed_mask = eci->material_mask_bits;
747 fixed_shadow = eci->shadow_mask_bits;
748 ec->level = fixed_occ;
749 ec->material_mask_bits = fixed_mask;
750 ec->shadow_mask_bits = fixed_shadow;
751 }
752 }
753 }
754
756
759 }
760}
761
766 LineartEdgeChain *onto,
767 LineartEdgeChain *sub,
768 int reverse_1,
769 int reverse_2)
770{
775 }
776 if (sub->object_ref) {
777 onto->object_ref = sub->object_ref;
778 }
779 }
780 else if (sub->type == MOD_LINEART_EDGE_FLAG_INTERSECTION) {
783 }
784 }
785 if (!reverse_1) { /* L--R L-R. */
786 if (reverse_2) { /* L--R R-L. */
788 }
789 eci = static_cast<LineartEdgeChainItem *>(sub->chain.first);
791 static_cast<LineartEdgeChainItem *>(onto->chain.last), eci->pos[0], eci->pos[1], 1e-5))
792 {
793 BLI_pophead(&sub->chain);
794 if (sub->chain.first == nullptr) {
795 return;
796 }
797 }
798 ((LineartEdgeChainItem *)onto->chain.last)->next = static_cast<LineartEdgeChainItem *>(
799 sub->chain.first);
800 ((LineartEdgeChainItem *)sub->chain.first)->prev = static_cast<LineartEdgeChainItem *>(
801 onto->chain.last);
802 onto->chain.last = sub->chain.last;
803 }
804 else { /* L-R L--R. */
805 if (!reverse_2) { /* R-L L--R. */
807 }
808 eci = static_cast<LineartEdgeChainItem *>(onto->chain.first);
810 static_cast<LineartEdgeChainItem *>(sub->chain.last), eci->pos[0], eci->pos[1], 1e-5))
811 {
812 BLI_pophead(&onto->chain);
813 if (onto->chain.first == nullptr) {
814 return;
815 }
816 }
817 ((LineartEdgeChainItem *)sub->chain.last)->next = static_cast<LineartEdgeChainItem *>(
818 onto->chain.first);
819 ((LineartEdgeChainItem *)onto->chain.first)->prev = static_cast<LineartEdgeChainItem *>(
820 sub->chain.last);
821 onto->chain.first = sub->chain.first;
822 }
823}
824
829 int occlusion,
830 uint8_t material_mask_bits,
831 uint8_t isec_mask,
832 uint32_t shadow_mask,
833 int loop_id,
834 float dist,
835 float *result_new_len,
836 LineartBoundingArea *caller_ba)
837{
838
839 LineartChainRegisterEntry *closest_cre = nullptr;
840
841 /* Keep using for loop because `cre` could be removed from the iteration before getting to the
842 * next one. */
844 if (cre->ec->object_ref != ec->object_ref) {
845 if (!ld->conf.fuzzy_everything) {
846 if (ld->conf.fuzzy_intersections) {
847 /* If none of those are intersection lines... */
848 if (!(cre->ec->type & MOD_LINEART_EDGE_FLAG_INTERSECTION) &&
850 {
851 continue; /* We don't want to chain along different objects at the moment. */
852 }
853 }
854 else {
855 continue;
856 }
857 }
858 }
859 if (cre->ec->picked || cre->picked) {
860 continue;
861 }
862 if (cre->ec == ec || (!cre->ec->chain.first) || (cre->ec->level != occlusion) ||
863 (cre->ec->material_mask_bits != material_mask_bits) ||
864 (cre->ec->intersection_mask != isec_mask) || (cre->ec->shadow_mask_bits != shadow_mask))
865 {
866 continue;
867 }
868 if (!ld->conf.fuzzy_everything) {
869 if (cre->ec->type != ec->type) {
870 if (ld->conf.fuzzy_intersections) {
871 if (!(cre->ec->type == MOD_LINEART_EDGE_FLAG_INTERSECTION ||
873 {
874 continue; /* Fuzzy intersections but no intersection line found. */
875 }
876 }
877 else { /* Line type different but no fuzzy. */
878 continue;
879 }
880 }
881 }
882
883 float new_len = ld->conf.use_geometry_space_chain ? len_v3v3(cre->eci->gpos, eci->gpos) :
884 len_v2v2(cre->eci->pos, eci->pos);
885 /* Even if the vertex is not from the same contour loop, we try to chain it still if the
886 * distance is small enough. This way we can better chain smaller loops and smooth them out
887 * later. */
888 if (((cre->ec->loop_id == loop_id) && (new_len < dist)) ||
889 ((cre->ec->loop_id != loop_id) && (new_len < dist / 10)))
890 {
891 closest_cre = cre;
892 dist = new_len;
893 if (result_new_len) {
894 (*result_new_len) = new_len;
895 }
896 }
897 }
898
899 /* We want a closer point anyway. So using modified dist is fine. */
900 float adjacent_new_len = dist;
901 LineartChainRegisterEntry *adjacent_closest;
902
903#define LRT_TEST_ADJACENT_AREAS(dist_to, list) \
904 if (dist_to < dist && dist_to > 0) { \
905 LISTBASE_FOREACH (LinkData *, link, list) { \
906 LineartBoundingArea *sba = (LineartBoundingArea *)link->data; \
907 adjacent_closest = lineart_chain_get_closest_cre(ld, \
908 sba, \
909 ec, \
910 eci, \
911 occlusion, \
912 material_mask_bits, \
913 isec_mask, \
914 shadow_mask, \
915 loop_id, \
916 dist, \
917 &adjacent_new_len, \
918 ba); \
919 if (adjacent_new_len < dist) { \
920 dist = adjacent_new_len; \
921 closest_cre = adjacent_closest; \
922 } \
923 } \
924 }
925 if (!caller_ba) {
926 LRT_TEST_ADJACENT_AREAS(eci->pos[0] - ba->l, &ba->lp);
927 LRT_TEST_ADJACENT_AREAS(ba->r - eci->pos[0], &ba->rp);
928 LRT_TEST_ADJACENT_AREAS(ba->u - eci->pos[1], &ba->up);
929 LRT_TEST_ADJACENT_AREAS(eci->pos[1] - ba->b, &ba->bp);
930 }
931 if (result_new_len) {
932 (*result_new_len) = dist;
933 }
934 return closest_cre;
935}
936
938{
939 LineartEdgeChainItem *eci_l, *eci_r;
940 LineartBoundingArea *ba_l, *ba_r;
941 LineartChainRegisterEntry *closest_cre_l, *closest_cre_r, *closest_cre;
942 float dist = ld->conf.chaining_image_threshold;
943 float dist_l, dist_r;
944 int reverse_main, loop_id;
945 uint8_t occlusion, material_mask_bits, isec_mask;
946 uint32_t shadow_mask;
947 ListBase swap = {nullptr};
948
949 if (ld->conf.chaining_image_threshold < 0.0001) {
950 return;
951 }
952
953 swap.first = ld->chains.first;
954 swap.last = ld->chains.last;
955
956 ld->chains.last = ld->chains.first = nullptr;
957
958 while (LineartEdgeChain *ec = static_cast<LineartEdgeChain *>(BLI_pophead(&swap))) {
959 ec->next = ec->prev = nullptr;
960 if (ec->picked || ec->chain.first == ec->chain.last) {
961 continue;
962 }
963 BLI_addtail(&ld->chains, ec);
964 loop_id = ec->loop_id;
965
966 if (ec->type == MOD_LINEART_EDGE_FLAG_LOOSE && (!ld->conf.use_loose_edge_chain)) {
967 continue;
968 }
969
970 occlusion = ec->level;
971 material_mask_bits = ec->material_mask_bits;
972 isec_mask = ec->intersection_mask;
973 shadow_mask = ec->shadow_mask_bits;
974
975 eci_l = static_cast<LineartEdgeChainItem *>(ec->chain.first);
976 eci_r = static_cast<LineartEdgeChainItem *>(ec->chain.last);
977 while ((ba_l = lineart_bounding_area_get_end_point(ld, eci_l)) &&
978 (ba_r = lineart_bounding_area_get_end_point(ld, eci_r)))
979 {
980 closest_cre_l = lineart_chain_get_closest_cre(ld,
981 ba_l,
982 ec,
983 eci_l,
984 occlusion,
985 material_mask_bits,
986 isec_mask,
987 shadow_mask,
988 loop_id,
989 dist,
990 &dist_l,
991 nullptr);
992 closest_cre_r = lineart_chain_get_closest_cre(ld,
993 ba_r,
994 ec,
995 eci_r,
996 occlusion,
997 material_mask_bits,
998 isec_mask,
999 shadow_mask,
1000 loop_id,
1001 dist,
1002 &dist_r,
1003 nullptr);
1004 if (closest_cre_l && closest_cre_r) {
1005 if (dist_l < dist_r) {
1006 closest_cre = closest_cre_l;
1007 reverse_main = 1;
1008 }
1009 else {
1010 closest_cre = closest_cre_r;
1011 reverse_main = 0;
1012 }
1013 }
1014 else if (closest_cre_l) {
1015 closest_cre = closest_cre_l;
1016 reverse_main = 1;
1017 }
1018 else if (closest_cre_r) {
1019 closest_cre = closest_cre_r;
1020 BLI_remlink(&ba_r->linked_chains, closest_cre_r);
1021 reverse_main = 0;
1022 }
1023 else {
1024 break;
1025 }
1026 closest_cre->picked = 1;
1027 closest_cre->ec->picked = 1;
1028 if (closest_cre->is_left) {
1029 lineart_chain_connect(ld, ec, closest_cre->ec, reverse_main, 0);
1030 }
1031 else {
1032 lineart_chain_connect(ld, ec, closest_cre->ec, reverse_main, 1);
1033 }
1034 BLI_remlink(&swap, closest_cre->ec);
1035 eci_l = static_cast<LineartEdgeChainItem *>(ec->chain.first);
1036 eci_r = static_cast<LineartEdgeChainItem *>(ec->chain.last);
1037 }
1038 ec->picked = 1;
1039 }
1040}
1041
1043{
1045 float offset_accum = 0;
1046 float dist;
1047 float last_point[2];
1048
1049 eci = static_cast<LineartEdgeChainItem *>(ec->chain.first);
1050 if (!eci) {
1051 return 0;
1052 }
1053 copy_v2_v2(last_point, eci->pos);
1055 dist = len_v2v2(eci->pos, last_point);
1056 offset_accum += dist;
1057 copy_v2_v2(last_point, eci->pos);
1058 }
1059 return offset_accum;
1060}
1061
1063 const float threshold,
1064 uint8_t max_occlusion)
1065{
1066 LineartEdgeChain *ec, *next_ec;
1067 for (ec = static_cast<LineartEdgeChain *>(ld->chains.first); ec; ec = next_ec) {
1068 next_ec = ec->next;
1069 if (ec->level > max_occlusion || MOD_lineart_chain_compute_length(ec) < threshold) {
1070 BLI_remlink(&ld->chains, ec);
1071 }
1072 }
1073}
1074
1076{
1077 int count = 0;
1079 count++;
1080 }
1081 return count;
1082}
1083
1085{
1086 if (lc == nullptr) {
1087 return;
1088 }
1090 ec->picked = 0;
1091 }
1092}
1093
1095{
1097 if (eln->object_ref == ob) {
1098 return eln;
1099 }
1100 }
1101 return nullptr;
1102}
1103
1105{
1107 if (ELEM(ec->type,
1111 {
1112 continue;
1113 }
1115 ec->object_ref);
1116 BLI_assert(eln != nullptr);
1117 if (LIKELY(eln)) {
1118 LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) {
1119 if (eci->index > eln->global_index_offset) {
1120 eci->index -= eln->global_index_offset;
1121 }
1122 }
1123 }
1124 }
1125}
1126
1127void MOD_lineart_smooth_chains(LineartData *ld, float tolerance)
1128{
1130 /* Go through the chain two times, once from each direction. */
1131 for (int times = 0; times < 2; times++) {
1132 for (LineartEdgeChainItem *eci = static_cast<LineartEdgeChainItem *>(ec->chain.first),
1133 *next_eci = eci->next;
1134 eci;
1135 eci = next_eci)
1136 {
1137 LineartEdgeChainItem *eci2, *eci3, *eci4;
1138
1139 if (!(eci2 = eci->next) || !(eci3 = eci2->next)) {
1140 /* Not enough points to simplify. */
1141 next_eci = eci->next;
1142 continue;
1143 }
1144 /* No need to care for different line types/occlusion and so on, because at this stage they
1145 * are all the same within a chain.
1146 *
1147 * We need to simplify a chain from this:
1148 * 1-----------2
1149 * 3-----------4
1150 * to this:
1151 * 1-----------2--_
1152 * `--4 */
1153
1154 /* If p3 is within the p1-p2 segment of a width of "tolerance", in other words, p3 is
1155 * approximately on the segment of p1-p2. */
1156 if (dist_to_line_segment_v2(eci3->pos, eci->pos, eci2->pos) < tolerance) {
1157 float vec2[2], vec3[2], v2n[2], ratio, len2;
1158 sub_v2_v2v2(vec2, eci2->pos, eci->pos);
1159 sub_v2_v2v2(vec3, eci3->pos, eci->pos);
1160 normalize_v2_v2(v2n, vec2);
1161 ratio = dot_v2v2(v2n, vec3);
1162 len2 = len_v2(vec2);
1163 /* Because this smoothing applies on geometries of different scales in the same scene,
1164 * some small scale features (e.g. the "tails" on the inner ring of a torus geometry)
1165 * could be completely erased if the tolerance value is set for accommodating the entire
1166 * scene. Those situations typically result in (ratio << 0), looks like this:
1167 * 1---2
1168 * 3-------------------------------4
1169 * (this sort of long zigzag obviously are "features" that can't be erased)
1170 * setting a ratio of -10 turned out to be a reasonable threshold in tests. */
1171 if (ratio < len2 && ratio > -len2 * 10) {
1172 /* We only remove p3 if p4 is on the extension of p1->p2. */
1173 if ((eci4 = eci3->next) &&
1174 (dist_to_line_v2(eci4->pos, eci->pos, eci2->pos) < tolerance))
1175 {
1176 BLI_remlink(&ec->chain, eci3);
1177 next_eci = eci;
1178 continue;
1179 }
1180 if (!eci4) {
1181 /* See if the last segment's direction is reversed, if so remove that.
1182 * Basically we don't need to preserve p3 if the entire chain looked like this:
1183 * ...----1----3===2 */
1184 if (len_v2(vec2) > len_v2(vec3)) {
1185 BLI_remlink(&ec->chain, eci3);
1186 }
1187 next_eci = nullptr;
1188 continue;
1189 }
1190 }
1191 }
1192 next_eci = eci->next;
1193 }
1194 BLI_listbase_reverse(&ec->chain);
1195 }
1196 }
1197}
1198
1200 LineartEdgeChainItem *eci_inside,
1201 LineartEdgeChainItem *eci_outside)
1202{
1203 float isec[2];
1204 /* l: left, r: right, b: bottom, u: top. */
1205 float ref_lu[2] = {-1.0f, 1.0f}, ref_lb[2] = {-1.0f, -1.0f}, ref_ru[2] = {1.0f, 1.0f},
1206 ref_rb[2] = {1.0f, -1.0f};
1207 bool found = false;
1208 LineartEdgeChainItem *eci2 = eci_outside, *eci1 = eci_inside;
1209 if (eci2->pos[0] < -1.0f) {
1210 found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, ref_lu, ref_lb, isec) > 0);
1211 }
1212 if (!found && eci2->pos[0] > 1.0f) {
1213 found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, ref_ru, ref_rb, isec) > 0);
1214 }
1215 if (!found && eci2->pos[1] < -1.0f) {
1216 found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, ref_lb, ref_rb, isec) > 0);
1217 }
1218 if (!found && eci2->pos[1] > 1.0f) {
1219 found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, ref_lu, ref_ru, isec) > 0);
1220 }
1221
1222 if (UNLIKELY(!found)) {
1223 return nullptr;
1224 }
1225
1226 float ratio = (fabs(eci2->pos[0] - eci1->pos[0]) > fabs(eci2->pos[1] - eci1->pos[1])) ?
1227 ratiof(eci1->pos[0], eci2->pos[0], isec[0]) :
1228 ratiof(eci1->pos[1], eci2->pos[1], isec[1]);
1229 float gratio = eci1->pos[3] * ratio / (ratio * eci1->pos[3] + (1 - ratio) * eci2->pos[3]);
1230
1231 LineartEdgeChainItem *eci = static_cast<LineartEdgeChainItem *>(
1233 memcpy(eci, eci1, sizeof(LineartEdgeChainItem));
1234 interp_v3_v3v3(eci->gpos, eci1->gpos, eci2->gpos, gratio);
1235 interp_v3_v3v3(eci->pos, eci1->pos, eci2->pos, ratio);
1236 eci->pos[3] = interpf(eci2->pos[3], eci1->pos[3], gratio);
1237 eci->next = eci->prev = nullptr;
1238 return eci;
1239}
1240
1241#define LRT_ECI_INSIDE(eci) \
1242 ((eci)->pos[0] >= -1.0f && (eci)->pos[0] <= 1.0f && (eci)->pos[1] >= -1.0f && \
1243 (eci)->pos[1] <= 1.0f)
1244
1246{
1247 LineartEdgeChainItem *eci, *next_eci, *prev_eci, *new_eci;
1248 bool is_inside, new_inside;
1249 ListBase swap = {nullptr};
1250 swap.first = ld->chains.first;
1251 swap.last = ld->chains.last;
1252
1253 ld->chains.last = ld->chains.first = nullptr;
1254 while (LineartEdgeChain *ec = static_cast<LineartEdgeChain *>(BLI_pophead(&swap))) {
1255 bool ec_added = false;
1256 LineartEdgeChainItem *first_eci = (LineartEdgeChainItem *)ec->chain.first;
1257 is_inside = LRT_ECI_INSIDE(first_eci) ? true : false;
1258 if (!is_inside) {
1259 ec->picked = 1;
1260 }
1261 for (eci = first_eci->next; eci; eci = next_eci) {
1262 next_eci = eci->next;
1263 prev_eci = eci->prev;
1264
1265 /* We only need to do something if the edge crossed from outside to the inside or from inside
1266 * to the outside. */
1267 if ((new_inside = LRT_ECI_INSIDE(eci)) != is_inside) {
1268 if (new_inside == false) {
1269 /* Stroke goes out. */
1270 new_eci = lineart_chain_create_crossing_point(ld, prev_eci, eci);
1271
1272 LineartEdgeChain *new_ec = static_cast<LineartEdgeChain *>(
1274 memcpy(new_ec, ec, sizeof(LineartEdgeChain));
1275 new_ec->chain.first = next_eci;
1276 eci->prev = nullptr;
1277 prev_eci->next = nullptr;
1278 ec->chain.last = prev_eci;
1279 BLI_addtail(&ec->chain, new_eci);
1280 BLI_addtail(&ld->chains, ec);
1281 ec_added = true;
1282 ec = new_ec;
1283
1284 next_eci = eci->next;
1285 is_inside = new_inside;
1286 continue;
1287 }
1288 /* Stroke comes in. */
1289 new_eci = lineart_chain_create_crossing_point(ld, eci, prev_eci);
1290
1291 ec->chain.first = eci;
1292 eci->prev = nullptr;
1293
1294 BLI_addhead(&ec->chain, new_eci);
1295
1296 ec_added = false;
1297
1298 next_eci = eci->next;
1299 is_inside = new_inside;
1300 continue;
1301 }
1302 }
1303
1304 if ((!ec_added) && is_inside) {
1305 BLI_addtail(&ld->chains, ec);
1306 }
1307 }
1308}
1309
1310void MOD_lineart_chain_split_angle(LineartData *ld, float angle_threshold_rad)
1311{
1312 LineartEdgeChainItem *eci, *next_eci, *prev_eci;
1313 ListBase swap = {nullptr};
1314
1315 swap.first = ld->chains.first;
1316 swap.last = ld->chains.last;
1317
1318 ld->chains.last = ld->chains.first = nullptr;
1319
1320 while (LineartEdgeChain *ec = static_cast<LineartEdgeChain *>(BLI_pophead(&swap))) {
1321 ec->next = ec->prev = nullptr;
1322 BLI_addtail(&ld->chains, ec);
1323 LineartEdgeChainItem *first_eci = (LineartEdgeChainItem *)ec->chain.first;
1324 for (eci = first_eci->next; eci; eci = next_eci) {
1325 next_eci = eci->next;
1326 prev_eci = eci->prev;
1327 float angle = M_PI;
1328 if (next_eci && prev_eci) {
1329 angle = angle_v2v2v2(prev_eci->pos, eci->pos, next_eci->pos);
1330 }
1331 else {
1332 break; /* No need to split at the last point anyway. */
1333 }
1334 if (angle < angle_threshold_rad) {
1335 LineartEdgeChain *new_ec;
1336 new_ec = lineart_chain_create(ld);
1337 new_ec->chain.first = eci;
1338 new_ec->chain.last = ec->chain.last;
1339 ec->chain.last = eci->prev;
1340 ((LineartEdgeChainItem *)ec->chain.last)->next = nullptr;
1341 eci->prev = nullptr;
1342
1343 /* End the previous one. */
1345 ec,
1346 eci->pos,
1347 eci->gpos,
1348 eci->normal,
1349 eci->line_type,
1350 ec->level,
1351 eci->material_mask_bits,
1352 eci->shadow_mask_bits,
1353 eci->index);
1354 new_ec->object_ref = ec->object_ref;
1355 new_ec->type = ec->type;
1356 new_ec->level = ec->level;
1357 new_ec->loop_id = ec->loop_id;
1358 new_ec->intersection_mask = ec->intersection_mask;
1359 new_ec->material_mask_bits = ec->material_mask_bits;
1360 new_ec->shadow_mask_bits = ec->shadow_mask_bits;
1361 ec = new_ec;
1362 }
1363 }
1364 }
1365}
1366
1367void MOD_lineart_chain_offset_towards_camera(LineartData *ld, float dist, bool use_custom_camera)
1368{
1369 float dir[3];
1370 float cam[3];
1371 float view[3];
1372 float view_clamp[3];
1373
1374 if (use_custom_camera) {
1375 copy_v3fl_v3db(cam, ld->conf.camera_pos);
1376 }
1377 else {
1379 }
1380
1381 if (ld->conf.cam_is_persp) {
1383 LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) {
1384 sub_v3_v3v3(dir, cam, eci->gpos);
1385 float orig_len = len_v3(dir);
1386 normalize_v3(dir);
1387 mul_v3_fl(dir, std::min<float>(dist, orig_len - ld->conf.near_clip));
1388 add_v3_v3(eci->gpos, dir);
1389 }
1390 }
1391 }
1392 else {
1395 LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) {
1396 sub_v3_v3v3(dir, cam, eci->gpos);
1397 float len_lim = dot_v3v3(view, dir) - ld->conf.near_clip;
1398 normalize_v3_v3(view_clamp, view);
1399 mul_v3_fl(view_clamp, std::min(dist, len_lim));
1400 add_v3_v3(eci->gpos, view_clamp);
1401 }
1402 }
1403 }
1404}
1405
1407{
1409 if (ec->type == MOD_LINEART_EDGE_FLAG_CONTOUR &&
1410 ec->shadow_mask_bits & LRT_SHADOW_SILHOUETTE_ERASED_GROUP)
1411 {
1412 uint32_t target = ec->shadow_mask_bits & LRT_OBINDEX_HIGHER;
1414 target);
1415 if (!eln) {
1416 continue;
1417 }
1418 ec->silhouette_backdrop = static_cast<Object *>(eln->object_ref);
1419 }
1420 }
1421}
#define BLI_assert(a)
Definition BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
#define LISTBASE_FOREACH_BACKWARD(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
void BLI_addhead(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:91
void void void void void void void BLI_listbase_reverse(ListBase *lb) ATTR_NONNULL(1)
Definition listbase.cc:836
void * BLI_pophead(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:252
MINLINE float interpf(float target, float origin, float t)
MINLINE float ratiof(float min, float max, float pos)
#define M_PI
float dist_to_line_segment_v2(const float p[2], const float l1[2], const float l2[2])
Definition math_geom.cc:300
int isect_seg_seg_v2_point(const float v0[2], const float v1[2], const float v2[2], const float v3[2], float r_vi[2])
float dist_to_line_v2(const float p[2], const float l1[2], const float l2[2])
Definition math_geom.cc:286
float angle_v2v2v2(const float a[2], const float b[2], const float c[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void copy_v4_v4(float r[4], const float a[4])
void interp_v3_v3v3_db(double target[3], const double a[3], const double b[3], double t)
MINLINE float len_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE float len_v2v2(const float v1[2], const float v2[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
void interp_v3_v3v3(float r[3], const float a[3], const float b[3], float t)
MINLINE void copy_v3fl_v3db(float r[3], const double a[3])
MINLINE float normalize_v3_v3(float r[3], const float a[3])
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE float dot_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void zero_v3(float r[3])
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v2_v2(float r[2], const float a[2])
MINLINE float normalize_v3(float n[3])
MINLINE float len_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT
#define UNLIKELY(x)
#define ELEM(...)
#define LIKELY(x)
@ MOD_LINEART_EDGE_FLAG_PROJECTED_SHADOW
@ MOD_LINEART_EDGE_FLAG_CONTOUR
@ MOD_LINEART_EDGE_FLAG_LIGHT_CONTOUR
@ MOD_LINEART_EDGE_FLAG_INTERSECTION
@ MOD_LINEART_EDGE_FLAG_CHAIN_PICKED
@ MOD_LINEART_EDGE_FLAG_LOOSE
#define MOD_LINEART_EDGE_FLAG_ALL_TYPE
static AppView * view
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
#define DBL_EDGE_LIM
#define LRT_OBINDEX_HIGHER
#define LRT_SHADOW_SILHOUETTE_ERASED_GROUP
return true
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
static bool is_inside(int x, int y, int cols, int rows)
Definition filesel.cc:772
int count
static LineartBoundingArea * lineart_bounding_area_get_eci_recursive(LineartData *ld, LineartBoundingArea *root, LineartEdgeChainItem *eci)
#define POS_TO_FLOAT(lpos, gpos)
static LineartEdgeChainItem * lineart_chain_create_crossing_point(LineartData *ld, LineartEdgeChainItem *eci_inside, LineartEdgeChainItem *eci_outside)
static bool lineart_chain_fix_ambiguous_segments(LineartEdgeChain *ec, LineartEdgeChainItem *last_matching_eci, float distance_threshold, bool preserve_details, LineartEdgeChainItem **r_next_eci)
#define LRT_ECI_INSIDE(eci)
LineartElementLinkNode * lineart_find_matching_eln_obj(ListBase *elns, Object *ob)
void MOD_lineart_chain_connect(LineartData *ld)
void MOD_lineart_chain_discard_unused(LineartData *ld, const float threshold, uint8_t max_occlusion)
static LineartEdge * lineart_line_get_connected(LineartBoundingArea *ba, LineartVert *vt, LineartVert **new_vt, int match_flag, uint8_t match_isec_mask, Object *match_isec_object)
static LineartBoundingArea * lineart_bounding_area_get_end_point(LineartData *ld, LineartEdgeChainItem *eci)
float MOD_lineart_chain_compute_length(LineartEdgeChain *ec)
static LineartEdgeChainItem * lineart_chain_append_point(LineartData *ld, LineartEdgeChain *ec, float fbcoord[4], float gpos[3], float normal[3], uint8_t type, int level, uint8_t material_mask_bits, uint32_t shadow_mask_bits, size_t index)
void MOD_lineart_chain_split_for_fixed_occlusion(LineartData *ld)
static LineartChainRegisterEntry * lineart_chain_get_closest_cre(LineartData *ld, LineartBoundingArea *ba, LineartEdgeChain *ec, LineartEdgeChainItem *eci, int occlusion, uint8_t material_mask_bits, uint8_t isec_mask, uint32_t shadow_mask, int loop_id, float dist, float *result_new_len, LineartBoundingArea *caller_ba)
void MOD_lineart_chain_clip_at_border(LineartData *ld)
int MOD_lineart_chain_count(const LineartEdgeChain *ec)
#define VERT_COORD_TO_FLOAT(a)
void MOD_lineart_finalize_chains(LineartData *ld)
static LineartEdgeChainItem * lineart_chain_prepend_point(LineartData *ld, LineartEdgeChain *ec, float fbcoord[4], float gpos[3], float normal[3], uint8_t type, int level, uint8_t material_mask_bits, uint32_t shadow_mask_bits, size_t index)
static void lineart_bounding_area_link_chain(LineartData *ld, LineartEdgeChain *ec)
#define LRT_OTHER_VERT(e, vt)
void MOD_lineart_chain_find_silhouette_backdrop_objects(LineartData *ld)
static bool lineart_point_overlapping(LineartEdgeChainItem *eci, float x, float y, double threshold)
void MOD_lineart_chain_feature_lines(LineartData *ld)
#define LRT_TEST_ADJACENT_AREAS(dist_to, list)
void MOD_lineart_chain_clear_picked_flag(LineartCache *lc)
static void lineart_bounding_area_link_point_recursive(LineartData *ld, LineartBoundingArea *root, LineartEdgeChain *ec, LineartEdgeChainItem *eci)
static LineartEdgeChain * lineart_chain_create(LineartData *ld)
void MOD_lineart_smooth_chains(LineartData *ld, float tolerance)
void MOD_lineart_chain_split_angle(LineartData *ld, float angle_threshold_rad)
void MOD_lineart_chain_offset_towards_camera(LineartData *ld, float dist, bool use_custom_camera)
static void lineart_chain_connect(LineartData *, LineartEdgeChain *onto, LineartEdgeChain *sub, int reverse_1, int reverse_2)
#define IN_BOUND(ba, eci)
LineartBoundingArea * MOD_lineart_get_parent_bounding_area(LineartData *ld, double x, double y)
LineartBoundingArea * MOD_lineart_get_bounding_area(LineartData *ld, double x, double y)
void * lineart_mem_acquire(struct LineartStaticMemPool *smp, size_t size)
void * lineart_list_append_pointer_pool_sized(ListBase *h, struct LineartStaticMemPool *smp, void *data, int size)
LineartElementLinkNode * lineart_find_matching_eln(struct ListBase *shadow_elns, int obindex)
#define LRT_ITER_ALL_LINES_END
#define LRT_ITER_ALL_LINES_NEXT
#define LRT_ITER_ALL_LINES_BEGIN
ccl_device_inline float2 fabs(const float2 a)
#define N
#define swap(a, b)
Definition sort.cc:59
LineartBoundingArea * child
LineartEdge ** linked_lines
ListBase chains
LineartEdgeChainItem * eci
LineartEdgeChain * ec
double active_camera_pos[3]
ListBase vertex_buffer_pointers
ListBase line_buffer_pointers
struct LineartData::_conf conf
struct LineartData::_geom geom
ListBase chains
LineartStaticMemPool * chain_data_pool
LineartEdgeChainItem * prev
LineartEdgeChainItem * next
uint8_t material_mask_bits
uint32_t shadow_mask_bits
LineartEdgeChain * next
uint8_t intersection_mask
LineartEdgeSegment * next
LineartEdgeSegment * prev
uint32_t shadow_mask_bits
uint16_t flags
LineartVert * v1
LineartVert * v2
ListBase segments
LineartTriangle * t2
uint8_t intersection_mask
LineartTriangle * t1
Object * object_ref
double fbcoord[4]
double gloc[3]
void * last
void * first
i
Definition text_draw.cc:230