Blender V5.0
lineart_shadow.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "MOD_lineart.hh"
10
11#include "lineart_intern.hh"
12
13#include "BKE_global.hh"
14#include "BKE_grease_pencil.hh"
16#include "BKE_object.hh"
17
18#include "BLI_math_matrix.h"
19#include "BLI_math_rotation.h"
20#include "BLI_task.h"
21#include "BLI_time.h"
22
23#include "DNA_light_types.h"
24#include "DNA_modifier_types.h"
25
26#include "MEM_guardedalloc.h"
27
28/* Shadow loading etc. ================== */
29
31{
32 LISTBASE_FOREACH (LineartElementLinkNode *, eln, shadow_elns) {
33 if (eln->obindex == obindex) {
34 return eln;
35 }
36 }
37 return nullptr;
38}
39
41 uint64_t edge_identifier)
42{
43 LineartEdge *elist = (LineartEdge *)shadow_eln->pointer;
44 for (int i = 0; i < shadow_eln->element_count; i++) {
45 if (elist[i].edge_identifier == edge_identifier) {
46 return &elist[i];
47 }
48 }
49 return nullptr;
50}
51
53{
54
56 return false;
57 }
58 double view_vector[3];
59 double light_vector[3];
60 bool side_1_facing_light = false;
61 bool side_2_facing_light = false;
62 bool side_1_facing_camera = false;
64 sub_v3_v3v3_db(light_vector, ld->conf.camera_pos_secondary, e->v1->gloc);
65 }
66 else {
67 copy_v3_v3_db(light_vector, ld->conf.view_vector_secondary);
68 }
69 double dot_light_1 = dot_v3v3_db(light_vector, e->t1->gn);
70 side_1_facing_light = (dot_light_1 > 0);
71 if (e->t2) {
72 double dot_light_2 = dot_v3v3_db(light_vector, e->t2->gn);
73 side_2_facing_light = (dot_light_2 > 0);
74 }
75 else {
76 side_2_facing_light = !side_1_facing_light;
77 }
78
79 if (ld->conf.cam_is_persp) {
80 sub_v3_v3v3_db(view_vector, ld->conf.camera_pos, e->v1->gloc);
81 }
82 else {
83 copy_v3_v3_db(view_vector, ld->conf.view_vector);
84 }
85 double dot_view_1 = dot_v3v3_db(view_vector, e->t1->gn);
86 side_1_facing_camera = (dot_view_1 > 0);
87
88 if ((side_1_facing_camera && (!side_1_facing_light) && side_2_facing_light) ||
89 ((!side_1_facing_camera) && side_1_facing_light && (!side_2_facing_light)))
90 {
91 return true;
92 }
93 return false;
94}
95
97{
98 LISTBASE_FOREACH (LineartEdgeSegment *, es, &shadow_edge->segments) {
99 /* Convert to view space cutting points. */
100 double la1 = es->ratio;
101 double la2 = es->next ? es->next->ratio : 1.0f;
102 la1 = la1 * e->v2->fbcoord[3] /
103 (e->v1->fbcoord[3] - la1 * (e->v1->fbcoord[3] - e->v2->fbcoord[3]));
104 la2 = la2 * e->v2->fbcoord[3] /
105 (e->v1->fbcoord[3] - la2 * (e->v1->fbcoord[3] - e->v2->fbcoord[3]));
106 uchar shadow_bits = (es->occlusion != 0) ? LRT_SHADOW_MASK_SHADED :
108
110 {
111 shadow_bits = LRT_SHADOW_MASK_SHADED;
112 }
113
114 lineart_edge_cut(ld, e, la1, la2, 0, 0, shadow_bits);
115 }
116}
117
119{
120 if (!shadow_elns) {
121 return;
122 }
123
124 LineartElementLinkNode *eln_isect_shadow = nullptr;
125 LineartElementLinkNode *eln_isect_original = nullptr;
126
127 LISTBASE_FOREACH (LineartElementLinkNode *, eln, shadow_elns) {
128 if (eln->flags & LRT_ELEMENT_INTERSECTION_DATA) {
129 eln_isect_shadow = eln;
130 break;
131 }
132 }
134 if (eln->flags & LRT_ELEMENT_INTERSECTION_DATA) {
135 eln_isect_original = eln;
136 break;
137 }
138 }
139 if (!eln_isect_shadow || !eln_isect_original) {
140 return;
141 }
142
143 /* Keeping it single threaded for now because a simple parallel_for could end up getting the same
144 * #shadow_e in different threads. */
145 for (int i = 0; i < eln_isect_original->element_count; i++) {
146 LineartEdge *e = &((LineartEdge *)eln_isect_original->pointer)[i];
147 LineartEdge *shadow_e = lineart_find_matching_edge(eln_isect_shadow, e->edge_identifier);
148 if (shadow_e) {
149 lineart_register_shadow_cuts(ld, e, shadow_e);
150 }
151 }
152}
153
154/* Shadow computation part ================== */
155
157{
159
160 /* See if there is any already allocated memory we can reuse. */
161 if (ld->wasted_shadow_cuts.first) {
164 memset(es, 0, sizeof(LineartShadowSegment));
165 return es;
166 }
168
169 /* Otherwise allocate some new memory. */
171 sizeof(LineartShadowSegment));
172}
173
174static void lineart_shadow_segment_slice_get(double *fb_co_1,
175 double *fb_co_2,
176 double *gloc_1,
177 double *gloc_2,
178 double ratio,
179 double at_1,
180 double at_2,
181 double *r_fb_co,
182 double *r_gloc)
183{
184 double real_at = ((at_2 - at_1) == 0) ? 0 : ((ratio - at_1) / (at_2 - at_1));
185 double ga_div = (fb_co_2[3] * (1.0f - real_at) + fb_co_1[3] * real_at);
186 double ga;
187 if (ga_div == 0) {
188 ga = 0;
189 }
190 else {
191 ga = fb_co_1[3] * real_at / ga_div;
192 }
193 interp_v3_v3v3_db(r_fb_co, fb_co_1, fb_co_2, real_at);
194 r_fb_co[3] = interpd(fb_co_2[3], fb_co_1[3], ga);
195 interp_v3_v3v3_db(r_gloc, gloc_1, gloc_2, ga);
196}
197
221static bool lineart_do_closest_segment(bool is_persp,
222 double *s1_fb_co_1,
223 double *s1_fb_co_2,
224 double *s2_fb_co_1,
225 double *s2_fb_co_2,
226 double *s1_gloc_1,
227 double *s1_gloc_2,
228 double *s2_gloc_1,
229 double *s2_gloc_2,
230 double *r_fb_co_1,
231 double *r_fb_co_2,
232 double *r_gloc_1,
233 double *r_gloc_2,
234 double *r_new_in_the_middle,
235 double *r_new_in_the_middle_global,
236 double *r_new_at,
237 bool *is_side_2r,
238 bool *use_new_ref)
239{
240 int side = 0;
241 int z_index = is_persp ? 3 : 2;
242
243 /* No need to do anything if the segment has no length. */
244 if (s2_fb_co_1[z_index] == s2_fb_co_2[z_index]) {
245 return false;
246 }
247
248 /* Always use the closest point to the light camera. */
249 if (s1_fb_co_1[z_index] >= s2_fb_co_1[z_index]) {
250 copy_v4_v4_db(r_fb_co_1, s2_fb_co_1);
251 copy_v3_v3_db(r_gloc_1, s2_gloc_1);
252 side++;
253 }
254 if (s1_fb_co_2[z_index] >= s2_fb_co_2[z_index]) {
255 copy_v4_v4_db(r_fb_co_2, s2_fb_co_2);
256 copy_v3_v3_db(r_gloc_2, s2_gloc_2);
257 *is_side_2r = true;
258 side++;
259 }
260 if (s1_fb_co_1[z_index] <= s2_fb_co_1[z_index]) {
261 copy_v4_v4_db(r_fb_co_1, s1_fb_co_1);
262 copy_v3_v3_db(r_gloc_1, s1_gloc_1);
263 side--;
264 }
265 if (s1_fb_co_2[z_index] <= s2_fb_co_2[z_index]) {
266 copy_v4_v4_db(r_fb_co_2, s1_fb_co_2);
267 copy_v3_v3_db(r_gloc_2, s1_gloc_2);
268 *is_side_2r = false;
269 side--;
270 }
271
272 /* No need to cut in the middle, because one segment completely overlaps the other. */
273 if (side) {
274 if (side > 0) {
275 *is_side_2r = true;
276 *use_new_ref = true;
277 }
278 else if (side < 0) {
279 *is_side_2r = false;
280 *use_new_ref = false;
281 }
282 return false;
283 }
284
285 /* Else there must be an intersection point in the middle. Use "w" value to linearly plot the
286 * position and get image space "ratio" position. */
287 double dl = s1_fb_co_1[z_index] - s2_fb_co_1[z_index];
288 double dr = s1_fb_co_2[z_index] - s2_fb_co_2[z_index];
289 double ga = ratiod(dl, dr, 0);
290 double ga_div = s2_fb_co_1[3] * (1.0f - ga) + s2_fb_co_2[3] * ga;
291 if (ga_div == 0) {
292 *r_new_at = 0;
293 return false;
294 }
295
296 *r_new_at = is_persp ? s2_fb_co_2[3] * ga / ga_div : ga;
297
298 interp_v3_v3v3_db(r_new_in_the_middle, s2_fb_co_1, s2_fb_co_2, *r_new_at);
299 r_new_in_the_middle[3] = interpd(s2_fb_co_2[3], s2_fb_co_1[3], ga);
300 interp_v3_v3v3_db(r_new_in_the_middle_global, s1_gloc_1, s1_gloc_2, ga);
301 *use_new_ref = true;
302
303 return true;
304}
305
306/* For each visible [segment] of the edge, create 1 shadow edge. Note if the original edge has
307 * multiple visible cuts, multiple shadow edges should be generated. */
309 bool transform_edge_cuts,
310 bool do_light_contour)
311{
312/* If the segment is short enough, we ignore them because it's not prominently visible anyway. */
313#define DISCARD_NONSENSE_SEGMENTS \
314 if (es->occlusion != 0 || \
315 (es->next && LRT_DOUBLE_CLOSE_ENOUGH(es->ratio, ((LineartEdgeSegment *)es->next)->ratio))) \
316 { \
317 LRT_ITER_ALL_LINES_NEXT; \
318 continue; \
319 }
320
321 /* Count and allocate at once to save time. */
322 int segment_count = 0;
324 if (do_light_contour) {
326 }
328 {
329 /* Only contour and loose edges can actually cast shadows. We allow light contour here because
330 * we want to see if it also doubles as a view contour, in that case we also need to project
331 * them. */
332 if (!(e->flags & accept_types)) {
333 continue;
334 }
336 /* Check if the light contour also doubles as a view contour. */
337 LineartEdge *orig_e = (LineartEdge *)e->t1;
338 if (!orig_e->t2) {
340 }
341 else {
342 double vv[3];
343 double *view_vector = vv;
344 double dot_1 = 0, dot_2 = 0;
345 double result;
346 if (ld->conf.cam_is_persp) {
347 sub_v3_v3v3_db(view_vector, orig_e->v1->gloc, ld->conf.camera_pos);
348 }
349 else {
350 view_vector = ld->conf.view_vector;
351 }
352
353 dot_1 = dot_v3v3_db(view_vector, orig_e->t1->gn);
354 dot_2 = dot_v3v3_db(view_vector, orig_e->t2->gn);
355
356 if ((result = dot_1 * dot_2) <= 0 && (dot_1 + dot_2)) {
357 /* If this edge is both a light contour and a view contour, mark it for the convenience
358 * of generating it in the next iteration. */
360 }
361 }
362 if (!(e->flags & MOD_LINEART_EDGE_FLAG_CONTOUR)) {
363 continue;
364 }
365 }
366 LISTBASE_FOREACH (LineartEdgeSegment *, es, &e->segments) {
368 segment_count++;
369 }
370 }
372
373 LineartShadowEdge *sedge = static_cast<LineartShadowEdge *>(
374 lineart_mem_acquire(&ld->render_data_pool, sizeof(LineartShadowEdge) * segment_count));
376 &ld->render_data_pool, sizeof(LineartShadowSegment) * segment_count * 2));
377
378 ld->shadow_edges = sedge;
379 ld->shadow_edges_count = segment_count;
380
381 int i = 0;
383 {
385 continue;
386 }
387 LISTBASE_FOREACH (LineartEdgeSegment *, es, &e->segments) {
389
390 double next_at = es->next ? (es->next)->ratio : 1.0f;
391 /* Get correct XYZ and W coordinates. */
392 interp_v3_v3v3_db(sedge[i].fbc1, e->v1->fbcoord, e->v2->fbcoord, es->ratio);
393 interp_v3_v3v3_db(sedge[i].fbc2, e->v1->fbcoord, e->v2->fbcoord, next_at);
394
395 /* Global coord for light-shadow separation line (occlusion-corrected light contour). */
396 double ga1 = e->v1->fbcoord[3] * es->ratio /
397 (es->ratio * e->v1->fbcoord[3] + (1 - es->ratio) * e->v2->fbcoord[3]);
398 double ga2 = e->v1->fbcoord[3] * next_at /
399 (next_at * e->v1->fbcoord[3] + (1 - next_at) * e->v2->fbcoord[3]);
400 interp_v3_v3v3_db(sedge[i].g1, e->v1->gloc, e->v2->gloc, ga1);
401 interp_v3_v3v3_db(sedge[i].g2, e->v1->gloc, e->v2->gloc, ga2);
402
403 /* Assign an absurdly big W for initial distance so when triangles show up to catch the
404 * shadow, their w must certainly be smaller than this value so the shadow catches
405 * successfully. */
406 sedge[i].fbc1[3] = 1e30;
407 sedge[i].fbc2[3] = 1e30;
408 sedge[i].fbc1[2] = 1e30;
409 sedge[i].fbc2[2] = 1e30;
410
411 /* Assign to the first segment's right and the last segment's left position */
412 copy_v4_v4_db(sseg[i * 2].fbc2, sedge[i].fbc1);
413 copy_v4_v4_db(sseg[i * 2 + 1].fbc1, sedge[i].fbc2);
414 sseg[i * 2].ratio = 0.0f;
415 sseg[i * 2 + 1].ratio = 1.0f;
416 BLI_addtail(&sedge[i].shadow_segments, &sseg[i * 2]);
417 BLI_addtail(&sedge[i].shadow_segments, &sseg[i * 2 + 1]);
418
420 sedge[i].e_ref = (LineartEdge *)e->t1;
421 sedge[i].e_ref_light_contour = e;
422 /* Restore original edge flag for edges "who is both view and light contour" so we still
423 * have correct edge flags. */
425 }
426 else {
427 sedge[i].e_ref = e;
428 }
429
430 sedge[i].es_ref = es;
431
432 i++;
433 }
434 }
436
437 /* Transform the cutting position to global space for regular feature lines. This is for
438 * convenience of reusing the shadow cast function for both shadow line generation and silhouette
439 * registration, which the latter one needs view-space coordinates, while cast shadow needs
440 * global-space coordinates. */
441 if (transform_edge_cuts) {
443 {
444 LISTBASE_FOREACH (LineartEdgeSegment *, es, &e->segments) {
445 es->ratio = e->v1->fbcoord[3] * es->ratio /
446 (es->ratio * e->v1->fbcoord[3] + (1 - es->ratio) * e->v2->fbcoord[3]);
447 }
448 }
450 }
451
452 if (G.debug_value == 4000) {
453 printf("Shadow: Added %d raw shadow_edges\n", segment_count);
454 }
455}
456
457/* This function does the actual cutting on a given "shadow edge".
458 * #start / #end determines the view(from light camera) space cutting ratio.
459 * #start/end_gloc/fbc are the respective start/end coordinates.
460 * #facing_light is set from the caller which determines if this edge landed on a triangle's light
461 * facing side or not.
462 *
463 * Visually this function does this: (Top is the far side of the camera)
464 * _-end
465 * _-`
466 * l[-------------_-`--------------]r [e] 1) Calls for cut on top of #e.
467 * _-`
468 * _-`
469 * start-`
470 *
471 * _-end
472 * _-`
473 * l[-----][------_-`----][--------]r [e] 2) Add cutting points on #e at #start/#end.
474 * _-`
475 * _-`
476 * start-`
477 *
478 * _-end
479 * _-`
480 * [------_-`----] 3) Call lineart_shadow_segment_slice_get() to
481 * _-` get coordinates of a visually aligned segment on
482 * _-` #e with the incoming segment.
483 * start-`
484 *
485 * _c-----] 4) Call lineart_do_closest_segment() to find out the
486 * _-` actual geometry after cut, add a new cut if needed.
487 * _-`
488 * [`
489 *
490 * l[-----] _][----][--------]r [e] 5) Write coordinates on cuts.
491 * _-`
492 * _-`
493 * [`
494 *
495 * This process is repeated on each existing segments of the shadow edge (#e), which ensures they
496 * all have been tested for closest segments after cutting. And in the diagram it's clear that the
497 * left/right side of cuts are likely to be discontinuous, each cut's left side designates the
498 * right side of the last segment, and vice-versa. */
501 double start,
502 double end,
503 double *start_gloc,
504 double *end_gloc,
505 double *start_fb_co,
506 double *end_fb_co,
507 bool facing_light,
508 uint32_t target_reference)
509{
510 LineartShadowSegment *seg, *i_seg;
511 LineartShadowSegment *cut_start_after = static_cast<LineartShadowSegment *>(
512 e->shadow_segments.first),
513 *cut_end_before = static_cast<LineartShadowSegment *>(
514 e->shadow_segments.last);
515 LineartShadowSegment *new_seg_1 = nullptr, *new_seg_2 = nullptr, *seg_1 = nullptr,
516 *seg_2 = nullptr;
517 int untouched = 0;
518
519 /* If for some reason the occlusion function may give a result that has zero length, or
520 * reversed in direction, or NAN, we take care of them here. */
521 if (LRT_DOUBLE_CLOSE_ENOUGH(start, end)) {
522 return;
523 }
524 if (LRT_DOUBLE_CLOSE_ENOUGH(start, 1) || LRT_DOUBLE_CLOSE_ENOUGH(end, 0)) {
525 return;
526 }
527 if (UNLIKELY(start != start)) {
528 start = 0;
529 }
530 if (UNLIKELY(end != end)) {
531 end = 0;
532 }
533
534 if (start > end) {
535 double t = start;
536 start = end;
537 end = t;
538 }
539
540 /* Begin looking for starting position of the segment. */
541 /* Not using a list iteration macro because of it more clear when using for loops to iterate
542 * through the segments. */
543 LISTBASE_FOREACH (LineartShadowSegment *, seg, &e->shadow_segments) {
544 if (LRT_DOUBLE_CLOSE_ENOUGH(seg->ratio, start)) {
545 cut_start_after = seg;
546 new_seg_1 = cut_start_after;
547 break;
548 }
549 if (seg->next == nullptr) {
550 break;
551 }
552 i_seg = seg->next;
553 if (i_seg->ratio > start + 1e-09 && start > seg->ratio) {
554 cut_start_after = seg;
555 new_seg_1 = lineart_give_shadow_segment(ld);
556 break;
557 }
558 }
559 if (!cut_start_after && LRT_DOUBLE_CLOSE_ENOUGH(1, end)) {
560 untouched = 1;
561 }
562 for (seg = cut_start_after->next; seg; seg = seg->next) {
563 /* We tried to cut at existing cutting point (e.g. where the line's occluded by a triangle
564 * strip). */
565 if (LRT_DOUBLE_CLOSE_ENOUGH(seg->ratio, end)) {
566 cut_end_before = seg;
567 new_seg_2 = cut_end_before;
568 break;
569 }
570 /* This check is to prevent `es->ratio == 1.0` (where we don't need to cut because we are ratio
571 * the end point). */
572 if (!seg->next && LRT_DOUBLE_CLOSE_ENOUGH(1, end)) {
573 cut_end_before = seg;
574 new_seg_2 = cut_end_before;
575 untouched = 1;
576 break;
577 }
578 /* When an actual cut is needed in the line. */
579 if (seg->ratio > end) {
580 cut_end_before = seg;
581 new_seg_2 = lineart_give_shadow_segment(ld);
582 break;
583 }
584 }
585
586 /* When we still can't find any existing cut in the line, we allocate new ones. */
587 if (new_seg_1 == nullptr) {
588 new_seg_1 = lineart_give_shadow_segment(ld);
589 }
590 if (new_seg_2 == nullptr) {
591 if (untouched) {
592 new_seg_2 = new_seg_1;
593 cut_end_before = new_seg_2;
594 }
595 else {
596 new_seg_2 = lineart_give_shadow_segment(ld);
597 }
598 }
599
600 /* If we touched the cut list, we assign the new cut position based on new cut position,
601 * this way we accommodate precision lost due to multiple cut inserts. */
602 new_seg_1->ratio = start;
603 if (!untouched) {
604 new_seg_2->ratio = end;
605 }
606
607 double r_fb_co_1[4] = {0}, r_fb_co_2[4] = {0}, r_gloc_1[3] = {0}, r_gloc_2[3] = {0};
608 double r_new_in_the_middle[4], r_new_in_the_middle_global[3], r_new_at;
609 double *s1_fb_co_1, *s1_fb_co_2, *s1_gloc_1, *s1_gloc_2;
610
611 /* Temporary coordinate records and "middle" records. */
612 double t_g1[3], t_g2[3], t_fbc1[4], t_fbc2[4], m_g1[3], m_fbc1[4], m_g2[3], m_fbc2[4];
613 bool is_side_2r, has_middle = false, use_new_ref;
614 copy_v4_v4_db(t_fbc1, start_fb_co);
615 copy_v3_v3_db(t_g1, start_gloc);
616
617 /* Do max stuff before insert. */
619 for (seg = cut_start_after; seg != cut_end_before; seg = nes) {
620 nes = seg->next;
621
622 s1_fb_co_1 = seg->fbc2;
623 s1_fb_co_2 = nes->fbc1;
624
625 s1_gloc_1 = seg->g2;
626 s1_gloc_2 = nes->g1;
627
628 seg_1 = seg;
629 seg_2 = nes;
630
631 if (seg == cut_start_after) {
633 nes->fbc1,
634 seg->g2,
635 nes->g1,
636 new_seg_1->ratio,
637 seg->ratio,
638 nes->ratio,
639 m_fbc1,
640 m_g1);
641 s1_fb_co_1 = m_fbc1;
642 s1_gloc_1 = m_g1;
643
644 seg_1 = new_seg_1;
645 if (cut_start_after != new_seg_1) {
646 BLI_insertlinkafter(&e->shadow_segments, cut_start_after, new_seg_1);
647 copy_v4_v4_db(new_seg_1->fbc1, m_fbc1);
648 copy_v3_v3_db(new_seg_1->g1, m_g1);
649 }
650 }
651 if (nes == cut_end_before) {
653 nes->fbc1,
654 seg->g2,
655 nes->g1,
656 new_seg_2->ratio,
657 seg->ratio,
658 nes->ratio,
659 m_fbc2,
660 m_g2);
661 s1_fb_co_2 = m_fbc2;
662 s1_gloc_2 = m_g2;
663
664 seg_2 = new_seg_2;
665 if (cut_end_before != new_seg_2) {
666 BLI_insertlinkbefore(&e->shadow_segments, cut_end_before, new_seg_2);
667 copy_v4_v4_db(new_seg_2->fbc2, m_fbc2);
668 copy_v3_v3_db(new_seg_2->g2, m_g2);
669 /* Need to restore the flag for next segment's reference. */
670 seg_2->flag = seg->flag;
671 seg_2->target_reference = seg->target_reference;
672 }
673 }
674
676 start_fb_co, end_fb_co, start_gloc, end_gloc, seg_2->ratio, start, end, t_fbc2, t_g2);
677
678 if ((has_middle = lineart_do_closest_segment(ld->conf.cam_is_persp,
679 s1_fb_co_1,
680 s1_fb_co_2,
681 t_fbc1,
682 t_fbc2,
683 s1_gloc_1,
684 s1_gloc_2,
685 t_g1,
686 t_g2,
687 r_fb_co_1,
688 r_fb_co_2,
689 r_gloc_1,
690 r_gloc_2,
691 r_new_in_the_middle,
692 r_new_in_the_middle_global,
693 &r_new_at,
694 &is_side_2r,
695 &use_new_ref)))
696 {
698 ss_middle->ratio = interpf(seg_2->ratio, seg_1->ratio, r_new_at);
699 ss_middle->flag = LRT_SHADOW_CASTED |
700 (use_new_ref ? (facing_light ? LRT_SHADOW_FACING_LIGHT : 0) : seg_1->flag);
701 ss_middle->target_reference = (use_new_ref ? (target_reference) : seg_1->target_reference);
702 copy_v3_v3_db(ss_middle->g1, r_new_in_the_middle_global);
703 copy_v3_v3_db(ss_middle->g2, r_new_in_the_middle_global);
704 copy_v4_v4_db(ss_middle->fbc1, r_new_in_the_middle);
705 copy_v4_v4_db(ss_middle->fbc2, r_new_in_the_middle);
706 BLI_insertlinkafter(&e->shadow_segments, seg_1, ss_middle);
707 }
708 /* Always assign the "closest" value to the segment. */
709 copy_v4_v4_db(seg_1->fbc2, r_fb_co_1);
710 copy_v3_v3_db(seg_1->g2, r_gloc_1);
711 copy_v4_v4_db(seg_2->fbc1, r_fb_co_2);
712 copy_v3_v3_db(seg_2->g1, r_gloc_2);
713
714 if (has_middle) {
715 seg_1->flag = LRT_SHADOW_CASTED |
716 (is_side_2r ? seg->flag : (facing_light ? LRT_SHADOW_FACING_LIGHT : 0));
717 seg_1->target_reference = is_side_2r ? seg->target_reference : target_reference;
718 }
719 else {
720 seg_1->flag = LRT_SHADOW_CASTED |
721 (use_new_ref ? (facing_light ? LRT_SHADOW_FACING_LIGHT : 0) : seg->flag);
722 seg_1->target_reference = use_new_ref ? target_reference : seg->target_reference;
723 }
724
725 copy_v4_v4_db(t_fbc1, t_fbc2);
726 copy_v3_v3_db(t_g1, t_g2);
727 }
728}
729
731 LineartTriangle *tri,
732 LineartShadowEdge *sedge,
733 double *r_at_1,
734 double *r_at_2,
735 double *r_fb_co_1,
736 double *r_fb_co_2,
737 double *r_gloc_1,
738 double *r_gloc_2,
739 bool *r_facing_light)
740{
741 using namespace blender;
742 double *LFBC = sedge->fbc1, *RFBC = sedge->fbc2, *FBC0 = tri->v[0]->fbcoord,
743 *FBC1 = tri->v[1]->fbcoord, *FBC2 = tri->v[2]->fbcoord;
744
745 /* Bound box check. Because we have already done occlusion in the shadow camera, so any visual
746 * intersection found in this function must mean that the triangle is behind the given line so it
747 * will always project a shadow, hence no need to do depth bound-box check. */
748 if ((std::max({FBC0[0], FBC1[0], FBC2[0]}) < std::min(LFBC[0], RFBC[0])) ||
749 (std::min({FBC0[0], FBC1[0], FBC2[0]}) > std::max(LFBC[0], RFBC[0])) ||
750 (std::max({FBC0[1], FBC1[1], FBC2[1]}) < std::min(LFBC[1], RFBC[1])) ||
751 (std::min({FBC0[1], FBC1[1], FBC2[1]}) > std::max(LFBC[1], RFBC[1])))
752 {
753 return false;
754 }
755
756 bool is_persp = ld->conf.cam_is_persp;
757 double ratio[2];
758 int trie[2];
759 int pi = 0;
760 if (lineart_line_isec_2d_ignore_line2pos(FBC0, FBC1, LFBC, RFBC, &ratio[pi])) {
761 trie[pi] = 0;
762 pi++;
763 }
764 if (lineart_line_isec_2d_ignore_line2pos(FBC1, FBC2, LFBC, RFBC, &ratio[pi])) {
765 /* ratio[0] == 1 && ratio[1] == 0 means we found a intersection at the same point of the
766 * edge (FBC1), ignore this one and try get the intersection point from the other side of
767 * the edge
768 */
769 if (!(pi && LRT_DOUBLE_CLOSE_ENOUGH(ratio[0], 1.0f) &&
770 LRT_DOUBLE_CLOSE_ENOUGH(ratio[1], 0.0f)))
771 {
772 trie[pi] = 1;
773 pi++;
774 }
775 }
776 if (!pi) {
777 return false;
778 }
779 if (pi == 1 && lineart_line_isec_2d_ignore_line2pos(FBC2, FBC0, LFBC, RFBC, &ratio[pi])) {
780
781 if ((trie[0] == 0 && LRT_DOUBLE_CLOSE_ENOUGH(ratio[0], 0.0f) &&
782 LRT_DOUBLE_CLOSE_ENOUGH(ratio[1], 1.0f)) ||
783 (trie[0] == 1 && LRT_DOUBLE_CLOSE_ENOUGH(ratio[0], 1.0f) &&
784 LRT_DOUBLE_CLOSE_ENOUGH(ratio[1], 0.0f)))
785 {
786 return false;
787 }
788 trie[pi] = 2;
789 pi++;
790 }
791
792 if (pi != 2) {
793 return false;
794 }
795
796 /* Get projected global position. */
797
798 double3 gpos1, gpos2;
799 double *v1 = (trie[0] == 0 ? FBC0 : (trie[0] == 1 ? FBC1 : FBC2));
800 double *v2 = (trie[0] == 0 ? FBC1 : (trie[0] == 1 ? FBC2 : FBC0));
801 double *v3 = (trie[1] == 0 ? FBC0 : (trie[1] == 1 ? FBC1 : FBC2));
802 double *v4 = (trie[1] == 0 ? FBC1 : (trie[1] == 1 ? FBC2 : FBC0));
803 double *gv1 = (trie[0] == 0 ? tri->v[0]->gloc :
804 (trie[0] == 1 ? tri->v[1]->gloc : tri->v[2]->gloc));
805 double *gv2 = (trie[0] == 0 ? tri->v[1]->gloc :
806 (trie[0] == 1 ? tri->v[2]->gloc : tri->v[0]->gloc));
807 double *gv3 = (trie[1] == 0 ? tri->v[0]->gloc :
808 (trie[1] == 1 ? tri->v[1]->gloc : tri->v[2]->gloc));
809 double *gv4 = (trie[1] == 0 ? tri->v[1]->gloc :
810 (trie[1] == 1 ? tri->v[2]->gloc : tri->v[0]->gloc));
811 double gr1 = is_persp ? v1[3] * ratio[0] / (ratio[0] * v1[3] + (1 - ratio[0]) * v2[3]) :
812 ratio[0];
813 double gr2 = is_persp ? v3[3] * ratio[1] / (ratio[1] * v3[3] + (1 - ratio[1]) * v4[3]) :
814 ratio[1];
815 interp_v3_v3v3_db(gpos1, gv1, gv2, gr1);
816 interp_v3_v3v3_db(gpos2, gv3, gv4, gr2);
817
818 double4 fbc1, fbc2;
819
820 mul_v4_m4v3_db(fbc1, ld->conf.view_projection, gpos1);
821 mul_v4_m4v3_db(fbc2, ld->conf.view_projection, gpos2);
822 if (is_persp) {
823 mul_v3db_db(fbc1, 1.0f / fbc1[3]);
824 mul_v3db_db(fbc2, 1.0f / fbc2[3]);
825 }
826
827 int use = (fabs(LFBC[0] - RFBC[0]) > fabs(LFBC[1] - RFBC[1])) ? 0 : 1;
828 double at1 = ratiod(LFBC[use], RFBC[use], fbc1[use]);
829 double at2 = ratiod(LFBC[use], RFBC[use], fbc2[use]);
830 if (at1 > at2) {
831 std::swap(gpos1, gpos2);
832 std::swap(fbc1, fbc2);
833 std::swap(at1, at2);
834 }
835
836 /* If not effectively projecting anything. */
837 if (at1 > (1.0f - FLT_EPSILON) || at2 < FLT_EPSILON) {
838 return false;
839 }
840
841 /* Trim to edge's end points. */
842
843 double t_fbc1[4], t_fbc2[4], t_gpos1[3], t_gpos2[3];
844 bool trimmed1 = false, trimmed2 = false;
845 if (at1 < 0 || at2 > 1) {
846 double rat1 = (-at1) / (at2 - at1);
847 double rat2 = (1.0f - at1) / (at2 - at1);
848 double gat1 = is_persp ? fbc1[3] * rat1 / (rat1 * fbc1[3] + (1 - rat1) * fbc2[3]) : rat1;
849 double gat2 = is_persp ? fbc1[3] * rat2 / (rat2 * fbc1[3] + (1 - rat2) * fbc2[3]) : rat2;
850 if (at1 < 0) {
851 interp_v3_v3v3_db(t_gpos1, gpos1, gpos2, gat1);
852 interp_v3_v3v3_db(t_fbc1, fbc1, fbc2, rat1);
853 t_fbc1[3] = interpd(fbc2[3], fbc1[3], gat1);
854 at1 = 0;
855 trimmed1 = true;
856 }
857 if (at2 > 1) {
858 interp_v3_v3v3_db(t_gpos2, gpos1, gpos2, gat2);
859 interp_v3_v3v3_db(t_fbc2, fbc1, fbc2, rat2);
860 t_fbc2[3] = interpd(fbc2[3], fbc1[3], gat2);
861 at2 = 1;
862 trimmed2 = true;
863 }
864 }
865 if (trimmed1) {
866 copy_v4_v4_db(fbc1, t_fbc1);
867 copy_v3_v3_db(gpos1, t_gpos1);
868 }
869 if (trimmed2) {
870 copy_v4_v4_db(fbc2, t_fbc2);
871 copy_v3_v3_db(gpos2, t_gpos2);
872 }
873
874 *r_at_1 = at1;
875 *r_at_2 = at2;
876 copy_v4_v4_db(r_fb_co_1, fbc1);
877 copy_v4_v4_db(r_fb_co_2, fbc2);
878 copy_v3_v3_db(r_gloc_1, gpos1);
879 copy_v3_v3_db(r_gloc_2, gpos2);
880
881 double camera_vector[3];
882
883 if (is_persp) {
884 sub_v3_v3v3_db(camera_vector, ld->conf.camera_pos, tri->v[0]->gloc);
885 }
886 else {
887 copy_v3_v3_db(camera_vector, ld->conf.view_vector);
888 }
889
890 double dot_f = dot_v3v3_db(camera_vector, tri->gn);
891 *r_facing_light = (dot_f < 0);
892
893 return true;
894}
895
896/* The one step all to cast all visible edges in light camera back to other geometries behind them,
897 * the result of this step can then be generated as actual LineartEdge's for occlusion test in view
898 * camera. */
899static void lineart_shadow_cast(LineartData *ld, bool transform_edge_cuts, bool do_light_contour)
900{
901
902 lineart_shadow_create_shadow_edge_array(ld, transform_edge_cuts, do_light_contour);
903
904 /* Keep it single threaded for now because the loop will write "done" pointers to triangles. */
905 for (int edge_i = 0; edge_i < ld->shadow_edges_count; edge_i++) {
906 LineartShadowEdge *sedge = &ld->shadow_edges[edge_i];
907
909 double at_1, at_2;
910 double fb_co_1[4], fb_co_2[4];
911 double global_1[3], global_2[3];
912 bool facing_light;
913
914 LRT_EDGE_BA_MARCHING_BEGIN(sedge->fbc1, sedge->fbc2)
915 {
916 for (int i = 0; i < nba->triangle_count; i++) {
917 tri = (LineartTriangleThread *)nba->linked_triangles[i];
918 if (tri->testing_e[0] == (LineartEdge *)sedge || tri->base.mat_occlusion == 0 ||
921 {
922 continue;
923 }
924 tri->testing_e[0] = (LineartEdge *)sedge;
925
927 (LineartTriangle *)tri,
928 sedge,
929 &at_1,
930 &at_2,
931 fb_co_1,
932 fb_co_2,
933 global_1,
934 global_2,
935 &facing_light))
936 {
938 sedge,
939 at_1,
940 at_2,
941 global_1,
942 global_2,
943 fb_co_1,
944 fb_co_2,
945 facing_light,
946 tri->base.target_reference);
947 }
948 }
949 LRT_EDGE_BA_MARCHING_NEXT(sedge->fbc1, sedge->fbc2);
950 }
952 }
953}
954
955/* For each [segment] on a shadow shadow_edge, 1 LineartEdge will be generated with a cast shadow
956 * edge flag (if that segment failed to cast onto anything then it's not generated). The original
957 * shadow shadow_edge is optionally generated as a light contour. */
959 bool do_original_edges,
960 LineartElementLinkNode **r_veln,
961 LineartElementLinkNode **r_eeln)
962{
963 int tot_edges = 0;
964 int tot_orig_edges = 0;
965 for (int i = 0; i < ld->shadow_edges_count; i++) {
966 LineartShadowEdge *sedge = &ld->shadow_edges[i];
968 if (!(sseg->flag & LRT_SHADOW_CASTED)) {
969 continue;
970 }
971 if (!sseg->next) {
972 break;
973 }
974 tot_edges++;
975 }
976 tot_orig_edges++;
977 }
978
979 int edge_alloc = tot_edges + (do_original_edges ? tot_orig_edges : 0);
980
981 if (G.debug_value == 4000) {
982 printf("Line art shadow segments total: %d\n", tot_edges);
983 }
984
985 if (!edge_alloc) {
986 return false;
987 }
988 LineartElementLinkNode *veln = static_cast<LineartElementLinkNode *>(
990 LineartElementLinkNode *eeln = static_cast<LineartElementLinkNode *>(
992 veln->pointer = lineart_mem_acquire(ld->shadow_data_pool, sizeof(LineartVert) * edge_alloc * 2);
993 eeln->pointer = lineart_mem_acquire(ld->shadow_data_pool, sizeof(LineartEdge) * edge_alloc);
994 LineartEdgeSegment *es = static_cast<LineartEdgeSegment *>(
995 lineart_mem_acquire(ld->shadow_data_pool, sizeof(LineartEdgeSegment) * edge_alloc));
996 *r_veln = veln;
997 *r_eeln = eeln;
998
999 veln->element_count = edge_alloc * 2;
1000 eeln->element_count = edge_alloc;
1001
1002 LineartVert *vlist = static_cast<LineartVert *>(veln->pointer);
1003 LineartEdge *elist = static_cast<LineartEdge *>(eeln->pointer);
1004
1005 int ei = 0;
1006 for (int i = 0; i < ld->shadow_edges_count; i++) {
1007 LineartShadowEdge *sedge = &ld->shadow_edges[i];
1009 if (!(sseg->flag & LRT_SHADOW_CASTED)) {
1010 continue;
1011 }
1012 if (!sseg->next) {
1013 break;
1014 }
1015 LineartEdge *e = &elist[ei];
1016 BLI_addtail(&e->segments, &es[ei]);
1017 LineartVert *v1 = &vlist[ei * 2], *v2 = &vlist[ei * 2 + 1];
1018 copy_v3_v3_db(v1->gloc, sseg->g2);
1019 copy_v3_v3_db(v2->gloc, (sseg->next)->g1);
1020 e->v1 = v1;
1021 e->v2 = v2;
1022 e->t1 = (LineartTriangle *)sedge->e_ref; /* See LineartEdge::t1 for usage. */
1023 e->t2 = (LineartTriangle *)(sedge->e_ref_light_contour ? sedge->e_ref_light_contour :
1024 sedge->e_ref);
1025 e->target_reference = sseg->target_reference;
1026 e->edge_identifier = sedge->e_ref->edge_identifier;
1028 ((sseg->flag & LRT_SHADOW_FACING_LIGHT) ?
1030 0));
1031 ei++;
1032 }
1033 if (do_original_edges) {
1034 /* Occlusion-corrected light contour. */
1035 LineartEdge *e = &elist[ei];
1036 BLI_addtail(&e->segments, &es[ei]);
1037 LineartVert *v1 = &vlist[ei * 2], *v2 = &vlist[ei * 2 + 1];
1038 v1->index = sedge->e_ref->v1->index;
1039 v2->index = sedge->e_ref->v2->index;
1040 copy_v3_v3_db(v1->gloc, sedge->g1);
1041 copy_v3_v3_db(v2->gloc, sedge->g2);
1042 uint64_t ref_1 = sedge->e_ref->t1 ? sedge->e_ref->t1->target_reference : 0;
1043 uint64_t ref_2 = sedge->e_ref->t2 ? sedge->e_ref->t2->target_reference : 0;
1044 e->edge_identifier = sedge->e_ref->edge_identifier;
1045 e->target_reference = ((ref_1 << 32) | ref_2);
1046 e->v1 = v1;
1047 e->v2 = v2;
1048 e->t1 = e->t2 = (LineartTriangle *)sedge->e_ref;
1051 lineart_edge_cut(ld, e, 0.0f, 1.0f, 0, 0, LRT_SHADOW_MASK_SHADED);
1052 }
1053 ei++;
1054 }
1055 }
1056 return true;
1057}
1058
1060{
1061 /* Keeping it single threaded for now because a simple parallel_for could end up getting the same
1062 * #sedge->e_ref in different threads. */
1063 for (int i = 0; i < ld->shadow_edges_count; i++) {
1064 LineartShadowEdge *sedge = &ld->shadow_edges[i];
1065
1066 LineartEdge *e = sedge->e_ref;
1067 LineartEdgeSegment *es = sedge->es_ref;
1068 double es_start = es->ratio, es_end = es->next ? es->next->ratio : 1.0f;
1070 if (!(sseg->flag & LRT_SHADOW_CASTED)) {
1071 continue;
1072 }
1073 if (!sseg->next) {
1074 break;
1075 }
1076
1077 uint32_t silhouette_flags = (sseg->target_reference & LRT_OBINDEX_HIGHER) |
1079
1080 double at_start = interpd(es_end, es_start, sseg->ratio);
1081 double at_end = interpd(es_end, es_start, sseg->next->ratio);
1082 lineart_edge_cut(ld, e, at_start, at_end, 0, 0, silhouette_flags);
1083 }
1084 }
1085}
1086
1087/* To achieve enclosed shape effect, we need to:
1088 * 1) Show shaded segments against lit background.
1089 * 2) Erase lit segments against lit background. */
1091{
1092 LineartEdge *e;
1093 for (int i = 0; i < shadow_ld->pending_edges.next; i++) {
1094 e = shadow_ld->pending_edges.array[i];
1095
1096 /* Only care about shade-on-light and light-on-light situations, hence we only need
1097 * non-occluded segments in shadow buffer. */
1098 if (e->min_occ > 0) {
1099 continue;
1100 }
1101 LISTBASE_FOREACH (LineartEdgeSegment *, es, &e->segments) {
1102 if (es->occlusion > 0) {
1103 continue;
1104 }
1105 double next_at = es->next ? (es->next)->ratio : 1.0f;
1106 LineartEdge *orig_e = (LineartEdge *)e->t2;
1107
1108 /* Shadow view space to global. */
1109 double ga1 = e->v1->fbcoord[3] * es->ratio /
1110 (es->ratio * e->v1->fbcoord[3] + (1 - es->ratio) * e->v2->fbcoord[3]);
1111 double ga2 = e->v1->fbcoord[3] * next_at /
1112 (next_at * e->v1->fbcoord[3] + (1 - next_at) * e->v2->fbcoord[3]);
1113 double g1[3], g2[3], g1v[4], g2v[4];
1114 interp_v3_v3v3_db(g1, e->v1->gloc, e->v2->gloc, ga1);
1115 interp_v3_v3v3_db(g2, e->v1->gloc, e->v2->gloc, ga2);
1116 mul_v4_m4v3_db(g1v, ld->conf.view_projection, g1);
1117 mul_v4_m4v3_db(g2v, ld->conf.view_projection, g2);
1118
1119 if (ld->conf.cam_is_persp) {
1120 mul_v3db_db(g1v, (1 / g1v[3]));
1121 mul_v3db_db(g2v, (1 / g2v[3]));
1122 }
1123
1124 g1v[0] -= ld->conf.shift_x * 2;
1125 g1v[1] -= ld->conf.shift_y * 2;
1126 g2v[0] -= ld->conf.shift_x * 2;
1127 g2v[1] -= ld->conf.shift_y * 2;
1128
1129#define GET_RATIO(n) \
1130 (fabs(orig_e->v2->fbcoord[0] - orig_e->v1->fbcoord[0]) > \
1131 fabs(orig_e->v2->fbcoord[1] - orig_e->v1->fbcoord[1])) ? \
1132 ((g##n##v[0] - orig_e->v1->fbcoord[0]) / \
1133 (orig_e->v2->fbcoord[0] - orig_e->v1->fbcoord[0])) : \
1134 ((g##n##v[1] - orig_e->v1->fbcoord[1]) / (orig_e->v2->fbcoord[1] - orig_e->v1->fbcoord[1]))
1135 double la1, la2;
1136 la1 = GET_RATIO(1);
1137 la2 = GET_RATIO(2);
1138#undef GET_RATIO
1139
1140 lineart_edge_cut(ld, orig_e, la1, la2, 0, 0, LRT_SHADOW_MASK_ENCLOSED_SHAPE);
1141 }
1142 }
1143}
1144
1146 Scene *scene,
1147 LineartData *original_ld,
1149 LineartStaticMemPool *shadow_data_pool,
1150 LineartElementLinkNode **r_veln,
1151 LineartElementLinkNode **r_eeln,
1152 ListBase *r_calculated_edges_eln_list,
1153 LineartData **r_shadow_ld_if_reproject)
1154{
1155 if ((!original_ld->conf.use_shadow && !original_ld->conf.use_light_contour &&
1156 !original_ld->conf.shadow_selection) ||
1157 (!lmd->light_contour_object))
1158 {
1159 return false;
1160 }
1161
1162 double t_start;
1163 if (G.debug_value == 4000) {
1164 t_start = BLI_time_now_seconds();
1165 }
1166
1167 bool is_persp = true;
1168
1169 if (lmd->light_contour_object->type == OB_LAMP) {
1170 Light *la = (Light *)lmd->light_contour_object->data;
1171 if (la->type == LA_SUN) {
1172 is_persp = false;
1173 }
1174 }
1175
1176 LineartData *ld = MEM_mallocN<LineartData>("LineArt render buffer copied");
1177 memcpy(ld, original_ld, sizeof(LineartData));
1178
1182
1183 ld->conf.do_shadow_cast = true;
1184 ld->shadow_data_pool = shadow_data_pool;
1185
1186 /* See LineartData::edge_data_pool for explanation. */
1187 if (ld->conf.shadow_selection) {
1188 ld->edge_data_pool = shadow_data_pool;
1189 }
1190 else {
1192 }
1193
1196
1197 copy_m4_m4(ld->conf.cam_obmat, lmd->light_contour_object->object_to_world().ptr());
1200 ld->conf.cam_is_persp = is_persp;
1201 ld->conf.near_clip = is_persp ? lmd->shadow_camera_near : -lmd->shadow_camera_far;
1202 ld->conf.far_clip = lmd->shadow_camera_far;
1203 ld->w = lmd->shadow_camera_size;
1204 ld->h = lmd->shadow_camera_size;
1205 /* Need to prevent wrong camera configuration so that shadow computation won't stall. */
1206 if (!ld->w || !ld->h) {
1207 ld->w = ld->h = 200;
1208 }
1209 if (!ld->conf.near_clip || !ld->conf.far_clip) {
1210 ld->conf.near_clip = 0.1f;
1211 ld->conf.far_clip = 200.0f;
1212 }
1214
1215 /* Contour and loose edge from light viewing direction will be cast as shadow, so only
1216 * force them on. If we need lit/shaded information for other line types, they are then
1217 * enabled as-is so that cutting positions can also be calculated through shadow projection.
1218 */
1219 if (!ld->conf.shadow_selection) {
1221 ld->conf.use_intersections = ld->conf.use_light_contour = false;
1222 }
1223 else {
1224 ld->conf.use_contour_secondary = true;
1225 ld->conf.allow_duplicated_types = true;
1226 }
1227 ld->conf.use_loose = true;
1228 ld->conf.use_contour = true;
1229
1230 ld->conf.max_occlusion_level = 0; /* No point getting see-through projections there. */
1231 ld->conf.use_back_face_culling = false;
1232
1233 /* Override matrices to light "camera". */
1234 double proj[4][4], view[4][4], result[4][4];
1235 float inv[4][4];
1236 if (is_persp) {
1238 }
1239 else {
1241 proj, -ld->w, ld->w, -ld->h, ld->h, ld->conf.near_clip, ld->conf.far_clip);
1242 }
1243 invert_m4_m4(inv, ld->conf.cam_obmat);
1244 mul_m4db_m4db_m4fl(result, proj, inv);
1245 copy_m4_m4_db(proj, result);
1249
1251
1252 LineartModifierRuntime *runtime = reinterpret_cast<LineartModifierRuntime *>(lmd->runtime);
1253 blender::Set<const Object *> *included_objects = runtime ? &runtime->object_dependencies :
1254 nullptr;
1255
1257 scene,
1258 nullptr,
1259 ld,
1261 true,
1262 nullptr,
1263 included_objects);
1264
1266 /* No geometry loaded, return early. */
1268 MEM_freeN(ld);
1269 return false;
1270 }
1271
1272 /* The exact same process as in MOD_lineart_compute_feature_lines() until occlusion finishes.
1273 */
1274
1276 lineart_main_cull_triangles(ld, false);
1285
1286 /* Do shadow cast stuff then get generated vert/edge data. */
1287 lineart_shadow_cast(ld, true, false);
1288 bool any_generated = lineart_shadow_cast_generate_edges(ld, true, r_veln, r_eeln);
1289
1290 if (ld->conf.shadow_selection) {
1291 memcpy(r_calculated_edges_eln_list, &ld->geom.line_buffer_pointers, sizeof(ListBase));
1292 }
1293
1294 if (ld->conf.shadow_enclose_shapes) {
1295 /* Need loaded data for re-projecting the 3rd time to get shape boundary against lit/shaded
1296 * region. */
1297 (*r_shadow_ld_if_reproject) = ld;
1298 }
1299 else {
1301 MEM_freeN(ld);
1302 }
1303
1304 if (G.debug_value == 4000) {
1305 double t_elapsed = BLI_time_now_seconds() - t_start;
1306 printf("Line art shadow stage 1 time: %f\n", t_elapsed);
1307 }
1308
1309 return any_generated;
1310}
1311
1313 Scene *scene,
1314 LineartData *original_ld,
1315 LineartGpencilModifierData *lmd_legacy,
1316 LineartStaticMemPool *shadow_data_pool,
1317 LineartElementLinkNode **r_veln,
1318 LineartElementLinkNode **r_eeln,
1319 ListBase *r_calculated_edges_eln_list,
1320 LineartData **r_shadow_ld_if_reproject)
1321{
1322 bool ret = false;
1326 scene,
1327 original_ld,
1328 &lmd,
1329 shadow_data_pool,
1330 r_veln,
1331 r_eeln,
1332 r_calculated_edges_eln_list,
1333 r_shadow_ld_if_reproject);
1335 return ret;
1336}
1337
1343
1344static void lineart_shadow_transform_task(void *__restrict userdata,
1345 const int element_index,
1346 const TaskParallelTLS *__restrict /*tls*/)
1347{
1349 LineartData *ld = data->ld;
1350 LineartVert *v = &data->v[element_index];
1351 mul_v4_m4v3_db(v->fbcoord, ld->conf.view_projection, v->gloc);
1352}
1353
1354static void lineart_shadow_finalize_shadow_edges_task(void *__restrict userdata,
1355 const int i,
1356 const TaskParallelTLS *__restrict /*tls*/)
1357{
1359 LineartData *ld = data->ld;
1360 LineartEdge *e = data->e;
1361
1364 &ld->geom.vertex_buffer_pointers, e[i].edge_identifier & LRT_OBINDEX_HIGHER);
1365 if (eln) {
1366 int v1i = (((e[i].edge_identifier) >> 32) & LRT_OBINDEX_LOWER);
1367 int v2i = (e[i].edge_identifier & LRT_OBINDEX_LOWER);
1368 LineartVert *v = (LineartVert *)eln->pointer;
1369 /* If the global position is close enough, use the original vertex to prevent flickering
1370 * caused by very slim boundary condition in point_triangle_relation(). */
1371 if (LRT_CLOSE_LOOSER_v3(e[i].v1->gloc, v[v1i].gloc)) {
1372 e[i].v1 = &v[v1i];
1373 }
1374 if (LRT_CLOSE_LOOSER_v3(e[i].v2->gloc, v[v2i].gloc)) {
1375 e[i].v2 = &v[v2i];
1376 }
1377 }
1378 }
1379}
1380
1384{
1385
1386 TaskParallelSettings transform_settings;
1387 BLI_parallel_range_settings_defaults(&transform_settings);
1388 /* Set the minimum amount of edges a thread has to process. */
1389 transform_settings.min_iter_per_thread = 8192;
1390
1391 LineartShadowFinalizeData data = {nullptr};
1392 data.ld = ld;
1393 data.v = (LineartVert *)veln->pointer;
1394 data.e = (LineartEdge *)eeln->pointer;
1395
1397 0, veln->element_count, &data, lineart_shadow_transform_task, &transform_settings);
1399 eeln->element_count,
1400 &data,
1402 &transform_settings);
1403 for (int i = 0; i < eeln->element_count; i++) {
1405 }
1406
1409}
1410
1412{
1413 double t_start;
1414 if (G.debug_value == 4000) {
1415 t_start = BLI_time_now_seconds();
1416 }
1417
1418 if (shadow_ld || ld->conf.shadow_use_silhouette) {
1419 lineart_shadow_cast(ld, false, shadow_ld ? true : false);
1420 if (ld->conf.shadow_use_silhouette) {
1422 }
1423 }
1424
1425 if (G.debug_value == 4000) {
1426 double t_elapsed = BLI_time_now_seconds() - t_start;
1427 printf("Line art shadow stage 2 cast and silhouette time: %f\n", t_elapsed);
1428 }
1429
1430 if (!shadow_ld) {
1431 return;
1432 }
1433
1435
1436 if (shadow_ld->pending_edges.array) {
1437 MEM_freeN(shadow_ld->pending_edges.array);
1438 shadow_ld->pending_edges.array = nullptr;
1439 shadow_ld->pending_edges.next = shadow_ld->pending_edges.max = 0;
1440 }
1441
1442 LineartElementLinkNode *shadow_veln, *shadow_eeln;
1443
1444 bool any_generated = lineart_shadow_cast_generate_edges(ld, false, &shadow_veln, &shadow_eeln);
1445
1446 if (!any_generated) {
1447 return;
1448 }
1449
1450 LineartVert *v = static_cast<LineartVert *>(shadow_veln->pointer);
1451 for (int i = 0; i < shadow_veln->element_count; i++) {
1452 mul_v4_m4v3_db(v[i].fbcoord, shadow_ld->conf.view_projection, v[i].gloc);
1453 if (shadow_ld->conf.cam_is_persp) {
1454 mul_v3db_db(v[i].fbcoord, (1 / v[i].fbcoord[3]));
1455 }
1456 }
1457
1459 shadow_eeln->element_count);
1460
1461 LineartEdge *se = static_cast<LineartEdge *>(shadow_eeln->pointer);
1462 for (int i = 0; i < shadow_eeln->element_count; i++) {
1463 lineart_add_edge_to_array(&shadow_ld->pending_edges, &se[i]);
1464 }
1465
1466 shadow_ld->scheduled_count = 0;
1467
1469 lineart_main_link_lines(shadow_ld);
1471
1473
1474 if (G.debug_value == 4000) {
1475 double t_elapsed = BLI_time_now_seconds() - t_start;
1476 printf("Line art shadow stage 2 total time: %f\n", t_elapsed);
1477 }
1478}
Low-level operations for grease pencil.
General operations, lookup, etc. for blender objects.
#define LISTBASE_FOREACH(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void BLI_insertlinkafter(ListBase *listbase, void *vprevlink, void *vnewlink) ATTR_NONNULL(1)
Definition listbase.cc:332
void * BLI_pophead(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:252
void BLI_insertlinkbefore(ListBase *listbase, void *vnextlink, void *vnewlink) ATTR_NONNULL(1)
Definition listbase.cc:371
MINLINE double ratiod(double min, double max, double pos)
MINLINE float interpf(float target, float origin, float t)
MINLINE double interpd(double target, double origin, double t)
#define DEG2RAD(_deg)
void mul_v4_m4v3_db(double r[4], 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_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 copy_m4_m4_db(double m1[4][4], const double m2[4][4])
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 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 copy_v3_v3_db(double r[3], const double a[3])
MINLINE void copy_v4_v4_db(double r[4], const double a[4])
MINLINE void sub_v3_v3v3_db(double r[3], const double a[3], const double b[3])
unsigned char uchar
void BLI_task_parallel_range(int start, int stop, void *userdata, TaskParallelRangeFunc func, const TaskParallelSettings *settings)
Definition task_range.cc:99
BLI_INLINE void BLI_parallel_range_settings_defaults(TaskParallelSettings *settings)
Definition BLI_task.h:221
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
Platform independent time functions.
double BLI_time_now_seconds(void)
Definition time.cc:113
#define UNLIKELY(x)
@ 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_CONTOUR_SECONDARY
@ MOD_LINEART_EDGE_FLAG_LOOSE
@ MOD_LINEART_ALLOW_DUPLI_OBJECTS
@ OB_LAMP
static AppView * view
Read Guarded memory(de)allocation.
#define LRT_DOUBLE_CLOSE_ENOUGH(a, b)
#define LRT_OBINDEX_LOWER
#define LRT_SHADOW_MASK_ENCLOSED_SHAPE
#define LRT_OBINDEX_HIGHER
@ LRT_TILE_RECURSIVE_PERSPECTIVE
@ LRT_TILE_RECURSIVE_ORTHO
#define LRT_SHADOW_SILHOUETTE_ERASED_GROUP
#define LRT_SHADOW_MASK_ILLUMINATED
#define LRT_CLOSE_LOOSER_v3(a, b)
#define LRT_SHADOW_MASK_SHADED
@ LRT_ELEMENT_INTERSECTION_DATA
BLI_INLINE int lineart_line_isec_2d_ignore_line2pos(const double a1[2], const double a2[2], const double b1[2], const double b2[2], double *r_a_ratio)
@ LRT_SHADOW_CASTED
@ LRT_SHADOW_FACING_LIGHT
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
BPy_StructRNA * depsgraph
unsigned long long int uint64_t
#define printf(...)
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)
void lineart_main_occlusion_begin(LineartData *ld)
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)
bool lineart_edge_from_triangle(const LineartTriangle *tri, const LineartEdge *e, bool allow_overlapping_edges)
void lineart_main_bounding_areas_connect_post(LineartData *ld)
void lineart_main_add_triangles(LineartData *ld)
void lineart_add_edge_to_array(LineartPendingEdges *pe, LineartEdge *e)
void lineart_main_clear_linked_edges(LineartData *ld)
void lineart_main_cull_triangles(LineartData *ld, bool clip_far)
void lineart_main_bounding_area_make_initial(LineartData *ld)
void lineart_main_free_adjacent_data(LineartData *ld)
void lineart_destroy_render_data_keep_init(LineartData *ld)
void lineart_edge_cut(LineartData *ld, LineartEdge *e, double start, double end, uchar material_mask_bits, uchar mat_occlusion, uint32_t shadow_bits)
void lineart_main_perspective_division(LineartData *ld)
void lineart_finalize_object_edge_array_reserve(LineartPendingEdges *pe, int count)
#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)
#define LRT_EDGE_BA_MARCHING_END
#define LRT_ITER_ALL_LINES_END
void lineart_matrix_perspective_44d(double(*mProjection)[4], double fFov_rad, double fAspect, double zMin, double zMax)
void * lineart_mem_acquire_thread(struct LineartStaticMemPool *smp, size_t size)
#define LRT_ITER_ALL_LINES_BEGIN
void lineart_matrix_ortho_44d(double(*mProjection)[4], double xMin, double xMax, double yMin, double yMax, double zMin, double zMax)
static bool lineart_contour_viewed_from_dark_side(LineartData *ld, LineartEdge *e)
LineartElementLinkNode * lineart_find_matching_eln(ListBase *shadow_elns, int obindex)
static void lineart_shadow_register_enclosed_shapes(LineartData *ld, LineartData *shadow_ld)
static bool lineart_shadow_cast_generate_edges(LineartData *ld, bool do_original_edges, LineartElementLinkNode **r_veln, LineartElementLinkNode **r_eeln)
bool lineart_main_try_generate_shadow(Depsgraph *depsgraph, Scene *scene, LineartData *original_ld, LineartGpencilModifierData *lmd_legacy, LineartStaticMemPool *shadow_data_pool, LineartElementLinkNode **r_veln, LineartElementLinkNode **r_eeln, ListBase *r_calculated_edges_eln_list, LineartData **r_shadow_ld_if_reproject)
static void lineart_shadow_segment_slice_get(double *fb_co_1, double *fb_co_2, double *gloc_1, double *gloc_2, double ratio, double at_1, double at_2, double *r_fb_co, double *r_gloc)
static void lineart_shadow_cast(LineartData *ld, bool transform_edge_cuts, bool do_light_contour)
LineartEdge * lineart_find_matching_edge(LineartElementLinkNode *shadow_eln, uint64_t edge_identifier)
static void lineart_shadow_register_silhouette(LineartData *ld)
static void lineart_shadow_create_shadow_edge_array(LineartData *ld, bool transform_edge_cuts, bool do_light_contour)
bool lineart_main_try_generate_shadow_v3(Depsgraph *depsgraph, Scene *scene, LineartData *original_ld, GreasePencilLineartModifierData *lmd, LineartStaticMemPool *shadow_data_pool, LineartElementLinkNode **r_veln, LineartElementLinkNode **r_eeln, ListBase *r_calculated_edges_eln_list, LineartData **r_shadow_ld_if_reproject)
void lineart_register_intersection_shadow_cuts(LineartData *ld, ListBase *shadow_elns)
static LineartShadowSegment * lineart_give_shadow_segment(LineartData *ld)
static void lineart_shadow_transform_task(void *__restrict userdata, const int element_index, const TaskParallelTLS *__restrict)
static bool lineart_shadow_cast_onto_triangle(LineartData *ld, LineartTriangle *tri, LineartShadowEdge *sedge, double *r_at_1, double *r_at_2, double *r_fb_co_1, double *r_fb_co_2, double *r_gloc_1, double *r_gloc_2, bool *r_facing_light)
#define GET_RATIO(n)
static void lineart_shadow_edge_cut(LineartData *ld, LineartShadowEdge *e, double start, double end, double *start_gloc, double *end_gloc, double *start_fb_co, double *end_fb_co, bool facing_light, uint32_t target_reference)
void lineart_main_make_enclosed_shapes(LineartData *ld, LineartData *shadow_ld)
static void lineart_shadow_finalize_shadow_edges_task(void *__restrict userdata, const int i, const TaskParallelTLS *__restrict)
static bool lineart_do_closest_segment(bool is_persp, double *s1_fb_co_1, double *s1_fb_co_2, double *s2_fb_co_1, double *s2_fb_co_2, double *s1_gloc_1, double *s1_gloc_2, double *s2_gloc_1, double *s2_gloc_2, double *r_fb_co_1, double *r_fb_co_2, double *r_gloc_1, double *r_gloc_2, double *r_new_in_the_middle, double *r_new_in_the_middle_global, double *r_new_at, bool *is_side_2r, bool *use_new_ref)
#define DISCARD_NONSENSE_SEGMENTS
void lineart_register_shadow_cuts(LineartData *ld, LineartEdge *e, LineartEdge *shadow_edge)
void lineart_main_transform_and_add_shadow(LineartData *ld, LineartElementLinkNode *veln, LineartElementLinkNode *eeln)
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
ccl_device_inline float2 fabs(const float2 a)
#define G(x, y, z)
void lineart_unwrap_v3(LineartGpencilModifierData *lmd_legacy, const GreasePencilLineartModifierData *lmd)
void lineart_wrap_v3(const LineartGpencilModifierData *lmd_legacy, GreasePencilLineartModifierData *lmd)
VecBase< double, 3 > double3
VecBase< double, 4 > double4
return ret
struct LineartModifierRuntime * runtime
short type
float cam_obmat_secondary[4][4]
double view_projection[4][4]
double view_vector_secondary[3]
double camera_pos_secondary[3]
double view[4][4]
float cam_obmat[4][4]
ListBase vertex_buffer_pointers
ListBase line_buffer_pointers
LineartShadowEdge * shadow_edges
ListBase wasted_shadow_cuts
LineartStaticMemPool render_data_pool
struct LineartData::_conf conf
struct LineartData::_geom geom
struct LineartData::_qtree qtree
SpinLock lock_task
LineartStaticMemPool * edge_data_pool
SpinLock lock_cuts
struct LineartPendingEdges pending_edges
int shadow_edges_count
LineartStaticMemPool * shadow_data_pool
LineartEdgeSegment * next
LineartVert * v1
LineartVert * v2
ListBase segments
LineartTriangle * t2
uint64_t edge_identifier
LineartTriangle * t1
blender::Set< const Object * > object_dependencies
LineartEdge ** array
LineartEdge * e_ref_light_contour
ListBase shadow_segments
LineartEdgeSegment * es_ref
LineartEdge * e_ref
LineartShadowSegment * next
LineartEdge * testing_e[1]
LineartTriangle base
LineartVert * v[3]
uint32_t target_reference
uint8_t mat_occlusion
double fbcoord[4]
double gloc[3]
void * first
i
Definition text_draw.cc:230