1
$\begingroup$

My program must interpolate between two sets of quaternion fcurves by a given value. Normally, one would SLERP or LERP. However, my program includes the option to 'turn off' the individual x/y/z axes, as well as adjust the overall influence of the rotation.

It is my understanding that I must convert my quaternions to Eulers in order to align the individual axes of two fcurves.

The problem is that the converted Eulers' values shift at specific points in my animation: When the +/- signs of my two quaternations' 'W' axes are misaligned (i.e., when one is positive and the other negative). When this happens, the resulting Euler values aren't property aligned. Given that the behavior seems predictable, I was wondering if there was a predictable, elegant way to solve for it, or alternatively, a different method for disabling axes when dealing with quaternations.

My code:

def_rot_quat = mathutils.Quaternion([fcurve.evaluate(frame) for fcurve in def_rot_fcurves])
          
# Convert to euler
bake_euler = bake_rot_quat.to_euler()
def_euler = def_rot_quat.to_euler()

# disable axes
for axis in range(3):
    if rot_axes_inf[axis] == 0:
        def_euler[axis] = bake_euler[axis]

# Convert back to quaternion
def_rot_quat = def_euler.to_quaternion()            
influenced_rot_quat = bake_rot_quat.slerp(def_rot_quat, influence)

print statement, where the x-axis was disabled

frame: 15.0 (broken result)
bake_rot_quat: <Quaternion (w=-0.0784, x=0.6405, y=0.2995, z=0.7027)>
def_rot_quat (before update): <Quaternion (w=0.0107, x=0.6732, y=0.2165, z=0.7070)>
bake_rot_euler: (-89.99996835353231, -108.69269347023982, -58.57019718955832)
def_rot_euler (before update): (90.00000933466734, -71.30731836890568, 106.95994960988845)
def_rot_euler (after update): (-89.99996835353231, -71.30731836890568, 106.95994960988845)
def_rot_quat (after conversion): <Quaternion (w=0.6732, x=-0.0107, y=-0.7070, z=0.2165)>

frame: 16.0 (correct result)
bake_rot_quat: <Quaternion (w=-0.1036, x=0.6294, y=0.3223, z=0.6995)>
def_rot_quat (before update): <Quaternion (w=-0.0208, x=0.6628, y=0.2462, z=0.7068)>
bake_rot_euler: (-89.99997518372149, -108.69268664005064, -54.45409638437144)
def_rot_euler (before update): (-89.99997518372149, -108.69269347023982, -67.93306148494682)
def_rot_euler (after update): (-89.99997518372149, -108.69269347023982, -67.93306148494682)
def_rot_quat (after conversion): <Quaternion (w=0.0208, x=-0.6628, y=-0.2462, z=-0.7068)>
$\endgroup$
3
  • $\begingroup$ Looks a bit strange for me, can you provide blend file to test? $\endgroup$
    – Crantisz
    Commented Dec 13, 2023 at 22:31
  • 1
    $\begingroup$ @Crantisz drive.google.com/file/d/1qtZiVbfMyilg-KtUPwJ1n9yxM7MLW_d_/… I have everything open where it needs to be, just run the script and you can look at the fcurves as needed. The relevant fcurves are visible - the standard quaternation fcurves within the baked keyframe data, and then the custom fcurves. Click on the 'x', 'y', or 'z', axis buttons in the animation>space switching UI panel to test as needed. Sorry for the messy/long code, it's my first attempt at coding. $\endgroup$ Commented Dec 13, 2023 at 23:15
  • 1
    $\begingroup$ I assume that the Euler transformation of quaternions can lead to different combinations of XYZ (multiple XYZ values can lead to the same rotation), this is what caused you to get this result. How to fix it? I have no idea. First you need to determine what the XYZ axis means in a quaternionic rotation? This is not as simple a question as it seems at first glance. A quaternion does not have axes, instead it is two pairs of real and imaginary numbers that form the model point transformation matrix to bypass the axis rotation $\endgroup$
    – Crantisz
    Commented Dec 13, 2023 at 23:42

1 Answer 1

0
$\begingroup$

I searched through the documentation and found that to_euler() has two conditional arguments: one to specify the euler rotation order, and a second to specify a 'compatible' euler. This solved my problem.

# Convert to euler
euler1 = quat1.to_euler('XYZ')
euler2 = quat2.to_euler('XYZ', euler1)
$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .