From f51e93bec7a47558d21e2cb42c287b3a05243eca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Wed, 6 Mar 2024 12:05:00 +0100 Subject: [PATCH] GPv3: Shrinkwrap modifier Port of shrinkwrap modifier from GPv2. Adds a new API function in `BKE_shrinkwrap.hh` for the modifier. Pull Request: https://projects.blender.org/blender/blender/pulls/119118 --- .../startup/bl_ui/properties_data_modifier.py | 1 + source/blender/blenkernel/BKE_shrinkwrap.hh | 34 ++ .../intern/grease_pencil_convert_legacy.cc | 46 ++- .../blender/blenkernel/intern/shrinkwrap.cc | 50 +++ .../blender/makesdna/DNA_modifier_defaults.h | 15 + source/blender/makesdna/DNA_modifier_types.h | 37 ++ source/blender/makesdna/intern/dna_defaults.c | 2 + .../blender/makesrna/intern/rna_modifier.cc | 170 ++++++++ source/blender/modifiers/CMakeLists.txt | 1 + source/blender/modifiers/MOD_modifiertypes.hh | 1 + .../intern/MOD_grease_pencil_shrinkwrap.cc | 365 ++++++++++++++++++ source/blender/modifiers/intern/MOD_util.cc | 1 + 12 files changed, 722 insertions(+), 1 deletion(-) create mode 100644 source/blender/modifiers/intern/MOD_grease_pencil_shrinkwrap.cc diff --git a/scripts/startup/bl_ui/properties_data_modifier.py b/scripts/startup/bl_ui/properties_data_modifier.py index 4ef55af7a38..e234a0ee41e 100644 --- a/scripts/startup/bl_ui/properties_data_modifier.py +++ b/scripts/startup/bl_ui/properties_data_modifier.py @@ -205,6 +205,7 @@ class OBJECT_MT_modifier_add_deform(ModifierAddMenu, Menu): self.operator_modifier_add(layout, 'GREASE_PENCIL_LATTICE') self.operator_modifier_add(layout, 'GREASE_PENCIL_NOISE') self.operator_modifier_add(layout, 'GREASE_PENCIL_OFFSET') + self.operator_modifier_add(layout, 'GREASE_PENCIL_SHRINKWRAP') self.operator_modifier_add(layout, 'GREASE_PENCIL_SMOOTH') self.operator_modifier_add(layout, 'GREASE_PENCIL_THICKNESS') layout.template_modifier_asset_menu_items(catalog_path=self.bl_label) diff --git a/source/blender/blenkernel/BKE_shrinkwrap.hh b/source/blender/blenkernel/BKE_shrinkwrap.hh index 4e4ba6c29f4..e68b97dfde0 100644 --- a/source/blender/blenkernel/BKE_shrinkwrap.hh +++ b/source/blender/blenkernel/BKE_shrinkwrap.hh @@ -29,6 +29,7 @@ */ struct BVHTree; +struct GreasePencilShrinkwrapModifierData; struct MDeformVert; struct Mesh; struct ModifierEvalContext; @@ -118,6 +119,39 @@ void shrinkwrapGpencilModifier_deform(ShrinkwrapGpencilModifierData *mmd, float (*vertexCos)[3], int numVerts); +struct ShrinkwrapParams { + /** Shrink target. */ + Object *target = nullptr; + /** Additional shrink target. */ + Object *aux_target = nullptr; + /* Use inverse vertex group weights. */ + bool invert_vertex_weights = false; + /** Distance offset to keep from mesh/projection point. */ + float keep_distance = 0.05f; + /** Shrink type projection. */ + short shrink_type = 0 /*MOD_SHRINKWRAP_NEAREST_SURFACE*/; + /** Shrink options. */ + char shrink_options = 0 /*MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR*/; + /** Shrink to surface mode. */ + char shrink_mode = 0 /*MOD_SHRINKWRAP_ON_SURFACE*/; + /** Limit the projection ray cast. */ + float projection_limit = 0.0f; + /** Axis to project over. */ + char projection_axis = 0 /*MOD_SHRINKWRAP_PROJECT_OVER_NORMAL*/; + /** + * If using projection over vertex normal this controls the level of subsurface that must be + * done before getting the vertex coordinates and normal. + */ + char subsurf_levels = 0; +}; + +void shrinkwrapParams_deform(const ShrinkwrapParams ¶ms, + Object &object, + ShrinkwrapTreeData &tree, + blender::Span dvert, + int defgrp_index, + blender::MutableSpan positions); + /** * Used in `editmesh_mask_extract.cc` to shrink-wrap the extracted mesh to the sculpt. */ diff --git a/source/blender/blenkernel/intern/grease_pencil_convert_legacy.cc b/source/blender/blenkernel/intern/grease_pencil_convert_legacy.cc index a3c5e5eb3e1..baa049f631e 100644 --- a/source/blender/blenkernel/intern/grease_pencil_convert_legacy.cc +++ b/source/blender/blenkernel/intern/grease_pencil_convert_legacy.cc @@ -1358,6 +1358,48 @@ static void legacy_object_modifier_outline(Object &object, GpencilModifierData & false); } +static void legacy_object_modifier_shrinkwrap(Object &object, GpencilModifierData &legacy_md) +{ + ModifierData &md = legacy_object_modifier_common( + object, eModifierType_GreasePencilShrinkwrap, legacy_md); + auto &md_shrinkwrap = reinterpret_cast(md); + auto &legacy_md_shrinkwrap = reinterpret_cast(legacy_md); + + /* Shrinkwrap enums and flags do not have named types. */ + /* MOD_SHRINKWRAP_NEAREST_SURFACE etc. */ + md_shrinkwrap.shrink_type = legacy_md_shrinkwrap.shrink_type; + /* MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR etc. */ + md_shrinkwrap.shrink_opts = legacy_md_shrinkwrap.shrink_opts; + /* MOD_SHRINKWRAP_ON_SURFACE etc. */ + md_shrinkwrap.shrink_mode = legacy_md_shrinkwrap.shrink_mode; + /* MOD_SHRINKWRAP_PROJECT_OVER_NORMAL etc. */ + md_shrinkwrap.proj_axis = legacy_md_shrinkwrap.proj_axis; + + md_shrinkwrap.target = legacy_md_shrinkwrap.target; + legacy_md_shrinkwrap.target = nullptr; + md_shrinkwrap.aux_target = legacy_md_shrinkwrap.aux_target; + legacy_md_shrinkwrap.aux_target = nullptr; + md_shrinkwrap.keep_dist = legacy_md_shrinkwrap.keep_dist; + md_shrinkwrap.proj_limit = legacy_md_shrinkwrap.proj_limit; + md_shrinkwrap.subsurf_levels = legacy_md_shrinkwrap.subsurf_levels; + md_shrinkwrap.smooth_factor = legacy_md_shrinkwrap.smooth_factor; + md_shrinkwrap.smooth_step = legacy_md_shrinkwrap.smooth_step; + + legacy_object_modifier_influence(md_shrinkwrap.influence, + legacy_md_shrinkwrap.layername, + legacy_md_shrinkwrap.layer_pass, + legacy_md_shrinkwrap.flag & GP_SHRINKWRAP_INVERT_LAYER, + legacy_md_shrinkwrap.flag & GP_SHRINKWRAP_INVERT_LAYERPASS, + &legacy_md_shrinkwrap.material, + legacy_md_shrinkwrap.pass_index, + legacy_md_shrinkwrap.flag & GP_SHRINKWRAP_INVERT_MATERIAL, + legacy_md_shrinkwrap.flag & GP_SHRINKWRAP_INVERT_PASS, + legacy_md_shrinkwrap.vgname, + legacy_md_shrinkwrap.flag & GP_SHRINKWRAP_INVERT_VGROUP, + nullptr, + false); +} + static void legacy_object_modifier_smooth(Object &object, GpencilModifierData &legacy_md) { ModifierData &md = legacy_object_modifier_common( @@ -1734,6 +1776,9 @@ static void legacy_object_modifiers(Main & /*bmain*/, Object &object) case eGpencilModifierType_Outline: legacy_object_modifier_outline(object, *gpd_md); break; + case eGpencilModifierType_Shrinkwrap: + legacy_object_modifier_shrinkwrap(object, *gpd_md); + break; case eGpencilModifierType_Smooth: legacy_object_modifier_smooth(object, *gpd_md); break; @@ -1761,7 +1806,6 @@ static void legacy_object_modifiers(Main & /*bmain*/, Object &object) case eGpencilModifierType_Build: case eGpencilModifierType_Simplify: case eGpencilModifierType_Texture: - case eGpencilModifierType_Shrinkwrap: break; } diff --git a/source/blender/blenkernel/intern/shrinkwrap.cc b/source/blender/blenkernel/intern/shrinkwrap.cc index 1baec9458bf..56ec41ccbcf 100644 --- a/source/blender/blenkernel/intern/shrinkwrap.cc +++ b/source/blender/blenkernel/intern/shrinkwrap.cc @@ -1518,6 +1518,56 @@ void shrinkwrapGpencilModifier_deform(ShrinkwrapGpencilModifierData *mmd, } } +void shrinkwrapParams_deform(const ShrinkwrapParams ¶ms, + Object &object, + ShrinkwrapTreeData &tree, + const blender::Span dvert, + const int defgrp_index, + const blender::MutableSpan positions) +{ + using namespace blender::bke; + + ShrinkwrapCalcData calc = NULL_ShrinkwrapCalcData; + /* Convert params struct to use the same struct and function used with meshes. */ + ShrinkwrapModifierData smd; + smd.target = params.target; + smd.auxTarget = params.aux_target; + smd.keepDist = params.keep_distance; + smd.shrinkType = params.shrink_type; + smd.shrinkOpts = params.shrink_options; + smd.shrinkMode = params.shrink_mode; + smd.projLimit = params.projection_limit; + smd.projAxis = params.projection_axis; + + /* Configure Shrinkwrap calc data. */ + calc.smd = &smd; + calc.ob = &object; + calc.numVerts = int(positions.size()); + calc.vertexCos = reinterpret_cast(positions.data()); + calc.dvert = dvert.is_empty() ? nullptr : dvert.data(); + calc.vgroup = defgrp_index; + calc.invert_vgroup = params.invert_vertex_weights; + + BLI_SPACE_TRANSFORM_SETUP(&calc.local2target, &object, params.target); + calc.keepDist = params.keep_distance; + calc.tree = &tree; + + switch (params.shrink_type) { + case MOD_SHRINKWRAP_NEAREST_SURFACE: + case MOD_SHRINKWRAP_TARGET_PROJECT: + TIMEIT_BENCH(shrinkwrap_calc_nearest_surface_point(&calc), gpdeform_surface); + break; + + case MOD_SHRINKWRAP_PROJECT: + TIMEIT_BENCH(shrinkwrap_calc_normal_projection(&calc), gpdeform_project); + break; + + case MOD_SHRINKWRAP_NEAREST_VERTEX: + TIMEIT_BENCH(shrinkwrap_calc_nearest_vertex(&calc), gpdeform_vertex); + break; + } +} + void BKE_shrinkwrap_mesh_nearest_surface_deform(Depsgraph *depsgraph, Scene *scene, Object *ob_source, diff --git a/source/blender/makesdna/DNA_modifier_defaults.h b/source/blender/makesdna/DNA_modifier_defaults.h index 436f6c8cefa..9a7a80803fd 100644 --- a/source/blender/makesdna/DNA_modifier_defaults.h +++ b/source/blender/makesdna/DNA_modifier_defaults.h @@ -1030,4 +1030,19 @@ .outline_material = NULL, \ } +#define _DNA_DEFAULT_GreasePencilShrinkwrapModifierData \ + { \ + .target = NULL, \ + .aux_target = NULL, \ + .keep_dist = 0.05f, \ + .shrink_type = MOD_SHRINKWRAP_NEAREST_SURFACE, \ + .shrink_opts = MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR, \ + .shrink_mode = MOD_SHRINKWRAP_ON_SURFACE, \ + .proj_limit = 0.0f, \ + .proj_axis = MOD_SHRINKWRAP_PROJECT_OVER_NORMAL, \ + .subsurf_levels = 0, \ + .smooth_factor = 0.05f, \ + .smooth_step = 1, \ + } + /* clang-format off */ diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index b3bcf55c112..1b57acf4a64 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -117,6 +117,7 @@ typedef enum ModifierType { eModifierType_GreasePencilTime = 80, eModifierType_GreasePencilEnvelope = 81, eModifierType_GreasePencilOutline = 82, + eModifierType_GreasePencilShrinkwrap = 83, NUM_MODIFIER_TYPES, } ModifierType; @@ -3273,3 +3274,39 @@ typedef struct GreasePencilOutlineModifierData { typedef enum GreasePencilOutlineModifierFlag { MOD_GREASE_PENCIL_OUTLINE_KEEP_SHAPE = (1 << 0), } GreasePencilOutlineModifierFlag; + +typedef struct GreasePencilShrinkwrapModifierData { + ModifierData modifier; + GreasePencilModifierInfluenceData influence; + + /** Shrink target. */ + struct Object *target; + /** Additional shrink target. */ + struct Object *aux_target; + /** Distance offset to keep from mesh/projection point. */ + float keep_dist; + /** Shrink type projection. */ + short shrink_type; + /** Shrink options. */ + char shrink_opts; + /** Shrink to surface mode. */ + char shrink_mode; + /** Limit the projection ray cast. */ + float proj_limit; + /** Axis to project over. */ + char proj_axis; + + /** + * If using projection over vertex normal this controls the level of subsurface that must be + * done before getting the vertex coordinates and normal. + */ + char subsurf_levels; + char _pad[2]; + /** Factor of smooth. */ + float smooth_factor; + /** How many times apply smooth. */ + int smooth_step; + + /** Runtime only. */ + struct ShrinkwrapTreeData *cache_data; +} GreasePencilShrinkwrapModifierData; diff --git a/source/blender/makesdna/intern/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c index 6e4d38f26db..dd2b17d8971 100644 --- a/source/blender/makesdna/intern/dna_defaults.c +++ b/source/blender/makesdna/intern/dna_defaults.c @@ -351,6 +351,7 @@ SDNA_DEFAULT_DECL_STRUCT(GreasePencilTimeModifierSegment); SDNA_DEFAULT_DECL_STRUCT(GreasePencilTimeModifierData); SDNA_DEFAULT_DECL_STRUCT(GreasePencilEnvelopeModifierData); SDNA_DEFAULT_DECL_STRUCT(GreasePencilOutlineModifierData); +SDNA_DEFAULT_DECL_STRUCT(GreasePencilShrinkwrapModifierData); #undef SDNA_DEFAULT_DECL_STRUCT @@ -621,6 +622,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = { SDNA_DEFAULT_DECL(GreasePencilTimeModifierData), SDNA_DEFAULT_DECL(GreasePencilEnvelopeModifierData), SDNA_DEFAULT_DECL(GreasePencilOutlineModifierData), + SDNA_DEFAULT_DECL(GreasePencilShrinkwrapModifierData), }; #undef SDNA_DEFAULT_DECL #undef SDNA_DEFAULT_DECL_EX diff --git a/source/blender/makesrna/intern/rna_modifier.cc b/source/blender/makesrna/intern/rna_modifier.cc index d3315b3b105..9379da7c8b2 100644 --- a/source/blender/makesrna/intern/rna_modifier.cc +++ b/source/blender/makesrna/intern/rna_modifier.cc @@ -389,6 +389,11 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = { ICON_MOD_ARMATURE, "Armature", "Deform stroke points using armature object"}, + {eModifierType_GreasePencilShrinkwrap, + "GREASE_PENCIL_SHRINKWRAP", + ICON_MOD_SHRINKWRAP, + "Shrinkwrap", + "Project the shape onto another object"}, RNA_ENUM_ITEM_HEADING(N_("Physics"), nullptr), {eModifierType_Cloth, "CLOTH", ICON_MOD_CLOTH, "Cloth", ""}, @@ -1022,6 +1027,8 @@ RNA_MOD_OBJECT_SET(GreasePencilWeightProximity, object, OB_EMPTY); RNA_MOD_OBJECT_SET(GreasePencilHook, object, OB_EMPTY); RNA_MOD_OBJECT_SET(GreasePencilArmature, object, OB_ARMATURE); RNA_MOD_OBJECT_SET(GreasePencilOutline, object, OB_EMPTY); +RNA_MOD_OBJECT_SET(GreasePencilShrinkwrap, target, OB_MESH); +RNA_MOD_OBJECT_SET(GreasePencilShrinkwrap, aux_target, OB_MESH); static void rna_HookModifier_object_set(PointerRNA *ptr, PointerRNA value, @@ -1961,6 +1968,7 @@ RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilWeightProximity); RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilHook); RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilEnvelope); RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilOutline); +RNA_MOD_GREASE_PENCIL_MATERIAL_FILTER_SET(GreasePencilShrinkwrap); RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilOffset); RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilOpacity); @@ -1974,6 +1982,7 @@ RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilWeightProximity); RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilHook); RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilArmature); RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilEnvelope); +RNA_MOD_GREASE_PENCIL_VERTEX_GROUP_SET(GreasePencilShrinkwrap); static void rna_GreasePencilLineartModifier_material_set(PointerRNA *ptr, PointerRNA value, @@ -2184,6 +2193,20 @@ static void rna_GreasePencilOutlineModifier_outline_material_set(PointerRNA *ptr rna_GreasePencilModifier_material_set(ptr, value, reports, &omd->outline_material); } +static int rna_GreasePencilShrinkwrapModifier_face_cull_get(PointerRNA *ptr) +{ + const GreasePencilShrinkwrapModifierData *smd = + static_cast(ptr->data); + return smd->shrink_opts & MOD_SHRINKWRAP_CULL_TARGET_MASK; +} + +static void rna_GreasePencilShrinkwrapModifier_face_cull_set(PointerRNA *ptr, int value) +{ + GreasePencilShrinkwrapModifierData *smd = static_cast( + ptr->data); + smd->shrink_opts = (smd->shrink_opts & ~MOD_SHRINKWRAP_CULL_TARGET_MASK) | value; +} + #else static void rna_def_modifier_panel_open_prop(StructRNA *srna, const char *identifier, const int id) @@ -10271,6 +10294,152 @@ static void rna_def_modifier_grease_pencil_outline(BlenderRNA *brna) RNA_define_lib_overridable(false); } +static void rna_def_modifier_grease_pencil_shrinkwrap(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "GreasePencilShrinkwrapModifier", "Modifier"); + RNA_def_struct_ui_text(srna, + "Shrinkwrap Modifier", + "Shrink wrapping modifier to shrink wrap an object to a target"); + RNA_def_struct_sdna(srna, "GreasePencilShrinkwrapModifierData"); + RNA_def_struct_ui_icon(srna, ICON_MOD_SHRINKWRAP); + + rna_def_modifier_grease_pencil_layer_filter(srna); + rna_def_modifier_grease_pencil_material_filter( + srna, "rna_GreasePencilShrinkwrapModifier_material_filter_set"); + rna_def_modifier_grease_pencil_vertex_group( + srna, "rna_GreasePencilShrinkwrapModifier_vertex_group_name_set"); + + rna_def_modifier_panel_open_prop(srna, "open_influence_panel", 0); + + RNA_define_lib_overridable(true); + + prop = RNA_def_property(srna, "wrap_method", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, nullptr, "shrink_type"); + RNA_def_property_enum_items(prop, rna_enum_shrinkwrap_type_items); + RNA_def_property_ui_text(prop, "Wrap Method", ""); + RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); + + prop = RNA_def_property(srna, "wrap_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, nullptr, "shrink_mode"); + RNA_def_property_enum_items(prop, rna_enum_modifier_shrinkwrap_mode_items); + RNA_def_property_ui_text( + prop, "Snap Mode", "Select how vertices are constrained to the target surface"); + RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); + + prop = RNA_def_property(srna, "cull_face", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, nullptr, "shrink_opts"); + RNA_def_property_enum_items(prop, rna_enum_shrinkwrap_face_cull_items); + RNA_def_property_enum_funcs(prop, + "rna_GreasePencilShrinkwrapModifier_face_cull_get", + "rna_GreasePencilShrinkwrapModifier_face_cull_set", + nullptr); + RNA_def_property_ui_text( + prop, + "Face Cull", + "Stop vertices from projecting to a face on the target when facing towards/away"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "target", PROP_POINTER, PROP_NONE); + RNA_def_property_ui_text(prop, "Target", "Mesh target to shrink to"); + RNA_def_property_pointer_funcs(prop, + nullptr, + "rna_GreasePencilShrinkwrapModifier_target_set", + nullptr, + "rna_Mesh_object_poll"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); + RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); + + prop = RNA_def_property(srna, "auxiliary_target", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, nullptr, "aux_target"); + RNA_def_property_ui_text(prop, "Auxiliary Target", "Additional mesh target to shrink to"); + RNA_def_property_pointer_funcs(prop, + nullptr, + "rna_GreasePencilShrinkwrapModifier_aux_target_set", + nullptr, + "rna_Mesh_object_poll"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); + RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); + + prop = RNA_def_property(srna, "offset", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_float_sdna(prop, nullptr, "keep_dist"); + RNA_def_property_range(prop, -FLT_MAX, FLT_MAX); + RNA_def_property_ui_range(prop, -100, 100, 1, 2); + RNA_def_property_ui_text(prop, "Offset", "Distance to keep from the target"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "project_limit", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_float_sdna(prop, nullptr, "proj_limit"); + RNA_def_property_range(prop, 0.0, FLT_MAX); + RNA_def_property_ui_range(prop, 0, 100, 1, 2); + RNA_def_property_ui_text( + prop, "Project Limit", "Limit the distance used for projection (zero disables)"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_project_x", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "proj_axis", MOD_SHRINKWRAP_PROJECT_OVER_X_AXIS); + RNA_def_property_ui_text(prop, "X", ""); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_project_y", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "proj_axis", MOD_SHRINKWRAP_PROJECT_OVER_Y_AXIS); + RNA_def_property_ui_text(prop, "Y", ""); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_project_z", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "proj_axis", MOD_SHRINKWRAP_PROJECT_OVER_Z_AXIS); + RNA_def_property_ui_text(prop, "Z", ""); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "subsurf_levels", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, nullptr, "subsurf_levels"); + RNA_def_property_range(prop, 0, 6); + RNA_def_property_ui_range(prop, 0, 6, 1, -1); + RNA_def_property_ui_text( + prop, + "Subdivision Levels", + "Number of subdivisions that must be performed before extracting vertices' " + "positions and normals"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_negative_direction", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "shrink_opts", MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR); + RNA_def_property_ui_text( + prop, "Negative", "Allow vertices to move in the negative direction of axis"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_positive_direction", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, nullptr, "shrink_opts", MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR); + RNA_def_property_ui_text( + prop, "Positive", "Allow vertices to move in the positive direction of axis"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_invert_cull", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "shrink_opts", MOD_SHRINKWRAP_INVERT_CULL_TARGET); + RNA_def_property_ui_text( + prop, "Invert Cull", "When projecting in the negative direction invert the face cull mode"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "smooth_factor", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, nullptr, "smooth_factor"); + RNA_def_property_range(prop, 0, 1); + RNA_def_property_ui_text(prop, "Smooth Factor", "Amount of smoothing to apply"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "smooth_step", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, nullptr, "smooth_step"); + RNA_def_property_range(prop, 1, 10); + RNA_def_property_ui_text( + prop, "Steps", "Number of times to apply smooth (high numbers can reduce FPS)"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + RNA_define_lib_overridable(false); +} + void RNA_def_modifier(BlenderRNA *brna) { StructRNA *srna; @@ -10462,6 +10631,7 @@ void RNA_def_modifier(BlenderRNA *brna) rna_def_modifier_grease_pencil_time(brna); rna_def_modifier_grease_pencil_envelope(brna); rna_def_modifier_grease_pencil_outline(brna); + rna_def_modifier_grease_pencil_shrinkwrap(brna); } #endif diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index 9416337984a..5f191b0ca37 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -63,6 +63,7 @@ set(SRC intern/MOD_grease_pencil_offset.cc intern/MOD_grease_pencil_opacity.cc intern/MOD_grease_pencil_outline.cc + intern/MOD_grease_pencil_shrinkwrap.cc intern/MOD_grease_pencil_smooth.cc intern/MOD_grease_pencil_subdiv.cc intern/MOD_grease_pencil_thickness.cc diff --git a/source/blender/modifiers/MOD_modifiertypes.hh b/source/blender/modifiers/MOD_modifiertypes.hh index 265ae56d7a7..4bd949ba47b 100644 --- a/source/blender/modifiers/MOD_modifiertypes.hh +++ b/source/blender/modifiers/MOD_modifiertypes.hh @@ -95,6 +95,7 @@ extern ModifierTypeInfo modifierType_GreasePencilArmature; extern ModifierTypeInfo modifierType_GreasePencilTime; extern ModifierTypeInfo modifierType_GreasePencilEnvelope; extern ModifierTypeInfo modifierType_GreasePencilOutline; +extern ModifierTypeInfo modifierType_GreasePencilShrinkwrap; /* MOD_util.cc */ diff --git a/source/blender/modifiers/intern/MOD_grease_pencil_shrinkwrap.cc b/source/blender/modifiers/intern/MOD_grease_pencil_shrinkwrap.cc new file mode 100644 index 00000000000..8c736370875 --- /dev/null +++ b/source/blender/modifiers/intern/MOD_grease_pencil_shrinkwrap.cc @@ -0,0 +1,365 @@ +/* SPDX-FileCopyrightText: 2024 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup modifiers + */ + +#include "BKE_attribute.hh" +#include "BKE_material.h" + +#include "DNA_defaults.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_scene_types.h" + +#include "BKE_curves.hh" +#include "BKE_deform.hh" +#include "BKE_geometry_set.hh" +#include "BKE_grease_pencil.hh" +#include "BKE_instances.hh" +#include "BKE_lib_query.hh" +#include "BKE_modifier.hh" +#include "BKE_screen.hh" +#include "BKE_shrinkwrap.hh" + +#include "BLO_read_write.hh" + +#include "DEG_depsgraph_query.hh" + +#include "GEO_smooth_curves.hh" + +#include "UI_interface.hh" +#include "UI_resources.hh" + +#include "BLT_translation.hh" + +#include "WM_api.hh" +#include "WM_types.hh" + +#include "RNA_access.hh" +#include "RNA_prototypes.h" + +#include "MOD_grease_pencil_util.hh" +#include "MOD_ui_common.hh" + +namespace blender { + +static void init_data(ModifierData *md) +{ + auto *smd = reinterpret_cast(md); + + BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(smd, modifier)); + + MEMCPY_STRUCT_AFTER(smd, DNA_struct_default_get(GreasePencilShrinkwrapModifierData), modifier); + modifier::greasepencil::init_influence_data(&smd->influence, false); +} + +static void copy_data(const ModifierData *md, ModifierData *target, const int flag) +{ + const auto *smd = reinterpret_cast(md); + auto *tsmd = reinterpret_cast(target); + + modifier::greasepencil::free_influence_data(&tsmd->influence); + + BKE_modifier_copydata_generic(md, target, flag); + modifier::greasepencil::copy_influence_data(&smd->influence, &tsmd->influence, flag); +} + +static void free_data(ModifierData *md) +{ + auto *smd = reinterpret_cast(md); + modifier::greasepencil::free_influence_data(&smd->influence); + + if (smd->cache_data) { + BKE_shrinkwrap_free_tree(smd->cache_data); + MEM_SAFE_FREE(smd->cache_data); + } +} + +static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data) +{ + auto *smd = reinterpret_cast(md); + modifier::greasepencil::foreach_influence_ID_link(&smd->influence, ob, walk, user_data); + walk(user_data, ob, (ID **)&smd->target, IDWALK_CB_NOP); + walk(user_data, ob, (ID **)&smd->aux_target, IDWALK_CB_NOP); +} + +static bool is_disabled(const Scene * /*scene*/, ModifierData *md, bool /*use_render_params*/) +{ + auto *smd = reinterpret_cast(md); + + /* The object type check is only needed here in case we have a placeholder + * object assigned (because the library containing the mesh is missing). + * + * In other cases it should be impossible to have a type mismatch. + */ + if (smd->target == nullptr || smd->target->type != OB_MESH) { + return true; + } + if (smd->aux_target != nullptr && smd->aux_target->type != OB_MESH) { + return true; + } + return false; +} + +static void update_depsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx) +{ + auto *smd = reinterpret_cast(md); + CustomData_MeshMasks mask = {0}; + + if (BKE_shrinkwrap_needs_normals(smd->shrink_type, smd->shrink_mode)) { + mask.lmask |= CD_MASK_CUSTOMLOOPNORMAL; + } + + if (smd->target != nullptr) { + DEG_add_object_relation( + ctx->node, smd->target, DEG_OB_COMP_TRANSFORM, "Grease Pencil Shrinkwrap Modifier"); + DEG_add_object_relation( + ctx->node, smd->target, DEG_OB_COMP_GEOMETRY, "Grease Pencil Shrinkwrap Modifier"); + DEG_add_customdata_mask(ctx->node, smd->target, &mask); + if (smd->shrink_type == MOD_SHRINKWRAP_TARGET_PROJECT) { + DEG_add_special_eval_flag(ctx->node, &smd->target->id, DAG_EVAL_NEED_SHRINKWRAP_BOUNDARY); + } + } + if (smd->aux_target != nullptr) { + DEG_add_object_relation( + ctx->node, smd->aux_target, DEG_OB_COMP_TRANSFORM, "Grease Pencil Shrinkwrap Modifier"); + DEG_add_object_relation( + ctx->node, smd->aux_target, DEG_OB_COMP_GEOMETRY, "Grease Pencil Shrinkwrap Modifier"); + DEG_add_customdata_mask(ctx->node, smd->aux_target, &mask); + if (smd->shrink_type == MOD_SHRINKWRAP_TARGET_PROJECT) { + DEG_add_special_eval_flag( + ctx->node, &smd->aux_target->id, DAG_EVAL_NEED_SHRINKWRAP_BOUNDARY); + } + } + DEG_add_depends_on_transform_relation(ctx->node, "Grease Pencil Shrinkwrap Modifier"); +} + +static void modify_drawing(const GreasePencilShrinkwrapModifierData &smd, + const ModifierEvalContext &ctx, + bke::greasepencil::Drawing &drawing) +{ + bke::CurvesGeometry &curves = drawing.strokes_for_write(); + const OffsetIndices points_by_curve = curves.points_by_curve(); + const Span dverts = curves.deform_verts(); + const MutableSpan positions = curves.positions_for_write(); + const int defgrp_idx = BKE_object_defgroup_name_index(ctx.object, + smd.influence.vertex_group_name); + + /* Selected source curves. */ + IndexMaskMemory curve_mask_memory; + const IndexMask curves_mask = modifier::greasepencil::get_filtered_stroke_mask( + ctx.object, drawing.strokes(), smd.influence, curve_mask_memory); + + ShrinkwrapParams params; + params.target = smd.target; + params.aux_target = smd.aux_target; + params.invert_vertex_weights = smd.influence.flag & GREASE_PENCIL_INFLUENCE_INVERT_VERTEX_GROUP; + params.keep_distance = smd.keep_dist; + params.shrink_type = smd.shrink_type; + params.shrink_options = smd.shrink_opts; + params.shrink_mode = smd.shrink_mode; + params.projection_limit = smd.proj_limit; + params.projection_axis = smd.proj_axis; + params.subsurf_levels = smd.subsurf_levels; + + curves_mask.foreach_index([&](const int curve_i) { + const IndexRange points = points_by_curve[curve_i]; + const Span curve_dverts = dverts.is_empty() ? dverts : dverts.slice(points); + const MutableSpan curve_positions = positions.slice(points); + + shrinkwrapParams_deform( + params, *ctx.object, *smd.cache_data, curve_dverts, defgrp_idx, curve_positions); + }); + + /* Optional smoothing after shrinkwrap. */ + const VArray point_selection = VArray::ForSingle(true, curves.points_num()); + const bool smooth_ends = false; + const bool keep_shape = true; + geometry::smooth_curve_attribute(curves_mask, + points_by_curve, + point_selection, + curves.cyclic(), + smd.smooth_step, + smd.smooth_factor, + smooth_ends, + keep_shape, + positions); + + drawing.tag_positions_changed(); +} + +static void ensure_shrinkwrap_cache_data(GreasePencilShrinkwrapModifierData &smd, + const ModifierEvalContext &ctx) +{ + if (smd.cache_data) { + BKE_shrinkwrap_free_tree(smd.cache_data); + MEM_SAFE_FREE(smd.cache_data); + } + Object *target_ob = DEG_get_evaluated_object(ctx.depsgraph, smd.target); + Mesh *target_mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(target_ob); + + smd.cache_data = static_cast( + MEM_callocN(sizeof(ShrinkwrapTreeData), __func__)); + const bool tree_ok = BKE_shrinkwrap_init_tree( + smd.cache_data, target_mesh, smd.shrink_type, smd.shrink_mode, false); + if (!tree_ok) { + MEM_SAFE_FREE(smd.cache_data); + } +} + +static void modify_geometry_set(ModifierData *md, + const ModifierEvalContext *ctx, + bke::GeometrySet *geometry_set) +{ + using bke::greasepencil::Drawing; + using bke::greasepencil::Layer; + using modifier::greasepencil::LayerDrawingInfo; + + auto &smd = *reinterpret_cast(md); + BLI_assert(smd.target != nullptr); + if (smd.target == ctx->object || smd.aux_target == ctx->object) { + return; + } + if (!geometry_set->has_grease_pencil()) { + return; + } + GreasePencil &grease_pencil = *geometry_set->get_grease_pencil_for_write(); + const int frame = grease_pencil.runtime->eval_frame; + + ensure_shrinkwrap_cache_data(smd, *ctx); + + IndexMaskMemory mask_memory; + const IndexMask layer_mask = modifier::greasepencil::get_filtered_layer_mask( + grease_pencil, smd.influence, mask_memory); + + const Vector drawings = modifier::greasepencil::get_drawings_for_write( + grease_pencil, layer_mask, frame); + threading::parallel_for_each(drawings, + [&](Drawing *drawing) { modify_drawing(smd, *ctx, *drawing); }); +} + +static void panel_draw(const bContext *C, Panel *panel) +{ + static const eUI_Item_Flag toggles_flag = UI_ITEM_R_TOGGLE | UI_ITEM_R_FORCE_BLANK_DECORATE; + + uiLayout *layout = panel->layout; + + PointerRNA ob_ptr; + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); + int wrap_method = RNA_enum_get(ptr, "wrap_method"); + uiLayout *col, *row; + + uiLayoutSetPropSep(layout, true); + + uiItemR(layout, ptr, "wrap_method", UI_ITEM_NONE, nullptr, ICON_NONE); + + if (ELEM(wrap_method, + MOD_SHRINKWRAP_PROJECT, + MOD_SHRINKWRAP_NEAREST_SURFACE, + MOD_SHRINKWRAP_TARGET_PROJECT)) + { + uiItemR(layout, ptr, "wrap_mode", UI_ITEM_NONE, nullptr, ICON_NONE); + } + + if (wrap_method == MOD_SHRINKWRAP_PROJECT) { + uiItemR(layout, ptr, "project_limit", UI_ITEM_NONE, IFACE_("Limit"), ICON_NONE); + uiItemR(layout, ptr, "subsurf_levels", UI_ITEM_NONE, nullptr, ICON_NONE); + + col = uiLayoutColumn(layout, false); + row = uiLayoutRowWithHeading(col, true, IFACE_("Axis")); + uiItemR(row, ptr, "use_project_x", toggles_flag, nullptr, ICON_NONE); + uiItemR(row, ptr, "use_project_y", toggles_flag, nullptr, ICON_NONE); + uiItemR(row, ptr, "use_project_z", toggles_flag, nullptr, ICON_NONE); + + uiItemR(col, ptr, "use_negative_direction", UI_ITEM_NONE, nullptr, ICON_NONE); + uiItemR(col, ptr, "use_positive_direction", UI_ITEM_NONE, nullptr, ICON_NONE); + + uiItemR(layout, ptr, "cull_face", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + col = uiLayoutColumn(layout, false); + uiLayoutSetActive(col, + RNA_boolean_get(ptr, "use_negative_direction") && + RNA_enum_get(ptr, "cull_face") != 0); + uiItemR(col, ptr, "use_invert_cull", UI_ITEM_NONE, nullptr, ICON_NONE); + } + + uiItemR(layout, ptr, "target", UI_ITEM_NONE, nullptr, ICON_NONE); + if (wrap_method == MOD_SHRINKWRAP_PROJECT) { + uiItemR(layout, ptr, "auxiliary_target", UI_ITEM_NONE, nullptr, ICON_NONE); + } + uiItemR(layout, ptr, "offset", UI_ITEM_NONE, nullptr, ICON_NONE); + + uiLayoutSetPropSep(layout, true); + + uiItemR(layout, ptr, "smooth_factor", UI_ITEM_NONE, nullptr, ICON_NONE); + uiItemR(layout, ptr, "smooth_step", UI_ITEM_NONE, IFACE_("Repeat"), ICON_NONE); + + if (uiLayout *influence_panel = uiLayoutPanelProp( + C, layout, ptr, "open_influence_panel", "Influence")) + { + modifier::greasepencil::draw_layer_filter_settings(C, influence_panel, ptr); + modifier::greasepencil::draw_material_filter_settings(C, influence_panel, ptr); + modifier::greasepencil::draw_vertex_group_settings(C, influence_panel, ptr); + } + + modifier_panel_end(layout, ptr); +} + +static void panel_register(ARegionType *region_type) +{ + modifier_panel_register(region_type, eModifierType_GreasePencilShrinkwrap, panel_draw); +} + +static void blend_write(BlendWriter *writer, const ID * /*id_owner*/, const ModifierData *md) +{ + const auto *smd = reinterpret_cast(md); + + BLO_write_struct(writer, GreasePencilShrinkwrapModifierData, smd); + modifier::greasepencil::write_influence_data(writer, &smd->influence); +} + +static void blend_read(BlendDataReader *reader, ModifierData *md) +{ + auto *smd = reinterpret_cast(md); + + modifier::greasepencil::read_influence_data(reader, &smd->influence); +} + +} // namespace blender + +ModifierTypeInfo modifierType_GreasePencilShrinkwrap = { + /*idname*/ "GreasePencilShrinkwrap", + /*name*/ N_("Shrinkwrap"), + /*struct_name*/ "GreasePencilShrinkwrapModifierData", + /*struct_size*/ sizeof(GreasePencilShrinkwrapModifierData), + /*srna*/ &RNA_GreasePencilShrinkwrapModifier, + /*type*/ ModifierTypeType::OnlyDeform, + /*flags*/ eModifierTypeFlag_AcceptsGreasePencil | eModifierTypeFlag_SupportsEditmode | + eModifierTypeFlag_EnableInEditmode | eModifierTypeFlag_SupportsMapping, + /*icon*/ ICON_MOD_SHRINKWRAP, + + /*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*/ blender::free_data, + /*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*/ blender::blend_read, +}; diff --git a/source/blender/modifiers/intern/MOD_util.cc b/source/blender/modifiers/intern/MOD_util.cc index 7e2c680f213..85766fa5894 100644 --- a/source/blender/modifiers/intern/MOD_util.cc +++ b/source/blender/modifiers/intern/MOD_util.cc @@ -286,5 +286,6 @@ void modifier_type_init(ModifierTypeInfo *types[]) INIT_TYPE(GreasePencilTime); INIT_TYPE(GreasePencilEnvelope); INIT_TYPE(GreasePencilOutline); + INIT_TYPE(GreasePencilShrinkwrap); #undef INIT_TYPE }