diff options
Diffstat (limited to 'source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c')
-rw-r--r-- | source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c | 3027 |
1 files changed, 1805 insertions, 1222 deletions
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index 016b70cedb0..874da3b88c9 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -39,6 +39,7 @@ #include "DNA_camera_types.h" #include "DNA_collection_types.h" #include "DNA_gpencil_types.h" +#include "DNA_light_types.h" #include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -46,105 +47,117 @@ #include "DNA_scene_types.h" #include "MEM_guardedalloc.h" -#include "bmesh.h" -#include "bmesh_class.h" -#include "bmesh_tools.h" - #include "lineart_intern.h" -static LineartBoundingArea *lineart_edge_first_bounding_area(LineartRenderBuffer *rb, - LineartEdge *e); - -static void lineart_bounding_area_link_edge(LineartRenderBuffer *rb, +typedef struct LineartIsecSingle { + float v1[3], v2[3]; + LineartTriangle *tri1, *tri2; +} LineartIsecSingle; + +typedef struct LineartIsecThread { + int thread_id; + + /* Scheduled work range. */ + LineartElementLinkNode *pending_from; + LineartElementLinkNode *pending_to; + int index_from; + int index_to; + + /* Thread intersection result data. */ + LineartIsecSingle *array; + int current; + int max; + int count_test; + + /* For individual thread reference.*/ + LineartData *ld; +} LineartIsecThread; + +typedef struct LineartIsecData { + LineartData *ld; + LineartIsecThread *threads; + int thread_count; +} LineartIsecData; + +static void lineart_bounding_area_link_edge(LineartData *ld, LineartBoundingArea *root_ba, LineartEdge *e); -static LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this, - LineartEdge *e, - double x, - double y, - double k, - int positive_x, - int positive_y, - double *next_x, - double *next_y); - -static bool lineart_get_edge_bounding_areas(LineartRenderBuffer *rb, - LineartEdge *e, - int *rowbegin, - int *rowend, - int *colbegin, - int *colend); - -static void lineart_bounding_area_link_triangle(LineartRenderBuffer *rb, - LineartBoundingArea *root_ba, - LineartTriangle *tri, - double *LRUB, - int recursive, - int recursive_level, - bool do_intersection); +static bool lineart_get_edge_bounding_areas( + LineartData *ld, LineartEdge *e, int *rowbegin, int *rowend, int *colbegin, int *colend); -static bool lineart_triangle_edge_image_space_occlusion(SpinLock *spl, - const LineartTriangle *tri, +static bool lineart_triangle_edge_image_space_occlusion(const LineartTriangle *tri, const LineartEdge *e, const double *override_camera_loc, const bool override_cam_is_persp, const bool allow_overlapping_edges, - const double vp[4][4], - const double *camera_dir, + const double m_view_projection[4][4], + const double camera_dir[3], const float cam_shift_x, const float cam_shift_y, double *from, double *to); -static void lineart_add_edge_to_array(LineartPendingEdges *pe, LineartEdge *e); +static void lineart_bounding_area_link_triangle(LineartData *ld, + LineartBoundingArea *root_ba, + LineartTriangle *tri, + double l_r_u_b[4], + int recursive, + int recursive_level, + bool do_intersection, + struct LineartIsecThread *th); + +static void lineart_free_bounding_area_memory(LineartBoundingArea *ba, bool recursive); + +static void lineart_free_bounding_area_memories(LineartData *ld); static LineartCache *lineart_init_cache(void); -static void lineart_discard_segment(LineartRenderBuffer *rb, LineartEdgeSegment *es) +static void lineart_discard_segment(LineartData *ld, LineartEdgeSegment *es) { - BLI_spin_lock(&rb->lock_cuts); + BLI_spin_lock(&ld->lock_cuts); memset(es, 0, sizeof(LineartEdgeSegment)); /* Storing the node for potentially reuse the memory for new segment data. * Line Art data is not freed after all calculations are done. */ - BLI_addtail(&rb->wasted_cuts, es); + BLI_addtail(&ld->wasted_cuts, es); - BLI_spin_unlock(&rb->lock_cuts); + BLI_spin_unlock(&ld->lock_cuts); } -static LineartEdgeSegment *lineart_give_segment(LineartRenderBuffer *rb) +static LineartEdgeSegment *lineart_give_segment(LineartData *ld) { - BLI_spin_lock(&rb->lock_cuts); + BLI_spin_lock(&ld->lock_cuts); /* See if there is any already allocated memory we can reuse. */ - if (rb->wasted_cuts.first) { - LineartEdgeSegment *es = (LineartEdgeSegment *)BLI_pophead(&rb->wasted_cuts); - BLI_spin_unlock(&rb->lock_cuts); + if (ld->wasted_cuts.first) { + LineartEdgeSegment *es = (LineartEdgeSegment *)BLI_pophead(&ld->wasted_cuts); + BLI_spin_unlock(&ld->lock_cuts); memset(es, 0, sizeof(LineartEdgeSegment)); return es; } - BLI_spin_unlock(&rb->lock_cuts); + BLI_spin_unlock(&ld->lock_cuts); /* Otherwise allocate some new memory. */ - return (LineartEdgeSegment *)lineart_mem_acquire_thread(&rb->render_data_pool, + return (LineartEdgeSegment *)lineart_mem_acquire_thread(ld->edge_data_pool, sizeof(LineartEdgeSegment)); } /** * Cuts the edge in image space and mark occlusion level for each segment. */ -static void lineart_edge_cut(LineartRenderBuffer *rb, - LineartEdge *e, - double start, - double end, - uchar material_mask_bits, - uchar mat_occlusion) +void lineart_edge_cut(LineartData *ld, + LineartEdge *e, + double start, + double end, + uchar material_mask_bits, + uchar mat_occlusion, + uint32_t shadow_bits) { - LineartEdgeSegment *es, *ies, *next_es, *prev_es; + LineartEdgeSegment *seg, *i_seg, *next_seg, *prev_seg; LineartEdgeSegment *cut_start_before = 0, *cut_end_before = 0; - LineartEdgeSegment *ns = 0, *ns2 = 0; + LineartEdgeSegment *new_seg1 = 0, *new_seg2 = 0; int untouched = 0; /* If for some reason the occlusion function may give a result that has zero length, or reversed @@ -171,132 +184,159 @@ static void lineart_edge_cut(LineartRenderBuffer *rb, /* Begin looking for starting position of the segment. */ /* Not using a list iteration macro because of it more clear when using for loops to iterate * through the segments. */ - for (es = e->segments.first; es; es = es->next) { - if (LRT_DOUBLE_CLOSE_ENOUGH(es->at, start)) { - cut_start_before = es; - ns = cut_start_before; + for (seg = e->segments.first; seg; seg = seg->next) { + if (LRT_DOUBLE_CLOSE_ENOUGH(seg->ratio, start)) { + cut_start_before = seg; + new_seg1 = cut_start_before; break; } - if (es->next == NULL) { + if (seg->next == NULL) { break; } - ies = es->next; - if (ies->at > start + 1e-09 && start > es->at) { - cut_start_before = ies; - ns = lineart_give_segment(rb); + i_seg = seg->next; + if (i_seg->ratio > start + 1e-09 && start > seg->ratio) { + cut_start_before = i_seg; + new_seg1 = lineart_give_segment(ld); break; } } if (!cut_start_before && LRT_DOUBLE_CLOSE_ENOUGH(1, end)) { untouched = 1; } - for (es = cut_start_before; es; es = es->next) { - /* We tried to cut at existing cutting point (e.g. where the line's occluded by a triangle + for (seg = cut_start_before; seg; seg = seg->next) { + /* We tried to cut ratio existing cutting point (e.g. where the line's occluded by a triangle * strip). */ - if (LRT_DOUBLE_CLOSE_ENOUGH(es->at, end)) { - cut_end_before = es; - ns2 = cut_end_before; + if (LRT_DOUBLE_CLOSE_ENOUGH(seg->ratio, end)) { + cut_end_before = seg; + new_seg2 = cut_end_before; break; } - /* This check is to prevent `es->at == 1.0` (where we don't need to cut because we are at the - * end point). */ - if (!es->next && LRT_DOUBLE_CLOSE_ENOUGH(1, end)) { - cut_end_before = es; - ns2 = cut_end_before; + /* This check is to prevent `es->ratio == 1.0` (where we don't need to cut because we are ratio + * the end point). */ + if (!seg->next && LRT_DOUBLE_CLOSE_ENOUGH(1, end)) { + cut_end_before = seg; + new_seg2 = cut_end_before; untouched = 1; break; } /* When an actual cut is needed in the line. */ - if (es->at > end) { - cut_end_before = es; - ns2 = lineart_give_segment(rb); + if (seg->ratio > end) { + cut_end_before = seg; + new_seg2 = lineart_give_segment(ld); break; } } /* When we still can't find any existing cut in the line, we allocate new ones. */ - if (ns == NULL) { - ns = lineart_give_segment(rb); + if (new_seg1 == NULL) { + new_seg1 = lineart_give_segment(ld); } - if (ns2 == NULL) { + if (new_seg2 == NULL) { if (untouched) { - ns2 = ns; - cut_end_before = ns2; + new_seg2 = new_seg1; + cut_end_before = new_seg2; } else { - ns2 = lineart_give_segment(rb); + new_seg2 = lineart_give_segment(ld); } } if (cut_start_before) { - if (cut_start_before != ns) { + if (cut_start_before != new_seg1) { /* Insert cutting points for when a new cut is needed. */ - ies = cut_start_before->prev ? cut_start_before->prev : NULL; - ns->occlusion = ies ? ies->occlusion : 0; - ns->material_mask_bits = ies->material_mask_bits; - BLI_insertlinkbefore(&e->segments, cut_start_before, ns); + i_seg = cut_start_before->prev ? cut_start_before->prev : NULL; + if (i_seg) { + new_seg1->occlusion = i_seg->occlusion; + new_seg1->material_mask_bits = i_seg->material_mask_bits; + new_seg1->shadow_mask_bits = i_seg->shadow_mask_bits; + } + BLI_insertlinkbefore(&e->segments, cut_start_before, new_seg1); } /* Otherwise we already found a existing cutting point, no need to insert a new one. */ } else { /* We have yet to reach a existing cutting point even after we searched the whole line, so we * append the new cut to the end. */ - ies = e->segments.last; - ns->occlusion = ies->occlusion; - ns->material_mask_bits = ies->material_mask_bits; - BLI_addtail(&e->segments, ns); + i_seg = e->segments.last; + new_seg1->occlusion = i_seg->occlusion; + new_seg1->material_mask_bits = i_seg->material_mask_bits; + new_seg1->shadow_mask_bits = i_seg->shadow_mask_bits; + BLI_addtail(&e->segments, new_seg1); } if (cut_end_before) { /* The same manipulation as on "cut_start_before". */ - if (cut_end_before != ns2) { - ies = cut_end_before->prev ? cut_end_before->prev : NULL; - ns2->occlusion = ies ? ies->occlusion : 0; - ns2->material_mask_bits = ies ? ies->material_mask_bits : 0; - BLI_insertlinkbefore(&e->segments, cut_end_before, ns2); + if (cut_end_before != new_seg2) { + i_seg = cut_end_before->prev ? cut_end_before->prev : NULL; + if (i_seg) { + new_seg2->occlusion = i_seg->occlusion; + new_seg2->material_mask_bits = i_seg->material_mask_bits; + new_seg2->shadow_mask_bits = i_seg->shadow_mask_bits; + } + BLI_insertlinkbefore(&e->segments, cut_end_before, new_seg2); } } else { - ies = e->segments.last; - ns2->occlusion = ies->occlusion; - ns2->material_mask_bits = ies->material_mask_bits; - BLI_addtail(&e->segments, ns2); + i_seg = e->segments.last; + new_seg2->occlusion = i_seg->occlusion; + new_seg2->material_mask_bits = i_seg->material_mask_bits; + new_seg2->shadow_mask_bits = i_seg->shadow_mask_bits; + if (!untouched) { + BLI_addtail(&e->segments, new_seg2); + } } /* If we touched the cut list, we assign the new cut position based on new cut position, * this way we accommodate precision lost due to multiple cut inserts. */ - ns->at = start; + new_seg1->ratio = start; if (!untouched) { - ns2->at = end; + new_seg2->ratio = end; } else { /* For the convenience of the loop below. */ - ns2 = ns2->next; + new_seg2 = new_seg2->next; } /* Register 1 level of occlusion for all touched segments. */ - for (es = ns; es && es != ns2; es = es->next) { - es->occlusion += mat_occlusion; - es->material_mask_bits |= material_mask_bits; + for (seg = new_seg1; seg && seg != new_seg2; seg = seg->next) { + seg->occlusion += mat_occlusion; + seg->material_mask_bits |= material_mask_bits; + + /* The enclosed shape flag will override regular lit/shaded + * flags. See LineartEdgeSegment::shadow_mask_bits for details. */ + if (shadow_bits == LRT_SHADOW_MASK_ENCLOSED_SHAPE) { + if (seg->shadow_mask_bits & LRT_SHADOW_MASK_LIT || e->flags & LRT_EDGE_FLAG_LIGHT_CONTOUR) { + seg->shadow_mask_bits &= ~LRT_SHADOW_MASK_LIT; + seg->shadow_mask_bits |= LRT_SHADOW_MASK_INHIBITED; + } + else if (seg->shadow_mask_bits & LRT_SHADOW_MASK_SHADED) { + seg->shadow_mask_bits &= ~LRT_SHADOW_MASK_SHADED; + seg->shadow_mask_bits |= LRT_SHADOW_MASK_LIT; + } + } + else { + seg->shadow_mask_bits |= shadow_bits; + } } /* Reduce adjacent cutting points of the same level, which saves memory. */ - char min_occ = 127; - prev_es = NULL; - for (es = e->segments.first; es; es = next_es) { - next_es = es->next; - - if (prev_es && prev_es->occlusion == es->occlusion && - prev_es->material_mask_bits == es->material_mask_bits) { - BLI_remlink(&e->segments, es); + int8_t min_occ = 127; + prev_seg = NULL; + for (seg = e->segments.first; seg; seg = next_seg) { + next_seg = seg->next; + + if (prev_seg && prev_seg->occlusion == seg->occlusion && + prev_seg->material_mask_bits == seg->material_mask_bits && + prev_seg->shadow_mask_bits == seg->shadow_mask_bits) { + BLI_remlink(&e->segments, seg); /* This puts the node back to the render buffer, if more cut happens, these unused nodes get * picked first. */ - lineart_discard_segment(rb, es); + lineart_discard_segment(ld, seg); continue; } - min_occ = MIN2(min_occ, es->occlusion); + min_occ = MIN2(min_occ, seg->occlusion); - prev_es = es; + prev_seg = seg; } e->min_occ = min_occ; } @@ -306,35 +346,18 @@ static void lineart_edge_cut(LineartRenderBuffer *rb, */ BLI_INLINE bool lineart_occlusion_is_adjacent_intersection(LineartEdge *e, LineartTriangle *tri) { - LineartVertIntersection *v1 = (void *)e->v1; - LineartVertIntersection *v2 = (void *)e->v2; - return ((v1->base.flag && v1->intersecting_with == tri) || - (v2->base.flag && v2->intersecting_with == tri)); + return (((e->target_reference & LRT_LIGHT_CONTOUR_TARGET) == tri->target_reference) || + (((e->target_reference >> 32) & LRT_LIGHT_CONTOUR_TARGET) == tri->target_reference)); } -static void lineart_bounding_area_triangle_add(LineartRenderBuffer *rb, - LineartBoundingArea *ba, - LineartTriangle *tri) -{ /* In case of too many triangles concentrating in one point, do not add anymore, these triangles - * will be either narrower than a single pixel, or will still be added into the list of other - * less dense areas. */ - if (ba->triangle_count >= 65535) { - return; - } - if (ba->triangle_count >= ba->max_triangle_count) { - LineartTriangle **new_array = lineart_mem_acquire( - &rb->render_data_pool, sizeof(LineartTriangle *) * ba->max_triangle_count * 2); - memcpy(new_array, ba->linked_triangles, sizeof(LineartTriangle *) * ba->max_triangle_count); - ba->max_triangle_count *= 2; - ba->linked_triangles = new_array; - } - ba->linked_triangles[ba->triangle_count] = tri; - ba->triangle_count++; +static void lineart_bounding_area_triangle_reallocate(LineartBoundingArea *ba) +{ + ba->max_triangle_count *= 2; + ba->linked_triangles = MEM_recallocN(ba->linked_triangles, + sizeof(LineartTriangle *) * ba->max_triangle_count); } -static void lineart_bounding_area_line_add(LineartRenderBuffer *rb, - LineartBoundingArea *ba, - LineartEdge *e) +static void lineart_bounding_area_line_add(LineartBoundingArea *ba, LineartEdge *e) { /* In case of too many lines concentrating in one point, do not add anymore, these lines will * be either shorter than a single pixel, or will still be added into the list of other less @@ -343,36 +366,23 @@ static void lineart_bounding_area_line_add(LineartRenderBuffer *rb, return; } if (ba->line_count >= ba->max_line_count) { - LineartEdge **new_array = lineart_mem_acquire(&rb->render_data_pool, - sizeof(LineartEdge *) * ba->max_line_count * 2); + LineartEdge **new_array = MEM_mallocN(sizeof(LineartEdge *) * ba->max_line_count * 2, + "new ba_line_array"); memcpy(new_array, ba->linked_lines, sizeof(LineartEdge *) * ba->max_line_count); ba->max_line_count *= 2; + MEM_freeN(ba->linked_lines); ba->linked_lines = new_array; } ba->linked_lines[ba->line_count] = e; ba->line_count++; } -static void lineart_occlusion_single_line(LineartRenderBuffer *rb, LineartEdge *e, int thread_id) +static void lineart_occlusion_single_line(LineartData *ld, LineartEdge *e, int thread_id) { - double x = e->v1->fbcoord[0], y = e->v1->fbcoord[1]; - LineartBoundingArea *ba = lineart_edge_first_bounding_area(rb, e); - LineartBoundingArea *nba = ba; LineartTriangleThread *tri; - - /* These values are used for marching along the line. */ double l, r; - double k = (e->v2->fbcoord[1] - e->v1->fbcoord[1]) / - (e->v2->fbcoord[0] - e->v1->fbcoord[0] + 1e-30); - int positive_x = (e->v2->fbcoord[0] - e->v1->fbcoord[0]) > 0 ? - 1 : - (e->v2->fbcoord[0] == e->v1->fbcoord[0] ? 0 : -1); - int positive_y = (e->v2->fbcoord[1] - e->v1->fbcoord[1]) > 0 ? - 1 : - (e->v2->fbcoord[1] == e->v1->fbcoord[1] ? 0 : -1); - - while (nba) { - + LRT_EDGE_BA_MARCHING_BEGIN(e->v1->fbcoord, e->v2->fbcoord) + { for (int i = 0; i < nba->triangle_count; i++) { tri = (LineartTriangleThread *)nba->linked_triangles[i]; /* If we are already testing the line in this thread, then don't do it. */ @@ -385,49 +395,48 @@ static void lineart_occlusion_single_line(LineartRenderBuffer *rb, LineartEdge * continue; } tri->testing_e[thread_id] = e; - if (lineart_triangle_edge_image_space_occlusion(&rb->lock_task, - (const LineartTriangle *)tri, + if (lineart_triangle_edge_image_space_occlusion((const LineartTriangle *)tri, e, - rb->camera_pos, - rb->cam_is_persp, - rb->allow_overlapping_edges, - rb->view_projection, - rb->view_vector, - rb->shift_x, - rb->shift_y, + ld->conf.camera_pos, + ld->conf.cam_is_persp, + ld->conf.allow_overlapping_edges, + ld->conf.view_projection, + ld->conf.view_vector, + ld->conf.shift_x, + ld->conf.shift_y, &l, &r)) { - lineart_edge_cut(rb, e, l, r, tri->base.material_mask_bits, tri->base.mat_occlusion); - if (e->min_occ > rb->max_occlusion_level) { + lineart_edge_cut(ld, e, l, r, tri->base.material_mask_bits, tri->base.mat_occlusion, 0); + if (e->min_occ > ld->conf.max_occlusion_level) { /* No need to calculate any longer on this line because no level more than set value is * going to show up in the rendered result. */ return; } } } - /* Marching along `e->v1` to `e->v2`, searching each possible bounding areas it may touch. */ - nba = lineart_bounding_area_next(nba, e, x, y, k, positive_x, positive_y, &x, &y); + LRT_EDGE_BA_MARCHING_NEXT(e->v1->fbcoord, e->v2->fbcoord) } + LRT_EDGE_BA_MARCHING_END } -static int lineart_occlusion_make_task_info(LineartRenderBuffer *rb, LineartRenderTaskInfo *rti) +static int lineart_occlusion_make_task_info(LineartData *ld, LineartRenderTaskInfo *rti) { int res = 0; int starting_index; - BLI_spin_lock(&rb->lock_task); + BLI_spin_lock(&ld->lock_task); - starting_index = rb->scheduled_count; - rb->scheduled_count += LRT_THREAD_EDGE_COUNT; + starting_index = ld->scheduled_count; + ld->scheduled_count += LRT_THREAD_EDGE_COUNT; - BLI_spin_unlock(&rb->lock_task); + BLI_spin_unlock(&ld->lock_task); - if (starting_index >= rb->pending_edges.next) { + if (starting_index >= ld->pending_edges.next) { res = 0; } else { - rti->pending_edges.array = &rb->pending_edges.array[starting_index]; - int remaining = rb->pending_edges.next - starting_index; + rti->pending_edges.array = &ld->pending_edges.array[starting_index]; + int remaining = ld->pending_edges.next - starting_index; rti->pending_edges.max = MIN2(remaining, LRT_THREAD_EDGE_COUNT); res = 1; } @@ -437,13 +446,13 @@ static int lineart_occlusion_make_task_info(LineartRenderBuffer *rb, LineartRend static void lineart_occlusion_worker(TaskPool *__restrict UNUSED(pool), LineartRenderTaskInfo *rti) { - LineartRenderBuffer *rb = rti->rb; + LineartData *ld = rti->ld; LineartEdge *eip; - while (lineart_occlusion_make_task_info(rb, rti)) { + while (lineart_occlusion_make_task_info(ld, rti)) { for (int i = 0; i < rti->pending_edges.max; i++) { eip = rti->pending_edges.array[i]; - lineart_occlusion_single_line(rb, eip, rti->thread_id); + lineart_occlusion_single_line(ld, eip, rti->thread_id); } } } @@ -453,9 +462,9 @@ static void lineart_occlusion_worker(TaskPool *__restrict UNUSED(pool), LineartR * #MOD_lineart_compute_feature_lines function. * This function handles all occlusion calculation. */ -static void lineart_main_occlusion_begin(LineartRenderBuffer *rb) +void lineart_main_occlusion_begin(LineartData *ld) { - int thread_count = rb->thread_count; + int thread_count = ld->thread_count; LineartRenderTaskInfo *rti = MEM_callocN(sizeof(LineartRenderTaskInfo) * thread_count, "Task Pool"); int i; @@ -464,7 +473,7 @@ static void lineart_main_occlusion_begin(LineartRenderBuffer *rb) for (i = 0; i < thread_count; i++) { rti[i].thread_id = i; - rti[i].rb = rb; + rti[i].ld = ld; BLI_task_pool_push(tp, (TaskRunFunction)lineart_occlusion_worker, &rti[i], 0, NULL); } BLI_task_pool_work_and_wait(tp); @@ -485,10 +494,10 @@ static bool lineart_point_inside_triangle(const double v[2], const double v1[2], const double v2[2]) { - double cl, c; + double cl, c, cl0; cl = (v0[0] - v[0]) * (v1[1] - v[1]) - (v0[1] - v[1]) * (v1[0] - v[0]); - c = cl; + c = cl0 = cl; cl = (v1[0] - v[0]) * (v2[1] - v[1]) - (v1[1] - v[1]) * (v2[0] - v[0]); if (c * cl <= 0) { @@ -504,8 +513,7 @@ static bool lineart_point_inside_triangle(const double v[2], c = cl; - cl = (v0[0] - v[0]) * (v1[1] - v[1]) - (v0[1] - v[1]) * (v1[0] - v[0]); - if (c * cl <= 0) { + if (c * cl0 <= 0) { return false; } @@ -554,6 +562,12 @@ static int lineart_point_on_line_segment(double v[2], double v0[2], double v1[2] return 0; } +enum LineartPointTri { + LRT_OUTSIDE_TRIANGLE = 0, + LRT_ON_TRIANGLE = 1, + LRT_INSIDE_TRIANGLE = 2, +}; + /** * Same algorithm as lineart_point_inside_triangle(), but returns differently: * 0-outside 1-on the edge 2-inside. @@ -564,7 +578,7 @@ static int lineart_point_triangle_relation(double v[2], double v0[2], double v1[ double r; if (lineart_point_on_line_segment(v, v0, v1) || lineart_point_on_line_segment(v, v1, v2) || lineart_point_on_line_segment(v, v2, v0)) { - return 1; + return LRT_ON_TRIANGLE; } cl = (v0[0] - v[0]) * (v1[1] - v[1]) - (v0[1] - v[1]) * (v1[0] - v[0]); @@ -572,28 +586,28 @@ static int lineart_point_triangle_relation(double v[2], double v0[2], double v1[ cl = (v1[0] - v[0]) * (v2[1] - v[1]) - (v1[1] - v[1]) * (v2[0] - v[0]); if ((r = c * cl) < 0) { - return 0; + return LRT_OUTSIDE_TRIANGLE; } c = cl; cl = (v2[0] - v[0]) * (v0[1] - v[1]) - (v2[1] - v[1]) * (v0[0] - v[0]); if ((r = c * cl) < 0) { - return 0; + return LRT_OUTSIDE_TRIANGLE; } c = cl; cl = (v0[0] - v[0]) * (v1[1] - v[1]) - (v0[1] - v[1]) * (v1[0] - v[0]); if ((r = c * cl) < 0) { - return 0; + return LRT_OUTSIDE_TRIANGLE; } if (r == 0) { - return 1; + return LRT_ON_TRIANGLE; } - return 2; + return LRT_INSIDE_TRIANGLE; } /** @@ -641,17 +655,17 @@ static bool lineart_point_inside_triangle3d(double v[3], double v0[3], double v1 * The following `lineart_memory_get_XXX_space` functions are for allocating new memory for some * modified geometries in the culling stage. */ -static LineartElementLinkNode *lineart_memory_get_triangle_space(LineartRenderBuffer *rb) +static LineartElementLinkNode *lineart_memory_get_triangle_space(LineartData *ld) { LineartElementLinkNode *eln; /* We don't need to allocate a whole bunch of triangles because the amount of clipped triangles * are relatively small. */ - LineartTriangle *render_triangles = lineart_mem_acquire(&rb->render_data_pool, - 64 * rb->triangle_size); + LineartTriangle *render_triangles = lineart_mem_acquire(&ld->render_data_pool, + 64 * ld->sizeof_triangle); - eln = lineart_list_append_pointer_pool_sized(&rb->triangle_buffer_pointers, - &rb->render_data_pool, + eln = lineart_list_append_pointer_pool_sized(&ld->geom.triangle_buffer_pointers, + &ld->render_data_pool, render_triangles, sizeof(LineartElementLinkNode)); eln->element_count = 64; @@ -660,15 +674,15 @@ static LineartElementLinkNode *lineart_memory_get_triangle_space(LineartRenderBu return eln; } -static LineartElementLinkNode *lineart_memory_get_vert_space(LineartRenderBuffer *rb) +static LineartElementLinkNode *lineart_memory_get_vert_space(LineartData *ld) { LineartElementLinkNode *eln; - LineartVert *render_vertices = lineart_mem_acquire(&rb->render_data_pool, + LineartVert *render_vertices = lineart_mem_acquire(&ld->render_data_pool, sizeof(LineartVert) * 64); - eln = lineart_list_append_pointer_pool_sized(&rb->vertex_buffer_pointers, - &rb->render_data_pool, + eln = lineart_list_append_pointer_pool_sized(&ld->geom.vertex_buffer_pointers, + &ld->render_data_pool, render_vertices, sizeof(LineartElementLinkNode)); eln->element_count = 64; @@ -677,18 +691,18 @@ static LineartElementLinkNode *lineart_memory_get_vert_space(LineartRenderBuffer return eln; } -static LineartElementLinkNode *lineart_memory_get_edge_space(LineartRenderBuffer *rb) +static LineartElementLinkNode *lineart_memory_get_edge_space(LineartData *ld) { LineartElementLinkNode *eln; - LineartEdge *render_edges = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdge) * 64); + LineartEdge *render_edges = lineart_mem_acquire(ld->edge_data_pool, sizeof(LineartEdge) * 64); - eln = lineart_list_append_pointer_pool_sized(&rb->line_buffer_pointers, - &rb->render_data_pool, + eln = lineart_list_append_pointer_pool_sized(&ld->geom.line_buffer_pointers, + ld->edge_data_pool, render_edges, sizeof(LineartElementLinkNode)); eln->element_count = 64; - eln->crease_threshold = rb->crease_threshold; + eln->crease_threshold = ld->conf.crease_threshold; eln->flags |= LRT_ELEMENT_IS_ADDITIONAL; return eln; @@ -699,8 +713,11 @@ static void lineart_triangle_post(LineartTriangle *tri, LineartTriangle *orig) /* Just re-assign normal and set cull flag. */ copy_v3_v3_db(tri->gn, orig->gn); tri->flags = LRT_CULL_GENERATED; + tri->intersection_mask = orig->intersection_mask; tri->material_mask_bits = orig->material_mask_bits; tri->mat_occlusion = orig->mat_occlusion; + tri->intersection_priority = orig->intersection_priority; + tri->target_reference = orig->target_reference; } static void lineart_triangle_set_cull_flag(LineartTriangle *tri, uchar flag) @@ -729,15 +746,15 @@ static void lineart_discard_duplicated_edges(LineartEdge *old_e) * Does near-plane cut on 1 triangle only. When cutting with far-plane, the camera vectors gets * reversed by the caller so don't need to implement one in a different direction. */ -static void lineart_triangle_cull_single(LineartRenderBuffer *rb, +static void lineart_triangle_cull_single(LineartData *ld, LineartTriangle *tri, int in0, int in1, int in2, - double *cam_pos, - double *view_dir, + double cam_pos[3], + double view_dir[3], bool allow_boundaries, - double (*vp)[4], + double m_view_projection[4][4], Object *ob, int *r_v_count, int *r_e_count, @@ -746,7 +763,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, LineartElementLinkNode *e_eln, LineartElementLinkNode *t_eln) { - double vv1[3], vv2[3], dot1, dot2; + double span_v1[3], span_v2[3], dot_v1, dot_v2; double a; int v_count = *r_v_count; int e_count = *r_e_count; @@ -755,7 +772,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, LineartEdge *new_e, *e, *old_e; LineartEdgeSegment *es; - LineartTriangleAdjacent *ta; + LineartTriangleAdjacent *tri_adj; if (tri->flags & (LRT_CULL_USED | LRT_CULL_GENERATED | LRT_CULL_DISCARD)) { return; @@ -763,11 +780,12 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, /* See definition of tri->intersecting_verts and the usage in * lineart_geometry_object_load() for details. */ - ta = (void *)tri->intersecting_verts; + tri_adj = (void *)tri->intersecting_verts; LineartVert *vt = &((LineartVert *)v_eln->pointer)[v_count]; - LineartTriangle *tri1 = (void *)(((uchar *)t_eln->pointer) + rb->triangle_size * t_count); - LineartTriangle *tri2 = (void *)(((uchar *)t_eln->pointer) + rb->triangle_size * (t_count + 1)); + LineartTriangle *tri1 = (void *)(((uchar *)t_eln->pointer) + ld->sizeof_triangle * t_count); + LineartTriangle *tri2 = (void *)(((uchar *)t_eln->pointer) + + ld->sizeof_triangle * (t_count + 1)); new_e = &((LineartEdge *)e_eln->pointer)[e_count]; /* Init `edge` to the last `edge` entry. */ @@ -777,12 +795,12 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, new_e = &((LineartEdge *)e_eln->pointer)[e_count]; \ e_count++; \ e = new_e; \ - es = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdgeSegment)); \ + es = lineart_mem_acquire(&ld->render_data_pool, sizeof(LineartEdgeSegment)); \ BLI_addtail(&e->segments, es); #define SELECT_EDGE(e_num, v1_link, v2_link, new_tri) \ - if (ta->e[e_num]) { \ - old_e = ta->e[e_num]; \ + if (tri_adj->e[e_num]) { \ + old_e = tri_adj->e[e_num]; \ new_flag = old_e->flags; \ old_e->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ lineart_discard_duplicated_edges(old_e); \ @@ -795,28 +813,28 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, e->object_ref = ob; \ e->t1 = ((old_e->t1 == tri) ? (new_tri) : (old_e->t1)); \ e->t2 = ((old_e->t2 == tri) ? (new_tri) : (old_e->t2)); \ - lineart_add_edge_to_array(&rb->pending_edges, e); \ + lineart_add_edge_to_array(&ld->pending_edges, e); \ } #define RELINK_EDGE(e_num, new_tri) \ - if (ta->e[e_num]) { \ - old_e = ta->e[e_num]; \ + if (tri_adj->e[e_num]) { \ + old_e = tri_adj->e[e_num]; \ old_e->t1 = ((old_e->t1 == tri) ? (new_tri) : (old_e->t1)); \ old_e->t2 = ((old_e->t2 == tri) ? (new_tri) : (old_e->t2)); \ } #define REMOVE_TRIANGLE_EDGE \ - if (ta->e[0]) { \ - ta->e[0]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ - lineart_discard_duplicated_edges(ta->e[0]); \ + if (tri_adj->e[0]) { \ + tri_adj->e[0]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ + lineart_discard_duplicated_edges(tri_adj->e[0]); \ } \ - if (ta->e[1]) { \ - ta->e[1]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ - lineart_discard_duplicated_edges(ta->e[1]); \ + if (tri_adj->e[1]) { \ + tri_adj->e[1]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ + lineart_discard_duplicated_edges(tri_adj->e[1]); \ } \ - if (ta->e[2]) { \ - ta->e[2]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ - lineart_discard_duplicated_edges(ta->e[2]); \ + if (tri_adj->e[2]) { \ + tri_adj->e[2]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ + lineart_discard_duplicated_edges(tri_adj->e[2]); \ } switch (in0 + in1 + in2) { @@ -856,32 +874,32 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, if (!in0) { /* Cut point for line 2---|-----0. */ - sub_v3_v3v3_db(vv1, tri->v[0]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, tri->v[2]->gloc); - dot1 = dot_v3v3_db(vv1, view_dir); - dot2 = dot_v3v3_db(vv2, view_dir); - a = dot1 / (dot1 + dot2); + sub_v3_v3v3_db(span_v1, tri->v[0]->gloc, cam_pos); + sub_v3_v3v3_db(span_v2, cam_pos, tri->v[2]->gloc); + dot_v1 = dot_v3v3_db(span_v1, view_dir); + dot_v2 = dot_v3v3_db(span_v2, view_dir); + a = dot_v1 / (dot_v1 + dot_v2); /* Assign it to a new point. */ interp_v3_v3v3_db(vt[0].gloc, tri->v[0]->gloc, tri->v[2]->gloc, a); - mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc); + mul_v4_m4v3_db(vt[0].fbcoord, m_view_projection, vt[0].gloc); vt[0].index = tri->v[2]->index; /* Cut point for line 1---|-----0. */ - sub_v3_v3v3_db(vv1, tri->v[0]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, tri->v[1]->gloc); - dot1 = dot_v3v3_db(vv1, view_dir); - dot2 = dot_v3v3_db(vv2, view_dir); - a = dot1 / (dot1 + dot2); + sub_v3_v3v3_db(span_v1, tri->v[0]->gloc, cam_pos); + sub_v3_v3v3_db(span_v2, cam_pos, tri->v[1]->gloc); + dot_v1 = dot_v3v3_db(span_v1, view_dir); + dot_v2 = dot_v3v3_db(span_v2, view_dir); + a = dot_v1 / (dot_v1 + dot_v2); /* Assign it to another new point. */ interp_v3_v3v3_db(vt[1].gloc, tri->v[0]->gloc, tri->v[1]->gloc, a); - mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc); + mul_v4_m4v3_db(vt[1].fbcoord, m_view_projection, vt[1].gloc); vt[1].index = tri->v[1]->index; /* New line connecting two new points. */ INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_add_edge_to_array(&rb->pending_edges, e); + lineart_add_edge_to_array(&ld->pending_edges, e); } /* NOTE: inverting `e->v1/v2` (left/right point) doesn't matter as long as * `tri->edge` and `tri->v` has the same sequence. and the winding direction @@ -909,28 +927,28 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, t_count += 1; } else if (!in2) { - sub_v3_v3v3_db(vv1, tri->v[2]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc); - dot1 = dot_v3v3_db(vv1, view_dir); - dot2 = dot_v3v3_db(vv2, view_dir); - a = dot1 / (dot1 + dot2); + sub_v3_v3v3_db(span_v1, tri->v[2]->gloc, cam_pos); + sub_v3_v3v3_db(span_v2, cam_pos, tri->v[0]->gloc); + dot_v1 = dot_v3v3_db(span_v1, view_dir); + dot_v2 = dot_v3v3_db(span_v2, view_dir); + a = dot_v1 / (dot_v1 + dot_v2); interp_v3_v3v3_db(vt[0].gloc, tri->v[2]->gloc, tri->v[0]->gloc, a); - mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc); + mul_v4_m4v3_db(vt[0].fbcoord, m_view_projection, vt[0].gloc); vt[0].index = tri->v[0]->index; - sub_v3_v3v3_db(vv1, tri->v[2]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, tri->v[1]->gloc); - dot1 = dot_v3v3_db(vv1, view_dir); - dot2 = dot_v3v3_db(vv2, view_dir); - a = dot1 / (dot1 + dot2); + sub_v3_v3v3_db(span_v1, tri->v[2]->gloc, cam_pos); + sub_v3_v3v3_db(span_v2, cam_pos, tri->v[1]->gloc); + dot_v1 = dot_v3v3_db(span_v1, view_dir); + dot_v2 = dot_v3v3_db(span_v2, view_dir); + a = dot_v1 / (dot_v1 + dot_v2); interp_v3_v3v3_db(vt[1].gloc, tri->v[2]->gloc, tri->v[1]->gloc, a); - mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc); + mul_v4_m4v3_db(vt[1].fbcoord, m_view_projection, vt[1].gloc); vt[1].index = tri->v[1]->index; INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_add_edge_to_array(&rb->pending_edges, e); + lineart_add_edge_to_array(&ld->pending_edges, e); } e->v1 = &vt[0]; e->v2 = &vt[1]; @@ -950,28 +968,28 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, t_count += 1; } else if (!in1) { - sub_v3_v3v3_db(vv1, tri->v[1]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, tri->v[2]->gloc); - dot1 = dot_v3v3_db(vv1, view_dir); - dot2 = dot_v3v3_db(vv2, view_dir); - a = dot1 / (dot1 + dot2); + sub_v3_v3v3_db(span_v1, tri->v[1]->gloc, cam_pos); + sub_v3_v3v3_db(span_v2, cam_pos, tri->v[2]->gloc); + dot_v1 = dot_v3v3_db(span_v1, view_dir); + dot_v2 = dot_v3v3_db(span_v2, view_dir); + a = dot_v1 / (dot_v1 + dot_v2); interp_v3_v3v3_db(vt[0].gloc, tri->v[1]->gloc, tri->v[2]->gloc, a); - mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc); + mul_v4_m4v3_db(vt[0].fbcoord, m_view_projection, vt[0].gloc); vt[0].index = tri->v[2]->index; - sub_v3_v3v3_db(vv1, tri->v[1]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc); - dot1 = dot_v3v3_db(vv1, view_dir); - dot2 = dot_v3v3_db(vv2, view_dir); - a = dot1 / (dot1 + dot2); + sub_v3_v3v3_db(span_v1, tri->v[1]->gloc, cam_pos); + sub_v3_v3v3_db(span_v2, cam_pos, tri->v[0]->gloc); + dot_v1 = dot_v3v3_db(span_v1, view_dir); + dot_v2 = dot_v3v3_db(span_v2, view_dir); + a = dot_v1 / (dot_v1 + dot_v2); interp_v3_v3v3_db(vt[1].gloc, tri->v[1]->gloc, tri->v[0]->gloc, a); - mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc); + mul_v4_m4v3_db(vt[1].fbcoord, m_view_projection, vt[1].gloc); vt[1].index = tri->v[0]->index; INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_add_edge_to_array(&rb->pending_edges, e); + lineart_add_edge_to_array(&ld->pending_edges, e); } e->v1 = &vt[1]; e->v2 = &vt[0]; @@ -1021,32 +1039,32 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, */ if (in0) { /* Cut point for line 0---|------1. */ - sub_v3_v3v3_db(vv1, tri->v[1]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc); - dot1 = dot_v3v3_db(vv1, view_dir); - dot2 = dot_v3v3_db(vv2, view_dir); - a = dot2 / (dot1 + dot2); + sub_v3_v3v3_db(span_v1, tri->v[1]->gloc, cam_pos); + sub_v3_v3v3_db(span_v2, cam_pos, tri->v[0]->gloc); + dot_v1 = dot_v3v3_db(span_v1, view_dir); + dot_v2 = dot_v3v3_db(span_v2, view_dir); + a = dot_v2 / (dot_v1 + dot_v2); /* Assign to a new point. */ interp_v3_v3v3_db(vt[0].gloc, tri->v[0]->gloc, tri->v[1]->gloc, a); - mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc); + mul_v4_m4v3_db(vt[0].fbcoord, m_view_projection, vt[0].gloc); vt[0].index = tri->v[0]->index; /* Cut point for line 0---|------2. */ - sub_v3_v3v3_db(vv1, tri->v[2]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc); - dot1 = dot_v3v3_db(vv1, view_dir); - dot2 = dot_v3v3_db(vv2, view_dir); - a = dot2 / (dot1 + dot2); + sub_v3_v3v3_db(span_v1, tri->v[2]->gloc, cam_pos); + sub_v3_v3v3_db(span_v2, cam_pos, tri->v[0]->gloc); + dot_v1 = dot_v3v3_db(span_v1, view_dir); + dot_v2 = dot_v3v3_db(span_v2, view_dir); + a = dot_v2 / (dot_v1 + dot_v2); /* Assign to other new point. */ interp_v3_v3v3_db(vt[1].gloc, tri->v[0]->gloc, tri->v[2]->gloc, a); - mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc); + mul_v4_m4v3_db(vt[1].fbcoord, m_view_projection, vt[1].gloc); vt[1].index = tri->v[0]->index; /* New line connects two new points. */ INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_add_edge_to_array(&rb->pending_edges, e); + lineart_add_edge_to_array(&ld->pending_edges, e); } e->v1 = &vt[1]; e->v2 = &vt[0]; @@ -1077,28 +1095,28 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, } else if (in1) { - sub_v3_v3v3_db(vv1, tri->v[1]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, tri->v[2]->gloc); - dot1 = dot_v3v3_db(vv1, view_dir); - dot2 = dot_v3v3_db(vv2, view_dir); - a = dot1 / (dot1 + dot2); + sub_v3_v3v3_db(span_v1, tri->v[1]->gloc, cam_pos); + sub_v3_v3v3_db(span_v2, cam_pos, tri->v[2]->gloc); + dot_v1 = dot_v3v3_db(span_v1, view_dir); + dot_v2 = dot_v3v3_db(span_v2, view_dir); + a = dot_v1 / (dot_v1 + dot_v2); interp_v3_v3v3_db(vt[0].gloc, tri->v[1]->gloc, tri->v[2]->gloc, a); - mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc); + mul_v4_m4v3_db(vt[0].fbcoord, m_view_projection, vt[0].gloc); vt[0].index = tri->v[1]->index; - sub_v3_v3v3_db(vv1, tri->v[1]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc); - dot1 = dot_v3v3_db(vv1, view_dir); - dot2 = dot_v3v3_db(vv2, view_dir); - a = dot1 / (dot1 + dot2); + sub_v3_v3v3_db(span_v1, tri->v[1]->gloc, cam_pos); + sub_v3_v3v3_db(span_v2, cam_pos, tri->v[0]->gloc); + dot_v1 = dot_v3v3_db(span_v1, view_dir); + dot_v2 = dot_v3v3_db(span_v2, view_dir); + a = dot_v1 / (dot_v1 + dot_v2); interp_v3_v3v3_db(vt[1].gloc, tri->v[1]->gloc, tri->v[0]->gloc, a); - mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc); + mul_v4_m4v3_db(vt[1].fbcoord, m_view_projection, vt[1].gloc); vt[1].index = tri->v[1]->index; INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_add_edge_to_array(&rb->pending_edges, e); + lineart_add_edge_to_array(&ld->pending_edges, e); } e->v1 = &vt[1]; e->v2 = &vt[0]; @@ -1126,28 +1144,28 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, } else if (in2) { - sub_v3_v3v3_db(vv1, tri->v[2]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc); - dot1 = dot_v3v3_db(vv1, view_dir); - dot2 = dot_v3v3_db(vv2, view_dir); - a = dot1 / (dot1 + dot2); + sub_v3_v3v3_db(span_v1, tri->v[2]->gloc, cam_pos); + sub_v3_v3v3_db(span_v2, cam_pos, tri->v[0]->gloc); + dot_v1 = dot_v3v3_db(span_v1, view_dir); + dot_v2 = dot_v3v3_db(span_v2, view_dir); + a = dot_v1 / (dot_v1 + dot_v2); interp_v3_v3v3_db(vt[0].gloc, tri->v[2]->gloc, tri->v[0]->gloc, a); - mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc); + mul_v4_m4v3_db(vt[0].fbcoord, m_view_projection, vt[0].gloc); vt[0].index = tri->v[2]->index; - sub_v3_v3v3_db(vv1, tri->v[2]->gloc, cam_pos); - sub_v3_v3v3_db(vv2, cam_pos, tri->v[1]->gloc); - dot1 = dot_v3v3_db(vv1, view_dir); - dot2 = dot_v3v3_db(vv2, view_dir); - a = dot1 / (dot1 + dot2); + sub_v3_v3v3_db(span_v1, tri->v[2]->gloc, cam_pos); + sub_v3_v3v3_db(span_v2, cam_pos, tri->v[1]->gloc); + dot_v1 = dot_v3v3_db(span_v1, view_dir); + dot_v2 = dot_v3v3_db(span_v2, view_dir); + a = dot_v1 / (dot_v1 + dot_v2); interp_v3_v3v3_db(vt[1].gloc, tri->v[2]->gloc, tri->v[1]->gloc, a); - mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc); + mul_v4_m4v3_db(vt[1].fbcoord, m_view_projection, vt[1].gloc); vt[1].index = tri->v[2]->index; INCREASE_EDGE if (allow_boundaries) { e->flags = LRT_EDGE_FLAG_CONTOUR; - lineart_add_edge_to_array(&rb->pending_edges, e); + lineart_add_edge_to_array(&ld->pending_edges, e); } e->v1 = &vt[1]; e->v2 = &vt[0]; @@ -1191,22 +1209,22 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, * new topology that represents the trimmed triangle. (which then became a triangle or a square * formed by two triangles) */ -static void lineart_main_cull_triangles(LineartRenderBuffer *rb, bool clip_far) +void lineart_main_cull_triangles(LineartData *ld, bool clip_far) { LineartTriangle *tri; LineartElementLinkNode *v_eln, *t_eln, *e_eln; - double(*vp)[4] = rb->view_projection; + double(*m_view_projection)[4] = ld->conf.view_projection; int i; int v_count = 0, t_count = 0, e_count = 0; Object *ob; - bool allow_boundaries = rb->allow_boundaries; + bool allow_boundaries = ld->conf.allow_boundaries; double cam_pos[3]; - double clip_start = rb->near_clip, clip_end = rb->far_clip; + double clip_start = ld->conf.near_clip, clip_end = ld->conf.far_clip; double view_dir[3], clip_advance[3]; - copy_v3_v3_db(view_dir, rb->view_vector); - copy_v3_v3_db(clip_advance, rb->view_vector); - copy_v3_v3_db(cam_pos, rb->camera_pos); + copy_v3_v3_db(view_dir, ld->conf.view_vector); + copy_v3_v3_db(clip_advance, ld->conf.view_vector); + copy_v3_v3_db(cam_pos, ld->conf.camera_pos); if (clip_far) { /* Move starting point to end plane. */ @@ -1222,25 +1240,25 @@ static void lineart_main_cull_triangles(LineartRenderBuffer *rb, bool clip_far) add_v3_v3_db(cam_pos, clip_advance); } - v_eln = lineart_memory_get_vert_space(rb); - t_eln = lineart_memory_get_triangle_space(rb); - e_eln = lineart_memory_get_edge_space(rb); + v_eln = lineart_memory_get_vert_space(ld); + t_eln = lineart_memory_get_triangle_space(ld); + e_eln = lineart_memory_get_edge_space(ld); /* Additional memory space for storing generated points and triangles. */ #define LRT_CULL_ENSURE_MEMORY \ if (v_count > 60) { \ v_eln->element_count = v_count; \ - v_eln = lineart_memory_get_vert_space(rb); \ + v_eln = lineart_memory_get_vert_space(ld); \ v_count = 0; \ } \ if (t_count > 60) { \ t_eln->element_count = t_count; \ - t_eln = lineart_memory_get_triangle_space(rb); \ + t_eln = lineart_memory_get_triangle_space(ld); \ t_count = 0; \ } \ if (e_count > 60) { \ e_eln->element_count = e_count; \ - e_eln = lineart_memory_get_edge_space(rb); \ + e_eln = lineart_memory_get_edge_space(ld); \ e_count = 0; \ } @@ -1275,21 +1293,21 @@ static void lineart_main_cull_triangles(LineartRenderBuffer *rb, bool clip_far) int use_w = 3; int in0 = 0, in1 = 0, in2 = 0; - if (!rb->cam_is_persp) { + if (!ld->conf.cam_is_persp) { clip_start = -1; clip_end = 1; use_w = 2; } /* Then go through all the other triangles. */ - LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->triangle_buffer_pointers) { + LISTBASE_FOREACH (LineartElementLinkNode *, eln, &ld->geom.triangle_buffer_pointers) { if (eln->flags & LRT_ELEMENT_IS_ADDITIONAL) { continue; } ob = eln->object_ref; for (i = 0; i < eln->element_count; i++) { /* Select the triangle in the array. */ - tri = (void *)(((uchar *)eln->pointer) + rb->triangle_size * i); + tri = (void *)(((uchar *)eln->pointer) + ld->sizeof_triangle * i); if (tri->flags & LRT_CULL_DISCARD) { continue; @@ -1297,7 +1315,7 @@ static void lineart_main_cull_triangles(LineartRenderBuffer *rb, bool clip_far) LRT_CULL_DECIDE_INSIDE LRT_CULL_ENSURE_MEMORY - lineart_triangle_cull_single(rb, + lineart_triangle_cull_single(ld, tri, in0, in1, @@ -1305,7 +1323,7 @@ static void lineart_main_cull_triangles(LineartRenderBuffer *rb, bool clip_far) cam_pos, view_dir, allow_boundaries, - vp, + m_view_projection, ob, &v_count, &e_count, @@ -1326,33 +1344,33 @@ static void lineart_main_cull_triangles(LineartRenderBuffer *rb, bool clip_far) * Adjacent data is only used during the initial stages of computing. * So we can free it using this function when it is not needed anymore. */ -static void lineart_main_free_adjacent_data(LineartRenderBuffer *rb) +void lineart_main_free_adjacent_data(LineartData *ld) { - LinkData *ld; - while ((ld = BLI_pophead(&rb->triangle_adjacent_pointers)) != NULL) { - MEM_freeN(ld->data); + LinkData *link; + while ((link = BLI_pophead(&ld->geom.triangle_adjacent_pointers)) != NULL) { + MEM_freeN(link->data); } - LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->triangle_buffer_pointers) { + LISTBASE_FOREACH (LineartElementLinkNode *, eln, &ld->geom.triangle_buffer_pointers) { LineartTriangle *tri = eln->pointer; int i; for (i = 0; i < eln->element_count; i++) { /* See definition of tri->intersecting_verts and the usage in * lineart_geometry_object_load() for detailed. */ tri->intersecting_verts = NULL; - tri = (LineartTriangle *)(((uchar *)tri) + rb->triangle_size); + tri = (LineartTriangle *)(((uchar *)tri) + ld->sizeof_triangle); } } } -static void lineart_main_perspective_division(LineartRenderBuffer *rb) +void lineart_main_perspective_division(LineartData *ld) { LineartVert *vt; int i; - LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->vertex_buffer_pointers) { + LISTBASE_FOREACH (LineartElementLinkNode *, eln, &ld->geom.vertex_buffer_pointers) { vt = eln->pointer; for (i = 0; i < eln->element_count; i++) { - if (rb->cam_is_persp) { + if (ld->conf.cam_is_persp) { /* Do not divide Z, we use Z to back transform cut points in later chaining process. */ vt[i].fbcoord[0] /= vt[i].fbcoord[3]; vt[i].fbcoord[1] /= vt[i].fbcoord[3]; @@ -1363,13 +1381,13 @@ static void lineart_main_perspective_division(LineartRenderBuffer *rb) // `vt[i].fbcoord[2] = -2 * vt[i].fbcoord[2] / (far - near) - (far + near) / (far - near); } /* Shifting is always needed. */ - vt[i].fbcoord[0] -= rb->shift_x * 2; - vt[i].fbcoord[1] -= rb->shift_y * 2; + vt[i].fbcoord[0] -= ld->conf.shift_x * 2; + vt[i].fbcoord[1] -= ld->conf.shift_y * 2; } } } -static void lineart_main_discard_out_of_frame_edges(LineartRenderBuffer *rb) +void lineart_main_discard_out_of_frame_edges(LineartData *ld) { LineartEdge *e; int i; @@ -1377,7 +1395,7 @@ static void lineart_main_discard_out_of_frame_edges(LineartRenderBuffer *rb) #define LRT_VERT_OUT_OF_BOUND(v) \ (v && (v->fbcoord[0] < -1 || v->fbcoord[0] > 1 || v->fbcoord[1] < -1 || v->fbcoord[1] > 1)) - LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->line_buffer_pointers) { + LISTBASE_FOREACH (LineartElementLinkNode *, eln, &ld->geom.line_buffer_pointers) { e = (LineartEdge *)eln->pointer; for (i = 0; i < eln->element_count; i++) { if ((LRT_VERT_OUT_OF_BOUND(e[i].v1) && LRT_VERT_OUT_OF_BOUND(e[i].v2))) { @@ -1414,14 +1432,23 @@ static void lineart_mvert_transform_task(void *__restrict userdata, v->index = i; } -#define LRT_EDGE_FLAG_TYPE_MAX_BITS 6 +static const int LRT_MESH_EDGE_TYPES[] = { + LRT_EDGE_FLAG_EDGE_MARK, + LRT_EDGE_FLAG_CONTOUR, + LRT_EDGE_FLAG_CREASE, + LRT_EDGE_FLAG_MATERIAL, + LRT_EDGE_FLAG_LOOSE, + LRT_EDGE_FLAG_CONTOUR_SECONDARY, +}; -static int lineart_edge_type_duplication_count(char eflag) +#define LRT_MESH_EDGE_TYPES_COUNT 6 + +static int lineart_edge_type_duplication_count(int eflag) { int count = 0; /* See eLineartEdgeFlag for details. */ - for (int i = 0; i < LRT_EDGE_FLAG_TYPE_MAX_BITS; i++) { - if (eflag & (1 << i)) { + for (int i = 0; i < LRT_MESH_EDGE_TYPES_COUNT; i++) { + if (eflag & LRT_MESH_EDGE_TYPES[i]) { count++; } } @@ -1432,18 +1459,19 @@ static int lineart_edge_type_duplication_count(char eflag) * Because we have a variable size for #LineartTriangle, we need an access helper. * See #LineartTriangleThread for more info. */ -static LineartTriangle *lineart_triangle_from_index(LineartRenderBuffer *rb, +static LineartTriangle *lineart_triangle_from_index(LineartData *ld, LineartTriangle *rt_array, int index) { - char *b = (char *)rt_array; - b += (index * rb->triangle_size); + int8_t *b = (int8_t *)rt_array; + b += (index * ld->sizeof_triangle); return (LineartTriangle *)b; } typedef struct EdgeFeatData { - LineartRenderBuffer *rb; + LineartData *ld; Mesh *me; + Object *ob; const MLoopTri *mlooptri; LineartTriangle *tri_array; LineartVert *v_array; @@ -1476,6 +1504,7 @@ static void lineart_identify_mlooptri_feature_edges(void *__restrict userdata, EdgeFeatData *e_feat_data = (EdgeFeatData *)userdata; EdgeFeatReduceData *reduce_data = (EdgeFeatReduceData *)tls->userdata_chunk; Mesh *me = e_feat_data->me; + Object *ob = e_feat_data->ob; LineartEdgeNeighbor *edge_nabr = e_feat_data->edge_nabr; const MLoopTri *mlooptri = e_feat_data->mlooptri; @@ -1488,7 +1517,8 @@ static void lineart_identify_mlooptri_feature_edges(void *__restrict userdata, } bool face_mark_filtered = false; - bool enable_face_mark = (e_feat_data->use_freestyle_face && e_feat_data->rb->filter_face_mark); + bool enable_face_mark = (e_feat_data->use_freestyle_face && + e_feat_data->ld->conf.filter_face_mark); bool only_contour = false; if (enable_face_mark) { FreestyleFace *ff1, *ff2; @@ -1505,7 +1535,8 @@ static void lineart_identify_mlooptri_feature_edges(void *__restrict userdata, * path is simper when it's assuming both ff1 and ff2 not NULL. */ ff2 = ff1; } - if (e_feat_data->rb->filter_face_mark_boundaries ^ e_feat_data->rb->filter_face_mark_invert) { + if (e_feat_data->ld->conf.filter_face_mark_boundaries ^ + e_feat_data->ld->conf.filter_face_mark_invert) { if ((ff1->flag & FREESTYLE_FACE_MARK) || (ff2->flag & FREESTYLE_FACE_MARK)) { face_mark_filtered = true; } @@ -1515,12 +1546,12 @@ static void lineart_identify_mlooptri_feature_edges(void *__restrict userdata, face_mark_filtered = true; } } - if (e_feat_data->rb->filter_face_mark_invert) { + if (e_feat_data->ld->conf.filter_face_mark_invert) { face_mark_filtered = !face_mark_filtered; } if (!face_mark_filtered) { edge_nabr[i].flags = LRT_EDGE_FLAG_INHIBIT; - if (e_feat_data->rb->filter_face_mark_keep_contour) { + if (e_feat_data->ld->conf.filter_face_mark_keep_contour) { only_contour = true; } } @@ -1539,60 +1570,77 @@ static void lineart_identify_mlooptri_feature_edges(void *__restrict userdata, LineartTriangle *tri1, *tri2; LineartVert *vert; - LineartRenderBuffer *rb = e_feat_data->rb; + LineartData *ld = e_feat_data->ld; int f1 = i / 3, f2 = edge_nabr[i].e / 3; /* The mesh should already be triangulated now, so we can assume each face is a triangle. */ - tri1 = lineart_triangle_from_index(rb, e_feat_data->tri_array, f1); - tri2 = lineart_triangle_from_index(rb, e_feat_data->tri_array, f2); + tri1 = lineart_triangle_from_index(ld, e_feat_data->tri_array, f1); + tri2 = lineart_triangle_from_index(ld, e_feat_data->tri_array, f2); vert = &e_feat_data->v_array[edge_nabr[i].v1]; double view_vector_persp[3]; double *view_vector = view_vector_persp; - double dot_1 = 0, dot_2 = 0; + double dot_v1 = 0, dot_v2 = 0; double result; bool material_back_face = ((tri1->flags | tri2->flags) & LRT_TRIANGLE_MAT_BACK_FACE_CULLING); - if (rb->use_contour || rb->use_back_face_culling || material_back_face) { - if (rb->cam_is_persp) { - sub_v3_v3v3_db(view_vector, rb->camera_pos, vert->gloc); + if (ld->conf.use_contour || ld->conf.use_back_face_culling || material_back_face) { + if (ld->conf.cam_is_persp) { + sub_v3_v3v3_db(view_vector, ld->conf.camera_pos, vert->gloc); } else { - view_vector = rb->view_vector; + view_vector = ld->conf.view_vector; } - dot_1 = dot_v3v3_db(view_vector, tri1->gn); - dot_2 = dot_v3v3_db(view_vector, tri2->gn); + dot_v1 = dot_v3v3_db(view_vector, tri1->gn); + dot_v2 = dot_v3v3_db(view_vector, tri2->gn); - if ((result = dot_1 * dot_2) <= 0 && (dot_1 + dot_2)) { + if ((result = dot_v1 * dot_v2) <= 0 && (dot_v1 + dot_v2)) { edge_flag_result |= LRT_EDGE_FLAG_CONTOUR; } - if (rb->use_back_face_culling) { - if (dot_1 < 0) { + if (ld->conf.use_back_face_culling) { + if (dot_v1 < 0) { tri1->flags |= LRT_CULL_DISCARD; } - if (dot_2 < 0) { + if (dot_v2 < 0) { tri2->flags |= LRT_CULL_DISCARD; } } if (material_back_face) { - if (tri1->flags & LRT_TRIANGLE_MAT_BACK_FACE_CULLING && dot_1 < 0) { + if (tri1->flags & LRT_TRIANGLE_MAT_BACK_FACE_CULLING && dot_v1 < 0) { tri1->flags |= LRT_CULL_DISCARD; } - if (tri2->flags & LRT_TRIANGLE_MAT_BACK_FACE_CULLING && dot_2 < 0) { + if (tri2->flags & LRT_TRIANGLE_MAT_BACK_FACE_CULLING && dot_v2 < 0) { tri2->flags |= LRT_CULL_DISCARD; } } } + if (ld->conf.use_contour_secondary) { + view_vector = view_vector_persp; + if (ld->conf.cam_is_persp_secondary) { + sub_v3_v3v3_db(view_vector, vert->gloc, ld->conf.camera_pos_secondary); + } + else { + view_vector = ld->conf.view_vector_secondary; + } + + dot_v1 = dot_v3v3_db(view_vector, tri1->gn); + dot_v2 = dot_v3v3_db(view_vector, tri2->gn); + + if ((result = dot_v1 * dot_v2) <= 0 && (dot_v1 + dot_v2)) { + edge_flag_result |= LRT_EDGE_FLAG_CONTOUR_SECONDARY; + } + } + if (!only_contour) { - if (rb->use_crease) { + if (ld->conf.use_crease) { bool do_crease = true; - if (!rb->force_crease && !e_feat_data->use_auto_smooth && + if (!ld->conf.force_crease && !e_feat_data->use_auto_smooth && (me->mpoly[mlooptri[f1].poly].flag & ME_SMOOTH) && (me->mpoly[mlooptri[f2].poly].flag & ME_SMOOTH)) { do_crease = false; @@ -1605,8 +1653,19 @@ static void lineart_identify_mlooptri_feature_edges(void *__restrict userdata, int mat1 = me->mpoly[mlooptri[f1].poly].mat_nr; int mat2 = me->mpoly[mlooptri[f2].poly].mat_nr; - if (rb->use_material && mat1 != mat2) { - edge_flag_result |= LRT_EDGE_FLAG_MATERIAL; + if (mat1 != mat2) { + Material *m1 = BKE_object_material_get(ob, mat1 + 1); + Material *m2 = BKE_object_material_get(ob, mat2 + 1); + if (m1 && m2 && + ((m1->lineart.mat_occlusion == 0 && m2->lineart.mat_occlusion != 0) || + (m2->lineart.mat_occlusion == 0 && m1->lineart.mat_occlusion != 0))) { + if (ld->conf.use_contour) { + edge_flag_result |= LRT_EDGE_FLAG_CONTOUR; + } + } + if (ld->conf.use_material) { + edge_flag_result |= LRT_EDGE_FLAG_MATERIAL; + } } } else { /* only_contour */ @@ -1621,11 +1680,11 @@ static void lineart_identify_mlooptri_feature_edges(void *__restrict userdata, if (real_edges[i % 3] >= 0) { MEdge *medge = &me->medge[real_edges[i % 3]]; - if (rb->use_crease && rb->sharp_as_crease && (medge->flag & ME_SHARP)) { + if (ld->conf.use_crease && ld->conf.sharp_as_crease && (medge->flag & ME_SHARP)) { edge_flag_result |= LRT_EDGE_FLAG_CREASE; } - if (rb->use_edge_marks && e_feat_data->use_freestyle_edge) { + if (ld->conf.use_edge_marks && e_feat_data->use_freestyle_edge) { FreestyleEdge *fe; int index = e_feat_data->freestyle_edge_index; fe = &((FreestyleEdge *)me->edata.layers[index].data)[real_edges[i % 3]]; @@ -1641,7 +1700,7 @@ static void lineart_identify_mlooptri_feature_edges(void *__restrict userdata, /* Only allocate for feature edge (instead of all edges) to save memory. * If allow duplicated edges, one edge gets added multiple times if it has multiple types. */ - reduce_data->feat_edges += e_feat_data->rb->allow_duplicated_types ? + reduce_data->feat_edges += e_feat_data->ld->conf.allow_duplicated_types ? lineart_edge_type_duplication_count(edge_flag_result) : 1; } @@ -1679,6 +1738,7 @@ static void lineart_join_loose_edge_arr(LooseEdgeData *loose_data, LooseEdgeData sizeof(MEdge *) * to_be_joined->loose_count); loose_data->loose_count += to_be_joined->loose_count; MEM_freeN(to_be_joined->loose_array); + to_be_joined->loose_array = NULL; } static void lineart_add_loose_edge(LooseEdgeData *loose_data, MEdge *e) @@ -1712,7 +1772,7 @@ static void loose_data_sum_reduce(const void *__restrict UNUSED(userdata), lineart_join_loose_edge_arr(final, loose_chunk); } -static void lineart_add_edge_to_array(LineartPendingEdges *pe, LineartEdge *e) +void lineart_add_edge_to_array(LineartPendingEdges *pe, LineartEdge *e) { if (pe->next >= pe->max || !pe->max) { if (!pe->max) { @@ -1731,15 +1791,14 @@ static void lineart_add_edge_to_array(LineartPendingEdges *pe, LineartEdge *e) pe->array[pe->next] = e; pe->next++; } - static void lineart_add_edge_to_array_thread(LineartObjectInfo *obi, LineartEdge *e) { lineart_add_edge_to_array(&obi->pending_edges, e); } -/* Note: For simplicity, this function doesn't actually do anything if you already have data in +/* NOTE: For simplicity, this function doesn't actually do anything if you already have data in * #pe. */ -static void lineart_finalize_object_edge_array_reserve(LineartPendingEdges *pe, int count) +void lineart_finalize_object_edge_array_reserve(LineartPendingEdges *pe, int count) { if (pe->max || pe->array) { return; @@ -1766,17 +1825,17 @@ static void lineart_finalize_object_edge_array(LineartPendingEdges *pe, LineartO } static void lineart_triangle_adjacent_assign(LineartTriangle *tri, - LineartTriangleAdjacent *ta, + LineartTriangleAdjacent *tri_adj, LineartEdge *e) { if (lineart_edge_match(tri, e, 0, 1)) { - ta->e[0] = e; + tri_adj->e[0] = e; } else if (lineart_edge_match(tri, e, 1, 2)) { - ta->e[1] = e; + tri_adj->e[1] = e; } else if (lineart_edge_match(tri, e, 2, 0)) { - ta->e[2] = e; + tri_adj->e[2] = e; } } @@ -1817,12 +1876,18 @@ static void lineart_load_tri_task(void *__restrict userdata, mat->lineart.material_mask_bits : 0); tri->mat_occlusion |= (mat ? mat->lineart.mat_occlusion : 1); + tri->intersection_priority = ((mat && (mat->lineart.flags & + LRT_MATERIAL_CUSTOM_INTERSECTION_PRIORITY)) ? + mat->lineart.intersection_priority : + ob_info->intersection_priority); tri->flags |= (mat && (mat->blend_flag & MA_BL_CULL_BACKFACE)) ? LRT_TRIANGLE_MAT_BACK_FACE_CULLING : 0; tri->intersection_mask = ob_info->override_intersection_mask; + tri->target_reference = (ob_info->obindex | (i & LRT_OBINDEX_LOWER)); + double gn[3]; float no[3]; normal_tri_v3(no, me->mvert[v1].co, me->mvert[v2].co, me->mvert[v3].co); @@ -1862,7 +1927,7 @@ static void lineart_edge_neighbor_init_task(void *__restrict userdata, adj_e->v1 = mloop[looptri->tri[i % 3]].v; adj_e->v2 = mloop[looptri->tri[(i + 1) % 3]].v; if (adj_e->v1 > adj_e->v2) { - SWAP(unsigned int, adj_e->v1, adj_e->v2); + SWAP(uint32_t, adj_e->v1, adj_e->v2); } edge_nabr->e = -1; @@ -1908,7 +1973,9 @@ static LineartEdgeNeighbor *lineart_build_edge_neighbor(Mesh *me, int total_edge return edge_nabr; } -static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartRenderBuffer *re_buf) +static void lineart_geometry_object_load(LineartObjectInfo *ob_info, + LineartData *la_data, + ListBase *shadow_elns) { LineartElementLinkNode *elem_link_node; LineartVert *la_v_arr; @@ -1942,20 +2009,22 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartRend /* If we allow duplicated edges, one edge should get added multiple times if is has been * classified as more than one edge type. This is so we can create multiple different line type * chains containing the same edge. */ - la_v_arr = lineart_mem_acquire_thread(&re_buf->render_data_pool, + la_v_arr = lineart_mem_acquire_thread(&la_data->render_data_pool, sizeof(LineartVert) * me->totvert); - la_tri_arr = lineart_mem_acquire_thread(&re_buf->render_data_pool, - tot_tri * re_buf->triangle_size); + la_tri_arr = lineart_mem_acquire_thread(&la_data->render_data_pool, + tot_tri * la_data->sizeof_triangle); Object *orig_ob = ob_info->original_ob; - BLI_spin_lock(&re_buf->lock_task); - elem_link_node = lineart_list_append_pointer_pool_sized_thread(&re_buf->vertex_buffer_pointers, - &re_buf->render_data_pool, - la_v_arr, - sizeof(LineartElementLinkNode)); - BLI_spin_unlock(&re_buf->lock_task); + BLI_spin_lock(&la_data->lock_task); + elem_link_node = lineart_list_append_pointer_pool_sized_thread( + &la_data->geom.vertex_buffer_pointers, + &la_data->render_data_pool, + la_v_arr, + sizeof(LineartElementLinkNode)); + BLI_spin_unlock(&la_data->lock_task); + elem_link_node->obindex = ob_info->obindex; elem_link_node->element_count = me->totvert; elem_link_node->object_ref = orig_ob; ob_info->v_eln = elem_link_node; @@ -1970,7 +2039,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartRend use_auto_smooth = true; } else { - crease_angle = re_buf->crease_threshold; + crease_angle = la_data->conf.crease_threshold; } /* FIXME(Yiming): Hack for getting clean 3D text, the seam that extruded text object creates @@ -1979,12 +2048,13 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartRend elem_link_node->flags |= LRT_ELEMENT_BORDER_ONLY; } - BLI_spin_lock(&re_buf->lock_task); - elem_link_node = lineart_list_append_pointer_pool_sized_thread(&re_buf->triangle_buffer_pointers, - &re_buf->render_data_pool, - la_tri_arr, - sizeof(LineartElementLinkNode)); - BLI_spin_unlock(&re_buf->lock_task); + BLI_spin_lock(&la_data->lock_task); + elem_link_node = lineart_list_append_pointer_pool_sized_thread( + &la_data->geom.triangle_buffer_pointers, + &la_data->render_data_pool, + la_tri_arr, + sizeof(LineartElementLinkNode)); + BLI_spin_unlock(&la_data->lock_task); int usage = ob_info->usage; @@ -1996,10 +2066,10 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartRend LineartTriangleAdjacent *tri_adj = MEM_callocN(sizeof(LineartTriangleAdjacent) * tot_tri, "LineartTriangleAdjacent"); /* Link is minimal so we use pool anyway. */ - BLI_spin_lock(&re_buf->lock_task); + BLI_spin_lock(&la_data->lock_task); lineart_list_append_pointer_pool_thread( - &re_buf->triangle_adjacent_pointers, &re_buf->render_data_pool, tri_adj); - BLI_spin_unlock(&re_buf->lock_task); + &la_data->geom.triangle_adjacent_pointers, &la_data->render_data_pool, tri_adj); + BLI_spin_unlock(&la_data->lock_task); /* Convert all vertices to lineart verts. */ TaskParallelSettings vert_settings; @@ -2028,10 +2098,10 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartRend tri_data.mlooptri = mlooptri; tri_data.vert_arr = la_v_arr; tri_data.tri_arr = la_tri_arr; - tri_data.lineart_triangle_size = re_buf->triangle_size; + tri_data.lineart_triangle_size = la_data->sizeof_triangle; tri_data.tri_adj = tri_adj; - unsigned int total_edges = tot_tri * 3; + uint32_t total_edges = tot_tri * 3; BLI_task_parallel_range(0, tot_tri, &tri_data, lineart_load_tri_task, &tri_settings); @@ -2049,8 +2119,9 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartRend edge_feat_settings.func_reduce = feat_data_sum_reduce; EdgeFeatData edge_feat_data = {0}; - edge_feat_data.rb = re_buf; + edge_feat_data.ld = la_data; edge_feat_data.me = me; + edge_feat_data.ob = orig_ob; edge_feat_data.mlooptri = mlooptri; edge_feat_data.edge_nabr = lineart_build_edge_neighbor(me, total_edges); edge_feat_data.tri_array = la_tri_arr; @@ -2075,7 +2146,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartRend &edge_feat_settings); LooseEdgeData loose_data = {0}; - if (re_buf->use_loose) { + if (la_data->conf.use_loose) { /* Only identifying floating edges at this point because other edges has been taken care of * inside #lineart_identify_mlooptri_feature_edges function. */ TaskParallelSettings edge_loose_settings; @@ -2091,20 +2162,27 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartRend int allocate_la_e = edge_reduce.feat_edges + loose_data.loose_count; - la_edge_arr = lineart_mem_acquire_thread(&re_buf->render_data_pool, + la_edge_arr = lineart_mem_acquire_thread(la_data->edge_data_pool, sizeof(LineartEdge) * allocate_la_e); - la_seg_arr = lineart_mem_acquire_thread(&re_buf->render_data_pool, + la_seg_arr = lineart_mem_acquire_thread(la_data->edge_data_pool, sizeof(LineartEdgeSegment) * allocate_la_e); - BLI_spin_lock(&re_buf->lock_task); - elem_link_node = lineart_list_append_pointer_pool_sized_thread(&re_buf->line_buffer_pointers, - &re_buf->render_data_pool, - la_edge_arr, - sizeof(LineartElementLinkNode)); - BLI_spin_unlock(&re_buf->lock_task); + BLI_spin_lock(&la_data->lock_task); + elem_link_node = lineart_list_append_pointer_pool_sized_thread( + &la_data->geom.line_buffer_pointers, + la_data->edge_data_pool, + la_edge_arr, + sizeof(LineartElementLinkNode)); + BLI_spin_unlock(&la_data->lock_task); elem_link_node->element_count = allocate_la_e; elem_link_node->object_ref = orig_ob; + elem_link_node->obindex = ob_info->obindex; + + LineartElementLinkNode *shadow_eln = NULL; + if (shadow_elns) { + shadow_eln = lineart_find_matching_eln(shadow_elns, ob_info->obindex); + } - // Start of the edge/seg arr + /* Start of the edge/seg arr */ LineartEdge *la_edge; LineartEdgeSegment *la_seg; la_edge = la_edge_arr; @@ -2125,8 +2203,8 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartRend LineartEdge *edge_added = NULL; /* See eLineartEdgeFlag for details. */ - for (int flag_bit = 0; flag_bit < LRT_EDGE_FLAG_TYPE_MAX_BITS; flag_bit++) { - char use_type = 1 << flag_bit; + for (int flag_bit = 0; flag_bit < LRT_MESH_EDGE_TYPES_COUNT; flag_bit++) { + int use_type = LRT_MESH_EDGE_TYPES[flag_bit]; if (!(use_type & edge_nabr->flags)) { continue; } @@ -2134,20 +2212,32 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartRend la_edge->v1 = &la_v_arr[edge_nabr->v1]; la_edge->v2 = &la_v_arr[edge_nabr->v2]; int findex = i / 3; - la_edge->t1 = lineart_triangle_from_index(re_buf, la_tri_arr, findex); + la_edge->t1 = lineart_triangle_from_index(la_data, la_tri_arr, findex); if (!edge_added) { lineart_triangle_adjacent_assign(la_edge->t1, &tri_adj[findex], la_edge); } if (edge_nabr->e != -1) { findex = edge_nabr->e / 3; - la_edge->t2 = lineart_triangle_from_index(re_buf, la_tri_arr, findex); + la_edge->t2 = lineart_triangle_from_index(la_data, la_tri_arr, findex); if (!edge_added) { lineart_triangle_adjacent_assign(la_edge->t2, &tri_adj[findex], la_edge); } } la_edge->flags = use_type; la_edge->object_ref = orig_ob; + la_edge->edge_identifier = LRT_EDGE_IDENTIFIER(ob_info, la_edge); BLI_addtail(&la_edge->segments, la_seg); + + if (shadow_eln) { + /* TODO(Yiming): It's gonna be faster to do this operation after second stage occlusion if + * we only need visible segments to have shadow info, however that way we lose information + * on "shadow behind transparency window" type of region. */ + LineartEdge *shadow_e = lineart_find_matching_edge(shadow_eln, la_edge->edge_identifier); + if (shadow_e) { + lineart_register_shadow_cuts(la_data, la_edge, shadow_e); + } + } + if (usage == OBJECT_LRT_INHERIT || usage == OBJECT_LRT_INCLUDE || usage == OBJECT_LRT_NO_INTERSECTION) { lineart_add_edge_to_array_thread(ob_info, la_edge); @@ -2162,7 +2252,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartRend la_edge++; la_seg++; - if (!re_buf->allow_duplicated_types) { + if (!la_data->conf.allow_duplicated_types) { break; } } @@ -2174,10 +2264,17 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartRend la_edge->v2 = &la_v_arr[loose_data.loose_array[i]->v2]; la_edge->flags = LRT_EDGE_FLAG_LOOSE; la_edge->object_ref = orig_ob; + la_edge->edge_identifier = LRT_EDGE_IDENTIFIER(ob_info, la_edge); BLI_addtail(&la_edge->segments, la_seg); if (usage == OBJECT_LRT_INHERIT || usage == OBJECT_LRT_INCLUDE || usage == OBJECT_LRT_NO_INTERSECTION) { lineart_add_edge_to_array_thread(ob_info, la_edge); + if (shadow_eln) { + LineartEdge *shadow_e = lineart_find_matching_edge(shadow_eln, la_edge->edge_identifier); + if (shadow_e) { + lineart_register_shadow_cuts(la_data, la_edge, shadow_e); + } + } } la_edge++; la_seg++; @@ -2196,10 +2293,7 @@ static void lineart_object_load_worker(TaskPool *__restrict UNUSED(pool), LineartObjectLoadTaskInfo *olti) { for (LineartObjectInfo *obi = olti->pending; obi; obi = obi->next) { - lineart_geometry_object_load(obi, olti->rb); - if (G.debug_value == 4000) { - printf("thread id: %d processed: %d\n", olti->thread_id, obi->original_me->totpoly); - } + lineart_geometry_object_load(obi, olti->ld, olti->shadow_elns); } } @@ -2221,6 +2315,26 @@ static uchar lineart_intersection_mask_check(Collection *c, Object *ob) return 0; } +static uchar lineart_intersection_priority_check(Collection *c, Object *ob) +{ + if (ob->lineart.flags & OBJECT_LRT_OWN_INTERSECTION_PRIORITY) { + return ob->lineart.intersection_priority; + } + + LISTBASE_FOREACH (CollectionChild *, cc, &c->children) { + uchar result = lineart_intersection_priority_check(cc->collection, ob); + if (result) { + return result; + } + } + if (BKE_collection_has_object(c, (Object *)(ob->id.orig_id))) { + if (c->lineart_flags & COLLECTION_LRT_USE_INTERSECTION_PRIORITY) { + return c->lineart_intersection_priority; + } + } + return 0; +} + /** * See if this object in such collection is used for generating line art, * Disabling a collection for line art will doable all objects inside. @@ -2277,7 +2391,7 @@ static void lineart_geometry_load_assign_thread(LineartObjectLoadTaskInfo *olti_ int this_face_count) { LineartObjectLoadTaskInfo *use_olti = olti_list; - long unsigned int min_face = use_olti->total_faces; + uint64_t min_face = use_olti->total_faces; for (int i = 0; i < thread_count; i++) { if (olti_list[i].total_faces < min_face) { min_face = olti_list[i].total_faces; @@ -2290,7 +2404,7 @@ static void lineart_geometry_load_assign_thread(LineartObjectLoadTaskInfo *olti_ use_olti->pending = obi; } -static bool lineart_geometry_check_visible(double (*model_view_proj)[4], +static bool lineart_geometry_check_visible(double model_view_proj[4][4], double shift_x, double shift_y, Mesh *use_mesh) @@ -2333,7 +2447,7 @@ static bool lineart_geometry_check_visible(double (*model_view_proj)[4], return true; } -static void lineart_object_load_single_instance(LineartRenderBuffer *rb, +static void lineart_object_load_single_instance(LineartData *ld, Depsgraph *depsgraph, Scene *scene, Object *ob, @@ -2341,21 +2455,25 @@ static void lineart_object_load_single_instance(LineartRenderBuffer *rb, float use_mat[4][4], bool is_render, LineartObjectLoadTaskInfo *olti, - int thread_count) + int thread_count, + int obindex) { - LineartObjectInfo *obi = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartObjectInfo)); + LineartObjectInfo *obi = lineart_mem_acquire(&ld->render_data_pool, sizeof(LineartObjectInfo)); obi->usage = lineart_usage_check(scene->master_collection, ob, is_render); obi->override_intersection_mask = lineart_intersection_mask_check(scene->master_collection, ob); + obi->intersection_priority = lineart_intersection_priority_check(scene->master_collection, ob); Mesh *use_mesh; if (obi->usage == OBJECT_LRT_EXCLUDE) { return; } + obi->obindex = obindex << LRT_OBINDEX_SHIFT; + /* Prepare the matrix used for transforming this specific object (instance). This has to be * done before mesh boundbox check because the function needs that. */ - mul_m4db_m4db_m4fl_uniq(obi->model_view_proj, rb->view_projection, use_mat); - mul_m4db_m4db_m4fl_uniq(obi->model_view, rb->view, use_mat); + mul_m4db_m4db_m4fl_uniq(obi->model_view_proj, ld->conf.view_projection, use_mat); + mul_m4db_m4db_m4fl_uniq(obi->model_view, ld->conf.view, use_mat); if (!ELEM(ob->type, OB_MESH, OB_MBALL, OB_CURVES_LEGACY, OB_SURF, OB_FONT)) { return; @@ -2377,7 +2495,8 @@ static void lineart_object_load_single_instance(LineartRenderBuffer *rb, return; } - if (!lineart_geometry_check_visible(obi->model_view_proj, rb->shift_x, rb->shift_y, use_mesh)) { + if (!lineart_geometry_check_visible( + obi->model_view_proj, ld->conf.shift_x, ld->conf.shift_y, use_mesh)) { return; } @@ -2396,89 +2515,105 @@ static void lineart_object_load_single_instance(LineartRenderBuffer *rb, lineart_geometry_load_assign_thread(olti, obi, thread_count, use_mesh->totpoly); } -static void lineart_main_load_geometries( - Depsgraph *depsgraph, - Scene *scene, - Object *camera /* Still use camera arg for convenience. */, - LineartRenderBuffer *rb, - bool allow_duplicates) +void lineart_main_load_geometries(Depsgraph *depsgraph, + Scene *scene, + Object *camera /* Still use camera arg for convenience. */, + LineartData *ld, + bool allow_duplicates, + bool do_shadow_casting, + ListBase *shadow_elns) { double proj[4][4], view[4][4], result[4][4]; float inv[4][4]; - Camera *cam = camera->data; - float sensor = BKE_camera_sensor_size(cam->sensor_fit, cam->sensor_x, cam->sensor_y); - int fit = BKE_camera_sensor_fit(cam->sensor_fit, rb->w, rb->h); - double asp = ((double)rb->w / (double)rb->h); - - int bound_box_discard_count = 0; - if (cam->type == CAM_PERSP) { - if (fit == CAMERA_SENSOR_FIT_VERT && asp > 1) { - sensor *= asp; + if (!do_shadow_casting) { + Camera *cam = camera->data; + float sensor = BKE_camera_sensor_size(cam->sensor_fit, cam->sensor_x, cam->sensor_y); + int fit = BKE_camera_sensor_fit(cam->sensor_fit, ld->w, ld->h); + double asp = ((double)ld->w / (double)ld->h); + if (cam->type == CAM_PERSP) { + if (fit == CAMERA_SENSOR_FIT_VERT && asp > 1) { + sensor *= asp; + } + if (fit == CAMERA_SENSOR_FIT_HOR && asp < 1) { + sensor /= asp; + } + const double fov = focallength_to_fov(cam->lens / (1 + ld->conf.overscan), sensor); + lineart_matrix_perspective_44d(proj, fov, asp, cam->clip_start, cam->clip_end); } - if (fit == CAMERA_SENSOR_FIT_HOR && asp < 1) { - sensor /= asp; + else if (cam->type == CAM_ORTHO) { + const double w = cam->ortho_scale / 2; + lineart_matrix_ortho_44d(proj, -w, w, -w / asp, w / asp, cam->clip_start, cam->clip_end); } - const double fov = focallength_to_fov(cam->lens / (1 + rb->overscan), sensor); - lineart_matrix_perspective_44d(proj, fov, asp, cam->clip_start, cam->clip_end); - } - else if (cam->type == CAM_ORTHO) { - const double w = cam->ortho_scale / 2; - lineart_matrix_ortho_44d(proj, -w, w, -w / asp, w / asp, cam->clip_start, cam->clip_end); + + invert_m4_m4(inv, ld->conf.cam_obmat); + mul_m4db_m4db_m4fl_uniq(result, proj, inv); + copy_m4_m4_db(proj, result); + copy_m4_m4_db(ld->conf.view_projection, proj); + + unit_m4_db(view); + copy_m4_m4_db(ld->conf.view, view); } - double t_start; + BLI_listbase_clear(&ld->geom.triangle_buffer_pointers); + BLI_listbase_clear(&ld->geom.vertex_buffer_pointers); + double t_start; if (G.debug_value == 4000) { t_start = PIL_check_seconds_timer(); } - invert_m4_m4(inv, rb->cam_obmat); - mul_m4db_m4db_m4fl_uniq(result, proj, inv); - copy_m4_m4_db(proj, result); - copy_m4_m4_db(rb->view_projection, proj); - - unit_m4_db(view); - copy_m4_m4_db(rb->view, view); - - BLI_listbase_clear(&rb->triangle_buffer_pointers); - BLI_listbase_clear(&rb->vertex_buffer_pointers); - - int thread_count = rb->thread_count; + int thread_count = ld->thread_count; + int bound_box_discard_count = 0; + int obindex = 0; - /* This memory is in render buffer memory pool. so we don't need to free those after loading. - */ + /* This memory is in render buffer memory pool. So we don't need to free those after loading. */ LineartObjectLoadTaskInfo *olti = lineart_mem_acquire( - &rb->render_data_pool, sizeof(LineartObjectLoadTaskInfo) * thread_count); + &ld->render_data_pool, sizeof(LineartObjectLoadTaskInfo) * thread_count); eEvaluationMode eval_mode = DEG_get_mode(depsgraph); bool is_render = eval_mode == DAG_EVAL_RENDER; - FOREACH_SCENE_OBJECT_BEGIN (scene, ob) { + int flags = DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET | + DEG_ITER_OBJECT_FLAG_VISIBLE; + + /* Instance duplicated & particles. */ + if (allow_duplicates) { + flags |= DEG_ITER_OBJECT_FLAG_DUPLI; + } + + /* XXX(@Yiming): Temporary solution, this iterator is technically unsafe to use *during* + * depsgraph evaluation, see D14997 for detailed explanations. */ + DEG_OBJECT_ITER_BEGIN (depsgraph, ob, flags) { + + obindex++; + Object *eval_ob = DEG_get_evaluated_object(depsgraph, ob); if (!eval_ob) { continue; } + /* DEG_OBJECT_ITER_BEGIN will include the instanced mesh of these curve object types, so don't + * load them twice. */ + if (allow_duplicates && ELEM(ob->type, OB_CURVES_LEGACY, OB_FONT, OB_SURF)) { + continue; + } + if (BKE_object_visibility(eval_ob, eval_mode) & OB_VISIBLE_SELF) { - lineart_object_load_single_instance( - rb, depsgraph, scene, eval_ob, eval_ob, eval_ob->obmat, is_render, olti, thread_count); - } - if (allow_duplicates) { - ListBase *dupli = object_duplilist(depsgraph, scene, eval_ob); - LISTBASE_FOREACH (DupliObject *, dob, dupli) { - if (BKE_object_visibility(eval_ob, eval_mode) & - (OB_VISIBLE_PARTICLES | OB_VISIBLE_INSTANCES)) { - Object *ob_ref = (dob->type & OB_DUPLIPARTS) ? eval_ob : dob->ob; - lineart_object_load_single_instance( - rb, depsgraph, scene, dob->ob, ob_ref, dob->mat, is_render, olti, thread_count); - } - } - free_object_duplilist(dupli); + lineart_object_load_single_instance(ld, + depsgraph, + scene, + eval_ob, + eval_ob, + eval_ob->obmat, + is_render, + olti, + thread_count, + obindex); } } - FOREACH_SCENE_OBJECT_END; + DEG_OBJECT_ITER_END; TaskPool *tp = BLI_task_pool_create(NULL, TASK_PRIORITY_HIGH); @@ -2486,7 +2621,8 @@ static void lineart_main_load_geometries( printf("thread count: %d\n", thread_count); } for (int i = 0; i < thread_count; i++) { - olti[i].rb = rb; + olti[i].ld = ld; + olti[i].shadow_elns = shadow_elns; olti[i].thread_id = i; BLI_task_pool_push(tp, (TaskRunFunction)lineart_object_load_worker, &olti[i], 0, NULL); } @@ -2506,7 +2642,7 @@ static void lineart_main_load_geometries( edge_count += obi->pending_edges.next; } } - lineart_finalize_object_edge_array_reserve(&rb->pending_edges, edge_count); + lineart_finalize_object_edge_array_reserve(&ld->pending_edges, edge_count); for (int i = 0; i < thread_count; i++) { for (LineartObjectInfo *obi = olti[i].pending; obi; obi = obi->next) { @@ -2524,7 +2660,7 @@ static void lineart_main_load_geometries( * same numeric index to come close together. */ obi->global_i_offset = global_i; global_i += v_count; - lineart_finalize_object_edge_array(&rb->pending_edges, obi); + lineart_finalize_object_edge_array(&ld->pending_edges, obi); } } @@ -2562,14 +2698,25 @@ static bool lineart_triangle_get_other_verts(const LineartTriangle *tri, return false; } -static bool lineart_edge_from_triangle(const LineartTriangle *tri, - const LineartEdge *e, - bool allow_overlapping_edges) +bool lineart_edge_from_triangle(const LineartTriangle *tri, + const LineartEdge *e, + bool allow_overlapping_edges) { - /* Normally we just determine from the pointer address. */ - if (e->t1 == tri || e->t2 == tri) { - return true; + const LineartEdge *use_e = e; + if (e->flags & LRT_EDGE_FLAG_LIGHT_CONTOUR) { + if (((e->target_reference & LRT_LIGHT_CONTOUR_TARGET) == tri->target_reference) || + (((e->target_reference >> 32) & LRT_LIGHT_CONTOUR_TARGET) == tri->target_reference)) { + return true; + } } + else { + /* Normally we just determine from identifiers of adjacent triangles. */ + if ((use_e->t1 && use_e->t1->target_reference == tri->target_reference) || + (use_e->t2 && use_e->t2->target_reference == tri->target_reference)) { + return true; + } + } + /* If allows overlapping, then we compare the vertex coordinates one by one to determine if one * edge is from specific triangle. This is slower but can handle edge split cases very well. */ if (allow_overlapping_edges) { @@ -2618,6 +2765,9 @@ static bool lineart_edge_from_triangle(const LineartTriangle *tri, (num > is[order[1]] ? order[1] : (num > is[order[0]] ? order[0] : -1))); \ } +#define LRT_ISEC(index) (index == 0 ? isec_e1 : (index == 1 ? isec_e2 : isec_e3)) +#define LRT_PARALLEL(index) (index == 0 ? para_e1 : (index == 1 ? para_e2 : para_e3)) + /** * This is the main function to calculate * the occlusion status between 1(one) triangle and 1(one) line. @@ -2631,7 +2781,7 @@ static bool lineart_edge_from_triangle(const LineartTriangle *tri, * extruding from one of the triangle's point. To get the information using one math process can * solve this problem. * - * 2) Currently using discrete a/b/c/pa/pb/pc/is[3] values for storing + * 2) Currently using discrete a/b/c/para_e1/para_e2/para_e3/is[3] values for storing * intersection/edge_aligned/intersection_order info, which isn't optimal, needs a better * representation (likely a struct) for readability and clarity of code path. * @@ -2640,31 +2790,32 @@ static bool lineart_edge_from_triangle(const LineartTriangle *tri, * While current "edge aligned" fix isn't ideal, it does solve most of the precision issue * especially in orthographic camera mode. */ -static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl), - const LineartTriangle *tri, +static bool lineart_triangle_edge_image_space_occlusion(const LineartTriangle *tri, const LineartEdge *e, const double *override_camera_loc, const bool override_cam_is_persp, const bool allow_overlapping_edges, - const double vp[4][4], - const double *camera_dir, + const double m_view_projection[4][4], + const double camera_dir[3], const float cam_shift_x, const float cam_shift_y, double *from, double *to) { - double is[3] = {0}; - int order[3]; - int LCross = -1, RCross = -1; - int a, b, c; /* Crossing info. */ - bool pa, pb, pc; /* Parallel info. */ - int st_l = 0, st_r = 0; - - double Lv[3]; - double Rv[3]; - double vd4[4]; - double Cv[3]; - double dot_l, dot_r, dot_la, dot_ra; + double cross_ratios[3] = {0}; + int cross_order[3]; + int cross_v1 = -1, cross_v2 = -1; + /* If the edge intersects with the triangle edges (including extensions). */ + int isec_e1, isec_e2, isec_e3; + /* If edge is parallel to one of the edges in the triangle. */ + bool para_e1, para_e2, para_e3; + enum LineartPointTri state_v1 = LRT_OUTSIDE_TRIANGLE, state_v2 = LRT_OUTSIDE_TRIANGLE; + + double dir_v1[3]; + double dir_v2[3]; + double view_vector[4]; + double dir_cam[3]; + double dot_v1, dot_v2, dot_v1a, dot_v2a; double dot_f; double gloc[4], trans[4]; double cut = -1; @@ -2687,31 +2838,37 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl), } /* Check if the line visually crosses one of the edge in the triangle. */ - a = lineart_intersect_seg_seg(LFBC, RFBC, FBC0, FBC1, &is[0], &pa); - b = lineart_intersect_seg_seg(LFBC, RFBC, FBC1, FBC2, &is[1], &pb); - c = lineart_intersect_seg_seg(LFBC, RFBC, FBC2, FBC0, &is[2], &pc); + isec_e1 = lineart_intersect_seg_seg(LFBC, RFBC, FBC0, FBC1, &cross_ratios[0], ¶_e1); + isec_e2 = lineart_intersect_seg_seg(LFBC, RFBC, FBC1, FBC2, &cross_ratios[1], ¶_e2); + isec_e3 = lineart_intersect_seg_seg(LFBC, RFBC, FBC2, FBC0, &cross_ratios[2], ¶_e3); /* Sort the intersection distance. */ - INTERSECT_SORT_MIN_TO_MAX_3(is[0], is[1], is[2], order); + INTERSECT_SORT_MIN_TO_MAX_3(cross_ratios[0], cross_ratios[1], cross_ratios[2], cross_order); - sub_v3_v3v3_db(Lv, e->v1->gloc, tri->v[0]->gloc); - sub_v3_v3v3_db(Rv, e->v2->gloc, tri->v[0]->gloc); + sub_v3_v3v3_db(dir_v1, e->v1->gloc, tri->v[0]->gloc); + sub_v3_v3v3_db(dir_v2, e->v2->gloc, tri->v[0]->gloc); - copy_v3_v3_db(Cv, camera_dir); - - if (override_cam_is_persp) { - copy_v3_v3_db(vd4, override_camera_loc); - } - else { - copy_v4_v4_db(vd4, override_camera_loc); - } + copy_v3_v3_db(dir_cam, camera_dir); + copy_v3_v3_db(view_vector, override_camera_loc); if (override_cam_is_persp) { - sub_v3_v3v3_db(Cv, vd4, tri->v[0]->gloc); + sub_v3_v3v3_db(dir_cam, view_vector, tri->v[0]->gloc); } - dot_l = dot_v3v3_db(Lv, tri->gn); - dot_r = dot_v3v3_db(Rv, tri->gn); - dot_f = dot_v3v3_db(Cv, tri->gn); + dot_v1 = dot_v3v3_db(dir_v1, tri->gn); + dot_v2 = dot_v3v3_db(dir_v2, tri->gn); + dot_f = dot_v3v3_db(dir_cam, tri->gn); + + if ((e->flags & LRT_EDGE_FLAG_PROJECTED_SHADOW) && + (e->target_reference == tri->target_reference)) { + if (((dot_f > 0) && (e->flags & LRT_EDGE_FLAG_SHADOW_FACING_LIGHT)) || + ((dot_f < 0) && (!(e->flags & LRT_EDGE_FLAG_SHADOW_FACING_LIGHT)))) { + *from = 0.0f; + *to = 1.0f; + return true; + } + + return false; + } /* NOTE(Yiming): When we don't use `dot_f==0` here, it's theoretically possible that _some_ * faces in perspective mode would get erroneously caught in this condition where they really @@ -2722,46 +2879,45 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl), return false; } + /* Whether two end points are inside/on_the_edge/outside of the triangle. */ + state_v1 = lineart_point_triangle_relation(LFBC, FBC0, FBC1, FBC2); + state_v2 = lineart_point_triangle_relation(RFBC, FBC0, FBC1, FBC2); + /* If the edge doesn't visually cross any edge of the triangle... */ - if (!a && !b && !c) { + if (!isec_e1 && !isec_e2 && !isec_e3) { /* And if both end point from the edge is outside of the triangle... */ - if (!(st_l = lineart_point_triangle_relation(LFBC, FBC0, FBC1, FBC2)) && - !(st_r = lineart_point_triangle_relation(RFBC, FBC0, FBC1, FBC2))) { + if ((!state_v1) && (!state_v2)) { return 0; /* We don't have any occlusion. */ } } - /* Whether two end points are inside/on_the_edge/outside of the triangle. */ - st_l = lineart_point_triangle_relation(LFBC, FBC0, FBC1, FBC2); - st_r = lineart_point_triangle_relation(RFBC, FBC0, FBC1, FBC2); - /* Determine the cut position. */ - dot_la = fabs(dot_l); - if (dot_la < DBL_EPSILON) { - dot_la = 0; - dot_l = 0; + dot_v1a = fabs(dot_v1); + if (dot_v1a < DBL_EPSILON) { + dot_v1a = 0; + dot_v1 = 0; } - dot_ra = fabs(dot_r); - if (dot_ra < DBL_EPSILON) { - dot_ra = 0; - dot_r = 0; + dot_v2a = fabs(dot_v2); + if (dot_v2a < DBL_EPSILON) { + dot_v2a = 0; + dot_v2 = 0; } - if (dot_l - dot_r == 0) { + if (dot_v1 - dot_v2 == 0) { cut = 100000; } - else if (dot_l * dot_r <= 0) { - cut = dot_la / fabs(dot_l - dot_r); + else if (dot_v1 * dot_v2 <= 0) { + cut = dot_v1a / fabs(dot_v1 - dot_v2); } else { - cut = fabs(dot_r + dot_l) / fabs(dot_l - dot_r); - cut = dot_ra > dot_la ? 1 - cut : cut; + cut = fabs(dot_v2 + dot_v1) / fabs(dot_v1 - dot_v2); + cut = dot_v2a > dot_v1a ? 1 - cut : cut; } /* Transform the cut from geometry space to image space. */ if (override_cam_is_persp) { interp_v3_v3v3_db(gloc, e->v1->gloc, e->v2->gloc, cut); - mul_v4_m4v3_db(trans, vp, gloc); + mul_v4_m4v3_db(trans, m_view_projection, gloc); mul_v3db_db(trans, (1 / trans[3])); trans[0] -= cam_shift_x * 2; trans[1] -= cam_shift_y * 2; @@ -2776,7 +2932,7 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl), } #define LRT_GUARD_NOT_FOUND \ - if (LCross < 0 || RCross < 0) { \ + if (cross_v1 < 0 || cross_v2 < 0) { \ return false; \ } @@ -2784,95 +2940,97 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl), * indicates triangle boundary. DBL_TRIANGLE_LIM is needed to for floating point precision * tolerance. */ - if (st_l == 2) { + if (state_v1 == LRT_INSIDE_TRIANGLE) { /* Left side is in the triangle. */ - if (st_r == 2) { + if (state_v2 == LRT_INSIDE_TRIANGLE) { /* | l---r | */ - INTERSECT_JUST_SMALLER(is, order, DBL_TRIANGLE_LIM, LCross); - INTERSECT_JUST_GREATER(is, order, 1 - DBL_TRIANGLE_LIM, RCross); + INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v1); + INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v2); } - else if (st_r == 1) { + else if (state_v2 == LRT_ON_TRIANGLE) { /* | l------r| */ - INTERSECT_JUST_SMALLER(is, order, DBL_TRIANGLE_LIM, LCross); - INTERSECT_JUST_GREATER(is, order, 1 - DBL_TRIANGLE_LIM, RCross); + INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v1); + INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v2); } - else if (st_r == 0) { + else if (state_v2 == LRT_OUTSIDE_TRIANGLE) { /* | l-------|------r */ - INTERSECT_JUST_SMALLER(is, order, DBL_TRIANGLE_LIM, LCross); - INTERSECT_JUST_GREATER(is, order, 0, RCross); + INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v1); + INTERSECT_JUST_GREATER(cross_ratios, cross_order, 0, cross_v2); } } - else if (st_l == 1) { + else if (state_v1 == LRT_ON_TRIANGLE) { /* Left side is on some edge of the triangle. */ - if (st_r == 2) { + if (state_v2 == LRT_INSIDE_TRIANGLE) { /* |l------r | */ - INTERSECT_JUST_SMALLER(is, order, DBL_TRIANGLE_LIM, LCross); - INTERSECT_JUST_GREATER(is, order, 1 - DBL_TRIANGLE_LIM, RCross); + INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v1); + INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v2); } - else if (st_r == 1) { + else if (state_v2 == LRT_ON_TRIANGLE) { /* |l---------r| */ - INTERSECT_JUST_SMALLER(is, order, DBL_TRIANGLE_LIM, LCross); - INTERSECT_JUST_GREATER(is, order, 1 - DBL_TRIANGLE_LIM, RCross); + INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v1); + INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v2); } - else if (st_r == 0) { + else if (state_v2 == LRT_OUTSIDE_TRIANGLE) { /* |l----------|-------r (crossing the triangle) [OR] * r---------|l | (not crossing the triangle) */ - INTERSECT_JUST_GREATER(is, order, DBL_TRIANGLE_LIM, RCross); - if (RCross >= 0 && LRT_ABC(RCross) && is[RCross] > (DBL_TRIANGLE_LIM)) { - INTERSECT_JUST_SMALLER(is, order, DBL_TRIANGLE_LIM, LCross); + INTERSECT_JUST_GREATER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v2); + if (cross_v2 >= 0 && LRT_ISEC(cross_v2) && cross_ratios[cross_v2] > (DBL_TRIANGLE_LIM)) { + INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v1); } else { - INTERSECT_JUST_SMALLER(is, order, DBL_TRIANGLE_LIM, RCross); - if (RCross > 0) { - INTERSECT_JUST_SMALLER(is, order, is[RCross], LCross); + INTERSECT_JUST_SMALLER(cross_ratios, cross_order, DBL_TRIANGLE_LIM, cross_v2); + if (cross_v2 > 0) { + INTERSECT_JUST_SMALLER(cross_ratios, cross_order, cross_ratios[cross_v2], cross_v1); } } LRT_GUARD_NOT_FOUND /* We could have the edge being completely parallel to the triangle where there isn't a * viable occlusion result. */ - if ((LRT_PABC(LCross) && !LRT_ABC(LCross)) || (LRT_PABC(RCross) && !LRT_ABC(RCross))) { + if ((LRT_PARALLEL(cross_v1) && !LRT_ISEC(cross_v1)) || + (LRT_PARALLEL(cross_v2) && !LRT_ISEC(cross_v2))) { return false; } } } - else if (st_l == 0) { + else if (state_v1 == LRT_OUTSIDE_TRIANGLE) { /* Left side is outside of the triangle. */ - if (st_r == 2) { + if (state_v2 == LRT_INSIDE_TRIANGLE) { /* l---|---r | */ - INTERSECT_JUST_SMALLER(is, order, 1 - DBL_TRIANGLE_LIM, LCross); - INTERSECT_JUST_GREATER(is, order, 1 - DBL_TRIANGLE_LIM, RCross); + INTERSECT_JUST_SMALLER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v1); + INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v2); } - else if (st_r == 1) { + else if (state_v2 == LRT_ON_TRIANGLE) { /* |r----------|-------l (crossing the triangle) [OR] * l---------|r | (not crossing the triangle) */ - INTERSECT_JUST_SMALLER(is, order, 1 - DBL_TRIANGLE_LIM, LCross); - if (LCross >= 0 && LRT_ABC(LCross) && is[LCross] < (1 - DBL_TRIANGLE_LIM)) { - INTERSECT_JUST_GREATER(is, order, 1 - DBL_TRIANGLE_LIM, RCross); + INTERSECT_JUST_SMALLER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v1); + if (cross_v1 >= 0 && LRT_ISEC(cross_v1) && cross_ratios[cross_v1] < (1 - DBL_TRIANGLE_LIM)) { + INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v2); } else { - INTERSECT_JUST_GREATER(is, order, 1 - DBL_TRIANGLE_LIM, LCross); - if (LCross > 0) { - INTERSECT_JUST_GREATER(is, order, is[LCross], RCross); + INTERSECT_JUST_GREATER(cross_ratios, cross_order, 1 - DBL_TRIANGLE_LIM, cross_v1); + if (cross_v1 > 0) { + INTERSECT_JUST_GREATER(cross_ratios, cross_order, cross_ratios[cross_v1], cross_v2); } } LRT_GUARD_NOT_FOUND /* The same logic applies as above case. */ - if ((LRT_PABC(LCross) && !LRT_ABC(LCross)) || (LRT_PABC(RCross) && !LRT_ABC(RCross))) { + if ((LRT_PARALLEL(cross_v1) && !LRT_ISEC(cross_v1)) || + (LRT_PARALLEL(cross_v2) && !LRT_ISEC(cross_v2))) { return false; } } - else if (st_r == 0) { + else if (state_v2 == LRT_OUTSIDE_TRIANGLE) { /* l---|----|----r (crossing the triangle) [OR] * l----r | | (not crossing the triangle) */ - INTERSECT_JUST_GREATER(is, order, -DBL_TRIANGLE_LIM, LCross); - if (LCross >= 0 && LRT_ABC(LCross)) { - INTERSECT_JUST_GREATER(is, order, is[LCross], RCross); + INTERSECT_JUST_GREATER(cross_ratios, cross_order, -DBL_TRIANGLE_LIM, cross_v1); + if (cross_v1 >= 0 && LRT_ISEC(cross_v1)) { + INTERSECT_JUST_GREATER(cross_ratios, cross_order, cross_ratios[cross_v1], cross_v2); } else { - if (LCross >= 0) { - INTERSECT_JUST_GREATER(is, order, is[LCross], LCross); - if (LCross >= 0) { - INTERSECT_JUST_GREATER(is, order, is[LCross], RCross); + if (cross_v1 >= 0) { + INTERSECT_JUST_GREATER(cross_ratios, cross_order, cross_ratios[cross_v1], cross_v1); + if (cross_v1 >= 0) { + INTERSECT_JUST_GREATER(cross_ratios, cross_order, cross_ratios[cross_v1], cross_v2); } } } @@ -2881,28 +3039,28 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl), LRT_GUARD_NOT_FOUND - double LF = dot_l * dot_f, RF = dot_r * dot_f; + double dot_1f = dot_v1 * dot_f, dot_2f = dot_v2 * dot_f; /* Determine the start and end point of image space cut on a line. */ - if (LF <= 0 && RF <= 0 && (dot_l || dot_r)) { - *from = MAX2(0, is[LCross]); - *to = MIN2(1, is[RCross]); + if (dot_1f <= 0 && dot_2f <= 0 && (dot_v1 || dot_v2)) { + *from = MAX2(0, cross_ratios[cross_v1]); + *to = MIN2(1, cross_ratios[cross_v2]); if (*from >= *to) { return false; } return true; } - if (LF >= 0 && RF <= 0 && (dot_l || dot_r)) { - *from = MAX2(cut, is[LCross]); - *to = MIN2(1, is[RCross]); + if (dot_1f >= 0 && dot_2f <= 0 && (dot_v1 || dot_v2)) { + *from = MAX2(cut, cross_ratios[cross_v1]); + *to = MIN2(1, cross_ratios[cross_v2]); if (*from >= *to) { return false; } return true; } - if (LF <= 0 && RF >= 0 && (dot_l || dot_r)) { - *from = MAX2(0, is[LCross]); - *to = MIN2(cut, is[RCross]); + if (dot_1f <= 0 && dot_2f >= 0 && (dot_v1 || dot_v2)) { + *from = MAX2(0, cross_ratios[cross_v1]); + *to = MIN2(cut, cross_ratios[cross_v2]); if (*from >= *to) { return false; } @@ -2916,6 +3074,8 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl), #undef INTERSECT_SORT_MIN_TO_MAX_3 #undef INTERSECT_JUST_GREATER #undef INTERSECT_JUST_SMALLER +#undef LRT_ISEC +#undef LRT_PARALLEL /** * At this stage of the computation we don't have triangle adjacent info anymore, @@ -2997,272 +3157,242 @@ static LineartVert *lineart_triangle_share_point(const LineartTriangle *l, return NULL; } -/** - * To save time and prevent overlapping lines when computing intersection lines. - */ -static bool lineart_vert_already_intersected_2v(LineartVertIntersection *vt, - LineartVertIntersection *v1, - LineartVertIntersection *v2) -{ - return ((vt->isec1 == v1->base.index && vt->isec2 == v2->base.index) || - (vt->isec2 == v2->base.index && vt->isec1 == v1->base.index)); -} - -static void lineart_vert_set_intersection_2v(LineartVert *vt, LineartVert *v1, LineartVert *v2) -{ - LineartVertIntersection *irv = (LineartVertIntersection *)vt; - irv->isec1 = v1->index; - irv->isec2 = v2->index; -} - -/** - * This tests a triangle against a virtual line represented by `v1---v2`. - * The vertices returned after repeated calls to this function - * is then used to create a triangle/triangle intersection line. - */ -static LineartVert *lineart_triangle_2v_intersection_test(LineartRenderBuffer *rb, - LineartVert *v1, - LineartVert *v2, - LineartTriangle *tri, - LineartTriangle *testing, - LineartVert *last) +static bool lineart_triangle_2v_intersection_math( + LineartVert *v1, LineartVert *v2, LineartTriangle *tri, const double *last, double *rv) { - double Lv[3]; - double Rv[3]; - double dot_l, dot_r; - LineartVert *result; + /* Direction vectors for the edge verts. We will check if the verts are on the same side of the + * triangle or not. */ + double dir_v1[3], dir_v2[3]; + double dot_v1, dot_v2; double gloc[3]; - LineartVert *l = v1, *r = v2; - for (LinkNode *ln = (void *)testing->intersecting_verts; ln; ln = ln->next) { - LineartVertIntersection *vt = ln->link; - if (vt->intersecting_with == tri && - lineart_vert_already_intersected_2v( - vt, (LineartVertIntersection *)l, (LineartVertIntersection *)r)) { - return (LineartVert *)vt; - } - } - - sub_v3_v3v3_db(Lv, l->gloc, testing->v[0]->gloc); - sub_v3_v3v3_db(Rv, r->gloc, testing->v[0]->gloc); + sub_v3_v3v3_db(dir_v1, v1->gloc, tri->v[0]->gloc); + sub_v3_v3v3_db(dir_v2, v2->gloc, tri->v[0]->gloc); - dot_l = dot_v3v3_db(Lv, testing->gn); - dot_r = dot_v3v3_db(Rv, testing->gn); + dot_v1 = dot_v3v3_db(dir_v1, tri->gn); + dot_v2 = dot_v3v3_db(dir_v2, tri->gn); - if (dot_l * dot_r > 0 || (!dot_l && !dot_r)) { - return 0; + if (dot_v1 * dot_v2 > 0 || (!dot_v1 && !dot_v2)) { + return false; } - dot_l = fabs(dot_l); - dot_r = fabs(dot_r); + dot_v1 = fabs(dot_v1); + dot_v2 = fabs(dot_v2); - interp_v3_v3v3_db(gloc, l->gloc, r->gloc, dot_l / (dot_l + dot_r)); + interp_v3_v3v3_db(gloc, v1->gloc, v2->gloc, dot_v1 / (dot_v1 + dot_v2)); - /* Due to precision issue, we might end up with the same point as the one we already detected. - */ - if (last && LRT_DOUBLE_CLOSE_ENOUGH(last->gloc[0], gloc[0]) && - LRT_DOUBLE_CLOSE_ENOUGH(last->gloc[1], gloc[1]) && - LRT_DOUBLE_CLOSE_ENOUGH(last->gloc[2], gloc[2])) { - return NULL; + /* Due to precision issue, we might end up with the same point as the one we already detected. */ + if (last && LRT_DOUBLE_CLOSE_ENOUGH(last[0], gloc[0]) && + LRT_DOUBLE_CLOSE_ENOUGH(last[1], gloc[1]) && LRT_DOUBLE_CLOSE_ENOUGH(last[2], gloc[2])) { + return false; } if (!(lineart_point_inside_triangle3d( - gloc, testing->v[0]->gloc, testing->v[1]->gloc, testing->v[2]->gloc))) { - return NULL; + gloc, tri->v[0]->gloc, tri->v[1]->gloc, tri->v[2]->gloc))) { + return false; } - /* This is an intersection vert, the size is bigger than LineartVert, - * allocated separately. */ - result = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartVertIntersection)); - - /* Indicate the data structure difference. */ - result->flag = LRT_VERT_HAS_INTERSECTION_DATA; + copy_v3_v3_db(rv, gloc); - copy_v3_v3_db(result->gloc, gloc); - - lineart_prepend_pool(&testing->intersecting_verts, &rb->render_data_pool, result); - - return result; + return true; } -/** - * Test if two triangles intersect. Generates one intersection line if the check succeeds. - */ -static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb, - LineartTriangle *tri, - LineartTriangle *testing) +static bool lineart_triangle_intersect_math(LineartTriangle *tri, + LineartTriangle *t2, + double *v1, + double *v2) { - LineartVert *v1 = 0, *v2 = 0; - LineartVert **next = &v1; - LineartEdge *result; - LineartVert *E0T = 0; - LineartVert *E1T = 0; - LineartVert *E2T = 0; - LineartVert *TE0 = 0; - LineartVert *TE1 = 0; - LineartVert *TE2 = 0; + double *next = v1, *last = NULL; LineartVert *sv1, *sv2; - double cl[3]; - double ZMin, ZMax; - ZMax = rb->far_clip; - ZMin = rb->near_clip; - copy_v3_v3_db(cl, rb->camera_pos); - LineartVert *share = lineart_triangle_share_point(testing, tri); + LineartVert *share = lineart_triangle_share_point(t2, tri); if (share) { /* If triangles have sharing points like `abc` and `acd`, then we only need to detect `bc` * against `acd` or `cd` against `abc`. */ - LineartVert *new_share; lineart_triangle_get_other_verts(tri, share, &sv1, &sv2); - v1 = new_share = lineart_mem_acquire(&rb->render_data_pool, (sizeof(LineartVertIntersection))); - - new_share->flag = LRT_VERT_HAS_INTERSECTION_DATA; - - copy_v3_v3_db(new_share->gloc, share->gloc); + copy_v3_v3_db(v1, share->gloc); - v2 = lineart_triangle_2v_intersection_test(rb, sv1, sv2, tri, testing, 0); - - if (v2 == NULL) { - lineart_triangle_get_other_verts(testing, share, &sv1, &sv2); - v2 = lineart_triangle_2v_intersection_test(rb, sv1, sv2, testing, tri, 0); - if (v2 == NULL) { - return 0; + if (!lineart_triangle_2v_intersection_math(sv1, sv2, t2, 0, v2)) { + lineart_triangle_get_other_verts(t2, share, &sv1, &sv2); + if (lineart_triangle_2v_intersection_math(sv1, sv2, tri, 0, v2)) { + return true; } - lineart_prepend_pool(&testing->intersecting_verts, &rb->render_data_pool, new_share); - } - else { - lineart_prepend_pool(&tri->intersecting_verts, &rb->render_data_pool, new_share); } } else { /* If not sharing any points, then we need to try all the possibilities. */ - E0T = lineart_triangle_2v_intersection_test(rb, tri->v[0], tri->v[1], tri, testing, 0); - if (E0T && (!(*next))) { - (*next) = E0T; - lineart_vert_set_intersection_2v((*next), tri->v[0], tri->v[1]); - next = &v2; - } - E1T = lineart_triangle_2v_intersection_test(rb, tri->v[1], tri->v[2], tri, testing, v1); - if (E1T && (!(*next))) { - (*next) = E1T; - lineart_vert_set_intersection_2v((*next), tri->v[1], tri->v[2]); - next = &v2; - } - if (!(*next)) { - E2T = lineart_triangle_2v_intersection_test(rb, tri->v[2], tri->v[0], tri, testing, v1); - } - if (E2T && (!(*next))) { - (*next) = E2T; - lineart_vert_set_intersection_2v((*next), tri->v[2], tri->v[0]); - next = &v2; + if (lineart_triangle_2v_intersection_math(tri->v[0], tri->v[1], t2, 0, v1)) { + next = v2; + last = v1; } - if (!(*next)) { - TE0 = lineart_triangle_2v_intersection_test( - rb, testing->v[0], testing->v[1], testing, tri, v1); - } - if (TE0 && (!(*next))) { - (*next) = TE0; - lineart_vert_set_intersection_2v((*next), testing->v[0], testing->v[1]); - next = &v2; + if (lineart_triangle_2v_intersection_math(tri->v[1], tri->v[2], t2, last, next)) { + if (last) { + return true; + } + next = v2; + last = v1; } - if (!(*next)) { - TE1 = lineart_triangle_2v_intersection_test( - rb, testing->v[1], testing->v[2], testing, tri, v1); + if (lineart_triangle_2v_intersection_math(tri->v[2], tri->v[0], t2, last, next)) { + if (last) { + return true; + } + next = v2; + last = v1; } - if (TE1 && (!(*next))) { - (*next) = TE1; - lineart_vert_set_intersection_2v((*next), testing->v[1], testing->v[2]); - next = &v2; + + if (lineart_triangle_2v_intersection_math(t2->v[0], t2->v[1], tri, last, next)) { + if (last) { + return true; + } + next = v2; + last = v1; } - if (!(*next)) { - TE2 = lineart_triangle_2v_intersection_test( - rb, testing->v[2], testing->v[0], testing, tri, v1); + if (lineart_triangle_2v_intersection_math(t2->v[1], t2->v[2], tri, last, next)) { + if (last) { + return true; + } + next = v2; + last = v1; } - if (TE2 && (!(*next))) { - (*next) = TE2; - lineart_vert_set_intersection_2v((*next), testing->v[2], testing->v[0]); - next = &v2; + if (lineart_triangle_2v_intersection_math(t2->v[2], t2->v[0], tri, last, next)) { + if (last) { + return true; + } + next = v2; + last = v1; } + } + return false; +} - if (!(*next)) { - return 0; - } +static void lineart_add_isec_thread(LineartIsecThread *th, + const double *v1, + const double *v2, + LineartTriangle *tri1, + LineartTriangle *tri2) +{ + if (th->current == th->max) { + + LineartIsecSingle *new_array = MEM_mallocN(sizeof(LineartIsecSingle) * th->max * 2, + "LineartIsecSingle"); + memcpy(new_array, th->array, sizeof(LineartIsecSingle) * th->max); + th->max *= 2; + MEM_freeN(th->array); + th->array = new_array; + } + LineartIsecSingle *isec_single = &th->array[th->current]; + copy_v3fl_v3db(isec_single->v1, v1); + copy_v3fl_v3db(isec_single->v2, v2); + isec_single->tri1 = tri1; + isec_single->tri2 = tri2; + if (tri1->target_reference > tri2->target_reference) { + SWAP(LineartTriangle *, isec_single->tri1, isec_single->tri2); + } + th->current++; +} + +#define LRT_ISECT_TRIANGLE_PER_THREAD 4096 + +static bool lineart_schedule_new_triangle_task(LineartIsecThread *th) +{ + LineartData *ld = th->ld; + int remaining = LRT_ISECT_TRIANGLE_PER_THREAD; + + BLI_spin_lock(&ld->lock_task); + LineartElementLinkNode *eln = ld->isect_scheduled_up_to; + + if (!eln) { + BLI_spin_unlock(&ld->lock_task); + return false; } - /* The intersection line has been generated only in geometry space, so we need to transform - * them as well. */ - mul_v4_m4v3_db(v1->fbcoord, rb->view_projection, v1->gloc); - mul_v4_m4v3_db(v2->fbcoord, rb->view_projection, v2->gloc); - if (rb->cam_is_persp) { - mul_v3db_db(v1->fbcoord, (1 / v1->fbcoord[3])); - mul_v3db_db(v2->fbcoord, (1 / v2->fbcoord[3])); + th->pending_from = eln; + th->index_from = ld->isect_scheduled_up_to_index; + + while (remaining > 0 && eln) { + int remaining_this_eln = eln->element_count - ld->isect_scheduled_up_to_index; + int added_count = MIN2(remaining, remaining_this_eln); + remaining -= added_count; + if (remaining || added_count == remaining_this_eln) { + eln = eln->next; + ld->isect_scheduled_up_to = eln; + ld->isect_scheduled_up_to_index = 0; + } + else { + ld->isect_scheduled_up_to_index += added_count; + } } - v1->fbcoord[0] -= rb->shift_x * 2; - v1->fbcoord[1] -= rb->shift_y * 2; - v2->fbcoord[0] -= rb->shift_x * 2; - v2->fbcoord[1] -= rb->shift_y * 2; - /* This z transformation is not the same as the rest of the part, because the data don't go - * through normal perspective division calls in the pipeline, but this way the 3D result and - * occlusion on the generated line is correct, and we don't really use 2D for viewport stroke - * generation anyway. */ - v1->fbcoord[2] = ZMin * ZMax / (ZMax - fabs(v1->fbcoord[2]) * (ZMax - ZMin)); - v2->fbcoord[2] = ZMin * ZMax / (ZMax - fabs(v2->fbcoord[2]) * (ZMax - ZMin)); + th->pending_to = eln ? eln : ld->geom.triangle_buffer_pointers.last; + th->index_to = ld->isect_scheduled_up_to_index; - ((LineartVertIntersection *)v1)->intersecting_with = tri; - ((LineartVertIntersection *)v2)->intersecting_with = testing; + BLI_spin_unlock(&ld->lock_task); - result = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdge)); - result->v1 = v1; - result->v2 = v2; - result->t1 = tri; - result->t2 = testing; + return true; +} - LineartEdgeSegment *es = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdgeSegment)); - BLI_addtail(&result->segments, es); - /* Don't need to OR flags right now, just a type mark. */ - result->flags = LRT_EDGE_FLAG_INTERSECTION; - result->intersection_mask = (tri->intersection_mask | testing->intersection_mask); +/* This function initializes two things: + * 1) Triangle array scheduling info, for each worker thread to get its chunk from the scheduler. + * 2) Per-thread intersection result array. Does not store actual #LineartEdge, these results will + * be finalized by #lineart_create_edges_from_isec_data + */ +static void lineart_init_isec_thread(LineartIsecData *d, LineartData *ld, int thread_count) +{ + d->threads = MEM_callocN(sizeof(LineartIsecThread) * thread_count, "LineartIsecThread arr"); + d->ld = ld; + d->thread_count = thread_count; - lineart_add_edge_to_array(&rb->pending_edges, result); + ld->isect_scheduled_up_to = ld->geom.triangle_buffer_pointers.first; + ld->isect_scheduled_up_to_index = 0; - return result; + for (int i = 0; i < thread_count; i++) { + LineartIsecThread *it = &d->threads[i]; + it->array = MEM_mallocN(sizeof(LineartIsecSingle) * 100, "LineartIsecSingle arr"); + it->max = 100; + it->current = 0; + it->thread_id = i; + it->ld = ld; + } } -static void lineart_triangle_intersect_in_bounding_area(LineartRenderBuffer *rb, - LineartTriangle *tri, - LineartBoundingArea *ba) +static void lineart_destroy_isec_thread(LineartIsecData *d) { - /* Testing_triangle->testing[0] is used to store pairing triangle reference. - * See definition of LineartTriangleThread for more info. */ - LineartTriangle *testing_triangle; - LineartTriangleThread *tt; + for (int i = 0; i < d->thread_count; i++) { + LineartIsecThread *it = &d->threads[i]; + MEM_freeN(it->array); + } + MEM_freeN(d->threads); +} - double *G0 = tri->v[0]->gloc, *G1 = tri->v[1]->gloc, *G2 = tri->v[2]->gloc; +static void lineart_triangle_intersect_in_bounding_area(LineartTriangle *tri, + LineartBoundingArea *ba, + LineartIsecThread *th, + int up_to) +{ + BLI_assert(th != NULL); - /* If this is not the smallest subdiv bounding area. */ - if (ba->child) { - lineart_triangle_intersect_in_bounding_area(rb, tri, &ba->child[0]); - lineart_triangle_intersect_in_bounding_area(rb, tri, &ba->child[1]); - lineart_triangle_intersect_in_bounding_area(rb, tri, &ba->child[2]); - lineart_triangle_intersect_in_bounding_area(rb, tri, &ba->child[3]); + if (!th) { return; } - /* If this _is_ the smallest subdiv bounding area, then do the intersections there. */ - for (int i = 0; i < ba->triangle_count; i++) { - testing_triangle = ba->linked_triangles[i]; - tt = (LineartTriangleThread *)testing_triangle; + double *G0 = tri->v[0]->gloc, *G1 = tri->v[1]->gloc, *G2 = tri->v[2]->gloc; - if (testing_triangle == tri || tt->testing_e[0] == (LineartEdge *)tri) { + /* If this _is_ the smallest subdivision bounding area, then do the intersections there. */ + for (int i = 0; i < up_to; i++) { + /* Testing_triangle->testing[0] is used to store pairing triangle reference. + * See definition of LineartTriangleThread for more info. */ + LineartTriangle *testing_triangle = ba->linked_triangles[i]; + LineartTriangleThread *tt = (LineartTriangleThread *)testing_triangle; + + if (testing_triangle == tri || tt->testing_e[th->thread_id] == (LineartEdge *)tri) { continue; } - tt->testing_e[0] = (LineartEdge *)tri; + tt->testing_e[th->thread_id] = (LineartEdge *)tri; if ((testing_triangle->flags & LRT_TRIANGLE_NO_INTERSECTION) || ((testing_triangle->flags & LRT_TRIANGLE_INTERSECTION_ONLY) && @@ -3285,65 +3415,106 @@ static void lineart_triangle_intersect_in_bounding_area(LineartRenderBuffer *rb, } /* If we do need to compute intersection, then finally do it. */ - lineart_triangle_intersect(rb, tri, testing_triangle); + + double iv1[3], iv2[3]; + if (lineart_triangle_intersect_math(tri, testing_triangle, iv1, iv2)) { + lineart_add_isec_thread(th, iv1, iv2, tri, testing_triangle); + } } } /** * The calculated view vector will point towards the far-plane from the camera position. */ -static void lineart_main_get_view_vector(LineartRenderBuffer *rb) +void lineart_main_get_view_vector(LineartData *ld) { float direction[3] = {0, 0, 1}; float trans[3]; float inv[4][4]; float obmat_no_scale[4][4]; - copy_m4_m4(obmat_no_scale, rb->cam_obmat); - + copy_m4_m4(obmat_no_scale, ld->conf.cam_obmat); normalize_v3(obmat_no_scale[0]); normalize_v3(obmat_no_scale[1]); normalize_v3(obmat_no_scale[2]); invert_m4_m4(inv, obmat_no_scale); transpose_m4(inv); mul_v3_mat3_m4v3(trans, inv, direction); - copy_m4_m4(rb->cam_obmat, obmat_no_scale); - copy_v3db_v3fl(rb->view_vector, trans); + copy_m4_m4(ld->conf.cam_obmat, obmat_no_scale); + copy_v3db_v3fl(ld->conf.view_vector, trans); + + if (ld->conf.light_reference_available) { + copy_m4_m4(obmat_no_scale, ld->conf.cam_obmat_secondary); + normalize_v3(obmat_no_scale[0]); + normalize_v3(obmat_no_scale[1]); + normalize_v3(obmat_no_scale[2]); + invert_m4_m4(inv, obmat_no_scale); + transpose_m4(inv); + mul_v3_mat3_m4v3(trans, inv, direction); + copy_m4_m4(ld->conf.cam_obmat_secondary, obmat_no_scale); + copy_v3db_v3fl(ld->conf.view_vector_secondary, trans); + } +} + +static void lineart_end_bounding_area_recursive(LineartBoundingArea *ba) +{ + BLI_spin_end(&ba->lock); + if (ba->child) { + for (int i = 0; i < 4; i++) { + lineart_end_bounding_area_recursive(&ba->child[i]); + } + } } -static void lineart_destroy_render_data(LineartRenderBuffer *rb) +void lineart_destroy_render_data_keep_init(LineartData *ld) { - if (rb == NULL) { + if (ld == NULL) { return; } - BLI_listbase_clear(&rb->chains); - BLI_listbase_clear(&rb->wasted_cuts); + BLI_listbase_clear(&ld->chains); + BLI_listbase_clear(&ld->wasted_cuts); + + BLI_listbase_clear(&ld->geom.vertex_buffer_pointers); + BLI_listbase_clear(&ld->geom.line_buffer_pointers); + BLI_listbase_clear(&ld->geom.triangle_buffer_pointers); + + if (ld->pending_edges.array) { + MEM_freeN(ld->pending_edges.array); + } - BLI_listbase_clear(&rb->vertex_buffer_pointers); - BLI_listbase_clear(&rb->line_buffer_pointers); - BLI_listbase_clear(&rb->triangle_buffer_pointers); + for (int i = 0; i < ld->qtree.initial_tile_count; i++) { + lineart_end_bounding_area_recursive(&ld->qtree.initials[i]); + } + lineart_free_bounding_area_memories(ld); - BLI_spin_end(&rb->lock_task); - BLI_spin_end(&rb->lock_cuts); - BLI_spin_end(&rb->render_data_pool.lock_mem); + lineart_mem_destroy(&ld->render_data_pool); +} - if (rb->pending_edges.array) { - MEM_freeN(rb->pending_edges.array); +static void lineart_destroy_render_data(LineartData *ld) +{ + if (ld == NULL) { + return; } - lineart_mem_destroy(&rb->render_data_pool); + BLI_spin_end(&ld->lock_task); + BLI_spin_end(&ld->lock_cuts); + BLI_spin_end(&ld->render_data_pool.lock_mem); + + lineart_destroy_render_data_keep_init(ld); + + lineart_mem_destroy(&ld->render_data_pool); } void MOD_lineart_destroy_render_data(LineartGpencilModifierData *lmd) { - LineartRenderBuffer *rb = lmd->render_buffer_ptr; + LineartData *ld = lmd->la_data_ptr; - lineart_destroy_render_data(rb); + lineart_destroy_render_data(ld); - if (rb) { - MEM_freeN(rb); - lmd->render_buffer_ptr = NULL; + if (ld) { + MEM_freeN(ld); + lmd->la_data_ptr = NULL; } if (G.debug_value == 4000) { @@ -3367,17 +3538,17 @@ void MOD_lineart_clear_cache(struct LineartCache **lc) (*lc) = NULL; } -static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, - LineartGpencilModifierData *lmd, - Object *camera, - Object *active_camera, - LineartCache *lc) +static LineartData *lineart_create_render_buffer(Scene *scene, + LineartGpencilModifierData *lmd, + Object *camera, + Object *active_camera, + LineartCache *lc) { - LineartRenderBuffer *rb = MEM_callocN(sizeof(LineartRenderBuffer), "Line Art render buffer"); + LineartData *ld = MEM_callocN(sizeof(LineartData), "Line Art render buffer"); lmd->cache = lc; - lmd->render_buffer_ptr = rb; - lc->rb_edge_types = lmd->edge_types_override; + lmd->la_data_ptr = ld; + lc->all_enabled_edge_types = lmd->edge_types_override; if (!scene || !camera || !lc) { return NULL; @@ -3390,98 +3561,120 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, clipping_offset = 0.0001; } - copy_v3db_v3fl(rb->camera_pos, camera->obmat[3]); + copy_v3db_v3fl(ld->conf.camera_pos, camera->obmat[3]); if (active_camera) { - copy_v3db_v3fl(rb->active_camera_pos, active_camera->obmat[3]); + copy_v3db_v3fl(ld->conf.active_camera_pos, active_camera->obmat[3]); } - copy_m4_m4(rb->cam_obmat, camera->obmat); - rb->cam_is_persp = (c->type == CAM_PERSP); - rb->near_clip = c->clip_start + clipping_offset; - rb->far_clip = c->clip_end - clipping_offset; - rb->w = scene->r.xsch; - rb->h = scene->r.ysch; + copy_m4_m4(ld->conf.cam_obmat, camera->obmat); + + ld->conf.cam_is_persp = (c->type == CAM_PERSP); + ld->conf.near_clip = c->clip_start + clipping_offset; + ld->conf.far_clip = c->clip_end - clipping_offset; + ld->w = scene->r.xsch; + ld->h = scene->r.ysch; - if (rb->cam_is_persp) { - rb->tile_recursive_level = LRT_TILE_RECURSIVE_PERSPECTIVE; + if (ld->conf.cam_is_persp) { + ld->qtree.recursive_level = LRT_TILE_RECURSIVE_PERSPECTIVE; } else { - rb->tile_recursive_level = LRT_TILE_RECURSIVE_ORTHO; + ld->qtree.recursive_level = LRT_TILE_RECURSIVE_ORTHO; } - double asp = ((double)rb->w / (double)rb->h); - int fit = BKE_camera_sensor_fit(c->sensor_fit, rb->w, rb->h); - rb->shift_x = fit == CAMERA_SENSOR_FIT_HOR ? c->shiftx : c->shiftx / asp; - rb->shift_y = fit == CAMERA_SENSOR_FIT_VERT ? c->shifty : c->shifty * asp; + double asp = ((double)ld->w / (double)ld->h); + int fit = BKE_camera_sensor_fit(c->sensor_fit, ld->w, ld->h); + ld->conf.shift_x = fit == CAMERA_SENSOR_FIT_HOR ? c->shiftx : c->shiftx / asp; + ld->conf.shift_y = fit == CAMERA_SENSOR_FIT_VERT ? c->shifty : c->shifty * asp; - rb->overscan = lmd->overscan; + ld->conf.overscan = lmd->overscan; - rb->shift_x /= (1 + rb->overscan); - rb->shift_y /= (1 + rb->overscan); + ld->conf.shift_x /= (1 + ld->conf.overscan); + ld->conf.shift_y /= (1 + ld->conf.overscan); - rb->crease_threshold = cos(M_PI - lmd->crease_threshold); - rb->chaining_image_threshold = lmd->chaining_image_threshold; - rb->angle_splitting_threshold = lmd->angle_splitting_threshold; - rb->chain_smooth_tolerance = lmd->chain_smooth_tolerance; + if (lmd->light_contour_object) { + Object *light_obj = lmd->light_contour_object; + copy_v3db_v3fl(ld->conf.camera_pos_secondary, light_obj->obmat[3]); + copy_m4_m4(ld->conf.cam_obmat_secondary, light_obj->obmat); + ld->conf.light_reference_available = true; + if (light_obj->type == OB_LAMP) { + ld->conf.cam_is_persp_secondary = ((Light *)light_obj->data)->type != LA_SUN; + } + } - rb->fuzzy_intersections = (lmd->calculation_flags & LRT_INTERSECTION_AS_CONTOUR) != 0; - rb->fuzzy_everything = (lmd->calculation_flags & LRT_EVERYTHING_AS_CONTOUR) != 0; - rb->allow_boundaries = (lmd->calculation_flags & LRT_ALLOW_CLIPPING_BOUNDARIES) != 0; - rb->use_loose_as_contour = (lmd->calculation_flags & LRT_LOOSE_AS_CONTOUR) != 0; - rb->use_loose_edge_chain = (lmd->calculation_flags & LRT_CHAIN_LOOSE_EDGES) != 0; - rb->use_geometry_space_chain = (lmd->calculation_flags & LRT_CHAIN_GEOMETRY_SPACE) != 0; - rb->use_image_boundary_trimming = (lmd->calculation_flags & LRT_USE_IMAGE_BOUNDARY_TRIMMING) != - 0; + ld->conf.crease_threshold = cos(M_PI - lmd->crease_threshold); + ld->conf.chaining_image_threshold = lmd->chaining_image_threshold; + ld->conf.angle_splitting_threshold = lmd->angle_splitting_threshold; + ld->conf.chain_smooth_tolerance = lmd->chain_smooth_tolerance; + + ld->conf.fuzzy_intersections = (lmd->calculation_flags & LRT_INTERSECTION_AS_CONTOUR) != 0; + ld->conf.fuzzy_everything = (lmd->calculation_flags & LRT_EVERYTHING_AS_CONTOUR) != 0; + ld->conf.allow_boundaries = (lmd->calculation_flags & LRT_ALLOW_CLIPPING_BOUNDARIES) != 0; + ld->conf.use_loose_as_contour = (lmd->calculation_flags & LRT_LOOSE_AS_CONTOUR) != 0; + ld->conf.use_loose_edge_chain = (lmd->calculation_flags & LRT_CHAIN_LOOSE_EDGES) != 0; + ld->conf.use_geometry_space_chain = (lmd->calculation_flags & LRT_CHAIN_GEOMETRY_SPACE) != 0; + ld->conf.use_image_boundary_trimming = (lmd->calculation_flags & + LRT_USE_IMAGE_BOUNDARY_TRIMMING) != 0; /* See lineart_edge_from_triangle() for how this option may impact performance. */ - rb->allow_overlapping_edges = (lmd->calculation_flags & LRT_ALLOW_OVERLAPPING_EDGES) != 0; + ld->conf.allow_overlapping_edges = (lmd->calculation_flags & LRT_ALLOW_OVERLAPPING_EDGES) != 0; - rb->allow_duplicated_types = (lmd->calculation_flags & LRT_ALLOW_OVERLAP_EDGE_TYPES) != 0; + ld->conf.allow_duplicated_types = (lmd->calculation_flags & LRT_ALLOW_OVERLAP_EDGE_TYPES) != 0; - rb->force_crease = (lmd->calculation_flags & LRT_USE_CREASE_ON_SMOOTH_SURFACES) != 0; - rb->sharp_as_crease = (lmd->calculation_flags & LRT_USE_CREASE_ON_SHARP_EDGES) != 0; + ld->conf.force_crease = (lmd->calculation_flags & LRT_USE_CREASE_ON_SMOOTH_SURFACES) != 0; + ld->conf.sharp_as_crease = (lmd->calculation_flags & LRT_USE_CREASE_ON_SHARP_EDGES) != 0; - rb->chain_preserve_details = (lmd->calculation_flags & LRT_CHAIN_PRESERVE_DETAILS) != 0; + ld->conf.chain_preserve_details = (lmd->calculation_flags & LRT_CHAIN_PRESERVE_DETAILS) != 0; /* This is used to limit calculation to a certain level to save time, lines who have higher * occlusion levels will get ignored. */ - rb->max_occlusion_level = lmd->level_end_override; - - rb->use_back_face_culling = (lmd->calculation_flags & LRT_USE_BACK_FACE_CULLING) != 0; + ld->conf.max_occlusion_level = lmd->level_end_override; int16_t edge_types = lmd->edge_types_override; - rb->use_contour = (edge_types & LRT_EDGE_FLAG_CONTOUR) != 0; - rb->use_crease = (edge_types & LRT_EDGE_FLAG_CREASE) != 0; - rb->use_material = (edge_types & LRT_EDGE_FLAG_MATERIAL) != 0; - rb->use_edge_marks = (edge_types & LRT_EDGE_FLAG_EDGE_MARK) != 0; - rb->use_intersections = (edge_types & LRT_EDGE_FLAG_INTERSECTION) != 0; - rb->use_loose = (edge_types & LRT_EDGE_FLAG_LOOSE) != 0; + /* lmd->edge_types_override contains all used flags in the modifier stack. */ + ld->conf.use_contour = (edge_types & LRT_EDGE_FLAG_CONTOUR) != 0; + ld->conf.use_crease = (edge_types & LRT_EDGE_FLAG_CREASE) != 0; + ld->conf.use_material = (edge_types & LRT_EDGE_FLAG_MATERIAL) != 0; + ld->conf.use_edge_marks = (edge_types & LRT_EDGE_FLAG_EDGE_MARK) != 0; + ld->conf.use_intersections = (edge_types & LRT_EDGE_FLAG_INTERSECTION) != 0; + ld->conf.use_loose = (edge_types & LRT_EDGE_FLAG_LOOSE) != 0; + ld->conf.use_light_contour = ((edge_types & LRT_EDGE_FLAG_LIGHT_CONTOUR) != 0 && + (lmd->light_contour_object != NULL)); + ld->conf.use_shadow = ((edge_types & LRT_EDGE_FLAG_PROJECTED_SHADOW) != 0 && + (lmd->light_contour_object != NULL)); + + ld->conf.shadow_selection = lmd->shadow_selection_override; + ld->conf.shadow_enclose_shapes = (lmd->calculation_flags & LRT_SHADOW_ENCLOSED_SHAPES) != 0; + ld->conf.shadow_use_silhouette = lmd->shadow_use_silhouette_override != 0; + + ld->conf.use_back_face_culling = (lmd->calculation_flags & LRT_USE_BACK_FACE_CULLING) != 0; + + ld->conf.filter_face_mark_invert = (lmd->calculation_flags & LRT_FILTER_FACE_MARK_INVERT) != 0; + ld->conf.filter_face_mark = (lmd->calculation_flags & LRT_FILTER_FACE_MARK) != 0; + ld->conf.filter_face_mark_boundaries = (lmd->calculation_flags & + LRT_FILTER_FACE_MARK_BOUNDARIES) != 0; + ld->conf.filter_face_mark_keep_contour = (lmd->calculation_flags & + LRT_FILTER_FACE_MARK_KEEP_CONTOUR) != 0; - rb->filter_face_mark_invert = (lmd->calculation_flags & LRT_FILTER_FACE_MARK_INVERT) != 0; - rb->filter_face_mark = (lmd->calculation_flags & LRT_FILTER_FACE_MARK) != 0; - rb->filter_face_mark_boundaries = (lmd->calculation_flags & LRT_FILTER_FACE_MARK_BOUNDARIES) != - 0; - rb->filter_face_mark_keep_contour = (lmd->calculation_flags & - LRT_FILTER_FACE_MARK_KEEP_CONTOUR) != 0; + ld->chain_data_pool = &lc->chain_data_pool; - rb->chain_data_pool = &lc->chain_data_pool; + /* See #LineartData::edge_data_pool for explanation. */ + ld->edge_data_pool = &ld->render_data_pool; - BLI_spin_init(&rb->lock_task); - BLI_spin_init(&rb->lock_cuts); - BLI_spin_init(&rb->render_data_pool.lock_mem); + BLI_spin_init(&ld->lock_task); + BLI_spin_init(&ld->lock_cuts); + BLI_spin_init(&ld->render_data_pool.lock_mem); - return rb; + ld->thread_count = BKE_render_num_threads(&scene->r); + + return ld; } -static int lineart_triangle_size_get(const Scene *scene, LineartRenderBuffer *rb) +static int lineart_triangle_size_get(LineartData *ld) { - if (rb->thread_count == 0) { - rb->thread_count = BKE_render_num_threads(&scene->r); - } - return sizeof(LineartTriangle) + (sizeof(LineartEdge *) * (rb->thread_count)); + return sizeof(LineartTriangle) + (sizeof(LineartEdge *) * (ld->thread_count)); } -static void lineart_main_bounding_area_make_initial(LineartRenderBuffer *rb) +void lineart_main_bounding_area_make_initial(LineartData *ld) { /* Initial tile split is defined as 4 (subdivided as 4*4), increasing the value allows the * algorithm to build the acceleration structure for bigger scenes a little faster but not as @@ -3491,24 +3684,35 @@ static void lineart_main_bounding_area_make_initial(LineartRenderBuffer *rb) int row, col; LineartBoundingArea *ba; + /* Always make sure the shortest side has at least LRT_BA_ROWS tiles. */ + if (ld->w > ld->h) { + sp_w = sp_h * ld->w / ld->h; + } + else { + sp_h = sp_w * ld->h / ld->w; + } + /* Because NDC (Normalized Device Coordinates) range is (-1,1), * so the span for each initial tile is double of that in the (0,1) range. */ double span_w = (double)1 / sp_w * 2.0; double span_h = (double)1 / sp_h * 2.0; - rb->tile_count_x = sp_w; - rb->tile_count_y = sp_h; - rb->width_per_tile = span_w; - rb->height_per_tile = span_h; + ld->qtree.count_x = sp_w; + ld->qtree.count_y = sp_h; + ld->qtree.tile_width = span_w; + ld->qtree.tile_height = span_h; - rb->bounding_area_count = sp_w * sp_h; - rb->initial_bounding_areas = lineart_mem_acquire( - &rb->render_data_pool, sizeof(LineartBoundingArea) * rb->bounding_area_count); + ld->qtree.initial_tile_count = sp_w * sp_h; + ld->qtree.initials = lineart_mem_acquire( + &ld->render_data_pool, sizeof(LineartBoundingArea) * ld->qtree.initial_tile_count); + for (int i = 0; i < ld->qtree.initial_tile_count; i++) { + BLI_spin_init(&ld->qtree.initials[i].lock); + } /* Initialize tiles. */ for (row = 0; row < sp_h; row++) { for (col = 0; col < sp_w; col++) { - ba = &rb->initial_bounding_areas[row * LRT_BA_ROWS + col]; + ba = &ld->qtree.initials[row * ld->qtree.count_x + col]; /* Set the four direction limits. */ ba->l = span_w * col - 1.0; @@ -3522,34 +3726,12 @@ static void lineart_main_bounding_area_make_initial(LineartRenderBuffer *rb) /* Init linked_triangles array. */ ba->max_triangle_count = LRT_TILE_SPLITTING_TRIANGLE_LIMIT; ba->max_line_count = LRT_TILE_EDGE_COUNT_INITIAL; - ba->linked_triangles = lineart_mem_acquire( - &rb->render_data_pool, sizeof(LineartTriangle *) * ba->max_triangle_count); - ba->linked_lines = lineart_mem_acquire(&rb->render_data_pool, - sizeof(LineartEdge *) * ba->max_line_count); + ba->linked_triangles = MEM_callocN(sizeof(LineartTriangle *) * ba->max_triangle_count, + "ba_linked_triangles"); + ba->linked_lines = MEM_callocN(sizeof(LineartEdge *) * ba->max_line_count, + "ba_linked_lines"); - /* Link adjacent ones. */ - if (row) { - lineart_list_append_pointer_pool( - &ba->up, - &rb->render_data_pool, - &rb->initial_bounding_areas[(row - 1) * LRT_BA_ROWS + col]); - } - if (col) { - lineart_list_append_pointer_pool(&ba->lp, - &rb->render_data_pool, - &rb->initial_bounding_areas[row * LRT_BA_ROWS + col - 1]); - } - if (row != sp_h - 1) { - lineart_list_append_pointer_pool( - &ba->bp, - &rb->render_data_pool, - &rb->initial_bounding_areas[(row + 1) * LRT_BA_ROWS + col]); - } - if (col != sp_w - 1) { - lineart_list_append_pointer_pool(&ba->rp, - &rb->render_data_pool, - &rb->initial_bounding_areas[row * LRT_BA_ROWS + col + 1]); - } + BLI_spin_init(&ba->lock); } } } @@ -3557,11 +3739,11 @@ static void lineart_main_bounding_area_make_initial(LineartRenderBuffer *rb) /** * Re-link adjacent tiles after one gets subdivided. */ -static void lineart_bounding_areas_connect_new(LineartRenderBuffer *rb, LineartBoundingArea *root) +static void lineart_bounding_areas_connect_new(LineartData *ld, LineartBoundingArea *root) { LineartBoundingArea *ba = root->child, *tba; LinkData *lip2, *next_lip; - LineartStaticMemPool *mph = &rb->render_data_pool; + LineartStaticMemPool *mph = &ld->render_data_pool; /* Inter-connection with newly created 4 child bounding areas. */ lineart_list_append_pointer_pool(&ba[1].rp, mph, &ba[0]); @@ -3697,17 +3879,59 @@ static void lineart_bounding_areas_connect_new(LineartRenderBuffer *rb, LineartB BLI_listbase_clear(&root->bp); } +static void lineart_bounding_areas_connect_recursive(LineartData *ld, LineartBoundingArea *root) +{ + if (root->child) { + lineart_bounding_areas_connect_new(ld, root); + for (int i = 0; i < 4; i++) { + lineart_bounding_areas_connect_recursive(ld, &root->child[i]); + } + } +} + +void lineart_main_bounding_areas_connect_post(LineartData *ld) +{ + int total_tile_initial = ld->qtree.count_x * ld->qtree.count_y; + int tiles_per_row = ld->qtree.count_x; + + for (int row = 0; row < ld->qtree.count_y; row++) { + for (int col = 0; col < ld->qtree.count_x; col++) { + LineartBoundingArea *ba = &ld->qtree.initials[row * tiles_per_row + col]; + /* Link adjacent ones. */ + if (row) { + lineart_list_append_pointer_pool( + &ba->up, &ld->render_data_pool, &ld->qtree.initials[(row - 1) * tiles_per_row + col]); + } + if (col) { + lineart_list_append_pointer_pool( + &ba->lp, &ld->render_data_pool, &ld->qtree.initials[row * tiles_per_row + col - 1]); + } + if (row != ld->qtree.count_y - 1) { + lineart_list_append_pointer_pool( + &ba->bp, &ld->render_data_pool, &ld->qtree.initials[(row + 1) * tiles_per_row + col]); + } + if (col != ld->qtree.count_x - 1) { + lineart_list_append_pointer_pool( + &ba->rp, &ld->render_data_pool, &ld->qtree.initials[row * tiles_per_row + col + 1]); + } + } + } + for (int i = 0; i < total_tile_initial; i++) { + lineart_bounding_areas_connect_recursive(ld, &ld->qtree.initials[i]); + } +} + /** - * Subdivide a tile after one tile contains too many triangles. + * Subdivide a tile after one tile contains too many triangles, then re-link triangles into all the + * child tiles. */ -static void lineart_bounding_area_split(LineartRenderBuffer *rb, +static void lineart_bounding_area_split(LineartData *ld, LineartBoundingArea *root, int recursive_level) { - LineartBoundingArea *ba = lineart_mem_acquire(&rb->render_data_pool, - sizeof(LineartBoundingArea) * 4); - LineartTriangle *tri; + LineartBoundingArea *ba = lineart_mem_acquire_thread(&ld->render_data_pool, + sizeof(LineartBoundingArea) * 4); ba[0].l = root->cx; ba[0].r = root->r; ba[0].u = root->u; @@ -3736,80 +3960,80 @@ static void lineart_bounding_area_split(LineartRenderBuffer *rb, ba[3].cx = (ba[3].l + ba[3].r) / 2; ba[3].cy = (ba[3].u + ba[3].b) / 2; - root->child = ba; - - lineart_bounding_areas_connect_new(rb, root); - - /* Init linked_triangles array. */ + /* Init linked_triangles array and locks. */ for (int i = 0; i < 4; i++) { ba[i].max_triangle_count = LRT_TILE_SPLITTING_TRIANGLE_LIMIT; ba[i].max_line_count = LRT_TILE_EDGE_COUNT_INITIAL; - ba[i].linked_triangles = lineart_mem_acquire( - &rb->render_data_pool, sizeof(LineartTriangle *) * LRT_TILE_SPLITTING_TRIANGLE_LIMIT); - ba[i].linked_lines = lineart_mem_acquire(&rb->render_data_pool, - sizeof(LineartEdge *) * LRT_TILE_EDGE_COUNT_INITIAL); + ba[i].linked_triangles = MEM_callocN(sizeof(LineartTriangle *) * ba[i].max_triangle_count, + "ba_linked_triangles"); + ba[i].linked_lines = MEM_callocN(sizeof(LineartEdge *) * ba[i].max_line_count, + "ba_linked_lines"); + BLI_spin_init(&ba[i].lock); } - for (int i = 0; i < root->triangle_count; i++) { - tri = root->linked_triangles[i]; - LineartBoundingArea *cba = root->child; + for (uint32_t i = 0; i < root->triangle_count; i++) { + LineartTriangle *tri = root->linked_triangles[i]; + double b[4]; b[0] = MIN3(tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]); b[1] = MAX3(tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]); b[2] = MAX3(tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]); b[3] = MIN3(tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]); - if (LRT_BOUND_AREA_CROSSES(b, &cba[0].l)) { - lineart_bounding_area_link_triangle(rb, &cba[0], tri, b, 0, recursive_level + 1, false); + + /* Re-link triangles into child tiles, not doing intersection lines during this because this + * batch of triangles are all tested with each other for intersections. */ + if (LRT_BOUND_AREA_CROSSES(b, &ba[0].l)) { + lineart_bounding_area_link_triangle(ld, &ba[0], tri, b, 0, recursive_level + 1, false, NULL); } - if (LRT_BOUND_AREA_CROSSES(b, &cba[1].l)) { - lineart_bounding_area_link_triangle(rb, &cba[1], tri, b, 0, recursive_level + 1, false); + if (LRT_BOUND_AREA_CROSSES(b, &ba[1].l)) { + lineart_bounding_area_link_triangle(ld, &ba[1], tri, b, 0, recursive_level + 1, false, NULL); } - if (LRT_BOUND_AREA_CROSSES(b, &cba[2].l)) { - lineart_bounding_area_link_triangle(rb, &cba[2], tri, b, 0, recursive_level + 1, false); + if (LRT_BOUND_AREA_CROSSES(b, &ba[2].l)) { + lineart_bounding_area_link_triangle(ld, &ba[2], tri, b, 0, recursive_level + 1, false, NULL); } - if (LRT_BOUND_AREA_CROSSES(b, &cba[3].l)) { - lineart_bounding_area_link_triangle(rb, &cba[3], tri, b, 0, recursive_level + 1, false); + if (LRT_BOUND_AREA_CROSSES(b, &ba[3].l)) { + lineart_bounding_area_link_triangle(ld, &ba[3], tri, b, 0, recursive_level + 1, false, NULL); } } - rb->bounding_area_count += 3; + /* At this point the child tiles are fully initialized and it's safe for new triangles to be + * inserted, so assign root->child for #lineart_bounding_area_link_triangle to use. */ + root->child = ba; } -static bool lineart_bounding_area_edge_intersect(LineartRenderBuffer *UNUSED(fb), +static bool lineart_bounding_area_edge_intersect(LineartData *UNUSED(fb), const double l[2], const double r[2], LineartBoundingArea *ba) { - double vx, vy; + double dx, dy; double converted[4]; double c1, c; - if (((converted[0] = (double)ba->l) > MAX2(l[0], r[0])) || - ((converted[1] = (double)ba->r) < MIN2(l[0], r[0])) || - ((converted[2] = (double)ba->b) > MAX2(l[1], r[1])) || - ((converted[3] = (double)ba->u) < MIN2(l[1], r[1]))) { + if (((converted[0] = ba->l) > MAX2(l[0], r[0])) || ((converted[1] = ba->r) < MIN2(l[0], r[0])) || + ((converted[2] = ba->b) > MAX2(l[1], r[1])) || ((converted[3] = ba->u) < MIN2(l[1], r[1]))) { return false; } - vx = l[0] - r[0]; - vy = l[1] - r[1]; + dx = l[0] - r[0]; + dy = l[1] - r[1]; - c1 = vx * (converted[2] - l[1]) - vy * (converted[0] - l[0]); + c1 = dx * (converted[2] - l[1]) - dy * (converted[0] - l[0]); c = c1; - c1 = vx * (converted[2] - l[1]) - vy * (converted[1] - l[0]); + c1 = dx * (converted[2] - l[1]) - dy * (converted[1] - l[0]); if (c1 * c <= 0) { return true; } c = c1; - c1 = vx * (converted[3] - l[1]) - vy * (converted[0] - l[0]); + c1 = dx * (converted[3] - l[1]) - dy * (converted[0] - l[0]); if (c1 * c <= 0) { return true; } c = c1; - c1 = vx * (converted[3] - l[1]) - vy * (converted[1] - l[0]); + c1 = dx * (converted[3] - l[1]) - dy * (converted[1] - l[0]); if (c1 * c <= 0) { return true; } @@ -3818,24 +4042,28 @@ static bool lineart_bounding_area_edge_intersect(LineartRenderBuffer *UNUSED(fb) return false; } -static bool lineart_bounding_area_triangle_intersect(LineartRenderBuffer *fb, +static bool lineart_bounding_area_triangle_intersect(LineartData *fb, LineartTriangle *tri, - LineartBoundingArea *ba) + LineartBoundingArea *ba, + bool *r_triangle_vert_inside) { double p1[2], p2[2], p3[2], p4[2]; double *FBC1 = tri->v[0]->fbcoord, *FBC2 = tri->v[1]->fbcoord, *FBC3 = tri->v[2]->fbcoord; - p3[0] = p1[0] = (double)ba->l; - p2[1] = p1[1] = (double)ba->b; - p2[0] = p4[0] = (double)ba->r; - p3[1] = p4[1] = (double)ba->u; + p3[0] = p1[0] = ba->l; + p2[1] = p1[1] = ba->b; + p2[0] = p4[0] = ba->r; + p3[1] = p4[1] = ba->u; if ((FBC1[0] >= p1[0] && FBC1[0] <= p2[0] && FBC1[1] >= p1[1] && FBC1[1] <= p3[1]) || (FBC2[0] >= p1[0] && FBC2[0] <= p2[0] && FBC2[1] >= p1[1] && FBC2[1] <= p3[1]) || (FBC3[0] >= p1[0] && FBC3[0] <= p2[0] && FBC3[1] >= p1[1] && FBC3[1] <= p3[1])) { + *r_triangle_vert_inside = true; return true; } + *r_triangle_vert_inside = false; + if (lineart_point_inside_triangle(p1, FBC1, FBC2, FBC3) || lineart_point_inside_triangle(p2, FBC1, FBC2, FBC3) || lineart_point_inside_triangle(p3, FBC1, FBC2, FBC3) || @@ -3853,87 +4081,180 @@ static bool lineart_bounding_area_triangle_intersect(LineartRenderBuffer *fb, } /** - * 1) Link triangles with bounding areas for later occlusion test. - * 2) Test triangles with existing(added previously) triangles for intersection lines. + * This function does two things: + * + * 1) Builds a quad-tree under ld->InitialBoundingAreas to achieve good geometry separation for + * fast overlapping test between triangles and lines. This acceleration structure makes the + * occlusion stage much faster. + * + * 2) Test triangles with other triangles that are previously linked into each tile + * (#LineartBoundingArea) for intersection lines. When splitting the tile into 4 children and + * re-linking triangles into the child tiles, intersections are inhibited so we don't get + * duplicated intersection lines. + * */ -static void lineart_bounding_area_link_triangle(LineartRenderBuffer *rb, +static void lineart_bounding_area_link_triangle(LineartData *ld, LineartBoundingArea *root_ba, LineartTriangle *tri, - double *LRUB, + double l_r_u_b[4], int recursive, int recursive_level, - bool do_intersection) + bool do_intersection, + struct LineartIsecThread *th) { - if (!lineart_bounding_area_triangle_intersect(rb, tri, root_ba)) { + bool triangle_vert_inside; + if (!lineart_bounding_area_triangle_intersect(ld, tri, root_ba, &triangle_vert_inside)) { return; } - if (root_ba->child == NULL) { - lineart_bounding_area_triangle_add(rb, root_ba, tri); - /* If splitting doesn't improve triangle separation, then shouldn't allow splitting anymore. - * Here we use recursive limit. This is especially useful in orthographic render, - * where a lot of faces could easily line up perfectly in image space, - * which can not be separated by simply slicing the image tile. */ - if (root_ba->triangle_count >= LRT_TILE_SPLITTING_TRIANGLE_LIMIT && recursive && - recursive_level < rb->tile_recursive_level) { - lineart_bounding_area_split(rb, root_ba, recursive_level); - } - if (recursive && do_intersection && rb->use_intersections) { - lineart_triangle_intersect_in_bounding_area(rb, tri, root_ba); - } - } - else { - LineartBoundingArea *ba = root_ba->child; - double *B1 = LRUB; + + LineartBoundingArea *old_ba = root_ba; + + if (old_ba->child) { + /* If old_ba->child is not NULL, then tile splitting is fully finished, safe to directly insert + * into child tiles. */ + double *B1 = l_r_u_b; double b[4]; - if (!LRUB) { + if (!l_r_u_b) { b[0] = MIN3(tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]); b[1] = MAX3(tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]); b[2] = MAX3(tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]); b[3] = MIN3(tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]); B1 = b; } - if (LRT_BOUND_AREA_CROSSES(B1, &ba[0].l)) { - lineart_bounding_area_link_triangle( - rb, &ba[0], tri, B1, recursive, recursive_level + 1, do_intersection); + for (int iba = 0; iba < 4; iba++) { + if (LRT_BOUND_AREA_CROSSES(B1, &old_ba->child[iba].l)) { + lineart_bounding_area_link_triangle( + ld, &old_ba->child[iba], tri, B1, recursive, recursive_level + 1, do_intersection, th); + } } - if (LRT_BOUND_AREA_CROSSES(B1, &ba[1].l)) { - lineart_bounding_area_link_triangle( - rb, &ba[1], tri, B1, recursive, recursive_level + 1, do_intersection); + return; + } + + /* When splitting tiles, triangles are relinked into new tiles by a single thread, #th is NULL + * in that situation. */ + if (th) { + BLI_spin_lock(&old_ba->lock); + } + + /* If there are still space left in this tile for insertion. */ + if (old_ba->triangle_count < old_ba->max_triangle_count) { + const uint32_t old_tri_count = old_ba->triangle_count; + + old_ba->linked_triangles[old_tri_count] = tri; + + if (triangle_vert_inside) { + old_ba->insider_triangle_count++; } - if (LRT_BOUND_AREA_CROSSES(B1, &ba[2].l)) { - lineart_bounding_area_link_triangle( - rb, &ba[2], tri, B1, recursive, recursive_level + 1, do_intersection); + old_ba->triangle_count++; + + /* Do intersections in place. */ + if (do_intersection && ld->conf.use_intersections) { + lineart_triangle_intersect_in_bounding_area(tri, old_ba, th, old_tri_count); + } + + if (th) { + BLI_spin_unlock(&old_ba->lock); } - if (LRT_BOUND_AREA_CROSSES(B1, &ba[3].l)) { - lineart_bounding_area_link_triangle( - rb, &ba[3], tri, B1, recursive, recursive_level + 1, do_intersection); + } + else { /* We need to wait for either splitting or array extension to be done. */ + + if (recursive_level < ld->qtree.recursive_level && + old_ba->insider_triangle_count >= LRT_TILE_SPLITTING_TRIANGLE_LIMIT) { + if (!old_ba->child) { + /* old_ba->child==NULL, means we are the thread that's doing the splitting. */ + lineart_bounding_area_split(ld, old_ba, recursive_level); + } /* Otherwise other thread has completed the splitting process. */ + } + else { + if (old_ba->triangle_count == old_ba->max_triangle_count) { + /* Means we are the thread that's doing the extension. */ + lineart_bounding_area_triangle_reallocate(old_ba); + } /* Otherwise other thread has completed the extending the array. */ + } + + /* Unlock before going into recursive call. */ + if (th) { + BLI_spin_unlock(&old_ba->lock); + } + + /* Of course we still have our own triangle needs to be added. */ + lineart_bounding_area_link_triangle( + ld, root_ba, tri, l_r_u_b, recursive, recursive_level, do_intersection, th); + } +} + +static void lineart_free_bounding_area_memory(LineartBoundingArea *ba, bool recursive) +{ + BLI_spin_end(&ba->lock); + if (ba->linked_lines) { + MEM_freeN(ba->linked_lines); + } + if (ba->linked_triangles) { + MEM_freeN(ba->linked_triangles); + } + if (recursive && ba->child) { + for (int i = 0; i < 4; i++) { + lineart_free_bounding_area_memory(&ba->child[i], recursive); + } + } +} +static void lineart_free_bounding_area_memories(LineartData *ld) +{ + for (int i = 0; i < ld->qtree.count_y; i++) { + for (int j = 0; j < ld->qtree.count_x; j++) { + lineart_free_bounding_area_memory(&ld->qtree.initials[i * ld->qtree.count_x + j], true); } } } -static void lineart_bounding_area_link_edge(LineartRenderBuffer *rb, +static void lineart_bounding_area_link_edge(LineartData *ld, LineartBoundingArea *root_ba, LineartEdge *e) { if (root_ba->child == NULL) { - lineart_bounding_area_line_add(rb, root_ba, e); + lineart_bounding_area_line_add(root_ba, e); } else { if (lineart_bounding_area_edge_intersect( - rb, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[0])) { - lineart_bounding_area_link_edge(rb, &root_ba->child[0], e); + ld, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[0])) { + lineart_bounding_area_link_edge(ld, &root_ba->child[0], e); } if (lineart_bounding_area_edge_intersect( - rb, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[1])) { - lineart_bounding_area_link_edge(rb, &root_ba->child[1], e); + ld, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[1])) { + lineart_bounding_area_link_edge(ld, &root_ba->child[1], e); } if (lineart_bounding_area_edge_intersect( - rb, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[2])) { - lineart_bounding_area_link_edge(rb, &root_ba->child[2], e); + ld, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[2])) { + lineart_bounding_area_link_edge(ld, &root_ba->child[2], e); } if (lineart_bounding_area_edge_intersect( - rb, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[3])) { - lineart_bounding_area_link_edge(rb, &root_ba->child[3], e); + ld, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[3])) { + lineart_bounding_area_link_edge(ld, &root_ba->child[3], e); + } + } +} + +static void lineart_clear_linked_edges_recursive(LineartData *ld, LineartBoundingArea *root_ba) +{ + if (root_ba->child) { + for (int i = 0; i < 4; i++) { + lineart_clear_linked_edges_recursive(ld, &root_ba->child[i]); + } + } + if (root_ba->linked_lines) { + MEM_freeN(root_ba->linked_lines); + } + root_ba->line_count = 0; + root_ba->max_line_count = 128; + root_ba->linked_lines = MEM_callocN(sizeof(LineartEdge *) * root_ba->max_line_count, + "cleared lineart edges"); +} +void lineart_main_clear_linked_edges(LineartData *ld) +{ + LineartBoundingArea *ba = ld->qtree.initials; + for (int i = 0; i < ld->qtree.count_y; i++) { + for (int j = 0; j < ld->qtree.count_x; j++) { + lineart_clear_linked_edges_recursive(ld, &ba[i * ld->qtree.count_x + j]); } } } @@ -3941,16 +4262,16 @@ static void lineart_bounding_area_link_edge(LineartRenderBuffer *rb, /** * Link lines to their respective bounding areas. */ -static void lineart_main_link_lines(LineartRenderBuffer *rb) +void lineart_main_link_lines(LineartData *ld) { LRT_ITER_ALL_LINES_BEGIN { int r1, r2, c1, c2, row, col; - if (lineart_get_edge_bounding_areas(rb, e, &r1, &r2, &c1, &c2)) { + if (lineart_get_edge_bounding_areas(ld, e, &r1, &r2, &c1, &c2)) { for (row = r1; row != r2 + 1; row++) { for (col = c1; col != c2 + 1; col++) { lineart_bounding_area_link_edge( - rb, &rb->initial_bounding_areas[row * LRT_BA_ROWS + col], e); + ld, &ld->qtree.initials[row * ld->qtree.count_x + col], e); } } } @@ -3958,14 +4279,66 @@ static void lineart_main_link_lines(LineartRenderBuffer *rb) LRT_ITER_ALL_LINES_END } -static bool lineart_get_triangle_bounding_areas(LineartRenderBuffer *rb, - LineartTriangle *tri, - int *rowbegin, - int *rowend, - int *colbegin, - int *colend) +static void lineart_main_remove_unused_lines_recursive(LineartBoundingArea *ba, + uint8_t max_occlusion) +{ + if (ba->child) { + for (int i = 0; i < 4; i++) { + lineart_main_remove_unused_lines_recursive(&ba->child[i], max_occlusion); + } + return; + } + + if (!ba->line_count) { + return; + } + + int usable_count = 0; + for (int i = 0; i < ba->line_count; i++) { + LineartEdge *e = ba->linked_lines[i]; + if (e->min_occ > max_occlusion) { + continue; + } + usable_count++; + } + + if (!usable_count) { + ba->line_count = 0; + return; + } + + LineartEdge **new_array = MEM_callocN(sizeof(LineartEdge *) * usable_count, + "cleaned lineart edge array"); + + int new_i = 0; + for (int i = 0; i < ba->line_count; i++) { + LineartEdge *e = ba->linked_lines[i]; + if (e->min_occ > max_occlusion) { + continue; + } + new_array[new_i] = e; + new_i++; + } + + MEM_freeN(ba->linked_lines); + ba->linked_lines = new_array; + ba->max_line_count = ba->line_count = usable_count; +} + +static void lineart_main_remove_unused_lines_from_tiles(LineartData *ld) { - double sp_w = rb->width_per_tile, sp_h = rb->height_per_tile; + for (int row = 0; row < ld->qtree.count_y; row++) { + for (int col = 0; col < ld->qtree.count_x; col++) { + lineart_main_remove_unused_lines_recursive( + &ld->qtree.initials[row * ld->qtree.count_x + col], ld->conf.max_occlusion_level); + } + } +} + +static bool lineart_get_triangle_bounding_areas( + LineartData *ld, LineartTriangle *tri, int *rowbegin, int *rowend, int *colbegin, int *colend) +{ + double sp_w = ld->qtree.tile_width, sp_h = ld->qtree.tile_height; double b[4]; if (!tri->v[0] || !tri->v[1] || !tri->v[2]) { @@ -3983,14 +4356,14 @@ static bool lineart_get_triangle_bounding_areas(LineartRenderBuffer *rb, (*colbegin) = (int)((b[0] + 1.0) / sp_w); (*colend) = (int)((b[1] + 1.0) / sp_w); - (*rowend) = rb->tile_count_y - (int)((b[2] + 1.0) / sp_h) - 1; - (*rowbegin) = rb->tile_count_y - (int)((b[3] + 1.0) / sp_h) - 1; + (*rowend) = ld->qtree.count_y - (int)((b[2] + 1.0) / sp_h) - 1; + (*rowbegin) = ld->qtree.count_y - (int)((b[3] + 1.0) / sp_h) - 1; - if ((*colend) >= rb->tile_count_x) { - (*colend) = rb->tile_count_x - 1; + if ((*colend) >= ld->qtree.count_x) { + (*colend) = ld->qtree.count_x - 1; } - if ((*rowend) >= rb->tile_count_y) { - (*rowend) = rb->tile_count_y - 1; + if ((*rowend) >= ld->qtree.count_y) { + (*rowend) = ld->qtree.count_y - 1; } if ((*colbegin) < 0) { (*colbegin) = 0; @@ -4002,14 +4375,10 @@ static bool lineart_get_triangle_bounding_areas(LineartRenderBuffer *rb, return true; } -static bool lineart_get_edge_bounding_areas(LineartRenderBuffer *rb, - LineartEdge *e, - int *rowbegin, - int *rowend, - int *colbegin, - int *colend) +static bool lineart_get_edge_bounding_areas( + LineartData *ld, LineartEdge *e, int *rowbegin, int *rowend, int *colbegin, int *colend) { - double sp_w = rb->width_per_tile, sp_h = rb->height_per_tile; + double sp_w = ld->qtree.tile_width, sp_h = ld->qtree.tile_height; double b[4]; if (!e->v1 || !e->v2) { @@ -4031,31 +4400,29 @@ static bool lineart_get_edge_bounding_areas(LineartRenderBuffer *rb, (*colbegin) = (int)((b[0] + 1.0) / sp_w); (*colend) = (int)((b[1] + 1.0) / sp_w); - (*rowend) = rb->tile_count_y - (int)((b[2] + 1.0) / sp_h) - 1; - (*rowbegin) = rb->tile_count_y - (int)((b[3] + 1.0) / sp_h) - 1; + (*rowend) = ld->qtree.count_y - (int)((b[2] + 1.0) / sp_h) - 1; + (*rowbegin) = ld->qtree.count_y - (int)((b[3] + 1.0) / sp_h) - 1; /* It's possible that the line stretches too much out to the side, resulting negative value. */ if ((*rowend) < (*rowbegin)) { - (*rowend) = rb->tile_count_y - 1; + (*rowend) = ld->qtree.count_y - 1; } if ((*colend) < (*colbegin)) { - (*colend) = rb->tile_count_x - 1; + (*colend) = ld->qtree.count_x - 1; } - CLAMP((*colbegin), 0, rb->tile_count_x - 1); - CLAMP((*rowbegin), 0, rb->tile_count_y - 1); - CLAMP((*colend), 0, rb->tile_count_x - 1); - CLAMP((*rowend), 0, rb->tile_count_y - 1); + CLAMP((*colbegin), 0, ld->qtree.count_x - 1); + CLAMP((*rowbegin), 0, ld->qtree.count_y - 1); + CLAMP((*colend), 0, ld->qtree.count_x - 1); + CLAMP((*rowend), 0, ld->qtree.count_y - 1); return true; } -LineartBoundingArea *MOD_lineart_get_parent_bounding_area(LineartRenderBuffer *rb, - double x, - double y) +LineartBoundingArea *MOD_lineart_get_parent_bounding_area(LineartData *ld, double x, double y) { - double sp_w = rb->width_per_tile, sp_h = rb->height_per_tile; + double sp_w = ld->qtree.tile_width, sp_h = ld->qtree.tile_height; int col, row; if (x > 1 || x < -1 || y > 1 || y < -1) { @@ -4063,13 +4430,13 @@ LineartBoundingArea *MOD_lineart_get_parent_bounding_area(LineartRenderBuffer *r } col = (int)((x + 1.0) / sp_w); - row = rb->tile_count_y - (int)((y + 1.0) / sp_h) - 1; + row = ld->qtree.count_y - (int)((y + 1.0) / sp_h) - 1; - if (col >= rb->tile_count_x) { - col = rb->tile_count_x - 1; + if (col >= ld->qtree.count_x) { + col = ld->qtree.count_x - 1; } - if (row >= rb->tile_count_y) { - row = rb->tile_count_y - 1; + if (row >= ld->qtree.count_y) { + row = ld->qtree.count_y - 1; } if (col < 0) { col = 0; @@ -4078,29 +4445,29 @@ LineartBoundingArea *MOD_lineart_get_parent_bounding_area(LineartRenderBuffer *r row = 0; } - return &rb->initial_bounding_areas[row * LRT_BA_ROWS + col]; + return &ld->qtree.initials[row * ld->qtree.count_x + col]; } -static LineartBoundingArea *lineart_get_bounding_area(LineartRenderBuffer *rb, double x, double y) +static LineartBoundingArea *lineart_get_bounding_area(LineartData *ld, double x, double y) { LineartBoundingArea *iba; - double sp_w = rb->width_per_tile, sp_h = rb->height_per_tile; + double sp_w = ld->qtree.tile_width, sp_h = ld->qtree.tile_height; int c = (int)((x + 1.0) / sp_w); - int r = rb->tile_count_y - (int)((y + 1.0) / sp_h) - 1; + int r = ld->qtree.count_y - (int)((y + 1.0) / sp_h) - 1; if (r < 0) { r = 0; } if (c < 0) { c = 0; } - if (r >= rb->tile_count_y) { - r = rb->tile_count_y - 1; + if (r >= ld->qtree.count_y) { + r = ld->qtree.count_y - 1; } - if (c >= rb->tile_count_x) { - c = rb->tile_count_x - 1; + if (c >= ld->qtree.count_x) { + c = ld->qtree.count_x - 1; } - iba = &rb->initial_bounding_areas[r * LRT_BA_ROWS + c]; + iba = &ld->qtree.initials[r * ld->qtree.count_x + c]; while (iba->child) { if (x > iba->cx) { if (y > iba->cy) { @@ -4122,101 +4489,243 @@ static LineartBoundingArea *lineart_get_bounding_area(LineartRenderBuffer *rb, d return iba; } -LineartBoundingArea *MOD_lineart_get_bounding_area(LineartRenderBuffer *rb, double x, double y) +LineartBoundingArea *MOD_lineart_get_bounding_area(LineartData *ld, double x, double y) { LineartBoundingArea *ba; - if ((ba = MOD_lineart_get_parent_bounding_area(rb, x, y)) != NULL) { - return lineart_get_bounding_area(rb, x, y); + if ((ba = MOD_lineart_get_parent_bounding_area(ld, x, y)) != NULL) { + return lineart_get_bounding_area(ld, x, y); } return NULL; } -/** - * Sequentially add triangles into render buffer. This also does intersection along the way. - */ -static void lineart_main_add_triangles(LineartRenderBuffer *rb) +static void lineart_add_triangles_worker(TaskPool *__restrict UNUSED(pool), LineartIsecThread *th) { - LineartTriangle *tri; - int i, lim; - int x1, x2, y1, y2; - int r, co; - - LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->triangle_buffer_pointers) { - tri = eln->pointer; - lim = eln->element_count; - for (i = 0; i < lim; i++) { - if ((tri->flags & LRT_CULL_USED) || (tri->flags & LRT_CULL_DISCARD)) { - tri = (void *)(((uchar *)tri) + rb->triangle_size); - continue; - } - if (lineart_get_triangle_bounding_areas(rb, tri, &y1, &y2, &x1, &x2)) { - for (co = x1; co <= x2; co++) { - for (r = y1; r <= y2; r++) { - lineart_bounding_area_link_triangle(rb, - &rb->initial_bounding_areas[r * LRT_BA_ROWS + co], - tri, - 0, - 1, - 0, - (!(tri->flags & LRT_TRIANGLE_NO_INTERSECTION))); + LineartData *ld = th->ld; + int _dir_control = 0; + while (lineart_schedule_new_triangle_task(th)) { + for (LineartElementLinkNode *eln = th->pending_from; eln != th->pending_to->next; + eln = eln->next) { + int index_start = eln == th->pending_from ? th->index_from : 0; + int index_end = eln == th->pending_to ? th->index_to : eln->element_count; + LineartTriangle *tri = (void *)(((uchar *)eln->pointer) + ld->sizeof_triangle * index_start); + for (int ei = index_start; ei < index_end; ei++) { + int x1, x2, y1, y2; + int r, co; + if ((tri->flags & LRT_CULL_USED) || (tri->flags & LRT_CULL_DISCARD)) { + tri = (void *)(((uchar *)tri) + ld->sizeof_triangle); + continue; + } + if (lineart_get_triangle_bounding_areas(ld, tri, &y1, &y2, &x1, &x2)) { + _dir_control++; + for (co = x1; co <= x2; co++) { + for (r = y1; r <= y2; r++) { + lineart_bounding_area_link_triangle(ld, + &ld->qtree.initials[r * ld->qtree.count_x + co], + tri, + 0, + 1, + 0, + (!(tri->flags & LRT_TRIANGLE_NO_INTERSECTION)), + th); + } } + } /* Else throw away. */ + tri = (void *)(((uchar *)tri) + ld->sizeof_triangle); + } + } + } +} + +static void lineart_create_edges_from_isec_data(LineartIsecData *d) +{ + LineartData *ld = d->ld; + double ZMax = ld->conf.far_clip; + double ZMin = ld->conf.near_clip; + int total_lines = 0; + + for (int i = 0; i < d->thread_count; i++) { + LineartIsecThread *th = &d->threads[i]; + if (G.debug_value == 4000) { + printf("Thread %d isec generated %d lines.\n", i, th->current); + } + if (!th->current) { + continue; + } + total_lines += th->current; + } + + if (!total_lines) { + return; + } + + /* We don't care about removing duplicated vert in this method, chaining can handle that, + * and it saves us from using locks and look up tables. */ + LineartVert *v = lineart_mem_acquire(ld->edge_data_pool, sizeof(LineartVert) * total_lines * 2); + LineartEdge *e = lineart_mem_acquire(ld->edge_data_pool, sizeof(LineartEdge) * total_lines); + LineartEdgeSegment *es = lineart_mem_acquire(ld->edge_data_pool, + sizeof(LineartEdgeSegment) * total_lines); + + LineartElementLinkNode *eln = lineart_mem_acquire(ld->edge_data_pool, + sizeof(LineartElementLinkNode)); + eln->element_count = total_lines; + eln->pointer = e; + eln->flags |= LRT_ELEMENT_INTERSECTION_DATA; + BLI_addhead(&ld->geom.line_buffer_pointers, eln); + + for (int i = 0; i < d->thread_count; i++) { + LineartIsecThread *th = &d->threads[i]; + if (!th->current) { + continue; + } + + for (int j = 0; j < th->current; j++) { + LineartIsecSingle *is = &th->array[j]; + LineartVert *v1 = v; + LineartVert *v2 = v + 1; + copy_v3db_v3fl(v1->gloc, is->v1); + copy_v3db_v3fl(v2->gloc, is->v2); + /* The intersection line has been generated only in geometry space, so we need to transform + * them as well. */ + mul_v4_m4v3_db(v1->fbcoord, ld->conf.view_projection, v1->gloc); + mul_v4_m4v3_db(v2->fbcoord, ld->conf.view_projection, v2->gloc); + mul_v3db_db(v1->fbcoord, (1 / v1->fbcoord[3])); + mul_v3db_db(v2->fbcoord, (1 / v2->fbcoord[3])); + + v1->fbcoord[0] -= ld->conf.shift_x * 2; + v1->fbcoord[1] -= ld->conf.shift_y * 2; + v2->fbcoord[0] -= ld->conf.shift_x * 2; + v2->fbcoord[1] -= ld->conf.shift_y * 2; + + /* This z transformation is not the same as the rest of the part, because the data don't go + * through normal perspective division calls in the pipeline, but this way the 3D result and + * occlusion on the generated line is correct, and we don't really use 2D for viewport stroke + * generation anyway. */ + v1->fbcoord[2] = ZMin * ZMax / (ZMax - fabs(v1->fbcoord[2]) * (ZMax - ZMin)); + v2->fbcoord[2] = ZMin * ZMax / (ZMax - fabs(v2->fbcoord[2]) * (ZMax - ZMin)); + e->v1 = v1; + e->v2 = v2; + e->t1 = is->tri1; + e->t2 = is->tri2; + /* This is so we can also match intersection edges from shadow to later viewing stage. */ + e->edge_identifier = (((uint64_t)e->t1->target_reference) << 32) | e->t2->target_reference; + e->flags = LRT_EDGE_FLAG_INTERSECTION; + e->intersection_mask = (is->tri1->intersection_mask | is->tri2->intersection_mask); + BLI_addtail(&e->segments, es); + + int obi1 = (e->t1->target_reference & LRT_OBINDEX_HIGHER); + int obi2 = (e->t2->target_reference & LRT_OBINDEX_HIGHER); + LineartElementLinkNode *eln1 = lineart_find_matching_eln(&ld->geom.line_buffer_pointers, + obi1); + LineartElementLinkNode *eln2 = obi1 == obi2 ? eln1 : + lineart_find_matching_eln( + &ld->geom.line_buffer_pointers, obi2); + Object *ob1 = eln1 ? eln1->object_ref : NULL; + Object *ob2 = eln2 ? eln2->object_ref : NULL; + if (e->t1->intersection_priority > e->t2->intersection_priority) { + e->object_ref = ob1; + } + else if (e->t1->intersection_priority < e->t2->intersection_priority) { + e->object_ref = ob2; + } + else { /* equal priority */ + if (ob1 == ob2) { + /* object_ref should be ambiguous if intersection lines comes from different objects. */ + e->object_ref = ob1; } - } /* Else throw away. */ - tri = (void *)(((uchar *)tri) + rb->triangle_size); + } + + lineart_add_edge_to_array(&ld->pending_edges, e); + + v += 2; + e++; + es++; } } } /** + * Sequentially add triangles into render buffer, intersection lines between those triangles will + * also be computed at the same time. + */ +void lineart_main_add_triangles(LineartData *ld) +{ + double t_start; + if (G.debug_value == 4000) { + t_start = PIL_check_seconds_timer(); + } + + /* Initialize per-thread data for thread task scheduling information and storing intersection + * results. */ + LineartIsecData d = {0}; + lineart_init_isec_thread(&d, ld, ld->thread_count); + + TaskPool *tp = BLI_task_pool_create(NULL, TASK_PRIORITY_HIGH); + for (int i = 0; i < ld->thread_count; i++) { + BLI_task_pool_push(tp, (TaskRunFunction)lineart_add_triangles_worker, &d.threads[i], 0, NULL); + } + BLI_task_pool_work_and_wait(tp); + BLI_task_pool_free(tp); + + if (ld->conf.use_intersections) { + lineart_create_edges_from_isec_data(&d); + } + + lineart_destroy_isec_thread(&d); + + if (G.debug_value == 4000) { + double t_elapsed = PIL_check_seconds_timer() - t_start; + printf("Line art intersection time: %f\n", t_elapsed); + } +} + +/** * This function gets the tile for the point `e->v1`, and later use #lineart_bounding_area_next() * to get next along the way. */ -static LineartBoundingArea *lineart_edge_first_bounding_area(LineartRenderBuffer *rb, - LineartEdge *e) +LineartBoundingArea *lineart_edge_first_bounding_area(LineartData *ld, + double *fbcoord1, + double *fbcoord2) { - double data[2] = {e->v1->fbcoord[0], e->v1->fbcoord[1]}; + double data[2] = {fbcoord1[0], fbcoord1[1]}; double LU[2] = {-1, 1}, RU[2] = {1, 1}, LB[2] = {-1, -1}, RB[2] = {1, -1}; double r = 1, sr = 1; bool p_unused; if (data[0] > -1 && data[0] < 1 && data[1] > -1 && data[1] < 1) { - return lineart_get_bounding_area(rb, data[0], data[1]); + return lineart_get_bounding_area(ld, data[0], data[1]); } - if (lineart_intersect_seg_seg(e->v1->fbcoord, e->v2->fbcoord, LU, RU, &sr, &p_unused) && - sr < r && sr > 0) { + if (lineart_intersect_seg_seg(fbcoord1, fbcoord2, LU, RU, &sr, &p_unused) && sr < r && sr > 0) { r = sr; } - if (lineart_intersect_seg_seg(e->v1->fbcoord, e->v2->fbcoord, LB, RB, &sr, &p_unused) && - sr < r && sr > 0) { + if (lineart_intersect_seg_seg(fbcoord1, fbcoord2, LB, RB, &sr, &p_unused) && sr < r && sr > 0) { r = sr; } - if (lineart_intersect_seg_seg(e->v1->fbcoord, e->v2->fbcoord, LB, LU, &sr, &p_unused) && - sr < r && sr > 0) { + if (lineart_intersect_seg_seg(fbcoord1, fbcoord2, LB, LU, &sr, &p_unused) && sr < r && sr > 0) { r = sr; } - if (lineart_intersect_seg_seg(e->v1->fbcoord, e->v2->fbcoord, RB, RU, &sr, &p_unused) && - sr < r && sr > 0) { + if (lineart_intersect_seg_seg(fbcoord1, fbcoord2, RB, RU, &sr, &p_unused) && sr < r && sr > 0) { r = sr; } - interp_v2_v2v2_db(data, e->v1->fbcoord, e->v2->fbcoord, r); + interp_v2_v2v2_db(data, fbcoord1, fbcoord2, r); - return lineart_get_bounding_area(rb, data[0], data[1]); + return lineart_get_bounding_area(ld, data[0], data[1]); } /** * This march along one render line in image space and * get the next bounding area the line is crossing. */ -static LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this, - LineartEdge *e, - double x, - double y, - double k, - int positive_x, - int positive_y, - double *next_x, - double *next_y) +LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this, + double *fbcoord1, + double *fbcoord2, + double x, + double y, + double k, + int positive_x, + int positive_y, + double *next_x, + double *next_y) { double rx, ry, ux, uy, lx, ly, bx, by; double r1, r2; @@ -4231,8 +4740,8 @@ static LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this if (positive_y > 0) { uy = this->u; ux = x + (uy - y) / k; - r1 = ratiod(e->v1->fbcoord[0], e->v2->fbcoord[0], rx); - r2 = ratiod(e->v1->fbcoord[0], e->v2->fbcoord[0], ux); + r1 = ratiod(fbcoord1[0], fbcoord2[0], rx); + r2 = ratiod(fbcoord1[0], fbcoord2[0], ux); if (MIN2(r1, r2) > 1) { return 0; } @@ -4264,8 +4773,8 @@ static LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this else if (positive_y < 0) { by = this->b; bx = x + (by - y) / k; - r1 = ratiod(e->v1->fbcoord[0], e->v2->fbcoord[0], rx); - r2 = ratiod(e->v1->fbcoord[0], e->v2->fbcoord[0], bx); + r1 = ratiod(fbcoord1[0], fbcoord2[0], rx); + r2 = ratiod(fbcoord1[0], fbcoord2[0], bx); if (MIN2(r1, r2) > 1) { return 0; } @@ -4292,7 +4801,7 @@ static LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this } /* If the line is completely horizontal, in which Y difference == 0. */ else { - r1 = ratiod(e->v1->fbcoord[0], e->v2->fbcoord[0], this->r); + r1 = ratiod(fbcoord1[0], fbcoord2[0], this->r); if (r1 > 1) { return 0; } @@ -4316,8 +4825,8 @@ static LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this if (positive_y > 0) { uy = this->u; ux = x + (uy - y) / k; - r1 = ratiod(e->v1->fbcoord[0], e->v2->fbcoord[0], lx); - r2 = ratiod(e->v1->fbcoord[0], e->v2->fbcoord[0], ux); + r1 = ratiod(fbcoord1[0], fbcoord2[0], lx); + r2 = ratiod(fbcoord1[0], fbcoord2[0], ux); if (MIN2(r1, r2) > 1) { return 0; } @@ -4347,8 +4856,8 @@ static LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this else if (positive_y < 0) { by = this->b; bx = x + (by - y) / k; - r1 = ratiod(e->v1->fbcoord[0], e->v2->fbcoord[0], lx); - r2 = ratiod(e->v1->fbcoord[0], e->v2->fbcoord[0], bx); + r1 = ratiod(fbcoord1[0], fbcoord2[0], lx); + r2 = ratiod(fbcoord1[0], fbcoord2[0], bx); if (MIN2(r1, r2) > 1) { return 0; } @@ -4375,7 +4884,7 @@ static LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this } /* Again, horizontal. */ else { - r1 = ratiod(e->v1->fbcoord[0], e->v2->fbcoord[0], this->l); + r1 = ratiod(fbcoord1[0], fbcoord2[0], this->l); if (r1 > 1) { return 0; } @@ -4392,7 +4901,7 @@ static LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this /* If the line is completely vertical, hence X difference == 0. */ else { if (positive_y > 0) { - r1 = ratiod(e->v1->fbcoord[1], e->v2->fbcoord[1], this->u); + r1 = ratiod(fbcoord1[1], fbcoord2[1], this->u); if (r1 > 1) { return 0; } @@ -4406,7 +4915,7 @@ static LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this } } else if (positive_y < 0) { - r1 = ratiod(e->v1->fbcoord[1], e->v2->fbcoord[1], this->b); + r1 = ratiod(fbcoord1[1], fbcoord2[1], this->b); if (r1 > 1) { return 0; } @@ -4427,24 +4936,26 @@ static LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this return 0; } +/** + * This is the entry point of all line art calculations. + * + * \return True when a change is made. + */ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, LineartGpencilModifierData *lmd, LineartCache **cached_result, bool enable_stroke_depth_offset) { - LineartRenderBuffer *rb; + LineartData *ld; Scene *scene = DEG_get_evaluated_scene(depsgraph); int intersections_only = 0; /* Not used right now, but preserve for future. */ Object *use_camera; double t_start; - if (G.debug_value == 4000) { t_start = PIL_check_seconds_timer(); } - BKE_scene_camera_switch_update(scene); - if (lmd->calculation_flags & LRT_USE_CUSTOM_CAMERA) { if (!lmd->source_camera || (use_camera = DEG_get_evaluated_object(depsgraph, lmd->source_camera))->type != @@ -4453,6 +4964,9 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, } } else { + + BKE_scene_camera_switch_update(scene); + if (!scene->camera) { return false; } @@ -4462,54 +4976,79 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, LineartCache *lc = lineart_init_cache(); *cached_result = lc; - rb = lineart_create_render_buffer(scene, lmd, use_camera, scene->camera, lc); + ld = lineart_create_render_buffer(scene, lmd, use_camera, scene->camera, lc); /* Triangle thread testing data size varies depending on the thread count. * See definition of LineartTriangleThread for details. */ - rb->triangle_size = lineart_triangle_size_get(scene, rb); - - /* FIXME(Yiming): See definition of int #LineartRenderBuffer::_source_type for detailed. */ - rb->_source_type = lmd->source_type; - rb->_source_collection = lmd->source_collection; - rb->_source_object = lmd->source_object; + ld->sizeof_triangle = lineart_triangle_size_get(ld); + + LineartData *shadow_rb = NULL; + LineartElementLinkNode *shadow_veln, *shadow_eeln; + ListBase *shadow_elns = ld->conf.shadow_selection ? &lc->shadow_elns : NULL; + bool shadow_generated = lineart_main_try_generate_shadow(depsgraph, + scene, + ld, + lmd, + &lc->shadow_data_pool, + &shadow_veln, + &shadow_eeln, + shadow_elns, + &shadow_rb); /* Get view vector before loading geometries, because we detect feature lines there. */ - lineart_main_get_view_vector(rb); - lineart_main_load_geometries( - depsgraph, scene, use_camera, rb, lmd->calculation_flags & LRT_ALLOW_DUPLI_OBJECTS); + lineart_main_get_view_vector(ld); + + lineart_main_load_geometries(depsgraph, + scene, + use_camera, + ld, + lmd->calculation_flags & LRT_ALLOW_DUPLI_OBJECTS, + false, + shadow_elns); - if (!rb->vertex_buffer_pointers.first) { + if (shadow_generated) { + lineart_main_transform_and_add_shadow(ld, shadow_veln, shadow_eeln); + } + + if (!ld->geom.vertex_buffer_pointers.first) { /* No geometry loaded, return early. */ return true; } /* Initialize the bounding box acceleration structure, it's a lot like BVH in 3D. */ - lineart_main_bounding_area_make_initial(rb); + lineart_main_bounding_area_make_initial(ld); /* We need to get cut into triangles that are crossing near/far plans, only this way can we get * correct coordinates of those clipped lines. Done in two steps, * setting clip_far==false for near plane. */ - lineart_main_cull_triangles(rb, false); + lineart_main_cull_triangles(ld, false); /* `clip_far == true` for far plane. */ - lineart_main_cull_triangles(rb, true); + lineart_main_cull_triangles(ld, true); /* At this point triangle adjacent info pointers is no longer needed, free them. */ - lineart_main_free_adjacent_data(rb); + lineart_main_free_adjacent_data(ld); /* Do the perspective division after clipping is done. */ - lineart_main_perspective_division(rb); + lineart_main_perspective_division(ld); - lineart_main_discard_out_of_frame_edges(rb); + lineart_main_discard_out_of_frame_edges(ld); /* Triangle intersections are done here during sequential adding of them. Only after this, * triangles and lines are all linked with acceleration structure, and the 2D occlusion stage * can do its job. */ - lineart_main_add_triangles(rb); + lineart_main_add_triangles(ld); + + /* Add shadow cuts to intersection lines as well. */ + lineart_register_intersection_shadow_cuts(ld, shadow_elns); + + /* Re-link bounding areas because they have been subdivided by worker threads and we need + * adjacent info. */ + lineart_main_bounding_areas_connect_post(ld); /* Link lines to acceleration structure, this can only be done after perspective division, if * we do it after triangles being added, the acceleration structure has already been * subdivided, this way we do less list manipulations. */ - lineart_main_link_lines(rb); + lineart_main_link_lines(ld); /* "intersection_only" is preserved for being called in a standalone fashion. * If so the data will already be available at the stage. Otherwise we do the occlusion and @@ -4518,54 +5057,65 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, if (!intersections_only) { /* Occlusion is work-and-wait. This call will not return before work is completed. */ - lineart_main_occlusion_begin(rb); + lineart_main_occlusion_begin(ld); + + lineart_main_make_enclosed_shapes(ld, shadow_rb); + + lineart_main_remove_unused_lines_from_tiles(ld); /* Chaining is all single threaded. See lineart_chain.c * In this particular call, only lines that are geometrically connected (share the _exact_ * same end point) will be chained together. */ - MOD_lineart_chain_feature_lines(rb); + MOD_lineart_chain_feature_lines(ld); /* We are unable to take care of occlusion if we only connect end points, so here we do a * spit, where the splitting point could be any cut in e->segments. */ - MOD_lineart_chain_split_for_fixed_occlusion(rb); + MOD_lineart_chain_split_for_fixed_occlusion(ld); /* Then we connect chains based on the _proximity_ of their end points in image space, here's * the place threshold value gets involved. */ - MOD_lineart_chain_connect(rb); - - float *t_image = &lmd->chaining_image_threshold; - /* This configuration ensures there won't be accidental lost of short unchained segments. */ - MOD_lineart_chain_discard_short(rb, MIN2(*t_image, 0.001f) - FLT_EPSILON); + MOD_lineart_chain_connect(ld); - if (rb->chain_smooth_tolerance > FLT_EPSILON) { + if (ld->conf.chain_smooth_tolerance > FLT_EPSILON) { /* Keeping UI range of 0-1 for ease of read while scaling down the actual value for best * effective range in image-space (Coordinate only goes from -1 to 1). This value is * somewhat arbitrary, but works best for the moment. */ - MOD_lineart_smooth_chains(rb, rb->chain_smooth_tolerance / 50); + MOD_lineart_smooth_chains(ld, ld->conf.chain_smooth_tolerance / 50); } - if (rb->use_image_boundary_trimming) { - MOD_lineart_chain_clip_at_border(rb); + if (ld->conf.use_image_boundary_trimming) { + MOD_lineart_chain_clip_at_border(ld); } - if (rb->angle_splitting_threshold > FLT_EPSILON) { - MOD_lineart_chain_split_angle(rb, rb->angle_splitting_threshold); + if (ld->conf.angle_splitting_threshold > FLT_EPSILON) { + MOD_lineart_chain_split_angle(ld, ld->conf.angle_splitting_threshold); } if (enable_stroke_depth_offset && lmd->stroke_depth_offset > FLT_EPSILON) { MOD_lineart_chain_offset_towards_camera( - rb, lmd->stroke_depth_offset, lmd->flags & LRT_GPENCIL_OFFSET_TOWARDS_CUSTOM_CAMERA); + ld, lmd->stroke_depth_offset, lmd->flags & LRT_GPENCIL_OFFSET_TOWARDS_CUSTOM_CAMERA); + } + + if (ld->conf.shadow_use_silhouette) { + MOD_lineart_chain_find_silhouette_backdrop_objects(ld); } /* Finally transfer the result list into cache. */ - memcpy(&lc->chains, &rb->chains, sizeof(ListBase)); + memcpy(&lc->chains, &ld->chains, sizeof(ListBase)); /* At last, we need to clear flags so we don't confuse GPencil generation calls. */ MOD_lineart_chain_clear_picked_flag(lc); } + lineart_mem_destroy(&lc->shadow_data_pool); + + if (ld->conf.shadow_enclose_shapes && shadow_rb) { + lineart_destroy_render_data_keep_init(shadow_rb); + MEM_freeN(shadow_rb); + } + if (G.debug_value == 4000) { - lineart_count_and_print_render_buffer_memory(rb); + lineart_count_and_print_render_buffer_memory(ld); double t_elapsed = PIL_check_seconds_timer() - t_start; printf("Line art total time: %lf\n", t_elapsed); @@ -4574,18 +5124,6 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, return true; } -static int UNUSED_FUNCTION(lineart_rb_edge_types)(LineartRenderBuffer *rb) -{ - int types = 0; - types |= rb->use_contour ? LRT_EDGE_FLAG_CONTOUR : 0; - types |= rb->use_crease ? LRT_EDGE_FLAG_CREASE : 0; - types |= rb->use_material ? LRT_EDGE_FLAG_MATERIAL : 0; - types |= rb->use_edge_marks ? LRT_EDGE_FLAG_EDGE_MARK : 0; - types |= rb->use_intersections ? LRT_EDGE_FLAG_INTERSECTION : 0; - types |= rb->use_loose ? LRT_EDGE_FLAG_LOOSE : 0; - return types; -} - static void lineart_gpencil_generate(LineartCache *cache, Depsgraph *depsgraph, Object *gpencil_object, @@ -4601,8 +5139,10 @@ static void lineart_gpencil_generate(LineartCache *cache, uchar mask_switches, uchar material_mask_bits, uchar intersection_mask, - short thickness, + int16_t thickness, float opacity, + uchar shaodow_selection, + uchar silhouette_mode, const char *source_vgname, const char *vgname, int modifier_flags) @@ -4630,9 +5170,10 @@ static void lineart_gpencil_generate(LineartCache *cache, /* (!orig_col && !orig_ob) means the whole scene is selected. */ - int enabled_types = cache->rb_edge_types; + int enabled_types = cache->all_enabled_edge_types; bool invert_input = modifier_flags & LRT_GPENCIL_INVERT_SOURCE_VGROUP; bool match_output = modifier_flags & LRT_GPENCIL_MATCH_OUTPUT_VGROUP; + bool inverse_silhouette = modifier_flags & LRT_GPENCIL_INVERT_SILHOUETTE_FILTER; LISTBASE_FOREACH (LineartEdgeChain *, ec, &cache->chains) { @@ -4684,11 +5225,55 @@ static void lineart_gpencil_generate(LineartCache *cache, } } } + if (shaodow_selection) { + if (ec->shadow_mask_bits != LRT_SHADOW_MASK_UNDEFINED) { + /* TODO(@Yiming): Give a behavior option for how to display undefined shadow info. */ + if ((shaodow_selection == LRT_SHADOW_FILTER_LIT && + (!(ec->shadow_mask_bits & LRT_SHADOW_MASK_LIT))) || + (shaodow_selection == LRT_SHADOW_FILTER_SHADED && + (!(ec->shadow_mask_bits & LRT_SHADOW_MASK_SHADED)))) { + continue; + } + } + } + if (silhouette_mode && (ec->type & (LRT_EDGE_FLAG_CONTOUR))) { + bool is_silhouette = false; + if (orig_col) { + if (!ec->silhouette_backdrop) { + is_silhouette = true; + } + else if (!BKE_collection_has_object_recursive_instanced(orig_col, + ec->silhouette_backdrop)) { + is_silhouette = true; + } + } + else { + if ((!orig_ob) && (!ec->silhouette_backdrop)) { + is_silhouette = true; + } + } + + if ((silhouette_mode == LRT_SILHOUETTE_FILTER_INDIVIDUAL || orig_ob) && + ec->silhouette_backdrop != ec->object_ref) { + is_silhouette = true; + } + + if (inverse_silhouette) { + is_silhouette = !is_silhouette; + } + if (!is_silhouette) { + continue; + } + } /* Preserved: If we ever do asynchronous generation, this picked flag should be set here. */ // ec->picked = 1; const int count = MOD_lineart_chain_count(ec); + if (count < 2) { + continue; + } + bGPDstroke *gps = BKE_gpencil_stroke_add(gpf, color_idx, count, thickness, false); int i; @@ -4760,17 +5345,19 @@ void MOD_lineart_gpencil_generate(LineartCache *cache, Object *ob, bGPDlayer *gpl, bGPDframe *gpf, - char source_type, + int8_t source_type, void *source_reference, int level_start, int level_end, int mat_nr, - short edge_types, + int16_t edge_types, uchar mask_switches, uchar material_mask_bits, uchar intersection_mask, - short thickness, + int16_t thickness, float opacity, + uchar shadow_selection, + uchar silhouette_mode, const char *source_vgname, const char *vgname, int modifier_flags) @@ -4782,26 +5369,20 @@ void MOD_lineart_gpencil_generate(LineartCache *cache, Object *source_object = NULL; Collection *source_collection = NULL; - short use_types = 0; + int16_t use_types = edge_types; if (source_type == LRT_SOURCE_OBJECT) { if (!source_reference) { return; } source_object = (Object *)source_reference; - /* Note that intersection lines will only be in collection. */ - use_types = edge_types & (~LRT_EDGE_FLAG_INTERSECTION); } else if (source_type == LRT_SOURCE_COLLECTION) { if (!source_reference) { return; } source_collection = (Collection *)source_reference; - use_types = edge_types; - } - else { - /* Whole scene. */ - use_types = edge_types; } + float gp_obmat_inverse[4][4]; invert_m4_m4(gp_obmat_inverse, ob->obmat); lineart_gpencil_generate(cache, @@ -4821,6 +5402,8 @@ void MOD_lineart_gpencil_generate(LineartCache *cache, intersection_mask, thickness, opacity, + shadow_selection, + silhouette_mode, source_vgname, vgname, modifier_flags); |