106 lines
3.4 KiB
C++
106 lines
3.4 KiB
C++
#ifndef ANIMATOR_CLASS
|
|
#define ANIMATOR_CLASS
|
|
|
|
#include <glm/glm.hpp>
|
|
#include <glm/gtc/matrix_transform.hpp>
|
|
#include <map>
|
|
#include <vector>
|
|
#include <string>
|
|
#include <assimp/scene.h>
|
|
#include <assimp/Importer.hpp>
|
|
#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<AssimpNodeData> 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<Bone> m_Bones;
|
|
std::vector<std::string> 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:
|