# Maya matrix nodes – Blending matrices

During the week, I got a comment on the first post in my Maya matrix nodes series – the matrix constraint one – about using the wtAddMatrix node to achieve the multiple targets with blending weights functionality similar to constraints. I have stumbled upon the wtAddMatrix node, but I think it is the fact that Autodesk have made it very fiddly to work with it – we need to show all attributes in the node editor and we have 0 access to setting the weight plug – that put me off ever using it. That being said, when RigVader commented on that post I decided I will give it a go. Since it actually works quite nicely, today I am looking at blending matrices in Maya.

Disclaimer: I will be using the matrix constraint setup outlined in the post I mentioned, so it might be worth having a look at that one if you have missed it.

Another disclaimer: In the comments, Harry Houghton flagged up the fact that the resulting orientation of the setup, when using weights other than half and half, is not comparable to the parentConstraint result, as the wtAddMatrix node does not have a way of controlling the interpolation type.

That being said, you might find that difference beneficial in certain cases, as the resulting rotation doesn’t flip when going beyond 180.

tl;dr: Using the wtAddMatrix we can blend between matrices before we plug the output into a matrix constraint setup to achieve having multiple targets with different weights.

Turns out, the wtAddMatrix is a really handy node. It gives us the chance to plug a number of matrices in the .matrixIn plugs of the .wtMatrix array attribute, and give them weight in the .weightIn plug. That, effectively lets us blend between them.

### Blending matrices for a matrix constraint setup

So, now that we know we can blend matrices, we just need to figure out exactly what do we need to blend.

Let us first have a look at the simpler case – not maintaining the offset.

The group1 on the graph is the parent of pCube1 and is used just so we convert the world matrix into a relative to the parent matrix, without using the parentInverseMatrix. The reason for that is we do not want to create benign cycles, which Raff sometimes talks about on the Cult of Rig streams. Other than that, everything seems to be pretty straightforward.

Bear in mind, the wtAddMatrix node does not normalize the weights, which means that we could have all of the targets fully influence our object. What is more, you could also push them beyond 1 or negate them, which would result in seemingly odd results, but that might just be what you need in some cases.

#### Maintaining the offset

Often we need to maintain the offset in order to achieve the desired behaviour, so the way we do that is we resort to the multMatrix node once more. I am not going in detail, as there are already a couple of ways you can do that outlined in the previous post, but let us see how it fits in our graph.

The two additional multMatrix nodes let us multiply the local offset for the current target by the world matrix of the current target, effectively constraining the object but also maintaining the initial offset.

Now, however clean and simple it may be, the graph gets to be a bit long. What this means is, it is probably getting a bit slower to evaluate as well. That is why, I thought I would do a bit of a performance test to see if there still is any benefit to using this setup over a parentConstraint.

### Performance

The way I usually do my tests is either loop a few hundred times in the scene and build the setup or build it once, save it in a file and then import the file a few hundred times and let it run with some dummy animation. Then I use Maya 2017’s Evaluation toolkit to Run a performance test, which gives us info about the performance in the different evaluation methods – DG, Serial and Parallel. Since, the results vary quite a bit, what I usually do is, run it three times and take the best ones.

In this case, I built the two setups in separate files, both with 2 target objects and maintain offset. Then I ran the tests on 200 hundred imported setups.

So here are the results.

##### Parent constraint
Playback Speeds
===============
DG = 89.8204 fps
EMS = 20.1613 fps
EMP = 59.2885 fps
##### Matrix constraint
Playback Speeds
===============
DG = 91.4634 fps
EMS = 24.6305 fps
EMP = 67.2646 fps

Bear in mind these tests are done on my 5 years old laptop, so the results you are going to get if you are to repeat this test are going to be significantly better.

As you can see even with the extended graph we are still getting about 7.5 fps increase by using the matrix constraint setup with blending matrices. Considering, we have 200 hundred instances in the scene (which is by no means a large number), that means we have about .0375 fps increase per setup, which in turn means that on every 26 setups we win a frame.

### Conclusion

So, there we have it, an even larger part of the parentConstraint functionality, can be implemented by just using matrix nodes. What this means is we can keep our outliner cleaner and get a better performance out of our rigs at the same time, which is a total win win.

Thanks to RigVader for pointing the wtAddMatrix node as a potential solution, it really works quite nicely!

