[QUATERNION] Slerp

647 views
Skip to first unread message

Rémi Deletrain

unread,
Sep 15, 2017, 9:35:08 AM9/15/17
to Python Programming for Autodesk Maya
Hello everyone,

I try to make a slerp between two quaternions.
I have a formula that works, but I encounter a problem with maya ...

This is my slerp function:

def slerp_quaternion(m_quaternion_1, m_quaternion_2, f_weight):

    m_quaternion_1 = m_quaternion_1.normal()
    m_quaternion_2 = m_quaternion_2.normal()

    #   If is equial return first quaternion
    if m_quaternion_1.isEquivalent(m_quaternion_2):
        return m_quaternion_1

    # If the dot product is negative, the quaternions
    # have opposite handed-ness and slerp won't take
    # the shorter path. Fix by reversing one quaternion.
    # dot = dot_product(m_quaternion_1, m_quaternion_2)
    dot = dot_product(m_quaternion_1, m_quaternion_2)
    if dot < 0.0:
        m_quaternion_2.negateIt()
        dot *= -1.0

    #   Weight Blend
    f_scale_1 = 1.0 - f_weight
    f_scale_2 = f_weight

    #   Get Quaternion median
    dot = max(min(dot, 1.0), -1.0)
    f_theta = math.acos(dot)
    f_sin_theta = math.sin(f_theta)

    f_scale_1 = math.sin(f_scale_1 * f_theta) / f_sin_theta
    f_scale_2 = math.sin(f_scale_2 * f_theta) / f_sin_theta

    #   New Quaternion
    a_new_values = []
    for i in xrange(4):
        a_new_values.append(lerp(m_quaternion_1[i], m_quaternion_2[i], f_weight))

    return pmc.datatypes.Quaternion(a_new_values[0], a_new_values[1], a_new_values[2], a_new_values[3])



In Maya, if I take two quaternions from an MFnTransform (getRotation) and I turn a transform on an axis I have:
    - A dot > 0 between 0 ° and 180 °
    - A dot < 0 if I am between 180 * and 270 *.

If I do this in a custom node since a matrix my dowry is always positive no matter what happens ...
In Maya to  get quaternion from a matrix you have to go through the MTransformationMatrix. And that only changes the value of my quaternions once I pass the 180 °.
Maya automatically reverse quaternion ? If is that, how i made a gool slerp ? ...
So, I also have a function to getter the quaternion since any matrix:

def quaternion_from_matrix(m_matrix):

    w = math.sqrt(max(0, 1 + m_matrix(0, 0) + m_matrix(1, 1) + m_matrix(2, 2))) / 2.0
    x = math.sqrt(max(0, 1 + m_matrix(0, 0) - m_matrix(1, 1) - m_matrix(2, 2))) / 2.0
    y = math.sqrt(max(0, 1 - m_matrix(0, 0) + m_matrix(1, 1) - m_matrix(2, 2))) / 2.0
    z = math.sqrt(max(0, 1 - m_matrix(0, 0) - m_matrix(1, 1) + m_matrix(2, 2))) / 2.0

    x *= -1 if x * (m_matrix(2, 1) - m_matrix(1, 2)) >= 0.0 else 1
    y *= -1 if y * (m_matrix(0, 2) - m_matrix(2, 0)) >= 0.0 else 1
    z *= -1 if z * (m_matrix(1, 0) - m_matrix(0, 1)) >= 0.0 else 1

    return OpenMaya.MQuaternion(x, y, z, w)



Michael Boon

unread,
Sep 18, 2017, 3:34:06 AM9/18/17
to Python Programming for Autodesk Maya
It's been a long time since I dealt with quaternions at this level, but if I understand your question correctly, yeah Maya will reverse the quaternion once you pass 180. Actually it's not Maya doing that. A quaternion can distinguish between, say, +270 and -90, but a transform matrix can only encode the resulting angle, so it can't tell the difference between the two. If you want to keep that distinction, you need to stay in quaternions and not use matrices (or Euler angles) at all.

Are you getting better results using your own quaternion_from_matrix code? If so, then I've misunderstood something...

By the way, your slerp function appears to be missing something. Near the end, you lerp, but you don't use your f_scale numbers. Should be scaling by them somewhere? As it is, you're just lerping.

If you can be a bit more specific about what works and what doesn't, I'd be happy to try to help more.

