C++ 骨骼动画是倾斜的

C++ 骨骼动画是倾斜的,c++,opengl,glm-math,assimp,skeletal-animation,C++,Opengl,Glm Math,Assimp,Skeletal Animation,我通过Thin Matrix和上的另一个代码示例学习了有关骨骼动画的流行教程 网格渲染“查找”而不使用任何动画。但一旦应用动画,它就会扭曲 如果我以BonetTransforms的形式传递身份矩阵,它就会起作用。工作原理与中的一样,它仍然可以在没有任何动画的情况下正确渲染 我还注意到我使用的collada文件使用Z作为up,使用Y作为up。但我导出所有数据,而不做任何更改,以确保所有变换和顶点数据按预期工作。我稍后计划在导出时对此进行调整,以便数据也使用Y 以下是我的骨骼动画代码: 标题: c

我通过Thin Matrix和上的另一个代码示例学习了有关骨骼动画的流行教程

网格渲染“查找”而不使用任何动画。但一旦应用动画,它就会扭曲

如果我以BonetTransforms的形式传递身份矩阵,它就会起作用。工作原理与中的一样,它仍然可以在没有任何动画的情况下正确渲染

我还注意到我使用的collada文件使用Z作为up,使用Y作为up。但我导出所有数据,而不做任何更改,以确保所有变换和顶点数据按预期工作。我稍后计划在导出时对此进行调整,以便数据也使用Y

以下是我的骨骼动画代码:

标题:

class SkeletalAnimation {
        
        typedef struct Bone{
            int id;
            std::string name;
            glm::mat4 offset;
            std::vector<Bone> children;
        } Bone;

        typedef struct  {
            std::vector<float> translationTimestamps;
            std::vector<float> rotationTimetamps;
            std::vector<float> scalingTimetamps;

            std::vector<glm::vec3> translations;
            std::vector<glm::quat> rotations;
            std::vector<glm::vec3> scalings;
        } BoneTransforms;

        typedef struct Animation {
            float duration;
            float ticksPerSecond;
            std::unordered_map<std::string, BoneTransforms> boneTransforms;
            Animation(float pDuration, float ticksPerSecond) :
                duration(pDuration),
                ticksPerSecond(ticksPerSecond),
                boneTransforms({})
            {}
            Animation() {}
        } Animation;

        typedef std::unordered_map<std::string, std::pair<int, glm::mat4>> BoneData;
        typedef std::unordered_map<std::string, Animation> AnimationMap;
        typedef std::vector<glm::mat4> Pose;

        typedef struct{
            unsigned int segment;
            float fracture;
        } Segment;

        typedef struct {
            Pose pose;
            BoneData boneData;
            unsigned int boneCount;
            std::string name;
            Bone skeleton;
        } MeshEntry;

        typedef std::unordered_map<std::string, MeshEntry> MeshBoneMap;

    private:

        const std::string mPath;
        SDL_Renderer* mRenderer;
        
        std::vector<MeshEntry> mMeshEntries;
        std::vector<SkeletalMesh*> mMeshes;
        std::vector<ImageTexture*> mTextures;
        std::vector<unsigned int> mMeshToTexture;

        std::string* mCurrentAnimation;
        std::vector<std::string> mAnimations;

        AnimationMap mAnimationMap;
        Segment mCurrentSegment;
        glm::mat4 mGlobalInverseTransform;
        MeshBoneMap mMeshBoneMap;

        static glm::mat4 sIdentityMatrix;

        void LoadNode(aiNode* pNode, const aiScene* pScene);
        void LoadSkeletalMesh(aiMesh* pMesh, const aiScene* pScene);
        bool LoadBones(Bone& pBone, aiNode* pNode, BoneData& pBoneData);
        void LoadAnimations(const aiScene* pScene);
        void LoadMaterials(const aiScene* pScene);
        void Animate(float pDeltaTime, Bone& pSkeleton, Pose& pPose, glm::mat4& pParentTransform);

        static inline glm::mat4 aiToGlmMat4(const aiMatrix4x4& pAiMat);
        static inline glm::vec3 aiToGlmVec3(const aiVector3D& pAiVec);
        static inline glm::quat aiToGlmQuat(const aiQuaternion& pAiVec);
        static inline void GetSegment(Segment* pSegment,const std::vector<float>& pTimestamps,const float pDeltaTime);

    public:
        SkeletalAnimation(const std::string pPath, SDL_Renderer* pRenderer);
        ~SkeletalAnimation();

