This program can load a keyframe animation from an .anim file and play it back on a skinned character.
ANIM File Description
The .anim file contains an array of channels, each channel containing an array of keyframes. The structure of the .anim file is as follows:
animation {
range [time_start] [time_end]
numchannels [num]
channel {
extrapolate [extrap_in] [extrap_out]
keys [numkeys] {
[time] [value] [tangent_in] [tangent_out]
...
}
}
channel {
...
}
...
}-
[time_start]and[time_end]describe the time range in seconds that the animation is intended to play. This range doesn't necessarily correspond to the times of the first and last keyframes. -
number of channels
[num]= 3 * number of joints + 3 (for the root translation). It’s also # poses -
The
channelsare listed with the 3 root translations first in x,y,z order, followed by the x,y,z rotation channels for each joint in depth-first order. -
The extrapolation modes
[extrap_in]and[extrap_out]will be one of the following:"constant","linear","cycle","cycle_offset", or"bounce". -
The keys themselves will be listed in increasing order based on their time.
[numkeys]specifies the number of keyframes in the channel. Each key specifies its[time]and[value]in floating point. The tangent types[tangent_in]and[tangent_out]will be one of the following:"flat","linear","smooth", or it will be an actual floating point slope value indicating thefixed tangent mode.
new classes should include: AnimationPlayer, AnimationClip, Channel, Keyframe, AnimRig
AnimationPlayer
|__AnimationClip
|__Channel
|__Keyframe
|__AnimRig
|__Skeleton
|__Joint
|__Cube
|__DOF
|__Skin
|__Vertex
|__Skeleton-
AnimationPlayer
class AnimationPlayer { AnimationClip* clip; // Need a skeleton to map the pose vector to a specific rig Skeleton* skeleton; float curTime, tStart, tEnd; float deltaT = 0.01f; // 60fps, slow-motion video std::vector<float> poses; glm::mat4 rootTranslation; AnimationPlayer(AnimationClip* Clip, Skeleton* Skeleton); ~AnimationPlayer(); void Update(); // evaluates current poses, set these poses, increments current time };
- A pose is an array of values that maps to a rig (that’s why we need a skeleton).
- If the rig contains only simple independent DOFs (instead of quarternions or other complex coupled DOFs) the pose can just be an array of floats with
size = # joints.
-
AnimationClip
class AnimationClip { float tStart, tEnd; int numChannels; std::vector<Channel*> channels; AnimationClip(); ~AnimationClip(); bool Load(const char* animfile); void Precompute(); // each channel performs precomputation void Evaluate(float time, std::vector<float>& pose); // passing pose vector by reference };
- An animation clip will be stored as an array of
channels. - The interface for accessing data (
Evaluate) will be through apose(Because we want to represent the whole pose of the character on each specific time/frame).
- An animation clip will be stored as an array of
-
Channel
class Channel { char extpIn[256], extpOut[256]; int numKeys; std::vector<Keyframe*> keyframes; Channel(); ~Channel(); bool Load(Tokenizer* tknizer); void Precompute(); // compute tangents & cubic coefficients float Evaluate(float time); };
-
precompute coefficients
$\left[\begin{array}{l} a \ b \ c \ d \end{array}\right]=\left[\begin{array}{cccc} 2 & -2 & 1 & 1 \ -3 & 3 & -2 & -1 \ 0 & 0 & 1 & 0 \ 1 & 0 & 0 & 0 \end{array}\right] \cdot\left[\begin{array}{c} p_0 \ p_1 \ \left(t_1-t_0\right){V_0} \ \left(t_1-t_0\right){V_1} \end{array}\right]$
-
-
Keyframe
class Keyframe { float time; float value; float tanIn, tanOut; // tan values for fixed tangent mode // Tangent rules to compute tan values that are not given, stored in char[] char ruleIn[256], ruleOut[256]; float a, b, c, d; // Cubic coefficients Keyframe(); ~Keyframe(); bool Load(Tokenizer* tknizer); };
Currently implemented functions:
-
Collapsible property panel;
-
Slider bar changes:
- Camera distance, azimuth, inclination;
- DOFs of every joint.
-
Press
Rto reset all DOFs. -
Able to present:
- Only Skeleton (
.skel) - Undeformed Skin (Skin at binding state,
.skin) - Skeleton with attached skin (rig)
- Animated Rig
- Only Skeleton (
-
play controls
- Pause
- playback speed
- progress bar
- play mode (what to do after the end of the clip?), default is walking till the end of the world
- To infinity!
- Loop from start
- Stop at end
- Walk back and forth
Technically feasible functions (to-dos):
-
// Change light according to time (simulate sunrise and sunset) or user input
-
// Textured character
-
// Textured ground
-
// Plot trajectory