For this project I set out to create a real-time implementation of skeletal implementation espically aimed towards game engines. The core functionality of the system will be to serve as implementing skeletal animation using keyframes provided by a modeller. Interpolation between keyframes will be doing using quaternions and spherical interpolation on the rotiation keys, and linear interpolation between the scaling and translation keys.
The model will be parsed so that it will be possible to control the model directly through code in addition to the playing animations.
To specifically address the needs for games, the project provides a method to split up the animation keys on the model. It allows the artists / developers to split the model up into logically addressable parts and separate the list of keyframes into individually addressable animations. It is this part of the project where my work provides its substantial value. For example, a model can be split into two parts, legs and torso. The artist would create a single walk cycle for the legs, and the torso can perform different actions independently from the legs. When the torso is not performing any other action it can swing its arms to complement the walk, but when the character wants to swing a sword or shoot a gun, it can do so but only switching the torso animation, while the legs continue to walk. This increases the possibilites of the model's animations without adding any significant load on the artist since he/she does not have to implement all of the various permutations of animation combonations.
To implement this system I used DirectX 8, which provides some helpful functionality. It provides a math library to handle matrix math, vector math, and quaternion math. It also provides a file format called the X File format that provides most of the information I need for the system. What it does not provide is the functionality described earlier about logically understanding the skeleton of the model, and addressing portions of the model to perform independent operations on the skeleton. To provide this information I created a new file format called the ANM file format to describe animations within X files, and the ANM format is general enough to extend to any model format that describes a model with a skeleton, and allows the artist to assign names (in the form of text strings) to the bones.
The X file format contains the following information:
I support all animation features of the X file except for the matrix keyframe, as you cannot do interpolation between matrix keyframes and thus are not a worthwhile addition to the project.
There are several approaches to rendering the bone mesh on hardware, but they all revolve around a few principles:
The skeleton is strictly a hierarchy of transforms, and is affected directly by its single parent. Each joint has 5 matricies:
In the mesh, or skin, of the model, each vertex can be affected by a single bone in a "rigid binding." Rigid binding was used in the game Half-Life for purposes of speed, but now is only used for animation of mechanical objects. In "smooth binding" a vertex may be affected by any number of joints. Current hardware supports for 2 to 4 bone weights per vertex.
There are two main approaches I discovered to do this on hardware. The first way is to break the model into groups of vertices each affected by a small set of bones (such as 4 bones). Through each of the segments of the model, set the 4 bone matrices and draw the verticies. Each vertex has a set of weights cooresponding to the number of bones you render per segment.
The second way is to create an index "palette", and have each vertex have both a blend weight and an index into this vertex palette. This way requires hardware capable of a much larger set of matrices, but this method is likely faster as the entire model can be drawn in a single render call while only uploading any particular matrix one time.
The project's implementation comes in the form of a real-time viewer application that runs the logic of the skeletal animation and renders the model. It provides an interface for setting looping flags and cycling through all available animations as specified in the ANM file. The viewer requires an X file and an ANM file of the same prefix to run. It displays all available animations and shows which animation is currently active for each animation set.
Most of this information following is provided in the technical documentation included with the project submission.
This class will be used to manage the memory of the resources in the program. For the skeletal animation project solely as it is for the CG project, this class is not needed, but in the context of the 3D game engine, it is used to manage resources. It is responsible for the allocation, loading, and deallocation of key program resources. Placing the resource management in this central location will help to stop memory leak errors and also help to split out some nastier loading code from game logic (for example I'd rather not have AbstractModel have to worry about application paths and file system, etc). Having a ResourceRepository also allows for easy transition to loading from data archives if desired someday.
I was not able to finish this class in time for the final submission, but plan on adding it within the next week when I merge the animation project code into the game programming project I am doing for another class.
When the skeletal animation is merged with the game code, this class will provide a common interface to the different models and implement shared functionality between StaticModel and SkinnedModel. In the CG3-specific project implementation, this class does not exist.
This is the pre-existing static model class I developed for the 3D game engine I am working on for Phelps's class. I used it in my other CG assignments, but it will not appear in the CG project.
This is the root of the core work that I am doing specific to the CG3 project -- this class, its children, and the classes they use. The SkinnedModel class is a model that is defined by a mesh attached to a skeleton. By itself it neither contains nor implements any animation, but the Skeleton can be addressed directly to modify the skeleton, and the mesh will be rendered using the changes made to the Skeleton.
The Bone class is a node in the skeleton tree. An alternate name for the Bone class could be Joint, as these terms are interchangeable as defined in this project. The tree formed by the Bone objects is a n-size tree. Each Bone has 0 or more children, and a parent. These links can be traversed publically. Bones act as containers for the transformation data, but do no other significant work. Each Bone stores 4 matrices: a Bone offset matrix, a "reference pose" for the bone, the current matrix, and the resulting matrix from its parent and its transformation matrix. A fifth matrix, the skinning matrix, can be queried from the Bone and is the result matrix times the offset matrix.
The AnimatedModel class extends the functionality of the SkinnedModel class to include animations on the skeleton. It is important to note that from the perspective of the program, it is the skeleton that is being animated, and not the skin or mesh. The SkinnedModel simple renders around the skeleton. Thus the AnimatedModel focuses on tying sets of animation to groups of joints in the Skeleton alone, and through the fact the skeleton is moving, the functionality in the SkinnedModel will not need to "know" about animation.
The animations for a model are split up into sets of animations. Each AnimationSet cooresponds to a logical section of the model. For example if a humanoid character was split at the waist, the two animation sets would be "torso animations" and "leg animations." There is no limit on the number of splits on a model (except that an animation set must contain at least one joint). The number of active animations in an AnimatedModel is at most equal to the number of sets it contains, since each AnimatedSet has a currently active animation. In the previous example, the humanoid character would have at most two active animations, one for the legs and one for the torso, that both operate independently.
The AnimationSet contains a set of SkeletalAnimation objects. It also contains a looping flag (set if the animation is to loop), the current time offset into the animation, and the currently active animation. It also takes update events and passes them to the active animation, which will modify the matrices of the bones to which it is attached. An exmaple of an AnimationSet would be a set of atomic animations for legs, walking, running, and standing.
The SkeletalAnimation represents a atomic animation component. It contains a list of BoneInterpolator objects under a common name and an animation length. An example of a single SkeletalAnimation would be a walk cycle.
The BoneInterpolator does the real work of joint keyframing. Each BoneInterpolator object references a single Bone (or joint), and all of the keyframes for a single animation. The functionality of this class is limited to simply calculating the position of a single bone given a time value.
Simple implementations based loosely on code/design from my first CG3 project, the keyframing assingnment. These classes are a little simpler since I do not implement anything but linear and spherical linear interpolation on the keyframes.
Download the code from the main section page.