#ifndef ANIMATOR_CLASS #define ANIMATOR_CLASS #include #include #include #include #include #include #include #include "Rig.h" // Scale of the "Head" bone (and what hangs off it), around the neck. 1 = as authored. inline float HeadScale = 1.0f; struct AssimpNodeData { glm::mat4 transformation; std::string name; int childrenCount; std::vector children; }; // ============================================================================ // Animation // ============================================================================ class Animation { public: Animation() = default; Animation(const std::string& animationPath, Rig* rig = nullptr) { Assimp::Importer importer; const aiScene* scene = importer.ReadFile(animationPath, aiProcess_Triangulate); assert(scene && scene->mRootNode); const aiAnimation* anim = scene->mAnimations[0]; m_Duration = (float)anim->mDuration; m_TicksPerSecond = (int)(anim->mTicksPerSecond != 0 ? anim->mTicksPerSecond : 24); ReadHierarchyData(m_RootNode, scene->mRootNode); for (unsigned int i = 0; i < anim->mNumChannels; i++) { const aiNodeAnim* channel = anim->mChannels[i]; m_Bones.emplace_back(channel->mNodeName.data, 0, channel); m_BoneNames.push_back(channel->mNodeName.data); } if (rig) RegisterBonesWithRig(*rig); } void RegisterBonesWithRig(Rig& rig) { for (const std::string& name : m_BoneNames) { if (rig.m_BoneInfoMap.find(name) == rig.m_BoneInfoMap.end()) { BoneInfo info; info.id = rig.m_BoneCounter++; info.offset = glm::mat4(1.0f); rig.m_BoneInfoMap[name] = info; } } } Bone* FindBone(const std::string& name) { for (Bone& b : m_Bones) if (b.GetBoneName() == name) return &b; return nullptr; } float GetTicksPerSecond() const { return (float)m_TicksPerSecond; } float GetDuration() const { return m_Duration; } const AssimpNodeData& GetRootNode() const { return m_RootNode; } public: float m_Duration = 0.0f; int m_TicksPerSecond = 24; std::vector m_Bones; std::vector m_BoneNames; AssimpNodeData m_RootNode; private: void ReadHierarchyData(AssimpNodeData& dest, const aiNode* src) { assert(src); dest.name = src->mName.data; dest.transformation = AssimpGLMHelpers::ConvertMatrixToGLMFormat(src->mTransformation); dest.childrenCount = (int)src->mNumChildren; for (unsigned int i = 0; i < src->mNumChildren; i++) { AssimpNodeData child; ReadHierarchyData(child, src->mChildren[i]); dest.children.push_back(child); } } }; // ============================================================================ // Animator // blendWeight ramps from 0?1 over blendDuration when PlayAnimation is called. // CharacterBody::GetBlendedBoneMatrices() lerps all animators by blendWeight. // ============================================================================ class Animator { public: