Animation Cycle Data
|
Animation
Cycle data is a deceptively tricky thing to get right within
a robust real time animation system. Animation data tends
to be generated by animation curves edited within an animation
package such as Maya. Transferring this data directly to
a real time animation system is however unlikely to work.
For one thing, if IK, expressions, set driven keys etc are
used within the character setup, then unless we fully support
all of these within our own engine, we will never be able
to re-create the animation.
A better approach is usually
to sample specific animation values at a given time interval.
This approach generates a huge amount of data, however it
means our animation system can be much simpler without the
need for IK chains and expression parsers/compilers.
|
|
|
|
The
Sampled Input Data
The animation data will be
assumed as being discrete samples of Scale, Translation
and Rotation keyframes. Therefore, for a given animation
cycle we may have 25 frame samples of each bones (local
space) transformation.
We will need to convert this
data to a new data storage, where the transforms have been
split into scale, rotation and translation components. Each
of these keys should also store the time at which these
keys occur. This should allow us to remove any redundant
key frames in the middle.
This portion of code translates
very well into C++ templates since the process is the same
for most data types. The TKeyFrame structure
holds a value and a time value, the TKeyList template
then manages an array of key frames for a core data type.
|
|
|
|
To
Scale or Not to Scale
There is often much debate
around games programming forums about whether it is acceptable
to use scale and translation values within animation cycles.
One school of thought is that you should only use rotations
within your animation engine for speed and storage reasons.
The downside to this approach is that you loose the possibility
of some cheap effects such as influence objects which can
help to overcome certain deformation problems around trouble
areas such as knees and elbows.
Since the main aim is to create
as generic and flexible an animation system as possible,
it seems that scale and translation data should also be
included. Since this adds extra data requirements into our
engine, it is worth investigating methods to reduce the
physical amount of data we will need to store for an animation
cycle.
This system will therefore
evaluate the scale vector, translation vector and rotation
quaternion individually. Since it is unusal for animations
to require scale and translation control whilst animating,
it seems sensible to split the key frame storage in order
to minimise the data storage as much as possible. After
all, if scale remains at 1,1,1 for the entire cycle, why
store the data?
|
|
|
|
Setting
The Data
The input data for the structure
here is assumed to be discrete keyframes for scale, translation
and rotations. This data should ideally be stored as vectors
and quaternions as apposed to matrices. Whilst storing matrices
may seem sensible, we will later want to blend the animation
cycles. When blending animation cycles we want to linearly
interpolate the scale and translation, but spherically interpolate
the rotation keys. For this reason, it is best to blend
all of the animations as components and then convert to
matrices at the last possible stage.
Since we also want to ideally
store multiple character instances, we should also consider
that the data these classes need to store is just that,
data. They should not contain information on the current
animation time etc, this would only cause the requirement
that data would have to be duplicated. We will have to store
this data elsewhere within the character instances.
|
|
|
|
Key
Stripping
Animation Data is going to
account for the greatest amount of memory usage within any
real time animation system. It is therefore of great importance
to find a way to reduce this data requirement as much as
possible without losing quality. Lets assume that our data
exporter has simply output the various transformations for
each bone on each key frame.
For each transform then, we
will have a list of the various positions of that transform
on each frame. In order to reduce this amount, we will need
to determine the extent to which a key frame will affect
the animation cycle. If for example a transform does not
move for the entirity of a cycle, we should be able to reduce
it's keyframes down to one. (zero would be a bad idea
since this transform may not be the same as the bind pose
transform. Removing this info would cause all manner of
problems when trying to blend animation cycles).
If our animation data is currently
stored as a list of keys, we normally only store a key for
each frame. To start reducing this data, we will have to
split each transform into seperate lists of scale, translation
and rotation keyframes. We should also store each key frame
with a time value |
|
|
|
Evaluating
The Animation Cycles
Animation Data is going to
account for the greatest amount of memory usage within any
real time animation system. It is therefore of great importance
to find a way to reduce this data requirement as much as
possible without losing quality. Lets assume that our data
exporter has simply output the various transformations for
each bone on each key frame.
For each transform then, we
will have a list of the various positions of that transform
on each frame. In order to reduce this amount, we will need
to determine the extent to which a key frame will affect
the animation cycle. If for example a transform does not
move for the entirity of a cycle, we should be able to reduce
it's keyframes down to one. (zero would be a bad idea
since this transform may not be the same as the bind pose
transform. Removing this info would cause all manner of
problems when trying to blend animation cycles).
If our animation data is currently
stored as a list of keys, we normally only store a key for
each frame. To start reducing this data, we will have to
split each transform into seperate lists of scale, translation
and rotation keyframes. We should also store each key frame
with a time value |
|
|
|
Storing
Multiple Animation Cycle Instances
Since ideally we want to
blend the multiple cycles together, we will need to run
multiple animation cycles at the same time for each character
instance. For this, we will need to create an animation
instance structure.
|
|
|
|
Blending
The Cycles
What we want to create is
essentially a small mixer class capable of blending our
component scale, rotation and translation values from both
cycles together. It contains two animation cycle instances,
both of which reference an Animation data chunk.
This Mixer simply allows you
to switch from one animation to another one via a smooth
blend of a specified number of seconds. The process of blending
itself is straight forward, we simply have to lineraly interpolate
the scale and translation values of both cycles, and spherically
interpolate their rotation quaternions.
If however the mixer is not
switching cycles, it automatically just runs using the current
cycle instance.
This animation mixer is a
fairly simple case of how to actually blend together two
animation cycles. For a fully fledged game engine you may
want to run numerous cycles on various parts of the body
blended together, this should just be a simple extension
of the techniques presented here.
|
|
|
|
Generating
The Local Space Bone Matrices
Having blended together the
local space transforms, we will need to convert these to local,
world and inverse matrices for each bone. |
|
|
|
Skeletal
Data and Skeleton instances
It's slightly important to order
the data into two seperate structures. First is all of the
skeletal data that does not vary, and then is all of the variable
data (which belongs to each instance). |
|
|
|