Posts Tagged ‘ matrix

## Find Euler rotation values of Maya matrix

Sparse posting lately:  New home + new job = very busy

From over on my Mel Wiki, I’ve finally figure out (thanks to this post) how to extract Euler rotation values from a Maya matrix via the OpenMaya Python API.  Not sure why I’ve wanted to know how to do this, but I’m glad I now can.

Note: Below is all updated bloggin’ goodness (as of 2011-02-18)  from the original post, since I’ve learned some new tricks:

—-

After learning more from the API, I realized the differences between the MMatrix class and the MTransformationMatrix class:  MMatrix purely holds matrix data, not unlike a MVector storing vector data and nothing else. the MTransformationMatrix does everything a MMatrix does, but it has knowledge of how Maya wants it, so provides many convenience methods for accessing the data and interaction with a Maya transform node..

The big takeaway I found is this:  MTransformationMatrix objects will keep track of rotations past 360 degrees & rotation order of the node, while MMatrix objects will not. It makes sense: A matrix itself stores orientations as vectors, which have no concept how how far they’ve been ‘rotated’, so the MMatrix object doesn’t track this data. But behind the scenes, the MTransformationMatrix does track this info, which the below examples illustrate.

One more very important piece of data: When extracting a MTransformationMatrixfrom a node via a MFnTransform function, what you’re actually getting is the local transformation matrix of that node (since the MTransformationMatrix appears to track transformations specific to that node, relative to its parent).  So if you were expecting the world-space matrix, prepare to be disappointed. You can build a MTransformationMatrix out of a worldspace-acquired MMatrix, but at that point ‘rotations past 360’ would be lost.

# Python code
import math
import maya.cmds as mc
import maya.OpenMaya as om

# Define a node to pull a matrix from.
node = 'pCube1'
# Set some rotation values for comparison later:
mc.setAttr('%s.rotate'%node, 15, -45, 1000)
mc.setAttr('%s.scale'%node, 3, 3, 3)
# Change the rot order, to make sure returned euler values are correct:
mc.setAttr('%s.rotateOrder'%node, 3)

Method A: Using MTransformationMatrix

This, in my opinion, is the best solution. Via the API, you get an MDagPath object for your node, wrapper it in a MFnTransform object, and then directly extract a MTransformationMatrix object from it. This object tracks the rotation order (so you don’t have to), and rotation values past 360.

#-------------------------------------------
# Part 1: Get a MTransformationMatrix from an object for the sake of the example.
# You can use your own MTransformationMatrix if it already exists of course.

# get a MDagPath for our node:
selList = om.MSelectionList() # make a sel list # MSelectionList
selList.add(node) # add our node by name
mDagPath = om.MDagPath() # create an empty dag path # MDagPath
selList.getDagPath(0, mDagPath) # fill the dag path with our node

# Create a MFnTransform object for our MDagPath,
# and extract a MTransformationMatrix from it:
transformFunc = om.MFnTransform(mDagPath) # MFnTransform
mTransformMtx = transformFunc.transformation() # MTransformationMatrix

#-------------------------------------------
# Part 2, get the euler values
# Get an MEulerRotation object
eulerRot = mTransformMtx.eulerRotation() # MEulerRotation
# note, we *don't* have to set the rot order here...

# Convert from radians to degrees:
angles = [math.degrees(angle) for angle in (eulerRot.x, eulerRot.y, eulerRot.z)]
print angles, "MTransformationMatrix"

Method B: Using MMatrix

While this method works well, it doesn’t track rotation values past 360: We create a MMatrix object directly from the worldMatrixattr queried on our node. We then convert that to a MTransformationMatrix object so we can extract the MEulerRotation object. However, the MMatrix doesn’t pass along the rotation order of the node, and has no knowledge of rotations past 360, so the resultant MTransformationMatrix won’t know that stuff either. To get accurate rotation values, we have to save the rotation order value ahead of time, and then apply it back before retrieving values from our MEulreRotation object.

#-------------------------------------------
# Part 1:  Get a MMatrix from an object for the sake of the example.
# You can use your own MMatrix if it already exists of course.

# Get the node's rotate order value:
rotOrder = mc.getAttr('%s.rotateOrder'%node)
# Get the world matrix as a list
matrixList = mc.getAttr('%s.worldMatrix'%node) # len(matrixList) = 16
# Create an empty MMatrix:
mMatrix = om.MMatrix() # MMatrix
# And populate the MMatrix object with the matrix list data:
om.MScriptUtil.createMatrixFromList(matrixList, mMatrix)

#-------------------------------------------
# Part 2, get the euler values
# Convert to MTransformationMatrix to extract rotations:
mTransformMtx = om.MTransformationMatrix(mMatrix)
# Get an MEulerRotation object
eulerRot = mTransformMtx.eulerRotation() # MEulerRotation
# Update rotate order to match original object, since the orig MMatrix has
# no knoweldge of it:
eulerRot.reorderIt(rotOrder)

# Convert from radians to degrees:
angles = [math.degrees(angle) for angle in (eulerRot.x, eulerRot.y, eulerRot.z)]
print angles, "MMatrix"

Running the above code prints (and I reformatted the float precision issues, just so it’s easier to compare to the above values):

[15, -45, 1000] MTransformationMatrix
[15, -45, -80] MMatrix

As you can see, the MTransformationMatrix stores the rotation values past 360 deg, while the MMatrix doesn’t.

## The Matrix

Since I never ‘went to school’ for any kind of programming, I’ve had to pick it up as needed throughout my career.  One of the ‘great mysteries’ has been matrix math.  I’m a visual person, and that includes learning math.  Last night, while reading chapter 9 in my PyGame book ‘Beginning Game Development with Python and PyGame: From Novice to Professional‘, the whole ‘matrix thing’ finally made sense in my brain.  So much in fact, I couldn’t fall asleep I was mulling it over so much.  Waking up, I decided to dive into it, and made some psuedo-code showing how matrix multiplication happens.  But it was still really hard to physically visualize what was going on.  So, working in GIMP, I made a graphical layout of what is going on.  It is a work of beauty

Couple things are presumed:  It is a description of multiplying two 4×4 matrices, with their translation along the bottom row, not the right column.  In each of the sixteen blocks, the sub-matrices (‘matrixA’ on the left, ‘matrixB’ on the right) are multiplied left to right, and added top to bottom to get the final result of each block.  Time for a t-shirt!

Click through (twice) for a larger version.
You can find it on flickr here as well.