diff --git a/examples/ExampleApps/MovingCubeApp.cpp b/examples/ExampleApps/MovingCubeApp.cpp index a3671f2..472583e 100644 --- a/examples/ExampleApps/MovingCubeApp.cpp +++ b/examples/ExampleApps/MovingCubeApp.cpp @@ -40,8 +40,8 @@ namespace OpenVulkano Scene::Material m_material; Scene::Shader m_shader; - std::unique_ptr m_simpleAnimationController; - std::unique_ptr m_sequenceAnimationController; + Scene::SimpleAnimationController m_simpleAnimationController; + Scene::SequenceAnimationController m_sequenceAnimationController; SceneElement m_whiteBox; SceneElement m_redBox; @@ -79,24 +79,22 @@ namespace OpenVulkano CreateSceneElement(&m_whiteBox, Math::Vector4f(1, 1, 1, 1), 1); CreateSceneElement(&m_redBox, Math::Vector4f(1, 0.2, 0.2, 1.0), 0.3); - m_simpleAnimationController = std::make_unique(); - m_simpleAnimationController->SetNode(&m_whiteBox.m_node); - m_simpleAnimationController->SetDuration(3); + m_simpleAnimationController.SetNode(&m_whiteBox.m_node); + m_simpleAnimationController.SetDuration(3); Math::Pose srcPose(Math::Quaternion(), Math::Vector3f_SIMD(-3, 0, 0)); Math::Pose destPose(Math::Quaternion(), Math::Vector3f_SIMD(3, 0, 0)); - m_simpleAnimationController->SetPoses(srcPose, destPose); - m_simpleAnimationController->m_completionEvent += EventHandler(this, &MovingCubeAppImpl::OnSimpleAnimationCompleted); + m_simpleAnimationController.SetPoses(srcPose, destPose); + m_simpleAnimationController.m_completionEvent += EventHandler(this, &MovingCubeAppImpl::OnSimpleAnimationCompleted); - m_sequenceAnimationController = std::make_unique(); - m_sequenceAnimationController->EnableLoop(false); - m_sequenceAnimationController->SetNode(&m_redBox.m_node); - m_sequenceAnimationController->AddAnimationStep(Math::PoseF(Math::Utils::normalize(Math::QuaternionF(1, 0, 0, 1)), Math::Vector3f_SIMD(0, 0, 1)), 5); - m_sequenceAnimationController->AddAnimationStep(Math::PoseF(Math::Utils::normalize(Math::QuaternionF(2, 1, 0, 1)), Math::Vector3f_SIMD(1, 1, -1)), 3); - m_sequenceAnimationController->AddAnimationStep(Math::PoseF(Math::Utils::normalize(Math::QuaternionF(1, 1, 1, 1)), Math::Vector3f_SIMD(2, 1, -2)), 3); - m_sequenceAnimationController->AddAnimationStep(Math::PoseF(Math::Utils::normalize(Math::QuaternionF(0, 1, 1, 0)), Math::Vector3f_SIMD(2, 1, -1)), 3); - m_sequenceAnimationController->AddAnimationStep(Math::PoseF(Math::Utils::normalize(Math::QuaternionF(3, 2, 1, 1)), Math::Vector3f_SIMD(1, 1, -1)), 3); - m_sequenceAnimationController->AddAnimationStep(Math::PoseF(Math::Utils::normalize(Math::QuaternionF(0, 0, 1, 1)), Math::Vector3f_SIMD(0, 1, 0)), 1); - m_sequenceAnimationController->m_sequenceCompletionEvent += EventHandler(this, &MovingCubeAppImpl::OnSequenceAnimationCompleted); + m_sequenceAnimationController.EnableLoop(true); + m_sequenceAnimationController.SetNode(&m_redBox.m_node); + m_sequenceAnimationController.AddAnimationStep(Math::PoseF(Math::Utils::normalize(Math::QuaternionF(1, 0, 0, 1)), Math::Vector3f_SIMD(0, 0, 1)), 5); + m_sequenceAnimationController.AddAnimationStep(Math::PoseF(Math::Utils::normalize(Math::QuaternionF(2, 1, 0, 1)), Math::Vector3f_SIMD(1, 1, -1)), 3); + m_sequenceAnimationController.AddAnimationStep(Math::PoseF(Math::Utils::normalize(Math::QuaternionF(1, 1, 1, 1)), Math::Vector3f_SIMD(2, 1, -2)), 3); + m_sequenceAnimationController.AddAnimationStep(Math::PoseF(Math::Utils::normalize(Math::QuaternionF(0, 1, 1, 0)), Math::Vector3f_SIMD(2, 1, -1)), 3); + m_sequenceAnimationController.AddAnimationStep(Math::PoseF(Math::Utils::normalize(Math::QuaternionF(3, 2, 1, 1)), Math::Vector3f_SIMD(1, 1, -1)), 3); + m_sequenceAnimationController.AddAnimationStep(Math::PoseF(Math::Utils::normalize(Math::QuaternionF(0, 0, 1, 1)), Math::Vector3f_SIMD(0, 1, 0)), 1); + m_sequenceAnimationController.SetAnimationPoseResetTime(10); } void OnSimpleAnimationCompleted(Scene::SimpleAnimationController *anim) @@ -105,17 +103,11 @@ namespace OpenVulkano anim->Reset(); } - void OnSequenceAnimationCompleted(Scene::SequenceAnimationController *anim) - { - Logger::APP->info("Animation sequence completed - restarting..."); - m_sequenceAnimationController->Restart(); - } - void Tick() override { m_cameraControl.Tick(); - m_simpleAnimationController->Tick(); - m_sequenceAnimationController->Tick(); + m_simpleAnimationController.Tick(); + m_sequenceAnimationController.Tick(); } void Close() override diff --git a/openVulkanoCpp/Math/Pose.hpp b/openVulkanoCpp/Math/Pose.hpp index 6ee6506..56d2dbb 100644 --- a/openVulkanoCpp/Math/Pose.hpp +++ b/openVulkanoCpp/Math/Pose.hpp @@ -44,7 +44,12 @@ namespace OpenVulkano::Math } } - [[nodiscard]] Quaternion& GetOrientation() const { return m_orientation; } + bool operator==(const Math::Pose &otherPose) + { + return (GetOrientation() == otherPose.GetOrientation()) && (GetPosition() == otherPose.GetPosition()); + } + + [[nodiscard]] Quaternion GetOrientation() const { return m_orientation; } [[nodiscard]] Vector3_SIMD GetPosition() const { return m_position; } diff --git a/openVulkanoCpp/Scene/SequenceAnimationController.cpp b/openVulkanoCpp/Scene/SequenceAnimationController.cpp index 3f0c76f..b056827 100644 --- a/openVulkanoCpp/Scene/SequenceAnimationController.cpp +++ b/openVulkanoCpp/Scene/SequenceAnimationController.cpp @@ -10,31 +10,47 @@ namespace OpenVulkano::Scene { SequenceAnimationController::SequenceAnimationController() { - // m_animationController.m_completionEvent += EventHandler(this, &SequenceAnimationController::OnCurrentFrameFinished); - m_completionEvent += EventHandler(this, &SequenceAnimationController::OnCurrentFrameFinished); + m_animationController.m_completionEvent += EventHandler(this, &SequenceAnimationController::OnCurrentFrameFinished); } - void SequenceAnimationController::AdvanceToNextStep() + SequenceAnimationController::PoseDurationPair SequenceAnimationController::GetStep(int index) + { + if(index >= 0 && index < m_steps.size()) + return m_steps[index]; + return PoseDurationPair(); + } + + void SequenceAnimationController::OnCurrentFrameFinished(SimpleAnimationController *ignored) { if(m_steps.empty()) return; if(m_currentStep < m_steps.size() - 1) { - ++m_currentStep; - SetPoses(GetTargetPose(), m_steps[m_currentStep].first); - SetDuration(m_steps[m_currentStep-1].second); - SimpleAnimationController::Reset(); + m_currentStep++; + m_animationController.SetPoses(m_animationController.GetTargetPose(), m_steps[m_currentStep].first); + m_animationController.SetDuration(m_steps[m_currentStep].second); + m_animationController.Reset(); } else { if(m_loop) { - m_currentStep = 0; - SetPoses(GetTargetPose(), m_steps[m_currentStep].first); - // We don't set the duration because transition from the last step to the first - // requires to use duration of the last step - SimpleAnimationController::Reset(); + // NOTE(vb): Maybe compare steps with some epsilon value + if(m_steps.size() > 1 && ((m_steps.front().first == m_steps.back().first) || m_resetTime == 0)) + { + m_currentStep = 1; + m_animationController.SetPoses(m_steps[m_currentStep-1].first, m_steps[m_currentStep].first); + m_animationController.SetDuration(m_steps[m_currentStep].second); + m_animationController.Reset(); + } + else + { + m_currentStep = 0; + m_animationController.SetPoses(m_steps.back().first, m_steps.front().first); + m_animationController.SetDuration(m_resetTime); + m_animationController.Reset(); + } } else { @@ -45,7 +61,9 @@ namespace OpenVulkano::Scene void SequenceAnimationController::Tick() { - SimpleAnimationController::Tick(); + if(m_steps.empty()) + return; + m_animationController.Tick(); } void SequenceAnimationController::Restart() @@ -53,26 +71,60 @@ namespace OpenVulkano::Scene m_currentStep = 0; if(!m_steps.empty()) { - SetPoses(GetTargetPose(), m_steps[m_currentStep].first); - SetDuration(m_steps.back().second); + m_animationController.SetPoses(m_animationController.GetTargetPose(), m_steps[m_currentStep].first); + m_animationController.SetDuration(m_steps[m_currentStep].second); } - SimpleAnimationController::Reset(); + m_animationController.Reset(); + } + + bool SequenceAnimationController::IsFinished() const + { + if(m_loop) + return false; + return m_currentStep >= m_steps.size(); } void SequenceAnimationController::AddAnimationStep(const Math::PoseF &pose, double duration) { m_steps.emplace_back(pose, duration); - if(m_steps.size() == 2) + if(m_steps.size() > 1) { m_currentStep = 1; - SetPoses(m_steps[0].first, m_steps[1].first); - SetDuration(m_steps[0].second); - Reset(); + m_animationController.SetPoses(m_steps[0].first, m_steps[1].first); + m_animationController.SetDuration(m_steps[1].second); + m_animationController.Reset(); } } - void SequenceAnimationController::OnCurrentFrameFinished(SimpleAnimationController *animationController) + void SequenceAnimationController::AddAnimationSteps(std::initializer_list poses, double duration) { - AdvanceToNextStep(); + for(const auto& pose : poses) + { + m_steps.emplace_back(pose, duration); + } + + if(m_steps.size() > 1) + { + m_currentStep = 1; + m_animationController.SetPoses(m_steps[0].first, m_steps[1].first); + m_animationController.SetDuration(m_steps[1].second); + m_animationController.Reset(); + } + } + + void SequenceAnimationController::AddAnimationSteps(std::initializer_list steps) + { + for(const auto& step : steps) + { + m_steps.emplace_back(step); + } + + if(m_steps.size() > 1) + { + m_currentStep = 1; + m_animationController.SetPoses(m_steps[0].first, m_steps[1].first); + m_animationController.SetDuration(m_steps[1].second); + m_animationController.Reset(); + } } } \ No newline at end of file diff --git a/openVulkanoCpp/Scene/SequenceAnimationController.hpp b/openVulkanoCpp/Scene/SequenceAnimationController.hpp index 0d711eb..3712e9c 100644 --- a/openVulkanoCpp/Scene/SequenceAnimationController.hpp +++ b/openVulkanoCpp/Scene/SequenceAnimationController.hpp @@ -14,32 +14,60 @@ #include #include +#include namespace OpenVulkano::Scene { - class SequenceAnimationController : public SimpleAnimationController + class SequenceAnimationController : public ITickable { - using PoseDurationPair = std::pair; - - std::vector m_steps; - int m_currentStep = 0; - bool m_loop = false; - - void AdvanceToNextStep(); - public: - SequenceAnimationController(); - - Event m_sequenceCompletionEvent; - - void EnableLoop(bool value) { m_loop = value; } - - void Restart(); - void AddAnimationStep(const Math::PoseF &pose, double duration); - bool IsFinished() { if(m_loop) return false; return m_currentStep >= m_steps.size(); } + using PoseDurationPair = std::pair; + + private: + SimpleAnimationController m_animationController; + std::vector m_steps; + size_t m_currentStep = 0; + bool m_loop = false; + double m_resetTime = 0; void OnCurrentFrameFinished(SimpleAnimationController *animationController); + public: + Event m_sequenceCompletionEvent; + + SequenceAnimationController(); + + /* + * Enables or disables looping of the animation sequence. + * + * When looping is enabled, the sequence will restart from the first step after the last step is completed. + * If the start and end poses of the step list are equal or the reset time is 0, the sequence will jump + * directly to the first step. Otherwise, it will animate the transition back to the first step using the + * reset time set by the user. + */ + void EnableLoop(bool value) { m_loop = value; } + + void SetNode(Node *node) { m_animationController.SetNode(node); } + Node* GetNode() const { return m_animationController.GetNode(); } + + /* + * Sets the time to transition back to the first pose when looping is enabled. + * + * If the reset time is set to 0, the sequence will jump directly to the first step. If the start and end poses + * of the step list are equal, the sequence will also jump directly to the first step regardless of the reset time. + * Otherwise, the sequence will animate the transition back to the first step using the reset time. + */ + void SetAnimationPoseResetTime(double resetTime) { m_resetTime = resetTime; } + double GetAnimationPoseResetTime() const { return m_resetTime; } + + void Restart(); + bool IsFinished() const; + + void AddAnimationStep(const Math::PoseF &pose, double duration); + void AddAnimationSteps(std::initializer_list poses, double duration); + void AddAnimationSteps(std::initializer_list steps); + PoseDurationPair GetStep(int index); + void Tick() override; }; } \ No newline at end of file diff --git a/openVulkanoCpp/Scene/SimpleAnimationController.hpp b/openVulkanoCpp/Scene/SimpleAnimationController.hpp index a369f4c..08271b6 100644 --- a/openVulkanoCpp/Scene/SimpleAnimationController.hpp +++ b/openVulkanoCpp/Scene/SimpleAnimationController.hpp @@ -32,14 +32,14 @@ namespace OpenVulkano::Scene void Reset() { m_elapsed = 0; } void SwapPoses() { std::swap(m_initialPose, m_targetPose); } - Node* GetNode() { return m_node; } + Node* GetNode() const { return m_node; } void SetNode(Node *node) { m_node = node; } - const Math::PoseF& GetInitialPose() { return m_initialPose; } - const Math::PoseF& GetTargetPose() { return m_targetPose; } + const Math::PoseF& GetInitialPose() const { return m_initialPose; } + const Math::PoseF& GetTargetPose() const { return m_targetPose; } void SetPoses(const Math::PoseF &initialPose, const Math::PoseF &targetPose) { m_initialPose = initialPose; m_targetPose = targetPose; } - double GetDuration() { return m_duration; } + double GetDuration() const { return m_duration; } void SetDuration(double duration) { m_duration = duration; } double GetProgress();