• Advertisement
Sign in to follow this  
  • entries
  • comments
  • views

An Animation Controller - Part 2 - Setting up for animation blending

Sign in to follow this  


Notice: Rather than continuing this particular part of my blog, I buckled down and wrote an article on a blending-capable animation controller. No sense in putting more here.

Continuing with exploration for the development of an animation controller for a skinned mesh, I've added a "track" to the concept to support blending of two or more animation sets (character "actions".) It isn't a fluke that it looks similar to ID3DXAnimationController. I'm familiar with the behavior of that object and could probably do worse than emulate its features.

The previous implementation of the animation controller calculated interpolated keyframes as matrices and stored them directly in the frame hierarchy. Blending of animation sets (for my purposes) means combining, in some way or another, interpolated values from each of the animation sets to be blended. Each timed key comes in one of three flavors: scale, rotation or translation. The AnimTrackKey structure (below) provides storage for the interpolated value of each type. I maintain the SRT values separately (rather than as a matrix) to permit interpolation with other animation sets without the need for matrix decomposition (which isn't robust, in any case), before the transformation matrices are formed and stored in the frame hierarchy.

To support blending of animation sets, a temporary location is needed for storing the AnimTrackKey interpolated data. The AnimTrack struct contains a std::vector of AnimTrackKeys, trackKeys, representing the interpolated data for each bone-frame at one tickcount of the animation set associated with that track. The order in which the keys are stored must be the same for all tracks. I.e., an index into the AnimTrackKey vector must correspond to the same frame in the frame hierarchy. That allows trackKey[ n ] for one track to be combined in some fashion with trackKey[ n ] in another track, with the assurance that the result is applicable to, and the transformation matrix can be stored in, the correct frame in the hierarchy.

I added some internal functions to support that concept. The process loads all the frame names in the hierarchy in the order they're recursivley accessed in a fixed manner (load a frame's name; load the frame-names from the frame's siblings and its children; load the frame-names from the frame's child and the child's children). A single instance of the animation controller corresponds to a single instance of a frame hierarchy. The frame names are stored in a std::vector frameNames, a data member of that animation controller.

When the frame names have been loaded, the animation sets are looped through, and each animation (the timed key data for a single frame) is given an index number corresponding to the index into the frameNames vector for that animation's frame-name. Following that, each track's trackKey vector is sized for frameNames.size() and filled with default AnimTrackKeys. Default AnimTrackKeys ("identity" transformations) are used to allow for animation sets which may not have animations for every frame in the hierarchy. In the process of blending, one track may animate just a head movement, and another track may not animate the head at all. When the track keys are combined, the Armature_Head bone (for instance) will be just be the transform from the first track.

The functions for interpolating a single animation timed key array was modified to store the data with the proper trackKeys index passed to it. The AdvanceTime function was modified to loop through each enabled track and do the calcs for that animationset. Before exiting that function, the various trackKeys are combined in a Transformation matrix which is stored in the frame hierarchy.

At present, the code works fine for a single animation set. I haven't actually blended two tracks yet, but I have the assets to test that. When I've done a bit more coding and testing, I'll post some more code that may be of interest.

Animation Blending

In the above discussion, note that I used the word "combined" with regard to the blending of trackKeys. At the present, it's hard-coded as an interpolation but may be modified to allow user selection of the combination method: interpolation, adding, subtracting, etc.

I'm trying to put a bit of flexibilty into the animation blending process as combining animations for various animation sets could result in enough difficulty for modelers and programmers as it is. Consider an sitting-idle action for a character that transitions to a walk action. Without synchronization of the animations, the character may appear to begin walking before it's risen from the sitting position. More research and contemplation needed.
struct AnimationTimedKey { DWORD keyTick; float val[4]; // accomodates quaternion (4) and vector (3) values};struct AnimationKey { int type; // 0 = rotation, 1 = scale, 2 = translate DWORD maxTicks; // used only for checking conistency for the animationset int numTimedKeys; // same as timedKeys.size() std::vector timedKeys;};struct AnimTrackKey{ AnimTrackKey() { Init(); } void Init() { tQuat = D3DXQUATERNION(0, 0, 0, 1); tScale.x = tScale.y = tScale.z = 1; tTrans.x = tTrans.y = tTrans.z = 0; } D3DXQUATERNION tQuat; D3DXVECTOR3 tScale, tTrans;};struct Animation { Animation() { trackKeyIndex = -1; } std::string frameName; int trackKeyIndex; // storage location in AnimTrack.trackKeys for interpolated timedKeys DWORD maxTicks; // used only for checking conistency for the animationset std::vector animKeys;};struct AnimationSet { bool checkedAgainstRootFrame; std::string animationSetName; std::vector animations; DWORD ticksPerSecond, maxTicks, curTicks; double currentTime, period, fCurTicks;};struct RootFrame { bool checkedAgainstAnimationSets; D3DXFRAME* _hierarchyRootFrame;};enum ANIMTRACK_PRIORITY { ANIMTRACK_LOW_PRI, ANIMTRACK_HIGH_PRI };struct AnimTrackDesc{ ANIMTRACK_PRIORITY trackPriority; float weight; float speed; double position; bool enable;};struct AnimTrack{ UINT animSet; // animationSet currently assigned to this track std::vector trackKeys; // interpolated timed keys for curTicks AnimTrackDesc desc;};
Sign in to follow this  


Recommended Comments

There are no comments to display.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Advertisement