        void LoadAnimation();
        void GetAllAnimations(std::vector<std::string>* pAnimations);
        float GetAnimationDuration();
        void SetAnimationTime(float pTime);
        void SetAnimation(std::string pAnimation);
        void RenderAnimation(float pDeltaTime, SkeletalAnimationShader* pSkeletalAnimationShader);
        void RenderStill(SkeletalAnimationShader* pSkeletalAnimationShader);
        void ClearModel();
    };
类骨架动画{
结构骨{
int-id;
std::字符串名;
glm::mat4偏移量;
性病媒儿童;
}骨;
类型定义结构{
向量转换时间戳;
std::向量旋转时间戳;
std::矢量缩放单位安培数;
向量翻译;
向量旋转;
std::向量缩放;
}骨转换;
typedef结构动画{
浮动持续时间;
浮动滴答每秒;
std::无序的骨转换图;
动画(浮动时间、浮动滴答秒):
持续时间(pDuration),
滴答声秒(滴答声秒),
骨转换({})
{}
动画(){}
}动画;
typedef std::无序映射BoneData;
typedef std::无序的_贴图动画贴图;
向量姿势;
类型定义结构{
无符号整数段;
漂浮骨折;
}段;
类型定义结构{
摆姿势;
博内达塔博内达塔;
无符号整数boneCount;
std::字符串名;
骨骼;
}网格入口;
typedef std::无序的网格映射;
私人:
常量std::字符串mPath;
SDL_渲染器*渲染器;
std::向量;
std::向量mMeshes;
std::向量mtexture;
std::向量mMeshToTexture;
std::string*mCurrentAnimation;
std::向量模拟;
动画地图;
段mcurrent段;
glm::mat4 mGlobalInverseTransform;
MeshBoneMap mMeshBoneMap;
静态glm::mat4铁质鼓室矩阵;
void LoadNode(aiNode*pNode,const aiScene*pScene);
空荷载骨架网格(aiMesh*pMesh、const-aiScene*pScene);
bool LoadBones(Bone和pBone、aiNode*pNode、BoneData和pBoneData);
无效加载动画(const-aiScene*pScene);
空荷载材料(施工现场*施工现场);
无效动画(浮动pDeltaTime、骨骼和假肢、姿势和姿势、glm::mat4和pParentTransform);
静态内联glm::mat4 aiToGlmMat4(常量aiMatrix4x4和pAiMat);
静态内联glm::vec3 aiToGlmVec3(const aiVector3D&pAiVec);
静态内联glm::quat-aiToGlmQuat(const-aiQuaternion&pAiVec);
静态内联void GetSegment(Segment*pSegment,const std::vector&pTimestamps,const float pdelatime);
公众:
骨架动画(常量std::string pPath,SDL_渲染器*pRenderer);
~SkeletalAnimation();
void LoadAnimation();
void GetAllAnimations(标准::向量*平移);
float GetAnimationDuration();
void设置动画时间(浮动时间);
void SetAnimation(标准::字符串平移);
无效渲染(浮动pDeltaTime、SkeletalAnimationShader*pSkeletalAnimationShader);
void RenderStill(SkeletalAnimationShader*pSkeletalAnimationShader);
void ClearModel();
};
资料来源:

