diff --git a/CopiumEngine/CopiumEngine.vcxproj b/CopiumEngine/CopiumEngine.vcxproj index 47cb8f3..9ff3538 100644 --- a/CopiumEngine/CopiumEngine.vcxproj +++ b/CopiumEngine/CopiumEngine.vcxproj @@ -178,6 +178,10 @@ + + + + @@ -236,6 +240,12 @@ + + + + + + diff --git a/CopiumEngine/CopiumEngine.vcxproj.filters b/CopiumEngine/CopiumEngine.vcxproj.filters index 8dbac8d..9558e2a 100644 --- a/CopiumEngine/CopiumEngine.vcxproj.filters +++ b/CopiumEngine/CopiumEngine.vcxproj.filters @@ -186,6 +186,18 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + @@ -380,5 +392,23 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + \ No newline at end of file diff --git a/CopiumEngine/src/copium/ecs/ComponentPool.h b/CopiumEngine/src/copium/ecs/ComponentPool.h new file mode 100644 index 0000000..2f0efdc --- /dev/null +++ b/CopiumEngine/src/copium/ecs/ComponentPool.h @@ -0,0 +1,90 @@ +#pragma once + +#include "copium/ecs/Config.h" +#include "copium/ecs/EntitySet.h" +#include "copium/ecs/ComponentPoolBase.h" + +#include + +namespace Copium +{ + template + class ComponentPool : public ComponentPoolBase + { + using Iterator = typename std::vector::iterator; + private: + std::vector components; + + public: + ComponentPool(EntityID entity, const Component& component) + { + Emplace(entity, component); + } + + Component& Emplace(EntityID entity, const Component& component) + { + components.push_back(component); + entities.Emplace(entity); + return components.back(); + } + + void Pop() + { + components.pop_back(); + entities.Pop(); + } + + bool Erase(EntityID entity) + { + size_t index = entities.Find(entity); + if (!entities.Erase(entity)) + return false; + components.erase(components.begin() + index); + return true; + } + + Component& At(size_t index) + { + return operator[](index); + } + + size_t Find(EntityID entity) + { + return entities.Find(entity); + } + + Component* FindComponent(EntityID entity) + { + size_t index = Find(entity); + if (index < Size()) + return &components[index]; + return nullptr; + } + + Component& operator[](size_t index) + { + CP_ASSERT(index < components.size(), "Index Out of Bound Exception"); + return components[index]; + } + + size_t Size() + { + return components.size(); + } + + Iterator Back() + { + return components.back(); + } + + Iterator begin() + { + return components.begin(); + } + + Iterator end() + { + return components.end(); + } + }; +} diff --git a/CopiumEngine/src/copium/ecs/ComponentPoolBase.cpp b/CopiumEngine/src/copium/ecs/ComponentPoolBase.cpp new file mode 100644 index 0000000..067e1bb --- /dev/null +++ b/CopiumEngine/src/copium/ecs/ComponentPoolBase.cpp @@ -0,0 +1,14 @@ +#include "copium/ecs/ComponentPoolBase.h" + +namespace Copium +{ + std::vector& ComponentPoolBase::GetEntities() + { + return entities.GetList(); + } + + const std::vector& ComponentPoolBase::GetEntities() const + { + return entities.GetList(); + } +} diff --git a/CopiumEngine/src/copium/ecs/ComponentPoolBase.h b/CopiumEngine/src/copium/ecs/ComponentPoolBase.h new file mode 100644 index 0000000..7ef4312 --- /dev/null +++ b/CopiumEngine/src/copium/ecs/ComponentPoolBase.h @@ -0,0 +1,23 @@ +#pragma once + +#include "copium/ecs/Config.h" +#include "copium/ecs/EntitySet.h" + +#include + +namespace Copium +{ + class ComponentPoolBase + { + protected: + EntitySet entities; + public: + virtual ~ComponentPoolBase() = default; + + virtual size_t Size() = 0; + virtual void Pop() = 0; + virtual bool Erase(EntityID entity) = 0; + std::vector& GetEntities(); + const std::vector& GetEntities() const; + }; +} diff --git a/CopiumEngine/src/copium/ecs/Config.h b/CopiumEngine/src/copium/ecs/Config.h new file mode 100644 index 0000000..21ccf20 --- /dev/null +++ b/CopiumEngine/src/copium/ecs/Config.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include + +namespace Copium +{ + using EntityID = uint32_t; + const static uint32_t MAX_NUM_ENTITIES = std::numeric_limits::max(); + const static uint32_t INVALID_ENTITY = 0; +} diff --git a/CopiumEngine/src/copium/ecs/ECSManager.cpp b/CopiumEngine/src/copium/ecs/ECSManager.cpp new file mode 100644 index 0000000..0ddccb2 --- /dev/null +++ b/CopiumEngine/src/copium/ecs/ECSManager.cpp @@ -0,0 +1,50 @@ +#include "copium/ecs/ECSManager.h" + +#include "copium/util/Common.h" + +namespace Copium +{ + ECSManager::~ECSManager() + { + for (auto&& components : componentPool) + { + delete components.second; + } + componentPool.clear(); + } + + size_t ECSManager::GetEntityCount() const + { + return entities.size(); + } + + EntityID ECSManager::CreateEntity() + { + CP_ASSERT(currentEntityId != MAX_NUM_ENTITIES, "No more entities available"); + entities.emplace(currentEntityId); + currentEntityId++; + return currentEntityId - 1; + } + + void ECSManager::DestroyEntity(EntityID entity) + { + auto it = entities.find(entity); + CP_ASSERT(it != entities.end(), "Entity does not exist in ECSManager (entity=%u)", entity); + entities.erase(it); + for (auto&& pool : componentPool) + { + pool.second->Erase(entity); + } + } + + bool ECSManager::ValidEntity(EntityID entity) + { + return entities.find(entity) != entities.end(); + } + + void ECSManager::Each(std::function function) + { + for (auto e : entities) + function(e); + } +} diff --git a/CopiumEngine/src/copium/ecs/ECSManager.h b/CopiumEngine/src/copium/ecs/ECSManager.h new file mode 100644 index 0000000..ecc69c1 --- /dev/null +++ b/CopiumEngine/src/copium/ecs/ECSManager.h @@ -0,0 +1,196 @@ +#pragma once + + +#include "copium/ecs/ComponentPool.h" +#include "copium/ecs/Config.h" +#include "copium/util/Common.h" + +#include +#include +#include +#include +#include + +namespace Copium +{ + class ECSManager final + { + private: + std::unordered_set entities; + std::map componentPool; + int currentEntityId = 1; + public: + ~ECSManager(); + + EntityID CreateEntity(); + void DestroyEntity(EntityID entity); + size_t GetEntityCount() const; + bool ValidEntity(EntityID entity); + void Each(std::function function); + + template + std::tuple AddComponents(EntityID entity, Components&&... components) + { + return std::forward_as_tuple(AddComponent(entity, Components(components))...); + } + + template + Component& AddComponent(EntityID entity, Args&&... args) + { + return AddComponent(entity, Component(args...)); + } + + template + Component& 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); + } + else + { + ComponentPool* pool{new ComponentPool{entity, component}}; + auto ret = componentPool.emplace(GetComponentId(), pool); + return pool->At(0); + } + } + + template + 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()); + } + + template + void RemoveComponents(EntityID entity) + { + (RemoveComponent(entity), ...); + } + + template + Component& GetComponent(EntityID entity) + { + auto pool = GetComponentPoolAssure(); + Component* component = pool->FindComponent(entity); + ASSERT(component, "Entity did not contain component (entity=%u, Component=%s)", entity, typeid(Component).name()); + return *component; + } + + template + bool HasComponent(EntityID entity) + { + auto pool = GetComponentPool(); + if (pool) + return pool->Find(entity) != pool->Size(); + return false; + } + + template + bool HasComponents(EntityID entity) + { + return (HasComponent(entity) && ...); + } + + template + bool HasAnyComponent(EntityID entity) + { + return (HasComponent(entity) || ...); + } + + template + void Each(Func function) + { + auto pool = GetComponentPool(); + if (pool) + { + size_t i = 0; + for (auto entity : pool->GetEntities()) + { + if (HasComponents(entity)) + { + std::apply(function, std::forward_as_tuple(entity, pool->At(i), GetComponent(entity)...)); + } + i++; + } + } + } + + template + void Each(std::function function) + { + auto pool = GetComponentPool(); + if (pool) + { + size_t i = 0; + for (auto e : pool->GetEntities()) + { + function(e, pool->At(i)); + i++; + } + } + } + + template + EntityID Find(Func function) + { + auto pool = GetComponentPool(); + if (pool) + { + size_t i = 0; + for (auto entity : pool->GetEntities()) + { + if (HasComponents(entity)) + { + if (std::apply(function, std::forward_as_tuple(entity, pool->At(i), GetComponent(entity)...))) + return entity; + } + i++; + } + } + return 0; + } + + template + EntityID Find(std::function function) + { + auto pool = GetComponentPool(); + if (pool) + { + size_t i = 0; + for (auto e : pool->GetEntities()) + { + if (function(e, pool->At(i))) + return e; + i++; + } + } + return 0; + } + + template + std::type_index GetComponentId() + { + return std::type_index(typeid(T)); + } + + private: + template + ComponentPool* GetComponentPool() + { + auto it = componentPool.find(GetComponentId()); + return it == componentPool.end() ? nullptr : static_cast*>(it->second); + } + + template + ComponentPool* GetComponentPoolAssure() + { + auto it = componentPool.find(GetComponentId()); + CP_ASSERT(it != componentPool.end(), "Component has not been added to an entity (Component=%s)", typeid(Component).name()); + return static_cast*>(it->second); + } + }; +} diff --git a/CopiumEngine/src/copium/ecs/Entity.cpp b/CopiumEngine/src/copium/ecs/Entity.cpp new file mode 100644 index 0000000..38f2b00 --- /dev/null +++ b/CopiumEngine/src/copium/ecs/Entity.cpp @@ -0,0 +1,76 @@ +#include "copium/ecs/Entity.h" + +namespace Copium +{ + Entity::Entity() + : manager{nullptr}, id{INVALID_ENTITY} + {} + + Entity::Entity(ECSManager* manager) + : manager{manager}, id{INVALID_ENTITY} + {} + + Entity::Entity(ECSManager* manager, EntityID id) + : manager{manager}, id{id} + {} + + Entity::operator EntityID() const + { + return id; + } + + void Entity::operator=(EntityID entityId) + { + id = entityId; + } + + bool Entity::operator==(const Entity& entity) + { + return id == entity.id; + } + + bool Entity::operator!=(const Entity& entity) + { + return id != entity.id; + } + + Entity::operator bool() const + { + if (id == INVALID_ENTITY) + return false; + if (manager) + return manager->ValidEntity(id); + return false; + } + + void Entity::Invalidate() + { + id = INVALID_ENTITY; + } + + void Entity::Destroy() + { + if (*this) + manager->DestroyEntity(id); + } + + void Entity::SetID(EntityID aId) + { + id = aId; + } + + EntityID Entity::GetID() const + { + return id; + } + + ECSManager* Entity::GetManager() const + { + return manager; + } + + Entity Entity::Create(ECSManager* manager) + { + 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 new file mode 100644 index 0000000..c8921ad --- /dev/null +++ b/CopiumEngine/src/copium/ecs/Entity.h @@ -0,0 +1,83 @@ +#pragma once + +#include "copium/ecs/Config.h" +#include "copium/ecs/ECSManager.h" + +namespace Copium +{ + class Entity + { + friend class ECSManager; + + private: + ECSManager* manager; + EntityID id; + + public: + Entity(); + Entity(ECSManager* manager); + Entity(ECSManager* manager, EntityID id); + + operator EntityID() const; + void operator=(EntityID entityId); + bool operator==(const Entity& entity); + bool operator!=(const Entity& entity); + operator bool() const; + + void Invalidate(); + void Destroy(); + void SetID(EntityID aId); + EntityID GetID() const; + ECSManager* GetManager() const; + + static Entity Create(ECSManager* manager); + + template + inline Component& AddComponent(Args... args) + { + return manager->AddComponent(id, args...); + } + + template + std::tuple AddComponents(Components&&... components) + { + return manager->AddComponents(id, components...); + } + + template + inline void RemoveComponent() + { + return manager->RemoveComponent(id); + } + + template + inline void RemoveComponents() + { + return manager->RemoveComponents(id); + } + + template + inline Component& GetComponent() const + { + return manager->GetComponent(id); + } + + template + inline bool HasComponent() const + { + return manager->HasComponent(id); + } + + template + inline bool HasComponents() const + { + return manager->HasComponents(id); + } + + template + inline bool HasAnyComponent() const + { + return manager->HasAnyComponent(id); + } + }; +} diff --git a/CopiumEngine/src/copium/ecs/EntitySet.cpp b/CopiumEngine/src/copium/ecs/EntitySet.cpp new file mode 100644 index 0000000..86db3bd --- /dev/null +++ b/CopiumEngine/src/copium/ecs/EntitySet.cpp @@ -0,0 +1,63 @@ +#include "copium/ecs/EntitySet.h" + +namespace Copium +{ + bool EntitySet::Emplace(EntityID entity) + { + auto res = entitiesMap.emplace(entity, entitiesList.size()); + + // Check if already exists + if (!res.second) + return false; + + entitiesList.push_back(entity); + return true; + } + + bool EntitySet::Erase(EntityID entity) + { + auto it = entitiesMap.find(entity); + if (it == entitiesMap.end()) + return false; + size_t componentPos = it->second; + entitiesList.erase(entitiesList.begin() + it->second); + entitiesMap.erase(it); + + for (auto&& entityPos : entitiesMap) + { + if (entityPos.second > componentPos) + { + entityPos.second--; + } + } + return true; + } + + bool EntitySet::Pop() + { + if (entitiesList.size() == 0) + return false; + entitiesMap.erase(entitiesMap.find(entitiesList.back())); + entitiesList.pop_back(); + return true; + } + + size_t EntitySet::Find(EntityID entity) + { + auto it = entitiesMap.find(entity); + if (it == entitiesMap.end()) + return entitiesList.size(); + return it->second; + } + + size_t EntitySet::Size() const + { + return entitiesList.size(); + } + + std::vector& EntitySet::GetList() { return entitiesList; } + const std::vector& EntitySet::GetList() const { return entitiesList; } + + std::vector::iterator EntitySet::begin() { return entitiesList.begin(); } + std::vector::iterator EntitySet::end() { return entitiesList.end(); } +} diff --git a/CopiumEngine/src/copium/ecs/EntitySet.h b/CopiumEngine/src/copium/ecs/EntitySet.h new file mode 100644 index 0000000..9031fd4 --- /dev/null +++ b/CopiumEngine/src/copium/ecs/EntitySet.h @@ -0,0 +1,27 @@ +#pragma once + +#include "copium/ecs/Config.h" + +#include +#include + +namespace Copium +{ + class EntitySet + { + private: + std::vector entitiesList; + std::unordered_map entitiesMap; // Maps the entity id to a component index + public: + bool Emplace(EntityID entity); + bool Erase(EntityID entity); + bool Pop(); + size_t Find(EntityID entity); + size_t Size() const; + std::vector& GetList(); + const std::vector& GetList() const; + + std::vector::iterator begin(); + std::vector::iterator end(); + }; +}