RNA: provide access to bone parent transform math from Python.
authorAlexander Gavrilov <angavrilov@gmail.com>
Sat, 8 Dec 2018 06:17:57 +0000 (09:17 +0300)
committerAlexander Gavrilov <angavrilov@gmail.com>
Tue, 11 Dec 2018 17:40:51 +0000 (20:40 +0300)
Applying the effect of bone parent is much more complicated than
simple matrix multiplication because of the various flags like
Inherit Scale. Thus it is reasonable to provide access to this
math from Python for complicated rest pose related manipulations.

The simple case of this is handled by Object.convert_space, so
the new method is only needed for complex tasks.

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

source/blender/blenkernel/BKE_armature.h
source/blender/blenkernel/intern/armature.c
source/blender/editors/transform/transform_conversions.c
source/blender/makesrna/intern/rna_armature_api.c

index e0e1103fe212ac31b8b0014b50fbb89b8b3fd875..3a29dd0c3cb7d0091006d5ce3ca757965ea4a866 100644 (file)
@@ -128,9 +128,26 @@ void BKE_pchan_apply_mat4(struct bPoseChannel *pchan, float mat[4][4], bool use_
 void BKE_pchan_to_mat4(struct bPoseChannel *pchan, float chan_mat[4][4]);
 void BKE_pchan_calc_mat(struct bPoseChannel *pchan);
 
-/* Get the "pchan to pose" transform matrix. These matrices apply the effects of
+/* Simple helper, computes the offset bone matrix. */
+void BKE_get_offset_bone_mat(struct Bone *bone, float offs_bone[4][4]);
+
+/* Transformation inherited from the parent bone. These matrices apply the effects of
  * HINGE/NO_SCALE/NO_LOCAL_LOCATION options over the pchan loc/rot/scale transformations. */
-void BKE_pchan_to_pose_mat(struct bPoseChannel *pchan, float rotscale_mat[4][4], float loc_mat[4][4]);
+typedef struct BoneParentTransform {
+       float rotscale_mat[4][4];       /* parent effect on rotation & scale pose channels */
+       float loc_mat[4][4];            /* parent effect on location pose channel */
+} BoneParentTransform;
+
+/* Matrix-like algebra operations on the transform */
+void BKE_clear_bone_parent_transform(struct BoneParentTransform *bpt);
+void BKE_invert_bone_parent_transform(struct BoneParentTransform *bpt);
+void BKE_combine_bone_parent_transform(const struct BoneParentTransform *in1, const struct BoneParentTransform *in2, struct BoneParentTransform *result);
+
+void BKE_apply_bone_parent_transform(const struct BoneParentTransform *bpt, const float inmat[4][4], float outmat[4][4]);
+
+/* Get the current parent transformation for the given pose bone. */
+void BKE_pchan_to_parent_transform(struct bPoseChannel *pchan, struct BoneParentTransform *r_bpt);
+void BKE_calc_bone_parent_transform(int bone_flag, const float offs_bone[4][4], const float parent_arm_mat[4][4], const float parent_pose_mat[4][4], struct BoneParentTransform *r_bpt);
 
 /* Rotation Mode Conversions - Used for PoseChannels + Objects... */
 void BKE_rotMode_change_values(float quat[4], float eul[3], float axis[3], float *angle, short oldMode, short newMode);
index de4f89fe14658bd870d53afc4dd782bf5c5d9989..0c63d66bb296f5ca11f94b3123429c22253f5cc4 100644 (file)
@@ -1459,9 +1459,8 @@ void BKE_armature_loc_world_to_pose(Object *ob, const float inloc[3], float outl
 }
 
 /* Simple helper, computes the offset bone matrix.
- *     offs_bone = yoffs(b-1) + root(b) + bonemat(b).
- * Not exported, as it is only used in this file currently... */
-static void get_offset_bone_mat(Bone *bone, float offs_bone[4][4])
+ *     offs_bone = yoffs(b-1) + root(b) + bonemat(b). */
+void BKE_get_offset_bone_mat(Bone *bone, float offs_bone[4][4])
 {
        BLI_assert(bone->parent != NULL);
 
@@ -1492,7 +1491,7 @@ static void get_offset_bone_mat(Bone *bone, float offs_bone[4][4])
  *       pose-channel into its local space (i.e. 'visual'-keyframing).
  *       (note: I don't understand that, so I keep it :p --mont29).
  */
-void BKE_pchan_to_pose_mat(bPoseChannel *pchan, float rotscale_mat[4][4], float loc_mat[4][4])
+void BKE_pchan_to_parent_transform(bPoseChannel *pchan, BoneParentTransform *r_bpt)
 {
        Bone *bone, *parbone;
        bPoseChannel *parchan;
@@ -1505,109 +1504,142 @@ void BKE_pchan_to_pose_mat(bPoseChannel *pchan, float rotscale_mat[4][4], float
        if (parchan) {
                float offs_bone[4][4];
                /* yoffs(b-1) + root(b) + bonemat(b). */
-               get_offset_bone_mat(bone, offs_bone);
+               BKE_get_offset_bone_mat(bone, offs_bone);
 
+               BKE_calc_bone_parent_transform(bone->flag, offs_bone, parbone->arm_mat, parchan->pose_mat, r_bpt);
+       }
+       else {
+               BKE_calc_bone_parent_transform(bone->flag, bone->arm_mat, NULL, NULL, r_bpt);
+       }
+}
+
+/* Compute the parent transform using data decoupled from specific data structures.
+ *
+ * bone_flag: Bone->flag containing settings
+ * offs_bone: delta from parent to current arm_mat (or just arm_mat if no parent)
+ * parent_arm_mat, parent_pose_mat: arm_mat and pose_mat of parent, or NULL
+ * r_bpt: OUTPUT parent transform */
+void BKE_calc_bone_parent_transform(int bone_flag, 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) {
                /* Compose the rotscale matrix for this bone. */
-               if ((bone->flag & BONE_HINGE) && (bone->flag & BONE_NO_SCALE)) {
+               if ((bone_flag & BONE_HINGE) && (bone_flag & BONE_NO_SCALE)) {
                        /* Parent rest rotation and scale. */
-                       mul_m4_m4m4(rotscale_mat, parbone->arm_mat, offs_bone);
+                       mul_m4_m4m4(r_bpt->rotscale_mat, parent_arm_mat, offs_bone);
                }
-               else if (bone->flag & BONE_HINGE) {
+               else if (bone_flag & BONE_HINGE) {
                        /* Parent rest rotation and pose scale. */
                        float tmat[4][4], tscale[3];
 
                        /* Extract the scale of the parent pose matrix. */
-                       mat4_to_size(tscale, parchan->pose_mat);
+                       mat4_to_size(tscale, parent_pose_mat);
                        size_to_mat4(tmat, tscale);
 
                        /* Applies the parent pose scale to the rest matrix. */
-                       mul_m4_m4m4(tmat, tmat, parbone->arm_mat);
+                       mul_m4_m4m4(tmat, tmat, parent_arm_mat);
 
-                       mul_m4_m4m4(rotscale_mat, tmat, offs_bone);
+                       mul_m4_m4m4(r_bpt->rotscale_mat, tmat, offs_bone);
                }
-               else if (bone->flag & BONE_NO_SCALE) {
+               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, parchan->pose_mat);
+                       copy_m4_m4(tmat, parent_pose_mat);
                        normalize_m4(tmat);
-                       mul_m4_m4m4(rotscale_mat, tmat, offs_bone);
+                       mul_m4_m4m4(r_bpt->rotscale_mat, tmat, offs_bone);
                }
                else
-                       mul_m4_m4m4(rotscale_mat, parchan->pose_mat, offs_bone);
+                       mul_m4_m4m4(r_bpt->rotscale_mat, parent_pose_mat, offs_bone);
 
                /* Compose the loc matrix for this bone. */
                /* NOTE: That version does not modify bone's loc when HINGE/NO_SCALE options are set. */
 
                /* In this case, use the object's space *orientation*. */
-               if (bone->flag & BONE_NO_LOCAL_LOCATION) {
+               if (bone_flag & BONE_NO_LOCAL_LOCATION) {
                        /* XXX I'm sure that code can be simplified! */
                        float bone_loc[4][4], bone_rotscale[3][3], tmat4[4][4], tmat3[3][3];
                        unit_m4(bone_loc);
-                       unit_m4(loc_mat);
+                       unit_m4(r_bpt->loc_mat);
                        unit_m4(tmat4);
 
-                       mul_v3_m4v3(bone_loc[3], parchan->pose_mat, offs_bone[3]);
+                       mul_v3_m4v3(bone_loc[3], parent_pose_mat, offs_bone[3]);
 
                        unit_m3(bone_rotscale);
-                       copy_m3_m4(tmat3, parchan->pose_mat);
+                       copy_m3_m4(tmat3, parent_pose_mat);
                        mul_m3_m3m3(bone_rotscale, tmat3, bone_rotscale);
 
                        copy_m4_m3(tmat4, bone_rotscale);
-                       mul_m4_m4m4(loc_mat, bone_loc, tmat4);
+                       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)) {
-                       mul_m4_m4m4(loc_mat, parchan->pose_mat, offs_bone);
+               else if (bone_flag & (BONE_HINGE | BONE_NO_SCALE)) {
+                       mul_m4_m4m4(r_bpt->loc_mat, parent_pose_mat, offs_bone);
                }
                /* Else (i.e. default, usual case), just use the same matrix for rotation/scaling, and location. */
                else
-                       copy_m4_m4(loc_mat, rotscale_mat);
+                       copy_m4_m4(r_bpt->loc_mat, r_bpt->rotscale_mat);
        }
        /* Root bones. */
        else {
                /* Rotation/scaling. */
-               copy_m4_m4(rotscale_mat, pchan->bone->arm_mat);
+               copy_m4_m4(r_bpt->rotscale_mat, offs_bone);
                /* Translation. */
-               if (pchan->bone->flag & BONE_NO_LOCAL_LOCATION) {
+               if (bone_flag & BONE_NO_LOCAL_LOCATION) {
                        /* Translation of arm_mat, without the rotation. */
-                       unit_m4(loc_mat);
-                       copy_v3_v3(loc_mat[3], pchan->bone->arm_mat[3]);
+                       unit_m4(r_bpt->loc_mat);
+                       copy_v3_v3(r_bpt->loc_mat[3], offs_bone[3]);
                }
                else
-                       copy_m4_m4(loc_mat, rotscale_mat);
+                       copy_m4_m4(r_bpt->loc_mat, r_bpt->rotscale_mat);
        }
 }
 
+void BKE_clear_bone_parent_transform(struct BoneParentTransform *bpt)
+{
+       unit_m4(bpt->rotscale_mat);
+       unit_m4(bpt->loc_mat);
+}
+
+void BKE_invert_bone_parent_transform(struct BoneParentTransform *bpt)
+{
+       invert_m4(bpt->rotscale_mat);
+       invert_m4(bpt->loc_mat);
+}
+
+void BKE_combine_bone_parent_transform(const struct BoneParentTransform *in1, const struct BoneParentTransform *in2, struct BoneParentTransform *result)
+{
+       mul_m4_m4m4(result->rotscale_mat, in1->rotscale_mat, in2->rotscale_mat);
+       mul_m4_m4m4(result->loc_mat, in1->loc_mat, in2->loc_mat);
+}
+
+void BKE_apply_bone_parent_transform(const struct BoneParentTransform *bpt, const float inmat[4][4], float outmat[4][4])
+{
+       /* in case inmat == outmat */
+       float tmploc[3];
+       copy_v3_v3(tmploc, inmat[3]);
+
+       mul_m4_m4m4(outmat, bpt->rotscale_mat, inmat);
+       mul_v3_m4v3(outmat[3], bpt->loc_mat, tmploc);
+}
+
 /* Convert Pose-Space Matrix to Bone-Space Matrix.
  * NOTE: this cannot be used to convert to pose-space transforms of the supplied
  *       pose-channel into its local space (i.e. 'visual'-keyframing) */
 void BKE_armature_mat_pose_to_bone(bPoseChannel *pchan, float inmat[4][4], float outmat[4][4])
 {
-       float rotscale_mat[4][4], loc_mat[4][4], inmat_[4][4];
-
-       /* Security, this allows to call with inmat == outmat! */
-       copy_m4_m4(inmat_, inmat);
+       BoneParentTransform bpt;
 
-       BKE_pchan_to_pose_mat(pchan, rotscale_mat, loc_mat);
-       invert_m4(rotscale_mat);
-       invert_m4(loc_mat);
-
-       mul_m4_m4m4(outmat, rotscale_mat, inmat_);
-       mul_v3_m4v3(outmat[3], loc_mat, inmat_[3]);
+       BKE_pchan_to_parent_transform(pchan, &bpt);
+       BKE_invert_bone_parent_transform(&bpt);
+       BKE_apply_bone_parent_transform(&bpt, inmat, outmat);
 }
 
 /* Convert Bone-Space Matrix to Pose-Space Matrix. */
 void BKE_armature_mat_bone_to_pose(bPoseChannel *pchan, float inmat[4][4], float outmat[4][4])
 {
-       float rotscale_mat[4][4], loc_mat[4][4], inmat_[4][4];
-
-       /* Security, this allows to call with inmat == outmat! */
-       copy_m4_m4(inmat_, inmat);
-
-       BKE_pchan_to_pose_mat(pchan, rotscale_mat, loc_mat);
+       BoneParentTransform bpt;
 
-       mul_m4_m4m4(outmat, rotscale_mat, inmat_);
-       mul_v3_m4v3(outmat[3], loc_mat, inmat_[3]);
+       BKE_pchan_to_parent_transform(pchan, &bpt);
+       BKE_apply_bone_parent_transform(&bpt, inmat, outmat);
 }
 
 /* Convert Pose-Space Location to Bone-Space Location
@@ -1917,7 +1949,7 @@ void BKE_armature_where_is_bone(Bone *bone, Bone *prevbone, const bool use_recur
        if (prevbone) {
                float offs_bone[4][4];
                /* yoffs(b-1) + root(b) + bonemat(b) */
-               get_offset_bone_mat(bone, offs_bone);
+               BKE_get_offset_bone_mat(bone, offs_bone);
 
                /* Compose the matrix for this bone  */
                mul_m4_m4m4(bone->arm_mat, prevbone->arm_mat, offs_bone);
index 599bff810da3e7f0300b12137f9da3ec7b1bd092..110f51d8fc12667d6997c34b05726aef27ab5abb 100644 (file)
@@ -677,22 +677,22 @@ static void add_pose_transdata(TransInfo *t, bPoseChannel *pchan, Object *ob, Tr
        /* proper way to get parent transform + own transform + constraints transform */
        copy_m3_m4(omat, ob->obmat);
 
-       /* New code, using "generic" BKE_pchan_to_pose_mat(). */
+       /* New code, using "generic" BKE_pchan_to_parent_transform(). */
        {
-               float rotscale_mat[4][4], loc_mat[4][4];
+               BoneParentTransform bpt;
                float rpmat[3][3];
 
-               BKE_pchan_to_pose_mat(pchan, rotscale_mat, loc_mat);
+               BKE_pchan_to_parent_transform(pchan, &bpt);
                if (t->mode == TFM_TRANSLATION)
-                       copy_m3_m4(pmat, loc_mat);
+                       copy_m3_m4(pmat, bpt.loc_mat);
                else
-                       copy_m3_m4(pmat, rotscale_mat);
+                       copy_m3_m4(pmat, bpt.rotscale_mat);
 
                /* Grrr! Exceptional case: When translating pose bones that are either Hinge or NoLocal,
                 * and want align snapping, we just need both loc_mat and rotscale_mat.
                 * So simply always store rotscale mat in td->ext, and always use it to apply rotations...
                 * Ugly to need such hacks! :/ */
-               copy_m3_m4(rpmat, rotscale_mat);
+               copy_m3_m4(rpmat, bpt.rotscale_mat);
 
                if (constraints_list_needinv(t, &pchan->constraints)) {
                        copy_m3_m4(tmat, pchan->constinv);
index 55caac973d8f257d43023534ad21e9b93ae7fb25..cc311fbe1d29605422877feb728f13b04dd0d770 100644 (file)
@@ -42,6 +42,8 @@
 
 #include <stddef.h>
 
+#include "DNA_armature_types.h"
+
 #include "BLI_math_vector.h"
 #include "BKE_armature.h"
 
@@ -57,6 +59,32 @@ static float rna_Bone_do_envelope(Bone *bone, float *vec)
                                  bone->rad_tail * scale, bone->dist * scale);
 }
 
+static void rna_Bone_convert_local_to_pose(Bone *bone, float *r_matrix, float *matrix, float *matrix_local, float *parent_matrix, float *parent_matrix_local, bool invert)
+{
+       BoneParentTransform bpt;
+       float offs_bone[4][4];
+       float (*bone_arm_mat)[4] = (float (*)[4])matrix_local;
+       float (*parent_pose_mat)[4] = (float (*)[4])parent_matrix;
+       float (*parent_arm_mat)[4] = (float (*)[4])parent_matrix_local;
+
+       if (is_zero_m4(parent_pose_mat) || is_zero_m4(parent_arm_mat)) {
+               /* No parent case. */
+               BKE_calc_bone_parent_transform(bone->flag, 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_calc_bone_parent_transform(bone->flag, offs_bone, parent_arm_mat, parent_pose_mat, &bpt);
+       }
+
+       if (invert) {
+               BKE_invert_bone_parent_transform(&bpt);
+       }
+
+       BKE_apply_bone_parent_transform(&bpt, (float (*)[4])matrix, (float (*)[4])r_matrix);
+}
+
 static void rna_Bone_MatrixFromAxisRoll(float *axis, float roll, float *r_matrix)
 {
        vec_roll_to_mat3(axis, roll, (float (*)[3])r_matrix);
@@ -103,6 +131,33 @@ void RNA_api_bone(StructRNA *srna)
        parm = RNA_def_float(func, "factor", 0, -FLT_MAX, FLT_MAX, "Factor", "Envelope factor", -FLT_MAX, FLT_MAX);
        RNA_def_function_return(func, parm);
 
+       func = RNA_def_function(srna, "convert_local_to_pose", "rna_Bone_convert_local_to_pose");
+       RNA_def_function_ui_description(func,
+                                       "Transform a matrix from Local to Pose space (or back), taking "
+                                       "into account options like Inherit Scale and Local Location. "
+                                       "Unlike Object.convert_space, this uses custom rest and pose "
+                                       "matrices provided by the caller. If the parent matrices are "
+                                       "omitted, the bone is assumed to have no parent.");
+       parm = RNA_def_property(func, "matrix_return", PROP_FLOAT, PROP_MATRIX);
+       RNA_def_property_multi_array(parm, 2, rna_matrix_dimsize_4x4);
+       RNA_def_property_ui_text(parm, "", "The transformed matrix");
+       RNA_def_function_output(func, parm);
+       parm = RNA_def_property(func, "matrix", PROP_FLOAT, PROP_MATRIX);
+       RNA_def_property_multi_array(parm, 2, rna_matrix_dimsize_4x4);
+       RNA_def_property_ui_text(parm, "", "The matrix to transform");
+       RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+       parm = RNA_def_property(func, "matrix_local", PROP_FLOAT, PROP_MATRIX);
+       RNA_def_property_multi_array(parm, 2, rna_matrix_dimsize_4x4);
+       RNA_def_property_ui_text(parm, "", "The custom rest matrix of this bone (Bone.matrix_local)");
+       RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+       parm = RNA_def_property(func, "parent_matrix", PROP_FLOAT, PROP_MATRIX);
+       RNA_def_property_multi_array(parm, 2, rna_matrix_dimsize_4x4);
+       RNA_def_property_ui_text(parm, "", "The custom pose matrix of the parent bone (PoseBone.matrix)");
+       parm = RNA_def_property(func, "parent_matrix_local", PROP_FLOAT, PROP_MATRIX);
+       RNA_def_property_multi_array(parm, 2, rna_matrix_dimsize_4x4);
+       RNA_def_property_ui_text(parm, "", "The custom rest matrix of the parent bone (Bone.matrix_local)");
+       parm = RNA_def_boolean(func, "invert", false, "", "Convert from Pose to Local space");
+
        /* Conversions between Matrix and Axis + Roll representations. */
        func = RNA_def_function(srna, "MatrixFromAxisRoll", "rna_Bone_MatrixFromAxisRoll");
        RNA_def_function_ui_description(func, "Convert the axis + roll representation to a matrix");