From 1628214370ff8bb0c21bd5e3c7f3e1eef35f18c3 Mon Sep 17 00:00:00 2001 From: Antoine Pilote Date: Wed, 6 Sep 2023 20:05:10 -0400 Subject: [PATCH] Animation serialization progress --- .../src/ComponentsPanel/SkinnedModelPanel.h | 12 ++- Nuake/src/Rendering/Mesh/SkinnedMesh.cpp | 26 ++++--- Nuake/src/Rendering/Mesh/SkinnedMesh.h | 6 -- Nuake/src/Resource/ModelLoader.cpp | 3 +- Nuake/src/Resource/SkeletalAnimation.cpp | 76 +++++++++++++++++++ Nuake/src/Resource/SkeletalAnimation.h | 22 +++++- Nuake/src/Resource/SkinnedModel.cpp | 16 ++++ Nuake/src/Scene/Components/BoneComponent.h | 15 ++++ Nuake/src/Scene/Entities/Entity.cpp | 9 ++- Nuake/src/Scene/Systems/AnimationSystem.cpp | 11 ++- 10 files changed, 170 insertions(+), 26 deletions(-) diff --git a/Editor/src/ComponentsPanel/SkinnedModelPanel.h b/Editor/src/ComponentsPanel/SkinnedModelPanel.h index eaf8e838..cdba235d 100644 --- a/Editor/src/ComponentsPanel/SkinnedModelPanel.h +++ b/Editor/src/ComponentsPanel/SkinnedModelPanel.h @@ -119,8 +119,9 @@ public: ImGui::TableNextColumn(); } + if(model->GetCurrentAnimation()) { - ImGui::Text("Type"); + ImGui::Text("Animation"); ImGui::TableNextColumn(); uint32_t animIndex = model->GetCurrentAnimationIndex(); @@ -131,7 +132,14 @@ public: for (int n = 0; n < model->GetAnimationsCount(); n++) { bool is_selected = (animIndex == n); - if (ImGui::Selectable(animations[n]->GetName().c_str(), is_selected)) + std::string animName = animations[n]->GetName(); + + if (animName.empty()) + { + animName = "Empty"; + } + + if (ImGui::Selectable(animName.c_str(), is_selected)) { animIndex = n; } diff --git a/Nuake/src/Rendering/Mesh/SkinnedMesh.cpp b/Nuake/src/Rendering/Mesh/SkinnedMesh.cpp index 2dad3af8..8d97a2d4 100644 --- a/Nuake/src/Rendering/Mesh/SkinnedMesh.cpp +++ b/Nuake/src/Rendering/Mesh/SkinnedMesh.cpp @@ -145,6 +145,12 @@ namespace Nuake v["Bitangent"]["y"] = m_Vertices[i].bitangent.y; v["Bitangent"]["z"] = m_Vertices[i].bitangent.z; + for (uint32_t b = 0; b < MAX_BONE_INFLUENCE; b++) + { + v["Weight"][b] = m_Vertices[i].weights[b]; + v["BoneIDs"][b] = m_Vertices[i].boneIDs[b]; + } + j["Vertices"][i] = v; } @@ -177,10 +183,17 @@ namespace Nuake vertex.uv = { 0.0, 0.0 }; } DESERIALIZE_VEC3(v["Position"], vertex.position) - DESERIALIZE_VEC3(v["Normal"], vertex.normal) - DESERIALIZE_VEC3(v["Tangent"], vertex.tangent) - DESERIALIZE_VEC3(v["Bitangent"], vertex.bitangent) - vertices.push_back(vertex); + DESERIALIZE_VEC3(v["Normal"], vertex.normal) + DESERIALIZE_VEC3(v["Tangent"], vertex.tangent) + DESERIALIZE_VEC3(v["Bitangent"], vertex.bitangent) + + for (uint32_t i = 0; i < MAX_BONE_INFLUENCE; i++) + { + vertex.weights[i] = v["Weight"][i]; + vertex.boneIDs[i] = v["boneIDs"][i]; + } + + vertices.push_back(vertex); } } ); @@ -190,9 +203,4 @@ namespace Nuake SetupMesh(); return true; } - - void SkinnedMesh::SetSkeletalAnimation(Ref skeletalAnimation) - { - m_SkeletalAnimation = skeletalAnimation; - } } diff --git a/Nuake/src/Rendering/Mesh/SkinnedMesh.h b/Nuake/src/Rendering/Mesh/SkinnedMesh.h index b7d5cb1c..c8af79f7 100644 --- a/Nuake/src/Rendering/Mesh/SkinnedMesh.h +++ b/Nuake/src/Rendering/Mesh/SkinnedMesh.h @@ -26,7 +26,6 @@ namespace Nuake void AddSurface(std::vector vertices, std::vector indices, std::vector bones); std::vector& GetVertices(); std::vector& GetIndices(); - std::vector& GetBones(); Ref GetMaterial() inline const; void SetMaterial(Ref material); @@ -40,12 +39,7 @@ namespace Nuake json Serialize() override; bool Deserialize(const json& j) override; - void SetSkeletalAnimation(Ref animation); - Ref GetSkeletalAnimation() const { return m_SkeletalAnimation; } - private: - Ref m_SkeletalAnimation; - Ref m_Material = nullptr; std::vector m_Indices; std::vector m_Vertices; diff --git a/Nuake/src/Resource/ModelLoader.cpp b/Nuake/src/Resource/ModelLoader.cpp index eed7afa5..41e3fc6e 100644 --- a/Nuake/src/Resource/ModelLoader.cpp +++ b/Nuake/src/Resource/ModelLoader.cpp @@ -67,7 +67,8 @@ namespace Nuake aiProcess_Triangulate | aiProcess_GenSmoothNormals | aiProcess_FixInfacingNormals | - aiProcess_CalcTangentSpace; + aiProcess_CalcTangentSpace | + aiProcess_OptimizeGraph; modelDir = absolute ? path + "/../" : FileSystem::Root + path + "/../"; const std::string filePath = absolute ? path : FileSystem::Root + path; diff --git a/Nuake/src/Resource/SkeletalAnimation.cpp b/Nuake/src/Resource/SkeletalAnimation.cpp index c2cfd7ee..80e5d180 100644 --- a/Nuake/src/Resource/SkeletalAnimation.cpp +++ b/Nuake/src/Resource/SkeletalAnimation.cpp @@ -82,6 +82,47 @@ namespace Nuake return glm::scale(glm::mat4(1.0f), finalScale); } + json BoneTransformTrack::Serialize() + { + BEGIN_SERIALIZE(); + for (uint32_t i = 0; i < m_PositionTimestamps.size(); i++) + { + j["m_PositionTimestamps"][i] = m_PositionTimestamps[i]; + } + + for (uint32_t i = 0; i < m_Positions.size(); i++) + { + j["m_Positions"][i] = SERIALIZE_VEC3(m_Positions[i]); + } + + for (uint32_t i = 0; i < m_RotationTimestamps.size(); i++) + { + j["m_RotationTimestamps"][i] = m_RotationTimestamps[i]; + } + + for (uint32_t i = 0; i < m_Rotations.size(); i++) + { + j["m_Rotations"][i] = SERIALIZE_VEC4(m_Rotations[i]); + } + + for (uint32_t i = 0; i < m_ScaleTimestamps.size(); i++) + { + j["m_ScaleTimestamps"][i] = m_ScaleTimestamps[i]; + } + + for (uint32_t i = 0; i < m_Scales.size(); i++) + { + j["m_Scales"][i] = SERIALIZE_VEC3(m_Scales[i]); + } + + END_SERIALIZE(); + } + + bool BoneTransformTrack::Deserialize(const json& j) + { + return true; + } + SkeletalAnimation::SkeletalAnimation(const std::string& name, float duration, float ticksPerSecond) { m_Name = name; @@ -101,4 +142,39 @@ namespace Nuake m_Tracks[name] = BoneTransformTrack(); return m_Tracks[name]; } + + json SkeletalAnimation::Serialize() + { + BEGIN_SERIALIZE(); + SERIALIZE_VAL(m_Name); + SERIALIZE_VAL(m_Duration); + SERIALIZE_VAL(m_TicksPerSecond); + SERIALIZE_VAL(m_CurrentTime); + SERIALIZE_VAL(m_Loop); + + for (auto& t : m_Tracks) + { + j["Tracks"][t.first] = t.second.Serialize(); + } + + return json(); + } + + bool SkeletalAnimation::Deserialize(const json& j) + { + m_Name = j["m_Name"]; + m_Duration = j["m_Duration"]; + m_TicksPerSecond = j["m_TicksPerSecond"]; + m_CurrentTime = j["m_CurrentTime"]; + m_Loop = j["m_Loop"]; + + for (auto& [trackName, trackData]: j["Tracks"].items()) + { + BoneTransformTrack track; + track.Deserialize(j["Tracks"][trackName]); + m_Tracks[trackName] = std::move(track); + } + return true; + } + } \ No newline at end of file diff --git a/Nuake/src/Resource/SkeletalAnimation.h b/Nuake/src/Resource/SkeletalAnimation.h index 4ea0419f..5e649bd9 100644 --- a/Nuake/src/Resource/SkeletalAnimation.h +++ b/Nuake/src/Resource/SkeletalAnimation.h @@ -2,6 +2,9 @@ #include "src/Core/Core.h" #include +#include "src/Resource/Serializable.h" +#include "src/Resource/Resource.h" + namespace Nuake { struct SkeletonAnimationBoneAnimation @@ -94,9 +97,12 @@ namespace Nuake Matrix4 InterpolatePosition(float time); Matrix4 InterpolateRotation(float time); Matrix4 InterpolateScale(float time); + + json Serialize(); + bool Deserialize(const json& j); }; - class SkeletalAnimation + class SkeletalAnimation : public Resource, ISerializable { private: std::unordered_map m_Tracks; @@ -104,7 +110,7 @@ namespace Nuake float m_Duration; float m_TicksPerSecond; std::string m_Name; - bool m_Loop; + bool m_Loop = false; public: SkeletalAnimation() = default; @@ -114,7 +120,14 @@ namespace Nuake void SetCurrentTime(float time) { - m_CurrentTime = fmod(time, m_Duration); + if (m_Loop) + { + m_CurrentTime = fmod(time, m_Duration); + } + else + { + m_CurrentTime = time; + } } float GetCurrentTime() const { return m_CurrentTime; } @@ -126,5 +139,8 @@ namespace Nuake const std::string& GetName() const { return m_Name; } BoneTransformTrack& GetTrack(const std::string& name); std::unordered_map& GetTracks() { return m_Tracks; } + + json Serialize() override; + bool Deserialize(const json& j) override; }; } diff --git a/Nuake/src/Resource/SkinnedModel.cpp b/Nuake/src/Resource/SkinnedModel.cpp index a6bf06d3..ac71f490 100644 --- a/Nuake/src/Resource/SkinnedModel.cpp +++ b/Nuake/src/Resource/SkinnedModel.cpp @@ -78,7 +78,19 @@ namespace Nuake { j["Meshes"][i] = m_Meshes[i]->Serialize(); } + + j["m_CurrentAnimation"] = m_CurrentAnimation; + j["m_NumAnimation"] = m_NumAnimation; + + uint32_t a = 0; + for (auto& animation : m_Animations) + { + j["m_Animations"][a] = animation->Serialize(); + } + } + + END_SERIALIZE(); } @@ -91,6 +103,10 @@ namespace Nuake ModelLoader loader; auto otherModel = loader.LoadSkinnedModel(j["Path"], false); m_Meshes = otherModel->GetMeshes(); + m_Animations = otherModel->GetAnimations(); + m_SkeletonRoot = otherModel->GetSkeletonRootNode(); + m_NumAnimation = m_Animations.size(); + m_CurrentAnimation = 0; this->Path = j["Path"]; } diff --git a/Nuake/src/Scene/Components/BoneComponent.h b/Nuake/src/Scene/Components/BoneComponent.h index 2bb08862..824484b9 100644 --- a/Nuake/src/Scene/Components/BoneComponent.h +++ b/Nuake/src/Scene/Components/BoneComponent.h @@ -1,5 +1,6 @@ #pragma once #include "src/Core/Core.h" +#include "src/Resource/Serializable.h" namespace Nuake @@ -11,5 +12,19 @@ namespace Nuake ~BoneComponent() = default; std::string Name; + + json Serialize() + { + BEGIN_SERIALIZE(); + SERIALIZE_VAL(Name); + END_SERIALIZE(); + } + + bool Deserialize(const json& j) + { + Name = j["Name"]; + + return true; + } }; } \ No newline at end of file diff --git a/Nuake/src/Scene/Entities/Entity.cpp b/Nuake/src/Scene/Entities/Entity.cpp index 2e1b4928..3b6f7c25 100644 --- a/Nuake/src/Scene/Entities/Entity.cpp +++ b/Nuake/src/Scene/Entities/Entity.cpp @@ -16,6 +16,8 @@ #include "src/Scene/Components/CapsuleColliderComponent.h" #include "src/Scene/Components/SpriteComponent.h" #include "src/Scene/Components/ParticleEmitterComponent.h" +#include "src/Scene/Components/BoneComponent.h" +#include "src/Scene/Components/SkinnedModelComponent.h" namespace Nuake { @@ -69,7 +71,10 @@ namespace Nuake SERIALIZE_OBJECT_REF_LBL("QuakeMapComponent", GetComponent()) if (HasComponent()) SERIALIZE_OBJECT_REF_LBL("RigidBodyComponent", GetComponent()) - + if (HasComponent()) + SERIALIZE_OBJECT_REF_LBL("SkinnedModelComponent", GetComponent()) + if (HasComponent()) + SERIALIZE_OBJECT_REF_LBL("BoneComponent", GetComponent()) END_SERIALIZE(); } @@ -98,6 +103,8 @@ namespace Nuake DESERIALIZE_COMPONENT(SphereColliderComponent) DESERIALIZE_COMPONENT(RigidBodyComponent) DESERIALIZE_COMPONENT(BSPBrushComponent) + DESERIALIZE_COMPONENT(BoneComponent) + DESERIALIZE_COMPONENT(SkinnedModelComponent) return true; } diff --git a/Nuake/src/Scene/Systems/AnimationSystem.cpp b/Nuake/src/Scene/Systems/AnimationSystem.cpp index 7c934f70..9242115d 100644 --- a/Nuake/src/Scene/Systems/AnimationSystem.cpp +++ b/Nuake/src/Scene/Systems/AnimationSystem.cpp @@ -30,11 +30,14 @@ namespace Nuake } Ref animation = model->GetCurrentAnimation(); - float newAnimationTime = animation->GetCurrentTime() + (ts * animation->GetTicksPerSecond()); - animation->SetCurrentTime(newAnimationTime); + if (animation && model->IsPlaying) + { + float newAnimationTime = animation->GetCurrentTime() + (ts * animation->GetTicksPerSecond()); + animation->SetCurrentTime(newAnimationTime); - auto& rootBone = model->GetSkeletonRootNode(); - UpdateBonePositionTraversal(rootBone, animation, animation->GetCurrentTime()); + auto& rootBone = model->GetSkeletonRootNode(); + UpdateBonePositionTraversal(rootBone, animation, animation->GetCurrentTime()); + } } }