Maintain Volume: introduce an option switching between modes.
authorAlexander Gavrilov <angavrilov@gmail.com>
Mon, 6 May 2019 18:47:51 +0000 (21:47 +0300)
committerAlexander Gavrilov <angavrilov@gmail.com>
Mon, 6 May 2019 18:55:20 +0000 (21:55 +0300)
After a lot of thinking about this, I decided that all operation modes
that I've tried over the past couple of years, including the original
2.79 one, have their uses after all. Thus the only reasonable solution
is to add yet another option.

The modes are:

- Strict: The current 2.80 mode, which overrides the original scaling
  of the non-free axes to strictly preserve the volume. This is the most
  obvious way one would expect a 'Maintain Volume' constraint to work.

- Uniform: The original 2.79 mode, which assumes that all axes have been
  scaled the same as the free one when computing the volume. This seems
  strange, but the net effect is that when simply scaling the object
  uniformly with S, the volume is preserved; however, scaling the non-
  free axes individually allows deviating from the locked volume.
  This was obviously intended as a more or less convenient UI tool.

- Single Axis: My own variant of the intent of the Uniform scale, which
  does volume-preserving if the object is scaled just on the Free axis,
  while passing the non-free axis scaling through. I.e. instead of
  uniform S scaling, the user has to scale the object just on its
  primary axis to achieve constant volume. This can allow reducing the
  number of animation curves when only constant volume scaling is needed,
  or be an easier to control tool inside a complex rig.

release/scripts/startup/bl_ui/properties_constraint.py
source/blender/blenkernel/intern/constraint.c
source/blender/blenloader/intern/versioning_280.c
source/blender/makesdna/DNA_constraint_types.h
source/blender/makesdna/intern/dna_rename_defs.h
source/blender/makesrna/intern/rna_constraint.c

index a5353a4e29e1d3b691d34fb70d5b4e62a577d866..fec90fef590cfe984cd35876534b1730b3c43369 100644 (file)
@@ -430,6 +430,8 @@ class ConstraintButtonsPanel:
 
     def MAINTAIN_VOLUME(self, _context, layout, con):
 
+        layout.prop(con, "mode")
+
         row = layout.row()
         row.label(text="Free:")
         row.prop(con, "free_axis", expand=True)
index 0e29f165992671d799a1c96a44e2529d01d04888..282847fe220d9bcfc7f48be92cf38d51e87a4249 100644 (file)
@@ -2037,7 +2037,7 @@ static void samevolume_new_data(void *cdata)
 {
   bSameVolumeConstraint *data = (bSameVolumeConstraint *)cdata;
 
-  data->flag = SAMEVOL_Y;
+  data->free_axis = SAMEVOL_Y;
   data->volume = 1.0f;
 }
 
@@ -2046,19 +2046,30 @@ static void samevolume_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *
   bSameVolumeConstraint *data = con->data;
 
   float volume = data->volume;
-  float fac = 1.0f, total_scale;
+  float fac = 1.0f, total_scale = 1.0f;
   float obsize[3];
 
   mat4_to_size(obsize, cob->matrix);
 
   /* calculate normalizing scale factor for non-essential values */
-  total_scale = obsize[0] * obsize[1] * obsize[2];
+  switch (data->mode) {
+    case SAMEVOL_STRICT:
+      total_scale = obsize[0] * obsize[1] * obsize[2];
+      break;
+    case SAMEVOL_UNIFORM:
+      total_scale = pow3f(obsize[data->free_axis]);
+      break;
+    case SAMEVOL_SINGLE_AXIS:
+      total_scale = obsize[data->free_axis];
+      break;
+  }
+
   if (total_scale != 0) {
     fac = sqrtf(volume / total_scale);
   }
 
   /* apply scaling factor to the channels not being kept */
-  switch (data->flag) {
+  switch (data->free_axis) {
     case SAMEVOL_X:
       mul_v3_fl(cob->matrix[1], fac);
       mul_v3_fl(cob->matrix[2], fac);
index afbb512fe2f4c45c33f7406624029d74a939228d..194459a1edade62a7a84b7f9287cd9f9dba1a8c3 100644 (file)
@@ -692,6 +692,16 @@ static void do_version_bbone_scale_animdata_cb(ID *UNUSED(id),
   }
 }
 
+static void do_version_constraints_maintain_volume_mode_uniform(ListBase *lb)
+{
+  for (bConstraint *con = lb->first; con; con = con->next) {
+    if (con->type == CONSTRAINT_TYPE_SAMEVOL) {
+      bSameVolumeConstraint *data = (bSameVolumeConstraint *)con->data;
+      data->mode = SAMEVOL_UNIFORM;
+    }
+  }
+}
+
 void do_versions_after_linking_280(Main *bmain)
 {
   bool use_collection_compat_28 = true;
@@ -1290,6 +1300,16 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
         }
       }
     }
+
+    /* 2.79 style Maintain Volume mode. */
+    LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+      do_version_constraints_maintain_volume_mode_uniform(&ob->constraints);
+      if (ob->pose) {
+        LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
+          do_version_constraints_maintain_volume_mode_uniform(&pchan->constraints);
+        }
+      }
+    }
   }
 
 #ifdef USE_COLLECTION_COMPAT_28
