diff options
-rw-r--r-- | source/blender/blenkernel/intern/fcurve.c | 19 | ||||
-rw-r--r-- | source/blender/blenlib/BLI_math_rotation.h | 2 | ||||
-rw-r--r-- | source/blender/blenlib/intern/math_rotation.c | 43 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_anim_types.h | 6 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_fcurve.c | 15 |
5 files changed, 85 insertions, 0 deletions
diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index c4da2d2efc9..b596eeb9e35 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -1798,6 +1798,25 @@ void BKE_driver_target_matrix_to_rot_channels( quaternion_to_angles(quat, channel); } } + else if (rotation_mode >= DTAR_ROTMODE_SWING_TWIST_X && + rotation_mode <= DTAR_ROTMODE_SWING_TWIST_Z) { + int axis = rotation_mode - DTAR_ROTMODE_SWING_TWIST_X; + float raw_quat[4], twist; + + mat4_to_quat(raw_quat, mat); + + if (channel == axis + 1) { + /* If only the twist angle is needed, skip computing swing. */ + twist = quat_split_swing_and_twist(raw_quat, axis, NULL, NULL); + } + else { + twist = quat_split_swing_and_twist(raw_quat, axis, quat, NULL); + + quaternion_to_angles(quat, channel); + } + + quat[axis + 1] = twist; + } else { BLI_assert(false); } diff --git a/source/blender/blenlib/BLI_math_rotation.h b/source/blender/blenlib/BLI_math_rotation.h index 7a4ac14970f..1e56b80bcf2 100644 --- a/source/blender/blenlib/BLI_math_rotation.h +++ b/source/blender/blenlib/BLI_math_rotation.h @@ -96,6 +96,8 @@ void rotation_between_vecs_to_mat3(float m[3][3], const float v1[3], const float void rotation_between_vecs_to_quat(float q[4], const float v1[3], const float v2[3]); void rotation_between_quats_to_quat(float q[4], const float q1[4], const float q2[4]); +float quat_split_swing_and_twist(const float q[4], int axis, float r_swing[4], float r_twist[4]); + float angle_normalized_qt(const float q[4]); float angle_normalized_qtqt(const float q1[4], const float q2[4]); float angle_qt(const float q[4]); diff --git a/source/blender/blenlib/intern/math_rotation.c b/source/blender/blenlib/intern/math_rotation.c index e4b44240272..5762d164914 100644 --- a/source/blender/blenlib/intern/math_rotation.c +++ b/source/blender/blenlib/intern/math_rotation.c @@ -535,6 +535,49 @@ void rotation_between_quats_to_quat(float q[4], const float q1[4], const float q mul_qt_qtqt(q, tquat, q2); } +/** Decompose a quaternion into a swing rotation (quaternion with the selected + * axis component locked at zero), followed by a twist rotation around the axis. + * + * \param q: input quaternion. + * \param axis: twist axis in [0,1,2] + * \param r_swing[out]: if not NULL, receives the swing quaternion. + * \param r_twist[out]: if not NULL, receives the twist quaternion. + * \returns twist angle. + */ +float quat_split_swing_and_twist(const float q[4], int axis, float r_swing[4], float r_twist[4]) +{ + BLI_assert(axis >= 0 && axis <= 2); + + /* Half-twist angle can be computed directly. */ + float t = atan2f(q[axis + 1], q[0]); + + if (r_swing || r_twist) { + float sin_t = sinf(t), cos_t = cosf(t); + + /* Compute swing by multiplying the original quaternion by inverted twist. */ + if (r_swing) { + float twist_inv[4]; + + twist_inv[0] = cos_t; + zero_v3(twist_inv + 1); + twist_inv[axis + 1] = -sin_t; + + mul_qt_qtqt(r_swing, q, twist_inv); + + BLI_assert(fabsf(r_swing[axis + 1]) < BLI_ASSERT_UNIT_EPSILON); + } + + /* Output twist last just in case q ovelaps r_twist. */ + if (r_twist) { + r_twist[0] = cos_t; + zero_v3(r_twist + 1); + r_twist[axis + 1] = sin_t; + } + } + + return 2.0f * t; +} + /* -------------------------------------------------------------------- */ /** \name Quaternion Angle * diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h index be4850d6779..70add4c156f 100644 --- a/source/blender/makesdna/DNA_anim_types.h +++ b/source/blender/makesdna/DNA_anim_types.h @@ -382,6 +382,12 @@ typedef enum eDriverTarget_RotationMode { DTAR_ROTMODE_QUATERNION, + /** Implements the very common Damped Track + child trick to decompose + * rotation into bending followed by twist around the remaining axis. */ + DTAR_ROTMODE_SWING_TWIST_X, + DTAR_ROTMODE_SWING_TWIST_Y, + DTAR_ROTMODE_SWING_TWIST_Z, + DTAR_ROTMODE_EULER_MIN = DTAR_ROTMODE_EULER_XYZ, DTAR_ROTMODE_EULER_MAX = DTAR_ROTMODE_EULER_ZYX, } eDriverTarget_RotationMode; diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c index 4f8e63e975f..254f3bc3710 100644 --- a/source/blender/makesrna/intern/rna_fcurve.c +++ b/source/blender/makesrna/intern/rna_fcurve.c @@ -141,6 +141,21 @@ const EnumPropertyItem rna_enum_driver_target_rotation_mode_items[] = { {DTAR_ROTMODE_EULER_ZXY, "ZXY", 0, "ZXY Euler", "Euler using the ZXY rotation order"}, {DTAR_ROTMODE_EULER_ZYX, "ZYX", 0, "ZYX Euler", "Euler using the ZYX rotation order"}, {DTAR_ROTMODE_QUATERNION, "QUATERNION", 0, "Quaternion", "Quaternion rotation"}, + {DTAR_ROTMODE_SWING_TWIST_X, + "SWING_TWIST_X", + 0, + "Swing and X Twist", + "Decompose into a swing rotation to aim the X axis, followed by twist around it"}, + {DTAR_ROTMODE_SWING_TWIST_Y, + "SWING_TWIST_Y", + 0, + "Swing and Y Twist", + "Decompose into a swing rotation to aim the Y axis, followed by twist around it"}, + {DTAR_ROTMODE_SWING_TWIST_Z, + "SWING_TWIST_Z", + 0, + "Swing and Z Twist", + "Decompose into a swing rotation to aim the Z axis, followed by twist around it"}, {0, NULL, 0, NULL, NULL}, }; |