No problem! I will try to help. Let's talk about the basic algorithm first, this should give you an idea what information/data you need. This should make it much easier for you to navigate the FBX file and retrieve the data you need. Finally we can talk about implementation details and common pitfalls and mistakes.
There are four principal data elements
1) A transform hierarchy (skeleton)
2) An animation clip
3) One or more meshes
4) A binding for each mesh to the skeleton (cluster)
The first thing you need is the skeleton. I already posted some code how to retrieve the skeleton, but let me know if you need more detail. There are two ways to describe a skeleton: hierarchical or linear. The hierarchical representation is a classic tree structure and could look something like this:
struct Bone
{
Vector Translation;
Quaternion Rotation;
Matrix4x4 RelativeTransform;
Matrix4x4 AbsoluteTransform;
Matrix4x4 BindPose;
Bone* Parent;
std::vector< Bone* > Children;
};
struct Skeleton
{
Bone* Root;
};
The second option is to 'linearize' the transform hierarchy. This is usually done by traversing in DFS or BFS order. This assures that the parent bone is located before all children in the array. The linear representation can look something like this:
struct Transform
{
Vector Translation;
Quaternion Rotation;
};
struct Skeleton
{
int BoneCount;
std::vector< std::string > BoneNames;
std::vector< int > BoneParents;
std::vector< Transform > BoneBindPoses;
};
struct Pose
{
Skeleton* Skeleton;
std::vector< Transform > RelativeTransforms;
std::vector< Transform > AbsoluteTransforms;
};
Note that I skipped scale as this would only unnecessarily make things more complicated here.
The next thing is the animation clip which you sample at each frame to retrieve a pose for the skeleton. A simple animation structure looks like this
struct Animation
{
int FrameCount;
float FrameRate;
std::vector< Vector > TranslationKeys;
std::vector< Quaternion > RotationKeys;
};
So each frame you get translation and rotation of each bone and write it into Bone structure (hierarchical representation) or better you just extract the current Pose (linear representation).
The next step is to deform the mesh using the new skeleton pose. This is where the skeleton/mesh binding comes in. This binding is often referred to as skin and it tells you how much a bone influences a vertex of the mesh. Again there are two ways to describe the data based on the association. E.g. either each bone knows which vertices it deforms or each vertex knows by which bone it is deformed. This looks something like this:
struct Cluster
{
Bone* Bone;
std::vector< int > VertexIndices;
std::vector< float > VertexWeight;
};
#define MAX_BONE_COUNT 4
struct Vertex
{
Vector Position;
Vector Normal;
// ...
int BoneIndex[ MAX_BONE_COUNT ];
float BoneWeight[ MAX_BONE_COUNT ];
};
The final step is to apply the new pose to the mesh. For the cluster this looks something like this:
std::vector< Vector > DeformMeshPositions( int ClusterCount, const Cluster* Clusters, const Mesh* Mesh )
{
std::vector< Vector > VertexPositions;
VertexPositions.resize( Mesh->VertexCount );
std::fill( VertexPositions.begin(), VertexPositions.end(), Vector::Zero );
for ( int i = 0; i < ClusterCount; ++i )
{
const Cluster* Cluster = Clusters[ i ];
const Bone* Bone = Cluster->Bone;
Matrix4x4 BoneTransform = Bone->AbsoluteTransform;
Matrix4x4 BindPose = Bone->BindPose;
Matrix4x4 Transform = BoneTransform * Inverse( BindPose );
for ( int k = 0; k < Cluster->VertexCount; ++k )
{
int VertexIndex = Cluster->VertexIndex[ k ];
int VertexWeight = Cluster->VertexWeight[ k ];
VertexPositions[ VertexIndex ] += VertexWeight * Transform * Mesh->VertexPositions[ VertexIndex ];
}
}
return VertexPositions;
};
The algorithm takes a vertex in model space into the local space of the bone at bind time. Then it moves to the new location of the bone and transforms it back to model space. Think of it as if you are attaching the vertex to the bone, then move the bone, and finally detach the vertex and release it at the new location. Finally apply a weight to this new vertex position and sum it up.
HTH,
-Dirk
PS:
IIRC the geometric transform is a concept from 3D MAX. It is a special transform which is applied to the associated shapes of a node, but it is not propagated down the hierarchy. This means you cannot just bake it into your bone transform. The simplest way to deal with it is to ignore it when retrieving the skeleton and then apply it to the mesh vertices when you read them. I look up some code and post it here. It is really simple.