index f839f22ec9f6fbb6089aa4beec4430e4f7de38ee..d3165efb64732c4f228a0869ddbcca6b5d764166 100644 (file)
@@ -296,7 +296,9 @@ typedef struct bSizeLikeConstraint {
 
 /* Maintain Volume Constraint */
 typedef struct bSameVolumeConstraint {
-  int flag;
+  char free_axis;
+  char mode;
+  char _pad[2];
   float volume;
 } bSameVolumeConstraint;
 
@@ -758,12 +760,22 @@ typedef enum eTransform_ToFrom {
   TRANS_SCALE = 2,
 } eTransform_ToFrom;
 
-/* bSameVolumeConstraint.flag */
-typedef enum eSameVolume_Modes {
+/* bSameVolumeConstraint.free_axis */
+typedef enum eSameVolume_Axis {
   SAMEVOL_X = 0,
   SAMEVOL_Y = 1,
   SAMEVOL_Z = 2,
-} eSameVolume_Modes;
+} eSameVolume_Axis;
+
+/* bSameVolumeConstraint.mode */
+typedef enum eSameVolume_Mode {
+  /* Strictly maintain the volume, overriding non-free axis scale. */
+  SAMEVOL_STRICT = 0,
+  /* Maintain the volume when scale is uniform, pass non-uniform other axis scale through. */
+  SAMEVOL_UNIFORM = 1,
+  /* Maintain the volume when scaled only on the free axis, pass other axis scale through. */
+  SAMEVOL_SINGLE_AXIS = 2,
+} eSameVolume_Mode;
 
 /* bActionConstraint.flag */
 typedef enum eActionConstraint_Flags {
index b9e0853b1ab1ecf019db25a328adef3353a97782..c02512d2e6b97e0a49bb9c6a8c80b4cbec5b264d 100644 (file)
@@ -83,6 +83,7 @@ DNA_STRUCT_RENAME_ELEM(bPoseChannel, curveInX, curve_in_x)
 DNA_STRUCT_RENAME_ELEM(bPoseChannel, curveInY, curve_in_y)
 DNA_STRUCT_RENAME_ELEM(bPoseChannel, curveOutX, curve_out_x)
 DNA_STRUCT_RENAME_ELEM(bPoseChannel, curveOutY, curve_out_y)
+DNA_STRUCT_RENAME_ELEM(bSameVolumeConstraint, flag, free_axis)
 DNA_STRUCT_RENAME_ELEM(bTheme, tact, space_action)
 DNA_STRUCT_RENAME_ELEM(bTheme, tbuts, space_properties)
 DNA_STRUCT_RENAME_ELEM(bTheme, tclip, space_clip)
index c1c235d497b2effa6403d55a16883e013e1ce469..49c1fa779ab55e2e75690bb4a7eb0c370214cb09 100644 (file)
@@ -1346,13 +1346,34 @@ static void rna_def_constraint_same_volume(BlenderRNA *brna)
   StructRNA *srna;
   PropertyRNA *prop;
 
-  static const EnumPropertyItem volume_items[] = {
+  static const EnumPropertyItem axis_items[] = {
       {SAMEVOL_X, "SAMEVOL_X", 0, "X", ""},
       {SAMEVOL_Y, "SAMEVOL_Y", 0, "Y", ""},
       {SAMEVOL_Z, "SAMEVOL_Z", 0, "Z", ""},
       {0, NULL, 0, NULL, NULL},
   };
 
+  static const EnumPropertyItem mode_items[] = {
+      {SAMEVOL_STRICT,
+       "STRICT",
+       0,
+       "Strict",
+       "Volume is strictly preserved, overriding the scaling of non-free axes"},
+      {SAMEVOL_UNIFORM,
+       "UNIFORM",
+       0,
+       "Uniform",
+       "Volume is preserved when the object is scaled uniformly. "
+       "Deviations from uniform scale on non-free axes are passed through"},
+      {SAMEVOL_SINGLE_AXIS,
+       "SINGLE_AXIS",
+       0,
+       "Single Axis",
+       "Volume is preserved when the object is scaled only on the free axis. "
+       "Non-free axis scaling is passed through"},
+      {0, NULL, 0, NULL, NULL},
+  };
+
   srna = RNA_def_struct(brna, "MaintainVolumeConstraint", "Constraint");
   RNA_def_struct_ui_text(srna,
                          "Maintain Volume Constraint",
@@ -1360,11 +1381,18 @@ static void rna_def_constraint_same_volume(BlenderRNA *brna)
   RNA_def_struct_sdna_from(srna, "bSameVolumeConstraint", "data");
 
   prop = RNA_def_property(srna, "free_axis", PROP_ENUM, PROP_NONE);
-  RNA_def_property_enum_sdna(prop, NULL, "flag");
-  RNA_def_property_enum_items(prop, volume_items);
+  RNA_def_property_enum_sdna(prop, NULL, "free_axis");
+  RNA_def_property_enum_items(prop, axis_items);
   RNA_def_property_ui_text(prop, "Free Axis", "The free scaling axis of the object");
   RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
 
+  prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
+  RNA_def_property_enum_sdna(prop, NULL, "mode");
+  RNA_def_property_enum_items(prop, mode_items);
+  RNA_def_property_ui_text(
+      prop, "Mode", "The way the constraint treats original non-free axis scaling");
+  RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
+
   prop = RNA_def_property(srna, "volume", PROP_FLOAT, PROP_DISTANCE);
   RNA_def_property_range(prop, 0.001f, 100.0f);
   RNA_def_property_ui_text(prop, "Volume", "Volume of the bone at rest");