diff options
| -rw-r--r-- | CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/ecs.cpp | 71 | ||||
| -rw-r--r-- | src/ecs.hpp | 25 | ||||
| -rw-r--r-- | src/game.cpp | 7 | ||||
| -rw-r--r-- | src/game.hpp | 4 | ||||
| -rw-r--r-- | src/main.cpp | 4 |
6 files changed, 112 insertions, 1 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 011126f..e90552d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,8 @@ set(CMAKE_CXX_FLAGS_RELEASE "-O3 -s DNDEBUG") set(PKE_SOURCE_FILES src/main.cpp src/macros.hpp + src/ecs.hpp + src/ecs.cpp src/game.hpp src/game.cpp src/memory.hpp diff --git a/src/ecs.cpp b/src/ecs.cpp new file mode 100644 index 0000000..2c0a26f --- /dev/null +++ b/src/ecs.cpp @@ -0,0 +1,71 @@ + +#include "ecs.hpp" + +TypeSafeInt_B(EntityHandle); +const uint64_t bucketItemCount = 256; + +struct EntityBucket{ + Entity entities[bucketItemCount]; +}; + +uint64_t entityBucketIncrementer = 2; +EntityHandle_T entityBucketCounter{0}; +EntityHandle_T entityCounter{0}; +EntityBucket *entityBuckets = nullptr; +DynArray<EntityHandle> entitiesMarkedForRemoval{16}; +DynArray<EntityHandle> EntitiesToBeRemoved{16}; + +void ECS_Init() { + entityBuckets = pke::PkeNew<EntityBucket>(entityBucketIncrementer); +} + +EntityHandle ECS_CreateEntity_Inner(EntityHandle parentEntityHandle) { + EntityHandle_T entityHandle_T{Buckets_NewHandle<EntityBucket>(bucketItemCount, entityBucketIncrementer, entityBucketCounter, entityCounter, entityBuckets)}; + EntityHandle entityHandle{entityHandle_T}; + + Entity *entity = &entityBuckets[Buckets_GetBucketIndex(entityHandle_T)].entities[Buckets_GetItemIndex(entityHandle_T)]; + + entity->handle = entityHandle; + entity->parentHandle = parentEntityHandle; + + return entityHandle; +} + +EntityHandle ECS_CreateEntity(EntityHandle parentEntityHandle) { + return ECS_CreateEntity_Inner(parentEntityHandle); +} + +void ECS_MarkForRemoval(EntityHandle entityHandle) { + auto b = Buckets_GetBucketIndex(static_cast<EntityHandle_T>(entityHandle)); + auto e = Buckets_GetItemIndex(static_cast<EntityHandle_T>(entityHandle)); + const Entity *ent = &entityBuckets[b].entities[e]; + assert(ent->isMarkedForRemoval == false && "Entity already marked for removal"); + entitiesMarkedForRemoval.Push(entityHandle); +} + +void ECS_Tick(double delta) { + EntitiesToBeRemoved.Resize(0); + for (long b = 0; b <= entityBucketCounter; ++b) { + uint64_t entCount = b == entityBucketCounter ? entityCounter >> 32 : bucketItemCount; + for (long e = 0; e < entCount; ++e) { + Entity *ent = &entityBuckets[b].entities[e]; + if (ent->handle == EntityHandle{EntityHandle_T{0xFFFFFFFFFFFFFFFF}}) continue; + if (ent->isMarkedForRemoval) { + ent->handle = EntityHandle{EntityHandle_T{0xFFFFFFFFFFFFFFFF}}; + ent->parentHandle = EntityHandle{EntityHandle_T{0xFFFFFFFFFFFFFFFF}}; + ent->isMarkedForRemoval = false; + } else { + if (entitiesMarkedForRemoval.Has(ent->handle)) { + ent->isMarkedForRemoval = true; + EntitiesToBeRemoved.Push(ent->handle); + continue; + } + if (EntitiesToBeRemoved.Has(ent->parentHandle)) { + ent->isMarkedForRemoval = true; + EntitiesToBeRemoved.Push(ent->handle); + } + } + } + } + entitiesMarkedForRemoval.Resize(0); +} diff --git a/src/ecs.hpp b/src/ecs.hpp new file mode 100644 index 0000000..d37ff57 --- /dev/null +++ b/src/ecs.hpp @@ -0,0 +1,25 @@ +#ifndef PKE_ECS_HPP +#define PKE_ECS_HPP + +#include "dynamic-array.hpp" +#include "macros.hpp" +#include "memory.hpp" + +#include "glm/vec3.hpp" + +TypeSafeInt_H(EntityHandle, uint64_t, UINT64_MAX); + +extern DynArray<EntityHandle> EntitiesToBeRemoved; + +struct Entity { + EntityHandle handle = EntityHandle{EntityHandle_T{0xFFFFFFFFFFFFFFFF}}; + EntityHandle parentHandle = EntityHandle{EntityHandle_T{0xFFFFFFFFFFFFFFFF}}; + bool isMarkedForRemoval = false; +}; + +void ECS_Init(); +void ECS_Tick(double delta); +EntityHandle ECS_CreateEntity(EntityHandle parentEntityHandle = EntityHandle{EntityHandle_T{0xFFFFFFFFFFFFFFFF}}); +void ECS_MarkForRemoval(EntityHandle entityHandle); + +#endif /* PKE_ECS_HPP */ diff --git a/src/game.cpp b/src/game.cpp index 3cf2109..cfb6d7c 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -3,3 +3,10 @@ GameSettings pkeSettings{}; +void GameTick(double delta) { + /* + * ECS_Tick() gets called first because it updates the public + * `EntitiesToBeRemoved` for all other ticks to use. + */ + ECS_Tick(delta); +} diff --git a/src/game.hpp b/src/game.hpp index 4ab021b..fad551b 100644 --- a/src/game.hpp +++ b/src/game.hpp @@ -4,6 +4,8 @@ #include <chrono> #include <cstdint> +#include "ecs.hpp" + using GameTimeDuration = std::chrono::duration<int64_t, std::nano>; using GameTimePoint = std::chrono::steady_clock::time_point; #define NANO_DENOM std::chrono::nanoseconds::period::den @@ -21,4 +23,6 @@ struct GameSettings { extern GameSettings pkeSettings; +void GameTick(double delta); + #endif /* PKE_GAME_HPP */ diff --git a/src/main.cpp b/src/main.cpp index 026ddb8..b5a0697 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,6 +7,7 @@ #include <csignal> #include "asset-manager.hpp" +#include "ecs.hpp" #include "game.hpp" #include "window.hpp" @@ -18,7 +19,7 @@ void signal_handler(int signal_num) { PKEWindowProperties windowProps{}; void Tick(double delta) { - /* no-op */ + GameTick(delta); } int main() { @@ -27,6 +28,7 @@ int main() { printf("PKE ENTERING\n"); try { AssetManagerInit(); + ECS_Init(); CreateWindow(&windowProps); auto steadyClock = std::chrono::steady_clock(); |
