So I tried to revisit this small project, and I found a few things:
-
The scale issue does indeed apply to all modes, not just stretch. For example, the amount of rotation for the twist and bend modes scales with the size of the object, so a 45 degree rotation for a cube with radius 3 will result in a 135 degree twist at each end. However this seems like it’s possible it could be intentional, so if anyone knows if this is the intended behavior or not, let me know.
-
Changing the limits also affects the transform in an unintuitive way. The factor value is scaled according to the limit range when it doesn’t really make sense for it to be. I have modified the code to remove this dependency.
I reworked the stretch formula a bit, this time the equation ensures that for the ideal case of a cylinder or cuboid, volume is preserved. The same squash/stretch style behavior is still present. Note that the code assumes that the first observation was in fact unintentional, so it includes code to remap the coordinates along the limit axis to the range [-1,1].
diff --git a/source/blender/modifiers/intern/MOD_simpledeform.c b/source/blender/modifiers/intern/MOD_simpledeform.c
index ec89176f97e..19ca1611db4 100644
--- a/source/blender/modifiers/intern/MOD_simpledeform.c
+++ b/source/blender/modifiers/intern/MOD_simpledeform.c
@@ -97,13 +97,23 @@ static void axis_limit(const int axis, const float limits[2], float co[3], float
co[axis] = val;
}
+/* Re-maps from: mesh_range[0] <= co <= mesh_range[1]
+ to: -1 <= return <= 1.
+ This prevents issues where the effecive angle/scale is affected by the object size */
+static float remap_limit_axis(const float co, const float mesh_range[2])
+{
+ return (2.0 * co - mesh_range[0] - mesh_range[1]) / (mesh_range[1] - mesh_range[0]);
+}
+
static void simpleDeform_taper(const float factor,
const int UNUSED(axis),
const float dcut[3],
- float r_co[3])
+ float r_co[3],
+ float mesh_range[2])
{
float x = r_co[0], y = r_co[1], z = r_co[2];
- float scale = z * factor;
+ float t = remap_limit_axis(z, mesh_range);
+ float scale = t * factor;
r_co[0] = x + x * scale;
r_co[1] = y + y * scale;
@@ -115,12 +125,12 @@ static void simpleDeform_taper(const float factor,
static void simpleDeform_stretch(const float factor,
const int UNUSED(axis),
const float dcut[3],
- float r_co[3])
+ float r_co[3],
+ float mesh_range[2])
{
float x = r_co[0], y = r_co[1], z = r_co[2];
- float scale;
-
- scale = (z * z * factor - factor + 1.0f);
+ float t = remap_limit_axis(z, mesh_range);
+ float scale = 1.0f / powf(1.0f - factor * (t * t - 1.0f), 0.75f);
r_co[0] = x * scale;
r_co[1] = y * scale;
@@ -132,14 +142,14 @@ static void simpleDeform_stretch(const float factor,
static void simpleDeform_twist(const float factor,
const int UNUSED(axis),
const float *dcut,
- float r_co[3])
+ float r_co[3],
+ float mesh_range[2])
{
float x = r_co[0], y = r_co[1], z = r_co[2];
- float theta, sint, cost;
-
- theta = z * factor;
- sint = sinf(theta);
- cost = cosf(theta);
+ float t = remap_limit_axis(z, mesh_range);
+ float theta = t * factor;
+ float sint = sinf(theta);
+ float cost = cosf(theta);
r_co[0] = x * cost - y * sint;
r_co[1] = x * sint + y * cost;
@@ -151,10 +161,13 @@ static void simpleDeform_twist(const float factor,
static void simpleDeform_bend(const float factor,
const int axis,
const float dcut[3],
- float r_co[3])
+ float r_co[3],
+ float mesh_range[2])
{
float x = r_co[0], y = r_co[1], z = r_co[2];
- float theta, sint, cost;
+ float tx = remap_limit_axis(x, mesh_range);
+ float tz = remap_limit_axis(z, mesh_range);
+ float theta;
BLI_assert(!(fabsf(factor) < BEND_EPS));
@@ -162,13 +175,13 @@ static void simpleDeform_bend(const float factor,
case 0:
ATTR_FALLTHROUGH;
case 1:
- theta = z * factor;
+ theta = tz * factor;
break;
default:
- theta = x * factor;
+ theta = tx * factor;
}
- sint = sinf(theta);
- cost = cosf(theta);
+ float sint = sinf(theta);
+ float cost = cosf(theta);
switch (axis) {
case 0:
@@ -213,12 +226,13 @@ static void SimpleDeformModifier_do(SimpleDeformModifierData *smd,
{
const float base_limit[2] = {0.0f, 0.0f};
int i;
- float smd_limit[2], smd_factor;
+ float smd_limit[2], smd_limit_range[2];
SpaceTransform *transf = NULL, tmp_transf;
void (*simpleDeform_callback)(const float factor,
const int axis,
const float dcut[3],
- float co[3]) = NULL; /* Mode callback */
+ float co[3],
+ float mesh_range[2]) = NULL; /* Mode callback */
int vgroup;
MDeformVert *dvert;
@@ -296,8 +310,10 @@ static void SimpleDeformModifier_do(SimpleDeformModifierData *smd,
/* SMD values are normalized to the BV, calculate the absolute values */
smd_limit[1] = lower + (upper - lower) * smd->limit[1];
smd_limit[0] = lower + (upper - lower) * smd->limit[0];
-
- smd_factor = smd->factor / max_ff(FLT_EPSILON, smd_limit[1] - smd_limit[0]);
+
+ /* Needed for deform callback */
+ smd_limit_range[0] = lower;
+ smd_limit_range[1] = upper;
}
switch (smd->mode) {
@@ -318,7 +334,7 @@ static void SimpleDeformModifier_do(SimpleDeformModifierData *smd,
}
if (smd->mode == MOD_SIMPLEDEFORM_MODE_BEND) {
- if (fabsf(smd_factor) < BEND_EPS) {
+ if (fabsf(smd->factor) < BEND_EPS) {
return;
}
}
@@ -361,7 +377,7 @@ static void SimpleDeformModifier_do(SimpleDeformModifierData *smd,
float dcut_remap[3];
copy_v3_v3_map(co_remap, co, axis_map);
copy_v3_v3_map(dcut_remap, dcut, axis_map);
- simpleDeform_callback(smd_factor, deform_axis, dcut_remap, co_remap); /* apply deform */
+ simpleDeform_callback(smd->factor, deform_axis, dcut_remap, co_remap, smd_limit_range); /* apply deform */
copy_v3_v3_unmap(co, co_remap, axis_map);
/* Use vertex weight has coef of linear interpolation */