diff options
author | quou <quou@disroot.org> | 2025-01-18 16:19:34 +1100 |
---|---|---|
committer | quou <quou@disroot.org> | 2025-01-18 16:19:34 +1100 |
commit | 025510f2928e123d12a7d6d3574ca70fdd9d7717 (patch) | |
tree | 5295711f97c7a437c2b1192534ad62f98e89c177 | |
parent | 9c9073374e1a8a21f6f46f6abfc5be853dbf3b45 (diff) |
ECS
-rw-r--r-- | Makefile | 10 | ||||
-rw-r--r-- | configure.lua | 1 | ||||
-rw-r--r-- | world.cpp | 204 | ||||
-rw-r--r-- | world.hpp | 178 |
4 files changed, 389 insertions, 4 deletions
@@ -62,8 +62,10 @@ ui.o: ui.cpp g++ -std=c++20 $(opt_com) $(cflags) -c ui.cpp -o ui.o video.o: video.cpp g++ -std=c++20 $(opt_com) $(cflags) -c video.cpp -o video.o -c2: app.o asset.o c2.o camera.o debugdraw.o editor.o maths.o model.o pipeline.o ui.o video.o libqstd.a - g++ $(opt_lnk) $(lflags) -o c2 app.o asset.o c2.o camera.o debugdraw.o editor.o maths.o model.o pipeline.o ui.o video.o libqstd.a -lX11 -lm +world.o: world.cpp + g++ -std=c++20 $(opt_com) $(cflags) -c world.cpp -o world.o +c2: app.o asset.o c2.o camera.o debugdraw.o editor.o maths.o model.o pipeline.o ui.o video.o world.o libqstd.a + g++ $(opt_lnk) $(lflags) -o c2 app.o asset.o c2.o camera.o debugdraw.o editor.o maths.o model.o pipeline.o ui.o video.o world.o libqstd.a -lX11 -lm c2: pack @@ -129,10 +131,10 @@ pack: packer data/debug.csh data/mip_spec.csh data/sky.csh data/surface.csh data data: mkdir -p data --include qstd/memory.d qstd/plat.d qstd/str.d qstd/pack.d cfg/cfgparse.d sc/sc.d sc/includer.d app.d asset.d c2.d camera.d debugdraw.d editor.d maths.d model.d pipeline.d ui.d video.d convtexture.d convmodel.d convmaterial.d packer.d +-include qstd/memory.d qstd/plat.d qstd/str.d qstd/pack.d cfg/cfgparse.d sc/sc.d sc/includer.d app.d asset.d c2.d camera.d debugdraw.d editor.d maths.d model.d pipeline.d ui.d video.d world.d convtexture.d convmodel.d convmaterial.d packer.d clean: - rm -f qstd/memory.o qstd/plat.o qstd/str.o qstd/pack.o cfg/cfgparse.o sc/sc.o sc/includer.o app.o asset.o c2.o camera.o debugdraw.o editor.o maths.o model.o pipeline.o ui.o video.o convtexture.o convmodel.o convmaterial.o packer.o qstd/memory.d qstd/plat.d qstd/str.d qstd/pack.d cfg/cfgparse.d sc/sc.d sc/includer.d app.d asset.d c2.d camera.d debugdraw.d editor.d maths.d model.d pipeline.d ui.d video.d convtexture.d convmodel.d convmaterial.d packer.d data/debug.csh data/mip_spec.csh data/sky.csh data/surface.csh data/surface_depthonly.csh data/triangle.csh data/ui.csh data/monkey.mdl data/22.tex data/kita.tex data/brick_albedo.tex data/brick_ao.tex data/brick_normal.tex data/sky.tex data/bricks.mat data/plastic.mat + rm -f qstd/memory.o qstd/plat.o qstd/str.o qstd/pack.o cfg/cfgparse.o sc/sc.o sc/includer.o app.o asset.o c2.o camera.o debugdraw.o editor.o maths.o model.o pipeline.o ui.o video.o world.o convtexture.o convmodel.o convmaterial.o packer.o qstd/memory.d qstd/plat.d qstd/str.d qstd/pack.d cfg/cfgparse.d sc/sc.d sc/includer.d app.d asset.d c2.d camera.d debugdraw.d editor.d maths.d model.d pipeline.d ui.d video.d world.d convtexture.d convmodel.d convmaterial.d packer.d data/debug.csh data/mip_spec.csh data/sky.csh data/surface.csh data/surface_depthonly.csh data/triangle.csh data/ui.csh data/monkey.mdl data/22.tex data/kita.tex data/brick_albedo.tex data/brick_ao.tex data/brick_normal.tex data/sky.tex data/bricks.mat data/plastic.mat rm -f shadercompiler rmdir data rm -f c2 diff --git a/configure.lua b/configure.lua index c716c33..7e1d5e1 100644 --- a/configure.lua +++ b/configure.lua @@ -11,6 +11,7 @@ config = { "pipeline", "ui", "video", + "world", }, qstd = { "memory", 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 <string.h> + +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)]++; +} + diff --git a/world.hpp b/world.hpp new file mode 100644 index 0000000..267b58a --- /dev/null +++ b/world.hpp @@ -0,0 +1,178 @@ +#ifndef world_hpp +#define world_hpp + +struct Arena; + +#include <stdint.h> + +#include <new> +#include <utility> + +using Component_Mask = uint64_t; +using Entity_Id = uint32_t; + +#define max_entities 1024 +#define pool_cap 128 +#define max_components 64 + +struct Pool { + Component_Mask mask; + Pool* next; + void* data; + Entity_Id entities[pool_cap]; + int mapping[max_entities]; + int offsets[max_components]; + int size, count; + + void init(Arena* a, Component_Mask m); + void* add(Arena* a, Entity_Id eid); + void* get(Entity_Id eid, int cid); + void* get(Entity_Id eid); + void remove(Entity_Id eid); +}; + +int entity_version(Entity_Id e); +int entity_index(Entity_Id e); +Entity_Id entity_id(int index, int version); +int get_new_component_id(int size); + +struct World { + static constexpr int max_pools = 32; + Pool pools[max_pools]; + Component_Mask masks[max_entities]; + uint8_t versions[max_entities]; + Entity_Id entities[max_entities]; + Entity_Id freelist[max_entities]; + Arena* arena; + int count, free_count; + + void init(Arena* a); + uint64_t hash_mask(Component_Mask m); + Pool& get_pool(Component_Mask m); + + template <typename T> + int get_component_id() { + static int id = get_new_component_id(sizeof(T)); + return id; + } + + Entity_Id create_entity(); + + template <typename... T> + Component_Mask get_mask() { + return (... | ((Component_Mask)1 << get_component_id<T>())); + } + + template <typename... T> + std::tuple<T&...> add(Entity_Id e) { + Component_Mask m = get_mask<T...>(); + add(e, m); + Pool& p = get_pool(masks[entity_index(e)]); + return { *(T*)p.get(e, get_component_id<T>())... }; + } + + template <typename T> + T& get(Entity_Id e) { + int id = get_component_id<T>(); + return *(T*)get(e, id); + } + + template <typename T> + void remove(Entity_Id e) { + int id = get_component_id<T>(); + remove(e, id); + } + + template <typename T> + bool has(Entity_Id e) { + int id = get_component_id<T>(); + Component_Mask m = masks[entity_index(e)]; + return (m & ((Component_Mask)1 << id)) != 0; + } + + void* add(Entity_Id eid, Component_Mask m); + void* get(Entity_Id eid, int cid); + void remove(Entity_Id eid, int cid); + void destroy(Entity_Id e); + + struct View { + World* w; + Pool* pools[max_pools]; + int pool_count; + + struct Iter { + View* v; + int ptr, ind; + + bool equals(const Iter& other) { + return ptr == other.ptr && ind == other.ind; + } + bool operator==(const Iter& other) { + return equals(other); + } + bool operator!=(const Iter& other) { + return !equals(other); + } + Iter operator++() { + Pool* p = v->pools[ind]; + ptr++; + if (ptr == p->count) { + ptr = 0; + ind++; + } + return *this; + } + + template <typename C> + C& get() { + Pool* p = v->pools[ind]; + return *(C*)( + (char*)p->data + ptr * p->size + + p->offsets[v->w->get_component_id<C>()] + ); + } + + Iter& operator*() { + return *this; + } + + Entity_Id entity() { + Pool* p = v->pools[ind]; + return p->entities[ptr]; + } + }; + + Iter begin() { + Iter i; + i.v = this; + i.ind = 0; + i.ptr = 0; + return i; + } + + Iter end() { + Iter i; + i.v = this; + i.ind = pool_count; + i.ptr = 0; + return i; + } + }; + + template <typename... T> + View view() { + int i; + auto m = get_mask<T...>(); + View v; + v.w = this; + v.pool_count = 0; + for (i = 0; i < max_pools; i++) { + Pool& p = pools[i]; + if (p.count && m == (p.mask & m)) + v.pools[v.pool_count++] = &p; + } + return v; + } +}; + +#endif |