From 3378782eeeb55a6ed64dba479e37f8b98fab63b1 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sat, 7 Jul 2018 18:39:45 +0300 Subject: [PATCH] Implement additional modes for Shrinkwrap to a surface. In addition to the original map to surface and Keep Above Surface, add modes that only affect vertices that are inside or outside the object. This is inspired by the Limit Distance constraint, and can be useful for crude collision detection in rigs. The inside/outside test works based on face normals and may not be completely reliable near 90 degree or sharper angles in the target. Reviewers: campbellbarton, mont29 Differential Revision: https://developer.blender.org/D3717 --- .../startup/bl_ui/properties_constraint.py | 3 + .../startup/bl_ui/properties_data_modifier.py | 6 +- source/blender/blenkernel/BKE_shrinkwrap.h | 5 + source/blender/blenkernel/intern/constraint.c | 24 +++-- source/blender/blenkernel/intern/shrinkwrap.c | 93 +++++++++++++++---- .../blenloader/intern/versioning_280.c | 16 ++++ .../blender/makesdna/DNA_constraint_types.h | 3 +- source/blender/makesdna/DNA_modifier_types.h | 18 +++- source/blender/makesrna/RNA_enum_types.h | 1 + .../blender/makesrna/intern/rna_constraint.c | 6 ++ source/blender/makesrna/intern/rna_modifier.c | 29 +++++- 11 files changed, 171 insertions(+), 33 deletions(-) diff --git a/release/scripts/startup/bl_ui/properties_constraint.py b/release/scripts/startup/bl_ui/properties_constraint.py index 76b87a9011d..c76f3255d97 100644 --- a/release/scripts/startup/bl_ui/properties_constraint.py +++ b/release/scripts/startup/bl_ui/properties_constraint.py @@ -748,6 +748,9 @@ class ConstraintButtonsPanel: layout.prop(con, "distance") layout.prop(con, "shrinkwrap_type") + if con.shrinkwrap_type in {'PROJECT', 'NEAREST_SURFACE'}: + layout.prop(con, 'wrap_mode', text="Snap Mode") + if con.shrinkwrap_type == 'PROJECT': row = layout.row(align=True) row.prop(con, "project_axis", expand=True) diff --git a/release/scripts/startup/bl_ui/properties_data_modifier.py b/release/scripts/startup/bl_ui/properties_data_modifier.py index 8833db28056..803abe98055 100644 --- a/release/scripts/startup/bl_ui/properties_data_modifier.py +++ b/release/scripts/startup/bl_ui/properties_data_modifier.py @@ -825,6 +825,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): col.label(text="Mode:") col.prop(md, "wrap_method", text="") + if md.wrap_method in {'PROJECT', 'NEAREST_SURFACEPOINT'}: + col.prop(md, "wrap_mode", text="") + if md.wrap_method == 'PROJECT': split = layout.split() col = split.column() @@ -851,9 +854,6 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): layout.prop(md, "auxiliary_target") - elif md.wrap_method == 'NEAREST_SURFACEPOINT': - layout.prop(md, "use_keep_above_surface") - def SIMPLE_DEFORM(self, layout, ob, md): layout.row().prop(md, "deform_method", expand=True) diff --git a/source/blender/blenkernel/BKE_shrinkwrap.h b/source/blender/blenkernel/BKE_shrinkwrap.h index d4df12783fa..40f3808b94b 100644 --- a/source/blender/blenkernel/BKE_shrinkwrap.h +++ b/source/blender/blenkernel/BKE_shrinkwrap.h @@ -94,6 +94,11 @@ bool BKE_shrinkwrap_project_normal( const struct SpaceTransform *transf, BVHTree *tree, BVHTreeRayHit *hit, BVHTree_RayCastCallback callback, void *userdata); +/* Apply the shrink to surface modes to the given original coordinates and nearest point. */ +void BKE_shrinkwrap_snap_point_to_surface( + int mode, const float hit_co[3], const float hit_no[3], float goal_dist, + const float point_co[3], float r_point_co[3]); + /* * NULL initializers to local data */ diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 3ea2f4e0ee4..b1a7c98dc9c 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -3456,7 +3456,6 @@ static void shrinkwrap_get_tarmat(struct Depsgraph *depsgraph, bConstraint *con, case MOD_SHRINKWRAP_NEAREST_VERTEX: { BVHTreeNearest nearest; - float dist; nearest.index = -1; nearest.dist_sq = FLT_MAX; @@ -3475,10 +3474,22 @@ static void shrinkwrap_get_tarmat(struct Depsgraph *depsgraph, bConstraint *con, BLI_bvhtree_find_nearest(treeData.tree, co, &nearest, treeData.nearest_callback, &treeData); - dist = len_v3v3(co, nearest.co); - if (dist != 0.0f) { - interp_v3_v3v3(co, co, nearest.co, (dist - scon->dist) / dist); /* linear interpolation */ + if (nearest.index < 0) { + fail = true; + break; } + + if (scon->shrinkType == MOD_SHRINKWRAP_NEAREST_SURFACE) { + BKE_shrinkwrap_snap_point_to_surface(scon->shrinkMode, nearest.co, nearest.no, scon->dist, co, co); + } + else { + const float dist = len_v3v3(co, nearest.co); + + if (dist != 0.0f) { + interp_v3_v3v3(co, co, nearest.co, (dist - scon->dist) / dist); /* linear interpolation */ + } + } + BLI_space_transform_invert(&transform, co); break; } @@ -3522,13 +3533,14 @@ static void shrinkwrap_get_tarmat(struct Depsgraph *depsgraph, bConstraint *con, break; } - if (BKE_shrinkwrap_project_normal(0, co, no, scon->dist, &transform, treeData.tree, + if (BKE_shrinkwrap_project_normal(0, co, no, 0.0f, &transform, treeData.tree, &hit, treeData.raycast_callback, &treeData) == false) { fail = true; break; } - copy_v3_v3(co, hit.co); + + BKE_shrinkwrap_snap_point_to_surface(scon->shrinkMode, hit.co, hit.no, scon->dist, co, co); break; } } diff --git a/source/blender/blenkernel/intern/shrinkwrap.c b/source/blender/blenkernel/intern/shrinkwrap.c index b5888856250..21b21d6ddd8 100644 --- a/source/blender/blenkernel/intern/shrinkwrap.c +++ b/source/blender/blenkernel/intern/shrinkwrap.c @@ -363,7 +363,7 @@ static void shrinkwrap_calc_normal_projection_cb_ex( } if (hit->index != -1) { - madd_v3_v3v3fl(hit->co, hit->co, tmp_no, calc->keepDist); + BKE_shrinkwrap_snap_point_to_surface(calc->smd->shrinkMode, hit->co, hit->no, calc->keepDist, tmp_co, hit->co); interp_v3_v3v3(co, co, hit->co, weight); } } @@ -541,22 +541,7 @@ static void shrinkwrap_calc_nearest_surface_point_cb_ex( /* Found the nearest vertex */ if (nearest->index != -1) { - if (calc->smd->shrinkOpts & MOD_SHRINKWRAP_KEEP_ABOVE_SURFACE) { - /* Make the vertex stay on the front side of the face */ - madd_v3_v3v3fl(tmp_co, nearest->co, nearest->no, calc->keepDist); - } - else { - /* Adjusting the vertex weight, - * so that after interpolating it keeps a certain distance from the nearest position */ - const float dist = sasqrt(nearest->dist_sq); - if (dist > FLT_EPSILON) { - /* linear interpolation */ - interp_v3_v3v3(tmp_co, tmp_co, nearest->co, (dist - calc->keepDist) / dist); - } - else { - copy_v3_v3(tmp_co, nearest->co); - } - } + BKE_shrinkwrap_snap_point_to_surface(calc->smd->shrinkMode, nearest->co, nearest->no, calc->keepDist, tmp_co, tmp_co); /* Convert the coordinates back to mesh coordinates */ BLI_space_transform_invert(&calc->local2target, tmp_co); @@ -564,6 +549,80 @@ static void shrinkwrap_calc_nearest_surface_point_cb_ex( } } +/* Helper for MOD_SHRINKWRAP_INSIDE, MOD_SHRINKWRAP_OUTSIDE and MOD_SHRINKWRAP_OUTSIDE_SURFACE. */ +static void shrinkwrap_snap_with_side(float r_point_co[3], const float point_co[3], const float hit_co[3], const float hit_no[3], float goal_dist, float forcesign, bool forcesnap) +{ + float dist = len_v3v3(point_co, hit_co); + + /* If exactly on the surface, push out along normal */ + if (dist < FLT_EPSILON) { + madd_v3_v3v3fl(r_point_co, hit_co, hit_no, goal_dist * forcesign); + } + /* Move to the correct side if needed */ + else { + float delta[3]; + sub_v3_v3v3(delta, point_co, hit_co); + float dsign = signf(dot_v3v3(delta, hit_no)); + + /* If on the wrong side or too close, move to correct */ + if (forcesnap || dsign * forcesign < 0 || dist < goal_dist) { + interp_v3_v3v3(r_point_co, point_co, hit_co, (dist - goal_dist * dsign * forcesign) / dist); + } + else { + copy_v3_v3(r_point_co, point_co); + } + } +} + +/** + * Apply the shrink to surface modes to the given original coordinates and nearest point. + * r_point_co may be the same memory location as point_co, hit_co, or hit_no. + */ +void BKE_shrinkwrap_snap_point_to_surface( + int mode, const float hit_co[3], const float hit_no[3], float goal_dist, + const float point_co[3], float r_point_co[3]) +{ + float dist; + + switch (mode) { + /* Offsets along the line between point_co and hit_co. */ + case MOD_SHRINKWRAP_ON_SURFACE: + if (goal_dist > 0 && (dist = len_v3v3(point_co, hit_co)) > FLT_EPSILON) { + interp_v3_v3v3(r_point_co, point_co, hit_co, (dist - goal_dist) / dist); + } + else { + copy_v3_v3(r_point_co, hit_co); + } + break; + + case MOD_SHRINKWRAP_INSIDE: + shrinkwrap_snap_with_side(r_point_co, point_co, hit_co, hit_no, goal_dist, -1, false); + break; + + case MOD_SHRINKWRAP_OUTSIDE: + shrinkwrap_snap_with_side(r_point_co, point_co, hit_co, hit_no, goal_dist, +1, false); + break; + + case MOD_SHRINKWRAP_OUTSIDE_SURFACE: + if (goal_dist > 0) { + shrinkwrap_snap_with_side(r_point_co, point_co, hit_co, hit_no, goal_dist, +1, true); + } + else { + copy_v3_v3(r_point_co, hit_co); + } + break; + + /* Offsets along the normal */ + case MOD_SHRINKWRAP_ABOVE_SURFACE: + madd_v3_v3v3fl(r_point_co, hit_co, hit_no, goal_dist); + break; + + default: + printf("Unknown Shrinkwrap surface snap mode: %d\n", mode); + copy_v3_v3(r_point_co, hit_co); + } +} + static void shrinkwrap_calc_nearest_surface_point(ShrinkwrapCalcData *calc) { BVHTreeFromMesh treeData = NULL_BVHTreeFromMesh; diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 6b74e4f2239..4ec283c35ca 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -2074,4 +2074,20 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + { + if (!DNA_struct_elem_find(fd->filesdna, "ShrinkwrapModifierData", "char", "shrinkMode")) { + for (Object *ob = bmain->object.first; ob; ob = ob->id.next) { + for (ModifierData *md = ob->modifiers.first; md; md = md->next) { + if (md->type == eModifierType_Shrinkwrap) { + ShrinkwrapModifierData *smd = (ShrinkwrapModifierData*)md; + if (smd->shrinkOpts & MOD_SHRINKWRAP_KEEP_ABOVE_SURFACE) { + smd->shrinkMode = MOD_SHRINKWRAP_ABOVE_SURFACE; + smd->shrinkOpts &= ~MOD_SHRINKWRAP_KEEP_ABOVE_SURFACE; + } + } + } + } + } + } } diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h index 4f3f3891240..e7d9e1fa7dd 100644 --- a/source/blender/makesdna/DNA_constraint_types.h +++ b/source/blender/makesdna/DNA_constraint_types.h @@ -428,7 +428,8 @@ typedef struct bShrinkwrapConstraint { char projAxis; /* axis to project/constrain */ char projAxisSpace; /* space to project axis in */ float projLimit; /* distance to search */ - char pad[4]; + char shrinkMode; /* inside/outside/on surface (see MOD shrinkwrap) */ + char pad[3]; } bShrinkwrapConstraint; /* Follow Track constraints */ diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 247b08475a1..811373e626c 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -870,7 +870,7 @@ typedef struct ShrinkwrapModifierData { float keepDist; /* distance offset to keep from mesh/projection point */ short shrinkType; /* shrink type projection */ char shrinkOpts; /* shrink options */ - char pad1; + char shrinkMode; /* shrink to surface mode */ float projLimit; /* limit the projection ray cast */ char projAxis; /* axis to project over */ @@ -889,6 +889,20 @@ enum { MOD_SHRINKWRAP_NEAREST_VERTEX = 2, }; +/* Shrinkwrap->shrinkMode */ +enum { + /* Move vertex to the surface of the target object (keepDist towards original position) */ + MOD_SHRINKWRAP_ON_SURFACE = 0, + /* Move the vertex inside the target object; don't change if already inside */ + MOD_SHRINKWRAP_INSIDE = 1, + /* Move the vertex outside the target object; don't change if already outside */ + MOD_SHRINKWRAP_OUTSIDE = 2, + /* Move vertex to the surface of the target object, with keepDist towards the outside */ + MOD_SHRINKWRAP_OUTSIDE_SURFACE = 3, + /* Move vertex to the surface of the target object, with keepDist along the normal */ + MOD_SHRINKWRAP_ABOVE_SURFACE = 4, +}; + /* Shrinkwrap->shrinkOpts */ enum { /* allow shrinkwrap to move the vertex in the positive direction of axis */ @@ -901,7 +915,9 @@ enum { /* ignore vertex moves if a vertex ends projected on a back face of the target */ MOD_SHRINKWRAP_CULL_TARGET_BACKFACE = (1 << 4), +#ifdef DNA_DEPRECATED_ALLOW MOD_SHRINKWRAP_KEEP_ABOVE_SURFACE = (1 << 5), /* distance is measure to the front face of the target */ +#endif MOD_SHRINKWRAP_INVERT_VGROUP = (1 << 6), }; diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h index 56b82c6c2f9..935af5c8bbd 100644 --- a/source/blender/makesrna/RNA_enum_types.h +++ b/source/blender/makesrna/RNA_enum_types.h @@ -72,6 +72,7 @@ extern const EnumPropertyItem rna_enum_object_shaderfx_type_items[]; extern const EnumPropertyItem rna_enum_modifier_triangulate_quad_method_items[]; extern const EnumPropertyItem rna_enum_modifier_triangulate_ngon_method_items[]; +extern const EnumPropertyItem rna_enum_modifier_shrinkwrap_mode_items[]; extern const EnumPropertyItem rna_enum_image_type_items[]; extern const EnumPropertyItem rna_enum_image_color_mode_items[]; diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c index 52c6bc105fd..39966983a8a 100644 --- a/source/blender/makesrna/intern/rna_constraint.c +++ b/source/blender/makesrna/intern/rna_constraint.c @@ -1934,6 +1934,12 @@ static void rna_def_constraint_shrinkwrap(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Shrinkwrap Type", "Select type of shrinkwrap algorithm for target position"); RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + prop = RNA_def_property(srna, "wrap_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "shrinkMode"); + RNA_def_property_enum_items(prop, rna_enum_modifier_shrinkwrap_mode_items); + RNA_def_property_ui_text(prop, "Snap Mode", "Select how to constrain the object to the target surface"); + RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + prop = RNA_def_property(srna, "distance", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_float_sdna(prop, NULL, "dist"); RNA_def_property_range(prop, 0.0f, FLT_MAX); diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 2b0f22eb8fc..1755a37016a 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -140,6 +140,24 @@ const EnumPropertyItem rna_enum_modifier_triangulate_ngon_method_items[] = { {0, NULL, 0, NULL, NULL} }; +const EnumPropertyItem rna_enum_modifier_shrinkwrap_mode_items[] = { + {MOD_SHRINKWRAP_ON_SURFACE, "ON_SURFACE", 0, "On Surface", + "The point is constrained to the surface of the target object, " + "with distance offset towards the original point location"}, + {MOD_SHRINKWRAP_INSIDE, "INSIDE", 0, "Inside", + "The point is constrained to be inside the target object"}, + {MOD_SHRINKWRAP_OUTSIDE, "OUTSIDE", 0, "Outside", + "The point is constrained to be outside the target object"}, + {MOD_SHRINKWRAP_OUTSIDE_SURFACE, "OUTSIDE_SURFACE", 0, "Outside Surface", + "The point is constrained to the surface of the target object, " + "with distance offset always to the outside, towards or away from the original location"}, + {MOD_SHRINKWRAP_ABOVE_SURFACE, "ABOVE_SURFACE", 0, "Above Surface", + "The point is constrained to the surface of the target object, " + "with distance offset applied exactly along the target normal"}, + {0, NULL, 0, NULL, NULL} +}; + + #ifndef RNA_RUNTIME /* use eWarp_Falloff_*** & eHook_Falloff_***, they're in sync */ static const EnumPropertyItem modifier_warp_falloff_items[] = { @@ -3173,6 +3191,12 @@ static void rna_def_modifier_shrinkwrap(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Mode", ""); RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "wrap_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "shrinkMode"); + 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_update"); + prop = RNA_def_property(srna, "cull_face", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "shrinkOpts"); RNA_def_property_enum_items(prop, shrink_face_cull_items); @@ -3249,11 +3273,6 @@ static void rna_def_modifier_shrinkwrap(BlenderRNA *brna) 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_keep_above_surface", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "shrinkOpts", MOD_SHRINKWRAP_KEEP_ABOVE_SURFACE); - RNA_def_property_ui_text(prop, "Keep Above Surface", ""); - RNA_def_property_update(prop, 0, "rna_Modifier_update"); - prop = RNA_def_property(srna, "invert_vertex_group", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "shrinkOpts", MOD_SHRINKWRAP_INVERT_VGROUP); RNA_def_property_ui_text(prop, "Invert", "Invert vertex group influence");