Now you say: Hi Esteban and learn with me… Im making this page so I can have my learing path accessible and maybe somebody that is in my situation can learn too! Im going from basics to no that basics… at least that the main idea!  


This i are my learning notes… do not consider any of this information valid nor up to date! If you see something wrong or not working please let me know to update the page. Thanks 🙂


Okok… i did lie a bit, i do used matrix nodes from time to time and even in code… i use them but i dont really undertand what is going on… So the real goal is to learn a bit more about them and be able to do fancy stuff with them… 

In my defense: Take a look into the below image and the thumbnail video and pls explain me WTF is the scale in diagonal and the positions on the buttom but in the next thumbnail is diferent.. .where are the rotations and why the fuck is scale a diagonal? So much to understand and so little did they teach me in animation school.

In order to see the matrix a bit easy in Maya I write this simple code to put the desired matrix inside a network node: 

from maya import cmds
from maya.api.OpenMaya import MMatrix

def matrix_in_null(this='', ws = True):
    if this == '':
        this = cmds.ls(sl=True)
    null = cmds.createNode('network', n = this + '_M')
    result_matrix = MMatrix(cmds.xform(this, ws=True if ws else False,q=True,m=True))

    cmds.addAttr(null, ln='OffsetMatrix', at='matrix')
    cmds.setAttr(null+'.OffsetMatrix', result_matrix, type="matrix")
matrix_in_null('', ws=False) 

The most Basics Tutorials to watch:

Child World Matrix = Local Matrix of child * Parent World Matrix

Child Local Matrix = World Matrix of Child * Inverse Parent Matrix


In order to se the matrix we need to use a decompose matrix so its a bit more readable since the way the matrix are working is similar to this:

Matrix : from top to buttom is XYZTranslate, diagonal is the scale and the las tolumn its what made the quadxquad matrix type to normalize it.



sx1 xr xr 0

ry sy1 ry 0

rz rz sz1 0

tx ty tz 1

Decompose Matrix

tx ty tz

rz ry rz

sx sy sz

Ok ok, ill start with the more basic one… a kinda parent constraint! Of course, we can connect all the translate, rotate and scale or just the one we needed.

Why the parent constraints are bad? They give us cycles… because they need the inverse parent matrix information in order to get the child world matrix as we see on the formula on top. If we have a parent we can use their matrix to avoid the cycle.

We are just multiplying the driver matrix * the inverse parent matrix to get the parent to work, then y we want to create a offset we need to parent first the difference between the local matrix and put that as input [0] since the mult order does affect the result.

from maya import cmds
def parent_matrix(this=None, that=None, translate=True, rotate=True, scale=True, offset=True):
    #get data by selection if not assign
    if this == None:
        this = cmds.ls(sl=True)[0]
    if that == None:
        that = cmds.ls(sl=True)[1]    
    #get offset values before connecting everything:
    if offset:
        from maya.api.OpenMaya import MMatrix        
        offset = cmds.createNode('network', n = this + '_Offset')
        this_matrix = MMatrix(cmds.xform(this, ws=True,q=True,m=True))
        that_matrix = MMatrix(cmds.xform(that, ws=True,q=True,m=True))
        result = this_matrix*that_matrix

        cmds.addAttr(offset, ln='OffsetMatrix', at='matrix')
        cmds.setAttr(offset+'.OffsetMatrix', result, type="matrix")
    #create mult matrix to multiply the world matrix * the invert matrix so we got a new worl matrix for the target
    decompose= cmds.createNode('decomposeMatrix', n = this + '_DecomposeMatrix')
    mult_matrix =  cmds.createNode('multMatrix', n = this + '_MultMatrix')

    cmds.connectAttr('{}.worldMatrix'.format(this), '{}.matrixIn[0]'.format(mult_matrix))
    cmds.connectAttr('{}.matrixSum'.format(mult_matrix), '{}.inputMatrix'.format(decompose))
    cmds.connectAttr('{}.parentInverseMatrix'.format(that), '{}.matrixIn[1]'.format(mult_matrix))
    #decide with one do we want to connect
    if translate:
        cmds.connectAttr('{}.outputTranslate'.format(decompose), '{}.translate'.format(that))    
    if rotate:
        cmds.connectAttr('{}.outputRotate'.format(decompose), '{}.rotate'.format(that))    
    if scale:
        cmds.connectAttr('{}.outputScale'.format(decompose), '{}.scale'.format(that))    

    #connect extra offset matrix to the main one
    if offset:
        #shift one connections down becouse mult order is important in matrix
        cmds.connectAttr('{}.OffsetMatrix'.format(offset), '{}.matrixIn[0]'.format(mult_matrix), f=True)
        cmds.connectAttr('{}.worldMatrix'.format(this), '{}.matrixIn[1]'.format(mult_matrix), f=True)
        cmds.connectAttr('{}.parentInverseMatrix'.format(that), '{}.matrixIn[2]'.format(mult_matrix), f=True)

from maya import cmds

def space_switch_matrix(all_this = [], that = '', rotate=True, translate=True):
    #if not inputs use selection
    if all_this == []:
        all_this = cmds.ls(sl=True)[:-1]
    if that == '':
        that = cmds.ls(sl=True)[-1]
    #matrix switch nodes
    #get all offset into a null node
    offset_node = cmds.createNode('network', name = that + '_OffsetsMatrix')
    choice_node = cmds.createNode('choice', name = that + '_Choice')

    for m in all_this:
        this_matrix = MMatrix(cmds.xform(m, ws=True,q=True,m=True))
        that_matrix = MMatrix(cmds.xform(that, ws=True,q=True,m=True))
        result = this_matrix*that_matrix

        cmds.addAttr(offset_node, ln='OffsetMatrix_' + m, at='matrix')
        cmds.setAttr(offset_node+'.OffsetMatrix_' + m, result, type="matrix")

space_switch_matrix(all_this = ['locator1','locator2','locator3'], that = 'pSphere1', rotate=True, translate=True) 

Baisc Setup Orient: 

Spaces > Conditions (Equal to enum)*Desire Amount > Aim Constraint On or Off Target > Target Ctrl

Simple Orient Matrix Setup:

Ideally all the ctrls have the same orientation so it makes more sense for the matrix

Spaces world matrix > Choice node > matrix[0] Mult> Decompose > rxryrz

Ctrl Parent > Matrix [1] Mult


Basic Setup Parent: 

New Locator under each space (Parent Constraint can only suport 1 Offset) > Conditions (Equal to enum)*Desire Amount > Aim Constraint On or Off Target 

Parent Matrix Setup with Offset:

offset_math = spaceMatrix*ctrlMatrix

Offset Math> Choice > matrix[0] Mult > Decompose > rxryrz txtytz

Space Matrix > Choice > matrix[1] Mult

Ctrl Inv Matrix (or parent World Matrix) > matrix[2] Mult


Some more into depth tutorials: