refactor: decouple sim from framework, remove voxel geometry
This commit is contained in:
parent
c538641ec8
commit
5a565844dd
41 changed files with 477 additions and 2407 deletions
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
12
pxl8.sh
12
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"
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<ChunkId, u32>,
|
||||
|
|
@ -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<ChunkId> {
|
||||
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(_)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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::*;
|
||||
|
|
|
|||
|
|
@ -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<u64> = 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<transport::ChunkMessage> {
|
||||
use alloc::vec::Vec;
|
||||
|
||||
pxl8_debug!("[SERVER] bsp_to_messages: serializing BSP");
|
||||
|
||||
let mut data = Vec::new();
|
||||
|
||||
let num_verts = bsp.vertices.len();
|
||||
|
|
|
|||
|
|
@ -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<u32>,
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<ChunkMessage> {
|
||||
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<u8>, chunk_id: u32, version: u32) -> Vec<ChunkMessage> {
|
||||
let total_size = data.len();
|
||||
|
||||
|
|
|
|||
|
|
@ -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<u8> {
|
||||
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<VoxelChunk>,
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include <string.h>
|
||||
|
||||
#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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
#define PXL8_UI_GRAY 15
|
||||
|
||||
static const u32 pxl8_ui_palette[PXL8_UI_PALETTE_SIZE] = {
|
||||
0xFF282828,
|
||||
0xFF21201d,
|
||||
0xFF3c3836,
|
||||
0xFF504945,
|
||||
0xFF665c54,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -2,15 +2,6 @@
|
|||
|
||||
#include <math.h>
|
||||
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,319 +0,0 @@
|
|||
#include "pxl8_vxl.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -1,571 +0,0 @@
|
|||
#include "pxl8_vxl_render.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -15,28 +15,20 @@
|
|||
#include "pxl8_log.h"
|
||||
#include "pxl8_mem.h"
|
||||
#include "pxl8_sim.h"
|
||||
#include "pxl8_vxl.h"
|
||||
#include "pxl8_vxl_render.h"
|
||||
|
||||
#define PXL8_WORLD_ENTITY_CAPACITY 256
|
||||
#define VOXEL_SCALE 16.0f
|
||||
#define VOXEL_CHUNK_SIZE 32.0f
|
||||
#define WORLD_CHUNK_SIZE (VOXEL_CHUNK_SIZE * VOXEL_SCALE)
|
||||
|
||||
struct pxl8_world {
|
||||
pxl8_world_chunk* active_chunk;
|
||||
pxl8_vxl_block_registry* block_registry;
|
||||
pxl8_world_chunk_cache* chunk_cache;
|
||||
pxl8_entity_pool* entities;
|
||||
pxl8_bsp_render_state* bsp_render_state;
|
||||
pxl8_vxl_render_state* vxl_render_state;
|
||||
i32 render_distance;
|
||||
i32 sim_distance;
|
||||
|
||||
pxl8_sim_entity local_player;
|
||||
u64 client_tick;
|
||||
|
||||
pxl8_vec2 pointer_motion;
|
||||
pxl8_sim_config sim_config;
|
||||
|
||||
#ifdef PXL8_ASYNC_THREADS
|
||||
pxl8_sim_entity render_state[2];
|
||||
|
|
@ -54,14 +46,22 @@ pxl8_world* pxl8_world_create(void) {
|
|||
pxl8_world* world = pxl8_calloc(1, sizeof(pxl8_world));
|
||||
if (!world) return NULL;
|
||||
|
||||
world->block_registry = pxl8_vxl_block_registry_create();
|
||||
world->chunk_cache = pxl8_world_chunk_cache_create();
|
||||
world->entities = pxl8_entity_pool_create(PXL8_WORLD_ENTITY_CAPACITY);
|
||||
world->vxl_render_state = pxl8_vxl_render_state_create();
|
||||
world->render_distance = 3;
|
||||
world->sim_distance = 4;
|
||||
world->sim_config = (pxl8_sim_config){
|
||||
.move_speed = 180.0f,
|
||||
.ground_accel = 10.0f,
|
||||
.air_accel = 1.0f,
|
||||
.stop_speed = 100.0f,
|
||||
.friction = 6.0f,
|
||||
.gravity = 800.0f,
|
||||
.jump_velocity = 200.0f,
|
||||
.player_radius = 16.0f,
|
||||
.player_height = 72.0f,
|
||||
.max_pitch = 1.5f,
|
||||
};
|
||||
|
||||
if (!world->block_registry || !world->chunk_cache || !world->entities || !world->vxl_render_state) {
|
||||
if (!world->chunk_cache || !world->entities) {
|
||||
pxl8_world_destroy(world);
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -72,11 +72,9 @@ pxl8_world* pxl8_world_create(void) {
|
|||
void pxl8_world_destroy(pxl8_world* world) {
|
||||
if (!world) return;
|
||||
|
||||
pxl8_vxl_block_registry_destroy(world->block_registry);
|
||||
pxl8_world_chunk_cache_destroy(world->chunk_cache);
|
||||
pxl8_entity_pool_destroy(world->entities);
|
||||
pxl8_bsp_render_state_destroy(world->bsp_render_state);
|
||||
pxl8_vxl_render_state_destroy(world->vxl_render_state);
|
||||
pxl8_free(world);
|
||||
}
|
||||
|
||||
|
|
@ -95,11 +93,6 @@ void pxl8_world_set_active_chunk(pxl8_world* world, pxl8_world_chunk* chunk) {
|
|||
world->active_chunk = chunk;
|
||||
}
|
||||
|
||||
pxl8_vxl_block_registry* pxl8_world_block_registry(pxl8_world* world) {
|
||||
if (!world) return NULL;
|
||||
return world->block_registry;
|
||||
}
|
||||
|
||||
pxl8_entity_pool* pxl8_world_entities(pxl8_world* world) {
|
||||
if (!world) return NULL;
|
||||
return world->entities;
|
||||
|
|
@ -110,53 +103,12 @@ pxl8_entity pxl8_world_spawn(pxl8_world* world) {
|
|||
return pxl8_entity_spawn(world->entities);
|
||||
}
|
||||
|
||||
static bool voxel_point_solid(pxl8_world_chunk_cache* cache, f32 x, f32 y, f32 z) {
|
||||
i32 cx = (i32)floorf(x / WORLD_CHUNK_SIZE);
|
||||
i32 cy = (i32)floorf(y / WORLD_CHUNK_SIZE);
|
||||
i32 cz = (i32)floorf(z / WORLD_CHUNK_SIZE);
|
||||
|
||||
f32 local_x = (x - cx * WORLD_CHUNK_SIZE) / VOXEL_SCALE;
|
||||
f32 local_y = (y - cy * WORLD_CHUNK_SIZE) / VOXEL_SCALE;
|
||||
f32 local_z = (z - cz * WORLD_CHUNK_SIZE) / VOXEL_SCALE;
|
||||
|
||||
i32 lx = (i32)floorf(local_x);
|
||||
i32 ly = (i32)floorf(local_y);
|
||||
i32 lz = (i32)floorf(local_z);
|
||||
|
||||
lx = lx < 0 ? 0 : (lx >= 32 ? 31 : lx);
|
||||
ly = ly < 0 ? 0 : (ly >= 32 ? 31 : ly);
|
||||
lz = lz < 0 ? 0 : (lz >= 32 ? 31 : lz);
|
||||
|
||||
pxl8_world_chunk* chunk = pxl8_world_chunk_cache_get_vxl(cache, cx, cy, cz);
|
||||
if (!chunk || !chunk->voxels) {
|
||||
return ly < 8;
|
||||
}
|
||||
|
||||
return pxl8_vxl_block_get(chunk->voxels, lx, ly, lz) != PXL8_VXL_BLOCK_AIR;
|
||||
}
|
||||
|
||||
static pxl8_sim_world make_sim_world(const pxl8_world* world, pxl8_vec3 pos) {
|
||||
pxl8_sim_world pxl8_world_sim_world(const pxl8_world* world, pxl8_vec3 pos) {
|
||||
(void)pos;
|
||||
pxl8_sim_world sim = {0};
|
||||
|
||||
if (world->active_chunk && world->active_chunk->type == PXL8_WORLD_CHUNK_BSP) {
|
||||
if (world->active_chunk && world->active_chunk->bsp) {
|
||||
sim.bsp = world->active_chunk->bsp;
|
||||
return sim;
|
||||
}
|
||||
|
||||
if (world->chunk_cache) {
|
||||
i32 cx = (i32)floorf(pos.x / WORLD_CHUNK_SIZE);
|
||||
i32 cy = (i32)floorf(pos.y / WORLD_CHUNK_SIZE);
|
||||
i32 cz = (i32)floorf(pos.z / WORLD_CHUNK_SIZE);
|
||||
|
||||
pxl8_world_chunk* chunk = pxl8_world_chunk_cache_get_vxl(world->chunk_cache, cx, cy, cz);
|
||||
if (chunk && chunk->voxels) {
|
||||
sim.vxl = chunk->voxels;
|
||||
sim.vxl_cx = cx;
|
||||
sim.vxl_cy = cy;
|
||||
sim.vxl_cz = cz;
|
||||
}
|
||||
}
|
||||
|
||||
return sim;
|
||||
}
|
||||
|
||||
|
|
@ -191,12 +143,8 @@ static void userdata_to_entity(const u8* userdata, pxl8_sim_entity* ent) {
|
|||
bool pxl8_world_point_solid(const pxl8_world* world, f32 x, f32 y, f32 z) {
|
||||
if (!world) return false;
|
||||
|
||||
if (world->active_chunk) {
|
||||
if (world->active_chunk->type == PXL8_WORLD_CHUNK_BSP && world->active_chunk->bsp) {
|
||||
return pxl8_bsp_point_solid(world->active_chunk->bsp, (pxl8_vec3){x, y, z});
|
||||
}
|
||||
} else if (world->chunk_cache) {
|
||||
return voxel_point_solid(world->chunk_cache, x, y, z);
|
||||
if (world->active_chunk && world->active_chunk->bsp) {
|
||||
return pxl8_bsp_point_solid(world->active_chunk->bsp, (pxl8_vec3){x, y, z});
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
@ -290,99 +238,30 @@ pxl8_ray pxl8_world_sweep(const pxl8_world* world, pxl8_vec3 from, pxl8_vec3 to,
|
|||
return result;
|
||||
}
|
||||
|
||||
void pxl8_world_update(pxl8_world* world, const pxl8_input_state* input, f32 dt) {
|
||||
void pxl8_world_update(pxl8_world* world, f32 dt) {
|
||||
(void)dt;
|
||||
if (!world) return;
|
||||
|
||||
pxl8_world_chunk_cache_tick(world->chunk_cache);
|
||||
|
||||
if (input && (world->local_player.flags & PXL8_SIM_FLAG_ALIVE)) {
|
||||
pxl8_input_msg msg = {0};
|
||||
msg.move_x = (pxl8_key_down(input, "d") ? 1.0f : 0.0f) - (pxl8_key_down(input, "a") ? 1.0f : 0.0f);
|
||||
msg.move_y = (pxl8_key_down(input, "w") ? 1.0f : 0.0f) - (pxl8_key_down(input, "s") ? 1.0f : 0.0f);
|
||||
msg.look_dx = (f32)pxl8_mouse_dx(input);
|
||||
msg.look_dy = (f32)pxl8_mouse_dy(input);
|
||||
msg.buttons = pxl8_key_down(input, "space") ? 1 : 0;
|
||||
msg.tick = world->client_tick;
|
||||
msg.timestamp = pxl8_get_ticks_ns();
|
||||
|
||||
if (world->net) {
|
||||
pxl8_net_send_input(world->net, &msg);
|
||||
}
|
||||
|
||||
pxl8_sim_world sim = make_sim_world(world, world->local_player.pos);
|
||||
pxl8_sim_move_player(&world->local_player, &msg, &sim, dt);
|
||||
world->client_tick++;
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos) {
|
||||
if (!world || !gfx) return;
|
||||
|
||||
if (world->active_chunk) {
|
||||
if (world->active_chunk->type == PXL8_WORLD_CHUNK_BSP && world->active_chunk->bsp) {
|
||||
pxl8_3d_set_bsp(gfx, world->active_chunk->bsp);
|
||||
pxl8_bsp_render(gfx, world->active_chunk->bsp,
|
||||
world->bsp_render_state, camera_pos);
|
||||
}
|
||||
if (world->active_chunk && world->active_chunk->bsp) {
|
||||
pxl8_3d_set_bsp(gfx, world->active_chunk->bsp);
|
||||
pxl8_bsp_render(gfx, world->active_chunk->bsp,
|
||||
world->bsp_render_state, camera_pos);
|
||||
} else {
|
||||
pxl8_3d_set_bsp(gfx, NULL);
|
||||
i32 cx = (i32)floorf(camera_pos.x / WORLD_CHUNK_SIZE);
|
||||
i32 cy = (i32)floorf(camera_pos.y / WORLD_CHUNK_SIZE);
|
||||
i32 cz = (i32)floorf(camera_pos.z / WORLD_CHUNK_SIZE);
|
||||
|
||||
const pxl8_frustum* frustum = pxl8_3d_get_frustum(gfx);
|
||||
|
||||
pxl8_gfx_material mat = {
|
||||
.texture_id = 0,
|
||||
.dynamic_lighting = true,
|
||||
.alpha = 255,
|
||||
};
|
||||
|
||||
i32 r = world->render_distance;
|
||||
for (i32 dy = -r; dy <= r; dy++) {
|
||||
for (i32 dz = -r; dz <= r; dz++) {
|
||||
for (i32 dx = -r; dx <= r; dx++) {
|
||||
i32 chunk_cx = cx + dx;
|
||||
i32 chunk_cy = cy + dy;
|
||||
i32 chunk_cz = cz + dz;
|
||||
f32 chunk_x = chunk_cx * WORLD_CHUNK_SIZE;
|
||||
f32 chunk_y = chunk_cy * WORLD_CHUNK_SIZE;
|
||||
f32 chunk_z = chunk_cz * WORLD_CHUNK_SIZE;
|
||||
|
||||
if (frustum) {
|
||||
pxl8_vec3 aabb_min = {chunk_x, chunk_y, chunk_z};
|
||||
pxl8_vec3 aabb_max = {chunk_x + WORLD_CHUNK_SIZE, chunk_y + WORLD_CHUNK_SIZE, chunk_z + WORLD_CHUNK_SIZE};
|
||||
if (!pxl8_frustum_test_aabb(frustum, aabb_min, aabb_max)) continue;
|
||||
}
|
||||
|
||||
pxl8_vxl_mesh_config config = PXL8_VXL_MESH_CONFIG_DEFAULT;
|
||||
config.chunk_x = chunk_cx;
|
||||
config.chunk_y = chunk_cy;
|
||||
config.chunk_z = chunk_cz;
|
||||
|
||||
pxl8_mesh* mesh = pxl8_world_chunk_cache_get_mesh(
|
||||
world->chunk_cache,
|
||||
chunk_cx, chunk_cy, chunk_cz,
|
||||
world->block_registry, &config);
|
||||
if (mesh && mesh->index_count > 0) {
|
||||
pxl8_mat4 scale = pxl8_mat4_scale(VOXEL_SCALE, VOXEL_SCALE, VOXEL_SCALE);
|
||||
pxl8_mat4 translate = pxl8_mat4_translate(chunk_x, chunk_y, chunk_z);
|
||||
pxl8_mat4 model = pxl8_mat4_multiply(translate, scale);
|
||||
pxl8_3d_draw_mesh(gfx, mesh, &model, &mat);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_world_sync(pxl8_world* world, pxl8_net* net) {
|
||||
if (!world || !net) return;
|
||||
|
||||
u8 chunk_type = pxl8_net_chunk_type(net);
|
||||
u32 chunk_id = pxl8_net_chunk_id(net);
|
||||
|
||||
if (chunk_type == PXL8_CHUNK_TYPE_BSP && chunk_id != 0) {
|
||||
if (chunk_id != 0) {
|
||||
pxl8_world_chunk* chunk = pxl8_world_chunk_cache_get_bsp(world->chunk_cache, chunk_id);
|
||||
if (chunk && chunk->bsp) {
|
||||
if (world->active_chunk != chunk) {
|
||||
|
|
@ -397,7 +276,7 @@ void pxl8_world_sync(pxl8_world* world, pxl8_net* net) {
|
|||
world->bsp_render_state = pxl8_bsp_render_state_create(chunk->bsp->num_faces);
|
||||
}
|
||||
}
|
||||
} else if (chunk_id == 0 && world->active_chunk != NULL) {
|
||||
} else if (world->active_chunk != NULL) {
|
||||
world->active_chunk = NULL;
|
||||
if (world->bsp_render_state) {
|
||||
pxl8_bsp_render_state_destroy(world->bsp_render_state);
|
||||
|
|
@ -409,7 +288,6 @@ void pxl8_world_sync(pxl8_world* world, pxl8_net* net) {
|
|||
static void ensure_bsp_render_state(pxl8_world* world) {
|
||||
if (!world || world->bsp_render_state) return;
|
||||
if (!world->active_chunk) return;
|
||||
if (world->active_chunk->type != PXL8_WORLD_CHUNK_BSP) return;
|
||||
if (!world->active_chunk->bsp) return;
|
||||
|
||||
world->bsp_render_state = pxl8_bsp_render_state_create(world->active_chunk->bsp->num_faces);
|
||||
|
|
@ -424,28 +302,9 @@ void pxl8_world_set_bsp_material(pxl8_world* world, u16 material_id, const pxl8_
|
|||
pxl8_bsp_set_material(world->bsp_render_state, material_id, material);
|
||||
}
|
||||
|
||||
i32 pxl8_world_get_render_distance(const pxl8_world* world) {
|
||||
if (!world) return 3;
|
||||
return world->render_distance;
|
||||
}
|
||||
|
||||
void pxl8_world_set_render_distance(pxl8_world* world, i32 distance) {
|
||||
if (!world) return;
|
||||
if (distance < 1) distance = 1;
|
||||
if (distance > 8) distance = 8;
|
||||
world->render_distance = distance;
|
||||
}
|
||||
|
||||
i32 pxl8_world_get_sim_distance(const pxl8_world* world) {
|
||||
if (!world) return 4;
|
||||
return world->sim_distance;
|
||||
}
|
||||
|
||||
void pxl8_world_set_sim_distance(pxl8_world* world, i32 distance) {
|
||||
if (!world) return;
|
||||
if (distance < 1) distance = 1;
|
||||
if (distance > 8) distance = 8;
|
||||
world->sim_distance = distance;
|
||||
void pxl8_world_set_sim_config(pxl8_world* world, const pxl8_sim_config* config) {
|
||||
if (!world || !config) return;
|
||||
world->sim_config = *config;
|
||||
}
|
||||
|
||||
void pxl8_world_init_local_player(pxl8_world* world, f32 x, f32 y, f32 z) {
|
||||
|
|
@ -481,8 +340,8 @@ void pxl8_world_predict(pxl8_world* world, pxl8_net* net, const pxl8_input_msg*
|
|||
if (!world || !net || !input) return;
|
||||
if (!(world->local_player.flags & PXL8_SIM_FLAG_ALIVE)) return;
|
||||
|
||||
pxl8_sim_world sim = make_sim_world(world, world->local_player.pos);
|
||||
pxl8_sim_move_player(&world->local_player, input, &sim, dt);
|
||||
pxl8_sim_world sim = pxl8_world_sim_world(world, world->local_player.pos);
|
||||
pxl8_sim_move_player(&world->local_player, input, &sim, &world->sim_config, dt);
|
||||
|
||||
world->client_tick++;
|
||||
|
||||
|
|
@ -515,8 +374,8 @@ void pxl8_world_reconcile(pxl8_world* world, pxl8_net* net, f32 dt) {
|
|||
const pxl8_input_msg* input = pxl8_net_input_at(net, tick);
|
||||
if (!input) continue;
|
||||
|
||||
pxl8_sim_world sim = make_sim_world(world, world->local_player.pos);
|
||||
pxl8_sim_move_player(&world->local_player, input, &sim, dt);
|
||||
pxl8_sim_world sim = pxl8_world_sim_world(world, world->local_player.pos);
|
||||
pxl8_sim_move_player(&world->local_player, input, &sim, &world->sim_config, dt);
|
||||
}
|
||||
|
||||
entity_to_userdata(&world->local_player, pxl8_net_predicted_state(net));
|
||||
|
|
@ -562,8 +421,8 @@ static void pxl8_world_sim_tick(pxl8_world* world, f32 dt) {
|
|||
merged.look_dx = 0;
|
||||
merged.look_dy = 0;
|
||||
|
||||
pxl8_sim_world sim = make_sim_world(world, world->local_player.pos);
|
||||
pxl8_sim_move_player(&world->local_player, &merged, &sim, dt);
|
||||
pxl8_sim_world sim = pxl8_world_sim_world(world, world->local_player.pos);
|
||||
pxl8_sim_move_player(&world->local_player, &merged, &sim, &world->sim_config, dt);
|
||||
|
||||
if (world->net) {
|
||||
entity_to_userdata(&world->local_player, pxl8_net_predicted_state(world->net));
|
||||
|
|
@ -672,8 +531,8 @@ void pxl8_world_push_input(pxl8_world* world, const pxl8_input_msg* input) {
|
|||
|
||||
world->pointer_motion.yaw -= input->look_dx * 0.008f;
|
||||
f32 pitch = world->pointer_motion.pitch - input->look_dy * 0.008f;
|
||||
if (pitch > PXL8_SIM_MAX_PITCH) pitch = PXL8_SIM_MAX_PITCH;
|
||||
if (pitch < -PXL8_SIM_MAX_PITCH) pitch = -PXL8_SIM_MAX_PITCH;
|
||||
if (pitch > world->sim_config.max_pitch) pitch = world->sim_config.max_pitch;
|
||||
if (pitch < -world->sim_config.max_pitch) pitch = -world->sim_config.max_pitch;
|
||||
world->pointer_motion.pitch = pitch;
|
||||
|
||||
pxl8_input_msg* copy = pxl8_malloc(sizeof(pxl8_input_msg));
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ pxl8_world_chunk_cache* pxl8_world_get_chunk_cache(pxl8_world* world);
|
|||
pxl8_world_chunk* pxl8_world_active_chunk(pxl8_world* world);
|
||||
void pxl8_world_set_active_chunk(pxl8_world* world, pxl8_world_chunk* chunk);
|
||||
|
||||
pxl8_vxl_block_registry* pxl8_world_block_registry(pxl8_world* world);
|
||||
pxl8_entity_pool* pxl8_world_entities(pxl8_world* world);
|
||||
pxl8_entity pxl8_world_spawn(pxl8_world* world);
|
||||
|
||||
|
|
@ -30,19 +29,16 @@ bool pxl8_world_point_solid(const pxl8_world* world, f32 x, f32 y, f32 z);
|
|||
pxl8_ray pxl8_world_ray(const pxl8_world* world, pxl8_vec3 from, pxl8_vec3 to);
|
||||
pxl8_ray pxl8_world_sweep(const pxl8_world* world, pxl8_vec3 from, pxl8_vec3 to, f32 radius);
|
||||
|
||||
void pxl8_world_update(pxl8_world* world, const pxl8_input_state* input, f32 dt);
|
||||
void pxl8_world_update(pxl8_world* world, f32 dt);
|
||||
void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos);
|
||||
void pxl8_world_sync(pxl8_world* world, pxl8_net* net);
|
||||
|
||||
void pxl8_world_set_bsp_material(pxl8_world* world, u16 material_id, const pxl8_gfx_material* material);
|
||||
|
||||
i32 pxl8_world_get_render_distance(const pxl8_world* world);
|
||||
void pxl8_world_set_render_distance(pxl8_world* world, i32 distance);
|
||||
i32 pxl8_world_get_sim_distance(const pxl8_world* world);
|
||||
void pxl8_world_set_sim_distance(pxl8_world* world, i32 distance);
|
||||
|
||||
void pxl8_world_set_sim_config(pxl8_world* world, const pxl8_sim_config* config);
|
||||
void pxl8_world_init_local_player(pxl8_world* world, f32 x, f32 y, f32 z);
|
||||
pxl8_sim_entity* pxl8_world_local_player(pxl8_world* world);
|
||||
pxl8_sim_world pxl8_world_sim_world(const pxl8_world* world, pxl8_vec3 pos);
|
||||
void pxl8_world_predict(pxl8_world* world, pxl8_net* net, const pxl8_input_msg* input, f32 dt);
|
||||
void pxl8_world_reconcile(pxl8_world* world, pxl8_net* net, f32 dt);
|
||||
|
||||
|
|
|
|||
|
|
@ -2,45 +2,16 @@
|
|||
|
||||
#include "pxl8_bsp.h"
|
||||
#include "pxl8_mem.h"
|
||||
#include "pxl8_vxl.h"
|
||||
|
||||
pxl8_world_chunk* pxl8_world_chunk_create_vxl(i32 cx, i32 cy, i32 cz) {
|
||||
pxl8_world_chunk* chunk = pxl8_calloc(1, sizeof(pxl8_world_chunk));
|
||||
if (!chunk) return NULL;
|
||||
|
||||
chunk->type = PXL8_WORLD_CHUNK_VXL;
|
||||
chunk->cx = cx;
|
||||
chunk->cy = cy;
|
||||
chunk->cz = cz;
|
||||
chunk->voxels = pxl8_vxl_chunk_create();
|
||||
|
||||
if (!chunk->voxels) {
|
||||
pxl8_free(chunk);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
pxl8_world_chunk* pxl8_world_chunk_create_bsp(u32 id) {
|
||||
pxl8_world_chunk* chunk = pxl8_calloc(1, sizeof(pxl8_world_chunk));
|
||||
if (!chunk) return NULL;
|
||||
|
||||
chunk->type = PXL8_WORLD_CHUNK_BSP;
|
||||
chunk->id = id;
|
||||
chunk->bsp = NULL;
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
void pxl8_world_chunk_destroy(pxl8_world_chunk* chunk) {
|
||||
if (!chunk) return;
|
||||
|
||||
if (chunk->type == PXL8_WORLD_CHUNK_VXL && chunk->voxels) {
|
||||
pxl8_vxl_chunk_destroy(chunk->voxels);
|
||||
} else if (chunk->type == PXL8_WORLD_CHUNK_BSP && chunk->bsp) {
|
||||
pxl8_bsp_destroy(chunk->bsp);
|
||||
}
|
||||
|
||||
if (chunk->bsp) pxl8_bsp_destroy(chunk->bsp);
|
||||
pxl8_free(chunk);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,29 +2,17 @@
|
|||
|
||||
#include "pxl8_bsp.h"
|
||||
#include "pxl8_types.h"
|
||||
#include "pxl8_vxl.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum pxl8_world_chunk_type {
|
||||
PXL8_WORLD_CHUNK_VXL,
|
||||
PXL8_WORLD_CHUNK_BSP
|
||||
} pxl8_world_chunk_type;
|
||||
|
||||
typedef struct pxl8_world_chunk {
|
||||
pxl8_world_chunk_type type;
|
||||
u32 id;
|
||||
u32 version;
|
||||
i32 cx, cy, cz;
|
||||
union {
|
||||
pxl8_bsp* bsp;
|
||||
pxl8_vxl_chunk* voxels;
|
||||
};
|
||||
pxl8_bsp* bsp;
|
||||
} pxl8_world_chunk;
|
||||
|
||||
pxl8_world_chunk* pxl8_world_chunk_create_vxl(i32 cx, i32 cy, i32 cz);
|
||||
pxl8_world_chunk* pxl8_world_chunk_create_bsp(u32 id);
|
||||
void pxl8_world_chunk_destroy(pxl8_world_chunk* chunk);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,24 +7,10 @@
|
|||
#include "pxl8_log.h"
|
||||
#include "pxl8_mem.h"
|
||||
|
||||
#define PXL8_VXL_CHUNK_VOLUME (PXL8_VXL_CHUNK_SIZE * PXL8_VXL_CHUNK_SIZE * PXL8_VXL_CHUNK_SIZE)
|
||||
|
||||
static pxl8_world_chunk_cache_entry* find_entry_vxl(pxl8_world_chunk_cache* cache, i32 cx, i32 cy, i32 cz) {
|
||||
for (u32 i = 0; i < cache->entry_count; i++) {
|
||||
pxl8_world_chunk_cache_entry* e = &cache->entries[i];
|
||||
if (e->valid && e->chunk && e->chunk->type == PXL8_WORLD_CHUNK_VXL &&
|
||||
e->chunk->cx == cx && e->chunk->cy == cy && e->chunk->cz == cz) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static pxl8_world_chunk_cache_entry* find_entry_bsp(pxl8_world_chunk_cache* cache, u32 id) {
|
||||
for (u32 i = 0; i < cache->entry_count; i++) {
|
||||
pxl8_world_chunk_cache_entry* e = &cache->entries[i];
|
||||
if (e->valid && e->chunk && e->chunk->type == PXL8_WORLD_CHUNK_BSP &&
|
||||
e->chunk->id == id) {
|
||||
if (e->valid && e->chunk && e->chunk->id == id) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
|
@ -56,14 +42,14 @@ static pxl8_world_chunk_cache_entry* alloc_entry(pxl8_world_chunk_cache* cache)
|
|||
}
|
||||
|
||||
pxl8_world_chunk_cache_entry* e = &cache->entries[slot];
|
||||
if (e->chunk) pxl8_world_chunk_destroy(e->chunk);
|
||||
if (e->mesh) pxl8_mesh_destroy(e->mesh);
|
||||
if (e->chunk) {
|
||||
pxl8_world_chunk_destroy(e->chunk);
|
||||
}
|
||||
memset(e, 0, sizeof(*e));
|
||||
return e;
|
||||
}
|
||||
|
||||
static void assembly_reset(pxl8_world_chunk_assembly* a) {
|
||||
a->type = PXL8_WORLD_CHUNK_VXL;
|
||||
a->id = 0;
|
||||
a->cx = 0;
|
||||
a->cy = 0;
|
||||
|
|
@ -78,7 +64,6 @@ static void assembly_reset(pxl8_world_chunk_assembly* a) {
|
|||
|
||||
static void assembly_init(pxl8_world_chunk_assembly* a, const pxl8_chunk_msg_header* hdr) {
|
||||
assembly_reset(a);
|
||||
a->type = hdr->chunk_type == PXL8_CHUNK_TYPE_BSP ? PXL8_WORLD_CHUNK_BSP : PXL8_WORLD_CHUNK_VXL;
|
||||
a->id = hdr->id;
|
||||
a->cx = hdr->cx;
|
||||
a->cy = hdr->cy;
|
||||
|
|
@ -94,33 +79,6 @@ static void assembly_init(pxl8_world_chunk_assembly* a, const pxl8_chunk_msg_hea
|
|||
}
|
||||
}
|
||||
|
||||
static bool rle_decode_voxel(const u8* src, usize src_len, pxl8_vxl_chunk* chunk) {
|
||||
u8* linear = pxl8_malloc(PXL8_VXL_CHUNK_VOLUME);
|
||||
if (!linear) return false;
|
||||
|
||||
usize src_pos = 0;
|
||||
usize dst_pos = 0;
|
||||
|
||||
while (src_pos + 1 < src_len && dst_pos < PXL8_VXL_CHUNK_VOLUME) {
|
||||
u8 block = src[src_pos++];
|
||||
u8 run_minus_one = src[src_pos++];
|
||||
usize run = (usize)run_minus_one + 1;
|
||||
|
||||
for (usize i = 0; i < run && dst_pos < PXL8_VXL_CHUNK_VOLUME; i++) {
|
||||
linear[dst_pos++] = block;
|
||||
}
|
||||
}
|
||||
|
||||
if (dst_pos != PXL8_VXL_CHUNK_VOLUME) {
|
||||
pxl8_free(linear);
|
||||
return false;
|
||||
}
|
||||
|
||||
pxl8_vxl_chunk_from_linear(chunk, linear);
|
||||
pxl8_free(linear);
|
||||
return true;
|
||||
}
|
||||
|
||||
static pxl8_result deserialize_vertex(pxl8_stream* s, pxl8_bsp_vertex* v) {
|
||||
v->position.x = pxl8_read_f32_be(s);
|
||||
v->position.y = pxl8_read_f32_be(s);
|
||||
|
|
@ -321,34 +279,6 @@ static pxl8_bsp* assembly_to_bsp(pxl8_world_chunk_assembly* a) {
|
|||
return bsp;
|
||||
}
|
||||
|
||||
static pxl8_result assemble_vxl(pxl8_world_chunk_cache* cache, pxl8_world_chunk_assembly* a) {
|
||||
pxl8_world_chunk_cache_entry* entry = find_entry_vxl(cache, a->cx, a->cy, a->cz);
|
||||
if (!entry) {
|
||||
entry = alloc_entry(cache);
|
||||
entry->chunk = pxl8_world_chunk_create_vxl(a->cx, a->cy, a->cz);
|
||||
entry->valid = true;
|
||||
}
|
||||
|
||||
entry->chunk->version = a->version;
|
||||
entry->mesh_dirty = true;
|
||||
entry->last_used = cache->frame_counter;
|
||||
|
||||
if (entry->mesh) {
|
||||
pxl8_mesh_destroy(entry->mesh);
|
||||
entry->mesh = NULL;
|
||||
}
|
||||
|
||||
pxl8_vxl_block_clear(entry->chunk->voxels);
|
||||
|
||||
if (!rle_decode_voxel(a->data, a->data_size, entry->chunk->voxels)) {
|
||||
pxl8_error("[CLIENT] RLE decode failed for chunk (%d,%d,%d)", a->cx, a->cy, a->cz);
|
||||
return PXL8_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
assembly_reset(a);
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
||||
static pxl8_result assemble_bsp(pxl8_world_chunk_cache* cache, pxl8_world_chunk_assembly* a) {
|
||||
pxl8_debug("[CLIENT] assemble_bsp: id=%u data_size=%zu", a->id, a->data_size);
|
||||
pxl8_bsp* bsp = assembly_to_bsp(a);
|
||||
|
|
@ -391,7 +321,6 @@ void pxl8_world_chunk_cache_destroy(pxl8_world_chunk_cache* cache) {
|
|||
for (u32 i = 0; i < cache->entry_count; i++) {
|
||||
pxl8_world_chunk_cache_entry* e = &cache->entries[i];
|
||||
if (e->chunk) pxl8_world_chunk_destroy(e->chunk);
|
||||
if (e->mesh) pxl8_mesh_destroy(e->mesh);
|
||||
}
|
||||
|
||||
pxl8_free(cache->assembly.data);
|
||||
|
|
@ -406,9 +335,7 @@ pxl8_result pxl8_world_chunk_cache_receive(pxl8_world_chunk_cache* cache,
|
|||
pxl8_world_chunk_assembly* a = &cache->assembly;
|
||||
|
||||
bool new_assembly = !a->active ||
|
||||
(hdr->chunk_type == PXL8_CHUNK_TYPE_BSP && a->id != hdr->id) ||
|
||||
(hdr->chunk_type == PXL8_CHUNK_TYPE_VXL &&
|
||||
(a->cx != hdr->cx || a->cy != hdr->cy || a->cz != hdr->cz)) ||
|
||||
a->id != hdr->id ||
|
||||
a->version != hdr->version;
|
||||
|
||||
if (new_assembly) {
|
||||
|
|
@ -436,26 +363,12 @@ pxl8_result pxl8_world_chunk_cache_receive(pxl8_world_chunk_cache* cache,
|
|||
|
||||
if (hdr->flags & PXL8_CHUNK_FLAG_FINAL) {
|
||||
a->complete = true;
|
||||
if (a->type == PXL8_WORLD_CHUNK_BSP) {
|
||||
return assemble_bsp(cache, a);
|
||||
} else {
|
||||
return assemble_vxl(cache, a);
|
||||
}
|
||||
return assemble_bsp(cache, a);
|
||||
}
|
||||
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
||||
pxl8_world_chunk* pxl8_world_chunk_cache_get_vxl(pxl8_world_chunk_cache* cache, i32 cx, i32 cy, i32 cz) {
|
||||
if (!cache) return NULL;
|
||||
pxl8_world_chunk_cache_entry* e = find_entry_vxl(cache, cx, cy, cz);
|
||||
if (e) {
|
||||
e->last_used = cache->frame_counter;
|
||||
return e->chunk;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pxl8_world_chunk* pxl8_world_chunk_cache_get_bsp(pxl8_world_chunk_cache* cache, u32 id) {
|
||||
if (!cache) return NULL;
|
||||
pxl8_world_chunk_cache_entry* e = find_entry_bsp(cache, id);
|
||||
|
|
@ -466,84 +379,7 @@ pxl8_world_chunk* pxl8_world_chunk_cache_get_bsp(pxl8_world_chunk_cache* cache,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
pxl8_mesh* pxl8_world_chunk_cache_get_mesh(pxl8_world_chunk_cache* cache,
|
||||
i32 cx, i32 cy, i32 cz,
|
||||
const pxl8_vxl_block_registry* registry,
|
||||
const pxl8_vxl_mesh_config* config) {
|
||||
if (!cache || !registry) return NULL;
|
||||
|
||||
pxl8_world_chunk_cache_entry* entry = find_entry_vxl(cache, cx, cy, cz);
|
||||
if (!entry || !entry->chunk || !entry->chunk->voxels) return NULL;
|
||||
|
||||
pxl8_world_chunk* nx = pxl8_world_chunk_cache_get_vxl(cache, cx - 1, cy, cz);
|
||||
pxl8_world_chunk* px = pxl8_world_chunk_cache_get_vxl(cache, cx + 1, cy, cz);
|
||||
pxl8_world_chunk* ny = pxl8_world_chunk_cache_get_vxl(cache, cx, cy - 1, cz);
|
||||
pxl8_world_chunk* py = pxl8_world_chunk_cache_get_vxl(cache, cx, cy + 1, cz);
|
||||
pxl8_world_chunk* nz = pxl8_world_chunk_cache_get_vxl(cache, cx, cy, cz - 1);
|
||||
pxl8_world_chunk* pz = pxl8_world_chunk_cache_get_vxl(cache, cx, cy, cz + 1);
|
||||
|
||||
bool all_neighbors = nx && px && ny && py && nz && pz;
|
||||
|
||||
if (entry->mesh && !entry->mesh_dirty) {
|
||||
if (entry->has_all_neighbors == all_neighbors) {
|
||||
return entry->mesh;
|
||||
}
|
||||
}
|
||||
|
||||
if (entry->mesh) {
|
||||
pxl8_mesh_destroy(entry->mesh);
|
||||
entry->mesh = NULL;
|
||||
}
|
||||
|
||||
const pxl8_vxl_chunk* neighbors[6] = {
|
||||
nx ? nx->voxels : NULL,
|
||||
px ? px->voxels : NULL,
|
||||
ny ? ny->voxels : NULL,
|
||||
py ? py->voxels : NULL,
|
||||
nz ? nz->voxels : NULL,
|
||||
pz ? pz->voxels : NULL
|
||||
};
|
||||
|
||||
pxl8_vxl_mesh_config local_config = config ? *config : PXL8_VXL_MESH_CONFIG_DEFAULT;
|
||||
|
||||
entry->mesh = pxl8_vxl_build_mesh(entry->chunk->voxels, neighbors, registry, &local_config);
|
||||
entry->mesh_dirty = false;
|
||||
entry->has_all_neighbors = all_neighbors;
|
||||
|
||||
return entry->mesh;
|
||||
}
|
||||
|
||||
void pxl8_world_chunk_cache_tick(pxl8_world_chunk_cache* cache) {
|
||||
if (!cache) return;
|
||||
cache->frame_counter++;
|
||||
}
|
||||
|
||||
void pxl8_world_chunk_cache_evict_distant(pxl8_world_chunk_cache* cache,
|
||||
i32 cx, i32 cy, i32 cz, i32 radius) {
|
||||
if (!cache) return;
|
||||
|
||||
for (u32 i = 0; i < cache->entry_count; i++) {
|
||||
pxl8_world_chunk_cache_entry* e = &cache->entries[i];
|
||||
if (!e->valid || !e->chunk || e->chunk->type != PXL8_WORLD_CHUNK_VXL) continue;
|
||||
|
||||
i32 dx = e->chunk->cx - cx;
|
||||
i32 dy = e->chunk->cy - cy;
|
||||
i32 dz = e->chunk->cz - cz;
|
||||
|
||||
if (abs(dx) > radius || abs(dy) > radius || abs(dz) > radius) {
|
||||
pxl8_world_chunk_destroy(e->chunk);
|
||||
if (e->mesh) pxl8_mesh_destroy(e->mesh);
|
||||
e->chunk = NULL;
|
||||
e->mesh = NULL;
|
||||
e->valid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_world_chunk_cache_invalidate_meshes(pxl8_world_chunk_cache* cache) {
|
||||
if (!cache) return;
|
||||
|
||||
for (u32 i = 0; i < cache->entry_count; i++) {
|
||||
cache->entries[i].mesh_dirty = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
#include "pxl8_mesh.h"
|
||||
#include "pxl8_protocol.h"
|
||||
#include "pxl8_types.h"
|
||||
#include "pxl8_vxl_render.h"
|
||||
#include "pxl8_world_chunk.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
@ -16,15 +15,11 @@ extern "C" {
|
|||
|
||||
typedef struct pxl8_world_chunk_cache_entry {
|
||||
pxl8_world_chunk* chunk;
|
||||
pxl8_mesh* mesh;
|
||||
u64 last_used;
|
||||
bool mesh_dirty;
|
||||
bool valid;
|
||||
bool has_all_neighbors;
|
||||
} pxl8_world_chunk_cache_entry;
|
||||
|
||||
typedef struct pxl8_world_chunk_assembly {
|
||||
pxl8_world_chunk_type type;
|
||||
u32 id;
|
||||
i32 cx, cy, cz;
|
||||
u32 version;
|
||||
|
|
@ -51,18 +46,9 @@ pxl8_result pxl8_world_chunk_cache_receive(pxl8_world_chunk_cache* cache,
|
|||
const pxl8_chunk_msg_header* hdr,
|
||||
const u8* payload, usize len);
|
||||
|
||||
pxl8_world_chunk* pxl8_world_chunk_cache_get_vxl(pxl8_world_chunk_cache* cache, i32 cx, i32 cy, i32 cz);
|
||||
pxl8_world_chunk* pxl8_world_chunk_cache_get_bsp(pxl8_world_chunk_cache* cache, u32 id);
|
||||
|
||||
pxl8_mesh* pxl8_world_chunk_cache_get_mesh(pxl8_world_chunk_cache* cache,
|
||||
i32 cx, i32 cy, i32 cz,
|
||||
const pxl8_vxl_block_registry* registry,
|
||||
const pxl8_vxl_mesh_config* config);
|
||||
|
||||
void pxl8_world_chunk_cache_tick(pxl8_world_chunk_cache* cache);
|
||||
void pxl8_world_chunk_cache_evict_distant(pxl8_world_chunk_cache* cache,
|
||||
i32 cx, i32 cy, i32 cz, i32 radius);
|
||||
void pxl8_world_chunk_cache_invalidate_meshes(pxl8_world_chunk_cache* cache);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue