/* * Usage: * Define BECS_IMPLEMENTATION in exactly _one_ file before including this file * * Eg: * #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; 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 */ 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 ecs_signature_has(BECS_Signature sig, BECS_ComponentID component_id) { return (sig & (1ULL << component_id)) != 0; } static bool ecs_signature_matches(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; } /* 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, 0, sizeof(bool) * BECS_MAX_ENTITIES); memset(ecs.freeEntities, BECS_NULL_ENTITY, sizeof(BECS_Entity) * BECS_MAX_ENTITIES); ecs.freeEntityCount = 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, 0, sizeof(bool) * BECS_MAX_ENTITIES); memset(ecs->freeEntities, BECS_NULL_ENTITY, sizeof(BECS_Entity) * BECS_MAX_ENTITIES); ecs->freeEntityCount = 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 */ /* TODO: Implement Entity API Functions 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) { } */ /* 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 ecs_signature_has(ecs->entitySignatures[entity], componentId); } #endif /* BECS_IMPLEMENTATION */ #endif /* BECS_H */