diff --git a/include/skeletal_animations_manager.hpp b/include/skeletal_animations_manager.hpp index 0820e89d..e741429a 100644 --- a/include/skeletal_animations_manager.hpp +++ b/include/skeletal_animations_manager.hpp @@ -11,37 +11,82 @@ #include "skeletal_animation_data.hpp" #include "engine_config.hpp" +/* + Let's call TPose - initial model position, without any animations +*/ + +// Interpolation types, none - no interpolation at all +enum class InterpolationType { + LINEAR, + NONE +}; + +enum class TPoseType { + BASIC, + FROMANIM +}; + class SkeletalAnimationsManager { public: SkeletalAnimationsManager() = default; - explicit SkeletalAnimationsManager(const std::string& animationPath, Model* model); - explicit SkeletalAnimationsManager(SkeletalAnimationData* animation); + explicit SkeletalAnimationsManager( + const std::string& animationPath, + Model* model, + TPoseType tposetype = TPoseType::BASIC, + int tposeAnimIndex = 0); + explicit SkeletalAnimationsManager( + SkeletalAnimationData* animation, + TPoseType tposetype = TPoseType::BASIC); void AddAnimation(SkeletalAnimationData* animation); void AddAnimation(const std::string& animationPath, Model* model); std::string GetAnimationsInfo(); - void PlayImmediately(int id, bool looped); - bool IsPlaying(); + // TPose or Animation -> Animation Immideately + void PlayImmediately( + int id, bool looped, + InterpolationType interpolationType = InterpolationType::LINEAR, + float interpolationTime = 0.2f); + + // Animation -> TPose + void StopImmediately( + InterpolationType interpolationType = InterpolationType::LINEAR, + float interpolationTime = 0.2f); - void StopImmediately(); - void Stop(); + void Stop(InterpolationType interpolationType = InterpolationType::LINEAR, + float interpolationTime = 0.2f); + + // Is animating or interpolating + bool IsPlaying(); + // If interpolating -> -1 + int GetPlayingAnimationIndex(); // Engine functions void Update(float dt); - const std::vector &GetFinalBoneMatrices(); private: void CalculateBoneTransform(const AssimpNodeData* node, glm::mat4 parentTransform); + void CalculateBoneTransformMatrices( + const AssimpNodeData* node, glm::mat4 parentTransform, + glm::mat4 *boneMatrices, + SkeletalAnimationData* m_CurrentAnimation, float); std::vector m_FinalBoneMatrices; std::vector m_Animations; + int m_CurrentAnimationIndex; float m_CurrentTime; bool m_Looped; -}; - + // Interpolation data + bool m_Interpolating = 0; + float m_InterpolationTime; + float m_InterpolationTimeLeft; + InterpolationType m_InterpolationType; + glm::mat4* m_BonesFrom = new glm::mat4[MAX_BONES]; + glm::mat4* m_BonesTo = new glm::mat4[MAX_BONES]; + glm::mat4* m_BonesTPose = new glm::mat4[MAX_BONES]; +}; diff --git a/resources/models/Husky.glb b/resources/models/Husky.glb new file mode 100644 index 00000000..b27a3050 Binary files /dev/null and b/resources/models/Husky.glb differ diff --git a/src/components/animation/skeletal_animations_manager.cpp b/src/components/animation/skeletal_animations_manager.cpp index ed031d1b..77afdb5c 100644 --- a/src/components/animation/skeletal_animations_manager.cpp +++ b/src/components/animation/skeletal_animations_manager.cpp @@ -1,18 +1,40 @@ #include "skeletal_animations_manager.hpp" -SkeletalAnimationsManager::SkeletalAnimationsManager(SkeletalAnimationData* animation) { +SkeletalAnimationsManager::SkeletalAnimationsManager( + SkeletalAnimationData* animation, + TPoseType tposetype) { m_FinalBoneMatrices.reserve(MAX_BONES); for (int i = 0; i < MAX_BONES; i++) { m_FinalBoneMatrices.push_back(glm::mat4(1.0f)); + m_BonesFrom[i] = (glm::mat4(1.0f)); + m_BonesTo[i] = glm::mat4(1.0f); + m_BonesTPose[i] = glm::mat4(1.0f); } m_Animations.push_back(animation); m_CurrentAnimationIndex = -1; + + if (tposetype == TPoseType::FROMANIM) { + CalculateBoneTransformMatrices( + &(animation->GetRootNode()), glm::mat4(1.0f), m_BonesTPose, animation, 0.0f); + for (int i = 0; i < MAX_BONES; i++) { + m_FinalBoneMatrices[i] = m_BonesFrom[i]; + } + } } -SkeletalAnimationsManager::SkeletalAnimationsManager(const std::string& animationPath, Model* model) { +SkeletalAnimationsManager::SkeletalAnimationsManager( + const std::string& animationPath, + Model* model, + TPoseType tposetype, + int tposeAnimIndex) { m_FinalBoneMatrices.reserve(MAX_BONES); + + m_CurrentAnimationIndex = -1; for (int i = 0; i < MAX_BONES; i++) { m_FinalBoneMatrices.push_back(glm::mat4(1.0f)); + m_BonesFrom[i] = (glm::mat4(1.0f)); + m_BonesTo[i] = glm::mat4(1.0f); + m_BonesTPose[i] = glm::mat4(1.0f); } Assimp::Importer importer; @@ -26,7 +48,25 @@ SkeletalAnimationsManager::SkeletalAnimationsManager(const std::string& animatio for (unsigned int i = 0; i < scene->mNumAnimations; i++) { m_Animations.push_back(new SkeletalAnimationData(animationPath, scene, i, model)); } - m_CurrentAnimationIndex = -1; + + if (tposetype == TPoseType::FROMANIM) { + if (tposeAnimIndex >= m_Animations.size()) { + Logger::Error("Can't use index for TPose Calculating, zero index will be used"); + tposeAnimIndex = 0; + } + if (m_Animations.size() == 0) { + Logger::Error("Can't use zero index for TPose Calculating, no animations found"); + return; + } + CalculateBoneTransformMatrices( + &(m_Animations[tposeAnimIndex]->GetRootNode()), + glm::mat4(1.0f), + m_BonesTPose, + m_Animations[tposeAnimIndex], 0.0f); + for (int i = 0; i < MAX_BONES; i++) { + m_FinalBoneMatrices[i] = m_BonesTPose[i]; + } + } } void SkeletalAnimationsManager::AddAnimation(SkeletalAnimationData* animation) { @@ -55,45 +95,91 @@ std::string SkeletalAnimationsManager::GetAnimationsInfo() { return info; } -void SkeletalAnimationsManager::PlayImmediately(int index, bool looped) { + +void SkeletalAnimationsManager::PlayImmediately( + int index, bool looped, + InterpolationType interpolationType, + float interpolationTime) { if (index < 0 || index >= m_Animations.size()) { Logger::Error("Can't play animation, wrong index"); return; } m_CurrentAnimationIndex = index; - m_CurrentTime = 0.0f; + m_CurrentTime = 0.f; m_Looped = looped; + + if (interpolationType != InterpolationType::NONE) { + m_Interpolating = 1; + m_InterpolationType = interpolationType; + m_InterpolationTime = interpolationTime; + m_InterpolationTimeLeft = interpolationTime; + for (int i = 0; i < MAX_BONES; i++) m_BonesFrom[i] = m_FinalBoneMatrices[i]; + CalculateBoneTransformMatrices( + &(m_Animations[m_CurrentAnimationIndex]->GetRootNode()), + glm::mat4(1.0f), + m_BonesTo, + m_Animations[m_CurrentAnimationIndex], + m_CurrentTime); + } } bool SkeletalAnimationsManager::IsPlaying() { - return m_CurrentAnimationIndex != -1; + return m_CurrentAnimationIndex != -1 || m_Interpolating; } -void SkeletalAnimationsManager::StopImmediately() { - m_CurrentTime = 0.0f; - m_Looped = 0; +int SkeletalAnimationsManager::GetPlayingAnimationIndex() { + return m_CurrentAnimationIndex; +} + +void SkeletalAnimationsManager::StopImmediately( + InterpolationType interpolationType, float interpolationTime) { + if (m_CurrentAnimationIndex == -1) { + return; + } + m_CurrentAnimationIndex = -1; + m_CurrentTime = 0.f; + + if (interpolationType != InterpolationType::NONE) { + m_Interpolating = 1; + m_InterpolationType = interpolationType; + m_InterpolationTime = interpolationTime; + m_InterpolationTimeLeft = interpolationTime; + for (int i = 0; i < MAX_BONES; i++) { + m_BonesFrom[i] = m_FinalBoneMatrices[i]; + m_BonesTo[i] = m_BonesTPose[i]; + } + } } -void SkeletalAnimationsManager::Stop() { +void SkeletalAnimationsManager::Stop(InterpolationType interpolationType, float interpolationTime) { m_Looped = 0; + m_InterpolationType = interpolationType; + m_InterpolationTime = interpolationTime; } - - void SkeletalAnimationsManager::Update(float dt) { - if (m_CurrentAnimationIndex < m_Animations.size() && m_CurrentAnimationIndex >= 0) { + if (m_Interpolating) { + m_InterpolationTimeLeft -= dt; + if (m_InterpolationTimeLeft < 0) { + m_Interpolating = 0; + for (int i = 0; i < MAX_BONES; i++) { + m_FinalBoneMatrices[i] = m_BonesTo[i]; + } + return; + } + for (int i = 0; i < MAX_BONES; i++) { + m_FinalBoneMatrices[i] = m_BonesFrom[i] + (m_BonesTo[i] - m_BonesFrom[i]) + * ((m_InterpolationTime - m_InterpolationTimeLeft) / m_InterpolationTime); + } + } else if (m_CurrentAnimationIndex < m_Animations.size() && m_CurrentAnimationIndex >= 0) { SkeletalAnimationData* m_CurrentAnimation = m_Animations[m_CurrentAnimationIndex]; m_CurrentTime += m_CurrentAnimation->GetTicksPerSecond() * dt; if (m_Looped) { m_CurrentTime = fmod(m_CurrentTime, m_CurrentAnimation->GetDuration()); } else { if (m_CurrentTime >= m_CurrentAnimation->GetDuration()) { - m_CurrentTime = 0.0f; - for (int i = 0; i < MAX_BONES; i++) { - m_FinalBoneMatrices[i] = glm::mat4(1.0f); - } - m_CurrentAnimationIndex = -1; + StopImmediately(m_InterpolationType, m_InterpolationTime); return; } } @@ -129,3 +215,31 @@ void SkeletalAnimationsManager::CalculateBoneTransform( for (int i = 0; i < node->children.size(); i++) CalculateBoneTransform(&node->children[i], globalTransformation); } + + +void SkeletalAnimationsManager::CalculateBoneTransformMatrices( + const AssimpNodeData* node, glm::mat4 parentTransform, + glm::mat4 *boneMatrices, SkeletalAnimationData* m_CurrentAnimation, float currentTime) { + + std::string nodeName = node->name; + glm::mat4 nodeTransform = node->transformation; + Bone* Bone = m_CurrentAnimation->FindBone(nodeName); + + if (Bone) { + Bone->Update(m_CurrentTime); + nodeTransform = Bone->GetLocalTransform(); + } + + glm::mat4 globalTransformation = parentTransform * nodeTransform; + auto boneInfo = m_CurrentAnimation->FindBoneInfo(nodeName); + if (boneInfo != nullptr) { + int index = boneInfo->id; + glm::mat4 offset = boneInfo->offset; + assert(index < MAX_BONES); + boneMatrices[index] = globalTransformation * offset; + } + + for (int i = 0; i < node->children.size(); i++) + CalculateBoneTransformMatrices(&node->children[i], globalTransformation, + boneMatrices, m_CurrentAnimation, currentTime); +} diff --git a/src/main.cpp b/src/main.cpp index f15c7fb5..3d1c5e4a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -81,7 +81,7 @@ int main() { auto wolfObj = engine->NewObject().SetName("Wolf"); Logger::Info("%s", wolfObj.GetName().c_str()); - wolfObj.AddTransform(Vec3(5.f, -10.f, -10.f), Vec3(10.f), Mat4(1.0)); + wolfObj.AddTransform(Vec3(0.f, -5.f, -10.f), Vec3(10.f), Mat4(1.0)); wolfObj.AddModel(*wolfModel); wolfObj.AddSkeletalAnimationsManager("Wolf/Wolf-Blender-2.82a.gltf", wolfModel).PlayImmediately(3, 1); @@ -108,6 +108,35 @@ int main() { wolfObj.AddBehaviour(); } + // Shiba inu (ETO FIASKO BRATAN) + { + /* Wolf */ + Model *wolfModel = Model::loadFromFile("Husky.glb", skeletalShaderProgram); + auto wolfObj = engine->NewObject(); + Logger::Info("%s", wolfObj.GetName().c_str()); + wolfObj.AddTransform(Vec3(5.f, -5.f, -2.f), Vec3(1.f), Mat4(1.0)); + wolfObj.AddModel(*wolfModel); + wolfObj.AddSkeletalAnimationsManager("Husky.glb", wolfModel, TPoseType::FROMANIM, 0); + + class WolfBehaviour : public Behaviour { + public: + void Update(float dt) override { + left -= dt; + if (left < 0) { + self.GetSkeletalAnimationsManager()->PlayImmediately(cur, 1); + left = delay; + cur++; + cur %= 10; + } + } + + float delay = 3.0f; + float left = delay; + int cur = 8; + }; + + wolfObj.AddBehaviour(); + } { Model * model = Model::loadFromFile(catSource); @@ -122,13 +151,6 @@ int main() { t.RotateGlobal(1.67f, Vec3(-1.f, 0.f, 0.f)); } - // Shiba inu (ETO FIASKO BRATAN) - Model *model = Model::loadFromFile("ShibaInu.fbx"); - model->shader = standartShaderProgram; - auto dog = engine->NewObject(); - dog.AddModel(*model); - dog.AddTransform(Transform(Vec3(2, -5, 0.0), Vec3(1.f), glm::radians(-90.f), Vec3(1.0f, 0.f, 0.f))); - Material material = { 4.f, Texture("wall.png", "wallspecular.png") @@ -193,13 +215,13 @@ int main() { Mat3(Vec3(1, 0, 0), Vec3(0, 1, 0), Vec3(0, 0, 1)), Vec3(0.5, 0.5, 0.5), }, - cubeModel); + cubeModel); obb3.AddBehaviour(); - auto cat = engine->NewObject(); - cat.AddModel(*model); - auto &t = cat.AddTransform(Vec3(0.f, -5.f, -8.f), Vec3(0.1f), Mat4(1.0)); + // auto cat = engine->NewObject(); + // cat.AddModel(*model); + // auto &t = cat.AddTransform(Vec3(0.f, -5.f, -8.f), Vec3(0.1f), Mat4(1.0)); auto staticAABB = setUpObjRigidBody(