Rémi Deletrain

unread,
Sep 18, 2017, 4:08:51 AM9/18/17
to Python Programming for Autodesk Maya
Ho I'm sorry ... I just see that the code I sent was not the right one ... I did some testing and suddenly the code had become anything ...

def slerp_quaternion(m_quaternion_1, m_quaternion_2, f_weight):

    """
    !@Brief Apply Spherical interpolation between two quaternions.

    @type m_quaternion_1: OpenMaya.MQuaternion
    @param m_quaternion_1: First Quaternion.
    @type m_quaternion_2: OpenMaya.MQuaternion
    @param m_quaternion_2: Second Quaternion.
    @type f_weight: float
    @param f_weight: Value for blending.
    """

    #   Normalize quaternions
    m_quaternion_1 = m_quaternion_1.normal()
    m_quaternion_2 = m_quaternion_2.normal()

    #   If is equial return first quaternion
    if m_quaternion_1.isEquivalent(m_quaternion_2):
        return m_quaternion_1

    # TODO: fixlater
    # If the inputs are too close for comfort,
    # linearly interpolate and normalize the result.
    # if abs(dot) > 0.9995:
    #     pass

    # If the dot product is negative, the quaternions
    # have opposite handed-ness and slerp won't take
    # the shorter path. Fix by reversing one quaternion.
    # dot = dot_product(m_quaternion_1, m_quaternion_2)
    dot = dot_product(m_quaternion_1, m_quaternion_2)
    if dot < 0.0:
        m_quaternion_2.negateIt()
        dot *= -1.0

    #   Weight Blend
    f_scale_1 = 1.0 - f_weight
    f_scale_2 = f_weight

    #   Get Quaternion median
    dot = max(min(dot, 1.0), -1.0)
    f_theta = math.acos(dot)
    f_sin_theta = math.sin(f_theta)

    f_scale_1 = math.sin(f_scale_1 * f_theta) / f_sin_theta
    f_scale_2 = math.sin(f_scale_2 * f_theta) / f_sin_theta

    #   New Quaternion
    a_new_values = []
    for i in xrange(4):
        a_new_values.append(f_scale_1 * m_quaternion_1[i] + f_scale_2 * m_quaternion_2[i])

    return OpenMaya.MQuaternion(a_new_values[0], a_new_values[1], a_new_values[2], a_new_values[3])


I do not get a better result. I wanted to use the function slerp of maya but it is accessible only in c ++ not in python. So I make a slerp function in python. The current problem is that as I can not know if my dot is positive or negative. So I have a flip of 180 that is done on my rotation. I understand better now with your explanation. I use the matrices to retrieve quaternions. So I would have to retrieve the quaternions since the euler rotations?

Michael and Amanda

unread,
Sep 18, 2017, 5:57:55 PM9/18/17
to python_in...@googlegroups.com
If your dot is >0 in that code, and there are no mistakes, then you shouldn't get a flip. If you're seeing a flip, my feeling is that you should be looking for mistakes rather than worrying about the source of the quaternions. You could print out some numbers.
- I'd expect you to get a dot < 0.0 from quaternions-from-matrices sometimes, even though neither matrix can be >180, for example, two matrices that represent rotations of +100 and -100 degrees, their difference would be 200.
- f_theta should be half of the angle between your matrices (in radians). Try some tests with simple rotations and see if it looks correct. It should always be between 0 and pi/2 (90 degrees).

You call negateIt() on m_quaternion_2. That changes the quat in-place. So even outside the function, m_quaternion_2 will be different, which possibly causes trouble. You could try inverting it differently, perhaps invert f_scale_2 instead since that will have the same effect.

Apart from that, I can't see anything wrong with your code.




--
You received this message because you are subscribed to a topic in the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/python_inside_maya/Ot9WVMEZRYA/unsubscribe.
To unsubscribe from this group and all its topics, send an email to python_inside_maya+unsub...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/54eec6b1-e0f0-42e1-af3b-0ee6d4e69d94%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Michael Boon

unread,
Sep 18, 2017, 6:06:10 PM9/18/17
to Python Programming for Autodesk Maya
I just tried this in Maya 2016 and 2018 and it worked in both, so perhaps this is all moot.
import maya.api.OpenMaya as om
q = om.MQuaternion()
print q
p = om.MQuaternion(0.7,0,0,0.7)
p.normalizeIt()
print p
print om.MQuaternion.slerp(p, q, 0.5, 0)
prints
(0, 0, 0, 1)
(0.707107, 0, 0, 0.707107)
(0.382683, 0, 0, 0.92388)
which is correct.

