c++matrixdirectx-11assimpskeleton

What am I doing wrong when loading a skeleton and animation?


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.

Im storing the animation and skeleton in the structure below.

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;
};

Code from the importer.

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;
            }
        }
    }
}

Code from the animator.

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.


Solution

  • I switched to FBXSDK wich solved everything.