Thursday, September 16, 2010

[Rigging FAQ] Basics of the New Driver System in 2.5

Monitoring bug reports over the past while, I have noticed that there appear to be some misconceptions about the Driver system in 2.5. As usual, these seem to stem from trying to carry old Blender knowledge straight across to the new system, which inevitably, will cause some problems as aspects of that old system were backwards/fundamentally broken.

So, as will become a common phrase for you in common weeks: "forget the old, in with the new!"

This is by no means a fully comprehensive guide on the Driver system. There are some things I can't cover here, as they'd probably be fodder for their own posts. So, hopefully this guide is enough to highlight some of the main things that you should know about this system.

[EDIT 2011Jan03 - See the extra notes for this post here]


Where do drivers live?
Drivers are no longer stored in the same "IPO-blocks" as animation curves. There aren't even "IPO-blocks" anymore. This was a serious design problem in the old system, where rig data was stored in the same places as motion data, which made it all too easy for animators to break rigs, and was the cause of many obscenities in the code to cope with this mess.

Now, drivers are stored in "AnimData" alongside but not within NLA data and the active action (i.e. keyframe animation lives inside the active action, NLA references actions, but drivers cannot exist within either of these). AnimData is simply a nameless 'container' for animation data that lives on ID-blocks (i.e. Object, Mesh, Material, Texture, Scene, World, Lamp, etc.), defining all the animation and drivers that can be applied to that ID-block and anything directly reachable from that ID-block (i.e. AnimData attached to an Object can access a texture's properties, as long as the Texture is linked to a Material that is linked to the Object. This is important to remember for later).

How do I see my drivers?
Because drivers are no longer stored in the same places as keyframes animation to achieve the separation I talked about earlier, they are also displayed in a different 'context'.

In the Graph Editor, there is a mode selector ("combobox" immediately after the menus) that should by default read "F-Curve Editor", thus indicating that you're currently looking at all Animation/Motion Data. If you change this to "Drivers", you should be able to see only the drivers.

This separation has a number of benefits:
- by separating these, it becomes impossible to accidentally delete a driver while editing animation data
- the meaning of the 'graphs' shown have different meanings (see next section), so showing these together on the same 'plot' actually muddles their meanings

The properties for the active driver can be found in the Properties Region (NKEY). The bulk of what you'll need to do can be found here.

Interpreting the driver 'graphs'
If this sounds any bit like good 'ol mathematics, you're right: it *IS* all math. But, if you've already animated a bit, and used the Graph Editor, then it's not too much of a stretch.

When animating: the X-Axis (horizontal) of the Graph Editor plot corresponds to the frame numbers (i.e. time) and the Y-Axis (vertical) the values of F-Curves on particular frames. So if you like, the graphs for standard animation show how the values of the properties controlled by F-Curves change over time.

For drivers: the X-Axis (horizontal) of the Graph Editor plot corresponds to the values that come out of evaluating the driver's dependencies (i.e. the stuff in the Properties Region), and the Y-Axis (vertical) the corresponding values for given driver "results". So, the graphs for drivers show how the values resulting from evaluating the driver dependencies are mapped to final values for the driver.

Another way of looking at this (closer to how it's implemented), is that instead of time, "Driver F-Curves" (i.e. F-Curves stored in the Drivers data areas, and with driver data attached) evaluate the driver data to obtain an "input" (x-value).

Driver Properties/Settings - General Stuff
The driver properties panel looks a bit like this:
Drivers panel - 'Scripted Expression' for newly added driver

The buttons work as follows:
  • Update Dependencies - Usually you won't need to use this, but if for some reason your drivers aren't updating correctly after changing their settings, try clicking on this to force a refresh
  • Remove Driver - This gets rid of the driver for a property. Unless you're re-rigging or wanting to change your rig a bit, you shouldn't really need this either.
  • Type - This is the 'type' of the driver, or in other words, how the dependencies are combined together to yield an "x-value". See next section.
  • Expr - This field is only relevant when using "Scripted Expression" type of driver. See next section.
  • Show Debug Info - This option shows the values at various points in the driver evaluation pipeline, to make it easier to debug when things are going wrong, and also to help you figure out what you need to do.
  • Add Variable - This adds a new 'Driver Variable', or in other words, a numeric value obtained from one of the dependencies of the driver. See the section on Driver Variables to understand this.
The typical workflow here is that once you've got this panel open, you:
1) Choose the type of driver
2) Add variables
3) If necessary, specify the expression to use for Scripted Expression

