diff --git a/CopiumEngine/src/copium/core/SwapChain.cpp b/CopiumEngine/src/copium/core/SwapChain.cpp index cc0c943..58a2ddc 100644 --- a/CopiumEngine/src/copium/core/SwapChain.cpp +++ b/CopiumEngine/src/copium/core/SwapChain.cpp @@ -430,7 +430,7 @@ namespace Copium int width, height; glfwGetFramebufferSize(window, &width, &height); - VkExtent2D extent{width, height}; + VkExtent2D extent{static_cast(width), static_cast(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; diff --git a/CopiumEngine/src/copium/ecs/ComponentPool.h b/CopiumEngine/src/copium/ecs/ComponentPool.h index e92b06c..d83e1ea 100644 --- a/CopiumEngine/src/copium/ecs/ComponentPool.h +++ b/CopiumEngine/src/copium/ecs/ComponentPool.h @@ -19,6 +19,16 @@ namespace Copium std::vector components; ComponentListener* listener = nullptr; + enum class QueueOperation + { + Add, + Remove + }; + + std::vector queueOperationOrder; + std::vector> addQueue; + std::vector 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); + } + } }; } diff --git a/CopiumEngine/src/copium/ecs/ComponentPoolBase.h b/CopiumEngine/src/copium/ecs/ComponentPoolBase.h index 676e412..061ee40 100644 --- a/CopiumEngine/src/copium/ecs/ComponentPoolBase.h +++ b/CopiumEngine/src/copium/ecs/ComponentPoolBase.h @@ -17,6 +17,7 @@ namespace Copium virtual size_t Size() = 0; virtual bool Erase(EntityId entity) = 0; + virtual void CommitUpdates() = 0; std::vector& GetEntities(); const std::vector& GetEntities() const; }; diff --git a/CopiumEngine/src/copium/ecs/ECSManager.cpp b/CopiumEngine/src/copium/ecs/ECSManager.cpp index 8a48403..5e5af79 100644 --- a/CopiumEngine/src/copium/ecs/ECSManager.cpp +++ b/CopiumEngine/src/copium/ecs/ECSManager.cpp @@ -4,30 +4,37 @@ namespace Copium { + std::vector ECSManager::emptyEntities = {}; + ECSManager::ECSManager() - : systemPool{std::make_unique(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); } diff --git a/CopiumEngine/src/copium/ecs/ECSManager.h b/CopiumEngine/src/copium/ecs/ECSManager.h index 7de5b6f..3f3b0c3 100644 --- a/CopiumEngine/src/copium/ecs/ECSManager.h +++ b/CopiumEngine/src/copium/ecs/ECSManager.h @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -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 entities; - std::map componentPool; + std::map componentPools; - std::unique_ptr systemPool; - int currentEntityId = 1; + std::map> systemPools; + EntityId currentEntityId = 1; + std::set destroyedEntityIds; + + std::map globalDatas; public: + static std::vector emptyEntities; + ECSManager(); ~ECSManager(); template - 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(this)).first; + + return it->second->AddSystem(typeid(SystemClass), new SystemClass{}); } template - 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 + void SendSignal(const Args&... args) + { + Signal::ValidateSignal(); + 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* pool{new ComponentPool{}}; - componentPool.emplace(GetComponentId(), pool); + componentPools.emplace(GetComponentId(), pool); pool->SetComponentListener(listener); } } template - std::tuple AddComponents(EntityId entity, Components&&... components) + void AddComponents(EntityId entity, Components&&... components) { - return std::forward_as_tuple(AddComponent(entity, Components(components))...); + (AddComponent(entity, components), ...); } template - Component& AddComponent(EntityId entity, Args&&... args) + void AddComponent(EntityId entity, Args&&... args) { - return AddComponent(entity, Component{args...}); + AddComponent(entity, Component{args...}); } template - Component& AddComponent(EntityId entity, const Component& component) + void AddComponent(EntityId entity, const Component& component) { auto pool = GetComponentPool(); if (pool) { - CP_ASSERT(!HasComponent(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* pool{new ComponentPool{entity, component}}; - auto ret = componentPool.emplace(GetComponentId(), pool); - return pool->At(0); + auto ret = componentPools.emplace(GetComponentId(), pool); } } @@ -103,10 +131,7 @@ namespace Copium void RemoveComponent(EntityId entity) { auto pool = GetComponentPoolAssure(); - CP_ASSERT(pool->Erase(entity), - "Entity did not contain component (entity=%u, Component=%s)", - entity, - typeid(Component).name()); + pool->Erase(entity); } template @@ -229,22 +254,54 @@ namespace Copium return std::type_index(typeid(T)); } - private: - template - ComponentPool* GetComponentPool() + template + void AddGlobalData(const Args&... args) { - auto it = componentPool.find(GetComponentId()); - return it == componentPool.end() ? nullptr : static_cast*>(it->second); + auto it = globalDatas.find(typeid(T)); + CP_ASSERT(!HasGlobalData(), "Global with typeid=%s already exists. Do nothing", typeid(T).name()); + + globalDatas.emplace(typeid(T), GenericType::Create(args...)); + } + + template + T& GetGlobalData() + { + auto it = globalDatas.find(typeid(T)); + CP_ASSERT(it != globalDatas.end(), "Global with typeid=%s doesn't exist"); + + return it->second.Get(); + } + + template + bool HasGlobalData() + { + return globalDatas.find(typeid(T)) != globalDatas.end(); + } + + template + 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 - ComponentPool* GetComponentPoolAssure() + ComponentPool>* GetComponentPool() { - auto it = componentPool.find(GetComponentId()); - CP_ASSERT(it != componentPool.end(), + auto it = componentPools.find(GetComponentId()); + return it == componentPools.end() ? nullptr + : static_cast>*>(it->second); + } + + template + ComponentPool>* GetComponentPoolAssure() + { + auto it = componentPools.find(GetComponentId()); + CP_ASSERT(it != componentPools.end(), "Component has not been added to an entity (Component=%s)", typeid(Component).name()); - return static_cast*>(it->second); + return static_cast>*>(it->second); } }; } diff --git a/CopiumEngine/src/copium/ecs/Entity.cpp b/CopiumEngine/src/copium/ecs/Entity.cpp index 398827f..a8d211e 100644 --- a/CopiumEngine/src/copium/ecs/Entity.cpp +++ b/CopiumEngine/src/copium/ecs/Entity.cpp @@ -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()}; } -} \ No newline at end of file +} diff --git a/CopiumEngine/src/copium/ecs/Entity.h b/CopiumEngine/src/copium/ecs/Entity.h index 8e4c68a..814a198 100644 --- a/CopiumEngine/src/copium/ecs/Entity.h +++ b/CopiumEngine/src/copium/ecs/Entity.h @@ -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 - inline Component& AddComponent(Args... args) + inline void AddComponent(Args... args) { - return manager->AddComponent(id, args...); + manager->AddComponent(id, args...); } template - std::tuple AddComponents(Components&&... components) + void AddComponents(Components&&... components) { - return manager->AddComponents(id, components...); + manager->AddComponents(id, components...); } template diff --git a/CopiumEngine/src/copium/ecs/Signal.cpp b/CopiumEngine/src/copium/ecs/Signal.cpp deleted file mode 100644 index 5eae991..0000000 --- a/CopiumEngine/src/copium/ecs/Signal.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "copium/ecs/Signal.h" - -namespace Copium -{ - int Signal::GetAllocatedId() - { - allocatedIds++; - return allocatedIds; - } -} diff --git a/CopiumEngine/src/copium/ecs/Signal.h b/CopiumEngine/src/copium/ecs/Signal.h index 24ee92d..626a82f 100644 --- a/CopiumEngine/src/copium/ecs/Signal.h +++ b/CopiumEngine/src/copium/ecs/Signal.h @@ -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 + static void ValidateSignal() + { + static_assert(std::is_base_of_v, "ValidateSignal : Given T is does not have Signal as base class"); + static_assert( + has_static_uuid(), + "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 + struct has_static_uuid : std::false_type + { + }; + + template + struct has_static_uuid> : std::true_type + { + }; }; - } diff --git a/CopiumEngine/src/copium/ecs/System.h b/CopiumEngine/src/copium/ecs/System.h index 4d14c67..2234419 100644 --- a/CopiumEngine/src/copium/ecs/System.h +++ b/CopiumEngine/src/copium/ecs/System.h @@ -3,44 +3,54 @@ #include #include "copium/ecs/ECSManager.h" -#include "copium/ecs/Entity.h" -#include "copium/ecs/SystemBase.h" +#include "copium/ecs/Signal.h" namespace Copium { - template - 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([&](EntityId entityId, Components&... components) - { RunEntity(Entity{manager, entityId}, components...); }); } - void Run(const Signal& signal) override + const bool IsSignalSubscribed(const Uuid& uuid) const { - manager->Each([&](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 loggedEntities; - - bool ValidateEntity(Entity entity) + template + void SubscribeToSignal() { - if (entity && entity.HasComponents()) - { - loggedEntities.erase(entity); - return true; - } - if (loggedEntities.find(entity) == loggedEntities.end()) - CP_WARN("Invalid Entity"); - loggedEntities.emplace(entity); - return false; + Signal::ValidateSignal(); + subscribedSignals.emplace(S::UUID); } + + template + void UnsubscribeToSignal() + { + Signal::ValidateSignal(); + subscribedSignals.erase(S::UUID); + } + + void SetECSManager(ECSManager* manager) + { + this->manager = manager; + } + + template + void SendSignal(const Args&... args) + { + manager->SendSignal(args...); + } + + protected: + ECSManager* manager; + + private: + std::set subscribedSignals{}; }; } diff --git a/CopiumEngine/src/copium/ecs/SystemBase.h b/CopiumEngine/src/copium/ecs/SystemBase.h deleted file mode 100644 index 0be05a5..0000000 --- a/CopiumEngine/src/copium/ecs/SystemBase.h +++ /dev/null @@ -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; - }; -} diff --git a/CopiumEngine/src/copium/ecs/SystemOrderer.cpp b/CopiumEngine/src/copium/ecs/SystemOrderer.cpp index 33bba23..1d74fdf 100644 --- a/CopiumEngine/src/copium/ecs/SystemOrderer.cpp +++ b/CopiumEngine/src/copium/ecs/SystemOrderer.cpp @@ -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); } -} \ No newline at end of file +} diff --git a/CopiumEngine/src/copium/ecs/SystemOrderer.h b/CopiumEngine/src/copium/ecs/SystemOrderer.h index ea23e99..75a28d0 100644 --- a/CopiumEngine/src/copium/ecs/SystemOrderer.h +++ b/CopiumEngine/src/copium/ecs/SystemOrderer.h @@ -1,6 +1,7 @@ #pragma once #include +#include 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 + template void Before() { - Before(typeid(Other)); + orderQueue.emplace_back(OrderOperation::Before, typeid(System)); } - template + template 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> orderQueue; + + private: + friend class SystemPool; + + void CommitOrdering(); + void CommitBefore(const std::type_index& otherSystemId); + void CommitAfter(const std::type_index& otherSystemId); }; } diff --git a/CopiumEngine/src/copium/ecs/SystemPool.cpp b/CopiumEngine/src/copium/ecs/SystemPool.cpp index 1ed53c7..261771f 100644 --- a/CopiumEngine/src/copium/ecs/SystemPool.cpp +++ b/CopiumEngine/src/copium/ecs/SystemPool.cpp @@ -2,6 +2,8 @@ #include +#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); + } } diff --git a/CopiumEngine/src/copium/ecs/SystemPool.h b/CopiumEngine/src/copium/ecs/SystemPool.h index 782ebd0..b639ca4 100644 --- a/CopiumEngine/src/copium/ecs/SystemPool.h +++ b/CopiumEngine/src/copium/ecs/SystemPool.h @@ -1,37 +1,57 @@ #pragma once #include +#include #include #include #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 systems; - std::vector 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 systems; + std::vector systemOrder; + + enum class QueueOperation + { + Add, + Remove + }; + + std::vector queueOperationOrder; + std::vector> addQueue; + std::vector removeQueue; + std::queue signalQueue; + + void CommitAddSystem(int queueIndex); + void CommitRemoveSystem(int queueIndex); }; } diff --git a/CopiumEngine/src/copium/ecs/View.h b/CopiumEngine/src/copium/ecs/View.h new file mode 100644 index 0000000..ff4d09f --- /dev/null +++ b/CopiumEngine/src/copium/ecs/View.h @@ -0,0 +1,113 @@ +#pragma once + +#include "copium/ecs/ECSManager.h" +#include "copium/ecs/Entity.h" + +namespace Copium +{ + template + struct View + { + class Iterator + { + public: + std::tuple operator*() + { + EntityId entityId{entities[index]}; + CP_ASSERT((manager->HasComponents(entityId)), + "entity doesn't contains all components"); + return std::forward_as_tuple( + Entity{manager, entityId}, firstPool->At(index), manager->GetComponent(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>* firstPool; + const std::vector& entities; + int index; + + Iterator(ECSManager* manager, ComponentPool>* firstPool) + : manager{manager}, + firstPool{firstPool}, + entities{firstPool ? firstPool->GetEntities() : ECSManager::emptyEntities}, + index{0} + { + } + + static Iterator Begin(ECSManager* manager) + { + Iterator iterator{manager, manager->GetComponentPool()}; + iterator.FindNextEntity(); + return iterator; + } + + static Iterator End(ECSManager* manager) + { + Iterator iterator{manager, manager->GetComponentPool()}; + if (iterator.firstPool) + iterator.index = iterator.firstPool->GetEntities().size(); + return iterator; + } + + void FindNextEntity() + { + while (index < entities.size() && !manager->HasComponents(entities[index])) + { + ++index; + } + } + }; + + View(ECSManager* manager) + : manager{manager} + { + } + + Iterator begin() + { + return Iterator::Begin(manager); + } + + Iterator end() + { + return Iterator::End(manager); + } + + private: + ECSManager* manager; + }; +} diff --git a/CopiumEngine/src/copium/event/EventSignal.cpp b/CopiumEngine/src/copium/event/EventSignal.cpp deleted file mode 100644 index 42b6137..0000000 --- a/CopiumEngine/src/copium/event/EventSignal.cpp +++ /dev/null @@ -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); -} diff --git a/CopiumEngine/src/copium/event/EventSignal.h b/CopiumEngine/src/copium/event/EventSignal.h deleted file mode 100644 index 3cdfe9c..0000000 --- a/CopiumEngine/src/copium/event/EventSignal.h +++ /dev/null @@ -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); - }; - -} diff --git a/CopiumEngine/src/copium/renderer/Renderer.cpp b/CopiumEngine/src/copium/renderer/Renderer.cpp index d066756..4ac0583 100644 --- a/CopiumEngine/src/copium/renderer/Renderer.cpp +++ b/CopiumEngine/src/copium/renderer/Renderer.cpp @@ -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) { diff --git a/CopiumEngine/src/copium/renderer/Renderer.h b/CopiumEngine/src/copium/renderer/Renderer.h index f347feb..1c250de 100644 --- a/CopiumEngine/src/copium/renderer/Renderer.h +++ b/CopiumEngine/src/copium/renderer/Renderer.h @@ -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(); diff --git a/CopiumEngine/src/copium/util/GenericType.h b/CopiumEngine/src/copium/util/GenericType.h new file mode 100644 index 0000000..4fb137b --- /dev/null +++ b/CopiumEngine/src/copium/util/GenericType.h @@ -0,0 +1,56 @@ +#pragma once + +#include + +class GenericType +{ +public: + template + 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 + T& Get() + { + return *(T*)data; + } + +private: + GenericType() = default; + + GenericType(const GenericType& type) = delete; + GenericType& operator=(const GenericType& type) = delete; + + void* data; + std::function deleter; +}; diff --git a/CopiumEngine/src/copium/util/Timer.cpp b/CopiumEngine/src/copium/util/Timer.cpp index b9e3e42..b982ba3 100644 --- a/CopiumEngine/src/copium/util/Timer.cpp +++ b/CopiumEngine/src/copium/util/Timer.cpp @@ -26,4 +26,9 @@ namespace Copium return elapsedTime; } + + void Timer::OffsetSeconds(int time) + { + startTime += std::chrono::seconds(time); + } } diff --git a/CopiumEngine/src/copium/util/Timer.h b/CopiumEngine/src/copium/util/Timer.h index 70c517e..1e7dd61 100644 --- a/CopiumEngine/src/copium/util/Timer.h +++ b/CopiumEngine/src/copium/util/Timer.h @@ -15,5 +15,6 @@ namespace Copium void Start(); double Elapsed(); double ElapsedRestart(); + void OffsetSeconds(int time); }; }