I tried to obtain bone transform matrix for animation implemented with Directx11 and FBX sdk. I found this function in some web resources:
fbxCluster->GetLink()->EvaluateGlobalTransform(); // base pose
It seems that it is able to recursively calculate global transformation of each bone, thus I don't need to traverse the node tree. However the result obtained from it is weird.
The obtained globalBoneBaseMatrix is used in vertex shader:
void MeshImporter::LoadWeight(FbxNode* fbxNode, MeshEntry* mesh)
{
FbxMesh* fbxMesh = fbxNode->GetMesh();
int numSkin = fbxMesh->GetDeformerCount(FbxDeformer::eSkin);
if (numSkin == 0)
{
return;
}
assert(numSkin == 1);
// Assume only one deformer
FbxSkin* fbxSkin = static_cast<FbxSkin*>(fbxMesh->GetDeformer(0, FbxDeformer::eSkin));
int numCluster = fbxSkin->GetClusterCount();
int numControlPoints = fbxMesh->GetControlPointsCount();
vector<VertexWeight> tmpWeightList(numControlPoints);
for (int i = 0; i < numCluster; i++)
{
FbxCluster* fbxCluster = fbxSkin->GetCluster(i);
unsigned int boneIndex = model->skeleton->bones.size();
assert(fbxCluster->GetLinkMode() == FbxCluster::eNormalize);
// Read skeleton bones data (transformation matrix of each bone)
string boneName(fbxCluster->GetLink()->GetName());
if (!model->skeleton->FindBoneByName(boneName))
{
if (boneIndex >= MAXBONE)
{
PrintTab("Too many bones to load!!");
}
else
{
// Read weights of each vertex
int numIndexInCluster = fbxCluster->GetControlPointIndicesCount();
int* indicesInCluster = fbxCluster->GetControlPointIndices();
double* weights = fbxCluster->GetControlPointWeights();
for (int j = 0; j < numIndexInCluster; j++)
{
tmpWeightList[indicesInCluster[j]].AddBoneData(boneIndex, weights[j]);
}
// Normalize weights
/*for (int inVert = 0; inVert < tmpWeightList.size(); inVert++)
{
tmpWeightList[inVert].Normalize();
}*/
// Read animation bone matrix
XMFLOAT4X4 globalBoneBaseMatrix;
FbxAMatrix fbxGlobalBoneBaseMatrix = fbxCluster->GetLink()->EvaluateGlobalTransform();
if (i == 0)
{
for (int r = 0; r < 4; r++)
for (int c = 0; c < 4; c++)
{
globalBoneBaseMatrix.m[r][c] = (float)fbxGlobalBoneBaseMatrix.mData[r][c];
PrintTab("Global mat: " + to_string(fbxGlobalBoneBaseMatrix.mData[r][c]));
}
}
Bone bone;
bone.name = boneName;
bone.globalBoneBaseMatrix = globalBoneBaseMatrix;
bone.boneIndex = boneIndex;
bone.fbxNode = fbxNode;
model->skeleton->bones.push_back(bone);
}
}
}
}
My code reading animation data:
// Animation transform data
struct AnimationConstantBuffer
{
XMFLOAT4X4 meshBoneMatrices[MAXBONE];
};
Vertex shader:
PixelShaderInput output;
float4 pos = float4(input.pos, 1.0f);
// Test animations
matrix boneTransform =
BoneMatrices[input.boneIndices.x] * input.weights.x +
BoneMatrices[input.boneIndices.y] * input.weights.y +
BoneMatrices[input.boneIndices.z] * input.weights.z +
BoneMatrices[input.boneIndices.w] * input.weights.w;
pos = mul(pos, boneTransform);
pos = mul(pos, model);
pos = mul(pos, view);
pos = mul(pos, projection);
output.pos = pos;
...
And for testing, I did not update the bone matrix in real time. I'm not sure whether the way I use it is correct, but the rendering result is obviously wrong. There might be other mistakes.