glm::mat4 SkeletalAnimation::sIdentityMatrix = glm::mat4();
    
        SkeletalAnimation::SkeletalAnimation(const std::string pPath, SDL_Renderer* pRenderer) :
            mPath(pPath),
            mRenderer(pRenderer),
            mCurrentAnimation(new std::string()),
            mAnimations({}),
            mAnimationMap({}),
            mMeshBoneMap({})
        {}

        SkeletalAnimation::~SkeletalAnimation() {
        
        }

        void SkeletalAnimation::LoadAnimation() {
            Assimp::Importer _importer;
            const aiScene* _scene = _importer.ReadFile(mPath, aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_GenSmoothNormals | aiProcess_JoinIdenticalVertices);

            if (!_scene) {
                SDL_Log("Assimp Error Loading Animation at path: %s \n Error: %s .", mPath.c_str(), _importer.GetErrorString());
                return;
            }

            
            mGlobalInverseTransform = glm::inverse(aiToGlmMat4(_scene->mRootNode->mTransformation));

            LoadNode(_scene->mRootNode, _scene);
            
            LoadAnimations(_scene);
            
            LoadMaterials(_scene);
            
        }

        void SkeletalAnimation::LoadNode(aiNode* pNode, const aiScene* pScene) {
            for (size_t i = 0; i < pNode->mNumMeshes; i++) {
                LoadSkeletalMesh(pScene->mMeshes[pNode->mMeshes[i]], pScene);
            }

            for (size_t i = 0; i < pNode->mNumChildren; i++) {
                LoadNode(pNode->mChildren[i], pScene);
            }
        }

        void SkeletalAnimation::LoadSkeletalMesh(aiMesh* pMesh, const aiScene* pScene) {

            MeshEntry _meshEntry;
            _meshEntry.boneCount = pMesh->mNumBones;
            _meshEntry.name = std::string(pMesh->mName.C_Str());
            _meshEntry.pose = {};
            _meshEntry.pose.resize(pMesh->mNumBones, sIdentityMatrix);
            _meshEntry.boneData = {};
            SkeletalMeshData _meshData;

            for (size_t i = 0; i < pMesh->mNumVertices; i++) {
                _meshData.vertices.insert(_meshData.vertices.end(),
                    {
                        pMesh->mVertices[i].x,
                        pMesh->mVertices[i].y , //Swaped Y and Z since Blender uses Z as up and I use Y as up.
                        pMesh->mVertices[i].z });

                if (pMesh->mTextureCoords[0]) {
                    _meshData.uvs.insert(_meshData.uvs.end(),
                        {
                            pMesh->mTextureCoords[0][i].x,
                            pMesh->mTextureCoords[0][i].y
                        });
                }
                else {
                    _meshData.uvs.insert(_meshData.uvs.end(),
                        {
                            0.0f,
                            0.0f
                        });
                }
                _meshData.normals.insert(_meshData.normals.end(),
                    {
                        pMesh->mNormals[i].x,
                        pMesh->mNormals[i].y ,
                        pMesh->mNormals[i].z });

                _meshData.boneIDs.insert(_meshData.boneIDs.end(), {
                    0,
                    0,
                    0,
                    0});

                _meshData.weights.insert(_meshData.weights.end(), {
                    0.0f,
                    0.0f,
                    0.0f,
                    0.0f});

            }

            for (size_t i = 0; i < pMesh->mNumFaces; i++) {
                aiFace _face = pMesh->mFaces[i];
                for (size_t j = 0; j < _face.mNumIndices; j++) {
                    _meshData.indices.push_back(_face.mIndices[j]);
                }
            }
            
            for (size_t i = 0; i < pMesh->mNumBones; i++) {
                aiBone* _bone = pMesh->mBones[i];
                glm::mat4 _offset = aiToGlmMat4(_bone->mOffsetMatrix);
                _meshEntry.boneData[_bone->mName.C_Str()] = std::make_pair(i , _offset);

                for (size_t j = 0; j < _bone->mNumWeights; j++) {
                    aiVertexWeight _weight = _bone->mWeights[j];
                    unsigned int _vertexID = _weight.mVertexId * 4;

                    for (size_t k = 0; k < 4; k++) {
                        if (_meshData.weights[_vertexID + k] == 0.0f) {
                            _meshData.weights[_vertexID + k] = _weight.mWeight;
                            _meshData.boneIDs[_vertexID + k] = i;
                            break;
                        }
                    }
                }

                
            }

            for (size_t i = 0; i < _meshData.weights.size(); i+=4) {
                float _totalWeight = 
                    _meshData.weights[i]    + 
                    _meshData.weights[i+1]  + 
                    _meshData.weights[i+2]  +
                    _meshData.weights[i+3];
                if (_totalWeight > 0.0f) {
                    _meshData.weights[i] /= _totalWeight;
                    _meshData.weights[i+1] /= _totalWeight;
                    _meshData.weights[i+2] /= _totalWeight;
                    _meshData.weights[i+3] /= _totalWeight;
                }
            }

            SkeletalMesh* _newMesh = new SkeletalMesh();
            _newMesh->BuildMesh(_meshData);
            mMeshes.push_back(_newMesh);
            mMeshToTexture.push_back(pMesh->mMaterialIndex);

            LoadBones(_meshEntry.skeleton, pScene->mRootNode, _meshEntry.boneData);
            mMeshEntries.push_back(_meshEntry);
        }

        bool SkeletalAnimation::LoadBones(Bone& pBone ,aiNode* pNode, BoneData& pBoneData) {
            if (pBoneData.find(pNode->mName.C_Str()) != pBoneData.end()) {
                pBone.name = pNode->mName.C_Str();
                pBone.id = pBoneData[pBone.name].first;
                pBone.offset = pBoneData[pBone.name].second;

                for (size_t i = 0; i < pNode->mNumChildren; i++) {
                    Bone _child;
                    LoadBones(_child, pNode->mChildren[i], pBoneData);
                    pBone.children.push_back(_child);
                }
                return true;
            }
            else { 
                for (size_t i = 0; i < pNode->mNumChildren; i++) {
                    if (LoadBones(pBone, pNode->mChildren[i], pBoneData)) {
                        return true;
                    }

                }
            }
            return false;
        }

        void SkeletalAnimation::LoadAnimations(const aiScene* pScene) {
            for (size_t i = 0; i < pScene->mNumAnimations; i++) {
                
                Animation _currentInternalAnimation(0.0f, 1.0f);
                aiAnimation* _currentAiAnimation = pScene->mAnimations[i];

                mAnimations.push_back(std::string(_currentAiAnimation->mName.C_Str()));
                if (i == 0) {
                    *mCurrentAnimation = mAnimations[0];
                }

                if (_currentAiAnimation->mTicksPerSecond != 0.0f) {
                    _currentInternalAnimation.ticksPerSecond = _currentAiAnimation->mTicksPerSecond;
                }
                else {
                    _currentInternalAnimation.ticksPerSecond = 1;
                }

                _currentInternalAnimation.duration = _currentAiAnimation->mDuration * _currentAiAnimation->mTicksPerSecond;
                _currentInternalAnimation.boneTransforms = {};

                BoneTransforms _transforms;

                for (size_t j = 0; j < _currentAiAnimation->mNumChannels; j++) {

                    aiNodeAnim* _channel = _currentAiAnimation->mChannels[j];

                    for (size_t k = 0; k < _channel->mNumPositionKeys; k++) {

                        _transforms.translations.push_back(aiToGlmVec3(_channel->mPositionKeys[k].mValue));
                        _transforms.translationTimestamps.push_back(_channel->mPositionKeys[k].mTime); 
                    }
                    for (size_t k = 0; k < _channel->mNumRotationKeys; k++) {
                        _transforms.rotations.push_back(aiToGlmQuat(_channel->mRotationKeys[k].mValue));
                        _transforms.rotationTimetamps.push_back(_channel->mRotationKeys[k].mTime);

                    }
                    for (size_t k = 0; k < _channel->mNumScalingKeys; k++) {
                        _transforms.scalings.push_back(aiToGlmVec3(_channel->mScalingKeys[k].mValue));
                        _transforms.scalingTimetamps.push_back(_channel->mScalingKeys[k].mTime);
                    }

                    _currentInternalAnimation.boneTransforms[_channel->mNodeName.C_Str()] = _transforms;
                }
                mAnimationMap[_currentAiAnimation->mName.C_Str()] = _currentInternalAnimation;
            }
        }

        void SkeletalAnimation::LoadMaterials(const aiScene* pScene) {
            mTextures.resize(pScene->mNumMaterials);
            for (size_t i = 0; i < pScene->mNumMaterials; i++) {
                aiMaterial* _material = pScene->mMaterials[i];
                mTextures[i] = nullptr;

                if (_material->GetTextureCount(aiTextureType_DIFFUSE)) {
                    aiString _path;
                    if (_material->GetTexture(aiTextureType_DIFFUSE, 0, &_path) == AI_SUCCESS) {
                        int _idx = std::string(_path.data).rfind("\\");
                        std::string _fileName = std::string(_path.data).substr(_idx + 1);
                        std::string _texturePath = std::string("assets/") + _fileName;
                        SDL_Log("Model Loading Texture at path: %s .", _texturePath.c_str());
                        mTextures[i] = new ImageTexture(_texturePath, mRenderer);
                        mTextures[i]->Load();

                        if (!mTextures[i]->IsLoaded()) {
                            delete mTextures[i];
                            mTextures[i] = nullptr;
                            SDL_Log("Model Error Loading Texture at path: %s .", _texturePath.c_str());
                        }

                    }
                }
            }

        }

        float SkeletalAnimation::GetAnimationDuration() {
            return mAnimationMap[*mCurrentAnimation].duration;
        }

        void SkeletalAnimation::SetAnimationTime(float pTime) {
            for (size_t i = 0; i < mMeshes.size(); i++) {
                MeshEntry& _entry = mMeshEntries[i];
                Animate(pTime, _entry.skeleton, _entry.pose, sIdentityMatrix);
            }
        }

        void SkeletalAnimation::Animate(float pDeltaTime, Bone& pSkeleton, Pose& pPose, glm::mat4& pParentTransform) {
            Animation& _currentAnimation = mAnimationMap[*mCurrentAnimation];
            
            BoneTransforms& _boneTransforms = _currentAnimation.boneTransforms[pSkeleton.name];
            pDeltaTime = fmod(pDeltaTime, _currentAnimation.duration);
            
            //Calculate translations
            GetSegment(&mCurrentSegment, _boneTransforms.translationTimestamps, pDeltaTime);

            glm::vec3 _translation = glm::mix(
                _boneTransforms.translations[mCurrentSegment.segment - 1], 
                _boneTransforms.translations[mCurrentSegment.segment], 
                mCurrentSegment.fracture);

            //Calculate rotations
            GetSegment(&mCurrentSegment, _boneTransforms.rotationTimetamps, pDeltaTime);

            glm::quat _rotation = glm::slerp(
                _boneTransforms.rotations[mCurrentSegment.segment - 1], 
                _boneTransforms.rotations[mCurrentSegment.segment], 
                mCurrentSegment.fracture);

            //Calculate scalings
            GetSegment(&mCurrentSegment, _boneTransforms.scalingTimetamps, pDeltaTime);

            glm::vec3 _scaling = glm::mix(
                _boneTransforms.scalings[mCurrentSegment.segment - 1],
                _boneTransforms.scalings[mCurrentSegment.segment],
                mCurrentSegment.fracture);

            glm::mat4 _translationMatrix = glm::translate(glm::mat4(1.0f), _translation);
            glm::mat4 _rotationMatrix = glm::toMat4(_rotation);
            glm::mat4 _scalingMatrix = glm::scale(glm::mat4(1.0f), _scaling); glm::mat4(1.0f);
            
            glm::mat4 _localTransform = _translationMatrix * _rotationMatrix * _scalingMatrix;
            glm::mat4 _globalTransform = pParentTransform * _localTransform;

            pPose[pSkeleton.id] = mGlobalInverseTransform * _globalTransform * pSkeleton.offset;

            for (Bone& _child : pSkeleton.children) {
                Animate(pDeltaTime, _child, pPose, _globalTransform);
            }
        }

        void SkeletalAnimation::GetAllAnimations(std::vector<std::string>* pAnimations) {
            pAnimations->clear();
            *pAnimations = mAnimations;
        }

        void SkeletalAnimation::SetAnimation(std::string pAnimation) {
            assert(std::find(mAnimations.begin(), mAnimations.end(), pAnimation) != mAnimations.end(), "Animation does not exist.");
            *mCurrentAnimation = pAnimation;
        }

        void SkeletalAnimation::RenderAnimation(float pDeltaTime, SkeletalAnimationShader* pSkeletalAnimationShader) {

            for (size_t i = 0; i < mMeshes.size(); i++) {
                unsigned int _materialIndex = mMeshToTexture[i];

                if (_materialIndex < mTextures.size() && mTextures[_materialIndex]) {
                    mTextures[_materialIndex]->Enable();
                }

                MeshEntry& _entry = mMeshEntries[i];
                Animate(pDeltaTime, _entry.skeleton, _entry.pose, sIdentityMatrix);
                pSkeletalAnimationShader->SetBoneTransforms(_entry.boneCount, _entry.pose);
                mMeshes[i]->Render();
            }
        }

        void SkeletalAnimation::RenderStill(SkeletalAnimationShader* pSkeletalAnimationShader) {
            for (size_t i = 0; i < mMeshes.size(); i++) {
                unsigned int _materialIndex = mMeshToTexture[i];

                if (_materialIndex < mTextures.size() && mTextures[_materialIndex]) {
                    mTextures[_materialIndex]->Enable();
                }

                MeshEntry& _entry = mMeshEntries[i];
                pSkeletalAnimationShader->SetBoneTransforms(_entry.boneCount, _entry.pose);
                mMeshes[i]->Render();
            }
        }

        void SkeletalAnimation::ClearModel() {
            for (size_t i = 0; i < mMeshes.size(); i++) {
                if (mMeshes[i]) {
                    delete mMeshes[i];
                    mMeshes[i] = nullptr;
                }
            }

            for (size_t i = 0; i < mTextures.size(); i++) {
                if (mTextures[i]) {
                    delete mTextures[i];
                    mTextures[i] = nullptr;
                }
            }
        }

        void SkeletalAnimation::GetSegment(Segment* pSegment,const std::vector<float>& pTimestamps, const float pDeltaTime) {
            unsigned int _segment = 1;
            while (pDeltaTime > pTimestamps[_segment]) {
                _segment++;
            }
            float _start = pTimestamps[_segment - 1];
            float _end = pTimestamps[_segment];
            float _fracture = (pDeltaTime - _start) / (_end - _start);
            pSegment->segment = _segment;
            pSegment->fracture = _fracture;
        }

        glm::mat4 SkeletalAnimation::aiToGlmMat4(const aiMatrix4x4& pAiMat) {
            glm::mat4 _glmMat;
            for (int y = 0; y < 4; y++)
            {
                for (int x = 0; x < 4; x++)
                {
                    _glmMat[x][y] = pAiMat[y][x];
                }
            }
            return _glmMat;
        }

        glm::vec3 SkeletalAnimation::aiToGlmVec3(const aiVector3D& pAiVec) {
            return glm::vec3(pAiVec.x, pAiVec.y, pAiVec.z); //Swapped Y and Z to correct Blender ups.
        }

        glm::quat SkeletalAnimation::aiToGlmQuat(const aiQuaternion& pAiQuat) {
            return glm::quat(pAiQuat.w, pAiQuat.x, pAiQuat.y, pAiQuat.z);
        }
