Sunday, April 22, 2012

Matters of Time - A few notes from the development file...

Time. It's certainly something I wish I had more of everyday (and also for all the deadlines lined up and nipping at my heels soon). But it turns out that playing with time might not be such a straightforward affair either.

In response to some questions recently from ROUBAL over on BlenderArtists regarding various time-related relics from 2.4x and their fate in 2.5+, I'll briefly cover some of the issues regarding time management in Blender relating back to the early days of 2.5 and Animato.

Blender 2.4
Time management in Blender 2.4 was a multi-headed beast, scattered all over the place and disguised in many different forms. Although there was some flexibility in this system, it wasn't always of the sort that was required. Like many other things at the time, it was a mess.

Multiple Times to Skin a Cat...
Due to the presence of at least 5 different ways (IIRC it was something like this, plus or minus a few - it was never too clear or easy keep track of) to manipulate time, some objects would need to be evaluated at different times. This resulted in entire sets of objects within a scene which would need to be updated multiple times every time an update was required.

The main consequence of this was this: it was impossible to know exactly what time anything should be evaluated at without re-evaluating a whole other bunch of stuff to make sure that it was evaluated correctly. This sucks.

The other consequence is that updates happen very very frequently. For instance, if we had an object that actually had a stack of dependencies on various time-altered objects, and this object needed to be ghosted (for use as dupliverts or something), then it's clear to see that everytime the view redraws there'd be a hell of a lot of recalculating of things going on.

Aside from the performance cost of all this recalculation, this was also the source of one of the most notable/annoying bugs around in the past: it can end up becoming impossible to edit some parameters at all (if they are keyed already). This is because editing the parameter forces some updates to occur, but those said updates end up triggering some other evaluations to occur, causing the new value to be overwritten. Annoying eh?

Great Big Muddling Pot
We all know the problems with mixing data which operates on different levels and/or scales. For example, you only have to look as far as the situation with Drivers and Animation curves sharing the same IPO Blocks in 2.4 to see the problems. On one hand, we have the pure fragility of this situation: it only takes a stressed animator trying to delete one stray keyframe to accidentally clobber his rig (so that an eyelid is now permanently droopy for instance).

But, the other problem with the Drivers and Animation situation was that the X-Axis represented two fundamentally different concepts.
- With a Driver, the X-Axis represents the input "evaluation time" derived from evaluating the driver relationship to get some numerical quantity (y-value at a given x). That is, Driver graphs represent static or response curves of a parameter in response to some stimulus.
- In contrast, with Animation, the X-Axis represents time units (presented in frames only back in the day IIRC). So, we're really looking at the "change over time" behaviour of some parameter.

This exact problem was also occurred with the "Time" IPO Curve. Many will be familiar with it, and have no doubt bemoaned its absence. The main issue here is that the Time IPO-Curve actually distorted the very reference time-frame that the other IPO-Curves living beside it were basing their timing on.

This meant that you couldn't just look at the frame number of a certain keyframe on an IPO-Curve and say, "yep, I know when that'll occur". Rather, you'd need to do some mental arithmetic to actually transform the curves relative to the time curve. I doubt that many/any people actually did that! Instead, most probably, you'd have jumped to a certain frame expecting to see a certain result, saw that things didn't add up, and then scrubbed around a bit to find said remapped frame.

So, fundamentally, the Time IPO-Curve was really a "meta" animation curve/control. As such, it really shouldn't have been muddled in with everything else.

"Speed" (or that lack thereof)
While we're talking about what IPO-Curves represented, the "Speed" IPO Curves had been bugging me for a while. The term, "Speed" implies some the rate at which something is moving at a point in time. However, in Blender, this was being used abused to represent the value at a point in time. If confused, consult your nearest physics/calculus textbook (hint, check on the seconds describing differentials).

Blender 2.5
So, with these problems in mind, I set about cleaning up the ways in which timing was being handled in Blender 2.5 as part of the Animato cleanup. Nevertheless, taming this multi-headed beast was not easy, and in the end, not everything was nicely tackled, as it was really a big ugly problem that needed a bit more attention to be solved.

Streamlining of Time Management
IMO, sometimes you just have to strip things back to get a clearer picture, then, maybe build back some of the complexity in a controlled manner later, hopefully in a cleaner form this time.

Under the hood, there was one significant change: everything should only be evaluated once to represent the global time.

Over the past few years, this principle has had to been twisted a bit for a few cases as we've restored some legitimately useful stuff which could only be implemented one way (namely duplis). There have also been some cases where it's been possible to maintain the functionality without requiring stuff to perform ad-hoc time remapping (e.g. "evaluation_time" controls for Paths are now a proper animatable property instead of being perpetually evaluated transient vagrants).

NLA and Time Control
From a user perspective, perhaps the most significant consequence from the cleanups in 2.5 was that the NLA System is the designated "single solution" to time control, replacing various time offsets and time curves. Both of these were already functions that the old NLA System was capable of. However, that system was never really adapted to the point where it could be used in such a way for all purposes.

Conceptually, this seems like a nice idea. The NLA effectively uses/references Actions, allowing a user to place an Action anywhere on a timeline, to manipulate the speed at which it plays back but also whether it repeats. The contents of referenced Actions are not visible/edited unless explicitly opened for editing. Hence, getting back to the earlier discussion about manipulating time being a kind of meta-control. By making the NLA the timing control for animation (in addition to its other roles as the system for layering passes of animation), we achieve separation of timing control and normal animation.

For those wondering about the "Time" IPO functionality, this can be replicated as follows:
1. Make the action in question an NLA Strip (as per usual)
2. From the NLA Editor Properties Region (with a strip selected), Evaluation Panel...
3. Enable "Animated Time" checkbox
4. Animate (insert keyframes) for the "Evaluation Time" parameter here. For various technical reasons, it's not possible to use drivers on these.

Global Time Adjustment
One of the things that has been brought up over the years is the need to be able to control the speed of animation across the whole scene (apparently it's quite necessary when dealing with "art directors" and clients who suddenly want a slow-mo shot of part of a shot). So far, there's only the crude measure of the map old/new stuff, which just does a single uniform remapping of the timeline, with little recourse for fine tuning of certain parts across the board.

Using certain scene setups, it may be possible to use the NLA as a mechanism for this (i.e. if the different sections were split up into strips, which could then be shuffled and retimed as needed), though you'd have to do this for all the relevant strips for all objects/data in the scene. This could get tedious.

As of the time of writing, no good mechanism for this exists yet. It's one of the rough/unfinished parts of things that still haven't been tackled here.


Hopefully that clears up some of the issues in this area. I've probably forgotten something somewhere about this, but as promised, this is just a brief overview of some of the most pertinent issues.


  1. I'm not exactly sure if I understand correctly. If the animation system in blender is done using curves, doesn't that mean it's independent from the samples (frames)?

    Shouldn't Blender be able to add what is needed in between two key frames?

    1. I just had an other idea if the above doesn't work. Once everything is baked, everything ends up being key framed. Wouldn't it be easy to scale all the curves relative to frame 0?

  2. The reality of baked curves is that in general, they tend to be practically difficult to edit.

    In any case, from what I understand, just moving every keyframe in the scene just doesn't cut it.