More often than one would think, I’ll come across a post on CGTalk or some similar forum where the following question:
“Hi, how do aim constraints work? I’d like to write my own.”
gets the reply:
“Thats stupid. Why would you want to do that? Just use the built-in aim constraint. It’s more efficient.”
I hate that reply. It’s generally true that the internal node or whatever that handles the aim constraint in your software *is*, in fact, much more efficient than rolling your own through scripting or API-level programming, but without knowing *how* the node works internally, without understanding *why* it behaves the way it does, you’ll only ever be able to use that constraint in a limited number of ways.
The examples in this post are going to focus specifically on Maya, but the thought process is the same regardless of your software package. You may not have the same level of control when modifying the aim constraint in your package, however. In that case, knowing how it works so you can build your own and extend upon the behavior that’s there becomes all the more important.
Anyway, on to how aim constraints work. I’ll be using “target” and “constrained” to refer to the two objects connected by the aim constraint.
The first thing that happens when you aim constrain one object to another is that constrained’s position is subtracted from target’s. This gives you a vector in the direction of target that passes through both target and constrained.
Why is this important?
Actually the vector is very important — it’s one of three that are needed to describe the orientation of an object in most 3D packages, due to the fact that matrices are used to hold object transforms. In a 4×4 matrix (where the upper-left corner is referred to as m00, and the bottom right is m33; a two-dimensional array of values), m00 – m02 represents the vector along which the object’s X axis lies. M10 – m12 and m20 – m22 are the Y and Z axes respectively. Each vector must sit at a 90 degree angle to the other two — must be orthogonal — just like their respective axes.
This first axis we’ve gotten can be plugged into one of the matrix axis spots to align the first axis on your object. So if you’re aiming the positive Y axis at target, the vector would go into m1. If you’re aiming the negative Y axis, you can either flip the vector you’ve gotten from the earlier subtraction or, if you’re smart, save the extra calculation and just subtract target from constrained instead.
Alright, one axis is constrained. How about the other two?
Next up is the pole. It’s just another direction vector, and the pole vector axis is snapped to it in the same manner that the first axis was set to aim at the target object. Pole vectors can be calculated in any number of ways: you could create a second vector through subtraction again, use a world vector (such as <0,0,1> for the world Z axis), or even just plug in the direction vector from another object’s matrix directly. Put the pole vector into the matrix for the axis that’s pole vector constrained and we’re halfway there.
Now, remember I said that the three axes in the matrix need to be at 90 degree angles to each other? I’m betting that if you draw out the two axes you’ve currently got plugged into your matrix, they’re not aligned. This is expected. If you create an aim-constrained object and target in a 3D scene right now and move the target around a bit, you’ll see that the pole vector axis will aim along the pole vector, but it won’t often snap to it. The pole vector is actually only there to get the third, unconstrained axis. If you do a cross product on the aim vector and the pole vector, the third angle you get will be at a 90 degree angle to them both. We’ll call this the “unconstrained” vector. This means that the first and final axes are finished. Afterwards, another cross product is done between the aim vector and the unconstrained vector to make sure the pole vector is orthogonal to the other two. While this makes the pole vector-constrained axis not always point exactly along the pole vector, you do have all three axes accounted for and the system is relatively stable. Pop the vectors into your matrix and voila — constrained aims at target.
As far as I know any aim constraint based on Euler angles works like this in most packages.
Now on to what’s interesting.
Take Maya, for example. For years I’ve been using the river script by Michael Bazhutki. In fact, I’m willing to bet there are few riggers out there who haven’t used it from time to time. However, until recently I hadn’t ever stopped to look at how his script works.
If you’ve never used rivet, it basically takes two edges or four vertices and lofts a nurbs surface between them, with construction history on. Then it snaps a locator to the center of that surface. The locator rotates perfectly with the surface even after joint and blendshape deformations, so it’s great for sticking buttons or other decorations to character meshes.
It turns out that the script’s main trick is using Maya’s aim constraint node in a neat way: he gets the surface normal from the nurbs surface at the position he wants through a pointOnSurfaceInfo node, uses it as the aim vector, then uses the same pointOnSurfaceInfo node to get the nurbs surface’s tangent vector at the same point and plugs that into the constraint’s aim vector slot. Since the constraint doesn’t care what vectors get plugged into it, and since a nurbs surface at any point can be evaluated to get three orthagonal axes (not unlike the X, Y, and Z axes), this works out great. It also keeps working regardless of how the mesh bends since all the construction history is kept, forcing updates down the line as a character deforms.
This is a trick I’ve used in the past few weeks on our current project at work, and it’s something I plan to expand upon in the coming months. It’s also something that’s gotten me thinking: just what else could I do if I, instead of using the constraint commands to constrain objects together, just created nodes in Maya and used the connections in ways the developers hadn’t envisioned?
But the better question is: how would someone even know to pull apart a set of node connections to a constraint if they didn’t know, roughly, how that constraint works?
I hope this helps someone. As soon as I have time, the next topic I want to write about is something I did with vectors over the weekend: replicating the smear effect from the Pigeon Impossible blog in Cinema 4D.
#1 by Kirk on 2010/10/30 - 3:47 pm
This sounds like a great explanation for something that I’ve been trying to figure out for a while, however, I have a question.
I understand a little bit about matrix math, but after carefully following your instructions to create the 4×4 matrix, I don’t understand how to use that matrix to get the Euler rotations that the constrained object needs to assume in order to aim at the target.
Could you help me understand?
Thanks so much,
Kirk