Armature: add Inherit Scale options to remove shear or average the scale.
authorAlexander Gavrilov <angavrilov@gmail.com>
Wed, 4 Sep 2019 07:10:27 +0000 (10:10 +0300)
committerAlexander Gavrilov <angavrilov@gmail.com>
Wed, 4 Sep 2019 07:34:21 +0000 (10:34 +0300)
As an inherent property of matrix-based transformation math, non-
uniform scaling of a parent bone induces shear into the transform
matrix of any rotated child. Such matrices cannot be cleanly
decomposed into a combination of location/rotation/scale, which
causes issues for rigging and animation tools.

Blender bones have options to exclude rotation and/or scale from the
inherited transformation, but don't have any support for removing the
often undesired shear component. That goal requires replacing simple
parenting with a combination of multiple bones and constraints. The
same is true about the goal of inheriting some scale, but completely
avoiding shear.

This patch replaces the old Inherit Scale checkbox with a enum that
supports multiple options:

* Full: inherit all effects of scale, like with enabled Inherit Scale.

* Fix Shear: removes shear from the final inherited transformation.

  The cleanup math is specifically designed to preserve the main
  axis of the bone, its length and total volume, and minimally
  affect roll on average. It however will not prevent reappearance
  of shear due to local rotation of the child or its children.

* Average: inherit uniform scale that represents the parent volume.

  This is the simplest foolproof solution that will inherit some
  scale without ever causing shear.

* None: completely remove scale and shear.

* None (Legacy): old disabled Inherit Scale checkbox.

  This mode does not handle parent shear in any way, so the child
  is likely to end up having both scale and shear. It is retained
  for backward compatibility.

Since many rigging-related addons access the use_inherit_scale
property from Python, it is retained as a backward compatibility
stub that provides the old functionality.

As a side effect of reworking the code, this also fixes a matrix
multiplication order bug in the Inherit Rotation code, which caused
the parent local scale to be applied in world space. In rigger
opinion this option is useless in production rigs, so this fix
should not be a problem.

Reviewers: brecht

Differential Revision: https://developer.blender.org/D5588

14 files changed:
release/scripts/startup/bl_ui/properties_data_bone.py
release/scripts/startup/bl_ui/space_view3d.py
source/blender/blenkernel/BKE_armature.h
source/blender/blenkernel/intern/armature.c
source/blender/blenlib/BLI_math_matrix.h
source/blender/blenlib/intern/math_matrix.c
source/blender/blenloader/intern/versioning_280.c
source/blender/editors/armature/armature_edit.c
source/blender/editors/armature/armature_utils.c
source/blender/editors/armature/pose_transform.c
source/blender/editors/include/ED_armature.h
source/blender/makesdna/DNA_armature_types.h
source/blender/makesrna/intern/rna_armature.c
source/blender/makesrna/intern/rna_armature_api.c

index 8b691ddcc2a5a0872c66d9b7ffeabe721b6d03fa..c50b94146676eb8970ad1a861daf74c6e73bbad2 100644 (file)
@@ -232,11 +232,13 @@ class BONE_PT_relations(BoneButtonsPanel, Panel):
         sub = col.column()
         sub.active = (bone.parent is not None)
         sub.prop(bone, "use_connect")
-        sub.prop(bone, "use_inherit_rotation")
-        sub.prop(bone, "use_inherit_scale")
         sub = col.column()
         sub.active = (not bone.parent or not bone.use_connect)
         sub.prop(bone, "use_local_location")
+        sub = col.column()
+        sub.active = (bone.parent is not None)
+        sub.prop(bone, "use_inherit_rotation")
+        sub.prop(bone, "inherit_scale")
 
 
 class BONE_PT_display(BoneButtonsPanel, Panel):
index 501cda1d67a3bda38de3fe0f7cc4940708540adc..fdf683c16eab10001b758b7c708c45d3c280b2f4 100644 (file)
@@ -3193,7 +3193,7 @@ class BoneOptions:
             "use_deform",
             "use_envelope_multiply",
             "use_inherit_rotation",
-            "use_inherit_scale",
+            "inherit_scale",
         ]
 
         if context.mode == 'EDIT_ARMATURE':