Rémi Deletrain

unread,
Sep 19, 2017, 2:48:16 AM9/19/17
to Python Programming for Autodesk Maya
I tried the Maya slerp but the problem is that I do not have access to it ... That's why I tried to do the slerp.

type object 'MQuaternion' has no attribute 'slerp'.

I tried to make the slerp from the eulers. I get the rotation of my transform and I transform it into quaternion. I do not have a flip as long as I turn on an axis. But if I turn on two axes it is another story.
So, as you told me, the problem is that I recover the quaternion from the matrix. effectively maybe it comes from the fact that I reverse my quaternion with negateIt
One of the solutions could be to decompose the rotation on the three axes (Pitch, Roll, Yaw) from the matrix and then add these rotations weighted.

Michael and Amanda

unread,
Sep 19, 2017, 2:56:27 AM9/19/17
to python_in...@googlegroups.com
If you want to post a complete test, with test matrices (or objects) that show the problem, is be happy to try it and see if I can figure it out.

Why don't you have access to slerp?

--
You received this message because you are subscribed to a topic in the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/python_inside_maya/Ot9WVMEZRYA/unsubscribe.
To unsubscribe from this group and all its topics, send an email to python_inside_maya+unsub...@googlegroups.com.

Rémi Deletrain

unread,
Sep 19, 2017, 3:06:20 AM9/19/17
to Python Programming for Autodesk Maya
I do not know why I do not have access ... :(
I try with Maya 2014, et 2018...

I'll try to prepare a scene and script that goes with it in the day.

thank you very much for your help !

Michael Boon

unread,
Sep 19, 2017, 6:27:41 PM9/19/17
to Python Programming for Autodesk Maya
I think I see your problem with accessing the slerp function.

There are two Maya Python "OpenMaya" APIs.
The old one (1.0) is maya.OpenMaya. maya.OpenMaya.MQuaternion does not have a slerp function.
The new one (2.0) is maya.api.OpenMaya. maya.api.OpenMaya.MQuaternion does have slerp.
The new OpenMaya is much better than the old one in most respects, but they are not compatible so if you have to interface with old scripts using the old API you have to use string node names instead of MFn objects.

Rémi Deletrain

unread,
Sep 20, 2017, 10:25:26 AM9/20/17
to Python Programming for Autodesk Maya
I did not risk using it in this case the ... :) The api 2.0 is really better. I have seen post with people who had a lot of problems with that. That's why we stayed on the 1.0.
I have not yet had time to make a test scene. I have bee overworked since the begenning of the week. I do this as soon as possible

Rémi Deletrain

unread,
Sep 27, 2017, 10:34:41 AM9/27/17
to Python Programming for Autodesk Maya
Michael Boon

I've attached two files to this post. rdTwist is my node. Browse this plugin with pluginManager and open ma file.
I try to make a twist between two rotation. Red handle is base of my rotation. Green handle is my principal rotation and yellow handles is slerped rotation.

But when you rotate hover 180° yellow handles flip. I don't arrived to have continous rotation.
I try to make twist with separate rotation (Roll, Pitch, Yaw) but is same result...

You have any idea ?
rdTwist.py
test_twist.ma

Michael Boon

unread,
Sep 27, 2017, 5:45:01 PM9/27/17
to Python Programming for Autodesk Maya
Oh, I should have spotted that before. I think your problem is that you don't want this bit at all:
if dot < 0.0:
    m_quaternion_2
.negateIt()
    dot
*= -1.0
The whole point of that negation is to get the two quaternions within 180° of each other. If you remove those lines, your quaternions slerp nicely up to 360°. They get messy at 360 though, but I think that is unavoidable.

Is that it?

Rémi Deletrain

unread,
Sep 28, 2017, 2:35:40 AM9/28/17
to Python Programming for Autodesk Maya
Oh it's so simple.... :(
Thank you very mush !

But effectively after 360° I have a flip again...
I Try to get difference between last rotation and new rotation for add rotation.
With that I think do not have a problem

Reply all
Reply to author
Forward
0 new messages