0
\$\begingroup\$

I created a level in Unity, and I want to place some of the level objects in Maya so I can create an animation based on these objects.

I can import the model FBX with no problems into Maya, but if I copy the translation, rotation and scale into Maya, the object doesn't get placed correctly. In particular, the rotation doesn't match.

Is there a way to consistently and accurately convert a Unity transform into Maya's coordinate system?

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

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.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .