- In MovingCubeApp animations are now allocated on stack
- m_sequenceAnimationController now uses SetAnimationPoseResetTime()
- Removed OnSequenceAnimationCompleted()
- Pose::GetOrientation() now returns by value(without it my code doesn't compile)
- GetStep() getter
- In if(m_loop) checking for m_resetTime to be zero or last step to be equal to the first
- Tick now ticks only if there are at least one element in m_steps
- IsFinished() function
- AddAnimationSteps() functions that take initializer_lists
- Getters in SimpleAnimationController are now const
This commit is contained in:
Vladyslav Baranovskyi
2024-06-08 17:36:03 +03:00
parent f3fcd049c4
commit 1311165139
5 changed files with 147 additions and 70 deletions

View File

@@ -40,8 +40,8 @@ namespace OpenVulkano
Scene::Material m_material; Scene::Material m_material;
Scene::Shader m_shader; Scene::Shader m_shader;
std::unique_ptr<Scene::SimpleAnimationController> m_simpleAnimationController; Scene::SimpleAnimationController m_simpleAnimationController;
std::unique_ptr<Scene::SequenceAnimationController> m_sequenceAnimationController; Scene::SequenceAnimationController m_sequenceAnimationController;
SceneElement m_whiteBox; SceneElement m_whiteBox;
SceneElement m_redBox; SceneElement m_redBox;
@@ -79,24 +79,22 @@ namespace OpenVulkano
CreateSceneElement(&m_whiteBox, Math::Vector4f(1, 1, 1, 1), 1); CreateSceneElement(&m_whiteBox, Math::Vector4f(1, 1, 1, 1), 1);
CreateSceneElement(&m_redBox, Math::Vector4f(1, 0.2, 0.2, 1.0), 0.3); CreateSceneElement(&m_redBox, Math::Vector4f(1, 0.2, 0.2, 1.0), 0.3);
m_simpleAnimationController = std::make_unique<Scene::SimpleAnimationController>(); m_simpleAnimationController.SetNode(&m_whiteBox.m_node);
m_simpleAnimationController->SetNode(&m_whiteBox.m_node); m_simpleAnimationController.SetDuration(3);
m_simpleAnimationController->SetDuration(3);
Math::Pose srcPose(Math::Quaternion<float>(), Math::Vector3f_SIMD(-3, 0, 0)); Math::Pose srcPose(Math::Quaternion<float>(), Math::Vector3f_SIMD(-3, 0, 0));
Math::Pose destPose(Math::Quaternion<float>(), Math::Vector3f_SIMD(3, 0, 0)); Math::Pose destPose(Math::Quaternion<float>(), Math::Vector3f_SIMD(3, 0, 0));
m_simpleAnimationController->SetPoses(srcPose, destPose); m_simpleAnimationController.SetPoses(srcPose, destPose);
m_simpleAnimationController->m_completionEvent += EventHandler(this, &MovingCubeAppImpl::OnSimpleAnimationCompleted); m_simpleAnimationController.m_completionEvent += EventHandler(this, &MovingCubeAppImpl::OnSimpleAnimationCompleted);
m_sequenceAnimationController = std::make_unique<Scene::SequenceAnimationController>(); m_sequenceAnimationController.EnableLoop(true);
m_sequenceAnimationController->EnableLoop(false); m_sequenceAnimationController.SetNode(&m_redBox.m_node);
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(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(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(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(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(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->AddAnimationStep(Math::PoseF(Math::Utils::normalize(Math::QuaternionF(0, 0, 1, 1)), Math::Vector3f_SIMD(0, 1, 0)), 1); m_sequenceAnimationController.SetAnimationPoseResetTime(10);
m_sequenceAnimationController->m_sequenceCompletionEvent += EventHandler(this, &MovingCubeAppImpl::OnSequenceAnimationCompleted);
} }
void OnSimpleAnimationCompleted(Scene::SimpleAnimationController *anim) void OnSimpleAnimationCompleted(Scene::SimpleAnimationController *anim)
@@ -105,17 +103,11 @@ namespace OpenVulkano
anim->Reset(); anim->Reset();
} }
void OnSequenceAnimationCompleted(Scene::SequenceAnimationController *anim)
{
Logger::APP->info("Animation sequence completed - restarting...");
m_sequenceAnimationController->Restart();
}
void Tick() override void Tick() override
{ {
m_cameraControl.Tick(); m_cameraControl.Tick();
m_simpleAnimationController->Tick(); m_simpleAnimationController.Tick();
m_sequenceAnimationController->Tick(); m_sequenceAnimationController.Tick();
} }
void Close() override void Close() override

View File

@@ -44,7 +44,12 @@ namespace OpenVulkano::Math
} }
} }
[[nodiscard]] Quaternion<T>& GetOrientation() const { return m_orientation; } bool operator==(const Math::Pose<T> &otherPose)
{
return (GetOrientation() == otherPose.GetOrientation()) && (GetPosition() == otherPose.GetPosition());
}
[[nodiscard]] Quaternion<T> GetOrientation() const { return m_orientation; }
[[nodiscard]] Vector3_SIMD<T> GetPosition() const { return m_position; } [[nodiscard]] Vector3_SIMD<T> GetPosition() const { return m_position; }

