Mati Codes
# _  _ ____ ___ _    ____ ____ ___  ____ ____ #
# |\/| |__|  |  |    |    |  | |  \ |___ [__  #
# |  | |  |  |  |    |___ |__| |__/ |___ ___] #
#                                             #
# _  _ ____ ___ _    ____ ____ ___  ____ ____ #
# |\/| |__|  |  |    |    |  | |  \ |___ [__  #
# |  | |  |  |  |    |___ |__| |__/ |___ ___] #
#                                             #

March 26, 2018


Quaternions are a very effective way of setting orientations or performing rotations, but if you want to do that using Python, you're going to need to leverage the OpenMaya API.

Euler or Quaternions?

Most of the time, when working with rotations in Maya, you're going to be working with Euler (pronounced oiler) angles. They're intuitive to work with, allow for rotations greater than 360 degrees, and provide greater interpolation control between keyframes because of the ability to manipulate keyframe tangents.

Sometimes, you'll need to use quaternions though. Especially when exporting out of Maya to other applications that prefer quaternions or working with other APIs. Quaterions have their benefits too. Quaternions allow for smooth rotations from one orientation to another using the shortest rotation. Thus, they don't suffer from gimbal locking or flipping.

Getting to work

To work with quaternions in Maya using Python, you'll need to use the OpenMaya API. Most of the work is done with MFnTransform and MQuaternion. Let's start out by getting a quaternion that represents a transform node's current orientation:

import maya.cmds
import maya.api.OpenMaya as OpenMaya

# create a cube to work with for this example.
transform_node, shape_node = maya.cmds.polyCube()
# let's rotate it a bit
maya.cmds.setAttr(transform_node+".rotateY", 60)
maya.cmds.setAttr(transform_node+".rotateX", 45)

# create an MSelectionList
sel_list = OpenMaya.MSelectionList()
# pass in the name of the xform node we want the quaternion for
# get the MObject for the xform node
obj = sel_list.getDependNode(0)

# get access to the transform node
xform = OpenMaya.MFnTransform(obj)
# get the rotation as an MQuaternion and store it
quat = xform.rotation(asQuaternion=True)
# normalize the quaternion in place.  you generally want a normalized quaternion.
# quat is still an MQuaternion object. I convert it to a 4 item list 
# to make it easier to work with. [x,y,z,w]
py_quat = [quat[x] for x in range(4)]

Setting a transform's orientation using quaternions

If you want to set the orientation of a transform node using a quaternion, you can use MFnTransform.setRotation or MFnTransform.setRotationComponents. The former takes an MQuaternion object as a parameter and the latter can take sequence of numbers representing x, y, z, w:

'''Continuing on from the example of how to get a quaternion...'''

# I can reset my cube's orientation with an identity quaternion
# which corresponds to "no rotation" or (0,0,0,1)
xform.setRotation(OpenMaya.MQuaternion.kIdentity, OpenMaya.MSpace.kObject )

# I can rotate it back to the rotation I set with getAttr
xform.setRotation(quat, om.MSpace.kObject)
# Or if I want to use my python list, I can use this method.
xform.setRotationComponents(py_quat, om.MSpace.kObject, asQuaternion=True)

Relative rotations using quaternions

If you want to perform a relative rotation instead of a complete orientation update, you can use MFnTransform.rotateBy:

# I'll use the math module to convert degrees to radians
import math
# create a quaternion with an angle of rotation and an axis of rotation
q = OpenMaya.MQuaternion( math.radians(10), OpenMaya.MVector(0,1,0) )
# This will rotate my object to 10 degrees about the world Y axis
xform.rotateBy(q, OpenMaya.MSpace.kTransform)

Lastly, quaternions can be used to interpolate between two rotations using a technique called, Spherical Linear Interpolation (slerp) and the Maya Python API provides a simple function to accomplish that, MQuaternion.slerp.

'''Continuing on from the previous examples...'''

# This gives me a quaternion for halfway between identity and the 
# rotation I set with maya.cmds
half = OpenMaya.MQuaternion.slerp(OpenMaya.MQuaternion.kIdentity, quat, 0.5)
# test it out to confirm
xform.setRotation(half, OpenMaya.MSpace.kObject)

You can take slerp a step further to animate the rotation.

'''Continuing on from the previous examples...'''

# use time to sleep and observe the animation.  otherwise, it happens too fast.
import time

# reset the rotation
xform.setRotation(OpenMaya.MQuaternion.kIdentity, OpenMaya.MSpace.kObject )
# grab a quaternion for a tenth of the rotation
step = OpenMaya.MQuaternion.slerp(OpenMaya.MQuaternion.kIdentity, quat, 0.1)
# rotate by that step 10 times
for x in range(10):
    xform.rotateBy(step, OpenMaya.MSpace.kObject)
    # force the 3D view to redraw


maya tips, openmaya, python, scripting, maya