From 025510f2928e123d12a7d6d3574ca70fdd9d7717 Mon Sep 17 00:00:00 2001 From: quou Date: Sat, 18 Jan 2025 16:19:34 +1100 Subject: ECS --- world.cpp | 204 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 world.cpp (limited to 'world.cpp') diff --git a/world.cpp b/world.cpp new file mode 100644 index 0000000..5cfd8bf --- /dev/null +++ b/world.cpp @@ -0,0 +1,204 @@ +#include "world.hpp" +extern "C" { +#include "memory.h" +#include "plat.h" +} + +#include + +static int component_sizes[max_components]; + +void Pool::init(Arena* a, Component_Mask m) { + int i, coff; + next = 0; + size = 0; + mask = m; + count = 0; + for (i = 0, coff = 0; i < max_components; i++) { + if (m & ((Component_Mask)1 << i)) { + offsets[i] = coff; + coff += component_sizes[i]; + } + } + size = coff; + data = arena_alloc(a, size * pool_cap); +} + +void* Pool::add(Arena* a, Entity_Id eid) { + int idx; + if (next) + return next->add(a, eid); + if (count >= pool_cap) { + next = (Pool*)arena_alloc(a, sizeof *next); + next->init(a, mask); + return next->add(a, eid); + } + idx = count++; + entities[idx] = eid; + mapping[entity_index(eid)] = idx; + return &((char*)data)[idx * size]; +} + +void Pool::remove(Entity_Id eid) { + int eind = entity_index(eid); + int end = count - 1; + assert(eid == entities[mapping[eind]]); + memmove( + (char*)data + eind * size, + (char*)data + end * size, + size + ); + entities[eind] = entities[count]; + mapping[end] = eind; + count = end; +} + +void* Pool::get(Entity_Id eid, int cid) { + int eind = entity_index(eid); + char* e = &((char*)data)[mapping[eind] * size]; + assert(eid == entities[mapping[eind]]); + return &e[offsets[cid]]; +} + +void* Pool::get(Entity_Id eid) { + int eind = entity_index(eid); + void* e = &((char*)data)[mapping[eind] * size]; + assert(eid == entities[mapping[eind]]); + return e; +} + +int get_new_component_id(int size) { + static int id = 0; + int i = id++; + component_sizes[i] = size; + return i; +} + +int entity_version(Entity_Id e) { + return e & 0xff; +} + +int entity_index(Entity_Id e) { + return (e >> 8) & 0xffffff; +} + +Entity_Id entity_id(int index, int version) { + return ((index & 0xffffff) << 8) | (version & 0xff); +} + +void World::init(Arena* a) { + int i; + arena = a; + count = 0; + free_count = 0; + for (i = 0; i < max_entities; i++) + versions[i] = 1; + for (i = 0; i < max_pools; i++) + pools[i].mask = 0; +} + +uint64_t World::hash_mask(Component_Mask m) { + uint64_t h = (uint64_t)m; + h = (h ^ (h >> 30)) * UINT64_C(0xbf58476d1ce4e5b9); + h = (h ^ (h >> 27)) * UINT64_C(0x94d049bb133111eb); + h = (h ^ (h >> 31)); + return h; +} + +Pool& World::get_pool(Component_Mask m) { + int i, idx; + idx = (int)(hash_mask(m) % max_pools); + for (i = 0; i < max_pools; i++) { + Pool& p = pools[idx]; + if (!p.mask) { + p.init(arena, m); + return p; + } else if (p.mask == m) + return p; + idx++; + idx %= max_pools; + } + assert(0); + return pools[0]; +} + +Entity_Id World::create_entity() { + Entity_Id e; + int ind; + assert(count < max_entities); + if (free_count) + ind = entity_index(freelist[--free_count]); + else + ind = count++; + e = entity_id(ind, versions[ind]); + masks[ind] = 0; + entities[ind] = e; + return e; +} + +void* World::add(Entity_Id eid, Component_Mask m) { + int eind = entity_index(eid); + int i; + assert(entity_version(eid) == versions[eind]); + Component_Mask om = masks[eind]; + Component_Mask tm = om | m; + assert(om != tm); + Pool& p = get_pool(tm); + p.add(arena, eid); + if (om) { + Pool& op = get_pool(om); + for (i = 0; i < max_components; i++) { + if (om & ((Component_Mask)1 << i)) { + memcpy( + p.get(eid, i), + op.get(eid, i), + component_sizes[i] + ); + } + } + op.remove(eid); + } + masks[eind] = tm; + return p.get(eid); +} + +void* World::get(Entity_Id eid, int cid) { + int eind = entity_index(eid); + Pool& p = get_pool(masks[eind]); + assert(versions[eind] == entity_version(eid)); + assert(p.entities[p.mapping[eind]] == eid); + return p.get(eid, cid); +} + +void World::remove(Entity_Id eid, int cid) { + int eind = entity_index(eid), i; + Component_Mask om = masks[eind]; + Component_Mask nm = om & ~((Component_Mask)1 << cid); + Pool& s = get_pool(om); + if (nm) { + Pool& d = get_pool(nm); + for (i = 0; i < max_components; i++) { + if (nm & ((uint64_t)1 << i)) { + memcpy( + d.get(eid, i), + s.get(eid, i), + component_sizes[i] + ); + } + } + s.remove(eid); + } + s.remove(eid); + masks[eind] = nm; +} + +void World::destroy(Entity_Id e) { + int eind = entity_index(e); + Component_Mask m = masks[eind]; + Pool& p = get_pool(m); + p.remove(e); + assert(free_count < max_entities); + freelist[free_count++] = e; + versions[entity_index(e)]++; +} + -- cgit v1.2.3-54-g00ecf