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