Pataz-Gooseberry Request: Limits on Volume Preservation for Spline IK
authorJoshua Leung <aligorith@gmail.com>
Tue, 13 Jan 2015 05:06:53 +0000 (18:06 +1300)
committerJoshua Leung <aligorith@gmail.com>
Tue, 13 Jan 2015 05:06:53 +0000 (18:06 +1300)
This commit adds a new type of volume preservation mode to Spline IK
which makes it possible to set limits on the minimum and maximum
scaling of bone "fatness".

* The old volume preseving mode has been kept but renamed, to avoid
  breaking old rigs. "Volume Presevation" uses the new method, while
  "Inverse Preservation" is the old one.

* The code and settings for this new xz scale mode are directly lifted
  from the improved Stretch To constraint

release/scripts/startup/bl_ui/properties_constraint.py
source/blender/blenkernel/intern/armature.c
source/blender/makesdna/DNA_constraint_types.h
source/blender/makesrna/intern/rna_constraint.c

index e6b62cae6ef279b7d344d7da0e290870b9abcbef..0c5aae1de2fffc9d5081eae953694eb4ba4347dd 100644 (file)
@@ -768,9 +768,27 @@ class ConstraintButtonsPanel():
         col = layout.column()
         col.label(text="Chain Scaling:")
         col.prop(con, "use_y_stretch")
-        col.prop(con, "xz_scale_mode")
         col.prop(con, "use_curve_radius")
 
+        layout.prop(con, "xz_scale_mode")
+
+        if con.xz_scale_mode == 'VOLUME_PRESERVE':
+            layout.prop(con, "bulge", text="Volume Variation")
+            split = layout.split()
+            col = split.column(align=True)
+            col.prop(con, "use_bulge_min", text="Volume Min")
+            sub = col.column()
+            sub.active = con.use_bulge_min
+            sub.prop(con, "bulge_min", text="")
+            col = split.column(align=True)
+            col.prop(con, "use_bulge_max", text="Volume Max")
+            sub = col.column()
+            sub.active = con.use_bulge_max
+            sub.prop(con, "bulge_max", text="")
+            col = layout.column()
+            col.active = con.use_bulge_min or con.use_bulge_max
+            col.prop(con, "bulge_smooth", text="Smooth")
+
     def PIVOT(self, context, layout, con):
         self.target_template(layout, con)
 
index df57db21e3b153734b6ef62a9d5f15f595c8575d..88b46efc9e6f2990db5cb4007a1770d44aff7727 100644 (file)
@@ -2166,9 +2166,9 @@ static void splineik_evaluate_bone(tSplineIK_Tree *tree, Scene *scene, Object *o
                                mul_v3_fl(poseMat[2], scale);
                                break;
                        }
