From 8d26a42db16a4716169098ea2f066f2ccbd03084 Mon Sep 17 00:00:00 2001 From: Rodolfo Barcelli Jo Date: Thu, 22 Jan 2026 08:25:56 +0800 Subject: [PATCH] Update: Merge all versions into becs.h --- becs.h | 443 ++++++++++++++++++++++++++++++++++++++++++++------------ becs2.h | 185 ----------------------- becs3.h | 390 ------------------------------------------------- 3 files changed, 348 insertions(+), 670 deletions(-) delete mode 100644 becs2.h delete mode 100644 becs3.h diff --git a/becs.h b/becs.h index 871bbe4..c79ccaf 100644 --- a/becs.h +++ b/becs.h @@ -1,137 +1,390 @@ +/* + * Usage: + * + * Define BECS_MAX_ENTITIES and BECS_MAX_COMPONENTS. Otherwise they will have + * the following defaults: + * + * #define BECS_MAX_ENTITIES 4096 + * #define BECS_MAX_COMPONENTS 64 + * + * Then define BECS_IMPLEMENTATION in exactly _one_ file before including this + * file as follows: + * + * #define BECS_IMPLEMENTATION + * #include "becs.h" + */ + #ifndef BECS_H #define BECS_H #include #include +#include +#include -typedef struct BECS_ComponentArray BECS_ComponentArray; -typedef struct BECS_Properties BECS_Properties; +#ifdef BECS_SLOW +#include +#else +#define assert(expression) +#endif /* BECS_SLOW */ + +typedef struct BECS_Arena BECS_Arena; +typedef struct BECS_ComponentPool BECS_ComponentPool; typedef struct BECS_ECS BECS_ECS; -size_t BECS_GetMinMemorySize(BECS_Properties props); -BECS_ECS *BECS_Init(void *memory, size_t size, BECS_Properties *props); -int32_t BECS_AddComponent(BECS_ECS *ecs, uint32_t entityID, uint64_t componentBitMask, void *component); +typedef uint32_t BECS_Entity; +typedef uint32_t BECS_ComponentID; -#define MAX_ENTITY_COUNT 100 +size_t BECS_GetMinMemoryArenaSize(size_t *componentSizes, size_t arrayLength); +BECS_ECS BECS_InitECS(void *memory, size_t size); +void BECS_ResetECS(BECS_ECS *ecs); + +BECS_Entity BECS_EntityCreate(BECS_ECS *ecs); +void BECS_EntityDestroy(BECS_ECS *ecs, BECS_Entity entity); +bool BECS_IsEntityAlive(BECS_ECS *ecs, BECS_Entity entity); + +BECS_ComponentID BECS_ComponentRegister(BECS_ECS *ecs, size_t componentSize, size_t maximumComponentCount); +void *BECS_ComponentAdd(BECS_ECS *ecs, BECS_Entity entity, BECS_ComponentID componentId); +void BECS_ComponentRemove(BECS_ECS *ecs, BECS_Entity entity, BECS_ComponentID componentId); +void *BECS_ComponentGet(BECS_ECS *ecs, BECS_Entity entity, BECS_ComponentID componentId); +bool BECS_ComponentHas(BECS_ECS *ecs, BECS_Entity entity, BECS_ComponentID componentId); #ifdef BECS_IMPLEMENTATION -#ifdef MAX_ENTITY_COUNT -struct BECS_ComponentArray { - uint32_t lastEntityAdded; - size_t unitSize; - uint32_t length; - uint32_t capacity; - void *array; +#ifndef BECS_MAX_ENTITIES +#define BECS_MAX_ENTITIES 4096 +#endif /* BECS_MAX_ENTITIES */ + +#ifndef BECS_MAX_COMPONENTS +#define BECS_MAX_COMPONENTS 64 +#endif /* BECS_MAX_COMPONENTS */ + +#define BECS_NULL_ENTITY BECS_MAX_COMPONENTS + 1 +#define BECS_INVALID_COMPONENT_ID UINT32_MAX + +typedef uint64_t BECS_Signature; + +struct BECS_Arena { + uintptr_t nextAllocation; + size_t capacity; + void *memory; }; -struct BECS_Properties { - uint32_t maxEntities; - uint32_t numComponents; - size_t *componentSizes; +struct BECS_ComponentPool { + size_t unitSize; + size_t capacity; + size_t count; + uint32_t sparseArray[BECS_MAX_ENTITIES]; + void *denseArray; + BECS_Entity *denseEntityMapping; }; struct BECS_ECS { - uint64_t *bitMasks; - BECS_Properties props; - BECS_ComponentArray *componentSets; + BECS_Arena arena; + + /* Entity Management */ + BECS_Entity nextEntity; + uint32_t entityCount; + BECS_Signature entitySignatures[BECS_MAX_ENTITIES]; + bool aliveEntites[BECS_MAX_ENTITIES]; + + BECS_Entity freeEntities[BECS_MAX_ENTITIES]; + uint32_t freeEntityCount; + + /* Component Management */ + uint32_t componentTypeCount; + BECS_ComponentPool componentPools[BECS_MAX_COMPONENTS]; }; -size_t BECS_GetMinMemorySize -(BECS_Properties props) +/* Signature Helpers */ +static BECS_Signature BECS_SignatureSet(BECS_Signature sig, BECS_ComponentID componentId) { + return sig | (1ULL << componentId); +} + +static bool BECS_SignatureHas(BECS_Signature sig, BECS_ComponentID component_id) +{ + return (sig & (1ULL << component_id)) != 0; +} + +static bool BECS_SignatureMatches(BECS_Signature entitySig, BECS_Signature querySig) +{ + return (entitySig & querySig) == querySig; +} + +/* Memory Arena */ + +BECS_Arena BECS_CreateArena(void *memory, size_t capacity) +{ + uintptr_t baseOffset = 64 - ((uintptr_t)memory % 64); + BECS_Arena arena = { + baseOffset, + capacity, + memory + }; + return arena; +} + +static void *BECS_AllocateMemory(BECS_Arena *arena, size_t size) +{ + size_t totalSizeBytes = size; + uintptr_t nextAllocOffset = arena->nextAllocation + ((64 - (arena->nextAllocation % 64)) & 63); + if (nextAllocOffset + totalSizeBytes <= arena->capacity) + { + arena->nextAllocation = nextAllocOffset + totalSizeBytes; + return (void *)((uintptr_t)arena->memory + (uintptr_t)nextAllocOffset); + } + else + { + return NULL; + } +} + +/* Component Pool Backend */ + +static void BECS_ComponentPoolInit +(BECS_Arena *arena, BECS_ComponentPool *pool ,size_t componentSize, size_t capacity) +{ + pool->unitSize = componentSize; + pool->capacity = capacity; + pool->count = 0; + + pool->denseArray = BECS_AllocateMemory(arena, componentSize * capacity); + pool->denseEntityMapping = (BECS_Entity *)BECS_AllocateMemory(arena, sizeof(BECS_Entity) * capacity); + + uint32_t i; + for (i = 0; i < BECS_MAX_ENTITIES; i++) + { + pool->sparseArray[i] = UINT32_MAX; + } +} + +static void *BECS_ComponentPoolAdd(BECS_ComponentPool *pool, BECS_Entity entity) +{ + uint32_t index = pool->count; + pool->count++; + pool->denseEntityMapping[index] = entity; + pool->sparseArray[entity] = index; + + return (uint8_t *)pool->denseArray + (index * pool->unitSize); +} + +static void BECS_ComponentPoolRemove(BECS_ComponentPool *pool, BECS_Entity entity) +{ + uint32_t index = pool->sparseArray[entity]; + if (index == UINT32_MAX) + { + return; + } + + uint32_t last_index = pool->count - 1; + BECS_Entity last_entity = pool->denseEntityMapping[last_index]; + + if (index != last_index) + { + void *dst = (uint8_t *)pool->denseArray + (index * pool->unitSize); + void *src = (uint8_t *)pool->denseArray + (last_index * pool->unitSize); + memcpy(dst, src, pool->unitSize); + + pool->denseEntityMapping[index] = last_entity; + pool->sparseArray[last_entity] = index; + } + + pool->sparseArray[entity] = UINT32_MAX; +} + +static void *BECS_ComponentPoolGet(BECS_ComponentPool *pool, BECS_Entity entity) +{ + uint32_t index = pool->sparseArray[entity]; + if (index == UINT32_MAX) + { + return NULL; + } + + return (uint8_t *)pool->denseArray + (index * pool->unitSize); +} + +static bool BECS_ComponentPoolHas(BECS_ComponentPool *pool, BECS_Entity entity) +{ + return pool->sparseArray[entity] != UINT32_MAX; +} + +/* Helper API Functions */ + +size_t BECS_GetMinMemoryArenaSize(size_t *componentSizes, size_t arrayLength) +{ + assert(arrayLength < BECS_MAX_COMPONENTS); size_t totalSize = 0; totalSize += sizeof(BECS_ECS); - totalSize += sizeof(BECS_ComponentArray) * props.numComponents; - for (uint32_t i = 0; i < props.numComponents; i++) + totalSize += sizeof(BECS_ComponentPool) * BECS_MAX_COMPONENTS; + totalSize += sizeof(BECS_Entity) * BECS_MAX_ENTITIES * BECS_MAX_COMPONENTS; + + size_t sumCompSizes = 0; + uint32_t i; + for (i = 0; i < arrayLength; i++) { - totalSize += props.componentSizes[i] * props.maxEntities; - totalSize += sizeof(uint64_t); + sumCompSizes += componentSizes[i]; } + + sumCompSizes *= BECS_MAX_ENTITIES; + totalSize += sumCompSizes; + return totalSize; } -BECS_ECS *BECS_Init -(void *memory, size_t size, BECS_Properties *props) +/* World API Functions */ + +BECS_ECS BECS_InitECS(void *memory, size_t size) { - BECS_ECS *ecs = (BECS_ECS *)memory; - uint64_t *bitMasks = (uint64_t *)(ecs + 1); - BECS_ComponentArray *componentSets = (BECS_ComponentArray *)((uint8_t *)(ecs + 1) + props->maxEntities); - ecs->bitMasks = bitMasks; - ecs->componentSets = componentSets; - ecs->props = *props; + BECS_ECS ecs; + ecs.arena = BECS_CreateArena(memory, size); + ecs.entityCount = 0; + memset(ecs.entitySignatures, 0, sizeof(BECS_Signature) * BECS_MAX_ENTITIES); + memset(ecs.aliveEntites, false, sizeof(bool) * BECS_MAX_ENTITIES); + memset(ecs.freeEntities, BECS_NULL_ENTITY, sizeof(BECS_Entity) * BECS_MAX_ENTITIES); + ecs.freeEntityCount = 0; + ecs.nextEntity = 0; - for (uint32_t i = 0; i < props->maxEntities; i++) + ecs.componentTypeCount = 0; + uint32_t i; + for (i = 0; i < BECS_MAX_COMPONENTS; i++) { - ecs->bitMasks[i] = 0; + ecs.componentPools[i].denseArray = NULL; + ecs.componentPools[i].denseEntityMapping = NULL; } - for (uint32_t i = 0; i < props->numComponents; i++) - { - ecs->componentSets[i].unitSize = props->componentSizes[i]; - ecs->componentSets[i].capacity = ecs->componentSets[i].unitSize * props->maxEntities; - ecs->componentSets[i].lastEntityAdded = props->maxEntities + 1; - ecs->componentSets[i].length = 0; - } - - uint32_t index = 0; - - // FIX: This is completely fucked. I feel like I am overcomplicating this... Might be easier to use a defined constant :/ - for (uint32_t i = 0; i < props->numComponents; i++) - { - uint32_t index = i; - if (index - 1 > props->numComponents) - { - index = 1; - } - ecs->componentSets[index - 1].array = componentSets + 4; - } return ecs; } -int32_t -BECS_AddComponent(BECS_ECS *ecs, uint32_t entityID, uint64_t componentBitMask, void *component) +void BECS_ResetECS(BECS_ECS *ecs) { - int32_t result = 0; - if (componentBitMask == 0) - { - return result; - } + ecs->arena = BECS_CreateArena(ecs->arena.memory, ecs->arena.capacity); + ecs->entityCount = 0; + memset(ecs->entitySignatures, 0, sizeof(BECS_Signature) * BECS_MAX_ENTITIES); + memset(ecs->aliveEntites, false, sizeof(bool) * BECS_MAX_ENTITIES); + memset(ecs->freeEntities, BECS_NULL_ENTITY, sizeof(BECS_Entity) * BECS_MAX_ENTITIES); + ecs->freeEntityCount = 0; + ecs->nextEntity = 0; - uint32_t componentType = 0; - while (!(componentBitMask & 1)) + ecs->componentTypeCount = 0; + uint32_t i; + for (i = 0; i < BECS_MAX_COMPONENTS; i++) { - componentBitMask >>= 1; - componentType++; + ecs->componentPools[i].denseArray = NULL; + ecs->componentPools[i].denseEntityMapping = NULL; } - - ecs->bitMasks[entityID] = ecs->bitMasks[entityID] | componentBitMask; - uint32_t insertIndex = ecs->componentSets[componentType].length; - if (insertIndex > ecs->props.maxEntities) - { - return result; - } - - size_t componentSize = ecs->props.componentSizes[componentType]; - uint8_t *componentArray = (uint8_t *)ecs->componentSets[componentType].array; - uint8_t *source = (uint8_t *)component; - componentArray += componentSize; - while (componentSize > 0) - { - *componentArray = *source; - componentSize--; - } - ecs->componentSets[componentType].length++; - result = 1; - - return result; } -// int32_t -// BECS_RemoveComponent() -// { -// } -#endif -#endif +/* Entity API Functions */ -#endif +BECS_Entity BECS_EntityCreate(BECS_ECS *ecs) +{ + BECS_Entity entity = BECS_NULL_ENTITY; + + if (ecs->nextEntity + 1 > BECS_NULL_ENTITY) + { + return entity; + } + + if (ecs->freeEntityCount) + { + /* Pop entity off the back of the free list */ + entity = ecs->freeEntities[--ecs->freeEntityCount]; + ecs->entitySignatures[entity] = 0; + ecs->aliveEntites[entity] = true; + return entity; + } + + entity = ecs->nextEntity; + ecs->aliveEntites[entity] = true; + ecs->entitySignatures[entity] = 0; + ecs->nextEntity++; + ecs->entityCount++; + return entity; +} + +void BECS_EntityDestroy(BECS_ECS *ecs, BECS_Entity entity) +{ + if (!ecs->aliveEntites[entity]) + { + return; + } + + uint32_t i; + for (i = 0; i < BECS_MAX_COMPONENTS; i++) + { + BECS_Signature sig = ecs->entitySignatures[entity]; + if (BECS_SignatureHas(sig, i)) + { + BECS_ComponentRemove(ecs, entity, i); + } + } + + ecs->aliveEntites[entity] = false; + ecs->entitySignatures[entity] = 0; + ecs->freeEntities[ecs->freeEntityCount] = entity; + ecs->entityCount--; + ecs->freeEntityCount++; +} + +bool BECS_IsEntityAlive(BECS_ECS *ecs, BECS_Entity entity) +{ + return ecs->aliveEntites[entity]; +} + +/* Component API Functions */ + +BECS_ComponentID BECS_ComponentRegister(BECS_ECS *ecs, size_t componentSize, size_t maximumComponentCount) +{ + if (maximumComponentCount == 0) + { + maximumComponentCount = BECS_MAX_ENTITIES; + } + + BECS_ComponentID id = ecs->componentTypeCount; + BECS_ComponentPoolInit(&ecs->arena, &ecs->componentPools[id], + componentSize, maximumComponentCount); + ecs->componentTypeCount++; + + return id; +} + +void *BECS_ComponentAdd(BECS_ECS *ecs, BECS_Entity entity, BECS_ComponentID componentId) +{ + void *component = BECS_ComponentPoolAdd(&ecs->componentPools[componentId], entity); + ecs->entitySignatures[entity] = BECS_SignatureSet(ecs->entitySignatures[entity], componentId); + memset(component, 0, ecs->componentPools[componentId].unitSize); + return component; +} + +void BECS_ComponentRemove(BECS_ECS *ecs, BECS_Entity entity, BECS_ComponentID componentId) +{ + if (!ecs->aliveEntites[entity]) + { + return; + } + BECS_ComponentPoolRemove(&ecs->componentPools[componentId], entity); + ecs->entitySignatures[entity] &= ~(1ULL << componentId); +} + +void *BECS_ComponentGet(BECS_ECS *ecs, BECS_Entity entity, BECS_ComponentID componentId) +{ + + if (!ecs->aliveEntites[entity]) + { + return NULL; + } + return BECS_ComponentPoolGet(&ecs->componentPools[componentId], entity); +} + +bool BECS_ComponentHas(BECS_ECS *ecs, BECS_Entity entity, BECS_ComponentID componentId) +{ + if (!ecs->aliveEntites[entity]) + { + return false; + } + return BECS_SignatureHas(ecs->entitySignatures[entity], componentId); +} + +#endif /* BECS_IMPLEMENTATION */ +#endif /* BECS_H */ diff --git a/becs2.h b/becs2.h deleted file mode 100644 index 68a7ea0..0000000 --- a/becs2.h +++ /dev/null @@ -1,185 +0,0 @@ -#ifndef BECS_H -#define BECS_H - -#include -#include -#include -#include - -typedef struct Entity Entity; -typedef struct BECS_Properties BECS_Properties; -typedef struct BECS_ComponentArray BECS_ComponentArray; -typedef struct BECS_ECS BECS_ECS; - -size_t BECS_GetMinMemorySize(BECS_Properties props); -BECS_ECS *BECS_Init(void *memory, size_t size, BECS_Properties props); -void *BECS_AddComponent(BECS_ECS *ecs, uint32_t entityID, uint32_t componentIndex); -void *BECS_GetComponent(BECS_ECS *ecs, uint32_t entityID, uint32_t componentIndex); - -#ifdef MAX_ENTITIES -#ifdef NUM_COMPONENTS -#ifdef BECS_IMPLEMENTATION - -#define BECS_NULL_INDEX MAX_ENTITIES + 1 - -struct Entity { - uint64_t componentBitMask; -}; - -struct BECS_Properties { - size_t componentSizes[NUM_COMPONENTS]; -}; - -struct BECS_ComponentArray { - uint32_t lastEntityAdded; - size_t unitSize; - uint32_t sparseArray[MAX_ENTITIES]; - void *denseArray; - uint32_t denseArraylength; -}; - -struct BECS_ECS { - Entity entities[MAX_ENTITIES]; - BECS_Properties props; - BECS_ComponentArray componentArrays[NUM_COMPONENTS]; -}; - -size_t BECS_GetMinMemorySize(BECS_Properties props) -{ - size_t totalSize = 0; - totalSize += sizeof(BECS_ECS); - totalSize += sizeof(BECS_ComponentArray) * NUM_COMPONENTS; - - for (uint32_t i = 0; i < NUM_COMPONENTS; i++) - { - totalSize += props.componentSizes[i] * MAX_ENTITIES; - } - - return totalSize; -} - -BECS_ECS *BECS_Init(void *memory, size_t size, BECS_Properties props) -{ - assert(memory); - BECS_ECS *ecs = (BECS_ECS *)memory; - ecs->props = props; - - for (uint32_t i = 0; i < NUM_COMPONENTS; i++) - { - ecs->componentArrays[i].lastEntityAdded = BECS_NULL_INDEX; - ecs->componentArrays[i].unitSize = props.componentSizes[i]; - ecs->componentArrays[i].denseArraylength = 0; - for (uint32_t j = 0; j < MAX_ENTITIES; j++) - { - ecs->componentArrays[i].sparseArray[j] = BECS_NULL_INDEX; - } - } - - uintptr_t startOfComponentArrays = (uintptr_t)(ecs + 1); - for (uint32_t i = 0; i < NUM_COMPONENTS; i++) - { - startOfComponentArrays += props.componentSizes[i] * MAX_ENTITIES; - ecs->componentArrays[i].denseArray = (void *)startOfComponentArrays; - } - return ecs; -} - -void *BECS_AddComponent(BECS_ECS *ecs, uint32_t entityID, uint32_t componentIndex) -{ - if (entityID > MAX_ENTITIES) - { - return NULL; - } - - uint64_t componentBitMask = 0 | (1 << (componentIndex + 1)); - if (ecs->entities[entityID].componentBitMask & componentBitMask) - { - return NULL; - } - - ecs->entities[entityID].componentBitMask |= componentBitMask; - BECS_ComponentArray *componentArray = &ecs->componentArrays[componentIndex]; - componentArray->sparseArray[entityID] = componentArray->denseArraylength; - size_t componentSize = ecs->props.componentSizes[componentIndex]; - uint32_t componentOffset = componentSize * componentArray->sparseArray[entityID]; - void *component = (void *)((uintptr_t)componentArray->denseArray + componentOffset); - - componentArray->denseArraylength++; - componentArray->lastEntityAdded = entityID; - - return component; -} - -int32_t BECS_RemoveComponent(BECS_ECS *ecs, uint32_t entityID, uint32_t componentIndex) -{ - assert(entityID < MAX_ENTITIES); - - uint64_t componentBitMask = 0 | (1 << (componentIndex + 1)); - if (!(ecs->entities[entityID].componentBitMask & componentBitMask)) - { - return 2; - } - - BECS_ComponentArray * componentArray = &ecs->componentArrays[componentIndex]; - - if (entityID == componentArray->lastEntityAdded) - { - componentArray->sparseArray[entityID] = BECS_NULL_INDEX; - componentArray->denseArraylength--; - for (uint32_t i = 0; i < MAX_ENTITIES; i++) - { - if (componentArray->sparseArray[i] == componentArray->denseArraylength - 1) - { - componentArray->lastEntityAdded = componentArray->sparseArray[i]; - break; - } - } - - ecs->entities[entityID].componentBitMask &= ~(1 << (componentIndex + 1)); - return 0; - } - - uint32_t lastEntityAddedIndex = componentArray->sparseArray[componentArray->lastEntityAdded]; - uint32_t deletedCompIndex = componentArray->sparseArray[entityID]; - - uintptr_t denseArray = (uintptr_t)componentArray->denseArray; - - uintptr_t lastEntityAdded = denseArray + (lastEntityAddedIndex * componentArray->unitSize); - uintptr_t deletedComp = denseArray + (lastEntityAddedIndex * componentArray->unitSize); - - memcpy((void *)deletedComp, (void*)lastEntityAdded, componentArray->unitSize); - componentArray->sparseArray[entityID] = lastEntityAddedIndex; - - componentArray->denseArraylength--; - - ecs->entities[entityID].componentBitMask &= ~(1 << (componentIndex + 1)); - return 0; -} - -void *BECS_GetComponent(BECS_ECS *ecs, uint32_t entityID, uint32_t componentIndex) -{ - assert(entityID < MAX_ENTITIES); - assert(componentIndex < NUM_COMPONENTS); - - void *component = NULL; - - BECS_ComponentArray *componentArray = &ecs->componentArrays[componentIndex]; - - uint32_t componentDenseIndex = componentArray->sparseArray[entityID]; - - if (componentDenseIndex == BECS_NULL_INDEX) - { - return component; - } else { - size_t componentSize = componentArray->unitSize; - component = (void *)((uintptr_t)componentArray->denseArray + (componentSize * componentDenseIndex)); - } - - return component; -} - -#endif -#endif -#endif - -#endif diff --git a/becs3.h b/becs3.h deleted file mode 100644 index c79ccaf..0000000 --- a/becs3.h +++ /dev/null @@ -1,390 +0,0 @@ -/* - * Usage: - * - * Define BECS_MAX_ENTITIES and BECS_MAX_COMPONENTS. Otherwise they will have - * the following defaults: - * - * #define BECS_MAX_ENTITIES 4096 - * #define BECS_MAX_COMPONENTS 64 - * - * Then define BECS_IMPLEMENTATION in exactly _one_ file before including this - * file as follows: - * - * #define BECS_IMPLEMENTATION - * #include "becs.h" - */ - -#ifndef BECS_H -#define BECS_H - -#include -#include -#include -#include - -#ifdef BECS_SLOW -#include -#else -#define assert(expression) -#endif /* BECS_SLOW */ - -typedef struct BECS_Arena BECS_Arena; -typedef struct BECS_ComponentPool BECS_ComponentPool; -typedef struct BECS_ECS BECS_ECS; - -typedef uint32_t BECS_Entity; -typedef uint32_t BECS_ComponentID; - -size_t BECS_GetMinMemoryArenaSize(size_t *componentSizes, size_t arrayLength); -BECS_ECS BECS_InitECS(void *memory, size_t size); -void BECS_ResetECS(BECS_ECS *ecs); - -BECS_Entity BECS_EntityCreate(BECS_ECS *ecs); -void BECS_EntityDestroy(BECS_ECS *ecs, BECS_Entity entity); -bool BECS_IsEntityAlive(BECS_ECS *ecs, BECS_Entity entity); - -BECS_ComponentID BECS_ComponentRegister(BECS_ECS *ecs, size_t componentSize, size_t maximumComponentCount); -void *BECS_ComponentAdd(BECS_ECS *ecs, BECS_Entity entity, BECS_ComponentID componentId); -void BECS_ComponentRemove(BECS_ECS *ecs, BECS_Entity entity, BECS_ComponentID componentId); -void *BECS_ComponentGet(BECS_ECS *ecs, BECS_Entity entity, BECS_ComponentID componentId); -bool BECS_ComponentHas(BECS_ECS *ecs, BECS_Entity entity, BECS_ComponentID componentId); - -#ifdef BECS_IMPLEMENTATION - -#ifndef BECS_MAX_ENTITIES -#define BECS_MAX_ENTITIES 4096 -#endif /* BECS_MAX_ENTITIES */ - -#ifndef BECS_MAX_COMPONENTS -#define BECS_MAX_COMPONENTS 64 -#endif /* BECS_MAX_COMPONENTS */ - -#define BECS_NULL_ENTITY BECS_MAX_COMPONENTS + 1 -#define BECS_INVALID_COMPONENT_ID UINT32_MAX - -typedef uint64_t BECS_Signature; - -struct BECS_Arena { - uintptr_t nextAllocation; - size_t capacity; - void *memory; -}; - -struct BECS_ComponentPool { - size_t unitSize; - size_t capacity; - size_t count; - uint32_t sparseArray[BECS_MAX_ENTITIES]; - void *denseArray; - BECS_Entity *denseEntityMapping; -}; - -struct BECS_ECS { - BECS_Arena arena; - - /* Entity Management */ - BECS_Entity nextEntity; - uint32_t entityCount; - BECS_Signature entitySignatures[BECS_MAX_ENTITIES]; - bool aliveEntites[BECS_MAX_ENTITIES]; - - BECS_Entity freeEntities[BECS_MAX_ENTITIES]; - uint32_t freeEntityCount; - - /* Component Management */ - uint32_t componentTypeCount; - BECS_ComponentPool componentPools[BECS_MAX_COMPONENTS]; -}; - -/* Signature Helpers */ -static BECS_Signature BECS_SignatureSet(BECS_Signature sig, BECS_ComponentID componentId) -{ - return sig | (1ULL << componentId); -} - -static bool BECS_SignatureHas(BECS_Signature sig, BECS_ComponentID component_id) -{ - return (sig & (1ULL << component_id)) != 0; -} - -static bool BECS_SignatureMatches(BECS_Signature entitySig, BECS_Signature querySig) -{ - return (entitySig & querySig) == querySig; -} - -/* Memory Arena */ - -BECS_Arena BECS_CreateArena(void *memory, size_t capacity) -{ - uintptr_t baseOffset = 64 - ((uintptr_t)memory % 64); - BECS_Arena arena = { - baseOffset, - capacity, - memory - }; - return arena; -} - -static void *BECS_AllocateMemory(BECS_Arena *arena, size_t size) -{ - size_t totalSizeBytes = size; - uintptr_t nextAllocOffset = arena->nextAllocation + ((64 - (arena->nextAllocation % 64)) & 63); - if (nextAllocOffset + totalSizeBytes <= arena->capacity) - { - arena->nextAllocation = nextAllocOffset + totalSizeBytes; - return (void *)((uintptr_t)arena->memory + (uintptr_t)nextAllocOffset); - } - else - { - return NULL; - } -} - -/* Component Pool Backend */ - -static void BECS_ComponentPoolInit -(BECS_Arena *arena, BECS_ComponentPool *pool ,size_t componentSize, size_t capacity) -{ - pool->unitSize = componentSize; - pool->capacity = capacity; - pool->count = 0; - - pool->denseArray = BECS_AllocateMemory(arena, componentSize * capacity); - pool->denseEntityMapping = (BECS_Entity *)BECS_AllocateMemory(arena, sizeof(BECS_Entity) * capacity); - - uint32_t i; - for (i = 0; i < BECS_MAX_ENTITIES; i++) - { - pool->sparseArray[i] = UINT32_MAX; - } -} - -static void *BECS_ComponentPoolAdd(BECS_ComponentPool *pool, BECS_Entity entity) -{ - uint32_t index = pool->count; - pool->count++; - pool->denseEntityMapping[index] = entity; - pool->sparseArray[entity] = index; - - return (uint8_t *)pool->denseArray + (index * pool->unitSize); -} - -static void BECS_ComponentPoolRemove(BECS_ComponentPool *pool, BECS_Entity entity) -{ - uint32_t index = pool->sparseArray[entity]; - if (index == UINT32_MAX) - { - return; - } - - uint32_t last_index = pool->count - 1; - BECS_Entity last_entity = pool->denseEntityMapping[last_index]; - - if (index != last_index) - { - void *dst = (uint8_t *)pool->denseArray + (index * pool->unitSize); - void *src = (uint8_t *)pool->denseArray + (last_index * pool->unitSize); - memcpy(dst, src, pool->unitSize); - - pool->denseEntityMapping[index] = last_entity; - pool->sparseArray[last_entity] = index; - } - - pool->sparseArray[entity] = UINT32_MAX; -} - -static void *BECS_ComponentPoolGet(BECS_ComponentPool *pool, BECS_Entity entity) -{ - uint32_t index = pool->sparseArray[entity]; - if (index == UINT32_MAX) - { - return NULL; - } - - return (uint8_t *)pool->denseArray + (index * pool->unitSize); -} - -static bool BECS_ComponentPoolHas(BECS_ComponentPool *pool, BECS_Entity entity) -{ - return pool->sparseArray[entity] != UINT32_MAX; -} - -/* Helper API Functions */ - -size_t BECS_GetMinMemoryArenaSize(size_t *componentSizes, size_t arrayLength) -{ - assert(arrayLength < BECS_MAX_COMPONENTS); - size_t totalSize = 0; - totalSize += sizeof(BECS_ECS); - totalSize += sizeof(BECS_ComponentPool) * BECS_MAX_COMPONENTS; - totalSize += sizeof(BECS_Entity) * BECS_MAX_ENTITIES * BECS_MAX_COMPONENTS; - - size_t sumCompSizes = 0; - uint32_t i; - for (i = 0; i < arrayLength; i++) - { - sumCompSizes += componentSizes[i]; - } - - sumCompSizes *= BECS_MAX_ENTITIES; - totalSize += sumCompSizes; - - return totalSize; -} - -/* World API Functions */ - -BECS_ECS BECS_InitECS(void *memory, size_t size) -{ - BECS_ECS ecs; - ecs.arena = BECS_CreateArena(memory, size); - ecs.entityCount = 0; - memset(ecs.entitySignatures, 0, sizeof(BECS_Signature) * BECS_MAX_ENTITIES); - memset(ecs.aliveEntites, false, sizeof(bool) * BECS_MAX_ENTITIES); - memset(ecs.freeEntities, BECS_NULL_ENTITY, sizeof(BECS_Entity) * BECS_MAX_ENTITIES); - ecs.freeEntityCount = 0; - ecs.nextEntity = 0; - - ecs.componentTypeCount = 0; - uint32_t i; - for (i = 0; i < BECS_MAX_COMPONENTS; i++) - { - ecs.componentPools[i].denseArray = NULL; - ecs.componentPools[i].denseEntityMapping = NULL; - } - - return ecs; -} - -void BECS_ResetECS(BECS_ECS *ecs) -{ - ecs->arena = BECS_CreateArena(ecs->arena.memory, ecs->arena.capacity); - ecs->entityCount = 0; - memset(ecs->entitySignatures, 0, sizeof(BECS_Signature) * BECS_MAX_ENTITIES); - memset(ecs->aliveEntites, false, sizeof(bool) * BECS_MAX_ENTITIES); - memset(ecs->freeEntities, BECS_NULL_ENTITY, sizeof(BECS_Entity) * BECS_MAX_ENTITIES); - ecs->freeEntityCount = 0; - ecs->nextEntity = 0; - - ecs->componentTypeCount = 0; - uint32_t i; - for (i = 0; i < BECS_MAX_COMPONENTS; i++) - { - ecs->componentPools[i].denseArray = NULL; - ecs->componentPools[i].denseEntityMapping = NULL; - } -} - -/* Entity API Functions */ - -BECS_Entity BECS_EntityCreate(BECS_ECS *ecs) -{ - BECS_Entity entity = BECS_NULL_ENTITY; - - if (ecs->nextEntity + 1 > BECS_NULL_ENTITY) - { - return entity; - } - - if (ecs->freeEntityCount) - { - /* Pop entity off the back of the free list */ - entity = ecs->freeEntities[--ecs->freeEntityCount]; - ecs->entitySignatures[entity] = 0; - ecs->aliveEntites[entity] = true; - return entity; - } - - entity = ecs->nextEntity; - ecs->aliveEntites[entity] = true; - ecs->entitySignatures[entity] = 0; - ecs->nextEntity++; - ecs->entityCount++; - return entity; -} - -void BECS_EntityDestroy(BECS_ECS *ecs, BECS_Entity entity) -{ - if (!ecs->aliveEntites[entity]) - { - return; - } - - uint32_t i; - for (i = 0; i < BECS_MAX_COMPONENTS; i++) - { - BECS_Signature sig = ecs->entitySignatures[entity]; - if (BECS_SignatureHas(sig, i)) - { - BECS_ComponentRemove(ecs, entity, i); - } - } - - ecs->aliveEntites[entity] = false; - ecs->entitySignatures[entity] = 0; - ecs->freeEntities[ecs->freeEntityCount] = entity; - ecs->entityCount--; - ecs->freeEntityCount++; -} - -bool BECS_IsEntityAlive(BECS_ECS *ecs, BECS_Entity entity) -{ - return ecs->aliveEntites[entity]; -} - -/* Component API Functions */ - -BECS_ComponentID BECS_ComponentRegister(BECS_ECS *ecs, size_t componentSize, size_t maximumComponentCount) -{ - if (maximumComponentCount == 0) - { - maximumComponentCount = BECS_MAX_ENTITIES; - } - - BECS_ComponentID id = ecs->componentTypeCount; - BECS_ComponentPoolInit(&ecs->arena, &ecs->componentPools[id], - componentSize, maximumComponentCount); - ecs->componentTypeCount++; - - return id; -} - -void *BECS_ComponentAdd(BECS_ECS *ecs, BECS_Entity entity, BECS_ComponentID componentId) -{ - void *component = BECS_ComponentPoolAdd(&ecs->componentPools[componentId], entity); - ecs->entitySignatures[entity] = BECS_SignatureSet(ecs->entitySignatures[entity], componentId); - memset(component, 0, ecs->componentPools[componentId].unitSize); - return component; -} - -void BECS_ComponentRemove(BECS_ECS *ecs, BECS_Entity entity, BECS_ComponentID componentId) -{ - if (!ecs->aliveEntites[entity]) - { - return; - } - BECS_ComponentPoolRemove(&ecs->componentPools[componentId], entity); - ecs->entitySignatures[entity] &= ~(1ULL << componentId); -} - -void *BECS_ComponentGet(BECS_ECS *ecs, BECS_Entity entity, BECS_ComponentID componentId) -{ - - if (!ecs->aliveEntites[entity]) - { - return NULL; - } - return BECS_ComponentPoolGet(&ecs->componentPools[componentId], entity); -} - -bool BECS_ComponentHas(BECS_ECS *ecs, BECS_Entity entity, BECS_ComponentID componentId) -{ - if (!ecs->aliveEntites[entity]) - { - return false; - } - return BECS_SignatureHas(ecs->entitySignatures[entity], componentId); -} - -#endif /* BECS_IMPLEMENTATION */ -#endif /* BECS_H */