From 5a565844dd180741ce4d6b0f845fd840fe883cca Mon Sep 17 00:00:00 2001 From: asrael Date: Fri, 27 Feb 2026 01:22:35 -0600 Subject: [PATCH] refactor: decouple sim from framework, remove voxel geometry --- demo/mod/first_person3d.fnl | 234 ++++-------- demo/mod/menu.fnl | 31 +- demo/mod/textures.fnl | 54 +++ pxl8.sh | 12 +- pxl8d/build.rs | 6 - pxl8d/src/chunk.rs | 17 - pxl8d/src/chunk/stream.rs | 22 -- pxl8d/src/lib.rs | 2 - pxl8d/src/main.rs | 87 ++--- pxl8d/src/sim.rs | 61 ++- pxl8d/src/transport.rs | 47 --- pxl8d/src/voxel.rs | 379 ------------------- src/core/pxl8.c | 14 +- src/core/pxl8_io.c | 7 + src/core/pxl8_io.h | 1 + src/gfx/pxl8_gfx.c | 9 - src/gfx/pxl8_gfx.h | 2 - src/gui/pxl8_gui.c | 38 +- src/gui/pxl8_gui.h | 3 +- src/gui/pxl8_gui_palette.h | 2 +- src/lua/pxl8.lua | 8 +- src/lua/pxl8/gui.lua | 4 + src/lua/pxl8/input.lua | 4 + src/lua/pxl8/net.lua | 12 +- src/lua/pxl8/world.lua | 93 +++-- src/net/pxl8_net.c | 39 +- src/net/pxl8_net.h | 5 +- src/net/pxl8_protocol.h | 5 +- src/script/pxl8_script_ffi.h | 48 ++- src/sim/pxl8_sim.c | 128 +++---- src/sim/pxl8_sim.h | 33 +- src/vxl/pxl8_vxl.c | 319 ---------------- src/vxl/pxl8_vxl.h | 78 ---- src/vxl/pxl8_vxl_render.c | 571 ----------------------------- src/vxl/pxl8_vxl_render.h | 47 --- src/world/pxl8_world.c | 217 ++--------- src/world/pxl8_world.h | 10 +- src/world/pxl8_world_chunk.c | 31 +- src/world/pxl8_world_chunk.h | 14 +- src/world/pxl8_world_chunk_cache.c | 176 +-------- src/world/pxl8_world_chunk_cache.h | 14 - 41 files changed, 477 insertions(+), 2407 deletions(-) delete mode 100644 pxl8d/src/voxel.rs delete mode 100644 src/vxl/pxl8_vxl.c delete mode 100644 src/vxl/pxl8_vxl.h delete mode 100644 src/vxl/pxl8_vxl_render.c delete mode 100644 src/vxl/pxl8_vxl_render.h diff --git a/demo/mod/first_person3d.fnl b/demo/mod/first_person3d.fnl index 857746f..20c1240 100644 --- a/demo/mod/first_person3d.fnl +++ b/demo/mod/first_person3d.fnl @@ -1,4 +1,5 @@ (local pxl8 (require :pxl8)) +(local bit (require :bit)) (local effects (require :pxl8.effects)) (local net (require :pxl8.net)) @@ -10,36 +11,37 @@ (local bob-amount 4.0) (local bob-speed 8.0) (local cam-smoothing 0.25) -(local ceiling-height 120) -(local chunk-size 64) -(local cursor-sensitivity 0.010) -(local gravity 600) -(local grid-size 64) -(local jump-velocity 180) (local land-recovery-speed 20) (local land-squash-amount -4) -(local max-pitch 1.5) -(local move-speed 200) (local player-eye-height 64) -(local player-height 72) -(local player-radius 12) -(local step-height 24) (local day-length 1800) -(local turn-speed 4.0) + +(local SIM_FLAG_GROUNDED 4) + +(local sim-cfg (pxl8.sim_config {:move_speed 150 + :gravity 600 + :jump_velocity 180 + :player_radius 12 + :player_height 72 + :max_pitch 1.5 + :friction 6.0 + :ground_accel 10.0 + :air_accel 1.0 + :stop_speed 100.0})) (var auto-run-cancel-key nil) (var auto-run? false) +(var was-auto-running false) (var bob-time 0) (var cam-pitch 0) (var day-time 0) (var cam-x 416) -(var cam-y 600) +(var cam-y 0) (var cam-yaw 0) (var cam-z 416) (var camera nil) (var ceiling-tex nil) (var floor-tex nil) -(var grounded true) (var land-squash 0) (var last-dt 0.016) (var light-time 0) @@ -51,37 +53,16 @@ (var real-time 0) (var smooth-cam-x 416) (var smooth-cam-z 416) -(var vel-y 0) +(var was-grounded true) (var trim-tex nil) (var wall-tex nil) (var world nil) -(local cursor-look? true) (local MOSS_COLOR 200) (local PLASTER_COLOR 16) -(local STONE_FLOOR_START 37) (local STONE_WALL_START 2) (local WOOD_COLOR 88) -(fn find-floor [x y z in-bsp] - (if (not world) - 0 - (if in-bsp - 0 - (let [start-y (math.max (+ y 64) 256) - ray (world:ray x start-y z x (- y 1000) z)] - (if ray.hit - (+ ray.point.y 1) - 128))))) - -(fn find-ceiling [x y z max-height] - (if (not world) - nil - (let [ray (world:ray x (+ y 1) z x (+ y max-height) z)] - (if ray.hit - (- ray.point.y 1) - nil)))) - (fn preload [] (when (not network) (set network (net.get)) @@ -90,6 +71,7 @@ (when (not world) (set world (pxl8.get_world)) (when world + (world:set_sim_config sim-cfg) (world:init_local_player cam-x cam-y cam-z)))) (fn is-connected [] @@ -126,6 +108,11 @@ (sky.generate-stars) (preload) + (when world + (world:init_local_player cam-x cam-y cam-z) + (set smooth-cam-x cam-x) + (set smooth-cam-z cam-z)) + (when (not ceiling-tex) (set ceiling-tex (textures.plaster-wall 66666 PLASTER_COLOR))) (when (not floor-tex) @@ -149,9 +136,6 @@ (set bsp-materials-setup true)))))) (fn sample-input [] - (var move-forward 0) - (var move-right 0) - (when (pxl8.key_pressed "`") (set auto-run? (not auto-run?)) (when (and auto-run? (pxl8.key_down "w")) @@ -163,19 +147,15 @@ (when (and auto-run-cancel-key (not (pxl8.key_down auto-run-cancel-key))) (set auto-run-cancel-key nil)) - (when (or (pxl8.key_down "w") auto-run?) - (set move-forward (+ move-forward 1))) - (when (and (pxl8.key_down "s") (not= auto-run-cancel-key "s")) - (set move-forward (- move-forward 1))) - (when (pxl8.key_down "a") - (set move-right (- move-right 1))) - (when (pxl8.key_down "d") - (set move-right (+ move-right 1))) + (set was-auto-running auto-run?) - {:move_x move-right - :move_y move-forward - :look_dx (pxl8.mouse_dx) - :look_dy (pxl8.mouse_dy)}) + (pxl8.make_input_msg + {:move_x (+ (if (pxl8.key_down "d") 1 0) (if (pxl8.key_down "a") -1 0)) + :move_y (+ (if (or (pxl8.key_down "w") auto-run?) 1 0) + (if (and (pxl8.key_down "s") (not= auto-run-cancel-key "s")) -1 0)) + :look_dx (pxl8.mouse_dx) + :look_dy (pxl8.mouse_dy) + :buttons (if (pxl8.key_down "space") 1 0)})) (fn update [dt] (set last-dt dt) @@ -185,137 +165,81 @@ (set portal-cooldown (- portal-cooldown dt))) (when (and world network (<= portal-cooldown 0)) - (let [chunk (world:active_chunk) - in-bsp (not= chunk nil) - (door-x door-z) (entities.get-door-position) + (let [(door-x door-z) (entities.get-door-position) door-radius (entities.get-door-radius) dist-to-door (math.sqrt (+ (* (- cam-x door-x) (- cam-x door-x)) (* (- cam-z door-z) (- cam-z door-z))))] (when (< dist-to-door door-radius) - (if in-bsp - (do - (pxl8.info "Exiting through door...") - (let [exit-x (+ door-x 50) - exit-z door-z - exit-y 600] - (network:exit_chunk exit-x exit-y exit-z) - (set cam-x exit-x) - (set cam-z exit-z) - (set cam-y exit-y)) - (set vel-y 0) - (set grounded false) - (set smooth-cam-x cam-x) - (set smooth-cam-z cam-z) - (set bsp-materials-setup false) - (set portal-cooldown 2.0)) - (do - (pxl8.info "Entering through door...") - (network:enter_chunk 1) - (set cam-x 416) - (set cam-z 416) - (set cam-y 0) - (set vel-y 0) - (set grounded true) - (set smooth-cam-x 416) - (set smooth-cam-z 416) - (set portal-cooldown 2.0)))))) + (let [current-id (network:chunk_id)] + (if (= current-id 1) + (do + (pxl8.info "Door: BSP 1 -> BSP 2") + (network:enter_scene 1 2 416 0 416) + (world:init_local_player 416 0 416) + (set cam-x 416) + (set cam-y 0) + (set cam-z 416) + (set smooth-cam-x 416) + (set smooth-cam-z 416) + (set bsp-materials-setup false) + (set portal-cooldown 2.0)) + (= current-id 2) + (do + (pxl8.info "Door: BSP 2 -> BSP 1") + (network:enter_scene 1 1 416 0 416) + (world:init_local_player 416 0 416) + (set cam-x 416) + (set cam-y 0) + (set cam-z 416) + (set smooth-cam-x 416) + (set smooth-cam-z 416) + (set bsp-materials-setup false) + (set portal-cooldown 2.0))))))) - (let [chunk (world:active_chunk) - chunk-id (if network (network:chunk_id) -1) - expecting-bsp (> chunk-id 0) - voxel-space (and (not chunk) (= chunk-id 0)) - ready (or voxel-space (and chunk (chunk:ready)))] (when world - (let [input (sample-input) - grid-max (if voxel-space 100000 (* grid-size chunk-size)) - movement-yaw cam-yaw] + (let [input-msg (sample-input) + player (world:local_player)] - (let [player (world:local_player)] - (when player - (set cam-x player.pos.x) - (set cam-y player.pos.y) - (set cam-z player.pos.z) - (set cam-yaw player.yaw) - (set cam-pitch player.pitch))) + (world:push_input input-msg) - (when (and voxel-space grounded) - (let [floor-y (find-floor cam-x cam-y cam-z false) - height-diff (- floor-y cam-y)] - (if (and (>= height-diff (- step-height)) (<= height-diff step-height)) - (let [lerp-speed 0.3 - smooth-y (+ (* cam-y (- 1 lerp-speed)) (* floor-y lerp-speed))] - (set cam-y smooth-y)) - (when (< height-diff (- step-height)) - (set grounded false))))) + (when player + (set cam-x player.pos.x) + (set cam-y player.pos.y) + (set cam-z player.pos.z) + (set cam-yaw player.yaw) + (set cam-pitch player.pitch)) + + (let [now-grounded (if player (not= (bit.band player.flags SIM_FLAG_GROUNDED) 0) true)] + (when (and (not was-grounded) now-grounded) + (set land-squash land-squash-amount)) + (set was-grounded now-grounded)) (set smooth-cam-x (+ (* smooth-cam-x (- 1 cam-smoothing)) (* cam-x cam-smoothing))) (set smooth-cam-z (+ (* smooth-cam-z (- 1 cam-smoothing)) (* cam-z cam-smoothing))) - (when (and (pxl8.key_pressed "space") grounded) - (set vel-y jump-velocity) - (set grounded false)) - - (when (or (not grounded) (not= vel-y 0)) - (set vel-y (- vel-y (* gravity dt))) - - (if (> vel-y 0) - (let [new-y (+ cam-y (* vel-y dt)) - head-y (+ new-y player-height) - ceiling-y (if voxel-space - (find-ceiling cam-x cam-y cam-z 200) - ceiling-height)] - (if (and ceiling-y (>= head-y ceiling-y)) - (do - (set cam-y (- ceiling-y player-height)) - (set vel-y 0)) - (set cam-y new-y))) - (let [new-y (+ cam-y (* vel-y dt)) - floor-y (find-floor cam-x cam-y cam-z (not voxel-space))] - (if (<= new-y floor-y) - (do - (set cam-y floor-y) - (set land-squash land-squash-amount) - (set vel-y 0) - (set grounded true)) - (do - (set cam-y new-y) - (set grounded false)))))) - (when (< land-squash 0) (set land-squash (math.min 0 (+ land-squash (* land-recovery-speed dt))))) - (let [moving (or (not= input.move_x 0) (not= input.move_y 0))] - (if (and moving grounded) + (let [moving (or (not= input-msg.move_x 0) (not= input-msg.move_y 0))] + (if (and moving was-grounded) (set bob-time (+ bob-time (* dt bob-speed))) (let [target-phase (* (math.floor (/ bob-time math.pi)) math.pi)] (set bob-time (+ (* bob-time 0.8) (* target-phase 0.2)))))) (set day-time (% (+ day-time (/ dt day-length)) 1)) (set light-time (+ light-time (* dt 0.5))) - (set real-time (+ real-time dt)))))) + (set real-time (+ real-time dt))))) (fn frame [] (pxl8.clear 1) - (when (not camera) - (pxl8.error "camera is nil!")) - - (when (not world) - (pxl8.error "world is nil!")) + (when (or (not camera) (not world)) + (lua "return")) (let [chunk (when world (world:active_chunk)) - expecting-bsp (and network (> (network:chunk_id) 0)) - voxel-space (and world (not chunk) (not expecting-bsp)) - ready (or voxel-space (and chunk (chunk:ready)))] + ready (and chunk (chunk:ready))] - (when (not camera) - (pxl8.text "camera nil" 5 30 12)) - (when (not world) - (pxl8.text "world nil" 5 40 12)) - (when (and world (not ready)) - (pxl8.text (.. "Waiting... chunk=" (tostring chunk) " voxel=" (tostring voxel-space)) 5 50 12)) - - (when (and camera world) + (when ready (let [bob-offset (* (math.sin bob-time) bob-amount) eye-y (+ cam-y player-eye-height bob-offset land-squash) forward-x (- (math.sin cam-yaw)) @@ -364,7 +288,7 @@ (when chunk (entities.render-fireball light-x light-y light-z (menu.is-wireframe))) - (entities.render-door (menu.is-wireframe) (if voxel-space 128 0)) + (entities.render-door (menu.is-wireframe) 0) (pxl8.end_frame_3d) diff --git a/demo/mod/menu.fnl b/demo/mod/menu.fnl index 4219e13..2529d7f 100644 --- a/demo/mod/menu.fnl +++ b/demo/mod/menu.fnl @@ -1,12 +1,8 @@ (local pxl8 (require :pxl8)) (local music (require :mod.music)) -(local world-mod (require :pxl8.world)) -(local net-mod (require :pxl8.net)) (var paused false) (var gui nil) -(var render-distance 3) -(var sim-distance 4) (var current-panel :main) (var selected-item nil) (var current-items []) @@ -18,11 +14,7 @@ (var wireframe false) (fn init [] - (set gui (pxl8.create_gui)) - (let [w (world-mod.World.get)] - (when w - (set render-distance (w:get_render_distance)) - (set sim-distance (w:get_sim_distance))))) + (set gui (pxl8.create_gui))) (fn show [] (set paused true) @@ -97,7 +89,7 @@ (set selected-item label)) (let [is-selected (= selected-item label)] (when is-selected - (pxl8.rect (- x 3) (- y 3) (+ w 6) (+ h 6) 15)) + (pxl8.rect (- x 3) (- y 3) (+ w 6) (+ h 6) (pxl8.gui_color 4))) (let [clicked (gui:button id x y w h label)] (when clicked (set selected-item label)) @@ -129,14 +121,14 @@ (music.stop) (music.start)))) - (pxl8.gui_label 215 185 "Volume/Devices: TODO" 15) + (pxl8.gui_label 215 185 "Volume/Devices: TODO" (pxl8.gui_color 4)) (when (menu-button 20 215 210 210 30 "Back") (set current-panel :main) (set selected-item nil))) (fn draw-gfx-panel [] - (pxl8.gui_window 200 60 240 220 "GFX") + (pxl8.gui_window 200 60 240 195 "GFX") (let [baked-label (if baked-lighting "Baked Lighting: On" "Baked Lighting: Off")] (when (menu-button 40 215 107 210 24 baked-label) @@ -146,24 +138,15 @@ (when (menu-button 41 215 134 210 24 dynamic-label) (set dynamic-lighting (not dynamic-lighting)))) - (pxl8.gui_label 215 162 (.. "Render: " render-distance) 15) - (let [(changed new-val) (gui:slider_int 30 215 175 210 14 render-distance 1 8)] - (when changed - (set render-distance new-val) - (let [w (world-mod.World.get) - n (net-mod.get)] - (when w (w:set_render_distance new-val)) - (when n (n:set_chunk_settings new-val sim-distance))))) - (let [tex-label (if textures "Textures: On" "Textures: Off")] - (when (menu-button 42 215 194 210 24 tex-label) + (when (menu-button 42 215 161 210 24 tex-label) (set textures (not textures)))) (let [wire-label (if wireframe "Wireframe: On" "Wireframe: Off")] - (when (menu-button 43 215 221 210 24 wire-label) + (when (menu-button 43 215 188 210 24 wire-label) (set wireframe (not wireframe)))) - (when (menu-button 32 215 248 210 24 "Back") + (when (menu-button 32 215 218 210 24 "Back") (set current-panel :main) (set selected-item nil))) diff --git a/demo/mod/textures.fnl b/demo/mod/textures.fnl index 6225235..a57bd85 100644 --- a/demo/mod/textures.fnl +++ b/demo/mod/textures.fnl @@ -327,4 +327,58 @@ (g:destroy) tex-id))) +(fn textures.rough-stone [seed base-color] + (let [g (build-graph seed + (fn [ctx] + (let [cell (voronoi-cell ctx 8) + edge (voronoi-edge ctx 8) + crack-threshold (const ctx 0.03) + is-crack (sub ctx crack-threshold edge) + crack-color (const ctx (- base-color 3)) + roughness (noise-turbulence ctx 3 24 0.6) + detail (noise-value ctx 48) + combined (add ctx (mul ctx roughness (const ctx 0.5)) + (add ctx (mul ctx cell (const ctx 0.3)) + (mul ctx detail (const ctx 0.2)))) + stone-quant (quantize ctx combined base-color 6)] + (select ctx is-crack crack-color stone-quant))))] + (let [tex-id (g:eval_texture 64 64)] + (g:destroy) + tex-id))) + +(fn textures.packed-dirt [seed base-color] + (let [g (build-graph seed + (fn [ctx] + (let [base-noise (noise-fbm ctx 3 16 0.5) + detail (noise-value ctx 32) + combined (add ctx (mul ctx base-noise (const ctx 0.6)) + (mul ctx detail (const ctx 0.4))) + dirt-quant (quantize ctx combined base-color 5) + pebble (voronoi-cell ctx 12) + pebble-edge (voronoi-edge ctx 12) + pebble-threshold (const ctx 0.08) + is-pebble (sub ctx pebble-threshold pebble-edge) + pebble-quant (quantize ctx pebble (- base-color 2) 4)] + (select ctx is-pebble pebble-quant dirt-quant))))] + (let [tex-id (g:eval_texture 64 64)] + (g:destroy) + tex-id))) + +(fn textures.grass-top [seed base-color] + (let [g (build-graph seed + (fn [ctx] + (let [blade-x (mul ctx ctx.x (const ctx 32)) + blade-y (mul ctx ctx.y (const ctx 4)) + blades (noise-fbm-at ctx blade-x blade-y 3 6 0.5) + fine (noise-value ctx 64) + patch (noise-fbm ctx 2 6 0.4) + combined (add ctx (mul ctx blades (const ctx 0.4)) + (add ctx (mul ctx fine (const ctx 0.3)) + (mul ctx patch (const ctx 0.3)))) + grass-quant (quantize ctx combined base-color 6)] + grass-quant)))] + (let [tex-id (g:eval_texture 64 64)] + (g:destroy) + tex-id))) + textures diff --git a/pxl8.sh b/pxl8.sh index db454c4..2401f38 100755 --- a/pxl8.sh +++ b/pxl8.sh @@ -302,6 +302,7 @@ setup_sdl3() { CFLAGS="$CFLAGS $SDL3_CFLAGS" LIBS="$LIBS $SDL3_LIBS $SDL3_RPATH" + HAS_SDL3=1 } timestamp() { @@ -476,7 +477,7 @@ case "$COMMAND" in print_info "Compiler cache: ccache enabled" fi - INCLUDES="-Isrc/asset -Isrc/bsp -Isrc/core -Isrc/gfx -Isrc/gui -Isrc/hal -Isrc/math -Isrc/net -Isrc/procgen -Isrc/script -Isrc/shader -Isrc/sfx -Isrc/sim -Isrc/vxl -Isrc/world -Ilib/linenoise -Ilib/luajit/src -Ilib/miniz -I.build/shaders/c" + INCLUDES="-Isrc/asset -Isrc/bsp -Isrc/core -Isrc/gfx -Isrc/gui -Isrc/hal -Isrc/math -Isrc/net -Isrc/procgen -Isrc/script -Isrc/shader -Isrc/sfx -Isrc/sim -Isrc/world -Ilib/linenoise -Ilib/luajit/src -Ilib/miniz -I.build/shaders/c" COMPILE_FLAGS="$CFLAGS $INCLUDES" DEP_COMPILE_FLAGS="$DEP_CFLAGS $INCLUDES" @@ -518,7 +519,6 @@ case "$COMMAND" in src/gfx/pxl8_transition.c src/gui/pxl8_gui.c src/hal/pxl8_hal_sdl3.c - src/hal/pxl8_mem_sdl3.c src/hal/pxl8_thread_sdl3.c src/math/pxl8_math.c src/math/pxl8_noise.c @@ -529,14 +529,18 @@ case "$COMMAND" in src/script/pxl8_script.c src/sfx/pxl8_sfx.c src/sim/pxl8_sim.c - src/vxl/pxl8_vxl.c - src/vxl/pxl8_vxl_render.c src/world/pxl8_entity.c src/world/pxl8_world.c src/world/pxl8_world_chunk.c src/world/pxl8_world_chunk_cache.c " + if [[ "$HAS_SDL3" -eq 1 ]]; then + PXL8_SOURCE_FILES="$PXL8_SOURCE_FILES src/hal/pxl8_mem_sdl3.c" + else + PXL8_SOURCE_FILES="$PXL8_SOURCE_FILES src/hal/pxl8_mem.c" + fi + LUAJIT_LIB="lib/luajit/src/libluajit.a" OBJECT_DIR="$BUILDDIR/obj" mkdir -p "$OBJECT_DIR" diff --git a/pxl8d/build.rs b/pxl8d/build.rs index d3875d8..6f3a44c 100644 --- a/pxl8d/build.rs +++ b/pxl8d/build.rs @@ -15,23 +15,18 @@ fn main() { println!("cargo:rerun-if-changed=../src/net/pxl8_protocol.h"); println!("cargo:rerun-if-changed=../src/sim/pxl8_sim.c"); println!("cargo:rerun-if-changed=../src/sim/pxl8_sim.h"); - println!("cargo:rerun-if-changed=../src/vxl/pxl8_vxl.c"); - println!("cargo:rerun-if-changed=../src/vxl/pxl8_vxl.h"); - cc::Build::new() .file(pxl8_src.join("core/pxl8_log.c")) .file(pxl8_src.join("hal/pxl8_mem.c")) .file(pxl8_src.join("math/pxl8_math.c")) .file(pxl8_src.join("math/pxl8_noise.c")) .file(pxl8_src.join("sim/pxl8_sim.c")) - .file(pxl8_src.join("vxl/pxl8_vxl.c")) .include(pxl8_src.join("bsp")) .include(pxl8_src.join("core")) .include(pxl8_src.join("hal")) .include(pxl8_src.join("math")) .include(pxl8_src.join("net")) .include(pxl8_src.join("sim")) - .include(pxl8_src.join("vxl")) .compile("pxl8"); let bindings = bindgen::Builder::default() @@ -43,7 +38,6 @@ fn main() { .clang_arg(format!("-I{}", pxl8_src.join("math").display())) .clang_arg(format!("-I{}", pxl8_src.join("net").display())) .clang_arg(format!("-I{}", pxl8_src.join("sim").display())) - .clang_arg(format!("-I{}", pxl8_src.join("vxl").display())) .blocklist_item("FP_NAN") .blocklist_item("FP_INFINITE") .blocklist_item("FP_ZERO") diff --git a/pxl8d/src/chunk.rs b/pxl8d/src/chunk.rs index ec1c5f3..6283fca 100644 --- a/pxl8d/src/chunk.rs +++ b/pxl8d/src/chunk.rs @@ -4,55 +4,38 @@ pub mod stream; use crate::bsp::Bsp; use crate::math::Vec3; -use crate::pxl8::pxl8_vxl_trace; -use crate::voxel::VoxelChunk; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] pub enum ChunkId { Bsp(u32), - Vxl(i32, i32, i32), } pub enum Chunk { Bsp { id: u32, bsp: Bsp, version: u32 }, - Vxl { cx: i32, cy: i32, cz: i32, data: VoxelChunk, version: u32 }, } impl Chunk { pub fn id(&self) -> ChunkId { match self { Chunk::Bsp { id, .. } => ChunkId::Bsp(*id), - Chunk::Vxl { cx, cy, cz, .. } => ChunkId::Vxl(*cx, *cy, *cz), } } pub fn version(&self) -> u32 { match self { Chunk::Bsp { version, .. } => *version, - Chunk::Vxl { version, .. } => *version, } } pub fn trace(&self, from: Vec3, to: Vec3, radius: f32) -> Vec3 { match self { Chunk::Bsp { bsp, .. } => bsp.trace(from, to, radius), - Chunk::Vxl { cx, cy, cz, data, .. } => unsafe { - pxl8_vxl_trace(data.chunk, *cx, *cy, *cz, from, to, radius) - }, } } pub fn as_bsp(&self) -> Option<&Bsp> { match self { Chunk::Bsp { bsp, .. } => Some(bsp), - _ => None, - } - } - - pub fn as_vxl(&self) -> Option<&VoxelChunk> { - match self { - Chunk::Vxl { data, .. } => Some(data), - _ => None, } } } diff --git a/pxl8d/src/chunk/stream.rs b/pxl8d/src/chunk/stream.rs index 9ca9744..6b5f66e 100644 --- a/pxl8d/src/chunk/stream.rs +++ b/pxl8d/src/chunk/stream.rs @@ -4,9 +4,7 @@ use alloc::collections::BTreeMap; use alloc::vec::Vec; use crate::chunk::ChunkId; -use crate::math::Vec3; use crate::transport::ChunkMessage; -use crate::voxel::VoxelWorld; pub struct ClientChunkState { known: BTreeMap, @@ -29,22 +27,6 @@ impl ClientChunkState { } } - pub fn request_vxl_radius(&mut self, pos: Vec3, radius: i32, world: &VoxelWorld) { - let cx = VoxelWorld::world_to_chunk(pos.x); - let cy = VoxelWorld::world_to_chunk(pos.y); - let cz = VoxelWorld::world_to_chunk(pos.z); - - for dz in -radius..=radius { - for dy in -radius..=radius { - for dx in -radius..=radius { - if world.get_chunk(cx + dx, cy + dy, cz + dz).is_some() { - self.request(ChunkId::Vxl(cx + dx, cy + dy, cz + dz)); - } - } - } - } - } - pub fn next_pending(&mut self) -> Option { self.pending.pop() } @@ -78,10 +60,6 @@ impl ClientChunkState { self.pending_messages.clear(); } - pub fn clear_known_vxl(&mut self) { - self.known.retain(|id, _| !matches!(id, ChunkId::Vxl(_, _, _))); - } - pub fn clear_known_bsp(&mut self) { self.known.retain(|id, _| !matches!(id, ChunkId::Bsp(_))); } diff --git a/pxl8d/src/lib.rs b/pxl8d/src/lib.rs index 4834756..b7fbb33 100644 --- a/pxl8d/src/lib.rs +++ b/pxl8d/src/lib.rs @@ -10,7 +10,6 @@ pub mod math; pub mod procgen; pub mod sim; pub mod transport; -pub mod voxel; pub mod world; use core::panic::PanicInfo; @@ -36,5 +35,4 @@ pub use procgen::{ProcgenParams, generate, generate_rooms}; pub use pxl8::*; pub use sim::*; pub use transport::*; -pub use voxel::*; pub use world::*; diff --git a/pxl8d/src/main.rs b/pxl8d/src/main.rs index 760b91e..e67ec8a 100644 --- a/pxl8d/src/main.rs +++ b/pxl8d/src/main.rs @@ -6,7 +6,6 @@ extern crate alloc; use pxl8d::*; use pxl8d::chunk::ChunkId; use pxl8d::chunk::stream::ClientChunkState; -use pxl8d::math::Vec3; const TICK_RATE: u64 = 30; const TICK_NS: u64 = 1_000_000_000 / TICK_RATE; @@ -74,7 +73,6 @@ pub extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 { let mut player_id: Option = None; let mut last_client_tick: u64 = 0; let mut client_chunks = ClientChunkState::new(); - let mut client_stream_radius: i32 = 3; let mut sequence: u32 = 0; let mut last_tick = get_time_ns(); @@ -84,7 +82,6 @@ pub extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 { }; 64]; let mut inputs_buf: [pxl8d::pxl8_input_msg; 16] = unsafe { core::mem::zeroed() }; - pxl8_debug!("[SERVER] Entering main loop"); loop { let now = get_time_ns(); let elapsed = now.saturating_sub(last_tick); @@ -105,53 +102,43 @@ pub extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 { let (x, y, z, _yaw, _pitch) = extract_spawn_position(&cmd.payload); player_id = Some(sim.spawn_player(x, y, z) as u64); - pxl8_debug!("[SERVER] Spawn command received, generating BSP..."); sim.world.generate_bsp(1, None); sim.world.set_active(ChunkId::Bsp(1)); client_chunks.request(ChunkId::Bsp(1)); - pxl8_debug!("[SERVER] Sending CHUNK_ENTER for BSP id=1"); transport.send_chunk_enter(1, transport::CHUNK_TYPE_BSP, sequence); sequence = sequence.wrapping_add(1); - - pxl8_debug!("[SERVER] Preloading voxel chunks around spawn and door"); - let spawn_pos = Vec3 { x, y, z }; - let door_y = sim.voxels.find_surface_y(942.0, 416.0); - let door_pos = Vec3 { x: 942.0, y: door_y, z: 416.0 }; - pxl8_debug!("[SERVER] Door placed at surface y={}", door_y); - sim.voxels.load_chunks_around(spawn_pos, client_stream_radius); - sim.voxels.load_chunks_around(door_pos, client_stream_radius); - client_chunks.request_vxl_radius(spawn_pos, client_stream_radius, &sim.voxels); - client_chunks.request_vxl_radius(door_pos, client_stream_radius, &sim.voxels); - } else if cmd.cmd_type == pxl8d::pxl8_cmd_type::PXL8_CMD_EXIT_CHUNK as u16 { - sim.world.clear_active(); - client_chunks.clear_pending(); - client_chunks.clear_known_vxl(); - client_chunks.clear_known_bsp(); - transport.send_chunk_exit(sequence); - sequence = sequence.wrapping_add(1); - let exit_x = f32::from_be_bytes([cmd.payload[0], cmd.payload[1], cmd.payload[2], cmd.payload[3]]); - let exit_y = f32::from_be_bytes([cmd.payload[4], cmd.payload[5], cmd.payload[6], cmd.payload[7]]); - let exit_z = f32::from_be_bytes([cmd.payload[8], cmd.payload[9], cmd.payload[10], cmd.payload[11]]); - let exit_pos = Vec3 { x: exit_x, y: exit_y, z: exit_z }; - sim.teleport_player(exit_x, exit_y, exit_z); - sim.voxels.load_chunks_around(exit_pos, client_stream_radius); - client_chunks.request_vxl_radius(exit_pos, client_stream_radius, &sim.voxels); - } else if cmd.cmd_type == pxl8d::pxl8_cmd_type::PXL8_CMD_ENTER_CHUNK as u16 { + } else if cmd.cmd_type == pxl8d::pxl8_cmd_type::PXL8_CMD_ENTER_SCENE as u16 { let chunk_id = u32::from_be_bytes([ cmd.payload[0], cmd.payload[1], cmd.payload[2], cmd.payload[3] ]); - pxl8_debug!("[SERVER] Enter chunk command - entering BSP {}", chunk_id); - if sim.world.contains(&ChunkId::Bsp(chunk_id)) { - sim.world.set_active(ChunkId::Bsp(chunk_id)); - client_chunks.request(ChunkId::Bsp(chunk_id)); - transport.send_chunk_enter(chunk_id, transport::CHUNK_TYPE_BSP, sequence); - sequence = sequence.wrapping_add(1); - } - } else if cmd.cmd_type == pxl8d::pxl8_cmd_type::PXL8_CMD_SET_CHUNK_SETTINGS as u16 { - let render_dist = i32::from_be_bytes([ - cmd.payload[0], cmd.payload[1], cmd.payload[2], cmd.payload[3] - ]); - client_stream_radius = render_dist.clamp(1, 8); + let pos_x = f32::from_be_bytes([cmd.payload[8], cmd.payload[9], cmd.payload[10], cmd.payload[11]]); + let pos_y = f32::from_be_bytes([cmd.payload[12], cmd.payload[13], cmd.payload[14], cmd.payload[15]]); + let pos_z = f32::from_be_bytes([cmd.payload[16], cmd.payload[17], cmd.payload[18], cmd.payload[19]]); + + sim.world.clear_active(); + client_chunks.clear_pending(); + client_chunks.clear_known_bsp(); + transport.send_chunk_exit(sequence); + sequence = sequence.wrapping_add(1); + + let params = match chunk_id { + 2 => Some(ProcgenParams { + width: 20, + height: 20, + seed: 12345u32.wrapping_add(2), + min_room_size: 14, + max_room_size: 16, + num_rooms: 1, + }), + _ => None, + }; + sim.world.generate_bsp(chunk_id, params.as_ref()); + sim.world.set_active(ChunkId::Bsp(chunk_id)); + client_chunks.request(ChunkId::Bsp(chunk_id)); + transport.send_chunk_enter(chunk_id, transport::CHUNK_TYPE_BSP, sequence); + sequence = sequence.wrapping_add(1); + + sim.teleport_player(pos_x, pos_y, pos_z); } } _ => {} @@ -186,12 +173,7 @@ pub extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 { sequence = sequence.wrapping_add(1); if let Some(pid) = player_id { - if let Some(player) = sim.get_player_position(pid) { - let pos = Vec3 { x: player.0, y: player.1, z: player.2 }; - - sim.voxels.load_chunks_around(pos, client_stream_radius); - client_chunks.request_vxl_radius(pos, client_stream_radius, &sim.voxels); - + if let Some(_player) = sim.get_player_position(pid) { while let Some(chunk_id) = client_chunks.next_pending() { match chunk_id { ChunkId::Bsp(id) => { @@ -203,13 +185,6 @@ pub extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 { } } } - ChunkId::Vxl(cx, cy, cz) => { - if let Some(chunk) = sim.voxels.get_chunk(cx, cy, cz) { - let msgs = transport::ChunkMessage::from_voxel(chunk, 1); - client_chunks.queue_messages(msgs); - client_chunks.mark_sent(chunk_id, 1); - } - } } } @@ -230,8 +205,6 @@ pub extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 { fn bsp_to_messages(bsp: &bsp::Bsp, id: u32, version: u32) -> alloc::vec::Vec { use alloc::vec::Vec; - pxl8_debug!("[SERVER] bsp_to_messages: serializing BSP"); - let mut data = Vec::new(); let num_verts = bsp.vertices.len(); diff --git a/pxl8d/src/sim.rs b/pxl8d/src/sim.rs index 4494a90..51d8ba9 100644 --- a/pxl8d/src/sim.rs +++ b/pxl8d/src/sim.rs @@ -4,7 +4,6 @@ use alloc::vec::Vec; use crate::math::Vec3; use crate::pxl8::*; -use crate::voxel::VoxelWorld; use crate::world::World; pub type Entity = pxl8_sim_entity; @@ -48,7 +47,6 @@ pub struct Simulation { pub player: Option, pub tick: u64, pub time: f32, - pub voxels: VoxelWorld, pub world: World, } @@ -68,7 +66,6 @@ impl Simulation { player: None, tick: 0, time: 0.0, - voxels: VoxelWorld::new(12345), world: World::new(12345), } } @@ -96,8 +93,6 @@ impl Simulation { ent.pos = Vec3::new(x, y, z); self.player = Some(id); - self.voxels.load_chunks_around(ent.pos, 2); - id } @@ -119,43 +114,33 @@ impl Simulation { self.integrate(dt); } - fn make_sim_world(&self, pos: Vec3) -> pxl8_sim_world { + fn make_sim_world(&self, _pos: Vec3) -> pxl8_sim_world { if let Some(chunk) = self.world.active() { if let Some(bsp) = chunk.as_bsp() { return pxl8_sim_world { bsp: bsp.as_c_bsp(), - vxl: core::ptr::null(), - vxl_cx: 0, - vxl_cy: 0, - vxl_cz: 0, }; } } - let cx = VoxelWorld::world_to_chunk(pos.x); - let cy = VoxelWorld::world_to_chunk(pos.y); - let cz = VoxelWorld::world_to_chunk(pos.z); - - if let Some(chunk) = self.voxels.get_chunk(cx, cy, cz) { - pxl8_sim_world { - bsp: core::ptr::null(), - vxl: chunk.chunk as *const _, - vxl_cx: cx, - vxl_cy: cy, - vxl_cz: cz, - } - } else { - pxl8_sim_world { - bsp: core::ptr::null(), - vxl: core::ptr::null(), - vxl_cx: 0, - vxl_cy: 0, - vxl_cz: 0, - } + pxl8_sim_world { + bsp: core::ptr::null(), } } fn integrate(&mut self, dt: f32) { + let cfg = pxl8_sim_config { + move_speed: 180.0, + ground_accel: 10.0, + air_accel: 1.0, + stop_speed: 100.0, + friction: 6.0, + gravity: 800.0, + jump_velocity: 200.0, + player_radius: 16.0, + player_height: 72.0, + max_pitch: 1.5, + }; for i in 0..self.entities.len() { let ent = &self.entities[i]; if ent.flags & ALIVE == 0 || ent.flags & PLAYER != 0 { @@ -165,12 +150,24 @@ impl Simulation { let world = self.make_sim_world(ent.pos); let ent = &mut self.entities[i]; unsafe { - pxl8_sim_integrate(ent, &world, dt); + pxl8_sim_integrate(ent, &world, &cfg, dt); } } } fn move_player(&mut self, input: &pxl8_input_msg, dt: f32) { + let cfg = pxl8_sim_config { + move_speed: 180.0, + ground_accel: 10.0, + air_accel: 1.0, + stop_speed: 100.0, + friction: 6.0, + gravity: 800.0, + jump_velocity: 200.0, + player_radius: 16.0, + player_height: 72.0, + max_pitch: 1.5, + }; let Some(id) = self.player else { return }; let ent = &self.entities[id as usize]; if ent.flags & ALIVE == 0 { @@ -180,7 +177,7 @@ impl Simulation { let world = self.make_sim_world(ent.pos); let ent = &mut self.entities[id as usize]; unsafe { - pxl8_sim_move_player(ent, input, &world, dt); + pxl8_sim_move_player(ent, input, &world, &cfg, dt); } } diff --git a/pxl8d/src/transport.rs b/pxl8d/src/transport.rs index b317a49..3d12652 100644 --- a/pxl8d/src/transport.rs +++ b/pxl8d/src/transport.rs @@ -4,14 +4,11 @@ use alloc::vec; use alloc::vec::Vec; use crate::pxl8::*; use crate::pxl8::pxl8_msg_type::*; -use crate::voxel::VoxelChunk; - pub const DEFAULT_PORT: u16 = 7777; pub const CHUNK_MAX_PAYLOAD: usize = 1400; pub const CHUNK_FLAG_RLE: u8 = 0x01; pub const CHUNK_FLAG_FINAL: u8 = 0x04; -pub const CHUNK_TYPE_VXL: u8 = 0; pub const CHUNK_TYPE_BSP: u8 = 1; pub struct ChunkMessage { @@ -28,50 +25,6 @@ pub struct ChunkMessage { } impl ChunkMessage { - pub fn from_voxel(chunk: &VoxelChunk, version: u32) -> Vec { - let rle_data = chunk.rle_encode(); - let total_size = rle_data.len(); - - if total_size <= CHUNK_MAX_PAYLOAD { - return vec![ChunkMessage { - chunk_type: CHUNK_TYPE_VXL, - id: 0, - cx: chunk.cx, - cy: chunk.cy, - cz: chunk.cz, - version, - flags: CHUNK_FLAG_RLE | CHUNK_FLAG_FINAL, - fragment_idx: 0, - fragment_count: 1, - payload: rle_data, - }]; - } - - let fragment_count = (total_size + CHUNK_MAX_PAYLOAD - 1) / CHUNK_MAX_PAYLOAD; - let mut messages = Vec::new(); - - for i in 0..fragment_count { - let start = i * CHUNK_MAX_PAYLOAD; - let end = ((i + 1) * CHUNK_MAX_PAYLOAD).min(total_size); - let is_final = i == fragment_count - 1; - - messages.push(ChunkMessage { - chunk_type: CHUNK_TYPE_VXL, - id: 0, - cx: chunk.cx, - cy: chunk.cy, - cz: chunk.cz, - version, - flags: CHUNK_FLAG_RLE | if is_final { CHUNK_FLAG_FINAL } else { 0 }, - fragment_idx: i as u8, - fragment_count: fragment_count as u8, - payload: rle_data[start..end].to_vec(), - }); - } - - messages - } - pub fn from_bsp(data: Vec, chunk_id: u32, version: u32) -> Vec { let total_size = data.len(); diff --git a/pxl8d/src/voxel.rs b/pxl8d/src/voxel.rs deleted file mode 100644 index 5c46dc5..0000000 --- a/pxl8d/src/voxel.rs +++ /dev/null @@ -1,379 +0,0 @@ -extern crate alloc; - -use alloc::vec; -use alloc::vec::Vec; -use core::ptr; - -use crate::math::Vec3; -use crate::pxl8::*; - -const CHUNK_SIZE: i32 = PXL8_VXL_CHUNK_SIZE as i32; -const CHUNK_VOLUME: usize = PXL8_VXL_CHUNK_VOLUME as usize; -const WORLD_CHUNK_SIZE: f32 = PXL8_VXL_WORLD_CHUNK_SIZE as f32; -const AIR: u8 = PXL8_VXL_BLOCK_AIR as u8; -const GRASS: u8 = 3; -const DIRT: u8 = 2; -const STONE: u8 = 1; - -pub struct VoxelChunk { - pub chunk: *mut pxl8_vxl_chunk, - pub cx: i32, - pub cy: i32, - pub cz: i32, -} - -impl VoxelChunk { - pub fn new(cx: i32, cy: i32, cz: i32) -> Self { - let chunk = unsafe { pxl8_vxl_chunk_create() }; - Self { chunk, cx, cy, cz } - } - - pub fn get(&self, x: i32, y: i32, z: i32) -> u8 { - if self.chunk.is_null() { - return AIR; - } - unsafe { pxl8_vxl_block_get(self.chunk, x, y, z) } - } - - pub fn set(&mut self, x: i32, y: i32, z: i32, block: u8) { - if self.chunk.is_null() { - return; - } - unsafe { pxl8_vxl_block_set(self.chunk, x, y, z, block) } - } - - pub fn fill(&mut self, block: u8) { - if self.chunk.is_null() { - return; - } - unsafe { pxl8_vxl_block_fill(self.chunk, block) } - } - - pub fn is_uniform(&self) -> bool { - if self.chunk.is_null() { - return true; - } - unsafe { pxl8_vxl_chunk_is_uniform(self.chunk) } - } - - pub fn rle_encode(&self) -> Vec { - if self.chunk.is_null() { - return Vec::new(); - } - - let mut linear = vec![0u8; CHUNK_VOLUME]; - unsafe { - pxl8_vxl_chunk_linearize(self.chunk, linear.as_mut_ptr()); - } - - let mut result = Vec::new(); - let mut i = 0; - - while i < CHUNK_VOLUME { - let block = linear[i]; - let mut run_len = 1usize; - - while i + run_len < CHUNK_VOLUME - && linear[i + run_len] == block - && run_len < 256 - { - run_len += 1; - } - - result.push(block); - result.push((run_len - 1) as u8); - i += run_len; - } - - result - } -} - -impl Drop for VoxelChunk { - fn drop(&mut self) { - if !self.chunk.is_null() { - unsafe { pxl8_vxl_chunk_destroy(self.chunk) }; - self.chunk = ptr::null_mut(); - } - } -} - -impl Clone for VoxelChunk { - fn clone(&self) -> Self { - let new_chunk = Self::new(self.cx, self.cy, self.cz); - for z in 0..CHUNK_SIZE { - for y in 0..CHUNK_SIZE { - for x in 0..CHUNK_SIZE { - let block = self.get(x, y, z); - if block != AIR { - unsafe { - pxl8_vxl_block_set(new_chunk.chunk, x, y, z, block); - } - } - } - } - } - new_chunk - } -} - -pub struct VoxelWorld { - pub chunks: Vec, - pub seed: u64, -} - -impl VoxelWorld { - pub fn new(seed: u64) -> Self { - Self { - chunks: Vec::new(), - seed, - } - } - - pub fn get_chunk(&self, cx: i32, cy: i32, cz: i32) -> Option<&VoxelChunk> { - self.chunks.iter().find(|c| c.cx == cx && c.cy == cy && c.cz == cz) - } - - pub fn get_chunk_mut(&mut self, cx: i32, cy: i32, cz: i32) -> Option<&mut VoxelChunk> { - self.chunks.iter_mut().find(|c| c.cx == cx && c.cy == cy && c.cz == cz) - } - - pub fn ensure_chunk(&mut self, cx: i32, cy: i32, cz: i32) -> &mut VoxelChunk { - if self.get_chunk(cx, cy, cz).is_none() { - let mut chunk = VoxelChunk::new(cx, cy, cz); - generate_chunk(&mut chunk, self.seed); - self.chunks.push(chunk); - } - self.get_chunk_mut(cx, cy, cz).unwrap() - } - - pub fn world_to_chunk(x: f32) -> i32 { - libm::floorf(x / WORLD_CHUNK_SIZE) as i32 - } - - pub fn chunks_near(&self, pos: Vec3, radius: i32) -> Vec<(i32, i32, i32)> { - let cx = Self::world_to_chunk(pos.x); - let cy = Self::world_to_chunk(pos.y); - let cz = Self::world_to_chunk(pos.z); - - let mut result = Vec::new(); - for dz in -radius..=radius { - for dy in -radius..=radius { - for dx in -radius..=radius { - result.push((cx + dx, cy + dy, cz + dz)); - } - } - } - result - } - - pub fn load_chunks_around(&mut self, pos: Vec3, radius: i32) { - let coords = self.chunks_near(pos, radius); - for (cx, cy, cz) in coords { - self.ensure_chunk(cx, cy, cz); - } - } - - pub fn find_surface_y(&mut self, world_x: f32, world_z: f32) -> f32 { - let scale = PXL8_VXL_SCALE as f32; - let block_x = libm::floorf(world_x / scale) as i32; - let block_z = libm::floorf(world_z / scale) as i32; - - let cx = libm::floorf(block_x as f32 / CHUNK_SIZE as f32) as i32; - let cz = libm::floorf(block_z as f32 / CHUNK_SIZE as f32) as i32; - let lx = ((block_x % CHUNK_SIZE) + CHUNK_SIZE) % CHUNK_SIZE; - let lz = ((block_z % CHUNK_SIZE) + CHUNK_SIZE) % CHUNK_SIZE; - - for cy in (-2..=2).rev() { - self.ensure_chunk(cx, cy, cz); - if let Some(chunk) = self.get_chunk(cx, cy, cz) { - for ly in (0..CHUNK_SIZE).rev() { - let block = chunk.get(lx, ly, lz); - if block == GRASS { - let world_y = (cy * CHUNK_SIZE + ly + 1) as f32 * scale; - return world_y; - } - } - } - } - - 0.0 - } -} - -fn noise3d(x: i32, y: i32, z: i32, seed: u64) -> f32 { - let h = hash(seed ^ (x as u64) ^ ((y as u64) << 21) ^ ((z as u64) << 42)); - (h & 0xFFFF) as f32 / 65535.0 -} - -fn hash(mut x: u64) -> u64 { - x ^= x >> 33; - x = x.wrapping_mul(0xff51afd7ed558ccd); - x ^= x >> 33; - x = x.wrapping_mul(0xc4ceb9fe1a85ec53); - x ^= x >> 33; - x -} - -fn smoothstep(t: f32) -> f32 { - t * t * (3.0 - 2.0 * t) -} - -fn lerp(a: f32, b: f32, t: f32) -> f32 { - a + (b - a) * t -} - -fn value_noise_3d(x: f32, y: f32, z: f32, seed: u64) -> f32 { - let x0 = libm::floorf(x) as i32; - let y0 = libm::floorf(y) as i32; - let z0 = libm::floorf(z) as i32; - let x1 = x0 + 1; - let y1 = y0 + 1; - let z1 = z0 + 1; - - let tx = smoothstep(x - x0 as f32); - let ty = smoothstep(y - y0 as f32); - let tz = smoothstep(z - z0 as f32); - - let c000 = noise3d(x0, y0, z0, seed); - let c100 = noise3d(x1, y0, z0, seed); - let c010 = noise3d(x0, y1, z0, seed); - let c110 = noise3d(x1, y1, z0, seed); - let c001 = noise3d(x0, y0, z1, seed); - let c101 = noise3d(x1, y0, z1, seed); - let c011 = noise3d(x0, y1, z1, seed); - let c111 = noise3d(x1, y1, z1, seed); - - let a00 = lerp(c000, c100, tx); - let a10 = lerp(c010, c110, tx); - let a01 = lerp(c001, c101, tx); - let a11 = lerp(c011, c111, tx); - - let b0 = lerp(a00, a10, ty); - let b1 = lerp(a01, a11, ty); - - lerp(b0, b1, tz) -} - -fn fbm_3d(x: f32, y: f32, z: f32, seed: u64, octaves: u32) -> f32 { - let mut value = 0.0; - let mut amplitude = 1.0; - let mut frequency = 1.0; - let mut max_value = 0.0; - - for i in 0..octaves { - value += amplitude * value_noise_3d( - x * frequency, - y * frequency, - z * frequency, - seed.wrapping_add(i as u64 * 1000) - ); - max_value += amplitude; - amplitude *= 0.5; - frequency *= 2.0; - } - - value / max_value -} - -fn fbm_2d(x: f32, z: f32, seed: u64, octaves: u32) -> f32 { - let mut value = 0.0; - let mut amplitude = 1.0; - let mut frequency = 1.0; - let mut max_value = 0.0; - - for i in 0..octaves { - let x0 = libm::floorf(x * frequency) as i32; - let z0 = libm::floorf(z * frequency) as i32; - let x1 = x0 + 1; - let z1 = z0 + 1; - - let tx = smoothstep(x * frequency - x0 as f32); - let tz = smoothstep(z * frequency - z0 as f32); - - let offset_seed = seed.wrapping_add(i as u64 * 1000); - let c00 = (hash(offset_seed ^ (x0 as u64) ^ ((z0 as u64) << 32)) & 0xFFFF) as f32 / 65535.0; - let c10 = (hash(offset_seed ^ (x1 as u64) ^ ((z0 as u64) << 32)) & 0xFFFF) as f32 / 65535.0; - let c01 = (hash(offset_seed ^ (x0 as u64) ^ ((z1 as u64) << 32)) & 0xFFFF) as f32 / 65535.0; - let c11 = (hash(offset_seed ^ (x1 as u64) ^ ((z1 as u64) << 32)) & 0xFFFF) as f32 / 65535.0; - - let a = lerp(c00, c10, tx); - let b = lerp(c01, c11, tx); - value += amplitude * lerp(a, b, tz); - - max_value += amplitude; - amplitude *= 0.5; - frequency *= 2.0; - } - - value / max_value -} - -fn generate_chunk(chunk: &mut VoxelChunk, seed: u64) { - let world_x = chunk.cx * CHUNK_SIZE; - let world_y = chunk.cy * CHUNK_SIZE; - let world_z = chunk.cz * CHUNK_SIZE; - - let mut height_cache = [[0i32; 32]; 32]; - for lz in 0..32 { - for lx in 0..32 { - let wx = (world_x + lx as i32) as f32; - let wz = (world_z + lz as i32) as f32; - height_cache[lz][lx] = (fbm_2d(wx * 0.02, wz * 0.02, seed, 4) * 32.0) as i32; - } - } - - let mut density = [[[0.0f32; 33]; 33]; 33]; - for lz in 0..33 { - for ly in 0..33 { - for lx in 0..33 { - let wx = (world_x + lx as i32) as f32; - let wy = (world_y + ly as i32) as f32; - let wz = (world_z + lz as i32) as f32; - - let hx = lx.min(31); - let hz = lz.min(31); - let base_height = height_cache[hz][hx] as f32; - let height_bias = (base_height - wy) * 0.1; - - let cave_noise = fbm_3d(wx * 0.05, wy * 0.05, wz * 0.05, seed.wrapping_add(1000), 2); - density[lz][ly][lx] = height_bias + (cave_noise - 0.55); - } - } - } - - for lz in 0..CHUNK_SIZE { - for ly in 0..CHUNK_SIZE { - for lx in 0..CHUNK_SIZE { - let d = density[lz as usize][ly as usize][lx as usize]; - if d <= 0.0 { - continue; - } - - let d_above = density[lz as usize][(ly + 1) as usize][lx as usize]; - let is_surface = d_above <= 0.0; - - let block = if is_surface { - GRASS - } else { - let wy = world_y + ly; - let base_height = height_cache[lz as usize][lx as usize]; - let depth = base_height - wy; - if depth < 4 { - DIRT - } else { - STONE - } - }; - - chunk.set(lx, ly, lz, block); - } - } - } -} - -impl Default for VoxelWorld { - fn default() -> Self { - Self::new(12345) - } -} diff --git a/src/core/pxl8.c b/src/core/pxl8.c index 1895703..5d9d794 100644 --- a/src/core/pxl8.c +++ b/src/core/pxl8.c @@ -391,18 +391,6 @@ pxl8_result pxl8_update(pxl8* sys) { } #ifdef PXL8_ASYNC_THREADS - if (game->world) { - pxl8_input_msg msg = {0}; - msg.move_x = (pxl8_key_down(&game->input, "d") ? 1.0f : 0.0f) - (pxl8_key_down(&game->input, "a") ? 1.0f : 0.0f); - msg.move_y = (pxl8_key_down(&game->input, "w") ? 1.0f : 0.0f) - (pxl8_key_down(&game->input, "s") ? 1.0f : 0.0f); - if (game->input.mouse_relative_mode) { - msg.look_dx = (f32)pxl8_mouse_dx(&game->input); - msg.look_dy = (f32)pxl8_mouse_dy(&game->input); - } - msg.buttons = pxl8_key_down(&game->input, "space") ? 1 : 0; - - pxl8_world_push_input(game->world, &msg); - } pxl8_net_update(game->net, dt); #else if (game->net) { @@ -410,7 +398,7 @@ pxl8_result pxl8_update(pxl8* sys) { pxl8_net_update(game->net, dt); pxl8_world_sync(game->world, game->net); } - pxl8_world_update(game->world, &game->input, dt); + pxl8_world_update(game->world, dt); #endif pxl8_gfx_update(game->gfx, dt); diff --git a/src/core/pxl8_io.c b/src/core/pxl8_io.c index 3de6ca1..1a0206c 100644 --- a/src/core/pxl8_io.c +++ b/src/core/pxl8_io.c @@ -167,6 +167,13 @@ bool pxl8_key_down(const pxl8_input_state* input, const char* key_name) { return input->keys_down[key]; } +void pxl8_set_key_down(pxl8_input_state* input, const char* key_name, bool down) { + if (!input) return; + i32 key = pxl8_key_code(key_name); + if (key < 0 || key >= PXL8_MAX_KEYS) return; + input->keys_down[key] = down; +} + bool pxl8_key_pressed(const pxl8_input_state* input, const char* key_name) { if (!input) return false; i32 key = pxl8_key_code(key_name); diff --git a/src/core/pxl8_io.h b/src/core/pxl8_io.h index 9770ebe..7ad1063 100644 --- a/src/core/pxl8_io.h +++ b/src/core/pxl8_io.h @@ -23,6 +23,7 @@ pxl8_result pxl8_io_write_file(const char* path, const char* content, usize size bool pxl8_key_down(const pxl8_input_state* input, const char* key_name); bool pxl8_key_pressed(const pxl8_input_state* input, const char* key_name); bool pxl8_key_released(const pxl8_input_state* input, const char* key_name); +void pxl8_set_key_down(pxl8_input_state* input, const char* key_name, bool down); i32 pxl8_mouse_dx(const pxl8_input_state* input); i32 pxl8_mouse_dy(const pxl8_input_state* input); diff --git a/src/gfx/pxl8_gfx.c b/src/gfx/pxl8_gfx.c index 06b5dd8..de4ad2c 100644 --- a/src/gfx/pxl8_gfx.c +++ b/src/gfx/pxl8_gfx.c @@ -1028,15 +1028,6 @@ u8 pxl8_gfx_find_closest_color(pxl8_gfx* gfx, u8 r, u8 g, u8 b) { return pxl8_palette_find_closest(gfx->palette, r, g, b); } -u8 pxl8_gfx_ui_color(pxl8_gfx* gfx, u8 index) { - if (!gfx || !gfx->palette || index >= PXL8_UI_PALETTE_SIZE) return 0; - u32 abgr = pxl8_ui_palette[index]; - u8 r = (abgr >> 0) & 0xFF; - u8 g = (abgr >> 8) & 0xFF; - u8 b = (abgr >> 16) & 0xFF; - return pxl8_palette_find_closest(gfx->palette, r, g, b); -} - void pxl8_gfx_set_wireframe(pxl8_gfx* gfx, bool enabled) { if (gfx) gfx->wireframe = enabled; } diff --git a/src/gfx/pxl8_gfx.h b/src/gfx/pxl8_gfx.h index 196b486..ad13479 100644 --- a/src/gfx/pxl8_gfx.h +++ b/src/gfx/pxl8_gfx.h @@ -7,7 +7,6 @@ #include "pxl8_colormap.h" #include "pxl8_palette.h" #include "pxl8_types.h" -#include "pxl8_gui_palette.h" typedef struct pxl8_gfx pxl8_gfx; @@ -77,7 +76,6 @@ void pxl8_gfx_colormap_update(pxl8_gfx* gfx); void pxl8_gfx_ensure_blend_tables(pxl8_gfx* gfx); u8 pxl8_gfx_find_closest_color(pxl8_gfx* gfx, u8 r, u8 g, u8 b); -u8 pxl8_gfx_ui_color(pxl8_gfx* gfx, u8 index); void pxl8_gfx_set_wireframe(pxl8_gfx* gfx, bool enabled); bool pxl8_gfx_get_wireframe(const pxl8_gfx* gfx); diff --git a/src/gui/pxl8_gui.c b/src/gui/pxl8_gui.c index 766091a..26dd4d8 100644 --- a/src/gui/pxl8_gui.c +++ b/src/gui/pxl8_gui.c @@ -4,6 +4,7 @@ #include #include "pxl8_gfx.h" +#include "pxl8_gui_palette.h" #include "pxl8_mem.h" pxl8_gui_state* pxl8_gui_state_create(void) { @@ -83,16 +84,16 @@ bool pxl8_gui_button(pxl8_gui_state* state, pxl8_gfx* gfx, u32 id, i32 x, i32 y, i32 offset_y = 0; if (is_active) { - bg_color = pxl8_gfx_ui_color(gfx, PXL8_UI_BG3); - border_color = pxl8_gfx_ui_color(gfx, PXL8_UI_BG2); + bg_color = pxl8_gui_color(gfx, PXL8_UI_BG3); + border_color = pxl8_gui_color(gfx, PXL8_UI_BG2); offset_x = 1; offset_y = 1; } else if (is_hot || cursor_over) { - bg_color = pxl8_gfx_ui_color(gfx, PXL8_UI_BG3); - border_color = pxl8_gfx_ui_color(gfx, PXL8_UI_FG0); + bg_color = pxl8_gui_color(gfx, PXL8_UI_BG3); + border_color = pxl8_gui_color(gfx, PXL8_UI_FG0); } else { - bg_color = pxl8_gfx_ui_color(gfx, PXL8_UI_BG2); - border_color = pxl8_gfx_ui_color(gfx, PXL8_UI_BG3); + bg_color = pxl8_gui_color(gfx, PXL8_UI_BG2); + border_color = pxl8_gui_color(gfx, PXL8_UI_BG3); } pxl8_2d_rect_fill(gfx, x, y, w, h, bg_color); @@ -101,11 +102,20 @@ bool pxl8_gui_button(pxl8_gui_state* state, pxl8_gfx* gfx, u32 id, i32 x, i32 y, i32 text_len = (i32)strlen(label); i32 text_x = x + (w / 2) - ((text_len * 8) / 2) + offset_x; i32 text_y = y + (h / 2) - 5 + offset_y; - pxl8_2d_text(gfx, label, text_x, text_y, pxl8_gfx_ui_color(gfx, PXL8_UI_FG1)); + pxl8_2d_text(gfx, label, text_x, text_y, pxl8_gui_color(gfx, PXL8_UI_FG0)); return clicked; } +u8 pxl8_gui_color(pxl8_gfx* gfx, u8 index) { + if (!gfx || index >= PXL8_UI_PALETTE_SIZE) return 0; + u32 abgr = pxl8_ui_palette[index]; + u8 r = (abgr >> 0) & 0xFF; + u8 g = (abgr >> 8) & 0xFF; + u8 b = (abgr >> 16) & 0xFF; + return pxl8_gfx_find_closest_color(gfx, r, g, b); +} + bool pxl8_gui_slider(pxl8_gui_state* state, pxl8_gfx* gfx, u32 id, i32 x, i32 y, i32 w, i32 h, f32* value, f32 min_val, f32 max_val) { if (!state || !gfx || !value) return false; @@ -132,9 +142,9 @@ bool pxl8_gui_slider(pxl8_gui_state* state, pxl8_gfx* gfx, u32 id, i32 x, i32 y, } } - u8 bg_color = pxl8_gfx_ui_color(gfx, PXL8_UI_BG1); - u8 fill_color = pxl8_gfx_ui_color(gfx, is_active ? PXL8_UI_FG0 : PXL8_UI_BG3); - u8 handle_color = pxl8_gfx_ui_color(gfx, PXL8_UI_FG1); + u8 bg_color = pxl8_gui_color(gfx, PXL8_UI_BG1); + u8 fill_color = pxl8_gui_color(gfx, is_active ? PXL8_UI_FG0 : PXL8_UI_BG3); + u8 handle_color = pxl8_gui_color(gfx, PXL8_UI_FG0); pxl8_2d_rect_fill(gfx, x, y, w, h, bg_color); @@ -166,10 +176,10 @@ bool pxl8_gui_slider_int(pxl8_gui_state* state, pxl8_gfx* gfx, u32 id, i32 x, i3 void pxl8_gui_window(pxl8_gfx* gfx, i32 x, i32 y, i32 w, i32 h, const char* title) { if (!gfx || !title) return; - u8 title_bg = pxl8_gfx_ui_color(gfx, PXL8_UI_BG1); - u8 body_bg = pxl8_gfx_ui_color(gfx, PXL8_UI_BG2); - u8 border = pxl8_gfx_ui_color(gfx, PXL8_UI_BG3); - u8 title_fg = pxl8_gfx_ui_color(gfx, PXL8_UI_FG0); + u8 title_bg = pxl8_gui_color(gfx, PXL8_UI_BG1); + u8 body_bg = pxl8_gui_color(gfx, PXL8_UI_BG2); + u8 border = pxl8_gui_color(gfx, PXL8_UI_BG3); + u8 title_fg = pxl8_gui_color(gfx, PXL8_UI_FG0); pxl8_2d_rect_fill(gfx, x, y, w, 28, title_bg); pxl8_2d_rect_fill(gfx, x, y + 28, w, h - 28, body_bg); diff --git a/src/gui/pxl8_gui.h b/src/gui/pxl8_gui.h index 982a3ff..9dcf770 100644 --- a/src/gui/pxl8_gui.h +++ b/src/gui/pxl8_gui.h @@ -30,9 +30,10 @@ void pxl8_gui_cursor_move(pxl8_gui_state* state, i32 x, i32 y); void pxl8_gui_cursor_up(pxl8_gui_state* state); bool pxl8_gui_button(pxl8_gui_state* state, pxl8_gfx* gfx, u32 id, i32 x, i32 y, i32 w, i32 h, const char* label); +u8 pxl8_gui_color(pxl8_gfx* gfx, u8 index); +void pxl8_gui_label(pxl8_gfx* gfx, i32 x, i32 y, const char* text, u8 color); bool pxl8_gui_slider(pxl8_gui_state* state, pxl8_gfx* gfx, u32 id, i32 x, i32 y, i32 w, i32 h, f32* value, f32 min_val, f32 max_val); bool pxl8_gui_slider_int(pxl8_gui_state* state, pxl8_gfx* gfx, u32 id, i32 x, i32 y, i32 w, i32 h, i32* value, i32 min_val, i32 max_val); -void pxl8_gui_label(pxl8_gfx* gfx, i32 x, i32 y, const char* text, u8 color); void pxl8_gui_window(pxl8_gfx* gfx, i32 x, i32 y, i32 w, i32 h, const char* title); #ifdef __cplusplus diff --git a/src/gui/pxl8_gui_palette.h b/src/gui/pxl8_gui_palette.h index 2d87599..91d8533 100644 --- a/src/gui/pxl8_gui_palette.h +++ b/src/gui/pxl8_gui_palette.h @@ -22,7 +22,7 @@ #define PXL8_UI_GRAY 15 static const u32 pxl8_ui_palette[PXL8_UI_PALETTE_SIZE] = { - 0xFF282828, + 0xFF21201d, 0xFF3c3836, 0xFF504945, 0xFF665c54, diff --git a/src/lua/pxl8.lua b/src/lua/pxl8.lua index 1bc830c..e7cc4dd 100644 --- a/src/lua/pxl8.lua +++ b/src/lua/pxl8.lua @@ -68,6 +68,7 @@ pxl8.pop_target = gfx.pop_target pxl8.key_down = input.key_down pxl8.key_pressed = input.key_pressed pxl8.key_released = input.key_released +pxl8.set_key_down = input.set_key_down pxl8.mouse_dx = input.mouse_dx pxl8.mouse_dy = input.mouse_dy pxl8.mouse_wheel_x = input.mouse_wheel_x @@ -132,6 +133,7 @@ pxl8.Gui = gui.Gui pxl8.create_gui = gui.Gui.new pxl8.gui_label = gui.label pxl8.gui_window = gui.window +pxl8.gui_color = gui.color pxl8.mat4_identity = math.mat4_identity pxl8.mat4_lookat = math.mat4_lookat @@ -235,8 +237,12 @@ pxl8.unpack_u64_le = bytes.unpack_u64_le pxl8.Bsp = world.Bsp pxl8.Chunk = world.Chunk pxl8.CHUNK_BSP = world.CHUNK_BSP -pxl8.CHUNK_VXL = world.CHUNK_VXL pxl8.World = world.World pxl8.get_world = world.World.get +pxl8.sim_config = world.sim_config +pxl8.sim_move_player = world.sim_move_player +pxl8.sim_trace = world.sim_trace +pxl8.sim_check_ground = world.sim_check_ground +pxl8.make_input_msg = world.make_input_msg return pxl8 diff --git a/src/lua/pxl8/gui.lua b/src/lua/pxl8/gui.lua index 395f584..b1a2f02 100644 --- a/src/lua/pxl8/gui.lua +++ b/src/lua/pxl8/gui.lua @@ -79,4 +79,8 @@ function gui.window(x, y, w, h, title) C.pxl8_gui_window(core.gfx, x, y, w, h, title) end +function gui.color(index) + return C.pxl8_gui_color(core.gfx, index) +end + return gui diff --git a/src/lua/pxl8/input.lua b/src/lua/pxl8/input.lua index 855e1aa..54b4d94 100644 --- a/src/lua/pxl8/input.lua +++ b/src/lua/pxl8/input.lua @@ -16,6 +16,10 @@ function input.key_released(key) return C.pxl8_key_released(core.input, key) end +function input.set_key_down(key, down) + C.pxl8_set_key_down(core.input, key, down) +end + function input.mouse_wheel_x() return C.pxl8_mouse_wheel_x(core.input) end diff --git a/src/lua/pxl8/net.lua b/src/lua/pxl8/net.lua index ef59901..5d4b17e 100644 --- a/src/lua/pxl8/net.lua +++ b/src/lua/pxl8/net.lua @@ -23,12 +23,8 @@ function Net:connected() return C.pxl8_net_connected(self._ptr) end -function Net:enter_chunk(chunk_id) - return C.pxl8_net_enter_chunk(self._ptr, chunk_id or 1) == 0 -end - -function Net:exit_chunk(x, y, z) - return C.pxl8_net_exit_chunk(self._ptr, x or 0, y or 0, z or 0) == 0 +function Net:enter_scene(chunk_type, chunk_id, x, y, z) + return C.pxl8_net_enter_scene(self._ptr, chunk_type or 0, chunk_id or 0, x or 0, y or 0, z or 0) == 0 end function Net:send_input(input) @@ -44,10 +40,6 @@ function Net:send_input(input) return C.pxl8_net_send_input(self._ptr, msg) == 0 end -function Net:set_chunk_settings(render_distance, sim_distance) - return C.pxl8_net_send_chunk_settings(self._ptr, render_distance or 3, sim_distance or 4) == 0 -end - function Net:spawn(x, y, z, yaw, pitch) return C.pxl8_net_spawn(self._ptr, x or 0, y or 0, z or 0, yaw or 0, pitch or 0) == 0 end diff --git a/src/lua/pxl8/world.lua b/src/lua/pxl8/world.lua index 27bc6b6..152ef84 100644 --- a/src/lua/pxl8/world.lua +++ b/src/lua/pxl8/world.lua @@ -4,9 +4,6 @@ local core = require("pxl8.core") local world = {} -world.CHUNK_VXL = 0 -world.CHUNK_BSP = 1 - local Bsp = {} Bsp.__index = Bsp @@ -33,25 +30,12 @@ Chunk.__index = Chunk function Chunk:bsp() if self._ptr == nil then return nil end - if self._ptr.type ~= world.CHUNK_BSP then return nil end - local ptr = self._ptr.bsp - if ptr == nil then return nil end - return setmetatable({ _ptr = ptr }, Bsp) + if self._ptr.bsp == nil then return nil end + return setmetatable({ _ptr = self._ptr.bsp }, Bsp) end function Chunk:ready() - if self._ptr == nil then return false end - if self._ptr.type == world.CHUNK_BSP then - return self._ptr.bsp ~= nil - elseif self._ptr.type == world.CHUNK_VXL then - return self._ptr.voxels ~= nil - end - return false -end - -function Chunk:type() - if self._ptr == nil then return nil end - return self._ptr.type + return self._ptr ~= nil and self._ptr.bsp ~= nil end function Chunk:version() @@ -76,14 +60,6 @@ function World:active_chunk() return setmetatable({ _ptr = ptr }, Chunk) end -function World:get_render_distance() - return C.pxl8_world_get_render_distance(self._ptr) -end - -function World:get_sim_distance() - return C.pxl8_world_get_sim_distance(self._ptr) -end - function World:init_local_player(x, y, z) C.pxl8_world_init_local_player(self._ptr, x, y, z) end @@ -113,20 +89,67 @@ function World:set_bsp_material(material_id, material) C.pxl8_world_set_bsp_material(self._ptr, material_id, material._ptr) end -function World:set_render_distance(distance) - C.pxl8_world_set_render_distance(self._ptr, distance) -end - -function World:set_sim_distance(distance) - C.pxl8_world_set_sim_distance(self._ptr, distance) -end - function World:sweep(from_x, from_y, from_z, to_x, to_y, to_z, radius) local from = ffi.new("pxl8_vec3", {x = from_x, y = from_y, z = from_z}) local to = ffi.new("pxl8_vec3", {x = to_x, y = to_y, z = to_z}) return C.pxl8_world_sweep(self._ptr, from, to, radius) end +function World:set_sim_config(config) + C.pxl8_world_set_sim_config(self._ptr, config) +end + +function World:push_input(input_msg) + C.pxl8_world_push_input(self._ptr, input_msg) +end + world.World = World +function world.sim_config(opts) + opts = opts or {} + return ffi.new("pxl8_sim_config", { + move_speed = opts.move_speed or 180.0, + ground_accel = opts.ground_accel or 10.0, + air_accel = opts.air_accel or 1.0, + stop_speed = opts.stop_speed or 100.0, + friction = opts.friction or 6.0, + gravity = opts.gravity or 800.0, + jump_velocity = opts.jump_velocity or 200.0, + player_radius = opts.player_radius or 16.0, + player_height = opts.player_height or 72.0, + max_pitch = opts.max_pitch or 1.5, + }) +end + +function world.sim_move_player(entity, input_msg, sim_world, config, dt) + C.pxl8_sim_move_player(entity, input_msg, sim_world, config, dt) +end + +function world.sim_trace(sim_world, from_x, from_y, from_z, to_x, to_y, to_z, radius, height) + local from = ffi.new("pxl8_vec3", {x = from_x, y = from_y, z = from_z}) + local to = ffi.new("pxl8_vec3", {x = to_x, y = to_y, z = to_z}) + return C.pxl8_sim_trace(sim_world, from, to, radius, height) +end + +function world.sim_check_ground(sim_world, x, y, z, radius) + local pos = ffi.new("pxl8_vec3", {x = x, y = y, z = z}) + return C.pxl8_sim_check_ground(sim_world, pos, radius) +end + +function World:sim_world(x, y, z) + local pos = ffi.new("pxl8_vec3", {x = x, y = y, z = z}) + return C.pxl8_world_sim_world(self._ptr, pos) +end + +function world.make_input_msg(opts) + opts = opts or {} + return ffi.new("pxl8_input_msg", { + move_x = opts.move_x or 0, + move_y = opts.move_y or 0, + look_dx = opts.look_dx or 0, + look_dy = opts.look_dy or 0, + buttons = opts.buttons or 0, + }) +end + return world diff --git a/src/net/pxl8_net.c b/src/net/pxl8_net.c index 5c52eb6..52681d9 100644 --- a/src/net/pxl8_net.c +++ b/src/net/pxl8_net.c @@ -402,23 +402,10 @@ u32 pxl8_net_chunk_id(const pxl8_net* net) { } u8 pxl8_net_chunk_type(const pxl8_net* net) { - if (!net) return PXL8_CHUNK_TYPE_VXL; + if (!net) return PXL8_CHUNK_TYPE_BSP; return net->chunk_type; } -pxl8_result pxl8_net_send_chunk_settings(pxl8_net* net, i32 render_distance, i32 sim_distance) { - if (!net) return PXL8_ERROR_NULL_POINTER; - if (!net->connected) return PXL8_ERROR_NOT_CONNECTED; - - pxl8_command_msg cmd = {0}; - cmd.cmd_type = PXL8_CMD_SET_CHUNK_SETTINGS; - pxl8_pack_i32_be(cmd.payload, 0, render_distance); - pxl8_pack_i32_be(cmd.payload, 4, sim_distance); - cmd.payload_size = 8; - - return pxl8_net_send_command(net, &cmd); -} - pxl8_result pxl8_net_spawn(pxl8_net* net, f32 x, f32 y, f32 z, f32 yaw, f32 pitch) { if (!net) return PXL8_ERROR_NULL_POINTER; if (!net->connected) return PXL8_ERROR_NOT_CONNECTED; @@ -435,28 +422,18 @@ pxl8_result pxl8_net_spawn(pxl8_net* net, f32 x, f32 y, f32 z, f32 yaw, f32 pitc return pxl8_net_send_command(net, &cmd); } -pxl8_result pxl8_net_exit_chunk(pxl8_net* net, f32 x, f32 y, f32 z) { +pxl8_result pxl8_net_enter_scene(pxl8_net* net, u8 chunk_type, u32 chunk_id, f32 x, f32 y, f32 z) { if (!net) return PXL8_ERROR_NULL_POINTER; if (!net->connected) return PXL8_ERROR_NOT_CONNECTED; pxl8_command_msg cmd = {0}; - cmd.cmd_type = PXL8_CMD_EXIT_CHUNK; - pxl8_pack_f32_be(cmd.payload, 0, x); - pxl8_pack_f32_be(cmd.payload, 4, y); - pxl8_pack_f32_be(cmd.payload, 8, z); - cmd.payload_size = 12; - - return pxl8_net_send_command(net, &cmd); -} - -pxl8_result pxl8_net_enter_chunk(pxl8_net* net, u32 chunk_id) { - if (!net) return PXL8_ERROR_NULL_POINTER; - if (!net->connected) return PXL8_ERROR_NOT_CONNECTED; - - pxl8_command_msg cmd = {0}; - cmd.cmd_type = PXL8_CMD_ENTER_CHUNK; + cmd.cmd_type = PXL8_CMD_ENTER_SCENE; pxl8_pack_u32_be(cmd.payload, 0, chunk_id); - cmd.payload_size = 4; + cmd.payload[4] = chunk_type; + pxl8_pack_f32_be(cmd.payload, 8, x); + pxl8_pack_f32_be(cmd.payload, 12, y); + pxl8_pack_f32_be(cmd.payload, 16, z); + cmd.payload_size = 20; return pxl8_net_send_command(net, &cmd); } diff --git a/src/net/pxl8_net.h b/src/net/pxl8_net.h index 0fdbae8..770c84e 100644 --- a/src/net/pxl8_net.h +++ b/src/net/pxl8_net.h @@ -57,11 +57,8 @@ void pxl8_net_set_world(pxl8_net* net, pxl8_world* world); u32 pxl8_net_chunk_id(const pxl8_net* net); u8 pxl8_net_chunk_type(const pxl8_net* net); -pxl8_result pxl8_net_send_chunk_settings(pxl8_net* net, i32 render_distance, i32 sim_distance); - pxl8_result pxl8_net_spawn(pxl8_net* net, f32 x, f32 y, f32 z, f32 yaw, f32 pitch); -pxl8_result pxl8_net_exit_chunk(pxl8_net* net, f32 x, f32 y, f32 z); -pxl8_result pxl8_net_enter_chunk(pxl8_net* net, u32 chunk_id); +pxl8_result pxl8_net_enter_scene(pxl8_net* net, u8 chunk_type, u32 chunk_id, f32 x, f32 y, f32 z); #ifdef PXL8_ASYNC_THREADS void pxl8_net_start_thread(pxl8_net* net); diff --git a/src/net/pxl8_protocol.h b/src/net/pxl8_protocol.h index f3bd88a..fd3cc09 100644 --- a/src/net/pxl8_protocol.h +++ b/src/net/pxl8_protocol.h @@ -35,9 +35,7 @@ typedef struct pxl8_msg_header { typedef enum pxl8_cmd_type { PXL8_CMD_NONE = 0, PXL8_CMD_SPAWN_ENTITY, - PXL8_CMD_EXIT_CHUNK, - PXL8_CMD_ENTER_CHUNK, - PXL8_CMD_SET_CHUNK_SETTINGS, + PXL8_CMD_ENTER_SCENE, } pxl8_cmd_type; typedef struct pxl8_input_msg { @@ -76,7 +74,6 @@ typedef struct pxl8_snapshot_header { f32 time; } pxl8_snapshot_header; -#define PXL8_CHUNK_TYPE_VXL 0 #define PXL8_CHUNK_TYPE_BSP 1 #define PXL8_CHUNK_FLAG_RLE 0x01 diff --git a/src/script/pxl8_script_ffi.h b/src/script/pxl8_script_ffi.h index 9f2206a..f3e27f9 100644 --- a/src/script/pxl8_script_ffi.h +++ b/src/script/pxl8_script_ffi.h @@ -73,6 +73,7 @@ static const char* pxl8_ffi_cdefs = "bool pxl8_key_down(const pxl8_input_state* input, const char* key_name);\n" "bool pxl8_key_pressed(const pxl8_input_state* input, const char* key_name);\n" "bool pxl8_key_released(const pxl8_input_state* input, const char* key_name);\n" +"void pxl8_set_key_down(pxl8_input_state* input, const char* key_name, bool down);\n" "bool pxl8_mouse_pressed(const pxl8_input_state* input, i32 button);\n" "bool pxl8_mouse_released(const pxl8_input_state* input, i32 button);\n" "int pxl8_mouse_wheel_x(const pxl8_input_state* input);\n" @@ -397,23 +398,15 @@ static const char* pxl8_ffi_cdefs = "void pxl8_graph_eval_texture(const pxl8_graph* graph, u8* buffer, i32 width, i32 height);\n" "\n" "typedef struct pxl8_bsp pxl8_bsp;\n" -"typedef struct pxl8_vxl_chunk pxl8_vxl_chunk;\n" "\n" "u32 pxl8_bsp_face_count(const pxl8_bsp* bsp);\n" "pxl8_vec3 pxl8_bsp_face_normal(const pxl8_bsp* bsp, u32 face_id);\n" "u8 pxl8_bsp_light_at(const pxl8_bsp* bsp, f32 x, f32 y, f32 z, u8 ambient);\n" "\n" -"typedef enum { PXL8_WORLD_CHUNK_VXL = 0, PXL8_WORLD_CHUNK_BSP = 1 } pxl8_world_chunk_type;\n" -"\n" "typedef struct pxl8_world_chunk {\n" -" pxl8_world_chunk_type type;\n" " u32 id;\n" " u32 version;\n" -" i32 cx, cy, cz;\n" -" union {\n" -" pxl8_bsp* bsp;\n" -" pxl8_vxl_chunk* voxels;\n" -" };\n" +" pxl8_bsp* bsp;\n" "} pxl8_world_chunk;\n" "\n" "typedef struct pxl8_world pxl8_world;\n" @@ -432,10 +425,6 @@ static const char* pxl8_ffi_cdefs = "pxl8_ray pxl8_world_sweep(const pxl8_world* world, pxl8_vec3 from, pxl8_vec3 to, float radius);\n" "void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos);\n" "void pxl8_world_set_bsp_material(pxl8_world* world, u16 material_id, const pxl8_gfx_material* material);\n" -"i32 pxl8_world_get_render_distance(const pxl8_world* world);\n" -"void pxl8_world_set_render_distance(pxl8_world* world, i32 distance);\n" -"i32 pxl8_world_get_sim_distance(const pxl8_world* world);\n" -"void pxl8_world_set_sim_distance(pxl8_world* world, i32 distance);\n" "\n" "typedef struct pxl8_sim_entity {\n" " pxl8_vec3 pos;\n" @@ -463,6 +452,7 @@ static const char* pxl8_ffi_cdefs = "bool pxl8_gui_slider_int(pxl8_gui_state* state, pxl8_gfx* gfx, u32 id, i32 x, i32 y, i32 w, i32 h, i32* value, i32 min_val, i32 max_val);\n" "void pxl8_gui_window(pxl8_gfx* gfx, i32 x, i32 y, i32 w, i32 h, const char* title);\n" "void pxl8_gui_label(pxl8_gfx* gfx, i32 x, i32 y, const char* text, u8 color);\n" +"u8 pxl8_gui_color(pxl8_gfx* gfx, u8 index);\n" "bool pxl8_gui_is_hovering(const pxl8_gui_state* state);\n" "void pxl8_gui_get_cursor_pos(const pxl8_gui_state* state, i32* x, i32* y);\n" "\n" @@ -524,7 +514,7 @@ static const char* pxl8_ffi_cdefs = "\n" "typedef struct pxl8_net pxl8_net;\n" "typedef struct pxl8_net_config { const char* address; u16 port; } pxl8_net_config;\n" -"typedef enum pxl8_cmd_type { PXL8_CMD_NONE = 0, PXL8_CMD_SPAWN_ENTITY, PXL8_CMD_EXIT_CHUNK, PXL8_CMD_ENTER_CHUNK } pxl8_cmd_type;\n" +"typedef enum pxl8_cmd_type { PXL8_CMD_NONE = 0, PXL8_CMD_SPAWN_ENTITY, PXL8_CMD_ENTER_SCENE } pxl8_cmd_type;\n" "\n" "typedef struct pxl8_command_msg {\n" " u16 cmd_type;\n" @@ -544,6 +534,32 @@ static const char* pxl8_ffi_cdefs = " u64 timestamp;\n" "} pxl8_input_msg;\n" "\n" +"typedef struct pxl8_sim_config {\n" +" f32 move_speed;\n" +" f32 ground_accel;\n" +" f32 air_accel;\n" +" f32 stop_speed;\n" +" f32 friction;\n" +" f32 gravity;\n" +" f32 jump_velocity;\n" +" f32 player_radius;\n" +" f32 player_height;\n" +" f32 max_pitch;\n" +"} pxl8_sim_config;\n" +"\n" +"typedef struct pxl8_sim_world {\n" +" const pxl8_bsp* bsp;\n" +"} pxl8_sim_world;\n" +"\n" +"void pxl8_sim_move_player(pxl8_sim_entity* ent, const pxl8_input_msg* input, const pxl8_sim_world* world, const pxl8_sim_config* cfg, f32 dt);\n" +"void pxl8_sim_integrate(pxl8_sim_entity* ent, const pxl8_sim_world* world, const pxl8_sim_config* cfg, f32 dt);\n" +"pxl8_vec3 pxl8_sim_trace(const pxl8_sim_world* world, pxl8_vec3 from, pxl8_vec3 to, f32 radius, f32 height);\n" +"bool pxl8_sim_check_ground(const pxl8_sim_world* world, pxl8_vec3 pos, f32 radius);\n" +"\n" +"pxl8_sim_world pxl8_world_sim_world(const pxl8_world* world, pxl8_vec3 pos);\n" +"void pxl8_world_set_sim_config(pxl8_world* world, const pxl8_sim_config* config);\n" +"void pxl8_world_push_input(pxl8_world* world, const pxl8_input_msg* input);\n" +"\n" "typedef struct pxl8_entity_state {\n" " u64 entity_id;\n" " u8 userdata[56];\n" @@ -578,10 +594,8 @@ static const char* pxl8_ffi_cdefs = "void pxl8_net_predicted_tick_set(pxl8_net* net, u64 tick);\n" "i32 pxl8_net_send_command(pxl8_net* net, const pxl8_command_msg* cmd);\n" "i32 pxl8_net_send_input(pxl8_net* net, const pxl8_input_msg* input);\n" -"i32 pxl8_net_send_chunk_settings(pxl8_net* net, i32 render_distance, i32 sim_distance);\n" "i32 pxl8_net_spawn(pxl8_net* net, f32 x, f32 y, f32 z, f32 yaw, f32 pitch);\n" -"i32 pxl8_net_exit_chunk(pxl8_net* net, f32 x, f32 y, f32 z);\n" -"i32 pxl8_net_enter_chunk(pxl8_net* net, u32 chunk_id);\n" +"i32 pxl8_net_enter_scene(pxl8_net* net, u8 chunk_type, u32 chunk_id, f32 x, f32 y, f32 z);\n" "const pxl8_snapshot_header* pxl8_net_snapshot(const pxl8_net* net);\n" "u64 pxl8_net_tick(const pxl8_net* net);\n" "void pxl8_net_update(pxl8_net* net, f32 dt);\n" diff --git a/src/sim/pxl8_sim.c b/src/sim/pxl8_sim.c index d6334ac..9de35e1 100644 --- a/src/sim/pxl8_sim.c +++ b/src/sim/pxl8_sim.c @@ -2,15 +2,6 @@ #include -static usize vxl_world_to_local(f32 x, i32 chunk_coord) { - f32 chunk_base = chunk_coord * PXL8_VXL_WORLD_CHUNK_SIZE; - f32 local_world = x - chunk_base; - i32 local_voxel = (i32)floorf(local_world / PXL8_VXL_SCALE); - if (local_voxel < 0) return 0; - if (local_voxel >= PXL8_VXL_CHUNK_SIZE) return PXL8_VXL_CHUNK_SIZE - 1; - return (usize)local_voxel; -} - static i32 bsp_find_leaf(const pxl8_bsp* bsp, pxl8_vec3 pos) { if (!bsp || bsp->num_nodes == 0) return -1; @@ -59,82 +50,36 @@ pxl8_vec3 pxl8_bsp_trace(const pxl8_bsp* bsp, pxl8_vec3 from, pxl8_vec3 to, f32 return result; } -bool pxl8_vxl_point_solid(const pxl8_vxl_chunk* chunk, i32 cx, i32 cy, i32 cz, - f32 x, f32 y, f32 z) { - if (!chunk) return false; - - usize lx = vxl_world_to_local(x, cx); - usize ly = vxl_world_to_local(y, cy); - usize lz = vxl_world_to_local(z, cz); - - return pxl8_vxl_block_get(chunk, (i32)lx, (i32)ly, (i32)lz) != PXL8_VXL_BLOCK_AIR; -} - -static bool vxl_point_clear(const pxl8_vxl_chunk* chunk, i32 cx, i32 cy, i32 cz, - f32 x, f32 y, f32 z, f32 radius) { - if (pxl8_vxl_point_solid(chunk, cx, cy, cz, x - radius, y, z)) return false; - if (pxl8_vxl_point_solid(chunk, cx, cy, cz, x + radius, y, z)) return false; - if (pxl8_vxl_point_solid(chunk, cx, cy, cz, x, y - radius, z)) return false; - if (pxl8_vxl_point_solid(chunk, cx, cy, cz, x, y + radius, z)) return false; - if (pxl8_vxl_point_solid(chunk, cx, cy, cz, x, y, z - radius)) return false; - if (pxl8_vxl_point_solid(chunk, cx, cy, cz, x, y, z + radius)) return false; - return true; -} - -pxl8_vec3 pxl8_vxl_trace(const pxl8_vxl_chunk* chunk, i32 cx, i32 cy, i32 cz, - pxl8_vec3 from, pxl8_vec3 to, f32 radius) { - if (!chunk) return to; - - if (vxl_point_clear(chunk, cx, cy, cz, to.x, to.y, to.z, radius)) { - return to; - } - - bool x_ok = vxl_point_clear(chunk, cx, cy, cz, to.x, from.y, from.z, radius); - bool y_ok = vxl_point_clear(chunk, cx, cy, cz, from.x, to.y, from.z, radius); - bool z_ok = vxl_point_clear(chunk, cx, cy, cz, from.x, from.y, to.z, radius); - - pxl8_vec3 result = from; - if (x_ok) result.x = to.x; - if (y_ok) result.y = to.y; - if (z_ok) result.z = to.z; - - return result; -} - -pxl8_vec3 pxl8_sim_trace(const pxl8_sim_world* world, pxl8_vec3 from, pxl8_vec3 to, f32 radius) { +pxl8_vec3 pxl8_sim_trace(const pxl8_sim_world* world, pxl8_vec3 from, pxl8_vec3 to, f32 radius, f32 height) { + (void)height; if (!world) return to; if (world->bsp) { return pxl8_bsp_trace(world->bsp, from, to, radius); } - if (world->vxl) { - return pxl8_vxl_trace(world->vxl, world->vxl_cx, world->vxl_cy, world->vxl_cz, - from, to, radius); - } - return to; } bool pxl8_sim_check_ground(const pxl8_sim_world* world, pxl8_vec3 pos, f32 radius) { if (!world) return true; - if (!world->bsp && !world->vxl) return true; + if (!world->bsp) return true; pxl8_vec3 down = {pos.x, pos.y - 2.0f, pos.z}; - pxl8_vec3 result = pxl8_sim_trace(world, pos, down, radius); + pxl8_vec3 result = pxl8_sim_trace(world, pos, down, radius, 0.0f); return result.y > down.y; } void pxl8_sim_move_player(pxl8_sim_entity* ent, const pxl8_input_msg* input, - const pxl8_sim_world* world, f32 dt) { - if (!ent || !input) return; + const pxl8_sim_world* world, const pxl8_sim_config* cfg, f32 dt) { + if (!ent || !input || !cfg) return; if (!(ent->flags & PXL8_SIM_FLAG_ALIVE)) return; ent->yaw -= input->look_dx * 0.008f; f32 new_pitch = ent->pitch - input->look_dy * 0.008f; - if (new_pitch > PXL8_SIM_MAX_PITCH) new_pitch = PXL8_SIM_MAX_PITCH; - if (new_pitch < -PXL8_SIM_MAX_PITCH) new_pitch = -PXL8_SIM_MAX_PITCH; + if (new_pitch > cfg->max_pitch) new_pitch = cfg->max_pitch; + if (new_pitch < -cfg->max_pitch) new_pitch = -cfg->max_pitch; ent->pitch = new_pitch; f32 sin_yaw = sinf(ent->yaw); @@ -150,7 +95,7 @@ void pxl8_sim_move_player(pxl8_sim_entity* ent, const pxl8_input_msg* input, f32 ny = input->move_y / input_len; move_dir.x = cos_yaw * nx - sin_yaw * ny; move_dir.z = -sin_yaw * nx - cos_yaw * ny; - target_speed = PXL8_SIM_MOVE_SPEED; + target_speed = cfg->move_speed; } ent->vel.x = move_dir.x * target_speed; @@ -159,13 +104,13 @@ void pxl8_sim_move_player(pxl8_sim_entity* ent, const pxl8_input_msg* input, bool grounded = (ent->flags & PXL8_SIM_FLAG_GROUNDED) != 0; if (grounded && (input->buttons & 1)) { - ent->vel.y = PXL8_SIM_JUMP_VELOCITY; + ent->vel.y = cfg->jump_velocity; ent->flags &= ~PXL8_SIM_FLAG_GROUNDED; grounded = false; } if (!grounded) { - ent->vel.y -= PXL8_SIM_GRAVITY * dt; + ent->vel.y -= cfg->gravity * dt; } pxl8_vec3 target = { @@ -174,10 +119,27 @@ void pxl8_sim_move_player(pxl8_sim_entity* ent, const pxl8_input_msg* input, ent->pos.z + ent->vel.z * dt }; - pxl8_vec3 new_pos = pxl8_sim_trace(world, ent->pos, target, PXL8_SIM_PLAYER_RADIUS); + pxl8_vec3 new_pos = pxl8_sim_trace(world, ent->pos, target, cfg->player_radius, cfg->player_height); + + if (ent->vel.y < 0 && new_pos.y > target.y + 0.01f) { + f32 hi = new_pos.y; + f32 lo = target.y; + for (i32 i = 0; i < 8; i++) { + f32 mid = (hi + lo) * 0.5f; + pxl8_vec3 test = {new_pos.x, mid, new_pos.z}; + pxl8_vec3 result = pxl8_sim_trace(world, new_pos, test, cfg->player_radius, cfg->player_height); + if (result.y > mid + 0.01f) { + lo = mid; + } else { + hi = mid; + } + } + new_pos.y = hi; + } + ent->pos = new_pos; - if (pxl8_sim_check_ground(world, ent->pos, PXL8_SIM_PLAYER_RADIUS)) { + if (pxl8_sim_check_ground(world, ent->pos, cfg->player_radius)) { ent->flags |= PXL8_SIM_FLAG_GROUNDED; if (ent->vel.y < 0) ent->vel.y = 0; } else { @@ -185,19 +147,20 @@ void pxl8_sim_move_player(pxl8_sim_entity* ent, const pxl8_input_msg* input, } } -void pxl8_sim_integrate(pxl8_sim_entity* ent, const pxl8_sim_world* world, f32 dt) { - if (!ent) return; +void pxl8_sim_integrate(pxl8_sim_entity* ent, const pxl8_sim_world* world, + const pxl8_sim_config* cfg, f32 dt) { + if (!ent || !cfg) return; if (!(ent->flags & PXL8_SIM_FLAG_ALIVE)) return; if (ent->flags & PXL8_SIM_FLAG_PLAYER) return; bool grounded = (ent->flags & PXL8_SIM_FLAG_GROUNDED) != 0; - ent->vel.y -= PXL8_SIM_GRAVITY * dt; + ent->vel.y -= cfg->gravity * dt; if (grounded) { f32 speed = sqrtf(ent->vel.x * ent->vel.x + ent->vel.z * ent->vel.z); if (speed > 0.0f) { - f32 drop = speed * PXL8_SIM_FRICTION * dt; + f32 drop = speed * cfg->friction * dt; f32 new_speed = speed - drop; if (new_speed < 0.0f) new_speed = 0.0f; f32 scale = new_speed / speed; @@ -212,10 +175,27 @@ void pxl8_sim_integrate(pxl8_sim_entity* ent, const pxl8_sim_world* world, f32 d ent->pos.z + ent->vel.z * dt }; - pxl8_vec3 new_pos = pxl8_sim_trace(world, ent->pos, target, PXL8_SIM_PLAYER_RADIUS); + pxl8_vec3 new_pos = pxl8_sim_trace(world, ent->pos, target, cfg->player_radius, cfg->player_height); + + if (ent->vel.y < 0 && new_pos.y > target.y + 0.01f) { + f32 hi = new_pos.y; + f32 lo = target.y; + for (i32 i = 0; i < 8; i++) { + f32 mid = (hi + lo) * 0.5f; + pxl8_vec3 test = {new_pos.x, mid, new_pos.z}; + pxl8_vec3 result = pxl8_sim_trace(world, new_pos, test, cfg->player_radius, cfg->player_height); + if (result.y > mid + 0.01f) { + lo = mid; + } else { + hi = mid; + } + } + new_pos.y = hi; + } + ent->pos = new_pos; - if (pxl8_sim_check_ground(world, ent->pos, PXL8_SIM_PLAYER_RADIUS)) { + if (pxl8_sim_check_ground(world, ent->pos, cfg->player_radius)) { ent->flags |= PXL8_SIM_FLAG_GROUNDED; if (ent->vel.y < 0.0f) ent->vel.y = 0.0f; } else { diff --git a/src/sim/pxl8_sim.h b/src/sim/pxl8_sim.h index 94cb67c..a17c540 100644 --- a/src/sim/pxl8_sim.h +++ b/src/sim/pxl8_sim.h @@ -4,7 +4,6 @@ #include "pxl8_math.h" #include "pxl8_protocol.h" #include "pxl8_types.h" -#include "pxl8_vxl.h" #ifdef __cplusplus extern "C" { @@ -14,15 +13,18 @@ extern "C" { #define PXL8_SIM_FLAG_PLAYER (1 << 1) #define PXL8_SIM_FLAG_GROUNDED (1 << 2) -#define PXL8_SIM_MOVE_SPEED 180.0f -#define PXL8_SIM_GROUND_ACCEL 10.0f -#define PXL8_SIM_AIR_ACCEL 1.0f -#define PXL8_SIM_STOP_SPEED 100.0f -#define PXL8_SIM_FRICTION 6.0f -#define PXL8_SIM_GRAVITY 800.0f -#define PXL8_SIM_JUMP_VELOCITY 200.0f -#define PXL8_SIM_PLAYER_RADIUS 16.0f -#define PXL8_SIM_MAX_PITCH 1.5f +typedef struct pxl8_sim_config { + f32 move_speed; + f32 ground_accel; + f32 air_accel; + f32 stop_speed; + f32 friction; + f32 gravity; + f32 jump_velocity; + f32 player_radius; + f32 player_height; + f32 max_pitch; +} pxl8_sim_config; typedef struct pxl8_sim_entity { pxl8_vec3 pos; @@ -36,20 +38,15 @@ typedef struct pxl8_sim_entity { typedef struct pxl8_sim_world { const pxl8_bsp* bsp; - const pxl8_vxl_chunk* vxl; - i32 vxl_cx, vxl_cy, vxl_cz; } pxl8_sim_world; bool pxl8_bsp_point_solid(const pxl8_bsp* bsp, pxl8_vec3 pos); pxl8_vec3 pxl8_bsp_trace(const pxl8_bsp* bsp, pxl8_vec3 from, pxl8_vec3 to, f32 radius); -bool pxl8_vxl_point_solid(const pxl8_vxl_chunk* chunk, i32 cx, i32 cy, i32 cz, f32 x, f32 y, f32 z); -pxl8_vec3 pxl8_vxl_trace(const pxl8_vxl_chunk* chunk, i32 cx, i32 cy, i32 cz, pxl8_vec3 from, pxl8_vec3 to, f32 radius); - -pxl8_vec3 pxl8_sim_trace(const pxl8_sim_world* world, pxl8_vec3 from, pxl8_vec3 to, f32 radius); +pxl8_vec3 pxl8_sim_trace(const pxl8_sim_world* world, pxl8_vec3 from, pxl8_vec3 to, f32 radius, f32 height); bool pxl8_sim_check_ground(const pxl8_sim_world* world, pxl8_vec3 pos, f32 radius); -void pxl8_sim_move_player(pxl8_sim_entity* ent, const pxl8_input_msg* input, const pxl8_sim_world* world, f32 dt); -void pxl8_sim_integrate(pxl8_sim_entity* ent, const pxl8_sim_world* world, f32 dt); +void pxl8_sim_move_player(pxl8_sim_entity* ent, const pxl8_input_msg* input, const pxl8_sim_world* world, const pxl8_sim_config* cfg, f32 dt); +void pxl8_sim_integrate(pxl8_sim_entity* ent, const pxl8_sim_world* world, const pxl8_sim_config* cfg, f32 dt); #ifdef __cplusplus } diff --git a/src/vxl/pxl8_vxl.c b/src/vxl/pxl8_vxl.c deleted file mode 100644 index 9e54a45..0000000 --- a/src/vxl/pxl8_vxl.c +++ /dev/null @@ -1,319 +0,0 @@ -#include "pxl8_vxl.h" - -#include - -#include "pxl8_mem.h" - -typedef struct pxl8_vxl_block_def { - char name[32]; - u8 texture_top; - u8 texture_side; - u8 texture_bottom; - pxl8_vxl_geometry geometry; - bool registered; -} pxl8_vxl_block_def; - -struct pxl8_vxl_block_registry { - pxl8_vxl_block_def blocks[PXL8_VXL_BLOCK_COUNT]; -}; - -static inline bool vxl_in_bounds(i32 x, i32 y, i32 z) { - return x >= 0 && x < PXL8_VXL_CHUNK_SIZE && - y >= 0 && y < PXL8_VXL_CHUNK_SIZE && - z >= 0 && z < PXL8_VXL_CHUNK_SIZE; -} - -static inline u32 vxl_index(i32 x, i32 y, i32 z) { - return (u32)(x + y * PXL8_VXL_CHUNK_SIZE + z * PXL8_VXL_CHUNK_SIZE * PXL8_VXL_CHUNK_SIZE); -} - -static pxl8_vxl_octree_node* octree_alloc_children(void) { - pxl8_vxl_octree_node* children = pxl8_calloc(8, sizeof(pxl8_vxl_octree_node)); - for (i32 i = 0; i < 8; i++) { - children[i].type = PXL8_VXL_OCTREE_UNIFORM; - children[i].block = PXL8_VXL_BLOCK_AIR; - } - return children; -} - -static void octree_free_node(pxl8_vxl_octree_node* node) { - if (node->type == PXL8_VXL_OCTREE_BRANCH && node->children) { - for (i32 i = 0; i < 8; i++) { - octree_free_node(&node->children[i]); - } - pxl8_free(node->children); - node->children = NULL; - } - node->type = PXL8_VXL_OCTREE_UNIFORM; - node->block = PXL8_VXL_BLOCK_AIR; -} - -static u8 octree_get(const pxl8_vxl_octree_node* node, i32 depth, i32 x, i32 y, i32 z) { - if (node->type == PXL8_VXL_OCTREE_UNIFORM) { - return node->block; - } - - if (depth == 0 || !node->children) { - return PXL8_VXL_BLOCK_AIR; - } - - i32 half = 1 << (depth - 1); - i32 idx = ((x >= half) ? 1 : 0) | - ((y >= half) ? 2 : 0) | - ((z >= half) ? 4 : 0); - - return octree_get(&node->children[idx], depth - 1, - x & (half - 1), y & (half - 1), z & (half - 1)); -} - -static void octree_subdivide(pxl8_vxl_octree_node* node) { - if (node->type == PXL8_VXL_OCTREE_BRANCH) return; - - u8 block = node->block; - node->type = PXL8_VXL_OCTREE_BRANCH; - node->children = octree_alloc_children(); - - for (i32 i = 0; i < 8; i++) { - node->children[i].type = PXL8_VXL_OCTREE_UNIFORM; - node->children[i].block = block; - } -} - -static bool octree_try_simplify(pxl8_vxl_octree_node* node) { - if (node->type != PXL8_VXL_OCTREE_BRANCH || !node->children) { - return false; - } - - for (i32 i = 0; i < 8; i++) { - if (node->children[i].type != PXL8_VXL_OCTREE_UNIFORM) { - return false; - } - } - - u8 first_block = node->children[0].block; - for (i32 i = 1; i < 8; i++) { - if (node->children[i].block != first_block) { - return false; - } - } - - pxl8_free(node->children); - node->children = NULL; - node->type = PXL8_VXL_OCTREE_UNIFORM; - node->block = first_block; - return true; -} - -static void octree_set(pxl8_vxl_octree_node* node, i32 depth, i32 x, i32 y, i32 z, u8 block) { - if (depth == 0) { - if (node->type == PXL8_VXL_OCTREE_BRANCH) { - octree_free_node(node); - } - node->type = PXL8_VXL_OCTREE_UNIFORM; - node->block = block; - return; - } - - if (node->type == PXL8_VXL_OCTREE_UNIFORM) { - if (node->block == block) { - return; - } - octree_subdivide(node); - } - - i32 half = 1 << (depth - 1); - i32 idx = ((x >= half) ? 1 : 0) | - ((y >= half) ? 2 : 0) | - ((z >= half) ? 4 : 0); - - octree_set(&node->children[idx], depth - 1, - x & (half - 1), y & (half - 1), z & (half - 1), block); - - octree_try_simplify(node); -} - -static void octree_linearize_recursive(const pxl8_vxl_octree_node* node, i32 depth, - i32 ox, i32 oy, i32 oz, u8* out) { - i32 size = 1 << depth; - - if (node->type == PXL8_VXL_OCTREE_UNIFORM) { - for (i32 z = 0; z < size; z++) { - for (i32 y = 0; y < size; y++) { - for (i32 x = 0; x < size; x++) { - out[vxl_index(ox + x, oy + y, oz + z)] = node->block; - } - } - } - return; - } - - if (depth == 0 || !node->children) return; - - i32 half = size / 2; - for (i32 i = 0; i < 8; i++) { - i32 cx = ox + ((i & 1) ? half : 0); - i32 cy = oy + ((i & 2) ? half : 0); - i32 cz = oz + ((i & 4) ? half : 0); - octree_linearize_recursive(&node->children[i], depth - 1, cx, cy, cz, out); - } -} - -static void octree_build_recursive(pxl8_vxl_octree_node* node, i32 depth, - i32 ox, i32 oy, i32 oz, const u8* data) { - i32 size = 1 << depth; - - if (depth == 0) { - node->type = PXL8_VXL_OCTREE_UNIFORM; - node->block = data[vxl_index(ox, oy, oz)]; - return; - } - - u8 first_block = data[vxl_index(ox, oy, oz)]; - bool all_same = true; - - for (i32 z = 0; z < size && all_same; z++) { - for (i32 y = 0; y < size && all_same; y++) { - for (i32 x = 0; x < size && all_same; x++) { - if (data[vxl_index(ox + x, oy + y, oz + z)] != first_block) { - all_same = false; - } - } - } - } - - if (all_same) { - node->type = PXL8_VXL_OCTREE_UNIFORM; - node->block = first_block; - return; - } - - node->type = PXL8_VXL_OCTREE_BRANCH; - node->children = octree_alloc_children(); - - i32 half = size / 2; - for (i32 i = 0; i < 8; i++) { - i32 cx = ox + ((i & 1) ? half : 0); - i32 cy = oy + ((i & 2) ? half : 0); - i32 cz = oz + ((i & 4) ? half : 0); - octree_build_recursive(&node->children[i], depth - 1, cx, cy, cz, data); - } - - octree_try_simplify(node); -} - -pxl8_vxl_chunk* pxl8_vxl_chunk_create(void) { - pxl8_vxl_chunk* chunk = pxl8_calloc(1, sizeof(pxl8_vxl_chunk)); - if (!chunk) return NULL; - chunk->root.type = PXL8_VXL_OCTREE_UNIFORM; - chunk->root.block = PXL8_VXL_BLOCK_AIR; - return chunk; -} - -void pxl8_vxl_chunk_destroy(pxl8_vxl_chunk* chunk) { - if (!chunk) return; - octree_free_node(&chunk->root); - pxl8_free(chunk); -} - -u8 pxl8_vxl_block_get(const pxl8_vxl_chunk* chunk, i32 x, i32 y, i32 z) { - if (!chunk || !vxl_in_bounds(x, y, z)) return PXL8_VXL_BLOCK_AIR; - return octree_get(&chunk->root, PXL8_VXL_OCTREE_DEPTH, x, y, z); -} - -void pxl8_vxl_block_set(pxl8_vxl_chunk* chunk, i32 x, i32 y, i32 z, u8 block) { - if (!chunk || !vxl_in_bounds(x, y, z)) return; - octree_set(&chunk->root, PXL8_VXL_OCTREE_DEPTH, x, y, z, block); -} - -void pxl8_vxl_block_fill(pxl8_vxl_chunk* chunk, u8 block) { - if (!chunk) return; - octree_free_node(&chunk->root); - chunk->root.type = PXL8_VXL_OCTREE_UNIFORM; - chunk->root.block = block; -} - -void pxl8_vxl_block_clear(pxl8_vxl_chunk* chunk) { - pxl8_vxl_block_fill(chunk, PXL8_VXL_BLOCK_AIR); -} - -void pxl8_vxl_chunk_linearize(const pxl8_vxl_chunk* chunk, u8* out) { - if (!chunk || !out) return; - octree_linearize_recursive(&chunk->root, PXL8_VXL_OCTREE_DEPTH, 0, 0, 0, out); -} - -void pxl8_vxl_chunk_from_linear(pxl8_vxl_chunk* chunk, const u8* data) { - if (!chunk || !data) return; - octree_free_node(&chunk->root); - octree_build_recursive(&chunk->root, PXL8_VXL_OCTREE_DEPTH, 0, 0, 0, data); -} - -bool pxl8_vxl_chunk_is_uniform(const pxl8_vxl_chunk* chunk) { - if (!chunk) return true; - return chunk->root.type == PXL8_VXL_OCTREE_UNIFORM; -} - -u8 pxl8_vxl_chunk_uniform_block(const pxl8_vxl_chunk* chunk) { - if (!chunk) return PXL8_VXL_BLOCK_AIR; - if (chunk->root.type == PXL8_VXL_OCTREE_UNIFORM) { - return chunk->root.block; - } - return PXL8_VXL_BLOCK_AIR; -} - -pxl8_vxl_block_registry* pxl8_vxl_block_registry_create(void) { - pxl8_vxl_block_registry* registry = pxl8_calloc(1, sizeof(pxl8_vxl_block_registry)); - if (!registry) return NULL; - - pxl8_vxl_block_registry_register(registry, 1, "stone", 8, PXL8_VXL_GEOMETRY_CUBE); - pxl8_vxl_block_registry_register(registry, 2, "dirt", 52, PXL8_VXL_GEOMETRY_CUBE); - pxl8_vxl_block_registry_register_ex(registry, 3, "grass", 200, 52, 52, PXL8_VXL_GEOMETRY_CUBE); - pxl8_vxl_block_registry_register_ex(registry, 4, "grass_slope_n", 200, 52, 52, PXL8_VXL_GEOMETRY_SLOPE_NORTH); - pxl8_vxl_block_registry_register_ex(registry, 5, "grass_slope_s", 200, 52, 52, PXL8_VXL_GEOMETRY_SLOPE_SOUTH); - pxl8_vxl_block_registry_register_ex(registry, 6, "grass_slope_e", 200, 52, 52, PXL8_VXL_GEOMETRY_SLOPE_EAST); - pxl8_vxl_block_registry_register_ex(registry, 7, "grass_slope_w", 200, 52, 52, PXL8_VXL_GEOMETRY_SLOPE_WEST); - - return registry; -} - -void pxl8_vxl_block_registry_destroy(pxl8_vxl_block_registry* registry) { - pxl8_free(registry); -} - -void pxl8_vxl_block_registry_register(pxl8_vxl_block_registry* registry, u8 id, const char* name, u8 texture_id, pxl8_vxl_geometry geo) { - pxl8_vxl_block_registry_register_ex(registry, id, name, texture_id, texture_id, texture_id, geo); -} - -void pxl8_vxl_block_registry_register_ex(pxl8_vxl_block_registry* registry, u8 id, const char* name, - u8 texture_top, u8 texture_side, u8 texture_bottom, pxl8_vxl_geometry geo) { - if (!registry || id == PXL8_VXL_BLOCK_AIR) return; - - pxl8_vxl_block_def* def = ®istry->blocks[id]; - strncpy(def->name, name ? name : "", sizeof(def->name) - 1); - def->texture_top = texture_top; - def->texture_side = texture_side; - def->texture_bottom = texture_bottom; - def->geometry = geo; - def->registered = true; -} - -const char* pxl8_vxl_block_registry_name(const pxl8_vxl_block_registry* registry, u8 id) { - if (!registry || !registry->blocks[id].registered) return NULL; - return registry->blocks[id].name; -} - -pxl8_vxl_geometry pxl8_vxl_block_registry_geometry(const pxl8_vxl_block_registry* registry, u8 id) { - if (!registry || !registry->blocks[id].registered) return PXL8_VXL_GEOMETRY_CUBE; - return registry->blocks[id].geometry; -} - -u8 pxl8_vxl_block_registry_texture(const pxl8_vxl_block_registry* registry, u8 id) { - if (!registry || !registry->blocks[id].registered) return 0; - return registry->blocks[id].texture_top; -} - -u8 pxl8_vxl_block_registry_texture_for_face(const pxl8_vxl_block_registry* registry, u8 id, i32 face) { - if (!registry || !registry->blocks[id].registered) return 0; - if (face == 3) return registry->blocks[id].texture_top; - if (face == 2) return registry->blocks[id].texture_bottom; - return registry->blocks[id].texture_side; -} diff --git a/src/vxl/pxl8_vxl.h b/src/vxl/pxl8_vxl.h deleted file mode 100644 index f1ec7c9..0000000 --- a/src/vxl/pxl8_vxl.h +++ /dev/null @@ -1,78 +0,0 @@ -#pragma once - -#include "pxl8_types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define PXL8_VXL_BLOCK_COUNT 256 -#define PXL8_VXL_CHUNK_SIZE 32 -#define PXL8_VXL_CHUNK_VOLUME (PXL8_VXL_CHUNK_SIZE * PXL8_VXL_CHUNK_SIZE * PXL8_VXL_CHUNK_SIZE) -#define PXL8_VXL_SCALE 16.0f -#define PXL8_VXL_WORLD_CHUNK_SIZE (PXL8_VXL_CHUNK_SIZE * PXL8_VXL_SCALE) -#define PXL8_VXL_OCTREE_DEPTH 5 - -#define PXL8_VXL_BLOCK_AIR 0 -#define PXL8_VXL_BLOCK_UNKNOWN 255 - -typedef struct pxl8_vxl_block_registry pxl8_vxl_block_registry; - -typedef enum pxl8_vxl_octree_type { - PXL8_VXL_OCTREE_UNIFORM = 0, - PXL8_VXL_OCTREE_BRANCH = 1 -} pxl8_vxl_octree_type; - -typedef struct pxl8_vxl_octree_node { - u8 type; - union { - u8 block; - struct pxl8_vxl_octree_node* children; - }; -} pxl8_vxl_octree_node; - -typedef struct pxl8_vxl_chunk { - pxl8_vxl_octree_node root; -} pxl8_vxl_chunk; - -typedef enum pxl8_vxl_geometry { - PXL8_VXL_GEOMETRY_CUBE, - PXL8_VXL_GEOMETRY_SLAB_BOTTOM, - PXL8_VXL_GEOMETRY_SLAB_TOP, - PXL8_VXL_GEOMETRY_SLOPE_NORTH, - PXL8_VXL_GEOMETRY_SLOPE_SOUTH, - PXL8_VXL_GEOMETRY_SLOPE_EAST, - PXL8_VXL_GEOMETRY_SLOPE_WEST, - PXL8_VXL_GEOMETRY_STAIRS_NORTH, - PXL8_VXL_GEOMETRY_STAIRS_SOUTH, - PXL8_VXL_GEOMETRY_STAIRS_EAST, - PXL8_VXL_GEOMETRY_STAIRS_WEST -} pxl8_vxl_geometry; - -pxl8_vxl_chunk* pxl8_vxl_chunk_create(void); -void pxl8_vxl_chunk_destroy(pxl8_vxl_chunk* chunk); - -u8 pxl8_vxl_block_get(const pxl8_vxl_chunk* chunk, i32 x, i32 y, i32 z); -void pxl8_vxl_block_set(pxl8_vxl_chunk* chunk, i32 x, i32 y, i32 z, u8 block); -void pxl8_vxl_block_fill(pxl8_vxl_chunk* chunk, u8 block); -void pxl8_vxl_block_clear(pxl8_vxl_chunk* chunk); - -void pxl8_vxl_chunk_linearize(const pxl8_vxl_chunk* chunk, u8* out); -void pxl8_vxl_chunk_from_linear(pxl8_vxl_chunk* chunk, const u8* data); - -bool pxl8_vxl_chunk_is_uniform(const pxl8_vxl_chunk* chunk); -u8 pxl8_vxl_chunk_uniform_block(const pxl8_vxl_chunk* chunk); - -pxl8_vxl_block_registry* pxl8_vxl_block_registry_create(void); -void pxl8_vxl_block_registry_destroy(pxl8_vxl_block_registry* registry); -void pxl8_vxl_block_registry_register(pxl8_vxl_block_registry* registry, u8 id, const char* name, u8 texture_id, pxl8_vxl_geometry geo); -void pxl8_vxl_block_registry_register_ex(pxl8_vxl_block_registry* registry, u8 id, const char* name, - u8 texture_top, u8 texture_side, u8 texture_bottom, pxl8_vxl_geometry geo); -const char* pxl8_vxl_block_registry_name(const pxl8_vxl_block_registry* registry, u8 id); -pxl8_vxl_geometry pxl8_vxl_block_registry_geometry(const pxl8_vxl_block_registry* registry, u8 id); -u8 pxl8_vxl_block_registry_texture(const pxl8_vxl_block_registry* registry, u8 id); -u8 pxl8_vxl_block_registry_texture_for_face(const pxl8_vxl_block_registry* registry, u8 id, i32 face); - -#ifdef __cplusplus -} -#endif diff --git a/src/vxl/pxl8_vxl_render.c b/src/vxl/pxl8_vxl_render.c deleted file mode 100644 index 06e5264..0000000 --- a/src/vxl/pxl8_vxl_render.c +++ /dev/null @@ -1,571 +0,0 @@ -#include "pxl8_vxl_render.h" - -#include - -#include "pxl8_mem.h" -#include "pxl8_noise.h" -#include "pxl8_vxl.h" - -typedef struct pxl8_vxl_greedy_mask { - u8 block[PXL8_VXL_CHUNK_SIZE][PXL8_VXL_CHUNK_SIZE]; - bool done[PXL8_VXL_CHUNK_SIZE][PXL8_VXL_CHUNK_SIZE]; -} pxl8_vxl_greedy_mask; - -static const pxl8_vec3 face_normals[6] = { - {-1, 0, 0}, { 1, 0, 0}, - { 0, -1, 0}, { 0, 1, 0}, - { 0, 0, -1}, { 0, 0, 1} -}; - -static const i32 face_dirs[6][3] = { - {-1, 0, 0}, {1, 0, 0}, {0, -1, 0}, {0, 1, 0}, {0, 0, -1}, {0, 0, 1} -}; - -static inline bool vxl_in_bounds(i32 x, i32 y, i32 z) { - return x >= 0 && x < PXL8_VXL_CHUNK_SIZE && - y >= 0 && y < PXL8_VXL_CHUNK_SIZE && - z >= 0 && z < PXL8_VXL_CHUNK_SIZE; -} - -static bool block_is_opaque(u8 block) { - return block != PXL8_VXL_BLOCK_AIR && block != PXL8_VXL_BLOCK_UNKNOWN; -} - -static bool block_is_full_cube(u8 block, const pxl8_vxl_block_registry* registry) { - if (block == PXL8_VXL_BLOCK_AIR) return false; - pxl8_vxl_geometry geo = pxl8_vxl_block_registry_geometry(registry, block); - return geo == PXL8_VXL_GEOMETRY_CUBE; -} - -static u8 get_block_or_neighbor(const pxl8_vxl_chunk* chunk, const pxl8_vxl_chunk** neighbors, i32 x, i32 y, i32 z) { - if (vxl_in_bounds(x, y, z)) { - return pxl8_vxl_block_get(chunk, x, y, z); - } - - i32 nx = x, ny = y, nz = z; - i32 neighbor_idx = -1; - - if (x < 0) { neighbor_idx = 0; nx = x + PXL8_VXL_CHUNK_SIZE; } - else if (x >= PXL8_VXL_CHUNK_SIZE) { neighbor_idx = 1; nx = x - PXL8_VXL_CHUNK_SIZE; } - else if (y < 0) { neighbor_idx = 2; ny = y + PXL8_VXL_CHUNK_SIZE; } - else if (y >= PXL8_VXL_CHUNK_SIZE) { neighbor_idx = 3; ny = y - PXL8_VXL_CHUNK_SIZE; } - else if (z < 0) { neighbor_idx = 4; nz = z + PXL8_VXL_CHUNK_SIZE; } - else if (z >= PXL8_VXL_CHUNK_SIZE) { neighbor_idx = 5; nz = z - PXL8_VXL_CHUNK_SIZE; } - - if (neighbor_idx >= 0 && neighbors && neighbors[neighbor_idx]) { - return pxl8_vxl_block_get(neighbors[neighbor_idx], nx, ny, nz); - } - - return PXL8_VXL_BLOCK_UNKNOWN; -} - -static f32 compute_ao(const pxl8_vxl_chunk* chunk, const pxl8_vxl_chunk** neighbors, - i32 x, i32 y, i32 z, i32 dx1, i32 dy1, i32 dz1, i32 dx2, i32 dy2, i32 dz2) { - u8 s1 = get_block_or_neighbor(chunk, neighbors, x + dx1, y + dy1, z + dz1); - u8 s2 = get_block_or_neighbor(chunk, neighbors, x + dx2, y + dy2, z + dz2); - u8 corner = get_block_or_neighbor(chunk, neighbors, x + dx1 + dx2, y + dy1 + dy2, z + dz1 + dz2); - - bool side1 = block_is_opaque(s1); - bool side2 = block_is_opaque(s2); - bool has_corner = block_is_opaque(corner); - - if (side1 && side2) return 0.0f; - return (3.0f - (f32)side1 - (f32)side2 - (f32)has_corner) / 3.0f; -} - -static f32 sample_corner_displacement(const pxl8_vxl_chunk* chunk, const pxl8_vxl_chunk** neighbors, - i32 cx, i32 cz, i32 base_y) { - f32 sum = 0.0f; - i32 count = 0; - - for (i32 dz = -1; dz <= 0; dz++) { - for (i32 dx = -1; dx <= 0; dx++) { - i32 x = cx + dx; - i32 z = cz + dz; - - u8 block = get_block_or_neighbor(chunk, neighbors, x, base_y, z); - u8 above = get_block_or_neighbor(chunk, neighbors, x, base_y + 1, z); - - if (block_is_opaque(block) && !block_is_opaque(above)) { - sum += 1.0f; - } else if (!block_is_opaque(block)) { - sum -= 1.0f; - } - count++; - } - } - - return count > 0 ? (sum / (f32)count) * 0.15f : 0.0f; -} - -static void slice_to_world(i32 face, i32 slice, i32 u, i32 v, i32* x, i32* y, i32* z) { - switch (face) { - case 0: *x = slice; *y = v; *z = u; break; - case 1: *x = slice; *y = v; *z = u; break; - case 2: *x = u; *y = slice; *z = v; break; - case 3: *x = u; *y = slice; *z = v; break; - case 4: *x = u; *y = v; *z = slice; break; - case 5: *x = u; *y = v; *z = slice; break; - } -} - -static f32 terrain_noise(f32 wx, f32 wz, u64 seed) { - f32 noise = pxl8_fbm(wx * 0.08f, wz * 0.08f, seed, 3); - return (noise - 0.5f) * 0.4f; -} - -static void emit_greedy_quad(pxl8_mesh* mesh, const pxl8_vxl_chunk* chunk, - const pxl8_vxl_chunk** neighbors, - const pxl8_vxl_mesh_config* config, - i32 face, i32 slice, - i32 u, i32 v, i32 width, i32 height, - u8 texture_id, f32 ao_avg, f32 ao_strength) { - pxl8_vec3 normal = face_normals[face]; - u8 light = (u8)(255.0f * (1.0f - ao_strength * (1.0f - ao_avg))); - - f32 p[4][3]; - - switch (face) { - case 0: - p[0][0] = (f32)slice; p[0][1] = (f32)v; p[0][2] = (f32)(u + width); - p[1][0] = (f32)slice; p[1][1] = (f32)v; p[1][2] = (f32)u; - p[2][0] = (f32)slice; p[2][1] = (f32)(v + height); p[2][2] = (f32)u; - p[3][0] = (f32)slice; p[3][1] = (f32)(v + height); p[3][2] = (f32)(u + width); - break; - case 1: - p[0][0] = (f32)(slice + 1); p[0][1] = (f32)v; p[0][2] = (f32)u; - p[1][0] = (f32)(slice + 1); p[1][1] = (f32)v; p[1][2] = (f32)(u + width); - p[2][0] = (f32)(slice + 1); p[2][1] = (f32)(v + height); p[2][2] = (f32)(u + width); - p[3][0] = (f32)(slice + 1); p[3][1] = (f32)(v + height); p[3][2] = (f32)u; - break; - case 2: - p[0][0] = (f32)u; p[0][1] = (f32)slice; p[0][2] = (f32)v; - p[1][0] = (f32)(u + width); p[1][1] = (f32)slice; p[1][2] = (f32)v; - p[2][0] = (f32)(u + width); p[2][1] = (f32)slice; p[2][2] = (f32)(v + height); - p[3][0] = (f32)u; p[3][1] = (f32)slice; p[3][2] = (f32)(v + height); - break; - case 3: { - f32 base_y = (f32)(slice + 1); - f32 wx0 = (f32)(config->chunk_x * PXL8_VXL_CHUNK_SIZE + u); - f32 wz0 = (f32)(config->chunk_z * PXL8_VXL_CHUNK_SIZE + v); - - f32 d0 = sample_corner_displacement(chunk, neighbors, u, v + height, slice); - f32 d1 = sample_corner_displacement(chunk, neighbors, u + width, v + height, slice); - f32 d2 = sample_corner_displacement(chunk, neighbors, u + width, v, slice); - f32 d3 = sample_corner_displacement(chunk, neighbors, u, v, slice); - - f32 n0 = terrain_noise(wx0, wz0 + (f32)height, config->seed); - f32 n1 = terrain_noise(wx0 + (f32)width, wz0 + (f32)height, config->seed); - f32 n2 = terrain_noise(wx0 + (f32)width, wz0, config->seed); - f32 n3 = terrain_noise(wx0, wz0, config->seed); - - p[0][0] = (f32)u; p[0][1] = base_y + d0 + n0; p[0][2] = (f32)(v + height); - p[1][0] = (f32)(u + width); p[1][1] = base_y + d1 + n1; p[1][2] = (f32)(v + height); - p[2][0] = (f32)(u + width); p[2][1] = base_y + d2 + n2; p[2][2] = (f32)v; - p[3][0] = (f32)u; p[3][1] = base_y + d3 + n3; p[3][2] = (f32)v; - - pxl8_vec3 e1 = {p[1][0] - p[0][0], p[1][1] - p[0][1], p[1][2] - p[0][2]}; - pxl8_vec3 e2 = {p[3][0] - p[0][0], p[3][1] - p[0][1], p[3][2] - p[0][2]}; - normal = (pxl8_vec3){ - e1.y * e2.z - e1.z * e2.y, - e1.z * e2.x - e1.x * e2.z, - e1.x * e2.y - e1.y * e2.x - }; - f32 len_sq = normal.x * normal.x + normal.y * normal.y + normal.z * normal.z; - if (len_sq > 0.0001f) { - f32 inv_len = pxl8_fast_inv_sqrt(len_sq); - normal.x *= inv_len; normal.y *= inv_len; normal.z *= inv_len; - } else { - normal = (pxl8_vec3){0, 1, 0}; - } - break; - } - case 4: - p[0][0] = (f32)u; p[0][1] = (f32)v; p[0][2] = (f32)slice; - p[1][0] = (f32)u; p[1][1] = (f32)(v + height); p[1][2] = (f32)slice; - p[2][0] = (f32)(u + width); p[2][1] = (f32)(v + height); p[2][2] = (f32)slice; - p[3][0] = (f32)(u + width); p[3][1] = (f32)v; p[3][2] = (f32)slice; - break; - case 5: - p[0][0] = (f32)(u + width); p[0][1] = (f32)v; p[0][2] = (f32)(slice + 1); - p[1][0] = (f32)(u + width); p[1][1] = (f32)(v + height); p[1][2] = (f32)(slice + 1); - p[2][0] = (f32)u; p[2][1] = (f32)(v + height); p[2][2] = (f32)(slice + 1); - p[3][0] = (f32)u; p[3][1] = (f32)v; p[3][2] = (f32)(slice + 1); - break; - } - - f32 tex_u = (f32)width; - f32 tex_v = (f32)height; - - pxl8_vertex verts[4]; - for (i32 i = 0; i < 4; i++) { - verts[i].position.x = p[i][0]; - verts[i].position.y = p[i][1]; - verts[i].position.z = p[i][2]; - verts[i].normal = normal; - verts[i].color = texture_id; - verts[i].light = light; - } - - verts[0].u = 0; verts[0].v = tex_v; - verts[1].u = tex_u; verts[1].v = tex_v; - verts[2].u = tex_u; verts[2].v = 0; - verts[3].u = 0; verts[3].v = 0; - - u16 i0 = pxl8_mesh_push_vertex(mesh, verts[0]); - u16 i1 = pxl8_mesh_push_vertex(mesh, verts[1]); - u16 i2 = pxl8_mesh_push_vertex(mesh, verts[2]); - u16 i3 = pxl8_mesh_push_vertex(mesh, verts[3]); - - pxl8_mesh_push_quad(mesh, i0, i1, i2, i3); -} - -static void build_greedy_slice(const pxl8_vxl_chunk* chunk, - const pxl8_vxl_chunk** neighbors, - const pxl8_vxl_block_registry* registry, - const pxl8_vxl_mesh_config* config, - i32 face, i32 slice, - pxl8_mesh* mesh) { - pxl8_vxl_greedy_mask mask; - memset(&mask, 0, sizeof(mask)); - - i32 dx = face_dirs[face][0]; - i32 dy = face_dirs[face][1]; - i32 dz = face_dirs[face][2]; - - for (i32 v = 0; v < PXL8_VXL_CHUNK_SIZE; v++) { - for (i32 u = 0; u < PXL8_VXL_CHUNK_SIZE; u++) { - i32 x, y, z; - slice_to_world(face, slice, u, v, &x, &y, &z); - - u8 block = pxl8_vxl_block_get(chunk, x, y, z); - if (block == PXL8_VXL_BLOCK_AIR) continue; - - pxl8_vxl_geometry geo = pxl8_vxl_block_registry_geometry(registry, block); - if (geo != PXL8_VXL_GEOMETRY_CUBE) continue; - - i32 nx = x + dx; - i32 ny = y + dy; - i32 nz = z + dz; - - u8 neighbor = get_block_or_neighbor(chunk, neighbors, nx, ny, nz); - if (neighbor == PXL8_VXL_BLOCK_UNKNOWN) continue; - if (block_is_full_cube(neighbor, registry)) continue; - - mask.block[u][v] = block; - } - } - - f32 ao_strength = config->ambient_occlusion ? config->ao_strength : 0.0f; - - for (i32 v = 0; v < PXL8_VXL_CHUNK_SIZE; v++) { - for (i32 u = 0; u < PXL8_VXL_CHUNK_SIZE; u++) { - if (mask.done[u][v] || mask.block[u][v] == 0) continue; - - u8 block = mask.block[u][v]; - u8 texture_id = pxl8_vxl_block_registry_texture_for_face(registry, block, face); - - i32 width = 1; - while (u + width < PXL8_VXL_CHUNK_SIZE && - !mask.done[u + width][v] && - mask.block[u + width][v] == block) { - width++; - } - - i32 height = 1; - bool can_expand = true; - while (can_expand && v + height < PXL8_VXL_CHUNK_SIZE) { - for (i32 wu = 0; wu < width; wu++) { - if (mask.done[u + wu][v + height] || - mask.block[u + wu][v + height] != block) { - can_expand = false; - break; - } - } - if (can_expand) height++; - } - - for (i32 dv = 0; dv < height; dv++) { - for (i32 du = 0; du < width; du++) { - mask.done[u + du][v + dv] = true; - } - } - - f32 ao_sum = 0.0f; - i32 ao_count = 0; - if (config->ambient_occlusion) { - i32 cx, cy, cz; - slice_to_world(face, slice, u, v, &cx, &cy, &cz); - i32 fx = cx + dx; - i32 fy = cy + dy; - i32 fz = cz + dz; - - static const i32 ao_offsets[6][4][2][3] = { - [0] = {{{0,-1,0}, {0,0,1}}, {{0,-1,0}, {0,0,-1}}, {{0,1,0}, {0,0,-1}}, {{0,1,0}, {0,0,1}}}, - [1] = {{{0,-1,0}, {0,0,-1}}, {{0,-1,0}, {0,0,1}}, {{0,1,0}, {0,0,1}}, {{0,1,0}, {0,0,-1}}}, - [2] = {{{-1,0,0}, {0,0,-1}}, {{1,0,0}, {0,0,-1}}, {{1,0,0}, {0,0,1}}, {{-1,0,0}, {0,0,1}}}, - [3] = {{{-1,0,0}, {0,0,1}}, {{1,0,0}, {0,0,1}}, {{1,0,0}, {0,0,-1}}, {{-1,0,0}, {0,0,-1}}}, - [4] = {{{-1,0,0}, {0,1,0}}, {{-1,0,0}, {0,-1,0}}, {{1,0,0}, {0,-1,0}}, {{1,0,0}, {0,1,0}}}, - [5] = {{{1,0,0}, {0,-1,0}}, {{1,0,0}, {0,1,0}}, {{-1,0,0}, {0,1,0}}, {{-1,0,0}, {0,-1,0}}} - }; - - for (i32 corner = 0; corner < 4; corner++) { - f32 ao = compute_ao(chunk, neighbors, fx, fy, fz, - ao_offsets[face][corner][0][0], - ao_offsets[face][corner][0][1], - ao_offsets[face][corner][0][2], - ao_offsets[face][corner][1][0], - ao_offsets[face][corner][1][1], - ao_offsets[face][corner][1][2]); - ao_sum += ao; - ao_count++; - } - } - - f32 ao_avg = ao_count > 0 ? ao_sum / (f32)ao_count : 1.0f; - - emit_greedy_quad(mesh, chunk, neighbors, config, face, slice, u, v, width, height, - texture_id, ao_avg, ao_strength); - } - } -} - -static void add_face_vertices(pxl8_mesh* mesh, const pxl8_vxl_mesh_config* config, - pxl8_vec3 pos, i32 face, u8 texture_id, - f32 ao0, f32 ao1, f32 ao2, f32 ao3) { - static const i32 face_vertices[6][4][3] = { - {{0,0,1}, {0,0,0}, {0,1,0}, {0,1,1}}, - {{1,0,0}, {1,0,1}, {1,1,1}, {1,1,0}}, - {{0,0,0}, {1,0,0}, {1,0,1}, {0,0,1}}, - {{0,1,1}, {1,1,1}, {1,1,0}, {0,1,0}}, - {{0,0,0}, {0,1,0}, {1,1,0}, {1,0,0}}, - {{1,0,1}, {1,1,1}, {0,1,1}, {0,0,1}} - }; - - static const f32 face_uvs[4][2] = { - {0, 1}, {1, 1}, {1, 0}, {0, 0} - }; - - f32 ao_values[4] = {ao0, ao1, ao2, ao3}; - f32 ao_strength = config->ambient_occlusion ? config->ao_strength : 0.0f; - f32 tex_scale = config->texture_scale; - - pxl8_vec3 normal = face_normals[face]; - u16 indices[4]; - - for (i32 i = 0; i < 4; i++) { - pxl8_vertex v = {0}; - v.position.x = pos.x + (f32)face_vertices[face][i][0]; - v.position.y = pos.y + (f32)face_vertices[face][i][1]; - v.position.z = pos.z + (f32)face_vertices[face][i][2]; - v.normal = normal; - v.u = face_uvs[i][0] * tex_scale; - v.v = face_uvs[i][1] * tex_scale; - v.color = texture_id; - v.light = (u8)(255.0f * (1.0f - ao_strength * (1.0f - ao_values[i]))); - - indices[i] = pxl8_mesh_push_vertex(mesh, v); - } - - if (ao0 + ao2 > ao1 + ao3) { - pxl8_mesh_push_quad(mesh, indices[0], indices[1], indices[2], indices[3]); - } else { - pxl8_mesh_push_triangle(mesh, indices[0], indices[1], indices[2]); - pxl8_mesh_push_triangle(mesh, indices[0], indices[2], indices[3]); - } -} - -static void add_slab_faces(pxl8_mesh* mesh, const pxl8_vxl_chunk* chunk, - const pxl8_vxl_chunk** neighbors, const pxl8_vxl_block_registry* registry, - const pxl8_vxl_mesh_config* config, - i32 x, i32 y, i32 z, u8 block, bool top) { - u8 texture_id = pxl8_vxl_block_registry_texture(registry, block); - f32 y_offset = top ? 0.5f : 0.0f; - pxl8_vec3 pos = {(f32)x, (f32)y + y_offset, (f32)z}; - - static const i32 horiz_faces[4] = {0, 1, 4, 5}; - - for (i32 i = 0; i < 4; i++) { - i32 face = horiz_faces[i]; - i32 nx = x + face_dirs[face][0]; - i32 nz = z + face_dirs[face][2]; - - u8 neighbor = get_block_or_neighbor(chunk, neighbors, nx, y, nz); - if (neighbor == PXL8_VXL_BLOCK_UNKNOWN) continue; - if (block_is_full_cube(neighbor, registry)) continue; - - add_face_vertices(mesh, config, pos, face, texture_id, 1.0f, 1.0f, 1.0f, 1.0f); - } - - i32 top_face = 3; - i32 bot_face = 2; - - if (top) { - u8 above = get_block_or_neighbor(chunk, neighbors, x, y + 1, z); - if (above != PXL8_VXL_BLOCK_UNKNOWN && !block_is_full_cube(above, registry)) { - add_face_vertices(mesh, config, pos, top_face, texture_id, 1.0f, 1.0f, 1.0f, 1.0f); - } - add_face_vertices(mesh, config, (pxl8_vec3){(f32)x, (f32)y + 0.5f, (f32)z}, bot_face, texture_id, 1.0f, 1.0f, 1.0f, 1.0f); - } else { - add_face_vertices(mesh, config, pos, top_face, texture_id, 1.0f, 1.0f, 1.0f, 1.0f); - u8 below = get_block_or_neighbor(chunk, neighbors, x, y - 1, z); - if (below != PXL8_VXL_BLOCK_UNKNOWN && !block_is_full_cube(below, registry)) { - add_face_vertices(mesh, config, pos, bot_face, texture_id, 1.0f, 1.0f, 1.0f, 1.0f); - } - } -} - -static void add_slope_faces(pxl8_mesh* mesh, const pxl8_vxl_chunk* chunk, - const pxl8_vxl_chunk** neighbors, const pxl8_vxl_block_registry* registry, - const pxl8_vxl_mesh_config* config, - i32 x, i32 y, i32 z, u8 block, i32 direction) { - u8 texture_top = pxl8_vxl_block_registry_texture_for_face(registry, block, 3); - u8 texture_side = pxl8_vxl_block_registry_texture_for_face(registry, block, 0); - pxl8_vec3 pos = {(f32)x, (f32)y, (f32)z}; - - u8 below = get_block_or_neighbor(chunk, neighbors, x, y - 1, z); - if (below != PXL8_VXL_BLOCK_UNKNOWN && !block_is_full_cube(below, registry)) { - add_face_vertices(mesh, config, pos, 2, texture_side, 1.0f, 1.0f, 1.0f, 1.0f); - } - - static const i32 dir_to_back_face[4] = {5, 4, 1, 0}; - i32 back_face = dir_to_back_face[direction]; - - i32 bx = x + face_dirs[back_face][0]; - i32 bz = z + face_dirs[back_face][2]; - u8 back_neighbor = get_block_or_neighbor(chunk, neighbors, bx, y, bz); - if (back_neighbor != PXL8_VXL_BLOCK_UNKNOWN && !block_is_full_cube(back_neighbor, registry)) { - add_face_vertices(mesh, config, pos, back_face, texture_side, 1.0f, 1.0f, 1.0f, 1.0f); - } - - static const f32 slope_verts[4][4][3] = { - {{0,0,0}, {1,0,0}, {1,1,1}, {0,1,1}}, - {{0,0,1}, {1,0,1}, {1,1,0}, {0,1,0}}, - {{1,0,0}, {1,0,1}, {0,1,1}, {0,1,0}}, - {{0,0,0}, {0,0,1}, {1,1,1}, {1,1,0}} - }; - - static const pxl8_vec3 slope_normals[4] = { - {0, 0.707f, -0.707f}, {0, 0.707f, 0.707f}, - {-0.707f, 0.707f, 0}, {0.707f, 0.707f, 0} - }; - - pxl8_vertex verts[4]; - memset(verts, 0, sizeof(verts)); - for (i32 i = 0; i < 4; i++) { - verts[i].position.x = pos.x + slope_verts[direction][i][0]; - verts[i].position.y = pos.y + slope_verts[direction][i][1]; - verts[i].position.z = pos.z + slope_verts[direction][i][2]; - verts[i].normal = slope_normals[direction]; - verts[i].color = texture_top; - verts[i].light = 255; - } - - u16 i0 = pxl8_mesh_push_vertex(mesh, verts[0]); - u16 i1 = pxl8_mesh_push_vertex(mesh, verts[1]); - u16 i2 = pxl8_mesh_push_vertex(mesh, verts[2]); - u16 i3 = pxl8_mesh_push_vertex(mesh, verts[3]); - pxl8_mesh_push_quad(mesh, i0, i1, i2, i3); - - static const i32 side_faces[4][2] = {{0, 1}, {0, 1}, {4, 5}, {4, 5}}; - static const f32 side_tris[4][2][3][3] = { - {{{0,0,0}, {0,1,1}, {0,0,1}}, {{1,0,0}, {1,0,1}, {1,1,1}}}, - {{{0,0,0}, {0,0,1}, {0,1,0}}, {{1,0,0}, {1,1,0}, {1,0,1}}}, - {{{0,0,0}, {0,0,1}, {0,1,1}}, {{1,0,0}, {1,1,0}, {1,0,1}}}, - {{{0,0,0}, {0,1,0}, {0,0,1}}, {{1,0,0}, {1,0,1}, {1,1,1}}} - }; - static const pxl8_vec3 side_normals[6] = { - {-1, 0, 0}, {1, 0, 0}, {0, -1, 0}, {0, 1, 0}, {0, 0, -1}, {0, 0, 1} - }; - - for (i32 s = 0; s < 2; s++) { - i32 side_face = side_faces[direction][s]; - i32 snx = x + face_dirs[side_face][0]; - i32 snz = z + face_dirs[side_face][2]; - u8 side_neighbor = get_block_or_neighbor(chunk, neighbors, snx, y, snz); - if (side_neighbor != PXL8_VXL_BLOCK_UNKNOWN && !block_is_full_cube(side_neighbor, registry)) { - pxl8_vertex tv[3]; - memset(tv, 0, sizeof(tv)); - for (i32 vi = 0; vi < 3; vi++) { - tv[vi].position.x = pos.x + side_tris[direction][s][vi][0]; - tv[vi].position.y = pos.y + side_tris[direction][s][vi][1]; - tv[vi].position.z = pos.z + side_tris[direction][s][vi][2]; - tv[vi].normal = side_normals[side_face]; - tv[vi].color = texture_side; - tv[vi].light = 255; - } - u16 ti0 = pxl8_mesh_push_vertex(mesh, tv[0]); - u16 ti1 = pxl8_mesh_push_vertex(mesh, tv[1]); - u16 ti2 = pxl8_mesh_push_vertex(mesh, tv[2]); - pxl8_mesh_push_triangle(mesh, ti0, ti1, ti2); - } - } -} - -pxl8_mesh* pxl8_vxl_build_mesh(const pxl8_vxl_chunk* chunk, - const pxl8_vxl_chunk** neighbors, - const pxl8_vxl_block_registry* registry, - const pxl8_vxl_mesh_config* config) { - if (!chunk || !registry) return NULL; - - pxl8_vxl_mesh_config cfg = config ? *config : PXL8_VXL_MESH_CONFIG_DEFAULT; - - u32 max_faces = PXL8_VXL_CHUNK_VOLUME * 6; - pxl8_mesh* mesh = pxl8_mesh_create(max_faces * 4, max_faces * 6); - if (!mesh) return NULL; - - for (i32 face = 0; face < 6; face++) { - for (i32 slice = 0; slice < PXL8_VXL_CHUNK_SIZE; slice++) { - build_greedy_slice(chunk, neighbors, registry, &cfg, face, slice, mesh); - } - } - - for (i32 z = 0; z < PXL8_VXL_CHUNK_SIZE; z++) { - for (i32 y = 0; y < PXL8_VXL_CHUNK_SIZE; y++) { - for (i32 x = 0; x < PXL8_VXL_CHUNK_SIZE; x++) { - u8 block = pxl8_vxl_block_get(chunk, x, y, z); - if (block == PXL8_VXL_BLOCK_AIR) continue; - - pxl8_vxl_geometry geo = pxl8_vxl_block_registry_geometry(registry, block); - - switch (geo) { - case PXL8_VXL_GEOMETRY_CUBE: - break; - case PXL8_VXL_GEOMETRY_SLAB_BOTTOM: - add_slab_faces(mesh, chunk, neighbors, registry, &cfg, x, y, z, block, false); - break; - case PXL8_VXL_GEOMETRY_SLAB_TOP: - add_slab_faces(mesh, chunk, neighbors, registry, &cfg, x, y, z, block, true); - break; - case PXL8_VXL_GEOMETRY_SLOPE_NORTH: - add_slope_faces(mesh, chunk, neighbors, registry, &cfg, x, y, z, block, 0); - break; - case PXL8_VXL_GEOMETRY_SLOPE_SOUTH: - add_slope_faces(mesh, chunk, neighbors, registry, &cfg, x, y, z, block, 1); - break; - case PXL8_VXL_GEOMETRY_SLOPE_EAST: - add_slope_faces(mesh, chunk, neighbors, registry, &cfg, x, y, z, block, 2); - break; - case PXL8_VXL_GEOMETRY_SLOPE_WEST: - add_slope_faces(mesh, chunk, neighbors, registry, &cfg, x, y, z, block, 3); - break; - case PXL8_VXL_GEOMETRY_STAIRS_NORTH: - case PXL8_VXL_GEOMETRY_STAIRS_SOUTH: - case PXL8_VXL_GEOMETRY_STAIRS_EAST: - case PXL8_VXL_GEOMETRY_STAIRS_WEST: - break; - } - } - } - } - - return mesh; -} - -pxl8_vxl_render_state* pxl8_vxl_render_state_create(void) { - return pxl8_calloc(1, sizeof(pxl8_vxl_render_state)); -} - -void pxl8_vxl_render_state_destroy(pxl8_vxl_render_state* state) { - pxl8_free(state); -} diff --git a/src/vxl/pxl8_vxl_render.h b/src/vxl/pxl8_vxl_render.h deleted file mode 100644 index 6e5b6cf..0000000 --- a/src/vxl/pxl8_vxl_render.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include "pxl8_mesh.h" -#include "pxl8_types.h" -#include "pxl8_vxl.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct pxl8_vxl_render_state { - u8 _unused; -} pxl8_vxl_render_state; - -pxl8_vxl_render_state* pxl8_vxl_render_state_create(void); -void pxl8_vxl_render_state_destroy(pxl8_vxl_render_state* state); - -typedef struct pxl8_vxl_mesh_config { - bool ambient_occlusion; - bool vertex_heights; - f32 ao_strength; - f32 texture_scale; - i32 chunk_x; - i32 chunk_y; - i32 chunk_z; - u64 seed; -} pxl8_vxl_mesh_config; - -#define PXL8_VXL_MESH_CONFIG_DEFAULT ((pxl8_vxl_mesh_config){ \ - .ambient_occlusion = true, \ - .vertex_heights = true, \ - .ao_strength = 0.2f, \ - .texture_scale = 1.0f, \ - .chunk_x = 0, \ - .chunk_y = 0, \ - .chunk_z = 0, \ - .seed = 12345 \ -}) - -pxl8_mesh* pxl8_vxl_build_mesh(const pxl8_vxl_chunk* chunk, - const pxl8_vxl_chunk** neighbors, - const pxl8_vxl_block_registry* registry, - const pxl8_vxl_mesh_config* config); - -#ifdef __cplusplus -} -#endif diff --git a/src/world/pxl8_world.c b/src/world/pxl8_world.c index 48913c0..854d7f3 100644 --- a/src/world/pxl8_world.c +++ b/src/world/pxl8_world.c @@ -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)); diff --git a/src/world/pxl8_world.h b/src/world/pxl8_world.h index 11f31fd..bb09a41 100644 --- a/src/world/pxl8_world.h +++ b/src/world/pxl8_world.h @@ -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); diff --git a/src/world/pxl8_world_chunk.c b/src/world/pxl8_world_chunk.c index 9ffe42f..be19fdc 100644 --- a/src/world/pxl8_world_chunk.c +++ b/src/world/pxl8_world_chunk.c @@ -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); } diff --git a/src/world/pxl8_world_chunk.h b/src/world/pxl8_world_chunk.h index 15f2551..3e9d8d3 100644 --- a/src/world/pxl8_world_chunk.h +++ b/src/world/pxl8_world_chunk.h @@ -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); diff --git a/src/world/pxl8_world_chunk_cache.c b/src/world/pxl8_world_chunk_cache.c index 89f5e38..181e807 100644 --- a/src/world/pxl8_world_chunk_cache.c +++ b/src/world/pxl8_world_chunk_cache.c @@ -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; - } -} diff --git a/src/world/pxl8_world_chunk_cache.h b/src/world/pxl8_world_chunk_cache.h index 756caa7..f3f73c2 100644 --- a/src/world/pxl8_world_chunk_cache.h +++ b/src/world/pxl8_world_chunk_cache.h @@ -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 }