View File

@@ -10,31 +10,47 @@ namespace OpenVulkano::Scene
{ {
SequenceAnimationController::SequenceAnimationController() SequenceAnimationController::SequenceAnimationController()
{ {
// m_animationController.m_completionEvent += EventHandler(this, &SequenceAnimationController::OnCurrentFrameFinished); m_animationController.m_completionEvent += EventHandler(this, &SequenceAnimationController::OnCurrentFrameFinished);
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()) if(m_steps.empty())
return; return;
if(m_currentStep < m_steps.size() - 1) if(m_currentStep < m_steps.size() - 1)
{ {
++m_currentStep; m_currentStep++;
SetPoses(GetTargetPose(), m_steps[m_currentStep].first); m_animationController.SetPoses(m_animationController.GetTargetPose(), m_steps[m_currentStep].first);
SetDuration(m_steps[m_currentStep-1].second); m_animationController.SetDuration(m_steps[m_currentStep].second);
SimpleAnimationController::Reset(); m_animationController.Reset();
} }
else else
{ {
if(m_loop) if(m_loop)
{
// 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_currentStep = 0;
SetPoses(GetTargetPose(), m_steps[m_currentStep].first); m_animationController.SetPoses(m_steps.back().first, m_steps.front().first);
// We don't set the duration because transition from the last step to the first m_animationController.SetDuration(m_resetTime);
// requires to use duration of the last step m_animationController.Reset();
SimpleAnimationController::Reset(); }
} }
else else
{ {
@@ -45,7 +61,9 @@ namespace OpenVulkano::Scene
void SequenceAnimationController::Tick() void SequenceAnimationController::Tick()
{ {
SimpleAnimationController::Tick(); if(m_steps.empty())
return;
m_animationController.Tick();
} }
void SequenceAnimationController::Restart() void SequenceAnimationController::Restart()
@@ -53,26 +71,60 @@ namespace OpenVulkano::Scene
m_currentStep = 0; m_currentStep = 0;
if(!m_steps.empty()) if(!m_steps.empty())
{ {
SetPoses(GetTargetPose(), m_steps[m_currentStep].first); m_animationController.SetPoses(m_animationController.GetTargetPose(), m_steps[m_currentStep].first);
SetDuration(m_steps.back().second); 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) void SequenceAnimationController::AddAnimationStep(const Math::PoseF &pose, double duration)
{ {
m_steps.emplace_back(pose, duration); m_steps.emplace_back(pose, duration);
if(m_steps.size() == 2) if(m_steps.size() > 1)
{ {
m_currentStep = 1; m_currentStep = 1;
SetPoses(m_steps[0].first, m_steps[1].first); m_animationController.SetPoses(m_steps[0].first, m_steps[1].first);
SetDuration(m_steps[0].second); m_animationController.SetDuration(m_steps[1].second);
Reset(); m_animationController.Reset();
} }
} }
void SequenceAnimationController::OnCurrentFrameFinished(SimpleAnimationController *animationController) void SequenceAnimationController::AddAnimationSteps(std::initializer_list<Math::PoseF> 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<PoseDurationPair> 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();
}
} }
} }

View File

@@ -14,32 +14,60 @@
#include <vector> #include <vector>
#include <utility> #include <utility>
#include <initializer_list>
namespace OpenVulkano::Scene namespace OpenVulkano::Scene
{ {
class SequenceAnimationController : public SimpleAnimationController class SequenceAnimationController : public ITickable
{ {
public:
using PoseDurationPair = std::pair<Math::PoseF, double>; using PoseDurationPair = std::pair<Math::PoseF, double>;
private:
SimpleAnimationController m_animationController;
std::vector<PoseDurationPair> m_steps; std::vector<PoseDurationPair> m_steps;
int m_currentStep = 0; size_t m_currentStep = 0;
bool m_loop = false; bool m_loop = false;
double m_resetTime = 0;
void AdvanceToNextStep();
public:
SequenceAnimationController();
Event<SequenceAnimationController *> 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(); }
void OnCurrentFrameFinished(SimpleAnimationController *animationController); void OnCurrentFrameFinished(SimpleAnimationController *animationController);
public:
Event<SequenceAnimationController *> 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<Math::PoseF> poses, double duration);
void AddAnimationSteps(std::initializer_list<PoseDurationPair> steps);
PoseDurationPair GetStep(int index);
void Tick() override; void Tick() override;
}; };
} }

View File

@@ -32,14 +32,14 @@ namespace OpenVulkano::Scene
void Reset() { m_elapsed = 0; } void Reset() { m_elapsed = 0; }
void SwapPoses() { std::swap(m_initialPose, m_targetPose); } 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; } void SetNode(Node *node) { m_node = node; }
const Math::PoseF& GetInitialPose() { return m_initialPose; } const Math::PoseF& GetInitialPose() const { return m_initialPose; }
const Math::PoseF& GetTargetPose() { return m_targetPose; } const Math::PoseF& GetTargetPose() const { return m_targetPose; }
void SetPoses(const Math::PoseF &initialPose, const Math::PoseF &targetPose) { m_initialPose = initialPose; m_targetPose = 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; } void SetDuration(double duration) { m_duration = duration; }
double GetProgress(); double GetProgress();