-                       case CONSTRAINT_SPLINEIK_XZS_VOLUMETRIC:
+                       case CONSTRAINT_SPLINEIK_XZS_INVERSE:
                        {
-                               /* 'volume preservation' */
+                               /* old 'volume preservation' method using the inverse scale */
                                float scale;
 
                                /* calculate volume preservation factor which is
@@ -2189,6 +2189,54 @@ static void splineik_evaluate_bone(tSplineIK_Tree *tree, Scene *scene, Object *o
                                mul_v3_fl(poseMat[2], scale);
                                break;
                        }
+                       case CONSTRAINT_SPLINEIK_XZS_VOLUMETRIC:
+                       {
+                               /* improved volume preservation based on the Stretch To constraint */
+                               float scale;
+                               
+                               /* as the basis for volume preservation, we use the inverse scale factor... */
+                               if (fabsf(scaleFac) != 0.0f) {
+                                       /* NOTE: The method here is taken wholesale from the Stretch To constraint */
+                                       float bulge = powf(1.0f / fabsf(scaleFac), ikData->bulge);
+                                       
+                                       if (bulge > 1.0f) {
+                                               if (ikData->flag & STRETCHTOCON_USE_BULGE_MAX) {
+                                                       float bulge_max = max_ff(ikData->bulge_max, 1.0f);
+                                                       float hard = min_ff(bulge, bulge_max);
+                                                       
+                                                       float range = bulge_max - 1.0f;
+                                                       float scale = (range > 0.0f) ? 1.0f / range : 0.0f;
+                                                       float soft = 1.0f + range * atanf((bulge - 1.0f) * scale) / (0.5f * M_PI);
+                                                       
+                                                       bulge = interpf(soft, hard, ikData->bulge_smooth);
+                                               }
+                                       }
+                                       if (bulge < 1.0f) {
+                                               if (ikData->flag & STRETCHTOCON_USE_BULGE_MIN) {
+                                                       float bulge_min = CLAMPIS(ikData->bulge_max, 0.0f, 1.0f);
+                                                       float hard = max_ff(bulge, bulge_min);
+                                                       
+                                                       float range = 1.0f - bulge_min;
+                                                       float scale = (range > 0.0f) ? 1.0f / range : 0.0f;
+                                                       float soft = 1.0f - range * atanf((1.0f - bulge) * scale) / (0.5f * M_PI);
+                                                       
+                                                       bulge = interpf(soft, hard, ikData->bulge_smooth);
+                                               }
+                                       }
+                                       
+                                       /* compute scale factor for xz axes from this value */
+                                       scale = sqrt(bulge);
+                               }
+                               else {
+                                       /* no scaling, so scale factor is simple */
+                                       scale = 1.0f;
+                               }
+                               
+                               /* apply the scaling (assuming normalised scale) */
+                               mul_v3_fl(poseMat[0], scale);
+                               mul_v3_fl(poseMat[2], scale);
+                               break;
+                       }
                }
 
                /* finally, multiply the x and z scaling by the radius of the curve too,
index fb33879e2c17866bb6ecabe1a9a7aef8fc71c331..869912450689d1024d226e8564d0ddefd16e5b86 100644 (file)
@@ -173,6 +173,12 @@ typedef struct bSplineIKConstraint {
                /* settings */
        short flag;                             /* general settings for constraint */
        short xzScaleMode;              /* method used for determining the x & z scaling of the bones */
+       
+               /* volume preservation settings */
+       float           bulge;
+       float           bulge_min;
+       float           bulge_max;
+       float           bulge_smooth;
 } bSplineIKConstraint;
 
 
@@ -680,15 +686,19 @@ typedef enum eKinematic_Flags {
 /* bSplineIKConstraint->flag */
 typedef enum eSplineIK_Flags {
        /* chain has been attached to spline */
-       CONSTRAINT_SPLINEIK_BOUND                       = (1<<0),
+       CONSTRAINT_SPLINEIK_BOUND                       = (1 << 0),
        /* root of chain is not influenced by the constraint */
-       CONSTRAINT_SPLINEIK_NO_ROOT                     = (1<<1),
+       CONSTRAINT_SPLINEIK_NO_ROOT                     = (1 << 1),
        /* bones in the chain should not scale to fit the curve */
-       CONSTRAINT_SPLINEIK_SCALE_LIMITED       = (1<<2),
+       CONSTRAINT_SPLINEIK_SCALE_LIMITED       = (1 << 2),
        /* evenly distribute the bones along the path regardless of length */
-       CONSTRAINT_SPLINEIK_EVENSPLITS          = (1<<3),
+       CONSTRAINT_SPLINEIK_EVENSPLITS          = (1 << 3),
        /* don't adjust the x and z scaling of the bones by the curve radius */
-       CONSTRAINT_SPLINEIK_NO_CURVERAD = (1<<4)
+       CONSTRAINT_SPLINEIK_NO_CURVERAD         = (1 << 4),
+       
+       /* for "volumetric" xz scale mode, limit the minimum or maximum scale values */
+       CONSTRAINT_SPLINEIK_USE_BULGE_MIN       = (1 << 5),
+       CONSTRAINT_SPLINEIK_USE_BULGE_MAX       = (1 << 6),
 } eSplineIK_Flags;
 
 /* bSplineIKConstraint->xzScaleMode */
@@ -698,7 +708,9 @@ typedef enum eSplineIK_XZScaleModes {
        /* bones in the chain should take their x/z scales from the original scaling */
        CONSTRAINT_SPLINEIK_XZS_ORIGINAL                = 1,
        /* x/z scales are the inverse of the y-scale */
-       CONSTRAINT_SPLINEIK_XZS_VOLUMETRIC              = 2
+       CONSTRAINT_SPLINEIK_XZS_INVERSE                 = 2,
+       /* x/z scales are computed using a volume preserving technique (from Stretch To constraint) */
+       CONSTRAINT_SPLINEIK_XZS_VOLUMETRIC              = 3
 } eSplineIK_XZScaleModes;
 
 /* MinMax (floor) flags */
index 77355dbad0eef913d9c2000a212a36e9f01d4137..b6845b1df2e8e0d225d784ee9299de1e4212539a 100644 (file)
@@ -1388,7 +1388,7 @@ static void rna_def_constraint_stretch_to(BlenderRNA *brna)
 
        prop = RNA_def_property(srna, "bulge_smooth", PROP_FLOAT, PROP_FACTOR);
        RNA_def_property_range(prop, 0.0, 1.0f);
-       RNA_def_property_ui_text(prop, "Volume Variation Smoothness", "");
+       RNA_def_property_ui_text(prop, "Volume Variation Smoothness", "Strength of volume stretching clamping");
        RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
 }
 
