Sometimes you want the avatar to interact with items
in your game. If you have a baseball game, you might want the avatars to
be able to hold a baseball bat and throw a baseball. The avatar bones
returned from the avatar animations and the values expected by the AvatarRenderer.Draw
method are in local bone space. This means that the bones are relative
to their parent bone and the bind pose for the avatar. To find the
location of the avatar’s hand to place a baseball, find the world space
position of the bone that represents the avatar’s hand. Then, use this
bone to place the baseball model.
Let’s see how this works by
creating an example of an avatar lifting a hand weight.
For additional member variables, add the following member variables.
// Avatar bones in world space
List<Matrix> bonesWorldSpace;
Model handWeight;
Matrix handWorldMatrix;
The bonesWorldSpace
stores the world space values for each of the bones that make up the
avatar skeleton. You also need variables for the hand-weight model and
the world matrix used to draw it.
In the LoadContent method, load the model and populate the list of bones in world space. Add the following lines of code to your game’s LoadContent method:
// Load the hand weight model
handWeight = Content.Load<Model>("HandWeight");
// Turn on default lighting
foreach (ModelMesh mesh in handWeight.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.EnableDefaultLighting();
}
}
// Populate the world space bone list
bonesWorldSpace = new List<Matrix>(AvatarRenderer.BoneCount);
for (int i = 0; i < AvatarRenderer.BoneCount; i++)
bonesWorldSpace.Add(Matrix.Identity);
Next, in the game’s Update method, calculate the world space transforms for the avatar animation. Add the following to your game’s Update method:
if (avatarRenderer.State == AvatarRendererState.Ready)
{
ConvertBonesToWorldSpace(avatarRenderer, avatarAnimation);
}
// Find the position of the right hand bone
handWorldMatrix = bonesWorldSpace[(int)AvatarBone.SpecialRight];
Before you convert the bone transforms into world space, check that the AvatarRenderer.State property is set to Ready so you can verify that the avatar and its skeleton have completed loading. Then, call the ConvertBonesToWorldSpace method passing in the AvatarRenderer and AvatarAnimation we want to find the world space position of. After calculating the world space transforms, use the AvatarBone.SpecialRight bones transform for the world matrix for the hand-weight model. The SpecialRight
bone is a piece of the avatar skeleton that is located near the inside
of the forearm in front of the hand. It is a good bone to use when you
want to place objects in the avatar’s hand.
To implement the ConvertBonesToWorldSpace helper method, add the following method to your game:
/// <summary>
/// Convert the avatar bones into world space
/// </summary>
private void ConvertBonesToWorldSpace(AvatarRenderer renderer, AvatarAnimation animation)
{
// Bind pose of the avatar skeleton
// Values are in local space and relative to the parent bone
IList<Matrix> bindPose = renderer.BindPose;
// Animation bone transforms are in local space and relative to the parent bone
IList<Matrix> animationPose = animation.BoneTransforms;
// Parent index values for each of the bones
IList<int> parentIndexes = renderer.ParentBones;
// Transform each bone into world space
for (int i = 0; i < AvatarRenderer.BoneCount; i++)
{
// Partent world space matrix
// The world matrix of the avatar renderer is used for the root
Matrix parentMatrix = (parentIndexes[i] != -1)
? bonesWorldSpace[parentIndexes[i]]
: renderer.World;
// Calculate world space matrix for the bone
bonesWorldSpace[i] = Matrix.Multiply(Matrix.Multiply(animationPose[i],
bindPose[i]),
parentMatrix);
}
}
To
calculate the bone’s position in world space, you first need the bind
pose of the avatar. The animation frames are relative to the bind pose
transforms, so you need both the animation transforms and the bind pose
transforms. Each of the bones is also relative to its parent bone, so
you also need to multiply the transform by its parent world space
matrix. For the root node, use the World property of the AvatarRenderer
because this is the location in world space of the avatar. Again, you
can loop over the bones linearly as you calculate the world space
transforms because you know the parent bones are calculated before any
of the children.
Finally, you can draw
the hand-weight model using the world space transform you calculated.
Add the following lines of code to your game’s Draw method:
// Draw the hand weight model
handWeight.Draw(handWorldMatrix, view, projection);
Running the sample displays an avatar with the weight model in his or her right hand (see Figure 1).