Rework ECS framework

- Add queue systems when adding/removing components and systems
- Add GlobalData to ECSManager, used to store data that is not specific
  to entities
- Add View class, used to for loop all entities that contains the given
  Components
- Rework how signaling works
This commit is contained in:
Thraix
2026-02-07 18:25:13 +01:00
parent e5866b2dcb
commit 9d5a5314a7
23 changed files with 729 additions and 245 deletions
+1 -1
View File
@@ -430,7 +430,7 @@ namespace Copium
int width, height;
glfwGetFramebufferSize(window, &width, &height);
VkExtent2D extent{width, height};
VkExtent2D extent{static_cast<uint32_t>(width), static_cast<uint32_t>(height)};
extent.width = std::clamp(extent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
extent.height = std::clamp(extent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height);
return extent;
+107 -19
View File
@@ -19,6 +19,16 @@ namespace Copium
std::vector<Component> components;
ComponentListener<Component>* listener = nullptr;
enum class QueueOperation
{
Add,
Remove
};
std::vector<QueueOperation> queueOperationOrder;
std::vector<std::pair<EntityId, Component>> addQueue;
std::vector<EntityId> removeQueue;
public:
ComponentPool()
{
@@ -35,34 +45,61 @@ namespace Copium
Emplace(entity, component);
}
Component& Emplace(EntityId entity, const Component& component)
void Emplace(EntityId entity, const Component& component)
{
components.push_back(component);
entities.Emplace(entity);
if (listener)
listener->Added(entity, components.back());
return components.back();
addQueue.emplace_back(entity, component);
queueOperationOrder.emplace_back(QueueOperation::Add);
}
bool Erase(EntityId entity) override
{
size_t index = entities.Find(entity);
if (!entities.Erase(entity))
if (entities.Find(entity) == entities.Size())
return false;
if (listener)
{
auto it = components.begin() + index;
Component component = *it;
components.erase(it);
listener->Removed(entity, component);
}
else
{
components.erase(components.begin() + index);
}
removeQueue.emplace_back(entity);
queueOperationOrder.emplace_back(QueueOperation::Remove);
return true;
}
void CommitUpdates() override
{
if (queueOperationOrder.empty())
return;
CP_ASSERT(queueOperationOrder.size() == addQueue.size() + removeQueue.size(),
"queueOperationOrder size=%zu doesn't match the sum of the addQueue size=%zu and removeQueue size=%zu, "
"which is %zu",
queueOperationOrder.size(),
addQueue.size(),
removeQueue.size(),
addQueue.size() + removeQueue.size());
int addQueueIndex = 0;
int removeQueueIndex = 0;
for (QueueOperation queueOperation : queueOperationOrder)
{
switch (queueOperation)
{
case QueueOperation::Add:
{
CommitAddComponent(addQueueIndex);
addQueueIndex++;
break;
}
case QueueOperation::Remove:
{
CommitRemoveComponent(removeQueueIndex);
removeQueueIndex++;
break;
}
}
}
removeQueue.clear();
addQueue.clear();
queueOperationOrder.clear();
}
Component& At(size_t index)
{
return operator[](index);
@@ -111,5 +148,56 @@ namespace Copium
{
return components.end();
}
void CommitAddComponent(int queueIndex)
{
CP_ASSERT(
queueIndex < addQueue.size(), "queueIndex=%d is greater than the addQueueSize=%d", queueIndex, addQueue.size());
const auto& [entity, component] = addQueue[queueIndex];
// TODO: Debugging errors caused by this assert might be a bit difficult, since there wont be any stacktrace for
// where the component was added. Might want to validate this in AddComponent somehow (like looping through
// the queued changes and work out if the component already exists)
CP_ASSERT(Find(entity) == Size(),
"Component already exists in entity (entity=%u, Component=%s)",
entity,
typeid(Component).name());
components.push_back(component);
entities.Emplace(entity);
if (listener)
listener->Added(entity, components.back());
}
void CommitRemoveComponent(int queueIndex)
{
CP_ASSERT(queueIndex < removeQueue.size(),
"queueIndex=%d is greater than the removeQueueSize=%d",
queueIndex,
removeQueue.size());
const auto& entity = removeQueue[queueIndex];
size_t index = entities.Find(entity);
if (!entities.Erase(entity))
{
// TODO: Debugging warnings caused by this might be a bit difficult, since there wont be any stacktrace for
// where the component was added. Might want to validate this in AddComponent somehow (like looping
// through the queued changes and work out if the component already exists)
CP_WARN("Entity did not contain component (entity=%u, Component=%s)", entity, typeid(Component).name());
return;
}
if (listener)
{
auto it = components.begin() + index;
Component component = *it;
components.erase(it);
listener->Removed(entity, component);
}
else
{
components.erase(components.begin() + index);
}
}
};
}
@@ -17,6 +17,7 @@ namespace Copium
virtual size_t Size() = 0;
virtual bool Erase(EntityId entity) = 0;
virtual void CommitUpdates() = 0;
std::vector<EntityId>& GetEntities();
const std::vector<EntityId>& GetEntities() const;
};
+37 -10
View File
@@ -4,30 +4,37 @@
namespace Copium
{
std::vector<EntityId> ECSManager::emptyEntities = {};
ECSManager::ECSManager()
: systemPool{std::make_unique<SystemPool>(this)}
{
}
ECSManager::~ECSManager()
{
for (auto&& components : componentPool)
for (auto&& components : componentPools)
{
delete components.second;
}
componentPool.clear();
componentPools.clear();
}
void ECSManager::UpdateSystems()
void ECSManager::CommitEntityUpdates()
{
systemPool->Update();
for (auto& componentPool : componentPools)
{
componentPool.second->CommitUpdates();
}
}
void ECSManager::UpdateSystems(const Signal& signal)
void ECSManager::UpdateSystems(const Uuid& systemPoolId)
{
// TODO: Maybe we want a different pool for Signal based Systems for performance reasons?
// Maybe even a pool for each type of Signal?
systemPool->Update(signal);
auto it = systemPools.find(systemPoolId);
CP_ASSERT(it != systemPools.end(), "SystemPool doesn't exist with Uuid=%s", systemPoolId.ToString().c_str());
it->second->CommitSignals();
CommitEntityUpdates();
it->second->CommitUpdates();
it->second->Update();
}
size_t ECSManager::GetEntityCount() const
@@ -37,6 +44,13 @@ namespace Copium
EntityId ECSManager::CreateEntity()
{
if (!destroyedEntityIds.empty())
{
EntityId newId = *destroyedEntityIds.begin();
destroyedEntityIds.erase(destroyedEntityIds.begin());
return newId;
}
CP_ASSERT(currentEntityId != MAX_NUM_ENTITIES, "No more entities available");
entities.emplace(currentEntityId);
currentEntityId++;
@@ -47,8 +61,21 @@ namespace Copium
{
auto it = entities.find(entity);
CP_ASSERT(it != entities.end(), "Entity does not exist in ECSManager (entity=%u)", entity);
if (entity == currentEntityId - 1)
{
currentEntityId--;
while (!destroyedEntityIds.empty() && *destroyedEntityIds.rbegin() == currentEntityId - 1)
{
destroyedEntityIds.erase(std::prev(destroyedEntityIds.end()));
}
}
else
{
destroyedEntityIds.emplace(entity);
}
entities.erase(it);
for (auto&& pool : componentPool)
for (auto&& pool : componentPools)
{
pool.second->Erase(entity);
}
+92 -35
View File
@@ -2,6 +2,7 @@
#include <functional>
#include <map>
#include <set>
#include <typeindex>
#include <unordered_set>
@@ -10,36 +11,68 @@
#include "copium/ecs/Signal.h"
#include "copium/ecs/SystemPool.h"
#include "copium/util/Common.h"
#include "copium/util/GenericType.h"
#include "copium/util/Uuid.h"
namespace Copium
{
class ECSManager final
{
CP_DELETE_COPY_AND_MOVE_CTOR(ECSManager);
private:
std::unordered_set<EntityId> entities;
std::map<std::type_index, ComponentPoolBase*> componentPool;
std::map<std::type_index, ComponentPoolBase*> componentPools;
std::unique_ptr<SystemPool> systemPool;
int currentEntityId = 1;
std::map<Uuid, std::unique_ptr<SystemPool>> systemPools;
EntityId currentEntityId = 1;
std::set<EntityId> destroyedEntityIds;
std::map<std::type_index, GenericType> globalDatas;
public:
static std::vector<EntityId> emptyEntities;
ECSManager();
~ECSManager();
template <typename SystemClass, typename... Args>
SystemOrderer AddSystem(const Args&... args)
SystemOrderer& AddSystem(const Uuid& systemPoolId)
{
return systemPool->AddSystem(typeid(SystemClass), new SystemClass{args...});
auto it = systemPools.find(systemPoolId);
if (it == systemPools.end())
it = systemPools.emplace(systemPoolId, std::make_unique<SystemPool>(this)).first;
return it->second->AddSystem(typeid(SystemClass), new SystemClass{});
}
template <typename SystemClass>
void RemoveSystem()
void RemoveSystem(const Uuid& systemPoolId)
{
systemPool->RemoveSystem(typeid(SystemClass));
auto it = systemPools.find(systemPoolId);
CP_ASSERT(it != systemPools.end(), "SystemPool doesn't exist with Uuid=%s", systemPoolId.ToString().c_str());
it->second->RemoveSystem(typeid(SystemClass));
if (it->second->IsEmpty())
systemPools.erase(it);
}
void UpdateSystems();
void UpdateSystems(const Signal& signal);
void CommitEntityUpdates();
void UpdateSystems(const Uuid& systemPoolId);
template <typename S, typename... Args>
void SendSignal(const Args&... args)
{
Signal::ValidateSignal<S>();
for (auto& systemPool : systemPools)
{
S* newSignal = new S{args...};
newSignal->uuid = S::UUID;
systemPool.second->SendSignal(newSignal);
}
}
EntityId CreateEntity();
void DestroyEntity(EntityId entity);
@@ -61,41 +94,36 @@ namespace Copium
else
{
ComponentPool<Component>* pool{new ComponentPool<Component>{}};
componentPool.emplace(GetComponentId<Component>(), pool);
componentPools.emplace(GetComponentId<Component>(), pool);
pool->SetComponentListener(listener);
}
}
template <typename... Components>
std::tuple<Components&...> AddComponents(EntityId entity, Components&&... components)
void AddComponents(EntityId entity, Components&&... components)
{
return std::forward_as_tuple(AddComponent(entity, Components(components))...);
(AddComponent(entity, components), ...);
}
template <typename Component, typename... Args>
Component& AddComponent(EntityId entity, Args&&... args)
void AddComponent(EntityId entity, Args&&... args)
{
return AddComponent(entity, Component{args...});
AddComponent(entity, Component{args...});
}
template <typename Component>
Component& AddComponent(EntityId entity, const Component& component)
void AddComponent(EntityId entity, const Component& component)
{
auto pool = GetComponentPool<Component>();
if (pool)
{
CP_ASSERT(!HasComponent<Component>(entity),
"Component already exists in entity (entity=%u, Component=%s)",
entity,
typeid(Component).name());
return pool->Emplace(entity, component);
pool->Emplace(entity, component);
}
else
{
ComponentPool<Component>* pool{new ComponentPool{entity, component}};
auto ret = componentPool.emplace(GetComponentId<Component>(), pool);
return pool->At(0);
auto ret = componentPools.emplace(GetComponentId<Component>(), pool);
}
}
@@ -103,10 +131,7 @@ namespace Copium
void RemoveComponent(EntityId entity)
{
auto pool = GetComponentPoolAssure<Component>();
CP_ASSERT(pool->Erase(entity),
"Entity did not contain component (entity=%u, Component=%s)",
entity,
typeid(Component).name());
pool->Erase(entity);
}
template <typename... Components>
@@ -229,22 +254,54 @@ namespace Copium
return std::type_index(typeid(T));
}
private:
template <typename Component>
ComponentPool<Component>* GetComponentPool()
template <typename T, typename... Args>
void AddGlobalData(const Args&... args)
{
auto it = componentPool.find(GetComponentId<Component>());
return it == componentPool.end() ? nullptr : static_cast<ComponentPool<Component>*>(it->second);
auto it = globalDatas.find(typeid(T));
CP_ASSERT(!HasGlobalData<T>(), "Global with typeid=%s already exists. Do nothing", typeid(T).name());
globalDatas.emplace(typeid(T), GenericType::Create<T>(args...));
}
template <typename T>
T& GetGlobalData()
{
auto it = globalDatas.find(typeid(T));
CP_ASSERT(it != globalDatas.end(), "Global with typeid=%s doesn't exist");
return it->second.Get<T>();
}
template <typename T>
bool HasGlobalData()
{
return globalDatas.find(typeid(T)) != globalDatas.end();
}
template <typename T>
void RemoveGlobalData()
{
auto it = globalDatas.find(typeid(T));
CP_ASSERT("Global with typeid=%s doesn't exist. Do nothing", typeid(T).name());
globalDatas.erase(it);
}
template <typename Component>
ComponentPool<Component>* GetComponentPoolAssure()
ComponentPool<std::remove_const_t<Component>>* GetComponentPool()
{
auto it = componentPool.find(GetComponentId<Component>());
CP_ASSERT(it != componentPool.end(),
auto it = componentPools.find(GetComponentId<Component>());
return it == componentPools.end() ? nullptr
: static_cast<ComponentPool<std::remove_const_t<Component>>*>(it->second);
}
template <typename Component>
ComponentPool<std::remove_const_t<Component>>* GetComponentPoolAssure()
{
auto it = componentPools.find(GetComponentId<Component>());
CP_ASSERT(it != componentPools.end(),
"Component has not been added to an entity (Component=%s)",
typeid(Component).name());
return static_cast<ComponentPool<Component>*>(it->second);
return static_cast<ComponentPool<std::remove_const_t<Component>>*>(it->second);
}
};
}
+7 -2
View File
@@ -40,7 +40,7 @@ namespace Copium
return id != entity.id;
}
Entity::operator bool() const
bool Entity::IsValid() const
{
if (id == INVALID_ENTITY)
return false;
@@ -49,6 +49,11 @@ namespace Copium
return false;
}
Entity::operator bool() const
{
return IsValid();
}
void Entity::Invalidate()
{
id = INVALID_ENTITY;
@@ -79,4 +84,4 @@ namespace Copium
{
return {manager, manager->CreateEntity()};
}
}
}
+5 -4
View File
@@ -24,6 +24,7 @@ namespace Copium
bool operator!=(const Entity& entity);
operator bool() const;
bool IsValid() const;
void Invalidate();
void Destroy();
void SetId(EntityId entityId);
@@ -33,15 +34,15 @@ namespace Copium
static Entity Create(ECSManager* manager);
template <typename Component, typename... Args>
inline Component& AddComponent(Args... args)
inline void AddComponent(Args... args)
{
return manager->AddComponent<Component>(id, args...);
manager->AddComponent<Component>(id, args...);
}
template <typename... Components>
std::tuple<Components&...> AddComponents(Components&&... components)
void AddComponents(Components&&... components)
{
return manager->AddComponents(id, components...);
manager->AddComponents(id, components...);
}
template <typename Component>
-10
View File
@@ -1,10 +0,0 @@
#include "copium/ecs/Signal.h"
namespace Copium
{
int Signal::GetAllocatedId()
{
allocatedIds++;
return allocatedIds;
}
}
+28 -34
View File
@@ -1,48 +1,42 @@
#pragma once
#define CP_SIGNAL_DECLERATION_DEFINITION() \
static int GetIdStatic() \
{ \
static int id = GetAllocatedId(); \
return id; \
} \
\
int GetId() const \
{ \
return GetIdStatic(); \
}
#include "copium/util/Uuid.h"
#define CP_SIGNAL_DECLERATION(SignalClass) \
static int GetIdStatic(); \
int GetId() const override
#define CP_SIGNAL_DEFINITION(SignalClass) \
int SignalClass::GetIdStatic() \
{ \
static int id = GetAllocatedId(); \
return id; \
} \
\
int SignalClass::GetId() const \
{ \
return GetIdStatic(); \
}
#define CP_REGISTER_SIGNAL static inline Copium::Uuid UUID = Copium::Uuid()
namespace Copium
{
class Signal
{
private:
static inline int allocatedIds = 0;
public:
Signal() = default;
virtual ~Signal() = default;
virtual int GetId() const = 0;
const Uuid& GetUuid() const
{
return uuid;
}
protected:
static int GetAllocatedId();
template <typename T>
static void ValidateSignal()
{
static_assert(std::is_base_of_v<Signal, T>, "ValidateSignal : Given T is does not have Signal as base class");
static_assert(
has_static_uuid<T>(),
"ValidateSignal : Given T has not defined a UUID. Did you add CP_REGISTER_SIGNAL to your Signal class?");
}
private:
friend class ECSManager;
Uuid uuid;
template <typename, typename = void>
struct has_static_uuid : std::false_type
{
};
template <typename T>
struct has_static_uuid<T, std::void_t<decltype(T::UUID)>> : std::true_type
{
};
};
}
+36 -26
View File
@@ -3,44 +3,54 @@
#include <set>
#include "copium/ecs/ECSManager.h"
#include "copium/ecs/Entity.h"
#include "copium/ecs/SystemBase.h"
#include "copium/ecs/Signal.h"
namespace Copium
{
template <typename... Components>
class System : public SystemBase
class System
{
public:
void Run() override
virtual ~System() = default;
virtual void Run() = 0;
virtual void HandleSignal(const Signal& signal)
{
manager->Each<Components...>([&](EntityId entityId, Components&... components)
{ RunEntity(Entity{manager, entityId}, components...); });
}
void Run(const Signal& signal) override
const bool IsSignalSubscribed(const Uuid& uuid) const
{
manager->Each<Components...>([&](EntityId entityId, Components&... components)
{ RunEntity(signal, Entity{manager, entityId}, components...); });
return subscribedSignals.count(uuid) != 0;
}
virtual void RunEntity(Entity entity, Components&... components) {};
virtual void RunEntity(const Signal& signal, Entity entity, Components&... components) {};
// TODO: Not sure if this is the way entities should be validated
std::set<EntityId> loggedEntities;
bool ValidateEntity(Entity entity)
template <typename S>
void SubscribeToSignal()
{
if (entity && entity.HasComponents<Components...>())
{
loggedEntities.erase(entity);
return true;
}
if (loggedEntities.find(entity) == loggedEntities.end())
CP_WARN("Invalid Entity");
loggedEntities.emplace(entity);
return false;
Signal::ValidateSignal<S>();
subscribedSignals.emplace(S::UUID);
}
template <typename S>
void UnsubscribeToSignal()
{
Signal::ValidateSignal<S>();
subscribedSignals.erase(S::UUID);
}
void SetECSManager(ECSManager* manager)
{
this->manager = manager;
}
template <typename S, typename... Args>
void SendSignal(const Args&... args)
{
manager->SendSignal<S>(args...);
}
protected:
ECSManager* manager;
private:
std::set<Uuid> subscribedSignals{};
};
}
-22
View File
@@ -1,22 +0,0 @@
#pragma once
#include "copium/ecs/Signal.h"
namespace Copium
{
class ECSManager;
class SystemBase
{
friend class SystemPool;
protected:
ECSManager* manager;
public:
virtual ~SystemBase() = default;
virtual void Run() = 0;
virtual void Run(const Signal& signal) = 0;
};
}
+24 -3
View File
@@ -10,13 +10,34 @@ namespace Copium
{
}
void SystemOrderer::Before(const std::type_index& otherSystemId)
void SystemOrderer::CommitOrdering()
{
for (const auto& [orderOperation, systemId] : orderQueue)
{
switch (orderOperation)
{
case OrderOperation::Before:
{
CommitBefore(systemId);
break;
}
case OrderOperation::After:
{
CommitAfter(systemId);
break;
}
}
}
orderQueue.clear();
}
void SystemOrderer::CommitBefore(const std::type_index& otherSystemId)
{
systemPool->MoveSystemBefore(systemId, otherSystemId);
}
void SystemOrderer::After(const std::type_index& otherSystemId)
void SystemOrderer::CommitAfter(const std::type_index& otherSystemId)
{
systemPool->MoveSystemAfter(systemId, otherSystemId);
}
}
}
+21 -10
View File
@@ -1,6 +1,7 @@
#pragma once
#include <typeindex>
#include <vector>
namespace Copium
{
@@ -8,27 +9,37 @@ namespace Copium
class SystemOrderer
{
private:
std::type_index systemId;
SystemPool* systemPool = nullptr;
public:
SystemOrderer(std::type_index systemId, SystemPool* systemPool);
template <typename Other>
template <typename System>
void Before()
{
Before(typeid(Other));
orderQueue.emplace_back(OrderOperation::Before, typeid(System));
}
template <typename Other>
template <typename System>
void After()
{
After(typeid(Other));
orderQueue.emplace_back(OrderOperation::After, typeid(System));
}
private:
void Before(const std::type_index& otherSystemId);
void After(const std::type_index& otherSystemId);
enum class OrderOperation
{
Before,
After
};
std::type_index systemId;
SystemPool* systemPool = nullptr;
std::vector<std::pair<OrderOperation, std::type_index>> orderQueue;
private:
friend class SystemPool;
void CommitOrdering();
void CommitBefore(const std::type_index& otherSystemId);
void CommitAfter(const std::type_index& otherSystemId);
};
}
+109 -23
View File
@@ -2,6 +2,8 @@
#include <algorithm>
#include "copium/ecs/System.h"
namespace Copium
{
SystemPool::SystemPool(ECSManager* manager)
@@ -19,28 +21,56 @@ namespace Copium
systems.clear();
}
SystemOrderer SystemPool::AddSystem(const std::type_index& systemId, SystemBase* system)
SystemOrderer& SystemPool::AddSystem(const std::type_index& systemId, System* system)
{
system->manager = manager;
CP_ASSERT(systems.find(systemId) == systems.end(), "System already exist in Ecs");
systems.emplace(systemId, system);
systemOrder.emplace_back(system);
return SystemOrderer{systemId, this};
system->SetECSManager(manager);
addQueue.emplace_back(systemId, system, SystemOrderer{systemId, this});
queueOperationOrder.emplace_back(QueueOperation::Add);
return std::get<2>(addQueue.back());
}
void SystemPool::RemoveSystem(const std::type_index& systemId)
{
auto it = systems.find(systemId);
if (it == systems.end())
{
CP_WARN("System does not exist in Ecs");
return;
}
removeQueue.emplace_back(systemId);
queueOperationOrder.emplace_back(QueueOperation::Remove);
}
auto itOrder = std::find(systemOrder.begin(), systemOrder.end(), it->second);
delete it->second;
systems.erase(it);
systemOrder.erase(itOrder);
void SystemPool::CommitUpdates()
{
if (queueOperationOrder.empty())
return;
CP_ASSERT(queueOperationOrder.size() == addQueue.size() + removeQueue.size(),
"queueOperationOrder size=%zu doesn't match the sum of the addQueue size=%zu and removeQueue size=%zu, "
"which is %zu",
queueOperationOrder.size(),
addQueue.size(),
removeQueue.size(),
addQueue.size() + removeQueue.size());
int addQueueIndex = 0;
int removeQueueIndex = 0;
for (QueueOperation queueOperation : queueOperationOrder)
{
switch (queueOperation)
{
case QueueOperation::Add:
{
CommitAddSystem(addQueueIndex);
addQueueIndex++;
break;
}
case QueueOperation::Remove:
{
CommitRemoveSystem(removeQueueIndex);
removeQueueIndex++;
break;
}
}
}
removeQueue.clear();
addQueue.clear();
queueOperationOrder.clear();
}
void SystemPool::Update()
@@ -51,20 +81,44 @@ namespace Copium
}
}
void SystemPool::Update(const Signal& signal)
void SystemPool::SendSignal(Signal* signal)
{
for (auto& system : systemOrder)
signalQueue.emplace(signal);
}
void SystemPool::CommitSignals()
{
while (!signalQueue.empty())
{
system->Run(signal);
auto& signal = signalQueue.front();
for (auto& system : systemOrder)
{
if (system->IsSignalSubscribed(signal->GetUuid()))
{
system->HandleSignal(*signal);
}
}
delete signal;
signalQueue.pop();
}
}
size_t SystemPool::Size() const
{
return systemOrder.size();
}
bool SystemPool::IsEmpty() const
{
return systemOrder.empty();
}
void SystemPool::MoveSystemAfter(const std::type_index& systemId, const std::type_index& afterSystemId)
{
auto it1 = systems.find(systemId);
CP_ASSERT(it1 != systems.end(), "System does not exist in SystemPool");
CP_ASSERT(it1 != systems.end(), "System with typeid=%s does not exist in SystemPool", systemId.name());
auto it2 = systems.find(afterSystemId);
CP_ASSERT(it2 != systems.end(), "System does not exist in SystemPool");
CP_ASSERT(it2 != systems.end(), "System with typeid=%s does not exist in SystemPool", afterSystemId.name());
auto itSystemId = std::find(systemOrder.rbegin(), systemOrder.rend(), it1->second);
auto itAfterSystemId = std::find(systemOrder.rbegin(), systemOrder.rend(), it2->second);
@@ -74,13 +128,45 @@ namespace Copium
void SystemPool::MoveSystemBefore(const std::type_index& systemId, const std::type_index& beforeSystemId)
{
auto it1 = systems.find(systemId);
CP_ASSERT(it1 != systems.end(), "System does not exist in SystemPool");
CP_ASSERT(it1 != systems.end(), "System with typeid=%s does not exist in SystemPool", systemId.name());
auto it2 = systems.find(beforeSystemId);
CP_ASSERT(it2 != systems.end(), "System does not exist in SystemPool");
CP_ASSERT(it2 != systems.end(), "System with typeid=%s does not exist in SystemPool", beforeSystemId.name());
auto itSystemId = std::find(systemOrder.rbegin(), systemOrder.rend(), it1->second);
auto itBeforeSystemId = std::find(systemOrder.rbegin(), systemOrder.rend(), it2->second);
std::rotate(itSystemId, itSystemId + 1, itBeforeSystemId + 1);
}
void SystemPool::CommitAddSystem(int queueIndex)
{
CP_ASSERT(
queueIndex < addQueue.size(), "queueIndex=%d is greater than the addQueueSize=%d", queueIndex, addQueue.size());
auto& [systemId, system, ordering] = addQueue[queueIndex];
CP_ASSERT(systems.find(systemId) == systems.end(), "System with typeid=%s already exists in SystemPool");
systems.emplace(systemId, system);
systemOrder.emplace_back(system);
ordering.CommitOrdering();
}
void SystemPool::CommitRemoveSystem(int queueIndex)
{
CP_ASSERT(queueIndex < removeQueue.size(),
"queueIndex=%d is greater than the removeQueueSize=%d",
queueIndex,
removeQueue.size());
const auto& systemId = removeQueue[queueIndex];
auto it = systems.find(systemId);
CP_ASSERT(it != systems.end(), "System with typeid=%s does not exist in SystemPool", systemId.name());
auto itOrder = std::find(systemOrder.begin(), systemOrder.end(), it->second);
CP_ASSERT(itOrder != systemOrder.end(), "System with typeid=%s does not exist in systemOrder", systemId.name());
delete it->second;
systems.erase(it);
systemOrder.erase(itOrder);
}
}
+28 -8
View File
@@ -1,37 +1,57 @@
#pragma once
#include <map>
#include <queue>
#include <typeindex>
#include <vector>
#include "copium/ecs/Signal.h"
#include "copium/ecs/SystemBase.h"
#include "copium/ecs/SystemOrderer.h"
#include "copium/util/Common.h"
namespace Copium
{
class ECSManager;
class System;
class SystemPool final
{
CP_DELETE_COPY_AND_MOVE_CTOR(SystemPool);
private:
ECSManager* manager;
std::map<std::type_index, SystemBase*> systems;
std::vector<SystemBase*> systemOrder;
public:
SystemPool(ECSManager* manager);
~SystemPool();
SystemOrderer AddSystem(const std::type_index& systemId, SystemBase* system);
SystemOrderer& AddSystem(const std::type_index& systemId, System* system);
void RemoveSystem(const std::type_index& systemId);
void Update();
void Update(const Signal& signal);
void SendSignal(Signal* signal);
void CommitSignals();
void CommitUpdates();
size_t Size() const;
bool IsEmpty() const;
void MoveSystemAfter(const std::type_index& systemId, const std::type_index& afterSystemId);
void MoveSystemBefore(const std::type_index& systemId, const std::type_index& beforeSystemId);
private:
ECSManager* manager;
std::map<std::type_index, System*> systems;
std::vector<System*> systemOrder;
enum class QueueOperation
{
Add,
Remove
};
std::vector<QueueOperation> queueOperationOrder;
std::vector<std::tuple<std::type_index, System*, SystemOrderer>> addQueue;
std::vector<std::type_index> removeQueue;
std::queue<Signal*> signalQueue;
void CommitAddSystem(int queueIndex);
void CommitRemoveSystem(int queueIndex);
};
}
+113
View File
@@ -0,0 +1,113 @@
#pragma once
#include "copium/ecs/ECSManager.h"
#include "copium/ecs/Entity.h"
namespace Copium
{
template <typename Component, typename... Components>
struct View
{
class Iterator
{
public:
std::tuple<Entity, Component&, Components&...> operator*()
{
EntityId entityId{entities[index]};
CP_ASSERT((manager->HasComponents<Component, Components...>(entityId)),
"entity doesn't contains all components");
return std::forward_as_tuple<Entity, Component&, Components&...>(
Entity{manager, entityId}, firstPool->At(index), manager->GetComponent<Components>(entityId)...);
}
Iterator& operator++()
{
++index;
FindNextEntity();
return *this;
}
Iterator operator++(int)
{
Iterator it = *this;
++*this;
return it;
}
Iterator operator+(size_t advance)
{
Iterator it = *this;
for (int i = 0; i < advance; i++)
++it;
return it;
}
bool operator==(const Iterator& other) const
{
return index == other.index;
}
bool operator!=(const Iterator& other) const
{
return !(*this == other);
}
private:
friend class View;
ECSManager* manager;
ComponentPool<std::remove_const_t<Component>>* firstPool;
const std::vector<EntityId>& entities;
int index;
Iterator(ECSManager* manager, ComponentPool<std::remove_const_t<Component>>* firstPool)
: manager{manager},
firstPool{firstPool},
entities{firstPool ? firstPool->GetEntities() : ECSManager::emptyEntities},
index{0}
{
}
static Iterator Begin(ECSManager* manager)
{
Iterator iterator{manager, manager->GetComponentPool<Component>()};
iterator.FindNextEntity();
return iterator;
}
static Iterator End(ECSManager* manager)
{
Iterator iterator{manager, manager->GetComponentPool<Component>()};
if (iterator.firstPool)
iterator.index = iterator.firstPool->GetEntities().size();
return iterator;
}
void FindNextEntity()
{
while (index < entities.size() && !manager->HasComponents<Components...>(entities[index]))
{
++index;
}
}
};
View(ECSManager* manager)
: manager{manager}
{
}
Iterator begin()
{
return Iterator::Begin(manager);
}
Iterator end()
{
return Iterator::End(manager);
}
private:
ECSManager* manager;
};
}
@@ -1,16 +0,0 @@
#include "copium/event/EventSignal.h"
namespace Copium
{
EventSignal::EventSignal(const Event& event)
: event{event}
{
}
const Event& EventSignal::GetEvent() const
{
return event;
}
CP_SIGNAL_DEFINITION(EventSignal);
}
@@ -1,22 +0,0 @@
#pragma once
#include "copium/ecs/Signal.h"
#include "copium/event/Event.h"
namespace Copium
{
class EventSignal : public Signal
{
private:
const Event& event;
public:
EventSignal(const Event& event);
const Event& GetEvent() const;
CP_SIGNAL_DECLERATION(EventSignal);
};
}
@@ -102,6 +102,58 @@ namespace Copium
return offset;
}
glm::vec2 Renderer::TextUi(
const std::string& str, const glm::vec2& position, const Font& font, float size, const glm::vec3& color)
{
glm::vec2 offset = position;
for (char c : str)
{
if (c == ' ')
{
const Glyph& glyph = font.GetGlyph(c);
offset.x += glyph.advance * size;
continue;
}
else if (c == '\t')
{
const Glyph& glyph = font.GetGlyph(' ');
offset.x += glyph.advance * size * 4;
continue;
}
else if (c == '\n')
{
offset.y += font.GetLineHeight() * size;
offset.x = position.x;
continue;
}
const Glyph& glyph = font.GetGlyph(c);
AllocateQuad();
int texIndex = AllocateSampler(font);
AddVertex(offset + glm::vec2{glyph.boundingBox.l * size, -glyph.boundingBox.t * size},
color,
texIndex,
glm::vec2{glyph.texCoordBoundingBox.l, glyph.texCoordBoundingBox.t},
RendererVertex::TYPE_TEXT);
AddVertex(offset + glm::vec2{glyph.boundingBox.l * size, -glyph.boundingBox.b * size},
color,
texIndex,
glm::vec2{glyph.texCoordBoundingBox.l, glyph.texCoordBoundingBox.b},
RendererVertex::TYPE_TEXT);
AddVertex(offset + glm::vec2{glyph.boundingBox.r * size, -glyph.boundingBox.b * size},
color,
texIndex,
glm::vec2{glyph.texCoordBoundingBox.r, glyph.texCoordBoundingBox.b},
RendererVertex::TYPE_TEXT);
AddVertex(offset + glm::vec2{glyph.boundingBox.r * size, -glyph.boundingBox.t * size},
color,
texIndex,
glm::vec2{glyph.texCoordBoundingBox.r, glyph.texCoordBoundingBox.t},
RendererVertex::TYPE_TEXT);
offset.x += glyph.advance * size;
}
return offset;
}
void Renderer::AddVertex(
const glm::vec2& position, const glm::vec3& color, int texindex, const glm::vec2& texCoord, int type)
{
@@ -46,6 +46,12 @@ namespace Copium
const Font& font,
float size,
const glm::vec3& color = glm::vec3(1, 1, 1));
// Returns the position where the text rendering ended
glm::vec2 TextUi(const std::string& str,
const glm::vec2& position,
const Font& font,
float size,
const glm::vec3& color = glm::vec3(1, 1, 1));
void Begin(CommandBuffer& commandBuffer);
void End();
@@ -0,0 +1,56 @@
#pragma once
#include <functional>
class GenericType
{
public:
template <typename T, typename... Args>
static GenericType Create(const Args&... args)
{
GenericType type;
type.data = new T{args...};
type.deleter = [](void* data) { delete (T*)data; };
return type;
}
GenericType(GenericType&& type)
: data{type.data},
deleter{type.deleter}
{
type.data = nullptr;
}
GenericType& operator=(GenericType&& type)
{
this->data = type.data;
this->deleter = type.deleter;
type.data = nullptr;
return *this;
}
~GenericType()
{
if (data)
{
deleter(data);
}
data = nullptr;
}
template <typename T>
T& Get()
{
return *(T*)data;
}
private:
GenericType() = default;
GenericType(const GenericType& type) = delete;
GenericType& operator=(const GenericType& type) = delete;
void* data;
std::function<void(void*)> deleter;
};
+5
View File
@@ -26,4 +26,9 @@ namespace Copium
return elapsedTime;
}
void Timer::OffsetSeconds(int time)
{
startTime += std::chrono::seconds(time);
}
}
+1
View File
@@ -15,5 +15,6 @@ namespace Copium
void Start();
double Elapsed();
double ElapsedRestart();
void OffsetSeconds(int time);
};
}