Blender V4.3
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
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
220 if (e->t1) {
221 N[0] += e->t1->gn[0];
222 N[1] += e->t1->gn[1];
223 N[2] += e->t1->gn[2];
224 }
225 if (e->t2) {
226 N[0] += e->t2->gn[0];
227 N[1] += e->t2->gn[1];
228 N[2] += e->t2->gn[2];
229 }
230 if (e->t1 || e->t2) {
232 }
233
234 /* Step 1: grow left. */
235 ba = MOD_lineart_get_bounding_area(ld, e->v1->fbcoord[0], e->v1->fbcoord[1]);
236 new_vt = e->v1;
237 es = static_cast<LineartEdgeSegment *>(e->segments.first);
238 VERT_COORD_TO_FLOAT(new_vt);
240 ec,
241 use_fbcoord,
242 use_gpos,
243 N,
244 e->flags,
245 es->occlusion,
248 e->v1->index);
249 while (ba && (new_e = lineart_line_get_connected(
250 ba, new_vt, &new_vt, e->flags, ec->intersection_mask, ec->object_ref)))
251 {
253
254 if (new_e->t1 || new_e->t2) {
255 zero_v3(N);
256 if (new_e->t1) {
257 N[0] += new_e->t1->gn[0];
258 N[1] += new_e->t1->gn[1];
259 N[2] += new_e->t1->gn[2];
260 }
261 if (new_e->t2) {
262 N[0] += new_e->t2->gn[0];
263 N[1] += new_e->t2->gn[1];
264 N[2] += new_e->t2->gn[2];
265 }
267 }
268
269 if (new_vt == new_e->v1) {
271 double gpos[3], lpos[3];
272 double *lfb = new_e->v1->fbcoord, *rfb = new_e->v2->fbcoord;
273 double global_at = lfb[3] * es->ratio / (es->ratio * lfb[3] + (1 - es->ratio) * rfb[3]);
274 interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->ratio);
275 interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at);
276 use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at);
277 POS_TO_FLOAT(lpos, gpos)
279 ec,
280 use_fbcoord,
281 use_gpos,
282 N,
283 new_e->flags,
284 es->occlusion,
287 new_e->v1->index);
288 last_occlusion = es->occlusion;
289 last_transparency = es->material_mask_bits;
290 last_shadow = es->shadow_mask_bits;
291 }
292 }
293 else if (new_vt == new_e->v2) {
294 es = static_cast<LineartEdgeSegment *>(new_e->segments.first);
295 last_occlusion = es->occlusion;
296 last_transparency = es->material_mask_bits;
297 last_shadow = es->shadow_mask_bits;
298 es = es->next;
299 for (; es; es = es->next) {
300 double gpos[3], lpos[3];
301 double *lfb = new_e->v1->fbcoord, *rfb = new_e->v2->fbcoord;
302 double global_at = lfb[3] * es->ratio / (es->ratio * lfb[3] + (1 - es->ratio) * rfb[3]);
303 interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->ratio);
304 interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at);
305 use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at);
306 POS_TO_FLOAT(lpos, gpos)
308 ec,
309 use_fbcoord,
310 use_gpos,
311 N,
312 new_e->flags,
313 last_occlusion,
314 last_transparency,
315 last_shadow,
316 new_e->v2->index);
317 last_occlusion = es->occlusion;
318 last_transparency = es->material_mask_bits;
319 last_shadow = es->shadow_mask_bits;
320 }
321 VERT_COORD_TO_FLOAT(new_e->v2);
323 ec,
324 use_fbcoord,
325 use_gpos,
326 N,
327 new_e->flags,
328 last_occlusion,
329 last_transparency,
330 last_shadow,
331 new_e->v2->index);
332 }
333 ba = MOD_lineart_get_bounding_area(ld, new_vt->fbcoord[0], new_vt->fbcoord[1]);
334 }
335
336 /* Restore normal value. */
337 if (e->t1 || e->t2) {
338 zero_v3(N);
339 if (e->t1) {
340 N[0] += e->t1->gn[0];
341 N[1] += e->t1->gn[1];
342 N[2] += e->t1->gn[2];
343 }
344 if (e->t2) {
345 N[0] += e->t2->gn[0];
346 N[1] += e->t2->gn[1];
347 N[2] += e->t2->gn[2];
348 }
350 }
351 /* Step 2: Adding all cuts from the given line, so we can continue connecting the right side
352 * of the line. */
353 es = static_cast<LineartEdgeSegment *>(e->segments.first);
354 last_occlusion = es->occlusion;
355 last_transparency = es->material_mask_bits;
356 last_shadow = es->shadow_mask_bits;
357 for (es = es->next; es; es = es->next) {
358 double gpos[3], lpos[3];
359 double *lfb = e->v1->fbcoord, *rfb = e->v2->fbcoord;
360 double global_at = lfb[3] * es->ratio / (es->ratio * lfb[3] + (1 - es->ratio) * rfb[3]);
361 interp_v3_v3v3_db(lpos, e->v1->fbcoord, e->v2->fbcoord, es->ratio);
362 interp_v3_v3v3_db(gpos, e->v1->gloc, e->v2->gloc, global_at);
363 use_fbcoord[3] = interpf(e->v2->fbcoord[3], e->v1->fbcoord[3], global_at);
364 POS_TO_FLOAT(lpos, gpos)
366 ec,
367 use_fbcoord,
368 use_gpos,
369 N,
370 e->flags,
371 es->occlusion,
374 e->v1->index);
375 last_occlusion = es->occlusion;
376 last_transparency = es->material_mask_bits;
377 last_shadow = es->shadow_mask_bits;
378 }
381 ec,
382 use_fbcoord,
383 use_gpos,
384 N,
385 e->flags,
386 last_occlusion,
387 last_transparency,
388 last_shadow,
389 e->v2->index);
390
391 /* Step 3: grow right. */
392 ba = MOD_lineart_get_bounding_area(ld, e->v2->fbcoord[0], e->v2->fbcoord[1]);
393 new_vt = e->v2;
394 while (ba && (new_e = lineart_line_get_connected(
395 ba, new_vt, &new_vt, e->flags, ec->intersection_mask, ec->object_ref)))
396 {
398
399 if (new_e->t1 || new_e->t2) {
400 zero_v3(N);
401 if (new_e->t1) {
402 N[0] += new_e->t1->gn[0];
403 N[1] += new_e->t1->gn[1];
404 N[2] += new_e->t1->gn[2];
405 }
406 if (new_e->t2) {
407 N[0] += new_e->t2->gn[0];
408 N[1] += new_e->t2->gn[1];
409 N[2] += new_e->t2->gn[2];
410 }
412 }
413
414 /* Fix leading vertex type. */
415 eci = static_cast<LineartEdgeChainItem *>(ec->chain.last);
417
418 if (new_vt == new_e->v1) {
419 es = static_cast<LineartEdgeSegment *>(new_e->segments.last);
420 last_occlusion = es->occlusion;
421 last_transparency = es->material_mask_bits;
422 last_shadow = es->shadow_mask_bits;
423 /* Fix leading vertex occlusion. */
424 eci->occlusion = last_occlusion;
425 eci->material_mask_bits = last_transparency;
426 eci->shadow_mask_bits = last_shadow;
428 double gpos[3], lpos[3];
429 double *lfb = new_e->v1->fbcoord, *rfb = new_e->v2->fbcoord;
430 double global_at = lfb[3] * es->ratio / (es->ratio * lfb[3] + (1 - es->ratio) * rfb[3]);
431 interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->ratio);
432 interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at);
433 use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at);
434 last_occlusion = es->prev ? es->prev->occlusion : last_occlusion;
435 last_transparency = es->prev ? es->prev->material_mask_bits : last_transparency;
436 last_shadow = es->prev ? es->prev->shadow_mask_bits : last_shadow;
437 POS_TO_FLOAT(lpos, gpos)
439 ec,
440 use_fbcoord,
441 use_gpos,
442 N,
443 new_e->flags,
444 last_occlusion,
445 last_transparency,
446 last_shadow,
447 new_e->v1->index);
448 }
449 }
450 else if (new_vt == new_e->v2) {
451 es = static_cast<LineartEdgeSegment *>(new_e->segments.first);
452 last_occlusion = es->occlusion;
453 last_transparency = es->material_mask_bits;
454 last_shadow = es->shadow_mask_bits;
455 eci->occlusion = last_occlusion;
456 eci->material_mask_bits = last_transparency;
457 eci->shadow_mask_bits = last_shadow;
458 es = es->next;
459 for (; es; es = es->next) {
460 double gpos[3], lpos[3];
461 double *lfb = new_e->v1->fbcoord, *rfb = new_e->v2->fbcoord;
462 double global_at = lfb[3] * es->ratio / (es->ratio * lfb[3] + (1 - es->ratio) * rfb[3]);
463 interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->ratio);
464 interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at);
465 use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at);
466 POS_TO_FLOAT(lpos, gpos)
468 ec,
469 use_fbcoord,
470 use_gpos,
471 N,
472 new_e->flags,
473 es->occlusion,
476 new_e->v2->index);
477 last_occlusion = es->occlusion;
478 last_transparency = es->material_mask_bits;
479 last_shadow = es->shadow_mask_bits;
480 }
481 VERT_COORD_TO_FLOAT(new_e->v2)
483 ec,
484 use_fbcoord,
485 use_gpos,
486 N,
487 new_e->flags,
488 last_occlusion,
489 last_transparency,
490 last_shadow,
491 new_e->v2->index);
492 }
493 ba = MOD_lineart_get_bounding_area(ld, new_vt->fbcoord[0], new_vt->fbcoord[1]);
494 }
495 if (ld->conf.fuzzy_everything) {
497 }
498 else {
499 ec->type = (e->flags & MOD_LINEART_EDGE_FLAG_ALL_TYPE);
500 }
501 }
503}
504
508{
509 if (root->child == nullptr) {
510 return root;
511 }
512
513 LineartBoundingArea *ch = root->child;
514#define IN_BOUND(ba, eci) \
515 ba.l <= eci->pos[0] && ba.r >= eci->pos[0] && ba.b <= eci->pos[1] && ba.u >= eci->pos[1]
516
517 if (IN_BOUND(ch[0], eci)) {
518 return lineart_bounding_area_get_eci_recursive(ld, &ch[0], eci);
519 }
520 if (IN_BOUND(ch[1], eci)) {
521 return lineart_bounding_area_get_eci_recursive(ld, &ch[1], eci);
522 }
523 if (IN_BOUND(ch[2], eci)) {
524 return lineart_bounding_area_get_eci_recursive(ld, &ch[2], eci);
525 }
526 if (IN_BOUND(ch[3], eci)) {
527 return lineart_bounding_area_get_eci_recursive(ld, &ch[3], eci);
528 }
529#undef IN_BOUND
530 return nullptr;
531}
532
535{
536 if (!eci) {
537 return nullptr;
538 }
540 if (root == nullptr) {
541 return nullptr;
542 }
543 return lineart_bounding_area_get_eci_recursive(ld, root, eci);
544}
545
556{
557 if (root->child == nullptr) {
561
562 cre->eci = eci;
563
564 if (eci == ec->chain.first) {
565 cre->is_left = 1;
566 }
567 }
568 else {
569 LineartBoundingArea *ch = root->child;
570
571#define IN_BOUND(ba, eci) \
572 ba.l <= eci->pos[0] && ba.r >= eci->pos[0] && ba.b <= eci->pos[1] && ba.u >= eci->pos[1]
573
574 if (IN_BOUND(ch[0], eci)) {
576 }
577 else if (IN_BOUND(ch[1], eci)) {
579 }
580 else if (IN_BOUND(ch[2], eci)) {
582 }
583 else if (IN_BOUND(ch[3], eci)) {
585 }
586
587#undef IN_BOUND
588 }
589}
590
592{
593 LineartEdgeChainItem *pl = static_cast<LineartEdgeChainItem *>(ec->chain.first);
594 LineartEdgeChainItem *pr = static_cast<LineartEdgeChainItem *>(ec->chain.last);
597
598 if (ba1) {
600 }
601 if (ba2) {
603 }
604}
605
607 LineartEdgeChainItem *last_matching_eci,
608 float distance_threshold,
609 bool preserve_details,
610 LineartEdgeChainItem **r_next_eci)
611{
612 float dist_accum = 0;
613
614 int fixed_occ = last_matching_eci->occlusion;
615 uint8_t fixed_mask = last_matching_eci->material_mask_bits;
616 uint32_t fixed_shadow = last_matching_eci->shadow_mask_bits;
617
618 LineartEdgeChainItem *can_skip_to = nullptr;
619 LineartEdgeChainItem *last_eci = last_matching_eci;
620 for (LineartEdgeChainItem *eci = last_matching_eci->next; eci; eci = eci->next) {
621 dist_accum += len_v2v2(last_eci->pos, eci->pos);
622 if (dist_accum > distance_threshold) {
623 break;
624 }
625 last_eci = eci;
626 /* The reason for this is because we don't want visible segments to be "skipped" into
627 * connecting with invisible segments. */
628 if (eci->occlusion < fixed_occ) {
629 break;
630 }
631 if (eci->material_mask_bits == fixed_mask && eci->occlusion == fixed_occ &&
632 eci->shadow_mask_bits == fixed_shadow)
633 {
634 can_skip_to = eci;
635 }
636 }
637 if (can_skip_to) {
638 /* Either mark all in-between segments with the same occlusion and mask or delete those
639 * different ones. */
640 LineartEdgeChainItem *next_eci;
641 for (LineartEdgeChainItem *eci = last_matching_eci->next; eci != can_skip_to; eci = next_eci) {
642 next_eci = eci->next;
643 if (eci->material_mask_bits == fixed_mask && eci->occlusion == fixed_occ &&
644 eci->shadow_mask_bits == fixed_shadow)
645 {
646 continue;
647 }
648 if (preserve_details) {
649 eci->material_mask_bits = fixed_mask;
650 eci->occlusion = fixed_occ;
651 eci->shadow_mask_bits = fixed_shadow;
652 }
653 else {
654 BLI_remlink(&ec->chain, eci);
655 }
656 }
657 *r_next_eci = can_skip_to;
658 return true;
659 }
660 return false;
661}
662
664{
665 LineartEdgeChainItem *eci, *next_eci;
666 ListBase swap = {nullptr};
667
668 swap.first = ld->chains.first;
669 swap.last = ld->chains.last;
670
671 ld->chains.last = ld->chains.first = nullptr;
672
673 int loop_id = 0;
674 while (LineartEdgeChain *ec = static_cast<LineartEdgeChain *>(BLI_pophead(&swap))) {
675 ec->next = ec->prev = nullptr;
676 BLI_addtail(&ld->chains, ec);
677
678 ec->loop_id = loop_id;
679 loop_id++;
680
681 LineartEdgeChainItem *first_eci = (LineartEdgeChainItem *)ec->chain.first;
682 int fixed_occ = first_eci->occlusion;
683 uint8_t fixed_mask = first_eci->material_mask_bits;
684 uint32_t fixed_shadow = first_eci->shadow_mask_bits;
685 ec->level = fixed_occ;
686 ec->material_mask_bits = fixed_mask;
687 ec->shadow_mask_bits = fixed_shadow;
688 for (eci = first_eci->next; eci; eci = next_eci) {
689 next_eci = eci->next;
690 if (eci->occlusion != fixed_occ || eci->material_mask_bits != fixed_mask ||
691 eci->shadow_mask_bits != fixed_shadow)
692 {
693 if (next_eci) {
694 if (lineart_point_overlapping(next_eci, eci->pos[0], eci->pos[1], 1e-5)) {
695 continue;
696 }
698 eci->prev,
701 &next_eci))
702 {
703 continue;
704 }
705 }
706 else {
707 /* Set the same occlusion level for the end vertex, so when further connection is needed
708 * the backwards occlusion info is also correct. */
709 eci->occlusion = fixed_occ;
710 eci->shadow_mask_bits = fixed_shadow;
711 eci->material_mask_bits = fixed_mask;
712 /* No need to split at the last point anyway. */
713 break;
714 }
716 new_ec->chain.first = eci;
717 new_ec->chain.last = ec->chain.last;
718 new_ec->loop_id = loop_id;
719 ec->chain.last = eci->prev;
720 ((LineartEdgeChainItem *)ec->chain.last)->next = nullptr;
721 eci->prev = nullptr;
722
723 /* End the previous one. */
725 ec,
726 eci->pos,
727 eci->gpos,
728 eci->normal,
729 eci->line_type,
730 fixed_occ,
731 fixed_mask,
732 fixed_shadow,
733 eci->index);
734 new_ec->object_ref = ec->object_ref;
735 new_ec->type = ec->type;
736 new_ec->intersection_mask = ec->intersection_mask;
737 ec = new_ec;
738 fixed_occ = eci->occlusion;
739 fixed_mask = eci->material_mask_bits;
740 fixed_shadow = eci->shadow_mask_bits;
741 ec->level = fixed_occ;
742 ec->material_mask_bits = fixed_mask;
743 ec->shadow_mask_bits = fixed_shadow;
744 }
745 }
746 }
747
749
752 }
753}
754
759 LineartEdgeChain *onto,
760 LineartEdgeChain *sub,
761 int reverse_1,
762 int reverse_2)
763{
765 if (onto->type == MOD_LINEART_EDGE_FLAG_INTERSECTION) {
768 }
769 if (sub->object_ref) {
770 onto->object_ref = sub->object_ref;
771 }
772 }
773 else if (sub->type == MOD_LINEART_EDGE_FLAG_INTERSECTION) {
774 if (onto->type != MOD_LINEART_EDGE_FLAG_INTERSECTION) {
776 }
777 }
778 if (!reverse_1) { /* L--R L-R. */
779 if (reverse_2) { /* L--R R-L. */
781 }
782 eci = static_cast<LineartEdgeChainItem *>(sub->chain.first);
784 static_cast<LineartEdgeChainItem *>(onto->chain.last), eci->pos[0], eci->pos[1], 1e-5))
785 {
786 BLI_pophead(&sub->chain);
787 if (sub->chain.first == nullptr) {
788 return;
789 }
790 }
791 ((LineartEdgeChainItem *)onto->chain.last)->next = static_cast<LineartEdgeChainItem *>(
792 sub->chain.first);
793 ((LineartEdgeChainItem *)sub->chain.first)->prev = static_cast<LineartEdgeChainItem *>(
794 onto->chain.last);
795 onto->chain.last = sub->chain.last;
796 }
797 else { /* L-R L--R. */
798 if (!reverse_2) { /* R-L L--R. */
800 }
801 eci = static_cast<LineartEdgeChainItem *>(onto->chain.first);
803 static_cast<LineartEdgeChainItem *>(sub->chain.last), eci->pos[0], eci->pos[1], 1e-5))
804 {
805 BLI_pophead(&onto->chain);
806 if (onto->chain.first == nullptr) {
807 return;
808 }
809 }
810 ((LineartEdgeChainItem *)sub->chain.last)->next = static_cast<LineartEdgeChainItem *>(
811 onto->chain.first);
812 ((LineartEdgeChainItem *)onto->chain.first)->prev = static_cast<LineartEdgeChainItem *>(
813 sub->chain.last);
814 onto->chain.first = sub->chain.first;
815 }
816}
817
822 int occlusion,
823 uint8_t material_mask_bits,
824 uint8_t isec_mask,
825 uint32_t shadow_mask,
826 int loop_id,
827 float dist,
828 float *result_new_len,
829 LineartBoundingArea *caller_ba)
830{
831
832 LineartChainRegisterEntry *closest_cre = nullptr;
833
834 /* Keep using for loop because `cre` could be removed from the iteration before getting to the
835 * next one. */
837 if (cre->ec->object_ref != ec->object_ref) {
838 if (!ld->conf.fuzzy_everything) {
839 if (ld->conf.fuzzy_intersections) {
840 /* If none of those are intersection lines... */
841 if (!(cre->ec->type & MOD_LINEART_EDGE_FLAG_INTERSECTION) &&
843 {
844 continue; /* We don't want to chain along different objects at the moment. */
845 }
846 }
847 else {
848 continue;
849 }
850 }
851 }
852 if (cre->ec->picked || cre->picked) {
853 continue;
854 }
855 if (cre->ec == ec || (!cre->ec->chain.first) || (cre->ec->level != occlusion) ||
856 (cre->ec->material_mask_bits != material_mask_bits) ||
857 (cre->ec->intersection_mask != isec_mask) || (cre->ec->shadow_mask_bits != shadow_mask))
858 {
859 continue;
860 }
861 if (!ld->conf.fuzzy_everything) {
862 if (cre->ec->type != ec->type) {
863 if (ld->conf.fuzzy_intersections) {
864 if (!(cre->ec->type == MOD_LINEART_EDGE_FLAG_INTERSECTION ||
866 {
867 continue; /* Fuzzy intersections but no intersection line found. */
868 }
869 }
870 else { /* Line type different but no fuzzy. */
871 continue;
872 }
873 }
874 }
875
876 float new_len = ld->conf.use_geometry_space_chain ? len_v3v3(cre->eci->gpos, eci->gpos) :
877 len_v2v2(cre->eci->pos, eci->pos);
878 /* Even if the vertex is not from the same contour loop, we try to chain it still if the
879 * distance is small enough. This way we can better chain smaller loops and smooth them out
880 * later. */
881 if (((cre->ec->loop_id == loop_id) && (new_len < dist)) ||
882 ((cre->ec->loop_id != loop_id) && (new_len < dist / 10)))
883 {
884 closest_cre = cre;
885 dist = new_len;
886 if (result_new_len) {
887 (*result_new_len) = new_len;
888 }
889 }
890 }
891
892 /* We want a closer point anyway. So using modified dist is fine. */
893 float adjacent_new_len = dist;
894 LineartChainRegisterEntry *adjacent_closest;
895
896#define LRT_TEST_ADJACENT_AREAS(dist_to, list) \
897 if (dist_to < dist && dist_to > 0) { \
898 LISTBASE_FOREACH (LinkData *, link, list) { \
899 LineartBoundingArea *sba = (LineartBoundingArea *)link->data; \
900 adjacent_closest = lineart_chain_get_closest_cre(ld, \
901 sba, \
902 ec, \
903 eci, \
904 occlusion, \
905 material_mask_bits, \
906 isec_mask, \
907 shadow_mask, \
908 loop_id, \
909 dist, \
910 &adjacent_new_len, \
911 ba); \
912 if (adjacent_new_len < dist) { \
913 dist = adjacent_new_len; \
914 closest_cre = adjacent_closest; \
915 } \
916 } \
917 }
918 if (!caller_ba) {
919 LRT_TEST_ADJACENT_AREAS(eci->pos[0] - ba->l, &ba->lp);
920 LRT_TEST_ADJACENT_AREAS(ba->r - eci->pos[0], &ba->rp);
921 LRT_TEST_ADJACENT_AREAS(ba->u - eci->pos[1], &ba->up);
922 LRT_TEST_ADJACENT_AREAS(eci->pos[1] - ba->b, &ba->bp);
923 }
924 if (result_new_len) {
925 (*result_new_len) = dist;
926 }
927 return closest_cre;
928}
929
931{
932 LineartEdgeChainItem *eci_l, *eci_r;
933 LineartBoundingArea *ba_l, *ba_r;
934 LineartChainRegisterEntry *closest_cre_l, *closest_cre_r, *closest_cre;
935 float dist = ld->conf.chaining_image_threshold;
936 float dist_l, dist_r;
937 int reverse_main, loop_id;
938 uint8_t occlusion, material_mask_bits, isec_mask;
939 uint32_t shadow_mask;
940 ListBase swap = {nullptr};
941
942 if (ld->conf.chaining_image_threshold < 0.0001) {
943 return;
944 }
945
946 swap.first = ld->chains.first;
947 swap.last = ld->chains.last;
948
949 ld->chains.last = ld->chains.first = nullptr;
950
951 while (LineartEdgeChain *ec = static_cast<LineartEdgeChain *>(BLI_pophead(&swap))) {
952 ec->next = ec->prev = nullptr;
953 if (ec->picked || ec->chain.first == ec->chain.last) {
954 continue;
955 }
956 BLI_addtail(&ld->chains, ec);
957 loop_id = ec->loop_id;
958
959 if (ec->type == MOD_LINEART_EDGE_FLAG_LOOSE && (!ld->conf.use_loose_edge_chain)) {
960 continue;
961 }
962
963 occlusion = ec->level;
964 material_mask_bits = ec->material_mask_bits;
965 isec_mask = ec->intersection_mask;
966 shadow_mask = ec->shadow_mask_bits;
967
968 eci_l = static_cast<LineartEdgeChainItem *>(ec->chain.first);
969 eci_r = static_cast<LineartEdgeChainItem *>(ec->chain.last);
970 while ((ba_l = lineart_bounding_area_get_end_point(ld, eci_l)) &&
971 (ba_r = lineart_bounding_area_get_end_point(ld, eci_r)))
972 {
973 closest_cre_l = lineart_chain_get_closest_cre(ld,
974 ba_l,
975 ec,
976 eci_l,
977 occlusion,
978 material_mask_bits,
979 isec_mask,
980 shadow_mask,
981 loop_id,
982 dist,
983 &dist_l,
984 nullptr);
985 closest_cre_r = lineart_chain_get_closest_cre(ld,
986 ba_r,
987 ec,
988 eci_r,
989 occlusion,
990 material_mask_bits,
991 isec_mask,
992 shadow_mask,
993 loop_id,
994 dist,
995 &dist_r,
996 nullptr);
997 if (closest_cre_l && closest_cre_r) {
998 if (dist_l < dist_r) {
999 closest_cre = closest_cre_l;
1000 reverse_main = 1;
1001 }
1002 else {
1003 closest_cre = closest_cre_r;
1004 reverse_main = 0;
1005 }
1006 }
1007 else if (closest_cre_l) {
1008 closest_cre = closest_cre_l;
1009 reverse_main = 1;
1010 }
1011 else if (closest_cre_r) {
1012 closest_cre = closest_cre_r;
1013 BLI_remlink(&ba_r->linked_chains, closest_cre_r);
1014 reverse_main = 0;
1015 }
1016 else {
1017 break;
1018 }
1019 closest_cre->picked = 1;
1020 closest_cre->ec->picked = 1;
1021 if (closest_cre->is_left) {
1022 lineart_chain_connect(ld, ec, closest_cre->ec, reverse_main, 0);
1023 }
1024 else {
1025 lineart_chain_connect(ld, ec, closest_cre->ec, reverse_main, 1);
1026 }
1027 BLI_remlink(&swap, closest_cre->ec);
1028 eci_l = static_cast<LineartEdgeChainItem *>(ec->chain.first);
1029 eci_r = static_cast<LineartEdgeChainItem *>(ec->chain.last);
1030 }
1031 ec->picked = 1;
1032 }
1033}
1034
1036{
1038 float offset_accum = 0;
1039 float dist;
1040 float last_point[2];
1041
1042 eci = static_cast<LineartEdgeChainItem *>(ec->chain.first);
1043 if (!eci) {
1044 return 0;
1045 }
1046 copy_v2_v2(last_point, eci->pos);
1048 dist = len_v2v2(eci->pos, last_point);
1049 offset_accum += dist;
1050 copy_v2_v2(last_point, eci->pos);
1051 }
1052 return offset_accum;
1053}
1054
1056 const float threshold,
1057 uint8_t max_occlusion)
1058{
1059 LineartEdgeChain *ec, *next_ec;
1060 for (ec = static_cast<LineartEdgeChain *>(ld->chains.first); ec; ec = next_ec) {
1061 next_ec = ec->next;
1062 if (ec->level > max_occlusion || MOD_lineart_chain_compute_length(ec) < threshold) {
1063 BLI_remlink(&ld->chains, ec);
1064 }
1065 }
1066}
1067
1069{
1070 int count = 0;
1072 count++;
1073 }
1074 return count;
1075}
1076
1078{
1079 if (lc == nullptr) {
1080 return;
1081 }
1083 ec->picked = 0;
1084 }
1085}
1086
1088{
1090 if (eln->object_ref == ob) {
1091 return eln;
1092 }
1093 }
1094 return nullptr;
1095}
1096
1098{
1100 if (ELEM(ec->type,
1104 {
1105 continue;
1106 }
1108 ec->object_ref);
1109 BLI_assert(eln != nullptr);
1110 if (LIKELY(eln)) {
1111 LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) {
1112 if (eci->index > eln->global_index_offset) {
1113 eci->index -= eln->global_index_offset;
1114 }
1115 }
1116 }
1117 }
1118}
1119
1120void MOD_lineart_smooth_chains(LineartData *ld, float tolerance)
1121{
1123 /* Go through the chain two times, once from each direction. */
1124 for (int times = 0; times < 2; times++) {
1125 for (LineartEdgeChainItem *eci = static_cast<LineartEdgeChainItem *>(ec->chain.first),
1126 *next_eci = eci->next;
1127 eci;
1128 eci = next_eci)
1129 {
1130 LineartEdgeChainItem *eci2, *eci3, *eci4;
1131
1132 if (!(eci2 = eci->next) || !(eci3 = eci2->next)) {
1133 /* Not enough points to simplify. */
1134 next_eci = eci->next;
1135 continue;
1136 }
1137 /* No need to care for different line types/occlusion and so on, because at this stage they
1138 * are all the same within a chain.
1139 *
1140 * We need to simplify a chain from this:
1141 * 1-----------2
1142 * 3-----------4
1143 * to this:
1144 * 1-----------2--_
1145 * `--4 */
1146
1147 /* If p3 is within the p1-p2 segment of a width of "tolerance", in other words, p3 is
1148 * approximately on the segment of p1-p2. */
1149 if (dist_to_line_segment_v2(eci3->pos, eci->pos, eci2->pos) < tolerance) {
1150 float vec2[2], vec3[2], v2n[2], ratio, len2;
1151 sub_v2_v2v2(vec2, eci2->pos, eci->pos);
1152 sub_v2_v2v2(vec3, eci3->pos, eci->pos);
1153 normalize_v2_v2(v2n, vec2);
1154 ratio = dot_v2v2(v2n, vec3);
1155 len2 = len_v2(vec2);
1156 /* Because this smoothing applies on geometries of different scales in the same scene,
1157 * some small scale features (e.g. the "tails" on the inner ring of a torus geometry)
1158 * could be completely erased if the tolerance value is set for accommodating the entire
1159 * scene. Those situations typically result in (ratio << 0), looks like this:
1160 * 1---2
1161 * 3-------------------------------4
1162 * (this sort of long zigzag obviously are "features" that can't be erased)
1163 * setting a ratio of -10 turned out to be a reasonable threshold in tests. */
1164 if (ratio < len2 && ratio > -len2 * 10) {
1165 /* We only remove p3 if p4 is on the extension of p1->p2. */
1166 if ((eci4 = eci3->next) &&
1167 (dist_to_line_v2(eci4->pos, eci->pos, eci2->pos) < tolerance))
1168 {
1169 BLI_remlink(&ec->chain, eci3);
1170 next_eci = eci;
1171 continue;
1172 }
1173 if (!eci4) {
1174 /* See if the last segment's direction is reversed, if so remove that.
1175 * Basically we don't need to preserve p3 if the entire chain looked like this:
1176 * ...----1----3===2 */
1177 if (len_v2(vec2) > len_v2(vec3)) {
1178 BLI_remlink(&ec->chain, eci3);
1179 }
1180 next_eci = nullptr;
1181 continue;
1182 }
1183 }
1184 }
1185 next_eci = eci->next;
1186 }
1187 BLI_listbase_reverse(&ec->chain);
1188 }
1189 }
1190}
1191
1193 LineartEdgeChainItem *eci_inside,
1194 LineartEdgeChainItem *eci_outside)
1195{
1196 float isec[2];
1197 /* l: left, r: right, b: bottom, u: top. */
1198 float ref_lu[2] = {-1.0f, 1.0f}, ref_lb[2] = {-1.0f, -1.0f}, ref_ru[2] = {1.0f, 1.0f},
1199 ref_rb[2] = {1.0f, -1.0f};
1200 bool found = false;
1201 LineartEdgeChainItem *eci2 = eci_outside, *eci1 = eci_inside;
1202 if (eci2->pos[0] < -1.0f) {
1203 found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, ref_lu, ref_lb, isec) > 0);
1204 }
1205 if (!found && eci2->pos[0] > 1.0f) {
1206 found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, ref_ru, ref_rb, isec) > 0);
1207 }
1208 if (!found && eci2->pos[1] < -1.0f) {
1209 found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, ref_lb, ref_rb, isec) > 0);
1210 }
1211 if (!found && eci2->pos[1] > 1.0f) {
1212 found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, ref_lu, ref_ru, isec) > 0);
1213 }
1214
1215 if (UNLIKELY(!found)) {
1216 return nullptr;
1217 }
1218
1219 float ratio = (fabs(eci2->pos[0] - eci1->pos[0]) > fabs(eci2->pos[1] - eci1->pos[1])) ?
1220 ratiof(eci1->pos[0], eci2->pos[0], isec[0]) :
1221 ratiof(eci1->pos[1], eci2->pos[1], isec[1]);
1222 float gratio = eci1->pos[3] * ratio / (ratio * eci1->pos[3] + (1 - ratio) * eci2->pos[3]);
1223
1224 LineartEdgeChainItem *eci = static_cast<LineartEdgeChainItem *>(
1226 memcpy(eci, eci1, sizeof(LineartEdgeChainItem));
1227 interp_v3_v3v3(eci->gpos, eci1->gpos, eci2->gpos, gratio);
1228 interp_v3_v3v3(eci->pos, eci1->pos, eci2->pos, ratio);
1229 eci->pos[3] = interpf(eci2->pos[3], eci1->pos[3], gratio);
1230 eci->next = eci->prev = nullptr;
1231 return eci;
1232}
1233
1234#define LRT_ECI_INSIDE(eci) \
1235 ((eci)->pos[0] >= -1.0f && (eci)->pos[0] <= 1.0f && (eci)->pos[1] >= -1.0f && \
1236 (eci)->pos[1] <= 1.0f)
1237
1239{
1240 LineartEdgeChainItem *eci, *next_eci, *prev_eci, *new_eci;
1241 bool is_inside, new_inside;
1242 ListBase swap = {nullptr};
1243 swap.first = ld->chains.first;
1244 swap.last = ld->chains.last;
1245
1246 ld->chains.last = ld->chains.first = nullptr;
1247 while (LineartEdgeChain *ec = static_cast<LineartEdgeChain *>(BLI_pophead(&swap))) {
1248 bool ec_added = false;
1249 LineartEdgeChainItem *first_eci = (LineartEdgeChainItem *)ec->chain.first;
1250 is_inside = LRT_ECI_INSIDE(first_eci) ? true : false;
1251 if (!is_inside) {
1252 ec->picked = 1;
1253 }
1254 for (eci = first_eci->next; eci; eci = next_eci) {
1255 next_eci = eci->next;
1256 prev_eci = eci->prev;
1257
1258 /* We only need to do something if the edge crossed from outside to the inside or from inside
1259 * to the outside. */
1260 if ((new_inside = LRT_ECI_INSIDE(eci)) != is_inside) {
1261 if (new_inside == false) {
1262 /* Stroke goes out. */
1263 new_eci = lineart_chain_create_crossing_point(ld, prev_eci, eci);
1264
1265 LineartEdgeChain *new_ec = static_cast<LineartEdgeChain *>(
1267 memcpy(new_ec, ec, sizeof(LineartEdgeChain));
1268 new_ec->chain.first = next_eci;
1269 eci->prev = nullptr;
1270 prev_eci->next = nullptr;
1271 ec->chain.last = prev_eci;
1272 BLI_addtail(&ec->chain, new_eci);
1273 BLI_addtail(&ld->chains, ec);
1274 ec_added = true;
1275 ec = new_ec;
1276
1277 next_eci = eci->next;
1278 is_inside = new_inside;
1279 continue;
1280 }
1281 /* Stroke comes in. */
1282 new_eci = lineart_chain_create_crossing_point(ld, eci, prev_eci);
1283
1284 ec->chain.first = eci;
1285 eci->prev = nullptr;
1286
1287 BLI_addhead(&ec->chain, new_eci);
1288
1289 ec_added = false;
1290
1291 next_eci = eci->next;
1292 is_inside = new_inside;
1293 continue;
1294 }
1295 }
1296
1297 if ((!ec_added) && is_inside) {
1298 BLI_addtail(&ld->chains, ec);
1299 }
1300 }
1301}
1302
1303void MOD_lineart_chain_split_angle(LineartData *ld, float angle_threshold_rad)
1304{
1305 LineartEdgeChainItem *eci, *next_eci, *prev_eci;
1306 ListBase swap = {nullptr};
1307
1308 swap.first = ld->chains.first;
1309 swap.last = ld->chains.last;
1310
1311 ld->chains.last = ld->chains.first = nullptr;
1312
1313 while (LineartEdgeChain *ec = static_cast<LineartEdgeChain *>(BLI_pophead(&swap))) {
1314 ec->next = ec->prev = nullptr;
1315 BLI_addtail(&ld->chains, ec);
1316 LineartEdgeChainItem *first_eci = (LineartEdgeChainItem *)ec->chain.first;
1317 for (eci = first_eci->next; eci; eci = next_eci) {
1318 next_eci = eci->next;
1319 prev_eci = eci->prev;
1320 float angle = M_PI;
1321 if (next_eci && prev_eci) {
1322 angle = angle_v2v2v2(prev_eci->pos, eci->pos, next_eci->pos);
1323 }
1324 else {
1325 break; /* No need to split at the last point anyway. */
1326 }
1327 if (angle < angle_threshold_rad) {
1328 LineartEdgeChain *new_ec;
1329 new_ec = lineart_chain_create(ld);
1330 new_ec->chain.first = eci;
1331 new_ec->chain.last = ec->chain.last;
1332 ec->chain.last = eci->prev;
1333 ((LineartEdgeChainItem *)ec->chain.last)->next = nullptr;
1334 eci->prev = nullptr;
1335
1336 /* End the previous one. */
1338 ec,
1339 eci->pos,
1340 eci->gpos,
1341 eci->normal,
1342 eci->line_type,
1343 ec->level,
1344 eci->material_mask_bits,
1345 eci->shadow_mask_bits,
1346 eci->index);
1347 new_ec->object_ref = ec->object_ref;
1348 new_ec->type = ec->type;
1349 new_ec->level = ec->level;
1350 new_ec->loop_id = ec->loop_id;
1351 new_ec->intersection_mask = ec->intersection_mask;
1352 new_ec->material_mask_bits = ec->material_mask_bits;
1353 new_ec->shadow_mask_bits = ec->shadow_mask_bits;
1354 ec = new_ec;
1355 }
1356 }
1357 }
1358}
1359
1360void MOD_lineart_chain_offset_towards_camera(LineartData *ld, float dist, bool use_custom_camera)
1361{
1362 float dir[3];
1363 float cam[3];
1364 float view[3];
1365 float view_clamp[3];
1366
1367 if (use_custom_camera) {
1368 copy_v3fl_v3db(cam, ld->conf.camera_pos);
1369 }
1370 else {
1372 }
1373
1374 if (ld->conf.cam_is_persp) {
1376 LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) {
1377 sub_v3_v3v3(dir, cam, eci->gpos);
1378 float orig_len = len_v3(dir);
1379 normalize_v3(dir);
1380 mul_v3_fl(dir, std::min<float>(dist, orig_len - ld->conf.near_clip));
1381 add_v3_v3(eci->gpos, dir);
1382 }
1383 }
1384 }
1385 else {
1386 copy_v3fl_v3db(view, ld->conf.view_vector);
1388 LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) {
1389 sub_v3_v3v3(dir, cam, eci->gpos);
1390 float len_lim = dot_v3v3(view, dir) - ld->conf.near_clip;
1391 normalize_v3_v3(view_clamp, view);
1392 mul_v3_fl(view_clamp, std::min(dist, len_lim));
1393 add_v3_v3(eci->gpos, view_clamp);
1394 }
1395 }
1396 }
1397}
1398
1400{
1402 if (ec->type == MOD_LINEART_EDGE_FLAG_CONTOUR &&
1403 ec->shadow_mask_bits & LRT_SHADOW_SILHOUETTE_ERASED_GROUP)
1404 {
1405 uint32_t target = ec->shadow_mask_bits & LRT_OBINDEX_HIGHER;
1407 target);
1408 if (!eln) {
1409 continue;
1410 }
1411 ec->silhouette_backdrop = static_cast<Object *>(eln->object_ref);
1412 }
1413 }
1414}
#define BLI_assert(a)
Definition BLI_assert.h:50
void BLI_addhead(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:90
#define LISTBASE_FOREACH(type, var, list)
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
#define LISTBASE_FOREACH_BACKWARD(type, var, list)
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:130
void void void void void void void BLI_listbase_reverse(struct ListBase *lb) ATTR_NONNULL(1)
Definition listbase.cc:823
void * BLI_pophead(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:251
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:298
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:284
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)
Definition math_vector.c:36
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
#define DBL_EDGE_LIM
#define LRT_OBINDEX_HIGHER
#define LRT_SHADOW_SILHOUETTE_ERASED_GROUP
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
static bool is_inside(int x, int y, int cols, int rows)
Definition filesel.cc:768
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
vec3(0.0)") .vertex_in(0
unsigned int uint32_t
Definition stdint.h:80
unsigned char uint8_t
Definition stdint.h:78
struct LineartBoundingArea * child
struct 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
struct LineartEdgeChainItem * next
struct LineartEdgeChainItem * prev
uint8_t material_mask_bits
struct LineartEdgeChain * next
uint32_t shadow_mask_bits
uint8_t intersection_mask
struct Object * object_ref
struct LineartEdgeSegment * prev
struct LineartEdgeSegment * next
uint32_t shadow_mask_bits
uint16_t flags
struct LineartVert * v2
ListBase segments
struct LineartTriangle * t2
uint8_t intersection_mask
struct LineartTriangle * t1
struct Object * object_ref
struct LineartVert * v1
double fbcoord[4]
double gloc[3]
void * last
void * first