( I just noticed, my website tool does not support opening the images below in full screen mode yet?! Here are the full size ones )
Sooner or later riggers need to think about speed and efficiency in their rigs. One of the first things they will come across then are matrix constraints. They are meant to replace Maya’s standard constraint nodes with a couple of matrix nodes. The reason to do this is that constraint nodes are rather slow as they are capable of calculating a lot of additional things we most of the time don’t really need and so have a lot of unnecessary overhead.
As this isn’t a new topic, there are already a lot of great articles about most of the constraints. However, most of them just explain how to replace parent, point, orient and scale constraint. If you are looking for an example on a matrix aim constraint, you don’t find too much good stuff. The only solution I found online uses so many nodes that the speed difference is insignificantly or even slower than the aim constraint node itself (what defeats the whole purpose, except you just want to learn more about matrix math). So, I had to come up with my own solution. Turned out digging through Mayas not so common nodes was very helpful (by 'not so common' I mean I have never needed them nor was it necessary for me to look at them).
In this article, I will not go too deep into matrix math as it is not necessary for the solution I came up with. My goal was to imitate the behavior of an aim constraint with three locators and a transform as aim position, target, up and aim object (from now on, I will refer to them as aimPos, up, target and aim). Using locator (shapes) simplifies the process of getting up and aim vector a lot.
The Base Setup
To get the aim (forward) and up vector, I just subtracted the aimPosShape.worldPosition from the targetShape.worldPosition and upShape.worldPosition.
The main calculation is transforming the vectors into euler angles. I could have done this with matrix nodes but there is a faster (and easier) way. Maya comes with a plugin called rotateHelper.mll which contains the rotateHelper node. I took the aim and up vector as inputs and the resulting rotation matrix as output. The node has a rotation attribute that can be connected directly to aim.rotate but there is problem with undoing transformations as the values are set implicitly. The rotate matrix should be multiplied by the aim.parentInverseMatrix to make it ‘ignore’ any rotation in the hierarchy above. The matrixSum (should be called product and not sum) will now be decomposed and the resulting outputRotate plugged into aim.rotate.
Offsets and more...
Two very important things to keep in mind are that a) I can’t define the aim or up axis but there is a work around and b) aim and up in the rotate helper behave different than you might think.
With the default aim axis being y and the default up axis being z, the rotate helper did not meet any of my expectations. If it was just to parent something below, it would have been fine but as I wanted a specific axis pointing at my target locator I needed an offset. This could either be done with a transform node rotated and parented under the aim, or with another rotation matrix that will be applied in the multMatrix node as first element. To get x as aim axis and y as up with the rotate order set to 'xyz', the offset is (90,0,90).
The strange behavior of the up and aim input in the rotateHelper is, that the aim vector behaves like the aim constraint’s up vector and vice verca. That means the z-axis will always point at the target and up controls the twist around the z axis. I am not sure if it’s a bug or a feature... To fix that I just swapped the inputs.
The reason I did not plug the decomposeMatrix.outputRotate directly in the aimPos.rotate was to avoid a cycle. To restore the position from a given transform matrix, all transform channels are needed. That’s the reason why a rotate input affects a translate output (aimPosShape.worldPosition), even though there shouldn't be a difference calculation wise, Maya still detects the cycle. There is a way around this issue with a vectorProduct node but the more utility nodes I use, the slower the constraint gets.
I hope whoever stumbles across this might find it helpful. If you have any questions, just leave a comment.
Cheers
Comments