glm::mat4 SkeletalAnimation::sIdentityMatrix=glm::mat4();
SkeletalAnimation::SkeletalAnimation(常量std::字符串pPath,SDL_渲染器*pRenderer):
兆帕,
伦德勒先生(普伦德勒),
mCurrentAnimation(新的std::string()),
人体模型({}),
mAnimationMap({}),
mMeshBoneMap({})
{}
SkeletalAnimation::~SkeletalAnimation(){
}
void SkeletalAnimation::LoadAnimation(){
助理:进口商\进口商;
const aiScene*_scene=_importer.ReadFile(mPath,aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_GenSmoothNormals | aiProcess_joinidenticalveartices);
如果(!\u场景){
SDL_日志(“在路径:%s\n错误:%s处加载动画时出错,mPath.c_str(),_importer.GetErrorString());
返回;
}
mGlobalInverseTransform=glm::inverse(AitoglMat4(_scene->mRootNode->mTransformation));
加载节点(\u场景->mRootNode,\u场景);
加载动画(_场景);
加载材料(场景);
}
void SkeletalAnimation::LoadNode(aiNode*pNode,const aiScene*pScene){
对于(大小i=0;imNumMeshes;i++){
LoadSkeletalMesh(pScene->mmesh[pNode->mmesh[i]],pScene);
}
对于(大小i=0;imNumChildren;i++){
LoadNode(pNode->mChildren[i],pScene);
}
}
void skeletalimation::LoadSkeletalMesh(aiMesh*pMesh,const aiScene*pScene){
网格入口_网格入口;
_meshEntry.bonecoount=pMesh->mNumBones;
_meshtry.name=std::string(pMesh->mName.C_Str());
_meshtry.pose={};
_meshtry.pose.resize(pMesh->mNumBones,sIdentityMatrix);
_meshtry.boneData={};
SkeletalMeshDatau meshData;
对于(大小i=0;imNumVertices;i++){
_meshData.vertices.insert(_meshData.vertices.end(),
{
layout (location = 0) in vec3 position;
layout (location = 1) in vec2 uv;
layout (location = 2) in vec3 normal;
layout (location = 3) in ivec4 boneIds; 
layout (location = 4) in vec4 boneWeights; 

out vec2 textureUV;
out vec3 lightNormal;
out vec4 worldPosition;

uniform mat4 model;
uniform mat4 projectionView;
uniform mat4 boneTransforms[50];

void main()
{
    mat4 boneTransform  =  mat4(0.0f);
    for(int i = 0; i < 4; i++){
        boneTransform  += boneTransforms[boneIds[i]] * boneWeights[i];
    }
    worldPosition = boneTransform * vec4(position, 1.0f);
    worldPosition = model * worldPosition;
    gl_Position = projectionView * worldPosition;
    textureUV = uv;
    lightNormal = mat3(transpose(inverse(model * boneTransform))) * normal;
}