refactor: decouple sim from framework, remove voxel geometry
This commit is contained in:
parent
c538641ec8
commit
5a565844dd
41 changed files with 477 additions and 2407 deletions
|
|
@ -15,28 +15,20 @@
|
|||
#include "pxl8_log.h"
|
||||
#include "pxl8_mem.h"
|
||||
#include "pxl8_sim.h"
|
||||
#include "pxl8_vxl.h"
|
||||
#include "pxl8_vxl_render.h"
|
||||
|
||||
#define PXL8_WORLD_ENTITY_CAPACITY 256
|
||||
#define VOXEL_SCALE 16.0f
|
||||
#define VOXEL_CHUNK_SIZE 32.0f
|
||||
#define WORLD_CHUNK_SIZE (VOXEL_CHUNK_SIZE * VOXEL_SCALE)
|
||||
|
||||
struct pxl8_world {
|
||||
pxl8_world_chunk* active_chunk;
|
||||
pxl8_vxl_block_registry* block_registry;
|
||||
pxl8_world_chunk_cache* chunk_cache;
|
||||
pxl8_entity_pool* entities;
|
||||
pxl8_bsp_render_state* bsp_render_state;
|
||||
pxl8_vxl_render_state* vxl_render_state;
|
||||
i32 render_distance;
|
||||
i32 sim_distance;
|
||||
|
||||
pxl8_sim_entity local_player;
|
||||
u64 client_tick;
|
||||
|
||||
pxl8_vec2 pointer_motion;
|
||||
pxl8_sim_config sim_config;
|
||||
|
||||
#ifdef PXL8_ASYNC_THREADS
|
||||
pxl8_sim_entity render_state[2];
|
||||
|
|
@ -54,14 +46,22 @@ pxl8_world* pxl8_world_create(void) {
|
|||
pxl8_world* world = pxl8_calloc(1, sizeof(pxl8_world));
|
||||
if (!world) return NULL;
|
||||
|
||||
world->block_registry = pxl8_vxl_block_registry_create();
|
||||
world->chunk_cache = pxl8_world_chunk_cache_create();
|
||||
world->entities = pxl8_entity_pool_create(PXL8_WORLD_ENTITY_CAPACITY);
|
||||
world->vxl_render_state = pxl8_vxl_render_state_create();
|
||||
world->render_distance = 3;
|
||||
world->sim_distance = 4;
|
||||
world->sim_config = (pxl8_sim_config){
|
||||
.move_speed = 180.0f,
|
||||
.ground_accel = 10.0f,
|
||||
.air_accel = 1.0f,
|
||||
.stop_speed = 100.0f,
|
||||
.friction = 6.0f,
|
||||
.gravity = 800.0f,
|
||||
.jump_velocity = 200.0f,
|
||||
.player_radius = 16.0f,
|
||||
.player_height = 72.0f,
|
||||
.max_pitch = 1.5f,
|
||||
};
|
||||
|
||||
if (!world->block_registry || !world->chunk_cache || !world->entities || !world->vxl_render_state) {
|
||||
if (!world->chunk_cache || !world->entities) {
|
||||
pxl8_world_destroy(world);
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -72,11 +72,9 @@ pxl8_world* pxl8_world_create(void) {
|
|||
void pxl8_world_destroy(pxl8_world* world) {
|
||||
if (!world) return;
|
||||
|
||||
pxl8_vxl_block_registry_destroy(world->block_registry);
|
||||
pxl8_world_chunk_cache_destroy(world->chunk_cache);
|
||||
pxl8_entity_pool_destroy(world->entities);
|
||||
pxl8_bsp_render_state_destroy(world->bsp_render_state);
|
||||
pxl8_vxl_render_state_destroy(world->vxl_render_state);
|
||||
pxl8_free(world);
|
||||
}
|
||||
|
||||
|
|
@ -95,11 +93,6 @@ void pxl8_world_set_active_chunk(pxl8_world* world, pxl8_world_chunk* chunk) {
|
|||
world->active_chunk = chunk;
|
||||
}
|
||||
|
||||
pxl8_vxl_block_registry* pxl8_world_block_registry(pxl8_world* world) {
|
||||
if (!world) return NULL;
|
||||
return world->block_registry;
|
||||
}
|
||||
|
||||
pxl8_entity_pool* pxl8_world_entities(pxl8_world* world) {
|
||||
if (!world) return NULL;
|
||||
return world->entities;
|
||||
|
|
@ -110,53 +103,12 @@ pxl8_entity pxl8_world_spawn(pxl8_world* world) {
|
|||
return pxl8_entity_spawn(world->entities);
|
||||
}
|
||||
|
||||
static bool voxel_point_solid(pxl8_world_chunk_cache* cache, f32 x, f32 y, f32 z) {
|
||||
i32 cx = (i32)floorf(x / WORLD_CHUNK_SIZE);
|
||||
i32 cy = (i32)floorf(y / WORLD_CHUNK_SIZE);
|
||||
i32 cz = (i32)floorf(z / WORLD_CHUNK_SIZE);
|
||||
|
||||
f32 local_x = (x - cx * WORLD_CHUNK_SIZE) / VOXEL_SCALE;
|
||||
f32 local_y = (y - cy * WORLD_CHUNK_SIZE) / VOXEL_SCALE;
|
||||
f32 local_z = (z - cz * WORLD_CHUNK_SIZE) / VOXEL_SCALE;
|
||||
|
||||
i32 lx = (i32)floorf(local_x);
|
||||
i32 ly = (i32)floorf(local_y);
|
||||
i32 lz = (i32)floorf(local_z);
|
||||
|
||||
lx = lx < 0 ? 0 : (lx >= 32 ? 31 : lx);
|
||||
ly = ly < 0 ? 0 : (ly >= 32 ? 31 : ly);
|
||||
lz = lz < 0 ? 0 : (lz >= 32 ? 31 : lz);
|
||||
|
||||
pxl8_world_chunk* chunk = pxl8_world_chunk_cache_get_vxl(cache, cx, cy, cz);
|
||||
if (!chunk || !chunk->voxels) {
|
||||
return ly < 8;
|
||||
}
|
||||
|
||||
return pxl8_vxl_block_get(chunk->voxels, lx, ly, lz) != PXL8_VXL_BLOCK_AIR;
|
||||
}
|
||||
|
||||
static pxl8_sim_world make_sim_world(const pxl8_world* world, pxl8_vec3 pos) {
|
||||
pxl8_sim_world pxl8_world_sim_world(const pxl8_world* world, pxl8_vec3 pos) {
|
||||
(void)pos;
|
||||
pxl8_sim_world sim = {0};
|
||||
|
||||
if (world->active_chunk && world->active_chunk->type == PXL8_WORLD_CHUNK_BSP) {
|
||||
if (world->active_chunk && world->active_chunk->bsp) {
|
||||
sim.bsp = world->active_chunk->bsp;
|
||||
return sim;
|
||||
}
|
||||
|
||||
if (world->chunk_cache) {
|
||||
i32 cx = (i32)floorf(pos.x / WORLD_CHUNK_SIZE);
|
||||
i32 cy = (i32)floorf(pos.y / WORLD_CHUNK_SIZE);
|
||||
i32 cz = (i32)floorf(pos.z / WORLD_CHUNK_SIZE);
|
||||
|
||||
pxl8_world_chunk* chunk = pxl8_world_chunk_cache_get_vxl(world->chunk_cache, cx, cy, cz);
|
||||
if (chunk && chunk->voxels) {
|
||||
sim.vxl = chunk->voxels;
|
||||
sim.vxl_cx = cx;
|
||||
sim.vxl_cy = cy;
|
||||
sim.vxl_cz = cz;
|
||||
}
|
||||
}
|
||||
|
||||
return sim;
|
||||
}
|
||||
|
||||
|
|
@ -191,12 +143,8 @@ static void userdata_to_entity(const u8* userdata, pxl8_sim_entity* ent) {
|
|||
bool pxl8_world_point_solid(const pxl8_world* world, f32 x, f32 y, f32 z) {
|
||||
if (!world) return false;
|
||||
|
||||
if (world->active_chunk) {
|
||||
if (world->active_chunk->type == PXL8_WORLD_CHUNK_BSP && world->active_chunk->bsp) {
|
||||
return pxl8_bsp_point_solid(world->active_chunk->bsp, (pxl8_vec3){x, y, z});
|
||||
}
|
||||
} else if (world->chunk_cache) {
|
||||
return voxel_point_solid(world->chunk_cache, x, y, z);
|
||||
if (world->active_chunk && world->active_chunk->bsp) {
|
||||
return pxl8_bsp_point_solid(world->active_chunk->bsp, (pxl8_vec3){x, y, z});
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
@ -290,99 +238,30 @@ pxl8_ray pxl8_world_sweep(const pxl8_world* world, pxl8_vec3 from, pxl8_vec3 to,
|
|||
return result;
|
||||
}
|
||||
|
||||
void pxl8_world_update(pxl8_world* world, const pxl8_input_state* input, f32 dt) {
|
||||
void pxl8_world_update(pxl8_world* world, f32 dt) {
|
||||
(void)dt;
|
||||
if (!world) return;
|
||||
|
||||
pxl8_world_chunk_cache_tick(world->chunk_cache);
|
||||
|
||||
if (input && (world->local_player.flags & PXL8_SIM_FLAG_ALIVE)) {
|
||||
pxl8_input_msg msg = {0};
|
||||
msg.move_x = (pxl8_key_down(input, "d") ? 1.0f : 0.0f) - (pxl8_key_down(input, "a") ? 1.0f : 0.0f);
|
||||
msg.move_y = (pxl8_key_down(input, "w") ? 1.0f : 0.0f) - (pxl8_key_down(input, "s") ? 1.0f : 0.0f);
|
||||
msg.look_dx = (f32)pxl8_mouse_dx(input);
|
||||
msg.look_dy = (f32)pxl8_mouse_dy(input);
|
||||
msg.buttons = pxl8_key_down(input, "space") ? 1 : 0;
|
||||
msg.tick = world->client_tick;
|
||||
msg.timestamp = pxl8_get_ticks_ns();
|
||||
|
||||
if (world->net) {
|
||||
pxl8_net_send_input(world->net, &msg);
|
||||
}
|
||||
|
||||
pxl8_sim_world sim = make_sim_world(world, world->local_player.pos);
|
||||
pxl8_sim_move_player(&world->local_player, &msg, &sim, dt);
|
||||
world->client_tick++;
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos) {
|
||||
if (!world || !gfx) return;
|
||||
|
||||
if (world->active_chunk) {
|
||||
if (world->active_chunk->type == PXL8_WORLD_CHUNK_BSP && world->active_chunk->bsp) {
|
||||
pxl8_3d_set_bsp(gfx, world->active_chunk->bsp);
|
||||
pxl8_bsp_render(gfx, world->active_chunk->bsp,
|
||||
world->bsp_render_state, camera_pos);
|
||||
}
|
||||
if (world->active_chunk && world->active_chunk->bsp) {
|
||||
pxl8_3d_set_bsp(gfx, world->active_chunk->bsp);
|
||||
pxl8_bsp_render(gfx, world->active_chunk->bsp,
|
||||
world->bsp_render_state, camera_pos);
|
||||
} else {
|
||||
pxl8_3d_set_bsp(gfx, NULL);
|
||||
i32 cx = (i32)floorf(camera_pos.x / WORLD_CHUNK_SIZE);
|
||||
i32 cy = (i32)floorf(camera_pos.y / WORLD_CHUNK_SIZE);
|
||||
i32 cz = (i32)floorf(camera_pos.z / WORLD_CHUNK_SIZE);
|
||||
|
||||
const pxl8_frustum* frustum = pxl8_3d_get_frustum(gfx);
|
||||
|
||||
pxl8_gfx_material mat = {
|
||||
.texture_id = 0,
|
||||
.dynamic_lighting = true,
|
||||
.alpha = 255,
|
||||
};
|
||||
|
||||
i32 r = world->render_distance;
|
||||
for (i32 dy = -r; dy <= r; dy++) {
|
||||
for (i32 dz = -r; dz <= r; dz++) {
|
||||
for (i32 dx = -r; dx <= r; dx++) {
|
||||
i32 chunk_cx = cx + dx;
|
||||
i32 chunk_cy = cy + dy;
|
||||
i32 chunk_cz = cz + dz;
|
||||
f32 chunk_x = chunk_cx * WORLD_CHUNK_SIZE;
|
||||
f32 chunk_y = chunk_cy * WORLD_CHUNK_SIZE;
|
||||
f32 chunk_z = chunk_cz * WORLD_CHUNK_SIZE;
|
||||
|
||||
if (frustum) {
|
||||
pxl8_vec3 aabb_min = {chunk_x, chunk_y, chunk_z};
|
||||
pxl8_vec3 aabb_max = {chunk_x + WORLD_CHUNK_SIZE, chunk_y + WORLD_CHUNK_SIZE, chunk_z + WORLD_CHUNK_SIZE};
|
||||
if (!pxl8_frustum_test_aabb(frustum, aabb_min, aabb_max)) continue;
|
||||
}
|
||||
|
||||
pxl8_vxl_mesh_config config = PXL8_VXL_MESH_CONFIG_DEFAULT;
|
||||
config.chunk_x = chunk_cx;
|
||||
config.chunk_y = chunk_cy;
|
||||
config.chunk_z = chunk_cz;
|
||||
|
||||
pxl8_mesh* mesh = pxl8_world_chunk_cache_get_mesh(
|
||||
world->chunk_cache,
|
||||
chunk_cx, chunk_cy, chunk_cz,
|
||||
world->block_registry, &config);
|
||||
if (mesh && mesh->index_count > 0) {
|
||||
pxl8_mat4 scale = pxl8_mat4_scale(VOXEL_SCALE, VOXEL_SCALE, VOXEL_SCALE);
|
||||
pxl8_mat4 translate = pxl8_mat4_translate(chunk_x, chunk_y, chunk_z);
|
||||
pxl8_mat4 model = pxl8_mat4_multiply(translate, scale);
|
||||
pxl8_3d_draw_mesh(gfx, mesh, &model, &mat);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_world_sync(pxl8_world* world, pxl8_net* net) {
|
||||
if (!world || !net) return;
|
||||
|
||||
u8 chunk_type = pxl8_net_chunk_type(net);
|
||||
u32 chunk_id = pxl8_net_chunk_id(net);
|
||||
|
||||
if (chunk_type == PXL8_CHUNK_TYPE_BSP && chunk_id != 0) {
|
||||
if (chunk_id != 0) {
|
||||
pxl8_world_chunk* chunk = pxl8_world_chunk_cache_get_bsp(world->chunk_cache, chunk_id);
|
||||
if (chunk && chunk->bsp) {
|
||||
if (world->active_chunk != chunk) {
|
||||
|
|
@ -397,7 +276,7 @@ void pxl8_world_sync(pxl8_world* world, pxl8_net* net) {
|
|||
world->bsp_render_state = pxl8_bsp_render_state_create(chunk->bsp->num_faces);
|
||||
}
|
||||
}
|
||||
} else if (chunk_id == 0 && world->active_chunk != NULL) {
|
||||
} else if (world->active_chunk != NULL) {
|
||||
world->active_chunk = NULL;
|
||||
if (world->bsp_render_state) {
|
||||
pxl8_bsp_render_state_destroy(world->bsp_render_state);
|
||||
|
|
@ -409,7 +288,6 @@ void pxl8_world_sync(pxl8_world* world, pxl8_net* net) {
|
|||
static void ensure_bsp_render_state(pxl8_world* world) {
|
||||
if (!world || world->bsp_render_state) return;
|
||||
if (!world->active_chunk) return;
|
||||
if (world->active_chunk->type != PXL8_WORLD_CHUNK_BSP) return;
|
||||
if (!world->active_chunk->bsp) return;
|
||||
|
||||
world->bsp_render_state = pxl8_bsp_render_state_create(world->active_chunk->bsp->num_faces);
|
||||
|
|
@ -424,28 +302,9 @@ void pxl8_world_set_bsp_material(pxl8_world* world, u16 material_id, const pxl8_
|
|||
pxl8_bsp_set_material(world->bsp_render_state, material_id, material);
|
||||
}
|
||||
|
||||
i32 pxl8_world_get_render_distance(const pxl8_world* world) {
|
||||
if (!world) return 3;
|
||||
return world->render_distance;
|
||||
}
|
||||
|
||||
void pxl8_world_set_render_distance(pxl8_world* world, i32 distance) {
|
||||
if (!world) return;
|
||||
if (distance < 1) distance = 1;
|
||||
if (distance > 8) distance = 8;
|
||||
world->render_distance = distance;
|
||||
}
|
||||
|
||||
i32 pxl8_world_get_sim_distance(const pxl8_world* world) {
|
||||
if (!world) return 4;
|
||||
return world->sim_distance;
|
||||
}
|
||||
|
||||
void pxl8_world_set_sim_distance(pxl8_world* world, i32 distance) {
|
||||
if (!world) return;
|
||||
if (distance < 1) distance = 1;
|
||||
if (distance > 8) distance = 8;
|
||||
world->sim_distance = distance;
|
||||
void pxl8_world_set_sim_config(pxl8_world* world, const pxl8_sim_config* config) {
|
||||
if (!world || !config) return;
|
||||
world->sim_config = *config;
|
||||
}
|
||||
|
||||
void pxl8_world_init_local_player(pxl8_world* world, f32 x, f32 y, f32 z) {
|
||||
|
|
@ -481,8 +340,8 @@ void pxl8_world_predict(pxl8_world* world, pxl8_net* net, const pxl8_input_msg*
|
|||
if (!world || !net || !input) return;
|
||||
if (!(world->local_player.flags & PXL8_SIM_FLAG_ALIVE)) return;
|
||||
|
||||
pxl8_sim_world sim = make_sim_world(world, world->local_player.pos);
|
||||
pxl8_sim_move_player(&world->local_player, input, &sim, dt);
|
||||
pxl8_sim_world sim = pxl8_world_sim_world(world, world->local_player.pos);
|
||||
pxl8_sim_move_player(&world->local_player, input, &sim, &world->sim_config, dt);
|
||||
|
||||
world->client_tick++;
|
||||
|
||||
|
|
@ -515,8 +374,8 @@ void pxl8_world_reconcile(pxl8_world* world, pxl8_net* net, f32 dt) {
|
|||
const pxl8_input_msg* input = pxl8_net_input_at(net, tick);
|
||||
if (!input) continue;
|
||||
|
||||
pxl8_sim_world sim = make_sim_world(world, world->local_player.pos);
|
||||
pxl8_sim_move_player(&world->local_player, input, &sim, dt);
|
||||
pxl8_sim_world sim = pxl8_world_sim_world(world, world->local_player.pos);
|
||||
pxl8_sim_move_player(&world->local_player, input, &sim, &world->sim_config, dt);
|
||||
}
|
||||
|
||||
entity_to_userdata(&world->local_player, pxl8_net_predicted_state(net));
|
||||
|
|
@ -562,8 +421,8 @@ static void pxl8_world_sim_tick(pxl8_world* world, f32 dt) {
|
|||
merged.look_dx = 0;
|
||||
merged.look_dy = 0;
|
||||
|
||||
pxl8_sim_world sim = make_sim_world(world, world->local_player.pos);
|
||||
pxl8_sim_move_player(&world->local_player, &merged, &sim, dt);
|
||||
pxl8_sim_world sim = pxl8_world_sim_world(world, world->local_player.pos);
|
||||
pxl8_sim_move_player(&world->local_player, &merged, &sim, &world->sim_config, dt);
|
||||
|
||||
if (world->net) {
|
||||
entity_to_userdata(&world->local_player, pxl8_net_predicted_state(world->net));
|
||||
|
|
@ -672,8 +531,8 @@ void pxl8_world_push_input(pxl8_world* world, const pxl8_input_msg* input) {
|
|||
|
||||
world->pointer_motion.yaw -= input->look_dx * 0.008f;
|
||||
f32 pitch = world->pointer_motion.pitch - input->look_dy * 0.008f;
|
||||
if (pitch > PXL8_SIM_MAX_PITCH) pitch = PXL8_SIM_MAX_PITCH;
|
||||
if (pitch < -PXL8_SIM_MAX_PITCH) pitch = -PXL8_SIM_MAX_PITCH;
|
||||
if (pitch > world->sim_config.max_pitch) pitch = world->sim_config.max_pitch;
|
||||
if (pitch < -world->sim_config.max_pitch) pitch = -world->sim_config.max_pitch;
|
||||
world->pointer_motion.pitch = pitch;
|
||||
|
||||
pxl8_input_msg* copy = pxl8_malloc(sizeof(pxl8_input_msg));
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ pxl8_world_chunk_cache* pxl8_world_get_chunk_cache(pxl8_world* world);
|
|||
pxl8_world_chunk* pxl8_world_active_chunk(pxl8_world* world);
|
||||
void pxl8_world_set_active_chunk(pxl8_world* world, pxl8_world_chunk* chunk);
|
||||
|
||||
pxl8_vxl_block_registry* pxl8_world_block_registry(pxl8_world* world);
|
||||
pxl8_entity_pool* pxl8_world_entities(pxl8_world* world);
|
||||
pxl8_entity pxl8_world_spawn(pxl8_world* world);
|
||||
|
||||
|
|
@ -30,19 +29,16 @@ bool pxl8_world_point_solid(const pxl8_world* world, f32 x, f32 y, f32 z);
|
|||
pxl8_ray pxl8_world_ray(const pxl8_world* world, pxl8_vec3 from, pxl8_vec3 to);
|
||||
pxl8_ray pxl8_world_sweep(const pxl8_world* world, pxl8_vec3 from, pxl8_vec3 to, f32 radius);
|
||||
|
||||
void pxl8_world_update(pxl8_world* world, const pxl8_input_state* input, f32 dt);
|
||||
void pxl8_world_update(pxl8_world* world, f32 dt);
|
||||
void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos);
|
||||
void pxl8_world_sync(pxl8_world* world, pxl8_net* net);
|
||||
|
||||
void pxl8_world_set_bsp_material(pxl8_world* world, u16 material_id, const pxl8_gfx_material* material);
|
||||
|
||||
i32 pxl8_world_get_render_distance(const pxl8_world* world);
|
||||
void pxl8_world_set_render_distance(pxl8_world* world, i32 distance);
|
||||
i32 pxl8_world_get_sim_distance(const pxl8_world* world);
|
||||
void pxl8_world_set_sim_distance(pxl8_world* world, i32 distance);
|
||||
|
||||
void pxl8_world_set_sim_config(pxl8_world* world, const pxl8_sim_config* config);
|
||||
void pxl8_world_init_local_player(pxl8_world* world, f32 x, f32 y, f32 z);
|
||||
pxl8_sim_entity* pxl8_world_local_player(pxl8_world* world);
|
||||
pxl8_sim_world pxl8_world_sim_world(const pxl8_world* world, pxl8_vec3 pos);
|
||||
void pxl8_world_predict(pxl8_world* world, pxl8_net* net, const pxl8_input_msg* input, f32 dt);
|
||||
void pxl8_world_reconcile(pxl8_world* world, pxl8_net* net, f32 dt);
|
||||
|
||||
|
|
|
|||
|
|
@ -2,45 +2,16 @@
|
|||
|
||||
#include "pxl8_bsp.h"
|
||||
#include "pxl8_mem.h"
|
||||
#include "pxl8_vxl.h"
|
||||
|
||||
pxl8_world_chunk* pxl8_world_chunk_create_vxl(i32 cx, i32 cy, i32 cz) {
|
||||
pxl8_world_chunk* chunk = pxl8_calloc(1, sizeof(pxl8_world_chunk));
|
||||
if (!chunk) return NULL;
|
||||
|
||||
chunk->type = PXL8_WORLD_CHUNK_VXL;
|
||||
chunk->cx = cx;
|
||||
chunk->cy = cy;
|
||||
chunk->cz = cz;
|
||||
chunk->voxels = pxl8_vxl_chunk_create();
|
||||
|
||||
if (!chunk->voxels) {
|
||||
pxl8_free(chunk);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
pxl8_world_chunk* pxl8_world_chunk_create_bsp(u32 id) {
|
||||
pxl8_world_chunk* chunk = pxl8_calloc(1, sizeof(pxl8_world_chunk));
|
||||
if (!chunk) return NULL;
|
||||
|
||||
chunk->type = PXL8_WORLD_CHUNK_BSP;
|
||||
chunk->id = id;
|
||||
chunk->bsp = NULL;
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
void pxl8_world_chunk_destroy(pxl8_world_chunk* chunk) {
|
||||
if (!chunk) return;
|
||||
|
||||
if (chunk->type == PXL8_WORLD_CHUNK_VXL && chunk->voxels) {
|
||||
pxl8_vxl_chunk_destroy(chunk->voxels);
|
||||
} else if (chunk->type == PXL8_WORLD_CHUNK_BSP && chunk->bsp) {
|
||||
pxl8_bsp_destroy(chunk->bsp);
|
||||
}
|
||||
|
||||
if (chunk->bsp) pxl8_bsp_destroy(chunk->bsp);
|
||||
pxl8_free(chunk);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,29 +2,17 @@
|
|||
|
||||
#include "pxl8_bsp.h"
|
||||
#include "pxl8_types.h"
|
||||
#include "pxl8_vxl.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum pxl8_world_chunk_type {
|
||||
PXL8_WORLD_CHUNK_VXL,
|
||||
PXL8_WORLD_CHUNK_BSP
|
||||
} pxl8_world_chunk_type;
|
||||
|
||||
typedef struct pxl8_world_chunk {
|
||||
pxl8_world_chunk_type type;
|
||||
u32 id;
|
||||
u32 version;
|
||||
i32 cx, cy, cz;
|
||||
union {
|
||||
pxl8_bsp* bsp;
|
||||
pxl8_vxl_chunk* voxels;
|
||||
};
|
||||
pxl8_bsp* bsp;
|
||||
} pxl8_world_chunk;
|
||||
|
||||
pxl8_world_chunk* pxl8_world_chunk_create_vxl(i32 cx, i32 cy, i32 cz);
|
||||
pxl8_world_chunk* pxl8_world_chunk_create_bsp(u32 id);
|
||||
void pxl8_world_chunk_destroy(pxl8_world_chunk* chunk);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
#include "pxl8_mesh.h"
|
||||
#include "pxl8_protocol.h"
|
||||
#include "pxl8_types.h"
|
||||
#include "pxl8_vxl_render.h"
|
||||
#include "pxl8_world_chunk.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
@ -16,15 +15,11 @@ extern "C" {
|
|||
|
||||
typedef struct pxl8_world_chunk_cache_entry {
|
||||
pxl8_world_chunk* chunk;
|
||||
pxl8_mesh* mesh;
|
||||
u64 last_used;
|
||||
bool mesh_dirty;
|
||||
bool valid;
|
||||
bool has_all_neighbors;
|
||||
} pxl8_world_chunk_cache_entry;
|
||||
|
||||
typedef struct pxl8_world_chunk_assembly {
|
||||
pxl8_world_chunk_type type;
|
||||
u32 id;
|
||||
i32 cx, cy, cz;
|
||||
u32 version;
|
||||
|
|
@ -51,18 +46,9 @@ pxl8_result pxl8_world_chunk_cache_receive(pxl8_world_chunk_cache* cache,
|
|||
const pxl8_chunk_msg_header* hdr,
|
||||
const u8* payload, usize len);
|
||||
|
||||
pxl8_world_chunk* pxl8_world_chunk_cache_get_vxl(pxl8_world_chunk_cache* cache, i32 cx, i32 cy, i32 cz);
|
||||
pxl8_world_chunk* pxl8_world_chunk_cache_get_bsp(pxl8_world_chunk_cache* cache, u32 id);
|
||||
|
||||
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);
|
||||
|
||||
void pxl8_world_chunk_cache_tick(pxl8_world_chunk_cache* cache);
|
||||
void pxl8_world_chunk_cache_evict_distant(pxl8_world_chunk_cache* cache,
|
||||
i32 cx, i32 cy, i32 cz, i32 radius);
|
||||
void pxl8_world_chunk_cache_invalidate_meshes(pxl8_world_chunk_cache* cache);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue