Working on a small project, currently trying to implement animation. Like most beginners I am simply following tutorials, this one being the LearnOpenGL Skeletal. All code for Bone/Animation/Animator is taken from that and implemented with my existing structure.
I get an issue where I load the model (From Mixamo, exported in Blender as a .gltf, and imported via assimp) where only some of the model is animating.
Has anyone encountered this issue before? My assumption would by my Shader or my BoneInfoMaps I am generating from ASSIMP are incorrect, but I can see what appears to be a correct amount of bones being created & Offsets applied:
Each aiMesh has these run against it:
void Model::SetVertexBoneData(Vertex& vertex, int boneID, float weight){
for (int i = 0; i < MAX_BONE_INFLUENCE; i++) {
if (vertex.BoneIDs[i] < 0) {
vertex.Weights[i] = weight;
vertex.BoneIDs[i] = boneID;
break;
}
}
}
void Model::ExtractBoneWeightForVertices(std::vector<Vertex>& vertices, aiMesh* mesh, const aiScene* scene){
for (int boneIndex = 0; boneIndex < mesh->mNumBones; boneIndex++) {
int boneID = -1;
std::string boneName = mesh->mBones[boneIndex]->mName.C_Str();
if (m_BoneInfoMap.find(boneName) == m_BoneInfoMap.end())
{
BoneInfo newBoneInfo;
newBoneInfo.ID = m_BoneCounter;
newBoneInfo.OffsetMatrix = Helpers::ConvertMatrixToGLMFormat(
mesh->mBones[boneIndex]->mOffsetMatrix);
m_BoneInfoMap[boneName] = newBoneInfo;
boneID = m_BoneCounter;
m_BoneCounter++;
}
else
{
boneID = m_BoneInfoMap[boneName].ID;
}
assert(boneID != -1);
auto weights = mesh->mBones[boneIndex]->mWeights;
int numWeights = mesh->mBones[boneIndex]->mNumWeights;
for (int weightIndex = 0; weightIndex < numWeights; ++weightIndex)
{
int vertexId = weights[weightIndex].mVertexId;
float weight = weights[weightIndex].mWeight;
assert(vertexId <= vertices.size());
SetVertexBoneData(vertices[vertexId], boneID, weight);
}
}
This is the calculation used to find the FinalBoneMatrices:
void Animator::CalculateBoneTransform(const NodeData* node, glm::mat4 parentTransform) {
std::string nodeName = node->Name;
glm::mat4 nodeTransform = node->Transformation;
Bone* Bone = m_CurrentAnimation->FindBone(nodeName);
if (Bone)
{
Bone->Update(m_CurrentTime);
nodeTransform = Bone->GetLocalTransform();
}
glm::mat4 globalTransformation = parentTransform * nodeTransform;
auto boneInfoMap = m_CurrentAnimation->GetBoneIDMap();
if (boneInfoMap.find(nodeName) != boneInfoMap.end())
{
int index = boneInfoMap[nodeName].ID;
glm::mat4 offset = boneInfoMap[nodeName].OffsetMatrix;
// m_FinalBoneMatrices[index] = offset * globalTransformation; // FBX Works this way
m_FinalBoneMatrices[index] = globalTransformation * offset; // GLTF Works this way
}
for (int i = 0; i < node->ChildrenCount; i++) CalculateBoneTransform(&node->Children[i], globalTransformation);
}
Which is finally called via:
m_Animator->UpdateAnimation(0.016f); // hard coded for now
auto transform = m_Animator->GetFinalBoneMatrices();
shader->UploadUniformMat4Array("u_BoneTransforms", transform);
m_Model.Draw();
