Maya matrix nodes - Aim constraint

by jgrow posted 82 days ago | 2 comments

Hey everyone, I've been loving the current swell of rigging with matrix nodes and cutting clutter out of rigs with resources like BindPose and Cult of Rig. This may have been covered in a cult of rig, I haven't gotten around to watching all of them yet, but I've been trying to make an aim constraint using matrices but haven't been too successful yet. Has anyone successfully done this and can give me some pointers?

by TehJoran posted 49 days ago

Hi jgrow, I just registered to the website and I see that your post is 33 days old, so maybe you've figured out the answer to your problem already. ^^' But here is some information just in case.

The key to knowing how to build an aim constraint with vectors and matrices is just to understand what a maya matrix transform is about: Three orientation vectors. The X, Y and Z axes, which make up the overall orientation of your object, but also contain the shearing and scaling information in them, and a position.

What you need for an aim constraint is a first aiming vector which is just a translation between two points (from your aiming object to the target) So aimVector = driverPosition - drivenPosition

Then, you need another vector which would be the up vector of your constraint. You can either set it to a certain static value like (0,1,0) or give it a dynamic value which can be computed in two different modes: object up and object rotation up. To get an object up behaviour which acts like a pole vector, you need to get another vector from your driven object towards your up object. So upVector = upObjectPosition - drivenPosition To have an objectRotationUp behaviour, you just need to get it directly from you up object matrix (you can use a pointMatrixMult or a vectorProduct node to extract that vector)

Now comes the fun part: vector operations

What you're after in the end is a composed matrix which you can decompose into transform values. Meaning that you want the X, Y and Z vectors to get your final orientation. They need to be orthonormal and orthogonal to not have any shearing or scaling occuring (which are basically just the axes with variable lengths and not normal to one another)

For that, you will want to use your two input vectors - normalized -, so that you can get the output vectors you are interested in as unit vectors. So you need to shrink or extend those vectors that you have into unit vector to be able to do cross products between them. You can use vectorProduct nodes for that, connect to the first input, set it to "noOp" and tick normalize output.

Once you've done that, you can calculate your third vector (which I usually call "side" vector, as I don't know a better name for it, you could go for "tertiary" I suppose) This vector will be the cross product between the first two (normalized) vectors that you have, so:

norm(sideVector) = norm(aimVector) x norm(upVector) (knowing that this operation is not commutative, so the order in which you do it matters)

If you reverse the order, you get a vector in the opposite direction, since you have two normal directions to a couple of two vectors. You can use the "right-hand rule to find about the direction of your cross product.

If you do c = a x b with a represented by your index, b by your middle, c will be represented by your thumb stretched out, its orientation normal to the two other fingers. It might be weird to conceive, said like this, but you can find images online which will hopefully make more sense to you.

You now have two, usable vectors, the normalized aim one, and the calculated "side" one, which is normal to the aim axis. aimOutVector = norm(aimVector) sideOutVector = norm(sideVector) The only axis left is the up vector, that we need to recalculate from these two, as we are not sure that the given one is normal to them (with an orientation of 90° between them):

upOutVector = sideOutVector x aimOutVector (again, the order matters)

And now you just need to compose your matrix from these three "outVectors", using a fourByFourMatrix node. In the case of a default aim X, up Y case scenario, you will connect these attributes: aimOutVector[x,y,z] to "in00", "in01" and "in02" upOutVector[x,y,z] to "in10", "in11", "in12" sideOutVector[x,y,z] to "in20", "in21", "in22"

Hopefully that makes some sense to you. The only things to be careful about, is the space in which to remap this matrix in. If your given vectors are given in worldSpace, the computed matrix will be a worldSpace one, meaning that if you want to drive transform values which are not directly seating under the world origin, you will need to remap this matrix in its parent space by multiply the worldSpace matrix by the worldInverseMatrix of the parent node (or parentInverseMatrix if you prefer). Also, since we need to use the translation of the driven object to calculate the aim vector, it can very quickly become a cycle as we are driving the transform orientation by some of its data. To avoid having this happening, you can compose a matrix from your driven translation, and multiply its parent worldMatrix by it to get the right matrix, without actually using the matrix of the driven transform (which would create a cycle as we are driving its rotation)

And that's pretty much all you need to know. Once you've figured out, what does what and how to use vectors to compose matrices, you can do whatever you want ! Such as using curve tangents as aim vectors, maybe using another curve as an up vector to compose your matrix. The rest is easy.

Good luck ! :) Joran

by jgrow posted 35 days ago

Thanks so much Joran! Sorry for the slow reply That has been bugging me for a while, though I put the thought in the back of my mind. Getting the aim portion of the rotation was easy to figure out, I was struggling getting the up vector aspect of it all this. You're awesome!