refactor: decouple sim from framework, remove voxel geometry

This commit is contained in:
asrael 2026-02-27 01:22:35 -06:00
parent c538641ec8
commit 5a565844dd
41 changed files with 477 additions and 2407 deletions

View file

@ -7,24 +7,10 @@
#include "pxl8_log.h"
#include "pxl8_mem.h"
#define PXL8_VXL_CHUNK_VOLUME (PXL8_VXL_CHUNK_SIZE * PXL8_VXL_CHUNK_SIZE * PXL8_VXL_CHUNK_SIZE)
static pxl8_world_chunk_cache_entry* find_entry_vxl(pxl8_world_chunk_cache* cache, i32 cx, i32 cy, i32 cz) {
for (u32 i = 0; i < cache->entry_count; i++) {
pxl8_world_chunk_cache_entry* e = &cache->entries[i];
if (e->valid && e->chunk && e->chunk->type == PXL8_WORLD_CHUNK_VXL &&
e->chunk->cx == cx && e->chunk->cy == cy && e->chunk->cz == cz) {
return e;
}
}
return NULL;
}
static pxl8_world_chunk_cache_entry* find_entry_bsp(pxl8_world_chunk_cache* cache, u32 id) {
for (u32 i = 0; i < cache->entry_count; i++) {
pxl8_world_chunk_cache_entry* e = &cache->entries[i];
if (e->valid && e->chunk && e->chunk->type == PXL8_WORLD_CHUNK_BSP &&
e->chunk->id == id) {
if (e->valid && e->chunk && e->chunk->id == id) {
return e;
}
}
@ -56,14 +42,14 @@ static pxl8_world_chunk_cache_entry* alloc_entry(pxl8_world_chunk_cache* cache)
}
pxl8_world_chunk_cache_entry* e = &cache->entries[slot];
if (e->chunk) pxl8_world_chunk_destroy(e->chunk);
if (e->mesh) pxl8_mesh_destroy(e->mesh);
if (e->chunk) {
pxl8_world_chunk_destroy(e->chunk);
}
memset(e, 0, sizeof(*e));
return e;
}
static void assembly_reset(pxl8_world_chunk_assembly* a) {
a->type = PXL8_WORLD_CHUNK_VXL;
a->id = 0;
a->cx = 0;
a->cy = 0;
@ -78,7 +64,6 @@ static void assembly_reset(pxl8_world_chunk_assembly* a) {
static void assembly_init(pxl8_world_chunk_assembly* a, const pxl8_chunk_msg_header* hdr) {
assembly_reset(a);
a->type = hdr->chunk_type == PXL8_CHUNK_TYPE_BSP ? PXL8_WORLD_CHUNK_BSP : PXL8_WORLD_CHUNK_VXL;
a->id = hdr->id;
a->cx = hdr->cx;
a->cy = hdr->cy;
@ -94,33 +79,6 @@ static void assembly_init(pxl8_world_chunk_assembly* a, const pxl8_chunk_msg_hea
}
}
static bool rle_decode_voxel(const u8* src, usize src_len, pxl8_vxl_chunk* chunk) {
u8* linear = pxl8_malloc(PXL8_VXL_CHUNK_VOLUME);
if (!linear) return false;
usize src_pos = 0;
usize dst_pos = 0;
while (src_pos + 1 < src_len && dst_pos < PXL8_VXL_CHUNK_VOLUME) {
u8 block = src[src_pos++];
u8 run_minus_one = src[src_pos++];
usize run = (usize)run_minus_one + 1;
for (usize i = 0; i < run && dst_pos < PXL8_VXL_CHUNK_VOLUME; i++) {
linear[dst_pos++] = block;
}
}
if (dst_pos != PXL8_VXL_CHUNK_VOLUME) {
pxl8_free(linear);
return false;
}
pxl8_vxl_chunk_from_linear(chunk, linear);
pxl8_free(linear);
return true;
}
static pxl8_result deserialize_vertex(pxl8_stream* s, pxl8_bsp_vertex* v) {
v->position.x = pxl8_read_f32_be(s);
v->position.y = pxl8_read_f32_be(s);
@ -321,34 +279,6 @@ static pxl8_bsp* assembly_to_bsp(pxl8_world_chunk_assembly* a) {
return bsp;
}
static pxl8_result assemble_vxl(pxl8_world_chunk_cache* cache, pxl8_world_chunk_assembly* a) {
pxl8_world_chunk_cache_entry* entry = find_entry_vxl(cache, a->cx, a->cy, a->cz);
if (!entry) {
entry = alloc_entry(cache);
entry->chunk = pxl8_world_chunk_create_vxl(a->cx, a->cy, a->cz);
entry->valid = true;
}
entry->chunk->version = a->version;
entry->mesh_dirty = true;
entry->last_used = cache->frame_counter;
if (entry->mesh) {
pxl8_mesh_destroy(entry->mesh);
entry->mesh = NULL;
}
pxl8_vxl_block_clear(entry->chunk->voxels);
if (!rle_decode_voxel(a->data, a->data_size, entry->chunk->voxels)) {
pxl8_error("[CLIENT] RLE decode failed for chunk (%d,%d,%d)", a->cx, a->cy, a->cz);
return PXL8_ERROR_INVALID_ARGUMENT;
}
assembly_reset(a);
return PXL8_OK;
}
static pxl8_result assemble_bsp(pxl8_world_chunk_cache* cache, pxl8_world_chunk_assembly* a) {
pxl8_debug("[CLIENT] assemble_bsp: id=%u data_size=%zu", a->id, a->data_size);
pxl8_bsp* bsp = assembly_to_bsp(a);
@ -391,7 +321,6 @@ void pxl8_world_chunk_cache_destroy(pxl8_world_chunk_cache* cache) {
for (u32 i = 0; i < cache->entry_count; i++) {
pxl8_world_chunk_cache_entry* e = &cache->entries[i];
if (e->chunk) pxl8_world_chunk_destroy(e->chunk);
if (e->mesh) pxl8_mesh_destroy(e->mesh);
}
pxl8_free(cache->assembly.data);
@ -406,9 +335,7 @@ pxl8_result pxl8_world_chunk_cache_receive(pxl8_world_chunk_cache* cache,
pxl8_world_chunk_assembly* a = &cache->assembly;
bool new_assembly = !a->active ||
(hdr->chunk_type == PXL8_CHUNK_TYPE_BSP && a->id != hdr->id) ||
(hdr->chunk_type == PXL8_CHUNK_TYPE_VXL &&
(a->cx != hdr->cx || a->cy != hdr->cy || a->cz != hdr->cz)) ||
a->id != hdr->id ||
a->version != hdr->version;
if (new_assembly) {
@ -436,26 +363,12 @@ pxl8_result pxl8_world_chunk_cache_receive(pxl8_world_chunk_cache* cache,
if (hdr->flags & PXL8_CHUNK_FLAG_FINAL) {
a->complete = true;
if (a->type == PXL8_WORLD_CHUNK_BSP) {
return assemble_bsp(cache, a);
} else {
return assemble_vxl(cache, a);
}
return assemble_bsp(cache, a);
}
return PXL8_OK;
}
pxl8_world_chunk* pxl8_world_chunk_cache_get_vxl(pxl8_world_chunk_cache* cache, i32 cx, i32 cy, i32 cz) {
if (!cache) return NULL;
pxl8_world_chunk_cache_entry* e = find_entry_vxl(cache, cx, cy, cz);
if (e) {
e->last_used = cache->frame_counter;
return e->chunk;
}
return NULL;
}
pxl8_world_chunk* pxl8_world_chunk_cache_get_bsp(pxl8_world_chunk_cache* cache, u32 id) {
if (!cache) return NULL;
pxl8_world_chunk_cache_entry* e = find_entry_bsp(cache, id);
@ -466,84 +379,7 @@ pxl8_world_chunk* pxl8_world_chunk_cache_get_bsp(pxl8_world_chunk_cache* cache,
return NULL;
}
pxl8_mesh* pxl8_world_chunk_cache_get_mesh(pxl8_world_chunk_cache* cache,
i32 cx, i32 cy, i32 cz,
const pxl8_vxl_block_registry* registry,
const pxl8_vxl_mesh_config* config) {
if (!cache || !registry) return NULL;
pxl8_world_chunk_cache_entry* entry = find_entry_vxl(cache, cx, cy, cz);
if (!entry || !entry->chunk || !entry->chunk->voxels) return NULL;
pxl8_world_chunk* nx = pxl8_world_chunk_cache_get_vxl(cache, cx - 1, cy, cz);
pxl8_world_chunk* px = pxl8_world_chunk_cache_get_vxl(cache, cx + 1, cy, cz);
pxl8_world_chunk* ny = pxl8_world_chunk_cache_get_vxl(cache, cx, cy - 1, cz);
pxl8_world_chunk* py = pxl8_world_chunk_cache_get_vxl(cache, cx, cy + 1, cz);
pxl8_world_chunk* nz = pxl8_world_chunk_cache_get_vxl(cache, cx, cy, cz - 1);
pxl8_world_chunk* pz = pxl8_world_chunk_cache_get_vxl(cache, cx, cy, cz + 1);
bool all_neighbors = nx && px && ny && py && nz && pz;
if (entry->mesh && !entry->mesh_dirty) {
if (entry->has_all_neighbors == all_neighbors) {
return entry->mesh;
}
}
if (entry->mesh) {
pxl8_mesh_destroy(entry->mesh);
entry->mesh = NULL;
}
const pxl8_vxl_chunk* neighbors[6] = {
nx ? nx->voxels : NULL,
px ? px->voxels : NULL,
ny ? ny->voxels : NULL,
py ? py->voxels : NULL,
nz ? nz->voxels : NULL,
pz ? pz->voxels : NULL
};
pxl8_vxl_mesh_config local_config = config ? *config : PXL8_VXL_MESH_CONFIG_DEFAULT;
entry->mesh = pxl8_vxl_build_mesh(entry->chunk->voxels, neighbors, registry, &local_config);
entry->mesh_dirty = false;
entry->has_all_neighbors = all_neighbors;
return entry->mesh;
}
void pxl8_world_chunk_cache_tick(pxl8_world_chunk_cache* cache) {
if (!cache) return;
cache->frame_counter++;
}
void pxl8_world_chunk_cache_evict_distant(pxl8_world_chunk_cache* cache,
i32 cx, i32 cy, i32 cz, i32 radius) {
if (!cache) return;
for (u32 i = 0; i < cache->entry_count; i++) {
pxl8_world_chunk_cache_entry* e = &cache->entries[i];
if (!e->valid || !e->chunk || e->chunk->type != PXL8_WORLD_CHUNK_VXL) continue;
i32 dx = e->chunk->cx - cx;
i32 dy = e->chunk->cy - cy;
i32 dz = e->chunk->cz - cz;
if (abs(dx) > radius || abs(dy) > radius || abs(dz) > radius) {
pxl8_world_chunk_destroy(e->chunk);
if (e->mesh) pxl8_mesh_destroy(e->mesh);
e->chunk = NULL;
e->mesh = NULL;
e->valid = false;
}
}
}
void pxl8_world_chunk_cache_invalidate_meshes(pxl8_world_chunk_cache* cache) {
if (!cache) return;
for (u32 i = 0; i < cache->entry_count; i++) {
cache->entries[i].mesh_dirty = true;
}
}