From 3d1cdfe2ca0446b699999d6bbbc749d23a4e758c Mon Sep 17 00:00:00 2001 From: YimingWu Date: Mon, 26 Feb 2024 15:28:15 +0100 Subject: [PATCH] GPv3: Line Art Modifier This is a migration of the current Line Art modifier to GPv3. Note: - The modifier is using the exact same DNA structure as the old one, it's re-defined in a different name. At the moment all the variable names and placement after the `ModifierData` part should stay exactly the same until we do proper versioning of the modifier data and completely remove the GPv2 support. - Vertex weight transfer feature no longer supports name initial matching ("group" used to match "group1","group2" etc). Now it will only transfer vertex weight from source vertex groups that has the exact same name as specified. Pull Request: https://projects.blender.org/blender/blender/pulls/117028 --- .../startup/bl_ui/properties_data_modifier.py | 1 + .../intern/gpencil_modifier_legacy.cc | 4 +- .../blenloader/intern/versioning_300.cc | 4 +- source/blender/editors/object/object_add.cc | 6 +- .../MOD_gpencil_legacy_lineart.h | 1 + .../intern/MOD_gpencil_legacy_lineart.cc | 22 +- .../intern/lineart/MOD_lineart.h | 51 +- .../intern/lineart/lineart_chain.cc | 56 +- .../intern/lineart/lineart_cpu.cc | 650 ++++++++++--- .../intern/lineart/lineart_intern.h | 12 +- .../intern/lineart/lineart_ops.cc | 17 +- .../intern/lineart/lineart_shadow.cc | 78 +- .../makesdna/DNA_gpencil_modifier_defaults.h | 8 +- .../makesdna/DNA_gpencil_modifier_types.h | 49 +- source/blender/makesdna/DNA_lineart_types.h | 80 +- .../blender/makesdna/DNA_modifier_defaults.h | 23 +- source/blender/makesdna/DNA_modifier_types.h | 178 ++++ source/blender/makesdna/intern/dna_defaults.c | 2 + .../intern/rna_gpencil_legacy_modifier.cc | 106 +- .../blender/makesrna/intern/rna_modifier.cc | 540 +++++++++++ source/blender/modifiers/CMakeLists.txt | 6 + source/blender/modifiers/MOD_modifiertypes.hh | 1 + .../blender/modifiers/intern/MOD_lineart.cc | 909 ++++++++++++++++++ source/blender/modifiers/intern/MOD_util.cc | 1 + 24 files changed, 2464 insertions(+), 341 deletions(-) create mode 100644 source/blender/modifiers/intern/MOD_lineart.cc diff --git a/scripts/startup/bl_ui/properties_data_modifier.py b/scripts/startup/bl_ui/properties_data_modifier.py index 43f8b71784b..f9264a4fb10 100644 --- a/scripts/startup/bl_ui/properties_data_modifier.py +++ b/scripts/startup/bl_ui/properties_data_modifier.py @@ -159,6 +159,7 @@ class OBJECT_MT_modifier_add_generate(ModifierAddMenu, Menu): self.operator_modifier_add(layout, 'GREASE_PENCIL_MIRROR') self.operator_modifier_add(layout, 'GREASE_PENCIL_MULTIPLY') self.operator_modifier_add(layout, 'GREASE_PENCIL_SUBDIV') + self.operator_modifier_add(layout, 'LINEART') layout.template_modifier_asset_menu_items(catalog_path=self.bl_label) diff --git a/source/blender/blenkernel/intern/gpencil_modifier_legacy.cc b/source/blender/blenkernel/intern/gpencil_modifier_legacy.cc index 65866b7945b..ce61fd55526 100644 --- a/source/blender/blenkernel/intern/gpencil_modifier_legacy.cc +++ b/source/blender/blenkernel/intern/gpencil_modifier_legacy.cc @@ -227,7 +227,7 @@ GpencilLineartLimitInfo BKE_gpencil_get_lineart_modifier_limits(const Object *ob LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) { if (md->type == eGpencilModifierType_Lineart) { LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md; - if (is_first || (lmd->flags & LRT_GPENCIL_USE_CACHE)) { + if (is_first || (lmd->flags & MOD_LINEART_USE_CACHE)) { info.min_level = std::min(info.min_level, lmd->level_start); info.max_level = std::max( info.max_level, (lmd->use_multiple_levels ? lmd->level_end : lmd->level_start)); @@ -248,7 +248,7 @@ void BKE_gpencil_set_lineart_modifier_limits(GpencilModifierData *md, { BLI_assert(md->type == eGpencilModifierType_Lineart); LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md; - if (is_first_lineart || lmd->flags & LRT_GPENCIL_USE_CACHE) { + if (is_first_lineart || lmd->flags & MOD_LINEART_USE_CACHE) { lmd->level_start_override = info->min_level; lmd->level_end_override = info->max_level; lmd->edge_types_override = info->edge_types; diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc index ebb6c73861e..cb8cb0b78d2 100644 --- a/source/blender/blenloader/intern/versioning_300.cc +++ b/source/blender/blenloader/intern/versioning_300.cc @@ -2658,7 +2658,7 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) { if (md->type == eGpencilModifierType_Lineart) { LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md; - lmd->flags |= LRT_GPENCIL_USE_CACHE; + lmd->flags |= MOD_LINEART_USE_CACHE; lmd->chain_smooth_tolerance = 0.2f; } } @@ -2897,7 +2897,7 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) { if (md->type == eGpencilModifierType_Lineart) { LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md; - lmd->calculation_flags |= LRT_USE_CREASE_ON_SMOOTH_SURFACES; + lmd->calculation_flags |= MOD_LINEART_USE_CREASE_ON_SMOOTH_SURFACES; } } } diff --git a/source/blender/editors/object/object_add.cc b/source/blender/editors/object/object_add.cc index a87b41f8403..e9ab0fce990 100644 --- a/source/blender/editors/object/object_add.cc +++ b/source/blender/editors/object/object_add.cc @@ -1448,16 +1448,16 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) BKE_gpencil_modifier_unique_name(&ob->greasepencil_modifiers, (GpencilModifierData *)md); if (type == GP_LRT_COLLECTION) { - md->source_type = LRT_SOURCE_COLLECTION; + md->source_type = LINEART_SOURCE_COLLECTION; md->source_collection = CTX_data_collection(C); } else if (type == GP_LRT_OBJECT) { - md->source_type = LRT_SOURCE_OBJECT; + md->source_type = LINEART_SOURCE_OBJECT; md->source_object = ob_orig; } else { /* Whole scene. */ - md->source_type = LRT_SOURCE_SCENE; + md->source_type = LINEART_SOURCE_SCENE; } /* Only created one layer and one material. */ STRNCPY(md->target_layer, ((bGPDlayer *)gpd->layers.first)->info); diff --git a/source/blender/gpencil_modifiers_legacy/MOD_gpencil_legacy_lineart.h b/source/blender/gpencil_modifiers_legacy/MOD_gpencil_legacy_lineart.h index ba54f78d916..160ac5a33c4 100644 --- a/source/blender/gpencil_modifiers_legacy/MOD_gpencil_legacy_lineart.h +++ b/source/blender/gpencil_modifiers_legacy/MOD_gpencil_legacy_lineart.h @@ -25,6 +25,7 @@ void WM_operatortypes_lineart(void); struct LineartCache; +LineartCache *MOD_lineart_init_cache(); void MOD_lineart_clear_cache(struct LineartCache **lc); #ifdef __cplusplus diff --git a/source/blender/gpencil_modifiers_legacy/intern/MOD_gpencil_legacy_lineart.cc b/source/blender/gpencil_modifiers_legacy/intern/MOD_gpencil_legacy_lineart.cc index e26ef771d86..7a07518547e 100644 --- a/source/blender/gpencil_modifiers_legacy/intern/MOD_gpencil_legacy_lineart.cc +++ b/source/blender/gpencil_modifiers_legacy/intern/MOD_gpencil_legacy_lineart.cc @@ -70,8 +70,8 @@ static void generate_strokes_actual( gpl, gpf, lmd->source_type, - lmd->source_type == LRT_SOURCE_OBJECT ? (void *)lmd->source_object : - (void *)lmd->source_collection, + lmd->source_type == LINEART_SOURCE_OBJECT ? (void *)lmd->source_object : + (void *)lmd->source_collection, lmd->level_start, lmd->use_multiple_levels ? lmd->level_end : lmd->level_start, lmd->target_material ? BKE_gpencil_object_material_index_get(ob, lmd->target_material) : 0, @@ -97,16 +97,16 @@ static bool isModifierDisabled(GpencilModifierData *md) return true; } - if (lmd->source_type == LRT_SOURCE_OBJECT && !lmd->source_object) { + if (lmd->source_type == LINEART_SOURCE_OBJECT && !lmd->source_object) { return true; } - if (lmd->source_type == LRT_SOURCE_COLLECTION && !lmd->source_collection) { + if (lmd->source_type == LINEART_SOURCE_COLLECTION && !lmd->source_collection) { return true; } /* Preventing calculation in depsgraph when baking frames. */ - if (lmd->flags & LRT_GPENCIL_IS_BAKED) { + if (lmd->flags & MOD_LINEART_IS_BAKED) { return true; } @@ -143,7 +143,7 @@ static void generate_strokes(GpencilModifierData *md, Depsgraph *depsgraph, Obje MOD_lineart_destroy_render_data(lmd); } else { - if (!(lmd->flags & LRT_GPENCIL_USE_CACHE)) { + if (!(lmd->flags & MOD_LINEART_USE_CACHE)) { MOD_lineart_compute_feature_lines(depsgraph, lmd, &local_lc, !(ob->dtx & OB_DRAW_IN_FRONT)); MOD_lineart_destroy_render_data(lmd); } @@ -153,7 +153,7 @@ static void generate_strokes(GpencilModifierData *md, Depsgraph *depsgraph, Obje generate_strokes_actual(md, depsgraph, ob, gpl, gpf); - if (!(lmd->flags & LRT_GPENCIL_USE_CACHE)) { + if (!(lmd->flags & MOD_LINEART_USE_CACHE)) { /* Clear local cache. */ if (local_lc != gpd->runtime.lineart_cache) { MOD_lineart_clear_cache(&local_lc); @@ -246,7 +246,7 @@ static void update_depsgraph(GpencilModifierData *md, * visibility computation. Line art exclusion is handled inside #add_this_collection. */ add_this_collection(ctx->scene->master_collection, ctx, mode); - if (lmd->calculation_flags & LRT_USE_CUSTOM_CAMERA && lmd->source_camera) { + if (lmd->calculation_flags & MOD_LINEART_USE_CUSTOM_CAMERA && lmd->source_camera) { DEG_add_object_relation( ctx->node, lmd->source_camera, DEG_OB_COMP_TRANSFORM, "Line Art Modifier"); DEG_add_object_relation( @@ -299,10 +299,10 @@ static void panel_draw(const bContext * /*C*/, Panel *panel) uiItemR(layout, ptr, "source_type", UI_ITEM_NONE, nullptr, ICON_NONE); - if (source_type == LRT_SOURCE_OBJECT) { + if (source_type == LINEART_SOURCE_OBJECT) { uiItemR(layout, ptr, "source_object", UI_ITEM_NONE, nullptr, ICON_OBJECT_DATA); } - else if (source_type == LRT_SOURCE_COLLECTION) { + else if (source_type == LINEART_SOURCE_COLLECTION) { uiLayout *sub = uiLayoutRow(layout, true); uiItemR(sub, ptr, "source_collection", UI_ITEM_NONE, nullptr, ICON_OUTLINER_COLLECTION); uiItemR(sub, ptr, "use_invert_collection", UI_ITEM_NONE, "", ICON_ARROW_LEFTRIGHT); @@ -369,7 +369,7 @@ static void edge_types_panel_draw(const bContext * /*C*/, Panel *panel) uiItemR(entry, ptr, "silhouette_filtering", UI_ITEM_NONE, "", ICON_NONE); const int silhouette_filtering = RNA_enum_get(ptr, "silhouette_filtering"); - if (silhouette_filtering != LRT_SILHOUETTE_FILTER_NONE) { + if (silhouette_filtering != LINEART_SILHOUETTE_FILTER_NONE) { uiItemR(entry, ptr, "use_invert_silhouette", UI_ITEM_NONE, "", ICON_ARROW_LEFTRIGHT); } diff --git a/source/blender/gpencil_modifiers_legacy/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers_legacy/intern/lineart/MOD_lineart.h index e9882aed214..6546a4bf0f0 100644 --- a/source/blender/gpencil_modifiers_legacy/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers_legacy/intern/lineart/MOD_lineart.h @@ -422,7 +422,18 @@ typedef struct LineartData { } LineartData; +/* Stores the maximum calculation range in the whole modifier stack for line art so the cache can + * cover everything that will be visible. */ +struct GreasePencilLineartLimitInfo { + int16_t edge_types; + uint8_t min_level; + uint8_t max_level; + uint8_t shadow_selection; + uint8_t silhouette_selection; +}; + typedef struct LineartCache { + GreasePencilLineartLimitInfo LimitInfo; /** Separate memory pool for chain data and shadow, this goes to the cache, so when we free the * main pool, chains and shadows will still be available. */ LineartStaticMemPool chain_data_pool; @@ -863,10 +874,17 @@ struct bGPDframe; struct bGPDlayer; struct Depsgraph; struct LineartGpencilModifierData; +struct GreasePencilLineartModifierData; struct LineartData; struct Scene; -void MOD_lineart_destroy_render_data(struct LineartGpencilModifierData *lmd); +void MOD_lineart_wrap_modifier_v3(const LineartGpencilModifierData *lmd_legacy, + GreasePencilLineartModifierData *lmd); +void MOD_lineart_unwrap_modifier_v3(LineartGpencilModifierData *lmd_legacy, + const GreasePencilLineartModifierData *lmd); + +void MOD_lineart_destroy_render_data(struct LineartGpencilModifierData *lmd_legacy); +void MOD_lineart_destroy_render_data_v3(struct GreasePencilLineartModifierData *lmd); void MOD_lineart_chain_feature_lines(LineartData *ld); void MOD_lineart_chain_split_for_fixed_occlusion(LineartData *ld); @@ -897,9 +915,13 @@ void MOD_lineart_finalize_chains(LineartData *ld); * \return True when a change is made. */ bool MOD_lineart_compute_feature_lines(struct Depsgraph *depsgraph, - struct LineartGpencilModifierData *lmd, + struct LineartGpencilModifierData *lmd_legacy, struct LineartCache **cached_result, bool enable_stroke_depth_offset); +bool MOD_lineart_compute_feature_lines_v3(struct Depsgraph *depsgraph, + struct GreasePencilLineartModifierData &lmd, + struct LineartCache **cached_result, + bool enable_stroke_depth_offset); /** * This only gets initial "biggest" tile. @@ -937,6 +959,31 @@ void MOD_lineart_gpencil_generate(LineartCache *cache, int modifier_flags, int modifier_calculation_flags); +namespace blender::bke::greasepencil { +class Drawing; +} +void MOD_lineart_gpencil_generate_v3(const LineartCache *cache, + Depsgraph *depsgraph, + blender::bke::greasepencil::Drawing &drawing, + int8_t source_type, + Object *source_object, + struct Collection *source_collection, + int level_start, + int level_end, + int mat_nr, + int16_t edge_types, + uchar mask_switches, + uchar material_mask_bits, + uchar intersection_mask, + float thickness, + float opacity, + uchar shadow_selection, + uchar silhouette_mode, + const char *source_vgname, + const char *vgname, + int modifier_flags, + int modifier_calculation_flags); + /** * Length is in image space. */ diff --git a/source/blender/gpencil_modifiers_legacy/intern/lineart/lineart_chain.cc b/source/blender/gpencil_modifiers_legacy/intern/lineart/lineart_chain.cc index 2d119fe4edb..4476c8c2766 100644 --- a/source/blender/gpencil_modifiers_legacy/intern/lineart/lineart_chain.cc +++ b/source/blender/gpencil_modifiers_legacy/intern/lineart/lineart_chain.cc @@ -32,11 +32,13 @@ static LineartEdge *lineart_line_get_connected(LineartBoundingArea *ba, for (int i = 0; i < ba->line_count; i++) { LineartEdge *n_e = ba->linked_lines[i]; - if (!(n_e->flags & LRT_EDGE_FLAG_ALL_TYPE) || (n_e->flags & LRT_EDGE_FLAG_CHAIN_PICKED)) { + if (!(n_e->flags & MOD_LINEART_EDGE_FLAG_ALL_TYPE) || + (n_e->flags & MOD_LINEART_EDGE_FLAG_CHAIN_PICKED)) + { continue; } - if (match_flag && ((n_e->flags & LRT_EDGE_FLAG_ALL_TYPE) & match_flag) == 0) { + if (match_flag && ((n_e->flags & MOD_LINEART_EDGE_FLAG_ALL_TYPE) & match_flag) == 0) { continue; } @@ -49,7 +51,7 @@ static LineartEdge *lineart_line_get_connected(LineartBoundingArea *ba, return n_e; } - if (n_e->flags & LRT_EDGE_FLAG_INTERSECTION) { + if (n_e->flags & MOD_LINEART_EDGE_FLAG_INTERSECTION) { if (n_e->object_ref != match_isec_object) { continue; } @@ -128,7 +130,7 @@ static LineartEdgeChainItem *lineart_chain_append_point(LineartData *ld, copy_v3_v3(eci->gpos, gpos); eci->index = index; copy_v3_v3(eci->normal, normal); - eci->line_type = type & LRT_EDGE_FLAG_ALL_TYPE; + eci->line_type = type & MOD_LINEART_EDGE_FLAG_ALL_TYPE; eci->occlusion = level; eci->material_mask_bits = material_mask_bits; eci->shadow_mask_bits = shadow_mask_bits; @@ -163,7 +165,7 @@ static LineartEdgeChainItem *lineart_chain_prepend_point(LineartData *ld, copy_v3_v3(eci->gpos, gpos); eci->index = index; copy_v3_v3(eci->normal, normal); - eci->line_type = type & LRT_EDGE_FLAG_ALL_TYPE; + eci->line_type = type & MOD_LINEART_EDGE_FLAG_ALL_TYPE; eci->occlusion = level; eci->material_mask_bits = material_mask_bits; eci->shadow_mask_bits = shadow_mask_bits; @@ -195,12 +197,14 @@ void MOD_lineart_chain_feature_lines(LineartData *ld) LRT_ITER_ALL_LINES_BEGIN { - if (!(e->flags & LRT_EDGE_FLAG_ALL_TYPE) || (e->flags & LRT_EDGE_FLAG_CHAIN_PICKED)) { + if (!(e->flags & MOD_LINEART_EDGE_FLAG_ALL_TYPE) || + (e->flags & MOD_LINEART_EDGE_FLAG_CHAIN_PICKED)) + { LRT_ITER_ALL_LINES_NEXT continue; } - e->flags |= LRT_EDGE_FLAG_CHAIN_PICKED; + e->flags |= MOD_LINEART_EDGE_FLAG_CHAIN_PICKED; ec = lineart_chain_create(ld); @@ -245,7 +249,7 @@ void MOD_lineart_chain_feature_lines(LineartData *ld) while (ba && (new_e = lineart_line_get_connected( ba, new_vt, &new_vt, e->flags, ec->intersection_mask, ec->object_ref))) { - new_e->flags |= LRT_EDGE_FLAG_CHAIN_PICKED; + new_e->flags |= MOD_LINEART_EDGE_FLAG_CHAIN_PICKED; if (new_e->t1 || new_e->t2) { zero_v3(N); @@ -390,7 +394,7 @@ void MOD_lineart_chain_feature_lines(LineartData *ld) while (ba && (new_e = lineart_line_get_connected( ba, new_vt, &new_vt, e->flags, ec->intersection_mask, ec->object_ref))) { - new_e->flags |= LRT_EDGE_FLAG_CHAIN_PICKED; + new_e->flags |= MOD_LINEART_EDGE_FLAG_CHAIN_PICKED; if (new_e->t1 || new_e->t2) { zero_v3(N); @@ -409,7 +413,7 @@ void MOD_lineart_chain_feature_lines(LineartData *ld) /* Fix leading vertex type. */ eci = static_cast(ec->chain.last); - eci->line_type = new_e->flags & LRT_EDGE_FLAG_ALL_TYPE; + eci->line_type = new_e->flags & MOD_LINEART_EDGE_FLAG_ALL_TYPE; if (new_vt == new_e->v1) { es = static_cast(new_e->segments.last); @@ -489,10 +493,10 @@ void MOD_lineart_chain_feature_lines(LineartData *ld) ba = MOD_lineart_get_bounding_area(ld, new_vt->fbcoord[0], new_vt->fbcoord[1]); } if (ld->conf.fuzzy_everything) { - ec->type = LRT_EDGE_FLAG_CONTOUR; + ec->type = MOD_LINEART_EDGE_FLAG_CONTOUR; } else { - ec->type = (e->flags & LRT_EDGE_FLAG_ALL_TYPE); + ec->type = (e->flags & MOD_LINEART_EDGE_FLAG_ALL_TYPE); } } LRT_ITER_ALL_LINES_END @@ -758,15 +762,15 @@ static void lineart_chain_connect(LineartData * /*ld*/, int reverse_2) { LineartEdgeChainItem *eci; - if (onto->type == LRT_EDGE_FLAG_INTERSECTION) { + if (onto->type == MOD_LINEART_EDGE_FLAG_INTERSECTION) { if (sub->object_ref) { onto->object_ref = sub->object_ref; - onto->type = LRT_EDGE_FLAG_CONTOUR; + onto->type = MOD_LINEART_EDGE_FLAG_CONTOUR; } } - else if (sub->type == LRT_EDGE_FLAG_INTERSECTION) { - if (onto->type != LRT_EDGE_FLAG_INTERSECTION) { - onto->type = LRT_EDGE_FLAG_CONTOUR; + else if (sub->type == MOD_LINEART_EDGE_FLAG_INTERSECTION) { + if (onto->type != MOD_LINEART_EDGE_FLAG_INTERSECTION) { + onto->type = MOD_LINEART_EDGE_FLAG_CONTOUR; } } if (!reverse_1) { /* L--R L-R. */ @@ -832,8 +836,8 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartData *ld, if (!ld->conf.fuzzy_everything) { if (ld->conf.fuzzy_intersections) { /* If none of those are intersection lines... */ - if (!(cre->ec->type & LRT_EDGE_FLAG_INTERSECTION) && - !(ec->type & LRT_EDGE_FLAG_INTERSECTION)) + if (!(cre->ec->type & MOD_LINEART_EDGE_FLAG_INTERSECTION) && + !(ec->type & MOD_LINEART_EDGE_FLAG_INTERSECTION)) { continue; /* We don't want to chain along different objects at the moment. */ } @@ -855,8 +859,8 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartData *ld, if (!ld->conf.fuzzy_everything) { if (cre->ec->type != ec->type) { if (ld->conf.fuzzy_intersections) { - if (!(cre->ec->type == LRT_EDGE_FLAG_INTERSECTION || - ec->type == LRT_EDGE_FLAG_INTERSECTION)) + if (!(cre->ec->type == MOD_LINEART_EDGE_FLAG_INTERSECTION || + ec->type == MOD_LINEART_EDGE_FLAG_INTERSECTION)) { continue; /* Fuzzy intersections but no intersection line found. */ } @@ -950,7 +954,7 @@ void MOD_lineart_chain_connect(LineartData *ld) BLI_addtail(&ld->chains, ec); loop_id = ec->loop_id; - if (ec->type == LRT_EDGE_FLAG_LOOSE && (!ld->conf.use_loose_edge_chain)) { + if (ec->type == MOD_LINEART_EDGE_FLAG_LOOSE && (!ld->conf.use_loose_edge_chain)) { continue; } @@ -1092,9 +1096,9 @@ void MOD_lineart_finalize_chains(LineartData *ld) { LISTBASE_FOREACH (LineartEdgeChain *, ec, &ld->chains) { if (ELEM(ec->type, - LRT_EDGE_FLAG_INTERSECTION, - LRT_EDGE_FLAG_PROJECTED_SHADOW, - LRT_EDGE_FLAG_LIGHT_CONTOUR)) + MOD_LINEART_EDGE_FLAG_INTERSECTION, + MOD_LINEART_EDGE_FLAG_PROJECTED_SHADOW, + MOD_LINEART_EDGE_FLAG_LIGHT_CONTOUR)) { continue; } @@ -1393,7 +1397,7 @@ void MOD_lineart_chain_offset_towards_camera(LineartData *ld, float dist, bool u void MOD_lineart_chain_find_silhouette_backdrop_objects(LineartData *ld) { LISTBASE_FOREACH (LineartEdgeChain *, ec, &ld->chains) { - if (ec->type == LRT_EDGE_FLAG_CONTOUR && + if (ec->type == MOD_LINEART_EDGE_FLAG_CONTOUR && ec->shadow_mask_bits & LRT_SHADOW_SILHOUETTE_ERASED_GROUP) { uint32_t target = ec->shadow_mask_bits & LRT_OBINDEX_HIGHER; diff --git a/source/blender/gpencil_modifiers_legacy/intern/lineart/lineart_cpu.cc b/source/blender/gpencil_modifiers_legacy/intern/lineart/lineart_cpu.cc index 699d0037466..d267b55a891 100644 --- a/source/blender/gpencil_modifiers_legacy/intern/lineart/lineart_cpu.cc +++ b/source/blender/gpencil_modifiers_legacy/intern/lineart/lineart_cpu.cc @@ -12,11 +12,14 @@ #include "MOD_lineart.h" #include "BLI_listbase.h" +#include "BLI_math_base.hh" #include "BLI_math_geom.h" #include "BLI_math_matrix.h" +#include "BLI_math_matrix.hh" #include "BLI_math_rotation.h" #include "BLI_math_vector.hh" #include "BLI_sort.hh" +#include "BLI_string.h" #include "BLI_task.h" #include "BLI_time.h" #include "BLI_utildefines.h" @@ -25,12 +28,14 @@ #include "BKE_attribute.hh" #include "BKE_camera.h" #include "BKE_collection.hh" +#include "BKE_curves.hh" #include "BKE_customdata.hh" #include "BKE_deform.hh" #include "BKE_global.hh" #include "BKE_gpencil_geom_legacy.h" #include "BKE_gpencil_legacy.h" #include "BKE_gpencil_modifier_legacy.h" +#include "BKE_grease_pencil.hh" #include "BKE_lib_id.hh" #include "BKE_material.h" #include "BKE_mesh.hh" @@ -45,6 +50,7 @@ #include "DNA_light_types.h" #include "DNA_material_types.h" #include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" #include "DNA_scene_types.h" #include "MEM_guardedalloc.h" @@ -52,11 +58,17 @@ #include "RE_pipeline.h" #include "intern/render_types.h" +#include "ED_grease_pencil.hh" + #include "lineart_intern.h" #include /* For `min/max`. */ +using blender::float3; using blender::int3; +using blender::MutableSpan; +using namespace blender::bke; +using blender::bke::mesh::corner_tri_get_real_edges; struct LineartIsecSingle { double v1[3], v2[3]; @@ -120,8 +132,6 @@ static void lineart_free_bounding_area_memory(LineartBoundingArea *ba, bool recu static void lineart_free_bounding_area_memories(LineartData *ld); -static LineartCache *lineart_init_cache(); - static void lineart_discard_segment(LineartData *ld, LineartEdgeSegment *es) { BLI_spin_lock(&ld->lock_cuts); @@ -311,7 +321,7 @@ void lineart_edge_cut(LineartData *ld, * flags. See LineartEdgeSegment::shadow_mask_bits for details. */ if (shadow_bits == LRT_SHADOW_MASK_ENCLOSED_SHAPE) { if (seg->shadow_mask_bits & LRT_SHADOW_MASK_ILLUMINATED || - e->flags & LRT_EDGE_FLAG_LIGHT_CONTOUR) + e->flags & MOD_LINEART_EDGE_FLAG_LIGHT_CONTOUR) { seg->shadow_mask_bits |= LRT_SHADOW_MASK_INHIBITED; } @@ -741,9 +751,9 @@ static bool lineart_edge_match(LineartTriangle *tri, LineartEdge *e, int v1, int static void lineart_discard_duplicated_edges(LineartEdge *old_e) { LineartEdge *e = old_e; - while (e->flags & LRT_EDGE_FLAG_NEXT_IS_DUPLICATION) { + while (e->flags & MOD_LINEART_EDGE_FLAG_NEXT_IS_DUPLICATION) { e++; - e->flags |= LRT_EDGE_FLAG_CHAIN_PICKED; + e->flags |= MOD_LINEART_EDGE_FLAG_CHAIN_PICKED; } } @@ -809,7 +819,7 @@ static void lineart_triangle_cull_single(LineartData *ld, 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; \ + old_e->flags = MOD_LINEART_EDGE_FLAG_CHAIN_PICKED; \ lineart_discard_duplicated_edges(old_e); \ INCREASE_EDGE \ e->v1 = (v1_link); \ @@ -832,15 +842,15 @@ static void lineart_triangle_cull_single(LineartData *ld, #define REMOVE_TRIANGLE_EDGE \ if (tri_adj->e[0]) { \ - tri_adj->e[0]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ + tri_adj->e[0]->flags = MOD_LINEART_EDGE_FLAG_CHAIN_PICKED; \ lineart_discard_duplicated_edges(tri_adj->e[0]); \ } \ if (tri_adj->e[1]) { \ - tri_adj->e[1]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ + tri_adj->e[1]->flags = MOD_LINEART_EDGE_FLAG_CHAIN_PICKED; \ lineart_discard_duplicated_edges(tri_adj->e[1]); \ } \ if (tri_adj->e[2]) { \ - tri_adj->e[2]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \ + tri_adj->e[2]->flags = MOD_LINEART_EDGE_FLAG_CHAIN_PICKED; \ lineart_discard_duplicated_edges(tri_adj->e[2]); \ } @@ -905,7 +915,7 @@ static void lineart_triangle_cull_single(LineartData *ld, /* New line connecting two new points. */ INCREASE_EDGE if (allow_boundaries) { - e->flags = LRT_EDGE_FLAG_CONTOUR; + e->flags = MOD_LINEART_EDGE_FLAG_CONTOUR; lineart_add_edge_to_array(&ld->pending_edges, e); } /* NOTE: inverting `e->v1/v2` (left/right point) doesn't matter as long as @@ -954,7 +964,7 @@ static void lineart_triangle_cull_single(LineartData *ld, INCREASE_EDGE if (allow_boundaries) { - e->flags = LRT_EDGE_FLAG_CONTOUR; + e->flags = MOD_LINEART_EDGE_FLAG_CONTOUR; lineart_add_edge_to_array(&ld->pending_edges, e); } e->v1 = &vt[0]; @@ -995,7 +1005,7 @@ static void lineart_triangle_cull_single(LineartData *ld, INCREASE_EDGE if (allow_boundaries) { - e->flags = LRT_EDGE_FLAG_CONTOUR; + e->flags = MOD_LINEART_EDGE_FLAG_CONTOUR; lineart_add_edge_to_array(&ld->pending_edges, e); } e->v1 = &vt[1]; @@ -1070,7 +1080,7 @@ static void lineart_triangle_cull_single(LineartData *ld, /* New line connects two new points. */ INCREASE_EDGE if (allow_boundaries) { - e->flags = LRT_EDGE_FLAG_CONTOUR; + e->flags = MOD_LINEART_EDGE_FLAG_CONTOUR; lineart_add_edge_to_array(&ld->pending_edges, e); } e->v1 = &vt[1]; @@ -1122,7 +1132,7 @@ static void lineart_triangle_cull_single(LineartData *ld, INCREASE_EDGE if (allow_boundaries) { - e->flags = LRT_EDGE_FLAG_CONTOUR; + e->flags = MOD_LINEART_EDGE_FLAG_CONTOUR; lineart_add_edge_to_array(&ld->pending_edges, e); } e->v1 = &vt[1]; @@ -1171,7 +1181,7 @@ static void lineart_triangle_cull_single(LineartData *ld, INCREASE_EDGE if (allow_boundaries) { - e->flags = LRT_EDGE_FLAG_CONTOUR; + e->flags = MOD_LINEART_EDGE_FLAG_CONTOUR; lineart_add_edge_to_array(&ld->pending_edges, e); } e->v1 = &vt[1]; @@ -1395,7 +1405,7 @@ void lineart_main_discard_out_of_frame_edges(LineartData *ld) e = (LineartEdge *)eln->pointer; for (int i = 0; i < eln->element_count; i++) { if (!e[i].v1 || !e[i].v2) { - e[i].flags = LRT_EDGE_FLAG_CHAIN_PICKED; + e[i].flags = MOD_LINEART_EDGE_FLAG_CHAIN_PICKED; continue; } const blender::float2 vec1(e[i].v1->fbcoord), vec2(e[i].v2->fbcoord); @@ -1407,7 +1417,7 @@ void lineart_main_discard_out_of_frame_edges(LineartData *ld) isect_seg_seg_v2(bounds[1], bounds[3], vec1, vec2) == ISECT_LINE_LINE_NONE && isect_seg_seg_v2(bounds[2], bounds[3], vec1, vec2) == ISECT_LINE_LINE_NONE) { - e[i].flags = LRT_EDGE_FLAG_CHAIN_PICKED; + e[i].flags = MOD_LINEART_EDGE_FLAG_CHAIN_PICKED; } } } @@ -1441,12 +1451,12 @@ static void lineart_mvert_transform_task(void *__restrict userdata, } 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, + MOD_LINEART_EDGE_FLAG_EDGE_MARK, + MOD_LINEART_EDGE_FLAG_CONTOUR, + MOD_LINEART_EDGE_FLAG_CREASE, + MOD_LINEART_EDGE_FLAG_MATERIAL, + MOD_LINEART_EDGE_FLAG_LOOSE, + MOD_LINEART_EDGE_FLAG_CONTOUR_SECONDARY, }; #define LRT_MESH_EDGE_TYPES_COUNT 6 @@ -1568,7 +1578,7 @@ static void lineart_identify_corner_tri_feature_edges(void *__restrict userdata, face_mark_filtered = !face_mark_filtered; } if (!face_mark_filtered) { - edge_nabr[i].flags = LRT_EDGE_FLAG_INHIBIT; + edge_nabr[i].flags = MOD_LINEART_EDGE_FLAG_INHIBIT; if (e_feat_data->ld->conf.filter_face_mark_keep_contour) { only_contour = true; } @@ -1581,7 +1591,7 @@ static void lineart_identify_corner_tri_feature_edges(void *__restrict userdata, /* Mesh boundary */ if (edge_nabr[i].e == -1) { - edge_nabr[i].flags = LRT_EDGE_FLAG_CONTOUR; + edge_nabr[i].flags = MOD_LINEART_EDGE_FLAG_CONTOUR; reduce_data->feat_edges += 1; return; } @@ -1616,7 +1626,7 @@ static void lineart_identify_corner_tri_feature_edges(void *__restrict userdata, 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; + edge_flag_result |= MOD_LINEART_EDGE_FLAG_CONTOUR; } if (ld->conf.use_back_face_culling) { @@ -1650,7 +1660,7 @@ static void lineart_identify_corner_tri_feature_edges(void *__restrict userdata, 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; + edge_flag_result |= MOD_LINEART_EDGE_FLAG_CONTOUR_SECONDARY; } } @@ -1663,7 +1673,7 @@ static void lineart_identify_corner_tri_feature_edges(void *__restrict userdata, do_crease = false; } if (do_crease && (dot_v3v3_db(tri1->gn, tri2->gn) < e_feat_data->crease_threshold)) { - edge_flag_result |= LRT_EDGE_FLAG_CREASE; + edge_flag_result |= MOD_LINEART_EDGE_FLAG_CREASE; } } @@ -1678,11 +1688,11 @@ static void lineart_identify_corner_tri_feature_edges(void *__restrict userdata, (m2->lineart.mat_occlusion == 0 && m1->lineart.mat_occlusion != 0))) { if (ld->conf.use_contour) { - edge_flag_result |= LRT_EDGE_FLAG_CONTOUR; + edge_flag_result |= MOD_LINEART_EDGE_FLAG_CONTOUR; } } if (ld->conf.use_material) { - edge_flag_result |= LRT_EDGE_FLAG_MATERIAL; + edge_flag_result |= MOD_LINEART_EDGE_FLAG_MATERIAL; } } } @@ -1692,17 +1702,16 @@ static void lineart_identify_corner_tri_feature_edges(void *__restrict userdata, } } - const blender::int3 real_edges = blender::bke::mesh::corner_tri_get_real_edges( - e_feat_data->edges, - e_feat_data->corner_verts, - e_feat_data->corner_edges, - corner_tris[i / 3]); + const int3 real_edges = corner_tri_get_real_edges(e_feat_data->edges, + e_feat_data->corner_verts, + e_feat_data->corner_edges, + corner_tris[i / 3]); if (real_edges[i % 3] >= 0) { if (ld->conf.use_crease && ld->conf.sharp_as_crease && e_feat_data->sharp_edges[real_edges[i % 3]]) { - edge_flag_result |= LRT_EDGE_FLAG_CREASE; + edge_flag_result |= MOD_LINEART_EDGE_FLAG_CREASE; } if (ld->conf.use_edge_marks && e_feat_data->use_freestyle_edge) { @@ -1710,7 +1719,7 @@ static void lineart_identify_corner_tri_feature_edges(void *__restrict userdata, int index = e_feat_data->freestyle_edge_index; fe = &((FreestyleEdge *)mesh->edge_data.layers[index].data)[real_edges[i % 3]]; if (fe->flag & FREESTYLE_EDGE_MARK) { - edge_flag_result |= LRT_EDGE_FLAG_EDGE_MARK; + edge_flag_result |= MOD_LINEART_EDGE_FLAG_EDGE_MARK; } } } @@ -1975,9 +1984,8 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, /* Triangulate. */ const Span corner_tris = mesh->corner_tris(); - const bke::AttributeAccessor attributes = mesh->attributes(); - const VArraySpan material_indices = *attributes.lookup("material_index", - bke::AttrDomain::Face); + const AttributeAccessor attributes = mesh->attributes(); + const VArraySpan material_indices = *attributes.lookup("material_index", AttrDomain::Face); /* Check if we should look for custom data tags like Freestyle edges or faces. */ bool can_find_freestyle_edge = false; @@ -2107,9 +2115,9 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, edge_feat_settings.func_reduce = feat_data_sum_reduce; const VArray sharp_edges = *attributes.lookup_or_default( - "sharp_edge", bke::AttrDomain::Edge, false); + "sharp_edge", AttrDomain::Edge, false); const VArray sharp_faces = *attributes.lookup_or_default( - "sharp_face", bke::AttrDomain::Face, false); + "sharp_face", AttrDomain::Face, false); EdgeFeatData edge_feat_data = {nullptr}; edge_feat_data.ld = la_data; @@ -2150,7 +2158,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, if (la_data->conf.use_loose) { /* Only identifying floating edges at this point because other edges has been taken care of * inside #lineart_identify_corner_tri_feature_edges function. */ - const bke::LooseEdgeCache &loose_edges = mesh->loose_edges(); + const LooseEdgeCache &loose_edges = mesh->loose_edges(); loose_data.loose_array = static_cast( MEM_malloc_arrayN(loose_edges.count, sizeof(int), __func__)); if (loose_edges.count > 0) { @@ -2252,7 +2260,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, } if (edge_added) { - edge_added->flags |= LRT_EDGE_FLAG_NEXT_IS_DUPLICATION; + edge_added->flags |= MOD_LINEART_EDGE_FLAG_NEXT_IS_DUPLICATION; } edge_added = la_edge; @@ -2272,7 +2280,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, const int2 &edge = edges[loose_data.loose_array[i]]; la_edge->v1 = &la_v_arr[edge[0]]; la_edge->v2 = &la_v_arr[edge[1]]; - la_edge->flags = LRT_EDGE_FLAG_LOOSE; + la_edge->flags = MOD_LINEART_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); @@ -2728,7 +2736,7 @@ bool lineart_edge_from_triangle(const LineartTriangle *tri, bool allow_overlapping_edges) { const LineartEdge *use_e = e; - if (e->flags & LRT_EDGE_FLAG_LIGHT_CONTOUR) { + if (e->flags & MOD_LINEART_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)) { @@ -2887,11 +2895,11 @@ static bool lineart_triangle_edge_image_space_occlusion(const LineartTriangle *t 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) && + if ((e->flags & MOD_LINEART_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))) + if (((dot_f > 0) && (e->flags & MOD_LINEART_EDGE_FLAG_SHADOW_FACING_LIGHT)) || + ((dot_f < 0) && !(e->flags & MOD_LINEART_EDGE_FLAG_SHADOW_FACING_LIGHT))) { *from = 0.0f; *to = 1.0f; @@ -3550,7 +3558,101 @@ static void lineart_destroy_render_data(LineartData *ld) lineart_mem_destroy(&ld->render_data_pool); } -void MOD_lineart_destroy_render_data(LineartGpencilModifierData *lmd) +void MOD_lineart_wrap_modifier_v3(const LineartGpencilModifierData *lmd_legacy, + GreasePencilLineartModifierData *lmd) +{ +#define LMD_WRAP(var) lmd->var = lmd_legacy->var + + LMD_WRAP(edge_types); + LMD_WRAP(source_type); + LMD_WRAP(use_multiple_levels); + LMD_WRAP(level_start); + LMD_WRAP(level_end); + LMD_WRAP(source_camera); + LMD_WRAP(light_contour_object); + LMD_WRAP(source_object); + LMD_WRAP(source_collection); + LMD_WRAP(target_material); + STRNCPY(lmd->source_vertex_group, lmd_legacy->source_vertex_group); + STRNCPY(lmd->vgname, lmd_legacy->vgname); + LMD_WRAP(overscan); + LMD_WRAP(shadow_camera_fov); + LMD_WRAP(shadow_camera_size); + LMD_WRAP(shadow_camera_near); + LMD_WRAP(shadow_camera_far); + LMD_WRAP(opacity); + LMD_WRAP(thickness); + LMD_WRAP(mask_switches); + LMD_WRAP(material_mask_bits); + LMD_WRAP(intersection_mask); + LMD_WRAP(shadow_selection); + LMD_WRAP(silhouette_selection); + LMD_WRAP(crease_threshold); + LMD_WRAP(angle_splitting_threshold); + LMD_WRAP(chain_smooth_tolerance); + LMD_WRAP(chaining_image_threshold); + LMD_WRAP(calculation_flags); + LMD_WRAP(flags); + LMD_WRAP(stroke_depth_offset); + LMD_WRAP(level_start_override); + LMD_WRAP(level_end_override); + LMD_WRAP(edge_types_override); + LMD_WRAP(shadow_selection_override); + LMD_WRAP(shadow_use_silhouette_override); + LMD_WRAP(cache); + LMD_WRAP(la_data_ptr); + +#undef LMD_WRAP +} + +void MOD_lineart_unwrap_modifier_v3(LineartGpencilModifierData *lmd_legacy, + const GreasePencilLineartModifierData *lmd) +{ +#define LMD_UNWRAP(var) lmd_legacy->var = lmd->var + + LMD_UNWRAP(edge_types); + LMD_UNWRAP(source_type); + LMD_UNWRAP(use_multiple_levels); + LMD_UNWRAP(level_start); + LMD_UNWRAP(level_end); + LMD_UNWRAP(source_camera); + LMD_UNWRAP(light_contour_object); + LMD_UNWRAP(source_object); + LMD_UNWRAP(source_collection); + LMD_UNWRAP(target_material); + STRNCPY(lmd_legacy->source_vertex_group, lmd->source_vertex_group); + STRNCPY(lmd_legacy->vgname, lmd->vgname); + LMD_UNWRAP(overscan); + LMD_UNWRAP(shadow_camera_fov); + LMD_UNWRAP(shadow_camera_size); + LMD_UNWRAP(shadow_camera_near); + LMD_UNWRAP(shadow_camera_far); + LMD_UNWRAP(opacity); + LMD_UNWRAP(thickness); + LMD_UNWRAP(mask_switches); + LMD_UNWRAP(material_mask_bits); + LMD_UNWRAP(intersection_mask); + LMD_UNWRAP(shadow_selection); + LMD_UNWRAP(silhouette_selection); + LMD_UNWRAP(crease_threshold); + LMD_UNWRAP(angle_splitting_threshold); + LMD_UNWRAP(chain_smooth_tolerance); + LMD_UNWRAP(chaining_image_threshold); + LMD_UNWRAP(calculation_flags); + LMD_UNWRAP(flags); + LMD_UNWRAP(stroke_depth_offset); + LMD_UNWRAP(level_start_override); + LMD_UNWRAP(level_end_override); + LMD_UNWRAP(edge_types_override); + LMD_UNWRAP(shadow_selection_override); + LMD_UNWRAP(shadow_use_silhouette_override); + LMD_UNWRAP(cache); + LMD_UNWRAP(la_data_ptr); + +#undef LMD_UNWRAP +} + +void MOD_lineart_destroy_render_data_v3(GreasePencilLineartModifierData *lmd) { LineartData *ld = lmd->la_data_ptr; @@ -3566,7 +3668,15 @@ void MOD_lineart_destroy_render_data(LineartGpencilModifierData *lmd) } } -static LineartCache *lineart_init_cache() +void MOD_lineart_destroy_render_data(LineartGpencilModifierData *lmd_legacy) +{ + GreasePencilLineartModifierData lmd; + MOD_lineart_wrap_modifier_v3(lmd_legacy, &lmd); + MOD_lineart_destroy_render_data_v3(&lmd); + MOD_lineart_unwrap_modifier_v3(lmd_legacy, &lmd); +} + +LineartCache *MOD_lineart_init_cache() { LineartCache *lc = static_cast( MEM_callocN(sizeof(LineartCache), "Lineart Cache")); @@ -3583,11 +3693,11 @@ void MOD_lineart_clear_cache(LineartCache **lc) (*lc) = nullptr; } -static LineartData *lineart_create_render_buffer(Scene *scene, - LineartGpencilModifierData *lmd, - Object *camera, - Object *active_camera, - LineartCache *lc) +static LineartData *lineart_create_render_buffer_v3(Scene *scene, + GreasePencilLineartModifierData *lmd, + Object *camera, + Object *active_camera, + LineartCache *lc) { LineartData *ld = static_cast( MEM_callocN(sizeof(LineartData), "Line Art render buffer")); @@ -3601,7 +3711,7 @@ static LineartData *lineart_create_render_buffer(Scene *scene, Camera *c = static_cast(camera->data); double clipping_offset = 0; - if (lmd->calculation_flags & LRT_ALLOW_CLIPPING_BOUNDARIES) { + if (lmd->calculation_flags & MOD_LINEART_ALLOW_CLIPPING_BOUNDARIES) { /* This way the clipped lines are "stably visible" by prevents depth buffer artifacts. */ clipping_offset = 0.0001; } @@ -3660,24 +3770,31 @@ static LineartData *lineart_create_render_buffer(Scene *scene, 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.fuzzy_intersections = (lmd->calculation_flags & MOD_LINEART_INTERSECTION_AS_CONTOUR) != + 0; + ld->conf.fuzzy_everything = (lmd->calculation_flags & MOD_LINEART_EVERYTHING_AS_CONTOUR) != 0; + ld->conf.allow_boundaries = (lmd->calculation_flags & MOD_LINEART_ALLOW_CLIPPING_BOUNDARIES) != + 0; + ld->conf.use_loose_as_contour = (lmd->calculation_flags & MOD_LINEART_LOOSE_AS_CONTOUR) != 0; + ld->conf.use_loose_edge_chain = (lmd->calculation_flags & MOD_LINEART_CHAIN_LOOSE_EDGES) != 0; + ld->conf.use_geometry_space_chain = (lmd->calculation_flags & + MOD_LINEART_CHAIN_GEOMETRY_SPACE) != 0; ld->conf.use_image_boundary_trimming = (lmd->calculation_flags & - LRT_USE_IMAGE_BOUNDARY_TRIMMING) != 0; + MOD_LINEART_USE_IMAGE_BOUNDARY_TRIMMING) != 0; /* See lineart_edge_from_triangle() for how this option may impact performance. */ - ld->conf.allow_overlapping_edges = (lmd->calculation_flags & LRT_ALLOW_OVERLAPPING_EDGES) != 0; + ld->conf.allow_overlapping_edges = (lmd->calculation_flags & + MOD_LINEART_ALLOW_OVERLAPPING_EDGES) != 0; - ld->conf.allow_duplicated_types = (lmd->calculation_flags & LRT_ALLOW_OVERLAP_EDGE_TYPES) != 0; + ld->conf.allow_duplicated_types = (lmd->calculation_flags & + MOD_LINEART_ALLOW_OVERLAP_EDGE_TYPES) != 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; + ld->conf.force_crease = (lmd->calculation_flags & MOD_LINEART_USE_CREASE_ON_SMOOTH_SURFACES) != + 0; + ld->conf.sharp_as_crease = (lmd->calculation_flags & MOD_LINEART_USE_CREASE_ON_SHARP_EDGES) != 0; - ld->conf.chain_preserve_details = (lmd->calculation_flags & LRT_CHAIN_PRESERVE_DETAILS) != 0; + ld->conf.chain_preserve_details = (lmd->calculation_flags & + MOD_LINEART_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. */ @@ -3686,30 +3803,32 @@ static LineartData *lineart_create_render_buffer(Scene *scene, int16_t edge_types = lmd->edge_types_override; /* 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 && + ld->conf.use_contour = (edge_types & MOD_LINEART_EDGE_FLAG_CONTOUR) != 0; + ld->conf.use_crease = (edge_types & MOD_LINEART_EDGE_FLAG_CREASE) != 0; + ld->conf.use_material = (edge_types & MOD_LINEART_EDGE_FLAG_MATERIAL) != 0; + ld->conf.use_edge_marks = (edge_types & MOD_LINEART_EDGE_FLAG_EDGE_MARK) != 0; + ld->conf.use_intersections = (edge_types & MOD_LINEART_EDGE_FLAG_INTERSECTION) != 0; + ld->conf.use_loose = (edge_types & MOD_LINEART_EDGE_FLAG_LOOSE) != 0; + ld->conf.use_light_contour = ((edge_types & MOD_LINEART_EDGE_FLAG_LIGHT_CONTOUR) != 0 && (lmd->light_contour_object != nullptr)); - ld->conf.use_shadow = ((edge_types & LRT_EDGE_FLAG_PROJECTED_SHADOW) != 0 && + ld->conf.use_shadow = ((edge_types & MOD_LINEART_EDGE_FLAG_PROJECTED_SHADOW) != 0 && (lmd->light_contour_object != nullptr)); ld->conf.shadow_selection = lmd->shadow_selection_override; ld->conf.shadow_enclose_shapes = lmd->shadow_selection_override == - LRT_SHADOW_FILTER_ILLUMINATED_ENCLOSED_SHAPES; + LINEART_SHADOW_FILTER_ILLUMINATED_ENCLOSED_SHAPES; 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.use_back_face_culling = (lmd->calculation_flags & MOD_LINEART_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_invert = (lmd->calculation_flags & + MOD_LINEART_FILTER_FACE_MARK_INVERT) != 0; + ld->conf.filter_face_mark = (lmd->calculation_flags & MOD_LINEART_FILTER_FACE_MARK) != 0; ld->conf.filter_face_mark_boundaries = (lmd->calculation_flags & - LRT_FILTER_FACE_MARK_BOUNDARIES) != 0; + MOD_LINEART_FILTER_FACE_MARK_BOUNDARIES) != 0; ld->conf.filter_face_mark_keep_contour = (lmd->calculation_flags & - LRT_FILTER_FACE_MARK_KEEP_CONTOUR) != 0; + MOD_LINEART_FILTER_FACE_MARK_KEEP_CONTOUR) != 0; ld->chain_data_pool = &lc->chain_data_pool; @@ -4686,7 +4805,7 @@ static void lineart_create_edges_from_isec_data(LineartIsecData *d) 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->flags = MOD_LINEART_EDGE_FLAG_INTERSECTION; e->intersection_mask = (is->tri1->intersection_mask | is->tri2->intersection_mask); BLI_addtail(&e->segments, es); @@ -5003,10 +5122,10 @@ LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *self, return nullptr; } -bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, - LineartGpencilModifierData *lmd, - LineartCache **cached_result, - bool enable_stroke_depth_offset) +bool MOD_lineart_compute_feature_lines_v3(Depsgraph *depsgraph, + GreasePencilLineartModifierData &lmd, + LineartCache **cached_result, + bool enable_stroke_depth_offset) { LineartData *ld; Scene *scene = DEG_get_evaluated_scene(depsgraph); @@ -5019,9 +5138,9 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, } bool use_render_camera_override = false; - if (lmd->calculation_flags & LRT_USE_CUSTOM_CAMERA) { - if (!lmd->source_camera || - (lineart_camera = DEG_get_evaluated_object(depsgraph, lmd->source_camera))->type != + if (lmd.calculation_flags & MOD_LINEART_USE_CUSTOM_CAMERA) { + if (!lmd.source_camera || + (lineart_camera = DEG_get_evaluated_object(depsgraph, lmd.source_camera))->type != OB_CAMERA) { return false; @@ -5042,11 +5161,14 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, } } - LineartCache *lc = lineart_init_cache(); + LineartCache *lc = MOD_lineart_init_cache(); *cached_result = lc; - ld = lineart_create_render_buffer( - scene, lmd, lineart_camera, use_render_camera_override ? lineart_camera : scene->camera, lc); + ld = lineart_create_render_buffer_v3(scene, + &lmd, + lineart_camera, + use_render_camera_override ? lineart_camera : scene->camera, + lc); /* Triangle thread testing data size varies depending on the thread count. * See definition of LineartTriangleThread for details. */ @@ -5055,15 +5177,15 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, LineartData *shadow_rb = nullptr; LineartElementLinkNode *shadow_veln, *shadow_eeln; ListBase *shadow_elns = ld->conf.shadow_selection ? &lc->shadow_elns : nullptr; - bool shadow_generated = lineart_main_try_generate_shadow(depsgraph, - scene, - ld, - lmd, - &lc->shadow_data_pool, - &shadow_veln, - &shadow_eeln, - shadow_elns, - &shadow_rb); + bool shadow_generated = lineart_main_try_generate_shadow_v3(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(ld); @@ -5072,7 +5194,7 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, scene, lineart_camera, ld, - lmd->calculation_flags & LRT_ALLOW_DUPLI_OBJECTS, + lmd.calculation_flags & MOD_LINEART_ALLOW_DUPLI_OBJECTS, false, shadow_elns); @@ -5161,9 +5283,9 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, MOD_lineart_chain_split_angle(ld, ld->conf.angle_splitting_threshold); } - if (enable_stroke_depth_offset && lmd->stroke_depth_offset > FLT_EPSILON) { + if (enable_stroke_depth_offset && lmd.stroke_depth_offset > FLT_EPSILON) { MOD_lineart_chain_offset_towards_camera( - ld, lmd->stroke_depth_offset, lmd->flags & LRT_GPENCIL_OFFSET_TOWARDS_CUSTOM_CAMERA); + ld, lmd.stroke_depth_offset, lmd.flags & MOD_LINEART_OFFSET_TOWARDS_CUSTOM_CAMERA); } if (ld->conf.shadow_use_silhouette) { @@ -5196,6 +5318,20 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, return true; } +bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, + LineartGpencilModifierData *lmd_legacy, + LineartCache **cached_result, + bool enable_stroke_depth_offset) +{ + bool ret = false; + GreasePencilLineartModifierData lmd; + MOD_lineart_wrap_modifier_v3(lmd_legacy, &lmd); + ret = MOD_lineart_compute_feature_lines_v3( + depsgraph, lmd, cached_result, enable_stroke_depth_offset); + MOD_lineart_unwrap_modifier_v3(lmd_legacy, &lmd); + return ret; +} + static void lineart_gpencil_generate(LineartCache *cache, Depsgraph *depsgraph, Object *gpencil_object, @@ -5244,9 +5380,9 @@ static void lineart_gpencil_generate(LineartCache *cache, /* (!orig_col && !orig_ob) means the whole scene is selected. */ int enabled_types = cache->all_enabled_edge_types; - bool invert_input = modifier_calculation_flags & LRT_GPENCIL_INVERT_SOURCE_VGROUP; - bool match_output = modifier_calculation_flags & LRT_GPENCIL_MATCH_OUTPUT_VGROUP; - bool inverse_silhouette = modifier_flags & LRT_GPENCIL_INVERT_SILHOUETTE_FILTER; + bool invert_input = modifier_calculation_flags & MOD_LINEART_INVERT_SOURCE_VGROUP; + bool match_output = modifier_calculation_flags & MOD_LINEART_MATCH_OUTPUT_VGROUP; + bool inverse_silhouette = modifier_flags & MOD_LINEART_INVERT_SILHOUETTE_FILTER; LISTBASE_FOREACH (LineartEdgeChain *, ec, &cache->chains) { @@ -5264,18 +5400,18 @@ static void lineart_gpencil_generate(LineartCache *cache, } if (orig_col && ec->object_ref) { if (BKE_collection_has_object_recursive_instanced(orig_col, (Object *)ec->object_ref)) { - if (modifier_flags & LRT_GPENCIL_INVERT_COLLECTION) { + if (modifier_flags & MOD_LINEART_INVERT_COLLECTION) { continue; } } else { - if (!(modifier_flags & LRT_GPENCIL_INVERT_COLLECTION)) { + if (!(modifier_flags & MOD_LINEART_INVERT_COLLECTION)) { continue; } } } - if (mask_switches & LRT_GPENCIL_MATERIAL_MASK_ENABLE) { - if (mask_switches & LRT_GPENCIL_MATERIAL_MASK_MATCH) { + if (mask_switches & MOD_LINEART_MATERIAL_MASK_ENABLE) { + if (mask_switches & MOD_LINEART_MATERIAL_MASK_MATCH) { if (ec->material_mask_bits != material_mask_bits) { continue; } @@ -5286,8 +5422,8 @@ static void lineart_gpencil_generate(LineartCache *cache, } } } - if (ec->type & LRT_EDGE_FLAG_INTERSECTION) { - if (mask_switches & LRT_GPENCIL_INTERSECTION_MATCH) { + if (ec->type & MOD_LINEART_EDGE_FLAG_INTERSECTION) { + if (mask_switches & MOD_LINEART_INTERSECTION_MATCH) { if (ec->intersection_mask != intersection_mask) { continue; } @@ -5301,17 +5437,17 @@ 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_ILLUMINATED && + if (shaodow_selection == LINEART_SHADOW_FILTER_ILLUMINATED && !(ec->shadow_mask_bits & LRT_SHADOW_MASK_ILLUMINATED)) { continue; } - if (shaodow_selection == LRT_SHADOW_FILTER_SHADED && + if (shaodow_selection == LINEART_SHADOW_FILTER_SHADED && !(ec->shadow_mask_bits & LRT_SHADOW_MASK_SHADED)) { continue; } - if (shaodow_selection == LRT_SHADOW_FILTER_ILLUMINATED_ENCLOSED_SHAPES) { + if (shaodow_selection == LINEART_SHADOW_FILTER_ILLUMINATED_ENCLOSED_SHAPES) { uint32_t test_bits = ec->shadow_mask_bits & LRT_SHADOW_TEST_SHAPE_BITS; if ((test_bits != LRT_SHADOW_MASK_ILLUMINATED) && (test_bits != (LRT_SHADOW_MASK_SHADED | LRT_SHADOW_MASK_ILLUMINATED_SHAPE))) @@ -5321,7 +5457,7 @@ static void lineart_gpencil_generate(LineartCache *cache, } } } - if (silhouette_mode && (ec->type & (LRT_EDGE_FLAG_CONTOUR))) { + if (silhouette_mode && (ec->type & (MOD_LINEART_EDGE_FLAG_CONTOUR))) { bool is_silhouette = false; if (orig_col) { if (!ec->silhouette_backdrop) { @@ -5338,7 +5474,7 @@ static void lineart_gpencil_generate(LineartCache *cache, } } - if ((silhouette_mode == LRT_SILHOUETTE_FILTER_INDIVIDUAL || orig_ob) && + if ((silhouette_mode == LINEART_SILHOUETTE_FILTER_INDIVIDUAL || orig_ob) && ec->silhouette_backdrop != ec->object_ref) { is_silhouette = true; @@ -5427,6 +5563,11 @@ static void lineart_gpencil_generate(LineartCache *cache, } } +typedef struct LineartChainWriteInfo { + LineartEdgeChain *chain; + int point_count; +} LineartChainWriteInfo; + void MOD_lineart_gpencil_generate(LineartCache *cache, Depsgraph *depsgraph, Object *ob, @@ -5458,13 +5599,13 @@ void MOD_lineart_gpencil_generate(LineartCache *cache, Object *source_object = nullptr; Collection *source_collection = nullptr; int16_t use_types = edge_types; - if (source_type == LRT_SOURCE_OBJECT) { + if (source_type == LINEART_SOURCE_OBJECT) { if (!source_reference) { return; } source_object = (Object *)source_reference; } - else if (source_type == LRT_SOURCE_COLLECTION) { + else if (source_type == LINEART_SOURCE_COLLECTION) { if (!source_reference) { return; } @@ -5497,3 +5638,268 @@ void MOD_lineart_gpencil_generate(LineartCache *cache, modifier_flags, modifier_calculation_flags); } + +void MOD_lineart_gpencil_generate_v3(const LineartCache *cache, + Depsgraph *depsgraph, + blender::bke::greasepencil::Drawing &drawing, + const int8_t source_type, + Object *source_object, + Collection *source_collection, + const int level_start, + const int level_end, + const int mat_nr, + const int16_t edge_types, + const uchar mask_switches, + const uchar material_mask_bits, + const uchar intersection_mask, + const float thickness, + const float opacity, + const uchar shadow_selection, + const uchar silhouette_mode, + const char *source_vgname, + const char *vgname, + const int modifier_flags, + const int modifier_calculation_flags) +{ + if (G.debug_value == 4000) { + printf("Line Art v3: Generating...\n"); + } + + if (cache == nullptr) { + if (G.debug_value == 4000) { + printf("nullptr Lineart cache!\n"); + } + return; + } + + Object *orig_ob = nullptr; + Collection *orig_col = nullptr; + + if (source_type == LINEART_SOURCE_OBJECT) { + if (!source_object) { + return; + } + orig_ob = source_object->id.orig_id ? (Object *)source_object->id.orig_id : source_object; + orig_col = nullptr; + } + else if (source_type == LINEART_SOURCE_COLLECTION) { + if (!source_collection) { + return; + } + orig_col = source_collection->id.orig_id ? (Collection *)source_collection->id.orig_id : + source_collection; + orig_ob = nullptr; + } + /* Otherwise the whole scene is selected. */ + + int enabled_types = cache->all_enabled_edge_types; + + bool invert_input = modifier_calculation_flags & MOD_LINEART_INVERT_SOURCE_VGROUP; + + bool inverse_silhouette = modifier_flags & MOD_LINEART_INVERT_SILHOUETTE_FILTER; + + blender::Vector writer; + writer.reserve(128); + int total_point_count = 0; + int stroke_count = 0; + LISTBASE_FOREACH (LineartEdgeChain *, ec, &cache->chains) { + + if (ec->picked) { + continue; + } + if (!(ec->type & (edge_types & enabled_types))) { + continue; + } + if (ec->level > level_end || ec->level < level_start) { + continue; + } + if (orig_ob && orig_ob != ec->object_ref) { + continue; + } + if (orig_col && ec->object_ref) { + if (BKE_collection_has_object_recursive_instanced(orig_col, (Object *)ec->object_ref)) { + if (modifier_flags & MOD_LINEART_INVERT_COLLECTION) { + continue; + } + } + else { + if (!(modifier_flags & MOD_LINEART_INVERT_COLLECTION)) { + continue; + } + } + } + if (mask_switches & MOD_LINEART_MATERIAL_MASK_ENABLE) { + if (mask_switches & MOD_LINEART_MATERIAL_MASK_MATCH) { + if (ec->material_mask_bits != material_mask_bits) { + continue; + } + } + else { + if (!(ec->material_mask_bits & material_mask_bits)) { + continue; + } + } + } + if (ec->type & MOD_LINEART_EDGE_FLAG_INTERSECTION) { + if (mask_switches & MOD_LINEART_INTERSECTION_MATCH) { + if (ec->intersection_mask != intersection_mask) { + continue; + } + } + else { + if ((intersection_mask) && !(ec->intersection_mask & intersection_mask)) { + continue; + } + } + } + if (shadow_selection) { + if (ec->shadow_mask_bits != LRT_SHADOW_MASK_UNDEFINED) { + /* TODO(@Yiming): Give a behavior option for how to display undefined shadow info. */ + if (shadow_selection == LINEART_SHADOW_FILTER_ILLUMINATED && + !(ec->shadow_mask_bits & LRT_SHADOW_MASK_ILLUMINATED)) + { + continue; + } + if (shadow_selection == LINEART_SHADOW_FILTER_SHADED && + !(ec->shadow_mask_bits & LRT_SHADOW_MASK_SHADED)) + { + continue; + } + if (shadow_selection == LINEART_SHADOW_FILTER_ILLUMINATED_ENCLOSED_SHAPES) { + uint32_t test_bits = ec->shadow_mask_bits & LRT_SHADOW_TEST_SHAPE_BITS; + if ((test_bits != LRT_SHADOW_MASK_ILLUMINATED) && + (test_bits != (LRT_SHADOW_MASK_SHADED | LRT_SHADOW_MASK_ILLUMINATED_SHAPE))) + { + continue; + } + } + } + } + if (silhouette_mode && (ec->type & (MOD_LINEART_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 == LINEART_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; + } + + total_point_count += count; + writer.append({ec, count}); + + stroke_count++; + } + + if (!total_point_count || !stroke_count) { + return; + } + + blender::bke::CurvesGeometry new_curves(total_point_count, stroke_count); + new_curves.fill_curve_types(CURVE_TYPE_POLY); + + MutableAttributeAccessor attributes = new_curves.attributes_for_write(); + MutableSpan point_positions = new_curves.positions_for_write(); + + SpanAttributeWriter point_radii = attributes.lookup_or_add_for_write_only_span( + "radius", AttrDomain::Point); + + SpanAttributeWriter point_opacities = attributes.lookup_or_add_for_write_span( + "opacity", AttrDomain::Point); + + SpanAttributeWriter stroke_materials = attributes.lookup_or_add_for_write_span( + "material_index", AttrDomain::Curve); + + MutableSpan offsets = new_curves.offsets_for_write(); + + SpanAttributeWriter vgroup_weights; + if (vgname) { + vgroup_weights = attributes.lookup_or_add_for_write_span(vgname, AttrDomain::Point); + } + + int up_to_point = 0; + for (int chain_i : writer.index_range()) { + LineartChainWriteInfo &cwi = writer[chain_i]; + + MDeformVert *src_dvert = nullptr; + int src_deform_group = -1; + Mesh *src_mesh = nullptr; + if (source_vgname && vgroup_weights) { + Object *eval_ob = DEG_get_evaluated_object(depsgraph, cwi.chain->object_ref); + if (eval_ob && eval_ob->type == OB_MESH) { + src_mesh = BKE_object_get_evaluated_mesh(eval_ob); + src_dvert = src_mesh->deform_verts_for_write().data(); + src_deform_group = BKE_id_defgroup_name_index(&src_mesh->id, source_vgname); + } + } + + int i; + LISTBASE_FOREACH_INDEX (LineartEdgeChainItem *, eci, &cwi.chain->chain, i) { + int point_i = i + up_to_point; + float *point = (float *)&point_positions[point_i]; + copy_v3_v3(point, eci->gpos); + point_radii.span[point_i] = thickness / 2.0f; + point_opacities.span[point_i] = opacity; + + if (src_deform_group >= 0) { + int vindex; + vindex = eci->index; + if (vindex >= src_mesh->verts_num) { + break; + } + MDeformWeight *mdw = BKE_defvert_ensure_index(&src_dvert[vindex], src_deform_group); + + vgroup_weights.span[point_i] = invert_input ? (1 - mdw->weight) : mdw->weight; + } + } + offsets[chain_i] = up_to_point; + stroke_materials.span[chain_i] = max_ii(mat_nr, 0); + up_to_point += cwi.point_count; + } + offsets[writer.index_range().last() + 1] = up_to_point; + + SpanAttributeWriter stroke_cyclic = attributes.lookup_or_add_for_write_span( + "cyclic", AttrDomain::Curve); + stroke_cyclic.span.fill(false); + stroke_cyclic.finish(); + + point_radii.finish(); + point_opacities.finish(); + stroke_materials.finish(); + + drawing.strokes_for_write() = std::move(new_curves); + drawing.tag_topology_changed(); + + if (G.debug_value == 4000) { + printf("LRT: Generated %d strokes.\n", stroke_count); + } +} diff --git a/source/blender/gpencil_modifiers_legacy/intern/lineart/lineart_intern.h b/source/blender/gpencil_modifiers_legacy/intern/lineart/lineart_intern.h index 169306da858..e2d616faf2e 100644 --- a/source/blender/gpencil_modifiers_legacy/intern/lineart/lineart_intern.h +++ b/source/blender/gpencil_modifiers_legacy/intern/lineart/lineart_intern.h @@ -25,6 +25,7 @@ struct LineartEdge; struct LineartData; struct LineartStaticMemPool; struct LineartStaticMemPoolNode; +struct GreasePencilLineartModifierData; void *lineart_list_append_pointer_pool(ListBase *h, struct LineartStaticMemPool *smp, void *data); void *lineart_list_append_pointer_pool_sized(ListBase *h, @@ -159,12 +160,21 @@ void lineart_main_add_triangles(struct LineartData *ld); bool lineart_main_try_generate_shadow(struct Depsgraph *depsgraph, struct Scene *scene, struct LineartData *original_ld, - struct LineartGpencilModifierData *lmd, + struct LineartGpencilModifierData *lmd_legacy, struct LineartStaticMemPool *shadow_data_pool, struct LineartElementLinkNode **r_veln, struct LineartElementLinkNode **r_eeln, struct ListBase *r_calculated_edges_eln_list, struct LineartData **r_shadow_ld_if_reproject); +bool lineart_main_try_generate_shadow_v3(struct Depsgraph *depsgraph, + struct Scene *scene, + struct LineartData *original_ld, + struct GreasePencilLineartModifierData *lmd, + struct LineartStaticMemPool *shadow_data_pool, + struct LineartElementLinkNode **r_veln, + struct LineartElementLinkNode **r_eeln, + struct ListBase *r_calculated_edges_eln_list, + struct LineartData **r_shadow_ld_if_reproject); /** * Does the 3rd stage reprojection, will not re-load objects because #shadow_ld is not deleted. * Only re-projects view camera edges and check visibility in light camera, then we can determine diff --git a/source/blender/gpencil_modifiers_legacy/intern/lineart/lineart_ops.cc b/source/blender/gpencil_modifiers_legacy/intern/lineart/lineart_ops.cc index 2084450ee66..5752a085a17 100644 --- a/source/blender/gpencil_modifiers_legacy/intern/lineart/lineart_ops.cc +++ b/source/blender/gpencil_modifiers_legacy/intern/lineart/lineart_ops.cc @@ -27,6 +27,7 @@ #include "DNA_gpencil_legacy_types.h" #include "DNA_gpencil_modifier_types.h" +#include "DNA_modifier_types.h" #include "DNA_scene_types.h" #include "MOD_gpencil_legacy_lineart.h" @@ -44,9 +45,9 @@ static bool lineart_mod_is_disabled(GpencilModifierData *md) /* Toggle on and off the baked flag as we are only interested in if something else is disabling * it. We can assume that the guard function has already toggled this on for all modifiers that * are sent here. */ - lmd->flags &= (~LRT_GPENCIL_IS_BAKED); + lmd->flags &= (~MOD_LINEART_IS_BAKED); bool disabled = info->is_disabled(md, false); - lmd->flags |= LRT_GPENCIL_IS_BAKED; + lmd->flags |= MOD_LINEART_IS_BAKED; return disabled; } @@ -107,7 +108,7 @@ static bool bake_strokes(Object *ob, MOD_lineart_destroy_render_data(lmd); } else { - if (is_first || !(lmd->flags & LRT_GPENCIL_USE_CACHE)) { + if (is_first || !(lmd->flags & MOD_LINEART_USE_CACHE)) { MOD_lineart_compute_feature_lines(dg, lmd, &local_lc, !(ob->dtx & OB_DRAW_IN_FRONT)); MOD_lineart_destroy_render_data(lmd); } @@ -122,8 +123,8 @@ static bool bake_strokes(Object *ob, gpl, gpf, lmd->source_type, - lmd->source_type == LRT_SOURCE_OBJECT ? (void *)lmd->source_object : - (void *)lmd->source_collection, + lmd->source_type == LINEART_SOURCE_OBJECT ? (void *)lmd->source_object : + (void *)lmd->source_collection, lmd->level_start, lmd->use_multiple_levels ? lmd->level_end : lmd->level_start, lmd->target_material ? BKE_gpencil_object_material_index_get(ob, lmd->target_material) : 0, @@ -140,7 +141,7 @@ static bool bake_strokes(Object *ob, lmd->flags, lmd->calculation_flags); - if (!(lmd->flags & LRT_GPENCIL_USE_CACHE)) { + if (!(lmd->flags & MOD_LINEART_USE_CACHE)) { /* Clear local cache. */ if (!is_first) { MOD_lineart_clear_cache(&local_lc); @@ -213,7 +214,7 @@ static void lineart_gpencil_guard_modifiers(LineartBakeJob *bj) LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) { if (md->type == eGpencilModifierType_Lineart) { LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md; - lmd->flags |= LRT_GPENCIL_IS_BAKED; + lmd->flags |= MOD_LINEART_IS_BAKED; } } } @@ -402,7 +403,7 @@ static void lineart_gpencil_clear_strokes_exec_common(Object *ob) md->mode |= eGpencilModifierMode_Realtime | eGpencilModifierMode_Render; - lmd->flags &= (~LRT_GPENCIL_IS_BAKED); + lmd->flags &= (~MOD_LINEART_IS_BAKED); } DEG_id_tag_update((ID *)ob->data, ID_RECALC_GEOMETRY); } diff --git a/source/blender/gpencil_modifiers_legacy/intern/lineart/lineart_shadow.cc b/source/blender/gpencil_modifiers_legacy/intern/lineart/lineart_shadow.cc index bf782810f68..43516d18efe 100644 --- a/source/blender/gpencil_modifiers_legacy/intern/lineart/lineart_shadow.cc +++ b/source/blender/gpencil_modifiers_legacy/intern/lineart/lineart_shadow.cc @@ -21,6 +21,7 @@ #include "BLI_time.h" #include "DNA_light_types.h" +#include "DNA_modifier_types.h" #include "MEM_guardedalloc.h" @@ -51,7 +52,7 @@ LineartEdge *lineart_find_matching_edge(LineartElementLinkNode *shadow_eln, static bool lineart_contour_viewed_from_dark_side(LineartData *ld, LineartEdge *e) { - if (!(e->flags & (LRT_EDGE_FLAG_CONTOUR | LRT_EDGE_FLAG_CONTOUR_SECONDARY))) { + if (!(e->flags & (MOD_LINEART_EDGE_FLAG_CONTOUR | MOD_LINEART_EDGE_FLAG_CONTOUR_SECONDARY))) { return false; } double view_vector[3]; @@ -307,9 +308,9 @@ static void lineart_shadow_create_shadow_edge_array(LineartData *ld, /* Count and allocate at once to save time. */ int segment_count = 0; - uint16_t accept_types = (LRT_EDGE_FLAG_CONTOUR | LRT_EDGE_FLAG_LOOSE); + uint16_t accept_types = (MOD_LINEART_EDGE_FLAG_CONTOUR | MOD_LINEART_EDGE_FLAG_LOOSE); if (do_light_contour) { - accept_types |= LRT_EDGE_FLAG_LIGHT_CONTOUR; + accept_types |= MOD_LINEART_EDGE_FLAG_LIGHT_CONTOUR; } LRT_ITER_ALL_LINES_BEGIN { @@ -319,11 +320,11 @@ static void lineart_shadow_create_shadow_edge_array(LineartData *ld, if (!(e->flags & accept_types)) { continue; } - if (e->flags == LRT_EDGE_FLAG_LIGHT_CONTOUR) { + if (e->flags == MOD_LINEART_EDGE_FLAG_LIGHT_CONTOUR) { /* Check if the light contour also doubles as a view contour. */ LineartEdge *orig_e = (LineartEdge *)e->t1; if (!orig_e->t2) { - e->flags |= LRT_EDGE_FLAG_CONTOUR; + e->flags |= MOD_LINEART_EDGE_FLAG_CONTOUR; } else { double vv[3]; @@ -343,10 +344,10 @@ static void lineart_shadow_create_shadow_edge_array(LineartData *ld, if ((result = dot_1 * dot_2) <= 0 && (dot_1 + dot_2)) { /* If this edge is both a light contour and a view contour, mark it for the convenience * of generating it in the next iteration. */ - e->flags |= LRT_EDGE_FLAG_CONTOUR; + e->flags |= MOD_LINEART_EDGE_FLAG_CONTOUR; } } - if (!(e->flags & LRT_EDGE_FLAG_CONTOUR)) { + if (!(e->flags & MOD_LINEART_EDGE_FLAG_CONTOUR)) { continue; } } @@ -368,7 +369,7 @@ static void lineart_shadow_create_shadow_edge_array(LineartData *ld, int i = 0; LRT_ITER_ALL_LINES_BEGIN { - if (!(e->flags & (LRT_EDGE_FLAG_CONTOUR | LRT_EDGE_FLAG_LOOSE))) { + if (!(e->flags & (MOD_LINEART_EDGE_FLAG_CONTOUR | MOD_LINEART_EDGE_FLAG_LOOSE))) { continue; } LISTBASE_FOREACH (LineartEdgeSegment *, es, &e->segments) { @@ -403,12 +404,12 @@ static void lineart_shadow_create_shadow_edge_array(LineartData *ld, BLI_addtail(&sedge[i].shadow_segments, &sseg[i * 2]); BLI_addtail(&sedge[i].shadow_segments, &sseg[i * 2 + 1]); - if (e->flags & LRT_EDGE_FLAG_LIGHT_CONTOUR) { + if (e->flags & MOD_LINEART_EDGE_FLAG_LIGHT_CONTOUR) { sedge[i].e_ref = (LineartEdge *)e->t1; sedge[i].e_ref_light_contour = e; /* Restore original edge flag for edges "who is both view and light contour" so we still * have correct edge flags. */ - e->flags &= (~LRT_EDGE_FLAG_CONTOUR); + e->flags &= (~MOD_LINEART_EDGE_FLAG_CONTOUR); } else { sedge[i].e_ref = e; @@ -1011,9 +1012,10 @@ static bool lineart_shadow_cast_generate_edges(LineartData *ld, sedge->e_ref); e->target_reference = sseg->target_reference; e->edge_identifier = sedge->e_ref->edge_identifier; - e->flags = (LRT_EDGE_FLAG_PROJECTED_SHADOW | - ((sseg->flag & LRT_SHADOW_FACING_LIGHT) ? LRT_EDGE_FLAG_SHADOW_FACING_LIGHT : - 0)); + e->flags = (MOD_LINEART_EDGE_FLAG_PROJECTED_SHADOW | + ((sseg->flag & LRT_SHADOW_FACING_LIGHT) ? + MOD_LINEART_EDGE_FLAG_SHADOW_FACING_LIGHT : + 0)); ei++; } if (do_original_edges) { @@ -1032,7 +1034,7 @@ static bool lineart_shadow_cast_generate_edges(LineartData *ld, e->v1 = v1; e->v2 = v2; e->t1 = e->t2 = (LineartTriangle *)sedge->e_ref; - e->flags = LRT_EDGE_FLAG_LIGHT_CONTOUR; + e->flags = MOD_LINEART_EDGE_FLAG_LIGHT_CONTOUR; if (lineart_contour_viewed_from_dark_side(ld, sedge->e_ref)) { lineart_edge_cut(ld, e, 0.0f, 1.0f, 0, 0, LRT_SHADOW_MASK_SHADED); } @@ -1128,15 +1130,15 @@ static void lineart_shadow_register_enclosed_shapes(LineartData *ld, LineartData } } -bool lineart_main_try_generate_shadow(Depsgraph *depsgraph, - Scene *scene, - LineartData *original_ld, - LineartGpencilModifierData *lmd, - LineartStaticMemPool *shadow_data_pool, - LineartElementLinkNode **r_veln, - LineartElementLinkNode **r_eeln, - ListBase *r_calculated_edges_eln_list, - LineartData **r_shadow_ld_if_reproject) +bool lineart_main_try_generate_shadow_v3(Depsgraph *depsgraph, + Scene *scene, + LineartData *original_ld, + GreasePencilLineartModifierData *lmd, + LineartStaticMemPool *shadow_data_pool, + LineartElementLinkNode **r_veln, + LineartElementLinkNode **r_eeln, + ListBase *r_calculated_edges_eln_list, + LineartData **r_shadow_ld_if_reproject) { if ((!original_ld->conf.use_shadow && !original_ld->conf.use_light_contour && !original_ld->conf.shadow_selection) || @@ -1237,7 +1239,7 @@ bool lineart_main_try_generate_shadow(Depsgraph *depsgraph, lineart_main_get_view_vector(ld); lineart_main_load_geometries( - depsgraph, scene, nullptr, ld, lmd->flags & LRT_ALLOW_DUPLI_OBJECTS, true, nullptr); + depsgraph, scene, nullptr, ld, lmd->flags & MOD_LINEART_ALLOW_DUPLI_OBJECTS, true, nullptr); if (!ld->geom.vertex_buffer_pointers.first) { /* No geometry loaded, return early. */ @@ -1286,6 +1288,32 @@ bool lineart_main_try_generate_shadow(Depsgraph *depsgraph, return any_generated; } +bool lineart_main_try_generate_shadow(Depsgraph *depsgraph, + Scene *scene, + LineartData *original_ld, + LineartGpencilModifierData *lmd_legacy, + LineartStaticMemPool *shadow_data_pool, + LineartElementLinkNode **r_veln, + LineartElementLinkNode **r_eeln, + ListBase *r_calculated_edges_eln_list, + LineartData **r_shadow_ld_if_reproject) +{ + bool ret = false; + GreasePencilLineartModifierData lmd; + MOD_lineart_wrap_modifier_v3(lmd_legacy, &lmd); + ret = lineart_main_try_generate_shadow_v3(depsgraph, + scene, + original_ld, + &lmd, + shadow_data_pool, + r_veln, + r_eeln, + r_calculated_edges_eln_list, + r_shadow_ld_if_reproject); + MOD_lineart_unwrap_modifier_v3(lmd_legacy, &lmd); + return ret; +} + struct LineartShadowFinalizeData { LineartData *ld; LineartVert *v; @@ -1310,7 +1338,7 @@ static void lineart_shadow_finalize_shadow_edges_task(void *__restrict userdata, LineartData *ld = data->ld; LineartEdge *e = data->e; - if (e[i].flags & LRT_EDGE_FLAG_LIGHT_CONTOUR) { + if (e[i].flags & MOD_LINEART_EDGE_FLAG_LIGHT_CONTOUR) { LineartElementLinkNode *eln = lineart_find_matching_eln( &ld->geom.vertex_buffer_pointers, e[i].edge_identifier & LRT_OBINDEX_HIGHER); if (eln) { diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h index ac8d34be803..cc1b6590320 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h @@ -329,13 +329,13 @@ #define _DNA_DEFAULT_LineartGpencilModifierData \ { \ - .edge_types = LRT_EDGE_FLAG_INIT_TYPE, \ + .edge_types = MOD_LINEART_EDGE_FLAG_INIT_TYPE, \ .thickness = 25, \ .opacity = 1.0f, \ .crease_threshold = DEG2RAD(140.0f), \ - .calculation_flags = LRT_ALLOW_DUPLI_OBJECTS | LRT_ALLOW_CLIPPING_BOUNDARIES | \ - LRT_USE_CREASE_ON_SHARP_EDGES | LRT_FILTER_FACE_MARK_KEEP_CONTOUR | \ - LRT_GPENCIL_MATCH_OUTPUT_VGROUP, \ + .calculation_flags = MOD_LINEART_ALLOW_DUPLI_OBJECTS | MOD_LINEART_ALLOW_CLIPPING_BOUNDARIES | \ + MOD_LINEART_USE_CREASE_ON_SHARP_EDGES | MOD_LINEART_FILTER_FACE_MARK_KEEP_CONTOUR | \ + MOD_LINEART_MATCH_OUTPUT_VGROUP, \ /* Do not split by default, this is for better chaining quality. */ \ .angle_splitting_threshold = 0.0f, \ .chaining_image_threshold = 0.001f, \ diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index 3907688fd55..822f1a27b90 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -1073,51 +1073,6 @@ typedef enum eGpencilModifierSpace { GP_SPACE_WORLD = 1, } eGpencilModifierSpace; -typedef enum eLineartGpencilModifierSource { - LRT_SOURCE_COLLECTION = 0, - LRT_SOURCE_OBJECT = 1, - LRT_SOURCE_SCENE = 2, -} eLineartGpencilModifierSource; - -typedef enum eLineartGpencilModifierShadowFilter { - /* These options need to be ordered in this way because those latter options requires line art to - * run a few extra stages. Having those values set up this way will allow - * #BKE_gpencil_get_lineart_modifier_limits() to find out maximum stages needed in multiple - * cached line art modifiers. */ - LRT_SHADOW_FILTER_NONE = 0, - LRT_SHADOW_FILTER_ILLUMINATED = 1, - LRT_SHADOW_FILTER_SHADED = 2, - LRT_SHADOW_FILTER_ILLUMINATED_ENCLOSED_SHAPES = 3, -} eLineartGpencilModifierShadowFilter; - -typedef enum eLineartGpencilModifierSilhouetteFilter { - LRT_SILHOUETTE_FILTER_NONE = 0, - LRT_SILHOUETTE_FILTER_GROUP = (1 << 0), - LRT_SILHOUETTE_FILTER_INDIVIDUAL = (1 << 1), -} eLineartGpencilModifierSilhouetteFilter; - -/* This enum is for modifier internal state only. */ -typedef enum eLineArtGPencilModifierFlags { - /* These two moved to #eLineartMainFlags to keep consistent with flag variable purpose. */ - /* LRT_GPENCIL_INVERT_SOURCE_VGROUP = (1 << 0), */ - /* LRT_GPENCIL_MATCH_OUTPUT_VGROUP = (1 << 1), */ - 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), - LRT_GPENCIL_INVERT_COLLECTION = (1 << 6), - LRT_GPENCIL_INVERT_SILHOUETTE_FILTER = (1 << 7), -} 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". */ - LRT_GPENCIL_MATERIAL_MASK_MATCH = (1 << 1), - LRT_GPENCIL_INTERSECTION_MATCH = (1 << 2), -} eLineartGpencilMaskSwitches; - -struct LineartCache; - struct LineartCache; typedef struct LineartGpencilModifierData { @@ -1125,7 +1080,7 @@ typedef struct LineartGpencilModifierData { uint16_t edge_types; /* line type enable flags, bits in eLineartEdgeFlag */ - /** Object or Collection, from #eLineartGpencilModifierSource. */ + /** Object or Collection, from #GreasePencilLineartModifierSource. */ char source_type; char use_multiple_levels; @@ -1164,7 +1119,7 @@ typedef struct LineartGpencilModifierData { float opacity; short thickness; - unsigned char mask_switches; /* #eLineartGpencilMaskSwitches */ + unsigned char mask_switches; /* #GreasePencilLineartMaskSwitches */ unsigned char material_mask_bits; unsigned char intersection_mask; diff --git a/source/blender/makesdna/DNA_lineart_types.h b/source/blender/makesdna/DNA_lineart_types.h index 8f815aa7b26..8c84333455b 100644 --- a/source/blender/makesdna/DNA_lineart_types.h +++ b/source/blender/makesdna/DNA_lineart_types.h @@ -17,61 +17,61 @@ /** These flags are used for 1 time calculation, not stroke selection afterwards. */ typedef enum eLineartMainFlags { - LRT_INTERSECTION_AS_CONTOUR = (1 << 0), - LRT_EVERYTHING_AS_CONTOUR = (1 << 1), - LRT_ALLOW_DUPLI_OBJECTS = (1 << 2), - LRT_ALLOW_OVERLAPPING_EDGES = (1 << 3), - LRT_ALLOW_CLIPPING_BOUNDARIES = (1 << 4), + MOD_LINEART_INTERSECTION_AS_CONTOUR = (1 << 0), + MOD_LINEART_EVERYTHING_AS_CONTOUR = (1 << 1), + MOD_LINEART_ALLOW_DUPLI_OBJECTS = (1 << 2), + MOD_LINEART_ALLOW_OVERLAPPING_EDGES = (1 << 3), + MOD_LINEART_ALLOW_CLIPPING_BOUNDARIES = (1 << 4), /* LRT_REMOVE_DOUBLES = (1 << 5), Deprecated */ - LRT_LOOSE_AS_CONTOUR = (1 << 6), - LRT_GPENCIL_INVERT_SOURCE_VGROUP = (1 << 7), - LRT_GPENCIL_MATCH_OUTPUT_VGROUP = (1 << 8), - LRT_FILTER_FACE_MARK = (1 << 9), - LRT_FILTER_FACE_MARK_INVERT = (1 << 10), - LRT_FILTER_FACE_MARK_BOUNDARIES = (1 << 11), - LRT_CHAIN_LOOSE_EDGES = (1 << 12), - LRT_CHAIN_GEOMETRY_SPACE = (1 << 13), - LRT_ALLOW_OVERLAP_EDGE_TYPES = (1 << 14), - LRT_USE_CREASE_ON_SMOOTH_SURFACES = (1 << 15), - LRT_USE_CREASE_ON_SHARP_EDGES = (1 << 16), - LRT_USE_CUSTOM_CAMERA = (1 << 17), - LRT_FILTER_FACE_MARK_KEEP_CONTOUR = (1 << 18), - LRT_USE_BACK_FACE_CULLING = (1 << 19), - LRT_USE_IMAGE_BOUNDARY_TRIMMING = (1 << 20), - LRT_CHAIN_PRESERVE_DETAILS = (1 << 22), - LRT_SHADOW_USE_SILHOUETTE = (1 << 24), + MOD_LINEART_LOOSE_AS_CONTOUR = (1 << 6), + MOD_LINEART_INVERT_SOURCE_VGROUP = (1 << 7), + MOD_LINEART_MATCH_OUTPUT_VGROUP = (1 << 8), + MOD_LINEART_FILTER_FACE_MARK = (1 << 9), + MOD_LINEART_FILTER_FACE_MARK_INVERT = (1 << 10), + MOD_LINEART_FILTER_FACE_MARK_BOUNDARIES = (1 << 11), + MOD_LINEART_CHAIN_LOOSE_EDGES = (1 << 12), + MOD_LINEART_CHAIN_GEOMETRY_SPACE = (1 << 13), + MOD_LINEART_ALLOW_OVERLAP_EDGE_TYPES = (1 << 14), + MOD_LINEART_USE_CREASE_ON_SMOOTH_SURFACES = (1 << 15), + MOD_LINEART_USE_CREASE_ON_SHARP_EDGES = (1 << 16), + MOD_LINEART_USE_CUSTOM_CAMERA = (1 << 17), + MOD_LINEART_FILTER_FACE_MARK_KEEP_CONTOUR = (1 << 18), + MOD_LINEART_USE_BACK_FACE_CULLING = (1 << 19), + MOD_LINEART_USE_IMAGE_BOUNDARY_TRIMMING = (1 << 20), + MOD_LINEART_CHAIN_PRESERVE_DETAILS = (1 << 22), + MOD_LINEART_SHADOW_USE_SILHOUETTE = (1 << 24), } eLineartMainFlags; typedef enum eLineartEdgeFlag { - LRT_EDGE_FLAG_EDGE_MARK = (1 << 0), - LRT_EDGE_FLAG_CONTOUR = (1 << 1), - LRT_EDGE_FLAG_CREASE = (1 << 2), - LRT_EDGE_FLAG_MATERIAL = (1 << 3), - LRT_EDGE_FLAG_INTERSECTION = (1 << 4), - LRT_EDGE_FLAG_LOOSE = (1 << 5), - LRT_EDGE_FLAG_LIGHT_CONTOUR = (1 << 6), - /* LRT_EDGE_FLAG_FOR_FUTURE = (1 << 7), */ + MOD_LINEART_EDGE_FLAG_EDGE_MARK = (1 << 0), + MOD_LINEART_EDGE_FLAG_CONTOUR = (1 << 1), + MOD_LINEART_EDGE_FLAG_CREASE = (1 << 2), + MOD_LINEART_EDGE_FLAG_MATERIAL = (1 << 3), + MOD_LINEART_EDGE_FLAG_INTERSECTION = (1 << 4), + MOD_LINEART_EDGE_FLAG_LOOSE = (1 << 5), + MOD_LINEART_EDGE_FLAG_LIGHT_CONTOUR = (1 << 6), + /* MOD_LINEART_EDGE_FLAG_FOR_FUTURE = (1 << 7), */ /** * It's a legacy limit of 8 bits for feature lines that come from original mesh edges. It should * not be needed in current object loading scheme, but might still be relevant if we are to * implement edit-mesh loading, so don't exceed 8 bits just yet. */ - LRT_EDGE_FLAG_PROJECTED_SHADOW = (1 << 8), + MOD_LINEART_EDGE_FLAG_PROJECTED_SHADOW = (1 << 8), /* To determine an edge to be occluded from the front or back face it's lying on. */ - LRT_EDGE_FLAG_SHADOW_FACING_LIGHT = (1 << 9), + MOD_LINEART_EDGE_FLAG_SHADOW_FACING_LIGHT = (1 << 9), /** Also used as discarded line mark. */ - LRT_EDGE_FLAG_CHAIN_PICKED = (1 << 10), - LRT_EDGE_FLAG_CLIPPED = (1 << 11), + MOD_LINEART_EDGE_FLAG_CHAIN_PICKED = (1 << 10), + MOD_LINEART_EDGE_FLAG_CLIPPED = (1 << 11), /** Used to specify contour from viewing camera when computing shadows. */ - LRT_EDGE_FLAG_CONTOUR_SECONDARY = (1 << 12), + MOD_LINEART_EDGE_FLAG_CONTOUR_SECONDARY = (1 << 12), /** Limited to 16 bits for the entire thing. */ /** For object loading code to use only. */ - LRT_EDGE_FLAG_INHIBIT = (1 << 14), + MOD_LINEART_EDGE_FLAG_INHIBIT = (1 << 14), /** For discarding duplicated edge types in culling stage. */ - LRT_EDGE_FLAG_NEXT_IS_DUPLICATION = (1 << 15), + MOD_LINEART_EDGE_FLAG_NEXT_IS_DUPLICATION = (1 << 15), } eLineartEdgeFlag; -#define LRT_EDGE_FLAG_ALL_TYPE 0x01ff -#define LRT_EDGE_FLAG_INIT_TYPE 0x37 /* Without material & light contour */ -#define LRT_EDGE_FLAG_TYPE_MAX_BITS 7 +#define MOD_LINEART_EDGE_FLAG_ALL_TYPE 0x01ff +#define MOD_LINEART_EDGE_FLAG_INIT_TYPE 0x37 /* Without material & light contour */ +#define MOD_LINEART_EDGE_FLAG_TYPE_MAX_BITS 7 diff --git a/source/blender/makesdna/DNA_modifier_defaults.h b/source/blender/makesdna/DNA_modifier_defaults.h index ce27dbb4c77..28ddbd883c2 100644 --- a/source/blender/makesdna/DNA_modifier_defaults.h +++ b/source/blender/makesdna/DNA_modifier_defaults.h @@ -800,7 +800,7 @@ .flag = MOD_WIREFRAME_REPLACE | MOD_WIREFRAME_OFS_EVEN, \ .mat_ofs = 0, \ } - + #define _DNA_DEFAULT_GreasePencilOpacityModifierData \ { \ .color_mode = MOD_GREASE_PENCIL_COLOR_BOTH, \ @@ -962,4 +962,25 @@ .falloff = 0.0f, \ .force = 0.5f, \ } + +#define _DNA_DEFAULT_GreasePencilLineartModifierData \ + { \ + .edge_types = MOD_LINEART_EDGE_FLAG_INIT_TYPE, \ + .thickness = 25, \ + .opacity = 1.0f, \ + .crease_threshold = DEG2RAD(140.0f), \ + .calculation_flags = MOD_LINEART_ALLOW_DUPLI_OBJECTS | MOD_LINEART_ALLOW_CLIPPING_BOUNDARIES | \ + MOD_LINEART_USE_CREASE_ON_SHARP_EDGES | MOD_LINEART_FILTER_FACE_MARK_KEEP_CONTOUR | \ + MOD_LINEART_MATCH_OUTPUT_VGROUP, \ + /* Do not split by default, this is for better chaining quality. */ \ + .angle_splitting_threshold = 0.0f, \ + .chaining_image_threshold = 0.001f, \ + .stroke_depth_offset = 0.05,\ + .chain_smooth_tolerance = 0.0f,\ + .overscan = 0.1f,\ + .shadow_camera_near = 0.1f, \ + .shadow_camera_far = 200.0f, \ + .shadow_camera_size = 200.0f, \ + } + /* clang-format off */ diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index fec5bb4f221..bb62d78a211 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -112,6 +112,7 @@ typedef enum ModifierType { eModifierType_GreasePencilArray = 75, eModifierType_GreasePencilWeightProximity = 76, eModifierType_GreasePencilHook = 77, + eModifierType_GreasePencilLineart = 78, NUM_MODIFIER_TYPES, } ModifierType; @@ -2986,3 +2987,180 @@ typedef enum GreasePencilHookFalloff { MOD_GREASE_PENCIL_HOOK_Falloff_Sphere = 7, MOD_GREASE_PENCIL_HOOK_Falloff_InvSquare = 8, } GreasePencilHookFalloff; + +/* This enum is for modifier internal state only. */ +typedef enum eGreasePencilLineartFlags { + /* These two moved to #eLineartMainFlags to keep consistent with flag variable purpose. */ + /* LINEART_GPENCIL_INVERT_SOURCE_VGROUP = (1 << 0), */ + /* LINEART_GPENCIL_MATCH_OUTPUT_VGROUP = (1 << 1), */ + LINEART_GPENCIL_BINARY_WEIGHTS = (1 + << 2) /* Deprecated, this is removed for lack of use case. */, + LINEART_GPENCIL_IS_BAKED = (1 << 3), + LINEART_GPENCIL_USE_CACHE = (1 << 4), + LINEART_GPENCIL_OFFSET_TOWARDS_CUSTOM_CAMERA = (1 << 5), + LINEART_GPENCIL_INVERT_COLLECTION = (1 << 6), + LINEART_GPENCIL_INVERT_SILHOUETTE_FILTER = (1 << 7), +} eGreasePencilLineartFlags; + +typedef enum GreasePencilLineartModifierSource { + LINEART_SOURCE_COLLECTION = 0, + LINEART_SOURCE_OBJECT = 1, + LINEART_SOURCE_SCENE = 2, +} GreasePencilLineartModifierSource; + +typedef enum GreasePencilLineartModifierShadowFilter { + /* These options need to be ordered in this way because those latter options requires line art to + * run a few extra stages. Having those values set up this way will allow + * #BKE_gpencil_get_lineart_modifier_limits() to find out maximum stages needed in multiple + * cached line art modifiers. */ + LINEART_SHADOW_FILTER_NONE = 0, + LINEART_SHADOW_FILTER_ILLUMINATED = 1, + LINEART_SHADOW_FILTER_SHADED = 2, + LINEART_SHADOW_FILTER_ILLUMINATED_ENCLOSED_SHAPES = 3, +} GreasePencilLineartModifierShadowFilter; + +/* This enum is for modifier internal state only. */ +typedef enum eLineArtGPencilModifierFlags { + /* These two moved to #eLineartMainFlags to keep consistent with flag variable purpose. */ + /* MOD_LINEART_INVERT_SOURCE_VGROUP = (1 << 0), */ + /* MOD_LINEART_MATCH_OUTPUT_VGROUP = (1 << 1), */ + MOD_LINEART_BINARY_WEIGHTS = (1 << 2) /* Deprecated, this is removed for lack of use case. */, + MOD_LINEART_IS_BAKED = (1 << 3), + MOD_LINEART_USE_CACHE = (1 << 4), + MOD_LINEART_OFFSET_TOWARDS_CUSTOM_CAMERA = (1 << 5), + MOD_LINEART_INVERT_COLLECTION = (1 << 6), + MOD_LINEART_INVERT_SILHOUETTE_FILTER = (1 << 7), +} eLineArtGPencilModifierFlags; + +typedef enum GreasePencilLineartMaskSwitches { + MOD_LINEART_MATERIAL_MASK_ENABLE = (1 << 0), + /** When set, material mask bit comparisons are done with bit wise "AND" instead of "OR". */ + MOD_LINEART_MATERIAL_MASK_MATCH = (1 << 1), + MOD_LINEART_INTERSECTION_MATCH = (1 << 2), +} GreasePencilLineartMaskSwitches; + +typedef enum eGreasePencilLineartMaskSwitches { + LINEART_GPENCIL_MATERIAL_MASK_ENABLE = (1 << 0), + /** When set, material mask bit comparisons are done with bit wise "AND" instead of "OR". */ + LINEART_GPENCIL_MATERIAL_MASK_MATCH = (1 << 1), + LINEART_GPENCIL_INTERSECTION_MATCH = (1 << 2), +} eGreasePencilLineartMaskSwitches; + +typedef enum eGreasePencilLineartSilhouetteFilter { + LINEART_SILHOUETTE_FILTER_NONE = 0, + LINEART_SILHOUETTE_FILTER_GROUP = (1 << 0), + LINEART_SILHOUETTE_FILTER_INDIVIDUAL = (1 << 1), +} eGreasePencilLineartSilhouetteFilter; + +struct LineartCache; + +typedef struct GreasePencilLineartModifierData { + ModifierData modifier; + + /* [Important] Note on legacy material/layer selection variables: + * + * Now uses the layer/material variables in the `influence` + * field above, thus old layer/material fields are obsolete. + * + * Do not change any of the data below since the layout of these + * data is currently shared with the old line art modifier. + * See `MOD_lineart_wrap_modifier_v3` for how it works. */ + + uint16_t edge_types; /* line type enable flags, bits in eLineartEdgeFlag */ + + /** Object or Collection, from #eGreasePencilLineartSource. */ + char source_type; + + char use_multiple_levels; + short level_start; + short level_end; + + struct Object *source_camera; + struct Object *light_contour_object; + + struct Object *source_object; + struct Collection *source_collection; + + /* These are redundant in GPv3, see above for explainations. */ + struct Material *target_material; + char target_layer[64]; + + /** + * These two variables are to pass on vertex group information from mesh to strokes. + * `vgname` specifies which vertex groups our strokes from source_vertex_group will go to. + * + * These are redundant in GPv3, see above for explainations. + */ + char source_vertex_group[64]; + char vgname[64]; + + /* Camera focal length is divided by (1 + over-scan), before calculation, which give a wider FOV, + * this doesn't change coordinates range internally (-1, 1), but makes the calculated 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; + + /* Values for point light and directional (sun) light. */ + /* For point light, fov always gonna be 120 deg horizontal, with 3 "cameras" covering 360 deg. */ + float shadow_camera_fov; + float shadow_camera_size; + float shadow_camera_near; + float shadow_camera_far; + + float opacity; + short thickness; + + unsigned char mask_switches; /* #eGreasePencilLineartMaskSwitches */ + unsigned char material_mask_bits; + unsigned char intersection_mask; + + unsigned char shadow_selection; + unsigned char silhouette_selection; + char _pad[1]; + + /** `0..1` range for cosine angle */ + float crease_threshold; + + /** `0..PI` angle, for splitting strokes at sharp points. */ + float angle_splitting_threshold; + + /** Strength for smoothing jagged chains. */ + float chain_smooth_tolerance; + + /* CPU mode */ + float chaining_image_threshold; + + /* eLineartMainFlags, for one time calculation. */ + int calculation_flags; + + /* #eGreasePencilLineartFlags, modifier internal state. */ + int flags; + + /* Move strokes towards camera to avoid clipping while preserve depth for the viewport. */ + float stroke_depth_offset; + + /* Runtime data. */ + + /* Because we can potentially only compute features lines once per modifier stack (Use Cache), we + * need to have these override values to ensure that we have the data we need is computed and + * stored in the cache. */ + char level_start_override; + char level_end_override; + short edge_types_override; + char shadow_selection_override; + char shadow_use_silhouette_override; + + char _pad2[6]; + + /* Shared cache will only be on the first line art modifier in the stack, and will exist until + * the end of modifier stack evaluation. If the object has line art modifiers, this variable is + * then initialized in #grease_pencil_evaluate_modifiers(). */ + struct LineartCache *shared_cache; + + /* Cache for single execution of line art, when LINEART_GPENCIL_USE_CACHE is enabled, this is a + * reference to first_lineart->shared_cache, otherwise it holds its own cache. */ + struct LineartCache *cache; + + /* Keep a pointer to the render buffer so we can call destroy from #ModifierData. */ + struct LineartData *la_data_ptr; +} GreasePencilLineartModifierData; diff --git a/source/blender/makesdna/intern/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c index 8c389c7e341..d3c4bf4f0b3 100644 --- a/source/blender/makesdna/intern/dna_defaults.c +++ b/source/blender/makesdna/intern/dna_defaults.c @@ -298,6 +298,7 @@ SDNA_DEFAULT_DECL_STRUCT(WireframeModifierData); SDNA_DEFAULT_DECL_STRUCT(GreasePencilSubdivModifierData); SDNA_DEFAULT_DECL_STRUCT(GreasePencilNoiseModifierData); SDNA_DEFAULT_DECL_STRUCT(GreasePencilLengthModifierData); +SDNA_DEFAULT_DECL_STRUCT(GreasePencilLineartModifierData); /* Grease Pencil 3.0 modifiers. */ SDNA_DEFAULT_DECL_STRUCT(GreasePencilSmoothModifierData); @@ -564,6 +565,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = { SDNA_DEFAULT_DECL(GreasePencilLengthModifierData), SDNA_DEFAULT_DECL(GreasePencilWeightAngleModifierData), SDNA_DEFAULT_DECL(GreasePencilHookModifierData), + SDNA_DEFAULT_DECL(GreasePencilLineartModifierData), /* Grease Pencil 3.0 defaults. */ SDNA_DEFAULT_DECL(GreasePencilSmoothModifierData), diff --git a/source/blender/makesrna/intern/rna_gpencil_legacy_modifier.cc b/source/blender/makesrna/intern/rna_gpencil_legacy_modifier.cc index c2cd70f73ff..4e1efc80c0e 100644 --- a/source/blender/makesrna/intern/rna_gpencil_legacy_modifier.cc +++ b/source/blender/makesrna/intern/rna_gpencil_legacy_modifier.cc @@ -3560,29 +3560,29 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) PropertyRNA *prop; static const EnumPropertyItem modifier_lineart_source_type[] = { - {LRT_SOURCE_COLLECTION, "COLLECTION", 0, "Collection", ""}, - {LRT_SOURCE_OBJECT, "OBJECT", 0, "Object", ""}, - {LRT_SOURCE_SCENE, "SCENE", 0, "Scene", ""}, + {LINEART_SOURCE_COLLECTION, "COLLECTION", 0, "Collection", ""}, + {LINEART_SOURCE_OBJECT, "OBJECT", 0, "Object", ""}, + {LINEART_SOURCE_SCENE, "SCENE", 0, "Scene", ""}, {0, nullptr, 0, nullptr, nullptr}, }; static const EnumPropertyItem modifier_lineart_shadow_region_filtering[] = { - {LRT_SHADOW_FILTER_NONE, + {LINEART_SHADOW_FILTER_NONE, "NONE", 0, "None", "Not filtering any lines based on illumination region"}, - {LRT_SHADOW_FILTER_ILLUMINATED, + {LINEART_SHADOW_FILTER_ILLUMINATED, "ILLUMINATED", 0, "Illuminated", "Only selecting lines from illuminated regions"}, - {LRT_SHADOW_FILTER_SHADED, + {LINEART_SHADOW_FILTER_SHADED, "SHADED", 0, "Shaded", "Only selecting lines from shaded regions"}, - {LRT_SHADOW_FILTER_ILLUMINATED_ENCLOSED_SHAPES, + {LINEART_SHADOW_FILTER_ILLUMINATED_ENCLOSED_SHAPES, "ILLUMINATED_ENCLOSED", 0, "Illuminated (Enclosed Shapes)", @@ -3592,9 +3592,9 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) }; static const EnumPropertyItem modifier_lineart_silhouette_filtering[] = { - {LRT_SILHOUETTE_FILTER_NONE, "NONE", 0, "Contour", ""}, - {LRT_SILHOUETTE_FILTER_GROUP, "GROUP", 0, "Silhouette", ""}, - {LRT_SILHOUETTE_FILTER_INDIVIDUAL, "INDIVIDUAL", 0, "Individual Silhouette", ""}, + {LINEART_SILHOUETTE_FILTER_NONE, "NONE", 0, "Contour", ""}, + {LINEART_SILHOUETTE_FILTER_GROUP, "GROUP", 0, "Silhouette", ""}, + {LINEART_SILHOUETTE_FILTER_INDIVIDUAL, "INDIVIDUAL", 0, "Individual Silhouette", ""}, {0, nullptr, 0, nullptr, nullptr}, }; @@ -3607,13 +3607,14 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) RNA_define_lib_overridable(true); prop = RNA_def_property(srna, "use_custom_camera", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "calculation_flags", LRT_USE_CUSTOM_CAMERA); + RNA_def_property_boolean_sdna(prop, nullptr, "calculation_flags", MOD_LINEART_USE_CUSTOM_CAMERA); RNA_def_property_ui_text( prop, "Use Custom Camera", "Use custom camera instead of the active camera"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update"); prop = RNA_def_property(srna, "use_fuzzy_intersections", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "calculation_flags", LRT_INTERSECTION_AS_CONTOUR); + RNA_def_property_boolean_sdna( + prop, nullptr, "calculation_flags", MOD_LINEART_INTERSECTION_AS_CONTOUR); RNA_def_property_ui_text(prop, "Intersection With Contour", "Treat intersection and contour lines as if they were the same type so " @@ -3621,20 +3622,23 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_fuzzy_all", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "calculation_flags", LRT_EVERYTHING_AS_CONTOUR); + RNA_def_property_boolean_sdna( + prop, nullptr, "calculation_flags", MOD_LINEART_EVERYTHING_AS_CONTOUR); RNA_def_property_ui_text( prop, "All Lines", "Treat all lines as the same line type so they can be chained together"); RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_object_instances", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "calculation_flags", LRT_ALLOW_DUPLI_OBJECTS); + RNA_def_property_boolean_sdna( + prop, nullptr, "calculation_flags", MOD_LINEART_ALLOW_DUPLI_OBJECTS); RNA_def_property_ui_text(prop, "Instanced Objects", "Allow particle objects and face/vertex instances to show in line art"); RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_edge_overlap", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "calculation_flags", LRT_ALLOW_OVERLAPPING_EDGES); + RNA_def_property_boolean_sdna( + prop, nullptr, "calculation_flags", MOD_LINEART_ALLOW_OVERLAPPING_EDGES); RNA_def_property_ui_text( prop, "Handle Overlapping Edges", @@ -3642,7 +3646,8 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_clip_plane_boundaries", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "calculation_flags", LRT_ALLOW_CLIPPING_BOUNDARIES); + RNA_def_property_boolean_sdna( + prop, nullptr, "calculation_flags", MOD_LINEART_ALLOW_CLIPPING_BOUNDARIES); RNA_def_property_ui_text(prop, "Clipping Boundaries", "Allow lines generated by the near/far clipping plane to be shown"); @@ -3676,43 +3681,44 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_loose_as_contour", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "calculation_flags", LRT_LOOSE_AS_CONTOUR); + RNA_def_property_boolean_sdna(prop, nullptr, "calculation_flags", MOD_LINEART_LOOSE_AS_CONTOUR); RNA_def_property_ui_text(prop, "Loose As Contour", "Loose edges will have contour type"); RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "invert_source_vertex_group", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna( - prop, nullptr, "calculation_flags", LRT_GPENCIL_INVERT_SOURCE_VGROUP); + prop, nullptr, "calculation_flags", MOD_LINEART_INVERT_SOURCE_VGROUP); RNA_def_property_ui_text(prop, "Invert Vertex Group", "Invert source vertex group values"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_output_vertex_group_match_by_name", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna( - prop, nullptr, "calculation_flags", LRT_GPENCIL_MATCH_OUTPUT_VGROUP); + prop, nullptr, "calculation_flags", MOD_LINEART_MATCH_OUTPUT_VGROUP); RNA_def_property_ui_text(prop, "Match Output", "Match output vertex group based on name"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_face_mark", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "calculation_flags", LRT_FILTER_FACE_MARK); + RNA_def_property_boolean_sdna(prop, nullptr, "calculation_flags", MOD_LINEART_FILTER_FACE_MARK); RNA_def_property_ui_text( prop, "Filter Face Marks", "Filter feature lines using freestyle face marks"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_face_mark_invert", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "calculation_flags", LRT_FILTER_FACE_MARK_INVERT); + RNA_def_property_boolean_sdna( + prop, nullptr, "calculation_flags", MOD_LINEART_FILTER_FACE_MARK_INVERT); RNA_def_property_ui_text(prop, "Invert", "Invert face mark filtering"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_face_mark_boundaries", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna( - prop, nullptr, "calculation_flags", LRT_FILTER_FACE_MARK_BOUNDARIES); + prop, nullptr, "calculation_flags", MOD_LINEART_FILTER_FACE_MARK_BOUNDARIES); RNA_def_property_ui_text( prop, "Boundaries", "Filter feature lines based on face mark boundaries"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_face_mark_keep_contour", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna( - prop, nullptr, "calculation_flags", LRT_FILTER_FACE_MARK_KEEP_CONTOUR); + prop, nullptr, "calculation_flags", MOD_LINEART_FILTER_FACE_MARK_KEEP_CONTOUR); RNA_def_property_ui_text(prop, "Keep Contour", "Preserve contour lines while filtering"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); @@ -3726,24 +3732,27 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_loose_edge_chain", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "calculation_flags", LRT_CHAIN_LOOSE_EDGES); + RNA_def_property_boolean_sdna(prop, nullptr, "calculation_flags", MOD_LINEART_CHAIN_LOOSE_EDGES); RNA_def_property_ui_text(prop, "Chain Loose Edges", "Allow loose edges to be chained together"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_geometry_space_chain", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "calculation_flags", LRT_CHAIN_GEOMETRY_SPACE); + RNA_def_property_boolean_sdna( + prop, nullptr, "calculation_flags", MOD_LINEART_CHAIN_GEOMETRY_SPACE); RNA_def_property_ui_text( prop, "Use Geometry Space", "Use geometry distance for chaining instead of image space"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_detail_preserve", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "calculation_flags", LRT_CHAIN_PRESERVE_DETAILS); + RNA_def_property_boolean_sdna( + prop, nullptr, "calculation_flags", MOD_LINEART_CHAIN_PRESERVE_DETAILS); RNA_def_property_ui_text( prop, "Preserve Details", "Keep the zig-zag \"noise\" in initial chaining"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_overlap_edge_type_support", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "calculation_flags", LRT_ALLOW_OVERLAP_EDGE_TYPES); + RNA_def_property_boolean_sdna( + prop, nullptr, "calculation_flags", MOD_LINEART_ALLOW_OVERLAP_EDGE_TYPES); RNA_def_property_ui_text(prop, "Overlapping Edge Types", "Allow an edge to have multiple overlapping types. This will create a " @@ -3760,7 +3769,7 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_offset_towards_custom_camera", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "flags", LRT_GPENCIL_OFFSET_TOWARDS_CUSTOM_CAMERA); + RNA_def_property_boolean_sdna(prop, nullptr, "flags", MOD_LINEART_OFFSET_TOWARDS_CUSTOM_CAMERA); RNA_def_property_ui_text(prop, "Offset Towards Custom Camera", "Offset strokes towards selected camera instead of the active camera"); @@ -3802,45 +3811,46 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) /* types */ prop = RNA_def_property(srna, "use_contour", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "edge_types", LRT_EDGE_FLAG_CONTOUR); + RNA_def_property_boolean_sdna(prop, nullptr, "edge_types", MOD_LINEART_EDGE_FLAG_CONTOUR); RNA_def_property_ui_text(prop, "Use Contour", "Generate strokes from contours lines"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_loose", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "edge_types", LRT_EDGE_FLAG_LOOSE); + RNA_def_property_boolean_sdna(prop, nullptr, "edge_types", MOD_LINEART_EDGE_FLAG_LOOSE); RNA_def_property_ui_text(prop, "Use Loose", "Generate strokes from loose edges"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_crease", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "edge_types", LRT_EDGE_FLAG_CREASE); + RNA_def_property_boolean_sdna(prop, nullptr, "edge_types", MOD_LINEART_EDGE_FLAG_CREASE); RNA_def_property_ui_text(prop, "Use Crease", "Generate strokes from creased edges"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_material", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "edge_types", LRT_EDGE_FLAG_MATERIAL); + RNA_def_property_boolean_sdna(prop, nullptr, "edge_types", MOD_LINEART_EDGE_FLAG_MATERIAL); RNA_def_property_ui_text( prop, "Use Material", "Generate strokes from borders between materials"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_edge_mark", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "edge_types", LRT_EDGE_FLAG_EDGE_MARK); + RNA_def_property_boolean_sdna(prop, nullptr, "edge_types", MOD_LINEART_EDGE_FLAG_EDGE_MARK); RNA_def_property_ui_text(prop, "Use Edge Mark", "Generate strokes from freestyle marked edges"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_intersection", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "edge_types", LRT_EDGE_FLAG_INTERSECTION); + RNA_def_property_boolean_sdna(prop, nullptr, "edge_types", MOD_LINEART_EDGE_FLAG_INTERSECTION); RNA_def_property_ui_text(prop, "Use Intersection", "Generate strokes from intersections"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_light_contour", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "edge_types", LRT_EDGE_FLAG_LIGHT_CONTOUR); + RNA_def_property_boolean_sdna(prop, nullptr, "edge_types", MOD_LINEART_EDGE_FLAG_LIGHT_CONTOUR); RNA_def_property_ui_text(prop, "Use Light Contour", "Generate light/shadow separation lines from a reference light object"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_shadow", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "edge_types", LRT_EDGE_FLAG_PROJECTED_SHADOW); + RNA_def_property_boolean_sdna( + prop, nullptr, "edge_types", MOD_LINEART_EDGE_FLAG_PROJECTED_SHADOW); RNA_def_property_ui_text( prop, "Use Shadow", "Project contour lines using a light source object"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); @@ -3911,12 +3921,12 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "is_baked", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "flags", LRT_GPENCIL_IS_BAKED); + RNA_def_property_boolean_sdna(prop, nullptr, "flags", MOD_LINEART_IS_BAKED); RNA_def_property_ui_text(prop, "Is Baked", "This modifier has baked data"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_cache", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "flags", LRT_GPENCIL_USE_CACHE); + RNA_def_property_boolean_sdna(prop, nullptr, "flags", MOD_LINEART_USE_CACHE); RNA_def_property_ui_text(prop, "Use Cache", "Use cached scene data from the first line art modifier in the stack. " @@ -3945,13 +3955,13 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_material_mask", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "mask_switches", LRT_GPENCIL_MATERIAL_MASK_ENABLE); + RNA_def_property_boolean_sdna(prop, nullptr, "mask_switches", MOD_LINEART_MATERIAL_MASK_ENABLE); RNA_def_property_ui_text( prop, "Use Material Mask", "Use material masks to filter out occluded strokes"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_material_mask_match", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "mask_switches", LRT_GPENCIL_MATERIAL_MASK_MATCH); + RNA_def_property_boolean_sdna(prop, nullptr, "mask_switches", MOD_LINEART_MATERIAL_MASK_MATCH); RNA_def_property_ui_text( prop, "Match Masks", "Require matching all material masks instead of just one"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); @@ -3963,7 +3973,7 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_intersection_match", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "mask_switches", LRT_GPENCIL_INTERSECTION_MATCH); + RNA_def_property_boolean_sdna(prop, nullptr, "mask_switches", MOD_LINEART_INTERSECTION_MATCH); RNA_def_property_ui_text( prop, "Match Intersection", "Require matching all intersection masks instead of just one"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); @@ -3976,26 +3986,28 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) prop = RNA_def_property(srna, "use_crease_on_smooth", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna( - prop, nullptr, "calculation_flags", LRT_USE_CREASE_ON_SMOOTH_SURFACES); + prop, nullptr, "calculation_flags", MOD_LINEART_USE_CREASE_ON_SMOOTH_SURFACES); RNA_def_property_ui_text( prop, "Crease On Smooth Surfaces", "Allow crease edges to show inside smooth surfaces"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_crease_on_sharp", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "calculation_flags", LRT_USE_CREASE_ON_SHARP_EDGES); + RNA_def_property_boolean_sdna( + prop, nullptr, "calculation_flags", MOD_LINEART_USE_CREASE_ON_SHARP_EDGES); RNA_def_property_ui_text(prop, "Crease On Sharp Edges", "Allow crease to show on sharp edges"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_image_boundary_trimming", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna( - prop, nullptr, "calculation_flags", LRT_USE_IMAGE_BOUNDARY_TRIMMING); + prop, nullptr, "calculation_flags", MOD_LINEART_USE_IMAGE_BOUNDARY_TRIMMING); RNA_def_property_ui_text( prop, "Image Boundary Trimming", "Trim all edges right at the boundary of image (including overscan region)"); prop = RNA_def_property(srna, "use_back_face_culling", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "calculation_flags", LRT_USE_BACK_FACE_CULLING); + RNA_def_property_boolean_sdna( + prop, nullptr, "calculation_flags", MOD_LINEART_USE_BACK_FACE_CULLING); RNA_def_property_ui_text( prop, "Back Face Culling", @@ -4026,14 +4038,14 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) RNA_def_property_range(prop, 0.0f, 10000.0f); prop = RNA_def_property(srna, "use_invert_collection", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "flags", LRT_GPENCIL_INVERT_COLLECTION); + RNA_def_property_boolean_sdna(prop, nullptr, "flags", MOD_LINEART_INVERT_COLLECTION); RNA_def_property_ui_text(prop, "Invert Collection Filtering", "Select everything except lines from specified collection"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_invert_silhouette", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "flags", LRT_GPENCIL_INVERT_SILHOUETTE_FILTER); + RNA_def_property_boolean_sdna(prop, nullptr, "flags", MOD_LINEART_INVERT_SILHOUETTE_FILTER); RNA_def_property_ui_text(prop, "Invert Silhouette Filtering", "Select anti-silhouette lines"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); diff --git a/source/blender/makesrna/intern/rna_modifier.cc b/source/blender/makesrna/intern/rna_modifier.cc index b314dd3390a..bc0c60440e8 100644 --- a/source/blender/makesrna/intern/rna_modifier.cc +++ b/source/blender/makesrna/intern/rna_modifier.cc @@ -13,6 +13,7 @@ #include "DNA_armature_types.h" #include "DNA_cachefile_types.h" #include "DNA_gpencil_modifier_types.h" +#include "DNA_lineart_types.h" #include "DNA_mesh_types.h" #include "DNA_modifier_types.h" #include "DNA_object_force_types.h" @@ -233,6 +234,11 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = { ICON_MOD_LENGTH, "Length", "Grease Pencil length modifier"}, + {eModifierType_GreasePencilLineart, + "LINEART", + ICON_GREASEPENCIL, + "Line Art", + "Generate line art from scene geometries"}, {eModifierType_GreasePencilMirror, "GREASE_PENCIL_MIRROR", ICON_MOD_MIRROR, @@ -912,6 +918,7 @@ RNA_MOD_VGROUP_NAME_SET(Weld, defgrp_name); RNA_MOD_VGROUP_NAME_SET(Wireframe, defgrp_name); RNA_MOD_VGROUP_NAME_SET(GreasePencilWeightAngle, target_vgname); RNA_MOD_VGROUP_NAME_SET(GreasePencilWeightProximity, target_vgname); +RNA_MOD_VGROUP_NAME_SET(GreasePencilLineart, vgname); static void rna_ExplodeModifier_vgroup_get(PointerRNA *ptr, char *value) { @@ -1808,6 +1815,24 @@ static IDProperty **rna_NodesModifier_properties(PointerRNA *ptr) return &settings->properties; } +static void rna_Lineart_start_level_set(PointerRNA *ptr, int value) +{ + GreasePencilLineartModifierData *lmd = (GreasePencilLineartModifierData *)ptr->data; + + value = std::clamp(value, 0, 128); + lmd->level_start = value; + lmd->level_end = std::max(value, int(lmd->level_end)); +} + +static void rna_Lineart_end_level_set(PointerRNA *ptr, int value) +{ + GreasePencilLineartModifierData *lmd = (GreasePencilLineartModifierData *)ptr->data; + + value = std::clamp(value, 0, 128); + lmd->level_end = value; + lmd->level_start = std::min(value, int(lmd->level_start)); +} + static const NodesModifierData *find_nodes_modifier_by_bake(const Object &object, const NodesModifierBake &bake) { @@ -1924,6 +1949,17 @@ RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilWeightAngle); RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilWeightProximity); RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilHook); +static void rna_GreasePencilLineartModifier_material_set(PointerRNA *ptr, + PointerRNA value, + ReportList *reports) +{ + GreasePencilLineartModifierData *lmd = reinterpret_cast( + ptr->data); + Material **ma_target = &lmd->target_material; + + rna_GreasePencilModifier_material_set(ptr, value, reports, ma_target); +} + static void rna_GreasePencilOpacityModifier_opacity_factor_range( PointerRNA *ptr, float *min, float *max, float *softmin, float *softmax) { @@ -8177,6 +8213,509 @@ static void rna_def_modifier_grease_pencil_tint(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Modifier_update"); } +static void rna_def_modifier_grease_pencil_lineart(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + static const EnumPropertyItem modifier_lineart_source_type[] = { + {LINEART_SOURCE_COLLECTION, "COLLECTION", 0, "Collection", ""}, + {LINEART_SOURCE_OBJECT, "OBJECT", 0, "Object", ""}, + {LINEART_SOURCE_SCENE, "SCENE", 0, "Scene", ""}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem modifier_lineart_shadow_region_filtering[] = { + {LINEART_SHADOW_FILTER_NONE, + "NONE", + 0, + "None", + "Not filtering any lines based on illumination region"}, + {LINEART_SHADOW_FILTER_ILLUMINATED, + "ILLUMINATED", + 0, + "Illuminated", + "Only selecting lines from illuminated regions"}, + {LINEART_SHADOW_FILTER_SHADED, + "SHADED", + 0, + "Shaded", + "Only selecting lines from shaded regions"}, + {LINEART_SHADOW_FILTER_ILLUMINATED_ENCLOSED_SHAPES, + "ILLUMINATED_ENCLOSED", + 0, + "Illuminated (Enclosed Shapes)", + "Selecting lines from lit regions, and make the combination of contour, light contour and " + "shadow lines into enclosed shapes"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem modifier_lineart_silhouette_filtering[] = { + {LINEART_SILHOUETTE_FILTER_NONE, "NONE", 0, "Contour", ""}, + {LINEART_SILHOUETTE_FILTER_GROUP, "GROUP", 0, "Silhouette", ""}, + {LINEART_SILHOUETTE_FILTER_INDIVIDUAL, "INDIVIDUAL", 0, "Individual Silhouette", ""}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + srna = RNA_def_struct(brna, "GreasePencilLineartModifier", "Modifier"); + RNA_def_struct_ui_text( + srna, "Line Art Modifier", "Generate line art strokes from selected source"); + RNA_def_struct_sdna(srna, "GreasePencilLineartModifierData"); + RNA_def_struct_ui_icon(srna, ICON_MOD_LINEART); + + RNA_define_lib_overridable(true); + + prop = RNA_def_property(srna, "use_custom_camera", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "calculation_flags", MOD_LINEART_USE_CUSTOM_CAMERA); + RNA_def_property_ui_text( + prop, "Use Custom Camera", "Use custom camera instead of the active camera"); + RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); + + prop = RNA_def_property(srna, "use_fuzzy_intersections", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "calculation_flags", MOD_LINEART_INTERSECTION_AS_CONTOUR); + RNA_def_property_ui_text(prop, + "Intersection With Contour", + "Treat intersection and contour lines as if they were the same type so " + "they can be chained together"); + RNA_def_property_update(prop, NC_SCENE, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_fuzzy_all", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "calculation_flags", MOD_LINEART_EVERYTHING_AS_CONTOUR); + RNA_def_property_ui_text( + prop, "All Lines", "Treat all lines as the same line type so they can be chained together"); + RNA_def_property_update(prop, NC_SCENE, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_object_instances", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "calculation_flags", MOD_LINEART_ALLOW_DUPLI_OBJECTS); + RNA_def_property_ui_text(prop, + "Instanced Objects", + "Allow particle objects and face/vertex instances to show in line art"); + RNA_def_property_update(prop, NC_SCENE, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_edge_overlap", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "calculation_flags", MOD_LINEART_ALLOW_OVERLAPPING_EDGES); + RNA_def_property_ui_text( + prop, + "Handle Overlapping Edges", + "Allow edges in the same location (i.e. from edge split) to show properly. May run slower"); + RNA_def_property_update(prop, NC_SCENE, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_clip_plane_boundaries", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "calculation_flags", MOD_LINEART_ALLOW_CLIPPING_BOUNDARIES); + RNA_def_property_ui_text(prop, + "Clipping Boundaries", + "Allow lines generated by the near/far clipping plane to be shown"); + RNA_def_property_update(prop, NC_SCENE, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "crease_threshold", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_range(prop, 0, DEG2RAD(180.0f)); + RNA_def_property_ui_range(prop, 0.0f, DEG2RAD(180.0f), 0.01f, 1); + RNA_def_property_ui_text(prop, + "Crease Threshold", + "Angles smaller than this will be treated as creases. Crease angle " + "priority: object line art crease override > mesh auto smooth angle > " + "line art default crease"); + RNA_def_property_update(prop, NC_SCENE, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "split_angle", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_float_sdna(prop, nullptr, "angle_splitting_threshold"); + RNA_def_property_ui_text( + prop, "Angle Splitting", "Angle in screen space below which a stroke is split in two"); + /* Don't allow value very close to PI, or we get a lot of small segments. */ + RNA_def_property_ui_range(prop, 0.0f, DEG2RAD(179.5f), 0.01f, 1); + RNA_def_property_range(prop, 0.0f, DEG2RAD(180.0f)); + RNA_def_property_update(prop, NC_SCENE, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "smooth_tolerance", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, nullptr, "chain_smooth_tolerance"); + RNA_def_property_ui_text( + prop, "Smooth Tolerance", "Strength of smoothing applied on jagged chains"); + RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.05f, 4); + RNA_def_property_range(prop, 0.0f, 30.0f); + RNA_def_property_update(prop, NC_SCENE, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_loose_as_contour", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "calculation_flags", MOD_LINEART_LOOSE_AS_CONTOUR); + RNA_def_property_ui_text(prop, "Loose As Contour", "Loose edges will have contour type"); + RNA_def_property_update(prop, NC_SCENE, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "invert_source_vertex_group", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "calculation_flags", MOD_LINEART_INVERT_SOURCE_VGROUP); + RNA_def_property_ui_text(prop, "Invert Vertex Group", "Invert source vertex group values"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_output_vertex_group_match_by_name", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "calculation_flags", MOD_LINEART_MATCH_OUTPUT_VGROUP); + RNA_def_property_ui_text(prop, "Match Output", "Match output vertex group based on name"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_face_mark", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "calculation_flags", MOD_LINEART_FILTER_FACE_MARK); + RNA_def_property_ui_text( + prop, "Filter Face Marks", "Filter feature lines using freestyle face marks"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_face_mark_invert", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "calculation_flags", MOD_LINEART_FILTER_FACE_MARK_INVERT); + RNA_def_property_ui_text(prop, "Invert", "Invert face mark filtering"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_face_mark_boundaries", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "calculation_flags", MOD_LINEART_FILTER_FACE_MARK_BOUNDARIES); + RNA_def_property_ui_text( + prop, "Boundaries", "Filter feature lines based on face mark boundaries"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_face_mark_keep_contour", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "calculation_flags", MOD_LINEART_FILTER_FACE_MARK_KEEP_CONTOUR); + RNA_def_property_ui_text(prop, "Keep Contour", "Preserve contour lines while filtering"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "chaining_image_threshold", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_ui_text( + prop, + "Image Threshold", + "Segments with an image distance smaller than this will be chained together"); + RNA_def_property_ui_range(prop, 0.0f, 0.3f, 0.001f, 4); + RNA_def_property_range(prop, 0.0f, 0.3f); + RNA_def_property_update(prop, NC_SCENE, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_loose_edge_chain", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "calculation_flags", MOD_LINEART_CHAIN_LOOSE_EDGES); + RNA_def_property_ui_text(prop, "Chain Loose Edges", "Allow loose edges to be chained together"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_geometry_space_chain", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "calculation_flags", MOD_LINEART_CHAIN_GEOMETRY_SPACE); + RNA_def_property_ui_text( + prop, "Use Geometry Space", "Use geometry distance for chaining instead of image space"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_detail_preserve", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "calculation_flags", MOD_LINEART_CHAIN_PRESERVE_DETAILS); + RNA_def_property_ui_text( + prop, "Preserve Details", "Keep the zig-zag \"noise\" in initial chaining"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_overlap_edge_type_support", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "calculation_flags", MOD_LINEART_ALLOW_OVERLAP_EDGE_TYPES); + RNA_def_property_ui_text(prop, + "Overlapping Edge Types", + "Allow an edge to have multiple overlapping types. This will create a " + "separate stroke for each overlapping type"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "stroke_depth_offset", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_ui_text(prop, + "Stroke Depth Offset", + "Move strokes slightly towards the camera to avoid clipping while " + "preserve depth for the viewport"); + RNA_def_property_ui_range(prop, 0.0, 0.5, 0.001, 4); + RNA_def_property_range(prop, -0.1, FLT_MAX); + RNA_def_property_update(prop, NC_SCENE, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_offset_towards_custom_camera", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "flags", LINEART_GPENCIL_OFFSET_TOWARDS_CUSTOM_CAMERA); + RNA_def_property_ui_text(prop, + "Offset Towards Custom Camera", + "Offset strokes towards selected camera instead of the active camera"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "source_camera", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); + RNA_def_property_struct_type(prop, "Object"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_ui_text( + prop, "Camera Object", "Use specified camera object for generating line art"); + RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); + + prop = RNA_def_property(srna, "light_contour_object", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); + RNA_def_property_struct_type(prop, "Object"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_ui_text( + prop, "Light Object", "Use this light object to generate light contour"); + RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); + + prop = RNA_def_property(srna, "source_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, modifier_lineart_source_type); + RNA_def_property_ui_text(prop, "Source Type", "Line art stroke source type"); + RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); + + prop = RNA_def_property(srna, "source_object", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); + RNA_def_property_struct_type(prop, "Object"); + RNA_def_property_ui_text(prop, "Object", "Generate strokes from this object"); + RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); + + prop = RNA_def_property(srna, "source_collection", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); + RNA_def_property_struct_type(prop, "Collection"); + RNA_def_property_ui_text( + prop, "Collection", "Generate strokes from the objects in this collection"); + RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); + + /* types */ + prop = RNA_def_property(srna, "use_contour", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "edge_types", MOD_LINEART_EDGE_FLAG_CONTOUR); + RNA_def_property_ui_text(prop, "Use Contour", "Generate strokes from contours lines"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_loose", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "edge_types", MOD_LINEART_EDGE_FLAG_LOOSE); + RNA_def_property_ui_text(prop, "Use Loose", "Generate strokes from loose edges"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_crease", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "edge_types", MOD_LINEART_EDGE_FLAG_CREASE); + RNA_def_property_ui_text(prop, "Use Crease", "Generate strokes from creased edges"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_material", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "edge_types", MOD_LINEART_EDGE_FLAG_MATERIAL); + RNA_def_property_ui_text( + prop, "Use Material", "Generate strokes from borders between materials"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_edge_mark", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "edge_types", MOD_LINEART_EDGE_FLAG_EDGE_MARK); + RNA_def_property_ui_text(prop, "Use Edge Mark", "Generate strokes from freestyle marked edges"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_intersection", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "edge_types", MOD_LINEART_EDGE_FLAG_INTERSECTION); + RNA_def_property_ui_text(prop, "Use Intersection", "Generate strokes from intersections"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_light_contour", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "edge_types", MOD_LINEART_EDGE_FLAG_LIGHT_CONTOUR); + RNA_def_property_ui_text(prop, + "Use Light Contour", + "Generate light/shadow separation lines from a reference light object"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_shadow", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "edge_types", MOD_LINEART_EDGE_FLAG_PROJECTED_SHADOW); + RNA_def_property_ui_text( + prop, "Use Shadow", "Project contour lines using a light source object"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "shadow_region_filtering", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, nullptr, "shadow_selection"); + RNA_def_property_enum_items(prop, modifier_lineart_shadow_region_filtering); + RNA_def_property_ui_text(prop, + "Shadow Region Filtering", + "Select feature lines that comes from lit or shaded regions. Will not " + "affect cast shadow and light contour since they are at the border"); + RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); + + prop = RNA_def_property(srna, "silhouette_filtering", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, nullptr, "silhouette_selection"); + RNA_def_property_enum_items(prop, modifier_lineart_silhouette_filtering); + RNA_def_property_ui_text(prop, "Silhouette Filtering", "Select contour or silhouette"); + RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); + + prop = RNA_def_property(srna, "use_multiple_levels", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "use_multiple_levels", 0); + RNA_def_property_ui_text( + prop, "Use Occlusion Range", "Generate strokes from a range of occlusion levels"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "level_start", PROP_INT, PROP_NONE); + RNA_def_property_ui_text( + prop, "Level Start", "Minimum number of occlusions for the generated strokes"); + RNA_def_property_range(prop, 0, 128); + RNA_def_property_int_funcs(prop, nullptr, "rna_Lineart_start_level_set", nullptr); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "level_end", PROP_INT, PROP_NONE); + RNA_def_property_ui_text( + prop, "Level End", "Maximum number of occlusions for the generated strokes"); + RNA_def_property_range(prop, 0, 128); + RNA_def_property_int_funcs(prop, nullptr, "rna_Lineart_end_level_set", nullptr); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "target_layer", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text( + prop, "Layer", "Grease Pencil layer to which assign the generated strokes"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "target_material", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_struct_type(prop, "Material"); + RNA_def_property_pointer_funcs(prop, + nullptr, + "rna_GreasePencilLineartModifier_material_set", + nullptr, + "rna_GreasePencilModifier_material_poll"); + RNA_def_property_ui_text( + prop, "Material", "Grease Pencil material assigned to the generated strokes"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "source_vertex_group", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text( + prop, + "Source Vertex Group", + "Match the beginning of vertex group names from mesh objects, match all when left empty"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "vertex_group", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, nullptr, "vgname"); + RNA_def_property_string_funcs( + prop, nullptr, nullptr, "rna_GreasePencilLineartModifier_vgname_set"); + RNA_def_property_ui_text(prop, "Vertex Group", "Vertex group name for selected strokes"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "is_baked", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "flags", LINEART_GPENCIL_IS_BAKED); + RNA_def_property_ui_text(prop, "Is Baked", "This modifier has baked data"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_cache", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "flags", LINEART_GPENCIL_USE_CACHE); + RNA_def_property_ui_text(prop, + "Use Cache", + "Use cached scene data from the first line art modifier in the stack. " + "Certain settings will be unavailable"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "overscan", PROP_FLOAT, PROP_NONE); + RNA_def_property_ui_text( + prop, + "Overscan", + "A margin to prevent strokes from ending abruptly at the edge of the image"); + RNA_def_property_ui_range(prop, 0.0f, 0.5f, 0.01f, 3); + RNA_def_property_range(prop, 0.0f, 0.5f); + RNA_def_property_update(prop, NC_SCENE, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "thickness", PROP_INT, PROP_NONE); + RNA_def_property_ui_text(prop, "Thickness", "The thickness for the generated strokes"); + RNA_def_property_ui_range(prop, 1, 100, 1, 1); + RNA_def_property_range(prop, 1, 200); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "opacity", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_ui_text(prop, "Opacity", "The strength value for the generate strokes"); + RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.01f, 2); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_material_mask", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "mask_switches", LINEART_GPENCIL_MATERIAL_MASK_ENABLE); + RNA_def_property_ui_text( + prop, "Use Material Mask", "Use material masks to filter out occluded strokes"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_material_mask_match", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "mask_switches", LINEART_GPENCIL_MATERIAL_MASK_MATCH); + RNA_def_property_ui_text( + prop, "Match Masks", "Require matching all material masks instead of just one"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_material_mask_bits", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "material_mask_bits", 1); + RNA_def_property_array(prop, 8); + RNA_def_property_ui_text(prop, "Masks", "Mask bits to match from Material Line Art settings"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_intersection_match", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "mask_switches", LINEART_GPENCIL_INTERSECTION_MATCH); + RNA_def_property_ui_text( + prop, "Match Intersection", "Require matching all intersection masks instead of just one"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_intersection_mask", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "intersection_mask", 1); + RNA_def_property_array(prop, 8); + RNA_def_property_ui_text(prop, "Masks", "Mask bits to match from Collection Line Art settings"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_crease_on_smooth", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "calculation_flags", MOD_LINEART_USE_CREASE_ON_SMOOTH_SURFACES); + RNA_def_property_ui_text( + prop, "Crease On Smooth Surfaces", "Allow crease edges to show inside smooth surfaces"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_crease_on_sharp", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "calculation_flags", MOD_LINEART_USE_CREASE_ON_SHARP_EDGES); + RNA_def_property_ui_text(prop, "Crease On Sharp Edges", "Allow crease to show on sharp edges"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_image_boundary_trimming", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "calculation_flags", MOD_LINEART_USE_IMAGE_BOUNDARY_TRIMMING); + RNA_def_property_ui_text( + prop, + "Image Boundary Trimming", + "Trim all edges right at the boundary of image (including overscan region)"); + + prop = RNA_def_property(srna, "use_back_face_culling", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "calculation_flags", MOD_LINEART_USE_BACK_FACE_CULLING); + RNA_def_property_ui_text( + prop, + "Back Face Culling", + "Remove all back faces to speed up calculation, this will create edges in " + "different occlusion levels than when disabled"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "shadow_camera_near", PROP_FLOAT, PROP_NONE); + RNA_def_property_ui_text(prop, "Shadow Camera Near", "Near clipping distance of shadow camera"); + RNA_def_property_ui_range(prop, 0.0f, 500.0f, 0.1f, 2); + RNA_def_property_range(prop, 0.0f, 10000.0f); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "shadow_camera_far", PROP_FLOAT, PROP_NONE); + RNA_def_property_ui_text(prop, "Shadow Camera Far", "Far clipping distance of shadow camera"); + RNA_def_property_ui_range(prop, 0.0f, 500.0f, 0.1f, 2); + RNA_def_property_range(prop, 0.0f, 10000.0f); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "shadow_camera_size", PROP_FLOAT, PROP_NONE); + RNA_def_property_ui_text( + prop, + "Shadow Camera Size", + "Represents the \"Orthographic Scale\" of an orthographic camera. " + "If the camera is positioned at the light's location with this scale, it will " + "represent the coverage of the shadow \"camera\""); + RNA_def_property_ui_range(prop, 0.0f, 500.0f, 0.1f, 2); + RNA_def_property_range(prop, 0.0f, 10000.0f); + + prop = RNA_def_property(srna, "use_invert_collection", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "flags", LINEART_GPENCIL_INVERT_COLLECTION); + RNA_def_property_ui_text(prop, + "Invert Collection Filtering", + "Select everything except lines from specified collection"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_invert_silhouette", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "flags", LINEART_GPENCIL_INVERT_SILHOUETTE_FILTER); + RNA_def_property_ui_text(prop, "Invert Silhouette Filtering", "Select anti-silhouette lines"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + RNA_define_lib_overridable(false); +} + static void rna_def_modifier_grease_pencil_smooth(BlenderRNA *brna) { StructRNA *srna; @@ -9425,6 +9964,7 @@ void RNA_def_modifier(BlenderRNA *brna) rna_def_modifier_grease_pencil_array(brna); rna_def_modifier_grease_pencil_weight_proximity(brna); rna_def_modifier_grease_pencil_hook(brna); + rna_def_modifier_grease_pencil_lineart(brna); } #endif diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index 18f1f61b1df..49687a4d904 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -17,6 +17,11 @@ set(INC ../render ../windowmanager ../../../intern/eigen + + # Needed for shared line art functions from legacy implementations. + ../gpencil_modifiers_legacy + ../gpencil_modifiers_legacy/intern + ../gpencil_modifiers_legacy/intern/lineart # RNA_prototypes.h ${CMAKE_BINARY_DIR}/source/blender/makesrna @@ -66,6 +71,7 @@ set(SRC intern/MOD_laplaciandeform.cc intern/MOD_laplaciansmooth.cc intern/MOD_lattice.cc + intern/MOD_lineart.cc intern/MOD_mask.cc intern/MOD_mesh_to_volume.cc intern/MOD_meshcache.cc diff --git a/source/blender/modifiers/MOD_modifiertypes.hh b/source/blender/modifiers/MOD_modifiertypes.hh index 2087e8dd332..c291472fb2d 100644 --- a/source/blender/modifiers/MOD_modifiertypes.hh +++ b/source/blender/modifiers/MOD_modifiertypes.hh @@ -90,6 +90,7 @@ extern ModifierTypeInfo modifierType_GreasePencilWeightAngle; extern ModifierTypeInfo modifierType_GreasePencilArray; extern ModifierTypeInfo modifierType_GreasePencilWeightProximity; extern ModifierTypeInfo modifierType_GreasePencilHook; +extern ModifierTypeInfo modifierType_GreasePencilLineart; /* MOD_util.cc */ diff --git a/source/blender/modifiers/intern/MOD_lineart.cc b/source/blender/modifiers/intern/MOD_lineart.cc new file mode 100644 index 00000000000..484f4cb1363 --- /dev/null +++ b/source/blender/modifiers/intern/MOD_lineart.cc @@ -0,0 +1,909 @@ +/* SPDX-FileCopyrightText: 2005 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup modifiers + */ + +#include "BLT_translation.hh" + +#include "BLO_read_write.hh" + +#include "DNA_collection_types.h" +#include "DNA_defaults.h" +#include "DNA_gpencil_modifier_types.h" +#include "DNA_scene_types.h" + +#include "BKE_collection.hh" +#include "BKE_geometry_set.hh" +#include "BKE_global.hh" +#include "BKE_grease_pencil.hh" +#include "BKE_lib_query.hh" +#include "BKE_material.h" +#include "BKE_modifier.hh" + +#include "UI_interface.hh" +#include "UI_resources.hh" + +#include "MOD_gpencil_legacy_lineart.h" /* Needed for line art cache functions. */ +#include "MOD_grease_pencil_util.hh" +#include "MOD_lineart.h" +#include "MOD_modifiertypes.hh" +#include "MOD_ui_common.hh" + +#include "RNA_access.hh" +#include "RNA_prototypes.h" + +#include "DEG_depsgraph_query.hh" + +namespace blender { + +static void get_lineart_modifier_limits(const Object &ob, GreasePencilLineartLimitInfo &info) +{ + bool is_first = true; + LISTBASE_FOREACH (const ModifierData *, md, &ob.modifiers) { + if (md->type == eModifierType_GreasePencilLineart) { + const auto *lmd = reinterpret_cast(md); + if (is_first || (lmd->flags & MOD_LINEART_USE_CACHE)) { + info.min_level = std::min(info.min_level, lmd->level_start); + info.max_level = std::max( + info.max_level, lmd->use_multiple_levels ? lmd->level_end : lmd->level_start); + info.edge_types |= lmd->edge_types; + info.shadow_selection = std::max(info.shadow_selection, lmd->shadow_selection); + info.silhouette_selection = std::max(info.silhouette_selection, lmd->silhouette_selection); + is_first = false; + } + } + } +} + +static void set_lineart_modifier_limits(GreasePencilLineartModifierData &lmd, + const GreasePencilLineartLimitInfo &info, + const bool is_first_lineart) +{ + BLI_assert(lmd.modifier.type == eModifierType_GreasePencilLineart); + if (is_first_lineart || lmd.flags & MOD_LINEART_USE_CACHE) { + lmd.level_start_override = info.min_level; + lmd.level_end_override = info.max_level; + lmd.edge_types_override = info.edge_types; + lmd.shadow_selection_override = info.shadow_selection; + lmd.shadow_use_silhouette_override = info.silhouette_selection; + } + else { + lmd.level_start_override = lmd.level_start; + lmd.level_end_override = lmd.level_end; + lmd.edge_types_override = lmd.edge_types; + lmd.shadow_selection_override = lmd.shadow_selection; + lmd.shadow_use_silhouette_override = lmd.silhouette_selection; + } +} + +static bool is_first_lineart(const GreasePencilLineartModifierData &md) +{ + if (md.modifier.type != eModifierType_GreasePencilLineart) { + return false; + } + ModifierData *imd = md.modifier.prev; + while (imd != nullptr) { + if (imd->type == eModifierType_GreasePencilLineart) { + return false; + } + imd = imd->prev; + } + return true; +} + +static bool is_last_line_art(const GreasePencilLineartModifierData &md) +{ + if (md.modifier.type != eModifierType_GreasePencilLineart) { + return false; + } + ModifierData *imd = md.modifier.next; + while (imd != nullptr) { + if (imd->type == eModifierType_GreasePencilLineart) { + return false; + } + imd = imd->next; + } + return true; +} + +static GreasePencilLineartModifierData *get_first_lineart_modifier(const Object &ob) +{ + LISTBASE_FOREACH (ModifierData *, i_md, &ob.modifiers) { + if (i_md->type == eModifierType_GreasePencilLineart) { + return reinterpret_cast(i_md); + } + } + return nullptr; +} + +static void init_data(ModifierData *md) +{ + GreasePencilLineartModifierData *gpmd = (GreasePencilLineartModifierData *)md; + + BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(gpmd, modifier)); + + MEMCPY_STRUCT_AFTER(gpmd, DNA_struct_default_get(GreasePencilLineartModifierData), modifier); +} + +static void copy_data(const ModifierData *md, ModifierData *target, const int flag) +{ + BKE_modifier_copydata_generic(md, target, flag); +} + +static bool is_disabled(const Scene * /*scene*/, ModifierData *md, bool /*use_render_params*/) +{ + GreasePencilLineartModifierData *lmd = (GreasePencilLineartModifierData *)md; + + if (lmd->target_layer[0] == '\0' || !lmd->target_material) { + return true; + } + if (lmd->source_type == LINEART_SOURCE_OBJECT && !lmd->source_object) { + return true; + } + if (lmd->source_type == LINEART_SOURCE_COLLECTION && !lmd->source_collection) { + return true; + } + /* Preventing calculation in depsgraph when baking frames. */ + if (lmd->flags & MOD_LINEART_IS_BAKED) { + return true; + } + + return false; +} + +static void add_this_collection(Collection &collection, + const ModifierUpdateDepsgraphContext *ctx, + const int mode) +{ + bool default_add = true; + /* Do not do nested collection usage check, this is consistent with lineart calculation, because + * collection usage doesn't have a INHERIT mode. This might initially be derived from the fact + * that an object can be inside multiple collections, but might be irrelevant now with the way + * objects are iterated. Keep this logic for now. */ + if (collection.lineart_usage & COLLECTION_LRT_EXCLUDE) { + default_add = false; + } + FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (&collection, ob, mode) { + if (ELEM(ob->type, OB_MESH, OB_MBALL, OB_CURVES_LEGACY, OB_SURF, OB_FONT)) { + if ((ob->lineart.usage == OBJECT_LRT_INHERIT && default_add) || + ob->lineart.usage != OBJECT_LRT_EXCLUDE) + { + DEG_add_object_relation(ctx->node, ob, DEG_OB_COMP_GEOMETRY, "Line Art Modifier"); + DEG_add_object_relation(ctx->node, ob, DEG_OB_COMP_TRANSFORM, "Line Art Modifier"); + } + } + if (ob->type == OB_EMPTY && (ob->transflag & OB_DUPLICOLLECTION)) { + if (!ob->instance_collection) { + continue; + } + add_this_collection(*ob->instance_collection, ctx, mode); + } + } + FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END; +} + +static void update_depsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx) +{ + DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Line Art Modifier"); + + GreasePencilLineartModifierData *lmd = (GreasePencilLineartModifierData *)md; + + /* Always add whole master collection because line art will need the whole scene for + * visibility computation. Line art exclusion is handled inside #add_this_collection. */ + + /* Do we need to distinguish DAG_EVAL_VIEWPORT or DAG_EVAL_RENDER here? */ + + add_this_collection(*ctx->scene->master_collection, ctx, DAG_EVAL_VIEWPORT); + + if (lmd->calculation_flags & MOD_LINEART_USE_CUSTOM_CAMERA && lmd->source_camera) { + DEG_add_object_relation( + ctx->node, lmd->source_camera, DEG_OB_COMP_TRANSFORM, "Line Art Modifier"); + DEG_add_object_relation( + ctx->node, lmd->source_camera, DEG_OB_COMP_PARAMETERS, "Line Art Modifier"); + } + else if (ctx->scene->camera) { + DEG_add_object_relation( + ctx->node, ctx->scene->camera, DEG_OB_COMP_TRANSFORM, "Line Art Modifier"); + DEG_add_object_relation( + ctx->node, ctx->scene->camera, DEG_OB_COMP_PARAMETERS, "Line Art Modifier"); + } + if (lmd->light_contour_object) { + DEG_add_object_relation( + ctx->node, lmd->light_contour_object, DEG_OB_COMP_TRANSFORM, "Line Art Modifier"); + } +} + +static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data) +{ + GreasePencilLineartModifierData *lmd = (GreasePencilLineartModifierData *)md; + + walk(user_data, ob, (ID **)&lmd->source_collection, IDWALK_CB_NOP); + + walk(user_data, ob, (ID **)&lmd->source_object, IDWALK_CB_NOP); + walk(user_data, ob, (ID **)&lmd->source_camera, IDWALK_CB_NOP); + walk(user_data, ob, (ID **)&lmd->light_contour_object, IDWALK_CB_NOP); +} + +static void panel_draw(const bContext * /*C*/, Panel *panel) +{ + uiLayout *layout = panel->layout; + + PointerRNA ob_ptr; + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); + + PointerRNA obj_data_ptr = RNA_pointer_get(&ob_ptr, "data"); + + const int source_type = RNA_enum_get(ptr, "source_type"); + const bool is_baked = RNA_boolean_get(ptr, "is_baked"); + + uiLayoutSetPropSep(layout, true); + uiLayoutSetEnabled(layout, !is_baked); + + if (!is_first_lineart(*static_cast(ptr->data))) { + uiItemR(layout, ptr, "use_cache", UI_ITEM_NONE, nullptr, ICON_NONE); + } + + uiItemR(layout, ptr, "source_type", UI_ITEM_NONE, nullptr, ICON_NONE); + + if (source_type == LINEART_SOURCE_OBJECT) { + uiItemR(layout, ptr, "source_object", UI_ITEM_NONE, nullptr, ICON_OBJECT_DATA); + } + else if (source_type == LINEART_SOURCE_COLLECTION) { + uiLayout *sub = uiLayoutRow(layout, true); + uiItemR(sub, ptr, "source_collection", UI_ITEM_NONE, nullptr, ICON_OUTLINER_COLLECTION); + uiItemR(sub, ptr, "use_invert_collection", UI_ITEM_NONE, "", ICON_ARROW_LEFTRIGHT); + } + else { + /* Source is Scene. */ + } + + uiLayout *col = uiLayoutColumn(layout, false); + uiItemPointerR(col, ptr, "target_layer", &obj_data_ptr, "layers", nullptr, ICON_GREASEPENCIL); + uiItemPointerR( + col, ptr, "target_material", &obj_data_ptr, "materials", nullptr, ICON_GREASEPENCIL); + + col = uiLayoutColumn(layout, false); + uiItemR(col, ptr, "thickness", UI_ITEM_R_SLIDER, IFACE_("Line Thickness"), ICON_NONE); + uiItemR(col, ptr, "opacity", UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + + modifier_panel_end(layout, ptr); +} + +static void edge_types_panel_draw(const bContext * /*C*/, Panel *panel) +{ + uiLayout *layout = panel->layout; + PointerRNA ob_ptr; + PointerRNA *ptr = 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 = is_first_lineart( + *static_cast(ptr->data)); + const bool has_light = RNA_pointer_get(ptr, "light_contour_object").data != nullptr; + + uiLayoutSetEnabled(layout, !is_baked); + + uiLayoutSetPropSep(layout, true); + + uiLayout *sub = uiLayoutRow(layout, false); + uiLayoutSetActive(sub, has_light); + uiItemR(sub, + ptr, + "shadow_region_filtering", + UI_ITEM_NONE, + IFACE_("Illumination Filtering"), + ICON_NONE); + + uiLayout *col = uiLayoutColumn(layout, true); + + sub = uiLayoutRowWithHeading(col, false, IFACE_("Create")); + uiItemR(sub, ptr, "use_contour", UI_ITEM_NONE, "", ICON_NONE); + + uiLayout *entry = uiLayoutRow(sub, true); + uiLayoutSetActive(entry, RNA_boolean_get(ptr, "use_contour")); + uiItemR(entry, ptr, "silhouette_filtering", UI_ITEM_NONE, "", ICON_NONE); + + const int silhouette_filtering = RNA_enum_get(ptr, "silhouette_filtering"); + if (silhouette_filtering != LINEART_SILHOUETTE_FILTER_NONE) { + uiItemR(entry, ptr, "use_invert_silhouette", UI_ITEM_NONE, "", ICON_ARROW_LEFTRIGHT); + } + + sub = uiLayoutRow(col, false); + if (use_cache && !is_first) { + uiItemR(sub, ptr, "use_crease", UI_ITEM_NONE, IFACE_("Crease (Angle Cached)"), ICON_NONE); + } + else { + uiItemR(sub, ptr, "use_crease", UI_ITEM_NONE, "", ICON_NONE); + uiItemR(sub, + ptr, + "crease_threshold", + UI_ITEM_R_SLIDER | UI_ITEM_R_FORCE_BLANK_DECORATE, + nullptr, + ICON_NONE); + } + + uiItemR(col, ptr, "use_intersection", UI_ITEM_NONE, IFACE_("Intersections"), ICON_NONE); + uiItemR(col, ptr, "use_material", UI_ITEM_NONE, IFACE_("Material Borders"), ICON_NONE); + uiItemR(col, ptr, "use_edge_mark", UI_ITEM_NONE, IFACE_("Edge Marks"), ICON_NONE); + uiItemR(col, ptr, "use_loose", UI_ITEM_NONE, IFACE_("Loose"), ICON_NONE); + + entry = uiLayoutColumn(col, false); + uiLayoutSetActive(entry, has_light); + + sub = uiLayoutRow(entry, false); + uiItemR(sub, ptr, "use_light_contour", UI_ITEM_NONE, IFACE_("Light Contour"), ICON_NONE); + + uiItemR(entry, + ptr, + "use_shadow", + UI_ITEM_NONE, + CTX_IFACE_(BLT_I18NCONTEXT_ID_GPENCIL, "Cast Shadow"), + ICON_NONE); + + uiItemL(layout, IFACE_("Options"), ICON_NONE); + + sub = uiLayoutColumn(layout, false); + if (use_cache && !is_first) { + uiItemL(sub, IFACE_("Type overlapping cached"), ICON_INFO); + } + else { + uiItemR(sub, + ptr, + "use_overlap_edge_type_support", + UI_ITEM_NONE, + IFACE_("Allow Overlapping Types"), + ICON_NONE); + } +} + +static void options_light_reference_draw(const bContext * /*C*/, Panel *panel) +{ + uiLayout *layout = panel->layout; + PointerRNA ob_ptr; + PointerRNA *ptr = 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 has_light = RNA_pointer_get(ptr, "light_contour_object").data != nullptr; + const bool is_first = is_first_lineart( + *static_cast(ptr->data)); + + uiLayoutSetPropSep(layout, true); + uiLayoutSetEnabled(layout, !is_baked); + + if (use_cache && !is_first) { + uiItemL(layout, "Cached from the first line art modifier.", ICON_INFO); + return; + } + + uiItemR(layout, ptr, "light_contour_object", UI_ITEM_NONE, nullptr, ICON_NONE); + + uiLayout *remaining = uiLayoutColumn(layout, false); + uiLayoutSetActive(remaining, has_light); + + uiItemR(remaining, ptr, "shadow_camera_size", UI_ITEM_NONE, nullptr, ICON_NONE); + + uiLayout *col = uiLayoutColumn(remaining, true); + uiItemR(col, ptr, "shadow_camera_near", UI_ITEM_NONE, IFACE_("Near"), ICON_NONE); + uiItemR(col, ptr, "shadow_camera_far", UI_ITEM_NONE, IFACE_("Far"), ICON_NONE); +} + +static void options_panel_draw(const bContext * /*C*/, Panel *panel) +{ + uiLayout *layout = panel->layout; + PointerRNA ob_ptr; + PointerRNA *ptr = 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 = is_first_lineart( + *static_cast(ptr->data)); + + uiLayoutSetPropSep(layout, true); + uiLayoutSetEnabled(layout, !is_baked); + + if (use_cache && !is_first) { + uiItemL(layout, TIP_("Cached from the first line art modifier"), ICON_INFO); + return; + } + + uiLayout *row = uiLayoutRowWithHeading(layout, false, IFACE_("Custom Camera")); + uiItemR(row, ptr, "use_custom_camera", UI_ITEM_NONE, "", ICON_NONE); + uiLayout *subrow = uiLayoutRow(row, true); + uiLayoutSetActive(subrow, RNA_boolean_get(ptr, "use_custom_camera")); + uiLayoutSetPropSep(subrow, true); + uiItemR(subrow, ptr, "source_camera", UI_ITEM_NONE, "", ICON_OBJECT_DATA); + + uiLayout *col = uiLayoutColumn(layout, true); + + uiItemR(col, + ptr, + "use_edge_overlap", + UI_ITEM_NONE, + IFACE_("Overlapping Edges As Contour"), + ICON_NONE); + uiItemR(col, ptr, "use_object_instances", UI_ITEM_NONE, nullptr, ICON_NONE); + uiItemR(col, ptr, "use_clip_plane_boundaries", UI_ITEM_NONE, nullptr, ICON_NONE); + uiItemR(col, ptr, "use_crease_on_smooth", UI_ITEM_NONE, IFACE_("Crease On Smooth"), ICON_NONE); + uiItemR(col, ptr, "use_crease_on_sharp", UI_ITEM_NONE, IFACE_("Crease On Sharp"), ICON_NONE); + uiItemR(col, + ptr, + "use_back_face_culling", + UI_ITEM_NONE, + IFACE_("Force Backface Culling"), + ICON_NONE); +} + +static void occlusion_panel_draw(const bContext * /*C*/, Panel *panel) +{ + uiLayout *layout = panel->layout; + PointerRNA ob_ptr; + PointerRNA *ptr = 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); + + if (!show_in_front) { + uiItemL(layout, TIP_("Object is not in front"), ICON_INFO); + } + + layout = uiLayoutColumn(layout, false); + uiLayoutSetActive(layout, show_in_front); + + uiItemR(layout, ptr, "use_multiple_levels", UI_ITEM_NONE, IFACE_("Range"), ICON_NONE); + + if (use_multiple_levels) { + uiLayout *col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "level_start", UI_ITEM_NONE, nullptr, ICON_NONE); + uiItemR(col, ptr, "level_end", UI_ITEM_NONE, IFACE_("End"), ICON_NONE); + } + else { + uiItemR(layout, ptr, "level_start", UI_ITEM_NONE, IFACE_("Level"), ICON_NONE); + } +} + +static bool anything_showing_through(PointerRNA *ptr) +{ + const bool use_multiple_levels = RNA_boolean_get(ptr, "use_multiple_levels"); + const int level_start = RNA_int_get(ptr, "level_start"); + const int level_end = RNA_int_get(ptr, "level_end"); + if (use_multiple_levels) { + return std::max(level_start, level_end) > 0; + } + return level_start > 0; +} + +static void material_mask_panel_draw_header(const bContext * /*C*/, Panel *panel) +{ + uiLayout *layout = panel->layout; + PointerRNA ob_ptr; + PointerRNA *ptr = 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 && anything_showing_through(ptr)); + + uiItemR(layout, ptr, "use_material_mask", UI_ITEM_NONE, IFACE_("Material Mask"), ICON_NONE); +} + +static void material_mask_panel_draw(const bContext * /*C*/, Panel *panel) +{ + uiLayout *layout = panel->layout; + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr); + + const bool is_baked = RNA_boolean_get(ptr, "is_baked"); + uiLayoutSetEnabled(layout, !is_baked); + uiLayoutSetActive(layout, anything_showing_through(ptr)); + + uiLayoutSetPropSep(layout, true); + + uiLayoutSetEnabled(layout, RNA_boolean_get(ptr, "use_material_mask")); + + uiLayout *col = uiLayoutColumn(layout, true); + uiLayout *sub = uiLayoutRowWithHeading(col, true, IFACE_("Masks")); + + PropertyRNA *prop = RNA_struct_find_property(ptr, "use_material_mask_bits"); + for (int i = 0; i < 8; i++) { + uiItemFullR(sub, ptr, prop, i, 0, UI_ITEM_R_TOGGLE, " ", ICON_NONE); + if (i == 3) { + sub = uiLayoutRow(col, true); + } + } + + uiItemR(layout, ptr, "use_material_mask_match", UI_ITEM_NONE, IFACE_("Exact Match"), ICON_NONE); +} + +static void intersection_panel_draw(const bContext * /*C*/, Panel *panel) +{ + uiLayout *layout = panel->layout; + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr); + + const bool is_baked = RNA_boolean_get(ptr, "is_baked"); + uiLayoutSetEnabled(layout, !is_baked); + + uiLayoutSetPropSep(layout, true); + + uiLayoutSetActive(layout, RNA_boolean_get(ptr, "use_intersection")); + + uiLayout *col = uiLayoutColumn(layout, true); + uiLayout *sub = uiLayoutRowWithHeading(col, true, IFACE_("Collection Masks")); + + PropertyRNA *prop = RNA_struct_find_property(ptr, "use_intersection_mask"); + for (int i = 0; i < 8; i++) { + uiItemFullR(sub, ptr, prop, i, 0, UI_ITEM_R_TOGGLE, " ", ICON_NONE); + if (i == 3) { + sub = uiLayoutRow(col, true); + } + } + + uiItemR(layout, ptr, "use_intersection_match", UI_ITEM_NONE, IFACE_("Exact Match"), ICON_NONE); +} + +static void face_mark_panel_draw_header(const bContext * /*C*/, Panel *panel) +{ + uiLayout *layout = panel->layout; + PointerRNA ob_ptr; + PointerRNA *ptr = 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 = is_first_lineart( + *static_cast(ptr->data)); + + if (!use_cache || is_first) { + uiLayoutSetEnabled(layout, !is_baked); + uiItemR(layout, ptr, "use_face_mark", UI_ITEM_NONE, IFACE_("Face Mark Filtering"), ICON_NONE); + } + else { + uiItemL(layout, IFACE_("Face Mark Filtering"), ICON_NONE); + } +} + +static void face_mark_panel_draw(const bContext * /*C*/, Panel *panel) +{ + uiLayout *layout = panel->layout; + PointerRNA ob_ptr; + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); + + 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 = is_first_lineart( + *static_cast(ptr->data)); + + uiLayoutSetEnabled(layout, !is_baked); + + if (use_cache && !is_first) { + uiItemL(layout, TIP_("Cached from the first line art modifier"), ICON_INFO); + return; + } + + uiLayoutSetPropSep(layout, true); + + uiLayoutSetActive(layout, use_mark); + + uiItemR(layout, ptr, "use_face_mark_invert", UI_ITEM_NONE, nullptr, ICON_NONE); + uiItemR(layout, ptr, "use_face_mark_boundaries", UI_ITEM_NONE, nullptr, ICON_NONE); + uiItemR(layout, ptr, "use_face_mark_keep_contour", UI_ITEM_NONE, nullptr, ICON_NONE); +} + +static void chaining_panel_draw(const bContext * /*C*/, Panel *panel) +{ + PointerRNA ob_ptr; + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); + + 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 is_first = is_first_lineart( + *static_cast(ptr->data)); + const bool is_geom = RNA_boolean_get(ptr, "use_geometry_space_chain"); + + uiLayoutSetPropSep(layout, true); + uiLayoutSetEnabled(layout, !is_baked); + + if (use_cache && !is_first) { + uiItemL(layout, TIP_("Cached from the first line art modifier"), ICON_INFO); + return; + } + + uiLayout *col = uiLayoutColumnWithHeading(layout, true, IFACE_("Chain")); + uiItemR(col, ptr, "use_fuzzy_intersections", UI_ITEM_NONE, nullptr, ICON_NONE); + uiItemR(col, ptr, "use_fuzzy_all", UI_ITEM_NONE, nullptr, ICON_NONE); + uiItemR(col, ptr, "use_loose_edge_chain", UI_ITEM_NONE, IFACE_("Loose Edges"), ICON_NONE); + uiItemR( + col, ptr, "use_loose_as_contour", UI_ITEM_NONE, IFACE_("Loose Edges As Contour"), ICON_NONE); + uiItemR(col, ptr, "use_detail_preserve", UI_ITEM_NONE, nullptr, ICON_NONE); + uiItemR(col, ptr, "use_geometry_space_chain", UI_ITEM_NONE, IFACE_("Geometry Space"), ICON_NONE); + + uiItemR(layout, + ptr, + "chaining_image_threshold", + UI_ITEM_NONE, + is_geom ? IFACE_("Geometry Threshold") : nullptr, + ICON_NONE); + + uiItemR(layout, ptr, "smooth_tolerance", UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(layout, ptr, "split_angle", UI_ITEM_R_SLIDER, nullptr, ICON_NONE); +} + +static void vgroup_panel_draw(const bContext * /*C*/, Panel *panel) +{ + PointerRNA ob_ptr; + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); + + 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 is_first = is_first_lineart( + *static_cast(ptr->data)); + + uiLayoutSetPropSep(layout, true); + uiLayoutSetEnabled(layout, !is_baked); + + if (use_cache && !is_first) { + uiItemL(layout, TIP_("Cached from the first line art modifier"), ICON_INFO); + return; + } + + uiLayout *col = uiLayoutColumn(layout, true); + + uiLayout *row = uiLayoutRow(col, true); + + uiItemR( + row, ptr, "source_vertex_group", UI_ITEM_NONE, IFACE_("Filter Source"), ICON_GROUP_VERTEX); + uiItemR(row, ptr, "invert_source_vertex_group", UI_ITEM_R_TOGGLE, "", ICON_ARROW_LEFTRIGHT); + + uiItemR(col, ptr, "use_output_vertex_group_match_by_name", UI_ITEM_NONE, nullptr, ICON_NONE); + + uiItemPointerR(col, ptr, "vertex_group", &ob_ptr, "vertex_groups", IFACE_("Target"), ICON_NONE); +} + +static void bake_panel_draw(const bContext * /*C*/, Panel *panel) +{ + uiLayout *layout = panel->layout; + PointerRNA ob_ptr; + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); + + const bool is_baked = RNA_boolean_get(ptr, "is_baked"); + + uiLayoutSetPropSep(layout, true); + + if (is_baked) { + uiLayout *col = uiLayoutColumn(layout, false); + uiLayoutSetPropSep(col, false); + uiItemL(col, TIP_("Modifier has baked data"), ICON_NONE); + uiItemR( + col, ptr, "is_baked", UI_ITEM_R_TOGGLE, IFACE_("Continue Without Clearing"), ICON_NONE); + } + + uiLayout *col = uiLayoutColumn(layout, false); + uiLayoutSetEnabled(col, !is_baked); + uiItemO(col, nullptr, ICON_NONE, "OBJECT_OT_lineart_bake_strokes"); + uiItemO(col, nullptr, ICON_NONE, "OBJECT_OT_lineart_bake_strokes_all"); + + col = uiLayoutColumn(layout, false); + uiItemO(col, nullptr, ICON_NONE, "OBJECT_OT_lineart_clear"); + uiItemO(col, nullptr, ICON_NONE, "OBJECT_OT_lineart_clear_all"); +} + +static void composition_panel_draw(const bContext * /*C*/, Panel *panel) +{ + PointerRNA ob_ptr; + PointerRNA *ptr = 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); + + uiItemR(layout, ptr, "overscan", UI_ITEM_NONE, nullptr, ICON_NONE); + uiItemR(layout, ptr, "use_image_boundary_trimming", UI_ITEM_NONE, nullptr, ICON_NONE); + + if (show_in_front) { + uiItemL(layout, TIP_("Object is shown in front"), ICON_ERROR); + } + + uiLayout *col = uiLayoutColumn(layout, false); + uiLayoutSetActive(col, !show_in_front); + + uiItemR(col, ptr, "stroke_depth_offset", UI_ITEM_R_SLIDER, IFACE_("Depth Offset"), ICON_NONE); + uiItemR(col, + ptr, + "use_offset_towards_custom_camera", + UI_ITEM_NONE, + IFACE_("Towards Custom Camera"), + ICON_NONE); +} + +static void panel_register(ARegionType *region_type) +{ + PanelType *panel_type = modifier_panel_register( + region_type, eModifierType_GreasePencilLineart, panel_draw); + + modifier_subpanel_register( + region_type, "edge_types", "Edge Types", nullptr, edge_types_panel_draw, panel_type); + modifier_subpanel_register(region_type, + "light_reference", + "Light Reference", + nullptr, + options_light_reference_draw, + panel_type); + modifier_subpanel_register( + region_type, "geometry", "Geometry Processing", nullptr, options_panel_draw, panel_type); + PanelType *occlusion_panel = modifier_subpanel_register( + region_type, "occlusion", "Occlusion", nullptr, occlusion_panel_draw, panel_type); + modifier_subpanel_register(region_type, + "material_mask", + "", + material_mask_panel_draw_header, + material_mask_panel_draw, + occlusion_panel); + modifier_subpanel_register( + region_type, "intersection", "Intersection", nullptr, intersection_panel_draw, panel_type); + modifier_subpanel_register( + region_type, "face_mark", "", face_mark_panel_draw_header, face_mark_panel_draw, panel_type); + modifier_subpanel_register( + region_type, "chaining", "Chaining", nullptr, chaining_panel_draw, panel_type); + modifier_subpanel_register( + region_type, "vgroup", "Vertex Weight Transfer", nullptr, vgroup_panel_draw, panel_type); + modifier_subpanel_register( + region_type, "composition", "Composition", nullptr, composition_panel_draw, panel_type); + modifier_subpanel_register(region_type, "bake", "Bake", nullptr, bake_panel_draw, panel_type); +} + +static void generate_strokes(ModifierData &md, + const ModifierEvalContext &ctx, + GreasePencil &grease_pencil, + GreasePencilLineartModifierData &first_lineart) +{ + auto &lmd = reinterpret_cast(md); + + bke::greasepencil::TreeNode *node = grease_pencil.find_node_by_name(lmd.target_layer); + if (!node || !node->is_layer()) { + return; + } + + LineartCache *local_lc = first_lineart.shared_cache; + + if (!(lmd.flags & MOD_LINEART_USE_CACHE)) { + MOD_lineart_compute_feature_lines_v3( + ctx.depsgraph, lmd, &local_lc, !(ctx.object->dtx & OB_DRAW_IN_FRONT)); + MOD_lineart_destroy_render_data_v3(&lmd); + } + MOD_lineart_chain_clear_picked_flag(local_lc); + lmd.cache = local_lc; + + const int current_frame = grease_pencil.runtime->eval_frame; + + /* Ensure we have a frame in the selected layer to put line art result in. */ + bke::greasepencil::Layer &layer = node->as_layer(); + + bke::greasepencil::Drawing &drawing = [&]() -> bke::greasepencil::Drawing & { + if (bke::greasepencil::Drawing *drawing = grease_pencil.get_editable_drawing_at(layer, + current_frame)) + { + return *drawing; + } + grease_pencil.insert_blank_frame(layer, current_frame, 0, BEZT_KEYTYPE_KEYFRAME); + return *grease_pencil.get_editable_drawing_at(layer, current_frame); + }(); + + MOD_lineart_gpencil_generate_v3( + lmd.cache, + ctx.depsgraph, + drawing, + lmd.source_type, + lmd.source_object, + lmd.source_collection, + lmd.level_start, + lmd.use_multiple_levels ? lmd.level_end : lmd.level_start, + lmd.target_material ? BKE_object_material_index_get(ctx.object, lmd.target_material) : 0, + lmd.edge_types, + lmd.mask_switches, + lmd.material_mask_bits, + lmd.intersection_mask, + float(lmd.thickness) / 1000.0f, + lmd.opacity, + lmd.shadow_selection, + lmd.silhouette_selection, + lmd.source_vertex_group, + lmd.vgname, + lmd.flags, + lmd.calculation_flags); + + if (!(lmd.flags & MOD_LINEART_USE_CACHE)) { + /* Clear local cache. */ + if (local_lc != first_lineart.shared_cache) { + MOD_lineart_clear_cache(&local_lc); + } + /* Restore the original cache pointer so the modifiers below still have access to the "global" + * cache. */ + lmd.cache = first_lineart.shared_cache; + } +} + +static void modify_geometry_set(ModifierData *md, + const ModifierEvalContext *ctx, + bke::GeometrySet *geometry_set) +{ + if (!geometry_set->has_grease_pencil()) { + return; + } + GreasePencil &grease_pencil = *geometry_set->get_grease_pencil_for_write(); + auto mmd = reinterpret_cast(md); + + GreasePencilLineartModifierData *first_lineart = get_first_lineart_modifier(*ctx->object); + BLI_assert(first_lineart); + + bool is_first_lineart = (mmd == first_lineart); + + if (is_first_lineart) { + mmd->shared_cache = MOD_lineart_init_cache(); + get_lineart_modifier_limits(*ctx->object, mmd->shared_cache->LimitInfo); + } + set_lineart_modifier_limits(*mmd, first_lineart->shared_cache->LimitInfo, is_first_lineart); + + generate_strokes(*md, *ctx, grease_pencil, *first_lineart); + + if (is_last_line_art(*mmd)) { + MOD_lineart_clear_cache(&first_lineart->shared_cache); + } + + DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY); +} + +static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const ModifierData *md) +{ + const auto *lmd = reinterpret_cast(md); + + BLO_write_struct(writer, GreasePencilLineartModifierData, lmd); +} +} // namespace blender + +ModifierTypeInfo modifierType_GreasePencilLineart = { + /*idname*/ "Lineart Modifier", + /*name*/ N_("Lineart"), + /*struct_name*/ "GreasePencilLineartModifierData", + /*struct_size*/ sizeof(GreasePencilLineartModifierData), + /*srna*/ &RNA_GreasePencilLineartModifier, + /*type*/ ModifierTypeType::Constructive, + /*flags*/ eModifierTypeFlag_AcceptsGreasePencil, + /*icon*/ ICON_MOD_LINEART, + + /*copy_data*/ blender::copy_data, + + /*deform_verts*/ nullptr, + /*deform_matrices*/ nullptr, + /*deform_verts_EM*/ nullptr, + /*deform_matrices_EM*/ nullptr, + /*modify_mesh*/ nullptr, + /*modify_geometry_set*/ blender::modify_geometry_set, + + /*init_data*/ blender::init_data, + /*required_data_mask*/ nullptr, + /*free_data*/ nullptr, + /*is_disabled*/ blender::is_disabled, + /*update_depsgraph*/ blender::update_depsgraph, + /*depends_on_time*/ nullptr, + /*depends_on_normals*/ nullptr, + /*foreach_ID_link*/ blender::foreach_ID_link, + /*foreach_tex_link*/ nullptr, + /*free_runtime_data*/ nullptr, + /*panel_register*/ blender::panel_register, + /*blend_write*/ blender::blend_write, + /*blend_read*/ nullptr, +}; diff --git a/source/blender/modifiers/intern/MOD_util.cc b/source/blender/modifiers/intern/MOD_util.cc index 7dffd43bc05..5e79aac8c15 100644 --- a/source/blender/modifiers/intern/MOD_util.cc +++ b/source/blender/modifiers/intern/MOD_util.cc @@ -281,5 +281,6 @@ void modifier_type_init(ModifierTypeInfo *types[]) INIT_TYPE(GreasePencilArray); INIT_TYPE(GreasePencilWeightProximity); INIT_TYPE(GreasePencilHook); + INIT_TYPE(GreasePencilLineart); #undef INIT_TYPE }