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
1816
1817static void lineart_load_tri_task(void *__restrict userdata,
1818 const int i,
1819 const TaskParallelTLS *__restrict /*tls*/)
1820{
1821 TriData *tri_task_data = (TriData *)userdata;
1822 LineartObjectInfo *ob_info = tri_task_data->ob_info;
1823 const blender::Span<blender::float3> positions = tri_task_data->positions;
1824 const blender::Span<int> corner_verts = tri_task_data->corner_verts;
1825 const int3 &corner_tri = tri_task_data->corner_tris[i];
1826 const int face_i = tri_task_data->tri_faces[i];
1827 const blender::Span<int> material_indices = tri_task_data->material_indices;
1828
1829 LineartVert *vert_arr = tri_task_data->vert_arr;
1830 LineartTriangle *tri = tri_task_data->tri_arr;
1831
1832 tri = (LineartTriangle *)(((uchar *)tri) + tri_task_data->lineart_triangle_size * i);
1833
1834 int v1 = corner_verts[corner_tri[0]];
1835 int v2 = corner_verts[corner_tri[1]];
1836 int v3 = corner_verts[corner_tri[2]];
1837
1838 tri->v[0] = &vert_arr[v1];
1839 tri->v[1] = &vert_arr[v2];
1840 tri->v[2] = &vert_arr[v3];
1841
1842 /* Material mask bits and occlusion effectiveness assignment. */
1844 ob_info->original_ob_eval, material_indices.is_empty() ? 1 : material_indices[face_i] + 1);
1845 tri->material_mask_bits |= ((mat && (mat->lineart.flags & LRT_MATERIAL_MASK_ENABLED)) ?
1847 0);
1848 tri->mat_occlusion |= (mat ? mat->lineart.mat_occlusion : 1);
1849 tri->intersection_priority = ((mat && (mat->lineart.flags &
1852 ob_info->intersection_priority);
1853 tri->flags |= (mat && (mat->blend_flag & MA_BL_CULL_BACKFACE)) ?
1855 0;
1856
1858
1859 tri->target_reference = (ob_info->obindex | (i & LRT_OBINDEX_LOWER));
1860
1861 double gn[3];
1862 float no[3];
1863 normal_tri_v3(no, positions[v1], positions[v2], positions[v3]);
1864 copy_v3db_v3fl(gn, no);
1865 mul_v3_mat3_m4v3_db(tri->gn, ob_info->normal, gn);
1866 normalize_v3_db(tri->gn);
1867
1868 if (ob_info->usage == OBJECT_LRT_INTERSECTION_ONLY) {
1870 }
1871 else if (ob_info->usage == OBJECT_LRT_FORCE_INTERSECTION) {
1873 }
1876 }
1877
1878 /* Re-use this field to refer to adjacent info, will be cleared after culling stage. */
1879 tri->intersecting_verts = static_cast<LinkNode *>((void *)&tri_task_data->tri_adj[i]);
1880}
1888
1889static void lineart_edge_neighbor_init_task(void *__restrict userdata,
1890 const int i,
1891 const TaskParallelTLS *__restrict /*tls*/)
1892{
1893 EdgeNeighborData *en_data = (EdgeNeighborData *)userdata;
1894 LineartAdjacentEdge *adj_e = &en_data->adj_e[i];
1895 const int3 &tri = en_data->corner_tris[i / 3];
1896 LineartEdgeNeighbor *edge_nabr = &en_data->edge_nabr[i];
1897 const blender::Span<int> corner_verts = en_data->corner_verts;
1898
1899 adj_e->e = i;
1900 adj_e->v1 = corner_verts[tri[i % 3]];
1901 adj_e->v2 = corner_verts[tri[(i + 1) % 3]];
1902 if (adj_e->v1 > adj_e->v2) {
1903 std::swap(adj_e->v1, adj_e->v2);
1904 }
1905 edge_nabr->e = -1;
1906
1907 edge_nabr->v1 = adj_e->v1;
1908 edge_nabr->v2 = adj_e->v2;
1909 edge_nabr->flags = 0;
1910}
1911
1913{
1915 ai, ai + length, [](const LineartAdjacentEdge &p1, const LineartAdjacentEdge &p2) {
1916 int a = p1.v1 - p2.v1;
1917 int b = p1.v2 - p2.v2;
1918 /* `parallel_sort()` requires `cmp()` to return true when the first element needs to appear
1919 * before the second element in the sorted array, false otherwise (strict weak ordering),
1920 * see https://en.cppreference.com/w/cpp/named_req/Compare. */
1921 if (a < 0) {
1922 return true;
1923 }
1924 if (a > 0) {
1925 return false;
1926 }
1927 return b < 0;
1928 });
1929}
1930
1932{
1933 /* Because the mesh is triangulated, so `mesh->edges_num` should be reliable? */
1935 "LineartAdjacentEdge arr");
1937 size_t(total_edges), "LineartEdgeNeighbor arr");
1938
1939 TaskParallelSettings en_settings;
1941 /* Set the minimum amount of edges a thread has to process. */
1942 en_settings.min_iter_per_thread = 50000;
1943
1944 EdgeNeighborData en_data;
1945 en_data.adj_e = adj_e;
1946 en_data.edge_nabr = edge_nabr;
1947 en_data.corner_verts = mesh->corner_verts();
1948 en_data.corner_tris = mesh->corner_tris();
1949 en_data.tri_faces = mesh->corner_tri_faces();
1950
1951 BLI_task_parallel_range(0, total_edges, &en_data, lineart_edge_neighbor_init_task, &en_settings);
1952
1953 lineart_sort_adjacent_items(adj_e, total_edges);
1954
1955 for (int i = 0; i < total_edges - 1; i++) {
1956 if (adj_e[i].v1 == adj_e[i + 1].v1 && adj_e[i].v2 == adj_e[i + 1].v2) {
1957 edge_nabr[adj_e[i].e].e = adj_e[i + 1].e;
1958 edge_nabr[adj_e[i + 1].e].e = adj_e[i].e;
1959 }
1960 }
1961
1962 MEM_freeN(adj_e);
1963
1964 return edge_nabr;
1965}
1966
1968 LineartData *la_data,
1969 ListBase *shadow_elns)
1970{
1971 using namespace blender;
1972 Mesh *mesh = ob_info->original_me;
1973 if (!mesh->edges_num) {
1974 return;
1975 }
1976
1977 /* Triangulate. */
1978 const Span<int3> corner_tris = mesh->corner_tris();
1979 const AttributeAccessor attributes = mesh->attributes();
1980 const VArraySpan material_indices = *attributes.lookup<int>("material_index", AttrDomain::Face);
1981
1982 /* Check if we should look for custom data tags like Freestyle edges or faces. */
1983 bool can_find_freestyle_edge = false;
1984 int layer_index = CustomData_get_active_layer_index(&mesh->edge_data, CD_FREESTYLE_EDGE);
1985 if (layer_index != -1) {
1986 can_find_freestyle_edge = true;
1987 }
1988
1989 bool can_find_freestyle_face = false;
1990 layer_index = CustomData_get_active_layer_index(&mesh->face_data, CD_FREESTYLE_FACE);
1991 if (layer_index != -1) {
1992 can_find_freestyle_face = true;
1993 }
1994
1995 /* If we allow duplicated edges, one edge should get added multiple times if is has been
1996 * classified as more than one edge type. This is so we can create multiple different line type
1997 * chains containing the same edge. */
1998 LineartVert *la_v_arr = static_cast<LineartVert *>(lineart_mem_acquire_thread(
1999 &la_data->render_data_pool, sizeof(LineartVert) * mesh->verts_num));
2000 LineartTriangle *la_tri_arr = static_cast<LineartTriangle *>(lineart_mem_acquire_thread(
2001 &la_data->render_data_pool, corner_tris.size() * la_data->sizeof_triangle));
2002
2003 Object *orig_ob = ob_info->original_ob;
2004
2005 BLI_spin_lock(&la_data->lock_task);
2006 LineartElementLinkNode *elem_link_node = static_cast<LineartElementLinkNode *>(
2008 &la_data->render_data_pool,
2009 la_v_arr,
2010 sizeof(LineartElementLinkNode)));
2011 BLI_spin_unlock(&la_data->lock_task);
2012
2013 elem_link_node->obindex = ob_info->obindex;
2014 elem_link_node->element_count = mesh->verts_num;
2015 elem_link_node->object_ref = orig_ob;
2016 ob_info->v_eln = elem_link_node;
2017
2018 bool use_auto_smooth = false;
2019 float crease_angle = 0;
2020 if (orig_ob->lineart.flags & OBJECT_LRT_OWN_CREASE) {
2021 crease_angle = cosf(M_PI - orig_ob->lineart.crease_threshold);
2022 }
2023 else {
2024 crease_angle = la_data->conf.crease_threshold;
2025 }
2026
2027 /* FIXME(Yiming): Hack for getting clean 3D text, the seam that extruded text object creates
2028 * erroneous detection on creases. Future configuration should allow options. */
2029 if (orig_ob->type == OB_FONT) {
2030 elem_link_node->flags |= LRT_ELEMENT_BORDER_ONLY;
2031 }
2032
2033 BLI_spin_lock(&la_data->lock_task);
2034 elem_link_node = static_cast<LineartElementLinkNode *>(
2036 &la_data->render_data_pool,
2037 la_tri_arr,
2038 sizeof(LineartElementLinkNode)));
2039 BLI_spin_unlock(&la_data->lock_task);
2040
2041 int usage = ob_info->usage;
2042
2043 elem_link_node->element_count = corner_tris.size();
2044 elem_link_node->object_ref = orig_ob;
2045 elem_link_node->flags = eLineArtElementNodeFlag(
2046 elem_link_node->flags |
2048
2049 /* Note this memory is not from pool, will be deleted after culling. */
2051 size_t(corner_tris.size()), "LineartTriangleAdjacent");
2052 /* Link is minimal so we use pool anyway. */
2053 BLI_spin_lock(&la_data->lock_task);
2055 &la_data->geom.triangle_adjacent_pointers, &la_data->render_data_pool, tri_adj);
2056 BLI_spin_unlock(&la_data->lock_task);
2057
2058 /* Convert all vertices to lineart verts. */
2059 TaskParallelSettings vert_settings;
2061 /* Set the minimum amount of verts a thread has to process. */
2062 vert_settings.min_iter_per_thread = 4000;
2063
2064 VertData vert_data;
2065 vert_data.positions = mesh->vert_positions();
2066 vert_data.v_arr = la_v_arr;
2067 vert_data.model_view = ob_info->model_view;
2068 vert_data.model_view_proj = ob_info->model_view_proj;
2069
2071 0, mesh->verts_num, &vert_data, lineart_mvert_transform_task, &vert_settings);
2072
2073 /* Convert all mesh triangles into lineart triangles.
2074 * Also create an edge map to get connectivity between edges and triangles. */
2075 TaskParallelSettings tri_settings;
2077 /* Set the minimum amount of triangles a thread has to process. */
2078 tri_settings.min_iter_per_thread = 4000;
2079
2080 TriData tri_data;
2081 tri_data.ob_info = ob_info;
2082 tri_data.positions = mesh->vert_positions();
2083 tri_data.corner_tris = corner_tris;
2084 tri_data.tri_faces = mesh->corner_tri_faces();
2085 tri_data.corner_verts = mesh->corner_verts();
2086 tri_data.material_indices = material_indices;
2087 tri_data.vert_arr = la_v_arr;
2088 tri_data.tri_arr = la_tri_arr;
2089 tri_data.lineart_triangle_size = la_data->sizeof_triangle;
2090 tri_data.tri_adj = tri_adj;
2091
2092 uint32_t total_edges = corner_tris.size() * 3;
2093
2094 BLI_task_parallel_range(0, corner_tris.size(), &tri_data, lineart_load_tri_task, &tri_settings);
2095
2096 /* Check for contour lines in the mesh.
2097 * IE check if the triangle edges lies in area where the triangles go from front facing to back
2098 * facing.
2099 */
2100 EdgeFeatReduceData edge_reduce = {0};
2101 TaskParallelSettings edge_feat_settings;
2102 BLI_parallel_range_settings_defaults(&edge_feat_settings);
2103 /* Set the minimum amount of edges a thread has to process. */
2104 edge_feat_settings.min_iter_per_thread = 4000;
2105 edge_feat_settings.userdata_chunk = &edge_reduce;
2106 edge_feat_settings.userdata_chunk_size = sizeof(EdgeFeatReduceData);
2107 edge_feat_settings.func_reduce = feat_data_sum_reduce;
2108
2109 const VArray<bool> sharp_edges = *attributes.lookup_or_default<bool>(
2110 "sharp_edge", AttrDomain::Edge, false);
2111 const VArray<bool> sharp_faces = *attributes.lookup_or_default<bool>(
2112 "sharp_face", AttrDomain::Face, false);
2113
2114 EdgeFeatData edge_feat_data = {nullptr};
2115 edge_feat_data.ld = la_data;
2116 edge_feat_data.mesh = mesh;
2117 edge_feat_data.ob_eval = ob_info->original_ob_eval;
2118 edge_feat_data.material_indices = material_indices;
2119 edge_feat_data.edges = mesh->edges();
2120 edge_feat_data.corner_verts = mesh->corner_verts();
2121 edge_feat_data.corner_edges = mesh->corner_edges();
2122 edge_feat_data.corner_tris = corner_tris;
2123 edge_feat_data.tri_faces = mesh->corner_tri_faces();
2124 edge_feat_data.sharp_edges = sharp_edges;
2125 edge_feat_data.sharp_faces = sharp_faces;
2126 edge_feat_data.edge_nabr = lineart_build_edge_neighbor(mesh, total_edges);
2127 edge_feat_data.tri_array = la_tri_arr;
2128 edge_feat_data.v_array = la_v_arr;
2129 edge_feat_data.crease_threshold = crease_angle;
2130 edge_feat_data.use_auto_smooth = use_auto_smooth;
2131 edge_feat_data.use_freestyle_face = can_find_freestyle_face;
2132 edge_feat_data.use_freestyle_edge = can_find_freestyle_edge;
2133 if (edge_feat_data.use_freestyle_face) {
2134 edge_feat_data.freestyle_face_index = CustomData_get_layer_index(&mesh->face_data,
2136 }
2137 if (edge_feat_data.use_freestyle_edge) {
2138 edge_feat_data.freestyle_edge_index = CustomData_get_layer_index(&mesh->edge_data,
2140 }
2141
2143 total_edges,
2144 &edge_feat_data,
2146 &edge_feat_settings);
2147
2148 LooseEdgeData loose_data = {0};
2149
2150 if (la_data->conf.use_loose) {
2151 /* Only identifying floating edges at this point because other edges has been taken care of
2152 * inside #lineart_identify_corner_tri_feature_edges function. */
2153 const LooseEdgeCache &loose_edges = mesh->loose_edges();
2154 loose_data.loose_array = MEM_malloc_arrayN<int>(size_t(loose_edges.count), __func__);
2155 if (loose_edges.count > 0) {
2156 loose_data.loose_count = 0;
2157 for (const int64_t edge_i : IndexRange(mesh->edges_num)) {
2158 if (loose_edges.is_loose_bits[edge_i]) {
2159 loose_data.loose_array[loose_data.loose_count] = int(edge_i);
2160 loose_data.loose_count++;
2161 }
2162 }
2163 }
2164 }
2165
2166 int allocate_la_e = edge_reduce.feat_edges + loose_data.loose_count;
2167
2168 LineartEdge *la_edge_arr = static_cast<LineartEdge *>(
2169 lineart_mem_acquire_thread(la_data->edge_data_pool, sizeof(LineartEdge) * allocate_la_e));
2171 la_data->edge_data_pool, sizeof(LineartEdgeSegment) * allocate_la_e));
2172 BLI_spin_lock(&la_data->lock_task);
2173 elem_link_node = static_cast<LineartElementLinkNode *>(
2175 la_data->edge_data_pool,
2176 la_edge_arr,
2177 sizeof(LineartElementLinkNode)));
2178 BLI_spin_unlock(&la_data->lock_task);
2179 elem_link_node->element_count = allocate_la_e;
2180 elem_link_node->object_ref = orig_ob;
2181 elem_link_node->obindex = ob_info->obindex;
2182
2183 LineartElementLinkNode *shadow_eln = nullptr;
2184 if (shadow_elns) {
2185 shadow_eln = lineart_find_matching_eln(shadow_elns, ob_info->obindex);
2186 }
2187
2188 /* Start of the edge/seg arr */
2189 LineartEdge *la_edge;
2190 LineartEdgeSegment *la_seg;
2191 la_edge = la_edge_arr;
2192 la_seg = la_seg_arr;
2193
2194 for (int i = 0; i < total_edges; i++) {
2195 LineartEdgeNeighbor *edge_nabr = &edge_feat_data.edge_nabr[i];
2196
2197 if (i < edge_nabr->e) {
2198 continue;
2199 }
2200
2201 /* Not a feature line, so we skip. */
2202 if (edge_nabr->flags == 0) {
2203 continue;
2204 }
2205
2206 LineartEdge *edge_added = nullptr;
2207
2208 /* See eLineartEdgeFlag for details. */
2209 for (int flag_bit = 0; flag_bit < LRT_MESH_EDGE_TYPES_COUNT; flag_bit++) {
2210 int use_type = LRT_MESH_EDGE_TYPES[flag_bit];
2211 if (!(use_type & edge_nabr->flags)) {
2212 continue;
2213 }
2214
2215 la_edge->v1 = &la_v_arr[edge_nabr->v1];
2216 la_edge->v2 = &la_v_arr[edge_nabr->v2];
2217 int findex = i / 3;
2218 la_edge->t1 = lineart_triangle_from_index(la_data, la_tri_arr, findex);
2219 if (!edge_added) {
2220 lineart_triangle_adjacent_assign(la_edge->t1, &tri_adj[findex], la_edge);
2221 }
2222 if (edge_nabr->e != -1) {
2223 findex = edge_nabr->e / 3;
2224 la_edge->t2 = lineart_triangle_from_index(la_data, la_tri_arr, findex);
2225 if (!edge_added) {
2226 lineart_triangle_adjacent_assign(la_edge->t2, &tri_adj[findex], la_edge);
2227 }
2228 }
2229 la_edge->flags = use_type;
2230 la_edge->object_ref = orig_ob;
2231 la_edge->edge_identifier = LRT_EDGE_IDENTIFIER(ob_info, la_edge);
2232 BLI_addtail(&la_edge->segments, la_seg);
2233
2234 if (shadow_eln) {
2235 /* TODO(Yiming): It's gonna be faster to do this operation after second stage occlusion if
2236 * we only need visible segments to have shadow info, however that way we lose information
2237 * on "shadow behind transparency window" type of region. */
2238 LineartEdge *shadow_e = lineart_find_matching_edge(shadow_eln, la_edge->edge_identifier);
2239 if (shadow_e) {
2240 lineart_register_shadow_cuts(la_data, la_edge, shadow_e);
2241 }
2242 }
2243
2244 if (ELEM(usage,
2249 {
2250 lineart_add_edge_to_array_thread(ob_info, la_edge);
2251 }
2252
2253 if (edge_added) {
2255 }
2256
2257 edge_added = la_edge;
2258
2259 la_edge++;
2260 la_seg++;
2261
2262 if (!la_data->conf.allow_duplicated_types) {
2263 break;
2264 }
2265 }
2266 }
2267
2268 if (loose_data.loose_array) {
2269 const Span<int2> edges = mesh->edges();
2270 for (int i = 0; i < loose_data.loose_count; i++) {
2271 const int2 &edge = edges[loose_data.loose_array[i]];
2272 la_edge->v1 = &la_v_arr[edge[0]];
2273 la_edge->v2 = &la_v_arr[edge[1]];
2275 la_edge->object_ref = orig_ob;
2276 la_edge->edge_identifier = LRT_EDGE_IDENTIFIER(ob_info, la_edge);
2277 BLI_addtail(&la_edge->segments, la_seg);
2278 if (ELEM(usage,
2283 {
2284 lineart_add_edge_to_array_thread(ob_info, la_edge);
2285 if (shadow_eln) {
2286 LineartEdge *shadow_e = lineart_find_matching_edge(shadow_eln, la_edge->edge_identifier);
2287 if (shadow_e) {
2288 lineart_register_shadow_cuts(la_data, la_edge, shadow_e);
2289 }
2290 }
2291 }
2292 la_edge++;
2293 la_seg++;
2294 }
2295 MEM_SAFE_FREE(loose_data.loose_array);
2296 }
2297
2298 MEM_freeN(edge_feat_data.edge_nabr);
2299
2300 if (ob_info->free_use_mesh) {
2301 BKE_id_free(nullptr, mesh);
2302 }
2303}
2304
2305static void lineart_object_load_worker(TaskPool *__restrict /*pool*/,
2307{
2308 for (LineartObjectInfo *obi = olti->pending; obi; obi = obi->next) {
2309 lineart_geometry_object_load(obi, olti->ld, olti->shadow_elns);
2310 }
2311}
2312
2314{
2316 uchar result = lineart_intersection_mask_check(cc->collection, ob);
2317 if (result) {
2318 return result;
2319 }
2320 }
2321
2322 if (BKE_collection_has_object(c, (Object *)(ob->id.orig_id))) {
2324 return c->lineart_intersection_mask;
2325 }
2326 }
2327
2328 return 0;
2329}
2330
2332{
2334 return ob->lineart.intersection_priority;
2335 }
2336
2338 uchar result = lineart_intersection_priority_check(cc->collection, ob);
2339 if (result) {
2340 return result;
2341 }
2342 }
2343 if (BKE_collection_has_object(c, (Object *)(ob->id.orig_id))) {
2346 }
2347 }
2348 return 0;
2349}
2350
2355static int lineart_usage_check(Collection *c, Object *ob, bool is_render)
2356{
2357
2358 if (!c) {
2359 return OBJECT_LRT_INHERIT;
2360 }
2361
2362 int object_has_special_usage = (ob->lineart.usage != OBJECT_LRT_INHERIT);
2363
2364 if (object_has_special_usage) {
2365 return ob->lineart.usage;
2366 }
2367
2368 if (c->gobject.first) {
2369 if (BKE_collection_has_object(c, (Object *)(ob->id.orig_id))) {
2370 if ((is_render && (c->flag & COLLECTION_HIDE_RENDER)) ||
2371 ((!is_render) && (c->flag & COLLECTION_HIDE_VIEWPORT)))
2372 {
2373 return OBJECT_LRT_EXCLUDE;
2374 }
2375 if (ob->lineart.usage == OBJECT_LRT_INHERIT) {
2376 switch (c->lineart_usage) {
2380 return OBJECT_LRT_EXCLUDE;
2387 }
2388 return OBJECT_LRT_INHERIT;
2389 }
2390 return ob->lineart.usage;
2391 }
2392 }
2393
2395 int result = lineart_usage_check(cc->collection, ob, is_render);
2396 if (result > OBJECT_LRT_INHERIT) {
2397 return result;
2398 }
2399 }
2400
2401 return OBJECT_LRT_INHERIT;
2402}
2403
2405 LineartObjectInfo *obi,
2406 int thread_count,
2407 int this_face_count)
2408{
2409 LineartObjectLoadTaskInfo *use_olti = olti_list;
2410 uint64_t min_face = use_olti->total_faces;
2411 for (int i = 0; i < thread_count; i++) {
2412 if (olti_list[i].total_faces < min_face) {
2413 min_face = olti_list[i].total_faces;
2414 use_olti = &olti_list[i];
2415 }
2416 }
2417
2418 use_olti->total_faces += this_face_count;
2419 obi->next = use_olti->pending;
2420 use_olti->pending = obi;
2421}
2422
2423static bool lineart_geometry_check_visible(double model_view_proj[4][4],
2424 double shift_x,
2425 double shift_y,
2426 Mesh *use_mesh)
2427{
2428 using namespace blender;
2429 if (!use_mesh) {
2430 return false;
2431 }
2432 const std::optional<Bounds<float3>> bounds = use_mesh->bounds_min_max();
2433 if (!bounds.has_value()) {
2434 return false;
2435 }
2436 BoundBox bb;
2437 BKE_boundbox_init_from_minmax(&bb, bounds.value().min, bounds.value().max);
2438
2439 double co[8][4];
2440 double tmp[3];
2441 for (int i = 0; i < 8; i++) {
2442 copy_v3db_v3fl(co[i], bb.vec[i]);
2443 copy_v3_v3_db(tmp, co[i]);
2444 mul_v4_m4v3_db(co[i], model_view_proj, tmp);
2445 co[i][0] -= shift_x * 2 * co[i][3];
2446 co[i][1] -= shift_y * 2 * co[i][3];
2447 }
2448
2449 bool cond[6] = {true, true, true, true, true, true};
2450 /* Because for a point to be inside clip space, it must satisfy `-Wc <= XYCc <= Wc`, here if
2451 * all verts falls to the same side of the clip space border, we know it's outside view. */
2452 for (int i = 0; i < 8; i++) {
2453 cond[0] &= (co[i][0] < -co[i][3]);
2454 cond[1] &= (co[i][0] > co[i][3]);
2455 cond[2] &= (co[i][1] < -co[i][3]);
2456 cond[3] &= (co[i][1] > co[i][3]);
2457 cond[4] &= (co[i][2] < -co[i][3]);
2458 cond[5] &= (co[i][2] > co[i][3]);
2459 }
2460 for (int i = 0; i < 6; i++) {
2461 if (cond[i]) {
2462 return false;
2463 }
2464 }
2465 return true;
2466}
2467
2469 Depsgraph *depsgraph,
2470 Scene *scene,
2471 Object *ob,
2472 Object *ref_ob,
2473 const float use_mat[4][4],
2474 bool is_render,
2476 int thread_count,
2477 int obindex)
2478{
2479 LineartObjectInfo *obi = static_cast<LineartObjectInfo *>(
2481 obi->usage = lineart_usage_check(scene->master_collection, ob, is_render);
2484 Mesh *use_mesh;
2485
2486 if (obi->usage == OBJECT_LRT_EXCLUDE) {
2487 return;
2488 }
2489
2490 obi->obindex = obindex << LRT_OBINDEX_SHIFT;
2491
2492 /* Prepare the matrix used for transforming this specific object (instance). This has to be
2493 * done before mesh boundbox check because the function needs that. */
2495 mul_m4db_m4db_m4fl(obi->model_view, ld->conf.view, use_mat);
2496
2498 return;
2499 }
2500 if (ob->type == OB_MESH) {
2501 use_mesh = BKE_object_get_evaluated_mesh(ob);
2502 if ((!use_mesh) || use_mesh->runtime->edit_mesh) {
2503 /* If the object is being edited, then the mesh is not evaluated fully into the final
2504 * result, do not load them. This could be caused by incorrect evaluation order due to
2505 * the way line art uses depsgraph.See #102612 for explanation of this workaround. */
2506 return;
2507 }
2508 }
2509 else {
2510 use_mesh = BKE_mesh_new_from_object(depsgraph, ob, true, true, true);
2511 }
2512
2513 /* In case we still can not get any mesh geometry data from the object, same as above. */
2514 if (!use_mesh) {
2515 return;
2516 }
2517
2519 obi->model_view_proj, ld->conf.shift_x, ld->conf.shift_y, use_mesh))
2520 {
2521 return;
2522 }
2523
2524 if (ob->type != OB_MESH) {
2525 obi->free_use_mesh = true;
2526 }
2527
2528 /* Make normal matrix. */
2529 float imat[4][4];
2530 invert_m4_m4(imat, use_mat);
2531 transpose_m4(imat);
2532 copy_m4d_m4(obi->normal, imat);
2533
2534 obi->original_me = use_mesh;
2535 obi->original_ob = (ref_ob->id.orig_id ? (Object *)ref_ob->id.orig_id : ref_ob);
2537 lineart_geometry_load_assign_thread(olti, obi, thread_count, use_mesh->faces_num);
2538}
2539
2541 Scene *scene,
2542 Object *camera /* Still use camera arg for convenience. */,
2543 LineartData *ld,
2544 bool allow_duplicates,
2545 bool do_shadow_casting,
2546 ListBase *shadow_elns,
2547 blender::Set<const Object *> *included_objects)
2548{
2549 double proj[4][4], view[4][4], result[4][4];
2550 float inv[4][4];
2551
2552 if (!do_shadow_casting) {
2553 Camera *cam = static_cast<Camera *>(camera->data);
2554 float sensor = BKE_camera_sensor_size(cam->sensor_fit, cam->sensor_x, cam->sensor_y);
2555 int fit = BKE_camera_sensor_fit(cam->sensor_fit, ld->w, ld->h);
2556 double asp = (double(ld->w) / double(ld->h));
2557 if (ELEM(cam->type, CAM_PERSP, CAM_PANO, CAM_CUSTOM)) {
2558 if (fit == CAMERA_SENSOR_FIT_VERT && asp > 1) {
2559 sensor *= asp;
2560 }
2561 if (fit == CAMERA_SENSOR_FIT_HOR && asp < 1) {
2562 sensor /= asp;
2563 }
2564 const double fov = focallength_to_fov(cam->lens / (1 + ld->conf.overscan), sensor);
2565 lineart_matrix_perspective_44d(proj, fov, asp, cam->clip_start, cam->clip_end);
2566 }
2567 else if (cam->type == CAM_ORTHO) {
2568 const double w = cam->ortho_scale / 2;
2569 lineart_matrix_ortho_44d(proj, -w, w, -w / asp, w / asp, cam->clip_start, cam->clip_end);
2570 }
2571 else {
2572 BLI_assert(!"Unsupported camera type in lineart_main_load_geometries");
2573 unit_m4_db(proj);
2574 }
2575
2576 invert_m4_m4(inv, ld->conf.cam_obmat);
2577 mul_m4db_m4db_m4fl(result, proj, inv);
2578 copy_m4_m4_db(proj, result);
2580
2583 }
2584
2587
2588 double t_start;
2589 if (G.debug_value == 4000) {
2590 t_start = BLI_time_now_seconds();
2591 }
2592
2593 int thread_count = ld->thread_count;
2594 int bound_box_discard_count = 0;
2595 int obindex = 0;
2596
2597 /* This memory is in render buffer memory pool. So we don't need to free those after loading. */
2599 &ld->render_data_pool, sizeof(LineartObjectLoadTaskInfo) * thread_count));
2600
2602 bool is_render = eval_mode == DAG_EVAL_RENDER;
2603
2606
2607 /* Instance duplicated & particles. */
2608 if (allow_duplicates) {
2610 }
2611
2612 DEGObjectIterSettings deg_iter_settings = {nullptr};
2613 deg_iter_settings.depsgraph = depsgraph;
2614 deg_iter_settings.flags = flags;
2615 deg_iter_settings.included_objects = included_objects;
2616
2617 DEG_OBJECT_ITER_BEGIN (&deg_iter_settings, ob) {
2618
2619 obindex++;
2620
2621 Object *eval_ob = DEG_get_evaluated(depsgraph, ob);
2622
2623 if (!eval_ob) {
2624 continue;
2625 }
2626
2627 /* DEG_OBJECT_ITER_BEGIN will include the instanced mesh of these curve object types, so don't
2628 * load them twice. */
2629 if (allow_duplicates && ELEM(ob->type, OB_CURVES_LEGACY, OB_FONT, OB_SURF)) {
2630 continue;
2631 }
2632
2633 if (BKE_object_visibility(eval_ob, eval_mode) & OB_VISIBLE_SELF) {
2635 depsgraph,
2636 scene,
2637 eval_ob,
2638 eval_ob,
2639 eval_ob->object_to_world().ptr(),
2640 is_render,
2641 olti,
2642 thread_count,
2643 obindex);
2644 }
2645 }
2647
2649
2650 if (G.debug_value == 4000) {
2651 printf("thread count: %d\n", thread_count);
2652 }
2653 for (int i = 0; i < thread_count; i++) {
2654 olti[i].ld = ld;
2655 olti[i].shadow_elns = shadow_elns;
2656 olti[i].thread_id = i;
2657 BLI_task_pool_push(tp, (TaskRunFunction)lineart_object_load_worker, &olti[i], false, nullptr);
2658 }
2661
2662 /* The step below is to serialize vertex index in the whole scene, so
2663 * lineart_triangle_share_edge() can work properly from the lack of triangle adjacent info. */
2664 int global_i = 0;
2665
2666 int edge_count = 0;
2667 for (int i = 0; i < thread_count; i++) {
2668 for (LineartObjectInfo *obi = olti[i].pending; obi; obi = obi->next) {
2669 if (!obi->v_eln) {
2670 continue;
2671 }
2672 edge_count += obi->pending_edges.next;
2673 }
2674 }
2676
2677 for (int i = 0; i < thread_count; i++) {
2678 for (LineartObjectInfo *obi = olti[i].pending; obi; obi = obi->next) {
2679 if (!obi->v_eln) {
2680 continue;
2681 }
2682 LineartVert *v = (LineartVert *)obi->v_eln->pointer;
2683 int v_count = obi->v_eln->element_count;
2684 obi->v_eln->global_index_offset = global_i;
2685 for (int vi = 0; vi < v_count; vi++) {
2686 v[vi].index += global_i;
2687 }
2688 /* Register a global index increment. See #lineart_triangle_share_edge() and
2689 * #lineart_main_load_geometries() for detailed. It's okay that global_vindex might
2690 * eventually overflow, in such large scene it's virtually impossible for two vertex of the
2691 * same numeric index to come close together. */
2692 obi->global_i_offset = global_i;
2693 global_i += v_count;
2695 }
2696 }
2697
2698 if (G.debug_value == 4000) {
2699 double t_elapsed = BLI_time_now_seconds() - t_start;
2700 printf("Line art loading time: %lf\n", t_elapsed);
2701 printf("Discarded %d object from bound box check\n", bound_box_discard_count);
2702 }
2703}
2704
2710 const LineartVert *vt,
2711 LineartVert **l,
2712 LineartVert **r)
2713{
2714 if (tri->v[0] == vt) {
2715 *l = tri->v[1];
2716 *r = tri->v[2];
2717 return true;
2718 }
2719 if (tri->v[1] == vt) {
2720 *l = tri->v[2];
2721 *r = tri->v[0];
2722 return true;
2723 }
2724 if (tri->v[2] == vt) {
2725 *l = tri->v[0];
2726 *r = tri->v[1];
2727 return true;
2728 }
2729 return false;
2730}
2731
2733 const LineartEdge *e,
2734 bool allow_overlapping_edges)
2735{
2736 const LineartEdge *use_e = e;
2738 if (((e->target_reference & LRT_LIGHT_CONTOUR_TARGET) == tri->target_reference) ||
2739 (((e->target_reference >> 32) & LRT_LIGHT_CONTOUR_TARGET) == tri->target_reference))
2740 {
2741 return true;
2742 }
2743 }
2744 else {
2745 /* Normally we just determine from identifiers of adjacent triangles. */
2746 if ((use_e->t1 && use_e->t1->target_reference == tri->target_reference) ||
2747 (use_e->t2 && use_e->t2->target_reference == tri->target_reference))
2748 {
2749 return true;
2750 }
2751 }
2752
2753 /* If allows overlapping, then we compare the vertex coordinates one by one to determine if one
2754 * edge is from specific triangle. This is slower but can handle edge split cases very well. */
2755 if (allow_overlapping_edges) {
2756#define LRT_TRI_SAME_POINT(tri, i, pt) \
2757 ((LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[0], pt->gloc[0]) && \
2758 LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[1], pt->gloc[1]) && \
2759 LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[2], pt->gloc[2])) || \
2760 (LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[0], pt->gloc[0]) && \
2761 LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[1], pt->gloc[1]) && \
2762 LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[2], pt->gloc[2])))
2763 if ((LRT_TRI_SAME_POINT(tri, 0, e->v1) || LRT_TRI_SAME_POINT(tri, 1, e->v1) ||
2764 LRT_TRI_SAME_POINT(tri, 2, e->v1)) &&
2765 (LRT_TRI_SAME_POINT(tri, 0, e->v2) || LRT_TRI_SAME_POINT(tri, 1, e->v2) ||
2766 LRT_TRI_SAME_POINT(tri, 2, e->v2)))
2767 {
2768 return true;
2769 }
2770#undef LRT_TRI_SAME_POINT
2771 }
2772 return false;
2773}
2774
2775/* Sorting three intersection points from min to max,
2776 * the order for each intersection is set in `lst[0]` to `lst[2]`. */
2777#define INTERSECT_SORT_MIN_TO_MAX_3(ia, ib, ic, lst) \
2778 { \
2779 lst[0] = LRT_MIN3_INDEX(ia, ib, ic); \
2780 lst[1] = (((ia <= ib && ib <= ic) || (ic <= ib && ib <= ia)) ? \
2781 1 : \
2782 (((ic <= ia && ia <= ib) || (ib < ia && ia <= ic)) ? 0 : 2)); \
2783 lst[2] = LRT_MAX3_INDEX(ia, ib, ic); \
2784 }
2785
2786/* `ia ib ic` are ordered. */
2787#define INTERSECT_JUST_GREATER(is, order, num, index) \
2788 { \
2789 index = (num < is[order[0]] ? \
2790 order[0] : \
2791 (num < is[order[1]] ? order[1] : (num < is[order[2]] ? order[2] : -1))); \
2792 }
2793
2794/* `ia ib ic` are ordered. */
2795#define INTERSECT_JUST_SMALLER(is, order, num, index) \
2796 { \
2797 index = (num > is[order[2]] ? \
2798 order[2] : \
2799 (num > is[order[1]] ? order[1] : (num > is[order[0]] ? order[0] : -1))); \
2800 }
2801
2802#define LRT_ISEC(index) (index == 0 ? isec_e1 : (index == 1 ? isec_e2 : isec_e3))
2803#define LRT_PARALLEL(index) (index == 0 ? para_e1 : (index == 1 ? para_e2 : para_e3))
2804
2828 const LineartEdge *e,
2829 const double *override_camera_loc,
2830 const bool override_cam_is_persp,
2831 const bool allow_overlapping_edges,
2832 const double m_view_projection[4][4],
2833 const double camera_dir[3],
2834 const float cam_shift_x,
2835 const float cam_shift_y,
2836 double *from,
2837 double *to)
2838{
2839 double cross_ratios[3] = {0};
2840 int cross_order[3];
2841 int cross_v1 = -1, cross_v2 = -1;
2842 /* If the edge intersects with the triangle edges (including extensions). */
2843 int isec_e1, isec_e2, isec_e3;
2844 /* If edge is parallel to one of the edges in the triangle. */
2845 bool para_e1, para_e2, para_e3;
2846 enum LineartPointTri state_v1 = LRT_OUTSIDE_TRIANGLE, state_v2 = LRT_OUTSIDE_TRIANGLE;
2847
2848 double dir_v1[3];
2849 double dir_v2[3];
2850 double view_vector[4];
2851 double dir_cam[3];
2852 double dot_v1, dot_v2, dot_v1a, dot_v2a;
2853 double dot_f;
2854 double gloc[4], trans[4];
2855 double cut = -1;
2856
2857 double *LFBC = e->v1->fbcoord, *RFBC = e->v2->fbcoord, *FBC0 = tri->v[0]->fbcoord,
2858 *FBC1 = tri->v[1]->fbcoord, *FBC2 = tri->v[2]->fbcoord;
2859
2860 /* Overlapping not possible, return early. */
2861 if ((std::max({FBC0[0], FBC1[0], FBC2[0]}) < std::min(LFBC[0], RFBC[0])) ||
2862 (std::min({FBC0[0], FBC1[0], FBC2[0]}) > std::max(LFBC[0], RFBC[0])) ||
2863 (std::max({FBC0[1], FBC1[1], FBC2[1]}) < std::min(LFBC[1], RFBC[1])) ||
2864 (std::min({FBC0[1], FBC1[1], FBC2[1]}) > std::max(LFBC[1], RFBC[1])) ||
2865 (std::min({FBC0[3], FBC1[3], FBC2[3]}) > std::max(LFBC[3], RFBC[3])))
2866 {
2867 return false;
2868 }
2869
2870 /* If the line is one of the edge in the triangle, then it's not occluded. */
2871 if (lineart_edge_from_triangle(tri, e, allow_overlapping_edges)) {
2872 return false;
2873 }
2874
2875 /* Check if the line visually crosses one of the edge in the triangle. */
2876 isec_e1 = lineart_intersect_seg_seg(LFBC, RFBC, FBC0, FBC1, &cross_ratios[0], &para_e1);
2877 isec_e2 = lineart_intersect_seg_seg(LFBC, RFBC, FBC1, FBC2, &cross_ratios[1], &para_e2);
2878 isec_e3 = lineart_intersect_seg_seg(LFBC, RFBC, FBC2, FBC0, &cross_ratios[2], &para_e3);
2879
2880 /* Sort the intersection distance. */
2881 INTERSECT_SORT_MIN_TO_MAX_3(cross_ratios[0], cross_ratios[1], cross_ratios[2], cross_order);
2882
2883 sub_v3_v3v3_db(dir_v1, e->v1->gloc, tri->v[0]->gloc);
2884 sub_v3_v3v3_db(dir_v2, e->v2->gloc, tri->v[0]->gloc);
2885
2886 copy_v3_v3_db(dir_cam, camera_dir);
2887 copy_v3_v3_db(view_vector, override_camera_loc);
2888 if (override_cam_is_persp) {
2889 sub_v3_v3v3_db(dir_cam, view_vector, tri->v[0]->gloc);
2890 }
2891
2892 dot_v1 = dot_v3v3_db(dir_v1, tri->gn);
2893 dot_v2 = dot_v3v3_db(dir_v2, tri->gn);
2894 dot_f = dot_v3v3_db(dir_cam, tri->gn);
2895
2897 (e->target_reference == tri->target_reference))
2898 {
2899 if (((dot_f > 0) && (e->flags & MOD_LINEART_EDGE_FLAG_SHADOW_FACING_LIGHT)) ||
2900 ((dot_f < 0) && !(e->flags & MOD_LINEART_EDGE_FLAG_SHADOW_FACING_LIGHT)))
2901 {
2902 *from = 0.0f;
2903 *to = 1.0f;
2904 return true;
2905 }
2906
2907 return false;
2908 }
2909
2910 /* NOTE(Yiming): When we don't use `dot_f==0` here, it's theoretically possible that _some_
2911 * faces in perspective mode would get erroneously caught in this condition where they really
2912 * are legit faces that would produce occlusion, but haven't encountered those yet in my test
2913 * files.
2914 */
2915 if (fabs(dot_f) < FLT_EPSILON) {
2916 return false;
2917 }
2918
2919 /* Whether two end points are inside/on_the_edge/outside of the triangle. */
2920 state_v1 = lineart_point_triangle_relation(LFBC, FBC0, FBC1, FBC2);
2921 state_v2 = lineart_point_triangle_relation(RFBC, FBC0, FBC1, FBC2);
2922
2923 /* If the edge doesn't visually cross any edge of the triangle... */
2924 if (!isec_e1 && !isec_e2 && !isec_e3) {
2925 /* And if both end point from the edge is outside of the triangle... */
2926 if ((!state_v1) && (!state_v2)) {
2927 return false; /* We don't have any occlusion. */
2928 }
2929 }
2930
2931 /* Determine the cut position. */
2932
2933 dot_v1a = fabs(dot_v1);
2934 if (dot_v1a < DBL_EPSILON) {
2935 dot_v1a = 0;
2936 dot_v1 = 0;
2937 }
2938 dot_v2a = fabs(dot_v2);
2939 if (dot_v2a < DBL_EPSILON) {
2940 dot_v2a = 0;
2941 dot_v2 = 0;
2942 }
2943 if (dot_v1 - dot_v2 == 0) {
2944 cut = 100000;
2945 }
2946 else if (dot_v1 * dot_v2 <= 0) {
2947 cut = dot_v1a / fabs(dot_v1 - dot_v2);
2948 }
2949 else {
2950 cut = fabs(dot_v2 + dot_v1) / fabs(dot_v1 - dot_v2);
2951 cut = dot_v2a > dot_v1a ? 1 - cut : cut;
2952 }
2953
2954 /* Transform the cut from geometry space to image space. */
2955 if (override_cam_is_persp) {
2956 interp_v3_v3v3_db(gloc, e->v1->gloc, e->v2->gloc, cut);
2957 mul_v4_m4v3_db(trans, m_view_projection, gloc);
2958 mul_v3db_db(trans, (1 / trans[3]));
2959 trans[0] -= cam_shift_x * 2;
2960 trans[1] -= cam_shift_y * 2;
2961 /* To accommodate `k=0` and `k=inf` (vertical) lines. here the cut is in image space. */
2962 if (fabs(e->v1->fbcoord[0] - e->v2->fbcoord[0]) > fabs(e->v1->fbcoord[1] - e->v2->fbcoord[1]))
2963 {
2964 cut = ratiod(e->v1->fbcoord[0], e->v2->fbcoord[0], trans[0]);
2965 }
2966 else {
2967 cut = ratiod(e->v1->fbcoord[1], e->v2->fbcoord[1], trans[1]);
2968 }
2969 }
2970
2971#define LRT_GUARD_NOT_FOUND \
2972 if (cross_v1 < 0 || cross_v2 < 0) { \
2973 return false; \
2974 }
2975
2976 /* Determine the pair of edges that the line has crossed. The "|" symbol in the comment
2977 * indicates triangle boundary. DBL_TRIANGLE_LIM is needed to for floating point precision
2978 * tolerance. */
2979
2980 if (state_v1 == LRT_INSIDE_TRIANGLE) {
2981 /* Left side is in the triangle. */
2982 if (state_v2 == LRT_INSIDE_TRIANGLE) {
2983 /* | l---r | */
2984 INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v1);
2985 INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v2);
2986 }
2987 else if (state_v2 == LRT_ON_TRIANGLE) {
2988 /* | l------r| */
2989 INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v1);
2990 INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v2);
2991 }
2992 else if (state_v2 == LRT_OUTSIDE_TRIANGLE) {
2993 /* | l-------|------r */
2994 INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v1);
2995 INTERSECT_JUST_GREATER(cross_ratios, cross_order, 0, cross_v2);
2996 }
2997 }
2998 else if (state_v1 == LRT_ON_TRIANGLE) {
2999 /* Left side is on some edge of the triangle. */
3000 if (state_v2 == LRT_INSIDE_TRIANGLE) {
3001 /* |l------r | */
3002 INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v1);
3003 INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v2);
3004 }
3005 else if (state_v2 == LRT_ON_TRIANGLE) {
3006 /* |l---------r| */
3007 INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v1);
3008 INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v2);
3009 }
3010 else if (state_v2 == LRT_OUTSIDE_TRIANGLE) {
3011 /* |l----------|-------r (crossing the triangle) [OR]
3012 * r---------|l | (not crossing the triangle) */
3013 INTERSECT_JUST_GREATER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v2);
3014 if (cross_v2 >= 0 && LRT_ISEC(cross_v2) && cross_ratios[cross_v2] > (DBL_TRIANGLE_LIM)) {
3015 INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v1);
3016 }
3017 else {
3018 INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v2);
3019 if (cross_v2 > 0) {
3020 INTERSECT_JUST_SMALLER(cross_ratios, cross_order, cross_ratios[cross_v2], cross_v1);
3021 }
3022 }
3024 /* We could have the edge being completely parallel to the triangle where there isn't a
3025 * viable occlusion result. */
3026 if ((LRT_PARALLEL(cross_v1) && !LRT_ISEC(cross_v1)) ||
3027 (LRT_PARALLEL(cross_v2) && !LRT_ISEC(cross_v2)))
3028 {
3029 return false;
3030 }
3031 }
3032 }
3033 else if (state_v1 == LRT_OUTSIDE_TRIANGLE) {
3034 /* Left side is outside of the triangle. */
3035 if (state_v2 == LRT_INSIDE_TRIANGLE) {
3036 /* l---|---r | */
3037 INTERSECT_JUST_SMALLER(cross_ratios, cross_order, 1 - 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_ON_TRIANGLE) {
3041 /* |r----------|-------l (crossing the triangle) [OR]
3042 * l---------|r | (not crossing the triangle) */
3043 INTERSECT_JUST_SMALLER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v1);
3044 if (cross_v1 >= 0 && LRT_ISEC(cross_v1) && cross_ratios[cross_v1] < (1 - DBL_TRIANGLE_LIM)) {
3045 INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v2);
3046 }
3047 else {
3048 INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v1);
3049 if (cross_v1 > 0) {
3050 INTERSECT_JUST_GREATER(cross_ratios, cross_order, cross_ratios[cross_v1], cross_v2);
3051 }
3052 }
3054 /* The same logic applies as above case. */
3055 if ((LRT_PARALLEL(cross_v1) && !LRT_ISEC(cross_v1)) ||
3056 (LRT_PARALLEL(cross_v2) && !LRT_ISEC(cross_v2)))
3057 {
3058 return false;
3059 }
3060 }
3061 else if (state_v2 == LRT_OUTSIDE_TRIANGLE) {
3062 /* l---|----|----r (crossing the triangle) [OR]
3063 * l----r | | (not crossing the triangle) */
3064 INTERSECT_JUST_GREATER(cross_ratios, cross_order, -DBL_TRIANGLE_LIM, cross_v1);
3065 if (cross_v1 >= 0 && LRT_ISEC(cross_v1)) {
3066 INTERSECT_JUST_GREATER(cross_ratios, cross_order, cross_ratios[cross_v1], cross_v2);
3067 }
3068 else {
3069 if (cross_v1 >= 0) {
3070 INTERSECT_JUST_GREATER(cross_ratios, cross_order, cross_ratios[cross_v1], cross_v1);
3071 if (cross_v1 >= 0) {
3072 INTERSECT_JUST_GREATER(cross_ratios, cross_order, cross_ratios[cross_v1], cross_v2);
3073 }
3074 }
3075 }
3076 }
3077 }
3078
3080
3081 double dot_1f = dot_v1 * dot_f, dot_2f = dot_v2 * dot_f;
3082
3083 /* Determine the start and end point of image space cut on a line. */
3084 if (dot_1f <= 0 && dot_2f <= 0 && (dot_v1 || dot_v2)) {
3085 *from = std::max(0.0, cross_ratios[cross_v1]);
3086 *to = std::min(1.0, cross_ratios[cross_v2]);
3087 if (*from >= *to) {
3088 return false;
3089 }
3090 return true;
3091 }
3092 if (dot_1f >= 0 && dot_2f <= 0 && (dot_v1 || dot_v2)) {
3093 *from = std::max(cut, cross_ratios[cross_v1]);
3094 *to = std::min(1.0, cross_ratios[cross_v2]);
3095 if (*from >= *to) {
3096 return false;
3097 }
3098 return true;
3099 }
3100 if (dot_1f <= 0 && dot_2f >= 0 && (dot_v1 || dot_v2)) {
3101 *from = std::max(0.0, cross_ratios[cross_v1]);
3102 *to = std::min(cut, cross_ratios[cross_v2]);
3103 if (*from >= *to) {
3104 return false;
3105 }
3106 return true;
3107 }
3108
3109 /* Unlikely, but here's the default failed value if anything fall through. */
3110 return false;
3111}
3112
3113#undef INTERSECT_SORT_MIN_TO_MAX_3
3114#undef INTERSECT_JUST_GREATER
3115#undef INTERSECT_JUST_SMALLER
3116#undef LRT_ISEC
3117#undef LRT_PARALLEL
3118
3124{
3125 if (l->v[0]->index == r->v[0]->index) {
3126 if (l->v[1]->index == r->v[1]->index || l->v[1]->index == r->v[2]->index ||
3127 l->v[2]->index == r->v[2]->index || l->v[2]->index == r->v[1]->index)
3128 {
3129 return true;
3130 }
3131 }
3132 if (l->v[0]->index == r->v[1]->index) {
3133 if (l->v[1]->index == r->v[0]->index || l->v[1]->index == r->v[2]->index ||
3134 l->v[2]->index == r->v[2]->index || l->v[2]->index == r->v[0]->index)
3135 {
3136 return true;
3137 }
3138 }
3139 if (l->v[0]->index == r->v[2]->index) {
3140 if (l->v[1]->index == r->v[1]->index || l->v[1]->index == r->v[0]->index ||
3141 l->v[2]->index == r->v[0]->index || l->v[2]->index == r->v[1]->index)
3142 {
3143 return true;
3144 }
3145 }
3146 if (l->v[1]->index == r->v[0]->index) {
3147 if (l->v[2]->index == r->v[1]->index || l->v[2]->index == r->v[2]->index ||
3148 l->v[0]->index == r->v[2]->index || l->v[0]->index == r->v[1]->index)
3149 {
3150 return true;
3151 }
3152 }
3153 if (l->v[1]->index == r->v[1]->index) {
3154 if (l->v[2]->index == r->v[0]->index || l->v[2]->index == r->v[2]->index ||
3155 l->v[0]->index == r->v[2]->index || l->v[0]->index == r->v[0]->index)
3156 {
3157 return true;
3158 }
3159 }
3160 if (l->v[1]->index == r->v[2]->index) {
3161 if (l->v[2]->index == r->v[1]->index || l->v[2]->index == r->v[0]->index ||
3162 l->v[0]->index == r->v[0]->index || l->v[0]->index == r->v[1]->index)
3163 {
3164 return true;
3165 }
3166 }
3167
3168 /* Otherwise not possible. */
3169 return false;
3170}
3171
3173 const LineartTriangle *r)
3174{
3175 if (l->v[0] == r->v[0]) {
3176 return r->v[0];
3177 }
3178 if (l->v[0] == r->v[1]) {
3179 return r->v[1];
3180 }
3181 if (l->v[0] == r->v[2]) {
3182 return r->v[2];
3183 }
3184 if (l->v[1] == r->v[0]) {
3185 return r->v[0];
3186 }
3187 if (l->v[1] == r->v[1]) {
3188 return r->v[1];
3189 }
3190 if (l->v[1] == r->v[2]) {
3191 return r->v[2];
3192 }
3193 if (l->v[2] == r->v[0]) {
3194 return r->v[0];
3195 }
3196 if (l->v[2] == r->v[1]) {
3197 return r->v[1];
3198 }
3199 if (l->v[2] == r->v[2]) {
3200 return r->v[2];
3201 }
3202 return nullptr;
3203}
3204
3206 LineartVert *v1, LineartVert *v2, LineartTriangle *tri, const double *last, double *rv)
3207{
3208 /* Direction vectors for the edge verts. We will check if the verts are on the same side of the
3209 * triangle or not. */
3210 double dir_v1[3], dir_v2[3];
3211 double dot_v1, dot_v2;
3212 double gloc[3];
3213
3214 sub_v3_v3v3_db(dir_v1, v1->gloc, tri->v[0]->gloc);
3215 sub_v3_v3v3_db(dir_v2, v2->gloc, tri->v[0]->gloc);
3216
3217 dot_v1 = dot_v3v3_db(dir_v1, tri->gn);
3218 dot_v2 = dot_v3v3_db(dir_v2, tri->gn);
3219
3220 if (dot_v1 * dot_v2 > 0 || (!dot_v1 && !dot_v2)) {
3221 return false;
3222 }
3223
3224 dot_v1 = fabs(dot_v1);
3225 dot_v2 = fabs(dot_v2);
3226
3227 interp_v3_v3v3_db(gloc, v1->gloc, v2->gloc, dot_v1 / (dot_v1 + dot_v2));
3228
3229 /* Due to precision issue, we might end up with the same point as the one we already detected. */
3230 if (last && LRT_DOUBLE_CLOSE_ENOUGH(last[0], gloc[0]) &&
3231 LRT_DOUBLE_CLOSE_ENOUGH(last[1], gloc[1]) && LRT_DOUBLE_CLOSE_ENOUGH(last[2], gloc[2]))
3232 {
3233 return false;
3234 }
3235
3236 if (!lineart_point_inside_triangle3d(gloc, tri->v[0]->gloc, tri->v[1]->gloc, tri->v[2]->gloc)) {
3237 return false;
3238 }
3239
3240 copy_v3_v3_db(rv, gloc);
3241
3242 return true;
3243}
3244
3246 LineartTriangle *t2,
3247 double *v1,
3248 double *v2)
3249{
3250 double *next = v1, *last = nullptr;
3251 LineartVert *sv1, *sv2;
3252
3253 LineartVert *share = lineart_triangle_share_point(t2, tri);
3254
3255 if (share) {
3256 /* If triangles have sharing points like `abc` and `acd`, then we only need to detect `bc`
3257 * against `acd` or `cd` against `abc`. */
3258
3259 lineart_triangle_get_other_verts(tri, share, &sv1, &sv2);
3260
3261 copy_v3_v3_db(v1, share->gloc);
3262
3263 if (!lineart_triangle_2v_intersection_math(sv1, sv2, t2, nullptr, v2)) {
3264 lineart_triangle_get_other_verts(t2, share, &sv1, &sv2);
3265 if (lineart_triangle_2v_intersection_math(sv1, sv2, tri, nullptr, v2)) {
3266 return true;
3267 }
3268 }
3269 }
3270 else {
3271 /* If not sharing any points, then we need to try all the possibilities. */
3272
3273 if (lineart_triangle_2v_intersection_math(tri->v[0], tri->v[1], t2, nullptr, v1)) {
3274 next = v2;
3275 last = v1;
3276 }
3277
3278 if (lineart_triangle_2v_intersection_math(tri->v[1], tri->v[2], t2, last, next)) {
3279 if (last) {
3280 return true;
3281 }
3282 next = v2;
3283 last = v1;
3284 }
3285 if (lineart_triangle_2v_intersection_math(tri->v[2], tri->v[0], t2, last, next)) {
3286 if (last) {
3287 return true;
3288 }
3289 next = v2;
3290 last = v1;
3291 }
3292
3293 if (lineart_triangle_2v_intersection_math(t2->v[0], t2->v[1], tri, last, next)) {
3294 if (last) {
3295 return true;
3296 }
3297 next = v2;
3298 last = v1;
3299 }
3300 if (lineart_triangle_2v_intersection_math(t2->v[1], t2->v[2], tri, last, next)) {
3301 if (last) {
3302 return true;
3303 }
3304 next = v2;
3305 last = v1;
3306 }
3307 if (lineart_triangle_2v_intersection_math(t2->v[2], t2->v[0], tri, last, next)) {
3308 if (last) {
3309 return true;
3310 }
3311 next = v2;
3312 last = v1;
3313 }
3314 }
3315 return false;
3316}
3317
3319 const double *v1,
3320 const double *v2,
3321 LineartTriangle *tri1,
3322 LineartTriangle *tri2)
3323{
3324 if (th->current == th->max) {
3325
3326 LineartIsecSingle *new_array = MEM_malloc_arrayN<LineartIsecSingle>(size_t(th->max) * 2,
3327 "LineartIsecSingle");
3328 memcpy(new_array, th->array, sizeof(LineartIsecSingle) * th->max);
3329 th->max *= 2;
3330 MEM_freeN(th->array);
3331 th->array = new_array;
3332 }
3333 LineartIsecSingle *isec_single = &th->array[th->current];
3334 copy_v3_v3_db(isec_single->v1, v1);
3335 copy_v3_v3_db(isec_single->v2, v2);
3336 isec_single->tri1 = tri1;
3337 isec_single->tri2 = tri2;
3338 if (tri1->target_reference > tri2->target_reference) {
3339 std::swap(isec_single->tri1, isec_single->tri2);
3340 }
3341 th->current++;
3342}
3343
3344#define LRT_ISECT_TRIANGLE_PER_THREAD 4096
3345
3347{
3348 LineartData *ld = th->ld;
3349 int remaining = LRT_ISECT_TRIANGLE_PER_THREAD;
3350
3353
3354 if (!eln) {
3356 return false;
3357 }
3358
3359 th->pending_from = eln;
3361
3362 while (remaining > 0 && eln) {
3363 int remaining_this_eln = eln->element_count - ld->isect_scheduled_up_to_index;
3364 int added_count = std::min(remaining, remaining_this_eln);
3365 remaining -= added_count;
3366 if (remaining || added_count == remaining_this_eln) {
3367 eln = eln->next;
3368 ld->isect_scheduled_up_to = eln;
3370 }
3371 else {
3372 ld->isect_scheduled_up_to_index += added_count;
3373 }
3374 }
3375
3376 th->pending_to = eln ? eln :
3377 static_cast<LineartElementLinkNode *>(
3380
3382
3383 return true;
3384}
3385
3386/* This function initializes two things:
3387 * 1) Triangle array scheduling info, for each worker thread to get its chunk from the scheduler.
3388 * 2) Per-thread intersection result array. Does not store actual #LineartEdge, these results will
3389 * be finalized by #lineart_create_edges_from_isec_data
3390 */
3391static void lineart_init_isec_thread(LineartIsecData *d, LineartData *ld, int thread_count)
3392{
3393 d->threads = MEM_calloc_arrayN<LineartIsecThread>(thread_count, "LineartIsecThread arr");
3394 d->ld = ld;
3395 d->thread_count = thread_count;
3396
3397 ld->isect_scheduled_up_to = static_cast<LineartElementLinkNode *>(
3400
3401 for (int i = 0; i < thread_count; i++) {
3402 LineartIsecThread *it = &d->threads[i];
3403 it->array = MEM_malloc_arrayN<LineartIsecSingle>(100, "LineartIsecSingle arr");
3404 it->max = 100;
3405 it->current = 0;
3406 it->thread_id = i;
3407 it->ld = ld;
3408 }
3409}
3410
3412{
3413 for (int i = 0; i < d->thread_count; i++) {
3414 LineartIsecThread *it = &d->threads[i];
3415 MEM_freeN(it->array);
3416 }
3417 MEM_freeN(d->threads);
3418}
3419
3423 int up_to)
3424{
3425 BLI_assert(th != nullptr);
3426
3427 if (!th) {
3428 return;
3429 }
3430
3431 double *G0 = tri->v[0]->gloc, *G1 = tri->v[1]->gloc, *G2 = tri->v[2]->gloc;
3432
3433 /* If this _is_ the smallest subdivision bounding area, then do the intersections there. */
3434 for (int i = 0; i < up_to; i++) {
3435 /* Testing_triangle->testing[0] is used to store pairing triangle reference.
3436 * See definition of LineartTriangleThread for more info. */
3437 LineartTriangle *testing_triangle = ba->linked_triangles[i];
3438 LineartTriangleThread *tt = (LineartTriangleThread *)testing_triangle;
3439
3440 if (testing_triangle == tri || tt->testing_e[th->thread_id] == (LineartEdge *)tri) {
3441 continue;
3442 }
3443 tt->testing_e[th->thread_id] = (LineartEdge *)tri;
3444
3445 if (!((testing_triangle->flags | tri->flags) & LRT_TRIANGLE_FORCE_INTERSECTION)) {
3446 if (((testing_triangle->flags | tri->flags) & LRT_TRIANGLE_NO_INTERSECTION) ||
3447 (testing_triangle->flags & tri->flags & LRT_TRIANGLE_INTERSECTION_ONLY))
3448 {
3449 continue;
3450 }
3451 }
3452
3453 double *RG0 = testing_triangle->v[0]->gloc, *RG1 = testing_triangle->v[1]->gloc,
3454 *RG2 = testing_triangle->v[2]->gloc;
3455
3456 /* Bounding box not overlapping or triangles share edges, not potential of intersecting. */
3457 if ((std::min({G0[2], G1[2], G2[2]}) > std::max({RG0[2], RG1[2], RG2[2]})) ||
3458 (std::max({G0[2], G1[2], G2[2]}) < std::min({RG0[2], RG1[2], RG2[2]})) ||
3459 (std::min({G0[0], G1[0], G2[0]}) > std::max({RG0[0], RG1[0], RG2[0]})) ||
3460 (std::max({G0[0], G1[0], G2[0]}) < std::min({RG0[0], RG1[0], RG2[0]})) ||
3461 (std::min({G0[1], G1[1], G2[1]}) > std::max({RG0[1], RG1[1], RG2[1]})) ||
3462 (std::max({G0[1], G1[1], G2[1]}) < std::min({RG0[1], RG1[1], RG2[1]})) ||
3463 lineart_triangle_share_edge(tri, testing_triangle))
3464 {
3465 continue;
3466 }
3467
3468 /* If we do need to compute intersection, then finally do it. */
3469
3470 double iv1[3], iv2[3];
3471 if (lineart_triangle_intersect_math(tri, testing_triangle, iv1, iv2)) {
3472 lineart_add_isec_thread(th, iv1, iv2, tri, testing_triangle);
3473 }
3474 }
3475}
3476
3478{
3479 float direction[3] = {0, 0, 1};
3480 float trans[3];
3481 float inv[4][4];
3482 float obmat_no_scale[4][4];
3483
3484 copy_m4_m4(obmat_no_scale, ld->conf.cam_obmat);
3485 normalize_v3(obmat_no_scale[0]);
3486 normalize_v3(obmat_no_scale[1]);
3487 normalize_v3(obmat_no_scale[2]);
3488 invert_m4_m4(inv, obmat_no_scale);
3489 transpose_m4(inv);
3490 mul_v3_mat3_m4v3(trans, inv, direction);
3491 copy_m4_m4(ld->conf.cam_obmat, obmat_no_scale);
3492 copy_v3db_v3fl(ld->conf.view_vector, trans);
3493
3495 copy_m4_m4(obmat_no_scale, ld->conf.cam_obmat_secondary);
3496 normalize_v3(obmat_no_scale[0]);
3497 normalize_v3(obmat_no_scale[1]);
3498 normalize_v3(obmat_no_scale[2]);
3499 invert_m4_m4(inv, obmat_no_scale);
3500 transpose_m4(inv);
3501 mul_v3_mat3_m4v3(trans, inv, direction);
3502 copy_m4_m4(ld->conf.cam_obmat_secondary, obmat_no_scale);
3504 }
3505}
3506
3508{
3509 BLI_spin_end(&ba->lock);
3510 if (ba->child) {
3511 for (int i = 0; i < 4; i++) {
3513 }
3514 }
3515}
3516
3518{
3519 if (ld == nullptr) {
3520 return;
3521 }
3522
3525
3529
3530 if (ld->pending_edges.array) {
3532 }
3533
3534 for (int i = 0; i < ld->qtree.initial_tile_count; i++) {
3536 }
3538
3540}
3541
3543{
3544 if (ld == nullptr) {
3545 return;
3546 }
3547
3548 BLI_spin_end(&ld->lock_task);
3549 BLI_spin_end(&ld->lock_cuts);
3551
3553
3555}
3556
3558{
3559 LineartData *ld = lmd->la_data_ptr;
3560
3562
3563 if (ld) {
3564 MEM_freeN(ld);
3565 lmd->la_data_ptr = nullptr;
3566 }
3567
3568 if (G.debug_value == 4000) {
3569 printf("LRT: Destroyed render data.\n");
3570 }
3571}
3572
3574{
3575 LineartCache *lc = MEM_callocN<LineartCache>("Lineart Cache");
3576 return lc;
3577}
3578
3580{
3581 if (!(*lc)) {
3582 return;
3583 }
3584 lineart_mem_destroy(&((*lc)->chain_data_pool));
3585 MEM_freeN(*lc);
3586 (*lc) = nullptr;
3587}
3588
3591 Object *camera,
3592 Object *active_camera,
3593 LineartCache *lc)
3594{
3595 LineartData *ld = MEM_callocN<LineartData>("Line Art render buffer");
3596 lmd->cache = lc;
3597 lmd->la_data_ptr = ld;
3599
3600 if (!scene || !camera || !lc) {
3601 return nullptr;
3602 }
3603 const Camera *c = static_cast<Camera *>(camera->data);
3604 double clipping_offset = 0;
3605
3607 /* This way the clipped lines are "stably visible" by prevents depth buffer artifacts. */
3608 clipping_offset = 0.0001;
3609 }
3610
3611 copy_v3db_v3fl(ld->conf.camera_pos, camera->object_to_world().location());
3612 if (active_camera) {
3613 copy_v3db_v3fl(ld->conf.active_camera_pos, active_camera->object_to_world().location());
3614 }
3615 copy_m4_m4(ld->conf.cam_obmat, camera->object_to_world().ptr());
3616 /* Make sure none of the scaling factor makes in, line art expects no scaling on cameras and
3617 * lights. */
3618 normalize_v3(ld->conf.cam_obmat[0]);
3619 normalize_v3(ld->conf.cam_obmat[1]);
3620 normalize_v3(ld->conf.cam_obmat[2]);
3621
3622 ld->conf.cam_is_persp = (c->type == CAM_PERSP);
3623 ld->conf.near_clip = c->clip_start + clipping_offset;
3624 ld->conf.far_clip = c->clip_end - clipping_offset;
3625 ld->w = scene->r.xsch;
3626 ld->h = scene->r.ysch;
3627
3628 if (ld->conf.cam_is_persp) {
3630 }
3631 else {
3633 }
3634
3635 double asp = double(ld->w) / double(ld->h);
3636 int fit = BKE_camera_sensor_fit(c->sensor_fit, ld->w, ld->h);
3637 ld->conf.shift_x = fit == CAMERA_SENSOR_FIT_HOR ? c->shiftx : c->shiftx / asp;
3638 ld->conf.shift_y = fit == CAMERA_SENSOR_FIT_VERT ? c->shifty : c->shifty * asp;
3639
3640 ld->conf.overscan = lmd->overscan;
3641
3642 ld->conf.shift_x /= (1 + ld->conf.overscan);
3643 ld->conf.shift_y /= (1 + ld->conf.overscan);
3644
3645 if (lmd->light_contour_object) {
3646 Object *light_obj = lmd->light_contour_object;
3647 copy_v3db_v3fl(ld->conf.camera_pos_secondary, light_obj->object_to_world().location());
3648 copy_m4_m4(ld->conf.cam_obmat_secondary, light_obj->object_to_world().ptr());
3649 /* Make sure none of the scaling factor makes in, line art expects no scaling on cameras and
3650 * lights. */
3655 if (light_obj->type == OB_LAMP) {
3656 ld->conf.cam_is_persp_secondary = ((Light *)light_obj->data)->type != LA_SUN;
3657 }
3658 }
3659
3664
3666 0;
3669 0;
3676
3677 /* See lineart_edge_from_triangle() for how this option may impact performance. */
3680
3683
3685 0;
3687
3690
3691 /* This is used to limit calculation to a certain level to save time, lines who have higher
3692 * occlusion levels will get ignored. */
3694
3695 int16_t edge_types = lmd->edge_types_override;
3696
3697 /* lmd->edge_types_override contains all used flags in the modifier stack. */
3698 ld->conf.use_contour = (edge_types & MOD_LINEART_EDGE_FLAG_CONTOUR) != 0;
3699 ld->conf.use_crease = (edge_types & MOD_LINEART_EDGE_FLAG_CREASE) != 0;
3700 ld->conf.use_material = (edge_types & MOD_LINEART_EDGE_FLAG_MATERIAL) != 0;
3701 ld->conf.use_edge_marks = (edge_types & MOD_LINEART_EDGE_FLAG_EDGE_MARK) != 0;
3703 ld->conf.use_loose = (edge_types & MOD_LINEART_EDGE_FLAG_LOOSE) != 0;
3704 ld->conf.use_light_contour = ((edge_types & MOD_LINEART_EDGE_FLAG_LIGHT_CONTOUR) != 0 &&
3705 (lmd->light_contour_object != nullptr));
3706 ld->conf.use_shadow = ((edge_types & MOD_LINEART_EDGE_FLAG_PROJECTED_SHADOW) != 0 &&
3707 (lmd->light_contour_object != nullptr));
3708
3713
3715 0;
3716
3724
3726
3727 /* See #LineartData::edge_data_pool for explanation. */
3729
3733
3734 ld->thread_count = BKE_render_num_threads(&scene->r);
3735
3736 return ld;
3737}
3738
3740{
3741 return sizeof(LineartTriangle) + (sizeof(LineartEdge *) * (ld->thread_count));
3742}
3743
3745{
3746 /* Initial tile split is defined as 4 (subdivided as 4*4), increasing the value allows the
3747 * algorithm to build the acceleration structure for bigger scenes a little faster but not as
3748 * efficient at handling medium to small scenes. */
3749 int sp_w = LRT_BA_ROWS;
3750 int sp_h = LRT_BA_ROWS;
3751 int row, col;
3753
3754 /* Always make sure the shortest side has at least LRT_BA_ROWS tiles. */
3755 if (ld->w > ld->h) {
3756 sp_w = sp_h * ld->w / ld->h;
3757 }
3758 else {
3759 sp_h = sp_w * ld->h / ld->w;
3760 }
3761
3762 /* Because NDC (Normalized Device Coordinates) range is (-1,1),
3763 * so the span for each initial tile is double of that in the (0,1) range. */
3764 double span_w = 1.0 / sp_w * 2.0;
3765 double span_h = 1.0 / sp_h * 2.0;
3766
3767 ld->qtree.count_x = sp_w;
3768 ld->qtree.count_y = sp_h;
3769 ld->qtree.tile_width = span_w;
3770 ld->qtree.tile_height = span_h;
3771
3772 ld->qtree.initial_tile_count = sp_w * sp_h;
3775 for (int i = 0; i < ld->qtree.initial_tile_count; i++) {
3777 }
3778
3779 /* Initialize tiles. */
3780 for (row = 0; row < sp_h; row++) {
3781 for (col = 0; col < sp_w; col++) {
3782 ba = &ld->qtree.initials[row * ld->qtree.count_x + col];
3783
3784 /* Set the four direction limits. */
3785 ba->l = span_w * col - 1.0;
3786 ba->r = (col == sp_w - 1) ? 1.0 : (span_w * (col + 1) - 1.0);
3787 ba->u = 1.0 - span_h * row;
3788 ba->b = (row == sp_h - 1) ? -1.0 : (1.0 - span_h * (row + 1));
3789
3790 ba->cx = (ba->l + ba->r) / 2;
3791 ba->cy = (ba->u + ba->b) / 2;
3792
3793 /* Init linked_triangles array. */
3797 "ba_linked_triangles");
3798 ba->linked_lines = MEM_calloc_arrayN<LineartEdge *>(ba->max_line_count, "ba_linked_lines");
3799
3800 BLI_spin_init(&ba->lock);
3801 }
3802 }
3803}
3804
3809{
3810 LineartBoundingArea *ba = root->child, *tba;
3811 LinkData *lip2, *next_lip;
3813
3814 /* Inter-connection with newly created 4 child bounding areas. */
3815 lineart_list_append_pointer_pool(&ba[1].rp, mph, &ba[0]);
3816 lineart_list_append_pointer_pool(&ba[0].lp, mph, &ba[1]);
3817 lineart_list_append_pointer_pool(&ba[1].bp, mph, &ba[2]);
3818 lineart_list_append_pointer_pool(&ba[2].up, mph, &ba[1]);
3819 lineart_list_append_pointer_pool(&ba[2].rp, mph, &ba[3]);
3820 lineart_list_append_pointer_pool(&ba[3].lp, mph, &ba[2]);
3821 lineart_list_append_pointer_pool(&ba[3].up, mph, &ba[0]);
3822 lineart_list_append_pointer_pool(&ba[0].bp, mph, &ba[3]);
3823
3824 /* Connect 4 child bounding areas to other areas that are
3825 * adjacent to their original parents. */
3826 LISTBASE_FOREACH (LinkData *, lip, &root->lp) {
3827
3828 /* For example, we are dealing with parent's left side
3829 * "tba" represents each adjacent neighbor of the parent. */
3830 tba = static_cast<LineartBoundingArea *>(lip->data);
3831
3832 /* if this neighbor is adjacent to
3833 * the two new areas on the left side of the parent,
3834 * then add them to the adjacent list as well. */
3835 if (ba[1].u > tba->b && ba[1].b < tba->u) {
3836 lineart_list_append_pointer_pool(&ba[1].lp, mph, tba);
3837 lineart_list_append_pointer_pool(&tba->rp, mph, &ba[1]);
3838 }
3839 if (ba[2].u > tba->b && ba[2].b < tba->u) {
3840 lineart_list_append_pointer_pool(&ba[2].lp, mph, tba);
3841 lineart_list_append_pointer_pool(&tba->rp, mph, &ba[2]);
3842 }
3843 }
3844 LISTBASE_FOREACH (LinkData *, lip, &root->rp) {
3845 tba = static_cast<LineartBoundingArea *>(lip->data);
3846 if (ba[0].u > tba->b && ba[0].b < tba->u) {
3847 lineart_list_append_pointer_pool(&ba[0].rp, mph, tba);
3848 lineart_list_append_pointer_pool(&tba->lp, mph, &ba[0]);
3849 }
3850 if (ba[3].u > tba->b && ba[3].b < tba->u) {
3851 lineart_list_append_pointer_pool(&ba[3].rp, mph, tba);
3852 lineart_list_append_pointer_pool(&tba->lp, mph, &ba[3]);
3853 }
3854 }
3855 LISTBASE_FOREACH (LinkData *, lip, &root->up) {
3856 tba = static_cast<LineartBoundingArea *>(lip->data);
3857 if (ba[0].r > tba->l && ba[0].l < tba->r) {
3858 lineart_list_append_pointer_pool(&ba[0].up, mph, tba);
3859 lineart_list_append_pointer_pool(&tba->bp, mph, &ba[0]);
3860 }
3861 if (ba[1].r > tba->l && ba[1].l < tba->r) {
3862 lineart_list_append_pointer_pool(&ba[1].up, mph, tba);
3863 lineart_list_append_pointer_pool(&tba->bp, mph, &ba[1]);
3864 }
3865 }
3866 LISTBASE_FOREACH (LinkData *, lip, &root->bp) {
3867 tba = static_cast<LineartBoundingArea *>(lip->data);
3868 if (ba[2].r > tba->l && ba[2].l < tba->r) {
3869 lineart_list_append_pointer_pool(&ba[2].bp, mph, tba);
3870 lineart_list_append_pointer_pool(&tba->up, mph, &ba[2]);
3871 }
3872 if (ba[3].r > tba->l && ba[3].l < tba->r) {
3873 lineart_list_append_pointer_pool(&ba[3].bp, mph, tba);
3874 lineart_list_append_pointer_pool(&tba->up, mph, &ba[3]);
3875 }
3876 }
3877
3878 /* Then remove the parent bounding areas from
3879 * their original adjacent areas. */
3880 LISTBASE_FOREACH (LinkData *, lip, &root->lp) {
3881 for (lip2 = static_cast<LinkData *>(((LineartBoundingArea *)lip->data)->rp.first); lip2;
3882 lip2 = next_lip)
3883 {
3884 next_lip = lip2->next;
3885 tba = static_cast<LineartBoundingArea *>(lip2->data);
3886 if (tba == root) {
3888 if (ba[1].u > tba->b && ba[1].b < tba->u) {
3889 lineart_list_append_pointer_pool(&tba->rp, mph, &ba[1]);
3890 }
3891 if (ba[2].u > tba->b && ba[2].b < tba->u) {
3892 lineart_list_append_pointer_pool(&tba->rp, mph, &ba[2]);
3893 }
3894 }
3895 }
3896 }
3897 LISTBASE_FOREACH (LinkData *, lip, &root->rp) {
3898 for (lip2 = static_cast<LinkData *>(((LineartBoundingArea *)lip->data)->lp.first); lip2;
3899 lip2 = next_lip)
3900 {
3901 next_lip = lip2->next;
3902 tba = static_cast<LineartBoundingArea *>(lip2->data);
3903 if (tba == root) {
3905 if (ba[0].u > tba->b && ba[0].b < tba->u) {
3906 lineart_list_append_pointer_pool(&tba->lp, mph, &ba[0]);
3907 }
3908 if (ba[3].u > tba->b && ba[3].b < tba->u) {
3909 lineart_list_append_pointer_pool(&tba->lp, mph, &ba[3]);
3910 }
3911 }
3912 }
3913 }
3914 LISTBASE_FOREACH (LinkData *, lip, &root->up) {
3915 for (lip2 = static_cast<LinkData *>(((LineartBoundingArea *)lip->data)->bp.first); lip2;
3916 lip2 = next_lip)
3917 {
3918 next_lip = lip2->next;
3919 tba = static_cast<LineartBoundingArea *>(lip2->data);
3920 if (tba == root) {
3922 if (ba[0].r > tba->l && ba[0].l < tba->r) {
3923 lineart_list_append_pointer_pool(&tba->up, mph, &ba[0]);
3924 }
3925 if (ba[1].r > tba->l && ba[1].l < tba->r) {
3926 lineart_list_append_pointer_pool(&tba->up, mph, &ba[1]);
3927 }
3928 }
3929 }
3930 }
3931 LISTBASE_FOREACH (LinkData *, lip, &root->bp) {
3932 for (lip2 = static_cast<LinkData *>(((LineartBoundingArea *)lip->data)->up.first); lip2;
3933 lip2 = next_lip)
3934 {
3935 next_lip = lip2->next;
3936 tba = static_cast<LineartBoundingArea *>(lip2->data);
3937 if (tba == root) {
3939 if (ba[2].r > tba->l && ba[2].l < tba->r) {
3940 lineart_list_append_pointer_pool(&tba->bp, mph, &ba[2]);
3941 }
3942 if (ba[3].r > tba->l && ba[3].l < tba->r) {
3943 lineart_list_append_pointer_pool(&tba->bp, mph, &ba[3]);
3944 }
3945 }
3946 }
3947 }
3948
3949 /* Finally clear parent's adjacent list. */
3950 BLI_listbase_clear(&root->lp);
3951 BLI_listbase_clear(&root->rp);
3952 BLI_listbase_clear(&root->up);
3953 BLI_listbase_clear(&root->bp);
3954}
3955
3957{
3958 if (root->child) {
3960 for (int i = 0; i < 4; i++) {
3962 }
3963 }
3964}
3965
3967{
3968 int total_tile_initial = ld->qtree.count_x * ld->qtree.count_y;
3969 int tiles_per_row = ld->qtree.count_x;
3970
3971 for (int row = 0; row < ld->qtree.count_y; row++) {
3972 for (int col = 0; col < ld->qtree.count_x; col++) {
3973 LineartBoundingArea *ba = &ld->qtree.initials[row * tiles_per_row + col];
3974 /* Link adjacent ones. */
3975 if (row) {
3977 &ba->up, &ld->render_data_pool, &ld->qtree.initials[(row - 1) * tiles_per_row + col]);
3978 }
3979 if (col) {
3981 &ba->lp, &ld->render_data_pool, &ld->qtree.initials[row * tiles_per_row + col - 1]);
3982 }
3983 if (row != ld->qtree.count_y - 1) {
3985 &ba->bp, &ld->render_data_pool, &ld->qtree.initials[(row + 1) * tiles_per_row + col]);
3986 }
3987 if (col != ld->qtree.count_x - 1) {
3989 &ba->rp, &ld->render_data_pool, &ld->qtree.initials[row * tiles_per_row + col + 1]);
3990 }
3991 }
3992 }
3993 for (int i = 0; i < total_tile_initial; i++) {
3995 }
3996}
3997
4003 LineartBoundingArea *root,
4004 int recursive_level)
4005{
4006 LineartBoundingArea *ba = static_cast<LineartBoundingArea *>(
4008 ba[0].l = root->cx;
4009 ba[0].r = root->r;
4010 ba[0].u = root->u;
4011 ba[0].b = root->cy;
4012 ba[0].cx = (ba[0].l + ba[0].r) / 2;
4013 ba[0].cy = (ba[0].u + ba[0].b) / 2;
4014
4015 ba[1].l = root->l;
4016 ba[1].r = root->cx;
4017 ba[1].u = root->u;
4018 ba[1].b = root->cy;
4019 ba[1].cx = (ba[1].l + ba[1].r) / 2;
4020 ba[1].cy = (ba[1].u + ba[1].b) / 2;
4021
4022 ba[2].l = root->l;
4023 ba[2].r = root->cx;
4024 ba[2].u = root->cy;
4025 ba[2].b = root->b;
4026 ba[2].cx = (ba[2].l + ba[2].r) / 2;
4027 ba[2].cy = (ba[2].u + ba[2].b) / 2;
4028
4029 ba[3].l = root->cx;
4030 ba[3].r = root->r;
4031 ba[3].u = root->cy;
4032 ba[3].b = root->b;
4033 ba[3].cx = (ba[3].l + ba[3].r) / 2;
4034 ba[3].cy = (ba[3].u + ba[3].b) / 2;
4035
4036 /* Init linked_triangles array and locks. */
4037 for (int i = 0; i < 4; i++) {
4040 ba[i].linked_triangles = MEM_calloc_arrayN<LineartTriangle *>(ba[i].max_triangle_count,
4041 "ba_linked_triangles");
4042 ba[i].linked_lines = MEM_calloc_arrayN<LineartEdge *>(ba[i].max_line_count, "ba_linked_lines");
4043 BLI_spin_init(&ba[i].lock);
4044 }
4045
4046 for (uint32_t i = 0; i < root->triangle_count; i++) {
4047 LineartTriangle *tri = root->linked_triangles[i];
4048
4049 double b[4];
4050 b[0] = std::min({tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]});
4051 b[1] = std::max({tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]});
4052 b[2] = std::max({tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]});
4053 b[3] = std::min({tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]});
4054
4055 /* Re-link triangles into child tiles, not doing intersection lines during this because this
4056 * batch of triangles are all tested with each other for intersections. */
4057 if (LRT_BOUND_AREA_CROSSES(b, &ba[0].l)) {
4059 ld, &ba[0], tri, b, 0, recursive_level + 1, false, nullptr);
4060 }
4061 if (LRT_BOUND_AREA_CROSSES(b, &ba[1].l)) {
4063 ld, &ba[1], tri, b, 0, recursive_level + 1, false, nullptr);
4064 }
4065 if (LRT_BOUND_AREA_CROSSES(b, &ba[2].l)) {
4067 ld, &ba[2], tri, b, 0, recursive_level + 1, false, nullptr);
4068 }
4069 if (LRT_BOUND_AREA_CROSSES(b, &ba[3].l)) {
4071 ld, &ba[3], tri, b, 0, recursive_level + 1, false, nullptr);
4072 }
4073 }
4074
4075 /* At this point the child tiles are fully initialized and it's safe for new triangles to be
4076 * inserted, so assign root->child for #lineart_bounding_area_link_triangle to use. */
4077 root->child = ba;
4078}
4079
4081 const double l[2],
4082 const double r[2],
4084{
4085 double dx, dy;
4086 double converted[4];
4087 double c1, c;
4088
4089 if (((converted[0] = ba->l) > std::max(l[0], r[0])) ||
4090 ((converted[1] = ba->r) < std::min(l[0], r[0])) ||
4091 ((converted[2] = ba->b) > std::max(l[1], r[1])) ||
4092 ((converted[3] = ba->u) < std::min(l[1], r[1])))
4093 {
4094 return false;
4095 }
4096
4097 dx = l[0] - r[0];
4098 dy = l[1] - r[1];
4099
4100 c1 = dx * (converted[2] - l[1]) - dy * (converted[0] - l[0]);
4101 c = c1;
4102
4103 c1 = dx * (converted[2] - l[1]) - dy * (converted[1] - l[0]);
4104 if (c1 * c <= 0) {
4105 return true;
4106 }
4107 c = c1;
4108
4109 c1 = dx * (converted[3] - l[1]) - dy * (converted[0] - l[0]);
4110 if (c1 * c <= 0) {
4111 return true;
4112 }
4113 c = c1;
4114
4115 c1 = dx * (converted[3] - l[1]) - dy * (converted[1] - l[0]);
4116 if (c1 * c <= 0) {
4117 return true;
4118 }
4119 c = c1;
4120
4121 return false;
4122}
4123
4125 LineartTriangle *tri,
4127 bool *r_triangle_vert_inside)
4128{
4129 double p1[2], p2[2], p3[2], p4[2];
4130 double *FBC1 = tri->v[0]->fbcoord, *FBC2 = tri->v[1]->fbcoord, *FBC3 = tri->v[2]->fbcoord;
4131
4132 p3[0] = p1[0] = ba->l;
4133 p2[1] = p1[1] = ba->b;
4134 p2[0] = p4[0] = ba->r;
4135 p3[1] = p4[1] = ba->u;
4136
4137 if ((FBC1[0] >= p1[0] && FBC1[0] <= p2[0] && FBC1[1] >= p1[1] && FBC1[1] <= p3[1]) ||
4138 (FBC2[0] >= p1[0] && FBC2[0] <= p2[0] && FBC2[1] >= p1[1] && FBC2[1] <= p3[1]) ||
4139 (FBC3[0] >= p1[0] && FBC3[0] <= p2[0] && FBC3[1] >= p1[1] && FBC3[1] <= p3[1]))
4140 {
4141 *r_triangle_vert_inside = true;
4142 return true;
4143 }
4144
4145 *r_triangle_vert_inside = false;
4146
4147 if (lineart_point_inside_triangle(p1, FBC1, FBC2, FBC3) ||
4148 lineart_point_inside_triangle(p2, FBC1, FBC2, FBC3) ||
4149 lineart_point_inside_triangle(p3, FBC1, FBC2, FBC3) ||
4150 lineart_point_inside_triangle(p4, FBC1, FBC2, FBC3))
4151 {
4152 return true;
4153 }
4154
4155 if (lineart_bounding_area_edge_intersect(fb, FBC1, FBC2, ba) ||
4156 lineart_bounding_area_edge_intersect(fb, FBC2, FBC3, ba) ||
4158 {
4159 return true;
4160 }
4161
4162 return false;
4163}
4164
4178 LineartBoundingArea *root_ba,
4179 LineartTriangle *tri,
4180 double l_r_u_b[4],
4181 int recursive,
4182 int recursive_level,
4183 bool do_intersection,
4185{
4186 bool triangle_vert_inside;
4187 if (!lineart_bounding_area_triangle_intersect(ld, tri, root_ba, &triangle_vert_inside)) {
4188 return;
4189 }
4190
4191 LineartBoundingArea *old_ba = root_ba;
4192
4193 if (old_ba->child) {
4194 /* If old_ba->child is not nullptr, then tile splitting is fully finished, safe to directly
4195 * insert into child tiles. */
4196 double *B1 = l_r_u_b;
4197 double b[4];
4198 if (!l_r_u_b) {
4199 b[0] = std::min({tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]});
4200 b[1] = std::max({tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]});
4201 b[2] = std::max({tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]});
4202 b[3] = std::min({tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]});
4203 B1 = b;
4204 }
4205 for (int iba = 0; iba < 4; iba++) {
4206 if (LRT_BOUND_AREA_CROSSES(B1, &old_ba->child[iba].l)) {
4208 ld, &old_ba->child[iba], tri, B1, recursive, recursive_level + 1, do_intersection, th);
4209 }
4210 }
4211 return;
4212 }
4213
4214 /* When splitting tiles, triangles are relinked into new tiles by a single thread, #th is nullptr
4215 * in that situation. */
4216 if (th) {
4217 BLI_spin_lock(&old_ba->lock);
4218 }
4219
4220 /* If there are still space left in this tile for insertion. */
4221 if (old_ba->triangle_count < old_ba->max_triangle_count) {
4222 const uint32_t old_tri_count = old_ba->triangle_count;
4223
4224 old_ba->linked_triangles[old_tri_count] = tri;
4225
4226 if (triangle_vert_inside) {
4227 old_ba->insider_triangle_count++;
4228 }
4229 old_ba->triangle_count++;
4230
4231 /* Do intersections in place. */
4232 if (do_intersection && ld->conf.use_intersections) {
4233 lineart_triangle_intersect_in_bounding_area(tri, old_ba, th, old_tri_count);
4234 }
4235
4236 if (th) {
4237 BLI_spin_unlock(&old_ba->lock);
4238 }
4239 }
4240 else { /* We need to wait for either splitting or array extension to be done. */
4241
4242 if (recursive_level < ld->qtree.recursive_level &&
4244 {
4245 if (!old_ba->child) {
4246 /* old_ba->child==nullptr, means we are the thread that's doing the splitting. */
4247 lineart_bounding_area_split(ld, old_ba, recursive_level);
4248 } /* Otherwise other thread has completed the splitting process. */
4249 }
4250 else {
4251 if (old_ba->triangle_count == old_ba->max_triangle_count) {
4252 /* Means we are the thread that's doing the extension. */
4254 } /* Otherwise other thread has completed the extending the array. */
4255 }
4256
4257 /* Unlock before going into recursive call. */
4258 if (th) {
4259 BLI_spin_unlock(&old_ba->lock);
4260 }
4261
4262 /* Of course we still have our own triangle needs to be added. */
4264 ld, root_ba, tri, l_r_u_b, recursive, recursive_level, do_intersection, th);
4265 }
4266}
4267
4269{
4270 BLI_spin_end(&ba->lock);
4271 if (ba->linked_lines) {
4273 }
4274 if (ba->linked_triangles) {
4276 }
4277 if (recursive && ba->child) {
4278 for (int i = 0; i < 4; i++) {
4279 lineart_free_bounding_area_memory(&ba->child[i], recursive);
4280 }
4281 }
4282}
4284{
4285 for (int i = 0; i < ld->qtree.count_y; i++) {
4286 for (int j = 0; j < ld->qtree.count_x; j++) {
4288 }
4289 }
4290}
4291
4293 LineartBoundingArea *root_ba,
4294 LineartEdge *e)
4295{
4296 if (root_ba->child == nullptr) {
4298 }
4299 else {
4301 ld, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[0]))
4302 {
4303 lineart_bounding_area_link_edge(ld, &root_ba->child[0], e);
4304 }
4306 ld, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[1]))
4307 {
4308 lineart_bounding_area_link_edge(ld, &root_ba->child[1], e);
4309 }
4311 ld, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[2]))
4312 {
4313 lineart_bounding_area_link_edge(ld, &root_ba->child[2], e);
4314 }
4316 ld, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[3]))
4317 {
4318 lineart_bounding_area_link_edge(ld, &root_ba->child[3], e);
4319 }
4320 }
4321}
4322
4324{
4325 if (root_ba->child) {
4326 for (int i = 0; i < 4; i++) {
4328 }
4329 }
4330 if (root_ba->linked_lines) {
4331 MEM_freeN(root_ba->linked_lines);
4332 }
4333 root_ba->line_count = 0;
4334 root_ba->max_line_count = 128;
4336 "cleared lineart edges");
4337}
4339{
4341 for (int i = 0; i < ld->qtree.count_y; i++) {
4342 for (int j = 0; j < ld->qtree.count_x; j++) {
4344 }
4345 }
4346}
4347
4349{
4351 {
4352 int r1, r2, c1, c2, row, col;
4353 if (lineart_get_edge_bounding_areas(ld, e, &r1, &r2, &c1, &c2)) {
4354 for (row = r1; row != r2 + 1; row++) {
4355 for (col = c1; col != c2 + 1; col++) {
4357 ld, &ld->qtree.initials[row * ld->qtree.count_x + col], e);
4358 }
4359 }
4360 }
4361 }
4363}
4364
4366 uint8_t max_occlusion)
4367{
4368 if (ba->child) {
4369 for (int i = 0; i < 4; i++) {
4371 }
4372 return;
4373 }
4374
4375 if (!ba->line_count) {
4376 return;
4377 }
4378
4379 int usable_count = 0;
4380 for (int i = 0; i < ba->line_count; i++) {
4381 LineartEdge *e = ba->linked_lines[i];
4382 if (e->min_occ > max_occlusion) {
4383 continue;
4384 }
4385 usable_count++;
4386 }
4387
4388 if (!usable_count) {
4389 ba->line_count = 0;
4390 return;
4391 }
4392
4393 LineartEdge **new_array = MEM_calloc_arrayN<LineartEdge *>(usable_count,
4394 "cleaned lineart edge array");
4395
4396 int new_i = 0;
4397 for (int i = 0; i < ba->line_count; i++) {
4398 LineartEdge *e = ba->linked_lines[i];
4399 if (e->min_occ > max_occlusion) {
4400 continue;
4401 }
4402 new_array[new_i] = e;
4403 new_i++;
4404 }
4405
4407 ba->linked_lines = new_array;
4408 ba->max_line_count = ba->line_count = usable_count;
4409}
4410
4412{
4413 for (int row = 0; row < ld->qtree.count_y; row++) {
4414 for (int col = 0; col < ld->qtree.count_x; col++) {
4416 &ld->qtree.initials[row * ld->qtree.count_x + col], ld->conf.max_occlusion_level);
4417 }
4418 }
4419}
4420
4422 LineartData *ld, LineartTriangle *tri, int *rowbegin, int *rowend, int *colbegin, int *colend)
4423{
4424 double sp_w = ld->qtree.tile_width, sp_h = ld->qtree.tile_height;
4425 double b[4];
4426
4427 if (!tri->v[0] || !tri->v[1] || !tri->v[2]) {
4428 return false;
4429 }
4430
4431 b[0] = std::min({tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]});
4432 b[1] = std::max({tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]});
4433 b[2] = std::min({tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]});
4434 b[3] = std::max({tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]});
4435
4436 if (b[0] > 1 || b[1] < -1 || b[2] > 1 || b[3] < -1) {
4437 return false;
4438 }
4439
4440 (*colbegin) = int((b[0] + 1.0) / sp_w);
4441 (*colend) = int((b[1] + 1.0) / sp_w);
4442 (*rowend) = ld->qtree.count_y - int((b[2] + 1.0) / sp_h) - 1;
4443 (*rowbegin) = ld->qtree.count_y - int((b[3] + 1.0) / sp_h) - 1;
4444
4445 if ((*colend) >= ld->qtree.count_x) {
4446 (*colend) = ld->qtree.count_x - 1;
4447 }
4448 if ((*rowend) >= ld->qtree.count_y) {
4449 (*rowend) = ld->qtree.count_y - 1;
4450 }
4451 *colbegin = std::max(*colbegin, 0);
4452 *rowbegin = std::max(*rowbegin, 0);
4453
4454 return true;
4455}
4456
4458 LineartData *ld, LineartEdge *e, int *rowbegin, int *rowend, int *colbegin, int *colend)
4459{
4460 double sp_w = ld->qtree.tile_width, sp_h = ld->qtree.tile_height;
4461 double b[4];
4462
4463 if (!e->v1 || !e->v2) {
4464 return false;
4465 }
4466
4467 if (e->v1->fbcoord[0] != e->v1->fbcoord[0] || e->v2->fbcoord[0] != e->v2->fbcoord[0]) {
4468 return false;
4469 }
4470
4471 b[0] = std::min(e->v1->fbcoord[0], e->v2->fbcoord[0]);
4472 b[1] = std::max(e->v1->fbcoord[0], e->v2->fbcoord[0]);
4473 b[2] = std::min(e->v1->fbcoord[1], e->v2->fbcoord[1]);
4474 b[3] = std::max(e->v1->fbcoord[1], e->v2->fbcoord[1]);
4475
4476 if (b[0] > 1 || b[1] < -1 || b[2] > 1 || b[3] < -1) {
4477 return false;
4478 }
4479
4480 (*colbegin) = int((b[0] + 1.0) / sp_w);
4481 (*colend) = int((b[1] + 1.0) / sp_w);
4482 (*rowend) = ld->qtree.count_y - int((b[2] + 1.0) / sp_h) - 1;
4483 (*rowbegin) = ld->qtree.count_y - int((b[3] + 1.0) / sp_h) - 1;
4484
4485 /* It's possible that the line stretches too much out to the side, resulting negative value. */
4486 if ((*rowend) < (*rowbegin)) {
4487 (*rowend) = ld->qtree.count_y - 1;
4488 }
4489
4490 if ((*colend) < (*colbegin)) {
4491 (*colend) = ld->qtree.count_x - 1;
4492 }
4493
4494 CLAMP((*colbegin), 0, ld->qtree.count_x - 1);
4495 CLAMP((*rowbegin), 0, ld->qtree.count_y - 1);
4496 CLAMP((*colend), 0, ld->qtree.count_x - 1);
4497 CLAMP((*rowend), 0, ld->qtree.count_y - 1);
4498
4499 return true;
4500}
4501
4503{
4504 double sp_w = ld->qtree.tile_width, sp_h = ld->qtree.tile_height;
4505 int col, row;
4506
4507 if (x > 1 || x < -1 || y > 1 || y < -1) {
4508 return nullptr;
4509 }
4510
4511 col = int((x + 1.0) / sp_w);
4512 row = ld->qtree.count_y - int((y + 1.0) / sp_h) - 1;
4513
4514 if (col >= ld->qtree.count_x) {
4515 col = ld->qtree.count_x - 1;
4516 }
4517 if (row >= ld->qtree.count_y) {
4518 row = ld->qtree.count_y - 1;
4519 }
4520 col = std::max(col, 0);
4521 row = std::max(row, 0);
4522
4523 return &ld->qtree.initials[row * ld->qtree.count_x + col];
4524}
4525
4527{
4529 double sp_w = ld->qtree.tile_width, sp_h = ld->qtree.tile_height;
4530 int c = int((x + 1.0) / sp_w);
4531 int r = ld->qtree.count_y - int((y + 1.0) / sp_h) - 1;
4532 r = std::max(r, 0);
4533 c = std::max(c, 0);
4534 if (r >= ld->qtree.count_y) {
4535 r = ld->qtree.count_y - 1;
4536 }
4537 if (c >= ld->qtree.count_x) {
4538 c = ld->qtree.count_x - 1;
4539 }
4540
4541 iba = &ld->qtree.initials[r * ld->qtree.count_x + c];
4542 while (iba->child) {
4543 if (x > iba->cx) {
4544 if (y > iba->cy) {
4545 iba = &iba->child[0];
4546 }
4547 else {
4548 iba = &iba->child[3];
4549 }
4550 }
4551 else {
4552 if (y > iba->cy) {
4553 iba = &iba->child[1];
4554 }
4555 else {
4556 iba = &iba->child[2];
4557 }
4558 }
4559 }
4560 return iba;
4561}
4562
4564{
4566 if ((ba = MOD_lineart_get_parent_bounding_area(ld, x, y)) != nullptr) {
4567 return lineart_get_bounding_area(ld, x, y);
4568 }
4569 return nullptr;
4570}
4571
4572static void lineart_add_triangles_worker(TaskPool *__restrict /*pool*/, LineartIsecThread *th)
4573{
4574 LineartData *ld = th->ld;
4575 // int _dir_control = 0; /* UNUSED */
4577 for (LineartElementLinkNode *eln = th->pending_from; eln != th->pending_to->next;
4578 eln = eln->next)
4579 {
4580 int index_start = eln == th->pending_from ? th->index_from : 0;
4581 int index_end = eln == th->pending_to ? th->index_to : eln->element_count;
4582 LineartTriangle *tri = static_cast<LineartTriangle *>(
4583 (void *)(((uchar *)eln->pointer) + ld->sizeof_triangle * index_start));
4584 for (int ei = index_start; ei < index_end; ei++) {
4585 int x1, x2, y1, y2;
4586 int r, co;
4587 if ((tri->flags & LRT_CULL_USED) || (tri->flags & LRT_CULL_DISCARD)) {
4588 tri = static_cast<LineartTriangle *>((void *)(((uchar *)tri) + ld->sizeof_triangle));
4589 continue;
4590 }
4591 if (lineart_get_triangle_bounding_areas(ld, tri, &y1, &y2, &x1, &x2)) {
4592 // _dir_control++;
4593 for (co = x1; co <= x2; co++) {
4594 for (r = y1; r <= y2; r++) {
4596 &ld->qtree.initials[r * ld->qtree.count_x + co],
4597 tri,
4598 nullptr,
4599 1,
4600 0,
4601 true,
4602 th);
4603 }
4604 }
4605 } /* Else throw away. */
4606 tri = static_cast<LineartTriangle *>((void *)(((uchar *)tri) + ld->sizeof_triangle));
4607 }
4608 }
4609 }
4610}
4611
4613{
4614 LineartData *ld = d->ld;
4615 double ZMax = ld->conf.far_clip;
4616 double ZMin = ld->conf.near_clip;
4617 int total_lines = 0;
4618
4619 for (int i = 0; i < d->thread_count; i++) {
4620 LineartIsecThread *th = &d->threads[i];
4621 if (G.debug_value == 4000) {
4622 printf("Thread %d isec generated %d lines.\n", i, th->current);
4623 }
4624 if (!th->current) {
4625 continue;
4626 }
4627 total_lines += th->current;
4628 }
4629
4630 if (!total_lines) {
4631 return;
4632 }
4633
4634 /* We don't care about removing duplicated vert in this method, chaining can handle that,
4635 * and it saves us from using locks and look up tables. */
4636 LineartVert *v = static_cast<LineartVert *>(
4637 lineart_mem_acquire(ld->edge_data_pool, sizeof(LineartVert) * total_lines * 2));
4638 LineartEdge *e = static_cast<LineartEdge *>(
4639 lineart_mem_acquire(ld->edge_data_pool, sizeof(LineartEdge) * total_lines));
4640 LineartEdgeSegment *es = static_cast<LineartEdgeSegment *>(
4641 lineart_mem_acquire(ld->edge_data_pool, sizeof(LineartEdgeSegment) * total_lines));
4642
4643 LineartElementLinkNode *eln = static_cast<LineartElementLinkNode *>(
4645 eln->element_count = total_lines;
4646 eln->pointer = e;
4649
4650 for (int i = 0; i < d->thread_count; i++) {
4651 LineartIsecThread *th = &d->threads[i];
4652 if (!th->current) {
4653 continue;
4654 }
4655
4656 for (int j = 0; j < th->current; j++) {
4657 LineartIsecSingle *is = &th->array[j];
4658 LineartVert *v1 = v;
4659 LineartVert *v2 = v + 1;
4660 copy_v3_v3_db(v1->gloc, is->v1);
4661 copy_v3_v3_db(v2->gloc, is->v2);
4662 /* The intersection line has been generated only in geometry space, so we need to transform
4663 * them as well. */
4665 mul_v4_m4v3_db(v2->fbcoord, ld->conf.view_projection, v2->gloc);
4666 mul_v3db_db(v1->fbcoord, (1 / v1->fbcoord[3]));
4667 mul_v3db_db(v2->fbcoord, (1 / v2->fbcoord[3]));
4668
4669 v1->fbcoord[0] -= ld->conf.shift_x * 2;
4670 v1->fbcoord[1] -= ld->conf.shift_y * 2;
4671 v2->fbcoord[0] -= ld->conf.shift_x * 2;
4672 v2->fbcoord[1] -= ld->conf.shift_y * 2;
4673
4674 /* This z transformation is not the same as the rest of the part, because the data don't go
4675 * through normal perspective division calls in the pipeline, but this way the 3D result and
4676 * occlusion on the generated line is correct, and we don't really use 2D for viewport stroke
4677 * generation anyway. */
4678 v1->fbcoord[2] = ZMin * ZMax / (ZMax - fabs(v1->fbcoord[2]) * (ZMax - ZMin));
4679 v2->fbcoord[2] = ZMin * ZMax / (ZMax - fabs(v2->fbcoord[2]) * (ZMax - ZMin));
4680 e->v1 = v1;
4681 e->v2 = v2;
4682 e->t1 = is->tri1;
4683 e->t2 = is->tri2;
4684 /* This is so we can also match intersection edges from shadow to later viewing stage. */
4685 e->edge_identifier = (uint64_t(e->t1->target_reference) << 32) | e->t2->target_reference;
4687 e->intersection_mask = (is->tri1->intersection_mask | is->tri2->intersection_mask);
4688 BLI_addtail(&e->segments, es);
4689
4690 int obi1 = (e->t1->target_reference & LRT_OBINDEX_HIGHER);
4691 int obi2 = (e->t2->target_reference & LRT_OBINDEX_HIGHER);
4693 obi1);
4694 LineartElementLinkNode *eln2 = obi1 == obi2 ? eln1 :
4696 &ld->geom.line_buffer_pointers, obi2);
4697 Object *ob1 = eln1 ? static_cast<Object *>(eln1->object_ref) : nullptr;
4698 Object *ob2 = eln2 ? static_cast<Object *>(eln2->object_ref) : nullptr;
4699 if (e->t1->intersection_priority > e->t2->intersection_priority) {
4700 e->object_ref = ob1;
4701 }
4702 else if (e->t1->intersection_priority < e->t2->intersection_priority) {
4703 e->object_ref = ob2;
4704 }
4705 else { /* equal priority */
4706 if (ob1 == ob2) {
4707 /* object_ref should be ambiguous if intersection lines comes from different objects. */
4708 e->object_ref = ob1;
4709 }
4710 }
4711
4713
4714 v += 2;
4715 e++;
4716 es++;
4717 }
4718 }
4719}
4720
4722{
4723 double t_start;
4724 if (G.debug_value == 4000) {
4725 t_start = BLI_time_now_seconds();
4726 }
4727
4728 /* Initialize per-thread data for thread task scheduling information and storing intersection
4729 * results. */
4730 LineartIsecData d = {nullptr};
4732
4734 for (int i = 0; i < ld->thread_count; i++) {
4736 tp, (TaskRunFunction)lineart_add_triangles_worker, &d.threads[i], false, nullptr);
4737 }
4740
4741 if (ld->conf.use_intersections) {
4743 }
4744
4746
4747 if (G.debug_value == 4000) {
4748 double t_elapsed = BLI_time_now_seconds() - t_start;
4749 printf("Line art intersection time: %f\n", t_elapsed);
4750 }
4751}
4752
4754 double *fbcoord1,
4755 double *fbcoord2)
4756{
4757 double data[2] = {fbcoord1[0], fbcoord1[1]};
4758 double LU[2] = {-1, 1}, RU[2] = {1, 1}, LB[2] = {-1, -1}, RB[2] = {1, -1};
4759 double r = 1, sr = 1;
4760 bool p_unused;
4761
4762 if (data[0] > -1 && data[0] < 1 && data[1] > -1 && data[1] < 1) {
4763 return lineart_get_bounding_area(ld, data[0], data[1]);
4764 }
4765
4766 if (lineart_intersect_seg_seg(fbcoord1, fbcoord2, LU, RU, &sr, &p_unused) && sr < r && sr > 0) {
4767 r = sr;
4768 }
4769 if (lineart_intersect_seg_seg(fbcoord1, fbcoord2, LB, RB, &sr, &p_unused) && sr < r && sr > 0) {
4770 r = sr;
4771 }
4772 if (lineart_intersect_seg_seg(fbcoord1, fbcoord2, LB, LU, &sr, &p_unused) && sr < r && sr > 0) {
4773 r = sr;
4774 }
4775 if (lineart_intersect_seg_seg(fbcoord1, fbcoord2, RB, RU, &sr, &p_unused) && sr < r && sr > 0) {
4776 r = sr;
4777 }
4778 interp_v2_v2v2_db(data, fbcoord1, fbcoord2, r);
4779
4780 return lineart_get_bounding_area(ld, data[0], data[1]);
4781}
4782
4784 double *fbcoord1,
4785 double *fbcoord2,
4786 double x,
4787 double y,
4788 double k,
4789 int positive_x,
4790 int positive_y,
4791 double *next_x,
4792 double *next_y)
4793{
4794 double rx, ry, ux, uy, lx, ly, bx, by;
4795 double r1, r2;
4797
4798 /* If we are marching towards the right. */
4799 if (positive_x > 0) {
4800 rx = self->r;
4801 ry = y + k * (rx - x);
4802
4803 /* If we are marching towards the top. */
4804 if (positive_y > 0) {
4805 uy = self->u;
4806 ux = x + (uy - y) / k;
4807 r1 = ratiod(fbcoord1[0], fbcoord2[0], rx);
4808 r2 = ratiod(fbcoord1[0], fbcoord2[0], ux);
4809 if (std::min(r1, r2) > 1) {
4810 return nullptr;
4811 }
4812
4813 /* We reached the right side before the top side. */
4814 if (r1 <= r2) {
4815 LISTBASE_FOREACH (LinkData *, lip, &self->rp) {
4816 ba = static_cast<LineartBoundingArea *>(lip->data);
4817 if (ba->u >= ry && ba->b < ry) {
4818 *next_x = rx;
4819 *next_y = ry;
4820 return ba;
4821 }
4822 }
4823 }
4824 /* We reached the top side before the right side. */
4825 else {
4826 LISTBASE_FOREACH (LinkData *, lip, &self->up) {
4827 ba = static_cast<LineartBoundingArea *>(lip->data);
4828 if (ba->r >= ux && ba->l < ux) {
4829 *next_x = ux;
4830 *next_y = uy;
4831 return ba;
4832 }
4833 }
4834 }
4835 }
4836 /* If we are marching towards the bottom. */
4837 else if (positive_y < 0) {
4838 by = self->b;
4839 bx = x + (by - y) / k;
4840 r1 = ratiod(fbcoord1[0], fbcoord2[0], rx);
4841 r2 = ratiod(fbcoord1[0], fbcoord2[0], bx);
4842 if (std::min(r1, r2) > 1) {
4843 return nullptr;
4844 }
4845 if (r1 <= r2) {
4846 LISTBASE_FOREACH (LinkData *, lip, &self->rp) {
4847 ba = static_cast<LineartBoundingArea *>(lip->data);
4848 if (ba->u >= ry && ba->b < ry) {
4849 *next_x = rx;
4850 *next_y = ry;
4851 return ba;
4852 }
4853 }
4854 }
4855 else {
4856 LISTBASE_FOREACH (LinkData *, lip, &self->bp) {
4857 ba = static_cast<LineartBoundingArea *>(lip->data);
4858 if (ba->r >= bx && ba->l < bx) {
4859 *next_x = bx;
4860 *next_y = by;
4861 return ba;
4862 }
4863 }
4864 }
4865 }
4866 /* If the line is completely horizontal, in which Y difference == 0. */
4867 else {
4868 r1 = ratiod(fbcoord1[0], fbcoord2[0], self->r);
4869 if (r1 > 1) {
4870 return nullptr;
4871 }
4872 LISTBASE_FOREACH (LinkData *, lip, &self->rp) {
4873 ba = static_cast<LineartBoundingArea *>(lip->data);
4874 if (ba->u >= y && ba->b < y) {
4875 *next_x = self->r;
4876 *next_y = y;
4877 return ba;
4878 }
4879 }
4880 }
4881 }
4882
4883 /* If we are marching towards the left. */
4884 else if (positive_x < 0) {
4885 lx = self->l;
4886 ly = y + k * (lx - x);
4887
4888 /* If we are marching towards the top. */
4889 if (positive_y > 0) {
4890 uy = self->u;
4891 ux = x + (uy - y) / k;
4892 r1 = ratiod(fbcoord1[0], fbcoord2[0], lx);
4893 r2 = ratiod(fbcoord1[0], fbcoord2[0], ux);
4894 if (std::min(r1, r2) > 1) {
4895 return nullptr;
4896 }
4897 if (r1 <= r2) {
4898 LISTBASE_FOREACH (LinkData *, lip, &self->lp) {
4899 ba = static_cast<LineartBoundingArea *>(lip->data);
4900 if (ba->u >= ly && ba->b < ly) {
4901 *next_x = lx;
4902 *next_y = ly;
4903 return ba;
4904 }
4905 }
4906 }
4907 else {
4908 LISTBASE_FOREACH (LinkData *, lip, &self->up) {
4909 ba = static_cast<LineartBoundingArea *>(lip->data);
4910 if (ba->r >= ux && ba->l < ux) {
4911 *next_x = ux;
4912 *next_y = uy;
4913 return ba;
4914 }
4915 }
4916 }
4917 }
4918
4919 /* If we are marching towards the bottom. */
4920 else if (positive_y < 0) {
4921 by = self->b;
4922 bx = x + (by - y) / k;
4923 r1 = ratiod(fbcoord1[0], fbcoord2[0], lx);
4924 r2 = ratiod(fbcoord1[0], fbcoord2[0], bx);
4925 if (std::min(r1, r2) > 1) {
4926 return nullptr;
4927 }
4928 if (r1 <= r2) {
4929 LISTBASE_FOREACH (LinkData *, lip, &self->lp) {
4930 ba = static_cast<LineartBoundingArea *>(lip->data);
4931 if (ba->u >= ly && ba->b < ly) {
4932 *next_x = lx;
4933 *next_y = ly;
4934 return ba;
4935 }
4936 }
4937 }
4938 else {
4939 LISTBASE_FOREACH (LinkData *, lip, &self->bp) {
4940 ba = static_cast<LineartBoundingArea *>(lip->data);
4941 if (ba->r >= bx && ba->l < bx) {
4942 *next_x = bx;
4943 *next_y = by;
4944 return ba;
4945 }
4946 }
4947 }
4948 }
4949 /* Again, horizontal. */
4950 else {
4951 r1 = ratiod(fbcoord1[0], fbcoord2[0], self->l);
4952 if (r1 > 1) {
4953 return nullptr;
4954 }
4955 LISTBASE_FOREACH (LinkData *, lip, &self->lp) {
4956 ba = static_cast<LineartBoundingArea *>(lip->data);
4957 if (ba->u >= y && ba->b < y) {
4958 *next_x = self->l;
4959 *next_y = y;
4960 return ba;
4961 }
4962 }
4963 }
4964 }
4965 /* If the line is completely vertical, hence X difference == 0. */
4966 else {
4967 if (positive_y > 0) {
4968 r1 = ratiod(fbcoord1[1], fbcoord2[1], self->u);
4969 if (r1 > 1) {
4970 return nullptr;
4971 }
4972 LISTBASE_FOREACH (LinkData *, lip, &self->up) {
4973 ba = static_cast<LineartBoundingArea *>(lip->data);
4974 if (ba->r > x && ba->l <= x) {
4975 *next_x = x;
4976 *next_y = self->u;
4977 return ba;
4978 }
4979 }
4980 }
4981 else if (positive_y < 0) {
4982 r1 = ratiod(fbcoord1[1], fbcoord2[1], self->b);
4983 if (r1 > 1) {
4984 return nullptr;
4985 }
4986 LISTBASE_FOREACH (LinkData *, lip, &self->bp) {
4987 ba = static_cast<LineartBoundingArea *>(lip->data);
4988 if (ba->r > x && ba->l <= x) {
4989 *next_x = x;
4990 *next_y = self->b;
4991 return ba;
4992 }
4993 }
4994 }
4995 else {
4996 /* Segment has no length. */
4997 return nullptr;
4998 }
4999 }
5000 return nullptr;
5001}
5002
5005 LineartCache **cached_result,
5006 bool enable_stroke_depth_offset)
5007{
5008 LineartData *ld;
5010 int intersections_only = 0; /* Not used right now, but preserve for future. */
5011 Object *lineart_camera = nullptr;
5012
5013 double t_start;
5014 if (G.debug_value == 4000) {
5015 t_start = BLI_time_now_seconds();
5016 }
5017
5018 bool use_render_camera_override = false;
5020 if (!lmd.source_camera ||
5021 (lineart_camera = DEG_get_evaluated(depsgraph, lmd.source_camera))->type != OB_CAMERA)
5022 {
5023 return false;
5024 }
5025 }
5026 else {
5028 if (render && render->camera_override) {
5029 lineart_camera = DEG_get_evaluated(depsgraph, render->camera_override);
5030 use_render_camera_override = true;
5031 }
5032 if (!lineart_camera) {
5034 if (!scene->camera) {
5035 return false;
5036 }
5037 lineart_camera = scene->camera;
5038 }
5039 }
5040
5041 LineartCache *lc = *cached_result;
5042 if (!lc) {
5044 *cached_result = lc;
5045 }
5046
5048 &lmd,
5049 lineart_camera,
5050 use_render_camera_override ? lineart_camera : scene->camera,
5051 lc);
5052
5053 /* Triangle thread testing data size varies depending on the thread count.
5054 * See definition of LineartTriangleThread for details. */
5056
5057 LineartData *shadow_rb = nullptr;
5058 LineartElementLinkNode *shadow_veln, *shadow_eeln;
5059 ListBase *shadow_elns = ld->conf.shadow_selection ? &lc->shadow_elns : nullptr;
5060 bool shadow_generated = lineart_main_try_generate_shadow_v3(depsgraph,
5061 scene,
5062 ld,
5063 &lmd,
5064 &lc->shadow_data_pool,
5065 &shadow_veln,
5066 &shadow_eeln,
5067 shadow_elns,
5068 &shadow_rb);
5069
5070 /* Get view vector before loading geometries, because we detect feature lines there. */
5072
5073 LineartModifierRuntime *runtime = reinterpret_cast<LineartModifierRuntime *>(lmd.runtime);
5074 blender::Set<const Object *> *included_objects = runtime ? &runtime->object_dependencies :
5075 nullptr;
5076
5078 scene,
5079 lineart_camera,
5080 ld,
5082 false,
5083 shadow_elns,
5084 included_objects);
5085
5086 if (shadow_generated) {
5087 lineart_main_transform_and_add_shadow(ld, shadow_veln, shadow_eeln);
5088 }
5089
5091 /* No geometry loaded, return early. */
5092 return true;
5093 }
5094
5095 /* Initialize the bounding box acceleration structure, it's a lot like BVH in 3D. */
5097
5098 /* We need to get cut into triangles that are crossing near/far plans, only this way can we get
5099 * correct coordinates of those clipped lines. Done in two steps,
5100 * setting clip_far==false for near plane. */
5101 lineart_main_cull_triangles(ld, false);
5102 /* `clip_far == true` for far plane. */
5104
5105 /* At this point triangle adjacent info pointers is no longer needed, free them. */
5107
5108 /* Do the perspective division after clipping is done. */
5110
5112
5113 /* Triangle intersections are done here during sequential adding of them. Only after this,
5114 * triangles and lines are all linked with acceleration structure, and the 2D occlusion stage
5115 * can do its job. */
5117
5118 /* Add shadow cuts to intersection lines as well. */
5120
5121 /* Re-link bounding areas because they have been subdivided by worker threads and we need
5122 * adjacent info. */
5124
5125 /* Link lines to acceleration structure, this can only be done after perspective division, if
5126 * we do it after triangles being added, the acceleration structure has already been
5127 * subdivided, this way we do less list manipulations. */
5129
5130 /* "intersection_only" is preserved for being called in a standalone fashion.
5131 * If so the data will already be available at the stage. Otherwise we do the occlusion and
5132 * chaining etc. */
5133
5134 if (!intersections_only) {
5135
5136 /* Occlusion is work-and-wait. This call will not return before work is completed. */
5138
5139 lineart_main_make_enclosed_shapes(ld, shadow_rb);
5140
5142
5143 /* Chaining is all single threaded. See `lineart_chain.cc`.
5144 * In this particular call, only lines that are geometrically connected (share the _exact_
5145 * same end point) will be chained together. */
5147
5148 /* We are unable to take care of occlusion if we only connect end points, so here we do a
5149 * spit, where the splitting point could be any cut in e->segments. */
5151
5152 /* Then we connect chains based on the _proximity_ of their end points in image space, here's
5153 * the place threshold value gets involved. */
5155
5156 if (ld->conf.chain_smooth_tolerance > FLT_EPSILON) {
5157 /* Keeping UI range of 0-1 for ease of read while scaling down the actual value for best
5158 * effective range in image-space (Coordinate only goes from -1 to 1). This value is
5159 * somewhat arbitrary, but works best for the moment. */
5161 }
5162
5165 }
5166
5167 if (ld->conf.angle_splitting_threshold > FLT_EPSILON) {
5169 }
5170
5171 if (enable_stroke_depth_offset && lmd.stroke_depth_offset > FLT_EPSILON) {
5174 }
5175
5176 if (ld->conf.shadow_use_silhouette) {
5178 }
5179
5180 /* Finally transfer the result list into cache. */
5181 memcpy(&lc->chains, &ld->chains, sizeof(ListBase));
5182
5183 /* At last, we need to clear flags so we don't confuse GPencil generation calls. */
5185
5187 }
5188
5190
5191 if (ld->conf.shadow_enclose_shapes && shadow_rb) {
5193 MEM_freeN(shadow_rb);
5194 }
5195
5196 if (G.debug_value == 4000) {
5198
5199 double t_elapsed = BLI_time_now_seconds() - t_start;
5200 printf("Line art total time: %lf\n", t_elapsed);
5201 }
5202
5203 return true;
5204}
5205
5210
5212 const blender::float4x4 &inverse_mat,
5213 Depsgraph *depsgraph,
5215 const int8_t source_type,
5216 Object *source_object,
5217 Collection *source_collection,
5218 const int level_start,
5219 const int level_end,
5220 const int mat_nr,
5221 const int16_t edge_types,
5222 const uchar mask_switches,
5223 const uchar material_mask_bits,
5224 const uchar intersection_mask,
5225 const float thickness,
5226 const float opacity,
5227 const uchar shadow_selection,
5228 const uchar silhouette_mode,
5229 const char *source_vgname,
5230 const char *vgname,
5231 const int modifier_flags,
5232 const int modifier_calculation_flags)
5233{
5234 if (G.debug_value == 4000) {
5235 printf("Line Art v3: Generating...\n");
5236 }
5237
5238 if (cache == nullptr) {
5239 if (G.debug_value == 4000) {
5240 printf("nullptr Lineart cache!\n");
5241 }
5242 return;
5243 }
5244
5245 Object *orig_ob = nullptr;
5246 Collection *orig_col = nullptr;
5247
5248 if (source_type == LINEART_SOURCE_OBJECT) {
5249 if (!source_object) {
5250 return;
5251 }
5252 orig_ob = source_object->id.orig_id ? (Object *)source_object->id.orig_id : source_object;
5253 orig_col = nullptr;
5254 }
5255 else if (source_type == LINEART_SOURCE_COLLECTION) {
5256 if (!source_collection) {
5257 return;
5258 }
5259 orig_col = source_collection->id.orig_id ? (Collection *)source_collection->id.orig_id :
5260 source_collection;
5261 orig_ob = nullptr;
5262 }
5263 /* Otherwise the whole scene is selected. */
5264
5265 int enabled_types = cache->all_enabled_edge_types;
5266
5267 bool invert_input = modifier_calculation_flags & MOD_LINEART_INVERT_SOURCE_VGROUP;
5268
5269 bool inverse_silhouette = modifier_flags & MOD_LINEART_INVERT_SILHOUETTE_FILTER;
5270
5272 writer.reserve(128);
5273 int total_point_count = 0;
5274 int stroke_count = 0;
5275 LISTBASE_FOREACH (LineartEdgeChain *, ec, &cache->chains) {
5276
5277 if (ec->picked) {
5278 continue;
5279 }
5280 if (!(ec->type & (edge_types & enabled_types))) {
5281 continue;
5282 }
5283 if (ec->level > level_end || ec->level < level_start) {
5284 continue;
5285 }
5286 if (orig_ob && orig_ob != ec->object_ref) {
5287 continue;
5288 }
5289 if (orig_col && ec->object_ref) {
5290 if (BKE_collection_has_object_recursive_instanced(orig_col, ec->object_ref)) {
5291 if (modifier_flags & MOD_LINEART_INVERT_COLLECTION) {
5292 continue;
5293 }
5294 }
5295 else {
5296 if (!(modifier_flags & MOD_LINEART_INVERT_COLLECTION)) {
5297 continue;
5298 }
5299 }
5300 }
5301 if (mask_switches & MOD_LINEART_MATERIAL_MASK_ENABLE) {
5302 if (mask_switches & MOD_LINEART_MATERIAL_MASK_MATCH) {
5303 if (ec->material_mask_bits != material_mask_bits) {
5304 continue;
5305 }
5306 }
5307 else {
5308 if (!(ec->material_mask_bits & material_mask_bits)) {
5309 continue;
5310 }
5311 }
5312 }
5313 if (ec->type & MOD_LINEART_EDGE_FLAG_INTERSECTION) {
5314 if (mask_switches & MOD_LINEART_INTERSECTION_MATCH) {
5315 if (ec->intersection_mask != intersection_mask) {
5316 continue;
5317 }
5318 }
5319 else {
5320 if ((intersection_mask) && !(ec->intersection_mask & intersection_mask)) {
5321 continue;
5322 }
5323 }
5324 }
5325 if (shadow_selection) {
5326 if (ec->shadow_mask_bits != LRT_SHADOW_MASK_UNDEFINED) {
5327 /* TODO(@Yiming): Give a behavior option for how to display undefined shadow info. */
5328 if (shadow_selection == LINEART_SHADOW_FILTER_ILLUMINATED &&
5329 !(ec->shadow_mask_bits & LRT_SHADOW_MASK_ILLUMINATED))
5330 {
5331 continue;
5332 }
5333 if (shadow_selection == LINEART_SHADOW_FILTER_SHADED &&
5334 !(ec->shadow_mask_bits & LRT_SHADOW_MASK_SHADED))
5335 {
5336 continue;
5337 }
5338 if (shadow_selection == LINEART_SHADOW_FILTER_ILLUMINATED_ENCLOSED_SHAPES) {
5339 uint32_t test_bits = ec->shadow_mask_bits & LRT_SHADOW_TEST_SHAPE_BITS;
5340 if ((test_bits != LRT_SHADOW_MASK_ILLUMINATED) &&
5342 {
5343 continue;
5344 }
5345 }
5346 }
5347 }
5348 if (silhouette_mode && (ec->type & (MOD_LINEART_EDGE_FLAG_CONTOUR))) {
5349 bool is_silhouette = false;
5350 if (orig_col) {
5351 if (!ec->silhouette_backdrop) {
5352 is_silhouette = true;
5353 }
5354 else if (!BKE_collection_has_object_recursive_instanced(orig_col, ec->silhouette_backdrop))
5355 {
5356 is_silhouette = true;
5357 }
5358 }
5359 else {
5360 if ((!orig_ob) && (!ec->silhouette_backdrop)) {
5361 is_silhouette = true;
5362 }
5363 }
5364
5365 if ((silhouette_mode == LINEART_SILHOUETTE_FILTER_INDIVIDUAL || orig_ob) &&
5366 ec->silhouette_backdrop != ec->object_ref)
5367 {
5368 is_silhouette = true;
5369 }
5370
5371 if (inverse_silhouette) {
5372 is_silhouette = !is_silhouette;
5373 }
5374 if (!is_silhouette) {
5375 continue;
5376 }
5377 }
5378
5379 /* Preserved: If we ever do asynchronous generation, this picked flag should be set here. */
5380 // ec->picked = 1;
5381
5382 const int count = MOD_lineart_chain_count(ec);
5383 if (count < 2) {
5384 continue;
5385 }
5386
5387 total_point_count += count;
5388 writer.append({ec, count});
5389
5390 stroke_count++;
5391 }
5392
5393 if (!total_point_count || !stroke_count) {
5394 return;
5395 }
5396
5397 blender::bke::CurvesGeometry new_curves(total_point_count, stroke_count);
5399
5400 MutableAttributeAccessor attributes = new_curves.attributes_for_write();
5401 MutableSpan<float3> point_positions = new_curves.positions_for_write();
5402
5403 SpanAttributeWriter<float> point_radii = attributes.lookup_or_add_for_write_only_span<float>(
5404 "radius", AttrDomain::Point);
5405
5406 SpanAttributeWriter<float> point_opacities = attributes.lookup_or_add_for_write_span<float>(
5407 "opacity", AttrDomain::Point);
5408
5409 SpanAttributeWriter<int> stroke_materials = attributes.lookup_or_add_for_write_span<int>(
5410 "material_index", AttrDomain::Curve);
5411
5412 MutableSpan<int> offsets = new_curves.offsets_for_write();
5413
5414 const bool weight_transfer_match_output = modifier_calculation_flags &
5416
5417 using blender::StringRef;
5418
5419 int up_to_point = 0;
5420 for (int chain_i : writer.index_range()) {
5421 LineartChainWriteInfo &cwi = writer[chain_i];
5422
5424 MDeformVert *src_dvert = nullptr;
5425 Mesh *src_mesh = nullptr;
5426 if (source_vgname) {
5428 if (eval_ob && eval_ob->type == OB_MESH) {
5429 src_mesh = BKE_object_get_evaluated_mesh(eval_ob);
5430 src_dvert = src_mesh->deform_verts_for_write().data();
5431 const ListBase *deflist = BKE_id_defgroup_list_get(&src_mesh->id);
5432 LISTBASE_FOREACH (bDeformGroup *, defgroup, deflist) {
5433 if (StringRef(defgroup->name).startswith(source_vgname)) {
5434 defnames.append(defgroup->name);
5435 }
5436 }
5437 }
5438 }
5439
5440 auto transfer_to_matching_groups = [&](const int point_i, const int64_t vindex) {
5441 for (const StringRef defname : defnames) {
5442 const int src_deform_group = BKE_id_defgroup_name_index(&src_mesh->id, defname);
5443 if (UNLIKELY(src_deform_group < 0)) {
5444 continue;
5445 }
5446 MDeformWeight *mdw = BKE_defvert_ensure_index(&src_dvert[vindex], src_deform_group);
5447 SpanAttributeWriter<float> weights = attributes.lookup_or_add_for_write_span<float>(
5448 defname, AttrDomain::Point);
5449 if (UNLIKELY(!weights)) {
5450 continue;
5451 }
5452 weights.span[point_i] = invert_input ? (1 - mdw->weight) : mdw->weight;
5453 weights.finish();
5454 }
5455 };
5456
5457 auto transfer_to_singular_group = [&](const int point_i, const int64_t vindex) {
5458 SpanAttributeWriter<float> target_weights = attributes.lookup_or_add_for_write_span<float>(
5459 vgname, AttrDomain::Point);
5460 if (UNLIKELY(!target_weights)) {
5461 return;
5462 }
5463 float highest_weight = 0.0f;
5464 for (const StringRef defname : defnames) {
5465 const int src_deform_group = BKE_id_defgroup_name_index(&src_mesh->id, defname);
5466 if (UNLIKELY(src_deform_group < 0)) {
5467 continue;
5468 }
5469 MDeformWeight *mdw = BKE_defvert_ensure_index(&src_dvert[vindex], src_deform_group);
5470 const AttributeReader<float> weights = attributes.lookup<float>(defname);
5471 if (UNLIKELY(!weights)) {
5472 continue;
5473 }
5474 highest_weight = std::max(highest_weight, mdw->weight);
5475 }
5476 target_weights.span[point_i] = highest_weight;
5477 target_weights.finish();
5478 };
5479
5480 int i;
5482 int point_i = i + up_to_point;
5483 point_positions[point_i] = blender::math::transform_point(inverse_mat, float3(eci->gpos));
5484 point_radii.span[point_i] = thickness / 2.0f;
5485 if (point_opacities) {
5486 point_opacities.span[point_i] = opacity;
5487 }
5488
5489 const int64_t vindex = eci->index - cwi.chain->index_offset;
5490
5491 if (weight_transfer_match_output) {
5492 transfer_to_matching_groups(point_i, vindex);
5493 }
5494 else {
5495 transfer_to_singular_group(point_i, vindex);
5496 }
5497 }
5498 offsets[chain_i] = up_to_point;
5499 stroke_materials.span[chain_i] = max_ii(mat_nr, 0);
5500 up_to_point += cwi.point_count;
5501 }
5502
5503 offsets[writer.index_range().last() + 1] = up_to_point;
5504
5505 SpanAttributeWriter<bool> stroke_cyclic = attributes.lookup_or_add_for_write_span<bool>(
5506 "cyclic", AttrDomain::Curve);
5507 stroke_cyclic.span.fill(false);
5508 stroke_cyclic.finish();
5509
5510 point_radii.finish();
5511 point_opacities.finish();
5512 stroke_materials.finish();
5513
5514 Curves *original_curves = blender::bke::curves_new_nomain(drawing.strokes());
5515 Curves *created_curves = blender::bke::curves_new_nomain(std::move(new_curves));
5516 std::array<blender::bke::GeometrySet, 2> geometry_sets{
5520
5521 drawing.strokes_for_write() = std::move(joined.get_curves_for_write()->geometry.wrap());
5522 drawing.tag_topology_changed();
5523
5524 if (G.debug_value == 4000) {
5525 printf("LRT: Generated %d strokes.\n", stroke_count);
5526 }
5527}
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
int BKE_id_defgroup_name_index(const ID *id, blender::StringRef name)
Definition deform.cc:538
const ListBase * BKE_id_defgroup_list_get(const ID *id)
Definition deform.cc:459
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)
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
@ LRT_MATERIAL_CUSTOM_INTERSECTION_PRIORITY
@ LRT_MATERIAL_MASK_ENABLED
@ MA_BL_CULL_BACKFACE
@ 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
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr int64_t last(const int64_t n=0) const
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()
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
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
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
LineartObjectInfo * ob_info
blender::Span< int > corner_verts
blender::Span< int > tri_faces
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