## Maya matrix nodes – Part 1: Node based matrix constraint

This post is a part of a three post series, where I will try to implement popular rigging functionalities by only using maya’s native matrix nodes.

**Part 1: Node based matrix constraint**- Part 2: Matrix twist calculator (Coming soon)
- Part 3: Matrix rivet (Coming soon)

Following the Cult of rig lately, I realized I have been **very** wasteful in my rigs in terms of constraints. I have always known that they are slower than direct connections and parenting, but then I thought that is the only way to do broken hierarchy rigs. Even though I did matrix math at university, I never used it in maya as I weirdly thought the matrix nodes are broken or limited. There was always the option of writing my own nodes, but since I would like to make it as easy for people to use my rigs, I would rather keep everything in vanilla maya.

Therefore, when Raffaele used the `matrixMult`

and `decomposeMatrix`

nodes to reparent a transform, I was very pleasantly inspired. Since then, I have tried applying the concept to a couple of other rigging functionalities, such as the twist calculation and rivets and it has been giving me steadily good results. So, in this post we will have a look at how we can use the technique he showed in the stream, to simulate a parent + scale constraint, without the performance overhead of constraints, effectively creating a node based matrix constraint.

### Limitations

There are some limitations with using this approach, though. Some of them are not complex to go around, but the issue is that this adds extra nodes to the graph, which in turn leads to performance overhead and clutter. That being said, constraints add up to the outliner clutter, so I suppose it might be a matter of a preference.

#### Joints

Constraining a joint with `jointOrient`

values, will not work, as the `jointOrient`

matrix is applied before the rotation. There is a way to get around this, but it involves creating a number of other nodes, which add some overhead and for me are making it unreasonable to use the setup instead of an orient constraint.

If you want to see how we go around the `jointOrient`

issue just out of curiosity, have a look at the joint orient section.

#### Weights and multiple targets

Weights and multiple targets are also not entirely suitable for this approach. Again, it is definitely not impossible, since we can always blend the output values of the matrix decomposition, but that will also involve an additional `blendColors`

node for each of the transform attributes we need – `translate`

, `rotate`

and `scale`

. And similarly to the previous one, that means extra overhead and more node graph clutter. If there was an easy way to blend matrices with maya’s native nodes, that would be great.

#### Rotate order

Weirdly, even though the decompose matrix has a `rotateOrder`

attribute, it does not seem to do anything, so this method will work with only the `xyz`

rotate order. Last week I received an email from the maya_he3d mailing list, about that issue and it seems like it has been flagged to Autodesk for fixing, which is great.

### Construction

The construction of such a node based matrix constraint is fairly simple both in terms of nodes and the math. We will be constructing the graph as shown in the Cult of Rig stream, so feel free to have a look at it for a more visual approach. The only addition I will make to it is supporting a *maintainOffset* functionality. Also, Raffaele talks a lot about math in his other videos as well, so have a look at them, too.

All the math is happening inside the `matrixMult`

node. Essentially, we are taking the `worldMatrix`

of a target object and we are converting it to relative space by multiplying by the `parentInverseMatrix`

of the constrained object. The `decomposeMatrix`

after that is there to break the matrix into attributes which we could actually connect to a transform – `translate`

, `rotate`

, `scale`

and `shear`

. It would be great if we could directly connect to an input matrix attribute, but that would probably create it’s own set of problems.

That’s the basic node based matrix constraint. How about maintaining the offset, though?

### Maintain offset

In order to be able to maintain the offset, we need to just calculate it first and then put it in the `multMatrix`

node before the other two matrices.

#### Calculating offset

The way we calculate the local matrix offset is by multiplying the `worldMatrix`

of the object by the `worldInverseMatrix`

of the parent (object relative to). The result is the local matrix offset.

##### Using the multMatrix node

It is entirely possible to do this using another `matrixMult`

node, and then doing a `getAttr`

of the output and set it in the main `matrixMult`

by doing a `setAttr`

with the `type`

flag set to `"matrix"`

. The local `matrixMult`

is then free to be deleted. The reason we get and set the attribute, instead of connecting it, is that otherwise we create a cycle.

##### Using the Maya API

What I prefer doing, though, is getting the local offset via the API, as it does not involve creating nodes and then deleting them, which is much nicer when you need to code it. Let’s have a look.

```
import maya.OpenMaya as om
def getDagPath(node=None):
sel = om.MSelectionList()
sel.add(node)
d = om.MDagPath()
sel.getDagPath(0, d)
return d
def getLocalOffset(parent, child):
parentWorldMatrix = getDagPath(parent).inclusiveMatrix()
childWorldMatrix = getDagPath(child).inclusiveMatrix()
return childWorldMatrix * parentWorldMatrix.inverse()
```

The `getDagPath`

function is just there to give us a reference to an `MDagPath`

instance of the passed object. Then, inside the `getLocalOffset`

we get the `inclusiveMatrix`

of the object, which is the full world matrix equivalent to the `worldMatrix`

attribute. And in the end we return the local offset as an `MMatrix`

instance.

Then, all we need to do is to set the `multMatrix.matrixIn[0]`

attribute to our local offset matrix. The way we do that is by using the `MMatrix`

‘s `()`

operator which returns the element of the matrix specified by the row and column index. So, we can write it like this.

```
localOffset = getLocalOffset(parent, child)
mc.setAttr("multMatrix1.matrixIn[0]", [localOffset(i, j) for i in range(4) for j in range(4)], type="matrix")
```

Essentially, we are calculating the difference between the `parent`

and `child`

objects and we are applying it before the other two matrices in the `multMatrix`

node in order to implement the `maintainOffset`

functionality in our own node based matrix constraint.

### Joint orient

Lastly, let us have a look at how we can go around the joint orientation issue I mentioned in the *Limitations* section.

What we need to do is account for the `jointOrient`

attribute on joints. The difficulty comes from the fact that the `jointOrient`

is a separate matrix that is applied after the `rotation`

matrix. That means, that all we need to do is, in the end of our matrix chain rotate by the inverse of the `jointOrient`

. I tried doing it a couple of times via matrices, but I could not get it to work. Then I resolved to write a node and test how I would do it from within. It is really simple, to do it via the API as all we need to do is use the `rotateBy`

function of the `MTransformationMatrix`

class, with the inverse of the `jointOrient`

attribute taken as a `MQuaternion`

.

Then, I thought that this should not be too hard to implement in vanilla maya too, since there are the quaternion nodes as well. And yeah there is, but honestly, I do not think that graph looks nice at all. Have a look.

As you can see, what we do is, we create a quaternion from the joint orientation, then we invert it and apply it to the calculated output matrix of the `multMatrix`

. The way we apply it is by doing a quaternion product. All we do after that is just convert it to euler and connect it to the rotation of the joint. Bear in mind, the `quatToEuler`

node supports rotate orders, so it is quite useful.

Of course, you can still use the `maintainOffset`

functionality with this method. As I said though, comparing this to just an orient constraint it seems like the orient constraint was performing faster every time, so I see no reason of doing this other than keeping the outliner cleaner.

Additionally, I am assuming that there is probably an easier way of doing this, but I could not find it. If you have something in mind, give me a shout.

### Conclusion

Using this node based constrain I was able to remove parent, point and orient constraints from my body rig, making it perform much faster than before, and also the outliner is much nicer to look at. Stay tuned for parts 2 and 3 from this matrix series, where I will look at creating a twist calculator and a rivet by using just matrix nodes.