Modifying Bone Orientation
After you have loaded the bone hierarchy, you can manipulate it. To modify
the orientation of a bone, you first need to locate its respective frame
structure by creating a function that recursively searches the frames for a
specific bone name. Once it is found, a pointer to the frame is provided so you
can directly access the frame's transformation matrix. The recursive search
function might look something like this:
Note You can apply any transformation to any bone in the hierarchy,
but it's recommended that you only work with rotations. Why only rotations?
Think of it this way−when you bend your elbow, it rotates. How would you explain
it if you translated your elbow instead? That would make your elbow pop off your
arm−something you definitely don't want!
If you are trying to move the entire mesh through the world, just translate
the root bone; all other bones will inherit that transformation. Better yet, use
the world transformation to move the skinned mesh object through the 3D world.
D3DXFRAME_EX *FindFrame(D3DXFRAME_EX *Frame, char *Name)
{
// Only match non−NULL names
if(Frame && Frame−>Name && Name)
{
// Return frame pointer if matching name found
if(!strcmp(Frame−>Name, Name))
return Frame;
}
// Try to find matching name in sibling frames
if(Frame && Frame−>pFrameSibling)
{
D3DXFRAME_EX *FramePtr = FindFrame((D3DXFRAME_EX*)Frame−>pFrameSibling, Name);
if(FramePtr)
return FramePtr;
}
// Try to find matching name in child frames
if(Frame && Frame−>pFrameFirstChild)
{
D3DXFRAME_EX *FramePtr = FindFrame((D3DXFRAME_EX*)Frame−>pFrameFirstChild, Name);
if(FramePtr)
return FramePtr;
}
// No matches found, return NULL
return NULL;
}
Suppose you want to find a bone called "Leg" using the FindFrame function.
You simply provide the name of the bone to find and a pointer to your root
frame, as shown here:
// pRootframe = D3DXFRAME_EX root frame pointer
D3DXFRAME_EX *Frame = FindFrame(pRootFrame, "Leg");
if(Frame) {
// Do something with frame, like changing the
D3DXFRAME_EX::TransformationMatrix to something
// you want. For here, let's rotate the bone a little
D3DXMatrixRotationY(&Frame−>TransformationMatrix, 1.57f);
}
Updating the Hierarchy
Once you've modified the bone transformations, you need to update the entire
hierarchy so you can use it later for rendering. Even if you haven't modified
the bone transformations, you still need to update the hierarchy because you
need to set certain variables before rendering.
During the hierarchy update, you must combine each successive transformation
down through the hierarchy. Starting at the root, you apply the bone's
transformation matrix to the frame's combined transformation matrix. The bone's
transformation matrix is passed to any siblings of the root to be combined as
well. From there, the combined transformation matrix you just calculated is
passed to each child of the root. This process propagates itself throughout the
hierarchy.
Although it is hard to understand at first, you can think of the process this
way: Take the skeletal structure in Figure 4.2, start at the root, and multiply
it by a transformation matrix that positions the root in the world.
As you can see in Figure 4.2, the combined transformation from the root is
passed to all of its child bones, which in turn are combined. The results are
passed to the child bones of those bones. However, trying to compute the
transformation matrices in the manner shown is very difficult, so other means
are necessary.
The easiest way to update your frame hierarchy is to create a recursive
function that combines the frame's transformation with a provided transformation
matrix. From there, the transformation matrix is passed to the frame's siblings,
and the combined matrix is passed to the frame's child frames. Take a look at
the function in question.
void UpdateHierarchy(D3DXFRAME_EX *Frame, D3DXMATRIX matTransformation = NULL)
{
D3DXFRAME_EX *pFramePtr;
D3DXMATRIX matIdentity;
// Use an identity matrix if none passed
if(!matTransformation)
{
D3DXMatrixIdentity(&matIdentity);
matTransformation = &matIdentity;
}
// Combine matrices with supplied transformation matrix
matCombined = TransformationMatrix * (*matTransformation);
// Combine with sibling frames
if((pFramePtr = (D3DXFRAME_EX*)pFrameSibling))
pFramePtr−>UpdateHierarchy(matTransformation);
// Combine with child frames
if((pFramePtr = (D3DXFRAME_EX*)pFrameFirstChild))
pFramePtr−>UpdateHierarchy(&matCombined);
}
As you can see, the UpdateHierarchy function takes a D3DXFRAME_EX object as
the first parameter−this is the current frame being processed. You only need to
call UpdateHierarchy once, to provide a pointer to your root frame; the function
will recursively call itself for each frame.
Notice the second parameter of UpdateHierarchy−matTransformation. The
matTransformation parameter is the transformation matrix to apply to the frame's
transformation. By default, the matTransformation pointer is NULL, meaning that
an identity matrix is used during the call to UpdateHierarchy. After a frame's
matrix is combined with the provided transformation, the resulting
transformation is passed to the child frames by setting matTransformation during
the next call.
As I just mentioned, you only need to call the UpdateHierarchy function using
your root frame. Don't provide a transformation matrix as the second
parameter−this should be left up to the recursive calls. If you do provide a
transformation matrix with the root frame, you'll be moving the entire mesh
using that transformation matrix. That's the same as setting the world
transformation matrix to position and orient the mesh to render.
// pRootFrame = D3DXFRAME_EX root frame object
UpdateHierarchy(pRootFrame);
Now that you have a little understanding of the skeletal structure and how to
work with bone hierarchies, it's time to move on to the second piece of the
animation puzzle−the overlaid skinned mesh that deforms to match the orientation
of the bone hierarchy.