@@ -2272,8 +2272,10 @@ static void rna_def_constraint_spline_ik(BlenderRNA *brna)
                {CONSTRAINT_SPLINEIK_XZS_NONE, "NONE", 0, "None", "Don't scale the X and Z axes (Default)"},
                {CONSTRAINT_SPLINEIK_XZS_ORIGINAL, "BONE_ORIGINAL", 0, "Bone Original",
                                                   "Use the original scaling of the bones"},
+               {CONSTRAINT_SPLINEIK_XZS_INVERSE,  "INVERSE_PRESERVE", 0, "Inverse Scale",
+                                                   "Scale of the X and Z axes is the inverse of the Y-Scale"},
                {CONSTRAINT_SPLINEIK_XZS_VOLUMETRIC, "VOLUME_PRESERVE", 0, "Volume Preservation",
-                                                    "Scale of the X and Z axes is the inverse of the Y-Scale"},
+                                                    "Scale of the X and Z axes are adjusted to preserve the volume of the bones"},
                {0, NULL, 0, NULL, NULL}
        };
 
@@ -2333,12 +2335,44 @@ static void rna_def_constraint_spline_ik(BlenderRNA *brna)
                                 "on top of XZ Scale mode");
        RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
        
+       /* xz scaling mode */
        prop = RNA_def_property(srna, "xz_scale_mode", PROP_ENUM, PROP_NONE);
        RNA_def_property_enum_sdna(prop, NULL, "xzScaleMode");
        RNA_def_property_enum_items(prop, splineik_xz_scale_mode);
        RNA_def_property_ui_text(prop, "XZ Scale Mode",
                                 "Method used for determining the scaling of the X and Z axes of the bones");
        RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
+       
+       /* volume presevation for "volumetric" scale mode */
+       prop = RNA_def_property(srna, "bulge", PROP_FLOAT, PROP_NONE);
+       RNA_def_property_range(prop, 0.0, 100.f);
+       RNA_def_property_ui_text(prop, "Volume Variation", "Factor between volume variation and stretching");
+       RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
+       
+       prop = RNA_def_property(srna, "use_bulge_min", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "flag", CONSTRAINT_SPLINEIK_USE_BULGE_MIN);
+       RNA_def_property_ui_text(prop, "Use Volume Variation Minimum", "Use lower limit for volume variation");
+       RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
+       
+       prop = RNA_def_property(srna, "use_bulge_max", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "flag", CONSTRAINT_SPLINEIK_USE_BULGE_MAX);
+       RNA_def_property_ui_text(prop, "Use Volume Variation Maximum", "Use upper limit for volume variation");
+       RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
+       
+       prop = RNA_def_property(srna, "bulge_min", PROP_FLOAT, PROP_NONE);
+       RNA_def_property_range(prop, 0.0, 1.0f);
+       RNA_def_property_ui_text(prop, "Volume Variation Minimum", "Minimum volume stretching factor");
+       RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
+       
+       prop = RNA_def_property(srna, "bulge_max", PROP_FLOAT, PROP_NONE);
+       RNA_def_property_range(prop, 1.0, 100.0f);
+       RNA_def_property_ui_text(prop, "Volume Variation Maximum", "Maximum volume stretching factor");
+       RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
+       
+       prop = RNA_def_property(srna, "bulge_smooth", PROP_FLOAT, PROP_FACTOR);
+       RNA_def_property_range(prop, 0.0, 1.0f);
+       RNA_def_property_ui_text(prop, "Volume Variation Smoothness", "Strength of volume stretching clamping");
+       RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
 }
 
 static void rna_def_constraint_pivot(BlenderRNA *brna)