Friday, February 4, 2011

[Extending 2.5] Coding your own "Builtin Keying Set"

As a companion article to my introduction to Keying Sets, here is a companion article for the more ambitious users out there who want to get a taste for coding their own Builtin Keying Set. By setting up your own "Builtin" Keying Sets, you'll be able to tweak the ways that the Builtin Keying Sets bundled with Blender work to your liking, be able to "mix and match" behaviors and components, and even set up some new tools that fit your personal workflow really nicely.

Prerequisites
Before going any further, you should have some basic knowledge of the Python Programming Language, as we're going to have to make extensive use of it in this tutorial. But if you don't, then don't worry, as it's really easy to pick up (I'll try to include some useful recipes for use in the current Blender Python API, which as of writing - mid January 2011 - is the one which ships with 2.56).

Examples
While following along this tutorial, it'd probably be helpful to have some practical examples of Keying Sets to look at while you read this.
One of the simplest examples is to look at the Script Template for Builtin Keying Sets, from the Text Editor (Text :: Script Templates menu, as seen in the screenshot above). This template illustrates the basic layout you'll need, as well as giving a few useful examples that you can easily adapt to whatever cases you have.


For examples of how "more powerful" builtin Keying Sets are coded, take a look in scripts/keyingsets/keyingsets_builtins.py (actual definitions) and scripts/keyingsets/keyingsets_utils.py (modular components). These are the definitions of the Keying Sets that you'll have been using since you started learning to animate in 2.5.

Although most of these have been written in a modular style, the "Whole Character" one is a bit of an exception, since it has only been added recently and required some more complicated "smartness" that the others lacked. Having said that, it is a nice example to go through for character riggers in particular.


Anatomy of a Builtin Keying Set - 1) "My name is and I am an..."
Just like any other user-definable type in Blender, such as operators and panels, Builtin Keying Sets can be defined by "subclassing" some "abstract" type, which in this case is bpy.types.KeyingSetInfo.
This defines the basic skeleton upon which you have to slot all the "flesh" of your custom Builtin Keying Set into. And just like a skeleton, it won't "work" as a body until you have all the interacting components included!

The first thing here you'll notice is that the name of the class. Currently, using the "BUILTIN_KSI_" prefix for the name just ensures that everybody knows that this is a Builtin Keying Set definition. This may not always be optional, so just get into the habit now :) The only thing you need to be wary of is that this name is unique (at least on your own Blender setup + files), otherwise you may get some unwanted surprises!

Next up, we have the bl_label property. This is the name of the Keying Set that you'll see in the UI (i.e. menus and search lists). Unless you want to confuse yourself, avoid the temptation to set every one of these to "Bob" (or similar) ;)

In some Keying Sets, notably the "Visual" ones (for including constraint effects, see the keyingsets_builtins.py file I mentioned earlier),  you'll notice some "bl_options" dictionaries. That is, you'll see lines like:
bl_options = {'INSERTKEY_VISUAL'}
These define the set of options which affect how all keyframes for the Keying Set will be inserted (i.e. the "Keyframing Settings" set of toggles in the "Keying Sets" panel).

Anatomy of a Builtin Keying Set - 2) "Every good story has a..."
Like any good story and every complete body, you need a head, a middle, and an end.

Builtin Keying Sets have 3 such parts:
1) Poll - This callback is called to see whether the Keying Set can be used at all in the current context. This gets called when deciding what to show in the Keying Sets menu.
2) Iterator - Once we know that a Keying Set is valid in the current context, the next step is to go over the context data and pick out the data for which we want keyframes to be added, and then call the generate method on that data.
3) Generate - This method takes the given data and figures out which of its properties we need to keyframe. These are added to the associated Keying Set (which is cleared out before the generate callback is called for the first time per keying session/operation).


When designing a Keying Set, keep in mind the following considerations about the ways that Builtin Keying Sets work:
- The iterator() callbacks may not always be used, especially when some code decides to run the generate callbacks directly (i.e. like transform/clear-transform operators performing auto-keying) where they already "know" what data they want to work on, thus making the iterator() redundant.
- The callbacks will always be run in the order shown: poll(), iterator(), generate()
- The "associated Keying Set", a special Keying Set which gets created when the Builtin Keying Set Type Info (which is what you're defining) gets "registered" (this Keying Set is not part of the Scene's collection of Keying Sets, but rather just part of some global collection) is what gets populated with paths by our Builtin Keying Set code defined in the generate() callbacks.
- As mentioned earlier, the associated Keying Set's list of paths will get cleared out before every bunch of generate() callbacks are run. To clarify this, what I mean here is that before the iterator() is run (i.e. but not after every poll() run). This ensures that none of the old stuff carries through.

At this point, a quick note should also be made about the way the modularity of the standard builtins work. To cut down the amount of duplicate code needed, I've split off some of the commonly used callback functions and dumped them in keyingsets_utils.py. To use these for constructing your own Keying Set code, you've got 2 options:
1) Use the whole function as-is (i.e. if it does everything you'll need it to do). For example,
iterator = RKS_ITER_selected_item     # this "binds" the iterator method of your Keying Set to use the existing function that is getting "assigned" to it
2) Call several of these functions in sequence (i.e. if you need more than one of them). For example,
 # location
 RKS_GEN_location(self, context, ks, data)
 # rotation
 RKS_GEN_rotation(self, context, ks, data)

Anatomy of a Builtin Keying Set - 3) Generating paths...
Perhaps the crux of knowing what to do with a Builtin Keying Set lies in what you put in the generate() callback to make things happen.

For examples of how to do this (i.e. examples of the API involved), generate an example Keying Set by hand, and then via the UI for Keying Sets (Properties Editor :: Scene Tab :: Keying Sets panel), use the "Export to Script" operator to generate a script with the necessary API commands to regenerate the Keying Set in another file. After checking out the ks.paths.add(...) calls there and in the other Keying Sets I've mentioned, you should be able to piece together how to do this.

If not, maybe then I'll do a proper writeup about these options. But it really shouldn't be too difficult to grasp :)

2 comments:

  1. this is awesome ! thanks for the info !

    ReplyDelete
  2. Can someone please show me or write one for Location Scale Color? LocScaleCol? Thanks!

    ReplyDelete