Thursday, January 3, 2013

FAQ - Constraints: The good, the bad, and the gotchas

In response to a recent post on BlenderArtists about the state of various constraints, I thought I'd take some time to clarify the current status of various constraints. In particular, those which are likely to cause some confusion.

Disclaimer: This is not a complete list. It only includes the most common problems people are likely to encounter.


Rotations and Gears
A perennial problem many people encounter relates to copying rotations, specifically "making a bunch of things rotate at a speed relative to a particular thing". In other words, gears.


For a comprehensive breakdown about why we have these problems, read my previous write up on the issue:
http://aligorith.blogspot.co.nz/2010/09/rigging-faq-constraints-and-rotations.html

In short - if doing Gears:
  • Copy Rotation and Transformation constraints are the wrong tools for the job
  • Use Drivers instead for now

Tracking
Another common rigging problem which arises is: "making something point at a target". For example, a gun turret following targets in a no fly zone, or for the "eye control" targets commonly seen in many rigs.

I had started drafting an article about this (embarrassingly, some 2-3 years ago now) which was aimed at clarifying these issues and explaining how it all works.

For now, here's the short version:
  • Classic "Track To" is pathetic. It works most of the time, but completely flips out (i.e. it just goes into a crazy spin) once the target gets near 90-degrees (i.e. directly above) the object tracking it, since the math breaks down. In other words, it is nearly completely unusable/unsuitable for production rigging, and is broken by design (TBH, from the code, it's nearly impossible to understand how it's even supposed to work!). Instead, we recommend that you use...
  • "Damped Track" (new in 2.5+). Like classic Track To, Damped Track is for general-purpose tracking (i.e. eye control targets, etc.) However, the key difference is that it works a lot better, is simpler to use, and the way that it works can be easily visualised/understood visually.
  • "Locked Track" is not used too often. The main use case is for things like billboard trees, where the billboards must rotate to face the camera, but must otherwise stay standing perfectly straight (lest they look like they're all falling down in a strong breeze). The "lock" axis defines the axis around which the billboard rotates around so that it faces the target (e.g. "z" in the billboard tree example).
Here's the even shorter version:
Just use "Damped Track" if you want to make one thing (e.g. an eye) point/look at another thing (e.g. a target control, such as an empty).

Oh, and one more thing for the mech-rigging crowd: Bidirectional/Mutual Relationships (e.g. the two parts of a piston staying in line with each other) cannot be represented in the simple ways that you may have thought. See below for notes about Limit Distance constraints.

Curve/Path Following Constraints
There are a few constraints which make objects/bones move relative to curves. These are: Follow Path, Clamp To, and Spline IK.

Path Following Method
In particular, there seems to be some confusion about how these constraints "do their magic". Specifically, what happens when within a single curve object/datablock, you have a few separate curves. How does Blender choose which one it uses (and, why just this one)?

Let's take a step back, and look at how the Follow Path constraint works on a simple curve/path. Understanding this, it's easy to see how all the others work, as all of these were built on top of the very same base mechanism that Follow Path uses.

So, you've got a scene that you want to perform a camera fly-through. What do you do? Well, you create a curve, and add a Follow Path constraint to the camera which uses that curve as the path on which it runs. When you've done that, you may see that in the Graph Editor, you've now got a F-Curve for a "Evaluation Time" property on Curve.Path, which goes from 0.0 to 1.0. What's that, where's it coming from, and why 0 to 1?

The answers like here:
Yep, the "Path Animation" panel in the Curve datablock. As you can see, there's an "Evaluation Time" property here, that by default will have a value of 0.0. But what does this value do exactly?

Well, the "Path Animation" panel actually corresponds to an internal copy of the curve data that Blender makes (if the checkbox in the header of this panel is enabled). This tessellated curve data, known as "Path", is used and required by the Follow Path constraint (and the other curve following constraints).

The "Frames" property in the UI corresponds to how many data points along the curve Blender samples (a higher number means more points sampled = a smoother path to follow). This was necessary in the past as each sample on the path was meant to correspond to the position of a target along the curve on a particular frame in the animation (hence the name; setting this too low would mean that the path followed by the target would not actually match the curve, as it may be just linearly interpolating or straight-line averaging the position of two known samples to find the required position instead of simply looking up the right value along the actual curve).

BTW, in case you weren't aware, curves do have a direction. Next time you're in Edit Mode, pay careful attention to those spikey bits that come out the sides of the curve, making it look like a fish skeleton. These define the direction of the curve. If this is opposite what you intended, use the "WKEY -> Switch Direction" operator.

Hence, the "Evaluation Time" value actually corresponds to how far along this chain of samples we travel (you can visualise this as a long narrow hose with little markings on it, with the "evaluation time" being how far the liquid has travelled along the hose and past those markings). To find out where a point is somewhere along this path, we simply use the evaluation time to "fill" the hose up to the relative point along the hose. If prior to filling the hose, we put a little bead in the hose, the position (in 3d space) where the bead ends up after being pushed down the hose by the ET liquid is the point we wanted to find. Thus, "Evaluation Time" is the decimal proportion of the hose that's been filled up (remember 0% = 0.0, 100% = 1.0, n% = n/100).

Now, back to the original question. So far, we've just been dealing with a single curve, which we tessellate into the "Path" hose. What if we've got more than one curve in the curve datablock? Well, look at it this way: can we have water flowing through several separate (and unconnected hose segments)? No, we can only do the first. That's what we're doing here.

Clamp To
The Clamp To constraint was designed to project the location of an object along one axis of the curve's boundbox (choose the longest side for this), taking the relative location along this as a decimal to act as the ET value. This is then fed into the Path evaluation mechanism to find the nearest location of the object along the curve.

The intention of this constraint was to allow you to constrain the movement of an object to a particular curved trajectory, while still allowing it to move. For example, eyelid fine-tweak controls moving in an arc, while still following a gross eyelid movement control's up/down direction.

As such, it can only use curve objects as the "target" of the constraint, so the box may appear empty otherwise.

Spline IK
The Spline IK constraint simply takes all this to the extreme: Firstly, it tries to map joint locations to 0-1 parameters along the bone-chain (binding step). From these joint binding parameters, it finds the corresponding 3D-space locations for the actual joints using the Path mapping mechanism again, then solves the bone transforms such that they allow the bones to have joint locations as specified.


Easy (* conditions apply)! Just one weekend of hacking away :P

Surface Tracking
Now, if you actually want an object to be able to travel+stick around the surface of another object instead, then you actually want to be using the "Shrinkwrap" constraint, which is based on the technology behind the Shrinkwrap modifier/tools.

Don't use the Clamp To constraint for this. It wasn't designed for it at all. In fact, the Clamp To constraint is older/simpler tech which was around several releases before Jaguarandi coded the Shrinkwrap stuff.

Limit Distance Constraints
The Limit Distance constraints allow you to try to set limits on how far away an object can get away from another. However, due to the way the dependency graph and constraint systems work, there are a few notable gotchas which will sting and perhaps confuse you. I've included this example here to illustrate these two important points:
  • Bidirectional/Mutual Relationships between two entities are not possible, as they end up causing cyclic dependencies (there's no single order that can be defined if you try and make them point at each other for example - lack of granularity)
  • The Constraint Stack executes as a once-over, top to bottom, last-one-wins effect stack
There are some consequences to these two points. Each constraint simply executes once. That means that at the time when it executes, it ensure that its requirements are satisfied (even if no-one else's are, or if that change will now break earlier relationships). That is, we don't iterate to try and balance out everything. In the case of dependency cycles, the need to ensure a single unique ordering means that bidirectional setups may not be mutually consistent with each other (i.e. there will be lagging).

Knowing these facts, you may have to change your rigging approach. Namely, try reversing a few setups so that all the dependencies flow in a single direction with no loops.

Transform Copying/Inheriting
These work pretty straightforwardly. Select what you want, and copy.

A few words of caution though:
  • "Offset" option is a bit of a hack, and may not always give the desired results. Still, it's better than absolutely no control if you need it.
  • Avoid influence < 1.0 for Copy Rotation and Child Of. This just doesn't work, and you're going to be in for a lot of pain - blame the rotation decomposition problems and the weirdness of trying to blend crazyspace matrices together.... Ewww!
  • Child Of Constraints should only be used with all transforms enabled. Disabling anything can give weird results.
  • Use Copy Transform constraint instead of three separate Loc/Rot/Scale constraints for copying all transforms. This constraint (added during Durian IIRC) is simply more efficient.

Action Constraints
When all else fails, they are your saviours. Simply keyframe the exact movements you want, and hook these up to some transform (location works best).

Slightly less portable than other methods as they are more labour intensive, but give you ultimate control over the results. You may have also noticed heavy use of these for the Tears of Steel rigs (IIRC).

Avoid Pivot Constraints
AFAIK, there's only one constraint that's turned out to be not really ready for the prime time: Pivot Constraint. It's turned out to be far too hard to build a generic solution that works for all the different cases where pivoting is required.

2 comments:

  1. Thank you so much for this post. I am keeping it for reference as it will save so much time and frustration.

    ReplyDelete
  2. Very useful post - thank you. One thing - I can't get Damped Track to work for cameras - they end up rotated around the camera Z axis. Any ideas? or just keep using Track To?

    ReplyDelete