Blender V4.5
lineart_cpu.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
5/* \file
6 * \ingroup editors
7 */
8
9#include <algorithm>
10
11#include "MOD_lineart.hh"
12
13#include "BLI_listbase.h"
14#include "BLI_math_geom.h"
15#include "BLI_math_matrix.h"
16#include "BLI_math_matrix.hh"
17#include "BLI_math_rotation.h"
18#include "BLI_sort.hh"
19#include "BLI_task.h"
20#include "BLI_time.h"
21#include "BLI_utildefines.h"
22#include "BLI_vector.hh"
23
24#include "BKE_attribute.hh"
25#include "BKE_camera.h"
26#include "BKE_collection.hh"
27#include "BKE_curves.hh"
28#include "BKE_customdata.hh"
29#include "BKE_deform.hh"
30#include "BKE_geometry_set.hh"
31#include "BKE_global.hh"
32#include "BKE_gpencil_legacy.h"
33#include "BKE_grease_pencil.hh"
34#include "BKE_lib_id.hh"
35#include "BKE_material.hh"
36#include "BKE_mesh.hh"
37#include "BKE_object.hh"
38#include "BKE_scene.hh"
39
41
42#include "DNA_camera_types.h"
44#include "DNA_light_types.h"
45#include "DNA_material_types.h"
46#include "DNA_meshdata_types.h"
47#include "DNA_modifier_types.h"
48#include "DNA_scene_types.h"
49
50#include "MEM_guardedalloc.h"
51
52#include "RE_pipeline.h"
53#include "intern/render_types.h"
54
55#include "ED_grease_pencil.hh"
56
58
59#include "lineart_intern.hh"
60
61using blender::float3;
62using blender::int3;
64using namespace blender::bke;
66
68 double v1[3], v2[3];
70};
71
74
75 /* Scheduled work range. */
80
81 /* Thread intersection result data. */
84 int max;
86
87 /* For individual thread reference. */
89};
90
96
98 LineartBoundingArea *root_ba,
99 LineartEdge *e);
100
102 LineartData *ld, LineartEdge *e, int *rowbegin, int *rowend, int *colbegin, int *colend);
103
105 const LineartEdge *e,
106 const double *override_camera_loc,
107 const bool override_cam_is_persp,
108 const bool allow_overlapping_edges,
109 const double m_view_projection[4][4],
110 const double camera_dir[3],
111 const float cam_shift_x,
112 const float cam_shift_y,
113 double *from,
114 double *to);
115
117 LineartBoundingArea *root_ba,
118 LineartTriangle *tri,
119 double l_r_u_b[4],
120 int recursive,
121 int recursive_level,
122 bool do_intersection,
124
125static void lineart_free_bounding_area_memory(LineartBoundingArea *ba, bool recursive);
126
128
130{
132
133 memset(es, 0, sizeof(LineartEdgeSegment));
134
135 /* Storing the node for potentially reuse the memory for new segment data.
136 * Line Art data is not freed after all calculations are done. */
137 BLI_addtail(&ld->wasted_cuts, es);
138
140}
141
143{
145
146 /* See if there is any already allocated memory we can reuse. */
147 if (ld->wasted_cuts.first) {
150 memset(es, 0, sizeof(LineartEdgeSegment));
151 return es;
152 }
154
155 /* Otherwise allocate some new memory. */
157 sizeof(LineartEdgeSegment));
158}
159
161 LineartEdge *e,
162 double start,
163 double end,
164 uchar material_mask_bits,
165 uchar mat_occlusion,
166 uint32_t shadow_bits)
167{
168 LineartEdgeSegment *i_seg, *prev_seg;
169 LineartEdgeSegment *cut_start_before = nullptr, *cut_end_before = nullptr;
170 LineartEdgeSegment *new_seg1 = nullptr, *new_seg2 = nullptr;
171 int untouched = 0;
172
173 /* If for some reason the occlusion function may give a result that has zero length, or reversed
174 * in direction, or NAN, we take care of them here. */
175 if (LRT_DOUBLE_CLOSE_ENOUGH(start, end)) {
176 return;
177 }
178 if (LRT_DOUBLE_CLOSE_ENOUGH(start, 1) || LRT_DOUBLE_CLOSE_ENOUGH(end, 0)) {
179 return;
180 }
181 if (UNLIKELY(start != start)) {
182 start = 0.0;
183 }
184 if (UNLIKELY(end != end)) {
185 end = 0.0;
186 }
187
188 if (start > end) {
189 double t = start;
190 start = end;
191 end = t;
192 }
193
194 /* Begin looking for starting position of the segment. */
195 /* Not using a list iteration macro because of it more clear when using for loops to iterate
196 * through the segments. */
197 LISTBASE_FOREACH (LineartEdgeSegment *, seg, &e->segments) {
198 if (LRT_DOUBLE_CLOSE_ENOUGH(seg->ratio, start)) {
199 cut_start_before = seg;
200 new_seg1 = cut_start_before;
201 break;
202 }
203 if (seg->next == nullptr) {
204 break;
205 }
206 i_seg = seg->next;
207 if (i_seg->ratio > start + 1e-09 && start > seg->ratio) {
208 cut_start_before = i_seg;
209 new_seg1 = lineart_give_segment(ld);
210 break;
211 }
212 }
213 if (!cut_start_before && LRT_DOUBLE_CLOSE_ENOUGH(1, end)) {
214 untouched = 1;
215 }
216 for (LineartEdgeSegment *seg = cut_start_before; seg; seg = seg->next) {
217 /* We tried to cut ratio existing cutting point (e.g. where the line's occluded by a triangle
218 * strip). */
219 if (LRT_DOUBLE_CLOSE_ENOUGH(seg->ratio, end)) {
220 cut_end_before = seg;
221 new_seg2 = cut_end_before;
222 break;
223 }
224 /* This check is to prevent `es->ratio == 1.0` (where we don't need to cut because we are ratio
225 * the end point). */
226 if (!seg->next && LRT_DOUBLE_CLOSE_ENOUGH(1, end)) {
227 cut_end_before = seg;
228 new_seg2 = cut_end_before;
229 untouched = 1;
230 break;
231 }
232 /* When an actual cut is needed in the line. */
233 if (seg->ratio > end) {
234 cut_end_before = seg;
235 new_seg2 = lineart_give_segment(ld);
236 break;
237 }
238 }
239
240 /* When we still can't find any existing cut in the line, we allocate new ones. */
241 if (new_seg1 == nullptr) {
242 new_seg1 = lineart_give_segment(ld);
243 }
244 if (new_seg2 == nullptr) {
245 if (untouched) {
246 new_seg2 = new_seg1;
247 cut_end_before = new_seg2;
248 }
249 else {
250 new_seg2 = lineart_give_segment(ld);
251 }
252 }
253
254 if (cut_start_before) {
255 if (cut_start_before != new_seg1) {
256 /* Insert cutting points for when a new cut is needed. */
257 i_seg = cut_start_before->prev ? cut_start_before->prev : nullptr;
258 if (i_seg) {
259 new_seg1->occlusion = i_seg->occlusion;
260 new_seg1->material_mask_bits = i_seg->material_mask_bits;
261 new_seg1->shadow_mask_bits = i_seg->shadow_mask_bits;
262 }
263 BLI_insertlinkbefore(&e->segments, cut_start_before, new_seg1);
264 }
265 /* Otherwise we already found a existing cutting point, no need to insert a new one. */
266 }
267 else {
268 /* We have yet to reach a existing cutting point even after we searched the whole line, so we
269 * append the new cut to the end. */
270 i_seg = static_cast<LineartEdgeSegment *>(e->segments.last);
271 new_seg1->occlusion = i_seg->occlusion;
272 new_seg1->material_mask_bits = i_seg->material_mask_bits;
273 new_seg1->shadow_mask_bits = i_seg->shadow_mask_bits;
274 BLI_addtail(&e->segments, new_seg1);
275 }
276 if (cut_end_before) {
277 /* The same manipulation as on "cut_start_before". */
278 if (cut_end_before != new_seg2) {
279 i_seg = cut_end_before->prev ? cut_end_before->prev : nullptr;
280 if (i_seg) {
281 new_seg2->occlusion = i_seg->occlusion;
282 new_seg2->material_mask_bits = i_seg->material_mask_bits;
283 new_seg2->shadow_mask_bits = i_seg->shadow_mask_bits;
284 }
285 BLI_insertlinkbefore(&e->segments, cut_end_before, new_seg2);
286 }
287 }
288 else {
289 i_seg = static_cast<LineartEdgeSegment *>(e->segments.last);
290 new_seg2->occlusion = i_seg->occlusion;
291 new_seg2->material_mask_bits = i_seg->material_mask_bits;
292 new_seg2->shadow_mask_bits = i_seg->shadow_mask_bits;
293 if (!untouched) {
294 BLI_addtail(&e->segments, new_seg2);
295 }
296 }
297
298 /* If we touched the cut list, we assign the new cut position based on new cut position,
299 * this way we accommodate precision lost due to multiple cut inserts. */
300 new_seg1->ratio = start;
301 if (!untouched) {
302 new_seg2->ratio = end;
303 }
304 else {
305 /* For the convenience of the loop below. */
306 new_seg2 = new_seg2->next;
307 }
308
309 /* Register 1 level of occlusion for all touched segments. */
310 for (LineartEdgeSegment *seg = new_seg1; seg && seg != new_seg2; seg = seg->next) {
311 seg->occlusion += mat_occlusion;
312 seg->material_mask_bits |= material_mask_bits;
313
314 /* The enclosed shape flag will override regular lit/shaded
315 * flags. See LineartEdgeSegment::shadow_mask_bits for details. */
316 if (shadow_bits == LRT_SHADOW_MASK_ENCLOSED_SHAPE) {
317 if (seg->shadow_mask_bits & LRT_SHADOW_MASK_ILLUMINATED ||
319 {
320 seg->shadow_mask_bits |= LRT_SHADOW_MASK_INHIBITED;
321 }
322 else if (seg->shadow_mask_bits & LRT_SHADOW_MASK_SHADED) {
323 seg->shadow_mask_bits |= LRT_SHADOW_MASK_ILLUMINATED_SHAPE;
324 }
325 }
326 else {
327 seg->shadow_mask_bits |= shadow_bits;
328 }
329 }
330
331 /* Reduce adjacent cutting points of the same level, which saves memory. */
332 int8_t min_occ = 127;
333 prev_seg = nullptr;
334 LISTBASE_FOREACH_MUTABLE (LineartEdgeSegment *, seg, &e->segments) {
335
336 if (prev_seg && prev_seg->occlusion == seg->occlusion &&
337 prev_seg->material_mask_bits == seg->material_mask_bits &&
338 prev_seg->shadow_mask_bits == seg->shadow_mask_bits)
339 {
340 BLI_remlink(&e->segments, seg);
341 /* This puts the node back to the render buffer, if more cut happens, these unused nodes get
342 * picked first. */
344 continue;
345 }
346
347 min_occ = std::min<int8_t>(min_occ, seg->occlusion);
348
349 prev_seg = seg;
350 }
351 e->min_occ = min_occ;
352}
353
358{
359 return (((e->target_reference & LRT_LIGHT_CONTOUR_TARGET) == tri->target_reference) ||
360 (((e->target_reference >> 32) & LRT_LIGHT_CONTOUR_TARGET) == tri->target_reference));
361}
362
369
371{
372 /* In case of too many lines concentrating in one point, do not add anymore, these lines will
373 * be either shorter than a single pixel, or will still be added into the list of other less
374 * dense areas. */
375 if (ba->line_count >= 65535) {
376 return;
377 }
378 if (ba->line_count >= ba->max_line_count) {
379 LineartEdge **new_array = MEM_malloc_arrayN<LineartEdge *>(ba->max_line_count * 2, __func__);
380 memcpy(new_array, ba->linked_lines, sizeof(LineartEdge *) * ba->max_line_count);
381 ba->max_line_count *= 2;
383 ba->linked_lines = new_array;
384 }
385 ba->linked_lines[ba->line_count] = e;
386 ba->line_count++;
387}
388
390{
392 double l, r;
393 LRT_EDGE_BA_MARCHING_BEGIN(e->v1->fbcoord, e->v2->fbcoord)
394 {
395 for (int i = 0; i < nba->triangle_count; i++) {
396 tri = (LineartTriangleThread *)nba->linked_triangles[i];
397 /* If we are already testing the line in this thread, then don't do it. */
398 if (tri->testing_e[thread_id] == e || (tri->base.flags & LRT_TRIANGLE_INTERSECTION_ONLY) ||
399 /* Ignore this triangle if an intersection line directly comes from it, */
401 /* Or if this triangle isn't effectively occluding anything nor it's providing a
402 * material flag. */
403 ((!tri->base.mat_occlusion) && (!tri->base.material_mask_bits)))
404 {
405 continue;
406 }
407 tri->testing_e[thread_id] = e;
409 e,
410 ld->conf.camera_pos,
411 ld->conf.cam_is_persp,
414 ld->conf.view_vector,
415 ld->conf.shift_x,
416 ld->conf.shift_y,
417 &l,
418 &r))
419 {
420 lineart_edge_cut(ld, e, l, r, tri->base.material_mask_bits, tri->base.mat_occlusion, 0);
421 if (e->min_occ > ld->conf.max_occlusion_level) {
422 /* No need to calculate any longer on this line because no level more than set value is
423 * going to show up in the rendered result. */
424 return;
425 }
426 }
427 }
428 LRT_EDGE_BA_MARCHING_NEXT(e->v1->fbcoord, e->v2->fbcoord)
429 }
431}
432
434{
435 int res = 0;
436 int starting_index;
437
439
440 starting_index = ld->scheduled_count;
442
444
445 if (starting_index >= ld->pending_edges.next) {
446 res = 0;
447 }
448 else {
449 rti->pending_edges.array = &ld->pending_edges.array[starting_index];
450 int remaining = ld->pending_edges.next - starting_index;
451 rti->pending_edges.max = std::min(remaining, LRT_THREAD_EDGE_COUNT);
452 res = 1;
453 }
454
455 return res;
456}
457
458static void lineart_occlusion_worker(TaskPool *__restrict /*pool*/, LineartRenderTaskInfo *rti)
459{
460 LineartData *ld = rti->ld;
461 LineartEdge *eip;
462
463 while (lineart_occlusion_make_task_info(ld, rti)) {
464 for (int i = 0; i < rti->pending_edges.max; i++) {
465 eip = rti->pending_edges.array[i];
467 }
468 }
469}
470
472{
473 int thread_count = ld->thread_count;
475 int i;
476
478
479 for (i = 0; i < thread_count; i++) {
480 rti[i].thread_id = i;
481 rti[i].ld = ld;
483 }
486
487 MEM_freeN(rti);
488}
489
497static bool lineart_point_inside_triangle(const double v[2],
498 const double v0[2],
499 const double v1[2],
500 const double v2[2])
501{
502 double cl, c, cl0;
503
504 cl = (v0[0] - v[0]) * (v1[1] - v[1]) - (v0[1] - v[1]) * (v1[0] - v[0]);
505 c = cl0 = cl;
506
507 cl = (v1[0] - v[0]) * (v2[1] - v[1]) - (v1[1] - v[1]) * (v2[0] - v[0]);
508 if (c * cl <= 0) {
509 return false;
510 }
511
512 c = cl;
513
514 cl = (v2[0] - v[0]) * (v0[1] - v[1]) - (v2[1] - v[1]) * (v0[0] - v[0]);
515 if (c * cl <= 0) {
516 return false;
517 }
518
519 c = cl;
520
521 if (c * cl0 <= 0) {
522 return false;
523 }
524
525 return true;
526}
527
528static int lineart_point_on_line_segment(double v[2], double v0[2], double v1[2])
529{
530 /* `c1 != c2` by default. */
531 double c1 = 1, c2 = 0;
532 double l0[2], l1[2];
533
534 sub_v2_v2v2_db(l0, v, v0);
535 sub_v2_v2v2_db(l1, v, v1);
536
537 if (v1[0] == v0[0] && v1[1] == v0[1]) {
538 return 0;
539 }
540
541 if (!LRT_DOUBLE_CLOSE_ENOUGH(v1[0], v0[0])) {
542 c1 = ratiod(v0[0], v1[0], v[0]);
543 }
544 else {
545 if (LRT_DOUBLE_CLOSE_ENOUGH(v[0], v1[0])) {
546 c2 = ratiod(v0[1], v1[1], v[1]);
547 return (c2 >= -DBL_TRIANGLE_LIM && c2 <= 1 + DBL_TRIANGLE_LIM);
548 }
549 return false;
550 }
551
552 if (!LRT_DOUBLE_CLOSE_ENOUGH(v1[1], v0[1])) {
553 c2 = ratiod(v0[1], v1[1], v[1]);
554 }
555 else {
556 if (LRT_DOUBLE_CLOSE_ENOUGH(v[1], v1[1])) {
557 c1 = ratiod(v0[0], v1[0], v[0]);
558 return (c1 >= -DBL_TRIANGLE_LIM && c1 <= 1 + DBL_TRIANGLE_LIM);
559 }
560 return false;
561 }
562
563 if (LRT_DOUBLE_CLOSE_ENOUGH(c1, c2) && c1 >= 0 && c1 <= 1) {
564 return 1;
565 }
566
567 return 0;
568}
569
575
581 double v0[2],
582 double v1[2],
583 double v2[2])
584{
585 double cl, c;
586 double r;
589 {
590 return LRT_ON_TRIANGLE;
591 }
592
593 cl = (v0[0] - v[0]) * (v1[1] - v[1]) - (v0[1] - v[1]) * (v1[0] - v[0]);
594 c = cl;
595
596 cl = (v1[0] - v[0]) * (v2[1] - v[1]) - (v1[1] - v[1]) * (v2[0] - v[0]);
597 if ((r = c * cl) < 0) {
599 }
600
601 c = cl;
602
603 cl = (v2[0] - v[0]) * (v0[1] - v[1]) - (v2[1] - v[1]) * (v0[0] - v[0]);
604 if ((r = c * cl) < 0) {
606 }
607
608 c = cl;
609
610 cl = (v0[0] - v[0]) * (v1[1] - v[1]) - (v0[1] - v[1]) * (v1[0] - v[0]);
611 if ((r = c * cl) < 0) {
613 }
614
615 if (r == 0) {
616 return LRT_ON_TRIANGLE;
617 }
618
619 return LRT_INSIDE_TRIANGLE;
620}
621
626static bool lineart_point_inside_triangle3d(double v[3], double v0[3], double v1[3], double v2[3])
627{
628 double l[3], r[3];
629 double N1[3], N2[3];
630 double d;
631
632 sub_v3_v3v3_db(l, v1, v0);
633 sub_v3_v3v3_db(r, v, v1);
634 cross_v3_v3v3_db(N1, l, r);
635
636 sub_v3_v3v3_db(l, v2, v1);
637 sub_v3_v3v3_db(r, v, v2);
638 cross_v3_v3v3_db(N2, l, r);
639
640 if ((d = dot_v3v3_db(N1, N2)) < 0) {
641 return false;
642 }
643
644 sub_v3_v3v3_db(l, v0, v2);
645 sub_v3_v3v3_db(r, v, v0);
646 cross_v3_v3v3_db(N1, l, r);
647
648 if ((d = dot_v3v3_db(N1, N2)) < 0) {
649 return false;
650 }
651
652 sub_v3_v3v3_db(l, v1, v0);
653 sub_v3_v3v3_db(r, v, v1);
654 cross_v3_v3v3_db(N2, l, r);
655
656 if ((d = dot_v3v3_db(N1, N2)) < 0) {
657 return false;
658 }
659
660 return true;
661}
662
668{
669 /* We don't need to allocate a whole bunch of triangles because the amount of clipped triangles
670 * are relatively small. */
671 LineartTriangle *render_triangles = static_cast<LineartTriangle *>(
673
676 &ld->render_data_pool,
677 render_triangles,
678 sizeof(LineartElementLinkNode)));
679 eln->element_count = 64;
681
682 return eln;
683}
684
686{
687 LineartVert *render_vertices = static_cast<LineartVert *>(
689
692 &ld->render_data_pool,
693 render_vertices,
694 sizeof(LineartElementLinkNode)));
695 eln->element_count = 64;
697
698 return eln;
699}
700
702{
703 LineartEdge *render_edges = static_cast<LineartEdge *>(
705
708 ld->edge_data_pool,
709 render_edges,
710 sizeof(LineartElementLinkNode)));
711 eln->element_count = 64;
714
715 return eln;
716}
717
719{
720 /* Just re-assign normal and set cull flag. */
721 copy_v3_v3_db(tri->gn, orig->gn);
725 tri->mat_occlusion = orig->mat_occlusion;
728}
729
731{
732 uchar intersection_only = (tri->flags & LRT_TRIANGLE_INTERSECTION_ONLY);
733 tri->flags = flag;
734 tri->flags |= intersection_only;
735}
736
737static bool lineart_edge_match(LineartTriangle *tri, LineartEdge *e, int v1, int v2)
738{
739 return ((tri->v[v1] == e->v1 && tri->v[v2] == e->v2) ||
740 (tri->v[v2] == e->v1 && tri->v[v1] == e->v2));
741}
742
744{
745 LineartEdge *e = old_e;
747 e++;
749 }
750}
751
757 LineartTriangle *tri,
758 int in0,
759 int in1,
760 int in2,
761 double cam_pos[3],
762 double view_dir[3],
763 bool allow_boundaries,
764 double m_view_projection[4][4],
765 Object *ob,
766 int *r_v_count,
767 int *r_e_count,
768 int *r_t_count,
772{
773 double span_v1[3], span_v2[3], dot_v1, dot_v2;
774 double a;
775 int v_count = *r_v_count;
776 int e_count = *r_e_count;
777 int t_count = *r_t_count;
778 uint16_t new_flag = 0;
779
780 LineartEdge *new_e, *e, *old_e;
782
784 return;
785 }
786
787 /* See definition of tri->intersecting_verts and the usage in
788 * lineart_geometry_object_load() for details. */
789 LineartTriangleAdjacent *tri_adj = reinterpret_cast<LineartTriangleAdjacent *>(
790 tri->intersecting_verts);
791
792 LineartVert *vt = &((LineartVert *)v_eln->pointer)[v_count];
793 LineartTriangle *tri1 = static_cast<LineartTriangle *>(
794 (void *)(((uchar *)t_eln->pointer) + ld->sizeof_triangle * t_count));
795 LineartTriangle *tri2 = static_cast<LineartTriangle *>(
796 (void *)(((uchar *)t_eln->pointer) + ld->sizeof_triangle * (t_count + 1)));
797
798 new_e = &((LineartEdge *)e_eln->pointer)[e_count];
799 /* Init `edge` to the last `edge` entry. */
800 e = new_e;
801
802#define INCREASE_EDGE \
803 new_e = &((LineartEdge *)e_eln->pointer)[e_count]; \
804 e_count++; \
805 e = new_e; \
806 es = static_cast<LineartEdgeSegment *>( \
807 lineart_mem_acquire(&ld->render_data_pool, sizeof(LineartEdgeSegment))); \
808 BLI_addtail(&e->segments, es);
809
810#define SELECT_EDGE(e_num, v1_link, v2_link, new_tri) \
811 if (tri_adj->e[e_num]) { \
812 old_e = tri_adj->e[e_num]; \
813 new_flag = old_e->flags; \
814 old_e->flags = MOD_LINEART_EDGE_FLAG_CHAIN_PICKED; \
815 lineart_discard_duplicated_edges(old_e); \
816 INCREASE_EDGE \
817 e->v1 = (v1_link); \
818 e->v2 = (v2_link); \
819 e->v1->index = (v1_link)->index; \
820 e->v2->index = (v1_link)->index; \
821 e->flags = new_flag; \
822 e->object_ref = ob; \
823 e->t1 = ((old_e->t1 == tri) ? (new_tri) : (old_e->t1)); \
824 e->t2 = ((old_e->t2 == tri) ? (new_tri) : (old_e->t2)); \
825 lineart_add_edge_to_array(&ld->pending_edges, e); \
826 }
827
828#define RELINK_EDGE(e_num, new_tri) \
829 if (tri_adj->e[e_num]) { \
830 old_e = tri_adj->e[e_num]; \
831 old_e->t1 = ((old_e->t1 == tri) ? (new_tri) : (old_e->t1)); \
832 old_e->t2 = ((old_e->t2 == tri) ? (new_tri) : (old_e->t2)); \
833 }
834
835#define REMOVE_TRIANGLE_EDGE \
836 if (tri_adj->e[0]) { \
837 tri_adj->e[0]->flags = MOD_LINEART_EDGE_FLAG_CHAIN_PICKED; \
838 lineart_discard_duplicated_edges(tri_adj->e[0]); \
839 } \
840 if (tri_adj->e[1]) { \
841 tri_adj->e[1]->flags = MOD_LINEART_EDGE_FLAG_CHAIN_PICKED; \
842 lineart_discard_duplicated_edges(tri_adj->e[1]); \
843 } \
844 if (tri_adj->e[2]) { \
845 tri_adj->e[2]->flags = MOD_LINEART_EDGE_FLAG_CHAIN_PICKED; \
846 lineart_discard_duplicated_edges(tri_adj->e[2]); \
847 }
848
849 switch (in0 + in1 + in2) {
850 case 0: /* Triangle is visible. Ignore this triangle. */
851 return;
852 case 3:
853 /* Triangle completely behind near plane, throw it away
854 * also remove render lines form being computed. */
857 return;
858 case 2:
859 /* Two points behind near plane, cut those and
860 * generate 2 new points, 3 lines and 1 triangle. */
862
883 if (!in0) {
884
885 /* Cut point for line 2---|-----0. */
886 sub_v3_v3v3_db(span_v1, tri->v[0]->gloc, cam_pos);
887 sub_v3_v3v3_db(span_v2, cam_pos, tri->v[2]->gloc);
888 dot_v1 = dot_v3v3_db(span_v1, view_dir);
889 dot_v2 = dot_v3v3_db(span_v2, view_dir);
890 a = dot_v1 / (dot_v1 + dot_v2);
891 /* Assign it to a new point. */
892 interp_v3_v3v3_db(vt[0].gloc, tri->v[0]->gloc, tri->v[2]->gloc, a);
893 mul_v4_m4v3_db(vt[0].fbcoord, m_view_projection, vt[0].gloc);
894 vt[0].index = tri->v[2]->index;
895
896 /* Cut point for line 1---|-----0. */
897 sub_v3_v3v3_db(span_v1, tri->v[0]->gloc, cam_pos);
898 sub_v3_v3v3_db(span_v2, cam_pos, tri->v[1]->gloc);
899 dot_v1 = dot_v3v3_db(span_v1, view_dir);
900 dot_v2 = dot_v3v3_db(span_v2, view_dir);
901 a = dot_v1 / (dot_v1 + dot_v2);
902 /* Assign it to another new point. */
903 interp_v3_v3v3_db(vt[1].gloc, tri->v[0]->gloc, tri->v[1]->gloc, a);
904 mul_v4_m4v3_db(vt[1].fbcoord, m_view_projection, vt[1].gloc);
905 vt[1].index = tri->v[1]->index;
906
907 /* New line connecting two new points. */
909 if (allow_boundaries) {
912 }
913 /* NOTE: inverting `e->v1/v2` (left/right point) doesn't matter as long as
914 * `tri->edge` and `tri->v` has the same sequence. and the winding direction
915 * can be either CW or CCW but needs to be consistent throughout the calculation. */
916 e->v1 = &vt[1];
917 e->v2 = &vt[0];
918 /* Only one adjacent triangle, because the other side is the near plane. */
919 /* Use `tl` or `tr` doesn't matter. */
920 e->t1 = tri1;
921 e->object_ref = ob;
922
923 /* New line connecting original point 0 and a new point, only when it's a selected line. */
924 SELECT_EDGE(2, tri->v[0], &vt[0], tri1)
925 /* New line connecting original point 0 and another new point. */
926 SELECT_EDGE(0, tri->v[0], &vt[1], tri1)
927
928 /* Re-assign triangle point array to two new points. */
929 tri1->v[0] = tri->v[0];
930 tri1->v[1] = &vt[1];
931 tri1->v[2] = &vt[0];
932
933 lineart_triangle_post(tri1, tri);
934
935 v_count += 2;
936 t_count += 1;
937 }
938 else if (!in2) {
939 sub_v3_v3v3_db(span_v1, tri->v[2]->gloc, cam_pos);
940 sub_v3_v3v3_db(span_v2, cam_pos, tri->v[0]->gloc);
941 dot_v1 = dot_v3v3_db(span_v1, view_dir);
942 dot_v2 = dot_v3v3_db(span_v2, view_dir);
943 a = dot_v1 / (dot_v1 + dot_v2);
944 interp_v3_v3v3_db(vt[0].gloc, tri->v[2]->gloc, tri->v[0]->gloc, a);
945 mul_v4_m4v3_db(vt[0].fbcoord, m_view_projection, vt[0].gloc);
946 vt[0].index = tri->v[0]->index;
947
948 sub_v3_v3v3_db(span_v1, tri->v[2]->gloc, cam_pos);
949 sub_v3_v3v3_db(span_v2, cam_pos, tri->v[1]->gloc);
950 dot_v1 = dot_v3v3_db(span_v1, view_dir);
951 dot_v2 = dot_v3v3_db(span_v2, view_dir);
952 a = dot_v1 / (dot_v1 + dot_v2);
953 interp_v3_v3v3_db(vt[1].gloc, tri->v[2]->gloc, tri->v[1]->gloc, a);
954 mul_v4_m4v3_db(vt[1].fbcoord, m_view_projection, vt[1].gloc);
955 vt[1].index = tri->v[1]->index;
956
958 if (allow_boundaries) {
961 }
962 e->v1 = &vt[0];
963 e->v2 = &vt[1];
964 e->t1 = tri1;
965 e->object_ref = ob;
966
967 SELECT_EDGE(2, tri->v[2], &vt[0], tri1)
968 SELECT_EDGE(1, tri->v[2], &vt[1], tri1)
969
970 tri1->v[0] = &vt[0];
971 tri1->v[1] = &vt[1];
972 tri1->v[2] = tri->v[2];
973
974 lineart_triangle_post(tri1, tri);
975
976 v_count += 2;
977 t_count += 1;
978 }
979 else if (!in1) {
980 sub_v3_v3v3_db(span_v1, tri->v[1]->gloc, cam_pos);
981 sub_v3_v3v3_db(span_v2, cam_pos, tri->v[2]->gloc);
982 dot_v1 = dot_v3v3_db(span_v1, view_dir);
983 dot_v2 = dot_v3v3_db(span_v2, view_dir);
984 a = dot_v1 / (dot_v1 + dot_v2);
985 interp_v3_v3v3_db(vt[0].gloc, tri->v[1]->gloc, tri->v[2]->gloc, a);
986 mul_v4_m4v3_db(vt[0].fbcoord, m_view_projection, vt[0].gloc);
987 vt[0].index = tri->v[2]->index;
988
989 sub_v3_v3v3_db(span_v1, tri->v[1]->gloc, cam_pos);
990 sub_v3_v3v3_db(span_v2, cam_pos, tri->v[0]->gloc);
991 dot_v1 = dot_v3v3_db(span_v1, view_dir);
992 dot_v2 = dot_v3v3_db(span_v2, view_dir);
993 a = dot_v1 / (dot_v1 + dot_v2);
994 interp_v3_v3v3_db(vt[1].gloc, tri->v[1]->gloc, tri->v[0]->gloc, a);
995 mul_v4_m4v3_db(vt[1].fbcoord, m_view_projection, vt[1].gloc);
996 vt[1].index = tri->v[0]->index;
997
999 if (allow_boundaries) {
1002 }
1003 e->v1 = &vt[1];
1004 e->v2 = &vt[0];
1005 e->t1 = tri1;
1006 e->object_ref = ob;
1007
1008 SELECT_EDGE(1, tri->v[1], &vt[0], tri1)
1009 SELECT_EDGE(0, tri->v[1], &vt[1], tri1)
1010
1011 tri1->v[0] = &vt[0];
1012 tri1->v[1] = tri->v[1];
1013 tri1->v[2] = &vt[1];
1014
1015 lineart_triangle_post(tri1, tri);
1016
1017 v_count += 2;
1018 t_count += 1;
1019 }
1020 break;
1021 case 1:
1022 /* One point behind near plane, cut those and
1023 * generate 2 new points, 4 lines and 2 triangles. */
1025
1049 if (in0) {
1050 /* Cut point for line 0---|------1. */
1051 sub_v3_v3v3_db(span_v1, tri->v[1]->gloc, cam_pos);
1052 sub_v3_v3v3_db(span_v2, cam_pos, tri->v[0]->gloc);
1053 dot_v1 = dot_v3v3_db(span_v1, view_dir);
1054 dot_v2 = dot_v3v3_db(span_v2, view_dir);
1055 a = dot_v2 / (dot_v1 + dot_v2);
1056 /* Assign to a new point. */
1057 interp_v3_v3v3_db(vt[0].gloc, tri->v[0]->gloc, tri->v[1]->gloc, a);
1058 mul_v4_m4v3_db(vt[0].fbcoord, m_view_projection, vt[0].gloc);
1059 vt[0].index = tri->v[0]->index;
1060
1061 /* Cut point for line 0---|------2. */
1062 sub_v3_v3v3_db(span_v1, tri->v[2]->gloc, cam_pos);
1063 sub_v3_v3v3_db(span_v2, cam_pos, tri->v[0]->gloc);
1064 dot_v1 = dot_v3v3_db(span_v1, view_dir);
1065 dot_v2 = dot_v3v3_db(span_v2, view_dir);
1066 a = dot_v2 / (dot_v1 + dot_v2);
1067 /* Assign to other new point. */
1068 interp_v3_v3v3_db(vt[1].gloc, tri->v[0]->gloc, tri->v[2]->gloc, a);
1069 mul_v4_m4v3_db(vt[1].fbcoord, m_view_projection, vt[1].gloc);
1070 vt[1].index = tri->v[0]->index;
1071
1072 /* New line connects two new points. */
1074 if (allow_boundaries) {
1077 }
1078 e->v1 = &vt[1];
1079 e->v2 = &vt[0];
1080 e->t1 = tri1;
1081 e->object_ref = ob;
1082
1083 /* New line connects new point 0 and old point 1,
1084 * this is a border line. */
1085
1086 SELECT_EDGE(0, tri->v[1], &vt[0], tri1)
1087 SELECT_EDGE(2, tri->v[2], &vt[1], tri2)
1088 RELINK_EDGE(1, tri2)
1089
1090 /* We now have one triangle closed. */
1091 tri1->v[0] = tri->v[1];
1092 tri1->v[1] = &vt[1];
1093 tri1->v[2] = &vt[0];
1094 /* Close the second triangle. */
1095 tri2->v[0] = &vt[1];
1096 tri2->v[1] = tri->v[1];
1097 tri2->v[2] = tri->v[2];
1098
1099 lineart_triangle_post(tri1, tri);
1100 lineart_triangle_post(tri2, tri);
1101
1102 v_count += 2;
1103 t_count += 2;
1104 }
1105 else if (in1) {
1106
1107 sub_v3_v3v3_db(span_v1, tri->v[1]->gloc, cam_pos);
1108 sub_v3_v3v3_db(span_v2, cam_pos, tri->v[2]->gloc);
1109 dot_v1 = dot_v3v3_db(span_v1, view_dir);
1110 dot_v2 = dot_v3v3_db(span_v2, view_dir);
1111 a = dot_v1 / (dot_v1 + dot_v2);
1112 interp_v3_v3v3_db(vt[0].gloc, tri->v[1]->gloc, tri->v[2]->gloc, a);
1113 mul_v4_m4v3_db(vt[0].fbcoord, m_view_projection, vt[0].gloc);
1114 vt[0].index = tri->v[1]->index;
1115
1116 sub_v3_v3v3_db(span_v1, tri->v[1]->gloc, cam_pos);
1117 sub_v3_v3v3_db(span_v2, cam_pos, tri->v[0]->gloc);
1118 dot_v1 = dot_v3v3_db(span_v1, view_dir);
1119 dot_v2 = dot_v3v3_db(span_v2, view_dir);
1120 a = dot_v1 / (dot_v1 + dot_v2);
1121 interp_v3_v3v3_db(vt[1].gloc, tri->v[1]->gloc, tri->v[0]->gloc, a);
1122 mul_v4_m4v3_db(vt[1].fbcoord, m_view_projection, vt[1].gloc);
1123 vt[1].index = tri->v[1]->index;
1124
1126 if (allow_boundaries) {
1129 }
1130 e->v1 = &vt[1];
1131 e->v2 = &vt[0];
1132
1133 e->t1 = tri1;
1134 e->object_ref = ob;
1135
1136 SELECT_EDGE(1, tri->v[2], &vt[0], tri1)
1137 SELECT_EDGE(0, tri->v[0], &vt[1], tri2)
1138 RELINK_EDGE(2, tri2)
1139
1140 tri1->v[0] = tri->v[2];
1141 tri1->v[1] = &vt[1];
1142 tri1->v[2] = &vt[0];
1143
1144 tri2->v[0] = &vt[1];
1145 tri2->v[1] = tri->v[2];
1146 tri2->v[2] = tri->v[0];
1147
1148 lineart_triangle_post(tri1, tri);
1149 lineart_triangle_post(tri2, tri);
1150
1151 v_count += 2;
1152 t_count += 2;
1153 }
1154 else if (in2) {
1155
1156 sub_v3_v3v3_db(span_v1, tri->v[2]->gloc, cam_pos);
1157 sub_v3_v3v3_db(span_v2, cam_pos, tri->v[0]->gloc);
1158 dot_v1 = dot_v3v3_db(span_v1, view_dir);
1159 dot_v2 = dot_v3v3_db(span_v2, view_dir);
1160 a = dot_v1 / (dot_v1 + dot_v2);
1161 interp_v3_v3v3_db(vt[0].gloc, tri->v[2]->gloc, tri->v[0]->gloc, a);
1162 mul_v4_m4v3_db(vt[0].fbcoord, m_view_projection, vt[0].gloc);
1163 vt[0].index = tri->v[2]->index;
1164
1165 sub_v3_v3v3_db(span_v1, tri->v[2]->gloc, cam_pos);
1166 sub_v3_v3v3_db(span_v2, cam_pos, tri->v[1]->gloc);
1167 dot_v1 = dot_v3v3_db(span_v1, view_dir);
1168 dot_v2 = dot_v3v3_db(span_v2, view_dir);
1169 a = dot_v1 / (dot_v1 + dot_v2);
1170 interp_v3_v3v3_db(vt[1].gloc, tri->v[2]->gloc, tri->v[1]->gloc, a);
1171 mul_v4_m4v3_db(vt[1].fbcoord, m_view_projection, vt[1].gloc);
1172 vt[1].index = tri->v[2]->index;
1173
1175 if (allow_boundaries) {
1178 }
1179 e->v1 = &vt[1];
1180 e->v2 = &vt[0];
1181
1182 e->t1 = tri1;
1183 e->object_ref = ob;
1184
1185 SELECT_EDGE(2, tri->v[0], &vt[0], tri1)
1186 SELECT_EDGE(1, tri->v[1], &vt[1], tri2)
1187 RELINK_EDGE(0, tri2)
1188
1189 tri1->v[0] = tri->v[0];
1190 tri1->v[1] = &vt[1];
1191 tri1->v[2] = &vt[0];
1192
1193 tri2->v[0] = &vt[1];
1194 tri2->v[1] = tri->v[0];
1195 tri2->v[2] = tri->v[1];
1196
1197 lineart_triangle_post(tri1, tri);
1198 lineart_triangle_post(tri2, tri);
1199
1200 v_count += 2;
1201 t_count += 2;
1202 }
1203 break;
1204 }
1205 *r_v_count = v_count;
1206 *r_e_count = e_count;
1207 *r_t_count = t_count;
1208
1209#undef INCREASE_EDGE
1210#undef SELECT_EDGE
1211#undef RELINK_EDGE
1212#undef REMOVE_TRIANGLE_EDGE
1213}
1214
1216{
1217 LineartTriangle *tri;
1218 LineartElementLinkNode *v_eln, *t_eln, *e_eln;
1219 double(*m_view_projection)[4] = ld->conf.view_projection;
1220 int i;
1221 int v_count = 0, t_count = 0, e_count = 0;
1222 Object *ob;
1223 bool allow_boundaries = ld->conf.allow_boundaries;
1224 double cam_pos[3];
1225 double clip_start = ld->conf.near_clip, clip_end = ld->conf.far_clip;
1226 double view_dir[3], clip_advance[3];
1227
1228 copy_v3_v3_db(view_dir, ld->conf.view_vector);
1229 copy_v3_v3_db(clip_advance, ld->conf.view_vector);
1230 copy_v3_v3_db(cam_pos, ld->conf.camera_pos);
1231
1232 if (clip_far) {
1233 /* Move starting point to end plane. */
1234 mul_v3db_db(clip_advance, -clip_end);
1235 add_v3_v3_db(cam_pos, clip_advance);
1236
1237 /* "reverse looking". */
1238 mul_v3db_db(view_dir, -1.0f);
1239 }
1240 else {
1241 /* Clip Near. */
1242 mul_v3db_db(clip_advance, -clip_start);
1243 add_v3_v3_db(cam_pos, clip_advance);
1244 }
1245
1249
1250 /* Additional memory space for storing generated points and triangles. */
1251#define LRT_CULL_ENSURE_MEMORY \
1252 if (v_count > 60) { \
1253 v_eln->element_count = v_count; \
1254 v_eln = lineart_memory_get_vert_space(ld); \
1255 v_count = 0; \
1256 } \
1257 if (t_count > 60) { \
1258 t_eln->element_count = t_count; \
1259 t_eln = lineart_memory_get_triangle_space(ld); \
1260 t_count = 0; \
1261 } \
1262 if (e_count > 60) { \
1263 e_eln->element_count = e_count; \
1264 e_eln = lineart_memory_get_edge_space(ld); \
1265 e_count = 0; \
1266 }
1267
1268#define LRT_CULL_DECIDE_INSIDE \
1269 /* These three represents points that are in the clipping range or not. */ \
1270 in0 = 0, in1 = 0, in2 = 0; \
1271 if (clip_far) { \
1272 /* Point outside far plane. */ \
1273 if (tri->v[0]->fbcoord[use_w] > clip_end) { \
1274 in0 = 1; \
1275 } \
1276 if (tri->v[1]->fbcoord[use_w] > clip_end) { \
1277 in1 = 1; \
1278 } \
1279 if (tri->v[2]->fbcoord[use_w] > clip_end) { \
1280 in2 = 1; \
1281 } \
1282 } \
1283 else { \
1284 /* Point inside near plane. */ \
1285 if (tri->v[0]->fbcoord[use_w] < clip_start) { \
1286 in0 = 1; \
1287 } \
1288 if (tri->v[1]->fbcoord[use_w] < clip_start) { \
1289 in1 = 1; \
1290 } \
1291 if (tri->v[2]->fbcoord[use_w] < clip_start) { \
1292 in2 = 1; \
1293 } \
1294 }
1295
1296 int use_w = 3;
1297 int in0 = 0, in1 = 0, in2 = 0;
1298
1299 if (!ld->conf.cam_is_persp) {
1300 clip_start = -1;
1301 clip_end = 1;
1302 use_w = 2;
1303 }
1304
1305 /* Then go through all the other triangles. */
1307 if (eln->flags & LRT_ELEMENT_IS_ADDITIONAL) {
1308 continue;
1309 }
1310 ob = static_cast<Object *>(eln->object_ref);
1311 for (i = 0; i < eln->element_count; i++) {
1312 /* Select the triangle in the array. */
1313 tri = static_cast<LineartTriangle *>(
1314 (void *)(((uchar *)eln->pointer) + ld->sizeof_triangle * i));
1315
1316 if (tri->flags & LRT_CULL_DISCARD) {
1317 continue;
1318 }
1319
1323 tri,
1324 in0,
1325 in1,
1326 in2,
1327 cam_pos,
1328 view_dir,
1329 allow_boundaries,
1330 m_view_projection,
1331 ob,
1332 &v_count,
1333 &e_count,
1334 &t_count,
1335 v_eln,
1336 e_eln,
1337 t_eln);
1338 }
1339 t_eln->element_count = t_count;
1340 v_eln->element_count = v_count;
1341 }
1342
1343#undef LRT_CULL_ENSURE_MEMORY
1344#undef LRT_CULL_DECIDE_INSIDE
1345}
1346
1348{
1349 while (
1350 LinkData *link = static_cast<LinkData *>(BLI_pophead(&ld->geom.triangle_adjacent_pointers)))
1351 {
1352 MEM_freeN(link->data);
1353 }
1355 LineartTriangle *tri = static_cast<LineartTriangle *>(eln->pointer);
1356 int i;
1357 for (i = 0; i < eln->element_count; i++) {
1358 /* See definition of tri->intersecting_verts and the usage in
1359 * lineart_geometry_object_load() for detailed. */
1360 tri->intersecting_verts = nullptr;
1361 tri = (LineartTriangle *)(((uchar *)tri) + ld->sizeof_triangle);
1362 }
1363 }
1364}
1365
1367{
1369 LineartVert *vt = static_cast<LineartVert *>(eln->pointer);
1370 for (int i = 0; i < eln->element_count; i++) {
1371 if (ld->conf.cam_is_persp) {
1372 /* Do not divide Z, we use Z to back transform cut points in later chaining process. */
1373 vt[i].fbcoord[0] /= vt[i].fbcoord[3];
1374 vt[i].fbcoord[1] /= vt[i].fbcoord[3];
1375 /* Re-map z into (0-1) range, because we no longer need NDC (Normalized Device Coordinates)
1376 * at the moment.
1377 * The algorithm currently doesn't need Z for operation, we use W instead. If Z is needed
1378 * in the future, the line below correctly transforms it to view space coordinates. */
1379 // `vt[i].fbcoord[2] = -2 * vt[i].fbcoord[2] / (far - near) - (far + near) / (far - near);
1380 }
1381 /* Shifting is always needed. */
1382 vt[i].fbcoord[0] -= ld->conf.shift_x * 2;
1383 vt[i].fbcoord[1] -= ld->conf.shift_y * 2;
1384 }
1385 }
1386}
1387
1389{
1390 LineartEdge *e;
1391 const float bounds[4][2] = {{-1.0f, -1.0f}, {-1.0f, 1.0f}, {1.0f, -1.0f}, {1.0f, 1.0f}};
1392
1393#define LRT_VERT_OUT_OF_BOUND(v) \
1394 (v->fbcoord[0] < -1 || v->fbcoord[0] > 1 || v->fbcoord[1] < -1 || v->fbcoord[1] > 1)
1395
1397 e = (LineartEdge *)eln->pointer;
1398 for (int i = 0; i < eln->element_count; i++) {
1399 if (!e[i].v1 || !e[i].v2) {
1401 continue;
1402 }
1403 const blender::float2 vec1(e[i].v1->fbcoord), vec2(e[i].v2->fbcoord);
1405 /* A line could still cross the image border even when both of the vertices are out of
1406 * bound. */
1407 if (isect_seg_seg_v2(bounds[0], bounds[1], vec1, vec2) == ISECT_LINE_LINE_NONE &&
1408 isect_seg_seg_v2(bounds[0], bounds[2], vec1, vec2) == ISECT_LINE_LINE_NONE &&
1409 isect_seg_seg_v2(bounds[1], bounds[3], vec1, vec2) == ISECT_LINE_LINE_NONE &&
1410 isect_seg_seg_v2(bounds[2], bounds[3], vec1, vec2) == ISECT_LINE_LINE_NONE)
1411 {
1413 }
1414 }
1415 }
1416 }
1417}
1418
1420 int e;
1421 uint16_t flags;
1422 int v1, v2;
1423};
1424
1431
1432static void lineart_mvert_transform_task(void *__restrict userdata,
1433 const int i,
1434 const TaskParallelTLS *__restrict /*tls*/)
1435{
1436 VertData *vert_task_data = (VertData *)userdata;
1437 double co[4];
1438 LineartVert *v = &vert_task_data->v_arr[i];
1439 copy_v3db_v3fl(co, vert_task_data->positions[i]);
1440 mul_v3_m4v3_db(v->gloc, vert_task_data->model_view, co);
1441 mul_v4_m4v3_db(v->fbcoord, vert_task_data->model_view_proj, co);
1442 v->index = i;
1443}
1444
1453
1454#define LRT_MESH_EDGE_TYPES_COUNT 6
1455
1457{
1458 int count = 0;
1459 /* See eLineartEdgeFlag for details. */
1460 for (int i = 0; i < LRT_MESH_EDGE_TYPES_COUNT; i++) {
1461 if (eflag & LRT_MESH_EDGE_TYPES[i]) {
1462 count++;
1463 }
1464 }
1465 return count;
1466}
1467
1473 LineartTriangle *rt_array,
1474 int index)
1475{
1476 int8_t *b = (int8_t *)rt_array;
1477 b += (index * ld->sizeof_triangle);
1478 return (LineartTriangle *)b;
1479}
1480
1503
1507
1508static void feat_data_sum_reduce(const void *__restrict /*userdata*/,
1509 void *__restrict chunk_join,
1510 void *__restrict chunk)
1511{
1512 EdgeFeatReduceData *feat_chunk_join = (EdgeFeatReduceData *)chunk_join;
1513 EdgeFeatReduceData *feat_chunk = (EdgeFeatReduceData *)chunk;
1514 feat_chunk_join->feat_edges += feat_chunk->feat_edges;
1515}
1516
1517static void lineart_identify_corner_tri_feature_edges(void *__restrict userdata,
1518 const int i,
1519 const TaskParallelTLS *__restrict tls)
1520{
1521 EdgeFeatData *e_feat_data = (EdgeFeatData *)userdata;
1522 EdgeFeatReduceData *reduce_data = (EdgeFeatReduceData *)tls->userdata_chunk;
1523 Mesh *mesh = e_feat_data->mesh;
1524 Object *ob_eval = e_feat_data->ob_eval;
1525 LineartEdgeNeighbor *edge_nabr = e_feat_data->edge_nabr;
1526 const blender::Span<int3> corner_tris = e_feat_data->corner_tris;
1527 const blender::Span<int> tri_faces = e_feat_data->tri_faces;
1528 const blender::Span<int> material_indices = e_feat_data->material_indices;
1529
1530 uint16_t edge_flag_result = 0;
1531
1532 /* Because the edge neighbor array contains loop edge pairs, we only need to process the first
1533 * edge in the pair. Otherwise we would add the same edge that the loops represent twice. */
1534 if (i < edge_nabr[i].e) {
1535 return;
1536 }
1537
1538 bool face_mark_filtered = false;
1539 bool enable_face_mark = (e_feat_data->use_freestyle_face &&
1540 e_feat_data->ld->conf.filter_face_mark);
1541 bool only_contour = false;
1542 if (enable_face_mark) {
1543 FreestyleFace *ff1, *ff2;
1544 int index = e_feat_data->freestyle_face_index;
1545 if (index > -1) {
1546 ff1 = &((FreestyleFace *)mesh->face_data.layers[index].data)[tri_faces[i / 3]];
1547 }
1548 if (edge_nabr[i].e > -1) {
1549 ff2 = &((FreestyleFace *)mesh->face_data.layers[index].data)[tri_faces[edge_nabr[i].e / 3]];
1550 }
1551 else {
1552 /* Handle mesh boundary cases: We want mesh boundaries to respect
1553 * `filter_face_mark_boundaries` option the same way as face mark boundaries, and the code
1554 * path is simper when it's assuming both ff1 and ff2 not nullptr. */
1555 ff2 = ff1;
1556 }
1557 if (e_feat_data->ld->conf.filter_face_mark_boundaries ^
1558 e_feat_data->ld->conf.filter_face_mark_invert)
1559 {
1560 if ((ff1->flag & FREESTYLE_FACE_MARK) || (ff2->flag & FREESTYLE_FACE_MARK)) {
1561 face_mark_filtered = true;
1562 }
1563 }
1564 else {
1565 if ((ff1->flag & FREESTYLE_FACE_MARK) && (ff2->flag & FREESTYLE_FACE_MARK) && (ff2 != ff1)) {
1566 face_mark_filtered = true;
1567 }
1568 }
1569 if (e_feat_data->ld->conf.filter_face_mark_invert) {
1570 face_mark_filtered = !face_mark_filtered;
1571 }
1572 if (!face_mark_filtered) {
1574 if (e_feat_data->ld->conf.filter_face_mark_keep_contour) {
1575 only_contour = true;
1576 }
1577 }
1578 }
1579
1580 if (enable_face_mark && !face_mark_filtered && !only_contour) {
1581 return;
1582 }
1583
1584 /* Mesh boundary */
1585 if (edge_nabr[i].e == -1) {
1587 reduce_data->feat_edges += 1;
1588 return;
1589 }
1590
1591 LineartTriangle *tri1, *tri2;
1592 LineartVert *vert;
1593 LineartData *ld = e_feat_data->ld;
1594
1595 int f1 = i / 3, f2 = edge_nabr[i].e / 3;
1596
1597 /* The mesh should already be triangulated now, so we can assume each face is a triangle. */
1598 tri1 = lineart_triangle_from_index(ld, e_feat_data->tri_array, f1);
1599 tri2 = lineart_triangle_from_index(ld, e_feat_data->tri_array, f2);
1600
1601 vert = &e_feat_data->v_array[edge_nabr[i].v1];
1602
1603 double view_vector_persp[3];
1604 double *view_vector = view_vector_persp;
1605 double dot_v1 = 0, dot_v2 = 0;
1606 double result;
1607 bool material_back_face = ((tri1->flags | tri2->flags) & LRT_TRIANGLE_MAT_BACK_FACE_CULLING);
1608
1609 if (ld->conf.use_contour || ld->conf.use_back_face_culling || material_back_face) {
1610 if (ld->conf.cam_is_persp) {
1611 sub_v3_v3v3_db(view_vector, ld->conf.camera_pos, vert->gloc);
1612 }
1613 else {
1614 view_vector = ld->conf.view_vector;
1615 }
1616
1617 dot_v1 = dot_v3v3_db(view_vector, tri1->gn);
1618 dot_v2 = dot_v3v3_db(view_vector, tri2->gn);
1619
1620 if ((result = dot_v1 * dot_v2) <= 0 && (dot_v1 + dot_v2)) {
1621 edge_flag_result |= MOD_LINEART_EDGE_FLAG_CONTOUR;
1622 }
1623
1624 if (ld->conf.use_back_face_culling) {
1625 if (dot_v1 < 0) {
1626 tri1->flags |= LRT_CULL_DISCARD;
1627 }
1628 if (dot_v2 < 0) {
1629 tri2->flags |= LRT_CULL_DISCARD;
1630 }
1631 }
1632 if (material_back_face) {
1633 if (tri1->flags & LRT_TRIANGLE_MAT_BACK_FACE_CULLING && dot_v1 < 0) {
1634 tri1->flags |= LRT_CULL_DISCARD;
1635 }
1636 if (tri2->flags & LRT_TRIANGLE_MAT_BACK_FACE_CULLING && dot_v2 < 0) {
1637 tri2->flags |= LRT_CULL_DISCARD;
1638 }
1639 }
1640 }
1641
1642 if (ld->conf.use_contour_secondary) {
1643 view_vector = view_vector_persp;
1644 if (ld->conf.cam_is_persp_secondary) {
1645 sub_v3_v3v3_db(view_vector, vert->gloc, ld->conf.camera_pos_secondary);
1646 }
1647 else {
1648 view_vector = ld->conf.view_vector_secondary;
1649 }
1650
1651 dot_v1 = dot_v3v3_db(view_vector, tri1->gn);
1652 dot_v2 = dot_v3v3_db(view_vector, tri2->gn);
1653
1654 if ((result = dot_v1 * dot_v2) <= 0 && (dot_v1 + dot_v2)) {
1655 edge_flag_result |= MOD_LINEART_EDGE_FLAG_CONTOUR_SECONDARY;
1656 }
1657 }
1658
1659 if (!only_contour) {
1660 if (ld->conf.use_crease) {
1661 bool do_crease = true;
1662 if (!ld->conf.force_crease && !e_feat_data->use_auto_smooth &&
1663 (!e_feat_data->sharp_faces[tri_faces[f1]]) && (!e_feat_data->sharp_faces[tri_faces[f2]]))
1664 {
1665 do_crease = false;
1666 }
1667 if (do_crease && (dot_v3v3_db(tri1->gn, tri2->gn) < e_feat_data->crease_threshold)) {
1668 edge_flag_result |= MOD_LINEART_EDGE_FLAG_CREASE;
1669 }
1670 }
1671
1672 int mat1 = material_indices.is_empty() ? 0 : material_indices[tri_faces[f1]];
1673 int mat2 = material_indices.is_empty() ? 0 : material_indices[tri_faces[f2]];
1674
1675 if (mat1 != mat2) {
1676 Material *m1 = BKE_object_material_get_eval(ob_eval, mat1 + 1);
1677 Material *m2 = BKE_object_material_get_eval(ob_eval, mat2 + 1);
1678 if (m1 && m2 &&
1679 ((m1->lineart.mat_occlusion == 0 && m2->lineart.mat_occlusion != 0) ||
1680 (m2->lineart.mat_occlusion == 0 && m1->lineart.mat_occlusion != 0)))
1681 {
1682 if (ld->conf.use_contour) {
1683 edge_flag_result |= MOD_LINEART_EDGE_FLAG_CONTOUR;
1684 }
1685 }
1686 if (ld->conf.use_material) {
1687 edge_flag_result |= MOD_LINEART_EDGE_FLAG_MATERIAL;
1688 }
1689 }
1690 }
1691 else { /* only_contour */
1692 if (!edge_flag_result) { /* Other edge types inhibited */
1693 return;
1694 }
1695 }
1696
1697 const int3 real_edges = corner_tri_get_real_edges(e_feat_data->edges,
1698 e_feat_data->corner_verts,
1699 e_feat_data->corner_edges,
1700 corner_tris[i / 3]);
1701
1702 if (real_edges[i % 3] >= 0) {
1703 if (ld->conf.use_crease && ld->conf.sharp_as_crease &&
1704 e_feat_data->sharp_edges[real_edges[i % 3]])
1705 {
1706 edge_flag_result |= MOD_LINEART_EDGE_FLAG_CREASE;
1707 }
1708
1709 if (ld->conf.use_edge_marks && e_feat_data->use_freestyle_edge) {
1710 FreestyleEdge *fe;
1711 int index = e_feat_data->freestyle_edge_index;
1712 fe = &((FreestyleEdge *)mesh->edge_data.layers[index].data)[real_edges[i % 3]];
1713 if (fe->flag & FREESTYLE_EDGE_MARK) {
1714 edge_flag_result |= MOD_LINEART_EDGE_FLAG_EDGE_MARK;
1715 }
1716 }
1717 }
1718
1719 edge_nabr[i].flags = edge_flag_result;
1720
1721 if (edge_flag_result) {
1722 /* Only allocate for feature edge (instead of all edges) to save memory.
1723 * If allow duplicated edges, one edge gets added multiple times if it has multiple types.
1724 */
1725 reduce_data->feat_edges += e_feat_data->ld->conf.allow_duplicated_types ?
1726 lineart_edge_type_duplication_count(edge_flag_result) :
1727 1;
1728 }
1729}
1730
1735
1737{
1738 if (pe->next >= pe->max || !pe->max) {
1739 if (!pe->max) {
1740 pe->max = 1000;
1741 }
1742
1743 LineartEdge **new_array = MEM_malloc_arrayN<LineartEdge *>(size_t(pe->max) * 2,
1744 "LineartPendingEdges array");
1745 if (LIKELY(pe->array)) {
1746 memcpy(new_array, pe->array, sizeof(LineartEdge *) * pe->max);
1747 MEM_freeN(pe->array);
1748 }
1749 pe->max *= 2;
1750 pe->array = new_array;
1751 }
1752 pe->array[pe->next] = e;
1753 pe->next++;
1754}
1759
1761{
1762 /* NOTE: For simplicity, this function doesn't actually do anything
1763 * if you already have data in #pe. */
1764
1765 if (pe->max || pe->array || count == 0) {
1766 return;
1767 }
1768
1769 pe->max = count;
1770 LineartEdge **new_array = MEM_malloc_arrayN<LineartEdge *>(size_t(pe->max),
1771 "LineartPendingEdges array final");
1772 pe->array = new_array;
1773}
1774
1776{
1777 /* In case of line art "occlusion only" or contour not enabled, it's possible for an object to
1778 * not produce any feature lines. */
1779 if (!obi->pending_edges.array) {
1780 return;
1781 }
1782 memcpy(&pe->array[pe->next],
1783 obi->pending_edges.array,
1784 sizeof(LineartEdge *) * obi->pending_edges.next);
1786 pe->next += obi->pending_edges.next;
1787}
1788
1790 LineartTriangleAdjacent *tri_adj,
1791 LineartEdge *e)
1792{
1793 if (lineart_edge_match(tri, e, 0, 1)) {
1794 tri_adj->e[0] = e;
1795 }
1796 else if (lineart_edge_match(tri, e, 1, 2)) {
1797 tri_adj->e[1] = e;
1798 }
1799 else if (lineart_edge_match(tri, e, 2, 0)) {
1800 tri_adj->e[2] = e;
1801 }
1802}
1803
1818
1819static void lineart_load_tri_task(void *__restrict userdata,
1820 const int i,
1821 const TaskParallelTLS *__restrict /*tls*/)
1822{
1823 TriData *tri_task_data = (TriData *)userdata;
1824 LineartObjectInfo *ob_info = tri_task_data->ob_info;
1825 const blender::Span<blender::float3> positions = tri_task_data->positions;
1826 const blender::Span<int> corner_verts = tri_task_data->corner_verts;
1827 const int3 &corner_tri = tri_task_data->corner_tris[i];
1828 const int face_i = tri_task_data->tri_faces[i];
1829 const blender::Span<int> material_indices = tri_task_data->material_indices;
1830
1831 LineartVert *vert_arr = tri_task_data->vert_arr;
1832 LineartTriangle *tri = tri_task_data->tri_arr;
1833
1834 tri = (LineartTriangle *)(((uchar *)tri) + tri_task_data->lineart_triangle_size * i);
1835
1836 int v1 = corner_verts[corner_tri[0]];
1837 int v2 = corner_verts[corner_tri[1]];
1838 int v3 = corner_verts[corner_tri[2]];
1839
1840 tri->v[0] = &vert_arr[v1];
1841 tri->v[1] = &vert_arr[v2];
1842 tri->v[2] = &vert_arr[v3];
1843
1844 /* Material mask bits and occlusion effectiveness assignment. */
1846 ob_info->original_ob_eval, material_indices.is_empty() ? 1 : material_indices[face_i] + 1);
1847 tri->material_mask_bits |= ((mat && (mat->lineart.flags & LRT_MATERIAL_MASK_ENABLED)) ?
1849 0);
1850 tri->mat_occlusion |= (mat ? mat->lineart.mat_occlusion : 1);
1851 tri->intersection_priority = ((mat && (mat->lineart.flags &
1854 ob_info->intersection_priority);
1855 tri->flags |= (mat && (mat->blend_flag & MA_BL_CULL_BACKFACE)) ?
1857 0;
1858
1860
1861 tri->target_reference = (ob_info->obindex | (i & LRT_OBINDEX_LOWER));
1862
1863 double gn[3];
1864 float no[3];
1865 normal_tri_v3(no, positions[v1], positions[v2], positions[v3]);
1866 copy_v3db_v3fl(gn, no);
1867 mul_v3_mat3_m4v3_db(tri->gn, ob_info->normal, gn);
1868 normalize_v3_db(tri->gn);
1869
1870 if (ob_info->usage == OBJECT_LRT_INTERSECTION_ONLY) {
1872 }
1873 else if (ob_info->usage == OBJECT_LRT_FORCE_INTERSECTION) {
1875 }
1878 }
1879
1880 if (!tri_task_data->face_marks.is_empty()) {
1881 const bool has_mark = tri_task_data->face_marks[face_i];
1882 const bool filtered = tri_task_data->invert_face_marks ? has_mark : (!has_mark);
1883 if (filtered) {
1885 }
1886 }
1887
1888 /* Re-use this field to refer to adjacent info, will be cleared after culling stage. */
1889 tri->intersecting_verts = static_cast<LinkNode *>((void *)&tri_task_data->tri_adj[i]);
1890}
1898
1899static void lineart_edge_neighbor_init_task(void *__restrict userdata,
1900 const int i,
1901 const TaskParallelTLS *__restrict /*tls*/)
1902{
1903 EdgeNeighborData *en_data = (EdgeNeighborData *)userdata;
1904 LineartAdjacentEdge *adj_e = &en_data->adj_e[i];
1905 const int3 &tri = en_data->corner_tris[i / 3];
1906 LineartEdgeNeighbor *edge_nabr = &en_data->edge_nabr[i];
1907 const blender::Span<int> corner_verts = en_data->corner_verts;
1908
1909 adj_e->e = i;
1910 adj_e->v1 = corner_verts[tri[i % 3]];
1911 adj_e->v2 = corner_verts[tri[(i + 1) % 3]];
1912 if (adj_e->v1 > adj_e->v2) {
1913 std::swap(adj_e->v1, adj_e->v2);
1914 }
1915 edge_nabr->e = -1;
1916
1917 edge_nabr->v1 = adj_e->v1;
1918 edge_nabr->v2 = adj_e->v2;
1919 edge_nabr->flags = 0;
1920}
1921
1923{
1925 ai, ai + length, [](const LineartAdjacentEdge &p1, const LineartAdjacentEdge &p2) {
1926 int a = p1.v1 - p2.v1;
1927 int b = p1.v2 - p2.v2;
1928 /* `parallel_sort()` requires `cmp()` to return true when the first element needs to appear
1929 * before the second element in the sorted array, false otherwise (strict weak ordering),
1930 * see https://en.cppreference.com/w/cpp/named_req/Compare. */
1931 if (a < 0) {
1932 return true;
1933 }
1934 if (a > 0) {
1935 return false;
1936 }
1937 return b < 0;
1938 });
1939}
1940
1942{
1943 /* Because the mesh is triangulated, so `mesh->edges_num` should be reliable? */
1945 "LineartAdjacentEdge arr");
1947 size_t(total_edges), "LineartEdgeNeighbor arr");
1948
1949 TaskParallelSettings en_settings;
1951 /* Set the minimum amount of edges a thread has to process. */
1952 en_settings.min_iter_per_thread = 50000;
1953
1954 EdgeNeighborData en_data;
1955 en_data.adj_e = adj_e;
1956 en_data.edge_nabr = edge_nabr;
1957 en_data.corner_verts = mesh->corner_verts();
1958 en_data.corner_tris = mesh->corner_tris();
1959 en_data.tri_faces = mesh->corner_tri_faces();
1960
1961 BLI_task_parallel_range(0, total_edges, &en_data, lineart_edge_neighbor_init_task, &en_settings);
1962
1963 lineart_sort_adjacent_items(adj_e, total_edges);
1964
1965 for (int i = 0; i < total_edges - 1; i++) {
1966 if (adj_e[i].v1 == adj_e[i + 1].v1 && adj_e[i].v2 == adj_e[i + 1].v2) {
1967 edge_nabr[adj_e[i].e].e = adj_e[i + 1].e;
1968 edge_nabr[adj_e[i + 1].e].e = adj_e[i].e;
1969 }
1970 }
1971
1972 MEM_freeN(adj_e);
1973
1974 return edge_nabr;
1975}
1976
1978 LineartData *la_data,
1979 ListBase *shadow_elns)
1980{
1981 using namespace blender;
1982 Mesh *mesh = ob_info->original_me;
1983 if (!mesh->edges_num) {
1984 return;
1985 }
1986
1987 /* Triangulate. */
1988 const Span<int3> corner_tris = mesh->corner_tris();
1989 const AttributeAccessor attributes = mesh->attributes();
1990 const VArraySpan material_indices = *attributes.lookup<int>("material_index", AttrDomain::Face);
1991 const VArraySpan face_marks = *attributes.lookup<bool>("freestyle_face", AttrDomain::Face);
1992
1993 /* Check if we should look for custom data tags like Freestyle edges or faces. */
1994 bool can_find_freestyle_edge = false;
1995 int layer_index = CustomData_get_active_layer_index(&mesh->edge_data, CD_FREESTYLE_EDGE);
1996 if (layer_index != -1) {
1997 can_find_freestyle_edge = true;
1998 }
1999
2000 bool can_find_freestyle_face = false;
2001 layer_index = CustomData_get_active_layer_index(&mesh->face_data, CD_FREESTYLE_FACE);
2002 if (layer_index != -1) {
2003 can_find_freestyle_face = true;
2004 }
2005
2006 /* If we allow duplicated edges, one edge should get added multiple times if is has been
2007 * classified as more than one edge type. This is so we can create multiple different line type
2008 * chains containing the same edge. */
2009 LineartVert *la_v_arr = static_cast<LineartVert *>(lineart_mem_acquire_thread(
2010 &la_data->render_data_pool, sizeof(LineartVert) * mesh->verts_num));
2011 LineartTriangle *la_tri_arr = static_cast<LineartTriangle *>(lineart_mem_acquire_thread(
2012 &la_data->render_data_pool, corner_tris.size() * la_data->sizeof_triangle));
2013
2014 Object *orig_ob = ob_info->original_ob;
2015
2016 BLI_spin_lock(&la_data->lock_task);
2017 LineartElementLinkNode *elem_link_node = static_cast<LineartElementLinkNode *>(
2019 &la_data->render_data_pool,
2020 la_v_arr,
2021 sizeof(LineartElementLinkNode)));
2022 BLI_spin_unlock(&la_data->lock_task);
2023
2024 elem_link_node->obindex = ob_info->obindex;
2025 elem_link_node->element_count = mesh->verts_num;
2026 elem_link_node->object_ref = orig_ob;
2027 ob_info->v_eln = elem_link_node;
2028
2029 bool use_auto_smooth = false;
2030 float crease_angle = 0;
2031 if (orig_ob->lineart.flags & OBJECT_LRT_OWN_CREASE) {
2032 crease_angle = cosf(M_PI - orig_ob->lineart.crease_threshold);
2033 }
2034 else {
2035 crease_angle = la_data->conf.crease_threshold;
2036 }
2037
2038 /* FIXME(Yiming): Hack for getting clean 3D text, the seam that extruded text object creates
2039 * erroneous detection on creases. Future configuration should allow options. */
2040 if (orig_ob->type == OB_FONT) {
2041 elem_link_node->flags |= LRT_ELEMENT_BORDER_ONLY;
2042 }
2043
2044 BLI_spin_lock(&la_data->lock_task);
2045 elem_link_node = static_cast<LineartElementLinkNode *>(
2047 &la_data->render_data_pool,
2048 la_tri_arr,
2049 sizeof(LineartElementLinkNode)));
2050 BLI_spin_unlock(&la_data->lock_task);
2051
2052 int usage = ob_info->usage;
2053
2054 elem_link_node->element_count = corner_tris.size();
2055 elem_link_node->object_ref = orig_ob;
2056 elem_link_node->flags = eLineArtElementNodeFlag(
2057 elem_link_node->flags |
2059
2060 /* Note this memory is not from pool, will be deleted after culling. */
2062 size_t(corner_tris.size()), "LineartTriangleAdjacent");
2063 /* Link is minimal so we use pool anyway. */
2064 BLI_spin_lock(&la_data->lock_task);
2066 &la_data->geom.triangle_adjacent_pointers, &la_data->render_data_pool, tri_adj);
2067 BLI_spin_unlock(&la_data->lock_task);
2068
2069 /* Convert all vertices to lineart verts. */
2070 TaskParallelSettings vert_settings;
2072 /* Set the minimum amount of verts a thread has to process. */
2073 vert_settings.min_iter_per_thread = 4000;
2074
2075 VertData vert_data;
2076 vert_data.positions = mesh->vert_positions();
2077 vert_data.v_arr = la_v_arr;
2078 vert_data.model_view = ob_info->model_view;
2079 vert_data.model_view_proj = ob_info->model_view_proj;
2080
2082 0, mesh->verts_num, &vert_data, lineart_mvert_transform_task, &vert_settings);
2083
2084 /* Convert all mesh triangles into lineart triangles.
2085 * Also create an edge map to get connectivity between edges and triangles. */
2086 TaskParallelSettings tri_settings;
2088 /* Set the minimum amount of triangles a thread has to process. */
2089 tri_settings.min_iter_per_thread = 4000;
2090
2091 TriData tri_data;
2092 tri_data.ob_info = ob_info;
2093 tri_data.positions = mesh->vert_positions();
2094 tri_data.corner_tris = corner_tris;
2095 tri_data.tri_faces = mesh->corner_tri_faces();
2096 tri_data.corner_verts = mesh->corner_verts();
2097 tri_data.material_indices = material_indices;
2098 tri_data.vert_arr = la_v_arr;
2099 tri_data.tri_arr = la_tri_arr;
2100 tri_data.lineart_triangle_size = la_data->sizeof_triangle;
2101 tri_data.tri_adj = tri_adj;
2102 if (la_data->conf.filter_face_mark) {
2103 tri_data.face_marks = face_marks;
2105 }
2106
2107 uint32_t total_edges = corner_tris.size() * 3;
2108
2109 BLI_task_parallel_range(0, corner_tris.size(), &tri_data, lineart_load_tri_task, &tri_settings);
2110
2111 /* Check for contour lines in the mesh.
2112 * IE check if the triangle edges lies in area where the triangles go from front facing to back
2113 * facing.
2114 */
2115 EdgeFeatReduceData edge_reduce = {0};
2116 TaskParallelSettings edge_feat_settings;
2117 BLI_parallel_range_settings_defaults(&edge_feat_settings);
2118 /* Set the minimum amount of edges a thread has to process. */
2119 edge_feat_settings.min_iter_per_thread = 4000;
2120 edge_feat_settings.userdata_chunk = &edge_reduce;
2121 edge_feat_settings.userdata_chunk_size = sizeof(EdgeFeatReduceData);
2122 edge_feat_settings.func_reduce = feat_data_sum_reduce;
2123
2124 const VArray<bool> sharp_edges = *attributes.lookup_or_default<bool>(
2125 "sharp_edge", AttrDomain::Edge, false);
2126 const VArray<bool> sharp_faces = *attributes.lookup_or_default<bool>(
2127 "sharp_face", AttrDomain::Face, false);
2128
2129 EdgeFeatData edge_feat_data = {nullptr};
2130 edge_feat_data.ld = la_data;
2131 edge_feat_data.mesh = mesh;
2132 edge_feat_data.ob_eval = ob_info->original_ob_eval;
2133 edge_feat_data.material_indices = material_indices;
2134 edge_feat_data.edges = mesh->edges();
2135 edge_feat_data.corner_verts = mesh->corner_verts();
2136 edge_feat_data.corner_edges = mesh->corner_edges();
2137 edge_feat_data.corner_tris = corner_tris;
2138 edge_feat_data.tri_faces = mesh->corner_tri_faces();
2139 edge_feat_data.sharp_edges = sharp_edges;
2140 edge_feat_data.sharp_faces = sharp_faces;
2141 edge_feat_data.edge_nabr = lineart_build_edge_neighbor(mesh, total_edges);
2142 edge_feat_data.tri_array = la_tri_arr;
2143 edge_feat_data.v_array = la_v_arr;
2144 edge_feat_data.crease_threshold = crease_angle;
2145 edge_feat_data.use_auto_smooth = use_auto_smooth;
2146 edge_feat_data.use_freestyle_face = can_find_freestyle_face;
2147 edge_feat_data.use_freestyle_edge = can_find_freestyle_edge;
2148 if (edge_feat_data.use_freestyle_face) {
2149 edge_feat_data.freestyle_face_index = CustomData_get_layer_index(&mesh->face_data,
2151 }
2152 if (edge_feat_data.use_freestyle_edge) {
2153 edge_feat_data.freestyle_edge_index = CustomData_get_layer_index(&mesh->edge_data,
2155 }
2156
2158 total_edges,
2159 &edge_feat_data,
2161 &edge_feat_settings);
2162
2163 LooseEdgeData loose_data = {0};
2164
2165 if (la_data->conf.use_loose) {
2166 /* Only identifying floating edges at this point because other edges has been taken care of
2167 * inside #lineart_identify_corner_tri_feature_edges function. */
2168 const LooseEdgeCache &loose_edges = mesh->loose_edges();
2169 loose_data.loose_array = MEM_malloc_arrayN<int>(size_t(loose_edges.count), __func__);
2170 if (loose_edges.count > 0) {
2171 loose_data.loose_count = 0;
2172 for (const int64_t edge_i : IndexRange(mesh->edges_num)) {
2173 if (loose_edges.is_loose_bits[edge_i]) {
2174 loose_data.loose_array[loose_data.loose_count] = int(edge_i);
2175 loose_data.loose_count++;
2176 }
2177 }
2178 }
2179 }
2180
2181 int allocate_la_e = edge_reduce.feat_edges + loose_data.loose_count;
2182
2183 LineartEdge *la_edge_arr = static_cast<LineartEdge *>(
2184 lineart_mem_acquire_thread(la_data->edge_data_pool, sizeof(LineartEdge) * allocate_la_e));
2186 la_data->edge_data_pool, sizeof(LineartEdgeSegment) * allocate_la_e));
2187 BLI_spin_lock(&la_data->lock_task);
2188 elem_link_node = static_cast<LineartElementLinkNode *>(
2190 la_data->edge_data_pool,
2191 la_edge_arr,
2192 sizeof(LineartElementLinkNode)));
2193 BLI_spin_unlock(&la_data->lock_task);
2194 elem_link_node->element_count = allocate_la_e;
2195 elem_link_node->object_ref = orig_ob;
2196 elem_link_node->obindex = ob_info->obindex;
2197
2198 LineartElementLinkNode *shadow_eln = nullptr;
2199 if (shadow_elns) {
2200 shadow_eln = lineart_find_matching_eln(shadow_elns, ob_info->obindex);
2201 }
2202
2203 /* Start of the edge/seg arr */
2204 LineartEdge *la_edge;
2205 LineartEdgeSegment *la_seg;
2206 la_edge = la_edge_arr;
2207 la_seg = la_seg_arr;
2208
2209 for (int i = 0; i < total_edges; i++) {
2210 LineartEdgeNeighbor *edge_nabr = &edge_feat_data.edge_nabr[i];
2211
2212 if (i < edge_nabr->e) {
2213 continue;
2214 }
2215
2216 /* Not a feature line, so we skip. */
2217 if (edge_nabr->flags == 0) {
2218 continue;
2219 }
2220
2221 LineartEdge *edge_added = nullptr;
2222
2223 /* See eLineartEdgeFlag for details. */
2224 for (int flag_bit = 0; flag_bit < LRT_MESH_EDGE_TYPES_COUNT; flag_bit++) {
2225 int use_type = LRT_MESH_EDGE_TYPES[flag_bit];
2226 if (!(use_type & edge_nabr->flags)) {
2227 continue;
2228 }
2229
2230 la_edge->v1 = &la_v_arr[edge_nabr->v1];
2231 la_edge->v2 = &la_v_arr[edge_nabr->v2];
2232 int findex = i / 3;
2233 la_edge->t1 = lineart_triangle_from_index(la_data, la_tri_arr, findex);
2234 if (!edge_added) {
2235 lineart_triangle_adjacent_assign(la_edge->t1, &tri_adj[findex], la_edge);
2236 }
2237 if (edge_nabr->e != -1) {
2238 findex = edge_nabr->e / 3;
2239 la_edge->t2 = lineart_triangle_from_index(la_data, la_tri_arr, findex);
2240 if (!edge_added) {
2241 lineart_triangle_adjacent_assign(la_edge->t2, &tri_adj[findex], la_edge);
2242 }
2243 }
2244 la_edge->flags = use_type;
2245 la_edge->object_ref = orig_ob;
2246 la_edge->edge_identifier = LRT_EDGE_IDENTIFIER(ob_info, la_edge);
2247 BLI_addtail(&la_edge->segments, la_seg);
2248
2249 if (shadow_eln) {
2250 /* TODO(Yiming): It's gonna be faster to do this operation after second stage occlusion if
2251 * we only need visible segments to have shadow info, however that way we lose information
2252 * on "shadow behind transparency window" type of region. */
2253 LineartEdge *shadow_e = lineart_find_matching_edge(shadow_eln, la_edge->edge_identifier);
2254 if (shadow_e) {
2255 lineart_register_shadow_cuts(la_data, la_edge, shadow_e);
2256 }
2257 }
2258
2259 if (ELEM(usage,
2264 {
2265 lineart_add_edge_to_array_thread(ob_info, la_edge);
2266 }
2267
2268 if (edge_added) {
2270 }
2271
2272 edge_added = la_edge;
2273
2274 la_edge++;
2275 la_seg++;
2276
2277 if (!la_data->conf.allow_duplicated_types) {
2278 break;
2279 }
2280 }
2281 }
2282
2283 if (loose_data.loose_array) {
2284 const Span<int2> edges = mesh->edges();
2285 for (int i = 0; i < loose_data.loose_count; i++) {
2286 const int2 &edge = edges[loose_data.loose_array[i]];
2287 la_edge->v1 = &la_v_arr[edge[0]];
2288 la_edge->v2 = &la_v_arr[edge[1]];
2290 la_edge->object_ref = orig_ob;
2291 la_edge->edge_identifier = LRT_EDGE_IDENTIFIER(ob_info, la_edge);
2292 BLI_addtail(&la_edge->segments, la_seg);
2293 if (ELEM(usage,
2298 {
2299 lineart_add_edge_to_array_thread(ob_info, la_edge);
2300 if (shadow_eln) {
2301 LineartEdge *shadow_e = lineart_find_matching_edge(shadow_eln, la_edge->edge_identifier);
2302 if (shadow_e) {
2303 lineart_register_shadow_cuts(la_data, la_edge, shadow_e);
2304 }
2305 }
2306 }
2307 la_edge++;
2308 la_seg++;
2309 }
2310 MEM_SAFE_FREE(loose_data.loose_array);
2311 }
2312
2313 MEM_freeN(edge_feat_data.edge_nabr);
2314
2315 if (ob_info->free_use_mesh) {
2316 BKE_id_free(nullptr, mesh);
2317 }
2318}
2319
2320static void lineart_object_load_worker(TaskPool *__restrict /*pool*/,
2322{
2323 for (LineartObjectInfo *obi = olti->pending; obi; obi = obi->next) {
2324 lineart_geometry_object_load(obi, olti->ld, olti->shadow_elns);
2325 }
2326}
2327
2329{
2331 uchar result = lineart_intersection_mask_check(cc->collection, ob);
2332 if (result) {
2333 return result;
2334 }
2335 }
2336
2337 /* We already did "depth-priority search" above, so if no child collection is overriding the
2338 * value, we use the parent's value. */
2339 if (BKE_collection_has_object_recursive_instanced(c, blender::id_cast<Object *>(ob->id.orig_id)))
2340 {
2342 return c->lineart_intersection_mask;
2343 }
2344 }
2345
2346 return 0;
2347}
2348
2350{
2352 return ob->lineart.intersection_priority;
2353 }
2354
2356 uchar result = lineart_intersection_priority_check(cc->collection, ob);
2357 if (result) {
2358 return result;
2359 }
2360 }
2361
2362 /* We already did "depth-priority search" above, so if no child collection is overriding the
2363 * value, we use the parent's value. */
2364 if (BKE_collection_has_object_recursive_instanced(c, blender::id_cast<Object *>(ob->id.orig_id)))
2365 {
2368 }
2369 }
2370 return 0;
2371}
2372
2377static int lineart_usage_check(Collection *c, Object *ob, bool is_render)
2378{
2379
2380 if (!c) {
2381 return OBJECT_LRT_INHERIT;
2382 }
2383
2384 int object_has_special_usage = (ob->lineart.usage != OBJECT_LRT_INHERIT);
2385
2386 if (object_has_special_usage) {
2387 return ob->lineart.usage;
2388 }
2389
2390 if (c->gobject.first) {
2391 if (BKE_collection_has_object(c, (Object *)(ob->id.orig_id))) {
2392 if ((is_render && (c->flag & COLLECTION_HIDE_RENDER)) ||
2393 ((!is_render) && (c->flag & COLLECTION_HIDE_VIEWPORT)))
2394 {
2395 return OBJECT_LRT_EXCLUDE;
2396 }
2397 if (ob->lineart.usage == OBJECT_LRT_INHERIT) {
2398 switch (c->lineart_usage) {
2402 return OBJECT_LRT_EXCLUDE;
2409 }
2410 return OBJECT_LRT_INHERIT;
2411 }
2412 return ob->lineart.usage;
2413 }
2414 }
2415
2417 int result = lineart_usage_check(cc->collection, ob, is_render);
2418 if (result > OBJECT_LRT_INHERIT) {
2419 return result;
2420 }
2421 }
2422
2423 return OBJECT_LRT_INHERIT;
2424}
2425
2427 LineartObjectInfo *obi,
2428 int thread_count,
2429 int this_face_count)
2430{
2431 LineartObjectLoadTaskInfo *use_olti = olti_list;
2432 uint64_t min_face = use_olti->total_faces;
2433 for (int i = 0; i < thread_count; i++) {
2434 if (olti_list[i].total_faces < min_face) {
2435 min_face = olti_list[i].total_faces;
2436 use_olti = &olti_list[i];
2437 }
2438 }
2439
2440 use_olti->total_faces += this_face_count;
2441 obi->next = use_olti->pending;
2442 use_olti->pending = obi;
2443}
2444
2445static bool lineart_geometry_check_visible(double model_view_proj[4][4],
2446 double shift_x,
2447 double shift_y,
2448 Mesh *use_mesh)
2449{
2450 using namespace blender;
2451 if (!use_mesh) {
2452 return false;
2453 }
2454 const std::optional<Bounds<float3>> bounds = use_mesh->bounds_min_max();
2455 if (!bounds.has_value()) {
2456 return false;
2457 }
2458 BoundBox bb;
2459 BKE_boundbox_init_from_minmax(&bb, bounds.value().min, bounds.value().max);
2460
2461 double co[8][4];
2462 double tmp[3];
2463 for (int i = 0; i < 8; i++) {
2464 copy_v3db_v3fl(co[i], bb.vec[i]);
2465 copy_v3_v3_db(tmp, co[i]);
2466 mul_v4_m4v3_db(co[i], model_view_proj, tmp);
2467 co[i][0] -= shift_x * 2 * co[i][3];
2468 co[i][1] -= shift_y * 2 * co[i][3];
2469 }
2470
2471 bool cond[6] = {true, true, true, true, true, true};
2472 /* Because for a point to be inside clip space, it must satisfy `-Wc <= XYCc <= Wc`, here if
2473 * all verts falls to the same side of the clip space border, we know it's outside view. */
2474 for (int i = 0; i < 8; i++) {
2475 cond[0] &= (co[i][0] < -co[i][3]);
2476 cond[1] &= (co[i][0] > co[i][3]);
2477 cond[2] &= (co[i][1] < -co[i][3]);
2478 cond[3] &= (co[i][1] > co[i][3]);
2479 cond[4] &= (co[i][2] < -co[i][3]);
2480 cond[5] &= (co[i][2] > co[i][3]);
2481 }
2482 for (int i = 0; i < 6; i++) {
2483 if (cond[i]) {
2484 return false;
2485 }
2486 }
2487 return true;
2488}
2489
2491 Depsgraph *depsgraph,
2492 Scene *scene,
2493 Object *ob,
2494 Object *ref_ob,
2495 const float use_mat[4][4],
2496 bool is_render,
2498 int thread_count,
2499 int obindex)
2500{
2501 LineartObjectInfo *obi = static_cast<LineartObjectInfo *>(
2503 obi->usage = lineart_usage_check(scene->master_collection, ob, is_render);
2506 Mesh *use_mesh;
2507
2508 if (obi->usage == OBJECT_LRT_EXCLUDE) {
2509 return;
2510 }
2511
2512 obi->obindex = obindex << LRT_OBINDEX_SHIFT;
2513
2514 /* Prepare the matrix used for transforming this specific object (instance). This has to be
2515 * done before mesh boundbox check because the function needs that. */
2517 mul_m4db_m4db_m4fl(obi->model_view, ld->conf.view, use_mat);
2518
2520 return;
2521 }
2522 if (ob->type == OB_MESH) {
2523 use_mesh = BKE_object_get_evaluated_mesh(ob);
2524 if ((!use_mesh) || use_mesh->runtime->edit_mesh) {
2525 /* If the object is being edited, then the mesh is not evaluated fully into the final
2526 * result, do not load them. This could be caused by incorrect evaluation order due to
2527 * the way line art uses depsgraph.See #102612 for explanation of this workaround. */
2528 return;
2529 }
2530 }
2531 else {
2532 use_mesh = BKE_mesh_new_from_object(depsgraph, ob, true, true, true);
2533 }
2534
2535 /* In case we still can not get any mesh geometry data from the object, same as above. */
2536 if (!use_mesh) {
2537 return;
2538 }
2539
2541 obi->model_view_proj, ld->conf.shift_x, ld->conf.shift_y, use_mesh))
2542 {
2543 return;
2544 }
2545
2546 if (ob->type != OB_MESH) {
2547 obi->free_use_mesh = true;
2548 }
2549
2550 /* Make normal matrix. */
2551 float imat[4][4];
2552 invert_m4_m4(imat, use_mat);
2553 transpose_m4(imat);
2554 copy_m4d_m4(obi->normal, imat);
2555
2556 obi->original_me = use_mesh;
2557 obi->original_ob = (ref_ob->id.orig_id ? (Object *)ref_ob->id.orig_id : ref_ob);
2559 lineart_geometry_load_assign_thread(olti, obi, thread_count, use_mesh->faces_num);
2560}
2561
2563 Scene *scene,
2564 Object *camera /* Still use camera arg for convenience. */,
2565 LineartData *ld,
2566 bool allow_duplicates,
2567 bool do_shadow_casting,
2568 ListBase *shadow_elns,
2569 blender::Set<const Object *> *included_objects)
2570{
2571 double proj[4][4], view[4][4], result[4][4];
2572 float inv[4][4];
2573
2574 if (!do_shadow_casting) {
2575 Camera *cam = static_cast<Camera *>(camera->data);
2576 float sensor = BKE_camera_sensor_size(cam->sensor_fit, cam->sensor_x, cam->sensor_y);
2577 int fit = BKE_camera_sensor_fit(cam->sensor_fit, ld->w, ld->h);
2578 double asp = (double(ld->w) / double(ld->h));
2579 if (ELEM(cam->type, CAM_PERSP, CAM_PANO, CAM_CUSTOM)) {
2580 if (fit == CAMERA_SENSOR_FIT_VERT && asp > 1) {
2581 sensor *= asp;
2582 }
2583 if (fit == CAMERA_SENSOR_FIT_HOR && asp < 1) {
2584 sensor /= asp;
2585 }
2586 const double fov = focallength_to_fov(cam->lens / (1 + ld->conf.overscan), sensor);
2587 lineart_matrix_perspective_44d(proj, fov, asp, cam->clip_start, cam->clip_end);
2588 }
2589 else if (cam->type == CAM_ORTHO) {
2590 double horizontal = cam->ortho_scale / 2;
2591 double vertical = horizontal;
2592 if (fit == CAMERA_SENSOR_FIT_VERT) {
2593 horizontal *= asp;
2594 }
2595 else {
2596 vertical /= asp;
2597 }
2599 proj, -horizontal, horizontal, -vertical, vertical, cam->clip_start, cam->clip_end);
2600 }
2601 else {
2602 BLI_assert(!"Unsupported camera type in lineart_main_load_geometries");
2603 unit_m4_db(proj);
2604 }
2605
2606 invert_m4_m4(inv, ld->conf.cam_obmat);
2607 mul_m4db_m4db_m4fl(result, proj, inv);
2608 copy_m4_m4_db(proj, result);
2610
2613 }
2614
2617
2618 double t_start;
2619 if (G.debug_value == 4000) {
2620 t_start = BLI_time_now_seconds();
2621 }
2622
2623 int thread_count = ld->thread_count;
2624 int bound_box_discard_count = 0;
2625 int obindex = 0;
2626
2627 /* This memory is in render buffer memory pool. So we don't need to free those after loading. */
2629 &ld->render_data_pool, sizeof(LineartObjectLoadTaskInfo) * thread_count));
2630
2632 bool is_render = eval_mode == DAG_EVAL_RENDER;
2633
2636
2637 /* Instance duplicated & particles. */
2638 if (allow_duplicates) {
2640 }
2641
2642 DEGObjectIterSettings deg_iter_settings = {nullptr};
2643 deg_iter_settings.depsgraph = depsgraph;
2644 deg_iter_settings.flags = flags;
2645 deg_iter_settings.included_objects = included_objects;
2646
2647 DEG_OBJECT_ITER_BEGIN (&deg_iter_settings, ob) {
2648
2649 obindex++;
2650
2651 Object *eval_ob = DEG_get_evaluated(depsgraph, ob);
2652
2653 if (!eval_ob) {
2654 continue;
2655 }
2656
2657 /* DEG_OBJECT_ITER_BEGIN will include the instanced mesh of these curve object types, so don't
2658 * load them twice. */
2659 if (allow_duplicates && ELEM(ob->type, OB_CURVES_LEGACY, OB_FONT, OB_SURF)) {
2660 continue;
2661 }
2662
2663 if (BKE_object_visibility(eval_ob, eval_mode) & OB_VISIBLE_SELF) {
2665 depsgraph,
2666 scene,
2667 eval_ob,
2668 eval_ob,
2669 eval_ob->object_to_world().ptr(),
2670 is_render,
2671 olti,
2672 thread_count,
2673 obindex);
2674 }
2675 }
2677
2679
2680 if (G.debug_value == 4000) {
2681 printf("thread count: %d\n", thread_count);
2682 }
2683 for (int i = 0; i < thread_count; i++) {
2684 olti[i].ld = ld;
2685 olti[i].shadow_elns = shadow_elns;
2686 olti[i].thread_id = i;
2687 BLI_task_pool_push(tp, (TaskRunFunction)lineart_object_load_worker, &olti[i], false, nullptr);
2688 }
2691
2692 /* The step below is to serialize vertex index in the whole scene, so
2693 * lineart_triangle_share_edge() can work properly from the lack of triangle adjacent info. */
2694 int global_i = 0;
2695
2696 int edge_count = 0;
2697 for (int i = 0; i < thread_count; i++) {
2698 for (LineartObjectInfo *obi = olti[i].pending; obi; obi = obi->next) {
2699 if (!obi->v_eln) {
2700 continue;
2701 }
2702 edge_count += obi->pending_edges.next;
2703 }
2704 }
2706
2707 for (int i = 0; i < thread_count; i++) {
2708 for (LineartObjectInfo *obi = olti[i].pending; obi; obi = obi->next) {
2709 if (!obi->v_eln) {
2710 continue;
2711 }
2712 LineartVert *v = (LineartVert *)obi->v_eln->pointer;
2713 int v_count = obi->v_eln->element_count;
2714 obi->v_eln->global_index_offset = global_i;
2715 for (int vi = 0; vi < v_count; vi++) {
2716 v[vi].index += global_i;
2717 }
2718 /* Register a global index increment. See #lineart_triangle_share_edge() and
2719 * #lineart_main_load_geometries() for detailed. It's okay that global_vindex might
2720 * eventually overflow, in such large scene it's virtually impossible for two vertex of the
2721 * same numeric index to come close together. */
2722 obi->global_i_offset = global_i;
2723 global_i += v_count;
2725 }
2726 }
2727
2728 if (G.debug_value == 4000) {
2729 double t_elapsed = BLI_time_now_seconds() - t_start;
2730 printf("Line art loading time: %lf\n", t_elapsed);
2731 printf("Discarded %d object from bound box check\n", bound_box_discard_count);
2732 }
2733}
2734
2740 const LineartVert *vt,
2741 LineartVert **l,
2742 LineartVert **r)
2743{
2744 if (tri->v[0] == vt) {
2745 *l = tri->v[1];
2746 *r = tri->v[2];
2747 return true;
2748 }
2749 if (tri->v[1] == vt) {
2750 *l = tri->v[2];
2751 *r = tri->v[0];
2752 return true;
2753 }
2754 if (tri->v[2] == vt) {
2755 *l = tri->v[0];
2756 *r = tri->v[1];
2757 return true;
2758 }
2759 return false;
2760}
2761
2763 const LineartEdge *e,
2764 bool allow_overlapping_edges)
2765{
2766 const LineartEdge *use_e = e;
2768 if (((e->target_reference & LRT_LIGHT_CONTOUR_TARGET) == tri->target_reference) ||
2769 (((e->target_reference >> 32) & LRT_LIGHT_CONTOUR_TARGET) == tri->target_reference))
2770 {
2771 return true;
2772 }
2773 }
2774 else {
2775 /* Normally we just determine from identifiers of adjacent triangles. */
2776 if ((use_e->t1 && use_e->t1->target_reference == tri->target_reference) ||
2777 (use_e->t2 && use_e->t2->target_reference == tri->target_reference))
2778 {
2779 return true;
2780 }
2781 }
2782
2783 /* If allows overlapping, then we compare the vertex coordinates one by one to determine if one
2784 * edge is from specific triangle. This is slower but can handle edge split cases very well. */
2785 if (allow_overlapping_edges) {
2786#define LRT_TRI_SAME_POINT(tri, i, pt) \
2787 ((LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[0], pt->gloc[0]) && \
2788 LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[1], pt->gloc[1]) && \
2789 LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[2], pt->gloc[2])) || \
2790 (LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[0], pt->gloc[0]) && \
2791 LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[1], pt->gloc[1]) && \
2792 LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[2], pt->gloc[2])))
2793 if ((LRT_TRI_SAME_POINT(tri, 0, e->v1) || LRT_TRI_SAME_POINT(tri, 1, e->v1) ||
2794 LRT_TRI_SAME_POINT(tri, 2, e->v1)) &&
2795 (LRT_TRI_SAME_POINT(tri, 0, e->v2) || LRT_TRI_SAME_POINT(tri, 1, e->v2) ||
2796 LRT_TRI_SAME_POINT(tri, 2, e->v2)))
2797 {
2798 return true;
2799 }
2800#undef LRT_TRI_SAME_POINT
2801 }
2802 return false;
2803}
2804
2805/* Sorting three intersection points from min to max,
2806 * the order for each intersection is set in `lst[0]` to `lst[2]`. */
2807#define INTERSECT_SORT_MIN_TO_MAX_3(ia, ib, ic, lst) \
2808 { \
2809 lst[0] = LRT_MIN3_INDEX(ia, ib, ic); \
2810 lst[1] = (((ia <= ib && ib <= ic) || (ic <= ib && ib <= ia)) ? \
2811 1 : \
2812 (((ic <= ia && ia <= ib) || (ib < ia && ia <= ic)) ? 0 : 2)); \
2813 lst[2] = LRT_MAX3_INDEX(ia, ib, ic); \
2814 }
2815
2816/* `ia ib ic` are ordered. */
2817#define INTERSECT_JUST_GREATER(is, order, num, index) \
2818 { \
2819 index = (num < is[order[0]] ? \
2820 order[0] : \
2821 (num < is[order[1]] ? order[1] : (num < is[order[2]] ? order[2] : -1))); \
2822 }
2823
2824/* `ia ib ic` are ordered. */
2825#define INTERSECT_JUST_SMALLER(is, order, num, index) \
2826 { \
2827 index = (num > is[order[2]] ? \
2828 order[2] : \
2829 (num > is[order[1]] ? order[1] : (num > is[order[0]] ? order[0] : -1))); \
2830 }
2831
2832#define LRT_ISEC(index) (index == 0 ? isec_e1 : (index == 1 ? isec_e2 : isec_e3))
2833#define LRT_PARALLEL(index) (index == 0 ? para_e1 : (index == 1 ? para_e2 : para_e3))
2834
2858 const LineartEdge *e,
2859 const double *override_camera_loc,
2860 const bool override_cam_is_persp,
2861 const bool allow_overlapping_edges,
2862 const double m_view_projection[4][4],
2863 const double camera_dir[3],
2864 const float cam_shift_x,
2865 const float cam_shift_y,
2866 double *from,
2867 double *to)
2868{
2869 double cross_ratios[3] = {0};
2870 int cross_order[3];
2871 int cross_v1 = -1, cross_v2 = -1;
2872 /* If the edge intersects with the triangle edges (including extensions). */
2873 int isec_e1, isec_e2, isec_e3;
2874 /* If edge is parallel to one of the edges in the triangle. */
2875 bool para_e1, para_e2, para_e3;
2876 enum LineartPointTri state_v1 = LRT_OUTSIDE_TRIANGLE, state_v2 = LRT_OUTSIDE_TRIANGLE;
2877
2878 double dir_v1[3];
2879 double dir_v2[3];
2880 double view_vector[4];
2881 double dir_cam[3];
2882 double dot_v1, dot_v2, dot_v1a, dot_v2a;
2883 double dot_f;
2884 double gloc[4], trans[4];
2885 double cut = -1;
2886
2887 double *LFBC = e->v1->fbcoord, *RFBC = e->v2->fbcoord, *FBC0 = tri->v[0]->fbcoord,
2888 *FBC1 = tri->v[1]->fbcoord, *FBC2 = tri->v[2]->fbcoord;
2889
2890 /* Overlapping not possible, return early. */
2891 if ((std::max({FBC0[0], FBC1[0], FBC2[0]}) < std::min(LFBC[0], RFBC[0])) ||
2892 (std::min({FBC0[0], FBC1[0], FBC2[0]}) > std::max(LFBC[0], RFBC[0])) ||
2893 (std::max({FBC0[1], FBC1[1], FBC2[1]}) < std::min(LFBC[1], RFBC[1])) ||
2894 (std::min({FBC0[1], FBC1[1], FBC2[1]}) > std::max(LFBC[1], RFBC[1])) ||
2895 (std::min({FBC0[3], FBC1[3], FBC2[3]}) > std::max(LFBC[3], RFBC[3])))
2896 {
2897 return false;
2898 }
2899
2900 /* If the line is one of the edge in the triangle, then it's not occluded. */
2901 if (lineart_edge_from_triangle(tri, e, allow_overlapping_edges)) {
2902 return false;
2903 }
2904
2905 /* Check if the line visually crosses one of the edge in the triangle. */
2906 isec_e1 = lineart_intersect_seg_seg(LFBC, RFBC, FBC0, FBC1, &cross_ratios[0], &para_e1);
2907 isec_e2 = lineart_intersect_seg_seg(LFBC, RFBC, FBC1, FBC2, &cross_ratios[1], &para_e2);
2908 isec_e3 = lineart_intersect_seg_seg(LFBC, RFBC, FBC2, FBC0, &cross_ratios[2], &para_e3);
2909
2910 /* Sort the intersection distance. */
2911 INTERSECT_SORT_MIN_TO_MAX_3(cross_ratios[0], cross_ratios[1], cross_ratios[2], cross_order);
2912
2913 sub_v3_v3v3_db(dir_v1, e->v1->gloc, tri->v[0]->gloc);
2914 sub_v3_v3v3_db(dir_v2, e->v2->gloc, tri->v[0]->gloc);
2915
2916 copy_v3_v3_db(dir_cam, camera_dir);
2917 copy_v3_v3_db(view_vector, override_camera_loc);
2918 if (override_cam_is_persp) {
2919 sub_v3_v3v3_db(dir_cam, view_vector, tri->v[0]->gloc);
2920 }
2921
2922 dot_v1 = dot_v3v3_db(dir_v1, tri->gn);
2923 dot_v2 = dot_v3v3_db(dir_v2, tri->gn);
2924 dot_f = dot_v3v3_db(dir_cam, tri->gn);
2925
2927 (e->target_reference == tri->target_reference))
2928 {
2929 if (((dot_f > 0) && (e->flags & MOD_LINEART_EDGE_FLAG_SHADOW_FACING_LIGHT)) ||
2930 ((dot_f < 0) && !(e->flags & MOD_LINEART_EDGE_FLAG_SHADOW_FACING_LIGHT)))
2931 {
2932 *from = 0.0f;
2933 *to = 1.0f;
2934 return true;
2935 }
2936
2937 return false;
2938 }
2939
2940 /* NOTE(Yiming): When we don't use `dot_f==0` here, it's theoretically possible that _some_
2941 * faces in perspective mode would get erroneously caught in this condition where they really
2942 * are legit faces that would produce occlusion, but haven't encountered those yet in my test
2943 * files.
2944 */
2945 if (fabs(dot_f) < FLT_EPSILON) {
2946 return false;
2947 }
2948
2949 /* Whether two end points are inside/on_the_edge/outside of the triangle. */
2950 state_v1 = lineart_point_triangle_relation(LFBC, FBC0, FBC1, FBC2);
2951 state_v2 = lineart_point_triangle_relation(RFBC, FBC0, FBC1, FBC2);
2952
2953 /* If the edge doesn't visually cross any edge of the triangle... */
2954 if (!isec_e1 && !isec_e2 && !isec_e3) {
2955 /* And if both end point from the edge is outside of the triangle... */
2956 if ((!state_v1) && (!state_v2)) {
2957 return false; /* We don't have any occlusion. */
2958 }
2959 }
2960
2961 /* Determine the cut position. */
2962
2963 dot_v1a = fabs(dot_v1);
2964 if (dot_v1a < DBL_EPSILON) {
2965 dot_v1a = 0;
2966 dot_v1 = 0;
2967 }
2968 dot_v2a = fabs(dot_v2);
2969 if (dot_v2a < DBL_EPSILON) {
2970 dot_v2a = 0;
2971 dot_v2 = 0;
2972 }
2973 if (dot_v1 - dot_v2 == 0) {
2974 cut = 100000;
2975 }
2976 else if (dot_v1 * dot_v2 <= 0) {
2977 cut = dot_v1a / fabs(dot_v1 - dot_v2);
2978 }
2979 else {
2980 cut = fabs(dot_v2 + dot_v1) / fabs(dot_v1 - dot_v2);
2981 cut = dot_v2a > dot_v1a ? 1 - cut : cut;
2982 }
2983
2984 /* Transform the cut from geometry space to image space. */
2985 if (override_cam_is_persp) {
2986 interp_v3_v3v3_db(gloc, e->v1->gloc, e->v2->gloc, cut);
2987 mul_v4_m4v3_db(trans, m_view_projection, gloc);
2988 mul_v3db_db(trans, (1 / trans[3]));
2989 trans[0] -= cam_shift_x * 2;
2990 trans[1] -= cam_shift_y * 2;
2991 /* To accommodate `k=0` and `k=inf` (vertical) lines. here the cut is in image space. */
2992 if (fabs(e->v1->fbcoord[0] - e->v2->fbcoord[0]) > fabs(e->v1->fbcoord[1] - e->v2->fbcoord[1]))
2993 {
2994 cut = ratiod(e->v1->fbcoord[0], e->v2->fbcoord[0], trans[0]);
2995 }
2996 else {
2997 cut = ratiod(e->v1->fbcoord[1], e->v2->fbcoord[1], trans[1]);
2998 }
2999 }
3000
3001#define LRT_GUARD_NOT_FOUND \
3002 if (cross_v1 < 0 || cross_v2 < 0) { \
3003 return false; \
3004 }
3005
3006 /* Determine the pair of edges that the line has crossed. The "|" symbol in the comment
3007 * indicates triangle boundary. DBL_TRIANGLE_LIM is needed to for floating point precision
3008 * tolerance. */
3009
3010 if (state_v1 == LRT_INSIDE_TRIANGLE) {
3011 /* Left side is in the triangle. */
3012 if (state_v2 == LRT_INSIDE_TRIANGLE) {
3013 /* | l---r | */
3014 INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v1);
3015 INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v2);
3016 }
3017 else if (state_v2 == LRT_ON_TRIANGLE) {
3018 /* | l------r| */
3019 INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v1);
3020 INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v2);
3021 }
3022 else if (state_v2 == LRT_OUTSIDE_TRIANGLE) {
3023 /* | l-------|------r */
3024 INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v1);
3025 INTERSECT_JUST_GREATER(cross_ratios, cross_order, 0, cross_v2);
3026 }
3027 }
3028 else if (state_v1 == LRT_ON_TRIANGLE) {
3029 /* Left side is on some edge of the triangle. */
3030 if (state_v2 == LRT_INSIDE_TRIANGLE) {
3031 /* |l------r | */
3032 INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v1);
3033 INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v2);
3034 }
3035 else if (state_v2 == LRT_ON_TRIANGLE) {
3036 /* |l---------r| */
3037 INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v1);
3038 INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v2);
3039 }
3040 else if (state_v2 == LRT_OUTSIDE_TRIANGLE) {
3041 /* |l----------|-------r (crossing the triangle) [OR]
3042 * r---------|l | (not crossing the triangle) */
3043 INTERSECT_JUST_GREATER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v2);
3044 if (cross_v2 >= 0 && LRT_ISEC(cross_v2) && cross_ratios[cross_v2] > (DBL_TRIANGLE_LIM)) {
3045 INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v1);
3046 }
3047 else {
3048 INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v2);
3049 if (cross_v2 > 0) {
3050 INTERSECT_JUST_SMALLER(cross_ratios, cross_order, cross_ratios[cross_v2], cross_v1);
3051 }
3052 }
3054 /* We could have the edge being completely parallel to the triangle where there isn't a
3055 * viable occlusion result. */
3056 if ((LRT_PARALLEL(cross_v1) && !LRT_ISEC(cross_v1)) ||
3057 (LRT_PARALLEL(cross_v2) && !LRT_ISEC(cross_v2)))
3058 {
3059 return false;
3060 }
3061 }
3062 }
3063 else if (state_v1 == LRT_OUTSIDE_TRIANGLE) {
3064 /* Left side is outside of the triangle. */
3065 if (state_v2 == LRT_INSIDE_TRIANGLE) {
3066 /* l---|---r | */
3067 INTERSECT_JUST_SMALLER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v1);
3068 INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v2);
3069 }
3070 else if (state_v2 == LRT_ON_TRIANGLE) {
3071 /* |r----------|-------l (crossing the triangle) [OR]
3072 * l---------|r | (not crossing the triangle) */
3073 INTERSECT_JUST_SMALLER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v1);
3074 if (cross_v1 >= 0 && LRT_ISEC(cross_v1) && cross_ratios[cross_v1] < (1 - DBL_TRIANGLE_LIM)) {
3075 INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v2);
3076 }
3077 else {
3078 INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v1);
3079 if (cross_v1 > 0) {
3080 INTERSECT_JUST_GREATER(cross_ratios, cross_order, cross_ratios[cross_v1], cross_v2);
3081 }
3082 }
3084 /* The same logic applies as above case. */
3085 if ((LRT_PARALLEL(cross_v1) && !LRT_ISEC(cross_v1)) ||
3086 (LRT_PARALLEL(cross_v2) && !LRT_ISEC(cross_v2)))
3087 {
3088 return false;
3089 }
3090 }
3091 else if (state_v2 == LRT_OUTSIDE_TRIANGLE) {
3092 /* l---|----|----r (crossing the triangle) [OR]
3093 * l----r | | (not crossing the triangle) */
3094 INTERSECT_JUST_GREATER(cross_ratios, cross_order, -DBL_TRIANGLE_LIM, cross_v1);
3095 if (cross_v1 >= 0 && LRT_ISEC(cross_v1)) {
3096 INTERSECT_JUST_GREATER(cross_ratios, cross_order, cross_ratios[cross_v1], cross_v2);
3097 }
3098 else {
3099 if (cross_v1 >= 0) {
3100 INTERSECT_JUST_GREATER(cross_ratios, cross_order, cross_ratios[cross_v1], cross_v1);
3101 if (cross_v1 >= 0) {
3102 INTERSECT_JUST_GREATER(cross_ratios, cross_order, cross_ratios[cross_v1], cross_v2);
3103 }
3104 }
3105 }
3106 }
3107 }
3108
3110
3111 double dot_1f = dot_v1 * dot_f, dot_2f = dot_v2 * dot_f;
3112
3113 /* Determine the start and end point of image space cut on a line. */
3114 if (dot_1f <= 0 && dot_2f <= 0 && (dot_v1 || dot_v2)) {
3115 *from = std::max(0.0, cross_ratios[cross_v1]);
3116 *to = std::min(1.0, cross_ratios[cross_v2]);
3117 if (*from >= *to) {
3118 return false;
3119 }
3120 return true;
3121 }
3122 if (dot_1f >= 0 && dot_2f <= 0 && (dot_v1 || dot_v2)) {
3123 *from = std::max(cut, cross_ratios[cross_v1]);
3124 *to = std::min(1.0, cross_ratios[cross_v2]);
3125 if (*from >= *to) {
3126 return false;
3127 }
3128 return true;
3129 }
3130 if (dot_1f <= 0 && dot_2f >= 0 && (dot_v1 || dot_v2)) {
3131 *from = std::max(0.0, cross_ratios[cross_v1]);
3132 *to = std::min(cut, cross_ratios[cross_v2]);
3133 if (*from >= *to) {
3134 return false;
3135 }
3136 return true;
3137 }
3138
3139 /* Unlikely, but here's the default failed value if anything fall through. */
3140 return false;
3141}
3142
3143#undef INTERSECT_SORT_MIN_TO_MAX_3
3144#undef INTERSECT_JUST_GREATER
3145#undef INTERSECT_JUST_SMALLER
3146#undef LRT_ISEC
3147#undef LRT_PARALLEL
3148
3154{
3155 if (l->v[0]->index == r->v[0]->index) {
3156 if (l->v[1]->index == r->v[1]->index || l->v[1]->index == r->v[2]->index ||
3157 l->v[2]->index == r->v[2]->index || l->v[2]->index == r->v[1]->index)
3158 {
3159 return true;
3160 }
3161 }
3162 if (l->v[0]->index == r->v[1]->index) {
3163 if (l->v[1]->index == r->v[0]->index || l->v[1]->index == r->v[2]->index ||
3164 l->v[2]->index == r->v[2]->index || l->v[2]->index == r->v[0]->index)
3165 {
3166 return true;
3167 }
3168 }
3169 if (l->v[0]->index == r->v[2]->index) {
3170 if (l->v[1]->index == r->v[1]->index || l->v[1]->index == r->v[0]->index ||
3171 l->v[2]->index == r->v[0]->index || l->v[2]->index == r->v[1]->index)
3172 {
3173 return true;
3174 }
3175 }
3176 if (l->v[1]->index == r->v[0]->index) {
3177 if (l->v[2]->index == r->v[1]->index || l->v[2]->index == r->v[2]->index ||
3178 l->v[0]->index == r->v[2]->index || l->v[0]->index == r->v[1]->index)
3179 {
3180 return true;
3181 }
3182 }
3183 if (l->v[1]->index == r->v[1]->index) {
3184 if (l->v[2]->index == r->v[0]->index || l->v[2]->index == r->v[2]->index ||
3185 l->v[0]->index == r->v[2]->index || l->v[0]->index == r->v[0]->index)
3186 {
3187 return true;
3188 }
3189 }
3190 if (l->v[1]->index == r->v[2]->index) {
3191 if (l->v[2]->index == r->v[1]->index || l->v[2]->index == r->v[0]->index ||
3192 l->v[0]->index == r->v[0]->index || l->v[0]->index == r->v[1]->index)
3193 {
3194 return true;
3195 }
3196 }
3197
3198 /* Otherwise not possible. */
3199 return false;
3200}
3201
3203 const LineartTriangle *r)
3204{
3205 if (l->v[0] == r->v[0]) {
3206 return r->v[0];
3207 }
3208 if (l->v[0] == r->v[1]) {
3209 return r->v[1];
3210 }
3211 if (l->v[0] == r->v[2]) {
3212 return r->v[2];
3213 }
3214 if (l->v[1] == r->v[0]) {
3215 return r->v[0];
3216 }
3217 if (l->v[1] == r->v[1]) {
3218 return r->v[1];
3219 }
3220 if (l->v[1] == r->v[2]) {
3221 return r->v[2];
3222 }
3223 if (l->v[2] == r->v[0]) {
3224 return r->v[0];
3225 }
3226 if (l->v[2] == r->v[1]) {
3227 return r->v[1];
3228 }
3229 if (l->v[2] == r->v[2]) {
3230 return r->v[2];
3231 }
3232 return nullptr;
3233}
3234
3236 LineartVert *v1, LineartVert *v2, LineartTriangle *tri, const double *last, double *rv)
3237{
3238 /* Direction vectors for the edge verts. We will check if the verts are on the same side of the
3239 * triangle or not. */
3240 double dir_v1[3], dir_v2[3];
3241 double dot_v1, dot_v2;
3242 double gloc[3];
3243
3244 sub_v3_v3v3_db(dir_v1, v1->gloc, tri->v[0]->gloc);
3245 sub_v3_v3v3_db(dir_v2, v2->gloc, tri->v[0]->gloc);
3246
3247 dot_v1 = dot_v3v3_db(dir_v1, tri->gn);
3248 dot_v2 = dot_v3v3_db(dir_v2, tri->gn);
3249
3250 if (dot_v1 * dot_v2 > 0 || (!dot_v1 && !dot_v2)) {
3251 return false;
3252 }
3253
3254 dot_v1 = fabs(dot_v1);
3255 dot_v2 = fabs(dot_v2);
3256
3257 interp_v3_v3v3_db(gloc, v1->gloc, v2->gloc, dot_v1 / (dot_v1 + dot_v2));
3258
3259 /* Due to precision issue, we might end up with the same point as the one we already detected. */
3260 if (last && LRT_DOUBLE_CLOSE_ENOUGH(last[0], gloc[0]) &&
3261 LRT_DOUBLE_CLOSE_ENOUGH(last[1], gloc[1]) && LRT_DOUBLE_CLOSE_ENOUGH(last[2], gloc[2]))
3262 {
3263 return false;
3264 }
3265
3266 if (!lineart_point_inside_triangle3d(gloc, tri->v[0]->gloc, tri->v[1]->gloc, tri->v[2]->gloc)) {
3267 return false;
3268 }
3269
3270 copy_v3_v3_db(rv, gloc);
3271
3272 return true;
3273}
3274
3276 LineartTriangle *t2,
3277 double *v1,
3278 double *v2)
3279{
3280 double *next = v1, *last = nullptr;
3281 LineartVert *sv1, *sv2;
3282
3283 LineartVert *share = lineart_triangle_share_point(t2, tri);
3284
3285 if (share) {
3286 /* If triangles have sharing points like `abc` and `acd`, then we only need to detect `bc`
3287 * against `acd` or `cd` against `abc`. */
3288
3289 lineart_triangle_get_other_verts(tri, share, &sv1, &sv2);
3290
3291 copy_v3_v3_db(v1, share->gloc);
3292
3293 if (!lineart_triangle_2v_intersection_math(sv1, sv2, t2, nullptr, v2)) {
3294 lineart_triangle_get_other_verts(t2, share, &sv1, &sv2);
3295 if (lineart_triangle_2v_intersection_math(sv1, sv2, tri, nullptr, v2)) {
3296 return true;
3297 }
3298 }
3299 }
3300 else {
3301 /* If not sharing any points, then we need to try all the possibilities. */
3302
3303 if (lineart_triangle_2v_intersection_math(tri->v[0], tri->v[1], t2, nullptr, v1)) {
3304 next = v2;
3305 last = v1;
3306 }
3307
3308 if (lineart_triangle_2v_intersection_math(tri->v[1], tri->v[2], t2, last, next)) {
3309 if (last) {
3310 return true;
3311 }
3312 next = v2;
3313 last = v1;
3314 }
3315 if (lineart_triangle_2v_intersection_math(tri->v[2], tri->v[0], t2, last, next)) {
3316 if (last) {
3317 return true;
3318 }
3319 next = v2;
3320 last = v1;
3321 }
3322
3323 if (lineart_triangle_2v_intersection_math(t2->v[0], t2->v[1], tri, last, next)) {
3324 if (last) {
3325 return true;
3326 }
3327 next = v2;
3328 last = v1;
3329 }
3330 if (lineart_triangle_2v_intersection_math(t2->v[1], t2->v[2], tri, last, next)) {
3331 if (last) {
3332 return true;
3333 }
3334 next = v2;
3335 last = v1;
3336 }
3337 if (lineart_triangle_2v_intersection_math(t2->v[2], t2->v[0], tri, last, next)) {
3338 if (last) {
3339 return true;
3340 }
3341 next = v2;
3342 last = v1;
3343 }
3344 }
3345 return false;
3346}
3347
3349 const double *v1,
3350 const double *v2,
3351 LineartTriangle *tri1,
3352 LineartTriangle *tri2)
3353{
3354 if (th->current == th->max) {
3355
3356 LineartIsecSingle *new_array = MEM_malloc_arrayN<LineartIsecSingle>(size_t(th->max) * 2,
3357 "LineartIsecSingle");
3358 memcpy(new_array, th->array, sizeof(LineartIsecSingle) * th->max);
3359 th->max *= 2;
3360 MEM_freeN(th->array);
3361 th->array = new_array;
3362 }
3363 LineartIsecSingle *isec_single = &th->array[th->current];
3364 copy_v3_v3_db(isec_single->v1, v1);
3365 copy_v3_v3_db(isec_single->v2, v2);
3366 isec_single->tri1 = tri1;
3367 isec_single->tri2 = tri2;
3368 if (tri1->target_reference > tri2->target_reference) {
3369 std::swap(isec_single->tri1, isec_single->tri2);
3370 }
3371 th->current++;
3372}
3373
3374#define LRT_ISECT_TRIANGLE_PER_THREAD 4096
3375
3377{
3378 LineartData *ld = th->ld;
3379 int remaining = LRT_ISECT_TRIANGLE_PER_THREAD;
3380
3383
3384 if (!eln) {
3386 return false;
3387 }
3388
3389 th->pending_from = eln;
3391
3392 while (remaining > 0 && eln) {
3393 int remaining_this_eln = eln->element_count - ld->isect_scheduled_up_to_index;
3394 int added_count = std::min(remaining, remaining_this_eln);
3395 remaining -= added_count;
3396 if (remaining || added_count == remaining_this_eln) {
3397 eln = eln->next;
3398 ld->isect_scheduled_up_to = eln;
3400 }
3401 else {
3402 ld->isect_scheduled_up_to_index += added_count;
3403 }
3404 }
3405
3406 th->pending_to = eln ? eln :
3407 static_cast<LineartElementLinkNode *>(
3410
3412
3413 return true;
3414}
3415
3416/* This function initializes two things:
3417 * 1) Triangle array scheduling info, for each worker thread to get its chunk from the scheduler.
3418 * 2) Per-thread intersection result array. Does not store actual #LineartEdge, these results will
3419 * be finalized by #lineart_create_edges_from_isec_data
3420 */
3421static void lineart_init_isec_thread(LineartIsecData *d, LineartData *ld, int thread_count)
3422{
3423 d->threads = MEM_calloc_arrayN<LineartIsecThread>(thread_count, "LineartIsecThread arr");
3424 d->ld = ld;
3425 d->thread_count = thread_count;
3426
3427 ld->isect_scheduled_up_to = static_cast<LineartElementLinkNode *>(
3430
3431 for (int i = 0; i < thread_count; i++) {
3432 LineartIsecThread *it = &d->threads[i];
3433 it->array = MEM_malloc_arrayN<LineartIsecSingle>(100, "LineartIsecSingle arr");
3434 it->max = 100;
3435 it->current = 0;
3436 it->thread_id = i;
3437 it->ld = ld;
3438 }
3439}
3440
3442{
3443 for (int i = 0; i < d->thread_count; i++) {
3444 LineartIsecThread *it = &d->threads[i];
3445 MEM_freeN(it->array);
3446 }
3447 MEM_freeN(d->threads);
3448}
3449
3453 int up_to)
3454{
3455 BLI_assert(th != nullptr);
3456
3457 if (!th) {
3458 return;
3459 }
3460
3461 double *G0 = tri->v[0]->gloc, *G1 = tri->v[1]->gloc, *G2 = tri->v[2]->gloc;
3462
3463 /* If this _is_ the smallest subdivision bounding area, then do the intersections there. */
3464 for (int i = 0; i < up_to; i++) {
3465 /* Testing_triangle->testing[0] is used to store pairing triangle reference.
3466 * See definition of LineartTriangleThread for more info. */
3467 LineartTriangle *testing_triangle = ba->linked_triangles[i];
3468 LineartTriangleThread *tt = (LineartTriangleThread *)testing_triangle;
3469
3470 if (testing_triangle == tri || tt->testing_e[th->thread_id] == (LineartEdge *)tri) {
3471 continue;
3472 }
3473 tt->testing_e[th->thread_id] = (LineartEdge *)tri;
3474
3475 if (!((testing_triangle->flags | tri->flags) & LRT_TRIANGLE_FORCE_INTERSECTION)) {
3476 if (((testing_triangle->flags | tri->flags) & LRT_TRIANGLE_NO_INTERSECTION) ||
3477 (testing_triangle->flags & tri->flags & LRT_TRIANGLE_INTERSECTION_ONLY))
3478 {
3479 continue;
3480 }
3481 }
3482
3483 double *RG0 = testing_triangle->v[0]->gloc, *RG1 = testing_triangle->v[1]->gloc,
3484 *RG2 = testing_triangle->v[2]->gloc;
3485
3486 /* Bounding box not overlapping or triangles share edges, not potential of intersecting. */
3487 if ((std::min({G0[2], G1[2], G2[2]}) > std::max({RG0[2], RG1[2], RG2[2]})) ||
3488 (std::max({G0[2], G1[2], G2[2]}) < std::min({RG0[2], RG1[2], RG2[2]})) ||
3489 (std::min({G0[0], G1[0], G2[0]}) > std::max({RG0[0], RG1[0], RG2[0]})) ||
3490 (std::max({G0[0], G1[0], G2[0]}) < std::min({RG0[0], RG1[0], RG2[0]})) ||
3491 (std::min({G0[1], G1[1], G2[1]}) > std::max({RG0[1], RG1[1], RG2[1]})) ||
3492 (std::max({G0[1], G1[1], G2[1]}) < std::min({RG0[1], RG1[1], RG2[1]})) ||
3493 lineart_triangle_share_edge(tri, testing_triangle))
3494 {
3495 continue;
3496 }
3497
3498 /* If we do need to compute intersection, then finally do it. */
3499
3500 double iv1[3], iv2[3];
3501 if (lineart_triangle_intersect_math(tri, testing_triangle, iv1, iv2)) {
3502 lineart_add_isec_thread(th, iv1, iv2, tri, testing_triangle);
3503 }
3504 }
3505}
3506
3508{
3509 float direction[3] = {0, 0, 1};
3510 float trans[3];
3511 float inv[4][4];
3512 float obmat_no_scale[4][4];
3513
3514 copy_m4_m4(obmat_no_scale, ld->conf.cam_obmat);
3515 normalize_v3(obmat_no_scale[0]);
3516 normalize_v3(obmat_no_scale[1]);
3517 normalize_v3(obmat_no_scale[2]);
3518 invert_m4_m4(inv, obmat_no_scale);
3519 transpose_m4(inv);
3520 mul_v3_mat3_m4v3(trans, inv, direction);
3521 copy_m4_m4(ld->conf.cam_obmat, obmat_no_scale);
3522 copy_v3db_v3fl(ld->conf.view_vector, trans);
3523
3525 copy_m4_m4(obmat_no_scale, ld->conf.cam_obmat_secondary);
3526 normalize_v3(obmat_no_scale[0]);
3527 normalize_v3(obmat_no_scale[1]);
3528 normalize_v3(obmat_no_scale[2]);
3529 invert_m4_m4(inv, obmat_no_scale);
3530 transpose_m4(inv);
3531 mul_v3_mat3_m4v3(trans, inv, direction);
3532 copy_m4_m4(ld->conf.cam_obmat_secondary, obmat_no_scale);
3534 }
3535}
3536
3538{
3539 BLI_spin_end(&ba->lock);
3540 if (ba->child) {
3541 for (int i = 0; i < 4; i++) {
3543 }
3544 }
3545}
3546
3548{
3549 if (ld == nullptr) {
3550 return;
3551 }
3552
3555
3559
3560 if (ld->pending_edges.array) {
3562 }
3563
3564 for (int i = 0; i < ld->qtree.initial_tile_count; i++) {
3566 }
3568
3570}
3571
3573{
3574 if (ld == nullptr) {
3575 return;
3576 }
3577
3578 BLI_spin_end(&ld->lock_task);
3579 BLI_spin_end(&ld->lock_cuts);
3581
3583
3585}
3586
3588{
3589 LineartData *ld = lmd->la_data_ptr;
3590
3592
3593 if (ld) {
3594 MEM_freeN(ld);
3595 lmd->la_data_ptr = nullptr;
3596 }
3597
3598 if (G.debug_value == 4000) {
3599 printf("LRT: Destroyed render data.\n");
3600 }
3601}
3602
3604{
3605 LineartCache *lc = MEM_callocN<LineartCache>("Lineart Cache");
3606 return lc;
3607}
3608
3610{
3611 if (!(*lc)) {
3612 return;
3613 }
3614 lineart_mem_destroy(&((*lc)->chain_data_pool));
3615 MEM_freeN(*lc);
3616 (*lc) = nullptr;
3617}
3618
3621 Object *camera,
3622 Object *active_camera,
3623 LineartCache *lc)
3624{
3625 LineartData *ld = MEM_callocN<LineartData>("Line Art render buffer");
3626 lmd->cache = lc;
3627 lmd->la_data_ptr = ld;
3629
3630 if (!scene || !camera || !lc) {
3631 return nullptr;
3632 }
3633 const Camera *c = static_cast<Camera *>(camera->data);
3634 double clipping_offset = 0;
3635
3637 /* This way the clipped lines are "stably visible" by prevents depth buffer artifacts. */
3638 clipping_offset = 0.0001;
3639 }
3640
3641 copy_v3db_v3fl(ld->conf.camera_pos, camera->object_to_world().location());
3642 if (active_camera) {
3643 copy_v3db_v3fl(ld->conf.active_camera_pos, active_camera->object_to_world().location());
3644 }
3645 copy_m4_m4(ld->conf.cam_obmat, camera->object_to_world().ptr());
3646 /* Make sure none of the scaling factor makes in, line art expects no scaling on cameras and
3647 * lights. */
3648 normalize_v3(ld->conf.cam_obmat[0]);
3649 normalize_v3(ld->conf.cam_obmat[1]);
3650 normalize_v3(ld->conf.cam_obmat[2]);
3651
3652 ld->conf.cam_is_persp = (c->type == CAM_PERSP);
3653 ld->conf.near_clip = c->clip_start + clipping_offset;
3654 ld->conf.far_clip = c->clip_end - clipping_offset;
3655 ld->w = scene->r.xsch;
3656 ld->h = scene->r.ysch;
3657
3658 if (ld->conf.cam_is_persp) {
3660 }
3661 else {
3663 }
3664
3665 double asp = double(ld->w) / double(ld->h);
3666 int fit = BKE_camera_sensor_fit(c->sensor_fit, ld->w, ld->h);
3667 ld->conf.shift_x = fit == CAMERA_SENSOR_FIT_HOR ? c->shiftx : c->shiftx / asp;
3668 ld->conf.shift_y = fit == CAMERA_SENSOR_FIT_VERT ? c->shifty : c->shifty * asp;
3669
3670 ld->conf.overscan = lmd->overscan;
3671
3672 ld->conf.shift_x /= (1 + ld->conf.overscan);
3673 ld->conf.shift_y /= (1 + ld->conf.overscan);
3674
3675 if (lmd->light_contour_object) {
3676 Object *light_obj = lmd->light_contour_object;
3677 copy_v3db_v3fl(ld->conf.camera_pos_secondary, light_obj->object_to_world().location());
3678 copy_m4_m4(ld->conf.cam_obmat_secondary, light_obj->object_to_world().ptr());
3679 /* Make sure none of the scaling factor makes in, line art expects no scaling on cameras and
3680 * lights. */
3685 if (light_obj->type == OB_LAMP) {
3686 ld->conf.cam_is_persp_secondary = ((Light *)light_obj->data)->type != LA_SUN;
3687 }
3688 }
3689
3694
3696 0;
3699 0;
3706
3707 /* See lineart_edge_from_triangle() for how this option may impact performance. */
3710
3713
3715 0;
3717
3720
3721 /* This is used to limit calculation to a certain level to save time, lines who have higher
3722 * occlusion levels will get ignored. */
3724
3725 int16_t edge_types = lmd->edge_types_override;
3726
3727 /* lmd->edge_types_override contains all used flags in the modifier stack. */
3728 ld->conf.use_contour = (edge_types & MOD_LINEART_EDGE_FLAG_CONTOUR) != 0;
3729 ld->conf.use_crease = (edge_types & MOD_LINEART_EDGE_FLAG_CREASE) != 0;
3730 ld->conf.use_material = (edge_types & MOD_LINEART_EDGE_FLAG_MATERIAL) != 0;
3731 ld->conf.use_edge_marks = (edge_types & MOD_LINEART_EDGE_FLAG_EDGE_MARK) != 0;
3733 ld->conf.use_loose = (edge_types & MOD_LINEART_EDGE_FLAG_LOOSE) != 0;
3734 ld->conf.use_light_contour = ((edge_types & MOD_LINEART_EDGE_FLAG_LIGHT_CONTOUR) != 0 &&
3735 (lmd->light_contour_object != nullptr));
3736 ld->conf.use_shadow = ((edge_types & MOD_LINEART_EDGE_FLAG_PROJECTED_SHADOW) != 0 &&
3737 (lmd->light_contour_object != nullptr));
3738
3743
3745 0;
3746
3754
3756
3757 /* See #LineartData::edge_data_pool for explanation. */
3759
3763
3764 ld->thread_count = BKE_render_num_threads(&scene->r);
3765
3766 return ld;
3767}
3768
3770{
3771 return sizeof(LineartTriangle) + (sizeof(LineartEdge *) * (ld->thread_count));
3772}
3773
3775{
3776 /* Initial tile split is defined as 4 (subdivided as 4*4), increasing the value allows the
3777 * algorithm to build the acceleration structure for bigger scenes a little faster but not as
3778 * efficient at handling medium to small scenes. */
3779 int sp_w = LRT_BA_ROWS;
3780 int sp_h = LRT_BA_ROWS;
3781 int row, col;
3783
3784 /* Always make sure the shortest side has at least LRT_BA_ROWS tiles. */
3785 if (ld->w > ld->h) {
3786 sp_w = sp_h * ld->w / ld->h;
3787 }
3788 else {
3789 sp_h = sp_w * ld->h / ld->w;
3790 }
3791
3792 /* Because NDC (Normalized Device Coordinates) range is (-1,1),
3793 * so the span for each initial tile is double of that in the (0,1) range. */
3794 double span_w = 1.0 / sp_w * 2.0;
3795 double span_h = 1.0 / sp_h * 2.0;
3796
3797 ld->qtree.count_x = sp_w;
3798 ld->qtree.count_y = sp_h;
3799 ld->qtree.tile_width = span_w;
3800 ld->qtree.tile_height = span_h;
3801
3802 ld->qtree.initial_tile_count = sp_w * sp_h;
3805 for (int i = 0; i < ld->qtree.initial_tile_count; i++) {
3807 }
3808
3809 /* Initialize tiles. */
3810 for (row = 0; row < sp_h; row++) {
3811 for (col = 0; col < sp_w; col++) {
3812 ba = &ld->qtree.initials[row * ld->qtree.count_x + col];
3813
3814 /* Set the four direction limits. */
3815 ba->l = span_w * col - 1.0;
3816 ba->r = (col == sp_w - 1) ? 1.0 : (span_w * (col + 1) - 1.0);
3817 ba->u = 1.0 - span_h * row;
3818 ba->b = (row == sp_h - 1) ? -1.0 : (1.0 - span_h * (row + 1));
3819
3820 ba->cx = (ba->l + ba->r) / 2;
3821 ba->cy = (ba->u + ba->b) / 2;
3822
3823 /* Init linked_triangles array. */
3827 "ba_linked_triangles");
3828 ba->linked_lines = MEM_calloc_arrayN<LineartEdge *>(ba->max_line_count, "ba_linked_lines");
3829
3830 BLI_spin_init(&ba->lock);
3831 }
3832 }
3833}
3834
3839{
3840 LineartBoundingArea *ba = root->child, *tba;
3841 LinkData *lip2, *next_lip;
3843
3844 /* Inter-connection with newly created 4 child bounding areas. */
3845 lineart_list_append_pointer_pool(&ba[1].rp, mph, &ba[0]);
3846 lineart_list_append_pointer_pool(&ba[0].lp, mph, &ba[1]);
3847 lineart_list_append_pointer_pool(&ba[1].bp, mph, &ba[2]);
3848 lineart_list_append_pointer_pool(&ba[2].up, mph, &ba[1]);
3849 lineart_list_append_pointer_pool(&ba[2].rp, mph, &ba[3]);
3850 lineart_list_append_pointer_pool(&ba[3].lp, mph, &ba[2]);
3851 lineart_list_append_pointer_pool(&ba[3].up, mph, &ba[0]);
3852 lineart_list_append_pointer_pool(&ba[0].bp, mph, &ba[3]);
3853
3854 /* Connect 4 child bounding areas to other areas that are
3855 * adjacent to their original parents. */
3856 LISTBASE_FOREACH (LinkData *, lip, &root->lp) {
3857
3858 /* For example, we are dealing with parent's left side
3859 * "tba" represents each adjacent neighbor of the parent. */
3860 tba = static_cast<LineartBoundingArea *>(lip->data);
3861
3862 /* if this neighbor is adjacent to
3863 * the two new areas on the left side of the parent,
3864 * then add them to the adjacent list as well. */
3865 if (ba[1].u > tba->b && ba[1].b < tba->u) {
3866 lineart_list_append_pointer_pool(&ba[1].lp, mph, tba);
3867 lineart_list_append_pointer_pool(&tba->rp, mph, &ba[1]);
3868 }
3869 if (ba[2].u > tba->b && ba[2].b < tba->u) {
3870 lineart_list_append_pointer_pool(&ba[2].lp, mph, tba);
3871 lineart_list_append_pointer_pool(&tba->rp, mph, &ba[2]);
3872 }
3873 }
3874 LISTBASE_FOREACH (LinkData *, lip, &root->rp) {
3875 tba = static_cast<LineartBoundingArea *>(lip->data);
3876 if (ba[0].u > tba->b && ba[0].b < tba->u) {
3877 lineart_list_append_pointer_pool(&ba[0].rp, mph, tba);
3878 lineart_list_append_pointer_pool(&tba->lp, mph, &ba[0]);
3879 }
3880 if (ba[3].u > tba->b && ba[3].b < tba->u) {
3881 lineart_list_append_pointer_pool(&ba[3].rp, mph, tba);
3882 lineart_list_append_pointer_pool(&tba->lp, mph, &ba[3]);
3883 }
3884 }
3885 LISTBASE_FOREACH (LinkData *, lip, &root->up) {
3886 tba = static_cast<LineartBoundingArea *>(lip->data);
3887 if (ba[0].r > tba->l && ba[0].l < tba->r) {
3888 lineart_list_append_pointer_pool(&ba[0].up, mph, tba);
3889 lineart_list_append_pointer_pool(&tba->bp, mph, &ba[0]);
3890 }
3891 if (ba[1].r > tba->l && ba[1].l < tba->r) {
3892 lineart_list_append_pointer_pool(&ba[1].up, mph, tba);
3893 lineart_list_append_pointer_pool(&tba->bp, mph, &ba[1]);
3894 }
3895 }
3896 LISTBASE_FOREACH (LinkData *, lip, &root->bp) {
3897 tba = static_cast<LineartBoundingArea *>(lip->data);
3898 if (ba[2].r > tba->l && ba[2].l < tba->r) {
3899 lineart_list_append_pointer_pool(&ba[2].bp, mph, tba);
3900 lineart_list_append_pointer_pool(&tba->up, mph, &ba[2]);
3901 }
3902 if (ba[3].r > tba->l && ba[3].l < tba->r) {
3903 lineart_list_append_pointer_pool(&ba[3].bp, mph, tba);
3904 lineart_list_append_pointer_pool(&tba->up, mph, &ba[3]);
3905 }
3906 }
3907
3908 /* Then remove the parent bounding areas from
3909 * their original adjacent areas. */
3910 LISTBASE_FOREACH (LinkData *, lip, &root->lp) {
3911 for (lip2 = static_cast<LinkData *>(((LineartBoundingArea *)lip->data)->rp.first); lip2;
3912 lip2 = next_lip)
3913 {
3914 next_lip = lip2->next;
3915 tba = static_cast<LineartBoundingArea *>(lip2->data);
3916 if (tba == root) {
3918 if (ba[1].u > tba->b && ba[1].b < tba->u) {
3919 lineart_list_append_pointer_pool(&tba->rp, mph, &ba[1]);
3920 }
3921 if (ba[2].u > tba->b && ba[2].b < tba->u) {
3922 lineart_list_append_pointer_pool(&tba->rp, mph, &ba[2]);
3923 }
3924 }
3925 }
3926 }
3927 LISTBASE_FOREACH (LinkData *, lip, &root->rp) {
3928 for (lip2 = static_cast<LinkData *>(((LineartBoundingArea *)lip->data)->lp.first); lip2;
3929 lip2 = next_lip)
3930 {
3931 next_lip = lip2->next;
3932 tba = static_cast<LineartBoundingArea *>(lip2->data);
3933 if (tba == root) {
3935 if (ba[0].u > tba->b && ba[0].b < tba->u) {
3936 lineart_list_append_pointer_pool(&tba->lp, mph, &ba[0]);
3937 }
3938 if (ba[3].u > tba->b && ba[3].b < tba->u) {
3939 lineart_list_append_pointer_pool(&tba->lp, mph, &ba[3]);
3940 }
3941 }
3942 }
3943 }
3944 LISTBASE_FOREACH (LinkData *, lip, &root->up) {
3945 for (lip2 = static_cast<LinkData *>(((LineartBoundingArea *)lip->data)->bp.first); lip2;
3946 lip2 = next_lip)
3947 {
3948 next_lip = lip2->next;
3949 tba = static_cast<LineartBoundingArea *>(lip2->data);
3950 if (tba == root) {
3952 if (ba[0].r > tba->l && ba[0].l < tba->r) {
3953 lineart_list_append_pointer_pool(&tba->up, mph, &ba[0]);
3954 }
3955 if (ba[1].r > tba->l && ba[1].l < tba->r) {
3956 lineart_list_append_pointer_pool(&tba->up, mph, &ba[1]);
3957 }
3958 }
3959 }
3960 }
3961 LISTBASE_FOREACH (LinkData *, lip, &root->bp) {
3962 for (lip2 = static_cast<LinkData *>(((LineartBoundingArea *)lip->data)->up.first); lip2;
3963 lip2 = next_lip)
3964 {
3965 next_lip = lip2->next;
3966 tba = static_cast<LineartBoundingArea *>(lip2->data);
3967 if (tba == root) {
3969 if (ba[2].r > tba->l && ba[2].l < tba->r) {
3970 lineart_list_append_pointer_pool(&tba->bp, mph, &ba[2]);
3971 }
3972 if (ba[3].r > tba->l && ba[3].l < tba->r) {
3973 lineart_list_append_pointer_pool(&tba->bp, mph, &ba[3]);
3974 }
3975 }
3976 }
3977 }
3978
3979 /* Finally clear parent's adjacent list. */
3980 BLI_listbase_clear(&root->lp);
3981 BLI_listbase_clear(&root->rp);
3982 BLI_listbase_clear(&root->up);
3983 BLI_listbase_clear(&root->bp);
3984}
3985
3987{
3988 if (root->child) {
3990 for (int i = 0; i < 4; i++) {
3992 }
3993 }
3994}
3995
3997{
3998 int total_tile_initial = ld->qtree.count_x * ld->qtree.count_y;
3999 int tiles_per_row = ld->qtree.count_x;
4000
4001 for (int row = 0; row < ld->qtree.count_y; row++) {
4002 for (int col = 0; col < ld->qtree.count_x; col++) {
4003 LineartBoundingArea *ba = &ld->qtree.initials[row * tiles_per_row + col];
4004 /* Link adjacent ones. */
4005 if (row) {
4007 &ba->up, &ld->render_data_pool, &ld->qtree.initials[(row - 1) * tiles_per_row + col]);
4008 }
4009 if (col) {
4011 &ba->lp, &ld->render_data_pool, &ld->qtree.initials[row * tiles_per_row + col - 1]);
4012 }
4013 if (row != ld->qtree.count_y - 1) {
4015 &ba->bp, &ld->render_data_pool, &ld->qtree.initials[(row + 1) * tiles_per_row + col]);
4016 }
4017 if (col != ld->qtree.count_x - 1) {
4019 &ba->rp, &ld->render_data_pool, &ld->qtree.initials[row * tiles_per_row + col + 1]);
4020 }
4021 }
4022 }
4023 for (int i = 0; i < total_tile_initial; i++) {
4025 }
4026}
4027
4033 LineartBoundingArea *root,
4034 int recursive_level)
4035{
4036 LineartBoundingArea *ba = static_cast<LineartBoundingArea *>(
4038 ba[0].l = root->cx;
4039 ba[0].r = root->r;
4040 ba[0].u = root->u;
4041 ba[0].b = root->cy;
4042 ba[0].cx = (ba[0].l + ba[0].r) / 2;
4043 ba[0].cy = (ba[0].u + ba[0].b) / 2;
4044
4045 ba[1].l = root->l;
4046 ba[1].r = root->cx;
4047 ba[1].u = root->u;
4048 ba[1].b = root->cy;
4049 ba[1].cx = (ba[1].l + ba[1].r) / 2;
4050 ba[1].cy = (ba[1].u + ba[1].b) / 2;
4051
4052 ba[2].l = root->l;
4053 ba[2].r = root->cx;
4054 ba[2].u = root->cy;
4055 ba[2].b = root->b;
4056 ba[2].cx = (ba[2].l + ba[2].r) / 2;
4057 ba[2].cy = (ba[2].u + ba[2].b) / 2;
4058
4059 ba[3].l = root->cx;
4060 ba[3].r = root->r;
4061 ba[3].u = root->cy;
4062 ba[3].b = root->b;
4063 ba[3].cx = (ba[3].l + ba[3].r) / 2;
4064 ba[3].cy = (ba[3].u + ba[3].b) / 2;
4065
4066 /* Init linked_triangles array and locks. */
4067 for (int i = 0; i < 4; i++) {
4070 ba[i].linked_triangles = MEM_calloc_arrayN<LineartTriangle *>(ba[i].max_triangle_count,
4071 "ba_linked_triangles");
4072 ba[i].linked_lines = MEM_calloc_arrayN<LineartEdge *>(ba[i].max_line_count, "ba_linked_lines");
4073 BLI_spin_init(&ba[i].lock);
4074 }
4075
4076 for (uint32_t i = 0; i < root->triangle_count; i++) {
4077 LineartTriangle *tri = root->linked_triangles[i];
4078
4079 double b[4];
4080 b[0] = std::min({tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]});
4081 b[1] = std::max({tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]});
4082 b[2] = std::max({tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]});
4083 b[3] = std::min({tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]});
4084
4085 /* Re-link triangles into child tiles, not doing intersection lines during this because this
4086 * batch of triangles are all tested with each other for intersections. */
4087 if (LRT_BOUND_AREA_CROSSES(b, &ba[0].l)) {
4089 ld, &ba[0], tri, b, 0, recursive_level + 1, false, nullptr);
4090 }
4091 if (LRT_BOUND_AREA_CROSSES(b, &ba[1].l)) {
4093 ld, &ba[1], tri, b, 0, recursive_level + 1, false, nullptr);
4094 }
4095 if (LRT_BOUND_AREA_CROSSES(b, &ba[2].l)) {
4097 ld, &ba[2], tri, b, 0, recursive_level + 1, false, nullptr);
4098 }
4099 if (LRT_BOUND_AREA_CROSSES(b, &ba[3].l)) {
4101 ld, &ba[3], tri, b, 0, recursive_level + 1, false, nullptr);
4102 }
4103 }
4104
4105 /* At this point the child tiles are fully initialized and it's safe for new triangles to be
4106 * inserted, so assign root->child for #lineart_bounding_area_link_triangle to use. */
4107 root->child = ba;
4108}
4109
4111 const double l[2],
4112 const double r[2],
4114{
4115 double dx, dy;
4116 double converted[4];
4117 double c1, c;
4118
4119 if (((converted[0] = ba->l) > std::max(l[0], r[0])) ||
4120 ((converted[1] = ba->r) < std::min(l[0], r[0])) ||
4121 ((converted[2] = ba->b) > std::max(l[1], r[1])) ||
4122 ((converted[3] = ba->u) < std::min(l[1], r[1])))
4123 {
4124 return false;
4125 }
4126
4127 dx = l[0] - r[0];
4128 dy = l[1] - r[1];
4129
4130 c1 = dx * (converted[2] - l[1]) - dy * (converted[0] - l[0]);
4131 c = c1;
4132
4133 c1 = dx * (converted[2] - l[1]) - dy * (converted[1] - l[0]);
4134 if (c1 * c <= 0) {
4135 return true;
4136 }
4137 c = c1;
4138
4139 c1 = dx * (converted[3] - l[1]) - dy * (converted[0] - l[0]);
4140 if (c1 * c <= 0) {
4141 return true;
4142 }
4143 c = c1;
4144
4145 c1 = dx * (converted[3] - l[1]) - dy * (converted[1] - l[0]);
4146 if (c1 * c <= 0) {
4147 return true;
4148 }
4149 c = c1;
4150
4151 return false;
4152}
4153
4155 LineartTriangle *tri,
4157 bool *r_triangle_vert_inside)
4158{
4159 double p1[2], p2[2], p3[2], p4[2];
4160 double *FBC1 = tri->v[0]->fbcoord, *FBC2 = tri->v[1]->fbcoord, *FBC3 = tri->v[2]->fbcoord;
4161
4162 p3[0] = p1[0] = ba->l;
4163 p2[1] = p1[1] = ba->b;
4164 p2[0] = p4[0] = ba->r;
4165 p3[1] = p4[1] = ba->u;
4166
4167 if ((FBC1[0] >= p1[0] && FBC1[0] <= p2[0] && FBC1[1] >= p1[1] && FBC1[1] <= p3[1]) ||
4168 (FBC2[0] >= p1[0] && FBC2[0] <= p2[0] && FBC2[1] >= p1[1] && FBC2[1] <= p3[1]) ||
4169 (FBC3[0] >= p1[0] && FBC3[0] <= p2[0] && FBC3[1] >= p1[1] && FBC3[1] <= p3[1]))
4170 {
4171 *r_triangle_vert_inside = true;
4172 return true;
4173 }
4174
4175 *r_triangle_vert_inside = false;
4176
4177 if (lineart_point_inside_triangle(p1, FBC1, FBC2, FBC3) ||
4178 lineart_point_inside_triangle(p2, FBC1, FBC2, FBC3) ||
4179 lineart_point_inside_triangle(p3, FBC1, FBC2, FBC3) ||
4180 lineart_point_inside_triangle(p4, FBC1, FBC2, FBC3))
4181 {
4182 return true;
4183 }
4184
4185 if (lineart_bounding_area_edge_intersect(fb, FBC1, FBC2, ba) ||
4186 lineart_bounding_area_edge_intersect(fb, FBC2, FBC3, ba) ||
4188 {
4189 return true;
4190 }
4191
4192 return false;
4193}
4194
4208 LineartBoundingArea *root_ba,
4209 LineartTriangle *tri,
4210 double l_r_u_b[4],
4211 int recursive,
4212 int recursive_level,
4213 bool do_intersection,
4215{
4216 bool triangle_vert_inside;
4217 if (!lineart_bounding_area_triangle_intersect(ld, tri, root_ba, &triangle_vert_inside)) {
4218 return;
4219 }
4220
4221 LineartBoundingArea *old_ba = root_ba;
4222
4223 if (old_ba->child) {
4224 /* If old_ba->child is not nullptr, then tile splitting is fully finished, safe to directly
4225 * insert into child tiles. */
4226 double *B1 = l_r_u_b;
4227 double b[4];
4228 if (!l_r_u_b) {
4229 b[0] = std::min({tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]});
4230 b[1] = std::max({tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]});
4231 b[2] = std::max({tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]});
4232 b[3] = std::min({tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]});
4233 B1 = b;
4234 }
4235 for (int iba = 0; iba < 4; iba++) {
4236 if (LRT_BOUND_AREA_CROSSES(B1, &old_ba->child[iba].l)) {
4238 ld, &old_ba->child[iba], tri, B1, recursive, recursive_level + 1, do_intersection, th);
4239 }
4240 }
4241 return;
4242 }
4243
4244 /* When splitting tiles, triangles are relinked into new tiles by a single thread, #th is nullptr
4245 * in that situation. */
4246 if (th) {
4247 BLI_spin_lock(&old_ba->lock);
4248 }
4249
4250 /* If there are still space left in this tile for insertion. */
4251 if (old_ba->triangle_count < old_ba->max_triangle_count) {
4252 const uint32_t old_tri_count = old_ba->triangle_count;
4253
4254 old_ba->linked_triangles[old_tri_count] = tri;
4255
4256 if (triangle_vert_inside) {
4257 old_ba->insider_triangle_count++;
4258 }
4259 old_ba->triangle_count++;
4260
4261 /* Do intersections in place. */
4262 if (do_intersection && ld->conf.use_intersections) {
4263 lineart_triangle_intersect_in_bounding_area(tri, old_ba, th, old_tri_count);
4264 }
4265
4266 if (th) {
4267 BLI_spin_unlock(&old_ba->lock);
4268 }
4269 }
4270 else { /* We need to wait for either splitting or array extension to be done. */
4271
4272 if (recursive_level < ld->qtree.recursive_level &&
4274 {
4275 if (!old_ba->child) {
4276 /* old_ba->child==nullptr, means we are the thread that's doing the splitting. */
4277 lineart_bounding_area_split(ld, old_ba, recursive_level);
4278 } /* Otherwise other thread has completed the splitting process. */
4279 }
4280 else {
4281 if (old_ba->triangle_count == old_ba->max_triangle_count) {
4282 /* Means we are the thread that's doing the extension. */
4284 } /* Otherwise other thread has completed the extending the array. */
4285 }
4286
4287 /* Unlock before going into recursive call. */
4288 if (th) {
4289 BLI_spin_unlock(&old_ba->lock);
4290 }
4291
4292 /* Of course we still have our own triangle needs to be added. */
4294 ld, root_ba, tri, l_r_u_b, recursive, recursive_level, do_intersection, th);
4295 }
4296}
4297
4299{
4300 BLI_spin_end(&ba->lock);
4301 if (ba->linked_lines) {
4303 }
4304 if (ba->linked_triangles) {
4306 }
4307 if (recursive && ba->child) {
4308 for (int i = 0; i < 4; i++) {
4309 lineart_free_bounding_area_memory(&ba->child[i], recursive);
4310 }
4311 }
4312}
4314{
4315 for (int i = 0; i < ld->qtree.count_y; i++) {
4316 for (int j = 0; j < ld->qtree.count_x; j++) {
4318 }
4319 }
4320}
4321
4323 LineartBoundingArea *root_ba,
4324 LineartEdge *e)
4325{
4326 if (root_ba->child == nullptr) {
4328 }
4329 else {
4331 ld, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[0]))
4332 {
4333 lineart_bounding_area_link_edge(ld, &root_ba->child[0], e);
4334 }
4336 ld, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[1]))
4337 {
4338 lineart_bounding_area_link_edge(ld, &root_ba->child[1], e);
4339 }
4341 ld, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[2]))
4342 {
4343 lineart_bounding_area_link_edge(ld, &root_ba->child[2], e);
4344 }
4346 ld, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[3]))
4347 {
4348 lineart_bounding_area_link_edge(ld, &root_ba->child[3], e);
4349 }
4350 }
4351}
4352
4354{
4355 if (root_ba->child) {
4356 for (int i = 0; i < 4; i++) {
4358 }
4359 }
4360 if (root_ba->linked_lines) {
4361 MEM_freeN(root_ba->linked_lines);
4362 }
4363 root_ba->line_count = 0;
4364 root_ba->max_line_count = 128;
4366 "cleared lineart edges");
4367}
4369{
4371 for (int i = 0; i < ld->qtree.count_y; i++) {
4372 for (int j = 0; j < ld->qtree.count_x; j++) {
4374 }
4375 }
4376}
4377
4379{
4381 {
4382 int r1, r2, c1, c2, row, col;
4383 if (lineart_get_edge_bounding_areas(ld, e, &r1, &r2, &c1, &c2)) {
4384 for (row = r1; row != r2 + 1; row++) {
4385 for (col = c1; col != c2 + 1; col++) {
4387 ld, &ld->qtree.initials[row * ld->qtree.count_x + col], e);
4388 }
4389 }
4390 }
4391 }
4393}
4394
4396 uint8_t max_occlusion)
4397{
4398 if (ba->child) {
4399 for (int i = 0; i < 4; i++) {
4401 }
4402 return;
4403 }
4404
4405 if (!ba->line_count) {
4406 return;
4407 }
4408
4409 int usable_count = 0;
4410 for (int i = 0; i < ba->line_count; i++) {
4411 LineartEdge *e = ba->linked_lines[i];
4412 if (e->min_occ > max_occlusion) {
4413 continue;
4414 }
4415 usable_count++;
4416 }
4417
4418 if (!usable_count) {
4419 ba->line_count = 0;
4420 return;
4421 }
4422
4423 LineartEdge **new_array = MEM_calloc_arrayN<LineartEdge *>(usable_count,
4424 "cleaned lineart edge array");
4425
4426 int new_i = 0;
4427 for (int i = 0; i < ba->line_count; i++) {
4428 LineartEdge *e = ba->linked_lines[i];
4429 if (e->min_occ > max_occlusion) {
4430 continue;
4431 }
4432 new_array[new_i] = e;
4433 new_i++;
4434 }
4435
4437 ba->linked_lines = new_array;
4438 ba->max_line_count = ba->line_count = usable_count;
4439}
4440
4442{
4443 for (int row = 0; row < ld->qtree.count_y; row++) {
4444 for (int col = 0; col < ld->qtree.count_x; col++) {
4446 &ld->qtree.initials[row * ld->qtree.count_x + col], ld->conf.max_occlusion_level);
4447 }
4448 }
4449}
4450
4452 LineartData *ld, LineartTriangle *tri, int *rowbegin, int *rowend, int *colbegin, int *colend)
4453{
4454 double sp_w = ld->qtree.tile_width, sp_h = ld->qtree.tile_height;
4455 double b[4];
4456
4457 if (!tri->v[0] || !tri->v[1] || !tri->v[2]) {
4458 return false;
4459 }
4460
4461 b[0] = std::min({tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]});
4462 b[1] = std::max({tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]});
4463 b[2] = std::min({tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]});
4464 b[3] = std::max({tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]});
4465
4466 if (b[0] > 1 || b[1] < -1 || b[2] > 1 || b[3] < -1) {
4467 return false;
4468 }
4469
4470 (*colbegin) = int((b[0] + 1.0) / sp_w);
4471 (*colend) = int((b[1] + 1.0) / sp_w);
4472 (*rowend) = ld->qtree.count_y - int((b[2] + 1.0) / sp_h) - 1;
4473 (*rowbegin) = ld->qtree.count_y - int((b[3] + 1.0) / sp_h) - 1;
4474
4475 if ((*colend) >= ld->qtree.count_x) {
4476 (*colend) = ld->qtree.count_x - 1;
4477 }
4478 if ((*rowend) >= ld->qtree.count_y) {
4479 (*rowend) = ld->qtree.count_y - 1;
4480 }
4481 *colbegin = std::max(*colbegin, 0);
4482 *rowbegin = std::max(*rowbegin, 0);
4483
4484 return true;
4485}
4486
4488 LineartData *ld, LineartEdge *e, int *rowbegin, int *rowend, int *colbegin, int *colend)
4489{
4490 double sp_w = ld->qtree.tile_width, sp_h = ld->qtree.tile_height;
4491 double b[4];
4492
4493 if (!e->v1 || !e->v2) {
4494 return false;
4495 }
4496
4497 if (e->v1->fbcoord[0] != e->v1->fbcoord[0] || e->v2->fbcoord[0] != e->v2->fbcoord[0]) {
4498 return false;
4499 }
4500
4501 b[0] = std::min(e->v1->fbcoord[0], e->v2->fbcoord[0]);
4502 b[1] = std::max(e->v1->fbcoord[0], e->v2->fbcoord[0]);
4503 b[2] = std::min(e->v1->fbcoord[1], e->v2->fbcoord[1]);
4504 b[3] = std::max(e->v1->fbcoord[1], e->v2->fbcoord[1]);
4505
4506 if (b[0] > 1 || b[1] < -1 || b[2] > 1 || b[3] < -1) {
4507 return false;
4508 }
4509
4510 (*colbegin) = int((b[0] + 1.0) / sp_w);
4511 (*colend) = int((b[1] + 1.0) / sp_w);
4512 (*rowend) = ld->qtree.count_y - int((b[2] + 1.0) / sp_h) - 1;
4513 (*rowbegin) = ld->qtree.count_y - int((b[3] + 1.0) / sp_h) - 1;
4514
4515 /* It's possible that the line stretches too much out to the side, resulting negative value. */
4516 if ((*rowend) < (*rowbegin)) {
4517 (*rowend) = ld->qtree.count_y - 1;
4518 }
4519
4520 if ((*colend) < (*colbegin)) {
4521 (*colend) = ld->qtree.count_x - 1;
4522 }
4523
4524 CLAMP((*colbegin), 0, ld->qtree.count_x - 1);
4525 CLAMP((*rowbegin), 0, ld->qtree.count_y - 1);
4526 CLAMP((*colend), 0, ld->qtree.count_x - 1);
4527 CLAMP((*rowend), 0, ld->qtree.count_y - 1);
4528
4529 return true;
4530}
4531
4533{
4534 double sp_w = ld->qtree.tile_width, sp_h = ld->qtree.tile_height;
4535 int col, row;
4536
4537 if (x > 1 || x < -1 || y > 1 || y < -1) {
4538 return nullptr;
4539 }
4540
4541 col = int((x + 1.0) / sp_w);
4542 row = ld->qtree.count_y - int((y + 1.0) / sp_h) - 1;
4543
4544 if (col >= ld->qtree.count_x) {
4545 col = ld->qtree.count_x - 1;
4546 }
4547 if (row >= ld->qtree.count_y) {
4548 row = ld->qtree.count_y - 1;
4549 }
4550 col = std::max(col, 0);
4551 row = std::max(row, 0);
4552
4553 return &ld->qtree.initials[row * ld->qtree.count_x + col];
4554}
4555
4557{
4559 double sp_w = ld->qtree.tile_width, sp_h = ld->qtree.tile_height;
4560 int c = int((x + 1.0) / sp_w);
4561 int r = ld->qtree.count_y - int((y + 1.0) / sp_h) - 1;
4562 r = std::max(r, 0);
4563 c = std::max(c, 0);
4564 if (r >= ld->qtree.count_y) {
4565 r = ld->qtree.count_y - 1;
4566 }
4567 if (c >= ld->qtree.count_x) {
4568 c = ld->qtree.count_x - 1;
4569 }
4570
4571 iba = &ld->qtree.initials[r * ld->qtree.count_x + c];
4572 while (iba->child) {
4573 if (x > iba->cx) {
4574 if (y > iba->cy) {
4575 iba = &iba->child[0];
4576 }
4577 else {
4578 iba = &iba->child[3];
4579 }
4580 }
4581 else {
4582 if (y > iba->cy) {
4583 iba = &iba->child[1];
4584 }
4585 else {
4586 iba = &iba->child[2];
4587 }
4588 }
4589 }
4590 return iba;
4591}
4592
4594{
4596 if ((ba = MOD_lineart_get_parent_bounding_area(ld, x, y)) != nullptr) {
4597 return lineart_get_bounding_area(ld, x, y);
4598 }
4599 return nullptr;
4600}
4601
4602static void lineart_add_triangles_worker(TaskPool *__restrict /*pool*/, LineartIsecThread *th)
4603{
4604 LineartData *ld = th->ld;
4605 // int _dir_control = 0; /* UNUSED */
4607 for (LineartElementLinkNode *eln = th->pending_from; eln != th->pending_to->next;
4608 eln = eln->next)
4609 {
4610 int index_start = eln == th->pending_from ? th->index_from : 0;
4611 int index_end = eln == th->pending_to ? th->index_to : eln->element_count;
4612 LineartTriangle *tri = static_cast<LineartTriangle *>(
4613 (void *)(((uchar *)eln->pointer) + ld->sizeof_triangle * index_start));
4614 for (int ei = index_start; ei < index_end; ei++) {
4615 int x1, x2, y1, y2;
4616 int r, co;
4617 if ((tri->flags & LRT_CULL_USED) || (tri->flags & LRT_CULL_DISCARD)) {
4618 tri = static_cast<LineartTriangle *>((void *)(((uchar *)tri) + ld->sizeof_triangle));
4619 continue;
4620 }
4621 if (lineart_get_triangle_bounding_areas(ld, tri, &y1, &y2, &x1, &x2)) {
4622 // _dir_control++;
4623 for (co = x1; co <= x2; co++) {
4624 for (r = y1; r <= y2; r++) {
4626 &ld->qtree.initials[r * ld->qtree.count_x + co],
4627 tri,
4628 nullptr,
4629 1,
4630 0,
4631 true,
4632 th);
4633 }
4634 }
4635 } /* Else throw away. */
4636 tri = static_cast<LineartTriangle *>((void *)(((uchar *)tri) + ld->sizeof_triangle));
4637 }
4638 }
4639 }
4640}
4641
4643{
4644 LineartData *ld = d->ld;
4645 double ZMax = ld->conf.far_clip;
4646 double ZMin = ld->conf.near_clip;
4647 int total_lines = 0;
4648
4649 for (int i = 0; i < d->thread_count; i++) {
4650 LineartIsecThread *th = &d->threads[i];
4651 if (G.debug_value == 4000) {
4652 printf("Thread %d isec generated %d lines.\n", i, th->current);
4653 }
4654 if (!th->current) {
4655 continue;
4656 }
4657 total_lines += th->current;
4658 }
4659
4660 if (!total_lines) {
4661 return;
4662 }
4663
4664 /* We don't care about removing duplicated vert in this method, chaining can handle that,
4665 * and it saves us from using locks and look up tables. */
4666 LineartVert *v = static_cast<LineartVert *>(
4667 lineart_mem_acquire(ld->edge_data_pool, sizeof(LineartVert) * total_lines * 2));
4668 LineartEdge *e = static_cast<LineartEdge *>(
4669 lineart_mem_acquire(ld->edge_data_pool, sizeof(LineartEdge) * total_lines));
4670 LineartEdgeSegment *es = static_cast<LineartEdgeSegment *>(
4671 lineart_mem_acquire(ld->edge_data_pool, sizeof(LineartEdgeSegment) * total_lines));
4672
4673 LineartElementLinkNode *eln = static_cast<LineartElementLinkNode *>(
4675 eln->element_count = total_lines;
4676 eln->pointer = e;
4679
4680 for (int i = 0; i < d->thread_count; i++) {
4681 LineartIsecThread *th = &d->threads[i];
4682 if (!th->current) {
4683 continue;
4684 }
4685
4686 for (int j = 0; j < th->current; j++) {
4687 LineartIsecSingle *is = &th->array[j];
4688 LineartVert *v1 = v;
4689 LineartVert *v2 = v + 1;
4690 copy_v3_v3_db(v1->gloc, is->v1);
4691 copy_v3_v3_db(v2->gloc, is->v2);
4692 /* The intersection line has been generated only in geometry space, so we need to transform
4693 * them as well. */
4695 mul_v4_m4v3_db(v2->fbcoord, ld->conf.view_projection, v2->gloc);
4696 mul_v3db_db(v1->fbcoord, (1 / v1->fbcoord[3]));
4697 mul_v3db_db(v2->fbcoord, (1 / v2->fbcoord[3]));
4698
4699 v1->fbcoord[0] -= ld->conf.shift_x * 2;
4700 v1->fbcoord[1] -= ld->conf.shift_y * 2;
4701 v2->fbcoord[0] -= ld->conf.shift_x * 2;
4702 v2->fbcoord[1] -= ld->conf.shift_y * 2;
4703
4704 /* This z transformation is not the same as the rest of the part, because the data don't go
4705 * through normal perspective division calls in the pipeline, but this way the 3D result and
4706 * occlusion on the generated line is correct, and we don't really use 2D for viewport stroke
4707 * generation anyway. */
4708 v1->fbcoord[2] = ZMin * ZMax / (ZMax - fabs(v1->fbcoord[2]) * (ZMax - ZMin));
4709 v2->fbcoord[2] = ZMin * ZMax / (ZMax - fabs(v2->fbcoord[2]) * (ZMax - ZMin));
4710 e->v1 = v1;
4711 e->v2 = v2;
4712 e->t1 = is->tri1;
4713 e->t2 = is->tri2;
4714 /* This is so we can also match intersection edges from shadow to later viewing stage. */
4715 e->edge_identifier = (uint64_t(e->t1->target_reference) << 32) | e->t2->target_reference;
4717 e->intersection_mask = (is->tri1->intersection_mask | is->tri2->intersection_mask);
4718 BLI_addtail(&e->segments, es);
4719
4720 int obi1 = (e->t1->target_reference & LRT_OBINDEX_HIGHER);
4721 int obi2 = (e->t2->target_reference & LRT_OBINDEX_HIGHER);
4723 obi1);
4724 LineartElementLinkNode *eln2 = obi1 == obi2 ? eln1 :
4726 &ld->geom.line_buffer_pointers, obi2);
4727 Object *ob1 = eln1 ? static_cast<Object *>(eln1->object_ref) : nullptr;
4728 Object *ob2 = eln2 ? static_cast<Object *>(eln2->object_ref) : nullptr;
4729 if (e->t1->intersection_priority >= e->t2->intersection_priority) {
4730 /* `object_ref` should be ambiguous if intersection lines comes from different objects with
4731 * the same priority. */
4732 e->object_ref = ob1;
4733 }
4734 else if (e->t1->intersection_priority < e->t2->intersection_priority) {
4735 e->object_ref = ob2;
4736 }
4737
4739
4740 v += 2;
4741 e++;
4742 es++;
4743 }
4744 }
4745}
4746
4748{
4749 double t_start;
4750 if (G.debug_value == 4000) {
4751 t_start = BLI_time_now_seconds();
4752 }
4753
4754 /* Initialize per-thread data for thread task scheduling information and storing intersection
4755 * results. */
4756 LineartIsecData d = {nullptr};
4758
4760 for (int i = 0; i < ld->thread_count; i++) {
4762 tp, (TaskRunFunction)lineart_add_triangles_worker, &d.threads[i], false, nullptr);
4763 }
4766
4767 if (ld->conf.use_intersections) {
4769 }
4770
4772
4773 if (G.debug_value == 4000) {
4774 double t_elapsed = BLI_time_now_seconds() - t_start;
4775 printf("Line art intersection time: %f\n", t_elapsed);
4776 }
4777}
4778
4780 double *fbcoord1,
4781 double *fbcoord2)
4782{
4783 double data[2] = {fbcoord1[0], fbcoord1[1]};
4784 double LU[2] = {-1, 1}, RU[2] = {1, 1}, LB[2] = {-1, -1}, RB[2] = {1, -1};
4785 double r = 1, sr = 1;
4786 bool p_unused;
4787
4788 if (data[0] > -1 && data[0] < 1 && data[1] > -1 && data[1] < 1) {
4789 return lineart_get_bounding_area(ld, data[0], data[1]);
4790 }
4791
4792 if (lineart_intersect_seg_seg(fbcoord1, fbcoord2, LU, RU, &sr, &p_unused) && sr < r && sr > 0) {
4793 r = sr;
4794 }
4795 if (lineart_intersect_seg_seg(fbcoord1, fbcoord2, LB, RB, &sr, &p_unused) && sr < r && sr > 0) {
4796 r = sr;
4797 }
4798 if (lineart_intersect_seg_seg(fbcoord1, fbcoord2, LB, LU, &sr, &p_unused) && sr < r && sr > 0) {
4799 r = sr;
4800 }
4801 if (lineart_intersect_seg_seg(fbcoord1, fbcoord2, RB, RU, &sr, &p_unused) && sr < r && sr > 0) {
4802 r = sr;
4803 }
4804 interp_v2_v2v2_db(data, fbcoord1, fbcoord2, r);
4805
4806 return lineart_get_bounding_area(ld, data[0], data[1]);
4807}
4808
4810 double *fbcoord1,
4811 double *fbcoord2,
4812 double x,
4813 double y,
4814 double k,
4815 int positive_x,
4816 int positive_y,
4817 double *next_x,
4818 double *next_y)
4819{
4820 double rx, ry, ux, uy, lx, ly, bx, by;
4821 double r1, r2;
4823
4824 /* If we are marching towards the right. */
4825 if (positive_x > 0) {
4826 rx = self->r;
4827 ry = y + k * (rx - x);
4828
4829 /* If we are marching towards the top. */
4830 if (positive_y > 0) {
4831 uy = self->u;
4832 ux = x + (uy - y) / k;
4833 r1 = ratiod(fbcoord1[0], fbcoord2[0], rx);
4834 r2 = ratiod(fbcoord1[0], fbcoord2[0], ux);
4835 if (std::min(r1, r2) > 1) {
4836 return nullptr;
4837 }
4838
4839 /* We reached the right side before the top side. */
4840 if (r1 <= r2) {
4841 LISTBASE_FOREACH (LinkData *, lip, &self->rp) {
4842 ba = static_cast<LineartBoundingArea *>(lip->data);
4843 if (ba->u >= ry && ba->b < ry) {
4844 *next_x = rx;
4845 *next_y = ry;
4846 return ba;
4847 }
4848 }
4849 }
4850 /* We reached the top side before the right side. */
4851 else {
4852 LISTBASE_FOREACH (LinkData *, lip, &self->up) {
4853 ba = static_cast<LineartBoundingArea *>(lip->data);
4854 if (ba->r >= ux && ba->l < ux) {
4855 *next_x = ux;
4856 *next_y = uy;
4857 return ba;
4858 }
4859 }
4860 }
4861 }
4862 /* If we are marching towards the bottom. */
4863 else if (positive_y < 0) {
4864 by = self->b;
4865 bx = x + (by - y) / k;
4866 r1 = ratiod(fbcoord1[0], fbcoord2[0], rx);
4867 r2 = ratiod(fbcoord1[0], fbcoord2[0], bx);
4868 if (std::min(r1, r2) > 1) {
4869 return nullptr;
4870 }
4871 if (r1 <= r2) {
4872 LISTBASE_FOREACH (LinkData *, lip, &self->rp) {
4873 ba = static_cast<LineartBoundingArea *>(lip->data);
4874 if (ba->u >= ry && ba->b < ry) {
4875 *next_x = rx;
4876 *next_y = ry;
4877 return ba;
4878 }
4879 }
4880 }
4881 else {
4882 LISTBASE_FOREACH (LinkData *, lip, &self->bp) {
4883 ba = static_cast<LineartBoundingArea *>(lip->data);
4884 if (ba->r >= bx && ba->l < bx) {
4885 *next_x = bx;
4886 *next_y = by;
4887 return ba;
4888 }
4889 }
4890 }
4891 }
4892 /* If the line is completely horizontal, in which Y difference == 0. */
4893 else {
4894 r1 = ratiod(fbcoord1[0], fbcoord2[0], self->r);
4895 if (r1 > 1) {
4896 return nullptr;
4897 }
4898 LISTBASE_FOREACH (LinkData *, lip, &self->rp) {
4899 ba = static_cast<LineartBoundingArea *>(lip->data);
4900 if (ba->u >= y && ba->b < y) {
4901 *next_x = self->r;
4902 *next_y = y;
4903 return ba;
4904 }
4905 }
4906 }
4907 }
4908
4909 /* If we are marching towards the left. */
4910 else if (positive_x < 0) {
4911 lx = self->l;
4912 ly = y + k * (lx - x);
4913
4914 /* If we are marching towards the top. */
4915 if (positive_y > 0) {
4916 uy = self->u;
4917 ux = x + (uy - y) / k;
4918 r1 = ratiod(fbcoord1[0], fbcoord2[0], lx);
4919 r2 = ratiod(fbcoord1[0], fbcoord2[0], ux);
4920 if (std::min(r1, r2) > 1) {
4921 return nullptr;
4922 }
4923 if (r1 <= r2) {
4924 LISTBASE_FOREACH (LinkData *, lip, &self->lp) {
4925 ba = static_cast<LineartBoundingArea *>(lip->data);
4926 if (ba->u >= ly && ba->b < ly) {
4927 *next_x = lx;
4928 *next_y = ly;
4929 return ba;
4930 }
4931 }
4932 }
4933 else {
4934 LISTBASE_FOREACH (LinkData *, lip, &self->up) {
4935 ba = static_cast<LineartBoundingArea *>(lip->data);
4936 if (ba->r >= ux && ba->l < ux) {
4937 *next_x = ux;
4938 *next_y = uy;
4939 return ba;
4940 }
4941 }
4942 }
4943 }
4944
4945 /* If we are marching towards the bottom. */
4946 else if (positive_y < 0) {
4947 by = self->b;
4948 bx = x + (by - y) / k;
4949 r1 = ratiod(fbcoord1[0], fbcoord2[0], lx);
4950 r2 = ratiod(fbcoord1[0], fbcoord2[0], bx);
4951 if (std::min(r1, r2) > 1) {
4952 return nullptr;
4953 }
4954 if (r1 <= r2) {
4955 LISTBASE_FOREACH (LinkData *, lip, &self->lp) {
4956 ba = static_cast<LineartBoundingArea *>(lip->data);
4957 if (ba->u >= ly && ba->b < ly) {
4958 *next_x = lx;
4959 *next_y = ly;
4960 return ba;
4961 }
4962 }
4963 }
4964 else {
4965 LISTBASE_FOREACH (LinkData *, lip, &self->bp) {
4966 ba = static_cast<LineartBoundingArea *>(lip->data);
4967 if (ba->r >= bx && ba->l < bx) {
4968 *next_x = bx;
4969 *next_y = by;
4970 return ba;
4971 }
4972 }
4973 }
4974 }
4975 /* Again, horizontal. */
4976 else {
4977 r1 = ratiod(fbcoord1[0], fbcoord2[0], self->l);
4978 if (r1 > 1) {
4979 return nullptr;
4980 }
4981 LISTBASE_FOREACH (LinkData *, lip, &self->lp) {
4982 ba = static_cast<LineartBoundingArea *>(lip->data);
4983 if (ba->u >= y && ba->b < y) {
4984 *next_x = self->l;
4985 *next_y = y;
4986 return ba;
4987 }
4988 }
4989 }
4990 }
4991 /* If the line is completely vertical, hence X difference == 0. */
4992 else {
4993 if (positive_y > 0) {
4994 r1 = ratiod(fbcoord1[1], fbcoord2[1], self->u);
4995 if (r1 > 1) {
4996 return nullptr;
4997 }
4998 LISTBASE_FOREACH (LinkData *, lip, &self->up) {
4999 ba = static_cast<LineartBoundingArea *>(lip->data);
5000 if (ba->r > x && ba->l <= x) {
5001 *next_x = x;
5002 *next_y = self->u;
5003 return ba;
5004 }
5005 }
5006 }
5007 else if (positive_y < 0) {
5008 r1 = ratiod(fbcoord1[1], fbcoord2[1], self->b);
5009 if (r1 > 1) {
5010 return nullptr;
5011 }
5012 LISTBASE_FOREACH (LinkData *, lip, &self->bp) {
5013 ba = static_cast<LineartBoundingArea *>(lip->data);
5014 if (ba->r > x && ba->l <= x) {
5015 *next_x = x;
5016 *next_y = self->b;
5017 return ba;
5018 }
5019 }
5020 }
5021 else {
5022 /* Segment has no length. */
5023 return nullptr;
5024 }
5025 }
5026 return nullptr;
5027}
5028
5031 LineartCache **cached_result,
5032 bool enable_stroke_depth_offset)
5033{
5034 LineartData *ld;
5036 int intersections_only = 0; /* Not used right now, but preserve for future. */
5037 Object *lineart_camera = nullptr;
5038
5039 double t_start;
5040 if (G.debug_value == 4000) {
5041 t_start = BLI_time_now_seconds();
5042 }
5043
5044 bool use_render_camera_override = false;
5046 if (!lmd.source_camera ||
5047 (lineart_camera = DEG_get_evaluated(depsgraph, lmd.source_camera))->type != OB_CAMERA)
5048 {
5049 return false;
5050 }
5051 }
5052 else {
5054 if (render && render->camera_override) {
5055 lineart_camera = DEG_get_evaluated(depsgraph, render->camera_override);
5056 use_render_camera_override = true;
5057 }
5058 if (!lineart_camera) {
5060 if (!scene->camera) {
5061 return false;
5062 }
5063 lineart_camera = scene->camera;
5064 }
5065 }
5066
5067 LineartCache *lc = *cached_result;
5068 if (!lc) {
5070 *cached_result = lc;
5071 }
5072
5074 &lmd,
5075 lineart_camera,
5076 use_render_camera_override ? lineart_camera : scene->camera,
5077 lc);
5078
5079 /* Triangle thread testing data size varies depending on the thread count.
5080 * See definition of LineartTriangleThread for details. */
5082
5083 LineartData *shadow_rb = nullptr;
5084 LineartElementLinkNode *shadow_veln, *shadow_eeln;
5085 ListBase *shadow_elns = ld->conf.shadow_selection ? &lc->shadow_elns : nullptr;
5086 bool shadow_generated = lineart_main_try_generate_shadow_v3(depsgraph,
5087 scene,
5088 ld,
5089 &lmd,
5090 &lc->shadow_data_pool,
5091 &shadow_veln,
5092 &shadow_eeln,
5093 shadow_elns,
5094 &shadow_rb);
5095
5096 /* Get view vector before loading geometries, because we detect feature lines there. */
5098
5099 LineartModifierRuntime *runtime = reinterpret_cast<LineartModifierRuntime *>(lmd.runtime);
5100 blender::Set<const Object *> *included_objects = runtime ? &runtime->object_dependencies :
5101 nullptr;
5102
5104 scene,
5105 lineart_camera,
5106 ld,
5108 false,
5109 shadow_elns,
5110 included_objects);
5111
5112 if (shadow_generated) {
5113 lineart_main_transform_and_add_shadow(ld, shadow_veln, shadow_eeln);
5114 }
5115
5117 /* No geometry loaded, return early. */
5118 return true;
5119 }
5120
5121 /* Initialize the bounding box acceleration structure, it's a lot like BVH in 3D. */
5123
5124 /* We need to get cut into triangles that are crossing near/far plans, only this way can we get
5125 * correct coordinates of those clipped lines. Done in two steps,
5126 * setting clip_far==false for near plane. */
5127 lineart_main_cull_triangles(ld, false);
5128 /* `clip_far == true` for far plane. */
5130
5131 /* At this point triangle adjacent info pointers is no longer needed, free them. */
5133
5134 /* Do the perspective division after clipping is done. */
5136
5138
5139 /* Triangle intersections are done here during sequential adding of them. Only after this,
5140 * triangles and lines are all linked with acceleration structure, and the 2D occlusion stage
5141 * can do its job. */
5143
5144 /* Add shadow cuts to intersection lines as well. */
5146
5147 /* Re-link bounding areas because they have been subdivided by worker threads and we need
5148 * adjacent info. */
5150
5151 /* Link lines to acceleration structure, this can only be done after perspective division, if
5152 * we do it after triangles being added, the acceleration structure has already been
5153 * subdivided, this way we do less list manipulations. */
5155
5156 /* "intersection_only" is preserved for being called in a standalone fashion.
5157 * If so the data will already be available at the stage. Otherwise we do the occlusion and
5158 * chaining etc. */
5159
5160 if (!intersections_only) {
5161
5162 /* Occlusion is work-and-wait. This call will not return before work is completed. */
5164
5165 lineart_main_make_enclosed_shapes(ld, shadow_rb);
5166
5168
5169 /* Chaining is all single threaded. See `lineart_chain.cc`.
5170 * In this particular call, only lines that are geometrically connected (share the _exact_
5171 * same end point) will be chained together. */
5173
5174 /* We are unable to take care of occlusion if we only connect end points, so here we do a
5175 * spit, where the splitting point could be any cut in e->segments. */
5177
5178 /* Then we connect chains based on the _proximity_ of their end points in image space, here's
5179 * the place threshold value gets involved. */
5181
5182 if (ld->conf.chain_smooth_tolerance > FLT_EPSILON) {
5183 /* Keeping UI range of 0-1 for ease of read while scaling down the actual value for best
5184 * effective range in image-space (Coordinate only goes from -1 to 1). This value is
5185 * somewhat arbitrary, but works best for the moment. */
5187 }
5188
5191 }
5192
5193 if (ld->conf.angle_splitting_threshold > FLT_EPSILON) {
5195 }
5196
5197 if (enable_stroke_depth_offset && lmd.stroke_depth_offset > FLT_EPSILON) {
5200 }
5201
5202 if (ld->conf.shadow_use_silhouette) {
5204 }
5205
5206 /* Finally transfer the result list into cache. */
5207 memcpy(&lc->chains, &ld->chains, sizeof(ListBase));
5208
5209 /* At last, we need to clear flags so we don't confuse GPencil generation calls. */
5211
5213 }
5214
5216
5217 if (ld->conf.shadow_enclose_shapes && shadow_rb) {
5219 MEM_freeN(shadow_rb);
5220 }
5221
5222 if (G.debug_value == 4000) {
5224
5225 double t_elapsed = BLI_time_now_seconds() - t_start;
5226 printf("Line art total time: %lf\n", t_elapsed);
5227 }
5228
5229 return true;
5230}
5231
5236
5238 const blender::float4x4 &inverse_mat,
5239 Depsgraph *depsgraph,
5241 const int8_t source_type,
5242 Object *source_object,
5243 Collection *source_collection,
5244 const int level_start,
5245 const int level_end,
5246 const int mat_nr,
5247 const int16_t edge_types,
5248 const uchar mask_switches,
5249 const uchar material_mask_bits,
5250 const uchar intersection_mask,
5251 const float thickness,
5252 const float opacity,
5253 const uchar shadow_selection,
5254 const uchar silhouette_mode,
5255 const char *source_vgname,
5256 const char *vgname,
5257 const int modifier_flags,
5258 const int modifier_calculation_flags)
5259{
5260 if (G.debug_value == 4000) {
5261 printf("Line Art v3: Generating...\n");
5262 }
5263
5264 if (cache == nullptr) {
5265 if (G.debug_value == 4000) {
5266 printf("nullptr Lineart cache!\n");
5267 }
5268 return;
5269 }
5270
5271 Object *orig_ob = nullptr;
5272 Collection *orig_col = nullptr;
5273
5274 if (source_type == LINEART_SOURCE_OBJECT) {
5275 if (!source_object) {
5276 return;
5277 }
5278 orig_ob = source_object->id.orig_id ? (Object *)source_object->id.orig_id : source_object;
5279 orig_col = nullptr;
5280 }
5281 else if (source_type == LINEART_SOURCE_COLLECTION) {
5282 if (!source_collection) {
5283 return;
5284 }
5285 orig_col = source_collection->id.orig_id ? (Collection *)source_collection->id.orig_id :
5286 source_collection;
5287 orig_ob = nullptr;
5288 }
5289 /* Otherwise the whole scene is selected. */
5290
5291 int enabled_types = cache->all_enabled_edge_types;
5292
5293 bool invert_input = modifier_calculation_flags & MOD_LINEART_INVERT_SOURCE_VGROUP;
5294
5295 bool inverse_silhouette = modifier_flags & MOD_LINEART_INVERT_SILHOUETTE_FILTER;
5296
5298 writer.reserve(128);
5299 int total_point_count = 0;
5300 int stroke_count = 0;
5301 LISTBASE_FOREACH (LineartEdgeChain *, ec, &cache->chains) {
5302
5303 if (ec->picked) {
5304 continue;
5305 }
5306 if (!(ec->type & (edge_types & enabled_types))) {
5307 continue;
5308 }
5309 if (ec->level > level_end || ec->level < level_start) {
5310 continue;
5311 }
5312 if (orig_ob && orig_ob != ec->object_ref) {
5313 continue;
5314 }
5315 if (orig_col && ec->object_ref) {
5316 if (BKE_collection_has_object_recursive_instanced(orig_col, ec->object_ref)) {
5317 if (modifier_flags & MOD_LINEART_INVERT_COLLECTION) {
5318 continue;
5319 }
5320 }
5321 else {
5322 if (!(modifier_flags & MOD_LINEART_INVERT_COLLECTION)) {
5323 continue;
5324 }
5325 }
5326 }
5327 if (mask_switches & MOD_LINEART_MATERIAL_MASK_ENABLE) {
5328 if (mask_switches & MOD_LINEART_MATERIAL_MASK_MATCH) {
5329 if (ec->material_mask_bits != material_mask_bits) {
5330 continue;
5331 }
5332 }
5333 else {
5334 if (!(ec->material_mask_bits & material_mask_bits)) {
5335 continue;
5336 }
5337 }
5338 }
5339 if (ec->type & MOD_LINEART_EDGE_FLAG_INTERSECTION) {
5340 if (mask_switches & MOD_LINEART_INTERSECTION_MATCH) {
5341 if (ec->intersection_mask != intersection_mask) {
5342 continue;
5343 }
5344 }
5345 else {
5346 if ((intersection_mask) && !(ec->intersection_mask & intersection_mask)) {
5347 continue;
5348 }
5349 }
5350 }
5351 if (shadow_selection) {
5352 if (ec->shadow_mask_bits != LRT_SHADOW_MASK_UNDEFINED) {
5353 /* TODO(@Yiming): Give a behavior option for how to display undefined shadow info. */
5354 if (shadow_selection == LINEART_SHADOW_FILTER_ILLUMINATED &&
5355 !(ec->shadow_mask_bits & LRT_SHADOW_MASK_ILLUMINATED))
5356 {
5357 continue;
5358 }
5359 if (shadow_selection == LINEART_SHADOW_FILTER_SHADED &&
5360 !(ec->shadow_mask_bits & LRT_SHADOW_MASK_SHADED))
5361 {
5362 continue;
5363 }
5364 if (shadow_selection == LINEART_SHADOW_FILTER_ILLUMINATED_ENCLOSED_SHAPES) {
5365 uint32_t test_bits = ec->shadow_mask_bits & LRT_SHADOW_TEST_SHAPE_BITS;
5366 if ((test_bits != LRT_SHADOW_MASK_ILLUMINATED) &&
5368 {
5369 continue;
5370 }
5371 }
5372 }
5373 }
5374 if (silhouette_mode && (ec->type & (MOD_LINEART_EDGE_FLAG_CONTOUR))) {
5375 bool is_silhouette = false;
5376 if (orig_col) {
5377 if (!ec->silhouette_backdrop) {
5378 is_silhouette = true;
5379 }
5380 else if (!BKE_collection_has_object_recursive_instanced(orig_col, ec->silhouette_backdrop))
5381 {
5382 is_silhouette = true;
5383 }
5384 }
5385 else {
5386 if ((!orig_ob) && (!ec->silhouette_backdrop)) {
5387 is_silhouette = true;
5388 }
5389 }
5390
5391 if ((silhouette_mode == LINEART_SILHOUETTE_FILTER_INDIVIDUAL || orig_ob) &&
5392 ec->silhouette_backdrop != ec->object_ref)
5393 {
5394 is_silhouette = true;
5395 }
5396
5397 if (inverse_silhouette) {
5398 is_silhouette = !is_silhouette;
5399 }
5400 if (!is_silhouette) {
5401 continue;
5402 }
5403 }
5404
5405 /* Preserved: If we ever do asynchronous generation, this picked flag should be set here. */
5406 // ec->picked = 1;
5407
5408 const int count = MOD_lineart_chain_count(ec);
5409 if (count < 2) {
5410 continue;
5411 }
5412
5413 total_point_count += count;
5414 writer.append({ec, count});
5415
5416 stroke_count++;
5417 }
5418
5419 if (!total_point_count || !stroke_count) {
5420 return;
5421 }
5422
5423 blender::bke::CurvesGeometry new_curves(total_point_count, stroke_count);
5425
5426 MutableAttributeAccessor attributes = new_curves.attributes_for_write();
5427 MutableSpan<float3> point_positions = new_curves.positions_for_write();
5428
5429 SpanAttributeWriter<float> point_radii = attributes.lookup_or_add_for_write_only_span<float>(
5430 "radius", AttrDomain::Point);
5431
5432 SpanAttributeWriter<float> point_opacities = attributes.lookup_or_add_for_write_span<float>(
5433 "opacity", AttrDomain::Point);
5434
5435 SpanAttributeWriter<int> stroke_materials = attributes.lookup_or_add_for_write_span<int>(
5436 "material_index", AttrDomain::Curve);
5437
5438 MutableSpan<int> offsets = new_curves.offsets_for_write();
5439
5440 const bool weight_transfer_match_output = modifier_calculation_flags &
5442
5443 using blender::StringRef;
5444 using blender::Vector;
5445
5446 auto ensure_target_defgroup = [&](StringRef group_name) {
5447 if (group_name.is_empty()) {
5448 return -1;
5449 }
5450 int group_index = 0;
5451 LISTBASE_FOREACH_INDEX (bDeformGroup *, group, &new_curves.vertex_group_names, group_index) {
5452 if (group_name == StringRef(group->name)) {
5453 return group_index;
5454 }
5455 }
5456 bDeformGroup *defgroup = MEM_callocN<bDeformGroup>(__func__);
5457 group_name.copy_utf8_truncated(defgroup->name);
5458 BLI_addtail(&new_curves.vertex_group_names, defgroup);
5459 return group_index;
5460 };
5461
5462 int up_to_point = 0;
5463 for (int chain_i : writer.index_range()) {
5464 LineartChainWriteInfo &cwi = writer[chain_i];
5465
5466 Vector<int> src_to_dst_defgroup;
5467
5469 Mesh *src_mesh = nullptr;
5471 int target_defgroup = ensure_target_defgroup(vgname);
5472 if (source_vgname) {
5474 if (eval_ob && eval_ob->type == OB_MESH) {
5475 src_mesh = BKE_object_get_evaluated_mesh(eval_ob);
5476 src_dvert = src_mesh->deform_verts();
5477 }
5478 }
5479
5480 if (!src_dvert.is_empty()) {
5481 const ListBase *deflist = &src_mesh->vertex_group_names;
5482 int group_index = 0;
5483 LISTBASE_FOREACH_INDEX (bDeformGroup *, defgroup, deflist, group_index) {
5484 if (StringRef(defgroup->name).startswith(source_vgname)) {
5485 const int target_group_index = weight_transfer_match_output ?
5486 ensure_target_defgroup(defgroup->name) :
5487 target_defgroup;
5488 src_to_dst_defgroup.append(target_group_index);
5489 }
5490 else {
5491 src_to_dst_defgroup.append(-1);
5492 }
5493 }
5494 }
5495
5496 auto transfer_to_matching_groups = [&](const int64_t source_index, const int target_index) {
5497 for (const int from_group : src_to_dst_defgroup.index_range()) {
5498 if (from_group < 0 || src_to_dst_defgroup[from_group] < 0 ||
5499 UNLIKELY(source_index >= src_dvert.size()))
5500 {
5501 continue;
5502 }
5503 const MDeformWeight *mdw_from = BKE_defvert_find_index(&src_dvert[source_index],
5504 from_group);
5505 MDeformWeight *mdw_to = BKE_defvert_ensure_index(&dv[target_index],
5506 src_to_dst_defgroup[from_group]);
5507 const float source_weight = mdw_from ? mdw_from->weight : 0.0f;
5508 mdw_to->weight = invert_input ? (1 - source_weight) : source_weight;
5509 }
5510 };
5511
5512 auto transfer_to_singular_group = [&](const int64_t source_index, const int target_index) {
5513 if (target_defgroup < 0) {
5514 return;
5515 }
5516 float highest_weight = 0.0f;
5517 for (const int from_group : src_to_dst_defgroup.index_range()) {
5518 if (from_group < 0 || UNLIKELY(source_index >= src_dvert.size())) {
5519 continue;
5520 }
5521 const MDeformWeight *mdw_from = BKE_defvert_find_index(&src_dvert[source_index],
5522 from_group);
5523 const float source_weight = mdw_from ? mdw_from->weight : 0.0f;
5524 highest_weight = std::max(highest_weight, source_weight);
5525 }
5526 MDeformWeight *mdw_to = BKE_defvert_ensure_index(&dv[target_index], target_defgroup);
5527 mdw_to->weight = invert_input ? (1 - highest_weight) : highest_weight;
5528 };
5529
5530 int i;
5532 int point_i = i + up_to_point;
5533 point_positions[point_i] = blender::math::transform_point(inverse_mat, float3(eci->gpos));
5534 point_radii.span[point_i] = thickness / 2.0f;
5535 if (point_opacities) {
5536 point_opacities.span[point_i] = opacity;
5537 }
5538
5539 const int64_t vindex = eci->index - cwi.chain->index_offset;
5540
5541 if (!src_to_dst_defgroup.is_empty()) {
5542 if (weight_transfer_match_output) {
5543 transfer_to_matching_groups(vindex, point_i);
5544 }
5545 else {
5546 transfer_to_singular_group(vindex, point_i);
5547 }
5548 }
5549 }
5550
5551 offsets[chain_i] = up_to_point;
5552 stroke_materials.span[chain_i] = max_ii(mat_nr, 0);
5553 up_to_point += cwi.point_count;
5554 }
5555
5556 offsets[writer.index_range().last() + 1] = up_to_point;
5557
5558 SpanAttributeWriter<bool> stroke_cyclic = attributes.lookup_or_add_for_write_span<bool>(
5559 "cyclic", AttrDomain::Curve);
5560 stroke_cyclic.span.fill(false);
5561 stroke_cyclic.finish();
5562
5563 point_radii.finish();
5564 point_opacities.finish();
5565 stroke_materials.finish();
5566
5567 Curves *original_curves = blender::bke::curves_new_nomain(drawing.strokes());
5568 Curves *created_curves = blender::bke::curves_new_nomain(std::move(new_curves));
5569 std::array<blender::bke::GeometrySet, 2> geometry_sets{
5573
5574 drawing.strokes_for_write() = std::move(joined.get_curves_for_write()->geometry.wrap());
5575 drawing.tag_topology_changed();
5576
5577 if (G.debug_value == 4000) {
5578 printf("LRT: Generated %d strokes.\n", stroke_count);
5579 }
5580}
Camera data-block and utility functions.
float BKE_camera_sensor_size(int sensor_fit, float sensor_x, float sensor_y)
int BKE_camera_sensor_fit(int sensor_fit, float sizex, float sizey)
bool BKE_collection_has_object_recursive_instanced(Collection *collection, Object *ob)
bool BKE_collection_has_object(Collection *collection, const Object *ob)
Low-level operations for curves.
CustomData interface, see also DNA_customdata_types.h.
int CustomData_get_layer_index(const CustomData *data, eCustomDataType type)
int CustomData_get_active_layer_index(const CustomData *data, eCustomDataType type)
support for deformation groups and hooks.
MDeformWeight * BKE_defvert_ensure_index(MDeformVert *dv, int defgroup)
Definition deform.cc:814
MDeformWeight * BKE_defvert_find_index(const MDeformVert *dv, int defgroup)
Definition deform.cc:795
Low-level operations for grease pencil.
void BKE_id_free(Main *bmain, void *idv)
General operations, lookup, etc. for materials.
Material * BKE_object_material_get(Object *ob, short act)
Material * BKE_object_material_get_eval(Object *ob, short act)
Mesh * BKE_mesh_new_from_object(Depsgraph *depsgraph, Object *object, bool preserve_all_data_layers, bool preserve_origindex, bool ensure_subdivision)
General operations, lookup, etc. for blender objects.
Mesh * BKE_object_get_evaluated_mesh(const Object *object_eval)
@ OB_VISIBLE_SELF
void BKE_boundbox_init_from_minmax(BoundBox *bb, const float min[3], const float max[3])
int BKE_object_visibility(const Object *ob, int dag_eval_mode)
int BKE_render_num_threads(const RenderData *r)
Definition scene.cc:2900
bool BKE_scene_camera_switch_update(Scene *scene)
Definition scene.cc:2267
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_INLINE
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE void BLI_listbase_clear(ListBase *lb)
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
#define LISTBASE_FOREACH_INDEX(type, var, list, index_var)
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 * BLI_pophead(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:252
void BLI_insertlinkbefore(ListBase *listbase, void *vnextlink, void *vnewlink) ATTR_NONNULL(1)
Definition listbase.cc:371
MINLINE double ratiod(double min, double max, double pos)
MINLINE int max_ii(int a, int b)
#define M_PI
int isect_seg_seg_v2(const float v1[2], const float v2[2], const float v3[2], const float v4[2])
#define ISECT_LINE_LINE_NONE
float normal_tri_v3(float n[3], const float v1[3], const float v2[3], const float v3[3])
Definition math_geom.cc:41
void mul_v4_m4v3_db(double r[4], const double mat[4][4], const double vec[3])
void mul_v3_m4v3_db(double r[3], const double mat[4][4], const double vec[3])
void mul_m4db_m4db_m4fl(double R[4][4], const double A[4][4], const float B[4][4])
void unit_m4_db(double m[4][4])
void copy_m4d_m4(double m1[4][4], const float m2[4][4])
void copy_m4_m4(float m1[4][4], const float m2[4][4])
bool invert_m4_m4(float inverse[4][4], const float mat[4][4])
void mul_v3_mat3_m4v3_db(double r[3], const double mat[4][4], const double vec[3])
void transpose_m4(float R[4][4])
void mul_v3_mat3_m4v3(float r[3], const float mat[4][4], const float vec[3])
void copy_m4_m4_db(double m1[4][4], const double m2[4][4])
float focallength_to_fov(float focal_length, float sensor)
MINLINE double normalize_v3_db(double n[3])
void interp_v3_v3v3_db(double target[3], const double a[3], const double b[3], double t)
MINLINE void mul_v3db_db(double r[3], double f)
MINLINE void add_v3_v3_db(double r[3], const double a[3])
MINLINE double dot_v3v3_db(const double a[3], const double b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void copy_v3db_v3fl(double r[3], const float a[3])
MINLINE void cross_v3_v3v3_db(double r[3], const double a[3], const double b[3])
MINLINE void copy_v3_v3_db(double r[3], const double a[3])
void interp_v2_v2v2_db(double target[2], const double a[2], const double b[2], double t)
MINLINE float normalize_v3(float n[3])
MINLINE void sub_v3_v3v3_db(double r[3], const double a[3], const double b[3])
MINLINE void sub_v2_v2v2_db(double r[2], const double a[2], const double b[2])
unsigned char uchar
@ TASK_PRIORITY_HIGH
Definition BLI_task.h:53
void BLI_task_pool_work_and_wait(TaskPool *pool)
Definition task_pool.cc:531
void(* TaskRunFunction)(TaskPool *__restrict pool, void *taskdata)
Definition BLI_task.h:57
void BLI_task_parallel_range(int start, int stop, void *userdata, TaskParallelRangeFunc func, const TaskParallelSettings *settings)
Definition task_range.cc:99
TaskPool * BLI_task_pool_create(void *userdata, eTaskPriority priority)
Definition task_pool.cc:480
BLI_INLINE void BLI_parallel_range_settings_defaults(TaskParallelSettings *settings)
Definition BLI_task.h:221
void BLI_task_pool_free(TaskPool *pool)
Definition task_pool.cc:517
void BLI_task_pool_push(TaskPool *pool, TaskRunFunction run, void *taskdata, bool free_taskdata, TaskFreeFunction freedata)
Definition task_pool.cc:522
void BLI_spin_init(SpinLock *spin)
Definition threads.cc:391
void BLI_spin_unlock(SpinLock *spin)
Definition threads.cc:430
void BLI_spin_lock(SpinLock *spin)
Definition threads.cc:405
void BLI_spin_end(SpinLock *spin)
Definition threads.cc:445
Platform independent time functions.
double BLI_time_now_seconds(void)
Definition time.cc:65
#define CLAMP(a, b, c)
#define UNLIKELY(x)
#define ELEM(...)
#define LIKELY(x)
float[3] Vector
eEvaluationMode
@ DAG_EVAL_RENDER
#define DEG_OBJECT_ITER_BEGIN(settings_, instance_)
#define DEG_OBJECT_ITER_END
Scene * DEG_get_evaluated_scene(const Depsgraph *graph)
eEvaluationMode DEG_get_mode(const Depsgraph *graph)
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
@ DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY
@ DEG_ITER_OBJECT_FLAG_VISIBLE
@ DEG_ITER_OBJECT_FLAG_DUPLI
@ DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET
@ CAMERA_SENSOR_FIT_HOR
@ CAMERA_SENSOR_FIT_VERT
@ CAM_PERSP
@ CAM_PANO
@ CAM_CUSTOM
@ CAM_ORTHO
Object groups, one object can be in many groups at once.
@ COLLECTION_HIDE_RENDER
@ COLLECTION_HIDE_VIEWPORT
@ COLLECTION_LRT_EXCLUDE
@ COLLECTION_LRT_INTERSECTION_ONLY
@ COLLECTION_LRT_FORCE_INTERSECTION
@ COLLECTION_LRT_OCCLUSION_ONLY
@ COLLECTION_LRT_NO_INTERSECTION
@ COLLECTION_LRT_USE_INTERSECTION_MASK
@ COLLECTION_LRT_USE_INTERSECTION_PRIORITY
@ CURVE_TYPE_POLY
@ CD_FREESTYLE_EDGE
@ CD_FREESTYLE_FACE
@ LA_SUN
@ MOD_LINEART_EDGE_FLAG_PROJECTED_SHADOW
@ MOD_LINEART_EDGE_FLAG_CONTOUR
@ MOD_LINEART_EDGE_FLAG_LIGHT_CONTOUR
@ MOD_LINEART_EDGE_FLAG_SHADOW_FACING_LIGHT
@ MOD_LINEART_EDGE_FLAG_INTERSECTION
@ MOD_LINEART_EDGE_FLAG_INHIBIT
@ MOD_LINEART_EDGE_FLAG_CHAIN_PICKED
@ MOD_LINEART_EDGE_FLAG_CREASE
@ MOD_LINEART_EDGE_FLAG_CONTOUR_SECONDARY
@ MOD_LINEART_EDGE_FLAG_NEXT_IS_DUPLICATION
@ MOD_LINEART_EDGE_FLAG_MATERIAL
@ MOD_LINEART_EDGE_FLAG_EDGE_MARK
@ MOD_LINEART_EDGE_FLAG_LOOSE
@ MOD_LINEART_FILTER_FACE_MARK
@ MOD_LINEART_FILTER_FACE_MARK_BOUNDARIES
@ MOD_LINEART_USE_CREASE_ON_SMOOTH_SURFACES
@ MOD_LINEART_USE_IMAGE_BOUNDARY_TRIMMING
@ MOD_LINEART_LOOSE_AS_CONTOUR
@ MOD_LINEART_CHAIN_PRESERVE_DETAILS
@ MOD_LINEART_USE_BACK_FACE_CULLING
@ MOD_LINEART_CHAIN_LOOSE_EDGES
@ MOD_LINEART_USE_CUSTOM_CAMERA
@ MOD_LINEART_USE_CREASE_ON_SHARP_EDGES
@ MOD_LINEART_INVERT_SOURCE_VGROUP
@ MOD_LINEART_EVERYTHING_AS_CONTOUR
@ MOD_LINEART_ALLOW_DUPLI_OBJECTS
@ MOD_LINEART_MATCH_OUTPUT_VGROUP
@ MOD_LINEART_CHAIN_GEOMETRY_SPACE
@ MOD_LINEART_FILTER_FACE_MARK_KEEP_CONTOUR
@ MOD_LINEART_INTERSECTION_AS_CONTOUR
@ MOD_LINEART_ALLOW_OVERLAP_EDGE_TYPES
@ MOD_LINEART_ALLOW_OVERLAPPING_EDGES
@ MOD_LINEART_ALLOW_CLIPPING_BOUNDARIES
@ MOD_LINEART_FILTER_FACE_MARK_INVERT
@ MA_BL_CULL_BACKFACE
@ LRT_MATERIAL_CUSTOM_INTERSECTION_PRIORITY
@ LRT_MATERIAL_MASK_ENABLED
@ FREESTYLE_FACE_MARK
@ FREESTYLE_EDGE_MARK
@ LINEART_SHADOW_FILTER_ILLUMINATED_ENCLOSED_SHAPES
@ LINEART_SHADOW_FILTER_SHADED
@ LINEART_SHADOW_FILTER_ILLUMINATED
@ MOD_LINEART_MATERIAL_MASK_ENABLE
@ MOD_LINEART_INTERSECTION_MATCH
@ MOD_LINEART_MATERIAL_MASK_MATCH
@ LINEART_SILHOUETTE_FILTER_INDIVIDUAL
@ MOD_LINEART_INVERT_SILHOUETTE_FILTER
@ MOD_LINEART_OFFSET_TOWARDS_CUSTOM_CAMERA
@ MOD_LINEART_INVERT_COLLECTION
@ LINEART_SOURCE_OBJECT
@ LINEART_SOURCE_COLLECTION
@ OBJECT_LRT_OWN_INTERSECTION_PRIORITY
@ OBJECT_LRT_OWN_CREASE
@ OB_MBALL
@ OB_SURF
@ OB_CAMERA
@ OB_FONT
@ OB_LAMP
@ OB_MESH
@ OB_CURVES_LEGACY
@ OBJECT_LRT_INCLUDE
@ OBJECT_LRT_NO_INTERSECTION
@ OBJECT_LRT_EXCLUDE
@ OBJECT_LRT_INHERIT
@ OBJECT_LRT_OCCLUSION_ONLY
@ OBJECT_LRT_INTERSECTION_ONLY
@ OBJECT_LRT_FORCE_INTERSECTION
static AppView * view
Read Guarded memory(de)allocation.
#define LRT_TILE_EDGE_COUNT_INITIAL
#define LRT_DOUBLE_CLOSE_ENOUGH(a, b)
#define LRT_SHADOW_MASK_INHIBITED
#define LRT_SHADOW_MASK_UNDEFINED
#define LRT_OBINDEX_LOWER
BLI_INLINE int lineart_intersect_seg_seg(const double a1[2], const double a2[2], const double b1[2], const double b2[2], double *r_ratio, bool *r_aligned)
#define LRT_OBINDEX_SHIFT
#define LRT_SHADOW_MASK_ILLUMINATED_SHAPE
#define LRT_LIGHT_CONTOUR_TARGET
#define DBL_TRIANGLE_LIM
#define LRT_SHADOW_MASK_ENCLOSED_SHAPE
#define LRT_SHADOW_TEST_SHAPE_BITS
#define LRT_OBINDEX_HIGHER
@ LRT_TILE_RECURSIVE_PERSPECTIVE
@ LRT_TILE_RECURSIVE_ORTHO
#define LRT_SHADOW_MASK_ILLUMINATED
#define LRT_TILE_SPLITTING_TRIANGLE_LIMIT
@ LRT_TRIANGLE_NO_INTERSECTION
@ LRT_CULL_GENERATED
@ LRT_CULL_DISCARD
@ LRT_TRIANGLE_MAT_BACK_FACE_CULLING
@ LRT_TRIANGLE_INTERSECTION_ONLY
@ LRT_TRIANGLE_FORCE_INTERSECTION
@ LRT_CULL_USED
#define LRT_THREAD_EDGE_COUNT
#define LRT_SHADOW_MASK_SHADED
eLineArtElementNodeFlag
@ LRT_ELEMENT_NO_INTERSECTION
@ LRT_ELEMENT_BORDER_ONLY
@ LRT_ELEMENT_INTERSECTION_DATA
@ LRT_ELEMENT_IS_ADDITIONAL
#define LRT_EDGE_IDENTIFIER(obi, e)
volatile int lock
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
PyObject * self
BPy_StructRNA * depsgraph
long long int int64_t
unsigned long long int uint64_t
static btDbvtVolume bounds(btDbvtNode **leaves, int count)
Definition btDbvt.cpp:299
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr bool is_empty() const
Definition BLI_span.hh:260
void append(const T &value)
bool is_empty() const
IndexRange index_range() const
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr bool is_empty() const
Definition BLI_span.hh:260
constexpr bool startswith(StringRef prefix) const
void append(const T &value)
IndexRange index_range() const
void reserve(const int64_t min_capacity)
GAttributeReader lookup(const StringRef attribute_id) const
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const void *default_value=nullptr) const
MutableSpan< float3 > positions_for_write()
MutableSpan< MDeformVert > deform_verts_for_write()
MutableAttributeAccessor attributes_for_write()
void fill_curve_types(CurveType type)
MutableSpan< int > offsets_for_write()
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type)
bke::CurvesGeometry & strokes_for_write()
const bke::CurvesGeometry & strokes() const
#define cosf(x)
uint col
#define cos
#define printf(...)
float length(VecOp< float, D >) RET
#define MEM_recallocN(vmemh, len)
#define MEM_SAFE_FREE(v)
BLI_INLINE float fb(float length, float L)
int count
void MOD_lineart_chain_connect(LineartData *ld)
void MOD_lineart_chain_split_for_fixed_occlusion(LineartData *ld)
void MOD_lineart_chain_clip_at_border(LineartData *ld)
int MOD_lineart_chain_count(const LineartEdgeChain *ec)
void MOD_lineart_finalize_chains(LineartData *ld)
void MOD_lineart_chain_find_silhouette_backdrop_objects(LineartData *ld)
void MOD_lineart_chain_feature_lines(LineartData *ld)
void MOD_lineart_chain_clear_picked_flag(LineartCache *lc)
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_bounding_area_line_add(LineartBoundingArea *ba, LineartEdge *e)
LineartCache * MOD_lineart_init_cache()
static void lineart_bounding_areas_connect_recursive(LineartData *ld, LineartBoundingArea *root)
void lineart_main_load_geometries(Depsgraph *depsgraph, Scene *scene, Object *camera, LineartData *ld, bool allow_duplicates, bool do_shadow_casting, ListBase *shadow_elns, blender::Set< const Object * > *included_objects)
static int lineart_triangle_size_get(LineartData *ld)
static LineartEdgeSegment * lineart_give_segment(LineartData *ld)
void lineart_main_occlusion_begin(LineartData *ld)
static void lineart_add_triangles_worker(TaskPool *__restrict, LineartIsecThread *th)
#define INTERSECT_JUST_SMALLER(is, order, num, index)
static void lineart_init_isec_thread(LineartIsecData *d, LineartData *ld, int thread_count)
static LineartBoundingArea * lineart_get_bounding_area(LineartData *ld, double x, double y)
#define LRT_MESH_EDGE_TYPES_COUNT
#define RELINK_EDGE(e_num, new_tri)
static void lineart_destroy_isec_thread(LineartIsecData *d)
static void lineart_occlusion_worker(TaskPool *__restrict, LineartRenderTaskInfo *rti)
static bool lineart_triangle_intersect_math(LineartTriangle *tri, LineartTriangle *t2, double *v1, double *v2)
static bool lineart_bounding_area_triangle_intersect(LineartData *fb, LineartTriangle *tri, LineartBoundingArea *ba, bool *r_triangle_vert_inside)
void lineart_main_discard_out_of_frame_edges(LineartData *ld)
void lineart_main_get_view_vector(LineartData *ld)
void lineart_main_link_lines(LineartData *ld)
LineartBoundingArea * MOD_lineart_get_parent_bounding_area(LineartData *ld, double x, double y)
bool lineart_edge_from_triangle(const LineartTriangle *tri, const LineartEdge *e, bool allow_overlapping_edges)
static bool lineart_edge_match(LineartTriangle *tri, LineartEdge *e, int v1, int v2)
static void lineart_create_edges_from_isec_data(LineartIsecData *d)
static LineartVert * lineart_triangle_share_point(const LineartTriangle *l, const LineartTriangle *r)
static void lineart_sort_adjacent_items(LineartAdjacentEdge *ai, int length)
static bool lineart_bounding_area_edge_intersect(LineartData *, const double l[2], const double r[2], LineartBoundingArea *ba)
static void lineart_add_isec_thread(LineartIsecThread *th, const double *v1, const double *v2, LineartTriangle *tri1, LineartTriangle *tri2)
static bool lineart_triangle_2v_intersection_math(LineartVert *v1, LineartVert *v2, LineartTriangle *tri, const double *last, double *rv)
void lineart_main_bounding_areas_connect_post(LineartData *ld)
static const int LRT_MESH_EDGE_TYPES[]
static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartData *la_data, ListBase *shadow_elns)
static void lineart_edge_neighbor_init_task(void *__restrict userdata, const int i, const TaskParallelTLS *__restrict)
#define LRT_VERT_OUT_OF_BOUND(v)
static void lineart_destroy_render_data(LineartData *ld)
void lineart_main_add_triangles(LineartData *ld)
LineartBoundingArea * lineart_bounding_area_next(LineartBoundingArea *self, double *fbcoord1, double *fbcoord2, double x, double y, double k, int positive_x, int positive_y, double *next_x, double *next_y)
static LineartElementLinkNode * lineart_memory_get_vert_space(LineartData *ld)
static bool lineart_triangle_share_edge(const LineartTriangle *l, const LineartTriangle *r)
static uchar lineart_intersection_mask_check(Collection *c, Object *ob)
void lineart_add_edge_to_array(LineartPendingEdges *pe, LineartEdge *e)
static bool lineart_point_inside_triangle3d(double v[3], double v0[3], double v1[3], double v2[3])
static int lineart_occlusion_make_task_info(LineartData *ld, LineartRenderTaskInfo *rti)
#define LRT_CULL_DECIDE_INSIDE
void lineart_main_clear_linked_edges(LineartData *ld)
static void lineart_triangle_adjacent_assign(LineartTriangle *tri, LineartTriangleAdjacent *tri_adj, LineartEdge *e)
static void lineart_bounding_area_triangle_reallocate(LineartBoundingArea *ba)
static void lineart_free_bounding_area_memories(LineartData *ld)
static bool lineart_triangle_edge_image_space_occlusion(const LineartTriangle *tri, const LineartEdge *e, const double *override_camera_loc, const bool override_cam_is_persp, const bool allow_overlapping_edges, const double m_view_projection[4][4], const double camera_dir[3], const float cam_shift_x, const float cam_shift_y, double *from, double *to)
static int lineart_edge_type_duplication_count(int eflag)
void lineart_main_cull_triangles(LineartData *ld, bool clip_far)
static LineartTriangle * lineart_triangle_from_index(LineartData *ld, LineartTriangle *rt_array, int index)
void MOD_lineart_clear_cache(LineartCache **lc)
static void lineart_bounding_area_split(LineartData *ld, LineartBoundingArea *root, int recursive_level)
void lineart_main_bounding_area_make_initial(LineartData *ld)
static void lineart_add_edge_to_array_thread(LineartObjectInfo *obi, LineartEdge *e)
int3 corner_tri_get_real_edges(Span< int2 > edges, Span< int > corner_verts, Span< int > corner_edges, const int3 &corner_tri)
#define INCREASE_EDGE
#define LRT_ISECT_TRIANGLE_PER_THREAD
static void lineart_load_tri_task(void *__restrict userdata, const int i, const TaskParallelTLS *__restrict)
static void lineart_triangle_post(LineartTriangle *tri, LineartTriangle *orig)
static bool lineart_get_triangle_bounding_areas(LineartData *ld, LineartTriangle *tri, int *rowbegin, int *rowend, int *colbegin, int *colend)
static void lineart_object_load_single_instance(LineartData *ld, Depsgraph *depsgraph, Scene *scene, Object *ob, Object *ref_ob, const float use_mat[4][4], bool is_render, LineartObjectLoadTaskInfo *olti, int thread_count, int obindex)
static void lineart_triangle_intersect_in_bounding_area(LineartTriangle *tri, LineartBoundingArea *ba, LineartIsecThread *th, int up_to)
static void lineart_triangle_set_cull_flag(LineartTriangle *tri, uchar flag)
static LineartElementLinkNode * lineart_memory_get_triangle_space(LineartData *ld)
static void lineart_finalize_object_edge_array(LineartPendingEdges *pe, LineartObjectInfo *obi)
LineartBoundingArea * MOD_lineart_get_bounding_area(LineartData *ld, double x, double y)
void lineart_main_free_adjacent_data(LineartData *ld)
static void lineart_occlusion_single_line(LineartData *ld, LineartEdge *e, int thread_id)
static void lineart_triangle_cull_single(LineartData *ld, LineartTriangle *tri, int in0, int in1, int in2, double cam_pos[3], double view_dir[3], bool allow_boundaries, double m_view_projection[4][4], Object *ob, int *r_v_count, int *r_e_count, int *r_t_count, LineartElementLinkNode *v_eln, LineartElementLinkNode *e_eln, LineartElementLinkNode *t_eln)
static void lineart_free_bounding_area_memory(LineartBoundingArea *ba, bool recursive)
#define REMOVE_TRIANGLE_EDGE
static bool lineart_point_inside_triangle(const double v[2], const double v0[2], const double v1[2], const double v2[2])
static void lineart_end_bounding_area_recursive(LineartBoundingArea *ba)
static LineartPointTri lineart_point_triangle_relation(double v[2], double v0[2], double v1[2], double v2[2])
static void lineart_bounding_area_link_edge(LineartData *ld, LineartBoundingArea *root_ba, LineartEdge *e)
#define LRT_PARALLEL(index)
static LineartData * lineart_create_render_buffer_v3(Scene *scene, GreasePencilLineartModifierData *lmd, Object *camera, Object *active_camera, LineartCache *lc)
static void lineart_geometry_load_assign_thread(LineartObjectLoadTaskInfo *olti_list, LineartObjectInfo *obi, int thread_count, int this_face_count)
static void lineart_object_load_worker(TaskPool *__restrict, LineartObjectLoadTaskInfo *olti)
void MOD_lineart_destroy_render_data_v3(GreasePencilLineartModifierData *lmd)
void lineart_destroy_render_data_keep_init(LineartData *ld)
static int lineart_point_on_line_segment(double v[2], double v0[2], double v1[2])
static uchar lineart_intersection_priority_check(Collection *c, Object *ob)
static void lineart_mvert_transform_task(void *__restrict userdata, const int i, const TaskParallelTLS *__restrict)
#define INTERSECT_JUST_GREATER(is, order, num, index)
static void lineart_identify_corner_tri_feature_edges(void *__restrict userdata, const int i, const TaskParallelTLS *__restrict tls)
static bool lineart_schedule_new_triangle_task(LineartIsecThread *th)
static void lineart_discard_duplicated_edges(LineartEdge *old_e)
static void feat_data_sum_reduce(const void *__restrict, void *__restrict chunk_join, void *__restrict chunk)
#define LRT_TRI_SAME_POINT(tri, i, pt)
static void lineart_clear_linked_edges_recursive(LineartData *ld, LineartBoundingArea *root_ba)
void lineart_edge_cut(LineartData *ld, LineartEdge *e, double start, double end, uchar material_mask_bits, uchar mat_occlusion, uint32_t shadow_bits)
static int lineart_usage_check(Collection *c, Object *ob, bool is_render)
static bool lineart_get_edge_bounding_areas(LineartData *ld, LineartEdge *e, int *rowbegin, int *rowend, int *colbegin, int *colend)
static void lineart_discard_segment(LineartData *ld, LineartEdgeSegment *es)
LineartPointTri
@ LRT_INSIDE_TRIANGLE
@ LRT_ON_TRIANGLE
@ LRT_OUTSIDE_TRIANGLE
void lineart_main_perspective_division(LineartData *ld)
void MOD_lineart_gpencil_generate_v3(const LineartCache *cache, const blender::float4x4 &inverse_mat, Depsgraph *depsgraph, blender::bke::greasepencil::Drawing &drawing, const int8_t source_type, Object *source_object, Collection *source_collection, const int level_start, const int level_end, const int mat_nr, const int16_t edge_types, const uchar mask_switches, const uchar material_mask_bits, const uchar intersection_mask, const float thickness, const float opacity, const uchar shadow_selection, const uchar silhouette_mode, const char *source_vgname, const char *vgname, const int modifier_flags, const int modifier_calculation_flags)
static LineartElementLinkNode * lineart_memory_get_edge_space(LineartData *ld)
LineartBoundingArea * lineart_edge_first_bounding_area(LineartData *ld, double *fbcoord1, double *fbcoord2)
static LineartEdgeNeighbor * lineart_build_edge_neighbor(Mesh *mesh, int total_edges)
static void lineart_main_remove_unused_lines_from_tiles(LineartData *ld)
static void lineart_bounding_areas_connect_new(LineartData *ld, LineartBoundingArea *root)
#define INTERSECT_SORT_MIN_TO_MAX_3(ia, ib, ic, lst)
void lineart_finalize_object_edge_array_reserve(LineartPendingEdges *pe, int count)
#define SELECT_EDGE(e_num, v1_link, v2_link, new_tri)
static void lineart_main_remove_unused_lines_recursive(LineartBoundingArea *ba, uint8_t max_occlusion)
BLI_INLINE bool lineart_occlusion_is_adjacent_intersection(LineartEdge *e, LineartTriangle *tri)
static bool lineart_triangle_get_other_verts(const LineartTriangle *tri, const LineartVert *vt, LineartVert **l, LineartVert **r)
static void lineart_bounding_area_link_triangle(LineartData *ld, LineartBoundingArea *root_ba, LineartTriangle *tri, double l_r_u_b[4], int recursive, int recursive_level, bool do_intersection, LineartIsecThread *th)
#define LRT_CULL_ENSURE_MEMORY
bool MOD_lineart_compute_feature_lines_v3(Depsgraph *depsgraph, GreasePencilLineartModifierData &lmd, LineartCache **cached_result, bool enable_stroke_depth_offset)
#define LRT_ISEC(index)
static bool lineart_geometry_check_visible(double model_view_proj[4][4], double shift_x, double shift_y, Mesh *use_mesh)
#define LRT_GUARD_NOT_FOUND
void lineart_register_intersection_shadow_cuts(struct LineartData *ld, struct ListBase *shadow_elns)
#define LRT_BOUND_AREA_CROSSES(b1, b2)
#define LRT_EDGE_BA_MARCHING_BEGIN(fb1, fb2)
#define LRT_EDGE_BA_MARCHING_NEXT(fb1, fb2)
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)
void lineart_register_shadow_cuts(struct LineartData *ld, struct LineartEdge *e, struct LineartEdge *shadow_edge)
LineartElementLinkNode * lineart_find_matching_eln(struct ListBase *shadow_elns, int obindex)
#define LRT_EDGE_BA_MARCHING_END
void * lineart_list_append_pointer_pool_thread(ListBase *h, struct LineartStaticMemPool *smp, void *data)
void lineart_main_make_enclosed_shapes(struct LineartData *ld, struct LineartData *shadow_ld)
#define LRT_ITER_ALL_LINES_END
void lineart_count_and_print_render_buffer_memory(struct LineartData *ld)
LineartEdge * lineart_find_matching_edge(struct LineartElementLinkNode *shadow_eln, uint64_t edge_identifier)
void lineart_matrix_perspective_44d(double(*mProjection)[4], double fFov_rad, double fAspect, double zMin, double zMax)
#define LRT_BA_ROWS
void * lineart_mem_acquire_thread(struct LineartStaticMemPool *smp, size_t size)
void * lineart_list_append_pointer_pool(ListBase *h, struct LineartStaticMemPool *smp, void *data)
void lineart_list_remove_pointer_item_no_free(ListBase *h, LinkData *lip)
#define LRT_ITER_ALL_LINES_BEGIN
void lineart_main_transform_and_add_shadow(struct LineartData *ld, struct LineartElementLinkNode *veln, struct LineartElementLinkNode *eeln)
bool lineart_main_try_generate_shadow_v3(struct Depsgraph *depsgraph, struct Scene *scene, struct LineartData *original_ld, struct GreasePencilLineartModifierData *lmd, struct LineartStaticMemPool *shadow_data_pool, struct LineartElementLinkNode **r_veln, struct LineartElementLinkNode **r_eeln, struct ListBase *r_calculated_edges_eln_list, struct LineartData **r_shadow_ld_if_reproject)
void lineart_mem_destroy(struct LineartStaticMemPool *smp)
void * lineart_list_append_pointer_pool_sized_thread(ListBase *h, LineartStaticMemPool *smp, void *data, int size)
void lineart_matrix_ortho_44d(double(*mProjection)[4], double xMin, double xMax, double yMin, double yMax, double zMin, double zMax)
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
ccl_device_inline float2 fabs(const float2 a)
static ulong * next
#define RB
#define LB
#define G(x, y, z)
int3 corner_tri_get_real_edges(Span< int2 > edges, Span< int > corner_verts, Span< int > corner_edges, const int3 &corner_tri)
Curves * curves_new_nomain(int points_num, int curves_num)
bke::GeometrySet join_geometries(Span< bke::GeometrySet > geometries, const bke::AttributeFilter &attribute_filter, const std::optional< Span< bke::GeometryComponent::Type > > &component_types_to_join=std::nullopt)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
MatBase< float, 4, 4 > float4x4
void parallel_sort(RandomAccessIterator begin, RandomAccessIterator end)
Definition BLI_sort.hh:23
VecBase< float, 2 > float2
VecBase< int32_t, 3 > int3
VecBase< float, 3 > float3
Render * RE_GetSceneRender(const Scene *scene)
float vec[8][3]
float clip_end
char sensor_fit
float sensor_y
float sensor_x
float clip_start
float ortho_scale
uint8_t lineart_intersection_priority
uint8_t lineart_intersection_mask
ListBase vertex_group_names
CurvesGeometry geometry
blender::Set< const Object * > * included_objects
blender::Span< int > corner_verts
bool use_freestyle_face
blender::VArray< bool > sharp_edges
LineartEdgeNeighbor * edge_nabr
Object * ob_eval
blender::Span< blender::int2 > edges
blender::Span< int > corner_edges
bool use_freestyle_edge
blender::Span< int > material_indices
LineartVert * v_array
blender::VArray< bool > sharp_faces
blender::Span< int3 > corner_tris
LineartTriangle * tri_array
blender::Span< int > tri_faces
LineartData * ld
float crease_threshold
LineartAdjacentEdge * adj_e
blender::Span< int > corner_verts
LineartEdgeNeighbor * edge_nabr
blender::Span< int > tri_faces
blender::Span< int3 > corner_tris
struct LineartModifierRuntime * runtime
struct ID * orig_id
Definition DNA_ID.h:466
LineartBoundingArea * child
uint32_t insider_triangle_count
LineartTriangle ** linked_triangles
LineartEdge ** linked_lines
uint32_t max_triangle_count
ListBase shadow_elns
ListBase chains
LineartStaticMemPool chain_data_pool
uint16_t all_enabled_edge_types
LineartStaticMemPool shadow_data_pool
LineartEdgeChain * chain
float cam_obmat_secondary[4][4]
double view_projection[4][4]
double view_vector_secondary[3]
double camera_pos_secondary[3]
bool filter_face_mark_keep_contour
double active_camera_pos[3]
double view[4][4]
float angle_splitting_threshold
float cam_obmat[4][4]
ListBase vertex_buffer_pointers
ListBase line_buffer_pointers
ListBase triangle_adjacent_pointers
ListBase triangle_buffer_pointers
uint32_t initial_tile_count
LineartBoundingArea * initials
LineartStaticMemPool render_data_pool
struct LineartData::_conf conf
struct LineartData::_geom geom
struct LineartData::_qtree qtree
int isect_scheduled_up_to_index
ListBase chains
LineartElementLinkNode * isect_scheduled_up_to
ListBase wasted_cuts
SpinLock lock_task
LineartStaticMemPool * edge_data_pool
SpinLock lock_cuts
struct LineartPendingEdges pending_edges
LineartStaticMemPool * chain_data_pool
uint32_t index_offset
LineartEdgeSegment * next
LineartEdgeSegment * prev
uint32_t shadow_mask_bits
uint16_t flags
LineartVert * v1
LineartVert * v2
ListBase segments
LineartTriangle * t2
uint64_t edge_identifier
LineartTriangle * t1
Object * object_ref
LineartElementLinkNode * next
eLineArtElementNodeFlag flags
LineartData * ld
LineartIsecThread * threads
LineartTriangle * tri1
LineartTriangle * tri2
LineartElementLinkNode * pending_from
LineartIsecSingle * array
LineartData * ld
LineartElementLinkNode * pending_to
blender::Set< const Object * > object_dependencies
LineartElementLinkNode * v_eln
LineartObjectInfo * next
double model_view[4][4]
Object * original_ob_eval
double normal[4][4]
double model_view_proj[4][4]
uint8_t override_intersection_mask
uint8_t intersection_priority
LineartPendingEdges pending_edges
LineartObjectInfo * pending
LineartEdge ** array
struct LineartData * ld
struct LineartPendingEdges pending_edges
LineartEdge * e[3]
LineartEdge * testing_e[1]
LineartTriangle base
LineartVert * v[3]
uint8_t material_mask_bits
LinkNode * intersecting_verts
uint8_t intersection_priority
uint8_t intersection_mask
uint32_t target_reference
uint8_t mat_occlusion
double fbcoord[4]
double gloc[3]
void * data
struct LinkData * next
void * last
void * first
unsigned char mat_occlusion
unsigned char intersection_priority
unsigned char material_mask_bits
struct MaterialLineArt lineart
MeshRuntimeHandle * runtime
ListBase vertex_group_names
int faces_num
unsigned char intersection_priority
ObjectLineArt lineart
struct Collection * master_collection
struct RenderData r
struct Object * camera
TaskParallelReduceFunc func_reduce
Definition BLI_task.h:176
size_t userdata_chunk_size
Definition BLI_task.h:164
int lineart_triangle_size
LineartTriangleAdjacent * tri_adj
blender::Span< blender::float3 > positions
blender::Span< int3 > corner_tris
LineartVert * vert_arr
blender::Span< bool > face_marks
LineartObjectInfo * ob_info
blender::Span< int > corner_verts
blender::Span< int > tri_faces
bool invert_face_marks
blender::Span< int > material_indices
LineartTriangle * tri_arr
double(* model_view_proj)[4]
blender::Span< blender::float3 > positions
LineartVert * v_arr
double(* model_view)[4]
static GeometrySet from_curves(Curves *curves, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
blender::BitVector is_loose_bits
i
Definition text_draw.cc:230
uint8_t flag
Definition wm_window.cc:139