Files
OpenVulkano/openVulkanoCpp/Scene/Node.hpp
2019-10-14 23:02:51 +02:00

213 lines
4.4 KiB
C++

#pragma once
#include <vector>
#include <stdexcept>
#include <glm/glm.hpp>
#include "../Base/Utils.hpp"
#include "../Base/IInitable.hpp"
#include "../Base/ICloseable.hpp"
#include "Drawable.hpp"
namespace openVulkanoCpp
{
namespace Scene
{
class Scene;
enum class UpdateFrequency
{
Always, Sometimes, Never
};
struct Node : virtual IInitable, virtual ICloseable
{
friend Scene;
protected:
static const glm::mat4x4 IDENTITY;
public:
glm::mat4x4 localMat, worldMat;
bool enabled = true;
Node* parent = nullptr;
Scene* scene = nullptr;
std::vector<Node*> children;
std::vector<Drawable*> drawables;
UpdateFrequency matrixUpdateFrequency = UpdateFrequency::Never;
ICloseable* renderNode = nullptr;
public:
Node() = default;
virtual ~Node() = default;
void Init() override
{
if (parent || scene || !children.empty() || !drawables.empty()) throw std::runtime_error("Node already initialized");
localMat = worldMat = IDENTITY;
enabled = true;
parent = nullptr;
children = std::vector<Node*>();
drawables = std::vector<Drawable*>();
}
void Close() override
{
children.clear();
if (renderNode) renderNode->Close();
parent = nullptr;
scene = nullptr;
enabled = false;
if (!children.empty()) Logger::SCENE->warn("Closing Node that has children!");
for (Node* child : children)
{
child->SetParent(nullptr);
}
children.clear();
for(size_t i = drawables.size(); i > 0; i--)
{
RemoveDrawable(drawables[i]);
}
}
void AddChild(Node* node)
{
node->SetParent(this);
children.push_back(node);
node->UpdateWorldMatrix(worldMat);
}
void AddChild(Drawable* drawable)
{
AddDrawable(drawable);
}
void RemoveChild(Node* node)
{
if (node->parent == this)
{
Utils::Remove(children, node);
node->SetParent(nullptr);
}
}
void RemoveChild(Drawable* drawable)
{
RemoveDrawable(drawable);
}
void AddDrawable(Drawable* drawable)
{
if (scene) drawable->SetScene(scene);
else if (drawable->GetScene()) Logger::SCENE->warn("Drawable is already associated with a scene, but the node it was added to is not!");
drawable->AddNode(this);
drawables.push_back(drawable);
}
void RemoveDrawable(Drawable* drawable)
{
drawable->RemoveNode(this);
Utils::Remove(drawables, drawable);
}
void SetMatrix(glm::mat4x4 mat)
{
localMat = mat;
UpdateWorldMatrix(parent ? parent->GetWorldMatrix() : IDENTITY);
}
const glm::mat4x4& GetMatrix() const
{
return localMat;
}
const glm::mat4x4& GetWorldMatrix() const
{
return worldMat;
}
bool IsEnabled() const
{
return enabled;
}
void Enable()
{
enabled = true;
}
void Disable()
{
enabled = false;
}
Node* GetParent() const
{
return parent;
}
Scene* GetScene() const
{
return scene;
}
bool IsRoot() const
{
return scene && parent == this;
}
UpdateFrequency GetUpdateFrequency()
{
return matrixUpdateFrequency;
}
void SetUpdateFrequency(UpdateFrequency frequency)
{
if (!children.empty()) throw std::runtime_error("The update must not be changed for nodes with children.");
this->matrixUpdateFrequency = frequency;
}
protected:
virtual void UpdateWorldMatrix(const glm::mat4x4& parentWorldMat)
{
worldMat = parentWorldMat * localMat;
for (const auto& node : children)
{
node->UpdateWorldMatrix(worldMat);
}
}
private:
void SetParent(Node* parent)
{
if (this->parent && parent) throw std::runtime_error("Node already has a parent! Nodes must not be used multiple times!");
this->parent = parent;
if(parent && parent != this) this->scene = parent->scene;
if (!parent) SetScene(nullptr);
}
void SetScene(Scene* scene)
{
if (this->scene && scene) throw std::runtime_error("Node already has a scene!");
this->scene = scene;
for (const auto& node : children)
{
node->SetScene(scene);
}
if (scene)
{
for (size_t i = 0; i < drawables.size(); i++)
{
Scene* drawableScene = drawables[i]->GetScene();
if(drawableScene)
{
if(drawableScene != scene)
{
Logger::SCENE->warn("Drawable is already associated with a scene! Creating copy.");
drawables[i] = drawables[i]->Copy();
}
}
drawables[i]->SetScene(scene);
}
}
}
};
}
}