Driver Types
The type of driver defines the way that Driver Variables are combined together to yield a single value. As of 2.54, the types of driver available are:
  • Averaged Value - the arithmetic mean (i.e. add up all values and divide by the number of items added) of the values obtained from the variables for the driver
  • Sum Values - simply add up all the values obtained from the variables for the driver
  • Minimum/Maximum Value - find the smallest/largest value (respectively) from the values obtained from the variables for the driver
  • Scripted Expression - calculate the value of the driver by executing whatever Python expression is entered into the "Expr" field.
Most of these are relatively straightforward, but I've found that "Scripted Expression" requires a bit more explaining to help people get the most out of it.
- You can use ANY valid Python expression in this field. So, any standard Python functions, including those in the math module (which have been imported into builtin namespace, meaning that you can just type cos(30) and similar into the field without problems) can be used. This opens up a LOT of possibilities.
- As of the time of writing, there is currently no file that you can dump a set of helper functions to, which are then available for use during driver evaluation. I haven't really seen anyone having a real need for this yet, but I could well be wrong.
- To ensure that everything works properly (i.e. so that Blender's lame but still not totally incompetent Dependency Graph knows about all the dependencies brought about by the driver, so that you don't get any lagging/not-working artifacts), ALL PROPERTIES used by the Driver MUST BE SPECIFIED AS VARIABLES. You then use the names of these variables (yes, they can be named, so please set these to convenient/short names) in the expression. By using short but legible names, you can make the expressions much more easily readable than the old-style expressions people used to have to write. Please DO NOT write stuff like bpy.data.objects["Parent1"].location.z in the expressions, as they will not be picked up as dependencies, and make the expressions hard to read.
- Expressions can be up to 255 characters long, which should be enough in most cases

Driver Variables
A Driver Variable represents a source of a single numeric value, obtained from some source (usually a property) data, which is then used when evaluating the driver it belongs to. They can be named (for use in Scripted Expressions), and act to define a dependency link to the source data they use (for making sure that all updates/changes get refreshed properly).

After adding them, they will appear like Constraints/Modifiers, but in the same panel after the 'Add Variable' button. 

Let's have a look at the available types of driver variables:
1) Single Property - this allows you to use any property (provided it can be turned into a number, i.e. can therefore be animated) anywhere in Blender to act as a dependency for the driver. Use this if you're doing anything other than simple transform-based rigs.
The row labelled "Prop:" defines the ID-block that the property comes from. The box on the left is used to specify the "type" of the ID-block (use this to see what types of data are considered ID-blocks), and then the box on the right lets you enter the name of an ID-block of that type.

The ID-block you specify here will act as the 'base' for the property lookup 'path'. That is, using this is equivalent to having written bpy.data.objects["Cube"]in a script or so. The remainder of whatever you'd have written (i.e. "location.z" for Z-Location of the Cube, as per the previous "bad" example above) then goes into the "Path" box.

Use the tooltips that popup when hovering over the names of properties to work out what they are called. For more "deeply nested" properties, you may wish to use the "Datablocks Viewer" in the Outliner, and follow a similar procedure to the one mentioned here under the tips on driving materials/textures, with the main aim of just finding out the paths used.

Just one word of caution: if you want to use this to get some transform of an object/bone, this will not take constraints into account. This is purely the values the the animation system sets via keyframes, and the values you haven't keyframed yet.

