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");