[Maya-Python] worldMatrix, pivots, orientations and axes

1,270 views
Skip to first unread message

Marcus Ottosson

unread,
Sep 8, 2020, 6:32:10 AM9/8/20
to python_in...@googlegroups.com

Hi all,

Given a world matrix, how can I convert this into translate/rotate suitable for a node with a potentially edited..

  • rotateAxis
  • rotatePivot
  • rotatePivotTranslate
  • jointOrient, in the case of joints

In addition to being the child of another node with equally edited pivots and axes and orientations.

Here’s what works if the target node has a parent, but zeroed out axes, pivots and orients.

from maya import cmds
from maya.api import OpenMaya as om

def setup():
    cmds.file(new=True, force=True)

    cube1, _ = cmds.polyCube(width=5)
    cube2, _ = cmds.polyCube(width=5)
    cmds.parent(cube2, cube1)

    cmds.rotate(0, 0, 30, cube1)
    cmds.rotate(0, 0, -60, cube2)
    cmds.move(0, 2, 0, cube1)
    cmds.move(5.1, 2, 0, cube2, absolute=True)

    # Now make a new world matrix
    cube3, _ = cmds.polyCube(width=6, height=0.5, depth=0.5)
    cmds.move(4, 2, -2, cube3, absolute=True)
    cmds.rotate(25, 15, 50, cube3)

Starting off with some general setup, this is for a scene that looks like this, where I would like to copy the worldmatrix of one onto the other.

image.png

Here’s some boilerplate for applying a MMatrix onto any node.

def setMatrix(mobj, matrix):
    fn = om.MFnDagNode(mobj)

    # Decompose
    tm = om.MTransformationMatrix(matrix)
    translate = tm.translation(om.MSpace.kPostTransform)
    rotate = tm.rotation()

    # Assign
    tx = fn.findPlug("translateX", False)
    ty = fn.findPlug("translateY", False)
    tz = fn.findPlug("translateZ", False)
    rx = fn.findPlug("rotateX", False)
    ry = fn.findPlug("rotateY", False)
    rz = fn.findPlug("rotateZ", False)

    tx.setDouble(translate.x)
    ty.setDouble(translate.y)
    tz.setDouble(translate.z)
    rx.setMAngle(om.MAngle(rotate.x, om.MAngle.kRadians))
    ry.setMAngle(om.MAngle(rotate.y, om.MAngle.kRadians))
    rz.setMAngle(om.MAngle(rotate.z, om.MAngle.kRadians))

And finally, the meat of our problem.

def copyMatrix(mobj1, mobj2):
    # Parent inverse
    obj2fn = om.MFnDagNode(mobj2)
    parentInverseMatrixPlug = obj2fn.findPlug("parentInverseMatrix", True).elementByLogicalIndex(0)
    parentInverseMatrix = om.MFnMatrixData(parentInverseMatrixPlug.asMObject()).matrix()

    # Get target worldmatrix we'd like cube2 to receive
    obj1fn = om.MFnDagNode(mobj1)
    worldMatrixPlug = obj1fn.findPlug("worldMatrix", False).elementByLogicalIndex(0)
    worldMatrix = om.MFnMatrixData(worldMatrixPlug.asMObject()).matrix()

    # Cancel out the parent matrix
    outMatrix = worldMatrix * parentInverseMatrix

    # Apply the thing and profit
    setMatrix(mobj2, outMatrix)

setup()

selectionList = om.MSelectionList()
selectionList.add("pCube2")
selectionList.add("pCube3")

cube2obj = selectionList.getDependNode(0)
cube3obj = selectionList.getDependNode(1)

# Copy from pCube3 -> pCube2
copyMatrix(cube3obj, cube2obj)

This works just fine. When you run it, you’ll find pCube2 perfectly aligns with pCube3, whilst still being parented to pCube1. Great.

image.png

Problem

Now let’s give pCube2 a custom rotatePivot. We’ll swap out our setup() with this.

