major gfx refactor

This commit is contained in:
asrael 2026-02-02 17:48:25 -06:00
parent 0c0aa792c1
commit 3c3e961995
58 changed files with 3681 additions and 2982 deletions

View file

@ -30,13 +30,14 @@ struct pxl8_world {
pxl8_entity_pool* entities;
pxl8_bsp_render_state* bsp_render_state;
pxl8_vxl_render_state* vxl_render_state;
pxl8_sdf sdf;
i32 render_distance;
i32 sim_distance;
pxl8_sim_entity local_player;
u64 client_tick;
pxl8_vec2 pointer_motion;
#ifdef PXL8_ASYNC_THREADS
pxl8_sim_entity render_state[2];
atomic_uint active_buffer;
@ -187,100 +188,6 @@ static void userdata_to_entity(const u8* userdata, pxl8_sim_entity* ent) {
memcpy(&ent->kind, p, 2);
}
static void update_sdf(pxl8_world* world, pxl8_vec3 center, f32 cell_size) {
if (!world) return;
world->sdf.cell = cell_size;
world->sdf.origin.x = center.x - (PXL8_SDF_X / 2) * cell_size;
world->sdf.origin.y = center.y - (PXL8_SDF_Y / 2) * cell_size;
world->sdf.origin.z = center.z - (PXL8_SDF_Z / 2) * cell_size;
i16 seed_x[PXL8_SDF_SIZE];
i16 seed_y[PXL8_SDF_SIZE];
i16 seed_z[PXL8_SDF_SIZE];
for (i32 iy = 0; iy < PXL8_SDF_Y; iy++) {
f32 wy = world->sdf.origin.y + (iy + 0.5f) * cell_size;
for (i32 iz = 0; iz < PXL8_SDF_Z; iz++) {
f32 wz = world->sdf.origin.z + (iz + 0.5f) * cell_size;
for (i32 ix = 0; ix < PXL8_SDF_X; ix++) {
f32 wx = world->sdf.origin.x + (ix + 0.5f) * cell_size;
i32 idx = iy * PXL8_SDF_Z * PXL8_SDF_X + iz * PXL8_SDF_X + ix;
if (pxl8_world_point_solid(world, wx, wy, wz)) {
seed_x[idx] = (i16)ix;
seed_y[idx] = (i16)iy;
seed_z[idx] = (i16)iz;
} else {
seed_x[idx] = -1;
seed_y[idx] = -1;
seed_z[idx] = -1;
}
}
}
}
for (i32 step = 16; step >= 1; step /= 2) {
for (i32 iy = 0; iy < PXL8_SDF_Y; iy++) {
for (i32 iz = 0; iz < PXL8_SDF_Z; iz++) {
for (i32 ix = 0; ix < PXL8_SDF_X; ix++) {
i32 idx = iy * PXL8_SDF_Z * PXL8_SDF_X + iz * PXL8_SDF_X + ix;
i32 best_dist_sq = (seed_x[idx] >= 0)
? (ix - seed_x[idx]) * (ix - seed_x[idx]) +
(iy - seed_y[idx]) * (iy - seed_y[idx]) +
(iz - seed_z[idx]) * (iz - seed_z[idx])
: 0x7FFFFFFF;
for (i32 dy = -step; dy <= step; dy += step) {
i32 ny = iy + dy;
if (ny < 0 || ny >= PXL8_SDF_Y) continue;
for (i32 dz = -step; dz <= step; dz += step) {
i32 nz = iz + dz;
if (nz < 0 || nz >= PXL8_SDF_Z) continue;
for (i32 dx = -step; dx <= step; dx += step) {
if (dx == 0 && dy == 0 && dz == 0) continue;
i32 nx = ix + dx;
if (nx < 0 || nx >= PXL8_SDF_X) continue;
i32 nidx = ny * PXL8_SDF_Z * PXL8_SDF_X + nz * PXL8_SDF_X + nx;
if (seed_x[nidx] < 0) continue;
i32 dist_sq = (ix - seed_x[nidx]) * (ix - seed_x[nidx]) +
(iy - seed_y[nidx]) * (iy - seed_y[nidx]) +
(iz - seed_z[nidx]) * (iz - seed_z[nidx]);
if (dist_sq < best_dist_sq) {
best_dist_sq = dist_sq;
seed_x[idx] = seed_x[nidx];
seed_y[idx] = seed_y[nidx];
seed_z[idx] = seed_z[nidx];
}
}
}
}
}
}
}
}
for (i32 i = 0; i < PXL8_SDF_SIZE; i++) {
if (seed_x[i] < 0) {
world->sdf.data[i] = 127;
} else {
i32 idx_x = i % PXL8_SDF_X;
i32 idx_z = (i / PXL8_SDF_X) % PXL8_SDF_Z;
i32 idx_y = i / (PXL8_SDF_X * PXL8_SDF_Z);
i32 dx = idx_x - seed_x[i];
i32 dy = idx_y - seed_y[i];
i32 dz = idx_z - seed_z[i];
f32 dist = sqrtf((f32)(dx * dx + dy * dy + dz * dz));
i32 d = (i32)(dist * cell_size);
if (d > 127) d = 127;
world->sdf.data[i] = (i8)d;
}
}
}
bool pxl8_world_point_solid(const pxl8_world* world, f32 x, f32 y, f32 z) {
if (!world) return false;
@ -395,18 +302,22 @@ void pxl8_world_update(pxl8_world* world, const pxl8_input_state* input, f32 dt)
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;
update_sdf(world, camera_pos, PXL8_SDF_CELL);
pxl8_3d_set_sdf(gfx, &world->sdf);
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);
@ -421,13 +332,10 @@ void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos) {
const pxl8_frustum* frustum = pxl8_3d_get_frustum(gfx);
bool wireframe = world->vxl_render_state && world->vxl_render_state->wireframe;
pxl8_gfx_material mat = {
.texture_id = 0,
.dynamic_lighting = true,
.vertex_color_passthrough = true,
.alpha = 255,
.wireframe = wireframe,
};
i32 r = world->render_distance;
@ -481,16 +389,27 @@ void pxl8_world_sync(pxl8_world* world, pxl8_net* net) {
world->active_chunk = chunk;
pxl8_debug("[CLIENT] Synced BSP chunk id=%u as active (verts=%u faces=%u)",
chunk_id, chunk->bsp->num_vertices, chunk->bsp->num_faces);
if (world->bsp_render_state) {
pxl8_bsp_render_state_destroy(world->bsp_render_state);
world->bsp_render_state = NULL;
}
world->bsp_render_state = pxl8_bsp_render_state_create(chunk->bsp->num_faces);
}
}
} else if (chunk_id == 0 && world->active_chunk != NULL) {
world->active_chunk = NULL;
if (world->bsp_render_state) {
pxl8_bsp_render_state_destroy(world->bsp_render_state);
world->bsp_render_state = NULL;
}
}
}
static void ensure_bsp_render_state(pxl8_world* world) {
if (!world || world->bsp_render_state) return;
if (!world->active_chunk || world->active_chunk->type != PXL8_WORLD_CHUNK_BSP) 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);
@ -505,18 +424,6 @@ 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);
}
void pxl8_world_set_wireframe(pxl8_world* world, bool enabled) {
if (!world) return;
ensure_bsp_render_state(world);
if (world->bsp_render_state) {
pxl8_bsp_set_wireframe(world->bsp_render_state, enabled);
}
if (world->vxl_render_state) {
pxl8_vxl_set_wireframe(world->vxl_render_state, enabled);
}
}
i32 pxl8_world_get_render_distance(const pxl8_world* world) {
if (!world) return 3;
return world->render_distance;
@ -559,8 +466,15 @@ 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) {
if (!world) return NULL;
#ifdef PXL8_ASYNC_THREADS
const pxl8_sim_entity* state = pxl8_world_get_render_state(world);
if (!state) return NULL;
if (!(state->flags & PXL8_SIM_FLAG_ALIVE)) return NULL;
return (pxl8_sim_entity*)state;
#else
if (!(world->local_player.flags & PXL8_SIM_FLAG_ALIVE)) return NULL;
return &world->local_player;
#endif
}
void pxl8_world_predict(pxl8_world* world, pxl8_net* net, const pxl8_input_msg* input, f32 dt) {
@ -585,7 +499,14 @@ void pxl8_world_reconcile(pxl8_world* world, pxl8_net* net, f32 dt) {
const u8* server_state = pxl8_net_entity_userdata(net, player_id);
if (!server_state) return;
userdata_to_entity(server_state, &world->local_player);
pxl8_sim_entity server_player = {0};
userdata_to_entity(server_state, &server_player);
if (!(server_player.flags & PXL8_SIM_FLAG_ALIVE)) {
return;
}
world->local_player = server_player;
const pxl8_snapshot_header* snap = pxl8_net_snapshot(net);
u64 server_tick = snap ? snap->tick : 0;
@ -606,11 +527,9 @@ void pxl8_world_reconcile(pxl8_world* world, pxl8_net* net, f32 dt) {
#define SIM_TIMESTEP (1.0f / 60.0f)
static void pxl8_world_sim_tick(pxl8_world* world, f32 dt) {
if (!(world->local_player.flags & PXL8_SIM_FLAG_ALIVE)) return;
bool alive = (world->local_player.flags & PXL8_SIM_FLAG_ALIVE) != 0;
pxl8_input_msg merged = {0};
pxl8_input_msg* input = NULL;
bool has_input = false;
while ((input = pxl8_queue_pop(&world->input_queue))) {
merged.look_dx += input->look_dx;
@ -618,16 +537,31 @@ static void pxl8_world_sim_tick(pxl8_world* world, f32 dt) {
merged.move_x = input->move_x;
merged.move_y = input->move_y;
merged.buttons |= input->buttons;
has_input = true;
pxl8_free(input);
}
if (!alive) {
return;
}
const f32 MAX_LOOK_DELTA = 100.0f;
if (merged.look_dx > MAX_LOOK_DELTA) merged.look_dx = MAX_LOOK_DELTA;
if (merged.look_dx < -MAX_LOOK_DELTA) merged.look_dx = -MAX_LOOK_DELTA;
if (merged.look_dy > MAX_LOOK_DELTA) merged.look_dy = MAX_LOOK_DELTA;
if (merged.look_dy < -MAX_LOOK_DELTA) merged.look_dy = -MAX_LOOK_DELTA;
merged.tick = world->client_tick;
merged.timestamp = pxl8_get_ticks_ns();
merged.yaw = world->pointer_motion.yaw;
if (world->net) {
pxl8_net_send_input(world->net, &merged);
}
world->local_player.yaw = world->pointer_motion.yaw;
world->local_player.pitch = world->pointer_motion.pitch;
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);
@ -637,7 +571,6 @@ static void pxl8_world_sim_tick(pxl8_world* world, f32 dt) {
}
world->client_tick++;
(void)has_input;
}
static void pxl8_world_swap_buffers(pxl8_world* world) {
@ -737,6 +670,12 @@ void pxl8_world_pause_sim(pxl8_world* world, bool paused) {
void pxl8_world_push_input(pxl8_world* world, const pxl8_input_msg* input) {
if (!world || !input) return;
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;
world->pointer_motion.pitch = pitch;
pxl8_input_msg* copy = pxl8_malloc(sizeof(pxl8_input_msg));
if (copy) {
*copy = *input;