Blender V5.0
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_bounds.hh"
14#include "BLI_listbase.h"
15#include "BLI_math_geom.h"
16#include "BLI_math_matrix.h"
17#include "BLI_math_matrix.hh"
18#include "BLI_math_rotation.h"
19#include "BLI_sort.hh"
20#include "BLI_task.h"
21#include "BLI_time.h"
22#include "BLI_utildefines.h"
23#include "BLI_vector.hh"
24
25#include "BKE_attribute.hh"
26#include "BKE_camera.h"
27#include "BKE_collection.hh"
28#include "BKE_curves.hh"
29#include "BKE_customdata.hh"
30#include "BKE_deform.hh"
31#include "BKE_geometry_set.hh"
32#include "BKE_global.hh"
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 Object *ob_eval = e_feat_data->ob_eval;
1524 LineartEdgeNeighbor *edge_nabr = e_feat_data->edge_nabr;
1525 const blender::Span<int3> corner_tris = e_feat_data->corner_tris;
1526 const blender::Span<int> tri_faces = e_feat_data->tri_faces;
1527 const blender::Span<int> material_indices = e_feat_data->material_indices;
1528
1529 uint16_t edge_flag_result = 0;
1530
1531 /* Because the edge neighbor array contains loop edge pairs, we only need to process the first
1532 * edge in the pair. Otherwise we would add the same edge that the loops represent twice. */
1533 if (i < edge_nabr[i].e) {
1534 return;
1535 }
1536
1537 bool face_mark_filtered = false;
1538 bool enable_face_mark = (e_feat_data->use_freestyle_face &&
1539 e_feat_data->ld->conf.filter_face_mark);
1540 bool only_contour = false;
1541 if (enable_face_mark) {
1542 bool ff1 = false;
1543 bool ff2 = false;
1544 if (const blender::VArray<bool> &freestyle_face = e_feat_data->freestyle_face) {
1545 if (freestyle_face[tri_faces[i / 3]]) {
1546 ff1 = true;
1547 }
1548 }
1549 if (edge_nabr[i].e > -1 && e_feat_data->freestyle_face) {
1550 ff2 = e_feat_data->freestyle_face[tri_faces[edge_nabr[i].e / 3]];
1551 }
1552 else {
1553 /* Handle mesh boundary cases: We want mesh boundaries to respect
1554 * `filter_face_mark_boundaries` option the same way as face mark boundaries, and the code
1555 * path is simper when it's assuming both ff1 and ff2 not nullptr. */
1556 ff2 = ff1;
1557 }
1558 if (e_feat_data->ld->conf.filter_face_mark_boundaries ^
1559 e_feat_data->ld->conf.filter_face_mark_invert)
1560 {
1561 if (ff1 || ff2) {
1562 face_mark_filtered = true;
1563 }
1564 }
1565 else {
1566 if (ff1 && ff2 && (ff2 != ff1)) {
1567 face_mark_filtered = true;
1568 }
1569 }
1570 if (e_feat_data->ld->conf.filter_face_mark_invert) {
1571 face_mark_filtered = !face_mark_filtered;
1572 }
1573 if (!face_mark_filtered) {
1575 if (e_feat_data->ld->conf.filter_face_mark_keep_contour) {
1576 only_contour = true;
1577 }
1578 }
1579 }
1580
1581 if (enable_face_mark && !face_mark_filtered && !only_contour) {
1582 return;
1583 }
1584
1585 /* Mesh boundary */
1586 if (edge_nabr[i].e == -1) {
1588 reduce_data->feat_edges += 1;
1589 return;
1590 }
1591
1592 LineartTriangle *tri1, *tri2;
1593 LineartVert *vert;
1594 LineartData *ld = e_feat_data->ld;
1595
1596 int f1 = i / 3, f2 = edge_nabr[i].e / 3;
1597
1598 /* The mesh should already be triangulated now, so we can assume each face is a triangle. */
1599 tri1 = lineart_triangle_from_index(ld, e_feat_data->tri_array, f1);
1600 tri2 = lineart_triangle_from_index(ld, e_feat_data->tri_array, f2);
1601
1602 vert = &e_feat_data->v_array[edge_nabr[i].v1];
1603
1604 double view_vector_persp[3];
1605 double *view_vector = view_vector_persp;
1606 double dot_v1 = 0, dot_v2 = 0;
1607 double result;
1608 bool material_back_face = ((tri1->flags | tri2->flags) & LRT_TRIANGLE_MAT_BACK_FACE_CULLING);
1609
1610 if (ld->conf.use_contour || ld->conf.use_back_face_culling || material_back_face) {
1611 if (ld->conf.cam_is_persp) {
1612 sub_v3_v3v3_db(view_vector, ld->conf.camera_pos, vert->gloc);
1613 }
1614 else {
1615 view_vector = ld->conf.view_vector;
1616 }
1617
1618 dot_v1 = dot_v3v3_db(view_vector, tri1->gn);
1619 dot_v2 = dot_v3v3_db(view_vector, tri2->gn);
1620
1621 if ((result = dot_v1 * dot_v2) <= 0 && (dot_v1 + dot_v2)) {
1622 edge_flag_result |= MOD_LINEART_EDGE_FLAG_CONTOUR;
1623 }
1624
1625 if (ld->conf.use_back_face_culling) {
1626 if (dot_v1 < 0) {
1627 tri1->flags |= LRT_CULL_DISCARD;
1628 }
1629 if (dot_v2 < 0) {
1630 tri2->flags |= LRT_CULL_DISCARD;
1631 }
1632 }
1633 if (material_back_face) {
1634 if (tri1->flags & LRT_TRIANGLE_MAT_BACK_FACE_CULLING && dot_v1 < 0) {
1635 tri1->flags |= LRT_CULL_DISCARD;
1636 }
1637 if (tri2->flags & LRT_TRIANGLE_MAT_BACK_FACE_CULLING && dot_v2 < 0) {
1638 tri2->flags |= LRT_CULL_DISCARD;
1639 }
1640 }
1641 }
1642
1643 if (ld->conf.use_contour_secondary) {
1644 view_vector = view_vector_persp;
1645 if (ld->conf.cam_is_persp_secondary) {
1646 sub_v3_v3v3_db(view_vector, vert->gloc, ld->conf.camera_pos_secondary);
1647 }
1648 else {
1649 view_vector = ld->conf.view_vector_secondary;
1650 }
1651
1652 dot_v1 = dot_v3v3_db(view_vector, tri1->gn);
1653 dot_v2 = dot_v3v3_db(view_vector, tri2->gn);
1654
1655 if ((result = dot_v1 * dot_v2) <= 0 && (dot_v1 + dot_v2)) {
1656 edge_flag_result |= MOD_LINEART_EDGE_FLAG_CONTOUR_SECONDARY;
1657 }
1658 }
1659
1660 if (!only_contour) {
1661 if (ld->conf.use_crease) {
1662 bool do_crease = true;
1663 if (!ld->conf.force_crease && !e_feat_data->use_auto_smooth &&
1664 (!e_feat_data->sharp_faces[tri_faces[f1]]) && (!e_feat_data->sharp_faces[tri_faces[f2]]))
1665 {
1666 do_crease = false;
1667 }
1668 if (do_crease && (dot_v3v3_db(tri1->gn, tri2->gn) < e_feat_data->crease_threshold)) {
1669 edge_flag_result |= MOD_LINEART_EDGE_FLAG_CREASE;
1670 }
1671 }
1672
1673 int mat1 = material_indices.is_empty() ? 0 : material_indices[tri_faces[f1]];
1674 int mat2 = material_indices.is_empty() ? 0 : material_indices[tri_faces[f2]];
1675
1676 if (mat1 != mat2) {
1677 Material *m1 = BKE_object_material_get_eval(ob_eval, mat1 + 1);
1678 Material *m2 = BKE_object_material_get_eval(ob_eval, mat2 + 1);
1679 if (m1 && m2 &&
1680 ((m1->lineart.mat_occlusion == 0 && m2->lineart.mat_occlusion != 0) ||
1681 (m2->lineart.mat_occlusion == 0 && m1->lineart.mat_occlusion != 0)))
1682 {
1683 if (ld->conf.use_contour) {
1684 edge_flag_result |= MOD_LINEART_EDGE_FLAG_CONTOUR;
1685 }
1686 }
1687 if (ld->conf.use_material) {
1688 edge_flag_result |= MOD_LINEART_EDGE_FLAG_MATERIAL;
1689 }
1690 }
1691 }
1692 else { /* only_contour */
1693 if (!edge_flag_result) { /* Other edge types inhibited */
1694 return;
1695 }
1696 }
1697
1698 const int3 real_edges = corner_tri_get_real_edges(e_feat_data->edges,
1699 e_feat_data->corner_verts,
1700 e_feat_data->corner_edges,
1701 corner_tris[i / 3]);
1702
1703 if (real_edges[i % 3] >= 0) {
1704 if (ld->conf.use_crease && ld->conf.sharp_as_crease &&
1705 e_feat_data->sharp_edges[real_edges[i % 3]])
1706 {
1707 edge_flag_result |= MOD_LINEART_EDGE_FLAG_CREASE;
1708 }
1709
1710 if (ld->conf.use_edge_marks && e_feat_data->use_freestyle_edge) {
1711 if (e_feat_data->freestyle_edge[real_edges[i % 3]]) {
1712 edge_flag_result |= MOD_LINEART_EDGE_FLAG_EDGE_MARK;
1713 }
1714 }
1715 }
1716
1717 edge_nabr[i].flags = edge_flag_result;
1718
1719 if (edge_flag_result) {
1720 /* Only allocate for feature edge (instead of all edges) to save memory.
1721 * If allow duplicated edges, one edge gets added multiple times if it has multiple types.
1722 */
1723 reduce_data->feat_edges += e_feat_data->ld->conf.allow_duplicated_types ?
1724 lineart_edge_type_duplication_count(edge_flag_result) :
1725 1;
1726 }
1727}
1728
1733
1735{
1736 if (pe->next >= pe->max || !pe->max) {
1737 if (!pe->max) {
1738 pe->max = 1000;
1739 }
1740
1741 LineartEdge **new_array = MEM_malloc_arrayN<LineartEdge *>(size_t(pe->max) * 2,
1742 "LineartPendingEdges array");
1743 if (LIKELY(pe->array)) {
1744 memcpy(new_array, pe->array, sizeof(LineartEdge *) * pe->max);
1745 MEM_freeN(pe->array);
1746 }
1747 pe->max *= 2;
1748 pe->array = new_array;
1749 }
1750 pe->array[pe->next] = e;
1751 pe->next++;
1752}
1757
1759{
1760 /* NOTE: For simplicity, this function doesn't actually do anything
1761 * if you already have data in #pe. */
1762
1763 if (pe->max || pe->array || count == 0) {
1764 return;
1765 }
1766
1767 pe->max = count;
1768 LineartEdge **new_array = MEM_malloc_arrayN<LineartEdge *>(size_t(pe->max),
1769 "LineartPendingEdges array final");
1770 pe->array = new_array;
1771}
1772
1774{
1775 /* In case of line art "occlusion only" or contour not enabled, it's possible for an object to
1776 * not produce any feature lines. */
1777 if (!obi->pending_edges.array) {
1778 return;
1779 }
1780 memcpy(&pe->array[pe->next],
1781 obi->pending_edges.array,
1782 sizeof(LineartEdge *) * obi->pending_edges.next);
1784 pe->next += obi->pending_edges.next;
1785}
1786
1788 LineartTriangleAdjacent *tri_adj,
1789 LineartEdge *e)
1790{
1791 if (lineart_edge_match(tri, e, 0, 1)) {
1792 tri_adj->e[0] = e;
1793 }
1794 else if (lineart_edge_match(tri, e, 1, 2)) {
1795 tri_adj->e[1] = e;
1796 }
1797 else if (lineart_edge_match(tri, e, 2, 0)) {
1798 tri_adj->e[2] = e;
1799 }
1800}
1801
1814
1815static void lineart_load_tri_task(void *__restrict userdata,
1816 const int i,
1817 const TaskParallelTLS *__restrict /*tls*/)
1818{
1819 TriData *tri_task_data = (TriData *)userdata;
1820 LineartObjectInfo *ob_info = tri_task_data->ob_info;
1821 const blender::Span<blender::float3> positions = tri_task_data->positions;
1822 const blender::Span<int> corner_verts = tri_task_data->corner_verts;
1823 const int3 &corner_tri = tri_task_data->corner_tris[i];
1824 const int face_i = tri_task_data->tri_faces[i];
1825 const blender::Span<int> material_indices = tri_task_data->material_indices;
1826
1827 LineartVert *vert_arr = tri_task_data->vert_arr;
1828 LineartTriangle *tri = tri_task_data->tri_arr;
1829
1830 tri = (LineartTriangle *)(((uchar *)tri) + tri_task_data->lineart_triangle_size * i);
1831
1832 int v1 = corner_verts[corner_tri[0]];
1833 int v2 = corner_verts[corner_tri[1]];
1834 int v3 = corner_verts[corner_tri[2]];
1835
1836 tri->v[0] = &vert_arr[v1];
1837 tri->v[1] = &vert_arr[v2];
1838 tri->v[2] = &vert_arr[v3];
1839
1840 /* Material mask bits and occlusion effectiveness assignment. */
1842 ob_info->original_ob_eval, material_indices.is_empty() ? 1 : material_indices[face_i] + 1);
1843 tri->material_mask_bits |= ((mat && (mat->lineart.flags & LRT_MATERIAL_MASK_ENABLED)) ?
1845 0);
1846 tri->mat_occlusion |= (mat ? mat->lineart.mat_occlusion : 1);
1847 tri->intersection_priority = ((mat && (mat->lineart.flags &
1850 ob_info->intersection_priority);
1851 tri->flags |= (mat && (mat->blend_flag & MA_BL_CULL_BACKFACE)) ?
1853 0;
1854
1856
1857 tri->target_reference = (ob_info->obindex | (i & LRT_OBINDEX_LOWER));
1858
1859 double gn[3];
1860 float no[3];
1861 normal_tri_v3(no, positions[v1], positions[v2], positions[v3]);
1862 copy_v3db_v3fl(gn, no);
1863 mul_v3_mat3_m4v3_db(tri->gn, ob_info->normal, gn);
1864 normalize_v3_db(tri->gn);
1865
1866 if (ob_info->usage == OBJECT_LRT_INTERSECTION_ONLY) {
1868 }
1869 else if (ob_info->usage == OBJECT_LRT_FORCE_INTERSECTION) {
1871 }
1874 }
1875
1876 /* Re-use this field to refer to adjacent info, will be cleared after culling stage. */
1877 tri->intersecting_verts = static_cast<LinkNode *>((void *)&tri_task_data->tri_adj[i]);
1878}
1886
1887static void lineart_edge_neighbor_init_task(void *__restrict userdata,
1888 const int i,
1889 const TaskParallelTLS *__restrict /*tls*/)
1890{
1891 EdgeNeighborData *en_data = (EdgeNeighborData *)userdata;
1892 LineartAdjacentEdge *adj_e = &en_data->adj_e[i];
1893 const int3 &tri = en_data->corner_tris[i / 3];
1894 LineartEdgeNeighbor *edge_nabr = &en_data->edge_nabr[i];
1895 const blender::Span<int> corner_verts = en_data->corner_verts;
1896
1897 adj_e->e = i;
1898 adj_e->v1 = corner_verts[tri[i % 3]];
1899 adj_e->v2 = corner_verts[tri[(i + 1) % 3]];
1900 if (adj_e->v1 > adj_e->v2) {
1901 std::swap(adj_e->v1, adj_e->v2);
1902 }
1903 edge_nabr->e = -1;
1904
1905 edge_nabr->v1 = adj_e->v1;
1906 edge_nabr->v2 = adj_e->v2;
1907 edge_nabr->flags = 0;
1908}
1909
1911{
1913 ai, ai + length, [](const LineartAdjacentEdge &p1, const LineartAdjacentEdge &p2) {
1914 int a = p1.v1 - p2.v1;
1915 int b = p1.v2 - p2.v2;
1916 /* `parallel_sort()` requires `cmp()` to return true when the first element needs to appear
1917 * before the second element in the sorted array, false otherwise (strict weak ordering),
1918 * see https://en.cppreference.com/w/cpp/named_req/Compare. */
1919 if (a < 0) {
1920 return true;
1921 }
1922 if (a > 0) {
1923 return false;
1924 }
1925 return b < 0;
1926 });
1927}
1928
1930{
1931 /* Because the mesh is triangulated, so `mesh->edges_num` should be reliable? */
1933 "LineartAdjacentEdge arr");
1935 size_t(total_edges), "LineartEdgeNeighbor arr");
1936
1937 TaskParallelSettings en_settings;
1939 /* Set the minimum amount of edges a thread has to process. */
1940 en_settings.min_iter_per_thread = 50000;
1941
1942 EdgeNeighborData en_data;
1943 en_data.adj_e = adj_e;
1944 en_data.edge_nabr = edge_nabr;
1945 en_data.corner_verts = mesh->corner_verts();
1946 en_data.corner_tris = mesh->corner_tris();
1947 en_data.tri_faces = mesh->corner_tri_faces();
1948
1949 BLI_task_parallel_range(0, total_edges, &en_data, lineart_edge_neighbor_init_task, &en_settings);
1950
1951 lineart_sort_adjacent_items(adj_e, total_edges);
1952
1953 for (int i = 0; i < total_edges - 1; i++) {
1954 if (adj_e[i].v1 == adj_e[i + 1].v1 && adj_e[i].v2 == adj_e[i + 1].v2) {
1955 edge_nabr[adj_e[i].e].e = adj_e[i + 1].e;
1956 edge_nabr[adj_e[i + 1].e].e = adj_e[i].e;
1957 }
1958 }
1959
1960 MEM_freeN(adj_e);
1961
1962 return edge_nabr;
1963}
1964
1966 LineartData *la_data,
1967 ListBase *shadow_elns)
1968{
1969 using namespace blender;
1970 Mesh *mesh = ob_info->original_me;
1971 if (!mesh->edges_num) {
1972 return;
1973 }
1974
1975 /* Triangulate. */
1976 const Span<int3> corner_tris = mesh->corner_tris();
1977 const AttributeAccessor attributes = mesh->attributes();
1978 const VArraySpan material_indices = *attributes.lookup<int>("material_index", AttrDomain::Face);
1979
1980 /* If we allow duplicated edges, one edge should get added multiple times if is has been
1981 * classified as more than one edge type. This is so we can create multiple different line type
1982 * chains containing the same edge. */
1983 LineartVert *la_v_arr = static_cast<LineartVert *>(lineart_mem_acquire_thread(
1984 &la_data->render_data_pool, sizeof(LineartVert) * mesh->verts_num));
1985 LineartTriangle *la_tri_arr = static_cast<LineartTriangle *>(lineart_mem_acquire_thread(
1986 &la_data->render_data_pool, corner_tris.size() * la_data->sizeof_triangle));
1987
1988 Object *orig_ob = ob_info->original_ob;
1989
1990 BLI_spin_lock(&la_data->lock_task);
1991 LineartElementLinkNode *elem_link_node = static_cast<LineartElementLinkNode *>(
1993 &la_data->render_data_pool,
1994 la_v_arr,
1995 sizeof(LineartElementLinkNode)));
1996 BLI_spin_unlock(&la_data->lock_task);
1997
1998 elem_link_node->obindex = ob_info->obindex;
1999 elem_link_node->element_count = mesh->verts_num;
2000 elem_link_node->object_ref = orig_ob;
2001 ob_info->v_eln = elem_link_node;
2002
2003 bool use_auto_smooth = false;
2004 float crease_angle = 0;
2005 if (orig_ob->lineart.flags & OBJECT_LRT_OWN_CREASE) {
2006 crease_angle = cosf(M_PI - orig_ob->lineart.crease_threshold);
2007 }
2008 else {
2009 crease_angle = la_data->conf.crease_threshold;
2010 }
2011
2012 /* FIXME(Yiming): Hack for getting clean 3D text, the seam that extruded text object creates
2013 * erroneous detection on creases. Future configuration should allow options. */
2014 if (orig_ob->type == OB_FONT) {
2015 elem_link_node->flags |= LRT_ELEMENT_BORDER_ONLY;
2016 }
2017
2018 BLI_spin_lock(&la_data->lock_task);
2019 elem_link_node = static_cast<LineartElementLinkNode *>(
2021 &la_data->render_data_pool,
2022 la_tri_arr,
2023 sizeof(LineartElementLinkNode)));
2024 BLI_spin_unlock(&la_data->lock_task);
2025
2026 int usage = ob_info->usage;
2027
2028 elem_link_node->element_count = corner_tris.size();
2029 elem_link_node->object_ref = orig_ob;
2030 elem_link_node->flags = eLineArtElementNodeFlag(
2031 elem_link_node->flags |
2033
2034 /* Note this memory is not from pool, will be deleted after culling. */
2036 size_t(corner_tris.size()), "LineartTriangleAdjacent");
2037 /* Link is minimal so we use pool anyway. */
2038 BLI_spin_lock(&la_data->lock_task);
2040 &la_data->geom.triangle_adjacent_pointers, &la_data->render_data_pool, tri_adj);
2041 BLI_spin_unlock(&la_data->lock_task);
2042
2043 /* Convert all vertices to lineart verts. */
2044 TaskParallelSettings vert_settings;
2046 /* Set the minimum amount of verts a thread has to process. */
2047 vert_settings.min_iter_per_thread = 4000;
2048
2049 VertData vert_data;
2050 vert_data.positions = mesh->vert_positions();
2051 vert_data.v_arr = la_v_arr;
2052 vert_data.model_view = ob_info->model_view;
2053 vert_data.model_view_proj = ob_info->model_view_proj;
2054
2056 0, mesh->verts_num, &vert_data, lineart_mvert_transform_task, &vert_settings);
2057
2058 /* Convert all mesh triangles into lineart triangles.
2059 * Also create an edge map to get connectivity between edges and triangles. */
2060 TaskParallelSettings tri_settings;
2062 /* Set the minimum amount of triangles a thread has to process. */
2063 tri_settings.min_iter_per_thread = 4000;
2064
2065 TriData tri_data;
2066 tri_data.ob_info = ob_info;
2067 tri_data.positions = mesh->vert_positions();
2068 tri_data.corner_tris = corner_tris;
2069 tri_data.tri_faces = mesh->corner_tri_faces();
2070 tri_data.corner_verts = mesh->corner_verts();
2071 tri_data.material_indices = material_indices;
2072 tri_data.vert_arr = la_v_arr;
2073 tri_data.tri_arr = la_tri_arr;
2074 tri_data.lineart_triangle_size = la_data->sizeof_triangle;
2075 tri_data.tri_adj = tri_adj;
2076
2077 uint32_t total_edges = corner_tris.size() * 3;
2078
2079 BLI_task_parallel_range(0, corner_tris.size(), &tri_data, lineart_load_tri_task, &tri_settings);
2080
2081 /* Check for contour lines in the mesh.
2082 * IE check if the triangle edges lies in area where the triangles go from front facing to back
2083 * facing.
2084 */
2085 EdgeFeatReduceData edge_reduce = {0};
2086 TaskParallelSettings edge_feat_settings;
2087 BLI_parallel_range_settings_defaults(&edge_feat_settings);
2088 /* Set the minimum amount of edges a thread has to process. */
2089 edge_feat_settings.min_iter_per_thread = 4000;
2090 edge_feat_settings.userdata_chunk = &edge_reduce;
2091 edge_feat_settings.userdata_chunk_size = sizeof(EdgeFeatReduceData);
2092 edge_feat_settings.func_reduce = feat_data_sum_reduce;
2093
2094 const VArray<bool> sharp_edges = *attributes.lookup_or_default<bool>(
2095 "sharp_edge", AttrDomain::Edge, false);
2096 const VArray<bool> sharp_faces = *attributes.lookup_or_default<bool>(
2097 "sharp_face", AttrDomain::Face, false);
2098
2099 EdgeFeatData edge_feat_data = {nullptr};
2100 edge_feat_data.ld = la_data;
2101 edge_feat_data.mesh = mesh;
2102 edge_feat_data.ob_eval = ob_info->original_ob_eval;
2103 edge_feat_data.material_indices = material_indices;
2104 edge_feat_data.edges = mesh->edges();
2105 edge_feat_data.corner_verts = mesh->corner_verts();
2106 edge_feat_data.corner_edges = mesh->corner_edges();
2107 edge_feat_data.corner_tris = corner_tris;
2108 edge_feat_data.tri_faces = mesh->corner_tri_faces();
2109 edge_feat_data.sharp_edges = sharp_edges;
2110 edge_feat_data.sharp_faces = sharp_faces;
2111 edge_feat_data.edge_nabr = lineart_build_edge_neighbor(mesh, total_edges);
2112 edge_feat_data.tri_array = la_tri_arr;
2113 edge_feat_data.v_array = la_v_arr;
2114 edge_feat_data.crease_threshold = crease_angle;
2115 edge_feat_data.use_auto_smooth = use_auto_smooth;
2116 edge_feat_data.freestyle_face = *attributes.lookup<bool>("freestyle_face", AttrDomain::Face);
2117 edge_feat_data.freestyle_edge = *attributes.lookup<bool>("freestyle_edge", AttrDomain::Edge);
2118 edge_feat_data.use_freestyle_face = bool(edge_feat_data.freestyle_face);
2119 edge_feat_data.use_freestyle_edge = bool(edge_feat_data.freestyle_edge);
2120
2122 total_edges,
2123 &edge_feat_data,
2125 &edge_feat_settings);
2126
2127 LooseEdgeData loose_data = {0};
2128
2129 if (la_data->conf.use_loose) {
2130 /* Only identifying floating edges at this point because other edges has been taken care of
2131 * inside #lineart_identify_corner_tri_feature_edges function. */
2132 const LooseEdgeCache &loose_edges = mesh->loose_edges();
2133 loose_data.loose_array = MEM_malloc_arrayN<int>(size_t(loose_edges.count), __func__);
2134 if (loose_edges.count > 0) {
2135 loose_data.loose_count = 0;
2136 for (const int64_t edge_i : IndexRange(mesh->edges_num)) {
2137 if (loose_edges.is_loose_bits[edge_i]) {
2138 loose_data.loose_array[loose_data.loose_count] = int(edge_i);
2139 loose_data.loose_count++;
2140 }
2141 }
2142 }
2143 }
2144
2145 int allocate_la_e = edge_reduce.feat_edges + loose_data.loose_count;
2146
2147 LineartEdge *la_edge_arr = static_cast<LineartEdge *>(
2148 lineart_mem_acquire_thread(la_data->edge_data_pool, sizeof(LineartEdge) * allocate_la_e));
2150 la_data->edge_data_pool, sizeof(LineartEdgeSegment) * allocate_la_e));
2151 BLI_spin_lock(&la_data->lock_task);
2152 elem_link_node = static_cast<LineartElementLinkNode *>(
2154 la_data->edge_data_pool,
2155 la_edge_arr,
2156 sizeof(LineartElementLinkNode)));
2157 BLI_spin_unlock(&la_data->lock_task);
2158 elem_link_node->element_count = allocate_la_e;
2159 elem_link_node->object_ref = orig_ob;
2160 elem_link_node->obindex = ob_info->obindex;
2161
2162 LineartElementLinkNode *shadow_eln = nullptr;
2163 if (shadow_elns) {
2164 shadow_eln = lineart_find_matching_eln(shadow_elns, ob_info->obindex);
2165 }
2166
2167 /* Start of the edge/seg arr */
2168 LineartEdge *la_edge;
2169 LineartEdgeSegment *la_seg;
2170 la_edge = la_edge_arr;
2171 la_seg = la_seg_arr;
2172
2173 for (int i = 0; i < total_edges; i++) {
2174 LineartEdgeNeighbor *edge_nabr = &edge_feat_data.edge_nabr[i];
2175
2176 if (i < edge_nabr->e) {
2177 continue;
2178 }
2179
2180 /* Not a feature line, so we skip. */
2181 if (edge_nabr->flags == 0) {
2182 continue;
2183 }
2184
2185 LineartEdge *edge_added = nullptr;
2186
2187 /* See eLineartEdgeFlag for details. */
2188 for (int flag_bit = 0; flag_bit < LRT_MESH_EDGE_TYPES_COUNT; flag_bit++) {
2189 int use_type = LRT_MESH_EDGE_TYPES[flag_bit];
2190 if (!(use_type & edge_nabr->flags)) {
2191 continue;
2192 }
2193
2194 la_edge->v1 = &la_v_arr[edge_nabr->v1];
2195 la_edge->v2 = &la_v_arr[edge_nabr->v2];
2196 int findex = i / 3;
2197 la_edge->t1 = lineart_triangle_from_index(la_data, la_tri_arr, findex);
2198 if (!edge_added) {
2199 lineart_triangle_adjacent_assign(la_edge->t1, &tri_adj[findex], la_edge);
2200 }
2201 if (edge_nabr->e != -1) {
2202 findex = edge_nabr->e / 3;
2203 la_edge->t2 = lineart_triangle_from_index(la_data, la_tri_arr, findex);
2204 if (!edge_added) {
2205 lineart_triangle_adjacent_assign(la_edge->t2, &tri_adj[findex], la_edge);
2206 }
2207 }
2208 la_edge->flags = use_type;
2209 la_edge->object_ref = orig_ob;
2210 la_edge->edge_identifier = LRT_EDGE_IDENTIFIER(ob_info, la_edge);
2211 BLI_addtail(&la_edge->segments, la_seg);
2212
2213 if (shadow_eln) {
2214 /* TODO(Yiming): It's gonna be faster to do this operation after second stage occlusion if
2215 * we only need visible segments to have shadow info, however that way we lose information
2216 * on "shadow behind transparency window" type of region. */
2217 LineartEdge *shadow_e = lineart_find_matching_edge(shadow_eln, la_edge->edge_identifier);
2218 if (shadow_e) {
2219 lineart_register_shadow_cuts(la_data, la_edge, shadow_e);
2220 }
2221 }
2222
2223 if (ELEM(usage,
2228 {
2229 lineart_add_edge_to_array_thread(ob_info, la_edge);
2230 }
2231
2232 if (edge_added) {
2234 }
2235
2236 edge_added = la_edge;
2237
2238 la_edge++;
2239 la_seg++;
2240
2241 if (!la_data->conf.allow_duplicated_types) {
2242 break;
2243 }
2244 }
2245 }
2246
2247 if (loose_data.loose_array) {
2248 const Span<int2> edges = mesh->edges();
2249 for (int i = 0; i < loose_data.loose_count; i++) {
2250 const int2 &edge = edges[loose_data.loose_array[i]];
2251 la_edge->v1 = &la_v_arr[edge[0]];
2252 la_edge->v2 = &la_v_arr[edge[1]];
2254 la_edge->object_ref = orig_ob;
2255 la_edge->edge_identifier = LRT_EDGE_IDENTIFIER(ob_info, la_edge);
2256 BLI_addtail(&la_edge->segments, la_seg);
2257 if (ELEM(usage,
2262 {
2263 lineart_add_edge_to_array_thread(ob_info, la_edge);
2264 if (shadow_eln) {
2265 LineartEdge *shadow_e = lineart_find_matching_edge(shadow_eln, la_edge->edge_identifier);
2266 if (shadow_e) {
2267 lineart_register_shadow_cuts(la_data, la_edge, shadow_e);
2268 }
2269 }
2270 }
2271 la_edge++;
2272 la_seg++;
2273 }
2274 MEM_SAFE_FREE(loose_data.loose_array);
2275 }
2276
2277 MEM_freeN(edge_feat_data.edge_nabr);
2278
2279 if (ob_info->free_use_mesh) {
2280 BKE_id_free(nullptr, mesh);
2281 }
2282}
2283
2284static void lineart_object_load_worker(TaskPool *__restrict /*pool*/,
2286{
2287 for (LineartObjectInfo *obi = olti->pending; obi; obi = obi->next) {
2288 lineart_geometry_object_load(obi, olti->ld, olti->shadow_elns);
2289 }
2290}
2291
2293{
2295 uchar result = lineart_intersection_mask_check(cc->collection, ob);
2296 if (result) {
2297 return result;
2298 }
2299 }
2300
2301 if (BKE_collection_has_object(c, (Object *)(ob->id.orig_id))) {
2303 return c->lineart_intersection_mask;
2304 }
2305 }
2306
2307 return 0;
2308}
2309
2311{
2313 return ob->lineart.intersection_priority;
2314 }
2315
2317 uchar result = lineart_intersection_priority_check(cc->collection, ob);
2318 if (result) {
2319 return result;
2320 }
2321 }
2322 if (BKE_collection_has_object(c, (Object *)(ob->id.orig_id))) {
2325 }
2326 }
2327 return 0;
2328}
2329
2334static int lineart_usage_check(Collection *c, Object *ob, bool is_render)
2335{
2336
2337 if (!c) {
2338 return OBJECT_LRT_INHERIT;
2339 }
2340
2341 int object_has_special_usage = (ob->lineart.usage != OBJECT_LRT_INHERIT);
2342
2343 if (object_has_special_usage) {
2344 return ob->lineart.usage;
2345 }
2346
2347 if (c->gobject.first) {
2348 if (BKE_collection_has_object(c, (Object *)(ob->id.orig_id))) {
2349 if ((is_render && (c->flag & COLLECTION_HIDE_RENDER)) ||
2350 ((!is_render) && (c->flag & COLLECTION_HIDE_VIEWPORT)))
2351 {
2352 return OBJECT_LRT_EXCLUDE;
2353 }
2354 if (ob->lineart.usage == OBJECT_LRT_INHERIT) {
2355 switch (c->lineart_usage) {
2359 return OBJECT_LRT_EXCLUDE;
2366 }
2367 return OBJECT_LRT_INHERIT;
2368 }
2369 return ob->lineart.usage;
2370 }
2371 }
2372
2374 int result = lineart_usage_check(cc->collection, ob, is_render);
2375 if (result > OBJECT_LRT_INHERIT) {
2376 return result;
2377 }
2378 }
2379
2380 return OBJECT_LRT_INHERIT;
2381}
2382
2384 LineartObjectInfo *obi,
2385 int thread_count,
2386 int this_face_count)
2387{
2388 LineartObjectLoadTaskInfo *use_olti = olti_list;
2389 uint64_t min_face = use_olti->total_faces;
2390 for (int i = 0; i < thread_count; i++) {
2391 if (olti_list[i].total_faces < min_face) {
2392 min_face = olti_list[i].total_faces;
2393 use_olti = &olti_list[i];
2394 }
2395 }
2396
2397 use_olti->total_faces += this_face_count;
2398 obi->next = use_olti->pending;
2399 use_olti->pending = obi;
2400}
2401
2402static bool lineart_geometry_check_visible(double model_view_proj[4][4],
2403 double shift_x,
2404 double shift_y,
2405 Mesh *use_mesh)
2406{
2407 using namespace blender;
2408 if (!use_mesh) {
2409 return false;
2410 }
2411 const std::optional<Bounds<float3>> bounds = use_mesh->bounds_min_max();
2412 if (!bounds.has_value()) {
2413 return false;
2414 }
2415 const std::array<float3, 8> corners = blender::bounds::corners(*bounds);
2416
2417 double co[8][4];
2418 double tmp[3];
2419 for (int i = 0; i < 8; i++) {
2420 copy_v3db_v3fl(co[i], corners[i]);
2421 copy_v3_v3_db(tmp, co[i]);
2422 mul_v4_m4v3_db(co[i], model_view_proj, tmp);
2423 co[i][0] -= shift_x * 2 * co[i][3];
2424 co[i][1] -= shift_y * 2 * co[i][3];
2425 }
2426
2427 bool cond[6] = {true, true, true, true, true, true};
2428 /* Because for a point to be inside clip space, it must satisfy `-Wc <= XYCc <= Wc`, here if
2429 * all verts falls to the same side of the clip space border, we know it's outside view. */
2430 for (int i = 0; i < 8; i++) {
2431 cond[0] &= (co[i][0] < -co[i][3]);
2432 cond[1] &= (co[i][0] > co[i][3]);
2433 cond[2] &= (co[i][1] < -co[i][3]);
2434 cond[3] &= (co[i][1] > co[i][3]);
2435 cond[4] &= (co[i][2] < -co[i][3]);
2436 cond[5] &= (co[i][2] > co[i][3]);
2437 }
2438 for (int i = 0; i < 6; i++) {
2439 if (cond[i]) {
2440 return false;
2441 }
2442 }
2443 return true;
2444}
2445
2447 Depsgraph *depsgraph,
2448 Scene *scene,
2449 Object *ob,
2450 Object *ref_ob,
2451 const float use_mat[4][4],
2452 bool is_render,
2454 int thread_count,
2455 int obindex)
2456{
2457 LineartObjectInfo *obi = static_cast<LineartObjectInfo *>(
2459 obi->usage = lineart_usage_check(scene->master_collection, ob, is_render);
2462 Mesh *use_mesh;
2463
2464 if (obi->usage == OBJECT_LRT_EXCLUDE) {
2465 return;
2466 }
2467
2468 obi->obindex = obindex << LRT_OBINDEX_SHIFT;
2469
2470 /* Prepare the matrix used for transforming this specific object (instance). This has to be
2471 * done before mesh bound-box check because the function needs that. */
2473 mul_m4db_m4db_m4fl(obi->model_view, ld->conf.view, use_mat);
2474
2476 return;
2477 }
2478 if (ob->type == OB_MESH) {
2479 use_mesh = BKE_object_get_evaluated_mesh(ob);
2480 if ((!use_mesh) || use_mesh->runtime->edit_mesh) {
2481 /* If the object is being edited, then the mesh is not evaluated fully into the final
2482 * result, do not load them. This could be caused by incorrect evaluation order due to
2483 * the way line art uses depsgraph.See #102612 for explanation of this workaround. */
2484 return;
2485 }
2486 }
2487 else {
2488 use_mesh = BKE_mesh_new_from_object(depsgraph, ob, true, true, true);
2489 }
2490
2491 /* In case we still can not get any mesh geometry data from the object, same as above. */
2492 if (!use_mesh) {
2493 return;
2494 }
2495
2497 obi->model_view_proj, ld->conf.shift_x, ld->conf.shift_y, use_mesh))
2498 {
2499 return;
2500 }
2501
2502 if (ob->type != OB_MESH) {
2503 obi->free_use_mesh = true;
2504 }
2505
2506 /* Make normal matrix. */
2507 float imat[4][4];
2508 invert_m4_m4(imat, use_mat);
2509 transpose_m4(imat);
2510 copy_m4d_m4(obi->normal, imat);
2511
2512 obi->original_me = use_mesh;
2513 obi->original_ob = (ref_ob->id.orig_id ? (Object *)ref_ob->id.orig_id : ref_ob);
2515 lineart_geometry_load_assign_thread(olti, obi, thread_count, use_mesh->faces_num);
2516}
2517
2519 Scene *scene,
2520 Object *camera /* Still use camera arg for convenience. */,
2521 LineartData *ld,
2522 bool allow_duplicates,
2523 bool do_shadow_casting,
2524 ListBase *shadow_elns,
2525 blender::Set<const Object *> *included_objects)
2526{
2527 double proj[4][4], view[4][4], result[4][4];
2528 float inv[4][4];
2529
2530 if (!do_shadow_casting) {
2531 Camera *cam = static_cast<Camera *>(camera->data);
2532 float sensor = BKE_camera_sensor_size(cam->sensor_fit, cam->sensor_x, cam->sensor_y);
2533 int fit = BKE_camera_sensor_fit(cam->sensor_fit, ld->w, ld->h);
2534 double asp = (double(ld->w) / double(ld->h));
2535 if (ELEM(cam->type, CAM_PERSP, CAM_PANO, CAM_CUSTOM)) {
2536 if (fit == CAMERA_SENSOR_FIT_VERT && asp > 1) {
2537 sensor *= asp;
2538 }
2539 if (fit == CAMERA_SENSOR_FIT_HOR && asp < 1) {
2540 sensor /= asp;
2541 }
2542 const double fov = focallength_to_fov(cam->lens / (1 + ld->conf.overscan), sensor);
2543 lineart_matrix_perspective_44d(proj, fov, asp, cam->clip_start, cam->clip_end);
2544 }
2545 else if (cam->type == CAM_ORTHO) {
2546 const double w = cam->ortho_scale / 2;
2547 lineart_matrix_ortho_44d(proj, -w, w, -w / asp, w / asp, cam->clip_start, cam->clip_end);
2548 }
2549 else {
2550 BLI_assert(!"Unsupported camera type in lineart_main_load_geometries");
2551 unit_m4_db(proj);
2552 }
2553
2554 invert_m4_m4(inv, ld->conf.cam_obmat);
2555 mul_m4db_m4db_m4fl(result, proj, inv);
2556 copy_m4_m4_db(proj, result);
2558
2561 }
2562
2565
2566 double t_start;
2567 if (G.debug_value == 4000) {
2568 t_start = BLI_time_now_seconds();
2569 }
2570
2571 int thread_count = ld->thread_count;
2572 int bound_box_discard_count = 0;
2573 int obindex = 0;
2574
2575 /* This memory is in render buffer memory pool. So we don't need to free those after loading. */
2577 &ld->render_data_pool, sizeof(LineartObjectLoadTaskInfo) * thread_count));
2578
2580 bool is_render = eval_mode == DAG_EVAL_RENDER;
2581
2584
2585 /* Instance duplicated & particles. */
2586 if (allow_duplicates) {
2588 }
2589
2590 DEGObjectIterSettings deg_iter_settings = {nullptr};
2591 deg_iter_settings.depsgraph = depsgraph;
2592 deg_iter_settings.flags = flags;
2593 deg_iter_settings.included_objects = included_objects;
2594
2595 DEG_OBJECT_ITER_BEGIN (&deg_iter_settings, ob) {
2596
2597 obindex++;
2598
2599 Object *eval_ob = DEG_get_evaluated(depsgraph, ob);
2600
2601 if (!eval_ob) {
2602 continue;
2603 }
2604
2605 /* DEG_OBJECT_ITER_BEGIN will include the instanced mesh of these curve object types, so don't
2606 * load them twice. */
2607 if (allow_duplicates && ELEM(ob->type, OB_CURVES_LEGACY, OB_FONT, OB_SURF, OB_CURVES)) {
2608 continue;
2609 }
2610
2611 if (BKE_object_visibility(eval_ob, eval_mode) & OB_VISIBLE_SELF) {
2613 depsgraph,
2614 scene,
2615 eval_ob,
2616 eval_ob,
2617 eval_ob->object_to_world().ptr(),
2618 is_render,
2619 olti,
2620 thread_count,
2621 obindex);
2622 }
2623 }
2625
2627
2628 if (G.debug_value == 4000) {
2629 printf("thread count: %d\n", thread_count);
2630 }
2631 for (int i = 0; i < thread_count; i++) {
2632 olti[i].ld = ld;
2633 olti[i].shadow_elns = shadow_elns;
2634 olti[i].thread_id = i;
2635 BLI_task_pool_push(tp, (TaskRunFunction)lineart_object_load_worker, &olti[i], false, nullptr);
2636 }
2639
2640 /* The step below is to serialize vertex index in the whole scene, so
2641 * lineart_triangle_share_edge() can work properly from the lack of triangle adjacent info. */
2642 int global_i = 0;
2643
2644 int edge_count = 0;
2645 for (int i = 0; i < thread_count; i++) {
2646 for (LineartObjectInfo *obi = olti[i].pending; obi; obi = obi->next) {
2647 if (!obi->v_eln) {
2648 continue;
2649 }
2650 edge_count += obi->pending_edges.next;
2651 }
2652 }
2654
2655 for (int i = 0; i < thread_count; i++) {
2656 for (LineartObjectInfo *obi = olti[i].pending; obi; obi = obi->next) {
2657 if (!obi->v_eln) {
2658 continue;
2659 }
2660 LineartVert *v = (LineartVert *)obi->v_eln->pointer;
2661 int v_count = obi->v_eln->element_count;
2662 obi->v_eln->global_index_offset = global_i;
2663 for (int vi = 0; vi < v_count; vi++) {
2664 v[vi].index += global_i;
2665 }
2666 /* Register a global index increment. See #lineart_triangle_share_edge() and
2667 * #lineart_main_load_geometries() for detailed. It's okay that global_vindex might
2668 * eventually overflow, in such large scene it's virtually impossible for two vertex of the
2669 * same numeric index to come close together. */
2670 obi->global_i_offset = global_i;
2671 global_i += v_count;
2673 }
2674 }
2675
2676 if (G.debug_value == 4000) {
2677 double t_elapsed = BLI_time_now_seconds() - t_start;
2678 printf("Line art loading time: %lf\n", t_elapsed);
2679 printf("Discarded %d object from bound box check\n", bound_box_discard_count);
2680 }
2681}
2682
2688 const LineartVert *vt,
2689 LineartVert **l,
2690 LineartVert **r)
2691{
2692 if (tri->v[0] == vt) {
2693 *l = tri->v[1];
2694 *r = tri->v[2];
2695 return true;
2696 }
2697 if (tri->v[1] == vt) {
2698 *l = tri->v[2];
2699 *r = tri->v[0];
2700 return true;
2701 }
2702 if (tri->v[2] == vt) {
2703 *l = tri->v[0];
2704 *r = tri->v[1];
2705 return true;
2706 }
2707 return false;
2708}
2709
2711 const LineartEdge *e,
2712 bool allow_overlapping_edges)
2713{
2714 const LineartEdge *use_e = e;
2716 if (((e->target_reference & LRT_LIGHT_CONTOUR_TARGET) == tri->target_reference) ||
2717 (((e->target_reference >> 32) & LRT_LIGHT_CONTOUR_TARGET) == tri->target_reference))
2718 {
2719 return true;
2720 }
2721 }
2722 else {
2723 /* Normally we just determine from identifiers of adjacent triangles. */
2724 if ((use_e->t1 && use_e->t1->target_reference == tri->target_reference) ||
2725 (use_e->t2 && use_e->t2->target_reference == tri->target_reference))
2726 {
2727 return true;
2728 }
2729 }
2730
2731 /* If allows overlapping, then we compare the vertex coordinates one by one to determine if one
2732 * edge is from specific triangle. This is slower but can handle edge split cases very well. */
2733 if (allow_overlapping_edges) {
2734#define LRT_TRI_SAME_POINT(tri, i, pt) \
2735 ((LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[0], pt->gloc[0]) && \
2736 LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[1], pt->gloc[1]) && \
2737 LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[2], pt->gloc[2])) || \
2738 (LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[0], pt->gloc[0]) && \
2739 LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[1], pt->gloc[1]) && \
2740 LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[2], pt->gloc[2])))
2741 if ((LRT_TRI_SAME_POINT(tri, 0, e->v1) || LRT_TRI_SAME_POINT(tri, 1, e->v1) ||
2742 LRT_TRI_SAME_POINT(tri, 2, e->v1)) &&
2743 (LRT_TRI_SAME_POINT(tri, 0, e->v2) || LRT_TRI_SAME_POINT(tri, 1, e->v2) ||
2744 LRT_TRI_SAME_POINT(tri, 2, e->v2)))
2745 {
2746 return true;
2747 }
2748#undef LRT_TRI_SAME_POINT
2749 }
2750 return false;
2751}
2752
2753/* Sorting three intersection points from min to max,
2754 * the order for each intersection is set in `lst[0]` to `lst[2]`. */
2755#define INTERSECT_SORT_MIN_TO_MAX_3(ia, ib, ic, lst) \
2756 { \
2757 lst[0] = LRT_MIN3_INDEX(ia, ib, ic); \
2758 lst[1] = (((ia <= ib && ib <= ic) || (ic <= ib && ib <= ia)) ? \
2759 1 : \
2760 (((ic <= ia && ia <= ib) || (ib < ia && ia <= ic)) ? 0 : 2)); \
2761 lst[2] = LRT_MAX3_INDEX(ia, ib, ic); \
2762 }
2763
2764/* `ia ib ic` are ordered. */
2765#define INTERSECT_JUST_GREATER(is, order, num, index) \
2766 { \
2767 index = (num < is[order[0]] ? \
2768 order[0] : \
2769 (num < is[order[1]] ? order[1] : (num < is[order[2]] ? order[2] : -1))); \
2770 }
2771
2772/* `ia ib ic` are ordered. */
2773#define INTERSECT_JUST_SMALLER(is, order, num, index) \
2774 { \
2775 index = (num > is[order[2]] ? \
2776 order[2] : \
2777 (num > is[order[1]] ? order[1] : (num > is[order[0]] ? order[0] : -1))); \
2778 }
2779
2780#define LRT_ISEC(index) (index == 0 ? isec_e1 : (index == 1 ? isec_e2 : isec_e3))
2781#define LRT_PARALLEL(index) (index == 0 ? para_e1 : (index == 1 ? para_e2 : para_e3))
2782
2806 const LineartEdge *e,
2807 const double *override_camera_loc,
2808 const bool override_cam_is_persp,
2809 const bool allow_overlapping_edges,
2810 const double m_view_projection[4][4],
2811 const double camera_dir[3],
2812 const float cam_shift_x,
2813 const float cam_shift_y,
2814 double *from,
2815 double *to)
2816{
2817 double cross_ratios[3] = {0};
2818 int cross_order[3];
2819 int cross_v1 = -1, cross_v2 = -1;
2820 /* If the edge intersects with the triangle edges (including extensions). */
2821 int isec_e1, isec_e2, isec_e3;
2822 /* If edge is parallel to one of the edges in the triangle. */
2823 bool para_e1, para_e2, para_e3;
2824 enum LineartPointTri state_v1 = LRT_OUTSIDE_TRIANGLE, state_v2 = LRT_OUTSIDE_TRIANGLE;
2825
2826 double dir_v1[3];
2827 double dir_v2[3];
2828 double view_vector[4];
2829 double dir_cam[3];
2830 double dot_v1, dot_v2, dot_v1a, dot_v2a;
2831 double dot_f;
2832 double gloc[4], trans[4];
2833 double cut = -1;
2834
2835 double *LFBC = e->v1->fbcoord, *RFBC = e->v2->fbcoord, *FBC0 = tri->v[0]->fbcoord,
2836 *FBC1 = tri->v[1]->fbcoord, *FBC2 = tri->v[2]->fbcoord;
2837
2838 /* Overlapping not possible, return early. */
2839 if ((std::max({FBC0[0], FBC1[0], FBC2[0]}) < std::min(LFBC[0], RFBC[0])) ||
2840 (std::min({FBC0[0], FBC1[0], FBC2[0]}) > std::max(LFBC[0], RFBC[0])) ||
2841 (std::max({FBC0[1], FBC1[1], FBC2[1]}) < std::min(LFBC[1], RFBC[1])) ||
2842 (std::min({FBC0[1], FBC1[1], FBC2[1]}) > std::max(LFBC[1], RFBC[1])) ||
2843 (std::min({FBC0[3], FBC1[3], FBC2[3]}) > std::max(LFBC[3], RFBC[3])))
2844 {
2845 return false;
2846 }
2847
2848 /* If the line is one of the edge in the triangle, then it's not occluded. */
2849 if (lineart_edge_from_triangle(tri, e, allow_overlapping_edges)) {
2850 return false;
2851 }
2852
2853 /* Check if the line visually crosses one of the edge in the triangle. */
2854 isec_e1 = lineart_intersect_seg_seg(LFBC, RFBC, FBC0, FBC1, &cross_ratios[0], &para_e1);
2855 isec_e2 = lineart_intersect_seg_seg(LFBC, RFBC, FBC1, FBC2, &cross_ratios[1], &para_e2);
2856 isec_e3 = lineart_intersect_seg_seg(LFBC, RFBC, FBC2, FBC0, &cross_ratios[2], &para_e3);
2857
2858 /* Sort the intersection distance. */
2859 INTERSECT_SORT_MIN_TO_MAX_3(cross_ratios[0], cross_ratios[1], cross_ratios[2], cross_order);
2860
2861 sub_v3_v3v3_db(dir_v1, e->v1->gloc, tri->v[0]->gloc);
2862 sub_v3_v3v3_db(dir_v2, e->v2->gloc, tri->v[0]->gloc);
2863
2864 copy_v3_v3_db(dir_cam, camera_dir);
2865 copy_v3_v3_db(view_vector, override_camera_loc);
2866 if (override_cam_is_persp) {
2867 sub_v3_v3v3_db(dir_cam, view_vector, tri->v[0]->gloc);
2868 }
2869
2870 dot_v1 = dot_v3v3_db(dir_v1, tri->gn);
2871 dot_v2 = dot_v3v3_db(dir_v2, tri->gn);
2872 dot_f = dot_v3v3_db(dir_cam, tri->gn);
2873
2875 (e->target_reference == tri->target_reference))
2876 {
2877 if (((dot_f > 0) && (e->flags & MOD_LINEART_EDGE_FLAG_SHADOW_FACING_LIGHT)) ||
2878 ((dot_f < 0) && !(e->flags & MOD_LINEART_EDGE_FLAG_SHADOW_FACING_LIGHT)))
2879 {
2880 *from = 0.0f;
2881 *to = 1.0f;
2882 return true;
2883 }
2884
2885 return false;
2886 }
2887
2888 /* NOTE(Yiming): When we don't use `dot_f==0` here, it's theoretically possible that _some_
2889 * faces in perspective mode would get erroneously caught in this condition where they really
2890 * are legit faces that would produce occlusion, but haven't encountered those yet in my test
2891 * files.
2892 */
2893 if (fabs(dot_f) < FLT_EPSILON) {
2894 return false;
2895 }
2896
2897 /* Whether two end points are inside/on_the_edge/outside of the triangle. */
2898 state_v1 = lineart_point_triangle_relation(LFBC, FBC0, FBC1, FBC2);
2899 state_v2 = lineart_point_triangle_relation(RFBC, FBC0, FBC1, FBC2);
2900
2901 /* If the edge doesn't visually cross any edge of the triangle... */
2902 if (!isec_e1 && !isec_e2 && !isec_e3) {
2903 /* And if both end point from the edge is outside of the triangle... */
2904 if ((!state_v1) && (!state_v2)) {
2905 return false; /* We don't have any occlusion. */
2906 }
2907 }
2908
2909 /* Determine the cut position. */
2910
2911 dot_v1a = fabs(dot_v1);
2912 if (dot_v1a < DBL_EPSILON) {
2913 dot_v1a = 0;
2914 dot_v1 = 0;
2915 }
2916 dot_v2a = fabs(dot_v2);
2917 if (dot_v2a < DBL_EPSILON) {
2918 dot_v2a = 0;
2919 dot_v2 = 0;
2920 }
2921 if (dot_v1 - dot_v2 == 0) {
2922 cut = 100000;
2923 }
2924 else if (dot_v1 * dot_v2 <= 0) {
2925 cut = dot_v1a / fabs(dot_v1 - dot_v2);
2926 }
2927 else {
2928 cut = fabs(dot_v2 + dot_v1) / fabs(dot_v1 - dot_v2);
2929 cut = dot_v2a > dot_v1a ? 1 - cut : cut;
2930 }
2931
2932 /* Transform the cut from geometry space to image space. */
2933 if (override_cam_is_persp) {
2934 interp_v3_v3v3_db(gloc, e->v1->gloc, e->v2->gloc, cut);
2935 mul_v4_m4v3_db(trans, m_view_projection, gloc);
2936 mul_v3db_db(trans, (1 / trans[3]));
2937 trans[0] -= cam_shift_x * 2;
2938 trans[1] -= cam_shift_y * 2;
2939 /* To accommodate `k=0` and `k=inf` (vertical) lines. here the cut is in image space. */
2940 if (fabs(e->v1->fbcoord[0] - e->v2->fbcoord[0]) > fabs(e->v1->fbcoord[1] - e->v2->fbcoord[1]))
2941 {
2942 cut = ratiod(e->v1->fbcoord[0], e->v2->fbcoord[0], trans[0]);
2943 }
2944 else {
2945 cut = ratiod(e->v1->fbcoord[1], e->v2->fbcoord[1], trans[1]);
2946 }
2947 }
2948
2949#define LRT_GUARD_NOT_FOUND \
2950 if (cross_v1 < 0 || cross_v2 < 0) { \
2951 return false; \
2952 }
2953
2954 /* Determine the pair of edges that the line has crossed. The "|" symbol in the comment
2955 * indicates triangle boundary. DBL_TRIANGLE_LIM is needed to for floating point precision
2956 * tolerance. */
2957
2958 if (state_v1 == LRT_INSIDE_TRIANGLE) {
2959 /* Left side is in the triangle. */
2960 if (state_v2 == LRT_INSIDE_TRIANGLE) {
2961 /* | l---r | */
2962 INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v1);
2963 INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v2);
2964 }
2965 else if (state_v2 == LRT_ON_TRIANGLE) {
2966 /* | l------r| */
2967 INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v1);
2968 INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v2);
2969 }
2970 else if (state_v2 == LRT_OUTSIDE_TRIANGLE) {
2971 /* | l-------|------r */
2972 INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v1);
2973 INTERSECT_JUST_GREATER(cross_ratios, cross_order, 0, cross_v2);
2974 }
2975 }
2976 else if (state_v1 == LRT_ON_TRIANGLE) {
2977 /* Left side is on some edge of the triangle. */
2978 if (state_v2 == LRT_INSIDE_TRIANGLE) {
2979 /* |l------r | */
2980 INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v1);
2981 INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v2);
2982 }
2983 else if (state_v2 == LRT_ON_TRIANGLE) {
2984 /* |l---------r| */
2985 INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v1);
2986 INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v2);
2987 }
2988 else if (state_v2 == LRT_OUTSIDE_TRIANGLE) {
2989 /* |l----------|-------r (crossing the triangle) [OR]
2990 * r---------|l | (not crossing the triangle) */
2991 INTERSECT_JUST_GREATER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v2);
2992 if (cross_v2 >= 0 && LRT_ISEC(cross_v2) && cross_ratios[cross_v2] > (DBL_TRIANGLE_LIM)) {
2993 INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v1);
2994 }
2995 else {
2996 INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v2);
2997 if (cross_v2 > 0) {
2998 INTERSECT_JUST_SMALLER(cross_ratios, cross_order, cross_ratios[cross_v2], cross_v1);
2999 }
3000 }
3002 /* We could have the edge being completely parallel to the triangle where there isn't a
3003 * viable occlusion result. */
3004 if ((LRT_PARALLEL(cross_v1) && !LRT_ISEC(cross_v1)) ||
3005 (LRT_PARALLEL(cross_v2) && !LRT_ISEC(cross_v2)))
3006 {
3007 return false;
3008 }
3009 }
3010 }
3011 else if (state_v1 == LRT_OUTSIDE_TRIANGLE) {
3012 /* Left side is outside of the triangle. */
3013 if (state_v2 == LRT_INSIDE_TRIANGLE) {
3014 /* l---|---r | */
3015 INTERSECT_JUST_SMALLER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v1);
3016 INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v2);
3017 }
3018 else if (state_v2 == LRT_ON_TRIANGLE) {
3019 /* |r----------|-------l (crossing the triangle) [OR]
3020 * l---------|r | (not crossing the triangle) */
3021 INTERSECT_JUST_SMALLER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v1);
3022 if (cross_v1 >= 0 && LRT_ISEC(cross_v1) && cross_ratios[cross_v1] < (1 - DBL_TRIANGLE_LIM)) {
3023 INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v2);
3024 }
3025 else {
3026 INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v1);
3027 if (cross_v1 > 0) {
3028 INTERSECT_JUST_GREATER(cross_ratios, cross_order, cross_ratios[cross_v1], cross_v2);
3029 }
3030 }
3032 /* The same logic applies as above case. */
3033 if ((LRT_PARALLEL(cross_v1) && !LRT_ISEC(cross_v1)) ||
3034 (LRT_PARALLEL(cross_v2) && !LRT_ISEC(cross_v2)))
3035 {
3036 return false;
3037 }
3038 }
3039 else if (state_v2 == LRT_OUTSIDE_TRIANGLE) {
3040 /* l---|----|----r (crossing the triangle) [OR]
3041 * l----r | | (not crossing the triangle) */
3042 INTERSECT_JUST_GREATER(cross_ratios, cross_order, -DBL_TRIANGLE_LIM, cross_v1);
3043 if (cross_v1 >= 0 && LRT_ISEC(cross_v1)) {
3044 INTERSECT_JUST_GREATER(cross_ratios, cross_order, cross_ratios[cross_v1], cross_v2);
3045 }
3046 else {
3047 if (cross_v1 >= 0) {
3048 INTERSECT_JUST_GREATER(cross_ratios, cross_order, cross_ratios[cross_v1], cross_v1);
3049 if (cross_v1 >= 0) {
3050 INTERSECT_JUST_GREATER(cross_ratios, cross_order, cross_ratios[cross_v1], cross_v2);
3051 }
3052 }
3053 }
3054 }
3055 }
3056
3058
3059 double dot_1f = dot_v1 * dot_f, dot_2f = dot_v2 * dot_f;
3060
3061 /* Determine the start and end point of image space cut on a line. */
3062 if (dot_1f <= 0 && dot_2f <= 0 && (dot_v1 || dot_v2)) {
3063 *from = std::max(0.0, cross_ratios[cross_v1]);
3064 *to = std::min(1.0, cross_ratios[cross_v2]);
3065 if (*from >= *to) {
3066 return false;
3067 }
3068 return true;
3069 }
3070 if (dot_1f >= 0 && dot_2f <= 0 && (dot_v1 || dot_v2)) {
3071 *from = std::max(cut, cross_ratios[cross_v1]);
3072 *to = std::min(1.0, cross_ratios[cross_v2]);
3073 if (*from >= *to) {
3074 return false;
3075 }
3076 return true;
3077 }
3078 if (dot_1f <= 0 && dot_2f >= 0 && (dot_v1 || dot_v2)) {
3079 *from = std::max(0.0, cross_ratios[cross_v1]);
3080 *to = std::min(cut, cross_ratios[cross_v2]);
3081 if (*from >= *to) {
3082 return false;
3083 }
3084 return true;
3085 }
3086
3087 /* Unlikely, but here's the default failed value if anything fall through. */
3088 return false;
3089}
3090
3091#undef INTERSECT_SORT_MIN_TO_MAX_3
3092#undef INTERSECT_JUST_GREATER
3093#undef INTERSECT_JUST_SMALLER
3094#undef LRT_ISEC
3095#undef LRT_PARALLEL
3096
3102{
3103 if (l->v[0]->index == r->v[0]->index) {
3104 if (l->v[1]->index == r->v[1]->index || l->v[1]->index == r->v[2]->index ||
3105 l->v[2]->index == r->v[2]->index || l->v[2]->index == r->v[1]->index)
3106 {
3107 return true;
3108 }
3109 }
3110 if (l->v[0]->index == r->v[1]->index) {
3111 if (l->v[1]->index == r->v[0]->index || l->v[1]->index == r->v[2]->index ||
3112 l->v[2]->index == r->v[2]->index || l->v[2]->index == r->v[0]->index)
3113 {
3114 return true;
3115 }
3116 }
3117 if (l->v[0]->index == r->v[2]->index) {
3118 if (l->v[1]->index == r->v[1]->index || l->v[1]->index == r->v[0]->index ||
3119 l->v[2]->index == r->v[0]->index || l->v[2]->index == r->v[1]->index)
3120 {
3121 return true;
3122 }
3123 }
3124 if (l->v[1]->index == r->v[0]->index) {
3125 if (l->v[2]->index == r->v[1]->index || l->v[2]->index == r->v[2]->index ||
3126 l->v[0]->index == r->v[2]->index || l->v[0]->index == r->v[1]->index)
3127 {
3128 return true;
3129 }
3130 }
3131 if (l->v[1]->index == r->v[1]->index) {
3132 if (l->v[2]->index == r->v[0]->index || l->v[2]->index == r->v[2]->index ||
3133 l->v[0]->index == r->v[2]->index || l->v[0]->index == r->v[0]->index)
3134 {
3135 return true;
3136 }
3137 }
3138 if (l->v[1]->index == r->v[2]->index) {
3139 if (l->v[2]->index == r->v[1]->index || l->v[2]->index == r->v[0]->index ||
3140 l->v[0]->index == r->v[0]->index || l->v[0]->index == r->v[1]->index)
3141 {
3142 return true;
3143 }
3144 }
3145
3146 /* Otherwise not possible. */
3147 return false;
3148}
3149
3151 const LineartTriangle *r)
3152{
3153 if (l->v[0] == r->v[0]) {
3154 return r->v[0];
3155 }
3156 if (l->v[0] == r->v[1]) {
3157 return r->v[1];
3158 }
3159 if (l->v[0] == r->v[2]) {
3160 return r->v[2];
3161 }
3162 if (l->v[1] == r->v[0]) {
3163 return r->v[0];
3164 }
3165 if (l->v[1] == r->v[1]) {
3166 return r->v[1];
3167 }
3168 if (l->v[1] == r->v[2]) {
3169 return r->v[2];
3170 }
3171 if (l->v[2] == r->v[0]) {
3172 return r->v[0];
3173 }
3174 if (l->v[2] == r->v[1]) {
3175 return r->v[1];
3176 }
3177 if (l->v[2] == r->v[2]) {
3178 return r->v[2];
3179 }
3180 return nullptr;
3181}
3182
3184 LineartVert *v1, LineartVert *v2, LineartTriangle *tri, const double *last, double *rv)
3185{
3186 /* Direction vectors for the edge verts. We will check if the verts are on the same side of the
3187 * triangle or not. */
3188 double dir_v1[3], dir_v2[3];
3189 double dot_v1, dot_v2;
3190 double gloc[3];
3191
3192 sub_v3_v3v3_db(dir_v1, v1->gloc, tri->v[0]->gloc);
3193 sub_v3_v3v3_db(dir_v2, v2->gloc, tri->v[0]->gloc);
3194
3195 dot_v1 = dot_v3v3_db(dir_v1, tri->gn);
3196 dot_v2 = dot_v3v3_db(dir_v2, tri->gn);
3197
3198 if (dot_v1 * dot_v2 > 0 || (!dot_v1 && !dot_v2)) {
3199 return false;
3200 }
3201
3202 dot_v1 = fabs(dot_v1);
3203 dot_v2 = fabs(dot_v2);
3204
3205 interp_v3_v3v3_db(gloc, v1->gloc, v2->gloc, dot_v1 / (dot_v1 + dot_v2));
3206
3207 /* Due to precision issue, we might end up with the same point as the one we already detected. */
3208 if (last && LRT_DOUBLE_CLOSE_ENOUGH(last[0], gloc[0]) &&
3209 LRT_DOUBLE_CLOSE_ENOUGH(last[1], gloc[1]) && LRT_DOUBLE_CLOSE_ENOUGH(last[2], gloc[2]))
3210 {
3211 return false;
3212 }
3213
3214 if (!lineart_point_inside_triangle3d(gloc, tri->v[0]->gloc, tri->v[1]->gloc, tri->v[2]->gloc)) {
3215 return false;
3216 }
3217
3218 copy_v3_v3_db(rv, gloc);
3219
3220 return true;
3221}
3222
3224 LineartTriangle *t2,
3225 double *v1,
3226 double *v2)
3227{
3228 double *next = v1, *last = nullptr;
3229 LineartVert *sv1, *sv2;
3230
3231 LineartVert *share = lineart_triangle_share_point(t2, tri);
3232
3233 if (share) {
3234 /* If triangles have sharing points like `abc` and `acd`, then we only need to detect `bc`
3235 * against `acd` or `cd` against `abc`. */
3236
3237 lineart_triangle_get_other_verts(tri, share, &sv1, &sv2);
3238
3239 copy_v3_v3_db(v1, share->gloc);
3240
3241 if (!lineart_triangle_2v_intersection_math(sv1, sv2, t2, nullptr, v2)) {
3242 lineart_triangle_get_other_verts(t2, share, &sv1, &sv2);
3243 if (lineart_triangle_2v_intersection_math(sv1, sv2, tri, nullptr, v2)) {
3244 return true;
3245 }
3246 }
3247 }
3248 else {
3249 /* If not sharing any points, then we need to try all the possibilities. */
3250
3251 if (lineart_triangle_2v_intersection_math(tri->v[0], tri->v[1], t2, nullptr, v1)) {
3252 next = v2;
3253 last = v1;
3254 }
3255
3256 if (lineart_triangle_2v_intersection_math(tri->v[1], tri->v[2], t2, last, next)) {
3257 if (last) {
3258 return true;
3259 }
3260 next = v2;
3261 last = v1;
3262 }
3263 if (lineart_triangle_2v_intersection_math(tri->v[2], tri->v[0], t2, last, next)) {
3264 if (last) {
3265 return true;
3266 }
3267 next = v2;
3268 last = v1;
3269 }
3270
3271 if (lineart_triangle_2v_intersection_math(t2->v[0], t2->v[1], tri, last, next)) {
3272 if (last) {
3273 return true;
3274 }
3275 next = v2;
3276 last = v1;
3277 }
3278 if (lineart_triangle_2v_intersection_math(t2->v[1], t2->v[2], tri, last, next)) {
3279 if (last) {
3280 return true;
3281 }
3282 next = v2;
3283 last = v1;
3284 }
3285 if (lineart_triangle_2v_intersection_math(t2->v[2], t2->v[0], tri, last, next)) {
3286 if (last) {
3287 return true;
3288 }
3289 next = v2;
3290 last = v1;
3291 }
3292 }
3293 return false;
3294}
3295
3297 const double *v1,
3298 const double *v2,
3299 LineartTriangle *tri1,
3300 LineartTriangle *tri2)
3301{
3302 if (th->current == th->max) {
3303
3304 LineartIsecSingle *new_array = MEM_malloc_arrayN<LineartIsecSingle>(size_t(th->max) * 2,
3305 "LineartIsecSingle");
3306 memcpy(new_array, th->array, sizeof(LineartIsecSingle) * th->max);
3307 th->max *= 2;
3308 MEM_freeN(th->array);
3309 th->array = new_array;
3310 }
3311 LineartIsecSingle *isec_single = &th->array[th->current];
3312 copy_v3_v3_db(isec_single->v1, v1);
3313 copy_v3_v3_db(isec_single->v2, v2);
3314 isec_single->tri1 = tri1;
3315 isec_single->tri2 = tri2;
3316 if (tri1->target_reference > tri2->target_reference) {
3317 std::swap(isec_single->tri1, isec_single->tri2);
3318 }
3319 th->current++;
3320}
3321
3322#define LRT_ISECT_TRIANGLE_PER_THREAD 4096
3323
3325{
3326 LineartData *ld = th->ld;
3327 int remaining = LRT_ISECT_TRIANGLE_PER_THREAD;
3328
3331
3332 if (!eln) {
3334 return false;
3335 }
3336
3337 th->pending_from = eln;
3339
3340 while (remaining > 0 && eln) {
3341 int remaining_this_eln = eln->element_count - ld->isect_scheduled_up_to_index;
3342 int added_count = std::min(remaining, remaining_this_eln);
3343 remaining -= added_count;
3344 if (remaining || added_count == remaining_this_eln) {
3345 eln = eln->next;
3346 ld->isect_scheduled_up_to = eln;
3348 }
3349 else {
3350 ld->isect_scheduled_up_to_index += added_count;
3351 }
3352 }
3353
3354 th->pending_to = eln ? eln :
3355 static_cast<LineartElementLinkNode *>(
3358
3360
3361 return true;
3362}
3363
3364/* This function initializes two things:
3365 * 1) Triangle array scheduling info, for each worker thread to get its chunk from the scheduler.
3366 * 2) Per-thread intersection result array. Does not store actual #LineartEdge, these results will
3367 * be finalized by #lineart_create_edges_from_isec_data
3368 */
3369static void lineart_init_isec_thread(LineartIsecData *d, LineartData *ld, int thread_count)
3370{
3371 d->threads = MEM_calloc_arrayN<LineartIsecThread>(thread_count, "LineartIsecThread arr");
3372 d->ld = ld;
3373 d->thread_count = thread_count;
3374
3375 ld->isect_scheduled_up_to = static_cast<LineartElementLinkNode *>(
3378
3379 for (int i = 0; i < thread_count; i++) {
3380 LineartIsecThread *it = &d->threads[i];
3381 it->array = MEM_malloc_arrayN<LineartIsecSingle>(100, "LineartIsecSingle arr");
3382 it->max = 100;
3383 it->current = 0;
3384 it->thread_id = i;
3385 it->ld = ld;
3386 }
3387}
3388
3390{
3391 for (int i = 0; i < d->thread_count; i++) {
3392 LineartIsecThread *it = &d->threads[i];
3393 MEM_freeN(it->array);
3394 }
3395 MEM_freeN(d->threads);
3396}
3397
3401 int up_to)
3402{
3403 BLI_assert(th != nullptr);
3404
3405 if (!th) {
3406 return;
3407 }
3408
3409 double *G0 = tri->v[0]->gloc, *G1 = tri->v[1]->gloc, *G2 = tri->v[2]->gloc;
3410
3411 /* If this _is_ the smallest subdivision bounding area, then do the intersections there. */
3412 for (int i = 0; i < up_to; i++) {
3413 /* Testing_triangle->testing[0] is used to store pairing triangle reference.
3414 * See definition of LineartTriangleThread for more info. */
3415 LineartTriangle *testing_triangle = ba->linked_triangles[i];
3416 LineartTriangleThread *tt = (LineartTriangleThread *)testing_triangle;
3417
3418 if (testing_triangle == tri || tt->testing_e[th->thread_id] == (LineartEdge *)tri) {
3419 continue;
3420 }
3421 tt->testing_e[th->thread_id] = (LineartEdge *)tri;
3422
3423 if (!((testing_triangle->flags | tri->flags) & LRT_TRIANGLE_FORCE_INTERSECTION)) {
3424 if (((testing_triangle->flags | tri->flags) & LRT_TRIANGLE_NO_INTERSECTION) ||
3425 (testing_triangle->flags & tri->flags & LRT_TRIANGLE_INTERSECTION_ONLY))
3426 {
3427 continue;
3428 }
3429 }
3430
3431 double *RG0 = testing_triangle->v[0]->gloc, *RG1 = testing_triangle->v[1]->gloc,
3432 *RG2 = testing_triangle->v[2]->gloc;
3433
3434 /* Bounding box not overlapping or triangles share edges, not potential of intersecting. */
3435 if ((std::min({G0[2], G1[2], G2[2]}) > std::max({RG0[2], RG1[2], RG2[2]})) ||
3436 (std::max({G0[2], G1[2], G2[2]}) < std::min({RG0[2], RG1[2], RG2[2]})) ||
3437 (std::min({G0[0], G1[0], G2[0]}) > std::max({RG0[0], RG1[0], RG2[0]})) ||
3438 (std::max({G0[0], G1[0], G2[0]}) < std::min({RG0[0], RG1[0], RG2[0]})) ||
3439 (std::min({G0[1], G1[1], G2[1]}) > std::max({RG0[1], RG1[1], RG2[1]})) ||
3440 (std::max({G0[1], G1[1], G2[1]}) < std::min({RG0[1], RG1[1], RG2[1]})) ||
3441 lineart_triangle_share_edge(tri, testing_triangle))
3442 {
3443 continue;
3444 }
3445
3446 /* If we do need to compute intersection, then finally do it. */
3447
3448 double iv1[3], iv2[3];
3449 if (lineart_triangle_intersect_math(tri, testing_triangle, iv1, iv2)) {
3450 lineart_add_isec_thread(th, iv1, iv2, tri, testing_triangle);
3451 }
3452 }
3453}
3454
3456{
3457 float direction[3] = {0, 0, 1};
3458 float trans[3];
3459 float inv[4][4];
3460 float obmat_no_scale[4][4];
3461
3462 copy_m4_m4(obmat_no_scale, ld->conf.cam_obmat);
3463 normalize_v3(obmat_no_scale[0]);
3464 normalize_v3(obmat_no_scale[1]);
3465 normalize_v3(obmat_no_scale[2]);
3466 invert_m4_m4(inv, obmat_no_scale);
3467 transpose_m4(inv);
3468 mul_v3_mat3_m4v3(trans, inv, direction);
3469 copy_m4_m4(ld->conf.cam_obmat, obmat_no_scale);
3470 copy_v3db_v3fl(ld->conf.view_vector, trans);
3471
3473 copy_m4_m4(obmat_no_scale, ld->conf.cam_obmat_secondary);
3474 normalize_v3(obmat_no_scale[0]);
3475 normalize_v3(obmat_no_scale[1]);
3476 normalize_v3(obmat_no_scale[2]);
3477 invert_m4_m4(inv, obmat_no_scale);
3478 transpose_m4(inv);
3479 mul_v3_mat3_m4v3(trans, inv, direction);
3480 copy_m4_m4(ld->conf.cam_obmat_secondary, obmat_no_scale);
3482 }
3483}
3484
3486{
3487 BLI_spin_end(&ba->lock);
3488 if (ba->child) {
3489 for (int i = 0; i < 4; i++) {
3491 }
3492 }
3493}
3494
3496{
3497 if (ld == nullptr) {
3498 return;
3499 }
3500
3503
3507
3508 if (ld->pending_edges.array) {
3510 }
3511
3512 for (int i = 0; i < ld->qtree.initial_tile_count; i++) {
3514 }
3516
3518}
3519
3521{
3522 if (ld == nullptr) {
3523 return;
3524 }
3525
3526 BLI_spin_end(&ld->lock_task);
3527 BLI_spin_end(&ld->lock_cuts);
3529
3531
3533}
3534
3536{
3537 LineartData *ld = lmd->la_data_ptr;
3538
3540
3541 if (ld) {
3542 MEM_freeN(ld);
3543 lmd->la_data_ptr = nullptr;
3544 }
3545
3546 if (G.debug_value == 4000) {
3547 printf("LRT: Destroyed render data.\n");
3548 }
3549}
3550
3552{
3553 LineartCache *lc = MEM_callocN<LineartCache>("Lineart Cache");
3554 return lc;
3555}
3556
3558{
3559 if (!(*lc)) {
3560 return;
3561 }
3562 lineart_mem_destroy(&((*lc)->chain_data_pool));
3563 MEM_freeN(*lc);
3564 (*lc) = nullptr;
3565}
3566
3569 Object *camera,
3570 Object *active_camera,
3571 LineartCache *lc)
3572{
3573 LineartData *ld = MEM_callocN<LineartData>("Line Art render buffer");
3574 lmd->cache = lc;
3575 lmd->la_data_ptr = ld;
3577
3578 if (!scene || !camera || !lc) {
3579 return nullptr;
3580 }
3581 const Camera *c = static_cast<Camera *>(camera->data);
3582 double clipping_offset = 0;
3583
3585 /* This way the clipped lines are "stably visible" by prevents depth buffer artifacts. */
3586 clipping_offset = 0.0001;
3587 }
3588
3589 copy_v3db_v3fl(ld->conf.camera_pos, camera->object_to_world().location());
3590 if (active_camera) {
3591 copy_v3db_v3fl(ld->conf.active_camera_pos, active_camera->object_to_world().location());
3592 }
3593 copy_m4_m4(ld->conf.cam_obmat, camera->object_to_world().ptr());
3594 /* Make sure none of the scaling factor makes in, line art expects no scaling on cameras and
3595 * lights. */
3596 normalize_v3(ld->conf.cam_obmat[0]);
3597 normalize_v3(ld->conf.cam_obmat[1]);
3598 normalize_v3(ld->conf.cam_obmat[2]);
3599
3600 ld->conf.cam_is_persp = (c->type == CAM_PERSP);
3601 ld->conf.near_clip = c->clip_start + clipping_offset;
3602 ld->conf.far_clip = c->clip_end - clipping_offset;
3603 ld->w = scene->r.xsch;
3604 ld->h = scene->r.ysch;
3605
3606 if (ld->conf.cam_is_persp) {
3608 }
3609 else {
3611 }
3612
3613 double asp = double(ld->w) / double(ld->h);
3614 int fit = BKE_camera_sensor_fit(c->sensor_fit, ld->w, ld->h);
3615 ld->conf.shift_x = fit == CAMERA_SENSOR_FIT_HOR ? c->shiftx : c->shiftx / asp;
3616 ld->conf.shift_y = fit == CAMERA_SENSOR_FIT_VERT ? c->shifty : c->shifty * asp;
3617
3618 ld->conf.overscan = lmd->overscan;
3619
3620 ld->conf.shift_x /= (1 + ld->conf.overscan);
3621 ld->conf.shift_y /= (1 + ld->conf.overscan);
3622
3623 if (lmd->light_contour_object) {
3624 Object *light_obj = lmd->light_contour_object;
3625 copy_v3db_v3fl(ld->conf.camera_pos_secondary, light_obj->object_to_world().location());
3626 copy_m4_m4(ld->conf.cam_obmat_secondary, light_obj->object_to_world().ptr());
3627 /* Make sure none of the scaling factor makes in, line art expects no scaling on cameras and
3628 * lights. */
3633 if (light_obj->type == OB_LAMP) {
3634 ld->conf.cam_is_persp_secondary = ((Light *)light_obj->data)->type != LA_SUN;
3635 }
3636 }
3637
3642
3644 0;
3647 0;
3654
3655 /* See lineart_edge_from_triangle() for how this option may impact performance. */
3658
3661
3663 0;
3665
3668
3669 /* This is used to limit calculation to a certain level to save time, lines who have higher
3670 * occlusion levels will get ignored. */
3672
3673 int16_t edge_types = lmd->edge_types_override;
3674
3675 /* lmd->edge_types_override contains all used flags in the modifier stack. */
3676 ld->conf.use_contour = (edge_types & MOD_LINEART_EDGE_FLAG_CONTOUR) != 0;
3677 ld->conf.use_crease = (edge_types & MOD_LINEART_EDGE_FLAG_CREASE) != 0;
3678 ld->conf.use_material = (edge_types & MOD_LINEART_EDGE_FLAG_MATERIAL) != 0;
3679 ld->conf.use_edge_marks = (edge_types & MOD_LINEART_EDGE_FLAG_EDGE_MARK) != 0;
3681 ld->conf.use_loose = (edge_types & MOD_LINEART_EDGE_FLAG_LOOSE) != 0;
3682 ld->conf.use_light_contour = ((edge_types & MOD_LINEART_EDGE_FLAG_LIGHT_CONTOUR) != 0 &&
3683 (lmd->light_contour_object != nullptr));
3684 ld->conf.use_shadow = ((edge_types & MOD_LINEART_EDGE_FLAG_PROJECTED_SHADOW) != 0 &&
3685 (lmd->light_contour_object != nullptr));
3686
3691
3693 0;
3694
3702
3704
3705 /* See #LineartData::edge_data_pool for explanation. */
3707
3711
3712 ld->thread_count = BKE_render_num_threads(&scene->r);
3713
3714 return ld;
3715}
3716
3718{
3719 return sizeof(LineartTriangle) + (sizeof(LineartEdge *) * (ld->thread_count));
3720}
3721
3723{
3724 /* Initial tile split is defined as 4 (subdivided as 4*4), increasing the value allows the
3725 * algorithm to build the acceleration structure for bigger scenes a little faster but not as
3726 * efficient at handling medium to small scenes. */
3727 int sp_w = LRT_BA_ROWS;
3728 int sp_h = LRT_BA_ROWS;
3729 int row, col;
3731
3732 /* Always make sure the shortest side has at least LRT_BA_ROWS tiles. */
3733 if (ld->w > ld->h) {
3734 sp_w = sp_h * ld->w / ld->h;
3735 }
3736 else {
3737 sp_h = sp_w * ld->h / ld->w;
3738 }
3739
3740 /* Because NDC (Normalized Device Coordinates) range is (-1,1),
3741 * so the span for each initial tile is double of that in the (0,1) range. */
3742 double span_w = 1.0 / sp_w * 2.0;
3743 double span_h = 1.0 / sp_h * 2.0;
3744
3745 ld->qtree.count_x = sp_w;
3746 ld->qtree.count_y = sp_h;
3747 ld->qtree.tile_width = span_w;
3748 ld->qtree.tile_height = span_h;
3749
3750 ld->qtree.initial_tile_count = sp_w * sp_h;
3753 for (int i = 0; i < ld->qtree.initial_tile_count; i++) {
3755 }
3756
3757 /* Initialize tiles. */
3758 for (row = 0; row < sp_h; row++) {
3759 for (col = 0; col < sp_w; col++) {
3760 ba = &ld->qtree.initials[row * ld->qtree.count_x + col];
3761
3762 /* Set the four direction limits. */
3763 ba->l = span_w * col - 1.0;
3764 ba->r = (col == sp_w - 1) ? 1.0 : (span_w * (col + 1) - 1.0);
3765 ba->u = 1.0 - span_h * row;
3766 ba->b = (row == sp_h - 1) ? -1.0 : (1.0 - span_h * (row + 1));
3767
3768 ba->cx = (ba->l + ba->r) / 2;
3769 ba->cy = (ba->u + ba->b) / 2;
3770
3771 /* Init linked_triangles array. */
3775 "ba_linked_triangles");
3776 ba->linked_lines = MEM_calloc_arrayN<LineartEdge *>(ba->max_line_count, "ba_linked_lines");
3777
3778 BLI_spin_init(&ba->lock);
3779 }
3780 }
3781}
3782
3787{
3788 LineartBoundingArea *ba = root->child, *tba;
3789 LinkData *lip2, *next_lip;
3791
3792 /* Inter-connection with newly created 4 child bounding areas. */
3793 lineart_list_append_pointer_pool(&ba[1].rp, mph, &ba[0]);
3794 lineart_list_append_pointer_pool(&ba[0].lp, mph, &ba[1]);
3795 lineart_list_append_pointer_pool(&ba[1].bp, mph, &ba[2]);
3796 lineart_list_append_pointer_pool(&ba[2].up, mph, &ba[1]);
3797 lineart_list_append_pointer_pool(&ba[2].rp, mph, &ba[3]);
3798 lineart_list_append_pointer_pool(&ba[3].lp, mph, &ba[2]);
3799 lineart_list_append_pointer_pool(&ba[3].up, mph, &ba[0]);
3800 lineart_list_append_pointer_pool(&ba[0].bp, mph, &ba[3]);
3801
3802 /* Connect 4 child bounding areas to other areas that are
3803 * adjacent to their original parents. */
3804 LISTBASE_FOREACH (LinkData *, lip, &root->lp) {
3805
3806 /* For example, we are dealing with parent's left side
3807 * "tba" represents each adjacent neighbor of the parent. */
3808 tba = static_cast<LineartBoundingArea *>(lip->data);
3809
3810 /* if this neighbor is adjacent to
3811 * the two new areas on the left side of the parent,
3812 * then add them to the adjacent list as well. */
3813 if (ba[1].u > tba->b && ba[1].b < tba->u) {
3814 lineart_list_append_pointer_pool(&ba[1].lp, mph, tba);
3815 lineart_list_append_pointer_pool(&tba->rp, mph, &ba[1]);
3816 }
3817 if (ba[2].u > tba->b && ba[2].b < tba->u) {
3818 lineart_list_append_pointer_pool(&ba[2].lp, mph, tba);
3819 lineart_list_append_pointer_pool(&tba->rp, mph, &ba[2]);
3820 }
3821 }
3822 LISTBASE_FOREACH (LinkData *, lip, &root->rp) {
3823 tba = static_cast<LineartBoundingArea *>(lip->data);
3824 if (ba[0].u > tba->b && ba[0].b < tba->u) {
3825 lineart_list_append_pointer_pool(&ba[0].rp, mph, tba);
3826 lineart_list_append_pointer_pool(&tba->lp, mph, &ba[0]);
3827 }
3828 if (ba[3].u > tba->b && ba[3].b < tba->u) {
3829 lineart_list_append_pointer_pool(&ba[3].rp, mph, tba);
3830 lineart_list_append_pointer_pool(&tba->lp, mph, &ba[3]);
3831 }
3832 }
3833 LISTBASE_FOREACH (LinkData *, lip, &root->up) {
3834 tba = static_cast<LineartBoundingArea *>(lip->data);
3835 if (ba[0].r > tba->l && ba[0].l < tba->r) {
3836 lineart_list_append_pointer_pool(&ba[0].up, mph, tba);
3837 lineart_list_append_pointer_pool(&tba->bp, mph, &ba[0]);
3838 }
3839 if (ba[1].r > tba->l && ba[1].l < tba->r) {
3840 lineart_list_append_pointer_pool(&ba[1].up, mph, tba);
3841 lineart_list_append_pointer_pool(&tba->bp, mph, &ba[1]);
3842 }
3843 }
3844 LISTBASE_FOREACH (LinkData *, lip, &root->bp) {
3845 tba = static_cast<LineartBoundingArea *>(lip->data);
3846 if (ba[2].r > tba->l && ba[2].l < tba->r) {
3847 lineart_list_append_pointer_pool(&ba[2].bp, mph, tba);
3848 lineart_list_append_pointer_pool(&tba->up, mph, &ba[2]);
3849 }
3850 if (ba[3].r > tba->l && ba[3].l < tba->r) {
3851 lineart_list_append_pointer_pool(&ba[3].bp, mph, tba);
3852 lineart_list_append_pointer_pool(&tba->up, mph, &ba[3]);
3853 }
3854 }
3855
3856 /* Then remove the parent bounding areas from
3857 * their original adjacent areas. */
3858 LISTBASE_FOREACH (LinkData *, lip, &root->lp) {
3859 for (lip2 = static_cast<LinkData *>(((LineartBoundingArea *)lip->data)->rp.first); lip2;
3860 lip2 = next_lip)
3861 {
3862 next_lip = lip2->next;
3863 tba = static_cast<LineartBoundingArea *>(lip2->data);
3864 if (tba == root) {
3866 if (ba[1].u > tba->b && ba[1].b < tba->u) {
3867 lineart_list_append_pointer_pool(&tba->rp, mph, &ba[1]);
3868 }
3869 if (ba[2].u > tba->b && ba[2].b < tba->u) {
3870 lineart_list_append_pointer_pool(&tba->rp, mph, &ba[2]);
3871 }
3872 }
3873 }
3874 }
3875 LISTBASE_FOREACH (LinkData *, lip, &root->rp) {
3876 for (lip2 = static_cast<LinkData *>(((LineartBoundingArea *)lip->data)->lp.first); lip2;
3877 lip2 = next_lip)
3878 {
3879 next_lip = lip2->next;
3880 tba = static_cast<LineartBoundingArea *>(lip2->data);
3881 if (tba == root) {
3883 if (ba[0].u > tba->b && ba[0].b < tba->u) {
3884 lineart_list_append_pointer_pool(&tba->lp, mph, &ba[0]);
3885 }
3886 if (ba[3].u > tba->b && ba[3].b < tba->u) {
3887 lineart_list_append_pointer_pool(&tba->lp, mph, &ba[3]);
3888 }
3889 }
3890 }
3891 }
3892 LISTBASE_FOREACH (LinkData *, lip, &root->up) {
3893 for (lip2 = static_cast<LinkData *>(((LineartBoundingArea *)lip->data)->bp.first); lip2;
3894 lip2 = next_lip)
3895 {
3896 next_lip = lip2->next;
3897 tba = static_cast<LineartBoundingArea *>(lip2->data);
3898 if (tba == root) {
3900 if (ba[0].r > tba->l && ba[0].l < tba->r) {
3901 lineart_list_append_pointer_pool(&tba->up, mph, &ba[0]);
3902 }
3903 if (ba[1].r > tba->l && ba[1].l < tba->r) {
3904 lineart_list_append_pointer_pool(&tba->up, mph, &ba[1]);
3905 }
3906 }
3907 }
3908 }
3909 LISTBASE_FOREACH (LinkData *, lip, &root->bp) {
3910 for (lip2 = static_cast<LinkData *>(((LineartBoundingArea *)lip->data)->up.first); lip2;
3911 lip2 = next_lip)
3912 {
3913 next_lip = lip2->next;
3914 tba = static_cast<LineartBoundingArea *>(lip2->data);
3915 if (tba == root) {
3917 if (ba[2].r > tba->l && ba[2].l < tba->r) {
3918 lineart_list_append_pointer_pool(&tba->bp, mph, &ba[2]);
3919 }
3920 if (ba[3].r > tba->l && ba[3].l < tba->r) {
3921 lineart_list_append_pointer_pool(&tba->bp, mph, &ba[3]);
3922 }
3923 }
3924 }
3925 }
3926
3927 /* Finally clear parent's adjacent list. */
3928 BLI_listbase_clear(&root->lp);
3929 BLI_listbase_clear(&root->rp);
3930 BLI_listbase_clear(&root->up);
3931 BLI_listbase_clear(&root->bp);
3932}
3933
3935{
3936 if (root->child) {
3938 for (int i = 0; i < 4; i++) {
3940 }
3941 }
3942}
3943
3945{
3946 int total_tile_initial = ld->qtree.count_x * ld->qtree.count_y;
3947 int tiles_per_row = ld->qtree.count_x;
3948
3949 for (int row = 0; row < ld->qtree.count_y; row++) {
3950 for (int col = 0; col < ld->qtree.count_x; col++) {
3951 LineartBoundingArea *ba = &ld->qtree.initials[row * tiles_per_row + col];
3952 /* Link adjacent ones. */
3953 if (row) {
3955 &ba->up, &ld->render_data_pool, &ld->qtree.initials[(row - 1) * tiles_per_row + col]);
3956 }
3957 if (col) {
3959 &ba->lp, &ld->render_data_pool, &ld->qtree.initials[row * tiles_per_row + col - 1]);
3960 }
3961 if (row != ld->qtree.count_y - 1) {
3963 &ba->bp, &ld->render_data_pool, &ld->qtree.initials[(row + 1) * tiles_per_row + col]);
3964 }
3965 if (col != ld->qtree.count_x - 1) {
3967 &ba->rp, &ld->render_data_pool, &ld->qtree.initials[row * tiles_per_row + col + 1]);
3968 }
3969 }
3970 }
3971 for (int i = 0; i < total_tile_initial; i++) {
3973 }
3974}
3975
3981 LineartBoundingArea *root,
3982 int recursive_level)
3983{
3984 LineartBoundingArea *ba = static_cast<LineartBoundingArea *>(
3986 ba[0].l = root->cx;
3987 ba[0].r = root->r;
3988 ba[0].u = root->u;
3989 ba[0].b = root->cy;
3990 ba[0].cx = (ba[0].l + ba[0].r) / 2;
3991 ba[0].cy = (ba[0].u + ba[0].b) / 2;
3992
3993 ba[1].l = root->l;
3994 ba[1].r = root->cx;
3995 ba[1].u = root->u;
3996 ba[1].b = root->cy;
3997 ba[1].cx = (ba[1].l + ba[1].r) / 2;
3998 ba[1].cy = (ba[1].u + ba[1].b) / 2;
3999
4000 ba[2].l = root->l;
4001 ba[2].r = root->cx;
4002 ba[2].u = root->cy;
4003 ba[2].b = root->b;
4004 ba[2].cx = (ba[2].l + ba[2].r) / 2;
4005 ba[2].cy = (ba[2].u + ba[2].b) / 2;
4006
4007 ba[3].l = root->cx;
4008 ba[3].r = root->r;
4009 ba[3].u = root->cy;
4010 ba[3].b = root->b;
4011 ba[3].cx = (ba[3].l + ba[3].r) / 2;
4012 ba[3].cy = (ba[3].u + ba[3].b) / 2;
4013
4014 /* Init linked_triangles array and locks. */
4015 for (int i = 0; i < 4; i++) {
4018 ba[i].linked_triangles = MEM_calloc_arrayN<LineartTriangle *>(ba[i].max_triangle_count,
4019 "ba_linked_triangles");
4020 ba[i].linked_lines = MEM_calloc_arrayN<LineartEdge *>(ba[i].max_line_count, "ba_linked_lines");
4021 BLI_spin_init(&ba[i].lock);
4022 }
4023
4024 for (uint32_t i = 0; i < root->triangle_count; i++) {
4025 LineartTriangle *tri = root->linked_triangles[i];
4026
4027 double b[4];
4028 b[0] = std::min({tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]});
4029 b[1] = std::max({tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]});
4030 b[2] = std::max({tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]});
4031 b[3] = std::min({tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]});
4032
4033 /* Re-link triangles into child tiles, not doing intersection lines during this because this
4034 * batch of triangles are all tested with each other for intersections. */
4035 if (LRT_BOUND_AREA_CROSSES(b, &ba[0].l)) {
4037 ld, &ba[0], tri, b, 0, recursive_level + 1, false, nullptr);
4038 }
4039 if (LRT_BOUND_AREA_CROSSES(b, &ba[1].l)) {
4041 ld, &ba[1], tri, b, 0, recursive_level + 1, false, nullptr);
4042 }
4043 if (LRT_BOUND_AREA_CROSSES(b, &ba[2].l)) {
4045 ld, &ba[2], tri, b, 0, recursive_level + 1, false, nullptr);
4046 }
4047 if (LRT_BOUND_AREA_CROSSES(b, &ba[3].l)) {
4049 ld, &ba[3], tri, b, 0, recursive_level + 1, false, nullptr);
4050 }
4051 }
4052
4053 /* At this point the child tiles are fully initialized and it's safe for new triangles to be
4054 * inserted, so assign root->child for #lineart_bounding_area_link_triangle to use. */
4055 root->child = ba;
4056}
4057
4059 const double l[2],
4060 const double r[2],
4062{
4063 double dx, dy;
4064 double converted[4];
4065 double c1, c;
4066
4067 if (((converted[0] = ba->l) > std::max(l[0], r[0])) ||
4068 ((converted[1] = ba->r) < std::min(l[0], r[0])) ||
4069 ((converted[2] = ba->b) > std::max(l[1], r[1])) ||
4070 ((converted[3] = ba->u) < std::min(l[1], r[1])))
4071 {
4072 return false;
4073 }
4074
4075 dx = l[0] - r[0];
4076 dy = l[1] - r[1];
4077
4078 c1 = dx * (converted[2] - l[1]) - dy * (converted[0] - l[0]);
4079 c = c1;
4080
4081 c1 = dx * (converted[2] - l[1]) - dy * (converted[1] - l[0]);
4082 if (c1 * c <= 0) {
4083 return true;
4084 }
4085 c = c1;
4086
4087 c1 = dx * (converted[3] - l[1]) - dy * (converted[0] - l[0]);
4088 if (c1 * c <= 0) {
4089 return true;
4090 }
4091 c = c1;
4092
4093 c1 = dx * (converted[3] - l[1]) - dy * (converted[1] - l[0]);
4094 if (c1 * c <= 0) {
4095 return true;
4096 }
4097 c = c1;
4098
4099 return false;
4100}
4101
4103 LineartTriangle *tri,
4105 bool *r_triangle_vert_inside)
4106{
4107 double p1[2], p2[2], p3[2], p4[2];
4108 double *FBC1 = tri->v[0]->fbcoord, *FBC2 = tri->v[1]->fbcoord, *FBC3 = tri->v[2]->fbcoord;
4109
4110 p3[0] = p1[0] = ba->l;
4111 p2[1] = p1[1] = ba->b;
4112 p2[0] = p4[0] = ba->r;
4113 p3[1] = p4[1] = ba->u;
4114
4115 if ((FBC1[0] >= p1[0] && FBC1[0] <= p2[0] && FBC1[1] >= p1[1] && FBC1[1] <= p3[1]) ||
4116 (FBC2[0] >= p1[0] && FBC2[0] <= p2[0] && FBC2[1] >= p1[1] && FBC2[1] <= p3[1]) ||
4117 (FBC3[0] >= p1[0] && FBC3[0] <= p2[0] && FBC3[1] >= p1[1] && FBC3[1] <= p3[1]))
4118 {
4119 *r_triangle_vert_inside = true;
4120 return true;
4121 }
4122
4123 *r_triangle_vert_inside = false;
4124
4125 if (lineart_point_inside_triangle(p1, FBC1, FBC2, FBC3) ||
4126 lineart_point_inside_triangle(p2, FBC1, FBC2, FBC3) ||
4127 lineart_point_inside_triangle(p3, FBC1, FBC2, FBC3) ||
4128 lineart_point_inside_triangle(p4, FBC1, FBC2, FBC3))
4129 {
4130 return true;
4131 }
4132
4133 if (lineart_bounding_area_edge_intersect(fb, FBC1, FBC2, ba) ||
4134 lineart_bounding_area_edge_intersect(fb, FBC2, FBC3, ba) ||
4136 {
4137 return true;
4138 }
4139
4140 return false;
4141}
4142
4156 LineartBoundingArea *root_ba,
4157 LineartTriangle *tri,
4158 double l_r_u_b[4],
4159 int recursive,
4160 int recursive_level,
4161 bool do_intersection,
4163{
4164 bool triangle_vert_inside;
4165 if (!lineart_bounding_area_triangle_intersect(ld, tri, root_ba, &triangle_vert_inside)) {
4166 return;
4167 }
4168
4169 LineartBoundingArea *old_ba = root_ba;
4170
4171 if (old_ba->child) {
4172 /* If old_ba->child is not nullptr, then tile splitting is fully finished, safe to directly
4173 * insert into child tiles. */
4174 double *B1 = l_r_u_b;
4175 double b[4];
4176 if (!l_r_u_b) {
4177 b[0] = std::min({tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]});
4178 b[1] = std::max({tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]});
4179 b[2] = std::max({tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]});
4180 b[3] = std::min({tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]});
4181 B1 = b;
4182 }
4183 for (int iba = 0; iba < 4; iba++) {
4184 if (LRT_BOUND_AREA_CROSSES(B1, &old_ba->child[iba].l)) {
4186 ld, &old_ba->child[iba], tri, B1, recursive, recursive_level + 1, do_intersection, th);
4187 }
4188 }
4189 return;
4190 }
4191
4192 /* When splitting tiles, triangles are relinked into new tiles by a single thread, #th is nullptr
4193 * in that situation. */
4194 if (th) {
4195 BLI_spin_lock(&old_ba->lock);
4196 }
4197
4198 /* If there are still space left in this tile for insertion. */
4199 if (old_ba->triangle_count < old_ba->max_triangle_count) {
4200 const uint32_t old_tri_count = old_ba->triangle_count;
4201
4202 old_ba->linked_triangles[old_tri_count] = tri;
4203
4204 if (triangle_vert_inside) {
4205 old_ba->insider_triangle_count++;
4206 }
4207 old_ba->triangle_count++;
4208
4209 /* Do intersections in place. */
4210 if (do_intersection && ld->conf.use_intersections) {
4211 lineart_triangle_intersect_in_bounding_area(tri, old_ba, th, old_tri_count);
4212 }
4213
4214 if (th) {
4215 BLI_spin_unlock(&old_ba->lock);
4216 }
4217 }
4218 else { /* We need to wait for either splitting or array extension to be done. */
4219
4220 if (recursive_level < ld->qtree.recursive_level &&
4222 {
4223 if (!old_ba->child) {
4224 /* old_ba->child==nullptr, means we are the thread that's doing the splitting. */
4225 lineart_bounding_area_split(ld, old_ba, recursive_level);
4226 } /* Otherwise other thread has completed the splitting process. */
4227 }
4228 else {
4229 if (old_ba->triangle_count == old_ba->max_triangle_count) {
4230 /* Means we are the thread that's doing the extension. */
4232 } /* Otherwise other thread has completed the extending the array. */
4233 }
4234
4235 /* Unlock before going into recursive call. */
4236 if (th) {
4237 BLI_spin_unlock(&old_ba->lock);
4238 }
4239
4240 /* Of course we still have our own triangle needs to be added. */
4242 ld, root_ba, tri, l_r_u_b, recursive, recursive_level, do_intersection, th);
4243 }
4244}
4245
4247{
4248 BLI_spin_end(&ba->lock);
4249 if (ba->linked_lines) {
4251 }
4252 if (ba->linked_triangles) {
4254 }
4255 if (recursive && ba->child) {
4256 for (int i = 0; i < 4; i++) {
4257 lineart_free_bounding_area_memory(&ba->child[i], recursive);
4258 }
4259 }
4260}
4262{
4263 for (int i = 0; i < ld->qtree.count_y; i++) {
4264 for (int j = 0; j < ld->qtree.count_x; j++) {
4266 }
4267 }
4268}
4269
4271 LineartBoundingArea *root_ba,
4272 LineartEdge *e)
4273{
4274 if (root_ba->child == nullptr) {
4276 }
4277 else {
4279 ld, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[0]))
4280 {
4281 lineart_bounding_area_link_edge(ld, &root_ba->child[0], e);
4282 }
4284 ld, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[1]))
4285 {
4286 lineart_bounding_area_link_edge(ld, &root_ba->child[1], e);
4287 }
4289 ld, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[2]))
4290 {
4291 lineart_bounding_area_link_edge(ld, &root_ba->child[2], e);
4292 }
4294 ld, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[3]))
4295 {
4296 lineart_bounding_area_link_edge(ld, &root_ba->child[3], e);
4297 }
4298 }
4299}
4300
4302{
4303 if (root_ba->child) {
4304 for (int i = 0; i < 4; i++) {
4306 }
4307 }
4308 if (root_ba->linked_lines) {
4309 MEM_freeN(root_ba->linked_lines);
4310 }
4311 root_ba->line_count = 0;
4312 root_ba->max_line_count = 128;
4314 "cleared lineart edges");
4315}
4317{
4319 for (int i = 0; i < ld->qtree.count_y; i++) {
4320 for (int j = 0; j < ld->qtree.count_x; j++) {
4322 }
4323 }
4324}
4325
4327{
4329 {
4330 int r1, r2, c1, c2, row, col;
4331 if (lineart_get_edge_bounding_areas(ld, e, &r1, &r2, &c1, &c2)) {
4332 for (row = r1; row != r2 + 1; row++) {
4333 for (col = c1; col != c2 + 1; col++) {
4335 ld, &ld->qtree.initials[row * ld->qtree.count_x + col], e);
4336 }
4337 }
4338 }
4339 }
4341}
4342
4344 uint8_t max_occlusion)
4345{
4346 if (ba->child) {
4347 for (int i = 0; i < 4; i++) {
4349 }
4350 return;
4351 }
4352
4353 if (!ba->line_count) {
4354 return;
4355 }
4356
4357 int usable_count = 0;
4358 for (int i = 0; i < ba->line_count; i++) {
4359 LineartEdge *e = ba->linked_lines[i];
4360 if (e->min_occ > max_occlusion) {
4361 continue;
4362 }
4363 usable_count++;
4364 }
4365
4366 if (!usable_count) {
4367 ba->line_count = 0;
4368 return;
4369 }
4370
4371 LineartEdge **new_array = MEM_calloc_arrayN<LineartEdge *>(usable_count,
4372 "cleaned lineart edge array");
4373
4374 int new_i = 0;
4375 for (int i = 0; i < ba->line_count; i++) {
4376 LineartEdge *e = ba->linked_lines[i];
4377 if (e->min_occ > max_occlusion) {
4378 continue;
4379 }
4380 new_array[new_i] = e;
4381 new_i++;
4382 }
4383
4385 ba->linked_lines = new_array;
4386 ba->max_line_count = ba->line_count = usable_count;
4387}
4388
4390{
4391 for (int row = 0; row < ld->qtree.count_y; row++) {
4392 for (int col = 0; col < ld->qtree.count_x; col++) {
4394 &ld->qtree.initials[row * ld->qtree.count_x + col], ld->conf.max_occlusion_level);
4395 }
4396 }
4397}
4398
4400 LineartData *ld, LineartTriangle *tri, int *rowbegin, int *rowend, int *colbegin, int *colend)
4401{
4402 double sp_w = ld->qtree.tile_width, sp_h = ld->qtree.tile_height;
4403 double b[4];
4404
4405 if (!tri->v[0] || !tri->v[1] || !tri->v[2]) {
4406 return false;
4407 }
4408
4409 b[0] = std::min({tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]});
4410 b[1] = std::max({tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]});
4411 b[2] = std::min({tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]});
4412 b[3] = std::max({tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]});
4413
4414 if (b[0] > 1 || b[1] < -1 || b[2] > 1 || b[3] < -1) {
4415 return false;
4416 }
4417
4418 (*colbegin) = int((b[0] + 1.0) / sp_w);
4419 (*colend) = int((b[1] + 1.0) / sp_w);
4420 (*rowend) = ld->qtree.count_y - int((b[2] + 1.0) / sp_h) - 1;
4421 (*rowbegin) = ld->qtree.count_y - int((b[3] + 1.0) / sp_h) - 1;
4422
4423 if ((*colend) >= ld->qtree.count_x) {
4424 (*colend) = ld->qtree.count_x - 1;
4425 }
4426 if ((*rowend) >= ld->qtree.count_y) {
4427 (*rowend) = ld->qtree.count_y - 1;
4428 }
4429 *colbegin = std::max(*colbegin, 0);
4430 *rowbegin = std::max(*rowbegin, 0);
4431
4432 return true;
4433}
4434
4436 LineartData *ld, LineartEdge *e, int *rowbegin, int *rowend, int *colbegin, int *colend)
4437{
4438 double sp_w = ld->qtree.tile_width, sp_h = ld->qtree.tile_height;
4439 double b[4];
4440
4441 if (!e->v1 || !e->v2) {
4442 return false;
4443 }
4444
4445 if (e->v1->fbcoord[0] != e->v1->fbcoord[0] || e->v2->fbcoord[0] != e->v2->fbcoord[0]) {
4446 return false;
4447 }
4448
4449 b[0] = std::min(e->v1->fbcoord[0], e->v2->fbcoord[0]);
4450 b[1] = std::max(e->v1->fbcoord[0], e->v2->fbcoord[0]);
4451 b[2] = std::min(e->v1->fbcoord[1], e->v2->fbcoord[1]);
4452 b[3] = std::max(e->v1->fbcoord[1], e->v2->fbcoord[1]);
4453
4454 if (b[0] > 1 || b[1] < -1 || b[2] > 1 || b[3] < -1) {
4455 return false;
4456 }
4457
4458 (*colbegin) = int((b[0] + 1.0) / sp_w);
4459 (*colend) = int((b[1] + 1.0) / sp_w);
4460 (*rowend) = ld->qtree.count_y - int((b[2] + 1.0) / sp_h) - 1;
4461 (*rowbegin) = ld->qtree.count_y - int((b[3] + 1.0) / sp_h) - 1;
4462
4463 /* It's possible that the line stretches too much out to the side, resulting negative value. */
4464 if ((*rowend) < (*rowbegin)) {
4465 (*rowend) = ld->qtree.count_y - 1;
4466 }
4467
4468 if ((*colend) < (*colbegin)) {
4469 (*colend) = ld->qtree.count_x - 1;
4470 }
4471
4472 CLAMP((*colbegin), 0, ld->qtree.count_x - 1);
4473 CLAMP((*rowbegin), 0, ld->qtree.count_y - 1);
4474 CLAMP((*colend), 0, ld->qtree.count_x - 1);
4475 CLAMP((*rowend), 0, ld->qtree.count_y - 1);
4476
4477 return true;
4478}
4479
4481{
4482 double sp_w = ld->qtree.tile_width, sp_h = ld->qtree.tile_height;
4483 int col, row;
4484
4485 if (x > 1 || x < -1 || y > 1 || y < -1) {
4486 return nullptr;
4487 }
4488
4489 col = int((x + 1.0) / sp_w);
4490 row = ld->qtree.count_y - int((y + 1.0) / sp_h) - 1;
4491
4492 if (col >= ld->qtree.count_x) {
4493 col = ld->qtree.count_x - 1;
4494 }
4495 if (row >= ld->qtree.count_y) {
4496 row = ld->qtree.count_y - 1;
4497 }
4498 col = std::max(col, 0);
4499 row = std::max(row, 0);
4500
4501 return &ld->qtree.initials[row * ld->qtree.count_x + col];
4502}
4503
4505{
4507 double sp_w = ld->qtree.tile_width, sp_h = ld->qtree.tile_height;
4508 int c = int((x + 1.0) / sp_w);
4509 int r = ld->qtree.count_y - int((y + 1.0) / sp_h) - 1;
4510 r = std::max(r, 0);
4511 c = std::max(c, 0);
4512 if (r >= ld->qtree.count_y) {
4513 r = ld->qtree.count_y - 1;
4514 }
4515 if (c >= ld->qtree.count_x) {
4516 c = ld->qtree.count_x - 1;
4517 }
4518
4519 iba = &ld->qtree.initials[r * ld->qtree.count_x + c];
4520 while (iba->child) {
4521 if (x > iba->cx) {
4522 if (y > iba->cy) {
4523 iba = &iba->child[0];
4524 }
4525 else {
4526 iba = &iba->child[3];
4527 }
4528 }
4529 else {
4530 if (y > iba->cy) {
4531 iba = &iba->child[1];
4532 }
4533 else {
4534 iba = &iba->child[2];
4535 }
4536 }
4537 }
4538 return iba;
4539}
4540
4542{
4544 if ((ba = MOD_lineart_get_parent_bounding_area(ld, x, y)) != nullptr) {
4545 return lineart_get_bounding_area(ld, x, y);
4546 }
4547 return nullptr;
4548}
4549
4550static void lineart_add_triangles_worker(TaskPool *__restrict /*pool*/, LineartIsecThread *th)
4551{
4552 LineartData *ld = th->ld;
4553 // int _dir_control = 0; /* UNUSED */
4555 for (LineartElementLinkNode *eln = th->pending_from; eln != th->pending_to->next;
4556 eln = eln->next)
4557 {
4558 int index_start = eln == th->pending_from ? th->index_from : 0;
4559 int index_end = eln == th->pending_to ? th->index_to : eln->element_count;
4560 LineartTriangle *tri = static_cast<LineartTriangle *>(
4561 (void *)(((uchar *)eln->pointer) + ld->sizeof_triangle * index_start));
4562 for (int ei = index_start; ei < index_end; ei++) {
4563 int x1, x2, y1, y2;
4564 int r, co;
4565 if ((tri->flags & LRT_CULL_USED) || (tri->flags & LRT_CULL_DISCARD)) {
4566 tri = static_cast<LineartTriangle *>((void *)(((uchar *)tri) + ld->sizeof_triangle));
4567 continue;
4568 }
4569 if (lineart_get_triangle_bounding_areas(ld, tri, &y1, &y2, &x1, &x2)) {
4570 // _dir_control++;
4571 for (co = x1; co <= x2; co++) {
4572 for (r = y1; r <= y2; r++) {
4574 &ld->qtree.initials[r * ld->qtree.count_x + co],
4575 tri,
4576 nullptr,
4577 1,
4578 0,
4579 true,
4580 th);
4581 }
4582 }
4583 } /* Else throw away. */
4584 tri = static_cast<LineartTriangle *>((void *)(((uchar *)tri) + ld->sizeof_triangle));
4585 }
4586 }
4587 }
4588}
4589
4591{
4592 LineartData *ld = d->ld;
4593 double ZMax = ld->conf.far_clip;
4594 double ZMin = ld->conf.near_clip;
4595 int total_lines = 0;
4596
4597 for (int i = 0; i < d->thread_count; i++) {
4598 LineartIsecThread *th = &d->threads[i];
4599 if (G.debug_value == 4000) {
4600 printf("Thread %d isec generated %d lines.\n", i, th->current);
4601 }
4602 if (!th->current) {
4603 continue;
4604 }
4605 total_lines += th->current;
4606 }
4607
4608 if (!total_lines) {
4609 return;
4610 }
4611
4612 /* We don't care about removing duplicated vert in this method, chaining can handle that,
4613 * and it saves us from using locks and look up tables. */
4614 LineartVert *v = static_cast<LineartVert *>(
4615 lineart_mem_acquire(ld->edge_data_pool, sizeof(LineartVert) * total_lines * 2));
4616 LineartEdge *e = static_cast<LineartEdge *>(
4617 lineart_mem_acquire(ld->edge_data_pool, sizeof(LineartEdge) * total_lines));
4618 LineartEdgeSegment *es = static_cast<LineartEdgeSegment *>(
4619 lineart_mem_acquire(ld->edge_data_pool, sizeof(LineartEdgeSegment) * total_lines));
4620
4621 LineartElementLinkNode *eln = static_cast<LineartElementLinkNode *>(
4623 eln->element_count = total_lines;
4624 eln->pointer = e;
4627
4628 for (int i = 0; i < d->thread_count; i++) {
4629 LineartIsecThread *th = &d->threads[i];
4630 if (!th->current) {
4631 continue;
4632 }
4633
4634 for (int j = 0; j < th->current; j++) {
4635 LineartIsecSingle *is = &th->array[j];
4636 LineartVert *v1 = v;
4637 LineartVert *v2 = v + 1;
4638 copy_v3_v3_db(v1->gloc, is->v1);
4639 copy_v3_v3_db(v2->gloc, is->v2);
4640 /* The intersection line has been generated only in geometry space, so we need to transform
4641 * them as well. */
4643 mul_v4_m4v3_db(v2->fbcoord, ld->conf.view_projection, v2->gloc);
4644 mul_v3db_db(v1->fbcoord, (1 / v1->fbcoord[3]));
4645 mul_v3db_db(v2->fbcoord, (1 / v2->fbcoord[3]));
4646
4647 v1->fbcoord[0] -= ld->conf.shift_x * 2;
4648 v1->fbcoord[1] -= ld->conf.shift_y * 2;
4649 v2->fbcoord[0] -= ld->conf.shift_x * 2;
4650 v2->fbcoord[1] -= ld->conf.shift_y * 2;
4651
4652 /* This z transformation is not the same as the rest of the part, because the data don't go
4653 * through normal perspective division calls in the pipeline, but this way the 3D result and
4654 * occlusion on the generated line is correct, and we don't really use 2D for viewport stroke
4655 * generation anyway. */
4656 v1->fbcoord[2] = ZMin * ZMax / (ZMax - fabs(v1->fbcoord[2]) * (ZMax - ZMin));
4657 v2->fbcoord[2] = ZMin * ZMax / (ZMax - fabs(v2->fbcoord[2]) * (ZMax - ZMin));
4658 e->v1 = v1;
4659 e->v2 = v2;
4660 e->t1 = is->tri1;
4661 e->t2 = is->tri2;
4662 /* This is so we can also match intersection edges from shadow to later viewing stage. */
4663 e->edge_identifier = (uint64_t(e->t1->target_reference) << 32) | e->t2->target_reference;
4665 e->intersection_mask = (is->tri1->intersection_mask | is->tri2->intersection_mask);
4666 BLI_addtail(&e->segments, es);
4667
4668 int obi1 = (e->t1->target_reference & LRT_OBINDEX_HIGHER);
4669 int obi2 = (e->t2->target_reference & LRT_OBINDEX_HIGHER);
4671 obi1);
4672 LineartElementLinkNode *eln2 = obi1 == obi2 ? eln1 :
4674 &ld->geom.line_buffer_pointers, obi2);
4675 Object *ob1 = eln1 ? static_cast<Object *>(eln1->object_ref) : nullptr;
4676 Object *ob2 = eln2 ? static_cast<Object *>(eln2->object_ref) : nullptr;
4677 if (e->t1->intersection_priority > e->t2->intersection_priority) {
4678 e->object_ref = ob1;
4679 }
4680 else if (e->t1->intersection_priority < e->t2->intersection_priority) {
4681 e->object_ref = ob2;
4682 }
4683 else { /* equal priority */
4684 if (ob1 == ob2) {
4685 /* object_ref should be ambiguous if intersection lines comes from different objects. */
4686 e->object_ref = ob1;
4687 }
4688 }
4689
4691
4692 v += 2;
4693 e++;
4694 es++;
4695 }
4696 }
4697}
4698
4700{
4701 double t_start;
4702 if (G.debug_value == 4000) {
4703 t_start = BLI_time_now_seconds();
4704 }
4705
4706 /* Initialize per-thread data for thread task scheduling information and storing intersection
4707 * results. */
4708 LineartIsecData d = {nullptr};
4710
4712 for (int i = 0; i < ld->thread_count; i++) {
4714 tp, (TaskRunFunction)lineart_add_triangles_worker, &d.threads[i], false, nullptr);
4715 }
4718
4719 if (ld->conf.use_intersections) {
4721 }
4722
4724
4725 if (G.debug_value == 4000) {
4726 double t_elapsed = BLI_time_now_seconds() - t_start;
4727 printf("Line art intersection time: %f\n", t_elapsed);
4728 }
4729}
4730
4732 double *fbcoord1,
4733 double *fbcoord2)
4734{
4735 double data[2] = {fbcoord1[0], fbcoord1[1]};
4736 double LU[2] = {-1, 1}, RU[2] = {1, 1}, LB[2] = {-1, -1}, RB[2] = {1, -1};
4737 double r = 1, sr = 1;
4738 bool p_unused;
4739
4740 if (data[0] > -1 && data[0] < 1 && data[1] > -1 && data[1] < 1) {
4741 return lineart_get_bounding_area(ld, data[0], data[1]);
4742 }
4743
4744 if (lineart_intersect_seg_seg(fbcoord1, fbcoord2, LU, RU, &sr, &p_unused) && sr < r && sr > 0) {
4745 r = sr;
4746 }
4747 if (lineart_intersect_seg_seg(fbcoord1, fbcoord2, LB, RB, &sr, &p_unused) && sr < r && sr > 0) {
4748 r = sr;
4749 }
4750 if (lineart_intersect_seg_seg(fbcoord1, fbcoord2, LB, LU, &sr, &p_unused) && sr < r && sr > 0) {
4751 r = sr;
4752 }
4753 if (lineart_intersect_seg_seg(fbcoord1, fbcoord2, RB, RU, &sr, &p_unused) && sr < r && sr > 0) {
4754 r = sr;
4755 }
4756 interp_v2_v2v2_db(data, fbcoord1, fbcoord2, r);
4757
4758 return lineart_get_bounding_area(ld, data[0], data[1]);
4759}
4760
4762 double *fbcoord1,
4763 double *fbcoord2,
4764 double x,
4765 double y,
4766 double k,
4767 int positive_x,
4768 int positive_y,
4769 double *next_x,
4770 double *next_y)
4771{
4772 double rx, ry, ux, uy, lx, ly, bx, by;
4773 double r1, r2;
4775
4776 /* If we are marching towards the right. */
4777 if (positive_x > 0) {
4778 rx = self->r;
4779 ry = y + k * (rx - x);
4780
4781 /* If we are marching towards the top. */
4782 if (positive_y > 0) {
4783 uy = self->u;
4784 ux = x + (uy - y) / k;
4785 r1 = ratiod(fbcoord1[0], fbcoord2[0], rx);
4786 r2 = ratiod(fbcoord1[0], fbcoord2[0], ux);
4787 if (std::min(r1, r2) > 1) {
4788 return nullptr;
4789 }
4790
4791 /* We reached the right side before the top side. */
4792 if (r1 <= r2) {
4793 LISTBASE_FOREACH (LinkData *, lip, &self->rp) {
4794 ba = static_cast<LineartBoundingArea *>(lip->data);
4795 if (ba->u >= ry && ba->b < ry) {
4796 *next_x = rx;
4797 *next_y = ry;
4798 return ba;
4799 }
4800 }
4801 }
4802 /* We reached the top side before the right side. */
4803 else {
4804 LISTBASE_FOREACH (LinkData *, lip, &self->up) {
4805 ba = static_cast<LineartBoundingArea *>(lip->data);
4806 if (ba->r >= ux && ba->l < ux) {
4807 *next_x = ux;
4808 *next_y = uy;
4809 return ba;
4810 }
4811 }
4812 }
4813 }
4814 /* If we are marching towards the bottom. */
4815 else if (positive_y < 0) {
4816 by = self->b;
4817 bx = x + (by - y) / k;
4818 r1 = ratiod(fbcoord1[0], fbcoord2[0], rx);
4819 r2 = ratiod(fbcoord1[0], fbcoord2[0], bx);
4820 if (std::min(r1, r2) > 1) {
4821 return nullptr;
4822 }
4823 if (r1 <= r2) {
4824 LISTBASE_FOREACH (LinkData *, lip, &self->rp) {
4825 ba = static_cast<LineartBoundingArea *>(lip->data);
4826 if (ba->u >= ry && ba->b < ry) {
4827 *next_x = rx;
4828 *next_y = ry;
4829 return ba;
4830 }
4831 }
4832 }
4833 else {
4834 LISTBASE_FOREACH (LinkData *, lip, &self->bp) {
4835 ba = static_cast<LineartBoundingArea *>(lip->data);
4836 if (ba->r >= bx && ba->l < bx) {
4837 *next_x = bx;
4838 *next_y = by;
4839 return ba;
4840 }
4841 }
4842 }
4843 }
4844 /* If the line is completely horizontal, in which Y difference == 0. */
4845 else {
4846 r1 = ratiod(fbcoord1[0], fbcoord2[0], self->r);
4847 if (r1 > 1) {
4848 return nullptr;
4849 }
4850 LISTBASE_FOREACH (LinkData *, lip, &self->rp) {
4851 ba = static_cast<LineartBoundingArea *>(lip->data);
4852 if (ba->u >= y && ba->b < y) {
4853 *next_x = self->r;
4854 *next_y = y;
4855 return ba;
4856 }
4857 }
4858 }
4859 }
4860
4861 /* If we are marching towards the left. */
4862 else if (positive_x < 0) {
4863 lx = self->l;
4864 ly = y + k * (lx - x);
4865
4866 /* If we are marching towards the top. */
4867 if (positive_y > 0) {
4868 uy = self->u;
4869 ux = x + (uy - y) / k;
4870 r1 = ratiod(fbcoord1[0], fbcoord2[0], lx);
4871 r2 = ratiod(fbcoord1[0], fbcoord2[0], ux);
4872 if (std::min(r1, r2) > 1) {
4873 return nullptr;
4874 }
4875 if (r1 <= r2) {
4876 LISTBASE_FOREACH (LinkData *, lip, &self->lp) {
4877 ba = static_cast<LineartBoundingArea *>(lip->data);
4878 if (ba->u >= ly && ba->b < ly) {
4879 *next_x = lx;
4880 *next_y = ly;
4881 return ba;
4882 }
4883 }
4884 }
4885 else {
4886 LISTBASE_FOREACH (LinkData *, lip, &self->up) {
4887 ba = static_cast<LineartBoundingArea *>(lip->data);
4888 if (ba->r >= ux && ba->l < ux) {
4889 *next_x = ux;
4890 *next_y = uy;
4891 return ba;
4892 }
4893 }
4894 }
4895 }
4896
4897 /* If we are marching towards the bottom. */
4898 else if (positive_y < 0) {
4899 by = self->b;
4900 bx = x + (by - y) / k;
4901 r1 = ratiod(fbcoord1[0], fbcoord2[0], lx);
4902 r2 = ratiod(fbcoord1[0], fbcoord2[0], bx);
4903 if (std::min(r1, r2) > 1) {
4904 return nullptr;
4905 }
4906 if (r1 <= r2) {
4907 LISTBASE_FOREACH (LinkData *, lip, &self->lp) {
4908 ba = static_cast<LineartBoundingArea *>(lip->data);
4909 if (ba->u >= ly && ba->b < ly) {
4910 *next_x = lx;
4911 *next_y = ly;
4912 return ba;
4913 }
4914 }
4915 }
4916 else {
4917 LISTBASE_FOREACH (LinkData *, lip, &self->bp) {
4918 ba = static_cast<LineartBoundingArea *>(lip->data);
4919 if (ba->r >= bx && ba->l < bx) {
4920 *next_x = bx;
4921 *next_y = by;
4922 return ba;
4923 }
4924 }
4925 }
4926 }
4927 /* Again, horizontal. */
4928 else {
4929 r1 = ratiod(fbcoord1[0], fbcoord2[0], self->l);
4930 if (r1 > 1) {
4931 return nullptr;
4932 }
4933 LISTBASE_FOREACH (LinkData *, lip, &self->lp) {
4934 ba = static_cast<LineartBoundingArea *>(lip->data);
4935 if (ba->u >= y && ba->b < y) {
4936 *next_x = self->l;
4937 *next_y = y;
4938 return ba;
4939 }
4940 }
4941 }
4942 }
4943 /* If the line is completely vertical, hence X difference == 0. */
4944 else {
4945 if (positive_y > 0) {
4946 r1 = ratiod(fbcoord1[1], fbcoord2[1], self->u);
4947 if (r1 > 1) {
4948 return nullptr;
4949 }
4950 LISTBASE_FOREACH (LinkData *, lip, &self->up) {
4951 ba = static_cast<LineartBoundingArea *>(lip->data);
4952 if (ba->r > x && ba->l <= x) {
4953 *next_x = x;
4954 *next_y = self->u;
4955 return ba;
4956 }
4957 }
4958 }
4959 else if (positive_y < 0) {
4960 r1 = ratiod(fbcoord1[1], fbcoord2[1], self->b);
4961 if (r1 > 1) {
4962 return nullptr;
4963 }
4964 LISTBASE_FOREACH (LinkData *, lip, &self->bp) {
4965 ba = static_cast<LineartBoundingArea *>(lip->data);
4966 if (ba->r > x && ba->l <= x) {
4967 *next_x = x;
4968 *next_y = self->b;
4969 return ba;
4970 }
4971 }
4972 }
4973 else {
4974 /* Segment has no length. */
4975 return nullptr;
4976 }
4977 }
4978 return nullptr;
4979}
4980
4983 LineartCache **cached_result,
4984 bool enable_stroke_depth_offset)
4985{
4986 LineartData *ld;
4988 int intersections_only = 0; /* Not used right now, but preserve for future. */
4989 Object *lineart_camera = nullptr;
4990
4991 double t_start;
4992 if (G.debug_value == 4000) {
4993 t_start = BLI_time_now_seconds();
4994 }
4995
4996 bool use_render_camera_override = false;
4998 if (!lmd.source_camera ||
4999 (lineart_camera = DEG_get_evaluated(depsgraph, lmd.source_camera))->type != OB_CAMERA)
5000 {
5001 return false;
5002 }
5003 }
5004 else {
5006 if (render && render->camera_override) {
5007 lineart_camera = DEG_get_evaluated(depsgraph, render->camera_override);
5008 use_render_camera_override = true;
5009 }
5010 if (!lineart_camera) {
5012 if (!scene->camera) {
5013 return false;
5014 }
5015 lineart_camera = scene->camera;
5016 }
5017 }
5018
5019 LineartCache *lc = *cached_result;
5020 if (!lc) {
5022 *cached_result = lc;
5023 }
5024
5026 &lmd,
5027 lineart_camera,
5028 use_render_camera_override ? lineart_camera : scene->camera,
5029 lc);
5030
5031 /* Triangle thread testing data size varies depending on the thread count.
5032 * See definition of LineartTriangleThread for details. */
5034
5035 LineartData *shadow_rb = nullptr;
5036 LineartElementLinkNode *shadow_veln, *shadow_eeln;
5037 ListBase *shadow_elns = ld->conf.shadow_selection ? &lc->shadow_elns : nullptr;
5038 bool shadow_generated = lineart_main_try_generate_shadow_v3(depsgraph,
5039 scene,
5040 ld,
5041 &lmd,
5042 &lc->shadow_data_pool,
5043 &shadow_veln,
5044 &shadow_eeln,
5045 shadow_elns,
5046 &shadow_rb);
5047
5048 /* Get view vector before loading geometries, because we detect feature lines there. */
5050
5051 LineartModifierRuntime *runtime = reinterpret_cast<LineartModifierRuntime *>(lmd.runtime);
5052 blender::Set<const Object *> *included_objects = runtime ? &runtime->object_dependencies :
5053 nullptr;
5054
5056 scene,
5057 lineart_camera,
5058 ld,
5060 false,
5061 shadow_elns,
5062 included_objects);
5063
5064 if (shadow_generated) {
5065 lineart_main_transform_and_add_shadow(ld, shadow_veln, shadow_eeln);
5066 }
5067
5069 /* No geometry loaded, return early. */
5070 return true;
5071 }
5072
5073 /* Initialize the bounding box acceleration structure, it's a lot like BVH in 3D. */
5075
5076 /* We need to get cut into triangles that are crossing near/far plans, only this way can we get
5077 * correct coordinates of those clipped lines. Done in two steps,
5078 * setting clip_far==false for near plane. */
5079 lineart_main_cull_triangles(ld, false);
5080 /* `clip_far == true` for far plane. */
5082
5083 /* At this point triangle adjacent info pointers is no longer needed, free them. */
5085
5086 /* Do the perspective division after clipping is done. */
5088
5090
5091 /* Triangle intersections are done here during sequential adding of them. Only after this,
5092 * triangles and lines are all linked with acceleration structure, and the 2D occlusion stage
5093 * can do its job. */
5095
5096 /* Add shadow cuts to intersection lines as well. */
5098
5099 /* Re-link bounding areas because they have been subdivided by worker threads and we need
5100 * adjacent info. */
5102
5103 /* Link lines to acceleration structure, this can only be done after perspective division, if
5104 * we do it after triangles being added, the acceleration structure has already been
5105 * subdivided, this way we do less list manipulations. */
5107
5108 /* "intersection_only" is preserved for being called in a standalone fashion.
5109 * If so the data will already be available at the stage. Otherwise we do the occlusion and
5110 * chaining etc. */
5111
5112 if (!intersections_only) {
5113
5114 /* Occlusion is work-and-wait. This call will not return before work is completed. */
5116
5117 lineart_main_make_enclosed_shapes(ld, shadow_rb);
5118
5120
5121 /* Chaining is all single threaded. See `lineart_chain.cc`.
5122 * In this particular call, only lines that are geometrically connected (share the _exact_
5123 * same end point) will be chained together. */
5125
5126 /* We are unable to take care of occlusion if we only connect end points, so here we do a
5127 * spit, where the splitting point could be any cut in e->segments. */
5129
5130 /* Then we connect chains based on the _proximity_ of their end points in image space, here's
5131 * the place threshold value gets involved. */
5133
5134 if (ld->conf.chain_smooth_tolerance > FLT_EPSILON) {
5135 /* Keeping UI range of 0-1 for ease of read while scaling down the actual value for best
5136 * effective range in image-space (Coordinate only goes from -1 to 1). This value is
5137 * somewhat arbitrary, but works best for the moment. */
5139 }
5140
5143 }
5144
5145 if (ld->conf.angle_splitting_threshold > FLT_EPSILON) {
5147 }
5148
5149 if (enable_stroke_depth_offset && lmd.stroke_depth_offset > FLT_EPSILON) {
5152 }
5153
5154 if (ld->conf.shadow_use_silhouette) {
5156 }
5157
5158 /* Finally transfer the result list into cache. */
5159 memcpy(&lc->chains, &ld->chains, sizeof(ListBase));
5160
5161 /* At last, we need to clear flags so we don't confuse GPencil generation calls. */
5163
5165 }
5166
5168
5169 if (ld->conf.shadow_enclose_shapes && shadow_rb) {
5171 MEM_freeN(shadow_rb);
5172 }
5173
5174 if (G.debug_value == 4000) {
5176
5177 double t_elapsed = BLI_time_now_seconds() - t_start;
5178 printf("Line art total time: %lf\n", t_elapsed);
5179 }
5180
5181 return true;
5182}
5183
5188
5190 const blender::float4x4 &inverse_mat,
5191 Depsgraph *depsgraph,
5193 const int8_t source_type,
5194 Object *source_object,
5195 Collection *source_collection,
5196 const int level_start,
5197 const int level_end,
5198 const int mat_nr,
5199 const int16_t edge_types,
5200 const uchar mask_switches,
5201 const uchar material_mask_bits,
5202 const uchar intersection_mask,
5203 const float thickness,
5204 const float opacity,
5205 const uchar shadow_selection,
5206 const uchar silhouette_mode,
5207 const char *source_vgname,
5208 const char *vgname,
5209 const int modifier_flags,
5210 const int modifier_calculation_flags)
5211{
5212 if (G.debug_value == 4000) {
5213 printf("Line Art v3: Generating...\n");
5214 }
5215
5216 if (cache == nullptr) {
5217 if (G.debug_value == 4000) {
5218 printf("nullptr Lineart cache!\n");
5219 }
5220 return;
5221 }
5222
5223 Object *orig_ob = nullptr;
5224 Collection *orig_col = nullptr;
5225
5226 if (source_type == LINEART_SOURCE_OBJECT) {
5227 if (!source_object) {
5228 return;
5229 }
5230 orig_ob = source_object->id.orig_id ? (Object *)source_object->id.orig_id : source_object;
5231 orig_col = nullptr;
5232 }
5233 else if (source_type == LINEART_SOURCE_COLLECTION) {
5234 if (!source_collection) {
5235 return;
5236 }
5237 orig_col = source_collection->id.orig_id ? (Collection *)source_collection->id.orig_id :
5238 source_collection;
5239 orig_ob = nullptr;
5240 }
5241 /* Otherwise the whole scene is selected. */
5242
5243 int enabled_types = cache->all_enabled_edge_types;
5244
5245 bool invert_input = modifier_calculation_flags & MOD_LINEART_INVERT_SOURCE_VGROUP;
5246
5247 bool inverse_silhouette = modifier_flags & MOD_LINEART_INVERT_SILHOUETTE_FILTER;
5248
5250 writer.reserve(128);
5251 int total_point_count = 0;
5252 int stroke_count = 0;
5253 LISTBASE_FOREACH (LineartEdgeChain *, ec, &cache->chains) {
5254
5255 if (ec->picked) {
5256 continue;
5257 }
5258 if (!(ec->type & (edge_types & enabled_types))) {
5259 continue;
5260 }
5261 if (ec->level > level_end || ec->level < level_start) {
5262 continue;
5263 }
5264 if (orig_ob && orig_ob != ec->object_ref) {
5265 continue;
5266 }
5267 if (orig_col && ec->object_ref) {
5268 if (BKE_collection_has_object_recursive_instanced(orig_col, ec->object_ref)) {
5269 if (modifier_flags & MOD_LINEART_INVERT_COLLECTION) {
5270 continue;
5271 }
5272 }
5273 else {
5274 if (!(modifier_flags & MOD_LINEART_INVERT_COLLECTION)) {
5275 continue;
5276 }
5277 }
5278 }
5279 if (mask_switches & MOD_LINEART_MATERIAL_MASK_ENABLE) {
5280 if (mask_switches & MOD_LINEART_MATERIAL_MASK_MATCH) {
5281 if (ec->material_mask_bits != material_mask_bits) {
5282 continue;
5283 }
5284 }
5285 else {
5286 if (!(ec->material_mask_bits & material_mask_bits)) {
5287 continue;
5288 }
5289 }
5290 }
5291 if (ec->type & MOD_LINEART_EDGE_FLAG_INTERSECTION) {
5292 if (mask_switches & MOD_LINEART_INTERSECTION_MATCH) {
5293 if (ec->intersection_mask != intersection_mask) {
5294 continue;
5295 }
5296 }
5297 else {
5298 if ((intersection_mask) && !(ec->intersection_mask & intersection_mask)) {
5299 continue;
5300 }
5301 }
5302 }
5303 if (shadow_selection) {
5304 if (ec->shadow_mask_bits != LRT_SHADOW_MASK_UNDEFINED) {
5305 /* TODO(@Yiming): Give a behavior option for how to display undefined shadow info. */
5306 if (shadow_selection == LINEART_SHADOW_FILTER_ILLUMINATED &&
5307 !(ec->shadow_mask_bits & LRT_SHADOW_MASK_ILLUMINATED))
5308 {
5309 continue;
5310 }
5311 if (shadow_selection == LINEART_SHADOW_FILTER_SHADED &&
5312 !(ec->shadow_mask_bits & LRT_SHADOW_MASK_SHADED))
5313 {
5314 continue;
5315 }
5316 if (shadow_selection == LINEART_SHADOW_FILTER_ILLUMINATED_ENCLOSED_SHAPES) {
5317 uint32_t test_bits = ec->shadow_mask_bits & LRT_SHADOW_TEST_SHAPE_BITS;
5318 if ((test_bits != LRT_SHADOW_MASK_ILLUMINATED) &&
5320 {
5321 continue;
5322 }
5323 }
5324 }
5325 }
5326 if (silhouette_mode && (ec->type & (MOD_LINEART_EDGE_FLAG_CONTOUR))) {
5327 bool is_silhouette = false;
5328 if (orig_col) {
5329 if (!ec->silhouette_backdrop) {
5330 is_silhouette = true;
5331 }
5332 else if (!BKE_collection_has_object_recursive_instanced(orig_col, ec->silhouette_backdrop))
5333 {
5334 is_silhouette = true;
5335 }
5336 }
5337 else {
5338 if ((!orig_ob) && (!ec->silhouette_backdrop)) {
5339 is_silhouette = true;
5340 }
5341 }
5342
5343 if ((silhouette_mode == LINEART_SILHOUETTE_FILTER_INDIVIDUAL || orig_ob) &&
5344 ec->silhouette_backdrop != ec->object_ref)
5345 {
5346 is_silhouette = true;
5347 }
5348
5349 if (inverse_silhouette) {
5350 is_silhouette = !is_silhouette;
5351 }
5352 if (!is_silhouette) {
5353 continue;
5354 }
5355 }
5356
5357 /* Preserved: If we ever do asynchronous generation, this picked flag should be set here. */
5358 // ec->picked = 1;
5359
5360 const int count = MOD_lineart_chain_count(ec);
5361 if (count < 2) {
5362 continue;
5363 }
5364
5365 total_point_count += count;
5366 writer.append({ec, count});
5367
5368 stroke_count++;
5369 }
5370
5371 if (!total_point_count || !stroke_count) {
5372 return;
5373 }
5374
5375 blender::bke::CurvesGeometry new_curves(total_point_count, stroke_count);
5377
5378 MutableAttributeAccessor attributes = new_curves.attributes_for_write();
5379 MutableSpan<float3> point_positions = new_curves.positions_for_write();
5380
5381 SpanAttributeWriter<float> point_radii = attributes.lookup_or_add_for_write_only_span<float>(
5382 "radius", AttrDomain::Point);
5383
5384 SpanAttributeWriter<float> point_opacities = attributes.lookup_or_add_for_write_span<float>(
5385 "opacity", AttrDomain::Point);
5386
5387 SpanAttributeWriter<int> stroke_materials = attributes.lookup_or_add_for_write_span<int>(
5388 "material_index", AttrDomain::Curve);
5389
5390 MutableSpan<int> offsets = new_curves.offsets_for_write();
5391
5392 const bool weight_transfer_match_output = modifier_calculation_flags &
5394
5395 using blender::StringRef;
5396 using blender::Vector;
5397
5398 auto ensure_target_defgroup = [&](StringRef group_name) {
5399 if (group_name.is_empty()) {
5400 return -1;
5401 }
5402 int group_index = 0;
5403 LISTBASE_FOREACH_INDEX (bDeformGroup *, group, &new_curves.vertex_group_names, group_index) {
5404 if (group_name == StringRef(group->name)) {
5405 return group_index;
5406 }
5407 }
5408 bDeformGroup *defgroup = MEM_callocN<bDeformGroup>(__func__);
5409 group_name.copy_utf8_truncated(defgroup->name);
5410 BLI_addtail(&new_curves.vertex_group_names, defgroup);
5411 return group_index;
5412 };
5413
5414 int up_to_point = 0;
5415 for (int chain_i : writer.index_range()) {
5416 LineartChainWriteInfo &cwi = writer[chain_i];
5417
5418 Vector<int> src_to_dst_defgroup;
5419
5421 Mesh *src_mesh = nullptr;
5423 int target_defgroup = ensure_target_defgroup(vgname);
5424 if (source_vgname) {
5426 if (eval_ob && eval_ob->type == OB_MESH) {
5427 src_mesh = BKE_object_get_evaluated_mesh(eval_ob);
5428 src_dvert = src_mesh->deform_verts();
5429 }
5430 }
5431
5432 if (!src_dvert.is_empty()) {
5433 const ListBase *deflist = &src_mesh->vertex_group_names;
5434 int group_index = 0;
5435 LISTBASE_FOREACH_INDEX (bDeformGroup *, defgroup, deflist, group_index) {
5436 if (StringRef(defgroup->name).startswith(source_vgname)) {
5437 const int target_group_index = weight_transfer_match_output ?
5438 ensure_target_defgroup(defgroup->name) :
5439 target_defgroup;
5440 src_to_dst_defgroup.append(target_group_index);
5441 }
5442 else {
5443 src_to_dst_defgroup.append(-1);
5444 }
5445 }
5446 }
5447
5448 auto transfer_to_matching_groups = [&](const int64_t source_index, const int target_index) {
5449 for (const int from_group : src_to_dst_defgroup.index_range()) {
5450 if (from_group < 0 || UNLIKELY(source_index >= src_dvert.size())) {
5451 continue;
5452 }
5453 const MDeformWeight *mdw_from = BKE_defvert_find_index(&src_dvert[source_index],
5454 from_group);
5455 MDeformWeight *mdw_to = BKE_defvert_ensure_index(&dv[target_index],
5456 src_to_dst_defgroup[from_group]);
5457 const float source_weight = mdw_from ? mdw_from->weight : 0.0f;
5458 mdw_to->weight = invert_input ? (1 - source_weight) : source_weight;
5459 }
5460 };
5461
5462 auto transfer_to_singular_group = [&](const int64_t source_index, const int target_index) {
5463 float highest_weight = 0.0f;
5464 for (const int from_group : src_to_dst_defgroup.index_range()) {
5465 if (from_group < 0 || UNLIKELY(source_index >= src_dvert.size())) {
5466 continue;
5467 }
5468 const MDeformWeight *mdw_from = BKE_defvert_find_index(&src_dvert[source_index],
5469 from_group);
5470 const float source_weight = mdw_from ? mdw_from->weight : 0.0f;
5471 highest_weight = std::max(highest_weight, source_weight);
5472 }
5473 MDeformWeight *mdw_to = BKE_defvert_ensure_index(&dv[target_index], target_defgroup);
5474 mdw_to->weight = invert_input ? (1 - highest_weight) : highest_weight;
5475 };
5476
5477 int i;
5479 int point_i = i + up_to_point;
5480 point_positions[point_i] = blender::math::transform_point(inverse_mat, float3(eci->gpos));
5481 point_radii.span[point_i] = thickness / 2.0f;
5482 if (point_opacities) {
5483 point_opacities.span[point_i] = opacity;
5484 }
5485
5486 const int64_t vindex = eci->index - cwi.chain->index_offset;
5487
5488 if (!src_to_dst_defgroup.is_empty()) {
5489 if (weight_transfer_match_output) {
5490 transfer_to_matching_groups(vindex, point_i);
5491 }
5492 else {
5493 transfer_to_singular_group(vindex, point_i);
5494 }
5495 }
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.
support for deformation groups and hooks.
MDeformWeight * BKE_defvert_ensure_index(MDeformVert *dv, int defgroup)
Definition deform.cc:825
MDeformWeight * BKE_defvert_find_index(const MDeformVert *dv, int defgroup)
Definition deform.cc:806
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
int BKE_object_visibility(const Object *ob, int dag_eval_mode)
int BKE_render_num_threads(const RenderData *r)
Definition scene.cc:2888
bool BKE_scene_camera_switch_update(Scene *scene)
Definition scene.cc:2265
#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:535
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:484
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:521
void BLI_task_pool_push(TaskPool *pool, TaskRunFunction run, void *taskdata, bool free_taskdata, TaskFreeFunction freedata)
Definition task_pool.cc:526
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:113
#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
@ CAM_PERSP
@ CAM_PANO
@ CAM_CUSTOM
@ CAM_ORTHO
@ CAMERA_SENSOR_FIT_HOR
@ CAMERA_SENSOR_FIT_VERT
Object groups, one object can be in many groups at once.
@ COLLECTION_LRT_EXCLUDE
@ COLLECTION_LRT_INTERSECTION_ONLY
@ COLLECTION_LRT_FORCE_INTERSECTION
@ COLLECTION_LRT_OCCLUSION_ONLY
@ COLLECTION_LRT_NO_INTERSECTION
@ COLLECTION_HIDE_RENDER
@ COLLECTION_HIDE_VIEWPORT
@ COLLECTION_LRT_USE_INTERSECTION_MASK
@ COLLECTION_LRT_USE_INTERSECTION_PRIORITY
@ LA_SUN
@ MOD_LINEART_EDGE_FLAG_PROJECTED_SHADOW
@ MOD_LINEART_EDGE_FLAG_CONTOUR
@ MOD_LINEART_EDGE_FLAG_LIGHT_CONTOUR
@ MOD_LINEART_EDGE_FLAG_SHADOW_FACING_LIGHT
@ MOD_LINEART_EDGE_FLAG_INTERSECTION
@ MOD_LINEART_EDGE_FLAG_INHIBIT
@ MOD_LINEART_EDGE_FLAG_CHAIN_PICKED
@ MOD_LINEART_EDGE_FLAG_CREASE
@ MOD_LINEART_EDGE_FLAG_CONTOUR_SECONDARY
@ MOD_LINEART_EDGE_FLAG_NEXT_IS_DUPLICATION
@ MOD_LINEART_EDGE_FLAG_MATERIAL
@ MOD_LINEART_EDGE_FLAG_EDGE_MARK
@ MOD_LINEART_EDGE_FLAG_LOOSE
@ MOD_LINEART_FILTER_FACE_MARK
@ MOD_LINEART_FILTER_FACE_MARK_BOUNDARIES
@ MOD_LINEART_USE_CREASE_ON_SMOOTH_SURFACES
@ MOD_LINEART_USE_IMAGE_BOUNDARY_TRIMMING
@ MOD_LINEART_LOOSE_AS_CONTOUR
@ MOD_LINEART_CHAIN_PRESERVE_DETAILS
@ MOD_LINEART_USE_BACK_FACE_CULLING
@ MOD_LINEART_CHAIN_LOOSE_EDGES
@ MOD_LINEART_USE_CUSTOM_CAMERA
@ MOD_LINEART_USE_CREASE_ON_SHARP_EDGES
@ MOD_LINEART_INVERT_SOURCE_VGROUP
@ MOD_LINEART_EVERYTHING_AS_CONTOUR
@ MOD_LINEART_ALLOW_DUPLI_OBJECTS
@ MOD_LINEART_MATCH_OUTPUT_VGROUP
@ MOD_LINEART_CHAIN_GEOMETRY_SPACE
@ MOD_LINEART_FILTER_FACE_MARK_KEEP_CONTOUR
@ MOD_LINEART_INTERSECTION_AS_CONTOUR
@ MOD_LINEART_ALLOW_OVERLAP_EDGE_TYPES
@ MOD_LINEART_ALLOW_OVERLAPPING_EDGES
@ MOD_LINEART_ALLOW_CLIPPING_BOUNDARIES
@ MOD_LINEART_FILTER_FACE_MARK_INVERT
@ MA_BL_CULL_BACKFACE
@ LRT_MATERIAL_CUSTOM_INTERSECTION_PRIORITY
@ LRT_MATERIAL_MASK_ENABLED
@ 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
@ OB_CURVES
@ 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 MEM_recallocN(vmemh, len)
#define MEM_SAFE_FREE(v)
#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
void append(const T &value)
bool is_empty() const
IndexRange index_range() const
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr bool is_empty() const
Definition BLI_span.hh:260
constexpr bool startswith(StringRef prefix) const
void append(const T &value)
IndexRange index_range() const
void reserve(const int64_t min_capacity)
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, AttrType data_type, const void *default_value=nullptr) const
GAttributeReader lookup(const StringRef attribute_id) const
MutableSpan< float3 > positions_for_write()
MutableSpan< MDeformVert > deform_verts_for_write()
MutableAttributeAccessor attributes_for_write()
void fill_curve_types(CurveType type)
MutableSpan< int > offsets_for_write()
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, AttrType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, AttrType data_type)
bke::CurvesGeometry & strokes_for_write()
const bke::CurvesGeometry & strokes() const
uint col
#define printf(...)
#define cos
float length(VecOp< float, D >) RET
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)
std::array< VecBase< T, 3 >, 8 > corners(const Bounds< VecBase< T, 3 > > &bounds)
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, bool allow_merging_instance_references=true)
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
#define cosf
Render * RE_GetSceneRender(const Scene *scene)
float clip_end
char sensor_fit
float sensor_y
float sensor_x
float clip_start
float ortho_scale
uint8_t lineart_intersection_priority
uint8_t lineart_intersection_mask
ListBase vertex_group_names
CurvesGeometry geometry
blender::Set< const Object * > * included_objects
blender::Span< int > corner_verts
bool use_freestyle_face
blender::VArray< bool > sharp_edges
LineartEdgeNeighbor * edge_nabr
Object * ob_eval
blender::Span< blender::int2 > edges
blender::Span< int > corner_edges
bool use_freestyle_edge
blender::Span< int > material_indices
LineartVert * v_array
blender::VArray< bool > sharp_faces
blender::Span< int3 > corner_tris
LineartTriangle * tri_array
blender::Span< int > tri_faces
LineartData * ld
blender::VArray< bool > freestyle_face
blender::VArray< bool > freestyle_edge
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:501
LineartBoundingArea * child
uint32_t insider_triangle_count
LineartTriangle ** linked_triangles
LineartEdge ** linked_lines
uint32_t max_triangle_count
ListBase shadow_elns
ListBase chains
LineartStaticMemPool chain_data_pool
uint16_t all_enabled_edge_types
LineartStaticMemPool shadow_data_pool
LineartEdgeChain * chain
float cam_obmat_secondary[4][4]
double view_projection[4][4]
double view_vector_secondary[3]
double camera_pos_secondary[3]
bool filter_face_mark_keep_contour
double active_camera_pos[3]
double view[4][4]
float angle_splitting_threshold
float cam_obmat[4][4]
ListBase vertex_buffer_pointers
ListBase line_buffer_pointers
ListBase triangle_adjacent_pointers
ListBase triangle_buffer_pointers
uint32_t initial_tile_count
LineartBoundingArea * initials
LineartStaticMemPool render_data_pool
struct LineartData::_conf conf
struct LineartData::_geom geom
struct LineartData::_qtree qtree
int isect_scheduled_up_to_index
ListBase chains
LineartElementLinkNode * isect_scheduled_up_to
ListBase wasted_cuts
SpinLock lock_task
LineartStaticMemPool * edge_data_pool
SpinLock lock_cuts
struct LineartPendingEdges pending_edges
LineartStaticMemPool * chain_data_pool
uint32_t index_offset
LineartEdgeSegment * next
LineartEdgeSegment * prev
uint32_t shadow_mask_bits
uint16_t flags
LineartVert * v1
LineartVert * v2
ListBase segments
LineartTriangle * t2
uint64_t edge_identifier
LineartTriangle * t1
Object * object_ref
LineartElementLinkNode * next
eLineArtElementNodeFlag flags
LineartData * ld
LineartIsecThread * threads
LineartTriangle * tri1
LineartTriangle * tri2
LineartElementLinkNode * pending_from
LineartIsecSingle * array
LineartData * ld
LineartElementLinkNode * pending_to
blender::Set< const Object * > object_dependencies
LineartElementLinkNode * v_eln
LineartObjectInfo * next
double model_view[4][4]
Object * original_ob_eval
double normal[4][4]
double model_view_proj[4][4]
uint8_t override_intersection_mask
uint8_t intersection_priority
LineartPendingEdges pending_edges
LineartObjectInfo * pending
LineartEdge ** array
struct LineartData * ld
struct LineartPendingEdges pending_edges
LineartEdge * e[3]
LineartEdge * testing_e[1]
LineartTriangle base
LineartVert * v[3]
uint8_t material_mask_bits
LinkNode * intersecting_verts
uint8_t intersection_priority
uint8_t intersection_mask
uint32_t target_reference
uint8_t mat_occlusion
double fbcoord[4]
double gloc[3]
void * data
struct LinkData * next
void * last
void * first
unsigned char mat_occlusion
unsigned char intersection_priority
unsigned char material_mask_bits
struct MaterialLineArt lineart
MeshRuntimeHandle * runtime
ListBase vertex_group_names
int faces_num
unsigned char intersection_priority
ObjectLineArt lineart
struct Collection * master_collection
struct RenderData r
struct Object * camera
TaskParallelReduceFunc func_reduce
Definition BLI_task.h:176
size_t userdata_chunk_size
Definition BLI_task.h:164
int lineart_triangle_size
LineartTriangleAdjacent * tri_adj
blender::Span< blender::float3 > positions
blender::Span< int3 > corner_tris
LineartVert * vert_arr
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:145