def setup():
    cmds.file(new=True, force=True)

    cube1, _ = cmds.polyCube(width=5)
    cube2, _ = cmds.polyCube(width=5)
    cmds.parent(cube2, cube1)

    # Here's our rotate pivot
    cmds.setAttr(cube2 + ".rotatePivot", -2.5, 0, 0, type="double3")

    cmds.move(0, 2, 0, cube1)
    cmds.move(5.1, 0, 0, cube2, relative=True)
    cmds.rotate(0, 0, 30, cube1)
    cmds.rotate(0, 0, -60, cube2)

    cube3, _ = cmds.polyCube(width=6, height=0.5, depth=0.5)
    cmds.move(4, 2, -2, cube3, absolute=True)
    cmds.rotate(25, 15, 50, cube3)

And presto, we’ve got a problem.

image.png

I figure maybe I could somehow include the pivots..

def copyMatrix(mobj1, mobj2):
    # Parent inverse
    obj2fn = om.MFnDagNode(mobj2)
    parentInverseMatrixPlug = obj2fn.findPlug("parentInverseMatrix", True).elementByLogicalIndex(0)
    parentInverseMatrix = om.MFnMatrixData(parentInverseMatrixPlug.asMObject()).matrix()

    # Get target worldmatrix we'd like cube2 to receive
    obj1fn = om.MFnDagNode(mobj1)
    worldMatrixPlug = obj1fn.findPlug("worldMatrix", False).elementByLogicalIndex(0)
    worldMatrix = om.MFnMatrixData(worldMatrixPlug.asMObject()).matrix()

    rotatePivot = obj1fn.findPlug("rotatePivot", False).asMDataHandle().asVector()
    rotatePivotTranslate = obj1fn.findPlug("rotatePivotTranslate", False).asMDataHandle().asVector()

    # Cancel out the parent matrix
    tm = om.MTransformationMatrix(worldMatrix * parentInverseMatrix)
    tm.setRotatePivot(om.MPoint(rotatePivot), om.MSpace.kPostTransform, False)
    tm.setRotatePivotTranslation(rotatePivotTranslate, om.MSpace.kPostTransform)

    # Apply the thing and profit
    setMatrix(mobj2, tm.asMatrix())

But it doesn’t work and also makes my brain spin.

So the question is; given an arbitrary world matrix from node A, how can I apply it to the translate/rotate of node B such that its world matrix is identical?

Roy Nieterau

unread,
Sep 9, 2020, 6:07:57 AM9/9/20
to Python Programming for Autodesk Maya
Without taking the math in account for now. Are you required to do this with the API? :)

Because maya.cmds.xform does take into account pivots, etc. For example this works for your setup:

m = cmds.xform("pCube2", query=True, matrix=True,  worldSpace=True)
cmds.xform("pCube3", matrix=m, worldSpace=True)

(What's with the new Google Groups look/layout? How can I get the code formatting in here?)

Regarding the actual matrix math. Do note that the xform command documentation at the top states the order of transformation that Maya applies. It's quite involved but could help you reproduce it exactly.

Marcus Ottosson

unread,
Sep 9, 2020, 6:24:41 AM9/9/20
to python_in...@googlegroups.com

Thanks for this! It’s the other way around, pCube3 goes into pCube2, but it still works.

Looking closlier, I found that it does something sneaky. It changes the rotate pivot on the destination. Or rather, the rotatePivotTranslate. So rather than just affecting translate and rotate - which is what I want - it most likely takes that worldmatrix and decomposes it into its components, including pivots, and overwrites everything on the destination node. That’s cool, but that’s cheating. :)

The only thing I want out of the worldmatrix is a rotate/translate for the destination. That translates and orients the destination to match the source.

And yes, sadly this needs to happen without cmds.xform, as it’s happening inside the MPxNode::compute of a C++ plugin.

(Code formatting with “Markdown Here”, sent from my mailbox)


--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/d50a8632-3c7c-4ed4-ae88-2cf10dec034bn%40googlegroups.com.

MaksZ

unread,
Sep 9, 2020, 7:43:28 PM9/9/20
to Python Programming for Autodesk Maya
In cmds there is also a neat command 

cmds.matchTransform(object_to_adjust, object_to_match) # flags pos, rot, scl, piv - pivots

But I want to second Marcus' question. I stuck with the same thing. Really curious what the solution might be. 

Marcus Ottosson

unread,
Sep 24, 2020, 4:13:28 AM9/24/20
to python_in...@googlegroups.com
Still stuck on this, making some good progress here:


But still not quite there yet.

--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages