Sorry for the hiatus, I’ve been very busy with work and life the last couple
weeks. Today, we’re going to look at loading meshes with skeletal animations in
DirectX 11, using SlimDX and Assimp.Net in C#. This will probably be our most complicated
example yet, so bear with me. This example is inspired by Chapter 25 of
Frank Luna’s Introduction to 3D Game
Programming with Direct3D 11.0
, although with some heavy modifications. Mr.
Luna’s code uses a custom animation format, which I found less than totally useful;
realistically, we would want to be able to load skinned meshes exported in one of the commonly
used 3D modeling formats. To facilitate this, we will again use the .NET port of the Assimp
library, Assimp.Net.
The code I am using to load and interpret the animation and bone data is heavily based on Scott
Lee’s Animation Importer code, ported to C#. The full source for this example can be
found on my GitHub repository, at https://github.com/ericrrichards/dx11.git under
the SkinnedModels project. The meshes used in the example are taken from the example code
for
Carl Granberg’s
Programming an RTS Game with Direct3D
.
Skeletal animation is the standard way to animate 3D character models. Generally, a
character model will be represented by two structures: the exterior vertex mesh, or skin, and a
tree of control points specifying the joints or bones that make up the skeleton of the
mesh. Each vertex in the skin is associated with one or more bones, along with a weight
that determines how much influence the bone should have on the final position of the skin
vertex. Each bone is represented by a transformation matrix specifying the translation,
rotation and scale that determines the final position of the bone. The bones are defined in
a hierarchy, so that each bone’s transformation is specified relative to its parent
bone. Thus, given a standard bipedal skeleton, if we rotate the upper arm bone of the
model, this rotation will propagate to the lower arm and hand bones of the model, analogously to
how our actual joints and bones work.
Animations are defined by a series of keyframes, each of which specifies the transformation of
each bone in the skeleton at a given time. To get the appropriate transformation at a given
time t, we linearly interpolate between the two closest keyframes. Because of this, we will
typically store the bone transformations in a decomposed form, specifying the translation, scale
and rotation components separately, building the transformation matrix at a given time from the
interpolated components. A skinned model may contain many different animation sets; for
instance, we’ll commonly have a walk animation, and attack animation, an idle animation,
and a death animation.
The process of loading an animated mesh can be summarized as follows:
- Extract the bone hierarchy of the model skeleton.
- Extract the animations from the model, along with all bone keyframes for each
animation.
- Extract the skin vertex data, along with the vertex bone indices and weights.
- Extract the model materials and textures.
To draw the skinned model, we need to advance the animation to the correct frame, then pass
the bone transforms to our vertex shader, where we will use the vertex indices and weights to
transform the vertex position to the proper location.