I am trying to load an animation and skeleton using assimp. Im not sure if im doing the matrix multiplication correct or if I'm missing a Inverse or transpose somewhere. My engine is using a lefthanded coordinate system, using row-major matrices.
Gif of how its currently looking.
struct Skeleton
{
struct Joint
{
Matrix4x4f BindPoseInverse;
int ParentIdx;
std::vector<int> Children;
std::string Name;
};
std::vector<Joint> Joints;
std::unordered_map<std::string, size_t> JointNameToIndex;
};
struct Animation
{
struct Frame
{
std::unordered_map<
std::string,
Matrix4x4f
> Transforms;
};
std::vector<Frame> Frames;
float Duration;
float FramesPerSecond;
};
Mesh* MeshFactory::LoadMesh(const char* aMeshPath)
{
static int flags = aiProcess_Triangulate |
aiProcess_FlipUVs |
aiProcess_GenSmoothNormals |
aiProcess_CalcTangentSpace |
aiProcess_MakeLeftHanded |
aiProcess_FlipWindingOrder;
Assimp::Importer importer;
importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, false);
importer.SetPropertyInteger(AI_CONFIG_PP_LBW_MAX_WEIGHTS, 4);
const aiScene* scene = importer.ReadFile(aMeshPath, flags);
Mesh* _mesh = new Mesh;
unsigned int vertexOffset = 0;
auto myMAtrixToassimp = [](aiMatrix4x4 aiMat) {
Matrix4x4f mat;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
mat(i + 1, j + 1) = aiMat[i][j];
}
}
return mat;
};
if (scene->mNumMeshes == 0 && scene->mNumAnimations > 0)
{
Animation* anim = LoadAnimation(scene);
AnimationFactory::GetInstance()->AddAnimation(anim);
}
else
{
for (unsigned int i = 0; i < scene->mNumMeshes; ++i)
{
aiMesh* mesh = scene->mMeshes[i];
for (unsigned int v = 0; v < mesh->mNumVertices; ++v)
{
aiVector3D vertex = mesh->mVertices[v];
aiVector3D normal = mesh->HasNormals() ? mesh->mNormals[v] : aiVector3D(0, 0, 0);
aiVector3D uv = mesh->HasTextureCoords(0) ? mesh->mTextureCoords[0][v] : aiVector3D(0, 0, 0);
aiVector3D tangent = mesh->HasTangentsAndBitangents() ? mesh->mTangents[v] : aiVector3D(0, 0, 0);
aiVector3D bitangent = mesh->HasTangentsAndBitangents() ? mesh->mBitangents[v] : aiVector3D(0, 0, 0);
Vertex vert =
{
{vertex.x, vertex.y, vertex.z, 1.f},
{1.f, 1.f, 1.f, 1.f},
{normal.x, normal.y, normal.z, 1.f},
{tangent.x, tangent.y, tangent.z, 1.f},
{bitangent.x, bitangent.y, bitangent.z, 1.f},
{0,0,0,0},
{0.f,0.f,0.f,0.f},
{uv.x, uv.y},
};
_mesh->AddVertex(vert);
}
for (unsigned int f = 0; f < mesh->mNumFaces; ++f)
{
aiFace face = mesh->mFaces[f];
if (face.mNumIndices == 3)
{
_mesh->AddFace(face.mIndices[0] + vertexOffset, face.mIndices[1] + vertexOffset, face.mIndices[2] + vertexOffset);
}
}
vertexOffset += mesh->mNumVertices;
}
if (scene->HasAnimations())
{
_mesh->mySkeleton = LoadSkeleton(scene, *_mesh);
ExtractBoneWeights(scene, *_mesh);
}
}
if (_mesh != nullptr)
{
_mesh->SetName(aMeshPath);
myMeshes[aMeshPath] = _mesh;
}
return _mesh;
}
Skeleton* MeshFactory::LoadSkeleton(const aiScene* aScene, Mesh& aMesh)
{
auto myMAtrixToassimp = [](aiMatrix4x4 aiMat) {
Matrix4x4f mat;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
mat(i + 1, j + 1) = aiMat[i][j];
}
}
for (int i = 0; i < 3; i++)
{
mat(i + 1, 4) = 0;
}
mat(4, 4) = 1;
return mat;
};
Skeleton* skeleton = new Skeleton();
for (unsigned int i = 0; i < aScene->mNumMeshes; ++i)
{
aiMesh* mesh = aScene->mMeshes[i];
for (unsigned int j = 0; j < mesh->mNumBones; ++j)
{
aiBone* aiBone = mesh->mBones[j];
std::string boneName = aiBone->mName.C_Str();
if (skeleton->JointNameToIndex.find(boneName) == skeleton->JointNameToIndex.end())
{
Skeleton::Joint joint;
joint.Name = boneName;
joint.BindPoseInverse = myMAtrixToassimp(aiBone->mOffsetMatrix).GetInverse();
size_t jointIndex = skeleton->Joints.size();
skeleton->JointNameToIndex[boneName] = jointIndex;
skeleton->Joints.push_back(joint);
}
}
}
std::function<void(aiNode*, int)> processNode = [&](aiNode* node, int parentIndex)
{
std::string nodeName = node->mName.C_Str();
if (skeleton->JointNameToIndex.find(nodeName) != skeleton->JointNameToIndex.end())
{
size_t jointIndex = skeleton->JointNameToIndex[nodeName];
Skeleton::Joint& joint = skeleton->Joints[jointIndex];
joint.ParentIdx = parentIndex;
// Set up parent-child relationship
if (parentIndex != -1)
{
skeleton->Joints[parentIndex].Children.push_back(jointIndex);
}
parentIndex = static_cast<int>(jointIndex);
}
for (unsigned int i = 0; i < node->mNumChildren; ++i)
{
processNode(node->mChildren[i], parentIndex);
}
};
processNode(aScene->mRootNode, -1);
return skeleton;
}
Animation* MeshFactory::LoadAnimation(const aiScene* aScene)
{
aiAnimation* aiAnim = aScene->mAnimations[0];
Animation* animation = new Animation();
float ticksPerSecond = aiAnim->mTicksPerSecond > 0.0 ? static_cast<float>(aiAnim->mTicksPerSecond) : 24.0f;
animation->Duration = static_cast<float>(aiAnim->mDuration) / ticksPerSecond;
animation->FramesPerSecond = ticksPerSecond;
int totalFrames = static_cast<int>(animation->Duration * animation->FramesPerSecond);
animation->Frames.resize(totalFrames);
for (unsigned int channelIndex = 0; channelIndex < aiAnim->mNumChannels; ++channelIndex)
{
aiNodeAnim* channel = aiAnim->mChannels[channelIndex];
std::string jointName = channel->mNodeName.C_Str();
for (int frameIndex = 0; frameIndex < totalFrames; ++frameIndex)
{
float timeInTicks = (frameIndex / animation->FramesPerSecond) * aiAnim->mTicksPerSecond;
aiVector3D position;
if (channel->mNumPositionKeys == 1)
{
position = channel->mPositionKeys[0].mValue;
}
else
{
for (unsigned int i = 0; i < channel->mNumPositionKeys - 1; i++)
{
if (timeInTicks < channel->mPositionKeys[i + 1].mTime)
{
aiVectorKey key1 = channel->mPositionKeys[i];
aiVectorKey key2 = channel->mPositionKeys[i + 1];
float factor = (timeInTicks - key1.mTime) / (key2.mTime - key1.mTime);
position = key1.mValue + (key2.mValue - key1.mValue) * factor;
break;
}
}
}
aiQuaternion rotation;
if (channel->mNumRotationKeys == 1)
{
rotation = channel->mRotationKeys[0].mValue;
}
else
{
for (unsigned int i = 0; i < channel->mNumRotationKeys - 1; i++)
{
if (timeInTicks < channel->mRotationKeys[i + 1].mTime)
{
aiQuatKey key1 = channel->mRotationKeys[i];
aiQuatKey key2 = channel->mRotationKeys[i + 1];
float factor = (timeInTicks - key1.mTime) / (key2.mTime - key1.mTime);
aiQuaternion::Interpolate(rotation, key1.mValue, key2.mValue, factor);
rotation.Normalize();
break;
}
}
}
aiVector3D scale;
if (channel->mNumScalingKeys == 1)
{
scale = channel->mScalingKeys[0].mValue;
}
else
{
for (unsigned int i = 0; i < channel->mNumScalingKeys - 1; i++)
{
if (timeInTicks < channel->mScalingKeys[i + 1].mTime)
{
aiVectorKey key1 = channel->mScalingKeys[i];
aiVectorKey key2 = channel->mScalingKeys[i + 1];
float factor = (timeInTicks - key1.mTime) / (key2.mTime - key1.mTime);
scale = key1.mValue + (key2.mValue - key1.mValue) * factor;
break;
}
}
}
Matrix4x4f translationMat;
translationMat.SetPosition({ position.x, position.y, position.z, 1.f });
Matrix4x4f rotationMat;
{
float x = rotation.x;
float y = rotation.y;
float z = rotation.z;
float w = rotation.w;
float xx = x * x;
float yy = y * y;
float zz = z * z;
float xy = x * y;
float xz = x * z;
float yz = y * z;
float wx = w * x;
float wy = w * y;
float wz = w * z;
float r11 = 1.0f - 2.0f * (yy + zz);
float r12 = 2.0f * (xy - wz);
float r13 = 2.0f * (xz + wy);
float r21 = 2.0f * (xy + wz);
float r22 = 1.0f - 2.0f * (xx + zz);
float r23 = 2.0f * (yz - wx);
float r31 = 2.0f * (xz - wy);
float r32 = 2.0f * (yz + wx);
float r33 = 1.0f - 2.0f * (xx + yy);
rotationMat(1, 1) = r11;
rotationMat(1, 2) = r12;
rotationMat(1, 3) = r13;
rotationMat(1, 4) = 0;
rotationMat(2, 1) = r21;
rotationMat(2, 2) = r22;
rotationMat(2, 3) = r23;
rotationMat(2, 4) = 0;
rotationMat(3, 1) = r31;
rotationMat(3, 2) = r32;
rotationMat(3, 3) = r33;
rotationMat(3, 4) = 0;
rotationMat = rotationMat.GetTranspose();
}
Matrix4x4f scaleMat;
scaleMat = Matrix4x4f::CreateScaleMatrix({ scale.x, scale.y, scale.z, 0.f });
Matrix4x4f finalTransform = translationMat * rotationMat ;
animation->Frames[frameIndex].Transforms[jointName] = finalTransform;
}
}
return animation;
}
void MeshFactory::ExtractBoneWeights(const aiScene* aScene, Mesh& aMesh)
{
if (!aScene) return;
for (unsigned int m = 0; m < aScene->mNumMeshes; ++m)
{
const aiMesh* mesh = aScene->mMeshes[m];
if (mesh->mNumBones > 0)
{
for (unsigned int b = 0; b < mesh->mNumBones; ++b)
{
aiBone* bone = mesh->mBones[b];
for (unsigned int w = 0; w < bone->mNumWeights; ++w)
{
unsigned int vertexId = bone->mWeights[w].mVertexId;
float weight = bone->mWeights[w].mWeight;
if (vertexId < aMesh.m_Verticies.size())
{
Vertex& vertex = aMesh.m_Verticies[vertexId];
for (size_t i = 0; i < 4; i++)
{
if (vertex.BoneWeights[i] == 0)
{
vertex.BoneIDs[i] = b;
vertex.BoneWeights[i] = weight;
}
}
}
}
}
}
}
for (Vertex& vertex : aMesh.m_Verticies)
{
float totalWeight = 0.0f;
for (int i = 0; i < 4; i++)
{
totalWeight += vertex.BoneWeights[i];
}
if (totalWeight > 0.0f)
{
for (int i = 0; i < 4; i++)
{
vertex.BoneWeights[i] /= totalWeight;
}
}
else
{
for (int i = 0; i < 4; i++)
{
vertex.BoneWeights[i] = 0.0f;
}
}
}
}
void Animator::UpdateAnimator(unsigned aJointID, const Matrix4x4f& aParentJointTransform, AnimationBuffer& aOutAnimationBuffer)
{
const std::string& jointName = mySkeleton->Joints[aJointID].Name;
const Matrix4x4f& bindPoseInverse = mySkeleton->Joints[aJointID].BindPoseInverse;
const Matrix4x4f& jointLocalTransform = myCurrentAnimation->Frames[myCurrentFrame].Transforms[jointName];
Matrix4x4f transform = jointLocalTransform * aParentJointTransform;
Matrix4x4f globalJointTransform = bindPoseInverse * transform;
aOutAnimationBuffer.JointTransforms[aJointID] = globalJointTransform;
for (unsigned childJointID : mySkeleton->Joints[aJointID].Children)
{
UpdateAnimator(childJointID, globalJointTransform, aOutAnimationBuffer);
}
}
Im not sure what im expecting. Expecting it to work? hehe.
But I've tried changing the order of how im multiplying the matricies and tried getting inverse and transpose of them.
I switched to FBXSDK wich solved everything.