improve sw renderer

This commit is contained in:
asrael 2026-01-21 23:19:50 -06:00
parent 415d424057
commit 39ee0fefb7
89 changed files with 9380 additions and 2307 deletions

4103
demo/mod/blendtable.fnl Normal file

File diff suppressed because it is too large Load diff

1031
demo/mod/colormap.fnl Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,13 +1,18 @@
(local pxl8 (require :pxl8))
(local effects (require :pxl8.effects))
(local net (require :pxl8.net))
(local colormap (require :mod.colormap))
(local menu (require :mod.menu))
(local palette (require :mod.palette))
(local sky (require :mod.sky))
(local textures (require :mod.textures))
(local bob-amount 4.0)
(local bob-speed 8.0)
(local cam-smoothing 0.25)
(local cell-size 64)
(local cursor-sensitivity 0.008)
(local cursor-sensitivity 0.010)
(local gravity -800)
(local grid-size 64)
(local ground-y 64)
@ -16,7 +21,7 @@
(local land-squash-amount -4)
(local max-pitch 1.5)
(local move-speed 200)
(local turn-speed 2.0)
(local turn-speed 4.0)
(local sim-tick-rate 60)
(local sim-dt (/ 1.0 sim-tick-rate))
@ -32,10 +37,10 @@
(var cam-yaw 0)
(var cam-z 1000)
(var camera nil)
(var cursor-look? true)
(var grounded? true)
(var land-squash 0)
(var light-time 0)
(var real-time 0)
(var network nil)
(var smooth-cam-x 1000)
(var smooth-cam-z 1000)
@ -43,16 +48,148 @@
(var world nil)
(var fps-avg 0)
(var fps-sample-count 0)
(var fireball-mesh nil)
(var last-dt 0.016)
(var lights nil)
(local FIREBALL_COLOR 184)
(local cursor-look? true)
(local FIREBALL_COLOR 218)
(local STONE_FLOOR_START 37)
(local STONE_WALL_START 2)
(local MOSS_COLOR 200)
(fn init-fireball-palette []
(for [i 0 7]
(let [t (/ i 7)
r (math.floor (+ 0xFF (* t 0)))
g (math.floor (+ 0x60 (* t (- 0xE0 0x60))))
b (math.floor (+ 0x10 (* t (- 0x80 0x10))))]
(pxl8.set_palette_rgb (+ FIREBALL_COLOR i) r g b))))
(local trail-positions [])
(local TRAIL_LENGTH 8)
(fn create-fireball-mesh []
(let [verts []
indices []
radius 5
rings 4
segments 6
core-color (+ FIREBALL_COLOR 6)
spike-color (+ FIREBALL_COLOR 2)]
;; top pole
(table.insert verts {:x 0 :y radius :z 0 :nx 0 :ny 1 :nz 0 :color core-color :light 255})
;; 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})))))
;; 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})
;; 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)
@ -99,10 +236,22 @@
(values new-x new-z))
(fn init []
(pxl8.set_relative_mouse_mode true)
(pxl8.set_palette palette 256)
(pxl8.set_colormap colormap 16384)
(for [i 0 7]
(let [t (/ i 7)
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)))
(sky.update-gradient 1 2 6 6 10 18)
(pxl8.update_palette_deps)
(set camera (pxl8.create_camera_3d))
(set world (pxl8.create_world))
(set lights (pxl8.create_lights))
(sky.generate-stars 12345)
(init-fireball-palette)
(create-fireball-mesh)
(set network (net.Net.new {:port 7777}))
(when network
@ -119,17 +268,9 @@
:num_rooms 20})]
(if (< result 0)
(pxl8.error (.. "Failed to generate rooms - result: " result))
(let [floor-tex (pxl8.procgen_tex {:name "floor"
:seed 11111
:width 64
:height 64
:base_color 19})
wall-tex (pxl8.procgen_tex {:name "wall"
:seed 12345
:width 64
:height 64
:base_color 4})
sky-tex (pxl8.create_texture [0] 1 1)]
(let [floor-tex (textures.mossy-cobblestone 44444 STONE_FLOOR_START MOSS_COLOR)
wall-tex (textures.ashlar-wall 55555 STONE_WALL_START MOSS_COLOR)
sky-tex (pxl8.create_texture [0] 1 1)]
(let [result (world:apply_textures [
{:name "floor"
@ -193,6 +334,7 @@
(store-position t cam-x cam-z hist.yaw))))))))))
(fn update [dt]
(set last-dt dt)
(let [fps (pxl8.get_fps)]
(set fps-sample-count (+ fps-sample-count 1))
(set fps-avg (+ (* fps-avg (/ (- fps-sample-count 1) fps-sample-count))
@ -234,9 +376,9 @@
(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 (and (not cursor-look?) (pxl8.key_down "right"))
(set cam-yaw (+ cam-yaw (* turn-speed dt))))
(when network
(let [(ok err) (pcall (fn []
@ -287,7 +429,8 @@
(let [target-phase (* (math.floor (/ bob-time math.pi)) math.pi)]
(set bob-time (+ (* bob-time 0.8) (* target-phase 0.2))))))
(set light-time (+ light-time (* dt 0.15))))))
(set light-time (+ light-time (* dt 0.5)))
(set real-time (+ real-time dt)))))
(fn frame []
(pxl8.clear 1)
@ -316,77 +459,57 @@
[0 1 0])
(camera:set_perspective 1.047 aspect 1.0 4096.0)
(let [light-pulse (+ 0.7 (* 0.3 (math.sin (* light-time 2))))
forward-x (- (math.sin cam-yaw))
forward-z (- (math.cos cam-yaw))
light-x (+ smooth-cam-x (* 150 forward-x) (* 50 (math.cos light-time)))
light-z (+ smooth-cam-z (* 150 forward-z) (* 50 (math.sin light-time)))
light-y (+ eye-y 30)]
(pxl8.begin_frame_3d camera {
:ambient 80
(let [light-x (+ 1000 (* 50 (math.cos light-time)))
light-z (+ 940 (* 50 (math.sin light-time)))
light-y 80
phase (+ (* light-x 0.01) 1.7)
f1 (* 0.08 (math.sin (+ (* real-time 2.5) phase)))
f2 (* 0.05 (math.sin (+ (* real-time 4.1) (* phase 0.7))))
f3 (* 0.03 (math.sin (+ (* real-time 7.3) (* phase 1.2))))
flicker (+ 0.92 f1 f2 f3)
light-intensity (math.floor (math.max 0 (math.min 255 (* 255 flicker))))
r1 (* 0.06 (math.sin (+ (* real-time 1.8) (* phase 0.5))))
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)
(pxl8.begin_frame_3d camera lights {
:ambient 30
:fog_density 0.0
:celestial_dir [0.5 -0.8 0.3]
:celestial_intensity 0.5
:lights [{:x light-x :y light-y :z light-z
:r 255 :g 200 :b 150
:intensity (* 255 light-pulse)
:radius 400}]})
:celestial_intensity 0.5})
(pxl8.clear_depth)
(sky.update-gradient 1 2 6 6 10 18)
(sky.render smooth-cam-x eye-y smooth-cam-z)
(sky.render smooth-cam-x eye-y smooth-cam-z (menu.is-wireframe))
(pxl8.clear_depth)
(world:set_wireframe (menu.is-wireframe) 15)
(world:render [smooth-cam-x eye-y smooth-cam-z])
(pxl8.end_frame_3d)
(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})))
(let [dx (- light-x smooth-cam-x)
dy (- light-y eye-y)
dz (- light-z smooth-cam-z)
dist (math.sqrt (+ (* dx dx) (* dy dy) (* dz dz)))]
(when (> dist 1)
(let [inv-dist (/ 1 dist)
dir-x (* dx inv-dist)
dir-y (* dy inv-dist)
dir-z (* dz inv-dist)
cos-yaw (math.cos cam-yaw)
sin-yaw (math.sin cam-yaw)
cos-pitch (math.cos cam-pitch)
sin-pitch (math.sin cam-pitch)
rx (+ (* dir-x cos-yaw) (* dir-z sin-yaw))
rz (+ (* (- dir-x) sin-yaw) (* dir-z cos-yaw))
ry (- (* dir-y cos-pitch) (* rz sin-pitch))
fz (+ (* dir-y sin-pitch) (* rz cos-pitch))]
(when (> fz 0.01)
(let [width (pxl8.get_width)
height (pxl8.get_height)
fov 1.047
half-fov-tan (math.tan (* fov 0.5))
ndc-x (/ rx (* fz half-fov-tan aspect))
ndc-y (/ ry (* fz half-fov-tan))
sx (math.floor (* (+ 1 ndc-x) 0.5 width))
sy (math.floor (* (- 1 ndc-y) 0.5 height))
screen-size (/ 400 dist)
base-radius (math.max 2 (math.min 12 (math.floor screen-size)))
pulse-int (math.floor (* 180 light-pulse))]
(when (and (>= sx 0) (< sx width) (>= sy 0) (< sy height))
(effects.glows [
{:x sx :y sy :radius (+ base-radius 2) :intensity (/ pulse-int 5) :color (+ FIREBALL_COLOR 1) :shape effects.GLOW_CIRCLE}
{:x sx :y sy :radius base-radius :intensity pulse-int :color (+ FIREBALL_COLOR 5) :shape effects.GLOW_DIAMOND}]))))))))
(pxl8.end_frame_3d))
(sky.render-stars cam-yaw cam-pitch 1.0)
(sky.render-stars smooth-cam-x eye-y smooth-cam-z 1.0 last-dt)
(let [cx (/ (pxl8.get_width) 2)
cy (/ (pxl8.get_height) 2)
crosshair-size 4
red-color 18]
(pxl8.line (- cx crosshair-size) cy (+ cx crosshair-size) cy red-color)
(pxl8.line cx (- cy crosshair-size) cx (+ cy crosshair-size) red-color))
crosshair-color 240
text-color 251]
(pxl8.line (- cx crosshair-size) cy (+ cx crosshair-size) cy crosshair-color)
(pxl8.line cx (- cy crosshair-size) cx (+ cy crosshair-size) crosshair-color)
(pxl8.text (.. "fps: " (string.format "%.0f" fps-avg)) 5 5 12)
(pxl8.text (.. "pos: " (string.format "%.0f" cam-x) ","
(string.format "%.0f" cam-y) ","
(string.format "%.0f" cam-z)) 5 15 12))))
(pxl8.text (.. "fps: " (string.format "%.0f" fps-avg)) 5 5 text-color)
(pxl8.text (.. "pos: " (string.format "%.0f" cam-x) ","
(string.format "%.0f" cam-y) ","
(string.format "%.0f" cam-z)) 5 15 text-color)))))
{:init init
:update update

View file

@ -2,6 +2,7 @@
(local music (require :mod.music))
(var paused false)
(var wireframe false)
(var gui nil)
(fn init []
@ -37,18 +38,22 @@
(when gui
(gui:begin_frame)
(pxl8.gui_window 200 100 240 180 "pxl8 demo")
(pxl8.gui_window 200 100 240 200 "pxl8 demo")
(when (gui:button 1 215 145 210 32 "Resume")
(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 185 210 32 music-label)
(when (gui:button 3 215 182 210 30 music-label)
(if (music.is-playing)
(music.stop)
(music.start))))
(when (gui:button 2 215 225 210 32 "Quit")
(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))
(if (gui:is_hovering)
@ -58,6 +63,7 @@
(gui:end_frame)))
{:is-paused (fn [] paused)
:is-wireframe (fn [] wireframe)
:toggle toggle
:show show
:hide hide

263
demo/mod/palette.fnl Normal file
View file

@ -0,0 +1,263 @@
(require :pxl8)
(local ffi (require :ffi))
(local data (ffi.new "u32[256]" [
0x080602
0x14120E
0x23211E
0x31302C
0x403E3B
0x4B4946
0x595755
0x676664
0x767573
0x858382
0x939290
0xA2A09F
0xB0AFAE
0xBEBDBC
0xCDCCCC
0xDADAD9
0x594625
0x544023
0x4F3C24
0x4C3A22
0x453821
0x40321F
0x3E2F20
0x382D1D
0x33291E
0x30271F
0x2F251D
0x2D231E
0x28211C
0x251F1D
0x23201A
0x221F1B
0x646269
0x5F5C61
0x5C545A
0x584F55
0x5B514F
0x554A47
0x4B413F
0x423C36
0x463D31
0x3E352A
0x362E25
0x2D2922
0x26221D
0x1C1916
0x151310
0x100F0D
0x8C6F52
0x7E6045
0x73553B
0x715134
0xBA8346
0xA1723B
0x815C2E
0x745226
0xCC8926
0xBB7E22
0x9F6B1F
0x875A1C
0x6E4918
0x553712
0x3B250D
0x24180A
0xA34331
0x9B3728
0x923220
0x882E18
0x842B16
0x772312
0x69200D
0x5A1C06
0x541C04
0x4C1A03
0x411701
0x371000
0x2E0D00
0x250B00
0x1B0600
0x130500
0x7D5741
0x76503A
0x6E4C37
0x684833
0x5D3F2F
0x553A2C
0x4F3628
0x483024
0x4A3126
0x483025
0x432D22
0x3C2C22
0x352922
0x2C241F
0x221C1B
0x1A1916
0x6E4626
0x5F4025
0x523924
0x433322
0x352B1E
0x28231A
0x1A1A14
0x1C1815
0x96544B
0xAC7369
0xB48C86
0xBCA7A4
0xB1BCC2
0x9DB0B9
0x8A9FAA
0x77929F
0x738995
0x5E7C8B
0x4A6C7D
0x345E72
0x1F4C64
0x19445C
0x143C51
0x10384B
0x183748
0x1A3341
0x192F39
0x152B34
0x13262E
0x101E23
0x0E1519
0x0B0E10
0x896463
0x815C5B
0x785352
0x6F4C4D
0x664444
0x5F3C3D
0x573738
0x523233
0x442929
0x392324
0x2D1D1D
0x241414
0x1A0E0E
0x100909
0x070403
0x000000
0x98936F
0x918B68
0x887F60
0x807759
0x797055
0x73684D
0x6B6146
0x63593F
0x5B523A
0x504834
0x423D2D
0x373226
0x2E2B1F
0x222018
0x161511
0x0E0F0A
0x9A554F
0x904D48
0x87453F
0x7D4037
0x743831
0x693329
0x612C24
0x572720
0x4F231A
0x441E16
0x391914
0x2D150F
0x22110D
0x1A0B06
0x0D0403
0x040202
0x7F77C0
0x7770B5
0x6E68A8
0x686099
0x60588C
0x575381
0x4E4C72
0x454263
0x3D3957
0x34324A
0x2C2940
0x242135
0x1E1928
0x16121D
0x0C0A12
0x050306
0x88AF7B
0x81A473
0x7B9A67
0x728E5D
0x6D8553
0x61794A
0x5B7144
0x61734B
0x586A3D
0x4D5E2D
0x465422
0x3F4D17
0x36420E
0x2F3507
0x272804
0x211F02
0x1EF708
0x3CE10D
0x51CC1B
0x64B621
0x6DA12C
0x69882B
0x727F3B
0xE4DDCE
0xEEE6BA
0xEAE290
0xE9E26D
0xE5DE43
0xE3DB20
0xE1CC18
0xDFB911
0xDCA60B
0xE8A306
0xDF9312
0xE17B05
0xC86815
0xBC5908
0xB14805
0xA63D07
0xB6431E
0xAA381A
0x9A2E12
0x8C270F
0x892B17
0x762311
0x5F1F0D
0x491B09
0x3B1809
0xE50F19
0x6A34C4
0xE00B28
0x2B08C8
0x322A33
0x281C0E
0x2F1E15
0xD48067
0xC26B4C
0x974928
0x814123
0xD5B3A9
0xBE9D93
0x9A7B6C
0x7F5F51
0x8E504C
]))
data

View file

@ -1,23 +1,32 @@
(local ffi (require :ffi))
(local pxl8 (require :pxl8))
(local effects (require :pxl8.effects))
(local SKY_GRADIENT_START 144)
(local SKY_GRADIENT_COUNT 16)
(local sky-radius 900)
(local sky-segments 16)
(local sky-rings 16)
(local max-theta (* math.pi 0.55))
(local STAR_COUNT 200)
(local TINY_STAR_COUNT 5000)
(local STAR_SILVER_START 160)
(local STAR_BLUE_START 168)
(local STAR_RED_START 176)
(local NUM_RANDOM_STARS 300)
(local NUM_TINY_STARS 7000)
(local STAR_SEED 0xDEADBEEF)
(local STAR_CYCLE_PERIOD 7200)
;; Use existing bright palette colors
;; Silver/white: indices 14-15 (brightest grays)
(local IDX_SILVER 14)
;; Warm/torch: indices 216-218 (bright creams/yellows)
(local IDX_TORCH 216)
;; Blue/magic: indices 176-178 (purples - brightest of the range)
(local IDX_MAGIC 176)
(var sky-mesh nil)
(var last-gradient-key nil)
(var stars [])
(var random-stars [])
(var sky-mesh nil)
(var star-count 0)
(var star-directions nil)
(var star-glows nil)
(var star-projected nil)
(var star-time 0)
(var tiny-stars [])
(fn generate-sky-gradient [zenith-r zenith-g zenith-b horizon-r horizon-g horizon-b]
@ -33,8 +42,8 @@
indices []]
(for [i 0 (- sky-rings 1)]
(let [theta0 (* (/ i sky-rings) max-theta)
theta1 (* (/ (+ i 1) sky-rings) max-theta)
(let [theta0 (* (/ i sky-rings) math.pi 0.5)
theta1 (* (/ (+ i 1) sky-rings) math.pi 0.5)
sin-t0 (math.sin theta0)
cos-t0 (math.cos theta0)
sin-t1 (math.sin theta1)
@ -67,22 +76,17 @@
(if (= i 0)
(do
;; First ring is degenerate - just a triangle from pole
;; Vertices: v00 (pole, c0), v11 (bottom-right, c1), v10 (bottom-left, c1)
(table.insert verts {:x x00 :y y0 :z z00 :nx nx00 :ny ny00 :nz nz00 :color c0 :light 255})
(table.insert verts {:x x11 :y y1 :z z11 :nx nx11 :ny ny11 :nz nz11 :color c1 :light 255})
(table.insert verts {:x x10 :y y1 :z z10 :nx nx10 :ny ny10 :nz nz10 :color c1 :light 255})
;; Triangle: base, base+2, base+1
(table.insert indices base-idx)
(table.insert indices (+ base-idx 2))
(table.insert indices (+ base-idx 1)))
(do
;; Regular quad: v00 (top-left), v01 (top-right), v11 (bottom-right), v10 (bottom-left)
(table.insert verts {:x x00 :y y0 :z z00 :nx nx00 :ny ny00 :nz nz00 :color c0 :light 255})
(table.insert verts {:x x01 :y y0 :z z01 :nx nx01 :ny ny01 :nz nz01 :color c0 :light 255})
(table.insert verts {:x x11 :y y1 :z z11 :nx nx11 :ny ny11 :nz nz11 :color c1 :light 255})
(table.insert verts {:x x10 :y y1 :z z10 :nx nx10 :ny ny10 :nz nz10 :color c1 :light 255})
;; push_quad(base, base+3, base+2, base+1) = triangles (base,base+3,base+2) and (base,base+2,base+1)
(table.insert indices base-idx)
(table.insert indices (+ base-idx 3))
(table.insert indices (+ base-idx 2))
@ -98,149 +102,142 @@
(generate-sky-gradient zenith-r zenith-g zenith-b horizon-r horizon-g horizon-b)
(set last-gradient-key key))))
(fn palette-ramp [start c0 c1]
(let [r0 (bit.rshift (bit.band c0 0xFF0000) 16)
g0 (bit.rshift (bit.band c0 0x00FF00) 8)
b0 (bit.band c0 0x0000FF)
r1 (bit.rshift (bit.band c1 0xFF0000) 16)
g1 (bit.rshift (bit.band c1 0x00FF00) 8)
b1 (bit.band c1 0x0000FF)]
(for [i 0 7]
(let [t (/ i 7)
r (math.floor (+ r0 (* t (- r1 r0))))
g (math.floor (+ g0 (* t (- g1 g0))))
b (math.floor (+ b0 (* t (- b1 b0))))]
(pxl8.set_palette_rgb (+ start i) r g b)))))
(fn galactic-band-factor [dx dy dz]
(let [band-len (math.sqrt (+ (* 0.6 0.6) (* 0.3 0.3) (* 0.742 0.742)))
bx (/ 0.6 band-len)
by (/ 0.3 band-len)
bz (/ 0.742 band-len)
dist-from-band (math.abs (+ (* dx bx) (* dy by) (* dz bz)))
in-band (- 1 (math.min (* dist-from-band 3) 1))]
(* in-band in-band)))
(fn init-star-palette []
(palette-ramp STAR_SILVER_START 0x707888 0xFFFFFF) ;; silver
(palette-ramp STAR_BLUE_START 0x5070B0 0xD0E8FF) ;; blue
(palette-ramp STAR_RED_START 0x802020 0xFF9090)) ;; red
(fn generate-stars [seed]
(set stars [])
(fn generate-stars []
(set random-stars [])
(set tiny-stars [])
(init-star-palette)
(pxl8.rng_seed seed)
(for [i 1 STAR_COUNT]
(let [theta (math.acos (- 1 (* (pxl8.rng_f32) 0.85)))
phi (* (pxl8.rng_f32) math.pi 2)
brightness (pxl8.rng_range 1 4)
color-type (pxl8.rng_range 0 100)
shade (pxl8.rng_range 0 5)
color (if (< color-type 3) (+ STAR_RED_START shade)
(< color-type 15) (+ STAR_BLUE_START shade)
(+ STAR_SILVER_START shade))
sin-t (math.sin theta)
cos-t (math.cos theta)]
(table.insert stars {:dx (* sin-t (math.cos phi))
:dy cos-t
:dz (* sin-t (math.sin phi))
:brightness brightness
:color color})))
;; Generate random stars - use full upper hemisphere (dy > -0.1)
(for [i 0 (- NUM_RANDOM_STARS 1)]
(let [h1 (pxl8.hash32 (+ STAR_SEED (* i 5)))
h2 (pxl8.hash32 (+ STAR_SEED (* i 5) 1))
h3 (pxl8.hash32 (+ STAR_SEED (* i 5) 2))
h4 (pxl8.hash32 (+ STAR_SEED (* i 5) 3))
theta (* (/ h1 0xFFFFFFFF) math.pi 2)
phi (math.acos (- 1 (* (/ h2 0xFFFFFFFF) 1.0)))
sin-phi (math.sin phi)
cos-phi (math.cos phi)
dx (* sin-phi (math.cos theta))
dy cos-phi
dz (* sin-phi (math.sin theta))
brightness-raw (/ (% h3 256) 255)
brightness (math.floor (+ 60 (* brightness-raw brightness-raw 195)))
color-type (% h4 100)
color (if (< color-type 8) (+ IDX_TORCH (% (bit.rshift h4 8) 2))
(< color-type 16) (+ IDX_MAGIC (% (bit.rshift h4 8) 2))
(+ IDX_SILVER (% (bit.rshift h4 8) 2)))]
(pxl8.rng_seed (+ seed 0xCAFEBABE))
(for [i 1 TINY_STAR_COUNT]
(let [theta (math.acos (- 1 (* (pxl8.rng_f32) 0.95)))
phi (* (pxl8.rng_f32) math.pi 2)
brightness (+ 25 (pxl8.rng_range 0 40))
shade (pxl8.rng_range 0 3)
color-type (pxl8.rng_range 0 100)
color (if (< color-type 15) (+ STAR_BLUE_START shade)
(+ STAR_SILVER_START shade))
sin-t (math.sin theta)
cos-t (math.cos theta)]
(table.insert tiny-stars {:dx (* sin-t (math.cos phi))
:dy cos-t
:dz (* sin-t (math.sin phi))
:brightness brightness
:color color}))))
(when (> dy -0.1)
(table.insert random-stars {:dx dx :dy dy :dz dz
:brightness brightness
:color color}))))
(fn project-direction [dir-x dir-y dir-z yaw pitch width height]
(let [cos-yaw (math.cos yaw)
sin-yaw (math.sin yaw)
cos-pitch (math.cos pitch)
sin-pitch (math.sin pitch)
rotated-x (+ (* dir-x cos-yaw) (* dir-z sin-yaw))
rotated-z (+ (* (- dir-x) sin-yaw) (* dir-z cos-yaw))
rotated-y (- (* dir-y cos-pitch) (* rotated-z sin-pitch))
final-z (+ (* dir-y sin-pitch) (* rotated-z cos-pitch))]
(when (> final-z 0.01)
(let [fov 1.047
aspect (/ width height)
half-fov-tan (math.tan (* fov 0.5))
ndc-x (/ rotated-x (* final-z half-fov-tan aspect))
ndc-y (/ rotated-y (* final-z half-fov-tan))]
(when (and (>= ndc-x -1) (<= ndc-x 1) (>= ndc-y -1) (<= ndc-y 1))
{:x (math.floor (* (+ 1 ndc-x) 0.5 width))
:y (math.floor (* (- 1 ndc-y) 0.5 height))})))))
(let [tiny-seed (+ STAR_SEED 0xCAFEBABE)]
(for [i 0 (- NUM_TINY_STARS 1)]
(let [h1 (pxl8.hash32 (+ tiny-seed (* i 4)))
h2 (pxl8.hash32 (+ tiny-seed (* i 4) 1))
h3 (pxl8.hash32 (+ tiny-seed (* i 4) 2))
h4 (pxl8.hash32 (+ tiny-seed (* i 4) 3))
theta (* (/ h1 0xFFFFFFFF) math.pi 2)
phi (math.acos (- 1 (* (/ h2 0xFFFFFFFF) 1.0)))
sin-phi (math.sin phi)
cos-phi (math.cos phi)
dx (* sin-phi (math.cos theta))
dy cos-phi
dz (* sin-phi (math.sin theta))
band-boost (galactic-band-factor dx dy dz)
base-bright (+ 40 (% h3 50))
brightness (+ base-bright (math.floor (* band-boost 40)))
color-shift (% h4 100)
color (if (< color-shift 3) (+ IDX_TORCH (% (bit.rshift h4 8) 2))
(< color-shift 12) (+ IDX_MAGIC (% (bit.rshift h4 8) 2))
(+ IDX_SILVER (% (bit.rshift h4 8) 2)))]
(when (> dy -0.1)
(table.insert tiny-stars {:dx dx :dy dy :dz dz
:brightness brightness
:color color})))))
(fn render-stars [yaw pitch intensity]
(when (> intensity 0)
(let [width (pxl8.get_width)
height (pxl8.get_height)
glows []
fade-sq (* intensity intensity)]
(set star-count (+ (length tiny-stars) (length random-stars)))
(set star-directions (pxl8.create_vec3_array star-count))
(set star-glows (pxl8.create_glows 10000))
(set star-projected (pxl8.create_vec3_array star-count))
(each [_ star (ipairs tiny-stars)]
(let [screen (project-direction star.dx star.dy star.dz yaw pitch width height)]
(when screen
(let [int (math.floor (* star.brightness fade-sq))]
(var idx 0)
(each [_ star (ipairs tiny-stars)]
(let [dir (. star-directions idx)]
(set dir.x star.dx)
(set dir.y star.dy)
(set dir.z star.dz))
(set idx (+ idx 1)))
(each [_ star (ipairs random-stars)]
(let [dir (. star-directions idx)]
(set dir.x star.dx)
(set dir.y star.dy)
(set dir.z star.dz))
(set idx (+ idx 1))))
(fn render-stars [cam-x cam-y cam-z intensity dt]
(set star-time (+ star-time (or dt 0)))
(when (and (> intensity 0) (> star-count 0) star-glows)
(let [fade-in (* intensity intensity)
time-factor (/ star-time 60)
star-rotation (/ (* star-time math.pi 2) STAR_CYCLE_PERIOD)
t (pxl8.mat4_translate cam-x cam-y cam-z)
r (pxl8.mat4_rotate_y star-rotation)
s (pxl8.mat4_scale sky-radius sky-radius sky-radius)
transform (pxl8.mat4_multiply t (pxl8.mat4_multiply r s))
tiny-count (length tiny-stars)]
(star-glows:clear)
(pxl8.project_points star-directions star-projected star-count transform)
(for [i 0 (- tiny-count 1)]
(let [screen (. star-projected i)]
(when (> screen.z 0)
(let [star (. tiny-stars (+ i 1))
int (math.floor (* star.brightness fade-in))]
(when (> int 8)
(table.insert glows {:x screen.x :y screen.y
:radius 1
:intensity int
:color star.color
:shape effects.GLOW_CIRCLE}))))))
(star-glows:add (math.floor screen.x) (math.floor screen.y)
1 int star.color pxl8.GLOW_CIRCLE))))))
(each [_ star (ipairs stars)]
(let [screen (project-direction star.dx star.dy star.dz yaw pitch width height)]
(when screen
(let [base-int (math.floor (* star.brightness 50 fade-sq 1.5))]
(if (>= star.brightness 4)
(for [i 0 (- (length random-stars) 1)]
(let [screen (. star-projected (+ tiny-count i))]
(when (> screen.z 0)
(let [star (. random-stars (+ i 1))
phase (+ (* (+ i 1) 2.137) (* time-factor 3.0))
twinkle (+ 0.75 (* 0.25 (math.sin (* phase 6.28))))
int (math.floor (* star.brightness fade-in twinkle))
sx (math.floor screen.x)
sy (math.floor screen.y)]
(if (> star.brightness 220)
(do
(table.insert glows {:x screen.x :y screen.y
:radius 4
:intensity (math.floor (/ base-int 4))
:color star.color
:shape effects.GLOW_CIRCLE})
(table.insert glows {:x screen.x :y screen.y
:radius 2
:intensity base-int
:color star.color
:shape effects.GLOW_DIAMOND}))
(>= star.brightness 3)
(star-glows:add sx sy 3 (math.floor (* int 1.5)) star.color pxl8.GLOW_DIAMOND)
(star-glows:add sx sy 5 (math.floor (/ int 2)) star.color pxl8.GLOW_CIRCLE))
(> star.brightness 180)
(do
(table.insert glows {:x screen.x :y screen.y
:radius 3
:intensity (math.floor (/ base-int 4))
:color star.color
:shape effects.GLOW_CIRCLE})
(table.insert glows {:x screen.x :y screen.y
:radius 2
:intensity base-int
:color star.color
:shape effects.GLOW_DIAMOND}))
(>= star.brightness 2)
(table.insert glows {:x screen.x :y screen.y
:radius 2
:intensity base-int
:color star.color
:shape effects.GLOW_DIAMOND})
(table.insert glows {:x screen.x :y screen.y
:radius 1
:intensity (math.floor (* base-int 0.7))
:color star.color
:shape effects.GLOW_CIRCLE}))))))
(star-glows:add sx sy 2 int star.color pxl8.GLOW_DIAMOND)
(star-glows:add sx sy 4 (math.floor (/ int 3)) star.color pxl8.GLOW_CIRCLE))
(> star.brightness 120)
(do
(star-glows:add sx sy 2 (math.floor (* int 0.67)) star.color pxl8.GLOW_DIAMOND)
(star-glows:add sx sy 3 (math.floor (/ int 4)) star.color pxl8.GLOW_CIRCLE))
(star-glows:add sx sy 2 (math.floor (* int 0.5)) star.color pxl8.GLOW_CIRCLE))))))
(when (> (length glows) 0)
(effects.glows glows)))))
(when (> (star-glows:count) 0)
(star-glows:render)))))
(fn render [cam-x cam-y cam-z]
(fn render [cam-x cam-y cam-z wireframe]
(when (not sky-mesh) (create-sky-dome))
(when sky-mesh
(pxl8.draw_mesh sky-mesh {:x cam-x :y cam-y :z cam-z :passthrough true})))
(pxl8.draw_mesh sky-mesh {:x cam-x :y cam-y :z cam-z :passthrough true :wireframe wireframe})))
{:render render
:render-stars render-stars

168
demo/mod/textures.fnl Normal file
View file

@ -0,0 +1,168 @@
(local pxl8 (require :pxl8))
(local procgen (require :pxl8.procgen))
(local textures {})
(fn build-graph [seed builder]
(let [g (pxl8.create_graph 128)
x (g:add_node procgen.OP_INPUT_X 0 0 0 0 0)
y (g:add_node procgen.OP_INPUT_Y 0 0 0 0 0)
ctx {:graph g :x x :y y}]
(g:set_seed seed)
(let [output (builder ctx)]
(g:set_output output)
g)))
(fn const [ctx val]
(ctx.graph:add_node procgen.OP_CONST 0 0 0 0 val))
(fn add [ctx a b]
(ctx.graph:add_node procgen.OP_ADD a b 0 0 0))
(fn sub [ctx a b]
(ctx.graph:add_node procgen.OP_SUB a b 0 0 0))
(fn mul [ctx a b]
(ctx.graph:add_node procgen.OP_MUL a b 0 0 0))
(fn div [ctx a b]
(ctx.graph:add_node procgen.OP_DIV a b 0 0 0))
(fn min-op [ctx a b]
(ctx.graph:add_node procgen.OP_MIN a b 0 0 0))
(fn max-op [ctx a b]
(ctx.graph:add_node procgen.OP_MAX a b 0 0 0))
(fn abs [ctx a]
(ctx.graph:add_node procgen.OP_ABS a 0 0 0 0))
(fn floor [ctx a]
(ctx.graph:add_node procgen.OP_FLOOR a 0 0 0 0))
(fn fract [ctx a]
(ctx.graph:add_node procgen.OP_FRACT a 0 0 0 0))
(fn lerp [ctx a b t]
(ctx.graph:add_node procgen.OP_LERP a b t 0 0))
(fn clamp [ctx val lo hi]
(ctx.graph:add_node procgen.OP_CLAMP val lo hi 0 0))
(fn select [ctx cond a b]
(ctx.graph:add_node procgen.OP_SELECT a b cond 0 0))
(fn smoothstep [ctx edge0 edge1 x]
(ctx.graph:add_node procgen.OP_SMOOTHSTEP edge0 edge1 x 0 0))
(fn noise-value [ctx scale]
(let [s (const ctx scale)]
(ctx.graph:add_node procgen.OP_NOISE_VALUE ctx.x ctx.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)))
(fn noise-fbm [ctx octaves scale persistence]
(let [s (const ctx scale)
p (const ctx persistence)]
(ctx.graph:add_node procgen.OP_NOISE_FBM ctx.x ctx.y s p octaves)))
(fn noise-ridged [ctx octaves scale persistence]
(let [s (const ctx scale)
p (const ctx persistence)]
(ctx.graph:add_node procgen.OP_NOISE_RIDGED ctx.x ctx.y s p octaves)))
(fn noise-turbulence [ctx octaves scale persistence]
(let [s (const ctx scale)
p (const ctx persistence)]
(ctx.graph:add_node procgen.OP_NOISE_TURBULENCE ctx.x ctx.y s p octaves)))
(fn voronoi-cell [ctx scale]
(let [s (const ctx scale)]
(ctx.graph:add_node procgen.OP_VORONOI_CELL ctx.x ctx.y s 0 0)))
(fn voronoi-edge [ctx scale]
(let [s (const ctx scale)]
(ctx.graph:add_node procgen.OP_VORONOI_EDGE ctx.x ctx.y s 0 0)))
(fn voronoi-id [ctx scale]
(let [s (const ctx scale)]
(ctx.graph:add_node procgen.OP_VORONOI_ID ctx.x ctx.y s 0 0)))
(fn gradient-linear [ctx angle]
(let [a (const ctx angle)]
(ctx.graph:add_node procgen.OP_GRADIENT_LINEAR ctx.x ctx.y a 0 0)))
(fn gradient-radial [ctx cx cy]
(let [center-x (const ctx cx)
center-y (const ctx cy)]
(ctx.graph:add_node procgen.OP_GRADIENT_RADIAL ctx.x ctx.y center-x center-y 0)))
(fn quantize [ctx val base range]
(let [r (const ctx range)]
(ctx.graph:add_node procgen.OP_QUANTIZE val r 0 0 base)))
(fn textures.mossy-cobblestone [seed base-color moss-color]
(let [g (build-graph seed
(fn [ctx]
(let [cell (voronoi-cell ctx 6)
edge (voronoi-edge ctx 6)
mortar-threshold (const ctx 0.05)
is-mortar (sub ctx mortar-threshold edge)
mortar-color (const ctx (- base-color 2))
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 base-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.ashlar-wall [seed base-color moss-color]
(let [g (build-graph seed
(fn [ctx]
(let [cell (voronoi-cell ctx 5)
edge (voronoi-edge ctx 5)
cell-id (voronoi-id ctx 5)
mortar-threshold (const ctx 0.12)
is-mortar (sub ctx mortar-threshold edge)
stone-detail (noise-fbm ctx 3 32 0.5)
stone-tint (mul ctx cell-id (const ctx 0.4))
stone-shade (mul ctx cell (const ctx 0.3))
stone-combined (add ctx stone-detail (add ctx stone-tint stone-shade))
stone-quant (quantize ctx stone-combined base-color 10)
crack-moss (noise-fbm ctx 3 16 0.5)
moss-in-crack (mul ctx crack-moss (sub ctx (const ctx 0.2) edge))
moss-threshold (const ctx 0.06)
has-moss (sub ctx moss-in-crack moss-threshold)
moss-quant (quantize ctx crack-moss moss-color 4)
mortar-color (const ctx (+ base-color 1))
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.gradient-sky [seed zenith-color horizon-color]
(let [g (build-graph seed
(fn [ctx]
(let [grad (gradient-linear ctx (* math.pi 0.5))
zenith (const ctx zenith-color)
horizon (const ctx horizon-color)
range (const ctx (- horizon-color zenith-color))]
(quantize ctx grad zenith-color (- horizon-color zenith-color)))))]
(let [tex-id (g:eval_texture 64 64)]
(g:destroy)
tex-id)))
textures