diff options
14 files changed, 774 insertions, 112 deletions
diff --git a/release/scripts/startup/bl_ui/properties_material.py b/release/scripts/startup/bl_ui/properties_material.py index b217e33de12..5df58f8ac4c 100644 --- a/release/scripts/startup/bl_ui/properties_material.py +++ b/release/scripts/startup/bl_ui/properties_material.py @@ -304,6 +304,11 @@ class MATERIAL_PT_lineart(MaterialButtonsPanel, Panel): sub.active = lineart.use_mat_occlusion sub.prop(lineart, "mat_occlusion", slider=True, text="Levels") + row = layout.row(align=True, heading="Custom occlusion effectiveness") + row.active = lineart.use_occlusion_effectiveness + row.prop(lineart, "use_occlusion_effectiveness", text="") + row.prop(lineart, "occlusion_effectiveness", slider=True) + classes = ( MATERIAL_MT_context_menu, diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 11cf3ea5083..f6b65170d1b 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -1439,10 +1439,7 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) id_us_plus(&md->target_material->id); } - if (use_lights) { - ob->dtx |= OB_USE_GPENCIL_LIGHTS; - } - else { + if (!use_lights) { ob->dtx &= ~OB_USE_GPENCIL_LIGHTS; } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c index 9593a1364e7..f58de44b04c 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c @@ -59,6 +59,7 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" +#include "MOD_gpencil_lineart.h" #include "MOD_gpencil_modifiertypes.h" #include "MOD_gpencil_ui_common.h" @@ -160,12 +161,14 @@ static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Objec LineartCache *local_lc = gpd->runtime.lineart_cache; if (!gpd->runtime.lineart_cache) { - MOD_lineart_compute_feature_lines(depsgraph, lmd, &gpd->runtime.lineart_cache); + MOD_lineart_compute_feature_lines( + depsgraph, lmd, &gpd->runtime.lineart_cache, (!(ob->dtx & OB_DRAW_IN_FRONT))); MOD_lineart_destroy_render_data(lmd); } else { if (!(lmd->flags & LRT_GPENCIL_USE_CACHE)) { - MOD_lineart_compute_feature_lines(depsgraph, lmd, &local_lc); + MOD_lineart_compute_feature_lines( + depsgraph, lmd, &local_lc, (!(ob->dtx & OB_DRAW_IN_FRONT))); MOD_lineart_destroy_render_data(lmd); } MOD_lineart_chain_clear_picked_flag(local_lc); @@ -205,7 +208,8 @@ static void bakeModifier(Main *UNUSED(bmain), } if (!gpd->runtime.lineart_cache) { - MOD_lineart_compute_feature_lines(depsgraph, lmd, &gpd->runtime.lineart_cache); + MOD_lineart_compute_feature_lines( + depsgraph, lmd, &gpd->runtime.lineart_cache, (!(ob->dtx & OB_DRAW_IN_FRONT))); MOD_lineart_destroy_render_data(lmd); } @@ -254,6 +258,7 @@ static void updateDepsgraph(GpencilModifierData *md, else { add_this_collection(ctx->scene->master_collection, ctx, mode); } + DEG_add_object_relation( ctx->node, ctx->scene->camera, DEG_OB_COMP_TRANSFORM, "Line Art Modifier"); DEG_add_object_relation( @@ -268,6 +273,8 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, walk(userData, ob, (ID **)&lmd->source_collection, IDWALK_CB_NOP); walk(userData, ob, (ID **)&lmd->source_object, IDWALK_CB_NOP); + walk(userData, ob, (ID **)&lmd->source_camera, IDWALK_CB_NOP); + walk(userData, ob, (ID **)&lmd->light_contour_object, IDWALK_CB_NOP); } static void panel_draw(const bContext *UNUSED(C), Panel *panel) @@ -404,14 +411,23 @@ static void style_panel_draw(const bContext *UNUSED(C), Panel *panel) static void occlusion_panel_draw(const bContext *UNUSED(C), Panel *panel) { uiLayout *layout = panel->layout; - PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); + PointerRNA ob_ptr; + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr); const bool is_baked = RNA_boolean_get(ptr, "is_baked"); + const bool use_multiple_levels = RNA_boolean_get(ptr, "use_multiple_levels"); + const bool show_in_front = RNA_boolean_get(&ob_ptr, "show_in_front"); + uiLayoutSetPropSep(layout, true); uiLayoutSetEnabled(layout, !is_baked); - const bool use_multiple_levels = RNA_boolean_get(ptr, "use_multiple_levels"); + if (!show_in_front) { + uiItemL(layout, IFACE_("Object is not in front"), ICON_INFO); + } + + layout = uiLayoutColumn(layout, false); + uiLayoutSetActive(layout, show_in_front); uiItemR(layout, ptr, "use_multiple_levels", 0, IFACE_("Range"), ICON_NONE); @@ -439,10 +455,14 @@ static void material_mask_panel_draw_header(const bContext *UNUSED(C), Panel *pa static void material_mask_panel_draw(const bContext *UNUSED(C), Panel *panel) { uiLayout *layout = panel->layout; - PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); + PointerRNA ob_ptr; + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr); const bool is_baked = RNA_boolean_get(ptr, "is_baked"); + const bool show_in_front = RNA_boolean_get(&ob_ptr, "show_in_front"); + uiLayoutSetEnabled(layout, !is_baked); + uiLayoutSetActive(layout, show_in_front); uiLayoutSetPropSep(layout, true); @@ -463,17 +483,33 @@ static void material_mask_panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(col, ptr, "use_material_mask_match", 0, IFACE_("Match All Masks"), ICON_NONE); } +static void intersection_panel_draw_header(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *layout = panel->layout; + PointerRNA ob_ptr; + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr); + + const bool is_baked = RNA_boolean_get(ptr, "is_baked"); + const bool use_isec = RNA_boolean_get(ptr, "use_intersection"); + + uiLayoutSetEnabled(layout, !is_baked); + uiLayoutSetActive(layout, use_isec); + + uiItemR(layout, ptr, "use_intersection_filter", 0, IFACE_("Filter Intersection"), ICON_NONE); +} + static void intersection_panel_draw(const bContext *UNUSED(C), Panel *panel) { uiLayout *layout = panel->layout; PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); const bool is_baked = RNA_boolean_get(ptr, "is_baked"); + const bool use_isec = RNA_boolean_get(ptr, "use_intersection"); + const bool use_isec_filter = RNA_boolean_get(ptr, "use_intersection_filter"); uiLayoutSetEnabled(layout, !is_baked); - uiLayoutSetPropSep(layout, true); - uiLayoutSetActive(layout, RNA_boolean_get(ptr, "use_intersection")); + uiLayoutSetActive(layout, use_isec && use_isec_filter); uiLayout *row = uiLayoutRow(layout, true); uiLayoutSetPropDecorate(row, false); @@ -489,6 +525,7 @@ static void intersection_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayout *col = uiLayoutColumn(layout, true); uiItemR(col, ptr, "use_intersection_match", 0, IFACE_("Match All Masks"), ICON_NONE); } + static void face_mark_panel_draw_header(const bContext *UNUSED(C), Panel *panel) { uiLayout *layout = panel->layout; @@ -496,16 +533,13 @@ static void face_mark_panel_draw_header(const bContext *UNUSED(C), Panel *panel) PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr); const bool is_baked = RNA_boolean_get(ptr, "is_baked"); - const bool use_cache = RNA_boolean_get(ptr, "use_cache"); - const bool is_first = BKE_gpencil_is_first_lineart_in_stack(ob_ptr.data, ptr->data); + const bool use_cache = RNA_boolean_get(ptr, "use_cached_result"); - if (!use_cache || is_first) { - uiLayoutSetEnabled(layout, !is_baked); - uiItemR(layout, ptr, "use_face_mark", 0, IFACE_("Face Mark Filtering"), ICON_NONE); - } - else { - uiItemL(layout, IFACE_("Face Mark Filtering"), ICON_NONE); - } + uiLayoutSetEnabled( + layout, + !is_baked && (!use_cache || BKE_gpencil_is_first_lineart_in_stack(ob_ptr.data, ptr->data))); + + uiItemR(layout, ptr, "use_face_mark", 0, IFACE_("Filter Face Mark"), ICON_NONE); } static void face_mark_panel_draw(const bContext *UNUSED(C), Panel *panel) @@ -516,15 +550,11 @@ static void face_mark_panel_draw(const bContext *UNUSED(C), Panel *panel) const bool is_baked = RNA_boolean_get(ptr, "is_baked"); const bool use_mark = RNA_boolean_get(ptr, "use_face_mark"); - const bool use_cache = RNA_boolean_get(ptr, "use_cache"); - const bool is_first = BKE_gpencil_is_first_lineart_in_stack(ob_ptr.data, ptr->data); + const bool use_cache = RNA_boolean_get(ptr, "use_cached_result"); - uiLayoutSetEnabled(layout, !is_baked); - - if (use_cache && !is_first) { - uiItemL(layout, "Cached from the first line art modifier.", ICON_INFO); - return; - } + uiLayoutSetEnabled( + layout, + !is_baked && (!use_cache || BKE_gpencil_is_first_lineart_in_stack(ob_ptr.data, ptr->data))); uiLayoutSetPropSep(layout, true); @@ -542,7 +572,7 @@ static void chaining_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayout *layout = panel->layout; const bool is_baked = RNA_boolean_get(ptr, "is_baked"); - const bool use_cache = RNA_boolean_get(ptr, "use_cache"); + const bool use_cache = RNA_boolean_get(ptr, "use_cached_result"); const bool is_first = BKE_gpencil_is_first_lineart_in_stack(ob_ptr.data, ptr->data); const bool is_geom = RNA_boolean_get(ptr, "use_geometry_space_chain"); @@ -568,6 +598,7 @@ static void chaining_panel_draw(const bContext *UNUSED(C), Panel *panel) is_geom ? IFACE_("Geometry Threshold") : NULL, ICON_NONE); + uiItemR(layout, ptr, "smooth_tolerance", UI_ITEM_R_SLIDER, NULL, ICON_NONE); uiItemR(layout, ptr, "split_angle", UI_ITEM_R_SLIDER, NULL, ICON_NONE); } @@ -583,7 +614,9 @@ static void vgroup_panel_draw(const bContext *UNUSED(C), Panel *panel) const bool is_first = BKE_gpencil_is_first_lineart_in_stack(ob_ptr.data, ptr->data); uiLayoutSetPropSep(layout, true); - uiLayoutSetEnabled(layout, !is_baked); + uiLayoutSetEnabled( + layout, + !is_baked && (!use_cache || BKE_gpencil_is_first_lineart_in_stack(ob_ptr.data, ptr->data))); if (use_cache && !is_first) { uiItemL(layout, "Cached from the first line art modifier.", ICON_INFO); @@ -634,6 +667,28 @@ static void baking_panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemO(col, NULL, ICON_NONE, "OBJECT_OT_lineart_clear_all"); } +static void composition_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + PointerRNA ob_ptr; + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr); + + uiLayout *layout = panel->layout; + + const bool show_in_front = RNA_boolean_get(&ob_ptr, "show_in_front"); + + uiLayoutSetPropSep(layout, true); + + if (show_in_front) { + uiItemL(layout, IFACE_("Object is shown in front"), ICON_ERROR); + } + + uiLayout *col = uiLayoutColumn(layout, false); + uiLayoutSetActive(col, !show_in_front); + + uiItemR(col, ptr, "stroke_offset", UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(col, ptr, "offset_towards_custom_camera", 0, IFACE_("Towards Custom Camera"), ICON_NONE); +} + static void panelRegister(ARegionType *region_type) { PanelType *panel_type = gpencil_modifier_panel_register( @@ -653,6 +708,14 @@ static void panelRegister(ARegionType *region_type) material_mask_panel_draw_header, material_mask_panel_draw, occlusion_panel); + gpencil_modifier_subpanel_register(region_type, + "intersection", + "", + intersection_panel_draw_header, + intersection_panel_draw, + panel_type); + gpencil_modifier_subpanel_register( + region_type, "face_mark", "", face_mark_panel_draw_header, face_mark_panel_draw, panel_type); gpencil_modifier_subpanel_register( region_type, "intersection", "Intersection", NULL, intersection_panel_draw, panel_type); gpencil_modifier_subpanel_register( @@ -662,6 +725,8 @@ static void panelRegister(ARegionType *region_type) gpencil_modifier_subpanel_register( region_type, "vgroup", "Vertex Weight Transfer", NULL, vgroup_panel_draw, panel_type); gpencil_modifier_subpanel_register( + region_type, "composition", "Composition", NULL, composition_panel_draw, panel_type); + gpencil_modifier_subpanel_register( region_type, "baking", "Baking", NULL, baking_panel_draw, panel_type); } diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h index 247b0b3f57b..d77e0900535 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h @@ -216,6 +216,14 @@ enum eLineArtTileRecursiveLimit { #define LRT_TILE_SPLITTING_TRIANGLE_LIMIT 100 #define LRT_TILE_EDGE_COUNT_INITIAL 32 +struct BVHTree; + +typedef struct LineartTriangleBufferIndexer { + int start_index; /* The first one. */ + int end_index; /* Not include the last one. */ + LineartElementLinkNode *eln; +} LineartTriangleBufferIndexer; + typedef struct LineartRenderBuffer { struct LineartRenderBuffer *prev, *next; @@ -228,17 +236,26 @@ typedef struct LineartRenderBuffer { double view_projection[4][4]; double view[4][4]; + float overscan; + struct LineartBoundingArea *initial_bounding_areas; unsigned int bounding_area_count; - /* When splitting bounding areas, if there's an ortho camera placed at a straight angle, there - * will be a lot of triangles aligned in line which can not be separated by continue subdividing - * the tile. So we set a strict limit when using ortho camera. See eLineArtTileRecursiveLimit. */ + struct BVHTree *bvh_main; + int bvh_face_count; + struct LineartTriangleBufferIndexer *bvh_triangle_indexer; + int bvh_inderxer_count; + + /* When splitting bounding areas, if there's an ortho camera placed at a straight angle, + * there will be a lot of triangles aligned in line which can not be separated by continue + * subdividing the tile. So we set a strict limit when using ortho camera. See + * eLineArtTileRecursiveLimit. */ int tile_recursive_level; ListBase vertex_buffer_pointers; ListBase line_buffer_pointers; ListBase triangle_buffer_pointers; + LineartTriangle **triangle_lookup; /** This one's memory is not from main pool and is free()ed after culling stage. */ ListBase triangle_adjacent_pointers; @@ -288,6 +305,7 @@ typedef struct LineartRenderBuffer { bool use_edge_marks; bool use_intersections; bool use_loose; + bool use_light_contour; bool fuzzy_intersections; bool fuzzy_everything; bool allow_boundaries; @@ -306,11 +324,17 @@ typedef struct LineartRenderBuffer { bool cam_is_persp; float cam_obmat[4][4]; double camera_pos[3]; + double active_camera_pos[3]; /* Stroke offset calculation may use active or selected camera. */ double near_clip, far_clip; float shift_x, shift_y; float crease_threshold; float chaining_image_threshold; float angle_splitting_threshold; + float chain_smooth_tolerance; + + /* Could be direction or position, depends on light_is_sun. */ + double light_vector[3]; + bool light_is_sun; /* FIXME(Yiming): Temporary solution for speeding up calculation by not including lines that * are not in the selected source. This will not be needed after we have a proper scene-wise @@ -320,6 +344,8 @@ typedef struct LineartRenderBuffer { struct Collection *_source_collection; struct Object *_source_object; + int debug_triangle_isec_test_count; + } LineartRenderBuffer; typedef struct LineartCache { @@ -368,6 +394,7 @@ typedef struct LineartRenderTaskInfo { ListBase material; ListBase edge_mark; ListBase floating; + ListBase light_contour; } LineartRenderTaskInfo; @@ -393,6 +420,7 @@ typedef struct LineartObjectInfo { ListBase material; ListBase edge_mark; ListBase floating; + ListBase light_contour; } LineartObjectInfo; @@ -402,7 +430,7 @@ typedef struct LineartObjectLoadTaskInfo { /* LinkNode styled list */ LineartObjectInfo *pending; /* Used to spread the load across several threads. This can not overflow. */ - long unsigned int total_faces; + uint64_t total_faces; } LineartObjectLoadTaskInfo; /** @@ -586,13 +614,18 @@ void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb); void MOD_lineart_chain_connect(LineartRenderBuffer *rb); void MOD_lineart_chain_discard_short(LineartRenderBuffer *rb, const float threshold); void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshold_rad); +void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance); +void MOD_lineart_chain_offset_towards_camera(LineartRenderBuffer *rb, + float dist, + bool use_custom_camera); int MOD_lineart_chain_count(const LineartEdgeChain *ec); void MOD_lineart_chain_clear_picked_flag(LineartCache *lc); bool MOD_lineart_compute_feature_lines(struct Depsgraph *depsgraph, struct LineartGpencilModifierData *lmd, - LineartCache **cached_result); + struct LineartCache **cached_result, + bool enable_stroke_offset); struct Scene; diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c index 52485648ee0..1471210d18b 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c @@ -520,7 +520,7 @@ static void lineart_bounding_area_link_point_recursive(LineartRenderBuffer *rb, { if (root->child == NULL) { LineartChainRegisterEntry *cre = lineart_list_append_pointer_pool_sized( - &root->linked_chains, &rb->render_data_pool, ec, sizeof(LineartChainRegisterEntry)); + &root->linked_chains, rb->chain_data_pool, ec, sizeof(LineartChainRegisterEntry)); cre->eci = eci; @@ -924,6 +924,34 @@ void MOD_lineart_chain_clear_picked_flag(LineartCache *lc) } } +void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance) +{ + LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) { + LineartEdgeChainItem *next_eci; + for (LineartEdgeChainItem *eci = ec->chain.first; eci; eci = next_eci) { + next_eci = eci->next; + LineartEdgeChainItem *eci2, *eci3, *eci4; + + /* Not enough point to do simplify. */ + if ((!(eci2 = eci->next)) || (!(eci3 = eci2->next))) { + continue; + } + + /* No need to care for different line types/occlusion and so on, because at this stage they + * are all the same within a chain. */ + + /* If p3 is within the p1-p2 segment of a width of "tolerance" */ + if (dist_to_line_segment_v2(eci3->pos, eci->pos, eci2->pos) < tolerance) { + /* And if p4 is on the extension of p1-p2 , we remove p3. */ + if ((eci4 = eci3->next) && (dist_to_line_v2(eci4->pos, eci->pos, eci2->pos) < tolerance)) { + BLI_remlink(&ec->chain, eci3); + next_eci = eci; + } + } + } + } +} + /** * This should always be the last stage!, see the end of * #MOD_lineart_chain_split_for_fixed_occlusion(). @@ -980,3 +1008,26 @@ void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshol } } } + +void MOD_lineart_chain_offset_towards_camera(LineartRenderBuffer *rb, + float dist, + bool use_custom_camera) +{ + float dir[3]; + float cam[3]; + + if (use_custom_camera) { + copy_v3fl_v3db(cam, rb->camera_pos); + } + else { + copy_v3fl_v3db(cam, rb->active_camera_pos); + } + + LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) { + LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) { + sub_v3_v3v3(dir, cam, eci->gpos); + normalize_v3_length(dir, dist); + add_v3_v3(eci->gpos, dir); + } + } +} diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index c05749061a9..9ef3cb895ac 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -24,6 +24,7 @@ #include "MOD_gpencil_lineart.h" #include "MOD_lineart.h" +#include "BLI_kdopbvh.h" #include "BLI_linklist.h" #include "BLI_listbase.h" #include "BLI_math.h" @@ -50,6 +51,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" @@ -63,6 +65,10 @@ #include "lineart_intern.h" +//#define LINEART_WITH_BVH +#define LINEART_WITH_BVH_THREAD +#define LINEART_BVH_OWN_ISEC + static LineartBoundingArea *lineart_edge_first_bounding_area(LineartRenderBuffer *rb, LineartEdge *e); @@ -340,7 +346,7 @@ static void lineart_bounding_area_triangle_add(LineartRenderBuffer *rb, static void lineart_bounding_area_line_add(LineartRenderBuffer *rb, LineartBoundingArea *ba, - LineartEdge *e) + LineartEdge *edge) { if (ba->line_count >= ba->max_line_count) { LineartEdge **new_array = lineart_mem_acquire(&rb->render_data_pool, @@ -349,7 +355,7 @@ static void lineart_bounding_area_line_add(LineartRenderBuffer *rb, ba->max_line_count *= 2; ba->linked_lines = new_array; } - ba->linked_lines[ba->line_count] = e; + ba->linked_lines[ba->line_count] = edge; ba->line_count++; } @@ -787,7 +793,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb, int e_count = *r_e_count; int t_count = *r_t_count; int v1_obi, v2_obi; - char new_flag = 0; + uint16_t new_flag = 0; LineartEdge *new_e, *e, *old_e; LineartEdgeSegment *es; @@ -1319,6 +1325,8 @@ static void lineart_main_cull_triangles(LineartRenderBuffer *rb, bool clip_far) /* Then go through all the other triangles. */ LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->triangle_buffer_pointers) { + rb->bvh_face_count += eln->element_count; + rb->bvh_inderxer_count++; if (eln->flags & LRT_ELEMENT_IS_ADDITIONAL) { continue; } @@ -1621,31 +1629,32 @@ static void lineart_finalize_object_edge_list(LineartRenderBuffer *rb, LineartOb } static void lineart_triangle_adjacent_assign(LineartTriangle *tri, - LineartTriangleAdjacent *ta, + LineartTriangleAdjacent *rta, LineartEdge *e) { if (lineart_edge_match(tri, e, 0, 1)) { - ta->e[0] = e; + rta->e[0] = e; } else if (lineart_edge_match(tri, e, 1, 2)) { - ta->e[1] = e; + rta->e[1] = e; } else if (lineart_edge_match(tri, e, 2, 0)) { - ta->e[2] = e; + rta->e[2] = e; } } -static int lineart_edge_type_duplication_count(char eflag) +static int lineart_edge_type_duplication_count(uint8_t eflag) { int count = 0; /* See eLineartEdgeFlag for details. */ - for (int i = 0; i < 6; i++) { + for (int i = 0; i < LRT_EDGE_FLAG_TYPE_MAX_BITS; i++) { if (eflag & (1 << i)) { count++; } } return count; } + static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBuffer *rb) { BMesh *bm; @@ -1781,8 +1790,8 @@ static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBu orv[i].index = i; } /* Register a global index increment. See #lineart_triangle_share_edge() and - * #lineart_main_load_geometries() for detailed. It's okay that global_vindex might eventually - * overflow, in such large scene it's virtually impossible for two vertex of the same numeric + * #lineart_main_load_geometries() for details. It's okay that global_vindex might eventually + * overflow, in such large scene it's virtually impossible for two vertices of the same numeric * index to come close together. */ obi->global_i_offset = bm->totvert; @@ -1851,7 +1860,8 @@ static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBu /* Here we just use bm's flag for when loading actual lines, then we don't need to call * lineart_identify_feature_line() again, e->head.hflag deleted after loading anyway. Always * set the flag, so hflag stays 0 for lines that are not feature lines. */ - e->head.hflag = eflag; + e->head.hflag = 0; + e->head.hflag |= eflag; } o_la_e = lineart_mem_acquire_thread(&rb->render_data_pool, sizeof(LineartEdge) * allocate_la_e); @@ -2003,7 +2013,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; @@ -2064,11 +2074,6 @@ static void lineart_main_load_geometries( int fit = BKE_camera_sensor_fit(cam->sensor_fit, rb->w, rb->h); double asp = ((double)rb->w / (double)rb->h); - double t_start; - - if (G.debug_value == 4000) { - t_start = PIL_check_seconds_timer(); - } int bound_box_discard_count = 0; if (cam->type == CAM_PERSP) { @@ -2078,7 +2083,7 @@ static void lineart_main_load_geometries( if (fit == CAMERA_SENSOR_FIT_HOR && asp < 1) { sensor /= asp; } - double fov = focallength_to_fov(cam->lens, sensor); + 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) { @@ -2086,6 +2091,12 @@ static void lineart_main_load_geometries( lineart_matrix_ortho_44d(proj, -w, w, -w / asp, w / asp, cam->clip_start, cam->clip_end); } + 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); @@ -2107,7 +2118,8 @@ static void lineart_main_load_geometries( int thread_count = rb->thread_count; - /* 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); @@ -2196,6 +2208,23 @@ static void lineart_main_load_geometries( } } + int tri_count = 0; + int global_tri_i = 0; + LineartTriangle *tri; + LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->triangle_buffer_pointers) { + tri_count += eln->element_count; + } + + rb->triangle_lookup = lineart_mem_acquire(&rb->render_data_pool, sizeof(void *) * tri_count); + + LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->triangle_buffer_pointers) { + for (int i = 0; i < eln->element_count; i++) { + tri = (void *)(((uchar *)eln->pointer) + rb->triangle_size * i); + rb->triangle_lookup[global_tri_i] = tri; + global_tri_i++; + } + } + if (G.debug_value == 4000) { double t_elapsed = PIL_check_seconds_timer() - t_start; printf("Line art loading time: %lf\n", t_elapsed); @@ -2898,6 +2927,8 @@ static void lineart_triangle_intersect_in_bounding_area(LineartRenderBuffer *rb, continue; } + rb->debug_triangle_isec_test_count++; + /* If we do need to compute intersection, then finally do it. */ lineart_triangle_intersect(rb, tri, testing_triangle); } @@ -2968,7 +2999,7 @@ void MOD_lineart_destroy_render_data(LineartGpencilModifierData *lmd) } } -static LineartCache *lineart_init_cache() +static LineartCache *lineart_init_cache(void) { LineartCache *lc = MEM_callocN(sizeof(LineartCache), "Lineart Cache"); return lc; @@ -2986,18 +3017,20 @@ void MOD_lineart_clear_cache(struct LineartCache **lc) static LineartRenderBuffer *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"); lmd->cache = lc; lmd->render_buffer_ptr = rb; - lc->rb_edge_types = lmd->edge_types_override; + lc->rb_edge_types = LRT_EDGE_FLAG_ALL_TYPE; - if (!scene || !scene->camera || !lc) { + if (!scene || !camera || !lc) { return NULL; } - Camera *c = scene->camera->data; + Camera *c = camera->data; double clipping_offset = 0; if (lmd->calculation_flags & LRT_ALLOW_CLIPPING_BOUNDARIES) { @@ -3005,8 +3038,11 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, clipping_offset = 0.0001; } - copy_v3db_v3fl(rb->camera_pos, scene->camera->obmat[3]); - copy_m4_m4(rb->cam_obmat, scene->camera->obmat); + copy_v3db_v3fl(rb->camera_pos, camera->obmat[3]); + if (active_camera) { + copy_v3db_v3fl(rb->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; @@ -3025,9 +3061,27 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, 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; + rb->overscan = lmd->overscan; + + rb->shift_x /= (1 + rb->overscan); + rb->shift_y /= (1 + rb->overscan); + + if (lmd->light_contour_object) { + Object *lo = lmd->light_contour_object; + if (lo->type == OB_LAMP && ((Light *)lo->data)->type == LA_SUN) { + rb->light_is_sun = true; + float vec[3] = {0.0f, 0.0f, 1.0f}; + mul_mat3_m4_v3(lo->obmat, vec); + copy_v3db_v3fl(rb->light_vector, vec); + } + else { + copy_v3db_v3fl(rb->light_vector, lmd->light_contour_object->obmat[3]); + } + } + rb->crease_threshold = cos(M_PI - lmd->crease_threshold); - rb->angle_splitting_threshold = lmd->angle_splitting_threshold; rb->chaining_image_threshold = lmd->chaining_image_threshold; + rb->angle_splitting_threshold = lmd->angle_splitting_threshold; rb->fuzzy_intersections = (lmd->calculation_flags & LRT_INTERSECTION_AS_CONTOUR) != 0; rb->fuzzy_everything = (lmd->calculation_flags & LRT_EVERYTHING_AS_CONTOUR) != 0; @@ -3450,6 +3504,16 @@ static bool lineart_bounding_area_triangle_intersect(LineartRenderBuffer *fb, return false; } +static void lineart_triangle_bbox(LineartTriangle *tri, float *result) +{ + result[0] = MIN3(tri->v[0]->gloc[0], tri->v[1]->gloc[0], tri->v[2]->gloc[0]); + result[1] = MIN3(tri->v[0]->gloc[1], tri->v[1]->gloc[1], tri->v[2]->gloc[1]); + result[2] = MIN3(tri->v[0]->gloc[2], tri->v[1]->gloc[2], tri->v[2]->gloc[2]); + result[3] = MAX3(tri->v[0]->gloc[0], tri->v[1]->gloc[0], tri->v[2]->gloc[0]); + result[4] = MAX3(tri->v[0]->gloc[1], tri->v[1]->gloc[1], tri->v[2]->gloc[1]); + result[5] = MAX3(tri->v[0]->gloc[2], tri->v[1]->gloc[2], tri->v[2]->gloc[2]); +} + /** * 1) Link triangles with bounding areas for later occlusion test. * 2) Test triangles with existing(added previously) triangles for intersection lines. @@ -3475,9 +3539,11 @@ static void lineart_bounding_area_link_triangle(LineartRenderBuffer *rb, recursive_level < rb->tile_recursive_level) { lineart_bounding_area_split(rb, root_ba, recursive_level); } +#ifndef LINEART_WITH_BVH if (recursive && do_intersection && rb->use_intersections) { lineart_triangle_intersect_in_bounding_area(rb, tri, root_ba); } +#endif } else { LineartBoundingArea *ba = root_ba->child; @@ -3744,15 +3810,41 @@ static void lineart_main_add_triangles(LineartRenderBuffer *rb) int i, lim; int x1, x2, y1, y2; int r, co; + int i_indexer = 0, i_triangle = 0; + +#ifdef LINEART_WITH_BVH + if (rb->use_intersections) { + rb->bvh_main = BLI_bvhtree_new(rb->bvh_face_count, 0, 8, 6); + rb->bvh_triangle_indexer = lineart_mem_acquire( + &rb->render_data_pool, sizeof(LineartTriangleBufferIndexer) * rb->bvh_inderxer_count); + } +#endif LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->triangle_buffer_pointers) { tri = eln->pointer; lim = eln->element_count; +#ifdef LINEART_WITH_BVH + if (rb->use_intersections) { + rb->bvh_triangle_indexer[i_indexer].eln = eln; + rb->bvh_triangle_indexer[i_indexer].start_index = i_triangle; + rb->bvh_triangle_indexer[i_indexer].end_index = i_triangle + lim; + } + i_indexer++; +#endif 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; } + +#ifdef LINEART_WITH_BVH + float bounding[6]; + lineart_triangle_bbox(tri, bounding); + if (!(tri->flags & LRT_TRIANGLE_NO_INTERSECTION)) { + BLI_bvhtree_insert(rb->bvh_main, i_triangle + i, bounding, 2); + } +#endif + if (lineart_get_triangle_bounding_areas(rb, tri, &y1, &y2, &x1, &x2)) { for (co = x1; co <= x2; co++) { for (r = y1; r <= y2; r++) { @@ -3768,7 +3860,374 @@ static void lineart_main_add_triangles(LineartRenderBuffer *rb) } /* Else throw away. */ tri = (void *)(((uchar *)tri) + rb->triangle_size); } + i_triangle += lim; + } +} + +static LineartTriangle *lineart_get_triangle_from_index(LineartRenderBuffer *rb, int index) +{ + return rb->triangle_lookup[index]; + int left = 0; + int right = rb->bvh_inderxer_count - 1; + while (left <= right) { + int mid = (left + right) / 2; + LineartTriangleBufferIndexer *tbi = &rb->bvh_triangle_indexer[mid]; + if (tbi->end_index <= index) { + left = mid + 1; + } + else if (tbi->start_index > index) { + right = mid - 1; + } + else { + return (LineartTriangle *)(((uint8_t *)tbi->eln->pointer) + + (rb->triangle_size * (index - tbi->start_index))); + } + } + return NULL; +} + +static bool lineart_triangle_2v_intersection_math(LineartVert *v1, + LineartVert *v2, + LineartTriangle *tri, + LineartTriangle *t2, + float *last, + float *rv) +{ + double Lv[3]; + double Rv[3]; + double dot_l, dot_r; + LineartVert *result; + double gloc[3]; + LineartVert *l = v1, *r = v2; + + sub_v3_v3v3_db(Lv, l->gloc, t2->v[0]->gloc); + sub_v3_v3v3_db(Rv, r->gloc, t2->v[0]->gloc); + + dot_l = dot_v3v3_db(Lv, t2->gn); + dot_r = dot_v3v3_db(Rv, t2->gn); + + if (dot_l * dot_r > 0 || (!dot_l && !dot_r)) { + return false; + } + + dot_l = fabs(dot_l); + dot_r = fabs(dot_r); + + interp_v3_v3v3_db(gloc, l->gloc, r->gloc, dot_l / (dot_l + dot_r)); + + /* 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, t2->v[0]->gloc, t2->v[1]->gloc, t2->v[2]->gloc))) { + return false; + } + + copy_v3fl_v3db(rv, gloc); + + return true; +} + +static bool lineart_triangle_intersect_math(LineartTriangle *tri, + LineartTriangle *t2, + float *v1, + float *v2) +{ + float *next = v1, *last = NULL; + LineartVert *sv1, *sv2; + double cl[3]; + + 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); + + copy_v3fl_v3db(v1, share->gloc); + + if (!lineart_triangle_2v_intersection_math(sv1, sv2, tri, t2, 0, v2)) { + lineart_triangle_get_other_verts(t2, share, &sv1, &sv2); + if (lineart_triangle_2v_intersection_math(sv1, sv2, t2, tri, 0, v2)) { + return true; + } + } + } + else { + /* If not sharing any points, then we need to try all the possibilities. */ + + if (lineart_triangle_2v_intersection_math(tri->v[0], tri->v[1], tri, t2, 0, v1)) { + next = v2; + last = v1; + } + + if (lineart_triangle_2v_intersection_math(tri->v[1], tri->v[2], tri, t2, last, next)) { + if (last) { + return true; + } + next = v2; + last = v1; + } + if (lineart_triangle_2v_intersection_math(tri->v[2], tri->v[0], tri, t2, last, next)) { + if (last) { + return true; + } + next = v2; + last = v1; + } + + if (lineart_triangle_2v_intersection_math(t2->v[0], t2->v[1], t2, tri, last, next)) { + if (last) { + return true; + } + next = v2; + last = v1; + } + if (lineart_triangle_2v_intersection_math(t2->v[1], t2->v[2], t2, tri, last, next)) { + if (last) { + return true; + } + next = v2; + last = v1; + } + if (lineart_triangle_2v_intersection_math(t2->v[2], t2->v[0], t2, tri, last, next)) { + if (last) { + return true; + } + next = v2; + last = v1; + } } + return false; +} + +typedef struct LineartIsecSingle { + float v1[3], v2[3]; + LineartTriangle *tri1, *tri2; +} LineartIsecSingle; +typedef struct LineartIsecThread { + LineartIsecSingle *array; + int current; + int max; + int count_test; +} LineartIsecThread; +typedef struct LineartIsecData { + LineartRenderBuffer *rb; + LineartIsecThread *threads; + int thread_count; +} LineartIsecData; + +static void lineart_add_isec_thread(LineartIsecData *data, + const int thread, + const float *v1, + const float *v2, + LineartTriangle *tri1, + LineartTriangle *tri2) +{ + LineartIsecThread *th = &data->threads[thread]; + 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 *is = &th->array[th->current]; + copy_v3_v3(is->v1, v1); + copy_v3_v3(is->v2, v2); + is->tri1 = tri1; + is->tri2 = tri2; + th->current++; +} + +static void lineart_init_isec_thread(LineartIsecData *d, LineartRenderBuffer *rb, int thread_count) +{ + d->threads = MEM_callocN(sizeof(LineartIsecThread) * thread_count, "LineartIsecThread arr"); + d->rb = rb; + d->thread_count = thread_count; + 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; + } +} + +static void lineart_destroy_isec_thread(LineartIsecData *d) +{ + for (int i = 0; i < d->thread_count; i++) { + LineartIsecThread *it = &d->threads[i]; + MEM_freeN(it->array); + } + MEM_freeN(d->threads); +} + +static bool lineart_bvh_isec_callback(void *userdata, int index_a, int index_b, int thread) +{ + LineartIsecData *d = (LineartIsecData *)userdata; + LineartRenderBuffer *rb = d->rb; + + LineartTriangle *tri1 = lineart_get_triangle_from_index(rb, index_a); + LineartTriangle *tri2 = lineart_get_triangle_from_index(rb, index_b); + + if (!tri1 || !tri2) { + return false; + } + + if ((tri2->flags & LRT_TRIANGLE_INTERSECTION_ONLY) && + (tri1->flags & LRT_TRIANGLE_INTERSECTION_ONLY)) { + return false; + } + /* Bounding box not overlapping or triangles share edges, not potential of intersecting. */ + if (lineart_triangle_share_edge(tri1, tri2)) { + return false; + } + + d->threads[thread].count_test++; + +#ifdef LINEART_BVH_OWN_ISEC + float e1[3], e2[3]; + + if (lineart_triangle_intersect_math(tri1, tri2, e1, e2)) { + lineart_add_isec_thread(d, thread, e1, e2, tri1, tri2); + } + +#else + + float t_a0[3], t_a1[3], t_a2[3], t_b0[3], t_b1[3], t_b2[3], e1[3], e2[3]; + copy_v3fl_v3db(t_a0, tri1->v[0]->gloc); + copy_v3fl_v3db(t_a1, tri1->v[1]->gloc); + copy_v3fl_v3db(t_a2, tri1->v[2]->gloc); + copy_v3fl_v3db(t_b0, tri2->v[0]->gloc); + copy_v3fl_v3db(t_b1, tri2->v[1]->gloc); + copy_v3fl_v3db(t_b2, tri2->v[2]->gloc); + + if (isect_tri_tri_v3(t_a0, t_a1, t_a2, t_b0, t_b1, t_b2, e1, e2)) { + lineart_add_isec_thread(d, thread, e1, e2, tri1, tri2); + }; +#endif + + return true; +} + +static void lineart_create_edges_from_isec_data(LineartIsecData *d) +{ + LineartRenderBuffer *rb = d->rb; + double ZMax = rb->far_clip; + double ZMin = rb->near_clip; + + for (int i = 0; i < d->thread_count; i++) { + LineartIsecThread *th = &d->threads[i]; + if (!th->current) { + continue; + } + /* We don't care about removing duplicated vert in this method, chaning can handle that, and it + * saves us from using locks and look up tables. */ + LineartVert *v = lineart_mem_acquire(&rb->render_data_pool, + sizeof(LineartVert) * th->current * 2); + LineartEdge *e = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdge) * th->current); + LineartEdgeSegment *es = lineart_mem_acquire(&rb->render_data_pool, + sizeof(LineartEdgeSegment) * th->current); + for (int j = 0; j < th->current; j++) { + LineartVert *v1 = v; + LineartVert *v2 = v + 1; + LineartIsecSingle *is = &th->array[j]; + 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, rb->view_projection, v1->gloc); + mul_v4_m4v3_db(v2->fbcoord, rb->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] -= 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)); + e->v1 = v1; + e->v2 = v2; + e->t1 = is->tri1; + e->t2 = is->tri2; + e->flags = LRT_EDGE_FLAG_INTERSECTION; + e->intersection_mask = (is->tri1->intersection_mask | is->tri2->intersection_mask); + BLI_addtail(&e->segments, es); + + lineart_prepend_edge_direct(&rb->intersection.first, e); + + v += 2; + e++; + es++; + } + } +} + +static void lineart_do_intersections(LineartRenderBuffer *rb) +{ + BLI_bvhtree_balance(rb->bvh_main); + unsigned int overlap_tot; + +#ifdef LINEART_WITH_BVH_THREAD + LineartIsecData d; + lineart_init_isec_thread(&d, rb, rb->thread_count); + + BVHTreeOverlap *overlap = BLI_bvhtree_overlap( + rb->bvh_main, rb->bvh_main, &overlap_tot, lineart_bvh_isec_callback, &d); + + if (G.debug_value == 4000) { + int total_test_count = 0; + for (int i = 0; i < rb->thread_count; i++) { + total_test_count += d.threads[i].count_test; + } + printf("BVH has done %d pairs.\n", total_test_count); + } + + lineart_create_edges_from_isec_data(&d); + lineart_destroy_isec_thread(&d); +#else + BVHTreeOverlap *overlap = BLI_bvhtree_overlap( + rb->bvh_main, rb->bvh_main, &overlap_tot, NULL, NULL); + + for (unsigned int i = 0; i < overlap_tot; i++) { + BVHTreeOverlap *ov = &overlap[i]; + LineartTriangle *tri1 = lineart_get_triangle_from_index(rb, ov->indexA); + LineartTriangle *tri2 = lineart_get_triangle_from_index(rb, ov->indexB); + + if (!tri1 || !tri2) { + continue; + } + + if ((tri2->flags & LRT_TRIANGLE_INTERSECTION_ONLY) && + (tri1->flags & LRT_TRIANGLE_INTERSECTION_ONLY)) { + continue; + } + /* Bounding box not overlapping or triangles share edges, not potential of intersecting. */ + if (lineart_triangle_share_edge(tri1, tri2)) { + continue; + } + + /* If we do need to compute intersection, then finally do it. */ + lineart_triangle_intersect(rb, tri1, tri2); + } + +#endif + + MEM_freeN(overlap); + BLI_bvhtree_free(rb->bvh_main); } /** @@ -4037,11 +4496,13 @@ static LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this */ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, LineartGpencilModifierData *lmd, - LineartCache **cached_result) + LineartCache **cached_result, + bool enable_stroke_offset) { LineartRenderBuffer *rb; 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; @@ -4054,11 +4515,12 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, if (!scene->camera) { return false; } + use_camera = scene->camera; LineartCache *lc = lineart_init_cache(); - *cached_result = lc; + (*cached_result) = lc; - rb = lineart_create_render_buffer(scene, lmd, lc); + rb = 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. */ @@ -4066,9 +4528,7 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, /* 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->flags & LRT_GPENCIL_USE_CACHE) ? - lmd->level_end_override : - (lmd->use_multiple_levels ? lmd->level_end : lmd->level_start); + rb->max_occlusion_level = lmd->level_end_override; /* FIXME(Yiming): See definition of int #LineartRenderBuffer::_source_type for detailed. */ rb->_source_type = lmd->source_type; @@ -4078,7 +4538,7 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, /* Get view vector before loading geometries, because we detect feature lines there. */ lineart_main_get_view_vector(rb); lineart_main_load_geometries( - depsgraph, scene, scene->camera, rb, lmd->calculation_flags & LRT_ALLOW_DUPLI_OBJECTS); + depsgraph, scene, use_camera, rb, lmd->calculation_flags & LRT_ALLOW_DUPLI_OBJECTS); if (!rb->vertex_buffer_pointers.first) { /* No geometry loaded, return early. */ @@ -4108,6 +4568,14 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, * can do its job. */ lineart_main_add_triangles(rb); +#ifdef LINEART_WITH_BVH + lineart_do_intersections(rb); +#else + if (G.debug_value == 4000) { + printf("Legacy triangle isec pairs: %d\n", rb->debug_triangle_isec_test_count); + } +#endif + /* 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. */ @@ -4139,10 +4607,22 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, /* 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); + if (rb->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); + } + if (rb->angle_splitting_threshold > FLT_EPSILON) { MOD_lineart_chain_split_angle(rb, rb->angle_splitting_threshold); } + if (enable_stroke_offset && lmd->stroke_offset > FLT_EPSILON) { + MOD_lineart_chain_offset_towards_camera( + rb, lmd->stroke_offset, lmd->flags & LRT_GPENCIL_OFFSET_TOWARDS_CUSTOM_CAMERA); + } + /* Finally transfer the result list into cache. */ memcpy(&lc->chains, &rb->chains, sizeof(ListBase)); @@ -4234,7 +4714,7 @@ static void lineart_gpencil_generate(LineartCache *cache, if (ec->level > level_end || ec->level < level_start) { continue; } - if (orig_ob && orig_ob != ec->object_ref) { + if (orig_ob && orig_ob != ec->object_ref && ec->type != LRT_EDGE_FLAG_INTERSECTION) { continue; } if (orig_col && ec->object_ref) { @@ -4266,6 +4746,19 @@ static void lineart_gpencil_generate(LineartCache *cache, } } } + if (ec->type == LRT_EDGE_FLAG_INTERSECTION && + (mask_switches & LRT_GPENCIL_INTERSECTION_FILTER)) { + if (mask_switches & LRT_GPENCIL_INTERSECTION_MATCH) { + if (ec->intersection_mask != intersection_mask) { + continue; + } + } + else { + if (!(ec->intersection_mask & intersection_mask)) { + continue; + } + } + } /* Preserved: If we ever do asynchronous generation, this picked flag should be set here. */ // ec->picked = 1; @@ -4376,26 +4869,20 @@ void MOD_lineart_gpencil_generate(LineartCache *cache, Object *source_object = NULL; Collection *source_collection = NULL; - short use_types = 0; + short 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, diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h index 9d109320f09..c862c488cf5 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h @@ -38,26 +38,28 @@ struct LineartStaticMemPoolNode; struct LineartEdge; struct LineartRenderBuffer; -void *lineart_list_append_pointer_pool(ListBase *h, struct LineartStaticMemPool *smp, void *data); -void *lineart_list_append_pointer_pool_sized(ListBase *h, +void *lineart_list_append_pointer_pool(ListBase *list, + struct LineartStaticMemPool *smp, + void *data); +void *lineart_list_append_pointer_pool_sized(ListBase *list, struct LineartStaticMemPool *smp, void *data, int size); -void *lineart_list_append_pointer_pool_thread(ListBase *h, +void *lineart_list_append_pointer_pool_thread(ListBase *list, struct LineartStaticMemPool *smp, void *data); -void *lineart_list_append_pointer_pool_sized_thread(ListBase *h, +void *lineart_list_append_pointer_pool_sized_thread(ListBase *list, LineartStaticMemPool *smp, void *data, int size); -void *list_push_pointer_static(ListBase *h, struct LineartStaticMemPool *smp, void *p); -void *list_push_pointer_static_sized(ListBase *h, +void *list_push_pointer_static(ListBase *list, struct LineartStaticMemPool *smp, void *p); +void *list_push_pointer_static_sized(ListBase *list, struct LineartStaticMemPool *smp, void *p, int size); -void *lineart_list_pop_pointer_no_free(ListBase *h); -void lineart_list_remove_pointer_item_no_free(ListBase *h, LinkData *lip); +void *lineart_list_pop_pointer_no_free(ListBase *list); +void lineart_list_remove_pointer_item_no_free(ListBase *list, LinkData *lip); struct LineartStaticMemPoolNode *lineart_mem_new_static_pool(struct LineartStaticMemPool *smp, size_t size); diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c index 988c90483a6..b74499daf6b 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c @@ -118,12 +118,12 @@ static bool bake_strokes(Object *ob, } LineartCache *local_lc = *lc; if (!(*lc)) { - MOD_lineart_compute_feature_lines(dg, lmd, lc); + MOD_lineart_compute_feature_lines(dg, lmd, lc, (!(ob->dtx & OB_DRAW_IN_FRONT))); MOD_lineart_destroy_render_data(lmd); } else { if (is_first || (!(lmd->flags & LRT_GPENCIL_USE_CACHE))) { - MOD_lineart_compute_feature_lines(dg, lmd, &local_lc); + MOD_lineart_compute_feature_lines(dg, lmd, &local_lc, (!(ob->dtx & OB_DRAW_IN_FRONT))); MOD_lineart_destroy_render_data(lmd); } MOD_lineart_chain_clear_picked_flag(local_lc); diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c index 47cca0ecd61..f905a305802 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c @@ -37,71 +37,73 @@ /* Line art memory and list helper */ -void *lineart_list_append_pointer_pool(ListBase *h, LineartStaticMemPool *smp, void *data) +void *lineart_list_append_pointer_pool(ListBase *list, LineartStaticMemPool *smp, void *data) { LinkData *lip; - if (h == NULL) { + if (list == NULL) { return 0; } lip = lineart_mem_acquire(smp, sizeof(LinkData)); lip->data = data; - BLI_addtail(h, lip); + BLI_addtail(list, lip); return lip; } -void *lineart_list_append_pointer_pool_sized(ListBase *h, +void *lineart_list_append_pointer_pool_sized(ListBase *list, LineartStaticMemPool *smp, void *data, int size) { LinkData *lip; - if (h == NULL) { + if (list == NULL) { return 0; } lip = lineart_mem_acquire(smp, size); lip->data = data; - BLI_addtail(h, lip); + BLI_addtail(list, lip); return lip; } -void *lineart_list_append_pointer_pool_thread(ListBase *h, LineartStaticMemPool *smp, void *data) +void *lineart_list_append_pointer_pool_thread(ListBase *list, + LineartStaticMemPool *smp, + void *data) { LinkData *lip; - if (h == NULL) { + if (list == NULL) { return 0; } lip = lineart_mem_acquire_thread(smp, sizeof(LinkData)); lip->data = data; - BLI_addtail(h, lip); + BLI_addtail(list, lip); return lip; } -void *lineart_list_append_pointer_pool_sized_thread(ListBase *h, +void *lineart_list_append_pointer_pool_sized_thread(ListBase *list, LineartStaticMemPool *smp, void *data, int size) { LinkData *lip; - if (h == NULL) { + if (list == NULL) { return 0; } lip = lineart_mem_acquire_thread(smp, size); lip->data = data; - BLI_addtail(h, lip); + BLI_addtail(list, lip); return lip; } -void *lineart_list_pop_pointer_no_free(ListBase *h) +void *lineart_list_pop_pointer_no_free(ListBase *list) { LinkData *lip; void *rev = 0; - if (h == NULL) { + if (list == NULL) { return 0; } - lip = BLI_pophead(h); + lip = BLI_pophead(list); rev = lip ? lip->data : 0; return rev; } -void lineart_list_remove_pointer_item_no_free(ListBase *h, LinkData *lip) +void lineart_list_remove_pointer_item_no_free(ListBase *list, LinkData *lip) { - BLI_remlink(h, (void *)lip); + BLI_remlink(list, (void *)lip); } LineartStaticMemPoolNode *lineart_mem_new_static_pool(LineartStaticMemPool *smp, size_t size) diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h index b9697beeea9..1bd2a2a56c6 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h @@ -307,6 +307,8 @@ .calculation_flags = LRT_ALLOW_DUPLI_OBJECTS | LRT_ALLOW_CLIPPING_BOUNDARIES, \ .angle_splitting_threshold = DEG2RAD(60.0f), \ .chaining_image_threshold = 0.001f, \ + .stroke_offset = 0.05,\ + .overscan = 0.1f,\ } #define _DNA_DEFAULT_LengthGpencilModifierData \ diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index fac5bd3d4f4..378599015e8 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -926,13 +926,15 @@ typedef enum eLineArtGPencilModifierFlags { LRT_GPENCIL_BINARY_WEIGHTS = (1 << 2) /* Deprecated, this is removed for lack of use case. */, LRT_GPENCIL_IS_BAKED = (1 << 3), LRT_GPENCIL_USE_CACHE = (1 << 4), + LRT_GPENCIL_OFFSET_TOWARDS_CUSTOM_CAMERA = (1 << 5), } eLineArtGPencilModifierFlags; typedef enum eLineartGpencilMaskSwitches { LRT_GPENCIL_MATERIAL_MASK_ENABLE = (1 << 0), - /** When set, material mask bit comparisons are done with bit wise "AND" instead of "OR". */ + /** Set to true means using "and" instead of "or" logic on mask bits. */ LRT_GPENCIL_MATERIAL_MASK_MATCH = (1 << 1), - LRT_GPENCIL_INTERSECTION_MATCH = (1 << 2), + LRT_GPENCIL_INTERSECTION_FILTER = (1 << 2), + LRT_GPENCIL_INTERSECTION_MATCH = (1 << 3), } eLineartGpencilMaskSwitches; struct LineartCache; @@ -942,7 +944,7 @@ struct LineartCache; typedef struct LineartGpencilModifierData { GpencilModifierData modifier; - short edge_types; /* line type enable flags, bits in eLineartEdgeFlag */ + uint16_t edge_types; /* line type enable flags, bits in eLineartEdgeFlag */ char source_type; /* Object or Collection, from eLineartGpencilModifierSource */ @@ -950,6 +952,9 @@ typedef struct LineartGpencilModifierData { short level_start; short level_end; + struct Object *source_camera; + struct Object *light_contour_object; + struct Object *source_object; struct Collection *source_collection; @@ -963,6 +968,12 @@ typedef struct LineartGpencilModifierData { char source_vertex_group[64]; char vgname[64]; + /* Camera focal length is divided by (1 + overscan), before caluclation, which give a wider FOV, + * this doesn't change coordinates range internally (-1, 1), but makes the caluclated frame + * bigger than actual output. This is for the easier shifting calculation. A value of 0.5 means + * the "internal" focal length become 2/3 of the actual camera. */ + float overscan; + float opacity; short thickness; @@ -987,6 +998,9 @@ typedef struct LineartGpencilModifierData { /* eLineArtGPencilModifierFlags, modifier internal state. */ int flags; + /* Move strokes towards camera to avoid clipping while preserve depth for the viewport. */ + float stroke_offset; + /* Runtime data. */ /* Because we can potentially only compute features lines once per modifier stack (Use Cache), we diff --git a/source/blender/makesdna/DNA_lineart_types.h b/source/blender/makesdna/DNA_lineart_types.h index d7e62d04af2..58473313ebc 100644 --- a/source/blender/makesdna/DNA_lineart_types.h +++ b/source/blender/makesdna/DNA_lineart_types.h @@ -70,6 +70,7 @@ typedef enum eLineartEdgeFlag { /** Limited to 8 bits, DON'T ADD ANYMORE until improvements on the data structure. */ } eLineartEdgeFlag; -#define LRT_EDGE_FLAG_ALL_TYPE 0x3f +#define LRT_EDGE_FLAG_ALL_TYPE 0x7f +#define LRT_EDGE_FLAG_TYPE_MAX_BITS 7 #endif diff --git a/source/blender/makesdna/DNA_material_types.h b/source/blender/makesdna/DNA_material_types.h index b7354aaa724..ac18116cc84 100644 --- a/source/blender/makesdna/DNA_material_types.h +++ b/source/blender/makesdna/DNA_material_types.h @@ -163,6 +163,10 @@ typedef enum eMaterialLineArtFlags { LRT_MATERIAL_CUSTOM_OCCLUSION_EFFECTIVENESS = (1 << 1), } eMaterialLineArtFlags; +#define LRT_TRANSPARENCY_BITS_DNA 0x3f /* Bits 1<<0 to 1<<6. */ +#define LRT_TRANSPARENCY_BITS 0xfc /* Bits 1<<2 to 1<<7. */ +#define LRT_OCCLUSION_EFFECTIVE_BITS 0x03 /* Bits 1<<0 to 1<<1. */ + typedef struct Material { ID id; /** Animation data (must be immediately after id for utilities to use it). */ diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index f1c05079d9c..5b3b06fb3a8 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -2885,7 +2885,6 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) srna, "Line Art Modifier", "Generate line art strokes from selected source"); RNA_def_struct_sdna(srna, "LineartGpencilModifierData"); RNA_def_struct_ui_icon(srna, ICON_MOD_EDGESPLIT); - RNA_define_lib_overridable(true); prop = RNA_def_property(srna, "use_fuzzy_intersections", PROP_BOOLEAN, PROP_NONE); |