index 3beae7a1f9db5e69fa0a63dc1e5531a0abc535d5..b255500272afab02c71de0d26d1cfe41d3731934 100644 (file)
@@ -182,6 +182,7 @@ void BKE_bone_parent_transform_apply(const struct BoneParentTransform *bpt,
 void BKE_bone_parent_transform_calc_from_pchan(const struct bPoseChannel *pchan,
                                                struct BoneParentTransform *r_bpt);
 void BKE_bone_parent_transform_calc_from_matrices(int bone_flag,
+                                                  int inherit_scale_mode,
                                                   const float offs_bone[4][4],
                                                   const float parent_arm_mat[4][4],
                                                   const float parent_pose_mat[4][4],
index 168422a44548a2912b63cde5e6747c3cc39c185a..c31d1a774047bda62d80b793ebf2488ea0f5a612 100644 (file)
@@ -1867,11 +1867,16 @@ void BKE_bone_parent_transform_calc_from_pchan(const bPoseChannel *pchan,
     /* yoffs(b-1) + root(b) + bonemat(b). */
     BKE_bone_offset_matrix_get(bone, offs_bone);
 
-    BKE_bone_parent_transform_calc_from_matrices(
-        bone->flag, offs_bone, parbone->arm_mat, parchan->pose_mat, r_bpt);
+    BKE_bone_parent_transform_calc_from_matrices(bone->flag,
+                                                 bone->inherit_scale_mode,
+                                                 offs_bone,
+                                                 parbone->arm_mat,
+                                                 parchan->pose_mat,
+                                                 r_bpt);
   }
   else {
-    BKE_bone_parent_transform_calc_from_matrices(bone->flag, bone->arm_mat, NULL, NULL, r_bpt);
+    BKE_bone_parent_transform_calc_from_matrices(
+        bone->flag, bone->inherit_scale_mode, bone->arm_mat, NULL, NULL, r_bpt);
   }
 }
 
@@ -1882,39 +1887,90 @@ void BKE_bone_parent_transform_calc_from_pchan(const bPoseChannel *pchan,
  * parent_arm_mat, parent_pose_mat: arm_mat and pose_mat of parent, or NULL
  * r_bpt: OUTPUT parent transform */
 void BKE_bone_parent_transform_calc_from_matrices(int bone_flag,
+                                                  int inherit_scale_mode,
                                                   const float offs_bone[4][4],
                                                   const float parent_arm_mat[4][4],
                                                   const float parent_pose_mat[4][4],
                                                   BoneParentTransform *r_bpt)
 {
   if (parent_pose_mat) {
+    const bool use_rotation = (bone_flag & BONE_HINGE) == 0;
+    const bool full_transform = use_rotation && inherit_scale_mode == BONE_INHERIT_SCALE_FULL;
+
     /* Compose the rotscale matrix for this bone. */
-    if ((bone_flag & BONE_HINGE) && (bone_flag & BONE_NO_SCALE)) {
-      /* Parent rest rotation and scale. */
-      mul_m4_m4m4(r_bpt->rotscale_mat, parent_arm_mat, offs_bone);
+    if (full_transform) {
+      /* Parent pose rotation and scale. */
+      mul_m4_m4m4(r_bpt->rotscale_mat, parent_pose_mat, offs_bone);
     }
-    else if (bone_flag & BONE_HINGE) {
-      /* Parent rest rotation and pose scale. */
+    else {
       float tmat[4][4], tscale[3];
 
-      /* Extract the scale of the parent pose matrix. */
-      mat4_to_size(tscale, parent_pose_mat);
-      size_to_mat4(tmat, tscale);
+      /* If using parent pose rotation: */
+      if (use_rotation) {
+        copy_m4_m4(tmat, parent_pose_mat);
+
+        /* Normalize the matrix when needed. */
+        switch (inherit_scale_mode) {
+          case BONE_INHERIT_SCALE_FULL:
+          case BONE_INHERIT_SCALE_FIX_SHEAR:
+            /* Keep scale and shear. */
+            break;
+
+          case BONE_INHERIT_SCALE_NONE:
+          case BONE_INHERIT_SCALE_AVERAGE:
+            /* Remove scale and shear from parent. */
+            orthogonalize_m4_stable(tmat, 1, true);
+            break;
+
+          case BONE_INHERIT_SCALE_NONE_LEGACY:
+            /* Remove only scale - bad legacy way. */
+            normalize_m4(tmat);
+            break;
+
+          default:
+            BLI_assert(false);
+        }
+      }
+      /* If removing parent pose rotation: */
+      else {
+        copy_m4_m4(tmat, parent_arm_mat);
+
+        /* Copy the parent scale when needed. */
+        switch (inherit_scale_mode) {
+          case BONE_INHERIT_SCALE_FULL:
+            /* Ignore effects of shear. */
+            mat4_to_size(tscale, parent_pose_mat);
+            rescale_m4(tmat, tscale);
+            break;
+
+          case BONE_INHERIT_SCALE_FIX_SHEAR:
+            /* Take the effects of parent shear into account to get exact volume. */
+            mat4_to_size_fix_shear(tscale, parent_pose_mat);
+            rescale_m4(tmat, tscale);
+            break;
+
+          case BONE_INHERIT_SCALE_NONE:
+          case BONE_INHERIT_SCALE_AVERAGE:
+          case BONE_INHERIT_SCALE_NONE_LEGACY:
+            /* Keep unscaled. */
+            break;
+
+          default:
+            BLI_assert(false);
+        }
+      }
 
-      /* Applies the parent pose scale to the rest matrix. */
-      mul_m4_m4m4(tmat, tmat, parent_arm_mat);
+      /* Apply the average parent scale when needed. */
+      if (inherit_scale_mode == BONE_INHERIT_SCALE_AVERAGE) {
+        mul_mat3_m4_fl(tmat, cbrtf(fabsf(mat4_to_volume_scale(parent_pose_mat))));
+      }
 
       mul_m4_m4m4(r_bpt->rotscale_mat, tmat, offs_bone);
-    }
-    else if (bone_flag & BONE_NO_SCALE) {
-      /* Parent pose rotation and rest scale (i.e. no scaling). */
-      float tmat[4][4];
-      copy_m4_m4(tmat, parent_pose_mat);
-      normalize_m4(tmat);
-      mul_m4_m4m4(r_bpt->rotscale_mat, tmat, offs_bone);
-    }
-    else {
-      mul_m4_m4m4(r_bpt->rotscale_mat, parent_pose_mat, offs_bone);
+
+      /* Remove remaining shear when needed, preserving volume. */
+      if (inherit_scale_mode == BONE_INHERIT_SCALE_FIX_SHEAR) {
+        orthogonalize_m4_stable(r_bpt->rotscale_mat, 1, false);
+      }
     }
 
     /* Compose the loc matrix for this bone. */
@@ -1938,7 +1994,7 @@ void BKE_bone_parent_transform_calc_from_matrices(int bone_flag,
       mul_m4_m4m4(r_bpt->loc_mat, bone_loc, tmat4);
     }
     /* Those flags do not affect position, use plain parent transform space! */
-    else if (bone_flag & (BONE_HINGE | BONE_NO_SCALE)) {
+    else if (!full_transform) {
       mul_m4_m4m4(r_bpt->loc_mat, parent_pose_mat, offs_bone);
     }
     /* Else (i.e. default, usual case),
index f5d87667b73f9fb08c251171b4100b83e5ad579c..00c301a01bc74f2f760ee54cb953f9215059e31f 100644 (file)
@@ -252,6 +252,9 @@ void normalize_m4_m4(float R[4][4], const float A[4][4]) ATTR_NONNULL();
 void orthogonalize_m3(float R[3][3], int axis);
 void orthogonalize_m4(float R[4][4], int axis);
 
+void orthogonalize_m3_stable(float R[3][3], int axis, bool normalize);
+void orthogonalize_m4_stable(float R[4][4], int axis, bool normalize);
+
 bool is_orthogonal_m3(const float mat[3][3]);
 bool is_orthogonal_m4(const float mat[4][4]);
 bool is_orthonormal_m3(const float mat[3][3]);
@@ -303,8 +306,11 @@ void size_to_mat4(float R[4][4], const float size[3]);
 void mat3_to_size(float r[3], const float M[3][3]);
 void mat4_to_size(float r[3], const float M[4][4]);
 
+void mat4_to_size_fix_shear(float r[3], const float M[4][4]);
+
 void translate_m4(float mat[4][4], float tx, float ty, float tz);
 void rotate_m4(float mat[4][4], const char axis, const float angle);
+void rescale_m4(float mat[4][4], float scale[3]);
 void transform_pivot_set_m4(float mat[4][4], const float pivot[3]);
 
 void mat3_to_rot_size(float rot[3][3], float size[3], const float mat3[3][3]);
index 2568d42566c2c7efe0391df2039d7f4e82da5f1b..7c1b44978e2e69c518969508398850393fe56c0a 100644 (file)
@@ -1516,6 +1516,111 @@ void orthogonalize_m4(float mat[4][4], int axis)
   mul_v3_fl(mat[2], size[2]);
 }
 
+/** Make an orthonormal basis around v1 in a way that is stable and symmetric. */
+static void orthogonalize_stable(float v1[3], float v2[3], float v3[3], bool normalize)
+{
+  /* Make secondary axis vectors orthogonal to the primary via
+   * plane projection, which preserves the determinant. */
+  float len_sq_v1 = len_squared_v3(v1);
+
+  if (len_sq_v1 > 0.0f) {
+    madd_v3_v3fl(v2, v1, -dot_v3v3(v2, v1) / len_sq_v1);
+    madd_v3_v3fl(v3, v1, -dot_v3v3(v3, v1) / len_sq_v1);
+
+    if (normalize) {
+      mul_v3_fl(v1, 1.0f / sqrtf(len_sq_v1));
+    }
+  }
+
+  /* Make secondary axis vectors orthogonal relative to each other. */
+  float norm_v2[3], norm_v3[3], tmp[3];
+  float length_v2 = normalize_v3_v3(norm_v2, v2);
+  float length_v3 = normalize_v3_v3(norm_v3, v3);
+  float cos_angle = dot_v3v3(norm_v2, norm_v3);
+  float abs_cos_angle = fabsf(cos_angle);
+
+  /* Apply correction if the shear angle is significant, and not degenerate. */
+  if (abs_cos_angle > 1e-4f && abs_cos_angle < 1.0f - FLT_EPSILON) {
+    /* Adjust v2 by half of the necessary angle correction.
+     * Thus the angle change is the same for both axis directions. */
+    float angle = acosf(cos_angle);
+    float target_angle = angle + ((float)M_PI_2 - angle) / 2;
+
+    madd_v3_v3fl(norm_v2, norm_v3, -cos_angle);
+    mul_v3_fl(norm_v2, sinf(target_angle) / len_v3(norm_v2));
+    madd_v3_v3fl(norm_v2, norm_v3, cosf(target_angle));
+
+    /* Make v3 orthogonal. */
+    cross_v3_v3v3(tmp, norm_v2, norm_v3);
+    cross_v3_v3v3(norm_v3, tmp, norm_v2);
+    normalize_v3(norm_v3);
+
+    /* Re-apply scale, preserving area and proportion. */
+    if (!normalize) {
+      float scale_fac = sqrtf(sinf(angle));
+      mul_v3_v3fl(v2, norm_v2, length_v2 * scale_fac);
+      mul_v3_v3fl(v3, norm_v3, length_v3 * scale_fac);
+    }
+  }
+
+  if (normalize) {
+    copy_v3_v3(v2, norm_v2);
+    copy_v3_v3(v3, norm_v3);
+  }
+}
+
+/**
+ * Make an orthonormal matrix around the selected axis of the given matrix,
+ * in a way that is symmetric and stable to variations in the input, and
+ * preserving the value of the determinant, i.e. the overall volume change.
+ *
+ * \param axis: Axis to build the orthonormal basis around.
+ * \param normalize: Normalize the matrix instead of preserving volume.
+ */
+void orthogonalize_m3_stable(float R[3][3], int axis, bool normalize)
+{
+  switch (axis) {
+    case 0:
+      orthogonalize_stable(R[0], R[1], R[2], normalize);
+      break;
+    case 1:
+      orthogonalize_stable(R[1], R[0], R[2], normalize);
+      break;
+    case 2:
+      orthogonalize_stable(R[2], R[0], R[1], normalize);
+      break;
+    default:
+      BLI_assert(0);
+      break;
+  }
+}
+
+/**
+ * Make an orthonormal matrix around the selected axis of the given matrix,
+ * in a way that is symmetric and stable to variations in the input, and
+ * preserving the value of the determinant, i.e. the overall volume change.
+ *
+ * \param axis: Axis to build the orthonormal basis around.
+ * \param normalize: Normalize the matrix instead of preserving volume.
+ */
+void orthogonalize_m4_stable(float R[4][4], int axis, bool normalize)
+{
+  switch (axis) {
+    case 0:
+      orthogonalize_stable(R[0], R[1], R[2], normalize);
+      break;
+    case 1:
+      orthogonalize_stable(R[1], R[0], R[2], normalize);
+      break;
+    case 2:
+      orthogonalize_stable(R[2], R[0], R[1], normalize);
+      break;
+    default:
+      BLI_assert(0);
+      break;
+  }
+}
+
 bool is_orthogonal_m3(const float m[3][3])
 {
   int i, j;
@@ -1851,6 +1956,19 @@ void mat4_to_size(float size[3], const float mat[4][4])
   size[2] = len_v3(mat[2]);
 }
 
+/** Extract scale factors from the matrix, with correction to ensure
+ *  exact volume in case of a sheared matrix. */
+void mat4_to_size_fix_shear(float size[3], const float mat[4][4])
+{
+  mat4_to_size(size, mat);
+
+  float volume = size[0] * size[1] * size[2];
+
+  if (volume != 0.0f) {
+    mul_v3_fl(size, cbrtf(fabsf(mat4_to_volume_scale(mat) / volume)));
+  }
+}
+
 /**
  * This computes the overall volume scale factor of a transformation matrix.
  * For an orthogonal matrix, it is the product of all three scale values.
@@ -2045,6 +2163,14 @@ void rotate_m4(float mat[4][4], const char axis, const float angle)
   }
 }
 
+/** Scale a matrix in-place. */
+void rescale_m4(float mat[4][4], float scale[3])
+{
+  mul_v3_fl(mat[0], scale[0]);
+  mul_v3_fl(mat[1], scale[1]);
+  mul_v3_fl(mat[2], scale[2]);
+}
+
 /**
  * Scale or rotate around a pivot point,
  * a convenience function to avoid having to do inline.
index 454f1921586fc1092baffeb435ea0c59629fa0fb..45276181102d63cae82bbc99e9888ccccd49718a 100644 (file)
@@ -637,6 +637,18 @@ static void do_version_bones_split_bbone_scale(ListBase *lb)
   }
 }
 
+static void do_version_bones_inherit_scale(ListBase *lb)
+{
+  for (Bone *bone = lb->first; bone; bone = bone->next) {
+    if (bone->flag & BONE_NO_SCALE) {
+      bone->inherit_scale_mode = BONE_INHERIT_SCALE_NONE_LEGACY;
+      bone->flag &= ~BONE_NO_SCALE;
+    }
+
+    do_version_bones_inherit_scale(&bone->childbase);
+  }
+}
+
 static bool replace_bbone_scale_rnapath(char **p_old_path)
 {
   char *old_path = *p_old_path;
@@ -3756,5 +3768,12 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
         }
       }
     }
+
+    /* Convert the BONE_NO_SCALE flag to inherit_scale_mode enum. */
+    if (!DNA_struct_elem_find(fd->filesdna, "Bone", "char", "inherit_scale_mode")) {
+      LISTBASE_FOREACH (bArmature *, arm, &bmain->armatures) {
+        do_version_bones_inherit_scale(&arm->bonebase);
+      }
+    }
   }
 }
index c4c10549da3ef3a3518ee1262d70a5a19d35f5db..64857dd1874f594b804e02fb14f3b3b5fa7a15c0 100644 (file)
@@ -908,8 +908,10 @@ static void bones_merge(
   newbone->parent = start->parent;
 
   /* TODO, copy more things to the new bone */
-  newbone->flag = start->flag & (BONE_HINGE | BONE_NO_DEFORM | BONE_NO_SCALE |
-                                 BONE_NO_CYCLICOFFSET | BONE_NO_LOCAL_LOCATION | BONE_DONE);
+  newbone->flag = start->flag & (BONE_HINGE | BONE_NO_DEFORM | BONE_NO_CYCLICOFFSET |
+                                 BONE_NO_LOCAL_LOCATION | BONE_DONE);
+
+  newbone->inherit_scale_mode = start->inherit_scale_mode;
 
   /* Step 2a: reparent any side chains which may be parented to any bone in the chain
    * of bones to merge - potentially several tips for side chains leading to some tree exist.
index cd299906b4cd93c6b6f864da60fcc2f847c06ae1..2b18fc15f634ddc587a27413403a142e1dde1855 100644 (file)
@@ -491,6 +491,7 @@ static EditBone *make_boneList_rec(ListBase *edbo,
     eBone->parent = parent;
     BLI_strncpy(eBone->name, curBone->name, sizeof(eBone->name));
     eBone->flag = curBone->flag;
+    eBone->inherit_scale_mode = curBone->inherit_scale_mode;
 
     /* fix selection flags */
     if (eBone->flag & BONE_SELECTED) {
@@ -719,6 +720,7 @@ void ED_armature_from_edit(Main *bmain, bArmature *arm)
     newBone->arm_roll = eBone->roll;
 
     newBone->flag = eBone->flag;
+    newBone->inherit_scale_mode = eBone->inherit_scale_mode;
 
     if (eBone == arm->act_edbone) {
       /* don't change active selection, this messes up separate which uses
index 854fb23792977aa602ade1ca89b2e9fa2b4390d9..52a3f7711c4128f6ccc3f6fc5aadca3e7fda3ee0 100644 (file)
@@ -241,13 +241,21 @@ static void applyarmature_process_selected_rec(bArmature *arm,
 
       /* Parent effects on the bone transform that have to be removed. */
       BKE_bone_offset_matrix_get(bone, offs_bone);
-      BKE_bone_parent_transform_calc_from_matrices(
-          bone->flag, offs_bone, bone->parent->arm_mat, pchan_eval->parent->pose_mat, &old_bpt);
+      BKE_bone_parent_transform_calc_from_matrices(bone->flag,
+                                                   bone->inherit_scale_mode,
+                                                   offs_bone,
+                                                   bone->parent->arm_mat,
+                                                   pchan_eval->parent->pose_mat,
+                                                   &old_bpt);
 
       /* Applied parent effects that have to be kept, if any. */
       float(*new_parent_pose)[4] = pstate ? pstate->new_rest_mat : bone->parent->arm_mat;
-      BKE_bone_parent_transform_calc_from_matrices(
-          bone->flag, offs_bone, bone->parent->arm_mat, new_parent_pose, &new_bpt);
+      BKE_bone_parent_transform_calc_from_matrices(bone->flag,
+                                                   bone->inherit_scale_mode,
+                                                   offs_bone,
+                                                   bone->parent->arm_mat,
+                                                   new_parent_pose,
+                                                   &new_bpt);
 
       BKE_bone_parent_transform_invert(&old_bpt);
       BKE_bone_parent_transform_combine(&new_bpt, &old_bpt, &invparent);
@@ -283,8 +291,12 @@ static void applyarmature_process_selected_rec(bArmature *arm,
 
     /* Include applied parent effects. */
     BKE_bone_offset_matrix_get(bone, offs_bone);
-    BKE_bone_parent_transform_calc_from_matrices(
-        bone->flag, offs_bone, pstate->bone->arm_mat, pstate->new_rest_mat, &bpt);
+    BKE_bone_parent_transform_calc_from_matrices(bone->flag,
+                                                 bone->inherit_scale_mode,
+                                                 offs_bone,
+                                                 pstate->bone->arm_mat,
+                                                 pstate->new_rest_mat,
+                                                 &bpt);
 
     unit_m4(new_pstate.new_rest_mat);
     BKE_bone_parent_transform_apply(&bpt, new_pstate.new_rest_mat, new_pstate.new_rest_mat);
@@ -306,8 +318,12 @@ static void applyarmature_process_selected_rec(bArmature *arm,
       /* Compute the channel coordinate space matrices for the new rest state. */
       invert_m4_m4(inv_parent_arm, pstate->new_arm_mat);
       mul_m4_m4m4(offs_bone, inv_parent_arm, new_pstate.new_arm_mat);
-      BKE_bone_parent_transform_calc_from_matrices(
-          bone->flag, offs_bone, pstate->new_arm_mat, pstate->new_arm_mat, &bpt);
+      BKE_bone_parent_transform_calc_from_matrices(bone->flag,
+                                                   bone->inherit_scale_mode,
+                                                   offs_bone,
+                                                   pstate->new_arm_mat,
+                                                   pstate->new_arm_mat,
+                                                   &bpt);
 
       /* Re-apply the location to keep the final effect. */
       invert_m4(bpt.loc_mat);
index 48d0a5fe8beb78beaf746c96fbf06141e91b2243..173ba65fc47f2bc72862b32cb06ebebaaabd888d 100644 (file)
@@ -73,6 +73,7 @@ typedef struct EditBone {
    * animation are automatically relative to the bones' rest positions*/
   int flag;
   int layer;
+  char inherit_scale_mode;
 
   /* Envelope distance & weight */
   float dist, weight;
index 088fd96a5aec8f75fcd563f260ab749039c7f1bf..68241df3f9345863bcfefdef49c42e91669e62d3 100644 (file)
@@ -60,6 +60,9 @@ typedef struct Bone {
 
   int flag;
 
+  char inherit_scale_mode;
+  char _pad[7];
+
   float arm_head[3];
   /**  head/tail in Armature Space (rest pos). */
   float arm_tail[3];
@@ -229,8 +232,10 @@ typedef enum eBone_Flag {
   BONE_UNKEYED = (1 << 13),
   /** set to prevent hinge child bones from influencing the transform center */
   BONE_HINGE_CHILD_TRANSFORM = (1 << 14),
+#ifdef DNA_DEPRECATED
   /** No parent scale */
   BONE_NO_SCALE = (1 << 15),
+#endif
   /** hidden bone when drawing PoseChannels (for ghost drawing) */
   BONE_HIDDEN_PG = (1 << 16),
   /** bone should be drawn as OB_WIRE, regardless of draw-types of view+armature */
@@ -254,6 +259,20 @@ typedef enum eBone_Flag {
 
 } eBone_Flag;
 
+/* bone->inherit_scale_mode */
+typedef enum eBone_InheritScaleMode {
+  /* Inherit all scale and shear. */
+  BONE_INHERIT_SCALE_FULL = 0,
+  /* Inherit scale, but remove final shear. */
+  BONE_INHERIT_SCALE_FIX_SHEAR,
+  /* Inherit average scale. */
+  BONE_INHERIT_SCALE_AVERAGE,
+  /* Inherit no scale or shear. */
+  BONE_INHERIT_SCALE_NONE,
+  /* Inherit effects of shear on parent (same as old disabled Inherit Scale). */
+  BONE_INHERIT_SCALE_NONE_LEGACY,
+} eBone_InheritScaleMode;
+
 /* bone->bbone_prev_type, bbone_next_type */
 typedef enum eBone_BBoneHandleType {
   BBONE_HANDLE_AUTO = 0, /* Default mode based on parents & children. */
index 7949db9055f2c3588970d4fce01360bc2ca56e12..cfcc37adf6c8878f49f174d19812992a82b29873 100644 (file)
@@ -303,6 +303,40 @@ static void rna_Bone_layer_set(PointerRNA *ptr, const bool *values)
   BKE_armature_refresh_layer_used(arm);
 }
 
+/* TODO: remove the deprecation stubs. */
+static bool rna_use_inherit_scale_get(char inherit_scale_mode)
+{
+  return inherit_scale_mode <= BONE_INHERIT_SCALE_FIX_SHEAR;
+}
+
+static void rna_use_inherit_scale_set(char *inherit_scale_mode, bool value)
+{
+  bool cur_value = (*inherit_scale_mode <= BONE_INHERIT_SCALE_FIX_SHEAR);
+  if (value != cur_value) {
+    *inherit_scale_mode = (value ? BONE_INHERIT_SCALE_FULL : BONE_INHERIT_SCALE_NONE);
+  }
+}
+
+static bool rna_EditBone_use_inherit_scale_get(PointerRNA *ptr)
+{
+  return rna_use_inherit_scale_get(((EditBone *)ptr->data)->inherit_scale_mode);
+}
+
+static void rna_EditBone_use_inherit_scale_set(PointerRNA *ptr, bool value)
+{
+  rna_use_inherit_scale_set(&((EditBone *)ptr->data)->inherit_scale_mode, value);
+}
+
+static bool rna_Bone_use_inherit_scale_get(PointerRNA *ptr)
+{
+  return rna_use_inherit_scale_get(((Bone *)ptr->data)->inherit_scale_mode);
+}
+
+static void rna_Bone_use_inherit_scale_set(PointerRNA *ptr, bool value)
+{
+  rna_use_inherit_scale_set(&((Bone *)ptr->data)->inherit_scale_mode, value);
+}
+
 static void rna_Armature_layer_set(PointerRNA *ptr, const bool *values)
 {
   bArmature *arm = (bArmature *)ptr->data;
@@ -773,6 +807,28 @@ static void rna_def_bone_common(StructRNA *srna, int editbone)
       {0, NULL, 0, NULL, NULL},
   };
 
+  static const EnumPropertyItem prop_inherit_scale_mode[] = {
+      {BONE_INHERIT_SCALE_FULL, "FULL", 0, "Full", "Inherit all effects of parent scaling"},
+      {BONE_INHERIT_SCALE_FIX_SHEAR,
+       "FIX_SHEAR",
+       0,
+       "Fix Shear",
+       "Inherit scaling, but remove shearing of the child in the rest orientation"},
+      {BONE_INHERIT_SCALE_AVERAGE,
+       "AVERAGE",
+       0,
+       "Average",
+       "Inherit uniform scaling representing the overall change in the volume of the parent"},
+      {BONE_INHERIT_SCALE_NONE, "NONE", 0, "None", "Completely ignore parent scaling"},
+      {BONE_INHERIT_SCALE_NONE_LEGACY,
+       "NONE_LEGACY",
+       0,
+       "None (Legacy)",
+       "Ignore parent scaling without compensating for parent shear. "
+       "Replicates the effect of disabling the original Inherit Scale checkbox"},
+      {0, NULL, 0, NULL, NULL},
+  };
+
   PropertyRNA *prop;
 
   /* strings */
@@ -832,9 +888,25 @@ static void rna_def_bone_common(StructRNA *srna, int editbone)
   RNA_def_property_ui_text(prop, "Deform", "Enable Bone to deform geometry");
   RNA_def_property_update(prop, 0, "rna_Armature_update_data");
 
+  prop = RNA_def_property(srna, "inherit_scale", PROP_ENUM, PROP_NONE);
+  RNA_def_property_ui_text(
+      prop, "Inherit Scale", "Specifies how the bone inherits scaling from the parent bone");
+  RNA_def_property_enum_sdna(prop, NULL, "inherit_scale_mode");
+  RNA_def_property_enum_items(prop, prop_inherit_scale_mode);
+  RNA_def_property_update(prop, 0, "rna_Armature_update_data");
+
+  /* TODO: remove the compatibility stub. */
   prop = RNA_def_property(srna, "use_inherit_scale", PROP_BOOLEAN, PROP_NONE);
-  RNA_def_property_ui_text(prop, "Inherit Scale", "Bone inherits scaling from parent bone");
-  RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", BONE_NO_SCALE);
+  RNA_def_property_ui_text(
+      prop, "Inherit Scale", "DEPRECATED: Bone inherits scaling from parent bone");
+  if (editbone) {
+    RNA_def_property_boolean_funcs(
+        prop, "rna_EditBone_use_inherit_scale_get", "rna_EditBone_use_inherit_scale_set");
+  }
+  else {
+    RNA_def_property_boolean_funcs(
+        prop, "rna_Bone_use_inherit_scale_get", "rna_Bone_use_inherit_scale_set");
+  }
   RNA_def_property_update(prop, 0, "rna_Armature_update_data");
 
   prop = RNA_def_property(srna, "use_local_location", PROP_BOOLEAN, PROP_NONE);
index f3bdef75c9c6dc843da67bc3024c081cee63d8d9..2b60dbde39f9f9ac976fffa04d26e0456383a895 100644 (file)
@@ -71,14 +71,15 @@ static void rna_Bone_convert_local_to_pose(Bone *bone,
 
   if (is_zero_m4(parent_pose_mat) || is_zero_m4(parent_arm_mat)) {
     /* No parent case. */
-    BKE_bone_parent_transform_calc_from_matrices(bone->flag, bone_arm_mat, NULL, NULL, &bpt);
+    BKE_bone_parent_transform_calc_from_matrices(
+        bone->flag, bone->inherit_scale_mode, bone_arm_mat, NULL, NULL, &bpt);
   }
   else {
     invert_m4_m4(offs_bone, parent_arm_mat);
     mul_m4_m4m4(offs_bone, offs_bone, bone_arm_mat);
 
     BKE_bone_parent_transform_calc_from_matrices(
-        bone->flag, offs_bone, parent_arm_mat, parent_pose_mat, &bpt);
+        bone->flag, bone->inherit_scale_mode, offs_bone, parent_arm_mat, parent_pose_mat, &bpt);
   }
 
   if (invert) {