It's not trivial.
Maya uses a right-hand coordinate system, while Unity uses left-hand; Maya uses centimeters, while Unity uses meters; Maya also allows you to choose a rotation order for each node, while Unity has a fixed rotation order. When you change the handedness and rotation order, the rotation values won't match in a trivial manner. We need to calculate the correct rotations.
To solve this problem once and for all, you can leverage Maya's Python OpenMaya API, which is compatible with Maya's coordinate system. You can use this function:
import maya.cmds as cmds
import maya.api.OpenMaya as OpenMaya
def applyMatrix(node, targetMatrixValues):
targetMatrix = OpenMaya.MMatrix(targetMatrixValues)
transformationMatrix = OpenMaya.MTransformationMatrix(targetMatrix)
targetX, targetY, targetZ = transformationMatrix.translation(OpenMaya.MSpace.kWorld)
scaleX, scaleY, scaleZ = transformationMatrix.scale(OpenMaya.MSpace.kWorld)
eulerRotation = transformationMatrix.rotation(asQuaternion = False)
eulerRotation.reorderIt(cmds.getAttr(node + '.rotateOrder'))
cmds.setAttr(node + '.translateX', targetX)
cmds.setAttr(node + '.translateY', targetY)
cmds.setAttr(node + '.translateZ', targetZ)
cmds.setAttr(node + '.rotateX', eulerRotation.x * 57.29577951)
cmds.setAttr(node + '.rotateY', eulerRotation.y * 57.29577951)
cmds.setAttr(node + '.rotateZ', eulerRotation.z * 57.29577951)
cmds.setAttr(node + '.scaleX', scaleX)
cmds.setAttr(node + '.scaleY', scaleY)
cmds.setAttr(node + '.scaleZ', scaleZ)
Basically, what the function does is that it takes a 4x4 matrix, creates an MTransformationMatrix
, and outputs the translation, rotation and scaling.
For the rotation, we get Euler angles, and we can even use reorderIt()
to match the rotation to the node's rotation order.
Finally, we convert the Euler angles to degrees, since Maya works in degrees, not radians.
All we have left to do is to export the 4x4 matrix from the Unity transform.
HOWEVER...
We can't simply extract the matrix and expect for it to work as-is. We first need to change the units (cm to m), and change the handedness of the matrix. I have a C# method that does this for me:
private static void UnfudgeMatrix(ref Matrix4x4 matrix)
{
// Convert a matrix from Unity's coordinate system to Maya coordinate system
matrix[1] = -matrix[1];
matrix[2] = -matrix[2];
matrix[4] = -matrix[4];
matrix[8] = -matrix[8];
matrix[12] = -matrix[12] * 100.0f;
matrix[13] = matrix[13] * 100.0f;
matrix[14] = matrix[14] * 100.0f;
}
You could do this on Maya's side instead too. I chose to do it on Unity.
And finally, to extract the matrix, I created a script that takes a transform and outputs the required applyMatrix()
invokation:
private static void Export(Transform t)
{
var m = t.localToWorldMatrix;
UnfudgeMatrix(ref m);
var s = new StringBuilder();
s.AppendFormat("applyMatrix('{0}', [", t.name);
for (int k = 0; k < 16; k++)
{
s.AppendFormat("{0:0.000}", m[k]);
if (k < 15)
s.Append(",");
}
s.Append("])");
s.AppendLine();
Debug.Log(s.ToString());
}
This method outputs the method invokation via Debug.Log()
, but you can do it in any other way as you wish.
The output of this method I then put it into Maya's Python console along with the applyMatrix()
definition, I run it, and it automagically places my object in the correct position!
This approach also works for converting more complicated things, such as skeleton poses. The OpenMaya documentation is your best friend if you want to adapt this code for other purposes.