Merge pull request 'Added SequenceAnimationController' (#39) from add_sequenceanimationcontroller into master

Reviewed-on: https://git.madvoxel.net/OpenVulkano/OpenVulkano/pulls/39
Reviewed-by: TymurStrelchyk <tymur.strelchyk.ext@madvoxel.com>
Reviewed-by: Georg Hagen <georg.hagen@madvoxel.com>
This commit is contained in:
Vladyslav_Baranovskyi_EXT
2024-06-11 10:42:51 +02:00
5 changed files with 285 additions and 38 deletions

View File

@@ -14,65 +14,90 @@
#include "Scene/SimpleDrawable.hpp"
#include "Scene/Camera.hpp"
#include "Scene/SimpleAnimationController.hpp"
#include "Scene/SequenceAnimationController.hpp"
#include "Input/InputManager.hpp"
#include "Host/GraphicsAppManager.hpp"
#include "Base/EngineConfiguration.hpp"
#include "Base/Logger.hpp"
#include "Controller/FreeCamCameraController.hpp"
namespace OpenVulkano
{
namespace
{
struct SceneElement
{
Scene::Geometry m_geometry;
Scene::SimpleDrawable m_drawable;
Scene::Node m_node;
};
}
class MovingCubeAppImpl final : public MovingCubeApp
{
OpenVulkano::Scene::Scene m_scene;
OpenVulkano::Scene::PerspectiveCamera m_camera;
OpenVulkano::FreeCamCameraController m_cameraControl;
OpenVulkano::Scene::Material m_material;
OpenVulkano::Scene::Shader m_shader;
Scene::Scene m_scene;
Scene::PerspectiveCamera m_camera;
FreeCamCameraController m_cameraControl;
Scene::Material m_material;
Scene::Shader m_shader;
OpenVulkano::Scene::Geometry m_geometry;
OpenVulkano::Scene::SimpleDrawable m_drawable;
OpenVulkano::Scene::Node m_node;
std::unique_ptr<OpenVulkano::Scene::SimpleAnimationController> m_animationController;
Scene::SimpleAnimationController m_simpleAnimationController;
Scene::SequenceAnimationController m_sequenceAnimationController;
SceneElement m_whiteBox;
SceneElement m_redBox;
void CreateSceneElement(SceneElement *dest, const Math::Vector4f &color, float scale)
{
dest->m_geometry.InitCube(scale, scale, scale, color);
dest->m_drawable.Init(&m_shader, &dest->m_geometry, &m_material);
dest->m_node.Init();
m_scene.GetRoot()->AddChild(&dest->m_node);
dest->m_node.SetUpdateFrequency(Scene::UpdateFrequency::Always);
dest->m_node.AddDrawable(&dest->m_drawable);
dest->m_node.SetMatrix(Math::Matrix4f(1));
}
public:
void Init() override
{
auto engineConfig = OpenVulkano::EngineConfiguration::GetEngineConfiguration();
auto engineConfig = EngineConfiguration::GetEngineConfiguration();
m_camera.Init(70, 16, 9, 0.1, 100);
// m_camera.SetMatrix(OpenVulkano::Math::Utils::translate(OpenVulkano::Math::Matrix4f(1), OpenVulkano::Math::Vector3f_SIMD(0, 0, -50)));
m_scene.Init();
m_scene.SetCamera(&m_camera);
m_shader.AddShaderProgram(OpenVulkano::ShaderProgramType::VERTEX, "Shader/basic");
m_shader.AddShaderProgram(OpenVulkano::ShaderProgramType::FRAGMENT, "Shader/basic");
m_shader.AddVertexInputDescription(OpenVulkano::Vertex::GetVertexInputDescription());
m_geometry.InitCube();
m_drawable.Init(&m_shader, &m_geometry, &m_material);
m_node.Init();
m_scene.GetRoot()->AddChild(&m_node);
m_node.SetUpdateFrequency(OpenVulkano::Scene::UpdateFrequency::Always);
m_node.AddDrawable(&m_drawable);
m_node.SetMatrix(OpenVulkano::Math::Matrix4f(1));
m_shader.AddShaderProgram(ShaderProgramType::VERTEX, "Shader/basic");
m_shader.AddShaderProgram(ShaderProgramType::FRAGMENT, "Shader/basic");
m_shader.AddVertexInputDescription(Vertex::GetVertexInputDescription());
GetGraphicsAppManager()->GetRenderer()->SetScene(&m_scene);
m_cameraControl.Init(&m_camera);
m_cameraControl.SetDefaultKeybindings();
m_animationController = std::make_unique<OpenVulkano::Scene::SimpleAnimationController>();
m_animationController->SetNode(&m_node);
m_animationController->SetDuration(3);
CreateSceneElement(&m_whiteBox, Math::Vector4f(1, 1, 1, 1), 1);
CreateSceneElement(&m_redBox, Math::Vector4f(1, 0.2, 0.2, 1.0), 0.3);
OpenVulkano::Math::Pose srcPose(OpenVulkano::Math::Quaternion<float>(), OpenVulkano::Math::Vector3f_SIMD(-3, 0, 0));
OpenVulkano::Math::Pose destPose(OpenVulkano::Math::Quaternion<float>(), OpenVulkano::Math::Vector3f_SIMD(3, 0, 0));
m_animationController->SetPoses(srcPose, destPose);
m_simpleAnimationController.SetNode(&m_whiteBox.m_node);
m_simpleAnimationController.SetDuration(3);
Math::Pose srcPose(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.m_completionEvent += EventHandler(this, &MovingCubeAppImpl::OnSimpleAnimationCompleted);
m_animationController->m_completionEvent += EventHandler(this, &MovingCubeAppImpl::OnAnimationCompleted);
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 OnAnimationCompleted(OpenVulkano::Scene::SimpleAnimationController *anim)
void OnSimpleAnimationCompleted(Scene::SimpleAnimationController *anim)
{
anim->SwapPoses();
anim->Reset();
@@ -81,7 +106,8 @@ namespace OpenVulkano
void Tick() override
{
m_cameraControl.Tick();
m_animationController->Tick();
m_simpleAnimationController.Tick();
m_sequenceAnimationController.Tick();
}
void Close() override

View File

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

View File

@@ -0,0 +1,130 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include "Scene/SequenceAnimationController.hpp"
namespace OpenVulkano::Scene
{
SequenceAnimationController::SequenceAnimationController()
{
m_animationController.m_completionEvent += EventHandler(this, &SequenceAnimationController::SequenceCompletedCallback);
}
const SequenceAnimationController::PoseDurationPair& SequenceAnimationController::GetStep(int index) const
{
if(index >= 0 && index < m_steps.size())
return m_steps[index];
return PoseDurationPair();
}
void SequenceAnimationController::SequenceCompletedCallback(SimpleAnimationController *ignored)
{
if(m_steps.empty())
return;
if(m_currentStep < m_steps.size() - 1)
{
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)
{
// 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
{
OnSequenceCompleted.NotifyAll(this);
}
}
}
void SequenceAnimationController::Tick()
{
if(m_steps.empty())
return;
m_animationController.Tick();
}
void SequenceAnimationController::Restart()
{
m_currentStep = 0;
if(!m_steps.empty())
{
m_animationController.SetPoses(m_animationController.GetTargetPose(), m_steps[m_currentStep].first);
m_animationController.SetDuration(m_steps[m_currentStep].second);
}
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() > 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<Math::PoseF> poses, double duration)
{
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

@@ -0,0 +1,86 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "Scene/SimpleAnimationController.hpp"
#include "Base/Event.hpp"
#include "Base/ITickable.hpp"
#include "Math/Math.hpp"
#include "Math/Pose.hpp"
#include <vector>
#include <utility>
#include <initializer_list>
namespace OpenVulkano::Scene
{
class SequenceAnimationController : public ITickable
{
public:
using PoseDurationPair = std::pair<Math::PoseF, double>;
private:
SimpleAnimationController m_animationController;
std::vector<PoseDurationPair> m_steps;
size_t m_currentStep = 0;
bool m_loop = false;
double m_resetTime = 0;
void SequenceCompletedCallback(SimpleAnimationController *animationController);
public:
Event<SequenceAnimationController *> OnSequenceCompleted;
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.
*
* @param loop A boolean value to enable or disable looping.
*/
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.
*
* @param resetTime The time to transition back to the first pose.
* @see GetAnimationPoseResetTime
*/
void SetAnimationPoseResetTime(double resetTime) { m_resetTime = resetTime; }
/**
* Gets the current reset time for transitioning back to the first pose.
*
* @return The current reset time.
* @see SetAnimationPoseResetTime
*/
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);
const PoseDurationPair& GetStep(int index) const;
const std::vector<PoseDurationPair> &GetSteps() const { return m_steps; }
void Tick() override;
};
}

View File

@@ -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();