Add ecs ComponentListener

- Add ecs ComponentListener which listens to Component addition and
  removal
- Add RefCounter class used to keep track of moves and copies
This commit is contained in:
Thraix
2023-05-29 17:49:37 +02:00
parent 5a615ecc4e
commit 3ec9bcd152
24 changed files with 351 additions and 70 deletions
+7
View File
@@ -180,6 +180,7 @@
<ClCompile Include="src\copium\core\Scene.cpp" />
<ClCompile Include="src\copium\core\Vulkan.cpp" />
<ClCompile Include="src\copium\core\Window.cpp" />
<ClCompile Include="src\copium\ecs\ComponentListener.cpp" />
<ClCompile Include="src\copium\ecs\ComponentPoolBase.cpp" />
<ClCompile Include="src\copium\ecs\ECSManager.cpp" />
<ClCompile Include="src\copium\ecs\Entity.cpp" />
@@ -213,6 +214,7 @@
<ClCompile Include="src\copium\pipeline\DescriptorSet.cpp" />
<ClCompile Include="src\copium\sampler\Font.cpp" />
<ClCompile Include="src\copium\util\BoundingBox.cpp" />
<ClCompile Include="src\copium\util\RefCounter.cpp" />
<ClCompile Include="src\copium\util\RuntimeException.cpp" />
<ClCompile Include="src\copium\util\FileSystem.cpp" />
<ClCompile Include="src\copium\buffer\Framebuffer.cpp" />
@@ -249,6 +251,7 @@
<ClInclude Include="src\copium\core\Scene.h" />
<ClInclude Include="src\copium\core\Vulkan.h" />
<ClInclude Include="src\copium\core\Window.h" />
<ClInclude Include="src\copium\ecs\ComponentListener.h" />
<ClInclude Include="src\copium\ecs\ComponentPool.h" />
<ClInclude Include="src\copium\ecs\ComponentPoolBase.h" />
<ClInclude Include="src\copium\ecs\Config.h" />
@@ -280,6 +283,9 @@
<ClInclude Include="src\copium\example\CameraUpdateSystem.h" />
<ClInclude Include="src\copium\example\Components.h" />
<ClInclude Include="src\copium\example\FrameCountSystem.h" />
<ClInclude Include="src\copium\example\HealthChangeSystem.h" />
<ClInclude Include="src\copium\example\HealthComponentListener.h" />
<ClInclude Include="src\copium\example\HealthDisplaySystem.h" />
<ClInclude Include="src\copium\example\PhysicsSystem.h" />
<ClInclude Include="src\copium\example\PlayerControllerSystem.h" />
<ClInclude Include="src\copium\example\RenderSystem.h" />
@@ -302,6 +308,7 @@
<ClInclude Include="src\copium\pipeline\DescriptorSet.h" />
<ClInclude Include="src\copium\pipeline\DescriptorPool.h" />
<ClInclude Include="src\copium\util\Enum.h" />
<ClInclude Include="src\copium\util\RefCounter.h" />
<ClInclude Include="src\copium\util\RuntimeException.h" />
<ClInclude Include="src\copium\util\FileSystem.h" />
<ClInclude Include="src\copium\buffer\Framebuffer.h" />
+21
View File
@@ -219,6 +219,12 @@
<ClCompile Include="src\copium\event\Input.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\copium\util\RefCounter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\copium\ecs\ComponentListener.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\copium\sampler\DepthAttachment.h">
@@ -485,5 +491,20 @@
<ClInclude Include="src\copium\example\CameraFollowPlayerSystem.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\copium\example\HealthDisplaySystem.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\copium\example\HealthChangeSystem.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\copium\util\RefCounter.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\copium\ecs\ComponentListener.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\copium\example\HealthComponentListener.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>
+2 -36
View File
@@ -5,51 +5,17 @@
namespace Copium
{
AssetRef::AssetRef(AssetHandle handle)
: handle{handle}, refCounter{new int{1}}
: handle{handle}
{}
AssetRef::~AssetRef()
{
if (refCounter == nullptr)
return;
(*refCounter)--;
if (*refCounter == 0)
if (refCounter.LastRef())
{
AssetManager::UnloadAsset(handle);
delete refCounter;
}
}
AssetRef::AssetRef(const AssetRef& other)
: handle{other.handle}, refCounter{other.refCounter}
{
(*refCounter)++;
}
AssetRef::AssetRef(AssetRef&& other)
: handle{other.handle}, refCounter{other.refCounter}
{
other.refCounter = nullptr;
}
AssetRef& AssetRef::operator=(const AssetRef& rhs)
{
handle = rhs.handle;
refCounter = rhs.refCounter;
(*refCounter)++;
return *this;
}
AssetRef& AssetRef::operator=(AssetRef&& rhs)
{
handle = rhs.handle;
refCounter = rhs.refCounter;
rhs.refCounter = nullptr;
return *this;
}
AssetRef::operator AssetHandle() const
{
return handle;
+2 -7
View File
@@ -1,6 +1,7 @@
#pragma once
#include "copium/asset/AssetMeta.h"
#include "copium/util/RefCounter.h"
namespace Copium
{
@@ -8,18 +9,12 @@ namespace Copium
{
private:
AssetHandle handle;
int* refCounter;
RefCounter refCounter;
public:
AssetRef(AssetHandle handle);
~AssetRef();
AssetRef(const AssetRef& other);
AssetRef(AssetRef&& other);
AssetRef& operator=(const AssetRef& rhs);
AssetRef& operator=(AssetRef&& rhs);
operator AssetHandle() const;
};
+1 -8
View File
@@ -77,7 +77,7 @@ namespace Copium
Vulkan::GetSwapChain().SubmitToGraphicsQueue(*commandBuffer);
Vulkan::GetSwapChain().EndPresent();
return !glfwWindowShouldClose(Vulkan::GetWindow().GetWindow());
return !Vulkan::GetWindow().ShouldClose();
}
EventResult Application::OnEvent(const Event& event)
@@ -100,13 +100,6 @@ namespace Copium
return EventResult::Focus;
}
case EventType::KeyPress:
{
const KeyPressEvent& keyPressEvent = static_cast<const KeyPressEvent&>(event);
CP_INFO("%d", keyPressEvent.GetButton());
return EventResult::Handled;
}
case EventType::MouseScroll:
{
const MouseScrollEvent& mouseScrollEvent = static_cast<const MouseScrollEvent&>(event);
+9 -2
View File
@@ -7,14 +7,17 @@
#include "copium/ecs/Entity.h"
#include "copium/ecs/System.h"
#include "copium/event/MouseMoveEvent.h"
#include "copium/example/CameraFollowPlayerSystem.h"
#include "copium/example/CameraUpdateSystem.h"
#include "copium/example/Components.h"
#include "copium/example/FrameCountSystem.h"
#include "copium/example/HealthChangeSystem.h"
#include "copium/example/HealthComponentListener.h"
#include "copium/example/HealthDisplaySystem.h"
#include "copium/example/MouseFollowSystem.h"
#include "copium/example/RenderSystem.h"
#include "copium/example/PhysicsSystem.h"
#include "copium/example/PlayerControllerSystem.h"
#include "copium/example/CameraFollowPlayerSystem.h"
#include "copium/example/RenderSystem.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
@@ -30,11 +33,14 @@ namespace Copium
ecs->AddSystem<PlayerControllerSystem>();
ecs->AddSystem<PhysicsSystem>();
ecs->AddSystem<HealthChangeSystem>();
ecs->AddSystem<HealthDisplaySystem>();
ecs->AddSystem<CameraFollowPlayerSystem>();
ecs->AddSystem<CameraUpdateSystem>(&viewMatrix, &projectionMatrix, &invPvMatrix);
ecs->AddSystem<MouseFollowSystem>(&invPvMatrix);
ecs->AddSystem<FrameCountSystem>();
ecs->AddSystem<RenderSystem>(renderer.get(), descriptorSetRenderer.get(), &commandBuffer, &viewMatrix, &projectionMatrix); // better way to store the RenderSystem data?
ecs->SetComponentListener<HealthComponentListener>();
// TODO: Load from scene file
for (int y = 0; y < 10; y++)
@@ -72,6 +78,7 @@ namespace Copium
Entity entityPlayer = Entity::Create(ecs.get());
entityPlayer.AddComponent<PlayerC>(entityCamera);
entityPlayer.AddComponent<HealthC>(10, 10);
entityPlayer.AddComponent<PhysicsC>(0.1f, glm::vec2{0.0f, 0.0f}, glm::vec2{0.0f, 0.0f});
entityPlayer.AddComponent<TransformC>(glm::vec2{0.0f}, glm::vec2{1.0f});
entityPlayer.AddComponent<TextureC>(AssetRef{AssetManager::LoadAsset("fox2.meta")}, glm::vec2{0.0f, 0.0f}, glm::vec2{1.0f, 1.0f});
@@ -4,6 +4,8 @@
#include "copium/core/Vulkan.h"
#include "copium/sampler/Image.h"
#include <GLFW/glfw3.h>
namespace Copium
{
SwapChainSupportDetails::SwapChainSupportDetails(VkSurfaceKHR surface, VkPhysicalDevice physicalDevice)
+18
View File
@@ -12,6 +12,8 @@
#include "copium/event/WindowResizeEvent.h"
#include "copium/event/WindowFocusEvent.h"
#include <GLFW/glfw3.h>
namespace Copium
{
Window::Window(const std::string& windowName, int width, int height, WindowMode mode)
@@ -27,6 +29,22 @@ namespace Copium
glfwDestroyWindow(window);
}
bool Window::ShouldClose() const
{
return glfwWindowShouldClose(window);
}
int Window::GetWidth() const
{
return width;
}
int Window::GetHeight() const
{
return height;
}
VkSurfaceKHR Window::GetSurface() const
{
return surface;
+12 -2
View File
@@ -3,11 +3,17 @@
#include "copium/util/Common.h"
#include "copium/util/Enum.h"
#include <GLFW/glfw3.h>
#include <vulkan/vulkan.hpp>
#define CP_WINDOW_MODE_ENUMS \
Fullscreen, \
BorderlessWindowed, \
Windowed
#define CP_WINDOW_MODE_ENUMS Fullscreen, BorderlessWindowed, Windowed
CP_ENUM_CREATOR(Copium, WindowMode, CP_WINDOW_MODE_ENUMS);
struct GLFWwindow;
namespace Copium
{
class Window final
@@ -24,6 +30,10 @@ namespace Copium
Window(const std::string& windowName, int width, int height, WindowMode mode);
~Window();
bool ShouldClose() const;
int GetWidth() const;
int GetHeight() const;
VkSurfaceKHR GetSurface() const;
GLFWwindow* GetWindow();
@@ -0,0 +1,19 @@
#pragma once
#include "copium/ecs/ECSManager.h"
namespace Copium
{
template <typename Component>
class ComponentListener
{
public:
using component_type = Component;
friend class ECSManager;
protected:
ECSManager* manager;
public:
virtual void Added(EntityId entityId, Component& component) {}
virtual void Removed(EntityId entityId, Component& component) {}
};
}
+31 -9
View File
@@ -3,6 +3,7 @@
#include "copium/ecs/Config.h"
#include "copium/ecs/EntitySet.h"
#include "copium/ecs/ComponentPoolBase.h"
#include "copium/ecs/ComponentListener.h"
#include <vector>
@@ -14,8 +15,18 @@ namespace Copium
using Iterator = typename std::vector<Component>::iterator;
private:
std::vector<Component> components;
ComponentListener<Component>* listener = nullptr;
public:
ComponentPool()
{}
~ComponentPool() override
{
if(listener)
delete listener;
}
ComponentPool(EntityId entity, const Component& component)
{
Emplace(entity, component);
@@ -25,21 +36,27 @@ namespace Copium
{
components.push_back(component);
entities.Emplace(entity);
if(listener)
listener->Added(entity, components.back());
return components.back();
}
void Pop()
{
components.pop_back();
entities.Pop();
}
bool Erase(EntityId entity)
bool Erase(EntityId entity) override
{
size_t index = entities.Find(entity);
if (!entities.Erase(entity))
return false;
components.erase(components.begin() + index);
if (listener)
{
auto it = components.begin() + index;
Component component = *it;
components.erase(it);
listener->Removed(entity, component);
}
else
{
components.erase(components.begin() + index);
}
return true;
}
@@ -61,13 +78,18 @@ namespace Copium
return nullptr;
}
void SetComponentListener(ComponentListener<Component>* listener)
{
ComponentPool::listener = listener;
}
Component& operator[](size_t index)
{
CP_ASSERT(index < components.size(), "Index Out of Bound Exception");
return components[index];
}
size_t Size()
size_t Size() override
{
return components.size();
}
@@ -15,7 +15,6 @@ namespace Copium
virtual ~ComponentPoolBase() = default;
virtual size_t Size() = 0;
virtual void Pop() = 0;
virtual bool Erase(EntityId entity) = 0;
std::vector<EntityId>& GetEntities();
const std::vector<EntityId>& GetEntities() const;
+19
View File
@@ -48,6 +48,25 @@ namespace Copium
bool ValidEntity(EntityId entity);
void Each(std::function<void(EntityId)> function);
template <typename Listener, typename... Args>
void SetComponentListener(const Args&... args)
{
using Component = typename Listener::component_type;
auto pool = GetComponentPool<Component>();
Listener* listener = new Listener{args...};
listener->manager = this;
if (pool)
{
pool->SetComponentListener(listener);
}
else
{
ComponentPool<Component>* pool{new ComponentPool<Component>{}};
componentPool.emplace(GetComponentId<Component>(), pool);
pool->SetComponentListener(listener);
}
}
template <typename... Components>
std::tuple<Components&...> AddComponents(EntityId entity, Components&&... components)
{
+9
View File
@@ -1,6 +1,9 @@
#include "copium/event/Input.h"
#include "copium/util/Common.h"
#include "copium/core/Vulkan.h"
#include <glm/gtc/matrix_transform.hpp>
namespace Copium
{
@@ -63,6 +66,11 @@ namespace Copium
return mousePos;
}
glm::vec2 Input::GetMouseWindowPos()
{
return glm::vec2{(mousePos.x + 1.0f) * 0.5f * Vulkan::GetWindow().GetWidth(), (1.0f - mousePos.y) * 0.5f * Vulkan::GetWindow().GetHeight()};
}
void Input::OnKey(int keyCode, bool pressed)
{
CP_ASSERT(keyCode >= 0 && keyCode < MAX_NUM_KEYS, "KeyCode is out of range");
@@ -85,5 +93,6 @@ namespace Copium
void Input::Update()
{
memset(keyEventList, false, sizeof(keyEventList));
memset(mouseEventList, false, sizeof(mouseEventList));
}
}
+1
View File
@@ -35,6 +35,7 @@ namespace Copium
static bool IsMouseDown(int button);
static bool IsMouseUp(int button);
static glm::vec2 GetMouseWindowPos();
static glm::vec2 GetMousePos();
static void OnKey(int keyCode, bool pressed);
@@ -57,4 +57,13 @@ namespace Copium
{
Entity camera;
};
struct HealthC
{
int current;
int max;
Entity background;
Entity foreground;
};
}
@@ -0,0 +1,21 @@
#pragma once
#include "copium/ecs/System.h"
#include "copium/example/Components.h"
#include "copium/event/Input.h"
namespace Copium
{
class HealthChangeSystem : public System<HealthC>
{
void RunEntity(Entity entity, HealthC& health) override
{
if (Input::IsKeyPressed(CP_KEY_K))
health.current++;
if (Input::IsKeyPressed(CP_KEY_J))
health.current--;
health.current = std::clamp(health.current, 0, health.max);
}
};
}
@@ -0,0 +1,35 @@
#pragma once
#include "copium/ecs/ComponentListener.h"
#include "copium/example/Components.h"
#include "copium/util/Common.h"
namespace Copium
{
class HealthComponentListener : public ComponentListener<HealthC>
{
void Added(EntityId entityId, HealthC& health) override
{
CP_ASSERT(!health.background, "Health already has background entity assigned");
CP_ASSERT(!health.foreground, "Health already has foreground entity assigned");
health.background = Entity::Create(manager);
health.background.AddComponent<TransformC>(glm::vec2{0.0f, 0.0f}, glm::vec2{1.0f, 0.2f});
health.background.AddComponent<ColorC>(glm::vec3{0.9f, 0.2f, 0.2f});
health.foreground = Entity::Create(manager);
health.foreground.AddComponent<TransformC>(glm::vec2{0.0f, 0.0f}, glm::vec2{std::clamp(health.current, 0, health.max) / (float)health.max, 0.2f});
health.foreground.AddComponent<ColorC>(glm::vec3{0.2f, 0.9f, 0.2f});
}
void Removed(EntityId entityId, HealthC& health) override
{
CP_ASSERT(health.background, "Health already removed background entity");
CP_ASSERT(health.foreground, "Health already removed foreground entity");
health.background.Destroy();
health.foreground.Destroy();
}
};
}
@@ -0,0 +1,22 @@
#pragma once
#include "copium/ecs/System.h"
#include "copium/example/Components.h"
namespace Copium
{
class HealthDisplaySystem : public System<HealthC, TransformC>
{
void RunEntity(Entity entity, HealthC& health, TransformC& transform) override
{
TransformC& foregroundTransform = health.foreground.GetComponent<TransformC>();
foregroundTransform.position = transform.position + glm::vec2{0.0f, transform.size.y * 1.13};
foregroundTransform.size = glm::vec2{std::clamp(health.current, 0, health.max) / (float)health.max, 0.2f};
TransformC& backgroundTransform = health.background.GetComponent<TransformC>();
backgroundTransform.position = transform.position + glm::vec2{0.0f, transform.size.y * 1.13};
}
};
}
@@ -18,8 +18,19 @@ namespace Copium
if (Input::IsKeyDown(CP_KEY_A)) force.x -= 1.0f;
if (Input::IsKeyDown(CP_KEY_D)) force.x += 1.0f;
glm::normalize(force);
physics.force += force * magnitude;
if (force.x != 0 || force.y != 0)
{
force = glm::normalize(force);
physics.force += force * magnitude;
}
if (Input::IsKeyPressed(CP_KEY_H))
{
if (entity.HasComponent<HealthC>())
entity.RemoveComponent<HealthC>();
else
entity.AddComponent<HealthC>(8, 10);
}
}
};
}
+2 -3
View File
@@ -16,10 +16,9 @@ int main(int argc, char** argv)
Copium::Application application;
while (application.Update())
{
glfwPollEvents();
Copium::EventDispatcher::Dispatch();
Copium::Input::Update();
glfwPollEvents();
Copium::EventDispatcher::Dispatch();
}
}
Copium::Vulkan::Destroy();
@@ -0,0 +1,72 @@
#include "copium/util/RefCounter.h"
#include "copium/util/Common.h"
namespace Copium
{
RefCounter::RefCounter()
: refCounter{new int{1}}
{}
RefCounter::~RefCounter()
{
if (Valid())
{
(*refCounter)--;
if (*refCounter == 0)
{
delete refCounter;
refCounter = nullptr;
}
}
}
RefCounter::RefCounter(RefCounter&& rhs)
: refCounter(rhs.refCounter)
{
CP_ASSERT(Valid(), "RefCounter : Moving a deleted RefCounter");
rhs.refCounter = nullptr;
}
RefCounter::RefCounter(const RefCounter& rhs)
: refCounter{rhs.refCounter}
{
CP_ASSERT(Valid(), "RefCounter : Copying a deleted RefCounter");
(*refCounter)++;
}
RefCounter& RefCounter::operator=(RefCounter&& rhs)
{
CP_ASSERT(Valid(), "operator= : Moving a deleted RefCounter");
refCounter = rhs.refCounter;
rhs.refCounter = nullptr;
return *this;
}
RefCounter& RefCounter::operator=(const RefCounter& rhs)
{
CP_ASSERT(Valid(), "operator= : Copying a deleted RefCounter");
refCounter = rhs.refCounter;
(*refCounter)++;
return *this;
}
bool RefCounter::Valid() const
{
return refCounter != nullptr;
}
bool RefCounter::LastRef() const
{
return Valid() && *refCounter == 1;
}
int RefCounter::Counter() const
{
CP_ASSERT(Valid(), "Counter : referencing a deleted RefCounter");
return *refCounter;
}
}
+24
View File
@@ -0,0 +1,24 @@
#pragma once
namespace Copium
{
class RefCounter final
{
private:
int* refCounter;
public:
RefCounter();
~RefCounter();
RefCounter(RefCounter&& rhs);
RefCounter(const RefCounter& rhs);
RefCounter& operator=(RefCounter&& rhs);
RefCounter& operator=(const RefCounter& rhs);
bool Valid() const;
bool LastRef() const;
int Counter() const;
};
}