From c0f1178e7d1284ea3306a02126b65ccd5f36ffb0 Mon Sep 17 00:00:00 2001 From: Antoine Pilote Date: Sun, 17 Sep 2023 20:16:04 -0400 Subject: [PATCH] Now properly serialize and deserialize rigged models --- Nuake/src/Resource/Serializable.h | 21 +++++++++ Nuake/src/Resource/SkeletalAnimation.cpp | 46 ++++++++++++++----- Nuake/src/Resource/SkeletalAnimation.h | 46 +++++++++++-------- Nuake/src/Resource/SkeletonNode.h | 43 +++++++++++++++++ Nuake/src/Resource/SkinnedModel.cpp | 11 ++++- .../Scene/Components/SkinnedModelComponent.h | 1 + Nuake/src/Scene/Systems/AnimationSystem.cpp | 12 +++-- 7 files changed, 143 insertions(+), 37 deletions(-) diff --git a/Nuake/src/Resource/Serializable.h b/Nuake/src/Resource/Serializable.h index 17a22834..5d243f59 100644 --- a/Nuake/src/Resource/Serializable.h +++ b/Nuake/src/Resource/Serializable.h @@ -34,6 +34,27 @@ p = j[#p]; \ #define DESERIALIZE_VEC2(v, p) \ p = Vector2(v["x"], v["y"]); +#define SERIALIZE_MAT4(lbl, m) \ +{ \ + int i = 0; \ + for (int l = 0; l < 4; l++) { \ + for (int k = 0; k < 4; k++) { \ + j[lbl][i] = m[l][k]; \ + i++; \ + } \ + } \ +} + +#define DESERIALIZE_MAT4(lbl, m) \ +{ \ + int i = 0; \ + for(int l = 0; l < 4; l++) { \ + for(int k = 0; k < 4; k++) { \ + m[l][k] = j[lbl][i];\ + i++; \ + } \ + } \ +} #define SERIALIZE_OBJECT(v) j[#v] = v->Serialize(); #define SERIALIZE_OBJECT_REF(v) j[#v] = v.Serialize(); diff --git a/Nuake/src/Resource/SkeletalAnimation.cpp b/Nuake/src/Resource/SkeletalAnimation.cpp index 80e5d180..46d73a82 100644 --- a/Nuake/src/Resource/SkeletalAnimation.cpp +++ b/Nuake/src/Resource/SkeletalAnimation.cpp @@ -11,6 +11,11 @@ namespace Nuake m_PositionTimestamps = std::vector(); m_RotationTimestamps = std::vector(); m_ScaleTimestamps = std::vector(); + + m_PositionTransform = Matrix4(1.0f); + m_RotationTransform = Matrix4(1.0f); + m_ScaleTransform = Matrix4(1.0f); + m_FinalTransform = Matrix4(1.0f); } float BoneTransformTrack::GetScaleFactor(float lastTime, float nextTime, float animationTime) @@ -22,7 +27,7 @@ namespace Nuake return scaleFactor; } - Nuake::Matrix4 BoneTransformTrack::InterpolatePosition(float time) + Matrix4 BoneTransformTrack::InterpolatePosition(float time) { if (m_Positions.size() == 0) { @@ -34,14 +39,20 @@ namespace Nuake return glm::translate(Matrix4(1.0f), m_Positions[0]); } + // This returns the last position when we are at the last keyframe int p0Index = GetPositionIndex(time); + if (p0Index == m_Positions.size() - 1) + { + return glm::translate(Matrix4(1.0f), m_Positions[p0Index]); + } + int p1Index = p0Index + 1; float scaleFactor = GetScaleFactor(m_PositionTimestamps[p0Index], m_PositionTimestamps[p1Index], time); - glm::vec3 finalPosition = glm::mix(m_Positions[p0Index], m_Positions[p1Index], scaleFactor); + Vector3 finalPosition = glm::mix(m_Positions[p0Index], m_Positions[p1Index], scaleFactor); return glm::translate(Matrix4(1.0f), finalPosition); } - Nuake::Matrix4 BoneTransformTrack::InterpolateRotation(float time) + Matrix4 BoneTransformTrack::InterpolateRotation(float time) { if (m_Rotations.size() == 0) { @@ -54,17 +65,21 @@ namespace Nuake return glm::toMat4(rotation); } + // This returns the last rotation when we are at the last keyframe int p0Index = GetRotationIndex(time); + if (p0Index == m_Rotations.size() - 1) + { + return glm::toMat4(m_Rotations[p0Index]); + } + int p1Index = p0Index + 1; - float scaleFactor = GetScaleFactor(m_RotationTimestamps[p0Index], - m_RotationTimestamps[p1Index], time); - glm::quat finalRotation = glm::slerp(m_Rotations[p0Index], - m_Rotations[p1Index], scaleFactor); + float scaleFactor = GetScaleFactor(m_RotationTimestamps[p0Index], m_RotationTimestamps[p1Index], time); + Quat finalRotation = glm::slerp(m_Rotations[p0Index],m_Rotations[p1Index], scaleFactor); finalRotation = glm::normalize(finalRotation); return glm::toMat4(finalRotation); } - Nuake::Matrix4 BoneTransformTrack::InterpolateScale(float time) + Matrix4 BoneTransformTrack::InterpolateScale(float time) { if (m_Scales.size() == 0) { @@ -72,14 +87,21 @@ namespace Nuake } if (1 == m_Scales.size()) + { return glm::scale(glm::mat4(1.0f), m_Scales[0]); + } + // This returns the last rotation when we are at the last keyframe int p0Index = GetScaleIndex(time); + if (p0Index == m_Scales.size() - 1) + { + return glm::scale(Matrix4(1.0f), m_Scales[p0Index]); + } + int p1Index = p0Index + 1; - float scaleFactor = GetScaleFactor(m_ScaleTimestamps[p0Index], - m_ScaleTimestamps[p1Index], time); - glm::vec3 finalScale = glm::mix(m_Scales[p0Index], m_Scales[p1Index], scaleFactor); - return glm::scale(glm::mat4(1.0f), finalScale); + float scaleFactor = GetScaleFactor(m_ScaleTimestamps[p0Index], m_ScaleTimestamps[p1Index], time); + Vector3 finalScale = glm::mix(m_Scales[p0Index], m_Scales[p1Index], scaleFactor); + return glm::scale(Matrix4(1.0f), finalScale); } json BoneTransformTrack::Serialize() diff --git a/Nuake/src/Resource/SkeletalAnimation.h b/Nuake/src/Resource/SkeletalAnimation.h index 15824a9f..cab1a2bc 100644 --- a/Nuake/src/Resource/SkeletalAnimation.h +++ b/Nuake/src/Resource/SkeletalAnimation.h @@ -59,25 +59,11 @@ namespace Nuake m_Scales.push_back(scale); } - int GetPositionIndex(float animationTime) - { - if (m_Positions.size() == 0) - { - return 0; - } - - for (int index = 0; index < m_Positions.size() - 1; index++) - { - if (animationTime < m_PositionTimestamps[index + 1]) - { - return index; - } - } - assert(0); - } - void Update(float time) { + // We cannot use const reference here because we would compare the reference to previous pos + // to the m_PositionTransform and previous pos always equale. + // Making the hasChanged always false. We would end up comparing the same values always. const Matrix4 previousPos = m_PositionTransform; const Matrix4 previousRot = m_RotationTransform; const Matrix4 previousSca = m_ScaleTransform; @@ -97,6 +83,24 @@ namespace Nuake return m_FinalTransform; } + int GetPositionIndex(float animationTime) + { + if (m_Positions.size() == 0) + { + return 0; + } + + for (int index = 0; index < m_Positions.size() - 1; index++) + { + if (animationTime < m_PositionTimestamps[index + 1]) + { + return index; + } + } + + return static_cast(m_Positions.size()) - 1; + } + /* Gets the current index on mKeyRotations to interpolate to based on the current animation time*/ int GetRotationIndex(float animationTime) @@ -108,7 +112,8 @@ namespace Nuake return index; } } - assert(0); + + return static_cast(m_Rotations.size()) - 1; } /* Gets the current index on mKeyScalings to interpolate to based on the @@ -122,7 +127,8 @@ namespace Nuake return index; } } - assert(0); + + return static_cast(m_Scales.size()) - 1; } float GetScaleFactor(float lastTime, float nextTime, float animationTime); @@ -158,7 +164,7 @@ namespace Nuake } else { - m_CurrentTime = time; + m_CurrentTime = std::max(time, m_Duration); } } diff --git a/Nuake/src/Resource/SkeletonNode.h b/Nuake/src/Resource/SkeletonNode.h index 32b54a3b..e5c22555 100644 --- a/Nuake/src/Resource/SkeletonNode.h +++ b/Nuake/src/Resource/SkeletonNode.h @@ -1,6 +1,7 @@ #pragma once #include "src/Core/Core.h" #include "src/Core/Maths.h" +#include "src/Resource/Serializable.h" namespace Nuake { @@ -14,5 +15,47 @@ namespace Nuake Matrix4 FinalTransform = Matrix4(1.0f); int32_t Id = -1; int32_t EntityHandle = 0; + + json Serialize() + { + BEGIN_SERIALIZE(); + SERIALIZE_MAT4("Transform", Transform); + SERIALIZE_MAT4("Offset", Offset); + SERIALIZE_VAL(Name); + SERIALIZE_VAL(ChildrenCount); + SERIALIZE_VAL(Id); + SERIALIZE_VAL(EntityHandle); + + uint32_t i = 0; + for (auto& c : Children) + { + j["Children"][i] = c.Serialize(); + i++; + } + + END_SERIALIZE(); + } + + bool Deserialize(json j) + { + DESERIALIZE_MAT4("Transform", Transform); + DESERIALIZE_MAT4("Offset", Offset); + DESERIALIZE_VAL(Name); + DESERIALIZE_VAL(ChildrenCount); + DESERIALIZE_VAL(Id); + DESERIALIZE_VAL(EntityHandle); + + if (j.contains("Children")) + { + for (uint32_t i = 0; i < ChildrenCount; i++) + { + SkeletonNode newChildren; + newChildren.Deserialize(j["Children"][i]); + Children.push_back(std::move(newChildren)); + } + } + + return true; + } }; } \ No newline at end of file diff --git a/Nuake/src/Resource/SkinnedModel.cpp b/Nuake/src/Resource/SkinnedModel.cpp index ac71f490..39f4187a 100644 --- a/Nuake/src/Resource/SkinnedModel.cpp +++ b/Nuake/src/Resource/SkinnedModel.cpp @@ -71,6 +71,7 @@ namespace Nuake if (this->Path != "") { j["Path"] = this->Path; + j["SkeletonNode"] = m_SkeletonRoot.Serialize(); } else { @@ -86,10 +87,9 @@ namespace Nuake for (auto& animation : m_Animations) { j["m_Animations"][a] = animation->Serialize(); + a++; } - } - END_SERIALIZE(); } @@ -108,6 +108,13 @@ namespace Nuake m_NumAnimation = m_Animations.size(); m_CurrentAnimation = 0; + if (j.contains("SkeletonNode")) + { + SkeletonNode skeletonNode; + skeletonNode.Deserialize(j["SkeletonNode"]); + m_SkeletonRoot = std::move(skeletonNode); + } + this->Path = j["Path"]; } else diff --git a/Nuake/src/Scene/Components/SkinnedModelComponent.h b/Nuake/src/Scene/Components/SkinnedModelComponent.h index 0e6bdb53..b0abed18 100644 --- a/Nuake/src/Scene/Components/SkinnedModelComponent.h +++ b/Nuake/src/Scene/Components/SkinnedModelComponent.h @@ -35,6 +35,7 @@ namespace Nuake bool Deserialize(const json& j) { ModelPath = j["ModelPath"]; + ModelResource = CreateRef(); if (j.contains("ModelResource")) diff --git a/Nuake/src/Scene/Systems/AnimationSystem.cpp b/Nuake/src/Scene/Systems/AnimationSystem.cpp index 9aa7fdd2..cd7987b3 100644 --- a/Nuake/src/Scene/Systems/AnimationSystem.cpp +++ b/Nuake/src/Scene/Systems/AnimationSystem.cpp @@ -47,17 +47,18 @@ namespace Nuake { const std::string& boneName = bone.Name; - auto& animationTrack = animation->GetTrack(boneName); + auto animationTrack = animation->GetTrack(boneName); Entity& boneEntity = m_Scene->GetEntity(boneName); Entity& boneEnt = m_Scene->GetEntityByID(bone.EntityHandle); ///assert(boneEnt.GetHandle() == boneEntity.GetHandle()); + if (boneEnt.IsValid()) { auto& transformComponent = boneEnt.GetComponent(); bone.FinalTransform = transformComponent.GetGlobalTransform() * bone.Offset; - //if (!animationTrack.IsEmpty()) + if (!animationTrack.IsEmpty()) { // Get Update transform animationTrack.Update(time); @@ -76,7 +77,12 @@ namespace Nuake transformComponent.Dirty = false; } } - + else + { + // Find bone + + } + for (auto& childBone : bone.Children) { UpdateBonePositionTraversal(childBone, animation, time);