Also, Euler Rotations are in radians not degrees.

2 Transform Channel - this is the one you want to be using for most transform-based driving, and the one which most closely relates to the old driver system in 2.4x

The Ob/Bone row lets you specify the object to use transforms from. If the object you specified is an Armature, you have the extra option of specifying a bone within that armature to get the transform of.

On the second row, the box on the left allows you to specify the X/Y/Z Transform to use.

The "Local Space" option is one that I think needs a bit of explanation, as there are a few gotchas there, especially relating to rotation:
- In general, the "Local Space" option takes the transform properties that the 'Single Property' variable type has access to, instead of using the final constrained location. Thus, stuff like IK is ignored when using this.
- Those who've read this should understand some of the problems facing rotations, and that drivers can solve this problem. Well, when using this type of variable, the rotation_euler (i.e. x/y/z rotation values) that are only affected by the animation system (and you) will be directly used always (not just if Local Space is enabled), if the source data is set to use "Euler" rotations. This is to prevent having to perform the lossy matrix convert stage when it can be avoided. However, for other rotation modes, this conversion will still need to go on as there's no way around this.
- Also, Euler Rotations are in radians not degrees.

3 Rotational Difference - this is best used between two bones in the same armature (i.e. elbow joints). Any other possibility also works, but will not likely give any understandable results.
Ideally, you should specify the same object, but then supply different bone names. The result of this is an absolute value in radians (i.e. it is always a positive value), representing the "angle" between the two bones.

This returned angle is defined by taking the "w" component of the quaternion performing a rotation from the orientations of bone 1 to bone 2, with the orientations being derived from the "pose-space" (i.e. pose-matrix results) for the bones.

4 Distance - this is used for measuring how far away (direction doesn't matter, it's only the relative 'magnitude' of the distance that counts) two points in space are. These points could be objects or bones, in different spaces too. It only matters that there are two points to measure between.

Driver Mapping - Curves and FModifiers
As I mentioned earlier, the visual representation of the driver in the Graph Editor represents how the input values (from the dependencies via variables) are mapped to output effects on the driven property.

By default, there is a F-Modifier (Generator Type) on the driver curve which defines a 1-1 mapping relationship. By adjusting the gradient (i.e. the value in front of the "x" in the panel for this under 'Modifiers'), you can quickly adjust the rate at which the mapping goes (I've got a method for how you can work out what you need to do here that I'll post at some later date).

I have to clarify here though, that F-Modifiers can be used on standard animation curves too to generate similar effects. They are not only restricted to drivers. Also, if you want objects to follow motion from a automatically generated curve, you want to use F-Modifiers on F-Curves (time dependency) vs using Drivers (data dependency).

However, it's perfectly fine to not have any generated curve at all. If you remove the modifier, the driver will still function; it will just implicitly do a 1-1 mapping, though you won't be able to see this in the Graph Editor window, which may be confusing.

Also, after removing the modifier, you can still hand-keyframe a curve to fit, by Ctrl-Click method, if you really insist on doing so.



Well, if you're still reading this, well done for getting this far. If there's interest, I might post some followups to this at a later stage, addressing some of the trickier issues that people have.

4 comments:

  1. Thanks for this explanation - too bad I can't find this in the blender wiki, at least as nicely explained like this.

    ReplyDelete
  2. Hi, I'm trying to figure out how to create a driver to change a material color. Could you help please? :)

    ReplyDelete
  3. roofoo: For driving material settings, you'll need to go through the datablocks editor to ensure that the created drivers are attached to the object (that the material is assigned to) instead of through the material itself.

    Refer to the "datablock" method described under http://aligorith.blogspot.com/2010/12/animating-in-25-getting-to-grips-with.html

    ReplyDelete
  4. Hi Aligorith,

    I also have problems to drive material settings.
    The explanation you gave to roofoo did not help me that much.
    Can you provide some simple screenshots e.g. how to drive the size of a halo material? This would help me a lot!

    ReplyDelete