better lighting

This commit is contained in:
asrael 2026-01-31 09:31:17 -06:00
parent 805a2713a3
commit 6ed4e17065
75 changed files with 6417 additions and 3667 deletions

180
demo/mod/entities.fnl Normal file
View file

@ -0,0 +1,180 @@
(local pxl8 (require :pxl8))
(local DOOR_HEIGHT 96)
(local DOOR_WIDTH 48)
(local DOOR_X 892)
(local DOOR_Z 416)
(local FIREBALL_COLOR 218)
(var door-mesh nil)
(var door-tex nil)
(var fireball-mesh nil)
(fn create-door-mesh []
(let [verts []
indices []
hw (/ DOOR_WIDTH 2)
y0 0
y1 DOOR_HEIGHT
x DOOR_X]
(table.insert verts {:x x :y y0 :z (- DOOR_Z hw) :u 0 :v 1 :nx -1 :ny 0 :nz 0 :color 80 :light 200})
(table.insert verts {:x x :y y0 :z (+ DOOR_Z hw) :u 1 :v 1 :nx -1 :ny 0 :nz 0 :color 80 :light 200})
(table.insert verts {:x x :y y1 :z (+ DOOR_Z hw) :u 1 :v 0 :nx -1 :ny 0 :nz 0 :color 80 :light 200})
(table.insert verts {:x x :y y1 :z (- DOOR_Z hw) :u 0 :v 0 :nx -1 :ny 0 :nz 0 :color 80 :light 200})
(table.insert indices 0)
(table.insert indices 1)
(table.insert indices 2)
(table.insert indices 0)
(table.insert indices 2)
(table.insert indices 3)
(set door-mesh (pxl8.create_mesh verts indices))))
(fn create-fireball-mesh []
(let [verts []
indices []
radius 5
rings 4
segments 6
core-color (+ FIREBALL_COLOR 6)
spike-color (- FIREBALL_COLOR 1)]
(table.insert verts {:x 0 :y radius :z 0 :nx 0 :ny 1 :nz 0 :color core-color :light 255})
(for [ring 1 (- rings 1)]
(let [phi (* (/ ring rings) math.pi)
sin-phi (math.sin phi)
cos-phi (math.cos phi)
y (* radius cos-phi)
ring-radius (* radius sin-phi)]
(for [seg 0 (- segments 1)]
(let [theta (* (/ seg segments) math.pi 2)
x (* ring-radius (math.cos theta))
z (* ring-radius (math.sin theta))
nx (* sin-phi (math.cos theta))
nz (* sin-phi (math.sin theta))]
(table.insert verts {:x x :y y :z z :nx nx :ny cos-phi :nz nz :color core-color :light 255})))))
(let [bottom-idx (length verts)]
(table.insert verts {:x 0 :y (- radius) :z 0 :nx 0 :ny -1 :nz 0 :color core-color :light 255})
(for [seg 0 (- segments 1)]
(let [next-seg (% (+ seg 1) segments)]
(table.insert indices 0)
(table.insert indices (+ 1 next-seg))
(table.insert indices (+ 1 seg))))
(for [ring 0 (- rings 3)]
(for [seg 0 (- segments 1)]
(let [next-seg (% (+ seg 1) segments)
curr-row (+ 1 (* ring segments))
next-row (+ 1 (* (+ ring 1) segments))]
(table.insert indices (+ curr-row seg))
(table.insert indices (+ curr-row next-seg))
(table.insert indices (+ next-row seg))
(table.insert indices (+ curr-row next-seg))
(table.insert indices (+ next-row next-seg))
(table.insert indices (+ next-row seg)))))
(let [last-ring-start (+ 1 (* (- rings 2) segments))]
(for [seg 0 (- segments 1)]
(let [next-seg (% (+ seg 1) segments)]
(table.insert indices bottom-idx)
(table.insert indices (+ last-ring-start seg))
(table.insert indices (+ last-ring-start next-seg))))))
(let [num-spikes 12
spike-len 8
base-size 1.2
golden-ratio (/ (+ 1 (math.sqrt 5)) 2)]
(for [i 0 (- num-spikes 1)]
(let [y (- 1 (* (/ i (- num-spikes 1)) 2))
r-at-y (math.sqrt (- 1 (* y y)))
theta (* math.pi 2 i golden-ratio)
nx (* r-at-y (math.cos theta))
ny y
nz (* r-at-y (math.sin theta))
tx (if (> (math.abs ny) 0.9) 1 0)
ty (if (> (math.abs ny) 0.9) 0 1)
tz 0
px (- (* ty nz) (* tz ny))
py (- (* tz nx) (* tx nz))
pz (- (* tx ny) (* ty nx))
pl (math.sqrt (+ (* px px) (* py py) (* pz pz)))
px (/ px pl) py (/ py pl) pz (/ pz pl)
qx (- (* ny pz) (* nz py))
qy (- (* nz px) (* nx pz))
qz (- (* nx py) (* ny px))
bx (* radius 0.8 nx)
by (* radius 0.8 ny)
bz (* radius 0.8 nz)
sx (* (+ radius spike-len) nx)
sy (* (+ radius spike-len) ny)
sz (* (+ radius spike-len) nz)
base-idx (length verts)]
(table.insert verts {:x (+ bx (* base-size px) (* base-size qx))
:y (+ by (* base-size py) (* base-size qy))
:z (+ bz (* base-size pz) (* base-size qz))
:nx nx :ny ny :nz nz :color core-color :light 255})
(table.insert verts {:x (+ bx (* base-size px) (* (- base-size) qx))
:y (+ by (* base-size py) (* (- base-size) qy))
:z (+ bz (* base-size pz) (* (- base-size) qz))
:nx nx :ny ny :nz nz :color core-color :light 255})
(table.insert verts {:x (+ bx (* (- base-size) px) (* (- base-size) qx))
:y (+ by (* (- base-size) py) (* (- base-size) qy))
:z (+ bz (* (- base-size) pz) (* (- base-size) qz))
:nx nx :ny ny :nz nz :color core-color :light 255})
(table.insert verts {:x (+ bx (* (- base-size) px) (* base-size qx))
:y (+ by (* (- base-size) py) (* base-size qy))
:z (+ bz (* (- base-size) pz) (* base-size qz))
:nx nx :ny ny :nz nz :color core-color :light 255})
(table.insert verts {:x sx :y sy :z sz :nx nx :ny ny :nz nz :color spike-color :light 255})
(table.insert indices base-idx)
(table.insert indices (+ base-idx 1))
(table.insert indices (+ base-idx 4))
(table.insert indices (+ base-idx 1))
(table.insert indices (+ base-idx 2))
(table.insert indices (+ base-idx 4))
(table.insert indices (+ base-idx 2))
(table.insert indices (+ base-idx 3))
(table.insert indices (+ base-idx 4))
(table.insert indices (+ base-idx 3))
(table.insert indices base-idx)
(table.insert indices (+ base-idx 4)))))
(set fireball-mesh (pxl8.create_mesh verts indices))))
(fn get-door-position []
(values DOOR_X DOOR_Z))
(fn get-door-radius []
20)
(fn init [textures]
(when (not door-mesh)
(create-door-mesh))
(when (not fireball-mesh)
(create-fireball-mesh))
(when (and (not door-tex) textures)
(set door-tex (textures.door))))
(fn render-door [wireframe floor-y]
(when (and door-mesh door-tex)
(pxl8.draw_mesh door-mesh {:x 0 :y (or floor-y 0) :z 0
:texture door-tex
:lighting true
:double_sided true
:wireframe wireframe})))
(fn render-fireball [x y z wireframe]
(when fireball-mesh
(pxl8.draw_mesh fireball-mesh {:x x :y y :z z
:passthrough true
:wireframe wireframe
:emissive 1.0})))
{:FIREBALL_COLOR FIREBALL_COLOR
:get-door-position get-door-position
:get-door-radius get-door-radius
:init init
:render-door render-door
:render-fireball render-fireball}

