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.
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 sel_list.add(transform_node) # 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.normalizeIt() # 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)]
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)
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.cmds.refresh() time.sleep(0.25)