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