加载骨架和动画时我做错了什么?

问题描述 投票:0回答:1

我正在尝试使用 assimp 加载动画和骨架。我不确定我的矩阵乘法是否正确,或者我是否在某处缺少逆或转置。 我的引擎使用左手坐标系,使用行主矩阵。

当前外观的 Gif。

我将动画和骨架存储在下面的结构中。

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

我不确定我在期待什么。期待它发挥作用吗?呵呵。

但是我尝试过改变矩阵相乘的顺序,并尝试对它们进行求逆和转置。

c++ matrix directx-11 assimp skeleton
1个回答
0
投票

我改用 FBXSDK 解决了一切。

© www.soinside.com 2019 - 2024. All rights reserved.