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
This commit is contained in:
Lukas Tönne
2024-03-06 12:05:00 +01:00
parent 8178bffd64
commit f51e93bec7
12 changed files with 722 additions and 1 deletions

View File

@@ -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)

View File

@@ -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 &params,
Object &object,
ShrinkwrapTreeData &tree,
blender::Span<MDeformVert> dvert,
int defgrp_index,
blender::MutableSpan<blender::float3> positions);
/**
* Used in `editmesh_mask_extract.cc` to shrink-wrap the extracted mesh to the sculpt.
*/

View File

@@ -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<GreasePencilShrinkwrapModifierData &>(md);
auto &legacy_md_shrinkwrap = reinterpret_cast<ShrinkwrapGpencilModifierData &>(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;
}

View File

@@ -1518,6 +1518,56 @@ void shrinkwrapGpencilModifier_deform(ShrinkwrapGpencilModifierData *mmd,
}
}
void shrinkwrapParams_deform(const ShrinkwrapParams &params,
Object &object,
ShrinkwrapTreeData &tree,
const blender::Span<MDeformVert> dvert,
const int defgrp_index,
const blender::MutableSpan<blender::float3> 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<float(*)[3]>(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,

View File

@@ -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 */

View File

@@ -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;

View File

@@ -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

View File

@@ -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<GreasePencilShrinkwrapModifierData *>(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<GreasePencilShrinkwrapModifierData *>(
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

View File

@@ -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

View File

@@ -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 */

View File

@@ -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<GreasePencilShrinkwrapModifierData *>(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<const GreasePencilShrinkwrapModifierData *>(md);
auto *tsmd = reinterpret_cast<GreasePencilShrinkwrapModifierData *>(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<GreasePencilShrinkwrapModifierData *>(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<GreasePencilShrinkwrapModifierData *>(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<GreasePencilShrinkwrapModifierData *>(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<GreasePencilShrinkwrapModifierData *>(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<int> points_by_curve = curves.points_by_curve();
const Span<MDeformVert> dverts = curves.deform_verts();
const MutableSpan<float3> 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<MDeformVert> curve_dverts = dverts.is_empty() ? dverts : dverts.slice(points);
const MutableSpan<float3> 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<bool> point_selection = VArray<bool>::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<ShrinkwrapTreeData *>(
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<GreasePencilShrinkwrapModifierData *>(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<Drawing *> 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<const GreasePencilShrinkwrapModifierData *>(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<GreasePencilShrinkwrapModifierData *>(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,
};

View File

@@ -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
}