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 }