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
This commit is contained in:
Alexander Gavrilov 2018-07-07 18:39:45 +03:00
parent 310f1b0579
commit 3378782eee
11 changed files with 171 additions and 33 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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),
};

View File

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

View File

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

View File

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