#### 9 Comments on "Maya matrix nodes – Blending matrices"

1. Thanks so much for trying this and measuring the performance, that’s awesome!

This inspired me do my own little scenario 🙂 Here I test the performance of a single target parent constraint vs. a single target matrix constraint (so just a multMatrix and decomposeMatrix node). Also I wanted to see how much a “benign” cycle actually cost me (connecting parentInverseMatrix vs worldInverseMatrix of actual parent).

So I made two hierarchies 100 nodes deep. One with nodes that animate in x, the other with nodes constrained to the first hierarchy. Then I duplicate this hierarchy 4 times, to get 4 entities that are not co-dependent for “parallelization”. I just used the heads up display to compare performance..

Results:
Just animation (no constraints or matrix nodes): 235 fps
With Parent Constraint: 80fps
With Matrix Constraint with benign cycle: 105
With Matrix Constraint without benign cycle: 108

This is on a fairly old i5 with 4 cores and no hyperthreading.

My takeaway is also that matrix nodes are clearly a win (add in a scale constraint and there is no competition). However I’m not so sure about avoiding the benign cycle of parentInverseMatrix. For me the benefit of being able to re-parent something and have the matrix constraint still work, might be worth the performance hit.

Cheers,
Sune

1. Oh nice, it’s great to create deeper hierarchies when doing the tests, since that’s much more similar to what an actual rig would work. If you’ve got some sort of a modular rigging system, the best approach to measure the performance would be to just replace the parentConstraints with the matrix ones inside the modules and then run tests on your actual rig. This way you would get practical results coming directly out of your rigs. That being said, it’s not always very easy to make such changes in the modules, so tests like these are definitely appropriate.

In terms of the results, that’s similar to what I have found as well. I actually tested the benign cycles as well, but did not get any performance difference, to be honest. The reason for that is I think that they become an issue in certain situation with a bit more complicated graphs than what I was doing. I tend to use the parent’s matrix directly, just because I never re-parent stuff once it is scripted and it is a bit nicer to look at in the node editor.

And yeah, it’s great we can grab the scale from the decomposeMatrix for free.

Thanks a lot for sharing your results Sune! The more data, the better!

1. Hi! So, that’s exactly what I meant when I said autodesk have made it hard to work with this node. The .wtMatrix[0] plug is actually a compound and the child that we are interested in is .wtMatrix[0].matrixIn.

If you are trying to connect it in the node editor, then you need to right click on the wtAddMatrix node and click Show all attributes in order for the .matrix[0].matrixIn plug to show up.

That should do it, but if you are still having issues let me know.

2. Hi,

I tried to use the wtAddMatrix to mimic a parentConstraint with two targets, when the weights for both targets were 0.5 it seemed to work but when I changed them to 0.7, 0.3 I got strange behaviour, I think it’s because there’s no choice of the interpolation type on the wtAddMatrix node and this makes the wtAddMatrix not very useful.

1. Hey,

I have actually used the wtAddMatrix node to blend matrices with different weights and I remember it working correctly. I did just test it as well and it did work fine with arbitrary weights. Can you maybe provide a bit more info on your setup, so I can try to help?

1. Sure, just run the following code and then twist driver_0 (locator1) and you’ll see the result is not the same as it would be if you used a parentConstraint:

from pymel import core as pmc

driver_0 = pmc.spaceLocator()
driver_1 = pmc.spaceLocator()
driver_1.tx.set(10)
driven = pmc.spaceLocator()
driven.tx.set(5)

driver_0_offset = driven.worldMatrix.get() * driver_0.worldInverseMatrix.get()
driver_1_offset = driven.worldMatrix.get() * driver_1.worldInverseMatrix.get()

for i, offset, driver, weight in zip((0, 1),
(driver_0_offset, driver_1_offset),
(driver_0, driver_1),
(0.3, 0.7)):
mult_mat = pmc.createNode(‘multMatrix’)
mult_mat.matrixIn[0].set(offset)
driver.worldMatrix.connect(mult_mat.matrixIn[1])

decomp = pmc.createNode(‘decomposeMatrix’)

for channel in (‘Translate’, ‘Rotate’):
decomp.attr(‘output{}’.format(channel)).connect(driven.attr(channel.lower()))

——
Thanks for the blog though, there’s lots of great information.