pxl8/demo/mod/entities.fnl
2026-01-31 09:31:31 -06:00

180 lines
7.2 KiB
Fennel

(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}