View file

@ -1,8 +1,10 @@
(local pxl8 (require :pxl8))
(local effects (require :pxl8.effects))
(local net (require :pxl8.net))
(local shader (require :pxl8.shader))
(local colormap (require :mod.colormap))
(local entities (require :mod.entities))
(local menu (require :mod.menu))
(local palette (require :mod.palette))
(local sky (require :mod.sky))
@ -11,234 +13,96 @@
(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 -800)
(local gravity 600)
(local grid-size 64)
(local ground-y 64)
(local jump-force 175)
(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 turn-speed 4.0)
(local sim-tick-rate 60)
(local sim-dt (/ 1.0 sim-tick-rate))
(local history-size 128)
(local correction-threshold 1.0)
(var auto-run? false)
(var auto-run-cancel-key nil)
(var auto-run? false)
(var bob-time 0)
(var cam-pitch 0)
(var cam-x 416)
(var cam-y 64)
(var cam-y 0)
(var cam-yaw 0)
(var cam-z 416)
(var camera nil)
(var ceiling-tex nil)
(var fireball-mesh nil)
(var floor-tex nil)
(var fps-avg 0)
(var fps-sample-count 0)
(var grounded? true)
(var grounded true)
(var land-squash 0)
(var last-dt 0.016)
(var light-time 0)
(var lights nil)
(var materials-setup false)
(var bsp-materials-setup false)
(var network nil)
(var portal-cooldown 0)
(var real-time 0)
(var smooth-cam-x 416)
(var smooth-cam-z 416)
(var velocity-y 0)
(var vel-y 0)
(var trim-tex nil)
(var wall-tex nil)
(var world nil)
(local cursor-look? true)
(local FIREBALL_COLOR 218)
(local MOSS_COLOR 200)
(local PLASTER_COLOR 16)
(local STONE_FLOOR_START 37)
(local STONE_WALL_START 2)
(local MOSS_COLOR 200)
(local WOOD_COLOR 88)
(local trail-positions [])
(local TRAIL_LENGTH 8)
(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 create-fireball-mesh []
(let [verts []
indices []
radius 5
rings 4
segments 6
core-color (+ FIREBALL_COLOR 6)
spike-color (+ FIREBALL_COLOR 2)]
(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))))
;; top pole
(table.insert verts {:x 0 :y radius :z 0 :nx 0 :ny 1 :nz 0 :color core-color :light 255})
(fn preload []
(when (not network)
(set network (net.get))
(when network
(network:spawn cam-x cam-y cam-z cam-yaw cam-pitch)))
(when (not world)
(set world (pxl8.get_world))
(when world
(world:init_local_player cam-x cam-y cam-z))))
;; sphere rings
(for [ring 1 (- rings 1)]
(let [phi (* (/ ring rings) math.pi)
sin-phi (math.sin phi)
cos-phi (math.cos phi)
y (* radius cos-phi)
ring-radius (* radius sin-phi)]
(for [seg 0 (- segments 1)]
(let [theta (* (/ seg segments) math.pi 2)
x (* ring-radius (math.cos theta))
z (* ring-radius (math.sin theta))
nx (* sin-phi (math.cos theta))
nz (* sin-phi (math.sin theta))]
(table.insert verts {:x x :y y :z z :nx nx :ny cos-phi :nz nz :color core-color :light 255})))))
(fn is-connected []
(and network (network:connected)))
;; bottom pole
(let [bottom-idx (length verts)]
(table.insert verts {:x 0 :y (- radius) :z 0 :nx 0 :ny -1 :nz 0 :color core-color :light 255})
(fn is-ready []
(if (not world)
false
(let [chunk (world:active_chunk)]
(and chunk (chunk:ready)))))
;; top cap triangles
(for [seg 0 (- segments 1)]
(let [next-seg (% (+ seg 1) segments)]
(table.insert indices 0)
(table.insert indices (+ 1 next-seg))
(table.insert indices (+ 1 seg))))
;; middle quads
(for [ring 0 (- rings 3)]
(for [seg 0 (- segments 1)]
(let [next-seg (% (+ seg 1) segments)
curr-row (+ 1 (* ring segments))
next-row (+ 1 (* (+ ring 1) segments))]
(table.insert indices (+ curr-row seg))
(table.insert indices (+ curr-row next-seg))
(table.insert indices (+ next-row seg))
(table.insert indices (+ curr-row next-seg))
(table.insert indices (+ next-row next-seg))
(table.insert indices (+ next-row seg)))))
;; bottom cap triangles
(let [last-ring-start (+ 1 (* (- rings 2) segments))]
(for [seg 0 (- segments 1)]
(let [next-seg (% (+ seg 1) segments)]
(table.insert indices bottom-idx)
(table.insert indices (+ last-ring-start seg))
(table.insert indices (+ last-ring-start next-seg))))))
;; add spikes - evenly distributed using golden ratio
(let [num-spikes 12
spike-len 8
base-size 1.2
golden-ratio (/ (+ 1 (math.sqrt 5)) 2)]
(for [i 0 (- num-spikes 1)]
(let [;; fibonacci sphere distribution
y (- 1 (* (/ i (- num-spikes 1)) 2))
r-at-y (math.sqrt (- 1 (* y y)))
theta (* math.pi 2 i golden-ratio)
nx (* r-at-y (math.cos theta))
ny y
nz (* r-at-y (math.sin theta))
;; tangent vectors for base
tx (if (> (math.abs ny) 0.9) 1 0)
ty (if (> (math.abs ny) 0.9) 0 1)
tz 0
;; cross product for perpendicular
px (- (* ty nz) (* tz ny))
py (- (* tz nx) (* tx nz))
pz (- (* tx ny) (* ty nx))
pl (math.sqrt (+ (* px px) (* py py) (* pz pz)))
px (/ px pl) py (/ py pl) pz (/ pz pl)
;; second perpendicular
qx (- (* ny pz) (* nz py))
qy (- (* nz px) (* nx pz))
qz (- (* nx py) (* ny px))
;; base center inside sphere
bx (* radius 0.8 nx)
by (* radius 0.8 ny)
bz (* radius 0.8 nz)
;; spike tip
sx (* (+ radius spike-len) nx)
sy (* (+ radius spike-len) ny)
sz (* (+ radius spike-len) nz)
base-idx (length verts)]
;; 4 base vertices forming a square
(table.insert verts {:x (+ bx (* base-size px) (* base-size qx))
:y (+ by (* base-size py) (* base-size qy))
:z (+ bz (* base-size pz) (* base-size qz))
:nx nx :ny ny :nz nz :color core-color :light 255})
(table.insert verts {:x (+ bx (* base-size px) (* (- base-size) qx))
:y (+ by (* base-size py) (* (- base-size) qy))
:z (+ bz (* base-size pz) (* (- base-size) qz))
:nx nx :ny ny :nz nz :color core-color :light 255})
(table.insert verts {:x (+ bx (* (- base-size) px) (* (- base-size) qx))
:y (+ by (* (- base-size) py) (* (- base-size) qy))
:z (+ bz (* (- base-size) pz) (* (- base-size) qz))
:nx nx :ny ny :nz nz :color core-color :light 255})
(table.insert verts {:x (+ bx (* (- base-size) px) (* base-size qx))
:y (+ by (* (- base-size) py) (* base-size qy))
:z (+ bz (* (- base-size) pz) (* base-size qz))
:nx nx :ny ny :nz nz :color core-color :light 255})
;; spike tip
(table.insert verts {:x sx :y sy :z sz :nx nx :ny ny :nz nz :color spike-color :light 255})
;; 4 triangular faces of pyramid
(table.insert indices base-idx)
(table.insert indices (+ base-idx 1))
(table.insert indices (+ base-idx 4))
(table.insert indices (+ base-idx 1))
(table.insert indices (+ base-idx 2))
(table.insert indices (+ base-idx 4))
(table.insert indices (+ base-idx 2))
(table.insert indices (+ base-idx 3))
(table.insert indices (+ base-idx 4))
(table.insert indices (+ base-idx 3))
(table.insert indices base-idx)
(table.insert indices (+ base-idx 4)))))
(set fireball-mesh (pxl8.create_mesh verts indices))))
(var client-tick 0)
(var last-processed-tick 0)
(var time-accumulator 0)
(var position-history {})
(var pending-inputs {})
(fn history-idx [tick]
(+ 1 (% tick history-size)))
(fn store-position [tick x z yaw]
(tset position-history (history-idx tick) {:tick tick :x x :z z :yaw yaw}))
(fn get-position [tick]
(let [entry (. position-history (history-idx tick))]
(when (and entry (= entry.tick tick))
entry)))
(fn store-pending-input [tick input]
(tset pending-inputs (history-idx tick) {:tick tick :input input}))
(fn get-pending-input [tick]
(let [entry (. pending-inputs (history-idx tick))]
(when (and entry (= entry.tick tick))
entry.input)))
(fn apply-movement [x z yaw input]
(var new-x x)
(var new-z z)
(let [move-forward (or input.move_y 0)
move-right (or input.move_x 0)]
(when (or (not= move-forward 0) (not= move-right 0))
(let [forward-x (- (math.sin yaw))
forward-z (- (math.cos yaw))
right-x (math.cos yaw)
right-z (- (math.sin yaw))
len (math.sqrt (+ (* move-forward move-forward) (* move-right move-right)))
norm-forward (/ move-forward len)
norm-right (/ move-right len)
move-delta (* move-speed sim-dt)]
(set new-x (+ new-x (* move-delta (+ (* forward-x norm-forward) (* right-x norm-right)))))
(set new-z (+ new-z (* move-delta (+ (* forward-z norm-forward) (* right-z norm-right))))))))
(values new-x new-z))
(fn init []
(pxl8.set_relative_mouse_mode true)
(pxl8.set_palette palette 256)
@ -248,7 +112,9 @@
r 0xFF
g (math.floor (+ 0x60 (* t (- 0xE0 0x60))))
b (math.floor (+ 0x10 (* t (- 0x80 0x10))))]
(pxl8.set_palette_rgb (+ FIREBALL_COLOR i) r g b)))
(pxl8.set_palette_rgb (+ entities.FIREBALL_COLOR i) r g b)))
(pxl8.set_palette_rgb (- entities.FIREBALL_COLOR 1) 0xFF 0x20 0x10)
(sky.reset-gradient)
(sky.update-gradient 1 2 6 6 10 18)
(pxl8.update_palette_deps)
@ -256,49 +122,38 @@
(set camera (pxl8.create_camera_3d)))
(when (not lights)
(set lights (pxl8.create_lights)))
(when (not fireball-mesh)
(create-fireball-mesh))
(entities.init textures)
(sky.generate-stars 12345)
(when (not network)
(set network (net.get))
(when network
(network:spawn cam-x cam-y cam-z cam-yaw cam-pitch)))
(preload)
(when (not world)
(set world (pxl8.get_world)))
(when (not floor-tex)
(set floor-tex (textures.mossy-cobblestone 44444 STONE_FLOOR_START MOSS_COLOR)))
(when (not wall-tex)
(set wall-tex (textures.ashlar-wall 55555 STONE_WALL_START MOSS_COLOR)))
(when (not ceiling-tex)
(set ceiling-tex (pxl8.create_texture [0] 1 1)))
(set ceiling-tex (textures.plaster-wall 66666 PLASTER_COLOR)))
(when (not floor-tex)
(set floor-tex (textures.wood-planks 44444 WOOD_COLOR)))
(when (not trim-tex)
(set trim-tex (textures.wood-trim 77777 WOOD_COLOR)))
(when (not wall-tex)
(set wall-tex (textures.cobble-timber 55555 STONE_WALL_START MOSS_COLOR WOOD_COLOR)))
)
(fn setup-materials []
(when (and world (not materials-setup))
(when (and world (not bsp-materials-setup) ceiling-tex floor-tex trim-tex wall-tex)
(let [chunk (world:active_chunk)]
(when (and chunk (chunk:ready))
(let [bsp (chunk:bsp)]
(when bsp
(let [floor-mat (pxl8.create_material {:texture floor-tex :lighting true})
wall-mat (pxl8.create_material {:texture wall-tex :lighting true})
ceiling-mat (pxl8.create_material {:texture ceiling-tex})]
(let [ceiling-mat (pxl8.create_material {:texture ceiling-tex})
floor-mat (pxl8.create_material {:texture floor-tex :lighting true})
trim-mat (pxl8.create_material {:texture trim-tex :lighting true})
wall-mat (pxl8.create_material {:texture wall-tex :lighting true})]
(bsp:set_material 0 floor-mat)
(bsp:set_material 1 wall-mat)
(bsp:set_material 2 ceiling-mat)
(for [i 0 (- (bsp:face_count) 1)]
(let [n (bsp:face_normal i)]
(bsp:face_set_material i
(if (> n.y 0.7) 0
(< n.y -0.7) 2
1))))
(set materials-setup true))))))))
(world:set_bsp_material 0 floor-mat)
(world:set_bsp_material 1 wall-mat)
(world:set_bsp_material 2 ceiling-mat)
(world:set_bsp_material 3 trim-mat)
(set bsp-materials-setup true))))))
(fn sample-input []
(var move-forward 0)
@ -329,26 +184,6 @@
:look_dx (pxl8.mouse_dx)
:look_dy (pxl8.mouse_dy)})
(fn reconcile [server-tick server-x server-z]
(let [predicted (get-position server-tick)]
(when predicted
(let [dx (- predicted.x server-x)
dz (- predicted.z server-z)
error (math.sqrt (+ (* dx dx) (* dz dz)))]
(when (> error correction-threshold)
(set cam-x server-x)
(set cam-z server-z)
(for [t (+ server-tick 1) client-tick]
(let [input (get-pending-input t)
hist (get-position t)]
(when (and input hist)
(let [(new-x new-z) (apply-movement cam-x cam-z hist.yaw input)
(resolved-x _ resolved-z) (world:resolve_collision cam-x cam-y cam-z new-x cam-y new-z 5)]
(set cam-x resolved-x)
(set cam-z resolved-z)
(store-position t cam-x cam-z hist.yaw))))))))))
(fn update [dt]
(set last-dt dt)
(let [fps (pxl8.get_fps)]
@ -361,87 +196,111 @@
(setup-materials)
(let [chunk (world:active_chunk)]
(when (and chunk (chunk:ready))
(when (> portal-cooldown 0)
(set portal-cooldown (- portal-cooldown dt)))
(when (and network (<= portal-cooldown 0))
(let [chunk (world:active_chunk)
in-bsp (not= chunk nil)
(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 200]
(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 [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 ready
(let [input (sample-input)
grid-max (* grid-size chunk-size)
grid-max (if voxel-space 100000 (* grid-size chunk-size))
movement-yaw cam-yaw]
(set time-accumulator (+ time-accumulator dt))
(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)))
(while (>= time-accumulator sim-dt)
(set time-accumulator (- time-accumulator sim-dt))
(set client-tick (+ client-tick 1))
(store-pending-input client-tick input)
(let [(new-x new-z) (apply-movement cam-x cam-z movement-yaw input)]
(when (and (>= new-x 0) (<= new-x grid-max)
(>= new-z 0) (<= new-z grid-max))
(let [(resolved-x _ resolved-z) (world:resolve_collision cam-x cam-y cam-z new-x cam-y new-z 5)]
(set cam-x resolved-x)
(set cam-z resolved-z)))
(store-position client-tick cam-x cam-z movement-yaw)))
(when cursor-look?
(set cam-yaw (- cam-yaw (* input.look_dx cursor-sensitivity)))
(set cam-pitch (math.max (- max-pitch)
(math.min max-pitch
(- cam-pitch (* input.look_dy cursor-sensitivity))))))
(when (and (not cursor-look?) (pxl8.key_down "up"))
(set cam-pitch (math.min max-pitch (+ cam-pitch (* turn-speed dt)))))
(when (and (not cursor-look?) (pxl8.key_down "down"))
(set cam-pitch (math.max (- max-pitch) (- cam-pitch (* turn-speed dt)))))
(when (and (not cursor-look?) (pxl8.key_down "left"))
(set cam-yaw (- cam-yaw (* turn-speed dt))))
(when (and (not cursor-look?) (pxl8.key_down "right"))
(set cam-yaw (+ cam-yaw (* turn-speed dt))))
(when network
(let [(ok err) (pcall (fn []
(network:send_input {:move_x input.move_x
:move_y input.move_y
:look_dx input.look_dx
:look_dy input.look_dy
:yaw movement-yaw
:tick client-tick})
(let [snapshot (network:snapshot)]
(when (and snapshot (> snapshot.tick last-processed-tick))
(set last-processed-tick snapshot.tick)
(let [player-id (network:player_id)]
(when (> player-id 0)
(let [curr (network:entity_userdata player-id)]
(when curr
(let [srv-x (pxl8.unpack_f32_be curr 0)
srv-z (pxl8.unpack_f32_be curr 8)]
(reconcile snapshot.tick srv-x srv-z))))))))))]
(when (not ok)
(pxl8.error (.. "Network error: " err)))))
(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)))))
(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 velocity-y jump-force)
(set grounded? false))
(when (and (pxl8.key_pressed "space") grounded)
(set vel-y jump-velocity)
(set grounded false))
(set velocity-y (+ velocity-y (* gravity dt)))
(set cam-y (+ cam-y (* velocity-y dt)))
(when (or (not grounded) (not= vel-y 0))
(set vel-y (- vel-y (* gravity dt)))
(when (<= cam-y ground-y)
(when (not grounded?)
(set land-squash land-squash-amount))
(set cam-y ground-y)
(set velocity-y 0)
(set grounded? true))
(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?)
(if (and moving 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))))))
@ -458,13 +317,17 @@
(when (not world)
(pxl8.error "world is nil!"))
(let [chunk (when world (world:active_chunk))]
(when (and world (or (not chunk) (not (chunk:ready))))
(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)))]
(when (and world (not ready))
(pxl8.text "Waiting for world data..." 5 30 12))
(when (and camera world chunk (chunk:ready))
(when (and camera world ready)
(let [bob-offset (* (math.sin bob-time) bob-amount)
eye-y (+ cam-y bob-offset land-squash)
eye-y (+ cam-y player-eye-height bob-offset land-squash)
forward-x (- (math.sin cam-yaw))
forward-z (- (math.cos cam-yaw))
target-x (+ smooth-cam-x forward-x)
@ -490,7 +353,7 @@
r2 (* 0.04 (math.sin (+ (* real-time 3.2) phase)))
light-radius (* 150 (+ 0.95 r1 r2))]
(lights:clear)
(lights:add light-x light-y light-z 255 200 150 light-intensity light-radius)
(lights:add light-x light-y light-z 0xFF8C32 light-intensity light-radius)
(pxl8.begin_frame_3d camera lights {
:ambient 30
:fog_density 0.0
@ -502,21 +365,22 @@
(sky.render smooth-cam-x eye-y smooth-cam-z (menu.is-wireframe))
(pxl8.clear_depth)
(when chunk
(let [bsp (chunk:bsp)]
(when bsp
(bsp:set_wireframe (menu.is-wireframe)))))
(world:set_wireframe (menu.is-wireframe))
(world:render [smooth-cam-x eye-y smooth-cam-z])
(when fireball-mesh
(let [wire (menu.is-wireframe)]
(pxl8.draw_mesh fireball-mesh {:x light-x :y light-y :z light-z
:passthrough true
:wireframe wire
:emissive 1.0})))
(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))
(pxl8.end_frame_3d))
;; TODO: shader needs to run at present time, not mid-frame
;; (shader.begin_frame)
;; (shader.run shader.presets.light_with_fog
;; {:fog_r 6 :fog_g 6 :fog_b 12
;; :fog_start 0.1 :fog_end 0.85})
(sky.render-stars smooth-cam-x eye-y smooth-cam-z 1.0 last-dt)
(let [cx (/ (pxl8.get_width) 2)
@ -532,6 +396,9 @@
(string.format "%.0f" cam-y) ","
(string.format "%.0f" cam-z)) 5 15 text-color))))))
{:init init
{:preload preload
:is-connected is-connected
:is-ready is-ready
:init init
:update update
:frame frame}

View file

@ -1,15 +1,28 @@
(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 wireframe false)
(var gui nil)
(var render-distance 3)
(var sim-distance 4)
(var current-panel :main)
(var selected-item nil)
(var current-items [])
(var pending-action nil)
(fn init []
(set gui (pxl8.create_gui)))
(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)))))
(fn show []
(set paused true)
(set current-panel :main)
(pxl8.set_relative_mouse_mode false)
(pxl8.center_cursor))
@ -23,7 +36,35 @@
(hide)
(show)))
(fn select-next []
(when (> (length current-items) 0)
(var found-idx nil)
(for [i 1 (length current-items)]
(when (= (. current-items i) selected-item)
(set found-idx i)))
(if found-idx
(let [next-idx (+ found-idx 1)]
(if (<= next-idx (length current-items))
(set selected-item (. current-items next-idx))
(set selected-item (. current-items 1))))
(set selected-item (. current-items 1)))))
(fn select-prev []
(when (> (length current-items) 0)
(var found-idx nil)
(for [i 1 (length current-items)]
(when (= (. current-items i) selected-item)
(set found-idx i)))
(if found-idx
(let [prev-idx (- found-idx 1)]
(if (>= prev-idx 1)
(set selected-item (. current-items prev-idx))
(set selected-item (. current-items (length current-items)))))
(set selected-item (. current-items (length current-items))))))
(fn update []
(set pending-action nil)
(when gui
(let [(mx my) (pxl8.get_mouse_pos)]
(gui:cursor_move mx my))
@ -32,29 +73,102 @@
(gui:cursor_down))
(when (pxl8.mouse_released 1)
(gui:cursor_up))))
(gui:cursor_up))
(when (or (pxl8.key_pressed "down")
(and (pxl8.key_pressed "tab") (not (pxl8.key_down "lshift")) (not (pxl8.key_down "rshift"))))
(select-next))
(when (or (pxl8.key_pressed "up")
(and (pxl8.key_pressed "tab") (or (pxl8.key_down "lshift") (pxl8.key_down "rshift"))))
(select-prev))
(when (or (pxl8.key_pressed "return") (pxl8.key_pressed "space"))
(when selected-item
(set pending-action selected-item)))))
(fn menu-button [id x y w h label]
(table.insert current-items label)
(when (= selected-item nil)
(set selected-item label))
(let [is-selected (= selected-item label)]
(when is-selected
(pxl8.rect (- x 3) (- y 3) (+ w 6) (+ h 6) 15))
(let [clicked (gui:button id x y w h label)]
(when clicked
(set selected-item label))
(or clicked (and is-selected (= pending-action label))))))
(fn draw-main-menu []
(pxl8.gui_window 200 80 240 235 "pxl8 demo")
(when (menu-button 1 215 127 210 30 "Resume")
(hide))
(let [wire-label (if wireframe "Wireframe: On" "Wireframe: Off")]
(when (menu-button 4 215 162 210 30 wire-label)
(set wireframe (not wireframe))))
(when (menu-button 5 215 197 210 30 "GFX")
(set current-panel :gfx)
(set selected-item nil))
(when (menu-button 6 215 232 210 30 "SFX")
(set current-panel :sfx)
(set selected-item nil))
(when (menu-button 2 215 267 210 30 "Quit")
(pxl8.quit)))
(fn draw-sfx-panel []
(pxl8.gui_window 200 100 240 145 "SFX")
(let [music-label (if (music.is-playing) "Music: On" "Music: Off")]
(when (menu-button 10 215 147 210 30 music-label)
(if (music.is-playing)
(music.stop)
(music.start))))
(pxl8.gui_label 215 185 "Volume/Devices: TODO" 15)
(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 100 240 180 "GFX")
(pxl8.gui_label 215 150 (.. "Render: " render-distance) 15)
(let [(changed new-val) (gui:slider_int 30 215 165 210 16 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)))))
(pxl8.gui_label 215 190 (.. "Sim: " sim-distance) 15)
(let [(changed new-val) (gui:slider_int 31 215 205 210 16 sim-distance 1 8)]
(when changed
(set sim-distance new-val)
(let [w (world-mod.World.get)
n (net-mod.get)]
(when w (w:set_sim_distance new-val))
(when n (n:set_chunk_settings render-distance new-val)))))
(when (menu-button 32 215 240 210 30 "Back")
(set current-panel :main)
(set selected-item nil)))
(fn draw []
(set current-items [])
(when gui
(gui:begin_frame)
(pxl8.gui_window 200 100 240 200 "pxl8 demo")
(when (gui:button 1 215 147 210 30 "Resume")
(hide))
(let [music-label (if (music.is-playing) "Music: On" "Music: Off")]
(when (gui:button 3 215 182 210 30 music-label)
(if (music.is-playing)
(music.stop)
(music.start))))
(let [wire-label (if wireframe "Wireframe: On" "Wireframe: Off")]
(when (gui:button 4 215 217 210 30 wire-label)
(set wireframe (not wireframe))))
(when (gui:button 2 215 252 210 30 "Quit")
(pxl8.quit))
(case current-panel
:main (draw-main-menu)
:sfx (draw-sfx-panel)
:gfx (draw-gfx-panel))
(if (gui:is_hovering)
(pxl8.set_cursor :hand)
@ -62,7 +176,8 @@
(gui:end_frame)))
{:is-paused (fn [] paused)
{:init init
:is-paused (fn [] paused)
:is-wireframe (fn [] wireframe)
:toggle toggle
:show show

View file

@ -96,6 +96,9 @@
(set sky-mesh (pxl8.create_mesh verts indices))))
(fn reset-gradient []
(set last-gradient-key nil))
(fn update-gradient [zenith-r zenith-g zenith-b horizon-r horizon-g horizon-b]
(let [key (.. zenith-r "," zenith-g "," zenith-b "," horizon-r "," horizon-g "," horizon-b)]
(when (not= key last-gradient-key)
@ -249,6 +252,7 @@
{:render render
:render-stars render-stars
:generate-stars generate-stars
:reset-gradient reset-gradient
:update-gradient update-gradient
:SKY_GRADIENT_START SKY_GRADIENT_START
:SKY_GRADIENT_COUNT SKY_GRADIENT_COUNT}

View file

@ -59,6 +59,10 @@
(let [s (const ctx scale)]
(ctx.graph:add_node procgen.OP_NOISE_VALUE ctx.x ctx.y s 0 0)))
(fn noise-value-at [ctx x y scale]
(let [s (const ctx scale)]
(ctx.graph:add_node procgen.OP_NOISE_VALUE x y s 0 0)))
(fn noise-perlin [ctx scale]
(let [s (const ctx scale)]
(ctx.graph:add_node procgen.OP_NOISE_PERLIN ctx.x ctx.y s 0 0)))
@ -68,6 +72,11 @@
p (const ctx persistence)]
(ctx.graph:add_node procgen.OP_NOISE_FBM ctx.x ctx.y s p octaves)))
(fn noise-fbm-at [ctx x y octaves scale persistence]
(let [s (const ctx scale)
p (const ctx persistence)]
(ctx.graph:add_node procgen.OP_NOISE_FBM x y s p octaves)))
(fn noise-ridged [ctx octaves scale persistence]
(let [s (const ctx scale)
p (const ctx persistence)]
@ -165,4 +174,157 @@
(g:destroy)
tex-id)))
(fn textures.wood-planks [seed base-color]
(let [g (build-graph seed
(fn [ctx]
(let [tex-size 64
plank-count 2
pixel-x (floor ctx (mul ctx ctx.x (const ctx tex-size)))
plank-x (div ctx pixel-x (const ctx (/ tex-size plank-count)))
plank-fract (fract ctx plank-x)
edge-dist (min-op ctx plank-fract (sub ctx (const ctx 1.0) plank-fract))
gap-threshold (const ctx 0.04)
is-gap (sub ctx gap-threshold edge-dist)
plank-tint (mul ctx (noise-value ctx 6) (const ctx 0.25))
grain-x (mul ctx ctx.x (const ctx 12))
grain-y (mul ctx ctx.y (const ctx 2))
grain-base (noise-fbm-at ctx grain-x grain-y 3 4 0.6)
grain-fine-x (mul ctx ctx.x (const ctx 48))
grain-fine-y (mul ctx ctx.y (const ctx 6))
grain-fine (noise-value-at ctx grain-fine-x grain-fine-y 1)
grain (add ctx (mul ctx grain-base (const ctx 0.6))
(mul ctx grain-fine (const ctx 0.4)))
wood-val (add ctx grain plank-tint)
wood-quant (quantize ctx wood-val base-color 6)
gap-shade (sub ctx wood-val (const ctx 0.15))
gap-quant (quantize ctx gap-shade base-color 6)]
(select ctx is-gap gap-quant wood-quant))))]
(let [tex-id (g:eval_texture 64 64)]
(g:destroy)
tex-id)))
(fn textures.diagonal-planks [seed base-color]
(let [g (build-graph seed
(fn [ctx]
(let [tex-size 64
plank-count 4
diag (add ctx ctx.x ctx.y)
pixel-diag (floor ctx (mul ctx diag (const ctx tex-size)))
plank-diag (div ctx pixel-diag (const ctx (/ tex-size plank-count)))
plank-fract (fract ctx plank-diag)
edge-dist (min-op ctx plank-fract (sub ctx (const ctx 1.0) plank-fract))
gap-threshold (const ctx 0.06)
is-gap (sub ctx gap-threshold edge-dist)
plank-id (floor ctx plank-diag)
plank-tint (mul ctx (noise-value-at ctx plank-id (const ctx 0) 8) (const ctx 0.2))
grain-diag (mul ctx diag (const ctx 8))
grain-perp (sub ctx ctx.x ctx.y)
grain-base (noise-fbm-at ctx grain-diag grain-perp 3 6 0.5)
grain-fine (noise-value-at ctx (mul ctx grain-diag (const ctx 4)) grain-perp 1)
grain (add ctx (mul ctx grain-base (const ctx 0.5))
(mul ctx grain-fine (const ctx 0.3)))
wood-val (add ctx grain plank-tint)
wood-quant (quantize ctx wood-val base-color 5)
gap-shade (sub ctx wood-val (const ctx 0.2))
gap-quant (quantize ctx gap-shade base-color 5)]
(select ctx is-gap gap-quant wood-quant))))]
(let [tex-id (g:eval_texture 64 64)]
(g:destroy)
tex-id)))
(fn textures.cobble-timber [seed stone-color moss-color wood-color]
(let [g (build-graph seed
(fn [ctx]
(let [warp-x (noise-fbm ctx 2 3 0.5)
warp-y (noise-value ctx 4)
warped-x (add ctx ctx.x (mul ctx warp-x (const ctx 0.15)))
warped-y (add ctx ctx.y (mul ctx warp-y (const ctx 0.15)))
cell (ctx.graph:add_node procgen.OP_VORONOI_CELL warped-x warped-y (const ctx 5) 0 0)
edge (ctx.graph:add_node procgen.OP_VORONOI_EDGE warped-x warped-y (const ctx 5) 0 0)
mortar-threshold (const ctx 0.08)
is-mortar (sub ctx mortar-threshold edge)
mortar-color (const ctx 79)
stone-detail (noise-value ctx 48)
stone-base (mul ctx cell (const ctx 0.6))
stone-combined (add ctx stone-base (mul ctx stone-detail (const ctx 0.4)))
stone-quant (quantize ctx stone-combined stone-color 8)
moss-pattern (noise-fbm ctx 4 10 0.5)
moss-detail (noise-value ctx 64)
moss-var (add ctx (mul ctx moss-pattern (const ctx 0.7))
(mul ctx moss-detail (const ctx 0.3)))
moss-threshold (const ctx 0.55)
has-moss (sub ctx moss-pattern moss-threshold)
moss-quant (quantize ctx moss-var moss-color 6)
stone-or-moss (select ctx has-moss moss-quant stone-quant)]
(select ctx is-mortar mortar-color stone-or-moss))))]
(let [tex-id (g:eval_texture 64 64)]
(g:destroy)
tex-id)))
(fn textures.plaster-wall [seed base-color]
(let [g (build-graph seed
(fn [ctx]
(let [plaster-base (noise-fbm ctx 3 24 0.4)
plaster-detail (noise-value ctx 64)
plaster-rough (noise-turbulence ctx 2 32 0.5)
combined (add ctx (mul ctx plaster-base (const ctx 0.5))
(add ctx (mul ctx plaster-detail (const ctx 0.3))
(mul ctx plaster-rough (const ctx 0.2))))
crack-noise (noise-ridged ctx 2 8 0.6)
crack-threshold (const ctx 0.75)
has-crack (sub ctx crack-noise crack-threshold)
crack-color (const ctx (- base-color 2))
plaster-quant (quantize ctx combined base-color 4)]
(select ctx has-crack crack-color plaster-quant))))]
(let [tex-id (g:eval_texture 64 64)]
(g:destroy)
tex-id)))
(fn textures.timber-frame [seed wood-color plaster-color]
(let [g (build-graph seed
(fn [ctx]
(let [h-beam-count 2
v-beam-count 1.5
beam-half-width 0.08
scaled-x (mul ctx ctx.x (const ctx h-beam-count))
scaled-y (mul ctx ctx.y (const ctx v-beam-count))
dist-to-h-beam (abs ctx (sub ctx (fract ctx scaled-y) (const ctx 0.5)))
dist-to-v-beam (abs ctx (sub ctx (fract ctx scaled-x) (const ctx 0.5)))
is-h-timber (sub ctx beam-half-width dist-to-h-beam)
is-v-timber (sub ctx beam-half-width dist-to-v-beam)
wood-grain (noise-fbm ctx 2 48 0.5)
wood-quant (quantize ctx wood-grain wood-color 4)
plaster-noise (noise-fbm ctx 3 16 0.4)
plaster-quant (quantize ctx plaster-noise plaster-color 3)
timber-or-plaster (select ctx is-h-timber wood-quant
(select ctx is-v-timber wood-quant plaster-quant))]
timber-or-plaster)))]
(let [tex-id (g:eval_texture 64 64)]
(g:destroy)
tex-id)))
(fn textures.door []
(let [(tex err) (pxl8.load_sprite "res/textures/door.ase")]
(if tex
tex
(do (pxl8.error (.. "Failed to load res/textures/door.ase, error: " (tostring err))) nil))))
(fn textures.wood-trim [seed base-color]
(let [g (build-graph seed
(fn [ctx]
(let [plank-tint (mul ctx (noise-value ctx 6) (const ctx 0.25))
grain-x (mul ctx ctx.x (const ctx 12))
grain-y (mul ctx ctx.y (const ctx 2))
grain-base (noise-fbm-at ctx grain-x grain-y 3 4 0.6)
grain-fine-x (mul ctx ctx.x (const ctx 48))
grain-fine-y (mul ctx ctx.y (const ctx 6))
grain-fine (noise-value-at ctx grain-fine-x grain-fine-y 1)
grain (add ctx (mul ctx grain-base (const ctx 0.6))
(mul ctx grain-fine (const ctx 0.4)))
wood-val (add ctx grain plank-tint)]
(quantize ctx wood-val base-color 6))))]
(let [tex-id (g:eval_texture 64 16)]
(g:destroy)
tex-id)))
textures