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);
2490 obi->override_intersection_mask = lineart_intersection_mask_check(scene->master_collection, ob);
2491 obi->intersection_priority = lineart_intersection_priority_check(scene->master_collection, ob);
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
2585 unit_m4_db(view);
2586 copy_m4_m4_db(ld->conf.view, view);
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 {
5050 Render *render = RE_GetSceneRender(scene);
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 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
@ CAMERA_SENSOR_FIT_HOR
@ CAMERA_SENSOR_FIT_VERT
@ CAM_PERSP
@ CAM_ORTHO
Object groups, one object can be in many groups at once.
@ COLLECTION_HIDE_RENDER
@ COLLECTION_HIDE_VIEWPORT
@ COLLECTION_LRT_EXCLUDE
@ COLLECTION_LRT_INTERSECTION_ONLY
@ COLLECTION_LRT_FORCE_INTERSECTION
@ COLLECTION_LRT_OCCLUSION_ONLY
@ COLLECTION_LRT_NO_INTERSECTION
@ COLLECTION_LRT_USE_INTERSECTION_MASK
@ COLLECTION_LRT_USE_INTERSECTION_PRIORITY
@ CURVE_TYPE_POLY
@ CD_FREESTYLE_EDGE
@ CD_FREESTYLE_FACE
@ LA_SUN
@ MOD_LINEART_EDGE_FLAG_PROJECTED_SHADOW
@ MOD_LINEART_EDGE_FLAG_CONTOUR
@ MOD_LINEART_EDGE_FLAG_LIGHT_CONTOUR
@ MOD_LINEART_EDGE_FLAG_SHADOW_FACING_LIGHT
@ MOD_LINEART_EDGE_FLAG_INTERSECTION
@ MOD_LINEART_EDGE_FLAG_INHIBIT
@ MOD_LINEART_EDGE_FLAG_CHAIN_PICKED
@ MOD_LINEART_EDGE_FLAG_CREASE
@ MOD_LINEART_EDGE_FLAG_CONTOUR_SECONDARY
@ MOD_LINEART_EDGE_FLAG_NEXT_IS_DUPLICATION
@ MOD_LINEART_EDGE_FLAG_MATERIAL
@ MOD_LINEART_EDGE_FLAG_EDGE_MARK
@ MOD_LINEART_EDGE_FLAG_LOOSE
@ MOD_LINEART_FILTER_FACE_MARK
@ MOD_LINEART_FILTER_FACE_MARK_BOUNDARIES
@ MOD_LINEART_USE_CREASE_ON_SMOOTH_SURFACES
@ MOD_LINEART_USE_IMAGE_BOUNDARY_TRIMMING
@ MOD_LINEART_LOOSE_AS_CONTOUR
@ MOD_LINEART_CHAIN_PRESERVE_DETAILS
@ MOD_LINEART_USE_BACK_FACE_CULLING
@ MOD_LINEART_CHAIN_LOOSE_EDGES
@ MOD_LINEART_USE_CUSTOM_CAMERA
@ MOD_LINEART_USE_CREASE_ON_SHARP_EDGES
@ MOD_LINEART_INVERT_SOURCE_VGROUP
@ MOD_LINEART_EVERYTHING_AS_CONTOUR
@ MOD_LINEART_ALLOW_DUPLI_OBJECTS
@ MOD_LINEART_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
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
struct LineartTriangle LineartTriangle
#define LRT_EDGE_IDENTIFIER(obi, e)
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
static btDbvtVolume bounds(btDbvtNode **leaves, int count)
Definition btDbvt.cpp:299
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t size() const
Definition BLI_span.hh:253
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)
MutableSpan< float3 > positions_for_write()
MutableAttributeAccessor attributes_for_write()
void fill_curve_types(CurveType type)
MutableSpan< int > offsets_for_write()
bke::CurvesGeometry & strokes_for_write()
const bke::CurvesGeometry & strokes() const
local_group_size(16, 16) .push_constant(Type b
#define printf
const Depsgraph * depsgraph
#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)
struct LineartChainWriteInfo LineartChainWriteInfo
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)
#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)
void parallel_sort(RandomAccessIterator begin, RandomAccessIterator end)
Definition BLI_sort.hh:23
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
struct BMVert * v
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
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
function< void(void)> TaskRunFunction
Definition task.h:19
uint8_t flag
Definition wm_window.cc:138