improve sw renderer
This commit is contained in:
parent
415d424057
commit
39ee0fefb7
89 changed files with 9380 additions and 2307 deletions
|
|
@ -6,11 +6,10 @@
|
|||
(var time 0)
|
||||
(var active-demo :logo)
|
||||
(var particles nil)
|
||||
(var particles2 nil)
|
||||
(var fire-init? false)
|
||||
(var rain-init? false)
|
||||
(var snow-init? false)
|
||||
(var snow-init2? false)
|
||||
(var first_person3d-init? false)
|
||||
(var use-famicube-palette? false)
|
||||
|
||||
(var logo-x 256)
|
||||
|
|
@ -32,9 +31,7 @@
|
|||
(pxl8.load_palette "res/sprites/pxl8_logo.ase")
|
||||
(set logo-sprite (pxl8.load_sprite "res/sprites/pxl8_logo.ase"))
|
||||
(set particles (pxl8.create_particles 1000))
|
||||
(set particles2 (pxl8.create_particles 500))
|
||||
(music.init)
|
||||
(first_person3d.init)))
|
||||
(music.init)))
|
||||
|
||||
(global update (fn [dt]
|
||||
(when (pxl8.key_pressed "escape")
|
||||
|
|
@ -57,7 +54,8 @@
|
|||
(set transition-pending nil)
|
||||
(when (= active-demo :fire) (set fire-init? false))
|
||||
(when (= active-demo :rain) (set rain-init? false))
|
||||
(when (= active-demo :snow) (set snow-init? false) (set snow-init2? false)))
|
||||
(when (= active-demo :snow) (set snow-init? false))
|
||||
(when (= active-demo :first_person3d) (set first_person3d-init? false)))
|
||||
(transition:destroy)
|
||||
(set transition nil)))
|
||||
|
||||
|
|
@ -92,12 +90,14 @@
|
|||
(when (> logo-y 296)
|
||||
(set logo-y 296)
|
||||
(set logo-dy (- (math.abs logo-dy)))))
|
||||
:first_person3d (first_person3d.update dt))
|
||||
:first_person3d (do
|
||||
(when (not first_person3d-init?)
|
||||
(first_person3d.init)
|
||||
(set first_person3d-init? true))
|
||||
(first_person3d.update dt)))
|
||||
|
||||
(when particles
|
||||
(particles:update dt))
|
||||
(when particles2
|
||||
(particles2:update dt)))
|
||||
(particles:update dt)))
|
||||
|
||||
(when (menu.is-paused)
|
||||
(menu.update))))
|
||||
|
|
@ -174,6 +174,7 @@
|
|||
|
||||
:first_person3d (first_person3d.frame)
|
||||
|
||||
|
||||
_ (pxl8.clear 0))
|
||||
|
||||
(when transition
|
||||
|
|
|
|||
4103
demo/mod/blendtable.fnl
Normal file
4103
demo/mod/blendtable.fnl
Normal file
File diff suppressed because it is too large
Load diff
1031
demo/mod/colormap.fnl
Normal file
1031
demo/mod/colormap.fnl
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -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,16 +268,8 @@
|
|||
: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})
|
||||
(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 [
|
||||
|
|
@ -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 (.. "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 12))))
|
||||
(string.format "%.0f" cam-z)) 5 15 text-color)))))
|
||||
|
||||
{:init init
|
||||
:update update
|
||||
|
|
|
|||
|
|
@ -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
263
demo/mod/palette.fnl
Normal 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
|
||||
281
demo/mod/sky.fnl
281
demo/mod/sky.fnl
|
|
@ -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))
|
||||
(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))
|
||||
|
||||
(var idx 0)
|
||||
(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))]
|
||||
(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
168
demo/mod/textures.fnl
Normal 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
|
||||
5
demo/profile_3d.fnl
Normal file
5
demo/profile_3d.fnl
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
(local first_person3d (require :mod.first_person3d))
|
||||
|
||||
(global init first_person3d.init)
|
||||
(global update first_person3d.update)
|
||||
(global frame first_person3d.frame)
|
||||
41
pxl8.sh
41
pxl8.sh
|
|
@ -62,12 +62,17 @@ build_server() {
|
|||
print_info "Building server ($mode mode)"
|
||||
cd server
|
||||
if [[ "$mode" == "release" ]]; then
|
||||
cargo build --release > /dev/null 2>&1
|
||||
cargo build --release
|
||||
else
|
||||
cargo build > /dev/null 2>&1
|
||||
cargo build
|
||||
fi
|
||||
local status=$?
|
||||
cd - > /dev/null
|
||||
if [[ $status -eq 0 ]]; then
|
||||
print_info "Built server"
|
||||
else
|
||||
print_error "Server build failed"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
|
|
@ -79,11 +84,16 @@ start_server() {
|
|||
else
|
||||
server_bin="server/target/debug/pxl8-server"
|
||||
fi
|
||||
print_info "Server mode: $mode, binary: $server_bin"
|
||||
if [[ -f "$server_bin" ]]; then
|
||||
print_info "Starting server"
|
||||
print_info "Starting server..."
|
||||
./$server_bin &
|
||||
SERVER_PID=$!
|
||||
sleep 0.2
|
||||
print_info "Server started with PID $SERVER_PID"
|
||||
sleep 0.5
|
||||
else
|
||||
print_error "Server binary not found: $server_bin"
|
||||
print_error "Build the server first with: cd server && cargo build"
|
||||
fi
|
||||
}
|
||||
|
||||
|
|
@ -213,7 +223,7 @@ timestamp() {
|
|||
update_fennel() {
|
||||
print_info "Fetching Fennel"
|
||||
|
||||
local version="1.6.0"
|
||||
local version="1.6.1"
|
||||
|
||||
if curl -sL --max-time 5 -o lib/fennel/fennel.lua "https://fennel-lang.org/downloads/fennel-${version}.lua" 2>/dev/null; then
|
||||
if [[ -f "lib/fennel/fennel.lua" ]] && [[ -s "lib/fennel/fennel.lua" ]]; then
|
||||
|
|
@ -304,7 +314,8 @@ for arg in "$@"; do
|
|||
done
|
||||
|
||||
if [ "$MODE" = "release" ]; then
|
||||
CFLAGS="$CFLAGS -O3 -ffast-math -funroll-loops -fno-unwind-tables -fno-asynchronous-unwind-tables"
|
||||
CFLAGS="$CFLAGS -O3 -flto -ffast-math -funroll-loops -fno-unwind-tables -fno-asynchronous-unwind-tables"
|
||||
LINKER_FLAGS="$LINKER_FLAGS -flto"
|
||||
BUILDDIR="$BUILDDIR/release"
|
||||
BINDIR="$BINDIR/release"
|
||||
else
|
||||
|
|
@ -313,7 +324,7 @@ else
|
|||
BINDIR="$BINDIR/debug"
|
||||
fi
|
||||
|
||||
DEP_CFLAGS="-O3 -ffast-math -funroll-loops"
|
||||
DEP_CFLAGS="-O3 -funroll-loops"
|
||||
|
||||
case "$COMMAND" in
|
||||
build)
|
||||
|
|
@ -360,7 +371,7 @@ case "$COMMAND" in
|
|||
print_info "Compiler cache: ccache enabled"
|
||||
fi
|
||||
|
||||
INCLUDES="-Isrc/core -Isrc/math -Isrc/gfx -Isrc/sfx -Isrc/script -Isrc/hal -Isrc/world -Isrc/asset -Isrc/game -Isrc/net -Ilib -Ilib/luajit/src -Ilib/linenoise -Ilib/miniz"
|
||||
INCLUDES="-Isrc/asset -Isrc/core -Isrc/gfx -Isrc/gui -Isrc/hal -Isrc/math -Isrc/net -Isrc/procgen -Isrc/script -Isrc/sfx -Isrc/world -Ilib/linenoise -Ilib/luajit/src -Ilib/miniz"
|
||||
COMPILE_FLAGS="$CFLAGS $INCLUDES"
|
||||
DEP_COMPILE_FLAGS="$DEP_CFLAGS $INCLUDES"
|
||||
|
||||
|
|
@ -377,7 +388,6 @@ case "$COMMAND" in
|
|||
src/math/pxl8_math.c
|
||||
src/gfx/pxl8_anim.c
|
||||
src/gfx/pxl8_atlas.c
|
||||
src/gfx/pxl8_blend.c
|
||||
src/gfx/pxl8_blit.c
|
||||
src/gfx/pxl8_3d_camera.c
|
||||
src/gfx/pxl8_colormap.c
|
||||
|
|
@ -385,6 +395,9 @@ case "$COMMAND" in
|
|||
src/gfx/pxl8_dither.c
|
||||
src/gfx/pxl8_font.c
|
||||
src/gfx/pxl8_gfx.c
|
||||
src/gfx/pxl8_glows.c
|
||||
src/gfx/pxl8_lightmap.c
|
||||
src/gfx/pxl8_lights.c
|
||||
src/gfx/pxl8_mesh.c
|
||||
src/gfx/pxl8_palette.c
|
||||
src/gfx/pxl8_particles.c
|
||||
|
|
@ -394,15 +407,17 @@ case "$COMMAND" in
|
|||
src/sfx/pxl8_sfx.c
|
||||
src/script/pxl8_repl.c
|
||||
src/script/pxl8_script.c
|
||||
src/hal/pxl8_sdl3.c
|
||||
src/hal/pxl8_hal_sdl3.c
|
||||
src/hal/pxl8_mem_sdl3.c
|
||||
src/world/pxl8_bsp.c
|
||||
src/world/pxl8_gen.c
|
||||
src/world/pxl8_world.c
|
||||
src/procgen/pxl8_graph.c
|
||||
src/asset/pxl8_ase.c
|
||||
src/asset/pxl8_cart.c
|
||||
src/asset/pxl8_save.c
|
||||
src/game/pxl8_gui.c
|
||||
src/game/pxl8_replay.c
|
||||
src/gui/pxl8_gui.c
|
||||
src/core/pxl8_replay.c
|
||||
src/net/pxl8_net.c
|
||||
src/net/pxl8_protocol.c
|
||||
"
|
||||
|
|
@ -477,7 +492,7 @@ case "$COMMAND" in
|
|||
RUN_SERVER=false
|
||||
for arg in "$@"; do
|
||||
if [[ "$arg" == "--release" ]]; then
|
||||
continue
|
||||
MODE="release"
|
||||
elif [[ "$arg" == "--repl" ]]; then
|
||||
EXTRA_ARGS="$EXTRA_ARGS --repl"
|
||||
elif [[ "$arg" == "--server" ]]; then
|
||||
|
|
|
|||
|
|
@ -3,14 +3,14 @@ use std::path::PathBuf;
|
|||
|
||||
fn main() {
|
||||
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||
let client_src = PathBuf::from(&manifest_dir).join("../client/src");
|
||||
let pxl8_src = PathBuf::from(&manifest_dir).join("../src");
|
||||
|
||||
println!("cargo:rerun-if-changed=../client/src/net/pxl8_protocol.h");
|
||||
println!("cargo:rerun-if-changed=../client/src/core/pxl8_types.h");
|
||||
println!("cargo:rerun-if-changed=../src/net/pxl8_protocol.h");
|
||||
println!("cargo:rerun-if-changed=../src/core/pxl8_types.h");
|
||||
|
||||
let bindings = bindgen::Builder::default()
|
||||
.header(client_src.join("net/pxl8_protocol.h").to_str().unwrap())
|
||||
.clang_arg(format!("-I{}", client_src.join("core").display()))
|
||||
.header(pxl8_src.join("net/pxl8_protocol.h").to_str().unwrap())
|
||||
.clang_arg(format!("-I{}", pxl8_src.join("core").display()))
|
||||
.use_core()
|
||||
.rustified_enum(".*")
|
||||
.generate()
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include "pxl8_io.h"
|
||||
#include "pxl8_log.h"
|
||||
#include "pxl8_mem.h"
|
||||
|
||||
static pxl8_result parse_ase_header(pxl8_stream* stream, pxl8_ase_header* header) {
|
||||
header->file_size = pxl8_read_u32(stream);
|
||||
|
|
@ -58,7 +59,7 @@ static pxl8_result parse_old_palette_chunk(pxl8_stream* stream, pxl8_ase_palette
|
|||
palette->entry_count = total_colors;
|
||||
palette->first_color = 0;
|
||||
palette->last_color = total_colors - 1;
|
||||
palette->colors = (u32*)malloc(total_colors * sizeof(u32));
|
||||
palette->colors = (u32*)pxl8_malloc(total_colors * sizeof(u32));
|
||||
if (!palette->colors) {
|
||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
|
@ -97,7 +98,7 @@ static pxl8_result parse_layer_chunk(pxl8_stream* stream, pxl8_ase_layer* layer)
|
|||
|
||||
u16 name_len = pxl8_read_u16(stream);
|
||||
if (name_len > 0) {
|
||||
layer->name = (char*)malloc(name_len + 1);
|
||||
layer->name = (char*)pxl8_malloc(name_len + 1);
|
||||
if (!layer->name) return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
pxl8_read_bytes(stream, layer->name, name_len);
|
||||
layer->name[name_len] = '\0';
|
||||
|
|
@ -120,7 +121,7 @@ static pxl8_result parse_palette_chunk(pxl8_stream* stream, pxl8_ase_palette* pa
|
|||
return PXL8_ERROR_ASE_MALFORMED_CHUNK;
|
||||
}
|
||||
|
||||
palette->colors = (u32*)malloc(color_count * sizeof(u32));
|
||||
palette->colors = (u32*)pxl8_malloc(color_count * sizeof(u32));
|
||||
if (!palette->colors) {
|
||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
|
@ -154,7 +155,7 @@ static pxl8_result parse_user_data_chunk(pxl8_stream* stream, pxl8_ase_user_data
|
|||
if (user_data->has_text) {
|
||||
u16 text_len = pxl8_read_u16(stream);
|
||||
if (text_len > 0) {
|
||||
user_data->text = (char*)malloc(text_len + 1);
|
||||
user_data->text = (char*)pxl8_malloc(text_len + 1);
|
||||
if (!user_data->text) return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
pxl8_read_bytes(stream, user_data->text, text_len);
|
||||
user_data->text[text_len] = '\0';
|
||||
|
|
@ -174,14 +175,14 @@ static pxl8_result parse_user_data_chunk(pxl8_stream* stream, pxl8_ase_user_data
|
|||
u32 num_properties = pxl8_read_u32(stream);
|
||||
|
||||
if (num_properties > 0) {
|
||||
user_data->properties = (pxl8_ase_property*)calloc(num_properties, sizeof(pxl8_ase_property));
|
||||
user_data->properties = (pxl8_ase_property*)pxl8_calloc(num_properties, sizeof(pxl8_ase_property));
|
||||
if (!user_data->properties) return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
user_data->property_count = num_properties;
|
||||
|
||||
for (u32 i = 0; i < num_properties; i++) {
|
||||
u16 name_len = pxl8_read_u16(stream);
|
||||
if (name_len > 0) {
|
||||
user_data->properties[i].name = (char*)malloc(name_len + 1);
|
||||
user_data->properties[i].name = (char*)pxl8_malloc(name_len + 1);
|
||||
if (!user_data->properties[i].name) return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
pxl8_read_bytes(stream, user_data->properties[i].name, name_len);
|
||||
user_data->properties[i].name[name_len] = '\0';
|
||||
|
|
@ -207,7 +208,7 @@ static pxl8_result parse_user_data_chunk(pxl8_stream* stream, pxl8_ase_user_data
|
|||
case 8: {
|
||||
u16 str_len = pxl8_read_u16(stream);
|
||||
if (str_len > 0) {
|
||||
user_data->properties[i].string_val = (char*)malloc(str_len + 1);
|
||||
user_data->properties[i].string_val = (char*)pxl8_malloc(str_len + 1);
|
||||
if (!user_data->properties[i].string_val) return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
pxl8_read_bytes(stream, user_data->properties[i].string_val, str_len);
|
||||
user_data->properties[i].string_val[str_len] = '\0';
|
||||
|
|
@ -236,7 +237,7 @@ static pxl8_result parse_tileset_chunk(pxl8_stream* stream, pxl8_ase_tileset* ti
|
|||
|
||||
u16 name_len = pxl8_read_u16(stream);
|
||||
if (name_len > 0) {
|
||||
tileset->name = (char*)malloc(name_len + 1);
|
||||
tileset->name = (char*)pxl8_malloc(name_len + 1);
|
||||
if (!tileset->name) return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
pxl8_read_bytes(stream, tileset->name, name_len);
|
||||
tileset->name[name_len] = '\0';
|
||||
|
|
@ -249,7 +250,7 @@ static pxl8_result parse_tileset_chunk(pxl8_stream* stream, pxl8_ase_tileset* ti
|
|||
if (tileset->flags & 2) {
|
||||
u32 compressed_size = pxl8_read_u32(stream);
|
||||
tileset->pixels_size = tileset->tile_width * tileset->tile_height * tileset->tile_count;
|
||||
tileset->pixels = (u8*)malloc(tileset->pixels_size);
|
||||
tileset->pixels = (u8*)pxl8_malloc(tileset->pixels_size);
|
||||
if (!tileset->pixels) return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
const u8* compressed_data = pxl8_read_ptr(stream, compressed_size);
|
||||
|
|
@ -257,13 +258,13 @@ static pxl8_result parse_tileset_chunk(pxl8_stream* stream, pxl8_ase_tileset* ti
|
|||
i32 result = mz_uncompress(tileset->pixels, &dest_len, compressed_data, compressed_size);
|
||||
if (result != MZ_OK) {
|
||||
pxl8_error("Failed to decompress tileset data: miniz error %d", result);
|
||||
free(tileset->pixels);
|
||||
pxl8_free(tileset->pixels);
|
||||
tileset->pixels = NULL;
|
||||
return PXL8_ERROR_ASE_MALFORMED_CHUNK;
|
||||
}
|
||||
}
|
||||
|
||||
tileset->tile_user_data = (pxl8_ase_user_data*)calloc(tileset->tile_count, sizeof(pxl8_ase_user_data));
|
||||
tileset->tile_user_data = (pxl8_ase_user_data*)pxl8_calloc(tileset->tile_count, sizeof(pxl8_ase_user_data));
|
||||
if (!tileset->tile_user_data) return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
return PXL8_OK;
|
||||
|
|
@ -288,7 +289,7 @@ static pxl8_result parse_cel_chunk(pxl8_stream* stream, u32 chunk_size, pxl8_ase
|
|||
u32 pixels_size = cel->image.width * cel->image.height;
|
||||
u32 compressed_size = chunk_size - 20;
|
||||
|
||||
cel->image.pixels = (u8*)malloc(pixels_size);
|
||||
cel->image.pixels = (u8*)pxl8_malloc(pixels_size);
|
||||
if (!cel->image.pixels) return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
const u8* compressed_data = pxl8_read_ptr(stream, compressed_size);
|
||||
|
|
@ -296,7 +297,7 @@ static pxl8_result parse_cel_chunk(pxl8_stream* stream, u32 chunk_size, pxl8_ase
|
|||
i32 result = mz_uncompress(cel->image.pixels, &dest_len, compressed_data, compressed_size);
|
||||
if (result != MZ_OK) {
|
||||
pxl8_error("Failed to decompress cel data: miniz error %d", result);
|
||||
free(cel->image.pixels);
|
||||
pxl8_free(cel->image.pixels);
|
||||
cel->image.pixels = NULL;
|
||||
return PXL8_ERROR_ASE_MALFORMED_CHUNK;
|
||||
}
|
||||
|
|
@ -321,7 +322,7 @@ static pxl8_result parse_cel_chunk(pxl8_stream* stream, u32 chunk_size, pxl8_ase
|
|||
u32 uncompressed_size = tile_count * bytes_per_tile;
|
||||
u32 compressed_size = chunk_size - 36;
|
||||
|
||||
u8* temp_buffer = (u8*)malloc(uncompressed_size);
|
||||
u8* temp_buffer = (u8*)pxl8_malloc(uncompressed_size);
|
||||
if (!temp_buffer) return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
const u8* compressed_data = pxl8_read_ptr(stream, compressed_size);
|
||||
|
|
@ -329,13 +330,13 @@ static pxl8_result parse_cel_chunk(pxl8_stream* stream, u32 chunk_size, pxl8_ase
|
|||
i32 result = mz_uncompress(temp_buffer, &dest_len, compressed_data, compressed_size);
|
||||
if (result != MZ_OK) {
|
||||
pxl8_error("Failed to decompress tilemap data: miniz error %d", result);
|
||||
free(temp_buffer);
|
||||
pxl8_free(temp_buffer);
|
||||
return PXL8_ERROR_ASE_MALFORMED_CHUNK;
|
||||
}
|
||||
|
||||
cel->tilemap.tiles = (u32*)calloc(tile_count, sizeof(u32));
|
||||
cel->tilemap.tiles = (u32*)pxl8_calloc(tile_count, sizeof(u32));
|
||||
if (!cel->tilemap.tiles) {
|
||||
free(temp_buffer);
|
||||
pxl8_free(temp_buffer);
|
||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
|
|
@ -353,7 +354,7 @@ static pxl8_result parse_cel_chunk(pxl8_stream* stream, u32 chunk_size, pxl8_ase
|
|||
}
|
||||
}
|
||||
|
||||
free(temp_buffer);
|
||||
pxl8_free(temp_buffer);
|
||||
}
|
||||
|
||||
return PXL8_OK;
|
||||
|
|
@ -367,7 +368,7 @@ pxl8_result pxl8_ase_load(const char* filepath, pxl8_ase_file* ase_file) {
|
|||
memset(ase_file, 0, sizeof(pxl8_ase_file));
|
||||
|
||||
u8* file_data;
|
||||
size_t file_size;
|
||||
usize file_size;
|
||||
pxl8_result result = pxl8_io_read_binary_file(filepath, &file_data, &file_size);
|
||||
if (result != PXL8_OK) {
|
||||
return result;
|
||||
|
|
@ -387,7 +388,7 @@ pxl8_result pxl8_ase_load(const char* filepath, pxl8_ase_file* ase_file) {
|
|||
}
|
||||
|
||||
ase_file->frame_count = ase_file->header.frames;
|
||||
ase_file->frames = (pxl8_ase_frame*)calloc(ase_file->frame_count, sizeof(pxl8_ase_frame));
|
||||
ase_file->frames = (pxl8_ase_frame*)pxl8_calloc(ase_file->frame_count, sizeof(pxl8_ase_frame));
|
||||
if (!ase_file->frames) {
|
||||
pxl8_io_free_binary_data(file_data);
|
||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
|
|
@ -425,7 +426,7 @@ pxl8_result pxl8_ase_load(const char* filepath, pxl8_ase_file* ase_file) {
|
|||
frame->duration = frame_header.duration;
|
||||
|
||||
u32 pixel_count = frame->width * frame->height;
|
||||
frame->pixels = (u8*)calloc(pixel_count, sizeof(u8));
|
||||
frame->pixels = (u8*)pxl8_calloc(pixel_count, sizeof(u8));
|
||||
if (!frame->pixels) {
|
||||
result = PXL8_ERROR_OUT_OF_MEMORY;
|
||||
break;
|
||||
|
|
@ -457,7 +458,7 @@ pxl8_result pxl8_ase_load(const char* filepath, pxl8_ase_file* ase_file) {
|
|||
|
||||
case PXL8_ASE_CHUNK_LAYER: {
|
||||
pxl8_ase_layer* new_layers =
|
||||
(pxl8_ase_layer*)realloc(ase_file->layers,
|
||||
(pxl8_ase_layer*)pxl8_realloc(ase_file->layers,
|
||||
(ase_file->layer_count + 1) * sizeof(pxl8_ase_layer));
|
||||
if (!new_layers) {
|
||||
result = PXL8_ERROR_OUT_OF_MEMORY;
|
||||
|
|
@ -476,7 +477,7 @@ pxl8_result pxl8_ase_load(const char* filepath, pxl8_ase_file* ase_file) {
|
|||
pxl8_ase_cel cel = {0};
|
||||
result = parse_cel_chunk(&stream, chunk_header.chunk_size - 6, &cel);
|
||||
if (result == PXL8_OK) {
|
||||
pxl8_ase_cel* new_cels = (pxl8_ase_cel*)realloc(frame->cels, (frame->cel_count + 1) * sizeof(pxl8_ase_cel));
|
||||
pxl8_ase_cel* new_cels = (pxl8_ase_cel*)pxl8_realloc(frame->cels, (frame->cel_count + 1) * sizeof(pxl8_ase_cel));
|
||||
if (!new_cels) {
|
||||
result = PXL8_ERROR_OUT_OF_MEMORY;
|
||||
break;
|
||||
|
|
@ -515,14 +516,14 @@ pxl8_result pxl8_ase_load(const char* filepath, pxl8_ase_file* ase_file) {
|
|||
|
||||
case PXL8_ASE_CHUNK_PALETTE:
|
||||
if (ase_file->palette.colors) {
|
||||
free(ase_file->palette.colors);
|
||||
pxl8_free(ase_file->palette.colors);
|
||||
ase_file->palette.colors = NULL;
|
||||
}
|
||||
result = parse_palette_chunk(&stream, &ase_file->palette);
|
||||
break;
|
||||
|
||||
case PXL8_ASE_CHUNK_TILESET: {
|
||||
pxl8_ase_tileset* new_tilesets = (pxl8_ase_tileset*)realloc(ase_file->tilesets,
|
||||
pxl8_ase_tileset* new_tilesets = (pxl8_ase_tileset*)pxl8_realloc(ase_file->tilesets,
|
||||
(ase_file->tileset_count + 1) * sizeof(pxl8_ase_tileset));
|
||||
if (!new_tilesets) {
|
||||
result = PXL8_ERROR_OUT_OF_MEMORY;
|
||||
|
|
@ -579,57 +580,57 @@ void pxl8_ase_destroy(pxl8_ase_file* ase_file) {
|
|||
|
||||
if (ase_file->frames) {
|
||||
for (u32 i = 0; i < ase_file->frame_count; i++) {
|
||||
if (ase_file->frames[i].pixels) free(ase_file->frames[i].pixels);
|
||||
if (ase_file->frames[i].pixels) pxl8_free(ase_file->frames[i].pixels);
|
||||
if (ase_file->frames[i].cels) {
|
||||
for (u32 j = 0; j < ase_file->frames[i].cel_count; j++) {
|
||||
pxl8_ase_cel* cel = &ase_file->frames[i].cels[j];
|
||||
if (cel->cel_type == 2 && cel->image.pixels) {
|
||||
free(cel->image.pixels);
|
||||
pxl8_free(cel->image.pixels);
|
||||
} else if (cel->cel_type == 3 && cel->tilemap.tiles) {
|
||||
free(cel->tilemap.tiles);
|
||||
pxl8_free(cel->tilemap.tiles);
|
||||
}
|
||||
}
|
||||
free(ase_file->frames[i].cels);
|
||||
pxl8_free(ase_file->frames[i].cels);
|
||||
}
|
||||
}
|
||||
free(ase_file->frames);
|
||||
pxl8_free(ase_file->frames);
|
||||
}
|
||||
|
||||
if (ase_file->palette.colors) {
|
||||
free(ase_file->palette.colors);
|
||||
pxl8_free(ase_file->palette.colors);
|
||||
}
|
||||
|
||||
if (ase_file->layers) {
|
||||
for (u32 i = 0; i < ase_file->layer_count; i++) {
|
||||
if (ase_file->layers[i].name) {
|
||||
free(ase_file->layers[i].name);
|
||||
pxl8_free(ase_file->layers[i].name);
|
||||
}
|
||||
}
|
||||
free(ase_file->layers);
|
||||
pxl8_free(ase_file->layers);
|
||||
}
|
||||
|
||||
if (ase_file->tilesets) {
|
||||
for (u32 i = 0; i < ase_file->tileset_count; i++) {
|
||||
if (ase_file->tilesets[i].name) free(ase_file->tilesets[i].name);
|
||||
if (ase_file->tilesets[i].pixels) free(ase_file->tilesets[i].pixels);
|
||||
if (ase_file->tilesets[i].name) pxl8_free(ase_file->tilesets[i].name);
|
||||
if (ase_file->tilesets[i].pixels) pxl8_free(ase_file->tilesets[i].pixels);
|
||||
if (ase_file->tilesets[i].tile_user_data) {
|
||||
for (u32 j = 0; j < ase_file->tilesets[i].tile_count; j++) {
|
||||
pxl8_ase_user_data* ud = &ase_file->tilesets[i].tile_user_data[j];
|
||||
if (ud->text) free(ud->text);
|
||||
if (ud->text) pxl8_free(ud->text);
|
||||
if (ud->properties) {
|
||||
for (u32 k = 0; k < ud->property_count; k++) {
|
||||
if (ud->properties[k].name) free(ud->properties[k].name);
|
||||
if (ud->properties[k].name) pxl8_free(ud->properties[k].name);
|
||||
if (ud->properties[k].type == 8 && ud->properties[k].string_val) {
|
||||
free(ud->properties[k].string_val);
|
||||
pxl8_free(ud->properties[k].string_val);
|
||||
}
|
||||
}
|
||||
free(ud->properties);
|
||||
pxl8_free(ud->properties);
|
||||
}
|
||||
}
|
||||
free(ase_file->tilesets[i].tile_user_data);
|
||||
pxl8_free(ase_file->tilesets[i].tile_user_data);
|
||||
}
|
||||
}
|
||||
free(ase_file->tilesets);
|
||||
pxl8_free(ase_file->tilesets);
|
||||
}
|
||||
|
||||
memset(ase_file, 0, sizeof(pxl8_ase_file));
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include <unistd.h>
|
||||
|
||||
#include "pxl8_log.h"
|
||||
#include "pxl8_mem.h"
|
||||
|
||||
#define PXL8_CART_MAGIC 0x43585850
|
||||
#define PXL8_CART_VERSION 1
|
||||
|
|
@ -62,7 +63,7 @@ static bool is_directory(const char* path) {
|
|||
}
|
||||
|
||||
static bool is_pxc_file(const char* path) {
|
||||
size_t len = strlen(path);
|
||||
usize len = strlen(path);
|
||||
return len > 4 && strcmp(path + len - 4, ".pxc") == 0;
|
||||
}
|
||||
|
||||
|
|
@ -97,7 +98,7 @@ static void collect_files_recursive(const char* dir_path, const char* prefix,
|
|||
} else {
|
||||
if (*count >= *capacity) {
|
||||
*capacity = (*capacity == 0) ? 64 : (*capacity * 2);
|
||||
*paths = realloc(*paths, *capacity * sizeof(char*));
|
||||
*paths = pxl8_realloc(*paths, *capacity * sizeof(char*));
|
||||
}
|
||||
(*paths)[(*count)++] = strdup(rel_path);
|
||||
}
|
||||
|
|
@ -123,13 +124,13 @@ static pxl8_result load_packed_cart(pxl8_cart* cart, const u8* data, u32 size) {
|
|||
if (header->magic != PXL8_CART_MAGIC) return PXL8_ERROR_INVALID_FORMAT;
|
||||
if (header->version > PXL8_CART_VERSION) return PXL8_ERROR_INVALID_FORMAT;
|
||||
|
||||
cart->data = malloc(size);
|
||||
cart->data = pxl8_malloc(size);
|
||||
if (!cart->data) return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
memcpy(cart->data, data, size);
|
||||
cart->data_size = size;
|
||||
|
||||
cart->file_count = header->file_count;
|
||||
cart->files = calloc(cart->file_count, sizeof(pxl8_cart_file));
|
||||
cart->files = pxl8_calloc(cart->file_count, sizeof(pxl8_cart_file));
|
||||
if (!cart->files) return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
const u8* toc = cart->data + sizeof(pxl8_cart_header);
|
||||
|
|
@ -137,7 +138,7 @@ static pxl8_result load_packed_cart(pxl8_cart* cart, const u8* data, u32 size) {
|
|||
const pxl8_cart_entry* entry = (const pxl8_cart_entry*)toc;
|
||||
toc += sizeof(pxl8_cart_entry);
|
||||
|
||||
cart->files[i].path = malloc(entry->path_len + 1);
|
||||
cart->files[i].path = pxl8_malloc(entry->path_len + 1);
|
||||
memcpy(cart->files[i].path, toc, entry->path_len);
|
||||
cart->files[i].path[entry->path_len] = '\0';
|
||||
toc += entry->path_len;
|
||||
|
|
@ -151,7 +152,7 @@ static pxl8_result load_packed_cart(pxl8_cart* cart, const u8* data, u32 size) {
|
|||
}
|
||||
|
||||
pxl8_cart* pxl8_cart_create(void) {
|
||||
pxl8_cart* cart = calloc(1, sizeof(pxl8_cart));
|
||||
pxl8_cart* cart = pxl8_calloc(1, sizeof(pxl8_cart));
|
||||
if (cart) {
|
||||
cart->resolution = PXL8_RESOLUTION_640x360;
|
||||
cart->window_size = (pxl8_size){1280, 720};
|
||||
|
|
@ -167,7 +168,7 @@ pxl8_cart* pxl8_get_cart(void) {
|
|||
void pxl8_cart_destroy(pxl8_cart* cart) {
|
||||
if (!cart) return;
|
||||
pxl8_cart_unload(cart);
|
||||
free(cart);
|
||||
pxl8_free(cart);
|
||||
}
|
||||
|
||||
pxl8_result pxl8_cart_load(pxl8_cart* cart, const char* path) {
|
||||
|
|
@ -202,16 +203,16 @@ pxl8_result pxl8_cart_load(pxl8_cart* cart, const char* path) {
|
|||
u32 size = (u32)ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
u8* data = malloc(size);
|
||||
u8* data = pxl8_malloc(size);
|
||||
if (!data || fread(data, 1, size, file) != size) {
|
||||
free(data);
|
||||
pxl8_free(data);
|
||||
fclose(file);
|
||||
return PXL8_ERROR_SYSTEM_FAILURE;
|
||||
}
|
||||
fclose(file);
|
||||
|
||||
pxl8_result result = load_packed_cart(cart, data, size);
|
||||
free(data);
|
||||
pxl8_free(data);
|
||||
|
||||
if (result == PXL8_OK) {
|
||||
pxl8_info("Loaded cart");
|
||||
|
|
@ -242,16 +243,16 @@ pxl8_result pxl8_cart_load_embedded(pxl8_cart* cart, const char* exe_path) {
|
|||
}
|
||||
|
||||
fseek(file, trailer.cart_offset, SEEK_SET);
|
||||
u8* data = malloc(trailer.cart_size);
|
||||
u8* data = pxl8_malloc(trailer.cart_size);
|
||||
if (!data || fread(data, 1, trailer.cart_size, file) != trailer.cart_size) {
|
||||
free(data);
|
||||
pxl8_free(data);
|
||||
fclose(file);
|
||||
return PXL8_ERROR_SYSTEM_FAILURE;
|
||||
}
|
||||
fclose(file);
|
||||
|
||||
pxl8_result result = load_packed_cart(cart, data, trailer.cart_size);
|
||||
free(data);
|
||||
pxl8_free(data);
|
||||
|
||||
if (result == PXL8_OK) {
|
||||
pxl8_info("Loaded embedded cart");
|
||||
|
|
@ -265,7 +266,7 @@ void pxl8_cart_unload(pxl8_cart* cart) {
|
|||
if (cart->title) {
|
||||
pxl8_info("Unloaded cart: %s", cart->title);
|
||||
pxl8_cart_unmount(cart);
|
||||
free(cart->title);
|
||||
pxl8_free(cart->title);
|
||||
cart->title = NULL;
|
||||
} else if (cart->base_path || cart->data) {
|
||||
pxl8_info("Unloaded cart");
|
||||
|
|
@ -274,18 +275,18 @@ void pxl8_cart_unload(pxl8_cart* cart) {
|
|||
|
||||
if (cart->files) {
|
||||
for (u32 i = 0; i < cart->file_count; i++) {
|
||||
free(cart->files[i].path);
|
||||
pxl8_free(cart->files[i].path);
|
||||
}
|
||||
free(cart->files);
|
||||
pxl8_free(cart->files);
|
||||
cart->files = NULL;
|
||||
}
|
||||
cart->file_count = 0;
|
||||
|
||||
free(cart->data);
|
||||
pxl8_free(cart->data);
|
||||
cart->data = NULL;
|
||||
cart->data_size = 0;
|
||||
|
||||
free(cart->base_path);
|
||||
pxl8_free(cart->base_path);
|
||||
cart->base_path = NULL;
|
||||
cart->is_folder = false;
|
||||
}
|
||||
|
|
@ -302,7 +303,7 @@ pxl8_result pxl8_cart_mount(pxl8_cart* cart) {
|
|||
pxl8_original_cwd = getcwd(NULL, 0);
|
||||
if (chdir(cart->base_path) != 0) {
|
||||
pxl8_error("Failed to change to cart directory: %s", cart->base_path);
|
||||
free(pxl8_original_cwd);
|
||||
pxl8_free(pxl8_original_cwd);
|
||||
pxl8_original_cwd = NULL;
|
||||
return PXL8_ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
|
@ -323,7 +324,7 @@ void pxl8_cart_unmount(pxl8_cart* cart) {
|
|||
|
||||
if (pxl8_original_cwd) {
|
||||
chdir(pxl8_original_cwd);
|
||||
free(pxl8_original_cwd);
|
||||
pxl8_free(pxl8_original_cwd);
|
||||
pxl8_original_cwd = NULL;
|
||||
}
|
||||
|
||||
|
|
@ -343,7 +344,7 @@ const char* pxl8_cart_get_title(const pxl8_cart* cart) {
|
|||
|
||||
void pxl8_cart_set_title(pxl8_cart* cart, const char* title) {
|
||||
if (!cart || !title) return;
|
||||
free(cart->title);
|
||||
pxl8_free(cart->title);
|
||||
cart->title = strdup(title);
|
||||
}
|
||||
|
||||
|
|
@ -399,16 +400,16 @@ bool pxl8_cart_file_exists(const pxl8_cart* cart, const char* path) {
|
|||
return find_file(cart, path) != NULL;
|
||||
}
|
||||
|
||||
bool pxl8_cart_resolve_path(const pxl8_cart* cart, const char* relative_path, char* out_path, size_t out_size) {
|
||||
bool pxl8_cart_resolve_path(const pxl8_cart* cart, const char* relative_path, char* out_path, usize out_size) {
|
||||
if (!cart || !relative_path || !out_path || out_size == 0) return false;
|
||||
|
||||
if (cart->is_folder && cart->base_path) {
|
||||
i32 written = snprintf(out_path, out_size, "%s/%s", cart->base_path, relative_path);
|
||||
return written >= 0 && (size_t)written < out_size;
|
||||
return written >= 0 && (usize)written < out_size;
|
||||
}
|
||||
|
||||
i32 written = snprintf(out_path, out_size, "%s", relative_path);
|
||||
return written >= 0 && (size_t)written < out_size;
|
||||
return written >= 0 && (usize)written < out_size;
|
||||
}
|
||||
|
||||
pxl8_result pxl8_cart_read_file(const pxl8_cart* cart, const char* path, u8** data_out, u32* size_out) {
|
||||
|
|
@ -427,9 +428,9 @@ pxl8_result pxl8_cart_read_file(const pxl8_cart* cart, const char* path, u8** da
|
|||
*size_out = (u32)ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
*data_out = malloc(*size_out);
|
||||
*data_out = pxl8_malloc(*size_out);
|
||||
if (!*data_out || fread(*data_out, 1, *size_out, file) != *size_out) {
|
||||
free(*data_out);
|
||||
pxl8_free(*data_out);
|
||||
*data_out = NULL;
|
||||
fclose(file);
|
||||
return PXL8_ERROR_SYSTEM_FAILURE;
|
||||
|
|
@ -442,7 +443,7 @@ pxl8_result pxl8_cart_read_file(const pxl8_cart* cart, const char* path, u8** da
|
|||
if (!cf) return PXL8_ERROR_FILE_NOT_FOUND;
|
||||
|
||||
*size_out = cf->size;
|
||||
*data_out = malloc(cf->size);
|
||||
*data_out = pxl8_malloc(cf->size);
|
||||
if (!*data_out) return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
memcpy(*data_out, cart->data + cf->offset, cf->size);
|
||||
|
|
@ -450,7 +451,7 @@ pxl8_result pxl8_cart_read_file(const pxl8_cart* cart, const char* path, u8** da
|
|||
}
|
||||
|
||||
void pxl8_cart_free_file(u8* data) {
|
||||
free(data);
|
||||
pxl8_free(data);
|
||||
}
|
||||
|
||||
pxl8_result pxl8_cart_pack(const char* folder_path, const char* output_path) {
|
||||
|
|
@ -483,7 +484,7 @@ pxl8_result pxl8_cart_pack(const char* folder_path, const char* output_path) {
|
|||
u32 data_offset = sizeof(pxl8_cart_header) + toc_size;
|
||||
u32 total_size = data_offset;
|
||||
|
||||
u32* file_sizes = malloc(count * sizeof(u32));
|
||||
u32* file_sizes = pxl8_malloc(count * sizeof(u32));
|
||||
for (u32 i = 0; i < count; i++) {
|
||||
char full_path[1024];
|
||||
snprintf(full_path, sizeof(full_path), "%s/%s", folder_path, paths[i]);
|
||||
|
|
@ -496,11 +497,11 @@ pxl8_result pxl8_cart_pack(const char* folder_path, const char* output_path) {
|
|||
}
|
||||
}
|
||||
|
||||
u8* buffer = calloc(1, total_size);
|
||||
u8* buffer = pxl8_calloc(1, total_size);
|
||||
if (!buffer) {
|
||||
free(file_sizes);
|
||||
for (u32 i = 0; i < count; i++) free(paths[i]);
|
||||
free(paths);
|
||||
pxl8_free(file_sizes);
|
||||
for (u32 i = 0; i < count; i++) pxl8_free(paths[i]);
|
||||
pxl8_free(paths);
|
||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
|
|
@ -536,20 +537,20 @@ pxl8_result pxl8_cart_pack(const char* folder_path, const char* output_path) {
|
|||
}
|
||||
|
||||
file_offset += file_sizes[i];
|
||||
free(paths[i]);
|
||||
pxl8_free(paths[i]);
|
||||
}
|
||||
free(paths);
|
||||
free(file_sizes);
|
||||
pxl8_free(paths);
|
||||
pxl8_free(file_sizes);
|
||||
|
||||
FILE* out = fopen(output_path, "wb");
|
||||
if (!out) {
|
||||
free(buffer);
|
||||
pxl8_free(buffer);
|
||||
return PXL8_ERROR_SYSTEM_FAILURE;
|
||||
}
|
||||
|
||||
fwrite(buffer, 1, total_size, out);
|
||||
fclose(out);
|
||||
free(buffer);
|
||||
pxl8_free(buffer);
|
||||
|
||||
pxl8_info("Cart packed: %u files, %u bytes", count, total_size);
|
||||
return PXL8_OK;
|
||||
|
|
@ -573,7 +574,7 @@ pxl8_result pxl8_cart_bundle(const char* input_path, const char* output_path, co
|
|||
fseek(f, 0, SEEK_END);
|
||||
cart_size = (u32)ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
cart_data = malloc(cart_size);
|
||||
cart_data = pxl8_malloc(cart_size);
|
||||
fread(cart_data, 1, cart_size, f);
|
||||
fclose(f);
|
||||
unlink(temp_pxc);
|
||||
|
|
@ -584,7 +585,7 @@ pxl8_result pxl8_cart_bundle(const char* input_path, const char* output_path, co
|
|||
fseek(f, 0, SEEK_END);
|
||||
cart_size = (u32)ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
cart_data = malloc(cart_size);
|
||||
cart_data = pxl8_malloc(cart_size);
|
||||
fread(cart_data, 1, cart_size, f);
|
||||
fclose(f);
|
||||
free_cart = true;
|
||||
|
|
@ -594,30 +595,30 @@ pxl8_result pxl8_cart_bundle(const char* input_path, const char* output_path, co
|
|||
|
||||
FILE* exe = fopen(exe_path, "rb");
|
||||
if (!exe) {
|
||||
if (free_cart) free(cart_data);
|
||||
if (free_cart) pxl8_free(cart_data);
|
||||
return PXL8_ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
fseek(exe, 0, SEEK_END);
|
||||
u32 exe_size = (u32)ftell(exe);
|
||||
fseek(exe, 0, SEEK_SET);
|
||||
|
||||
u8* exe_data = malloc(exe_size);
|
||||
u8* exe_data = pxl8_malloc(exe_size);
|
||||
fread(exe_data, 1, exe_size, exe);
|
||||
fclose(exe);
|
||||
|
||||
FILE* out = fopen(output_path, "wb");
|
||||
if (!out) {
|
||||
free(exe_data);
|
||||
if (free_cart) free(cart_data);
|
||||
pxl8_free(exe_data);
|
||||
if (free_cart) pxl8_free(cart_data);
|
||||
return PXL8_ERROR_SYSTEM_FAILURE;
|
||||
}
|
||||
|
||||
fwrite(exe_data, 1, exe_size, out);
|
||||
free(exe_data);
|
||||
pxl8_free(exe_data);
|
||||
|
||||
u32 cart_offset = exe_size;
|
||||
fwrite(cart_data, 1, cart_size, out);
|
||||
if (free_cart) free(cart_data);
|
||||
if (free_cart) pxl8_free(cart_data);
|
||||
|
||||
pxl8_cart_trailer trailer = {
|
||||
.magic = PXL8_CART_TRAILER_MAGIC,
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ bool pxl8_cart_is_packed(const pxl8_cart* cart);
|
|||
bool pxl8_cart_has_embedded(const char* exe_path);
|
||||
|
||||
bool pxl8_cart_file_exists(const pxl8_cart* cart, const char* path);
|
||||
bool pxl8_cart_resolve_path(const pxl8_cart* cart, const char* relative_path, char* out_path, size_t out_size);
|
||||
bool pxl8_cart_resolve_path(const pxl8_cart* cart, const char* relative_path, char* out_path, usize out_size);
|
||||
pxl8_result pxl8_cart_read_file(const pxl8_cart* cart, const char* path, u8** data_out, u32* size_out);
|
||||
void pxl8_cart_free_file(u8* data);
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#endif
|
||||
|
||||
#include "pxl8_log.h"
|
||||
#include "pxl8_mem.h"
|
||||
|
||||
typedef struct {
|
||||
u32 magic;
|
||||
|
|
@ -40,7 +41,7 @@ static u32 pxl8_save_checksum(const u8* data, u32 size) {
|
|||
return hash;
|
||||
}
|
||||
|
||||
static void pxl8_save_get_slot_path(pxl8_save* save, u8 slot, char* path, size_t path_size) {
|
||||
static void pxl8_save_get_slot_path(pxl8_save* save, u8 slot, char* path, usize path_size) {
|
||||
if (slot == PXL8_SAVE_HOTRELOAD_SLOT) {
|
||||
snprintf(path, path_size, "%s%chotreload.sav", save->directory, PATH_SEP);
|
||||
} else {
|
||||
|
|
@ -68,7 +69,7 @@ static pxl8_result pxl8_save_ensure_directory(const char* path) {
|
|||
pxl8_save* pxl8_save_create(const char* game_name, u32 magic, u32 version) {
|
||||
if (!game_name) return NULL;
|
||||
|
||||
pxl8_save* save = (pxl8_save*)calloc(1, sizeof(pxl8_save));
|
||||
pxl8_save* save = (pxl8_save*)pxl8_calloc(1, sizeof(pxl8_save));
|
||||
if (!save) return NULL;
|
||||
|
||||
save->magic = magic;
|
||||
|
|
@ -81,7 +82,7 @@ pxl8_save* pxl8_save_create(const char* game_name, u32 magic, u32 version) {
|
|||
snprintf(save->directory, sizeof(save->directory),
|
||||
"%s%cpxl8%c%s", base_dir, PATH_SEP, PATH_SEP, game_name);
|
||||
} else {
|
||||
free(save);
|
||||
pxl8_free(save);
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
|
|
@ -91,7 +92,7 @@ pxl8_save* pxl8_save_create(const char* game_name, u32 magic, u32 version) {
|
|||
if (pw) home = pw->pw_dir;
|
||||
}
|
||||
if (!home) {
|
||||
free(save);
|
||||
pxl8_free(save);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -106,7 +107,7 @@ pxl8_save* pxl8_save_create(const char* game_name, u32 magic, u32 version) {
|
|||
#endif
|
||||
|
||||
if (pxl8_save_ensure_directory(save->directory) != PXL8_OK) {
|
||||
free(save);
|
||||
pxl8_free(save);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -116,7 +117,7 @@ pxl8_save* pxl8_save_create(const char* game_name, u32 magic, u32 version) {
|
|||
|
||||
void pxl8_save_destroy(pxl8_save* save) {
|
||||
if (save) {
|
||||
free(save);
|
||||
pxl8_free(save);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -199,14 +200,14 @@ pxl8_result pxl8_save_read(pxl8_save* save, u8 slot, u8** data_out, u32* size_ou
|
|||
return PXL8_ERROR_INVALID_FORMAT;
|
||||
}
|
||||
|
||||
u8* data = (u8*)malloc(header.size);
|
||||
u8* data = (u8*)pxl8_malloc(header.size);
|
||||
if (!data) {
|
||||
fclose(file);
|
||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (fread(data, 1, header.size, file) != header.size) {
|
||||
free(data);
|
||||
pxl8_free(data);
|
||||
fclose(file);
|
||||
return PXL8_ERROR_SYSTEM_FAILURE;
|
||||
}
|
||||
|
|
@ -215,7 +216,7 @@ pxl8_result pxl8_save_read(pxl8_save* save, u8 slot, u8** data_out, u32* size_ou
|
|||
|
||||
u32 checksum = pxl8_save_checksum(data, header.size);
|
||||
if (checksum != header.checksum) {
|
||||
free(data);
|
||||
pxl8_free(data);
|
||||
pxl8_error("Save file checksum mismatch");
|
||||
return PXL8_ERROR_INVALID_FORMAT;
|
||||
}
|
||||
|
|
@ -234,7 +235,7 @@ pxl8_result pxl8_save_read(pxl8_save* save, u8 slot, u8** data_out, u32* size_ou
|
|||
|
||||
void pxl8_save_free(u8* data) {
|
||||
if (data) {
|
||||
free(data);
|
||||
pxl8_free(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
|
@ -14,6 +13,7 @@
|
|||
#include "pxl8_hal.h"
|
||||
#include "pxl8_log.h"
|
||||
#include "pxl8_macros.h"
|
||||
#include "pxl8_mem.h"
|
||||
#include "pxl8_repl.h"
|
||||
#include "pxl8_replay.h"
|
||||
#include "pxl8_script.h"
|
||||
|
|
@ -39,23 +39,23 @@ static void pxl8_audio_event_callback(u8 event_type, u8 context_id, u8 note, f32
|
|||
#endif
|
||||
|
||||
pxl8* pxl8_create(const pxl8_hal* hal) {
|
||||
pxl8* sys = (pxl8*)calloc(1, sizeof(pxl8));
|
||||
pxl8* sys = (pxl8*)pxl8_calloc(1, sizeof(pxl8));
|
||||
if (!sys) return NULL;
|
||||
|
||||
pxl8_log_init(&sys->log);
|
||||
|
||||
if (!hal) {
|
||||
pxl8_error("hal cannot be null");
|
||||
free(sys);
|
||||
pxl8_free(sys);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sys->hal = hal;
|
||||
|
||||
sys->game = (pxl8_game*)calloc(1, sizeof(pxl8_game));
|
||||
sys->game = (pxl8_game*)pxl8_calloc(1, sizeof(pxl8_game));
|
||||
if (!sys->game) {
|
||||
pxl8_error("failed to allocate game");
|
||||
free(sys);
|
||||
pxl8_free(sys);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -65,11 +65,11 @@ pxl8* pxl8_create(const pxl8_hal* hal) {
|
|||
void pxl8_destroy(pxl8* sys) {
|
||||
if (!sys) return;
|
||||
|
||||
if (sys->game) free(sys->game);
|
||||
if (sys->game) pxl8_free(sys->game);
|
||||
if (sys->cart) pxl8_cart_destroy(sys->cart);
|
||||
if (sys->hal && sys->platform_data) sys->hal->destroy(sys->platform_data);
|
||||
|
||||
free(sys);
|
||||
pxl8_free(sys);
|
||||
}
|
||||
|
||||
static void pxl8_print_help(void) {
|
||||
|
|
@ -171,7 +171,7 @@ pxl8_result pxl8_init(pxl8* sys, i32 argc, char* argv[]) {
|
|||
cart_path = ".";
|
||||
} else {
|
||||
pxl8_error("no main.fnl or main.lua found in current directory");
|
||||
free(original_cwd);
|
||||
pxl8_free(original_cwd);
|
||||
return PXL8_ERROR_INITIALIZATION_FAILED;
|
||||
}
|
||||
}
|
||||
|
|
@ -190,7 +190,7 @@ pxl8_result pxl8_init(pxl8* sys, i32 argc, char* argv[]) {
|
|||
pxl8_error("failed to load cart%s%s", load_from_path ? ": " : "", load_from_path ? cart_path : "");
|
||||
if (sys->cart) pxl8_cart_destroy(sys->cart);
|
||||
sys->cart = NULL;
|
||||
free(original_cwd);
|
||||
pxl8_free(original_cwd);
|
||||
return PXL8_ERROR_INITIALIZATION_FAILED;
|
||||
}
|
||||
|
||||
|
|
@ -203,7 +203,7 @@ pxl8_result pxl8_init(pxl8* sys, i32 argc, char* argv[]) {
|
|||
} else if (script_arg) {
|
||||
pxl8_strncpy(game->script_path, script_arg, sizeof(game->script_path));
|
||||
}
|
||||
free(original_cwd);
|
||||
pxl8_free(original_cwd);
|
||||
|
||||
const char* window_title = pxl8_cart_get_title(sys->cart);
|
||||
if (!window_title) window_title = "pxl8";
|
||||
|
|
@ -310,7 +310,7 @@ pxl8_result pxl8_update(pxl8* sys) {
|
|||
|
||||
if (pxl8_script_load_module(game->script, "pxl8") != PXL8_OK) {
|
||||
const char* err_msg = pxl8_script_get_last_error(game->script);
|
||||
pxl8_error("failed to setup pxl8 global: %s", err_msg);
|
||||
pxl8_error("failed to load pxl8 global: %s", err_msg);
|
||||
}
|
||||
|
||||
sys->repl = pxl8_repl_create();
|
||||
|
|
|
|||
|
|
@ -1,35 +1,35 @@
|
|||
#include "pxl8_bytes.h"
|
||||
#include <string.h>
|
||||
|
||||
void pxl8_pack_u8(u8* buf, size_t offset, u8 val) {
|
||||
void pxl8_pack_u8(u8* buf, usize offset, u8 val) {
|
||||
buf[offset] = val;
|
||||
}
|
||||
|
||||
void pxl8_pack_u16_le(u8* buf, size_t offset, u16 val) {
|
||||
void pxl8_pack_u16_le(u8* buf, usize offset, u16 val) {
|
||||
buf[offset] = (u8)(val);
|
||||
buf[offset + 1] = (u8)(val >> 8);
|
||||
}
|
||||
|
||||
void pxl8_pack_u16_be(u8* buf, size_t offset, u16 val) {
|
||||
void pxl8_pack_u16_be(u8* buf, usize offset, u16 val) {
|
||||
buf[offset] = (u8)(val >> 8);
|
||||
buf[offset + 1] = (u8)(val);
|
||||
}
|
||||
|
||||
void pxl8_pack_u32_le(u8* buf, size_t offset, u32 val) {
|
||||
void pxl8_pack_u32_le(u8* buf, usize offset, u32 val) {
|
||||
buf[offset] = (u8)(val);
|
||||
buf[offset + 1] = (u8)(val >> 8);
|
||||
buf[offset + 2] = (u8)(val >> 16);
|
||||
buf[offset + 3] = (u8)(val >> 24);
|
||||
}
|
||||
|
||||
void pxl8_pack_u32_be(u8* buf, size_t offset, u32 val) {
|
||||
void pxl8_pack_u32_be(u8* buf, usize offset, u32 val) {
|
||||
buf[offset] = (u8)(val >> 24);
|
||||
buf[offset + 1] = (u8)(val >> 16);
|
||||
buf[offset + 2] = (u8)(val >> 8);
|
||||
buf[offset + 3] = (u8)(val);
|
||||
}
|
||||
|
||||
void pxl8_pack_u64_le(u8* buf, size_t offset, u64 val) {
|
||||
void pxl8_pack_u64_le(u8* buf, usize offset, u64 val) {
|
||||
buf[offset] = (u8)(val);
|
||||
buf[offset + 1] = (u8)(val >> 8);
|
||||
buf[offset + 2] = (u8)(val >> 16);
|
||||
|
|
@ -40,7 +40,7 @@ void pxl8_pack_u64_le(u8* buf, size_t offset, u64 val) {
|
|||
buf[offset + 7] = (u8)(val >> 56);
|
||||
}
|
||||
|
||||
void pxl8_pack_u64_be(u8* buf, size_t offset, u64 val) {
|
||||
void pxl8_pack_u64_be(u8* buf, usize offset, u64 val) {
|
||||
buf[offset] = (u8)(val >> 56);
|
||||
buf[offset + 1] = (u8)(val >> 48);
|
||||
buf[offset + 2] = (u8)(val >> 40);
|
||||
|
|
@ -51,85 +51,85 @@ void pxl8_pack_u64_be(u8* buf, size_t offset, u64 val) {
|
|||
buf[offset + 7] = (u8)(val);
|
||||
}
|
||||
|
||||
void pxl8_pack_i8(u8* buf, size_t offset, i8 val) {
|
||||
void pxl8_pack_i8(u8* buf, usize offset, i8 val) {
|
||||
buf[offset] = (u8)val;
|
||||
}
|
||||
|
||||
void pxl8_pack_i16_le(u8* buf, size_t offset, i16 val) {
|
||||
void pxl8_pack_i16_le(u8* buf, usize offset, i16 val) {
|
||||
pxl8_pack_u16_le(buf, offset, (u16)val);
|
||||
}
|
||||
|
||||
void pxl8_pack_i16_be(u8* buf, size_t offset, i16 val) {
|
||||
void pxl8_pack_i16_be(u8* buf, usize offset, i16 val) {
|
||||
pxl8_pack_u16_be(buf, offset, (u16)val);
|
||||
}
|
||||
|
||||
void pxl8_pack_i32_le(u8* buf, size_t offset, i32 val) {
|
||||
void pxl8_pack_i32_le(u8* buf, usize offset, i32 val) {
|
||||
pxl8_pack_u32_le(buf, offset, (u32)val);
|
||||
}
|
||||
|
||||
void pxl8_pack_i32_be(u8* buf, size_t offset, i32 val) {
|
||||
void pxl8_pack_i32_be(u8* buf, usize offset, i32 val) {
|
||||
pxl8_pack_u32_be(buf, offset, (u32)val);
|
||||
}
|
||||
|
||||
void pxl8_pack_i64_le(u8* buf, size_t offset, i64 val) {
|
||||
void pxl8_pack_i64_le(u8* buf, usize offset, i64 val) {
|
||||
pxl8_pack_u64_le(buf, offset, (u64)val);
|
||||
}
|
||||
|
||||
void pxl8_pack_i64_be(u8* buf, size_t offset, i64 val) {
|
||||
void pxl8_pack_i64_be(u8* buf, usize offset, i64 val) {
|
||||
pxl8_pack_u64_be(buf, offset, (u64)val);
|
||||
}
|
||||
|
||||
void pxl8_pack_f32_le(u8* buf, size_t offset, f32 val) {
|
||||
void pxl8_pack_f32_le(u8* buf, usize offset, f32 val) {
|
||||
u32 bits;
|
||||
memcpy(&bits, &val, sizeof(bits));
|
||||
pxl8_pack_u32_le(buf, offset, bits);
|
||||
}
|
||||
|
||||
void pxl8_pack_f32_be(u8* buf, size_t offset, f32 val) {
|
||||
void pxl8_pack_f32_be(u8* buf, usize offset, f32 val) {
|
||||
u32 bits;
|
||||
memcpy(&bits, &val, sizeof(bits));
|
||||
pxl8_pack_u32_be(buf, offset, bits);
|
||||
}
|
||||
|
||||
void pxl8_pack_f64_le(u8* buf, size_t offset, f64 val) {
|
||||
void pxl8_pack_f64_le(u8* buf, usize offset, f64 val) {
|
||||
u64 bits;
|
||||
memcpy(&bits, &val, sizeof(bits));
|
||||
pxl8_pack_u64_le(buf, offset, bits);
|
||||
}
|
||||
|
||||
void pxl8_pack_f64_be(u8* buf, size_t offset, f64 val) {
|
||||
void pxl8_pack_f64_be(u8* buf, usize offset, f64 val) {
|
||||
u64 bits;
|
||||
memcpy(&bits, &val, sizeof(bits));
|
||||
pxl8_pack_u64_be(buf, offset, bits);
|
||||
}
|
||||
|
||||
u8 pxl8_unpack_u8(const u8* buf, size_t offset) {
|
||||
u8 pxl8_unpack_u8(const u8* buf, usize offset) {
|
||||
return buf[offset];
|
||||
}
|
||||
|
||||
u16 pxl8_unpack_u16_le(const u8* buf, size_t offset) {
|
||||
u16 pxl8_unpack_u16_le(const u8* buf, usize offset) {
|
||||
return (u16)buf[offset] | ((u16)buf[offset + 1] << 8);
|
||||
}
|
||||
|
||||
u16 pxl8_unpack_u16_be(const u8* buf, size_t offset) {
|
||||
u16 pxl8_unpack_u16_be(const u8* buf, usize offset) {
|
||||
return ((u16)buf[offset] << 8) | (u16)buf[offset + 1];
|
||||
}
|
||||
|
||||
u32 pxl8_unpack_u32_le(const u8* buf, size_t offset) {
|
||||
u32 pxl8_unpack_u32_le(const u8* buf, usize offset) {
|
||||
return (u32)buf[offset] |
|
||||
((u32)buf[offset + 1] << 8) |
|
||||
((u32)buf[offset + 2] << 16) |
|
||||
((u32)buf[offset + 3] << 24);
|
||||
}
|
||||
|
||||
u32 pxl8_unpack_u32_be(const u8* buf, size_t offset) {
|
||||
u32 pxl8_unpack_u32_be(const u8* buf, usize offset) {
|
||||
return ((u32)buf[offset] << 24) |
|
||||
((u32)buf[offset + 1] << 16) |
|
||||
((u32)buf[offset + 2] << 8) |
|
||||
(u32)buf[offset + 3];
|
||||
}
|
||||
|
||||
u64 pxl8_unpack_u64_le(const u8* buf, size_t offset) {
|
||||
u64 pxl8_unpack_u64_le(const u8* buf, usize offset) {
|
||||
return (u64)buf[offset] |
|
||||
((u64)buf[offset + 1] << 8) |
|
||||
((u64)buf[offset + 2] << 16) |
|
||||
|
|
@ -140,7 +140,7 @@ u64 pxl8_unpack_u64_le(const u8* buf, size_t offset) {
|
|||
((u64)buf[offset + 7] << 56);
|
||||
}
|
||||
|
||||
u64 pxl8_unpack_u64_be(const u8* buf, size_t offset) {
|
||||
u64 pxl8_unpack_u64_be(const u8* buf, usize offset) {
|
||||
return ((u64)buf[offset] << 56) |
|
||||
((u64)buf[offset + 1] << 48) |
|
||||
((u64)buf[offset + 2] << 40) |
|
||||
|
|
@ -151,56 +151,56 @@ u64 pxl8_unpack_u64_be(const u8* buf, size_t offset) {
|
|||
(u64)buf[offset + 7];
|
||||
}
|
||||
|
||||
i8 pxl8_unpack_i8(const u8* buf, size_t offset) {
|
||||
i8 pxl8_unpack_i8(const u8* buf, usize offset) {
|
||||
return (i8)buf[offset];
|
||||
}
|
||||
|
||||
i16 pxl8_unpack_i16_le(const u8* buf, size_t offset) {
|
||||
i16 pxl8_unpack_i16_le(const u8* buf, usize offset) {
|
||||
return (i16)pxl8_unpack_u16_le(buf, offset);
|
||||
}
|
||||
|
||||
i16 pxl8_unpack_i16_be(const u8* buf, size_t offset) {
|
||||
i16 pxl8_unpack_i16_be(const u8* buf, usize offset) {
|
||||
return (i16)pxl8_unpack_u16_be(buf, offset);
|
||||
}
|
||||
|
||||
i32 pxl8_unpack_i32_le(const u8* buf, size_t offset) {
|
||||
i32 pxl8_unpack_i32_le(const u8* buf, usize offset) {
|
||||
return (i32)pxl8_unpack_u32_le(buf, offset);
|
||||
}
|
||||
|
||||
i32 pxl8_unpack_i32_be(const u8* buf, size_t offset) {
|
||||
i32 pxl8_unpack_i32_be(const u8* buf, usize offset) {
|
||||
return (i32)pxl8_unpack_u32_be(buf, offset);
|
||||
}
|
||||
|
||||
i64 pxl8_unpack_i64_le(const u8* buf, size_t offset) {
|
||||
i64 pxl8_unpack_i64_le(const u8* buf, usize offset) {
|
||||
return (i64)pxl8_unpack_u64_le(buf, offset);
|
||||
}
|
||||
|
||||
i64 pxl8_unpack_i64_be(const u8* buf, size_t offset) {
|
||||
i64 pxl8_unpack_i64_be(const u8* buf, usize offset) {
|
||||
return (i64)pxl8_unpack_u64_be(buf, offset);
|
||||
}
|
||||
|
||||
f32 pxl8_unpack_f32_le(const u8* buf, size_t offset) {
|
||||
f32 pxl8_unpack_f32_le(const u8* buf, usize offset) {
|
||||
u32 bits = pxl8_unpack_u32_le(buf, offset);
|
||||
f32 result;
|
||||
memcpy(&result, &bits, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
f32 pxl8_unpack_f32_be(const u8* buf, size_t offset) {
|
||||
f32 pxl8_unpack_f32_be(const u8* buf, usize offset) {
|
||||
u32 bits = pxl8_unpack_u32_be(buf, offset);
|
||||
f32 result;
|
||||
memcpy(&result, &bits, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
f64 pxl8_unpack_f64_le(const u8* buf, size_t offset) {
|
||||
f64 pxl8_unpack_f64_le(const u8* buf, usize offset) {
|
||||
u64 bits = pxl8_unpack_u64_le(buf, offset);
|
||||
f64 result;
|
||||
memcpy(&result, &bits, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
f64 pxl8_unpack_f64_be(const u8* buf, size_t offset) {
|
||||
f64 pxl8_unpack_f64_be(const u8* buf, usize offset) {
|
||||
u64 bits = pxl8_unpack_u64_be(buf, offset);
|
||||
f64 result;
|
||||
memcpy(&result, &bits, sizeof(result));
|
||||
|
|
|
|||
|
|
@ -10,43 +10,43 @@ void pxl8_bit_set(u32* val, u8 bit);
|
|||
bool pxl8_bit_test(u32 val, u8 bit);
|
||||
void pxl8_bit_toggle(u32* val, u8 bit);
|
||||
|
||||
void pxl8_pack_u8(u8* buf, size_t offset, u8 val);
|
||||
void pxl8_pack_u16_be(u8* buf, size_t offset, u16 val);
|
||||
void pxl8_pack_u16_le(u8* buf, size_t offset, u16 val);
|
||||
void pxl8_pack_u32_be(u8* buf, size_t offset, u32 val);
|
||||
void pxl8_pack_u32_le(u8* buf, size_t offset, u32 val);
|
||||
void pxl8_pack_u64_be(u8* buf, size_t offset, u64 val);
|
||||
void pxl8_pack_u64_le(u8* buf, size_t offset, u64 val);
|
||||
void pxl8_pack_i8(u8* buf, size_t offset, i8 val);
|
||||
void pxl8_pack_i16_be(u8* buf, size_t offset, i16 val);
|
||||
void pxl8_pack_i16_le(u8* buf, size_t offset, i16 val);
|
||||
void pxl8_pack_i32_be(u8* buf, size_t offset, i32 val);
|
||||
void pxl8_pack_i32_le(u8* buf, size_t offset, i32 val);
|
||||
void pxl8_pack_i64_be(u8* buf, size_t offset, i64 val);
|
||||
void pxl8_pack_i64_le(u8* buf, size_t offset, i64 val);
|
||||
void pxl8_pack_f32_be(u8* buf, size_t offset, f32 val);
|
||||
void pxl8_pack_f32_le(u8* buf, size_t offset, f32 val);
|
||||
void pxl8_pack_f64_be(u8* buf, size_t offset, f64 val);
|
||||
void pxl8_pack_f64_le(u8* buf, size_t offset, f64 val);
|
||||
void pxl8_pack_u8(u8* buf, usize offset, u8 val);
|
||||
void pxl8_pack_u16_be(u8* buf, usize offset, u16 val);
|
||||
void pxl8_pack_u16_le(u8* buf, usize offset, u16 val);
|
||||
void pxl8_pack_u32_be(u8* buf, usize offset, u32 val);
|
||||
void pxl8_pack_u32_le(u8* buf, usize offset, u32 val);
|
||||
void pxl8_pack_u64_be(u8* buf, usize offset, u64 val);
|
||||
void pxl8_pack_u64_le(u8* buf, usize offset, u64 val);
|
||||
void pxl8_pack_i8(u8* buf, usize offset, i8 val);
|
||||
void pxl8_pack_i16_be(u8* buf, usize offset, i16 val);
|
||||
void pxl8_pack_i16_le(u8* buf, usize offset, i16 val);
|
||||
void pxl8_pack_i32_be(u8* buf, usize offset, i32 val);
|
||||
void pxl8_pack_i32_le(u8* buf, usize offset, i32 val);
|
||||
void pxl8_pack_i64_be(u8* buf, usize offset, i64 val);
|
||||
void pxl8_pack_i64_le(u8* buf, usize offset, i64 val);
|
||||
void pxl8_pack_f32_be(u8* buf, usize offset, f32 val);
|
||||
void pxl8_pack_f32_le(u8* buf, usize offset, f32 val);
|
||||
void pxl8_pack_f64_be(u8* buf, usize offset, f64 val);
|
||||
void pxl8_pack_f64_le(u8* buf, usize offset, f64 val);
|
||||
|
||||
u8 pxl8_unpack_u8(const u8* buf, size_t offset);
|
||||
u16 pxl8_unpack_u16_be(const u8* buf, size_t offset);
|
||||
u16 pxl8_unpack_u16_le(const u8* buf, size_t offset);
|
||||
u32 pxl8_unpack_u32_be(const u8* buf, size_t offset);
|
||||
u32 pxl8_unpack_u32_le(const u8* buf, size_t offset);
|
||||
u64 pxl8_unpack_u64_be(const u8* buf, size_t offset);
|
||||
u64 pxl8_unpack_u64_le(const u8* buf, size_t offset);
|
||||
i8 pxl8_unpack_i8(const u8* buf, size_t offset);
|
||||
i16 pxl8_unpack_i16_be(const u8* buf, size_t offset);
|
||||
i16 pxl8_unpack_i16_le(const u8* buf, size_t offset);
|
||||
i32 pxl8_unpack_i32_be(const u8* buf, size_t offset);
|
||||
i32 pxl8_unpack_i32_le(const u8* buf, size_t offset);
|
||||
i64 pxl8_unpack_i64_be(const u8* buf, size_t offset);
|
||||
i64 pxl8_unpack_i64_le(const u8* buf, size_t offset);
|
||||
f32 pxl8_unpack_f32_be(const u8* buf, size_t offset);
|
||||
f32 pxl8_unpack_f32_le(const u8* buf, size_t offset);
|
||||
f64 pxl8_unpack_f64_be(const u8* buf, size_t offset);
|
||||
f64 pxl8_unpack_f64_le(const u8* buf, size_t offset);
|
||||
u8 pxl8_unpack_u8(const u8* buf, usize offset);
|
||||
u16 pxl8_unpack_u16_be(const u8* buf, usize offset);
|
||||
u16 pxl8_unpack_u16_le(const u8* buf, usize offset);
|
||||
u32 pxl8_unpack_u32_be(const u8* buf, usize offset);
|
||||
u32 pxl8_unpack_u32_le(const u8* buf, usize offset);
|
||||
u64 pxl8_unpack_u64_be(const u8* buf, usize offset);
|
||||
u64 pxl8_unpack_u64_le(const u8* buf, usize offset);
|
||||
i8 pxl8_unpack_i8(const u8* buf, usize offset);
|
||||
i16 pxl8_unpack_i16_be(const u8* buf, usize offset);
|
||||
i16 pxl8_unpack_i16_le(const u8* buf, usize offset);
|
||||
i32 pxl8_unpack_i32_be(const u8* buf, usize offset);
|
||||
i32 pxl8_unpack_i32_le(const u8* buf, usize offset);
|
||||
i64 pxl8_unpack_i64_be(const u8* buf, usize offset);
|
||||
i64 pxl8_unpack_i64_le(const u8* buf, usize offset);
|
||||
f32 pxl8_unpack_f32_be(const u8* buf, usize offset);
|
||||
f32 pxl8_unpack_f32_le(const u8* buf, usize offset);
|
||||
f64 pxl8_unpack_f64_be(const u8* buf, usize offset);
|
||||
f64 pxl8_unpack_f64_le(const u8* buf, usize offset);
|
||||
|
||||
typedef struct {
|
||||
const u8* bytes;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "pxl8_io.h"
|
||||
#include "pxl8_mem.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
|
@ -10,7 +11,7 @@ static inline char pxl8_to_lower(char c) {
|
|||
return (c >= 'A' && c <= 'Z') ? c + 32 : c;
|
||||
}
|
||||
|
||||
pxl8_result pxl8_io_read_file(const char* path, char** content, size_t* size) {
|
||||
pxl8_result pxl8_io_read_file(const char* path, char** content, usize* size) {
|
||||
if (!path || !content || !size) return PXL8_ERROR_NULL_POINTER;
|
||||
|
||||
pxl8_cart* cart = pxl8_get_cart();
|
||||
|
|
@ -19,7 +20,7 @@ pxl8_result pxl8_io_read_file(const char* path, char** content, size_t* size) {
|
|||
u32 cart_size = 0;
|
||||
pxl8_result result = pxl8_cart_read_file(cart, path, &data, &cart_size);
|
||||
if (result == PXL8_OK) {
|
||||
*content = realloc(data, cart_size + 1);
|
||||
*content = pxl8_realloc(data, cart_size + 1);
|
||||
if (!*content) {
|
||||
pxl8_cart_free_file(data);
|
||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
|
|
@ -44,13 +45,13 @@ pxl8_result pxl8_io_read_file(const char* path, char** content, size_t* size) {
|
|||
return PXL8_ERROR_SYSTEM_FAILURE;
|
||||
}
|
||||
|
||||
*content = malloc(file_size + 1);
|
||||
*content = pxl8_malloc(file_size + 1);
|
||||
if (!*content) {
|
||||
fclose(file);
|
||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
size_t bytes_read = fread(*content, 1, file_size, file);
|
||||
usize bytes_read = fread(*content, 1, file_size, file);
|
||||
(*content)[bytes_read] = '\0';
|
||||
*size = bytes_read;
|
||||
|
||||
|
|
@ -58,7 +59,7 @@ pxl8_result pxl8_io_read_file(const char* path, char** content, size_t* size) {
|
|||
return PXL8_OK;
|
||||
}
|
||||
|
||||
pxl8_result pxl8_io_write_file(const char* path, const char* content, size_t size) {
|
||||
pxl8_result pxl8_io_write_file(const char* path, const char* content, usize size) {
|
||||
if (!path || !content) return PXL8_ERROR_NULL_POINTER;
|
||||
|
||||
FILE* file = fopen(path, "wb");
|
||||
|
|
@ -66,17 +67,17 @@ pxl8_result pxl8_io_write_file(const char* path, const char* content, size_t siz
|
|||
return PXL8_ERROR_SYSTEM_FAILURE;
|
||||
}
|
||||
|
||||
size_t bytes_written = fwrite(content, 1, size, file);
|
||||
usize bytes_written = fwrite(content, 1, size, file);
|
||||
fclose(file);
|
||||
|
||||
return (bytes_written == size) ? PXL8_OK : PXL8_ERROR_SYSTEM_FAILURE;
|
||||
}
|
||||
|
||||
pxl8_result pxl8_io_read_binary_file(const char* path, u8** data, size_t* size) {
|
||||
pxl8_result pxl8_io_read_binary_file(const char* path, u8** data, usize* size) {
|
||||
return pxl8_io_read_file(path, (char**)data, size);
|
||||
}
|
||||
|
||||
pxl8_result pxl8_io_write_binary_file(const char* path, const u8* data, size_t size) {
|
||||
pxl8_result pxl8_io_write_binary_file(const char* path, const u8* data, usize size) {
|
||||
return pxl8_io_write_file(path, (const char*)data, size);
|
||||
}
|
||||
|
||||
|
|
@ -112,13 +113,13 @@ pxl8_result pxl8_io_create_directory(const char* path) {
|
|||
|
||||
void pxl8_io_free_file_content(char* content) {
|
||||
if (content) {
|
||||
free(content);
|
||||
pxl8_free(content);
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_io_free_binary_data(u8* data) {
|
||||
if (data) {
|
||||
free(data);
|
||||
pxl8_free(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -144,7 +145,7 @@ static i32 pxl8_key_code(const char* key_name) {
|
|||
};
|
||||
|
||||
char lower_name[64];
|
||||
size_t i;
|
||||
usize i;
|
||||
for (i = 0; i < sizeof(lower_name) - 1 && key_name[i]; i++) {
|
||||
lower_name[i] = pxl8_to_lower(key_name[i]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,10 +15,10 @@ bool pxl8_io_file_exists(const char* path);
|
|||
void pxl8_io_free_binary_data(u8* data);
|
||||
void pxl8_io_free_file_content(char* content);
|
||||
f64 pxl8_io_get_file_modified_time(const char* path);
|
||||
pxl8_result pxl8_io_read_binary_file(const char* path, u8** data, size_t* size);
|
||||
pxl8_result pxl8_io_read_file(const char* path, char** content, size_t* size);
|
||||
pxl8_result pxl8_io_write_binary_file(const char* path, const u8* data, size_t size);
|
||||
pxl8_result pxl8_io_write_file(const char* path, const char* content, size_t size);
|
||||
pxl8_result pxl8_io_read_binary_file(const char* path, u8** data, usize* size);
|
||||
pxl8_result pxl8_io_read_file(const char* path, char** content, usize* size);
|
||||
pxl8_result pxl8_io_write_binary_file(const char* path, const u8* data, usize size);
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ void pxl8_log_set_level(pxl8_log_level level) {
|
|||
if (g_log) g_log->level = level;
|
||||
}
|
||||
|
||||
static void log_timestamp(char* buffer, size_t size) {
|
||||
static void log_timestamp(char* buffer, usize size) {
|
||||
time_t now = time(NULL);
|
||||
struct tm* tm_info = localtime(&now);
|
||||
strftime(buffer, size, "%H:%M:%S", tm_info);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "pxl8_replay.h"
|
||||
#include "pxl8_log.h"
|
||||
#include "pxl8_mem.h"
|
||||
#include "pxl8_sys.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
|
@ -42,8 +43,8 @@ struct pxl8_replay {
|
|||
static void pxl8_replay_chunk_free(pxl8_replay_chunk* chunk) {
|
||||
while (chunk) {
|
||||
pxl8_replay_chunk* next = chunk->next;
|
||||
free(chunk->data);
|
||||
free(chunk);
|
||||
pxl8_free(chunk->data);
|
||||
pxl8_free(chunk);
|
||||
chunk = next;
|
||||
}
|
||||
}
|
||||
|
|
@ -52,7 +53,7 @@ static void pxl8_replay_keyframe_entry_free(pxl8_keyframe_entry* entry) {
|
|||
while (entry) {
|
||||
pxl8_keyframe_entry* next = entry->next;
|
||||
pxl8_replay_chunk_free(entry->input_deltas);
|
||||
free(entry);
|
||||
pxl8_free(entry);
|
||||
entry = next;
|
||||
}
|
||||
}
|
||||
|
|
@ -64,7 +65,7 @@ pxl8_replay* pxl8_replay_create(const char* path, u32 keyframe_interval) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
pxl8_replay* r = calloc(1, sizeof(pxl8_replay));
|
||||
pxl8_replay* r = pxl8_calloc(1, sizeof(pxl8_replay));
|
||||
if (!r) {
|
||||
fclose(f);
|
||||
return NULL;
|
||||
|
|
@ -85,7 +86,7 @@ pxl8_replay* pxl8_replay_create(const char* path, u32 keyframe_interval) {
|
|||
}
|
||||
|
||||
pxl8_replay* pxl8_replay_create_buffer(u32 keyframe_interval, u32 max_keyframes) {
|
||||
pxl8_replay* r = calloc(1, sizeof(pxl8_replay));
|
||||
pxl8_replay* r = pxl8_calloc(1, sizeof(pxl8_replay));
|
||||
if (!r) return NULL;
|
||||
|
||||
r->recording = true;
|
||||
|
|
@ -125,7 +126,7 @@ pxl8_replay* pxl8_replay_open(const char* path) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
pxl8_replay* r = calloc(1, sizeof(pxl8_replay));
|
||||
pxl8_replay* r = pxl8_calloc(1, sizeof(pxl8_replay));
|
||||
if (!r) {
|
||||
fclose(f);
|
||||
return NULL;
|
||||
|
|
@ -146,14 +147,14 @@ pxl8_replay* pxl8_replay_open(const char* path) {
|
|||
if (fread(size_bytes, 3, 1, f) != 1) break;
|
||||
u32 size = size_bytes[0] | (size_bytes[1] << 8) | (size_bytes[2] << 16);
|
||||
|
||||
u8* data = malloc(size);
|
||||
u8* data = pxl8_malloc(size);
|
||||
if (!data || fread(data, size, 1, f) != 1) {
|
||||
free(data);
|
||||
pxl8_free(data);
|
||||
break;
|
||||
}
|
||||
|
||||
if (chunk_type == PXL8_REPLAY_CHUNK_KEYFRAME) {
|
||||
pxl8_keyframe_entry* entry = calloc(1, sizeof(pxl8_keyframe_entry));
|
||||
pxl8_keyframe_entry* entry = pxl8_calloc(1, sizeof(pxl8_keyframe_entry));
|
||||
if (entry && size >= sizeof(pxl8_keyframe)) {
|
||||
memcpy(&entry->keyframe, data, sizeof(pxl8_keyframe));
|
||||
entry->prev = r->current_keyframe;
|
||||
|
|
@ -166,10 +167,10 @@ pxl8_replay* pxl8_replay_open(const char* path) {
|
|||
}
|
||||
r->keyframe_count++;
|
||||
}
|
||||
free(data);
|
||||
pxl8_free(data);
|
||||
} else if (chunk_type == PXL8_REPLAY_CHUNK_INPUT) {
|
||||
if (r->current_keyframe) {
|
||||
pxl8_replay_chunk* chunk = calloc(1, sizeof(pxl8_replay_chunk));
|
||||
pxl8_replay_chunk* chunk = pxl8_calloc(1, sizeof(pxl8_replay_chunk));
|
||||
if (chunk) {
|
||||
chunk->type = chunk_type;
|
||||
chunk->size = size;
|
||||
|
|
@ -185,9 +186,9 @@ pxl8_replay* pxl8_replay_open(const char* path) {
|
|||
}
|
||||
}
|
||||
}
|
||||
free(data);
|
||||
pxl8_free(data);
|
||||
} else if (chunk_type == PXL8_REPLAY_CHUNK_AUDIO_EVENT) {
|
||||
pxl8_replay_chunk* chunk = calloc(1, sizeof(pxl8_replay_chunk));
|
||||
pxl8_replay_chunk* chunk = pxl8_calloc(1, sizeof(pxl8_replay_chunk));
|
||||
if (chunk) {
|
||||
chunk->type = chunk_type;
|
||||
chunk->size = size;
|
||||
|
|
@ -202,9 +203,9 @@ pxl8_replay* pxl8_replay_open(const char* path) {
|
|||
r->audio_events_tail = chunk;
|
||||
}
|
||||
}
|
||||
free(data);
|
||||
pxl8_free(data);
|
||||
} else {
|
||||
free(data);
|
||||
pxl8_free(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -231,7 +232,7 @@ void pxl8_replay_destroy(pxl8_replay* r) {
|
|||
pxl8_replay_chunk_free(r->pending_inputs);
|
||||
pxl8_replay_chunk_free(r->audio_events);
|
||||
|
||||
free(r);
|
||||
pxl8_free(r);
|
||||
}
|
||||
|
||||
bool pxl8_replay_is_recording(pxl8_replay* r) {
|
||||
|
|
@ -262,14 +263,14 @@ static void write_chunk(FILE* f, u8 type, const void* data, u32 size) {
|
|||
}
|
||||
|
||||
static void add_chunk_to_buffer(pxl8_replay* r, u8 type, const void* data, u32 size) {
|
||||
pxl8_replay_chunk* chunk = calloc(1, sizeof(pxl8_replay_chunk));
|
||||
pxl8_replay_chunk* chunk = pxl8_calloc(1, sizeof(pxl8_replay_chunk));
|
||||
if (!chunk) return;
|
||||
|
||||
chunk->type = type;
|
||||
chunk->size = size;
|
||||
chunk->data = malloc(size);
|
||||
chunk->data = pxl8_malloc(size);
|
||||
if (!chunk->data) {
|
||||
free(chunk);
|
||||
pxl8_free(chunk);
|
||||
return;
|
||||
}
|
||||
memcpy(chunk->data, data, size);
|
||||
|
|
@ -327,7 +328,7 @@ void pxl8_replay_write_keyframe(pxl8_replay* r, u32 frame, f32 time, pxl8_rng* r
|
|||
write_chunk(r->file, PXL8_REPLAY_CHUNK_KEYFRAME, &kf, sizeof(kf));
|
||||
fflush(r->file);
|
||||
} else {
|
||||
pxl8_keyframe_entry* entry = calloc(1, sizeof(pxl8_keyframe_entry));
|
||||
pxl8_keyframe_entry* entry = pxl8_calloc(1, sizeof(pxl8_keyframe_entry));
|
||||
if (!entry) return;
|
||||
|
||||
entry->keyframe = kf;
|
||||
|
|
@ -342,7 +343,7 @@ void pxl8_replay_write_keyframe(pxl8_replay* r, u32 frame, f32 time, pxl8_rng* r
|
|||
r->keyframes->prev = NULL;
|
||||
}
|
||||
pxl8_replay_chunk_free(oldest->input_deltas);
|
||||
free(oldest);
|
||||
pxl8_free(oldest);
|
||||
r->keyframe_count--;
|
||||
}
|
||||
|
||||
|
|
@ -446,14 +447,14 @@ void pxl8_replay_write_audio_event(pxl8_replay* r, u32 frame, u8 event_type, u8
|
|||
if (r->file) {
|
||||
write_chunk(r->file, PXL8_REPLAY_CHUNK_AUDIO_EVENT, &evt, sizeof(evt));
|
||||
} else {
|
||||
pxl8_replay_chunk* chunk = calloc(1, sizeof(pxl8_replay_chunk));
|
||||
pxl8_replay_chunk* chunk = pxl8_calloc(1, sizeof(pxl8_replay_chunk));
|
||||
if (!chunk) return;
|
||||
|
||||
chunk->type = PXL8_REPLAY_CHUNK_AUDIO_EVENT;
|
||||
chunk->size = sizeof(evt);
|
||||
chunk->data = malloc(sizeof(evt));
|
||||
chunk->data = pxl8_malloc(sizeof(evt));
|
||||
if (!chunk->data) {
|
||||
free(chunk);
|
||||
pxl8_free(chunk);
|
||||
return;
|
||||
}
|
||||
memcpy(chunk->data, &evt, sizeof(evt));
|
||||
|
|
@ -23,6 +23,9 @@ typedef uint16_t u16;
|
|||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
|
||||
typedef size_t usize;
|
||||
typedef ptrdiff_t isize;
|
||||
|
||||
#if defined(__SIZEOF_INT128__)
|
||||
typedef __int128_t i128;
|
||||
typedef __uint128_t u128;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "pxl8_3d_camera.h"
|
||||
#include "pxl8_mem.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
|
@ -28,7 +29,7 @@ struct pxl8_3d_camera {
|
|||
};
|
||||
|
||||
pxl8_3d_camera* pxl8_3d_camera_create(void) {
|
||||
pxl8_3d_camera* cam = calloc(1, sizeof(pxl8_3d_camera));
|
||||
pxl8_3d_camera* cam = pxl8_calloc(1, sizeof(pxl8_3d_camera));
|
||||
if (!cam) return NULL;
|
||||
|
||||
cam->position = (pxl8_vec3){0, 0, 0};
|
||||
|
|
@ -46,7 +47,7 @@ pxl8_3d_camera* pxl8_3d_camera_create(void) {
|
|||
}
|
||||
|
||||
void pxl8_3d_camera_destroy(pxl8_3d_camera* cam) {
|
||||
free(cam);
|
||||
pxl8_free(cam);
|
||||
}
|
||||
|
||||
void pxl8_3d_camera_lookat(pxl8_3d_camera* cam, pxl8_vec3 eye, pxl8_vec3 target, pxl8_vec3 up) {
|
||||
|
|
@ -56,8 +57,8 @@ void pxl8_3d_camera_lookat(pxl8_3d_camera* cam, pxl8_vec3 eye, pxl8_vec3 target,
|
|||
|
||||
pxl8_vec3 forward = pxl8_vec3_normalize(pxl8_vec3_sub(target, eye));
|
||||
|
||||
cam->pitch = asinf(-forward.y);
|
||||
cam->yaw = atan2f(forward.x, forward.z);
|
||||
cam->pitch = asinf(forward.y);
|
||||
cam->yaw = atan2f(-forward.x, -forward.z);
|
||||
cam->roll = 0;
|
||||
|
||||
(void)up;
|
||||
|
|
@ -104,9 +105,9 @@ pxl8_vec3 pxl8_3d_camera_get_forward(const pxl8_3d_camera* cam) {
|
|||
f32 sy = sinf(cam->yaw);
|
||||
|
||||
return (pxl8_vec3){
|
||||
cp * sy,
|
||||
-sp,
|
||||
cp * cy
|
||||
-sy * cp,
|
||||
sp,
|
||||
-cy * cp
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -121,7 +122,7 @@ pxl8_mat4 pxl8_3d_camera_get_projection(const pxl8_3d_camera* cam) {
|
|||
if (cam->mode == PXL8_3D_CAMERA_PERSPECTIVE) {
|
||||
return pxl8_mat4_perspective(cam->fov, cam->aspect, cam->near, cam->far);
|
||||
} else {
|
||||
return pxl8_mat4_ortho(
|
||||
return pxl8_mat4_orthographic(
|
||||
cam->ortho_left, cam->ortho_right,
|
||||
cam->ortho_bottom, cam->ortho_top,
|
||||
cam->near, cam->far
|
||||
|
|
@ -183,8 +184,8 @@ void pxl8_3d_camera_follow(pxl8_3d_camera* cam, pxl8_vec3 target, pxl8_vec3 offs
|
|||
cam->position = pxl8_vec3_lerp(cam->position, desired, t);
|
||||
|
||||
pxl8_vec3 forward = pxl8_vec3_normalize(pxl8_vec3_sub(target, cam->position));
|
||||
cam->pitch = asinf(-forward.y);
|
||||
cam->yaw = atan2f(forward.x, forward.z);
|
||||
cam->pitch = asinf(forward.y);
|
||||
cam->yaw = atan2f(-forward.x, -forward.z);
|
||||
}
|
||||
|
||||
void pxl8_3d_camera_shake(pxl8_3d_camera* cam, f32 intensity, f32 duration) {
|
||||
|
|
@ -212,3 +213,30 @@ void pxl8_3d_camera_update(pxl8_3d_camera* cam, f32 dt) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pxl8_projected_point pxl8_3d_camera_world_to_screen(const pxl8_3d_camera* cam, pxl8_vec3 world_pos, u32 screen_width, u32 screen_height) {
|
||||
pxl8_projected_point result = {0, 0, 0.0f, false};
|
||||
if (!cam) return result;
|
||||
|
||||
pxl8_mat4 view = pxl8_3d_camera_get_view(cam);
|
||||
pxl8_mat4 proj = pxl8_3d_camera_get_projection(cam);
|
||||
pxl8_mat4 vp = pxl8_mat4_multiply(proj, view);
|
||||
|
||||
pxl8_vec4 clip = pxl8_mat4_multiply_vec4(vp, (pxl8_vec4){world_pos.x, world_pos.y, world_pos.z, 1.0f});
|
||||
|
||||
if (clip.w <= 0.0f) return result;
|
||||
|
||||
f32 inv_w = 1.0f / clip.w;
|
||||
f32 ndc_x = clip.x * inv_w;
|
||||
f32 ndc_y = clip.y * inv_w;
|
||||
f32 ndc_z = clip.z * inv_w;
|
||||
|
||||
if (ndc_x < -1.0f || ndc_x > 1.0f || ndc_y < -1.0f || ndc_y > 1.0f) return result;
|
||||
|
||||
result.x = (i32)((ndc_x + 1.0f) * 0.5f * (f32)screen_width);
|
||||
result.y = (i32)((1.0f - ndc_y) * 0.5f * (f32)screen_height);
|
||||
result.depth = ndc_z;
|
||||
result.visible = true;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ pxl8_mat4 pxl8_3d_camera_get_view(const pxl8_3d_camera* cam);
|
|||
|
||||
void pxl8_3d_camera_blend(pxl8_3d_camera* dest, const pxl8_3d_camera* a, const pxl8_3d_camera* b, f32 t);
|
||||
void pxl8_3d_camera_follow(pxl8_3d_camera* cam, pxl8_vec3 target, pxl8_vec3 offset, f32 smoothing, f32 dt);
|
||||
pxl8_projected_point pxl8_3d_camera_world_to_screen(const pxl8_3d_camera* cam, pxl8_vec3 world_pos, u32 screen_width, u32 screen_height);
|
||||
void pxl8_3d_camera_shake(pxl8_3d_camera* cam, f32 intensity, f32 duration);
|
||||
void pxl8_3d_camera_update(pxl8_3d_camera* cam, f32 dt);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include "pxl8_atlas.h"
|
||||
#include "pxl8_gfx.h"
|
||||
#include "pxl8_log.h"
|
||||
#include "pxl8_mem.h"
|
||||
|
||||
#define PXL8_ANIM_MAX_STATES 32
|
||||
|
||||
|
|
@ -36,36 +37,36 @@ pxl8_anim* pxl8_anim_create(const u32* frame_ids, const u16* frame_durations, u1
|
|||
return NULL;
|
||||
}
|
||||
|
||||
pxl8_anim* anim = (pxl8_anim*)calloc(1, sizeof(pxl8_anim));
|
||||
pxl8_anim* anim = (pxl8_anim*)pxl8_calloc(1, sizeof(pxl8_anim));
|
||||
if (!anim) {
|
||||
pxl8_error("Failed to allocate animation");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
anim->frame_ids = (u32*)malloc(frame_count * sizeof(u32));
|
||||
anim->frame_ids = (u32*)pxl8_malloc(frame_count * sizeof(u32));
|
||||
if (!anim->frame_ids) {
|
||||
pxl8_error("Failed to allocate frame IDs");
|
||||
free(anim);
|
||||
pxl8_free(anim);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(anim->frame_ids, frame_ids, frame_count * sizeof(u32));
|
||||
|
||||
if (frame_durations) {
|
||||
anim->frame_durations = (u16*)malloc(frame_count * sizeof(u16));
|
||||
anim->frame_durations = (u16*)pxl8_malloc(frame_count * sizeof(u16));
|
||||
if (!anim->frame_durations) {
|
||||
pxl8_error("Failed to allocate frame durations");
|
||||
free(anim->frame_ids);
|
||||
free(anim);
|
||||
pxl8_free(anim->frame_ids);
|
||||
pxl8_free(anim);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(anim->frame_durations, frame_durations, frame_count * sizeof(u16));
|
||||
} else {
|
||||
anim->frame_durations = (u16*)calloc(frame_count, sizeof(u16));
|
||||
anim->frame_durations = (u16*)pxl8_calloc(frame_count, sizeof(u16));
|
||||
if (!anim->frame_durations) {
|
||||
pxl8_error("Failed to allocate frame durations");
|
||||
free(anim->frame_ids);
|
||||
free(anim);
|
||||
pxl8_free(anim->frame_ids);
|
||||
pxl8_free(anim);
|
||||
return NULL;
|
||||
}
|
||||
for (u16 i = 0; i < frame_count; i++) {
|
||||
|
|
@ -107,12 +108,12 @@ pxl8_anim* pxl8_anim_create_from_ase(pxl8_gfx* gfx, const char* path) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
u32* frame_ids = (u32*)malloc(ase_file.frame_count * sizeof(u32));
|
||||
u16* frame_durations = (u16*)malloc(ase_file.frame_count * sizeof(u16));
|
||||
u32* frame_ids = (u32*)pxl8_malloc(ase_file.frame_count * sizeof(u32));
|
||||
u16* frame_durations = (u16*)pxl8_malloc(ase_file.frame_count * sizeof(u16));
|
||||
if (!frame_ids || !frame_durations) {
|
||||
pxl8_error("Failed to allocate frame arrays");
|
||||
free(frame_ids);
|
||||
free(frame_durations);
|
||||
pxl8_free(frame_ids);
|
||||
pxl8_free(frame_durations);
|
||||
pxl8_ase_destroy(&ase_file);
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -122,8 +123,8 @@ pxl8_anim* pxl8_anim_create_from_ase(pxl8_gfx* gfx, const char* path) {
|
|||
result = pxl8_gfx_create_texture(gfx, frame->pixels, frame->width, frame->height);
|
||||
if (result != PXL8_OK) {
|
||||
pxl8_error("Failed to create texture for frame %u", i);
|
||||
free(frame_ids);
|
||||
free(frame_durations);
|
||||
pxl8_free(frame_ids);
|
||||
pxl8_free(frame_durations);
|
||||
pxl8_ase_destroy(&ase_file);
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -133,8 +134,8 @@ pxl8_anim* pxl8_anim_create_from_ase(pxl8_gfx* gfx, const char* path) {
|
|||
|
||||
pxl8_anim* anim = pxl8_anim_create(frame_ids, frame_durations, ase_file.frame_count);
|
||||
|
||||
free(frame_ids);
|
||||
free(frame_durations);
|
||||
pxl8_free(frame_ids);
|
||||
pxl8_free(frame_durations);
|
||||
pxl8_ase_destroy(&ase_file);
|
||||
|
||||
return anim;
|
||||
|
|
@ -145,15 +146,15 @@ void pxl8_anim_destroy(pxl8_anim* anim) {
|
|||
|
||||
if (anim->state_machine) {
|
||||
for (u16 i = 0; i < anim->state_machine->state_count; i++) {
|
||||
free(anim->state_machine->states[i].name);
|
||||
pxl8_free(anim->state_machine->states[i].name);
|
||||
pxl8_anim_destroy(anim->state_machine->states[i].anim);
|
||||
}
|
||||
free(anim->state_machine);
|
||||
pxl8_free(anim->state_machine);
|
||||
}
|
||||
|
||||
free(anim->frame_ids);
|
||||
free(anim->frame_durations);
|
||||
free(anim);
|
||||
pxl8_free(anim->frame_ids);
|
||||
pxl8_free(anim->frame_durations);
|
||||
pxl8_free(anim);
|
||||
}
|
||||
|
||||
pxl8_result pxl8_anim_add_state(pxl8_anim* anim, const char* name, pxl8_anim* state_anim) {
|
||||
|
|
@ -162,7 +163,7 @@ pxl8_result pxl8_anim_add_state(pxl8_anim* anim, const char* name, pxl8_anim* st
|
|||
}
|
||||
|
||||
if (!anim->state_machine) {
|
||||
anim->state_machine = (pxl8_anim_state_machine*)calloc(1, sizeof(pxl8_anim_state_machine));
|
||||
anim->state_machine = (pxl8_anim_state_machine*)pxl8_calloc(1, sizeof(pxl8_anim_state_machine));
|
||||
if (!anim->state_machine) {
|
||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include "pxl8_color.h"
|
||||
#include "pxl8_log.h"
|
||||
#include "pxl8_mem.h"
|
||||
|
||||
typedef struct pxl8_skyline_fit {
|
||||
bool found;
|
||||
|
|
@ -27,6 +28,8 @@ typedef struct pxl8_skyline {
|
|||
struct pxl8_atlas {
|
||||
u32 height, width;
|
||||
u8* pixels;
|
||||
u8* pixels_tiled;
|
||||
u32 tiled_capacity, tiled_size;
|
||||
|
||||
bool dirty;
|
||||
|
||||
|
|
@ -103,7 +106,7 @@ static bool pxl8_skyline_add_rect(pxl8_skyline* skyline, pxl8_point pos, u32 w,
|
|||
|
||||
if (skyline->count - nodes_to_remove + 1 > skyline->capacity) {
|
||||
u32 new_capacity = (skyline->count - nodes_to_remove + 1) * 2;
|
||||
pxl8_skyline_node* new_nodes = (pxl8_skyline_node*)realloc(
|
||||
pxl8_skyline_node* new_nodes = (pxl8_skyline_node*)pxl8_realloc(
|
||||
skyline->nodes,
|
||||
new_capacity * sizeof(pxl8_skyline_node)
|
||||
);
|
||||
|
|
@ -142,44 +145,44 @@ static void pxl8_skyline_compact(pxl8_skyline* skyline) {
|
|||
}
|
||||
|
||||
pxl8_atlas* pxl8_atlas_create(u32 width, u32 height, pxl8_pixel_mode pixel_mode) {
|
||||
pxl8_atlas* atlas = (pxl8_atlas*)calloc(1, sizeof(pxl8_atlas));
|
||||
pxl8_atlas* atlas = (pxl8_atlas*)pxl8_calloc(1, sizeof(pxl8_atlas));
|
||||
if (!atlas) return NULL;
|
||||
|
||||
atlas->height = height;
|
||||
atlas->width = width;
|
||||
|
||||
i32 bytes_per_pixel = pxl8_bytes_per_pixel(pixel_mode);
|
||||
atlas->pixels = (u8*)calloc(width * height, bytes_per_pixel);
|
||||
atlas->pixels = (u8*)pxl8_calloc(width * height, bytes_per_pixel);
|
||||
if (!atlas->pixels) {
|
||||
free(atlas);
|
||||
pxl8_free(atlas);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
atlas->entry_capacity = PXL8_DEFAULT_ATLAS_ENTRY_CAPACITY;
|
||||
atlas->entries = (pxl8_atlas_entry*)calloc(atlas->entry_capacity, sizeof(pxl8_atlas_entry));
|
||||
atlas->entries = (pxl8_atlas_entry*)pxl8_calloc(atlas->entry_capacity, sizeof(pxl8_atlas_entry));
|
||||
if (!atlas->entries) {
|
||||
free(atlas->pixels);
|
||||
free(atlas);
|
||||
pxl8_free(atlas->pixels);
|
||||
pxl8_free(atlas);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
atlas->free_capacity = 16;
|
||||
atlas->free_list = (u32*)calloc(atlas->free_capacity, sizeof(u32));
|
||||
atlas->free_list = (u32*)pxl8_calloc(atlas->free_capacity, sizeof(u32));
|
||||
if (!atlas->free_list) {
|
||||
free(atlas->entries);
|
||||
free(atlas->pixels);
|
||||
free(atlas);
|
||||
pxl8_free(atlas->entries);
|
||||
pxl8_free(atlas->pixels);
|
||||
pxl8_free(atlas);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
atlas->skyline.capacity = 16;
|
||||
atlas->skyline.nodes =
|
||||
(pxl8_skyline_node*)calloc(atlas->skyline.capacity, sizeof(pxl8_skyline_node));
|
||||
(pxl8_skyline_node*)pxl8_calloc(atlas->skyline.capacity, sizeof(pxl8_skyline_node));
|
||||
if (!atlas->skyline.nodes) {
|
||||
free(atlas->free_list);
|
||||
free(atlas->entries);
|
||||
free(atlas->pixels);
|
||||
free(atlas);
|
||||
pxl8_free(atlas->free_list);
|
||||
pxl8_free(atlas->entries);
|
||||
pxl8_free(atlas->pixels);
|
||||
pxl8_free(atlas);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -192,11 +195,12 @@ pxl8_atlas* pxl8_atlas_create(u32 width, u32 height, pxl8_pixel_mode pixel_mode)
|
|||
void pxl8_atlas_destroy(pxl8_atlas* atlas) {
|
||||
if (!atlas) return;
|
||||
|
||||
free(atlas->entries);
|
||||
free(atlas->free_list);
|
||||
free(atlas->pixels);
|
||||
free(atlas->skyline.nodes);
|
||||
free(atlas);
|
||||
pxl8_free(atlas->entries);
|
||||
pxl8_free(atlas->free_list);
|
||||
pxl8_free(atlas->pixels);
|
||||
pxl8_free(atlas->pixels_tiled);
|
||||
pxl8_free(atlas->skyline.nodes);
|
||||
pxl8_free(atlas);
|
||||
}
|
||||
|
||||
void pxl8_atlas_clear(pxl8_atlas* atlas, u32 preserve_count) {
|
||||
|
|
@ -209,6 +213,13 @@ void pxl8_atlas_clear(pxl8_atlas* atlas, u32 preserve_count) {
|
|||
atlas->entry_count = preserve_count;
|
||||
atlas->free_count = 0;
|
||||
|
||||
if (preserve_count == 0) {
|
||||
atlas->tiled_size = 0;
|
||||
} else {
|
||||
pxl8_atlas_entry* last = &atlas->entries[preserve_count - 1];
|
||||
atlas->tiled_size = last->tiled_base + (u32)(last->w * last->h);
|
||||
}
|
||||
|
||||
atlas->skyline.nodes[0] = (pxl8_skyline_node){0, 0, (i32)atlas->width};
|
||||
atlas->skyline.count = 1;
|
||||
|
||||
|
|
@ -222,13 +233,13 @@ bool pxl8_atlas_expand(pxl8_atlas* atlas, pxl8_pixel_mode pixel_mode) {
|
|||
u32 new_size = atlas->width * 2;
|
||||
u32 old_width = atlas->width;
|
||||
|
||||
u8* new_pixels = (u8*)calloc(new_size * new_size, bytes_per_pixel);
|
||||
u8* new_pixels = (u8*)pxl8_calloc(new_size * new_size, bytes_per_pixel);
|
||||
if (!new_pixels) return false;
|
||||
|
||||
pxl8_skyline new_skyline;
|
||||
new_skyline.nodes = (pxl8_skyline_node*)calloc(16, sizeof(pxl8_skyline_node));
|
||||
new_skyline.nodes = (pxl8_skyline_node*)pxl8_calloc(16, sizeof(pxl8_skyline_node));
|
||||
if (!new_skyline.nodes) {
|
||||
free(new_pixels);
|
||||
pxl8_free(new_pixels);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -248,8 +259,8 @@ bool pxl8_atlas_expand(pxl8_atlas* atlas, pxl8_pixel_mode pixel_mode) {
|
|||
);
|
||||
|
||||
if (!fit.found) {
|
||||
free(new_skyline.nodes);
|
||||
free(new_pixels);
|
||||
pxl8_free(new_skyline.nodes);
|
||||
pxl8_free(new_pixels);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -269,15 +280,15 @@ bool pxl8_atlas_expand(pxl8_atlas* atlas, pxl8_pixel_mode pixel_mode) {
|
|||
atlas->entries[i].y = fit.pos.y;
|
||||
|
||||
if (!pxl8_skyline_add_rect(&new_skyline, fit.pos, atlas->entries[i].w, atlas->entries[i].h)) {
|
||||
free(new_skyline.nodes);
|
||||
free(new_pixels);
|
||||
pxl8_free(new_skyline.nodes);
|
||||
pxl8_free(new_pixels);
|
||||
return false;
|
||||
}
|
||||
pxl8_skyline_compact(&new_skyline);
|
||||
}
|
||||
|
||||
free(atlas->pixels);
|
||||
free(atlas->skyline.nodes);
|
||||
pxl8_free(atlas->pixels);
|
||||
pxl8_free(atlas->skyline.nodes);
|
||||
|
||||
atlas->pixels = new_pixels;
|
||||
atlas->skyline = new_skyline;
|
||||
|
|
@ -316,7 +327,7 @@ u32 pxl8_atlas_add_texture(
|
|||
} else {
|
||||
if (atlas->entry_count >= atlas->entry_capacity) {
|
||||
u32 new_capacity = atlas->entry_capacity * 2;
|
||||
pxl8_atlas_entry* new_entries = (pxl8_atlas_entry*)realloc(
|
||||
pxl8_atlas_entry* new_entries = (pxl8_atlas_entry*)pxl8_realloc(
|
||||
atlas->entries,
|
||||
new_capacity * sizeof(pxl8_atlas_entry)
|
||||
);
|
||||
|
|
@ -334,6 +345,7 @@ u32 pxl8_atlas_add_texture(
|
|||
entry->y = fit.pos.y;
|
||||
entry->w = w;
|
||||
entry->h = h;
|
||||
entry->log2_w = pxl8_log2(w);
|
||||
|
||||
i32 bytes_per_pixel = pxl8_bytes_per_pixel(pixel_mode);
|
||||
for (u32 y = 0; y < h; y++) {
|
||||
|
|
@ -349,6 +361,30 @@ u32 pxl8_atlas_add_texture(
|
|||
}
|
||||
}
|
||||
|
||||
u32 tiled_tex_size = w * h;
|
||||
u32 new_tiled_size = atlas->tiled_size + tiled_tex_size;
|
||||
if (new_tiled_size > atlas->tiled_capacity) {
|
||||
u32 new_cap = atlas->tiled_capacity ? atlas->tiled_capacity * 2 : 4096;
|
||||
while (new_cap < new_tiled_size) new_cap *= 2;
|
||||
u8* new_tiled = (u8*)pxl8_realloc(atlas->pixels_tiled, new_cap);
|
||||
if (!new_tiled) {
|
||||
entry->active = false;
|
||||
return UINT32_MAX;
|
||||
}
|
||||
atlas->pixels_tiled = new_tiled;
|
||||
atlas->tiled_capacity = new_cap;
|
||||
}
|
||||
|
||||
entry->tiled_base = atlas->tiled_size;
|
||||
u8* tiled_dst = atlas->pixels_tiled + entry->tiled_base;
|
||||
for (u32 ty = 0; ty < h; ty++) {
|
||||
for (u32 tx = 0; tx < w; tx++) {
|
||||
u32 tiled_offset = pxl8_tile_addr(tx, ty, entry->log2_w);
|
||||
tiled_dst[tiled_offset] = pixels[ty * w + tx];
|
||||
}
|
||||
}
|
||||
atlas->tiled_size = new_tiled_size;
|
||||
|
||||
if (!pxl8_skyline_add_rect(&atlas->skyline, fit.pos, w, h)) {
|
||||
entry->active = false;
|
||||
return UINT32_MAX;
|
||||
|
|
@ -377,6 +413,10 @@ const u8* pxl8_atlas_get_pixels(const pxl8_atlas* atlas) {
|
|||
return atlas ? atlas->pixels : NULL;
|
||||
}
|
||||
|
||||
const u8* pxl8_atlas_get_pixels_tiled(const pxl8_atlas* atlas) {
|
||||
return atlas ? atlas->pixels_tiled : NULL;
|
||||
}
|
||||
|
||||
u32 pxl8_atlas_get_width(const pxl8_atlas* atlas) {
|
||||
return atlas ? atlas->width : 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,28 +8,42 @@ typedef struct pxl8_atlas_entry {
|
|||
bool active;
|
||||
u32 texture_id;
|
||||
i32 x, y, w, h;
|
||||
u32 tiled_base;
|
||||
u8 log2_w;
|
||||
} pxl8_atlas_entry;
|
||||
|
||||
static inline u32 pxl8_tile_addr(u32 u, u32 v, u8 log2_w) {
|
||||
u32 tile_y = v >> 3;
|
||||
u32 tile_x = u >> 3;
|
||||
u32 local_y = v & 7;
|
||||
u32 local_x = u & 7;
|
||||
return (tile_y << (log2_w + 3)) | (tile_x << 6) | (local_y << 3) | local_x;
|
||||
}
|
||||
|
||||
static inline u8 pxl8_log2(u32 v) {
|
||||
u8 r = 0;
|
||||
while (v >>= 1) r++;
|
||||
return r;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
u32 pxl8_atlas_add_texture(pxl8_atlas* atlas, const u8* pixels, u32 w, u32 h, pxl8_pixel_mode pixel_mode);
|
||||
void pxl8_atlas_clear(pxl8_atlas* atlas, u32 preserve_count);
|
||||
pxl8_atlas* pxl8_atlas_create(u32 width, u32 height, pxl8_pixel_mode pixel_mode);
|
||||
void pxl8_atlas_destroy(pxl8_atlas* atlas);
|
||||
|
||||
bool pxl8_atlas_expand(pxl8_atlas* atlas, pxl8_pixel_mode pixel_mode);
|
||||
const pxl8_atlas_entry* pxl8_atlas_get_entry(const pxl8_atlas* atlas, u32 id);
|
||||
u32 pxl8_atlas_get_entry_count(const pxl8_atlas* atlas);
|
||||
u32 pxl8_atlas_get_height(const pxl8_atlas* atlas);
|
||||
const u8* pxl8_atlas_get_pixels(const pxl8_atlas* atlas);
|
||||
const u8* pxl8_atlas_get_pixels_tiled(const pxl8_atlas* atlas);
|
||||
u32 pxl8_atlas_get_width(const pxl8_atlas* atlas);
|
||||
bool pxl8_atlas_is_dirty(const pxl8_atlas* atlas);
|
||||
|
||||
void pxl8_atlas_mark_clean(pxl8_atlas* atlas);
|
||||
|
||||
u32 pxl8_atlas_add_texture(pxl8_atlas* atlas, const u8* pixels, u32 w, u32 h, pxl8_pixel_mode pixel_mode);
|
||||
void pxl8_atlas_clear(pxl8_atlas* atlas, u32 preserve_count);
|
||||
bool pxl8_atlas_expand(pxl8_atlas* atlas, pxl8_pixel_mode pixel_mode);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,194 +0,0 @@
|
|||
#include "pxl8_blend.h"
|
||||
#include "pxl8_colormap.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
struct pxl8_palette_cube {
|
||||
u8 colors[PXL8_PALETTE_SIZE * 3];
|
||||
u8 table[PXL8_CUBE_ENTRIES];
|
||||
u8 stable[PXL8_CUBE_ENTRIES];
|
||||
};
|
||||
|
||||
struct pxl8_additive_table {
|
||||
u8 table[PXL8_BLEND_TABLE_SIZE];
|
||||
};
|
||||
|
||||
struct pxl8_overbright_table {
|
||||
u8 table[PXL8_OVERBRIGHT_TABLE_SIZE];
|
||||
};
|
||||
|
||||
static u8 find_closest_stable(const pxl8_palette* pal, u8 r, u8 g, u8 b) {
|
||||
u8 best_idx = 1;
|
||||
u32 best_dist = 0xFFFFFFFF;
|
||||
|
||||
u8 dynamic_end = PXL8_DYNAMIC_RANGE_START + PXL8_DYNAMIC_RANGE_COUNT;
|
||||
|
||||
for (u32 i = 1; i < PXL8_FULLBRIGHT_START; i++) {
|
||||
if (i >= PXL8_DYNAMIC_RANGE_START && i < dynamic_end) {
|
||||
continue;
|
||||
}
|
||||
|
||||
u8 pr, pg, pb;
|
||||
pxl8_palette_get_rgb(pal, (u8)i, &pr, &pg, &pb);
|
||||
|
||||
i32 dr = (i32)r - (i32)pr;
|
||||
i32 dg = (i32)g - (i32)pg;
|
||||
i32 db = (i32)b - (i32)pb;
|
||||
u32 dist = (u32)(dr * dr + dg * dg + db * db);
|
||||
|
||||
if (dist < best_dist) {
|
||||
best_dist = dist;
|
||||
best_idx = (u8)i;
|
||||
if (dist == 0) break;
|
||||
}
|
||||
}
|
||||
|
||||
return best_idx;
|
||||
}
|
||||
|
||||
pxl8_palette_cube* pxl8_palette_cube_create(const pxl8_palette* pal) {
|
||||
pxl8_palette_cube* cube = calloc(1, sizeof(pxl8_palette_cube));
|
||||
if (!cube) return NULL;
|
||||
pxl8_palette_cube_rebuild(cube, pal);
|
||||
return cube;
|
||||
}
|
||||
|
||||
void pxl8_palette_cube_destroy(pxl8_palette_cube* cube) {
|
||||
free(cube);
|
||||
}
|
||||
|
||||
void pxl8_palette_cube_rebuild(pxl8_palette_cube* cube, const pxl8_palette* pal) {
|
||||
if (!cube || !pal) return;
|
||||
|
||||
for (u32 i = 0; i < PXL8_PALETTE_SIZE; i++) {
|
||||
u8 r, g, b;
|
||||
pxl8_palette_get_rgb(pal, (u8)i, &r, &g, &b);
|
||||
cube->colors[i * 3 + 0] = r;
|
||||
cube->colors[i * 3 + 1] = g;
|
||||
cube->colors[i * 3 + 2] = b;
|
||||
}
|
||||
|
||||
for (u32 bi = 0; bi < PXL8_CUBE_SIZE; bi++) {
|
||||
for (u32 gi = 0; gi < PXL8_CUBE_SIZE; gi++) {
|
||||
for (u32 ri = 0; ri < PXL8_CUBE_SIZE; ri++) {
|
||||
u32 idx = (bi * PXL8_CUBE_SIZE * PXL8_CUBE_SIZE) + (gi * PXL8_CUBE_SIZE) + ri;
|
||||
u8 r8 = (u8)((ri * 255) / (PXL8_CUBE_SIZE - 1));
|
||||
u8 g8 = (u8)((gi * 255) / (PXL8_CUBE_SIZE - 1));
|
||||
u8 b8 = (u8)((bi * 255) / (PXL8_CUBE_SIZE - 1));
|
||||
|
||||
cube->table[idx] = pxl8_palette_find_closest(pal, r8, g8, b8);
|
||||
cube->stable[idx] = find_closest_stable(pal, r8, g8, b8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u8 pxl8_palette_cube_lookup(const pxl8_palette_cube* cube, u8 r, u8 g, u8 b) {
|
||||
u32 ri = (r * (PXL8_CUBE_SIZE - 1)) / 255;
|
||||
u32 gi = (g * (PXL8_CUBE_SIZE - 1)) / 255;
|
||||
u32 bi = (b * (PXL8_CUBE_SIZE - 1)) / 255;
|
||||
u32 idx = (bi * PXL8_CUBE_SIZE * PXL8_CUBE_SIZE) + (gi * PXL8_CUBE_SIZE) + ri;
|
||||
return cube->table[idx];
|
||||
}
|
||||
|
||||
u8 pxl8_palette_cube_lookup_stable(const pxl8_palette_cube* cube, u8 r, u8 g, u8 b) {
|
||||
u32 ri = (r * (PXL8_CUBE_SIZE - 1)) / 255;
|
||||
u32 gi = (g * (PXL8_CUBE_SIZE - 1)) / 255;
|
||||
u32 bi = (b * (PXL8_CUBE_SIZE - 1)) / 255;
|
||||
u32 idx = (bi * PXL8_CUBE_SIZE * PXL8_CUBE_SIZE) + (gi * PXL8_CUBE_SIZE) + ri;
|
||||
return cube->stable[idx];
|
||||
}
|
||||
|
||||
void pxl8_palette_cube_get_rgb(const pxl8_palette_cube* cube, u8 idx, u8* r, u8* g, u8* b) {
|
||||
*r = cube->colors[idx * 3 + 0];
|
||||
*g = cube->colors[idx * 3 + 1];
|
||||
*b = cube->colors[idx * 3 + 2];
|
||||
}
|
||||
|
||||
pxl8_additive_table* pxl8_additive_table_create(const pxl8_palette* pal) {
|
||||
pxl8_additive_table* table = calloc(1, sizeof(pxl8_additive_table));
|
||||
if (!table) return NULL;
|
||||
pxl8_additive_table_rebuild(table, pal);
|
||||
return table;
|
||||
}
|
||||
|
||||
void pxl8_additive_table_destroy(pxl8_additive_table* table) {
|
||||
free(table);
|
||||
}
|
||||
|
||||
void pxl8_additive_table_rebuild(pxl8_additive_table* table, const pxl8_palette* pal) {
|
||||
if (!table || !pal) return;
|
||||
|
||||
for (u32 src = 0; src < 256; src++) {
|
||||
u8 sr, sg, sb;
|
||||
pxl8_palette_get_rgb(pal, (u8)src, &sr, &sg, &sb);
|
||||
|
||||
for (u32 dst = 0; dst < 256; dst++) {
|
||||
u32 idx = src * 256 + dst;
|
||||
|
||||
if (src == PXL8_TRANSPARENT) {
|
||||
table->table[idx] = (u8)dst;
|
||||
continue;
|
||||
}
|
||||
|
||||
u8 dr, dg, db;
|
||||
pxl8_palette_get_rgb(pal, (u8)dst, &dr, &dg, &db);
|
||||
|
||||
u16 ar = (u16)sr + (u16)dr;
|
||||
u16 ag = (u16)sg + (u16)dg;
|
||||
u16 ab = (u16)sb + (u16)db;
|
||||
if (ar > 255) ar = 255;
|
||||
if (ag > 255) ag = 255;
|
||||
if (ab > 255) ab = 255;
|
||||
|
||||
table->table[idx] = find_closest_stable(pal, (u8)ar, (u8)ag, (u8)ab);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u8 pxl8_additive_blend(const pxl8_additive_table* table, u8 src, u8 dst) {
|
||||
return table->table[(u32)src * 256 + dst];
|
||||
}
|
||||
|
||||
pxl8_overbright_table* pxl8_overbright_table_create(const pxl8_palette* pal) {
|
||||
pxl8_overbright_table* table = calloc(1, sizeof(pxl8_overbright_table));
|
||||
if (!table) return NULL;
|
||||
pxl8_overbright_table_rebuild(table, pal);
|
||||
return table;
|
||||
}
|
||||
|
||||
void pxl8_overbright_table_destroy(pxl8_overbright_table* table) {
|
||||
free(table);
|
||||
}
|
||||
|
||||
void pxl8_overbright_table_rebuild(pxl8_overbright_table* table, const pxl8_palette* pal) {
|
||||
if (!table || !pal) return;
|
||||
|
||||
for (u32 level = 0; level < PXL8_OVERBRIGHT_LEVELS; level++) {
|
||||
f32 overbright = (f32)level / (f32)(PXL8_OVERBRIGHT_LEVELS - 1);
|
||||
|
||||
for (u32 pal_idx = 0; pal_idx < 256; pal_idx++) {
|
||||
u32 idx = level * 256 + pal_idx;
|
||||
|
||||
if (pal_idx == PXL8_TRANSPARENT) {
|
||||
table->table[idx] = PXL8_TRANSPARENT;
|
||||
continue;
|
||||
}
|
||||
|
||||
u8 r, g, b;
|
||||
pxl8_palette_get_rgb(pal, (u8)pal_idx, &r, &g, &b);
|
||||
|
||||
u8 or = (u8)(r + (255 - r) * overbright);
|
||||
u8 og = (u8)(g + (255 - g) * overbright);
|
||||
u8 ob = (u8)(b + (255 - b) * overbright);
|
||||
|
||||
table->table[idx] = find_closest_stable(pal, or, og, ob);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u8 pxl8_overbright_lookup(const pxl8_overbright_table* table, u8 pal_idx, f32 emissive) {
|
||||
u32 level = (u32)(emissive * (PXL8_OVERBRIGHT_LEVELS - 1));
|
||||
if (level >= PXL8_OVERBRIGHT_LEVELS) level = PXL8_OVERBRIGHT_LEVELS - 1;
|
||||
return table->table[level * 256 + pal_idx];
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_palette.h"
|
||||
#include "pxl8_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define PXL8_CUBE_SIZE 32
|
||||
#define PXL8_CUBE_ENTRIES (PXL8_CUBE_SIZE * PXL8_CUBE_SIZE * PXL8_CUBE_SIZE)
|
||||
#define PXL8_BLEND_TABLE_SIZE (256 * 256)
|
||||
#define PXL8_OVERBRIGHT_LEVELS 16
|
||||
#define PXL8_OVERBRIGHT_TABLE_SIZE (256 * PXL8_OVERBRIGHT_LEVELS)
|
||||
|
||||
typedef struct pxl8_additive_table pxl8_additive_table;
|
||||
typedef struct pxl8_overbright_table pxl8_overbright_table;
|
||||
typedef struct pxl8_palette_cube pxl8_palette_cube;
|
||||
|
||||
pxl8_additive_table* pxl8_additive_table_create(const pxl8_palette* pal);
|
||||
void pxl8_additive_table_destroy(pxl8_additive_table* table);
|
||||
void pxl8_additive_table_rebuild(pxl8_additive_table* table, const pxl8_palette* pal);
|
||||
u8 pxl8_additive_blend(const pxl8_additive_table* table, u8 src, u8 dst);
|
||||
|
||||
pxl8_overbright_table* pxl8_overbright_table_create(const pxl8_palette* pal);
|
||||
void pxl8_overbright_table_destroy(pxl8_overbright_table* table);
|
||||
void pxl8_overbright_table_rebuild(pxl8_overbright_table* table, const pxl8_palette* pal);
|
||||
u8 pxl8_overbright_lookup(const pxl8_overbright_table* table, u8 pal_idx, f32 emissive);
|
||||
|
||||
pxl8_palette_cube* pxl8_palette_cube_create(const pxl8_palette* pal);
|
||||
void pxl8_palette_cube_destroy(pxl8_palette_cube* cube);
|
||||
void pxl8_palette_cube_rebuild(pxl8_palette_cube* cube, const pxl8_palette* pal);
|
||||
u8 pxl8_palette_cube_lookup(const pxl8_palette_cube* cube, u8 r, u8 g, u8 b);
|
||||
u8 pxl8_palette_cube_lookup_stable(const pxl8_palette_cube* cube, u8 r, u8 g, u8 b);
|
||||
void pxl8_palette_cube_get_rgb(const pxl8_palette_cube* cube, u8 idx, u8* r, u8* g, u8* b);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1,42 +1,12 @@
|
|||
#include "pxl8_colormap.h"
|
||||
#include <string.h>
|
||||
|
||||
static void rgb_to_hsl(u8 r, u8 g, u8 b, i32* h, i32* s, i32* l) {
|
||||
i32 max = r > g ? (r > b ? r : b) : (g > b ? g : b);
|
||||
i32 min = r < g ? (r < b ? r : b) : (g < b ? g : b);
|
||||
i32 chroma = max - min;
|
||||
|
||||
*l = (max + min) / 2;
|
||||
|
||||
if (chroma == 0) {
|
||||
*h = 0;
|
||||
*s = 0;
|
||||
} else {
|
||||
i32 denom = 255 - (*l > 127 ? 2 * (*l) - 255 : 255 - 2 * (*l));
|
||||
if (denom <= 0) denom = 1;
|
||||
*s = (chroma * 255) / denom;
|
||||
if (*s > 255) *s = 255;
|
||||
|
||||
if (max == (i32)r) {
|
||||
*h = (((i32)g - (i32)b) * 60) / chroma;
|
||||
if (*h < 0) *h += 360;
|
||||
} else if (max == (i32)g) {
|
||||
*h = 120 + (((i32)b - (i32)r) * 60) / chroma;
|
||||
} else {
|
||||
*h = 240 + (((i32)r - (i32)g) * 60) / chroma;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static u8 find_closest_color(const u32* palette, u8 target_r, u8 target_g, u8 target_b) {
|
||||
u8 best_idx = 1;
|
||||
u32 best_dist = 0xFFFFFFFF;
|
||||
|
||||
u8 dynamic_end = PXL8_DYNAMIC_RANGE_START + PXL8_DYNAMIC_RANGE_COUNT;
|
||||
|
||||
i32 th, ts, tl;
|
||||
rgb_to_hsl(target_r, target_g, target_b, &th, &ts, &tl);
|
||||
|
||||
for (u32 i = 1; i < PXL8_FULLBRIGHT_START; i++) {
|
||||
if (i >= PXL8_DYNAMIC_RANGE_START && i < dynamic_end) {
|
||||
continue;
|
||||
|
|
@ -47,17 +17,10 @@ static u8 find_closest_color(const u32* palette, u8 target_r, u8 target_g, u8 ta
|
|||
u8 pg = (c >> 8) & 0xFF;
|
||||
u8 pb = (c >> 16) & 0xFF;
|
||||
|
||||
i32 ph, ps, pl;
|
||||
rgb_to_hsl(pr, pg, pb, &ph, &ps, &pl);
|
||||
|
||||
i32 dh = th - ph;
|
||||
if (dh > 180) dh -= 360;
|
||||
if (dh < -180) dh += 360;
|
||||
|
||||
i32 ds = ts - ps;
|
||||
i32 dl = tl - pl;
|
||||
|
||||
u32 dist = (u32)(dh * dh * 4 + ds * ds + dl * dl);
|
||||
i32 dr = (i32)target_r - (i32)pr;
|
||||
i32 dg = (i32)target_g - (i32)pg;
|
||||
i32 db = (i32)target_b - (i32)pb;
|
||||
u32 dist = (u32)(dr * dr + dg * dg + db * db);
|
||||
|
||||
if (dist < best_dist) {
|
||||
best_dist = dist;
|
||||
|
|
@ -69,26 +32,19 @@ static u8 find_closest_color(const u32* palette, u8 target_r, u8 target_g, u8 ta
|
|||
return best_idx;
|
||||
}
|
||||
|
||||
void pxl8_colormap_generate(pxl8_colormap* cm, const u32* palette, const pxl8_level_tint* tint) {
|
||||
if (!cm || !palette) return;
|
||||
void pxl8_set_colormap(pxl8_colormap* cm, const u8* data, u32 size) {
|
||||
if (!cm || !data || size == 0) return;
|
||||
u32 copy_size = size > PXL8_COLORMAP_SIZE ? PXL8_COLORMAP_SIZE : size;
|
||||
memcpy(cm->table, data, copy_size);
|
||||
}
|
||||
|
||||
u8 dark_r, dark_g, dark_b;
|
||||
if (tint && tint->tint_strength > 0.0f) {
|
||||
f32 t = tint->tint_strength;
|
||||
f32 inv = 1.0f - t;
|
||||
dark_r = (u8)(tint->dark_r * inv + tint->tint_r * t);
|
||||
dark_g = (u8)(tint->dark_g * inv + tint->tint_g * t);
|
||||
dark_b = (u8)(tint->dark_b * inv + tint->tint_b * t);
|
||||
} else if (tint) {
|
||||
dark_r = tint->dark_r;
|
||||
dark_g = tint->dark_g;
|
||||
dark_b = tint->dark_b;
|
||||
} else {
|
||||
dark_r = dark_g = dark_b = 0;
|
||||
}
|
||||
static void generate_light_table(pxl8_colormap* cm, const u32* palette, pxl8_light_color light_color) {
|
||||
pxl8_rgb light = pxl8_light_colors[light_color];
|
||||
u32 base_row = (u32)light_color * PXL8_LIGHT_LEVELS;
|
||||
|
||||
for (u32 light = 0; light < PXL8_LIGHT_LEVELS; light++) {
|
||||
f32 brightness = (f32)light / (f32)(PXL8_LIGHT_LEVELS - 1);
|
||||
for (u32 level = 0; level < PXL8_LIGHT_LEVELS; level++) {
|
||||
f32 brightness = (f32)level / (f32)(PXL8_LIGHT_LEVELS - 1);
|
||||
u32 row = base_row + level;
|
||||
|
||||
for (u32 pal_idx = 0; pal_idx < 256; pal_idx++) {
|
||||
u8 result_idx;
|
||||
|
|
@ -103,14 +59,65 @@ void pxl8_colormap_generate(pxl8_colormap* cm, const u32* palette, const pxl8_le
|
|||
u8 g = (c >> 8) & 0xFF;
|
||||
u8 b = (c >> 16) & 0xFF;
|
||||
|
||||
u8 target_r = (u8)(dark_r + (r - dark_r) * brightness);
|
||||
u8 target_g = (u8)(dark_g + (g - dark_g) * brightness);
|
||||
u8 target_b = (u8)(dark_b + (b - dark_b) * brightness);
|
||||
f32 lr = (f32)light.r / 255.0f;
|
||||
f32 lg = (f32)light.g / 255.0f;
|
||||
f32 lb = (f32)light.b / 255.0f;
|
||||
|
||||
u8 target_r = (u8)(r * brightness * lr);
|
||||
u8 target_g = (u8)(g * brightness * lg);
|
||||
u8 target_b = (u8)(b * brightness * lb);
|
||||
|
||||
result_idx = find_closest_color(palette, target_r, target_g, target_b);
|
||||
}
|
||||
|
||||
cm->table[light * 256 + pal_idx] = result_idx;
|
||||
cm->table[row * 256 + pal_idx] = result_idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void generate_blend_table(pxl8_colormap* cm, const u32* palette) {
|
||||
for (u32 src = 0; src < 256; src++) {
|
||||
u32 row = PXL8_LIGHT_ROWS + src;
|
||||
|
||||
u8 sr, sg, sb;
|
||||
if (src == PXL8_TRANSPARENT) {
|
||||
sr = sg = sb = 0;
|
||||
} else {
|
||||
u32 sc = palette[src];
|
||||
sr = sc & 0xFF;
|
||||
sg = (sc >> 8) & 0xFF;
|
||||
sb = (sc >> 16) & 0xFF;
|
||||
}
|
||||
|
||||
for (u32 dst = 0; dst < 256; dst++) {
|
||||
u8 result_idx;
|
||||
|
||||
if (src == PXL8_TRANSPARENT) {
|
||||
result_idx = (u8)dst;
|
||||
} else {
|
||||
u32 dc = palette[dst];
|
||||
u8 dr = dc & 0xFF;
|
||||
u8 dg = (dc >> 8) & 0xFF;
|
||||
u8 db = (dc >> 16) & 0xFF;
|
||||
|
||||
u8 blend_r = (u8)((sr + dr) / 2);
|
||||
u8 blend_g = (u8)((sg + dg) / 2);
|
||||
u8 blend_b = (u8)((sb + db) / 2);
|
||||
|
||||
result_idx = find_closest_color(palette, blend_r, blend_g, blend_b);
|
||||
}
|
||||
|
||||
cm->table[row * 256 + dst] = result_idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_colormap_generate(pxl8_colormap* cm, const u32* palette) {
|
||||
if (!cm || !palette) return;
|
||||
|
||||
for (u32 light = 0; light < PXL8_LIGHT_COLORS; light++) {
|
||||
generate_light_table(cm, palette, (pxl8_light_color)light);
|
||||
}
|
||||
|
||||
generate_blend_table(cm, palette);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,35 +7,65 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define PXL8_LIGHT_LEVELS 64
|
||||
#define PXL8_COLORMAP_SIZE (256 * PXL8_LIGHT_LEVELS)
|
||||
#define PXL8_LIGHT_COLORS 8
|
||||
#define PXL8_LIGHT_LEVELS 8
|
||||
#define PXL8_LIGHT_ROWS (PXL8_LIGHT_COLORS * PXL8_LIGHT_LEVELS)
|
||||
#define PXL8_BLEND_ROWS 256
|
||||
#define PXL8_COLORMAP_ROWS (PXL8_LIGHT_ROWS + PXL8_BLEND_ROWS)
|
||||
#define PXL8_COLORMAP_SIZE (256 * PXL8_COLORMAP_ROWS)
|
||||
|
||||
#define PXL8_FULLBRIGHT_START 240
|
||||
#define PXL8_TRANSPARENT 0
|
||||
#define PXL8_DYNAMIC_RANGE_START 144
|
||||
#define PXL8_DYNAMIC_RANGE_COUNT 16
|
||||
|
||||
typedef enum {
|
||||
PXL8_LIGHT_WHITE = 0,
|
||||
PXL8_LIGHT_RED = 1,
|
||||
PXL8_LIGHT_ORANGE = 2,
|
||||
PXL8_LIGHT_YELLOW = 3,
|
||||
PXL8_LIGHT_GREEN = 4,
|
||||
PXL8_LIGHT_CYAN = 5,
|
||||
PXL8_LIGHT_BLUE = 6,
|
||||
PXL8_LIGHT_PURPLE = 7,
|
||||
} pxl8_light_color;
|
||||
|
||||
typedef struct {
|
||||
u8 r, g, b;
|
||||
} pxl8_rgb;
|
||||
|
||||
static const pxl8_rgb pxl8_light_colors[PXL8_LIGHT_COLORS] = {
|
||||
{255, 255, 255},
|
||||
{255, 64, 64},
|
||||
{255, 160, 64},
|
||||
{255, 255, 64},
|
||||
{64, 255, 64},
|
||||
{64, 255, 255},
|
||||
{64, 64, 255},
|
||||
{255, 64, 255},
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
u8 table[PXL8_COLORMAP_SIZE];
|
||||
} pxl8_colormap;
|
||||
|
||||
typedef struct {
|
||||
u8 dark_r, dark_g, dark_b;
|
||||
u8 tint_r, tint_g, tint_b;
|
||||
f32 tint_strength;
|
||||
} pxl8_level_tint;
|
||||
void pxl8_colormap_generate(pxl8_colormap* cm, const u32* palette);
|
||||
void pxl8_set_colormap(pxl8_colormap* cm, const u8* data, u32 size);
|
||||
|
||||
void pxl8_colormap_generate(pxl8_colormap* cm, const u32* palette, const pxl8_level_tint* tint);
|
||||
|
||||
static inline u8 pxl8_colormap_lookup(const pxl8_colormap* cm, u8 pal_idx, u8 light) {
|
||||
u32 light_idx = light >> 2;
|
||||
return cm->table[light_idx * 256 + pal_idx];
|
||||
static inline u8 pxl8_colormap_lookup(const pxl8_colormap* cm, u8 pal_idx, pxl8_light_color light_color, u8 intensity) {
|
||||
u32 light_row = ((u32)light_color << 3) + (intensity >> 5);
|
||||
return cm->table[(light_row << 8) + pal_idx];
|
||||
}
|
||||
|
||||
static inline u8 pxl8_colormap_lookup_dithered(const pxl8_colormap* cm, u8 pal_idx, u8 light, u32 x, u32 y) {
|
||||
u8 dithered = pxl8_dither_light(light, x, y);
|
||||
u32 light_idx = dithered >> 2;
|
||||
return cm->table[light_idx * 256 + pal_idx];
|
||||
static inline u8 pxl8_colormap_lookup_dithered(const pxl8_colormap* cm, u8 pal_idx, pxl8_light_color light_color, u8 intensity, u32 x, u32 y) {
|
||||
u8 dithered = pxl8_dither_light(intensity, x, y);
|
||||
u32 light_row = ((u32)light_color << 3) + (dithered >> 5);
|
||||
return cm->table[(light_row << 8) + pal_idx];
|
||||
}
|
||||
|
||||
static inline u8 pxl8_colormap_blend(const pxl8_colormap* cm, u8 src, u8 dst) {
|
||||
u32 blend_row = PXL8_LIGHT_ROWS + src;
|
||||
return cm->table[(blend_row << 8) + dst];
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
#include "pxl8_cpu.h"
|
||||
#include "pxl8_mem.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pxl8_simd.h"
|
||||
|
||||
struct pxl8_cpu_render_target {
|
||||
u8* framebuffer;
|
||||
u32 height;
|
||||
|
|
@ -72,8 +72,8 @@ static pxl8_light_result calc_vertex_light(
|
|||
if (sky_factor < 0.0f) sky_factor = 0.0f;
|
||||
intensity += sky_factor * frame->uniforms.celestial_intensity * 0.3f;
|
||||
|
||||
for (u32 i = 0; i < frame->uniforms.num_lights; i++) {
|
||||
const pxl8_light* light = &frame->uniforms.lights[i];
|
||||
for (u32 i = 0; i < frame->lights_count; i++) {
|
||||
const pxl8_light* light = &frame->lights[i];
|
||||
f32 contrib = calc_light_intensity(light, world_pos, normal);
|
||||
if (contrib > 0.0f) {
|
||||
intensity += contrib;
|
||||
|
|
@ -119,6 +119,7 @@ struct pxl8_cpu_backend {
|
|||
pxl8_cpu_render_target* target_stack[PXL8_MAX_TARGET_STACK];
|
||||
u32 target_stack_depth;
|
||||
const pxl8_colormap* colormap;
|
||||
const pxl8_palette_cube* palette_cube;
|
||||
const u32* palette;
|
||||
pxl8_3d_frame frame;
|
||||
pxl8_mat4 mvp;
|
||||
|
|
@ -173,7 +174,7 @@ static inline void clip_line_2d(i32* x0, i32* y0, i32* x1, i32* y1, i32 w, i32 h
|
|||
}
|
||||
|
||||
pxl8_cpu_backend* pxl8_cpu_create(u32 width, u32 height) {
|
||||
pxl8_cpu_backend* cpu = calloc(1, sizeof(pxl8_cpu_backend));
|
||||
pxl8_cpu_backend* cpu = pxl8_calloc(1, sizeof(pxl8_cpu_backend));
|
||||
if (!cpu) return NULL;
|
||||
|
||||
pxl8_cpu_render_target_desc desc = {
|
||||
|
|
@ -184,7 +185,7 @@ pxl8_cpu_backend* pxl8_cpu_create(u32 width, u32 height) {
|
|||
};
|
||||
pxl8_cpu_render_target* base_target = pxl8_cpu_create_render_target(&desc);
|
||||
if (!base_target) {
|
||||
free(cpu);
|
||||
pxl8_free(cpu);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -193,10 +194,10 @@ pxl8_cpu_backend* pxl8_cpu_create(u32 width, u32 height) {
|
|||
cpu->current_target = base_target;
|
||||
|
||||
cpu->output_size = width * height;
|
||||
cpu->output = calloc(cpu->output_size, sizeof(u32));
|
||||
cpu->output = pxl8_calloc(cpu->output_size, sizeof(u32));
|
||||
if (!cpu->output) {
|
||||
pxl8_cpu_destroy_render_target(base_target);
|
||||
free(cpu);
|
||||
pxl8_free(cpu);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -208,14 +209,14 @@ void pxl8_cpu_destroy(pxl8_cpu_backend* cpu) {
|
|||
for (u32 i = 0; i < cpu->target_stack_depth; i++) {
|
||||
pxl8_cpu_destroy_render_target(cpu->target_stack[i]);
|
||||
}
|
||||
free(cpu->output);
|
||||
free(cpu);
|
||||
pxl8_free(cpu->output);
|
||||
pxl8_free(cpu);
|
||||
}
|
||||
|
||||
void pxl8_cpu_begin_frame(pxl8_cpu_backend* cpu, const pxl8_3d_frame* frame) {
|
||||
if (!cpu || !frame) return;
|
||||
cpu->frame = *frame;
|
||||
cpu->mvp = pxl8_mat4_mul(frame->projection, frame->view);
|
||||
cpu->mvp = pxl8_mat4_multiply(frame->projection, frame->view);
|
||||
}
|
||||
|
||||
void pxl8_cpu_end_frame(pxl8_cpu_backend* cpu) {
|
||||
|
|
@ -231,14 +232,12 @@ void pxl8_cpu_clear(pxl8_cpu_backend* cpu, u8 color) {
|
|||
void pxl8_cpu_clear_depth(pxl8_cpu_backend* cpu) {
|
||||
if (!cpu || !cpu->current_target) return;
|
||||
pxl8_cpu_render_target* render_target = cpu->current_target;
|
||||
if (render_target->zbuffer) {
|
||||
u32 count = render_target->width * render_target->height;
|
||||
for (u32 i = 0; i < count; i++) {
|
||||
render_target->zbuffer[i] = 0xFFFF;
|
||||
}
|
||||
if (render_target->zbuffer) {
|
||||
memset(render_target->zbuffer, 0xFF, count * sizeof(u16));
|
||||
}
|
||||
if (render_target->light_accum) {
|
||||
memset(render_target->light_accum, 0, render_target->width * render_target->height * sizeof(u32));
|
||||
memset(render_target->light_accum, 0, count * sizeof(u32));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -246,6 +245,10 @@ void pxl8_cpu_set_colormap(pxl8_cpu_backend* cpu, const pxl8_colormap* cm) {
|
|||
if (cpu) cpu->colormap = cm;
|
||||
}
|
||||
|
||||
void pxl8_cpu_set_palette_cube(pxl8_cpu_backend* cpu, const pxl8_palette_cube* cube) {
|
||||
if (cpu) cpu->palette_cube = cube;
|
||||
}
|
||||
|
||||
void pxl8_cpu_set_palette(pxl8_cpu_backend* cpu, const u32* palette) {
|
||||
if (cpu) cpu->palette = palette;
|
||||
}
|
||||
|
|
@ -366,8 +369,8 @@ void pxl8_cpu_draw_line_3d(pxl8_cpu_backend* cpu, pxl8_vec3 v0, pxl8_vec3 v1, u8
|
|||
if (!cpu || !cpu->current_target) return;
|
||||
pxl8_cpu_render_target* render_target = cpu->current_target;
|
||||
|
||||
pxl8_vec4 c0 = pxl8_mat4_mul_vec4(cpu->mvp, (pxl8_vec4){v0.x, v0.y, v0.z, 1.0f});
|
||||
pxl8_vec4 c1 = pxl8_mat4_mul_vec4(cpu->mvp, (pxl8_vec4){v1.x, v1.y, v1.z, 1.0f});
|
||||
pxl8_vec4 c0 = pxl8_mat4_multiply_vec4(cpu->mvp, (pxl8_vec4){v0.x, v0.y, v0.z, 1.0f});
|
||||
pxl8_vec4 c1 = pxl8_mat4_multiply_vec4(cpu->mvp, (pxl8_vec4){v1.x, v1.y, v1.z, 1.0f});
|
||||
|
||||
if (c0.w <= 0.0f || c1.w <= 0.0f) return;
|
||||
|
||||
|
|
@ -699,6 +702,22 @@ static void rasterize_triangle_opaque(
|
|||
f32 v_end = (vw + dvw * steps) * pw_end;
|
||||
f32 l_start = lw * pw_start;
|
||||
f32 l_end = (lw + d_lw * steps) * pw_end;
|
||||
|
||||
f32 fog_density = cpu->frame.uniforms.fog_density;
|
||||
if (fog_density > 0.0f) {
|
||||
f32 z_end_fog = z + dz * steps;
|
||||
f32 t_start = (z + 1.0f) * 0.5f;
|
||||
f32 t_end = (z_end_fog + 1.0f) * 0.5f;
|
||||
if (t_start < 0.0f) t_start = 0.0f;
|
||||
if (t_end < 0.0f) t_end = 0.0f;
|
||||
f32 fog_start = t_start * t_start * fog_density;
|
||||
f32 fog_end = t_end * t_end * fog_density;
|
||||
if (fog_start > 1.0f) fog_start = 1.0f;
|
||||
if (fog_end > 1.0f) fog_end = 1.0f;
|
||||
l_start *= (1.0f - fog_start);
|
||||
l_end *= (1.0f - fog_end);
|
||||
}
|
||||
|
||||
if (l_start > 255.0f) l_start = 255.0f;
|
||||
if (l_end > 255.0f) l_end = 255.0f;
|
||||
|
||||
|
|
@ -727,7 +746,7 @@ static void rasterize_triangle_opaque(
|
|||
if (dither) {
|
||||
light = pxl8_dither_light(light, (u32)px, (u32)y);
|
||||
}
|
||||
u8 pal_idx = cpu->colormap ? pxl8_colormap_lookup(cpu->colormap, tex_idx, light) : tex_idx;
|
||||
u8 pal_idx = cpu->colormap ? pxl8_colormap_lookup(cpu->colormap, tex_idx, PXL8_LIGHT_WHITE, light) : tex_idx;
|
||||
prow[px] = pal_idx;
|
||||
zrow[px] = z16;
|
||||
if (light_accum) {
|
||||
|
|
@ -948,6 +967,22 @@ static void rasterize_triangle_alpha(
|
|||
f32 v_end = (vw + dvw * steps) * pw_end;
|
||||
f32 l_start = lw * pw_start;
|
||||
f32 l_end = (lw + d_lw * steps) * pw_end;
|
||||
|
||||
f32 fog_density = cpu->frame.uniforms.fog_density;
|
||||
if (fog_density > 0.0f) {
|
||||
f32 z_end_fog = z + dz * steps;
|
||||
f32 t_start = (z + 1.0f) * 0.5f;
|
||||
f32 t_end = (z_end_fog + 1.0f) * 0.5f;
|
||||
if (t_start < 0.0f) t_start = 0.0f;
|
||||
if (t_end < 0.0f) t_end = 0.0f;
|
||||
f32 fog_start = t_start * t_start * fog_density;
|
||||
f32 fog_end = t_end * t_end * fog_density;
|
||||
if (fog_start > 1.0f) fog_start = 1.0f;
|
||||
if (fog_end > 1.0f) fog_end = 1.0f;
|
||||
l_start *= (1.0f - fog_start);
|
||||
l_end *= (1.0f - fog_end);
|
||||
}
|
||||
|
||||
if (l_start > 255.0f) l_start = 255.0f;
|
||||
if (l_end > 255.0f) l_end = 255.0f;
|
||||
|
||||
|
|
@ -974,7 +1009,7 @@ static void rasterize_triangle_alpha(
|
|||
if (dither) {
|
||||
light = pxl8_dither_light(light, (u32)px, (u32)y);
|
||||
}
|
||||
u8 src_idx = cpu->colormap ? pxl8_colormap_lookup(cpu->colormap, tex_idx, light) : tex_idx;
|
||||
u8 src_idx = cpu->colormap ? pxl8_colormap_lookup(cpu->colormap, tex_idx, PXL8_LIGHT_WHITE, light) : tex_idx;
|
||||
|
||||
if (mat_alpha >= 128) {
|
||||
prow[px] = src_idx;
|
||||
|
|
@ -1002,11 +1037,42 @@ static void rasterize_triangle_alpha(
|
|||
}
|
||||
}
|
||||
|
||||
static void rasterize_triangle_wireframe(
|
||||
pxl8_cpu_backend* cpu,
|
||||
const vertex_output* vo0, const vertex_output* vo1, const vertex_output* vo2,
|
||||
u8 color, bool double_sided
|
||||
) {
|
||||
pxl8_cpu_render_target* render_target = cpu->current_target;
|
||||
f32 hw = (f32)render_target->width * 0.5f;
|
||||
f32 hh = (f32)render_target->height * 0.5f;
|
||||
|
||||
i32 x0 = (i32)(hw + vo0->clip_pos.x / vo0->clip_pos.w * hw);
|
||||
i32 y0 = (i32)(hh - vo0->clip_pos.y / vo0->clip_pos.w * hh);
|
||||
i32 x1 = (i32)(hw + vo1->clip_pos.x / vo1->clip_pos.w * hw);
|
||||
i32 y1 = (i32)(hh - vo1->clip_pos.y / vo1->clip_pos.w * hh);
|
||||
i32 x2 = (i32)(hw + vo2->clip_pos.x / vo2->clip_pos.w * hw);
|
||||
i32 y2 = (i32)(hh - vo2->clip_pos.y / vo2->clip_pos.w * hh);
|
||||
|
||||
if (!double_sided) {
|
||||
i32 cross = (x1 - x0) * (y2 - y0) - (y1 - y0) * (x2 - x0);
|
||||
if (cross >= 0) return;
|
||||
}
|
||||
|
||||
pxl8_cpu_draw_line_2d(cpu, x0, y0, x1, y1, color);
|
||||
pxl8_cpu_draw_line_2d(cpu, x1, y1, x2, y2, color);
|
||||
pxl8_cpu_draw_line_2d(cpu, x2, y2, x0, y0, color);
|
||||
}
|
||||
|
||||
static void dispatch_triangle(
|
||||
pxl8_cpu_backend* cpu,
|
||||
const vertex_output* vo0, const vertex_output* vo1, const vertex_output* vo2,
|
||||
const pxl8_atlas* textures, const pxl8_material* material
|
||||
const pxl8_atlas* textures, const pxl8_gfx_material* material
|
||||
) {
|
||||
if (material->wireframe) {
|
||||
rasterize_triangle_wireframe(cpu, vo0, vo1, vo2, 15, material->double_sided);
|
||||
return;
|
||||
}
|
||||
|
||||
pxl8_cpu_render_target* render_target = cpu->current_target;
|
||||
tri_setup setup;
|
||||
if (!setup_triangle(&setup, vo0, vo1, vo2, render_target->width, render_target->height, material->double_sided)) {
|
||||
|
|
@ -1029,13 +1095,13 @@ void pxl8_cpu_draw_mesh(
|
|||
pxl8_cpu_backend* cpu,
|
||||
const pxl8_mesh* mesh,
|
||||
const pxl8_mat4* model,
|
||||
const pxl8_material* material,
|
||||
const pxl8_gfx_material* material,
|
||||
const pxl8_atlas* textures
|
||||
) {
|
||||
if (!cpu || !mesh || !model || !material || mesh->index_count < 3 || !cpu->current_target) return;
|
||||
|
||||
pxl8_mat4 mv = pxl8_mat4_mul(cpu->frame.view, *model);
|
||||
pxl8_mat4 mvp = pxl8_mat4_mul(cpu->frame.projection, mv);
|
||||
pxl8_mat4 mv = pxl8_mat4_multiply(cpu->frame.view, *model);
|
||||
pxl8_mat4 mvp = pxl8_mat4_multiply(cpu->frame.projection, mv);
|
||||
|
||||
f32 near = cpu->frame.near_clip > 0.0f ? cpu->frame.near_clip : 0.1f;
|
||||
|
||||
|
|
@ -1054,13 +1120,13 @@ void pxl8_cpu_draw_mesh(
|
|||
pxl8_vec4 p1 = {v1->position.x, v1->position.y, v1->position.z, 1.0f};
|
||||
pxl8_vec4 p2 = {v2->position.x, v2->position.y, v2->position.z, 1.0f};
|
||||
|
||||
vo0.clip_pos = pxl8_mat4_mul_vec4(mvp, p0);
|
||||
vo1.clip_pos = pxl8_mat4_mul_vec4(mvp, p1);
|
||||
vo2.clip_pos = pxl8_mat4_mul_vec4(mvp, p2);
|
||||
vo0.clip_pos = pxl8_mat4_multiply_vec4(mvp, p0);
|
||||
vo1.clip_pos = pxl8_mat4_multiply_vec4(mvp, p1);
|
||||
vo2.clip_pos = pxl8_mat4_multiply_vec4(mvp, p2);
|
||||
|
||||
pxl8_vec4 w0 = pxl8_mat4_mul_vec4(*model, p0);
|
||||
pxl8_vec4 w1 = pxl8_mat4_mul_vec4(*model, p1);
|
||||
pxl8_vec4 w2 = pxl8_mat4_mul_vec4(*model, p2);
|
||||
pxl8_vec4 w0 = pxl8_mat4_multiply_vec4(*model, p0);
|
||||
pxl8_vec4 w1 = pxl8_mat4_multiply_vec4(*model, p1);
|
||||
pxl8_vec4 w2 = pxl8_mat4_multiply_vec4(*model, p2);
|
||||
vo0.world_pos = (pxl8_vec3){w0.x, w0.y, w0.z};
|
||||
vo1.world_pos = (pxl8_vec3){w1.x, w1.y, w1.z};
|
||||
vo2.world_pos = (pxl8_vec3){w2.x, w2.y, w2.z};
|
||||
|
|
@ -1078,9 +1144,9 @@ void pxl8_cpu_draw_mesh(
|
|||
vo2.color = v2->color;
|
||||
|
||||
if (material->dynamic_lighting) {
|
||||
pxl8_vec3 n0 = pxl8_vec3_normalize(pxl8_mat4_mul_vec3(*model, v0->normal));
|
||||
pxl8_vec3 n1 = pxl8_vec3_normalize(pxl8_mat4_mul_vec3(*model, v1->normal));
|
||||
pxl8_vec3 n2 = pxl8_vec3_normalize(pxl8_mat4_mul_vec3(*model, v2->normal));
|
||||
pxl8_vec3 n0 = pxl8_vec3_normalize(pxl8_mat4_multiply_vec3(*model, v0->normal));
|
||||
pxl8_vec3 n1 = pxl8_vec3_normalize(pxl8_mat4_multiply_vec3(*model, v1->normal));
|
||||
pxl8_vec3 n2 = pxl8_vec3_normalize(pxl8_mat4_multiply_vec3(*model, v2->normal));
|
||||
|
||||
pxl8_light_result lr0 = calc_vertex_light(vo0.world_pos, n0, &cpu->frame);
|
||||
pxl8_light_result lr1 = calc_vertex_light(vo1.world_pos, n1, &cpu->frame);
|
||||
|
|
@ -1111,44 +1177,6 @@ void pxl8_cpu_draw_mesh(
|
|||
}
|
||||
}
|
||||
|
||||
void pxl8_cpu_draw_mesh_wireframe(
|
||||
pxl8_cpu_backend* cpu,
|
||||
const pxl8_mesh* mesh,
|
||||
pxl8_mat4 model,
|
||||
u8 color
|
||||
) {
|
||||
if (!cpu || !cpu->current_target || !mesh || mesh->index_count < 3) return;
|
||||
pxl8_cpu_render_target* render_target = cpu->current_target;
|
||||
|
||||
pxl8_mat4 mvp = pxl8_mat4_mul(cpu->frame.projection, pxl8_mat4_mul(cpu->frame.view, model));
|
||||
|
||||
for (u32 i = 0; i < mesh->index_count; i += 3) {
|
||||
const pxl8_vertex* v0 = &mesh->vertices[mesh->indices[i]];
|
||||
const pxl8_vertex* v1 = &mesh->vertices[mesh->indices[i + 1]];
|
||||
const pxl8_vertex* v2 = &mesh->vertices[mesh->indices[i + 2]];
|
||||
|
||||
pxl8_vec4 c0 = pxl8_mat4_mul_vec4(mvp, (pxl8_vec4){v0->position.x, v0->position.y, v0->position.z, 1.0f});
|
||||
pxl8_vec4 c1 = pxl8_mat4_mul_vec4(mvp, (pxl8_vec4){v1->position.x, v1->position.y, v1->position.z, 1.0f});
|
||||
pxl8_vec4 c2 = pxl8_mat4_mul_vec4(mvp, (pxl8_vec4){v2->position.x, v2->position.y, v2->position.z, 1.0f});
|
||||
|
||||
if (c0.w <= 0.0f || c1.w <= 0.0f || c2.w <= 0.0f) continue;
|
||||
|
||||
f32 hw = (f32)render_target->width * 0.5f;
|
||||
f32 hh = (f32)render_target->height * 0.5f;
|
||||
|
||||
i32 x0 = (i32)(hw + c0.x / c0.w * hw);
|
||||
i32 y0 = (i32)(hh - c0.y / c0.w * hh);
|
||||
i32 x1 = (i32)(hw + c1.x / c1.w * hw);
|
||||
i32 y1 = (i32)(hh - c1.y / c1.w * hh);
|
||||
i32 x2 = (i32)(hw + c2.x / c2.w * hw);
|
||||
i32 y2 = (i32)(hh - c2.y / c2.w * hh);
|
||||
|
||||
pxl8_cpu_draw_line_2d(cpu, x0, y0, x1, y1, color);
|
||||
pxl8_cpu_draw_line_2d(cpu, x1, y1, x2, y2, color);
|
||||
pxl8_cpu_draw_line_2d(cpu, x2, y2, x0, y0, color);
|
||||
}
|
||||
}
|
||||
|
||||
u8* pxl8_cpu_get_framebuffer(pxl8_cpu_backend* cpu) {
|
||||
if (!cpu || cpu->target_stack_depth == 0) return NULL;
|
||||
return cpu->target_stack[0]->framebuffer;
|
||||
|
|
@ -1167,33 +1195,33 @@ u32 pxl8_cpu_get_width(const pxl8_cpu_backend* cpu) {
|
|||
pxl8_cpu_render_target* pxl8_cpu_create_render_target(const pxl8_cpu_render_target_desc* desc) {
|
||||
if (!desc) return NULL;
|
||||
|
||||
pxl8_cpu_render_target* target = calloc(1, sizeof(pxl8_cpu_render_target));
|
||||
pxl8_cpu_render_target* target = pxl8_calloc(1, sizeof(pxl8_cpu_render_target));
|
||||
if (!target) return NULL;
|
||||
|
||||
u32 size = desc->width * desc->height;
|
||||
target->width = desc->width;
|
||||
target->height = desc->height;
|
||||
target->framebuffer = calloc(size, sizeof(u8));
|
||||
target->framebuffer = pxl8_calloc(size, sizeof(u8));
|
||||
if (!target->framebuffer) {
|
||||
free(target);
|
||||
pxl8_free(target);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (desc->with_depth) {
|
||||
target->zbuffer = calloc(size, sizeof(u16));
|
||||
target->zbuffer = pxl8_calloc(size, sizeof(u16));
|
||||
if (!target->zbuffer) {
|
||||
free(target->framebuffer);
|
||||
free(target);
|
||||
pxl8_free(target->framebuffer);
|
||||
pxl8_free(target);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (desc->with_lighting) {
|
||||
target->light_accum = calloc(size, sizeof(u32));
|
||||
target->light_accum = pxl8_calloc(size, sizeof(u32));
|
||||
if (!target->light_accum) {
|
||||
free(target->zbuffer);
|
||||
free(target->framebuffer);
|
||||
free(target);
|
||||
pxl8_free(target->zbuffer);
|
||||
pxl8_free(target->framebuffer);
|
||||
pxl8_free(target);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
|
@ -1203,10 +1231,10 @@ pxl8_cpu_render_target* pxl8_cpu_create_render_target(const pxl8_cpu_render_targ
|
|||
|
||||
void pxl8_cpu_destroy_render_target(pxl8_cpu_render_target* target) {
|
||||
if (!target) return;
|
||||
free(target->light_accum);
|
||||
free(target->zbuffer);
|
||||
free(target->framebuffer);
|
||||
free(target);
|
||||
pxl8_free(target->light_accum);
|
||||
pxl8_free(target->zbuffer);
|
||||
pxl8_free(target->framebuffer);
|
||||
pxl8_free(target);
|
||||
}
|
||||
|
||||
pxl8_cpu_render_target* pxl8_cpu_get_target(pxl8_cpu_backend* cpu) {
|
||||
|
|
@ -1237,10 +1265,12 @@ void pxl8_cpu_blit(pxl8_cpu_backend* cpu, pxl8_cpu_render_target* src, i32 x, i3
|
|||
for (i32 row = 0; row < copy_h; row++) {
|
||||
u8* src_row = src->framebuffer + (src_y0 + row) * src->width + src_x0;
|
||||
u8* dst_row = dst->framebuffer + (dst_y0 + row) * dst->width + dst_x0;
|
||||
u32* light_row = dst->light_accum ? dst->light_accum + (dst_y0 + row) * dst->width + dst_x0 : NULL;
|
||||
for (i32 col = 0; col < copy_w; col++) {
|
||||
u8 pixel = src_row[col];
|
||||
if (pixel != transparent_idx) {
|
||||
dst_row[col] = pixel;
|
||||
if (light_row) light_row[col] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1300,13 +1330,9 @@ u32* pxl8_cpu_get_output(pxl8_cpu_backend* cpu) {
|
|||
|
||||
void pxl8_cpu_render_glows(
|
||||
pxl8_cpu_backend* cpu,
|
||||
const pxl8_glow_source* glows,
|
||||
u32 glow_count,
|
||||
const pxl8_additive_table* additive,
|
||||
const pxl8_palette_cube* palette_cube,
|
||||
const pxl8_overbright_table* overbright
|
||||
const pxl8_glow* glows,
|
||||
u32 glow_count
|
||||
) {
|
||||
(void)overbright;
|
||||
if (!cpu || cpu->target_stack_depth == 0) return;
|
||||
|
||||
pxl8_cpu_render_target* target = cpu->target_stack[cpu->target_stack_depth - 1];
|
||||
|
|
@ -1319,7 +1345,7 @@ void pxl8_cpu_render_glows(
|
|||
u32* light_accum = target->light_accum;
|
||||
|
||||
for (u32 gi = 0; gi < glow_count; gi++) {
|
||||
const pxl8_glow_source* glow = &glows[gi];
|
||||
const pxl8_glow* glow = &glows[gi];
|
||||
i32 cx = glow->x;
|
||||
i32 cy = glow->y;
|
||||
i32 radius = glow->radius;
|
||||
|
|
@ -1392,25 +1418,21 @@ void pxl8_cpu_render_glows(
|
|||
u8 int_val = intensity < 1.0f ? (u8)(intensity * 255.0f) : 255;
|
||||
u8 light_level = pxl8_ordered_dither(int_val, (u32)x, (u32)y);
|
||||
|
||||
// Skip very dark pixels to create stippled transparency effect
|
||||
if (light_level < 8) continue;
|
||||
|
||||
u8 shaded = pxl8_colormap_lookup(cpu->colormap, base_color, light_level);
|
||||
u8 shaded = pxl8_colormap_lookup(cpu->colormap, base_color, PXL8_LIGHT_WHITE, light_level);
|
||||
|
||||
if (intensity > 1.0f && palette_cube) {
|
||||
f32 over = intensity - 1.0f;
|
||||
if (over > 1.0f) over = 1.0f;
|
||||
u8 r, g, b;
|
||||
pxl8_palette_cube_get_rgb(palette_cube, shaded, &r, &g, &b);
|
||||
u8 or = (u8)(r + (255 - r) * over);
|
||||
u8 og = (u8)(g + (255 - g) * over);
|
||||
u8 ob = (u8)(b + (255 - b) * over);
|
||||
shaded = pxl8_palette_cube_lookup_stable(palette_cube, or, og, ob);
|
||||
}
|
||||
|
||||
u8 dst = pixels[idx];
|
||||
if (additive) {
|
||||
pixels[idx] = pxl8_additive_blend(additive, shaded, dst);
|
||||
if (cpu->palette_cube) {
|
||||
u8 sr, sg, sb, dr, dg, db;
|
||||
pxl8_palette_cube_get_rgb(cpu->palette_cube, shaded, &sr, &sg, &sb);
|
||||
pxl8_palette_cube_get_rgb(cpu->palette_cube, pixels[idx], &dr, &dg, &db);
|
||||
u32 r = (u32)sr + (u32)dr;
|
||||
u32 g = (u32)sg + (u32)dg;
|
||||
u32 b = (u32)sb + (u32)db;
|
||||
if (r > 255) r = 255;
|
||||
if (g > 255) g = 255;
|
||||
if (b > 255) b = 255;
|
||||
pixels[idx] = pxl8_palette_cube_lookup(cpu->palette_cube, (u8)r, (u8)g, (u8)b);
|
||||
} else {
|
||||
pixels[idx] = shaded;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_atlas.h"
|
||||
#include "pxl8_blend.h"
|
||||
#include "pxl8_colormap.h"
|
||||
#include "pxl8_gfx.h"
|
||||
#include "pxl8_gfx3d.h"
|
||||
#include "pxl8_lightmap.h"
|
||||
#include "pxl8_math.h"
|
||||
#include "pxl8_mesh.h"
|
||||
#include "pxl8_palette.h"
|
||||
#include "pxl8_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
@ -34,6 +35,10 @@ void pxl8_cpu_clear_depth(pxl8_cpu_backend* cpu);
|
|||
|
||||
void pxl8_cpu_set_colormap(pxl8_cpu_backend* cpu, const pxl8_colormap* cm);
|
||||
void pxl8_cpu_set_palette(pxl8_cpu_backend* cpu, const u32* palette);
|
||||
void pxl8_cpu_set_palette_cube(pxl8_cpu_backend* cpu, const pxl8_palette_cube* cube);
|
||||
|
||||
u32 pxl8_cpu_add_lightmap(pxl8_cpu_backend* cpu, pxl8_lightmap* lm);
|
||||
pxl8_lightmap* pxl8_cpu_get_lightmap(pxl8_cpu_backend* cpu, u32 id);
|
||||
|
||||
void pxl8_cpu_draw_pixel(pxl8_cpu_backend* cpu, i32 x, i32 y, u8 color);
|
||||
u8 pxl8_cpu_get_pixel(pxl8_cpu_backend* cpu, i32 x, i32 y);
|
||||
|
|
@ -48,17 +53,10 @@ void pxl8_cpu_draw_mesh(
|
|||
pxl8_cpu_backend* cpu,
|
||||
const pxl8_mesh* mesh,
|
||||
const pxl8_mat4* model,
|
||||
const pxl8_material* material,
|
||||
const pxl8_gfx_material* material,
|
||||
const pxl8_atlas* textures
|
||||
);
|
||||
|
||||
void pxl8_cpu_draw_mesh_wireframe(
|
||||
pxl8_cpu_backend* cpu,
|
||||
const pxl8_mesh* mesh,
|
||||
pxl8_mat4 model,
|
||||
u8 color
|
||||
);
|
||||
|
||||
u8* pxl8_cpu_get_framebuffer(pxl8_cpu_backend* cpu);
|
||||
u32* pxl8_cpu_get_output(pxl8_cpu_backend* cpu);
|
||||
u32 pxl8_cpu_get_height(const pxl8_cpu_backend* cpu);
|
||||
|
|
@ -66,11 +64,8 @@ u32 pxl8_cpu_get_width(const pxl8_cpu_backend* cpu);
|
|||
|
||||
void pxl8_cpu_render_glows(
|
||||
pxl8_cpu_backend* cpu,
|
||||
const pxl8_glow_source* glows,
|
||||
u32 glow_count,
|
||||
const pxl8_additive_table* additive,
|
||||
const pxl8_palette_cube* palette_cube,
|
||||
const pxl8_overbright_table* overbright
|
||||
const pxl8_glow* glows,
|
||||
u32 glow_count
|
||||
);
|
||||
|
||||
void pxl8_cpu_resolve(pxl8_cpu_backend* cpu);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "pxl8_font.h"
|
||||
#include "pxl8_mem.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
|
@ -15,7 +16,7 @@ pxl8_result pxl8_font_create_atlas(const pxl8_font* font, u8** atlas_data, i32*
|
|||
*atlas_height = rows_needed * font->default_height;
|
||||
|
||||
i32 atlas_size = (*atlas_width) * (*atlas_height);
|
||||
*atlas_data = (u8*)malloc(atlas_size);
|
||||
*atlas_data = (u8*)pxl8_malloc(atlas_size);
|
||||
if (!*atlas_data) {
|
||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
#include "pxl8_ase.h"
|
||||
#include "pxl8_atlas.h"
|
||||
#include "pxl8_backend.h"
|
||||
#include "pxl8_blend.h"
|
||||
#include "pxl8_blit.h"
|
||||
#include "pxl8_color.h"
|
||||
#include "pxl8_colormap.h"
|
||||
|
|
@ -15,6 +14,7 @@
|
|||
#include "pxl8_log.h"
|
||||
#include "pxl8_macros.h"
|
||||
#include "pxl8_math.h"
|
||||
#include "pxl8_mem.h"
|
||||
#include "pxl8_sys.h"
|
||||
#include "pxl8_types.h"
|
||||
|
||||
|
|
@ -25,7 +25,6 @@ typedef struct pxl8_sprite_cache_entry {
|
|||
} pxl8_sprite_cache_entry;
|
||||
|
||||
struct pxl8_gfx {
|
||||
pxl8_additive_table* additive_table;
|
||||
pxl8_atlas* atlas;
|
||||
pxl8_gfx_backend backend;
|
||||
pxl8_colormap* colormap;
|
||||
|
|
@ -35,7 +34,6 @@ struct pxl8_gfx {
|
|||
pxl8_frustum frustum;
|
||||
const pxl8_hal* hal;
|
||||
bool initialized;
|
||||
pxl8_overbright_table* overbright_table;
|
||||
pxl8_palette* palette;
|
||||
pxl8_palette_cube* palette_cube;
|
||||
pxl8_pixel_mode pixel_mode;
|
||||
|
|
@ -44,6 +42,7 @@ struct pxl8_gfx {
|
|||
u32 sprite_cache_capacity;
|
||||
u32 sprite_cache_count;
|
||||
pxl8_viewport viewport;
|
||||
pxl8_mat4 view_proj;
|
||||
};
|
||||
|
||||
pxl8_bounds pxl8_gfx_get_bounds(pxl8_gfx* gfx) {
|
||||
|
|
@ -78,10 +77,14 @@ i32 pxl8_gfx_get_width(const pxl8_gfx* gfx) {
|
|||
return gfx ? gfx->framebuffer_width : 0;
|
||||
}
|
||||
|
||||
pxl8_palette* pxl8_gfx_get_palette(pxl8_gfx* gfx) {
|
||||
pxl8_palette* pxl8_gfx_palette(pxl8_gfx* gfx) {
|
||||
return gfx ? gfx->palette : NULL;
|
||||
}
|
||||
|
||||
pxl8_colormap* pxl8_gfx_colormap(pxl8_gfx* gfx) {
|
||||
return gfx ? gfx->colormap : NULL;
|
||||
}
|
||||
|
||||
u8 pxl8_gfx_find_color(pxl8_gfx* gfx, u32 color) {
|
||||
if (!gfx || !gfx->palette) return 0;
|
||||
if (color <= 0xFFFFFF) color = (color << 8) | 0xFF;
|
||||
|
|
@ -91,37 +94,24 @@ u8 pxl8_gfx_find_color(pxl8_gfx* gfx, u32 color) {
|
|||
return pxl8_palette_find_closest(gfx->palette, r, g, b);
|
||||
}
|
||||
|
||||
void pxl8_gfx_set_palette(pxl8_gfx* gfx, pxl8_palette* pal) {
|
||||
if (!gfx) return;
|
||||
if (gfx->palette) {
|
||||
pxl8_palette_destroy(gfx->palette);
|
||||
}
|
||||
gfx->palette = pal;
|
||||
|
||||
if (gfx->palette && gfx->pixel_mode != PXL8_PIXEL_HICOLOR) {
|
||||
u32* colors = pxl8_palette_colors(gfx->palette);
|
||||
if (gfx->colormap) {
|
||||
pxl8_colormap_generate(gfx->colormap, colors, NULL);
|
||||
}
|
||||
if (gfx->backend.type == PXL8_GFX_BACKEND_CPU) {
|
||||
pxl8_cpu_set_palette(gfx->backend.cpu, colors);
|
||||
}
|
||||
void pxl8_gfx_set_palette_colors(pxl8_gfx* gfx, const u32* colors, u16 count) {
|
||||
if (!gfx || !gfx->palette || !colors) return;
|
||||
pxl8_set_palette(gfx->palette, colors, count);
|
||||
if (gfx->pixel_mode != PXL8_PIXEL_HICOLOR && gfx->backend.type == PXL8_GFX_BACKEND_CPU) {
|
||||
pxl8_cpu_set_palette(gfx->backend.cpu, pxl8_palette_colors(gfx->palette));
|
||||
}
|
||||
}
|
||||
|
||||
i32 pxl8_gfx_load_palette(pxl8_gfx* gfx, const char* filepath) {
|
||||
if (!gfx || !filepath) return -1;
|
||||
pxl8_palette* pal = gfx->palette;
|
||||
if (!pal) return -1;
|
||||
pxl8_result result = pxl8_palette_load_ase(pal, filepath);
|
||||
if (!gfx || !gfx->palette || !filepath) return -1;
|
||||
pxl8_result result = pxl8_palette_load_ase(gfx->palette, filepath);
|
||||
if (result != PXL8_OK) return (i32)result;
|
||||
if (gfx->pixel_mode != PXL8_PIXEL_HICOLOR) {
|
||||
u32* colors = pxl8_palette_colors(pal);
|
||||
if (gfx->colormap) {
|
||||
pxl8_colormap_generate(gfx->colormap, colors, NULL);
|
||||
pxl8_colormap_generate(gfx->colormap, pxl8_palette_colors(gfx->palette));
|
||||
}
|
||||
if (gfx->backend.type == PXL8_GFX_BACKEND_CPU) {
|
||||
pxl8_cpu_set_palette(gfx->backend.cpu, colors);
|
||||
pxl8_cpu_set_palette(gfx->backend.cpu, pxl8_palette_colors(gfx->palette));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
|
@ -133,7 +123,7 @@ pxl8_gfx* pxl8_gfx_create(
|
|||
pxl8_pixel_mode mode,
|
||||
pxl8_resolution resolution
|
||||
) {
|
||||
pxl8_gfx* gfx = (pxl8_gfx*)calloc(1, sizeof(pxl8_gfx));
|
||||
pxl8_gfx* gfx = (pxl8_gfx*)pxl8_calloc(1, sizeof(pxl8_gfx));
|
||||
if (!gfx) {
|
||||
pxl8_error("Failed to allocate graphics context");
|
||||
return NULL;
|
||||
|
|
@ -149,7 +139,7 @@ pxl8_gfx* pxl8_gfx_create(
|
|||
|
||||
if (!gfx->platform_data) {
|
||||
pxl8_error("Platform data cannot be NULL");
|
||||
free(gfx);
|
||||
pxl8_free(gfx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -168,7 +158,7 @@ pxl8_gfx* pxl8_gfx_create(
|
|||
gfx->framebuffer = pxl8_cpu_get_framebuffer(gfx->backend.cpu);
|
||||
|
||||
if (mode != PXL8_PIXEL_HICOLOR) {
|
||||
gfx->colormap = calloc(1, sizeof(pxl8_colormap));
|
||||
gfx->colormap = pxl8_calloc(1, sizeof(pxl8_colormap));
|
||||
if (gfx->colormap) {
|
||||
pxl8_cpu_set_colormap(gfx->backend.cpu, gfx->colormap);
|
||||
}
|
||||
|
|
@ -187,18 +177,16 @@ pxl8_gfx* pxl8_gfx_create(
|
|||
void pxl8_gfx_destroy(pxl8_gfx* gfx) {
|
||||
if (!gfx) return;
|
||||
|
||||
pxl8_additive_table_destroy(gfx->additive_table);
|
||||
pxl8_atlas_destroy(gfx->atlas);
|
||||
if (gfx->backend.type == PXL8_GFX_BACKEND_CPU) {
|
||||
pxl8_cpu_destroy(gfx->backend.cpu);
|
||||
}
|
||||
free(gfx->colormap);
|
||||
pxl8_overbright_table_destroy(gfx->overbright_table);
|
||||
pxl8_free(gfx->colormap);
|
||||
pxl8_palette_cube_destroy(gfx->palette_cube);
|
||||
pxl8_palette_destroy(gfx->palette);
|
||||
free(gfx->sprite_cache);
|
||||
pxl8_free(gfx->sprite_cache);
|
||||
|
||||
free(gfx);
|
||||
pxl8_free(gfx);
|
||||
}
|
||||
|
||||
static pxl8_result pxl8_gfx_ensure_atlas(pxl8_gfx* gfx) {
|
||||
|
|
@ -239,7 +227,7 @@ pxl8_result pxl8_gfx_load_sprite(pxl8_gfx* gfx, const char* path) {
|
|||
|
||||
if (!gfx->sprite_cache) {
|
||||
gfx->sprite_cache_capacity = PXL8_DEFAULT_SPRITE_CACHE_CAPACITY;
|
||||
gfx->sprite_cache = (pxl8_sprite_cache_entry*)calloc(
|
||||
gfx->sprite_cache = (pxl8_sprite_cache_entry*)pxl8_calloc(
|
||||
gfx->sprite_cache_capacity, sizeof(pxl8_sprite_cache_entry)
|
||||
);
|
||||
if (!gfx->sprite_cache) return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
|
|
@ -284,7 +272,7 @@ pxl8_result pxl8_gfx_load_sprite(pxl8_gfx* gfx, const char* path) {
|
|||
|
||||
if (gfx->sprite_cache_count >= gfx->sprite_cache_capacity) {
|
||||
u32 new_capacity = gfx->sprite_cache_capacity * 2;
|
||||
pxl8_sprite_cache_entry* new_cache = (pxl8_sprite_cache_entry*)realloc(
|
||||
pxl8_sprite_cache_entry* new_cache = (pxl8_sprite_cache_entry*)pxl8_realloc(
|
||||
gfx->sprite_cache,
|
||||
new_capacity * sizeof(pxl8_sprite_cache_entry)
|
||||
);
|
||||
|
|
@ -523,6 +511,7 @@ void pxl8_2d_sprite(pxl8_gfx* gfx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h, bo
|
|||
if (!gfx || !gfx->atlas) return;
|
||||
|
||||
u8* framebuffer = NULL;
|
||||
u32* light_accum = NULL;
|
||||
i32 fb_width = 0;
|
||||
i32 fb_height = 0;
|
||||
|
||||
|
|
@ -531,6 +520,7 @@ void pxl8_2d_sprite(pxl8_gfx* gfx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h, bo
|
|||
pxl8_cpu_render_target* render_target = pxl8_cpu_get_target(gfx->backend.cpu);
|
||||
if (!render_target) return;
|
||||
framebuffer = pxl8_cpu_render_target_get_framebuffer(render_target);
|
||||
light_accum = pxl8_cpu_render_target_get_light_accum(render_target);
|
||||
fb_width = (i32)pxl8_cpu_render_target_get_width(render_target);
|
||||
fb_height = (i32)pxl8_cpu_render_target_get_height(render_target);
|
||||
break;
|
||||
|
|
@ -567,6 +557,11 @@ void pxl8_2d_sprite(pxl8_gfx* gfx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h, bo
|
|||
if (is_1to1_scale && is_unclipped && !is_flipped) {
|
||||
const u8* sprite_data = atlas_pixels + entry->y * atlas_width + entry->x;
|
||||
pxl8_blit_indexed(framebuffer, fb_width, sprite_data, atlas_width, x, y, w, h);
|
||||
if (light_accum) {
|
||||
for (i32 py = 0; py < h; py++) {
|
||||
memset(light_accum + (y + py) * fb_width + x, 0, w * sizeof(u32));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i32 py = 0; py < draw_height; py++) {
|
||||
for (i32 px = 0; px < draw_width; px++) {
|
||||
|
|
@ -580,6 +575,7 @@ void pxl8_2d_sprite(pxl8_gfx* gfx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h, bo
|
|||
i32 dest_idx = (dest_y + py) * fb_width + (dest_x + px);
|
||||
|
||||
framebuffer[dest_idx] = pxl8_blend_indexed(atlas_pixels[src_idx], framebuffer[dest_idx]);
|
||||
if (light_accum) light_accum[dest_idx] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -612,13 +608,17 @@ pxl8_3d_frame pxl8_3d_frame_from_camera(const pxl8_3d_camera* camera, const pxl8
|
|||
return frame;
|
||||
}
|
||||
|
||||
void pxl8_3d_begin_frame(pxl8_gfx* gfx, const pxl8_3d_camera* camera, const pxl8_3d_uniforms* uniforms) {
|
||||
void pxl8_3d_begin_frame(pxl8_gfx* gfx, const pxl8_3d_camera* camera, const pxl8_lights* lights, const pxl8_3d_uniforms* uniforms) {
|
||||
if (!gfx || !camera) return;
|
||||
|
||||
pxl8_3d_frame frame = pxl8_3d_frame_from_camera(camera, uniforms);
|
||||
frame.lights = lights ? pxl8_lights_data(lights) : NULL;
|
||||
frame.lights_count = lights ? pxl8_lights_count(lights) : 0;
|
||||
|
||||
pxl8_mat4 vp = pxl8_mat4_multiply(frame.projection, frame.view);
|
||||
|
||||
pxl8_mat4 vp = pxl8_mat4_mul(frame.projection, frame.view);
|
||||
gfx->frustum = pxl8_frustum_from_matrix(vp);
|
||||
gfx->view_proj = vp;
|
||||
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
|
|
@ -634,6 +634,38 @@ const pxl8_frustum* pxl8_3d_get_frustum(pxl8_gfx* gfx) {
|
|||
return &gfx->frustum;
|
||||
}
|
||||
|
||||
const pxl8_mat4* pxl8_3d_get_view_proj(pxl8_gfx* gfx) {
|
||||
if (!gfx) return NULL;
|
||||
return &gfx->view_proj;
|
||||
}
|
||||
|
||||
u32 pxl8_3d_project_points(pxl8_gfx* gfx, const pxl8_vec3* in, pxl8_vec3* out, u32 count, const pxl8_mat4* transform) {
|
||||
if (!gfx || !in || !out) return 0;
|
||||
|
||||
pxl8_mat4 mvp = transform ? pxl8_mat4_multiply(gfx->view_proj, *transform) : gfx->view_proj;
|
||||
|
||||
f32 hw = (f32)gfx->framebuffer_width * 0.5f;
|
||||
f32 hh = (f32)gfx->framebuffer_height * 0.5f;
|
||||
u32 visible = 0;
|
||||
|
||||
for (u32 i = 0; i < count; i++) {
|
||||
pxl8_vec4 clip = pxl8_mat4_multiply_vec4(mvp, (pxl8_vec4){in[i].x, in[i].y, in[i].z, 1.0f});
|
||||
|
||||
if (clip.w <= 0.0f) {
|
||||
out[i].z = -1.0f;
|
||||
continue;
|
||||
}
|
||||
|
||||
f32 inv_w = 1.0f / clip.w;
|
||||
out[i].x = hw + clip.x * inv_w * hw;
|
||||
out[i].y = hh - clip.y * inv_w * hh;
|
||||
out[i].z = clip.w;
|
||||
visible++;
|
||||
}
|
||||
|
||||
return visible;
|
||||
}
|
||||
|
||||
void pxl8_3d_clear(pxl8_gfx* gfx, u8 color) {
|
||||
if (!gfx) return;
|
||||
switch (gfx->backend.type) {
|
||||
|
|
@ -667,7 +699,7 @@ void pxl8_3d_draw_line(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, u8 color) {
|
|||
}
|
||||
}
|
||||
|
||||
void pxl8_3d_draw_mesh(pxl8_gfx* gfx, const pxl8_mesh* mesh, const pxl8_mat4* model, const pxl8_material* material) {
|
||||
void pxl8_3d_draw_mesh(pxl8_gfx* gfx, const pxl8_mesh* mesh, const pxl8_mat4* model, const pxl8_gfx_material* material) {
|
||||
if (!gfx || !mesh || !model || !material) return;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
|
|
@ -678,17 +710,6 @@ void pxl8_3d_draw_mesh(pxl8_gfx* gfx, const pxl8_mesh* mesh, const pxl8_mat4* mo
|
|||
}
|
||||
}
|
||||
|
||||
void pxl8_3d_draw_mesh_wireframe(pxl8_gfx* gfx, const pxl8_mesh* mesh, pxl8_mat4 model, u8 color) {
|
||||
if (!gfx || !mesh) return;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
pxl8_cpu_draw_mesh_wireframe(gfx->backend.cpu, mesh, model, color);
|
||||
break;
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_3d_end_frame(pxl8_gfx* gfx) {
|
||||
if (!gfx) return;
|
||||
switch (gfx->backend.type) {
|
||||
|
|
@ -733,31 +754,27 @@ void pxl8_gfx_pop_target(pxl8_gfx* gfx) {
|
|||
}
|
||||
}
|
||||
|
||||
static void pxl8_gfx_ensure_blend_tables(pxl8_gfx* gfx) {
|
||||
void pxl8_gfx_ensure_blend_tables(pxl8_gfx* gfx) {
|
||||
if (!gfx || !gfx->palette) return;
|
||||
|
||||
if (!gfx->additive_table) {
|
||||
gfx->additive_table = pxl8_additive_table_create(gfx->palette);
|
||||
}
|
||||
if (!gfx->overbright_table) {
|
||||
gfx->overbright_table = pxl8_overbright_table_create(gfx->palette);
|
||||
}
|
||||
if (!gfx->palette_cube) {
|
||||
gfx->palette_cube = pxl8_palette_cube_create(gfx->palette);
|
||||
if (gfx->backend.cpu) {
|
||||
pxl8_cpu_set_palette_cube(gfx->backend.cpu, gfx->palette_cube);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_gfx_blend_tables_update(pxl8_gfx* gfx) {
|
||||
if (!gfx || !gfx->palette) return;
|
||||
|
||||
if (gfx->additive_table) {
|
||||
pxl8_additive_table_rebuild(gfx->additive_table, gfx->palette);
|
||||
}
|
||||
if (gfx->overbright_table) {
|
||||
pxl8_overbright_table_rebuild(gfx->overbright_table, gfx->palette);
|
||||
}
|
||||
pxl8_gfx_ensure_blend_tables(gfx);
|
||||
|
||||
if (gfx->palette_cube) {
|
||||
pxl8_palette_cube_rebuild(gfx->palette_cube, gfx->palette);
|
||||
if (gfx->backend.cpu) {
|
||||
pxl8_cpu_set_palette_cube(gfx->backend.cpu, gfx->palette_cube);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -765,29 +782,33 @@ void pxl8_gfx_colormap_update(pxl8_gfx* gfx) {
|
|||
if (!gfx || !gfx->palette || !gfx->colormap) return;
|
||||
u32* colors = pxl8_palette_colors(gfx->palette);
|
||||
if (colors) {
|
||||
pxl8_colormap_generate(gfx->colormap, colors, NULL);
|
||||
pxl8_colormap_generate(gfx->colormap, colors);
|
||||
}
|
||||
}
|
||||
|
||||
u8 pxl8_gfx_find_closest_color(pxl8_gfx* gfx, u8 r, u8 g, u8 b) {
|
||||
if (!gfx || !gfx->palette) return 0;
|
||||
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_apply_effect(pxl8_gfx* gfx, pxl8_gfx_effect effect, const void* params, u32 count) {
|
||||
if (!gfx || !params || count == 0) return;
|
||||
|
||||
switch (effect) {
|
||||
case PXL8_GFX_EFFECT_GLOWS: {
|
||||
pxl8_gfx_ensure_blend_tables(gfx);
|
||||
if (!gfx->additive_table || !gfx->overbright_table || !gfx->palette_cube) return;
|
||||
|
||||
const pxl8_glow_source* glows = (const pxl8_glow_source*)params;
|
||||
const pxl8_glow* glows = (const pxl8_glow*)params;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
pxl8_cpu_render_glows(
|
||||
gfx->backend.cpu,
|
||||
glows,
|
||||
count,
|
||||
gfx->additive_table,
|
||||
gfx->palette_cube,
|
||||
gfx->overbright_table
|
||||
);
|
||||
pxl8_cpu_render_glows(gfx->backend.cpu, glows, count);
|
||||
break;
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -2,9 +2,12 @@
|
|||
|
||||
#include "pxl8_gfx2d.h"
|
||||
#include "pxl8_gfx3d.h"
|
||||
#include "pxl8_glows.h"
|
||||
#include "pxl8_hal.h"
|
||||
#include "pxl8_colormap.h"
|
||||
#include "pxl8_palette.h"
|
||||
#include "pxl8_types.h"
|
||||
#include "pxl8_gui_palette.h"
|
||||
|
||||
typedef struct pxl8_gfx pxl8_gfx;
|
||||
|
||||
|
|
@ -12,53 +15,6 @@ typedef enum pxl8_gfx_effect {
|
|||
PXL8_GFX_EFFECT_GLOWS = 0,
|
||||
} pxl8_gfx_effect;
|
||||
|
||||
#define PXL8_MAX_GLOWS 256
|
||||
|
||||
typedef enum pxl8_glow_shape {
|
||||
PXL8_GLOW_CIRCLE = 0,
|
||||
PXL8_GLOW_DIAMOND = 1,
|
||||
PXL8_GLOW_SHAFT = 2,
|
||||
} pxl8_glow_shape;
|
||||
|
||||
typedef struct pxl8_glow_source {
|
||||
u8 color;
|
||||
u16 depth;
|
||||
u8 height;
|
||||
u16 intensity;
|
||||
u8 radius;
|
||||
pxl8_glow_shape shape;
|
||||
i16 x;
|
||||
i16 y;
|
||||
} pxl8_glow_source;
|
||||
|
||||
static inline pxl8_glow_source pxl8_glow_create(i32 x, i32 y, u8 radius, u16 intensity, u8 color) {
|
||||
return (pxl8_glow_source){
|
||||
.color = color,
|
||||
.depth = 0xFFFF,
|
||||
.height = 0,
|
||||
.intensity = intensity,
|
||||
.radius = radius,
|
||||
.shape = PXL8_GLOW_CIRCLE,
|
||||
.x = (i16)x,
|
||||
.y = (i16)y,
|
||||
};
|
||||
}
|
||||
|
||||
static inline pxl8_glow_source pxl8_glow_with_depth(pxl8_glow_source g, u16 depth) {
|
||||
g.depth = depth;
|
||||
return g;
|
||||
}
|
||||
|
||||
static inline pxl8_glow_source pxl8_glow_with_shape(pxl8_glow_source g, pxl8_glow_shape shape) {
|
||||
g.shape = shape;
|
||||
return g;
|
||||
}
|
||||
|
||||
static inline pxl8_glow_source pxl8_glow_with_height(pxl8_glow_source g, u8 height) {
|
||||
g.height = height;
|
||||
return g;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
|
@ -76,13 +32,14 @@ pxl8_bounds pxl8_gfx_get_bounds(pxl8_gfx* gfx);
|
|||
u8* pxl8_gfx_get_framebuffer_indexed(pxl8_gfx* gfx);
|
||||
u16* pxl8_gfx_get_framebuffer_hicolor(pxl8_gfx* gfx);
|
||||
i32 pxl8_gfx_get_height(const pxl8_gfx* gfx);
|
||||
pxl8_palette* pxl8_gfx_get_palette(pxl8_gfx* gfx);
|
||||
pxl8_palette* pxl8_gfx_palette(pxl8_gfx* gfx);
|
||||
pxl8_colormap* pxl8_gfx_colormap(pxl8_gfx* gfx);
|
||||
pxl8_pixel_mode pxl8_gfx_get_pixel_mode(pxl8_gfx* gfx);
|
||||
i32 pxl8_gfx_get_width(const pxl8_gfx* gfx);
|
||||
|
||||
i32 pxl8_gfx_load_palette(pxl8_gfx* gfx, const char* filepath);
|
||||
void pxl8_gfx_project(pxl8_gfx* gfx, f32 left, f32 right, f32 top, f32 bottom);
|
||||
void pxl8_gfx_set_palette(pxl8_gfx* gfx, pxl8_palette* pal);
|
||||
void pxl8_gfx_set_palette_colors(pxl8_gfx* gfx, const u32* colors, u16 count);
|
||||
void pxl8_gfx_set_viewport(pxl8_gfx* gfx, pxl8_viewport vp);
|
||||
pxl8_viewport pxl8_gfx_viewport(pxl8_bounds bounds, i32 width, i32 height);
|
||||
|
||||
|
|
@ -97,6 +54,10 @@ void pxl8_gfx_pop_target(pxl8_gfx* gfx);
|
|||
void pxl8_gfx_apply_effect(pxl8_gfx* gfx, pxl8_gfx_effect effect, const void* params, u32 count);
|
||||
void pxl8_gfx_blend_tables_update(pxl8_gfx* gfx);
|
||||
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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,53 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_3d_camera.h"
|
||||
#include "pxl8_lights.h"
|
||||
#include "pxl8_math.h"
|
||||
#include "pxl8_mesh.h"
|
||||
#include "pxl8_types.h"
|
||||
|
||||
typedef struct pxl8_gfx pxl8_gfx;
|
||||
|
||||
#define PXL8_MAX_LIGHTS 16
|
||||
|
||||
typedef struct pxl8_light {
|
||||
pxl8_vec3 position;
|
||||
u8 r, g, b;
|
||||
u8 intensity;
|
||||
f32 radius;
|
||||
f32 radius_sq;
|
||||
f32 inv_radius_sq;
|
||||
} pxl8_light;
|
||||
|
||||
static inline pxl8_light pxl8_light_create(pxl8_vec3 pos, u8 r, u8 g, u8 b, u8 intensity, f32 radius) {
|
||||
f32 radius_sq = radius * radius;
|
||||
return (pxl8_light){
|
||||
.position = pos,
|
||||
.r = r, .g = g, .b = b,
|
||||
.intensity = intensity,
|
||||
.radius = radius,
|
||||
.radius_sq = radius_sq,
|
||||
.inv_radius_sq = radius_sq > 0.0f ? 1.0f / radius_sq : 0.0f,
|
||||
};
|
||||
}
|
||||
|
||||
typedef struct pxl8_3d_uniforms {
|
||||
u8 ambient;
|
||||
pxl8_vec3 celestial_dir;
|
||||
f32 celestial_intensity;
|
||||
u8 fog_color;
|
||||
f32 fog_density;
|
||||
pxl8_light lights[PXL8_MAX_LIGHTS];
|
||||
u32 num_lights;
|
||||
f32 time;
|
||||
} pxl8_3d_uniforms;
|
||||
|
||||
typedef struct pxl8_3d_frame {
|
||||
pxl8_3d_uniforms uniforms;
|
||||
pxl8_vec3 camera_dir;
|
||||
pxl8_vec3 camera_pos;
|
||||
f32 far_clip;
|
||||
const pxl8_light* lights;
|
||||
u32 lights_count;
|
||||
f32 near_clip;
|
||||
pxl8_mat4 projection;
|
||||
pxl8_3d_uniforms uniforms;
|
||||
pxl8_mat4 view;
|
||||
} pxl8_3d_frame;
|
||||
|
||||
|
|
@ -55,15 +33,16 @@ typedef struct pxl8_3d_frame {
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
void pxl8_3d_begin_frame(pxl8_gfx* gfx, const pxl8_3d_camera* camera, const pxl8_3d_uniforms* uniforms);
|
||||
void pxl8_3d_begin_frame(pxl8_gfx* gfx, const pxl8_3d_camera* camera, const pxl8_lights* lights, const pxl8_3d_uniforms* uniforms);
|
||||
void pxl8_3d_clear(pxl8_gfx* gfx, u8 color);
|
||||
void pxl8_3d_clear_depth(pxl8_gfx* gfx);
|
||||
void pxl8_3d_draw_line(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, u8 color);
|
||||
void pxl8_3d_draw_mesh(pxl8_gfx* gfx, const pxl8_mesh* mesh, const pxl8_mat4* model, const pxl8_material* material);
|
||||
void pxl8_3d_draw_mesh_wireframe(pxl8_gfx* gfx, const pxl8_mesh* mesh, pxl8_mat4 model, u8 color);
|
||||
void pxl8_3d_draw_mesh(pxl8_gfx* gfx, const pxl8_mesh* mesh, const pxl8_mat4* model, const pxl8_gfx_material* material);
|
||||
void pxl8_3d_end_frame(pxl8_gfx* gfx);
|
||||
u8* pxl8_3d_get_framebuffer(pxl8_gfx* gfx);
|
||||
const pxl8_frustum* pxl8_3d_get_frustum(pxl8_gfx* gfx);
|
||||
const pxl8_mat4* pxl8_3d_get_view_proj(pxl8_gfx* gfx);
|
||||
u32 pxl8_3d_project_points(pxl8_gfx* gfx, const pxl8_vec3* in, pxl8_vec3* out, u32 count, const pxl8_mat4* transform);
|
||||
|
||||
pxl8_3d_frame pxl8_3d_frame_from_camera(const pxl8_3d_camera* camera, const pxl8_3d_uniforms* uniforms);
|
||||
|
||||
|
|
|
|||
62
src/gfx/pxl8_glows.c
Normal file
62
src/gfx/pxl8_glows.c
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
#include "pxl8_glows.h"
|
||||
|
||||
#include "pxl8_gfx.h"
|
||||
#include "pxl8_mem.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
struct pxl8_glows {
|
||||
pxl8_glow* data;
|
||||
u32 capacity;
|
||||
u32 count;
|
||||
};
|
||||
|
||||
pxl8_glows* pxl8_glows_create(u32 capacity) {
|
||||
pxl8_glows* glows = pxl8_calloc(1, sizeof(pxl8_glows));
|
||||
if (!glows) return NULL;
|
||||
|
||||
glows->data = pxl8_calloc(capacity, sizeof(pxl8_glow));
|
||||
if (!glows->data) {
|
||||
pxl8_free(glows);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
glows->capacity = capacity;
|
||||
glows->count = 0;
|
||||
|
||||
return glows;
|
||||
}
|
||||
|
||||
void pxl8_glows_destroy(pxl8_glows* glows) {
|
||||
if (!glows) return;
|
||||
pxl8_free(glows->data);
|
||||
pxl8_free(glows);
|
||||
}
|
||||
|
||||
void pxl8_glows_add(pxl8_glows* glows, i16 x, i16 y, u8 radius, u16 intensity, u8 color, u8 shape) {
|
||||
if (!glows || glows->count >= glows->capacity) return;
|
||||
|
||||
pxl8_glow* g = &glows->data[glows->count++];
|
||||
g->x = x;
|
||||
g->y = y;
|
||||
g->radius = radius;
|
||||
g->intensity = intensity;
|
||||
g->color = color;
|
||||
g->shape = shape;
|
||||
g->depth = 0xFFFF;
|
||||
g->height = 0;
|
||||
}
|
||||
|
||||
void pxl8_glows_clear(pxl8_glows* glows) {
|
||||
if (!glows) return;
|
||||
glows->count = 0;
|
||||
}
|
||||
|
||||
u32 pxl8_glows_count(const pxl8_glows* glows) {
|
||||
return glows ? glows->count : 0;
|
||||
}
|
||||
|
||||
void pxl8_glows_render(pxl8_glows* glows, pxl8_gfx* gfx) {
|
||||
if (!glows || !gfx || glows->count == 0) return;
|
||||
pxl8_gfx_apply_effect(gfx, PXL8_GFX_EFFECT_GLOWS, glows->data, glows->count);
|
||||
}
|
||||
42
src/gfx/pxl8_glows.h
Normal file
42
src/gfx/pxl8_glows.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_types.h"
|
||||
|
||||
#define PXL8_GLOWS_MAX 16384
|
||||
|
||||
typedef enum pxl8_glow_shape {
|
||||
PXL8_GLOW_CIRCLE = 0,
|
||||
PXL8_GLOW_DIAMOND = 1,
|
||||
PXL8_GLOW_SHAFT = 2,
|
||||
} pxl8_glow_shape;
|
||||
|
||||
typedef struct pxl8_glow {
|
||||
u8 color;
|
||||
u16 depth;
|
||||
u8 height;
|
||||
u16 intensity;
|
||||
u8 radius;
|
||||
pxl8_glow_shape shape;
|
||||
i16 x;
|
||||
i16 y;
|
||||
} pxl8_glow;
|
||||
|
||||
typedef struct pxl8_gfx pxl8_gfx;
|
||||
typedef struct pxl8_glows pxl8_glows;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
pxl8_glows* pxl8_glows_create(u32 capacity);
|
||||
void pxl8_glows_destroy(pxl8_glows* glows);
|
||||
|
||||
void pxl8_glows_add(pxl8_glows* glows, i16 x, i16 y, u8 radius, u16 intensity, u8 color, u8 shape);
|
||||
void pxl8_glows_clear(pxl8_glows* glows);
|
||||
u32 pxl8_glows_count(const pxl8_glows* glows);
|
||||
const pxl8_glow* pxl8_glows_data(const pxl8_glows* glows);
|
||||
void pxl8_glows_render(pxl8_glows* glows, pxl8_gfx* gfx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
114
src/gfx/pxl8_lightmap.c
Normal file
114
src/gfx/pxl8_lightmap.c
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
#include "pxl8_lightmap.h"
|
||||
#include "pxl8_mem.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
pxl8_lightmap* pxl8_lightmap_create(u32 width, u32 height, u32 scale) {
|
||||
pxl8_lightmap* lm = pxl8_calloc(1, sizeof(pxl8_lightmap));
|
||||
if (!lm) return NULL;
|
||||
|
||||
lm->width = width;
|
||||
lm->height = height;
|
||||
lm->scale = scale;
|
||||
lm->data = pxl8_calloc(width * height * 3, sizeof(u8));
|
||||
if (!lm->data) {
|
||||
pxl8_free(lm);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pxl8_lightmap_clear(lm, PXL8_LIGHTMAP_NEUTRAL, PXL8_LIGHTMAP_NEUTRAL, PXL8_LIGHTMAP_NEUTRAL);
|
||||
return lm;
|
||||
}
|
||||
|
||||
void pxl8_lightmap_destroy(pxl8_lightmap* lm) {
|
||||
if (!lm) return;
|
||||
pxl8_free(lm->data);
|
||||
pxl8_free(lm);
|
||||
}
|
||||
|
||||
void pxl8_lightmap_clear(pxl8_lightmap* lm, u8 r, u8 g, u8 b) {
|
||||
if (!lm || !lm->data) return;
|
||||
u32 count = lm->width * lm->height;
|
||||
for (u32 i = 0; i < count; i++) {
|
||||
lm->data[i * 3 + 0] = r;
|
||||
lm->data[i * 3 + 1] = g;
|
||||
lm->data[i * 3 + 2] = b;
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_lightmap_set(pxl8_lightmap* lm, u32 x, u32 y, u8 r, u8 g, u8 b) {
|
||||
if (!lm || !lm->data || x >= lm->width || y >= lm->height) return;
|
||||
u32 idx = (y * lm->width + x) * 3;
|
||||
lm->data[idx + 0] = r;
|
||||
lm->data[idx + 1] = g;
|
||||
lm->data[idx + 2] = b;
|
||||
}
|
||||
|
||||
void pxl8_lightmap_get(const pxl8_lightmap* lm, u32 x, u32 y, u8* r, u8* g, u8* b) {
|
||||
if (!lm || !lm->data || x >= lm->width || y >= lm->height) {
|
||||
*r = *g = *b = PXL8_LIGHTMAP_NEUTRAL;
|
||||
return;
|
||||
}
|
||||
u32 idx = (y * lm->width + x) * 3;
|
||||
*r = lm->data[idx + 0];
|
||||
*g = lm->data[idx + 1];
|
||||
*b = lm->data[idx + 2];
|
||||
}
|
||||
|
||||
void pxl8_lightmap_add_point(
|
||||
pxl8_lightmap* lm,
|
||||
f32 lx, f32 ly,
|
||||
u8 r, u8 g, u8 b,
|
||||
f32 radius,
|
||||
f32 intensity
|
||||
) {
|
||||
if (!lm || !lm->data || radius <= 0.0f) return;
|
||||
|
||||
f32 radius_sq = radius * radius;
|
||||
f32 inv_radius_sq = 1.0f / radius_sq;
|
||||
|
||||
i32 cx = (i32)(lx * (f32)lm->width);
|
||||
i32 cy = (i32)(ly * (f32)lm->height);
|
||||
i32 rad_pixels = (i32)(radius * (f32)lm->width) + 1;
|
||||
|
||||
i32 x0 = cx - rad_pixels;
|
||||
i32 y0 = cy - rad_pixels;
|
||||
i32 x1 = cx + rad_pixels;
|
||||
i32 y1 = cy + rad_pixels;
|
||||
|
||||
if (x0 < 0) x0 = 0;
|
||||
if (y0 < 0) y0 = 0;
|
||||
if (x1 >= (i32)lm->width) x1 = (i32)lm->width - 1;
|
||||
if (y1 >= (i32)lm->height) y1 = (i32)lm->height - 1;
|
||||
|
||||
f32 scale_x = 1.0f / (f32)lm->width;
|
||||
f32 scale_y = 1.0f / (f32)lm->height;
|
||||
|
||||
for (i32 y = y0; y <= y1; y++) {
|
||||
f32 dy = ((f32)y * scale_y) - ly;
|
||||
for (i32 x = x0; x <= x1; x++) {
|
||||
f32 dx = ((f32)x * scale_x) - lx;
|
||||
f32 dist_sq = dx * dx + dy * dy;
|
||||
|
||||
if (dist_sq >= radius_sq) continue;
|
||||
|
||||
f32 falloff = 1.0f - dist_sq * inv_radius_sq;
|
||||
f32 contrib = falloff * falloff * intensity;
|
||||
|
||||
u32 idx = ((u32)y * lm->width + (u32)x) * 3;
|
||||
|
||||
i32 nr = (i32)lm->data[idx + 0] + (i32)((f32)(r - 128) * contrib);
|
||||
i32 ng = (i32)lm->data[idx + 1] + (i32)((f32)(g - 128) * contrib);
|
||||
i32 nb = (i32)lm->data[idx + 2] + (i32)((f32)(b - 128) * contrib);
|
||||
|
||||
if (nr < 0) nr = 0; if (nr > 255) nr = 255;
|
||||
if (ng < 0) ng = 0; if (ng > 255) ng = 255;
|
||||
if (nb < 0) nb = 0; if (nb > 255) nb = 255;
|
||||
|
||||
lm->data[idx + 0] = (u8)nr;
|
||||
lm->data[idx + 1] = (u8)ng;
|
||||
lm->data[idx + 2] = (u8)nb;
|
||||
}
|
||||
}
|
||||
}
|
||||
48
src/gfx/pxl8_lightmap.h
Normal file
48
src/gfx/pxl8_lightmap.h
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define PXL8_LIGHTMAP_MAX 16
|
||||
#define PXL8_LIGHTMAP_NEUTRAL 128
|
||||
|
||||
typedef struct pxl8_lightmap {
|
||||
u8* data;
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 scale;
|
||||
} pxl8_lightmap;
|
||||
|
||||
pxl8_lightmap* pxl8_lightmap_create(u32 width, u32 height, u32 scale);
|
||||
void pxl8_lightmap_destroy(pxl8_lightmap* lm);
|
||||
void pxl8_lightmap_clear(pxl8_lightmap* lm, u8 r, u8 g, u8 b);
|
||||
void pxl8_lightmap_set(pxl8_lightmap* lm, u32 x, u32 y, u8 r, u8 g, u8 b);
|
||||
void pxl8_lightmap_get(const pxl8_lightmap* lm, u32 x, u32 y, u8* r, u8* g, u8* b);
|
||||
|
||||
void pxl8_lightmap_add_point(
|
||||
pxl8_lightmap* lm,
|
||||
f32 lx, f32 ly,
|
||||
u8 r, u8 g, u8 b,
|
||||
f32 radius,
|
||||
f32 intensity
|
||||
);
|
||||
|
||||
static inline void pxl8_lightmap_sample(
|
||||
const pxl8_lightmap* lm,
|
||||
f32 u, f32 v,
|
||||
u8* r, u8* g, u8* b
|
||||
) {
|
||||
i32 x = (i32)(u * (f32)lm->width) & (i32)(lm->width - 1);
|
||||
i32 y = (i32)(v * (f32)lm->height) & (i32)(lm->height - 1);
|
||||
u32 idx = ((u32)y * lm->width + (u32)x) * 3;
|
||||
*r = lm->data[idx + 0];
|
||||
*g = lm->data[idx + 1];
|
||||
*b = lm->data[idx + 2];
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
64
src/gfx/pxl8_lights.c
Normal file
64
src/gfx/pxl8_lights.c
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
#include "pxl8_lights.h"
|
||||
#include "pxl8_mem.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
struct pxl8_lights {
|
||||
pxl8_light* data;
|
||||
u32 capacity;
|
||||
u32 count;
|
||||
};
|
||||
|
||||
pxl8_lights* pxl8_lights_create(u32 capacity) {
|
||||
if (capacity > PXL8_LIGHTS_MAX) capacity = PXL8_LIGHTS_MAX;
|
||||
|
||||
pxl8_lights* lights = pxl8_calloc(1, sizeof(pxl8_lights));
|
||||
if (!lights) return NULL;
|
||||
|
||||
lights->data = pxl8_calloc(capacity, sizeof(pxl8_light));
|
||||
if (!lights->data) {
|
||||
pxl8_free(lights);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lights->capacity = capacity;
|
||||
lights->count = 0;
|
||||
|
||||
return lights;
|
||||
}
|
||||
|
||||
void pxl8_lights_destroy(pxl8_lights* lights) {
|
||||
if (!lights) return;
|
||||
pxl8_free(lights->data);
|
||||
pxl8_free(lights);
|
||||
}
|
||||
|
||||
void pxl8_lights_add(pxl8_lights* lights, f32 x, f32 y, f32 z, u8 r, u8 g, u8 b, u8 intensity, f32 radius) {
|
||||
if (!lights || lights->count >= lights->capacity) return;
|
||||
|
||||
f32 radius_sq = radius * radius;
|
||||
pxl8_light* l = &lights->data[lights->count++];
|
||||
l->position.x = x;
|
||||
l->position.y = y;
|
||||
l->position.z = z;
|
||||
l->r = r;
|
||||
l->g = g;
|
||||
l->b = b;
|
||||
l->intensity = intensity;
|
||||
l->radius = radius;
|
||||
l->radius_sq = radius_sq;
|
||||
l->inv_radius_sq = radius_sq > 0.0f ? 1.0f / radius_sq : 0.0f;
|
||||
}
|
||||
|
||||
void pxl8_lights_clear(pxl8_lights* lights) {
|
||||
if (!lights) return;
|
||||
lights->count = 0;
|
||||
}
|
||||
|
||||
u32 pxl8_lights_count(const pxl8_lights* lights) {
|
||||
return lights ? lights->count : 0;
|
||||
}
|
||||
|
||||
const pxl8_light* pxl8_lights_data(const pxl8_lights* lights) {
|
||||
return lights ? lights->data : NULL;
|
||||
}
|
||||
33
src/gfx/pxl8_lights.h
Normal file
33
src/gfx/pxl8_lights.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_math.h"
|
||||
#include "pxl8_types.h"
|
||||
|
||||
#define PXL8_LIGHTS_MAX 256
|
||||
|
||||
typedef struct pxl8_light {
|
||||
pxl8_vec3 position;
|
||||
f32 inv_radius_sq;
|
||||
u8 r, g, b;
|
||||
u8 intensity;
|
||||
f32 radius;
|
||||
f32 radius_sq;
|
||||
} pxl8_light;
|
||||
|
||||
typedef struct pxl8_lights pxl8_lights;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
pxl8_lights* pxl8_lights_create(u32 capacity);
|
||||
void pxl8_lights_destroy(pxl8_lights* lights);
|
||||
|
||||
void pxl8_lights_add(pxl8_lights* lights, f32 x, f32 y, f32 z, u8 r, u8 g, u8 b, u8 intensity, f32 radius);
|
||||
void pxl8_lights_clear(pxl8_lights* lights);
|
||||
u32 pxl8_lights_count(const pxl8_lights* lights);
|
||||
const pxl8_light* pxl8_lights_data(const pxl8_lights* lights);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
#include "pxl8_mem.h"
|
||||
#include "pxl8_mesh.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
|
@ -6,16 +7,16 @@ pxl8_mesh* pxl8_mesh_create(u32 vertex_capacity, u32 index_capacity) {
|
|||
if (vertex_capacity > PXL8_MESH_MAX_VERTICES) vertex_capacity = PXL8_MESH_MAX_VERTICES;
|
||||
if (index_capacity > PXL8_MESH_MAX_INDICES) index_capacity = PXL8_MESH_MAX_INDICES;
|
||||
|
||||
pxl8_mesh* mesh = calloc(1, sizeof(pxl8_mesh));
|
||||
pxl8_mesh* mesh = pxl8_calloc(1, sizeof(pxl8_mesh));
|
||||
if (!mesh) return NULL;
|
||||
|
||||
mesh->vertices = calloc(vertex_capacity, sizeof(pxl8_vertex));
|
||||
mesh->indices = calloc(index_capacity, sizeof(u16));
|
||||
mesh->vertices = pxl8_calloc(vertex_capacity, sizeof(pxl8_vertex));
|
||||
mesh->indices = pxl8_calloc(index_capacity, sizeof(u16));
|
||||
|
||||
if (!mesh->vertices || !mesh->indices) {
|
||||
free(mesh->vertices);
|
||||
free(mesh->indices);
|
||||
free(mesh);
|
||||
pxl8_free(mesh->vertices);
|
||||
pxl8_free(mesh->indices);
|
||||
pxl8_free(mesh);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -29,9 +30,9 @@ pxl8_mesh* pxl8_mesh_create(u32 vertex_capacity, u32 index_capacity) {
|
|||
|
||||
void pxl8_mesh_destroy(pxl8_mesh* mesh) {
|
||||
if (!mesh) return;
|
||||
free(mesh->vertices);
|
||||
free(mesh->indices);
|
||||
free(mesh);
|
||||
pxl8_free(mesh->vertices);
|
||||
pxl8_free(mesh->indices);
|
||||
pxl8_free(mesh);
|
||||
}
|
||||
|
||||
void pxl8_mesh_clear(pxl8_mesh* mesh) {
|
||||
|
|
|
|||
|
|
@ -17,8 +17,15 @@ typedef enum pxl8_blend_mode {
|
|||
PXL8_BLEND_ADDITIVE,
|
||||
} pxl8_blend_mode;
|
||||
|
||||
typedef struct pxl8_material {
|
||||
typedef struct pxl8_gfx_material {
|
||||
char name[16];
|
||||
pxl8_vec3 u_axis;
|
||||
pxl8_vec3 v_axis;
|
||||
f32 u_offset;
|
||||
f32 v_offset;
|
||||
|
||||
u32 texture_id;
|
||||
u32 lightmap_id;
|
||||
u8 alpha;
|
||||
u8 blend_mode;
|
||||
bool dither;
|
||||
|
|
@ -26,13 +33,15 @@ typedef struct pxl8_material {
|
|||
bool dynamic_lighting;
|
||||
bool per_pixel;
|
||||
bool vertex_color_passthrough;
|
||||
bool wireframe;
|
||||
f32 emissive_intensity;
|
||||
} pxl8_material;
|
||||
} pxl8_gfx_material;
|
||||
|
||||
typedef struct pxl8_vertex {
|
||||
pxl8_vec3 position;
|
||||
pxl8_vec3 normal;
|
||||
f32 u, v;
|
||||
f32 lu, lv;
|
||||
u8 color;
|
||||
u8 light;
|
||||
u8 _pad[2];
|
||||
|
|
@ -62,62 +71,6 @@ static inline u32 pxl8_mesh_triangle_count(const pxl8_mesh* mesh) {
|
|||
return mesh->index_count / 3;
|
||||
}
|
||||
|
||||
static inline pxl8_material pxl8_material_create(u32 texture_id) {
|
||||
return (pxl8_material){
|
||||
.texture_id = texture_id,
|
||||
.alpha = 255,
|
||||
.blend_mode = PXL8_BLEND_OPAQUE,
|
||||
.dither = true,
|
||||
.double_sided = false,
|
||||
.dynamic_lighting = false,
|
||||
.per_pixel = false,
|
||||
.vertex_color_passthrough = false,
|
||||
.emissive_intensity = 0.0f,
|
||||
};
|
||||
}
|
||||
|
||||
static inline pxl8_material pxl8_material_with_alpha(pxl8_material m, u8 alpha) {
|
||||
m.alpha = alpha;
|
||||
if (alpha < 255 && m.blend_mode == PXL8_BLEND_OPAQUE) {
|
||||
m.blend_mode = PXL8_BLEND_ALPHA;
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
static inline pxl8_material pxl8_material_with_blend(pxl8_material m, pxl8_blend_mode mode) {
|
||||
m.blend_mode = mode;
|
||||
return m;
|
||||
}
|
||||
|
||||
static inline pxl8_material pxl8_material_with_double_sided(pxl8_material m) {
|
||||
m.double_sided = true;
|
||||
return m;
|
||||
}
|
||||
|
||||
static inline pxl8_material pxl8_material_with_emissive(pxl8_material m, f32 intensity) {
|
||||
m.emissive_intensity = intensity;
|
||||
return m;
|
||||
}
|
||||
|
||||
static inline pxl8_material pxl8_material_with_lighting(pxl8_material m) {
|
||||
m.dynamic_lighting = true;
|
||||
return m;
|
||||
}
|
||||
|
||||
static inline pxl8_material pxl8_material_with_no_dither(pxl8_material m) {
|
||||
m.dither = false;
|
||||
return m;
|
||||
}
|
||||
|
||||
static inline pxl8_material pxl8_material_with_passthrough(pxl8_material m) {
|
||||
m.vertex_color_passthrough = true;
|
||||
return m;
|
||||
}
|
||||
|
||||
static inline pxl8_material pxl8_material_with_per_pixel(pxl8_material m) {
|
||||
m.per_pixel = true;
|
||||
return m;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,18 @@
|
|||
|
||||
#include "pxl8_ase.h"
|
||||
#include "pxl8_color.h"
|
||||
#include "pxl8_colormap.h"
|
||||
#include "pxl8_log.h"
|
||||
#include "pxl8_mem.h"
|
||||
|
||||
#define PXL8_PALETTE_HASH_SIZE 512
|
||||
|
||||
struct pxl8_palette_cube {
|
||||
u8 colors[PXL8_PALETTE_SIZE * 3];
|
||||
u8 table[PXL8_CUBE_ENTRIES];
|
||||
u8 stable[PXL8_CUBE_ENTRIES];
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
u32 color;
|
||||
i16 index;
|
||||
|
|
@ -200,7 +208,7 @@ static void update_cycle_colors(pxl8_palette* pal, u8 slot) {
|
|||
}
|
||||
|
||||
pxl8_palette* pxl8_palette_create(void) {
|
||||
pxl8_palette* pal = calloc(1, sizeof(pxl8_palette));
|
||||
pxl8_palette* pal = pxl8_calloc(1, sizeof(pxl8_palette));
|
||||
if (!pal) return NULL;
|
||||
|
||||
pal->colors[0] = 0x00000000;
|
||||
|
|
@ -221,7 +229,7 @@ pxl8_palette* pxl8_palette_create(void) {
|
|||
}
|
||||
|
||||
void pxl8_palette_destroy(pxl8_palette* pal) {
|
||||
free(pal);
|
||||
pxl8_free(pal);
|
||||
}
|
||||
|
||||
pxl8_result pxl8_palette_load_ase(pxl8_palette* pal, const char* path) {
|
||||
|
|
@ -332,10 +340,6 @@ void pxl8_palette_get_rgba(const pxl8_palette* pal, u8 idx, u8* r, u8* g, u8* b,
|
|||
unpack_rgba(pal->colors[idx], r, g, b, a);
|
||||
}
|
||||
|
||||
void pxl8_palette_set(pxl8_palette* pal, u8 idx, u32 color) {
|
||||
if (pal) pal->colors[idx] = color;
|
||||
}
|
||||
|
||||
void pxl8_palette_set_rgb(pxl8_palette* pal, u8 idx, u8 r, u8 g, u8 b) {
|
||||
if (pal) pal->colors[idx] = pack_rgb(r, g, b);
|
||||
}
|
||||
|
|
@ -344,6 +348,17 @@ void pxl8_palette_set_rgba(pxl8_palette* pal, u8 idx, u8 r, u8 g, u8 b, u8 a) {
|
|||
if (pal) pal->colors[idx] = pack_rgba(r, g, b, a);
|
||||
}
|
||||
|
||||
void pxl8_set_palette(pxl8_palette* pal, const u32* colors, u16 count) {
|
||||
if (!pal || !colors) return;
|
||||
for (u16 i = 0; i < count; i++) {
|
||||
u32 rgb = colors[i];
|
||||
u8 r = (rgb >> 16) & 0xFF;
|
||||
u8 g = (rgb >> 8) & 0xFF;
|
||||
u8 b = rgb & 0xFF;
|
||||
pal->colors[i] = pack_rgb(r, g, b);
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_palette_fill_gradient(pxl8_palette* pal, u8 start, u8 count, u32 from, u32 to) {
|
||||
if (!pal || count == 0) return;
|
||||
|
||||
|
|
@ -472,3 +487,91 @@ pxl8_cycle_range pxl8_cycle_range_disabled(void) {
|
|||
};
|
||||
return range;
|
||||
}
|
||||
|
||||
static u8 find_closest_stable(const pxl8_palette* pal, u8 r, u8 g, u8 b) {
|
||||
u8 best_idx = 1;
|
||||
u32 best_dist = 0xFFFFFFFF;
|
||||
|
||||
u8 dynamic_end = PXL8_DYNAMIC_RANGE_START + PXL8_DYNAMIC_RANGE_COUNT;
|
||||
|
||||
for (u32 i = 1; i < PXL8_FULLBRIGHT_START; i++) {
|
||||
if (i >= PXL8_DYNAMIC_RANGE_START && i < dynamic_end) {
|
||||
continue;
|
||||
}
|
||||
|
||||
u8 pr, pg, pb;
|
||||
pxl8_palette_get_rgb(pal, (u8)i, &pr, &pg, &pb);
|
||||
|
||||
i32 dr = (i32)r - (i32)pr;
|
||||
i32 dg = (i32)g - (i32)pg;
|
||||
i32 db = (i32)b - (i32)pb;
|
||||
u32 dist = (u32)(dr * dr + dg * dg + db * db);
|
||||
|
||||
if (dist < best_dist) {
|
||||
best_dist = dist;
|
||||
best_idx = (u8)i;
|
||||
if (dist == 0) break;
|
||||
}
|
||||
}
|
||||
|
||||
return best_idx;
|
||||
}
|
||||
|
||||
pxl8_palette_cube* pxl8_palette_cube_create(const pxl8_palette* pal) {
|
||||
pxl8_palette_cube* cube = pxl8_calloc(1, sizeof(pxl8_palette_cube));
|
||||
if (!cube) return NULL;
|
||||
pxl8_palette_cube_rebuild(cube, pal);
|
||||
return cube;
|
||||
}
|
||||
|
||||
void pxl8_palette_cube_destroy(pxl8_palette_cube* cube) {
|
||||
pxl8_free(cube);
|
||||
}
|
||||
|
||||
void pxl8_palette_cube_rebuild(pxl8_palette_cube* cube, const pxl8_palette* pal) {
|
||||
if (!cube || !pal) return;
|
||||
|
||||
for (u32 i = 0; i < PXL8_PALETTE_SIZE; i++) {
|
||||
u8 r, g, b;
|
||||
pxl8_palette_get_rgb(pal, (u8)i, &r, &g, &b);
|
||||
cube->colors[i * 3 + 0] = r;
|
||||
cube->colors[i * 3 + 1] = g;
|
||||
cube->colors[i * 3 + 2] = b;
|
||||
}
|
||||
|
||||
for (u32 bi = 0; bi < PXL8_CUBE_SIZE; bi++) {
|
||||
for (u32 gi = 0; gi < PXL8_CUBE_SIZE; gi++) {
|
||||
for (u32 ri = 0; ri < PXL8_CUBE_SIZE; ri++) {
|
||||
u32 idx = (bi * PXL8_CUBE_SIZE * PXL8_CUBE_SIZE) + (gi * PXL8_CUBE_SIZE) + ri;
|
||||
u8 r8 = (u8)((ri * 255) / (PXL8_CUBE_SIZE - 1));
|
||||
u8 g8 = (u8)((gi * 255) / (PXL8_CUBE_SIZE - 1));
|
||||
u8 b8 = (u8)((bi * 255) / (PXL8_CUBE_SIZE - 1));
|
||||
|
||||
cube->table[idx] = pxl8_palette_find_closest(pal, r8, g8, b8);
|
||||
cube->stable[idx] = find_closest_stable(pal, r8, g8, b8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u8 pxl8_palette_cube_lookup(const pxl8_palette_cube* cube, u8 r, u8 g, u8 b) {
|
||||
u32 ri = (r * (PXL8_CUBE_SIZE - 1)) / 255;
|
||||
u32 gi = (g * (PXL8_CUBE_SIZE - 1)) / 255;
|
||||
u32 bi = (b * (PXL8_CUBE_SIZE - 1)) / 255;
|
||||
u32 idx = (bi * PXL8_CUBE_SIZE * PXL8_CUBE_SIZE) + (gi * PXL8_CUBE_SIZE) + ri;
|
||||
return cube->table[idx];
|
||||
}
|
||||
|
||||
u8 pxl8_palette_cube_lookup_stable(const pxl8_palette_cube* cube, u8 r, u8 g, u8 b) {
|
||||
u32 ri = (r * (PXL8_CUBE_SIZE - 1)) / 255;
|
||||
u32 gi = (g * (PXL8_CUBE_SIZE - 1)) / 255;
|
||||
u32 bi = (b * (PXL8_CUBE_SIZE - 1)) / 255;
|
||||
u32 idx = (bi * PXL8_CUBE_SIZE * PXL8_CUBE_SIZE) + (gi * PXL8_CUBE_SIZE) + ri;
|
||||
return cube->stable[idx];
|
||||
}
|
||||
|
||||
void pxl8_palette_cube_get_rgb(const pxl8_palette_cube* cube, u8 idx, u8* r, u8* g, u8* b) {
|
||||
*r = cube->colors[idx * 3 + 0];
|
||||
*g = cube->colors[idx * 3 + 1];
|
||||
*b = cube->colors[idx * 3 + 2];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,11 @@
|
|||
#define PXL8_PALETTE_SIZE 256
|
||||
#define PXL8_MAX_CYCLES 8
|
||||
#define PXL8_MAX_CYCLE_LEN 16
|
||||
#define PXL8_CUBE_SIZE 32
|
||||
#define PXL8_CUBE_ENTRIES (PXL8_CUBE_SIZE * PXL8_CUBE_SIZE * PXL8_CUBE_SIZE)
|
||||
|
||||
typedef struct pxl8_palette pxl8_palette;
|
||||
typedef struct pxl8_palette_cube pxl8_palette_cube;
|
||||
|
||||
typedef enum pxl8_cycle_mode {
|
||||
PXL8_CYCLE_LOOP,
|
||||
|
|
@ -49,9 +52,9 @@ u32 pxl8_palette_color(const pxl8_palette* pal, u8 idx);
|
|||
i32 pxl8_palette_index(const pxl8_palette* pal, u32 color);
|
||||
void pxl8_palette_get_rgb(const pxl8_palette* pal, u8 idx, u8* r, u8* g, u8* b);
|
||||
void pxl8_palette_get_rgba(const pxl8_palette* pal, u8 idx, u8* r, u8* g, u8* b, u8* a);
|
||||
void pxl8_palette_set(pxl8_palette* pal, u8 idx, u32 color);
|
||||
void pxl8_palette_set_rgb(pxl8_palette* pal, u8 idx, u8 r, u8 g, u8 b);
|
||||
void pxl8_palette_set_rgba(pxl8_palette* pal, u8 idx, u8 r, u8 g, u8 b, u8 a);
|
||||
void pxl8_set_palette(pxl8_palette* pal, const u32* colors, u16 count);
|
||||
|
||||
void pxl8_palette_fill_gradient(pxl8_palette* pal, u8 start, u8 count, u32 from, u32 to);
|
||||
void pxl8_palette_fill_gradient_rgb(pxl8_palette* pal, u8 start, u8 count, u8 r0, u8 g0, u8 b0, u8 r1, u8 g1, u8 b1);
|
||||
|
|
@ -65,6 +68,13 @@ void pxl8_palette_tick(pxl8_palette* pal, u16 delta_ticks);
|
|||
pxl8_cycle_range pxl8_cycle_range_create(u8 start, u8 len, u16 period);
|
||||
pxl8_cycle_range pxl8_cycle_range_disabled(void);
|
||||
|
||||
pxl8_palette_cube* pxl8_palette_cube_create(const pxl8_palette* pal);
|
||||
void pxl8_palette_cube_destroy(pxl8_palette_cube* cube);
|
||||
void pxl8_palette_cube_rebuild(pxl8_palette_cube* cube, const pxl8_palette* pal);
|
||||
u8 pxl8_palette_cube_lookup(const pxl8_palette_cube* cube, u8 r, u8 g, u8 b);
|
||||
u8 pxl8_palette_cube_lookup_stable(const pxl8_palette_cube* cube, u8 r, u8 g, u8 b);
|
||||
void pxl8_palette_cube_get_rgb(const pxl8_palette_cube* cube, u8 idx, u8* r, u8* g, u8* b);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "pxl8_gfx.h"
|
||||
#include "pxl8_mem.h"
|
||||
#include "pxl8_gfx2d.h"
|
||||
#include "pxl8_palette.h"
|
||||
#include "pxl8_rng.h"
|
||||
|
|
@ -37,12 +38,12 @@ struct pxl8_particles {
|
|||
};
|
||||
|
||||
pxl8_particles* pxl8_particles_create(u32 max_count, pxl8_rng* rng) {
|
||||
pxl8_particles* ps = calloc(1, sizeof(pxl8_particles));
|
||||
pxl8_particles* ps = pxl8_calloc(1, sizeof(pxl8_particles));
|
||||
if (!ps) return NULL;
|
||||
|
||||
ps->particles = calloc(max_count, sizeof(pxl8_particle));
|
||||
ps->particles = pxl8_calloc(max_count, sizeof(pxl8_particle));
|
||||
if (!ps->particles) {
|
||||
free(ps);
|
||||
pxl8_free(ps);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -61,8 +62,8 @@ pxl8_particles* pxl8_particles_create(u32 max_count, pxl8_rng* rng) {
|
|||
|
||||
void pxl8_particles_destroy(pxl8_particles* ps) {
|
||||
if (!ps) return;
|
||||
free(ps->particles);
|
||||
free(ps);
|
||||
pxl8_free(ps->particles);
|
||||
pxl8_free(ps);
|
||||
}
|
||||
|
||||
void pxl8_particles_clear(pxl8_particles* ps) {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include "pxl8_ase.h"
|
||||
#include "pxl8_log.h"
|
||||
#include "pxl8_macros.h"
|
||||
#include "pxl8_mem.h"
|
||||
#include "pxl8_tilesheet.h"
|
||||
|
||||
struct pxl8_tilesheet {
|
||||
|
|
@ -58,7 +59,7 @@ static pxl8_tile_chunk* pxl8_get_or_create_chunk(pxl8_tilemap_layer* layer, u32
|
|||
if (idx >= layer->chunks_wide * layer->chunks_high) return NULL;
|
||||
|
||||
if (!layer->chunks[idx]) {
|
||||
layer->chunks[idx] = calloc(1, sizeof(pxl8_tile_chunk));
|
||||
layer->chunks[idx] = pxl8_calloc(1, sizeof(pxl8_tile_chunk));
|
||||
if (!layer->chunks[idx]) return NULL;
|
||||
|
||||
layer->chunks[idx]->chunk_x = chunk_x;
|
||||
|
|
@ -75,7 +76,7 @@ pxl8_tilemap* pxl8_tilemap_create(u32 width, u32 height, u32 tile_size) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
pxl8_tilemap* tilemap = calloc(1, sizeof(pxl8_tilemap));
|
||||
pxl8_tilemap* tilemap = pxl8_calloc(1, sizeof(pxl8_tilemap));
|
||||
if (!tilemap) return NULL;
|
||||
|
||||
tilemap->width = width;
|
||||
|
|
@ -85,7 +86,7 @@ pxl8_tilemap* pxl8_tilemap_create(u32 width, u32 height, u32 tile_size) {
|
|||
|
||||
tilemap->tilesheet = pxl8_tilesheet_create(tilemap->tile_size);
|
||||
if (!tilemap->tilesheet) {
|
||||
free(tilemap);
|
||||
pxl8_free(tilemap);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -103,13 +104,13 @@ pxl8_tilemap* pxl8_tilemap_create(u32 width, u32 height, u32 tile_size) {
|
|||
layer->visible = (i == 0);
|
||||
layer->opacity = 255;
|
||||
|
||||
layer->chunks = calloc(layer->chunk_count, sizeof(pxl8_tile_chunk*));
|
||||
layer->chunks = pxl8_calloc(layer->chunk_count, sizeof(pxl8_tile_chunk*));
|
||||
if (!layer->chunks) {
|
||||
for (u32 j = 0; j < i; j++) {
|
||||
free(tilemap->layers[j].chunks);
|
||||
pxl8_free(tilemap->layers[j].chunks);
|
||||
}
|
||||
if (tilemap->tilesheet) pxl8_tilesheet_destroy(tilemap->tilesheet);
|
||||
free(tilemap);
|
||||
pxl8_free(tilemap);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
|
@ -125,17 +126,17 @@ void pxl8_tilemap_destroy(pxl8_tilemap* tilemap) {
|
|||
if (layer->chunks) {
|
||||
for (u32 j = 0; j < layer->chunk_count; j++) {
|
||||
if (layer->chunks[j]) {
|
||||
free(layer->chunks[j]);
|
||||
pxl8_free(layer->chunks[j]);
|
||||
}
|
||||
}
|
||||
free(layer->chunks);
|
||||
pxl8_free(layer->chunks);
|
||||
}
|
||||
}
|
||||
|
||||
if (tilemap->tilesheet) pxl8_tilesheet_unref(tilemap->tilesheet);
|
||||
if (tilemap->tile_user_data) free(tilemap->tile_user_data);
|
||||
if (tilemap->tile_user_data) pxl8_free(tilemap->tile_user_data);
|
||||
|
||||
free(tilemap);
|
||||
pxl8_free(tilemap);
|
||||
}
|
||||
|
||||
u32 pxl8_tilemap_get_width(const pxl8_tilemap* tilemap) {
|
||||
|
|
@ -155,7 +156,7 @@ void pxl8_tilemap_set_tile_user_data(pxl8_tilemap* tilemap, u16 tile_id, void* u
|
|||
|
||||
if (tile_id >= tilemap->tile_user_data_capacity) {
|
||||
u32 new_capacity = tile_id + 64;
|
||||
void** new_data = realloc(tilemap->tile_user_data, new_capacity * sizeof(void*));
|
||||
void** new_data = pxl8_realloc(tilemap->tile_user_data, new_capacity * sizeof(void*));
|
||||
if (!new_data) return;
|
||||
|
||||
for (u32 i = tilemap->tile_user_data_capacity; i < new_capacity; i++) {
|
||||
|
|
@ -478,7 +479,7 @@ void pxl8_tilemap_compress(pxl8_tilemap* tilemap) {
|
|||
}
|
||||
|
||||
if (!has_tiles) {
|
||||
free(chunk);
|
||||
pxl8_free(chunk);
|
||||
layer->chunks[j] = NULL;
|
||||
layer->allocated_chunks--;
|
||||
} else {
|
||||
|
|
@ -535,8 +536,8 @@ pxl8_result pxl8_tilemap_load_ase(pxl8_tilemap* tilemap, const char* filepath, u
|
|||
u32 tilesheet_width = tiles_per_row * tilemap->tile_size;
|
||||
u32 tilesheet_height = tilesheet_rows * tilemap->tile_size;
|
||||
|
||||
if (tilemap->tilesheet->data) free(tilemap->tilesheet->data);
|
||||
tilemap->tilesheet->data = calloc(tilesheet_width * tilesheet_height, 1);
|
||||
if (tilemap->tilesheet->data) pxl8_free(tilemap->tilesheet->data);
|
||||
tilemap->tilesheet->data = pxl8_calloc(tilesheet_width * tilesheet_height, 1);
|
||||
if (!tilemap->tilesheet->data) {
|
||||
pxl8_ase_destroy(&ase_file);
|
||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
|
|
@ -548,8 +549,8 @@ pxl8_result pxl8_tilemap_load_ase(pxl8_tilemap* tilemap, const char* filepath, u
|
|||
tilemap->tilesheet->total_tiles = tileset->tile_count;
|
||||
tilemap->tilesheet->pixel_mode = PXL8_PIXEL_INDEXED;
|
||||
|
||||
if (tilemap->tilesheet->tile_valid) free(tilemap->tilesheet->tile_valid);
|
||||
tilemap->tilesheet->tile_valid = calloc(tileset->tile_count + 1, sizeof(bool));
|
||||
if (tilemap->tilesheet->tile_valid) pxl8_free(tilemap->tilesheet->tile_valid);
|
||||
tilemap->tilesheet->tile_valid = pxl8_calloc(tileset->tile_count + 1, sizeof(bool));
|
||||
|
||||
for (u32 i = 0; i < tileset->tile_count; i++) {
|
||||
u32 sheet_row = i / tiles_per_row;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include "pxl8_color.h"
|
||||
#include "pxl8_gfx.h"
|
||||
#include "pxl8_log.h"
|
||||
#include "pxl8_mem.h"
|
||||
#include "pxl8_tilemap.h"
|
||||
|
||||
struct pxl8_tilesheet {
|
||||
|
|
@ -32,7 +33,7 @@ struct pxl8_tilesheet {
|
|||
};
|
||||
|
||||
pxl8_tilesheet* pxl8_tilesheet_create(u32 tile_size) {
|
||||
pxl8_tilesheet* tilesheet = calloc(1, sizeof(pxl8_tilesheet));
|
||||
pxl8_tilesheet* tilesheet = pxl8_calloc(1, sizeof(pxl8_tilesheet));
|
||||
if (!tilesheet) return NULL;
|
||||
|
||||
tilesheet->tile_size = tile_size;
|
||||
|
|
@ -45,37 +46,37 @@ void pxl8_tilesheet_destroy(pxl8_tilesheet* tilesheet) {
|
|||
if (!tilesheet) return;
|
||||
|
||||
if (tilesheet->data) {
|
||||
free(tilesheet->data);
|
||||
pxl8_free(tilesheet->data);
|
||||
}
|
||||
|
||||
if (tilesheet->tile_valid) {
|
||||
free(tilesheet->tile_valid);
|
||||
pxl8_free(tilesheet->tile_valid);
|
||||
}
|
||||
|
||||
if (tilesheet->animations) {
|
||||
for (u32 i = 0; i < tilesheet->animation_count; i++) {
|
||||
if (tilesheet->animations[i].frames) {
|
||||
free(tilesheet->animations[i].frames);
|
||||
pxl8_free(tilesheet->animations[i].frames);
|
||||
}
|
||||
}
|
||||
free(tilesheet->animations);
|
||||
pxl8_free(tilesheet->animations);
|
||||
}
|
||||
|
||||
if (tilesheet->properties) {
|
||||
free(tilesheet->properties);
|
||||
pxl8_free(tilesheet->properties);
|
||||
}
|
||||
|
||||
if (tilesheet->autotile_rules) {
|
||||
for (u32 i = 0; i <= tilesheet->total_tiles; i++) {
|
||||
if (tilesheet->autotile_rules[i]) {
|
||||
free(tilesheet->autotile_rules[i]);
|
||||
pxl8_free(tilesheet->autotile_rules[i]);
|
||||
}
|
||||
}
|
||||
free(tilesheet->autotile_rules);
|
||||
free(tilesheet->autotile_rule_counts);
|
||||
pxl8_free(tilesheet->autotile_rules);
|
||||
pxl8_free(tilesheet->autotile_rule_counts);
|
||||
}
|
||||
|
||||
free(tilesheet);
|
||||
pxl8_free(tilesheet);
|
||||
}
|
||||
|
||||
pxl8_result pxl8_tilesheet_load(pxl8_tilesheet* tilesheet, const char* filepath, pxl8_gfx* gfx) {
|
||||
|
|
@ -89,7 +90,7 @@ pxl8_result pxl8_tilesheet_load(pxl8_tilesheet* tilesheet, const char* filepath,
|
|||
}
|
||||
|
||||
if (tilesheet->data) {
|
||||
free(tilesheet->data);
|
||||
pxl8_free(tilesheet->data);
|
||||
}
|
||||
|
||||
u32 width = ase_file.header.width;
|
||||
|
|
@ -111,8 +112,8 @@ pxl8_result pxl8_tilesheet_load(pxl8_tilesheet* tilesheet, const char* filepath,
|
|||
u16 ase_depth = ase_file.header.color_depth;
|
||||
bool gfx_hicolor = (tilesheet->pixel_mode == PXL8_PIXEL_HICOLOR);
|
||||
|
||||
size_t data_size = pixel_count * pxl8_bytes_per_pixel(tilesheet->pixel_mode);
|
||||
tilesheet->data = malloc(data_size);
|
||||
usize data_size = pixel_count * pxl8_bytes_per_pixel(tilesheet->pixel_mode);
|
||||
tilesheet->data = pxl8_malloc(data_size);
|
||||
if (!tilesheet->data) {
|
||||
pxl8_ase_destroy(&ase_file);
|
||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
|
|
@ -138,9 +139,9 @@ pxl8_result pxl8_tilesheet_load(pxl8_tilesheet* tilesheet, const char* filepath,
|
|||
} else if (ase_depth == 8 && gfx_hicolor) {
|
||||
pxl8_warn("Indexed ASE with hicolor gfx - storing as indexed");
|
||||
tilesheet->pixel_mode = PXL8_PIXEL_INDEXED;
|
||||
u8* new_data = realloc(tilesheet->data, pixel_count);
|
||||
u8* new_data = pxl8_realloc(tilesheet->data, pixel_count);
|
||||
if (!new_data) {
|
||||
free(tilesheet->data);
|
||||
pxl8_free(tilesheet->data);
|
||||
tilesheet->data = NULL;
|
||||
pxl8_ase_destroy(&ase_file);
|
||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
|
|
@ -149,16 +150,16 @@ pxl8_result pxl8_tilesheet_load(pxl8_tilesheet* tilesheet, const char* filepath,
|
|||
memcpy(tilesheet->data, src, pixel_count);
|
||||
} else {
|
||||
pxl8_error("Unsupported ASE color depth %d for gfx mode", ase_depth);
|
||||
free(tilesheet->data);
|
||||
pxl8_free(tilesheet->data);
|
||||
tilesheet->data = NULL;
|
||||
pxl8_ase_destroy(&ase_file);
|
||||
return PXL8_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
}
|
||||
|
||||
tilesheet->tile_valid = calloc(tilesheet->total_tiles + 1, sizeof(bool));
|
||||
tilesheet->tile_valid = pxl8_calloc(tilesheet->total_tiles + 1, sizeof(bool));
|
||||
if (!tilesheet->tile_valid) {
|
||||
free(tilesheet->data);
|
||||
pxl8_free(tilesheet->data);
|
||||
tilesheet->data = NULL;
|
||||
pxl8_ase_destroy(&ase_file);
|
||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
|
|
@ -259,14 +260,14 @@ pxl8_result pxl8_tilesheet_add_animation(pxl8_tilesheet* tilesheet, u16 base_til
|
|||
if (base_tile_id == 0 || base_tile_id > tilesheet->total_tiles) return PXL8_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
if (!tilesheet->animations) {
|
||||
tilesheet->animations = calloc(tilesheet->total_tiles + 1, sizeof(pxl8_tile_animation));
|
||||
tilesheet->animations = pxl8_calloc(tilesheet->total_tiles + 1, sizeof(pxl8_tile_animation));
|
||||
if (!tilesheet->animations) return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
pxl8_tile_animation* anim = &tilesheet->animations[base_tile_id];
|
||||
if (anim->frames) free(anim->frames);
|
||||
if (anim->frames) pxl8_free(anim->frames);
|
||||
|
||||
anim->frames = malloc(frame_count * sizeof(u16));
|
||||
anim->frames = pxl8_malloc(frame_count * sizeof(u16));
|
||||
if (!anim->frames) return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
memcpy(anim->frames, frames, frame_count * sizeof(u16));
|
||||
|
|
@ -340,7 +341,7 @@ void pxl8_tilesheet_set_tile_property(pxl8_tilesheet* tilesheet, u16 tile_id,
|
|||
if (!tilesheet || !props || tile_id == 0 || tile_id > tilesheet->total_tiles) return;
|
||||
|
||||
if (!tilesheet->properties) {
|
||||
tilesheet->properties = calloc(tilesheet->total_tiles + 1, sizeof(pxl8_tile_properties));
|
||||
tilesheet->properties = pxl8_calloc(tilesheet->total_tiles + 1, sizeof(pxl8_tile_properties));
|
||||
if (!tilesheet->properties) return;
|
||||
}
|
||||
|
||||
|
|
@ -365,15 +366,15 @@ pxl8_result pxl8_tilesheet_add_autotile_rule(pxl8_tilesheet* tilesheet, u16 base
|
|||
}
|
||||
|
||||
if (!tilesheet->autotile_rules) {
|
||||
tilesheet->autotile_rules = calloc(tilesheet->total_tiles + 1, sizeof(pxl8_autotile_rule*));
|
||||
tilesheet->autotile_rule_counts = calloc(tilesheet->total_tiles + 1, sizeof(u32));
|
||||
tilesheet->autotile_rules = pxl8_calloc(tilesheet->total_tiles + 1, sizeof(pxl8_autotile_rule*));
|
||||
tilesheet->autotile_rule_counts = pxl8_calloc(tilesheet->total_tiles + 1, sizeof(u32));
|
||||
if (!tilesheet->autotile_rules || !tilesheet->autotile_rule_counts) {
|
||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
u32 count = tilesheet->autotile_rule_counts[base_tile_id];
|
||||
pxl8_autotile_rule* new_rules = realloc(tilesheet->autotile_rules[base_tile_id],
|
||||
pxl8_autotile_rule* new_rules = pxl8_realloc(tilesheet->autotile_rules[base_tile_id],
|
||||
(count + 1) * sizeof(pxl8_autotile_rule));
|
||||
if (!new_rules) return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include "pxl8_gfx.h"
|
||||
#include "pxl8_log.h"
|
||||
#include "pxl8_math.h"
|
||||
#include "pxl8_mem.h"
|
||||
|
||||
pxl8_transition* pxl8_transition_create(pxl8_transition_type type, f32 duration) {
|
||||
if (duration <= 0.0f) {
|
||||
|
|
@ -13,7 +14,7 @@ pxl8_transition* pxl8_transition_create(pxl8_transition_type type, f32 duration)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
pxl8_transition* transition = (pxl8_transition*)calloc(1, sizeof(pxl8_transition));
|
||||
pxl8_transition* transition = (pxl8_transition*)pxl8_calloc(1, sizeof(pxl8_transition));
|
||||
if (!transition) {
|
||||
pxl8_error("Failed to allocate transition");
|
||||
return NULL;
|
||||
|
|
@ -33,7 +34,7 @@ pxl8_transition* pxl8_transition_create(pxl8_transition_type type, f32 duration)
|
|||
|
||||
void pxl8_transition_destroy(pxl8_transition* transition) {
|
||||
if (!transition) return;
|
||||
free(transition);
|
||||
pxl8_free(transition);
|
||||
}
|
||||
|
||||
f32 pxl8_transition_get_progress(const pxl8_transition* transition) {
|
||||
|
|
|
|||
|
|
@ -4,9 +4,10 @@
|
|||
#include <string.h>
|
||||
|
||||
#include "pxl8_gfx.h"
|
||||
#include "pxl8_mem.h"
|
||||
|
||||
pxl8_gui_state* pxl8_gui_state_create(void) {
|
||||
pxl8_gui_state* state = (pxl8_gui_state*)malloc(sizeof(pxl8_gui_state));
|
||||
pxl8_gui_state* state = (pxl8_gui_state*)pxl8_malloc(sizeof(pxl8_gui_state));
|
||||
if (!state) return NULL;
|
||||
|
||||
memset(state, 0, sizeof(pxl8_gui_state));
|
||||
|
|
@ -15,21 +16,23 @@ pxl8_gui_state* pxl8_gui_state_create(void) {
|
|||
|
||||
void pxl8_gui_state_destroy(pxl8_gui_state* state) {
|
||||
if (!state) return;
|
||||
free(state);
|
||||
pxl8_free(state);
|
||||
}
|
||||
|
||||
void pxl8_gui_begin_frame(pxl8_gui_state* state) {
|
||||
void pxl8_gui_begin_frame(pxl8_gui_state* state, pxl8_gfx* gfx) {
|
||||
if (!state) return;
|
||||
state->hot_id = 0;
|
||||
if (gfx) pxl8_gfx_push_target(gfx);
|
||||
}
|
||||
|
||||
void pxl8_gui_end_frame(pxl8_gui_state* state) {
|
||||
void pxl8_gui_end_frame(pxl8_gui_state* state, pxl8_gfx* gfx) {
|
||||
if (!state) return;
|
||||
|
||||
if (!state->cursor_down) {
|
||||
state->active_id = 0;
|
||||
}
|
||||
state->cursor_clicked = false;
|
||||
if (gfx) pxl8_gfx_pop_target(gfx);
|
||||
}
|
||||
|
||||
void pxl8_gui_cursor_move(pxl8_gui_state* state, i32 x, i32 y) {
|
||||
|
|
@ -80,16 +83,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 = 4;
|
||||
border_color = 3;
|
||||
bg_color = pxl8_gfx_ui_color(gfx, PXL8_UI_BG3);
|
||||
border_color = pxl8_gfx_ui_color(gfx, PXL8_UI_BG2);
|
||||
offset_x = 1;
|
||||
offset_y = 1;
|
||||
} else if (is_hot || cursor_over) {
|
||||
bg_color = 4;
|
||||
border_color = 8;
|
||||
bg_color = pxl8_gfx_ui_color(gfx, PXL8_UI_BG3);
|
||||
border_color = pxl8_gfx_ui_color(gfx, PXL8_UI_FG0);
|
||||
} else {
|
||||
bg_color = 3;
|
||||
border_color = 4;
|
||||
bg_color = pxl8_gfx_ui_color(gfx, PXL8_UI_BG2);
|
||||
border_color = pxl8_gfx_ui_color(gfx, PXL8_UI_BG3);
|
||||
}
|
||||
|
||||
pxl8_2d_rect_fill(gfx, x, y, w, h, bg_color);
|
||||
|
|
@ -98,7 +101,7 @@ 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, 6);
|
||||
pxl8_2d_text(gfx, label, text_x, text_y, pxl8_gfx_ui_color(gfx, PXL8_UI_FG1));
|
||||
|
||||
return clicked;
|
||||
}
|
||||
|
|
@ -106,14 +109,19 @@ bool pxl8_gui_button(pxl8_gui_state* state, pxl8_gfx* gfx, u32 id, i32 x, i32 y,
|
|||
void pxl8_gui_window(pxl8_gfx* gfx, i32 x, i32 y, i32 w, i32 h, const char* title) {
|
||||
if (!gfx || !title) return;
|
||||
|
||||
pxl8_2d_rect_fill(gfx, x, y, w, 28, 1);
|
||||
pxl8_2d_rect_fill(gfx, x, y + 28, w, h - 28, 2);
|
||||
pxl8_2d_rect(gfx, x, y, w, h, 4);
|
||||
pxl8_2d_rect_fill(gfx, x, y + 28, w, 1, 4);
|
||||
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);
|
||||
|
||||
pxl8_2d_rect_fill(gfx, x, y, w, 28, title_bg);
|
||||
pxl8_2d_rect_fill(gfx, x, y + 28, w, h - 28, body_bg);
|
||||
pxl8_2d_rect(gfx, x, y, w, h, border);
|
||||
pxl8_2d_rect_fill(gfx, x, y + 28, w, 1, border);
|
||||
|
||||
i32 title_x = x + 10;
|
||||
i32 title_y = y + (28 / 2) - 5;
|
||||
pxl8_2d_text(gfx, title, title_x, title_y, 8);
|
||||
pxl8_2d_text(gfx, title, title_x, title_y, title_fg);
|
||||
}
|
||||
|
||||
void pxl8_gui_label(pxl8_gfx* gfx, i32 x, i32 y, const char* text, u8 color) {
|
||||
|
|
@ -22,8 +22,8 @@ void pxl8_gui_state_destroy(pxl8_gui_state* state);
|
|||
void pxl8_gui_get_cursor_pos(const pxl8_gui_state* state, i32* x, i32* y);
|
||||
bool pxl8_gui_is_hovering(const pxl8_gui_state* state);
|
||||
|
||||
void pxl8_gui_begin_frame(pxl8_gui_state* state);
|
||||
void pxl8_gui_end_frame(pxl8_gui_state* state);
|
||||
void pxl8_gui_begin_frame(pxl8_gui_state* state, pxl8_gfx* gfx);
|
||||
void pxl8_gui_end_frame(pxl8_gui_state* state, pxl8_gfx* gfx);
|
||||
|
||||
void pxl8_gui_cursor_down(pxl8_gui_state* state);
|
||||
void pxl8_gui_cursor_move(pxl8_gui_state* state, i32 x, i32 y);
|
||||
41
src/gui/pxl8_gui_palette.h
Normal file
41
src/gui/pxl8_gui_palette.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_types.h"
|
||||
|
||||
#define PXL8_UI_PALETTE_SIZE 16
|
||||
|
||||
#define PXL8_UI_BG0 0
|
||||
#define PXL8_UI_BG1 1
|
||||
#define PXL8_UI_BG2 2
|
||||
#define PXL8_UI_BG3 3
|
||||
#define PXL8_UI_FG0 4
|
||||
#define PXL8_UI_FG1 5
|
||||
#define PXL8_UI_FG2 6
|
||||
#define PXL8_UI_FG3 7
|
||||
#define PXL8_UI_RED 8
|
||||
#define PXL8_UI_GREEN 9
|
||||
#define PXL8_UI_YELLOW 10
|
||||
#define PXL8_UI_BLUE 11
|
||||
#define PXL8_UI_PURPLE 12
|
||||
#define PXL8_UI_AQUA 13
|
||||
#define PXL8_UI_ORANGE 14
|
||||
#define PXL8_UI_GRAY 15
|
||||
|
||||
static const u32 pxl8_ui_palette[PXL8_UI_PALETTE_SIZE] = {
|
||||
0xFF282828,
|
||||
0xFF3c3836,
|
||||
0xFF504945,
|
||||
0xFF665c54,
|
||||
0xFFc7f1fb,
|
||||
0xFFb2dbeb,
|
||||
0xFFa1c4d5,
|
||||
0xFF93aebd,
|
||||
0xFF3449fb,
|
||||
0xFF26bbb8,
|
||||
0xFF2fbdfa,
|
||||
0xFF98a583,
|
||||
0xFF9b86d3,
|
||||
0xFF7cc08e,
|
||||
0xFF1980fe,
|
||||
0xFF928374,
|
||||
};
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
#include "pxl8_sdl3.h"
|
||||
#include "pxl8_hal.h"
|
||||
|
||||
#define SDL_MAIN_USE_CALLBACKS
|
||||
#include <SDL3/SDL.h>
|
||||
|
|
@ -8,13 +8,15 @@
|
|||
#include "pxl8_log.h"
|
||||
#include "pxl8_sys.h"
|
||||
|
||||
extern const pxl8_hal pxl8_hal_sdl3;
|
||||
|
||||
typedef struct pxl8_sdl3_context {
|
||||
SDL_Texture* framebuffer;
|
||||
SDL_Renderer* renderer;
|
||||
SDL_Window* window;
|
||||
|
||||
u32* rgba_buffer;
|
||||
size_t rgba_buffer_size;
|
||||
usize rgba_buffer_size;
|
||||
} pxl8_sdl3_context;
|
||||
|
||||
static void* sdl3_create(i32 render_w, i32 render_h,
|
||||
|
|
@ -101,7 +103,7 @@ static void sdl3_upload_texture(void* platform_data, const void* pixels, u32 w,
|
|||
if (!platform_data || !pixels) return;
|
||||
|
||||
pxl8_sdl3_context* ctx = (pxl8_sdl3_context*)platform_data;
|
||||
size_t pixel_count = w * h;
|
||||
usize pixel_count = w * h;
|
||||
|
||||
if (bpp == 4) {
|
||||
SDL_UpdateTexture(ctx->framebuffer, NULL, pixels, w * 4);
|
||||
8
src/hal/pxl8_mem.h
Normal file
8
src/hal/pxl8_mem.h
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_types.h"
|
||||
|
||||
void* pxl8_malloc(usize size);
|
||||
void* pxl8_calloc(usize count, usize size);
|
||||
void* pxl8_realloc(void* ptr, usize size);
|
||||
void pxl8_free(void* ptr);
|
||||
19
src/hal/pxl8_mem_sdl3.c
Normal file
19
src/hal/pxl8_mem_sdl3.c
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#include "pxl8_mem.h"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
void* pxl8_malloc(usize size) {
|
||||
return SDL_malloc(size);
|
||||
}
|
||||
|
||||
void* pxl8_calloc(usize count, usize size) {
|
||||
return SDL_calloc(count, size);
|
||||
}
|
||||
|
||||
void* pxl8_realloc(void* ptr, usize size) {
|
||||
return SDL_realloc(ptr, size);
|
||||
}
|
||||
|
||||
void pxl8_free(void* ptr) {
|
||||
SDL_free(ptr);
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_hal.h"
|
||||
|
||||
extern const pxl8_hal pxl8_hal_sdl3;
|
||||
|
|
@ -1,13 +1,15 @@
|
|||
local anim = require("pxl8.anim")
|
||||
local bytes = require("pxl8.bytes")
|
||||
local core = require("pxl8.core")
|
||||
local effects = require("pxl8.effects")
|
||||
local gfx2d = require("pxl8.gfx2d")
|
||||
local gfx3d = require("pxl8.gfx3d")
|
||||
local gui = require("pxl8.gui")
|
||||
local input = require("pxl8.input")
|
||||
local math3d = require("pxl8.math")
|
||||
local math = require("pxl8.math")
|
||||
local net = require("pxl8.net")
|
||||
local particles = require("pxl8.particles")
|
||||
local procgen = require("pxl8.procgen")
|
||||
local sfx = require("pxl8.sfx")
|
||||
local tilemap = require("pxl8.tilemap")
|
||||
local transition = require("pxl8.transition")
|
||||
|
|
@ -27,16 +29,20 @@ pxl8.debug = core.debug
|
|||
pxl8.trace = core.trace
|
||||
pxl8.quit = core.quit
|
||||
|
||||
pxl8.rng_seed = core.rng_seed
|
||||
pxl8.rng_next = core.rng_next
|
||||
pxl8.hash32 = math.hash32
|
||||
pxl8.rng_f32 = core.rng_f32
|
||||
pxl8.rng_next = core.rng_next
|
||||
pxl8.rng_range = core.rng_range
|
||||
pxl8.rng_seed = core.rng_seed
|
||||
|
||||
pxl8.find_color = core.find_color
|
||||
pxl8.palette_color = core.palette_color
|
||||
pxl8.palette_index = core.palette_index
|
||||
pxl8.ramp_index = core.ramp_index
|
||||
pxl8.set_colormap = core.set_colormap
|
||||
pxl8.set_palette = core.set_palette
|
||||
pxl8.set_palette_rgb = core.set_palette_rgb
|
||||
pxl8.update_palette_deps = core.update_palette_deps
|
||||
|
||||
pxl8.clear = gfx2d.clear
|
||||
pxl8.pixel = gfx2d.pixel
|
||||
|
|
@ -78,18 +84,28 @@ pxl8.Anim = anim.Anim
|
|||
pxl8.create_anim = anim.Anim.new
|
||||
pxl8.create_anim_from_ase = anim.Anim.from_ase
|
||||
|
||||
pxl8.bounds = math3d.bounds
|
||||
pxl8.bounds = math.bounds
|
||||
|
||||
pxl8.Camera3D = gfx3d.Camera3D
|
||||
pxl8.create_camera_3d = gfx3d.Camera3D.new
|
||||
pxl8.Mesh = gfx3d.Mesh
|
||||
pxl8.begin_frame_3d = gfx3d.begin_frame
|
||||
pxl8.clear_3d = gfx3d.clear
|
||||
pxl8.clear_depth = gfx3d.clear_depth
|
||||
pxl8.create_camera_3d = gfx3d.Camera3D.new
|
||||
pxl8.create_mesh = gfx3d.Mesh.new
|
||||
pxl8.create_vec3_array = gfx3d.create_vec3_array
|
||||
pxl8.draw_line_3d = gfx3d.draw_line
|
||||
pxl8.draw_mesh = gfx3d.draw_mesh
|
||||
pxl8.end_frame_3d = gfx3d.end_frame
|
||||
pxl8.Mesh = gfx3d.Mesh
|
||||
pxl8.create_mesh = gfx3d.Mesh.new
|
||||
pxl8.project_points = gfx3d.project_points
|
||||
|
||||
pxl8.GLOW_CIRCLE = effects.GLOW_CIRCLE
|
||||
pxl8.GLOW_DIAMOND = effects.GLOW_DIAMOND
|
||||
pxl8.GLOW_SHAFT = effects.GLOW_SHAFT
|
||||
pxl8.Glows = effects.Glows
|
||||
pxl8.create_glows = effects.Glows.new
|
||||
pxl8.Lights = effects.Lights
|
||||
pxl8.create_lights = effects.Lights.new
|
||||
|
||||
pxl8.Compressor = sfx.Compressor
|
||||
pxl8.create_compressor = sfx.Compressor.new
|
||||
|
|
@ -103,16 +119,18 @@ pxl8.create_gui = gui.Gui.new
|
|||
pxl8.gui_label = gui.label
|
||||
pxl8.gui_window = gui.window
|
||||
|
||||
pxl8.mat4_identity = math3d.mat4_identity
|
||||
pxl8.mat4_lookat = math3d.mat4_lookat
|
||||
pxl8.mat4_multiply = math3d.mat4_multiply
|
||||
pxl8.mat4_ortho = math3d.mat4_ortho
|
||||
pxl8.mat4_perspective = math3d.mat4_perspective
|
||||
pxl8.mat4_rotate_x = math3d.mat4_rotate_x
|
||||
pxl8.mat4_rotate_y = math3d.mat4_rotate_y
|
||||
pxl8.mat4_rotate_z = math3d.mat4_rotate_z
|
||||
pxl8.mat4_scale = math3d.mat4_scale
|
||||
pxl8.mat4_translate = math3d.mat4_translate
|
||||
pxl8.mat4_identity = math.mat4_identity
|
||||
pxl8.mat4_lookat = math.mat4_lookat
|
||||
pxl8.mat4_multiply = math.mat4_multiply
|
||||
pxl8.mat4_multiply_vec3 = math.mat4_multiply_vec3
|
||||
pxl8.mat4_multiply_vec4 = math.mat4_multiply_vec4
|
||||
pxl8.mat4_orthographic = math.mat4_orthographic
|
||||
pxl8.mat4_perspective = math.mat4_perspective
|
||||
pxl8.mat4_rotate_x = math.mat4_rotate_x
|
||||
pxl8.mat4_rotate_y = math.mat4_rotate_y
|
||||
pxl8.mat4_rotate_z = math.mat4_rotate_z
|
||||
pxl8.mat4_scale = math.mat4_scale
|
||||
pxl8.mat4_translate = math.mat4_translate
|
||||
|
||||
pxl8.Net = net.Net
|
||||
pxl8.create_net = net.Net.new
|
||||
|
|
@ -141,9 +159,10 @@ pxl8.pack_u64_le = bytes.pack_u64_le
|
|||
pxl8.Particles = particles.Particles
|
||||
pxl8.create_particles = particles.Particles.new
|
||||
|
||||
pxl8.Graph = procgen.Graph
|
||||
pxl8.create_graph = procgen.create_graph
|
||||
pxl8.PROCGEN_ROOMS = world.PROCGEN_ROOMS
|
||||
pxl8.PROCGEN_TERRAIN = world.PROCGEN_TERRAIN
|
||||
pxl8.procgen_tex = world.procgen_tex
|
||||
|
||||
pxl8.SfxContext = sfx.SfxContext
|
||||
pxl8.SfxNode = sfx.SfxNode
|
||||
|
|
|
|||
|
|
@ -20,27 +20,40 @@ function core.get_fps()
|
|||
end
|
||||
|
||||
function core.palette_color(index)
|
||||
local pal = C.pxl8_gfx_get_palette(core.gfx)
|
||||
local pal = C.pxl8_gfx_palette(core.gfx)
|
||||
if pal == nil then return 0 end
|
||||
return C.pxl8_palette_color(pal, index)
|
||||
end
|
||||
|
||||
function core.palette_index(color)
|
||||
local pal = C.pxl8_gfx_get_palette(core.gfx)
|
||||
local pal = C.pxl8_gfx_palette(core.gfx)
|
||||
if pal == nil then return -1 end
|
||||
return C.pxl8_palette_index(pal, color)
|
||||
end
|
||||
|
||||
function core.set_palette_rgb(index, r, g, b)
|
||||
local pal = C.pxl8_gfx_get_palette(core.gfx)
|
||||
local pal = C.pxl8_gfx_palette(core.gfx)
|
||||
if pal == nil then return end
|
||||
C.pxl8_palette_set_rgb(pal, index, r, g, b)
|
||||
end
|
||||
|
||||
function core.set_palette(colors, count)
|
||||
C.pxl8_gfx_set_palette_colors(core.gfx, colors, count)
|
||||
end
|
||||
|
||||
function core.set_colormap(data, size)
|
||||
local cm = C.pxl8_gfx_colormap(core.gfx)
|
||||
if cm == nil then return end
|
||||
C.pxl8_set_colormap(cm, data, size)
|
||||
end
|
||||
|
||||
function core.update_palette_deps()
|
||||
C.pxl8_gfx_blend_tables_update(core.gfx)
|
||||
C.pxl8_gfx_colormap_update(core.gfx)
|
||||
end
|
||||
|
||||
function core.ramp_index(position)
|
||||
local pal = C.pxl8_gfx_get_palette(core.gfx)
|
||||
local pal = C.pxl8_gfx_palette(core.gfx)
|
||||
if pal == nil then return 0 end
|
||||
return C.pxl8_palette_ramp_index(pal, position)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,25 +8,72 @@ effects.GLOW_CIRCLE = 0
|
|||
effects.GLOW_DIAMOND = 1
|
||||
effects.GLOW_SHAFT = 2
|
||||
|
||||
function effects.glows(glows)
|
||||
if not glows or #glows == 0 then return end
|
||||
local Glows = {}
|
||||
Glows.__index = Glows
|
||||
|
||||
local count = #glows
|
||||
local glow_array = ffi.new("pxl8_glow_source[?]", count)
|
||||
|
||||
for i, g in ipairs(glows) do
|
||||
local idx = i - 1
|
||||
glow_array[idx].x = g.x or 0
|
||||
glow_array[idx].y = g.y or 0
|
||||
glow_array[idx].radius = g.radius or 8
|
||||
glow_array[idx].intensity = g.intensity or 255
|
||||
glow_array[idx].color = g.color or 15
|
||||
glow_array[idx].depth = g.depth or 0xFFFF
|
||||
glow_array[idx].height = g.height or 0
|
||||
glow_array[idx].shape = g.shape or 0
|
||||
function Glows.new(capacity)
|
||||
local ptr = C.pxl8_glows_create(capacity or 1000)
|
||||
if ptr == nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
C.pxl8_gfx_apply_effect(core.gfx, C.PXL8_GFX_EFFECT_GLOWS, glow_array, count)
|
||||
return setmetatable({ _ptr = ptr }, Glows)
|
||||
end
|
||||
|
||||
function Glows:add(x, y, radius, intensity, color, shape)
|
||||
C.pxl8_glows_add(self._ptr, x, y, radius or 8, intensity or 255, color or 15, shape or 0)
|
||||
end
|
||||
|
||||
function Glows:clear()
|
||||
C.pxl8_glows_clear(self._ptr)
|
||||
end
|
||||
|
||||
function Glows:count()
|
||||
return C.pxl8_glows_count(self._ptr)
|
||||
end
|
||||
|
||||
function Glows:destroy()
|
||||
if self._ptr then
|
||||
C.pxl8_glows_destroy(self._ptr)
|
||||
self._ptr = nil
|
||||
end
|
||||
end
|
||||
|
||||
function Glows:render()
|
||||
C.pxl8_glows_render(self._ptr, core.gfx)
|
||||
end
|
||||
|
||||
effects.Glows = Glows
|
||||
|
||||
local Lights = {}
|
||||
Lights.__index = Lights
|
||||
|
||||
function Lights.new(capacity)
|
||||
local ptr = C.pxl8_lights_create(capacity or 256)
|
||||
if ptr == nil then
|
||||
return nil
|
||||
end
|
||||
return setmetatable({ _ptr = ptr }, Lights)
|
||||
end
|
||||
|
||||
function Lights:add(x, y, z, r, g, b, intensity, radius)
|
||||
C.pxl8_lights_add(self._ptr, x, y, z, r or 255, g or 255, b or 255, intensity or 255, radius or 10)
|
||||
end
|
||||
|
||||
function Lights:clear()
|
||||
C.pxl8_lights_clear(self._ptr)
|
||||
end
|
||||
|
||||
function Lights:count()
|
||||
return C.pxl8_lights_count(self._ptr)
|
||||
end
|
||||
|
||||
function Lights:destroy()
|
||||
if self._ptr then
|
||||
C.pxl8_lights_destroy(self._ptr)
|
||||
self._ptr = nil
|
||||
end
|
||||
end
|
||||
|
||||
effects.Lights = Lights
|
||||
|
||||
return effects
|
||||
|
|
|
|||
|
|
@ -75,6 +75,15 @@ function Camera3D:update(dt)
|
|||
C.pxl8_3d_camera_update(self._ptr, dt)
|
||||
end
|
||||
|
||||
function Camera3D:world_to_screen(x, y, z, width, height)
|
||||
local pos = ffi.new("pxl8_vec3", {x = x, y = y, z = z})
|
||||
local result = C.pxl8_3d_camera_world_to_screen(self._ptr, pos, width, height)
|
||||
if result.visible then
|
||||
return {x = result.x, y = result.y, depth = result.depth}
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
gfx3d.Camera3D = Camera3D
|
||||
|
||||
local Mesh = {}
|
||||
|
|
@ -123,14 +132,15 @@ gfx3d.Mesh = Mesh
|
|||
function gfx3d.draw_mesh(mesh, opts)
|
||||
opts = opts or {}
|
||||
local model = ffi.new("pxl8_mat4")
|
||||
model.m[0] = 1
|
||||
model.m[5] = 1
|
||||
model.m[10] = 1
|
||||
local s = opts.scale or 1
|
||||
model.m[0] = s
|
||||
model.m[5] = s
|
||||
model.m[10] = s
|
||||
model.m[15] = 1
|
||||
if opts.x then model.m[12] = opts.x end
|
||||
if opts.y then model.m[13] = opts.y end
|
||||
if opts.z then model.m[14] = opts.z end
|
||||
local material = ffi.new("pxl8_material", {
|
||||
local material = ffi.new("pxl8_gfx_material", {
|
||||
texture_id = opts.texture or 0,
|
||||
alpha = opts.alpha or 255,
|
||||
blend_mode = opts.blend_mode or 0,
|
||||
|
|
@ -139,12 +149,13 @@ function gfx3d.draw_mesh(mesh, opts)
|
|||
dynamic_lighting = opts.lighting or false,
|
||||
per_pixel = opts.per_pixel or false,
|
||||
vertex_color_passthrough = opts.passthrough or false,
|
||||
wireframe = opts.wireframe or false,
|
||||
emissive_intensity = opts.emissive or 0.0,
|
||||
})
|
||||
C.pxl8_3d_draw_mesh(core.gfx, mesh._ptr, model, material)
|
||||
end
|
||||
|
||||
function gfx3d.begin_frame(camera, uniforms)
|
||||
function gfx3d.begin_frame(camera, lights, uniforms)
|
||||
uniforms = uniforms or {}
|
||||
local u = ffi.new("pxl8_3d_uniforms")
|
||||
|
||||
|
|
@ -164,27 +175,8 @@ function gfx3d.begin_frame(camera, uniforms)
|
|||
end
|
||||
u.celestial_intensity = uniforms.celestial_intensity or 0.0
|
||||
|
||||
u.num_lights = 0
|
||||
if uniforms.lights then
|
||||
for i, light in ipairs(uniforms.lights) do
|
||||
if i > 16 then break end
|
||||
local idx = i - 1
|
||||
u.lights[idx].position.x = light.x or 0
|
||||
u.lights[idx].position.y = light.y or 0
|
||||
u.lights[idx].position.z = light.z or 0
|
||||
u.lights[idx].r = light.r or 255
|
||||
u.lights[idx].g = light.g or 255
|
||||
u.lights[idx].b = light.b or 255
|
||||
u.lights[idx].intensity = light.intensity or 255
|
||||
u.lights[idx].radius = light.radius or 100
|
||||
local radius_sq = u.lights[idx].radius * u.lights[idx].radius
|
||||
u.lights[idx].radius_sq = radius_sq
|
||||
u.lights[idx].inv_radius_sq = radius_sq > 0 and (1.0 / radius_sq) or 0
|
||||
u.num_lights = i
|
||||
end
|
||||
end
|
||||
|
||||
C.pxl8_3d_begin_frame(core.gfx, camera._ptr, u)
|
||||
local lights_ptr = lights and lights._ptr or nil
|
||||
C.pxl8_3d_begin_frame(core.gfx, camera._ptr, lights_ptr, u)
|
||||
end
|
||||
|
||||
function gfx3d.clear(color)
|
||||
|
|
@ -205,4 +197,12 @@ function gfx3d.end_frame()
|
|||
C.pxl8_3d_end_frame(core.gfx)
|
||||
end
|
||||
|
||||
function gfx3d.project_points(input, output, count, transform)
|
||||
C.pxl8_3d_project_points(core.gfx, input, output, count, transform)
|
||||
end
|
||||
|
||||
function gfx3d.create_vec3_array(count)
|
||||
return ffi.new("pxl8_vec3[?]", count)
|
||||
end
|
||||
|
||||
return gfx3d
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ function Gui.new()
|
|||
end
|
||||
|
||||
function Gui:begin_frame()
|
||||
C.pxl8_gui_begin_frame(self._ptr)
|
||||
C.pxl8_gui_begin_frame(self._ptr, core.gfx)
|
||||
end
|
||||
|
||||
function Gui:button(id, x, y, w, h, label)
|
||||
|
|
@ -43,7 +43,7 @@ function Gui:destroy()
|
|||
end
|
||||
|
||||
function Gui:end_frame()
|
||||
C.pxl8_gui_end_frame(self._ptr)
|
||||
C.pxl8_gui_end_frame(self._ptr, core.gfx)
|
||||
end
|
||||
|
||||
function Gui:get_cursor_pos()
|
||||
|
|
|
|||
|
|
@ -1,53 +1,65 @@
|
|||
local ffi = require("ffi")
|
||||
local C = ffi.C
|
||||
|
||||
local math3d = {}
|
||||
local math = {}
|
||||
|
||||
function math3d.mat4_identity()
|
||||
function math.hash32(x)
|
||||
return C.pxl8_hash32(x)
|
||||
end
|
||||
|
||||
function math.mat4_identity()
|
||||
return C.pxl8_mat4_identity()
|
||||
end
|
||||
|
||||
function math3d.mat4_mul(a, b)
|
||||
return C.pxl8_mat4_mul(a, b)
|
||||
function math.mat4_multiply(a, b)
|
||||
return C.pxl8_mat4_multiply(a, b)
|
||||
end
|
||||
|
||||
function math3d.mat4_translate(x, y, z)
|
||||
function math.mat4_multiply_vec3(m, v)
|
||||
return C.pxl8_mat4_multiply_vec3(m, v)
|
||||
end
|
||||
|
||||
function math.mat4_multiply_vec4(m, v)
|
||||
return C.pxl8_mat4_multiply_vec4(m, v)
|
||||
end
|
||||
|
||||
function math.mat4_translate(x, y, z)
|
||||
return C.pxl8_mat4_translate(x, y, z)
|
||||
end
|
||||
|
||||
function math3d.mat4_rotate_x(angle)
|
||||
function math.mat4_rotate_x(angle)
|
||||
return C.pxl8_mat4_rotate_x(angle)
|
||||
end
|
||||
|
||||
function math3d.mat4_rotate_y(angle)
|
||||
function math.mat4_rotate_y(angle)
|
||||
return C.pxl8_mat4_rotate_y(angle)
|
||||
end
|
||||
|
||||
function math3d.mat4_rotate_z(angle)
|
||||
function math.mat4_rotate_z(angle)
|
||||
return C.pxl8_mat4_rotate_z(angle)
|
||||
end
|
||||
|
||||
function math3d.mat4_scale(x, y, z)
|
||||
function math.mat4_scale(x, y, z)
|
||||
return C.pxl8_mat4_scale(x, y, z)
|
||||
end
|
||||
|
||||
function math3d.mat4_ortho(left, right, bottom, top, near, far)
|
||||
return C.pxl8_mat4_ortho(left, right, bottom, top, near, far)
|
||||
function math.mat4_orthographic(left, right, bottom, top, near, far)
|
||||
return C.pxl8_mat4_orthographic(left, right, bottom, top, near, far)
|
||||
end
|
||||
|
||||
function math3d.mat4_perspective(fov, aspect, near, far)
|
||||
function math.mat4_perspective(fov, aspect, near, far)
|
||||
return C.pxl8_mat4_perspective(fov, aspect, near, far)
|
||||
end
|
||||
|
||||
function math3d.mat4_lookat(eye, center, up)
|
||||
function math.mat4_lookat(eye, center, up)
|
||||
local eye_vec = ffi.new("pxl8_vec3", {x = eye[1], y = eye[2], z = eye[3]})
|
||||
local center_vec = ffi.new("pxl8_vec3", {x = center[1], y = center[2], z = center[3]})
|
||||
local up_vec = ffi.new("pxl8_vec3", {x = up[1], y = up[2], z = up[3]})
|
||||
return C.pxl8_mat4_lookat(eye_vec, center_vec, up_vec)
|
||||
end
|
||||
|
||||
function math3d.bounds(x, y, w, h)
|
||||
function math.bounds(x, y, w, h)
|
||||
return ffi.new("pxl8_bounds", {x = x, y = y, w = w, h = h})
|
||||
end
|
||||
|
||||
return math3d
|
||||
return math
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ function Particles.new(max_count)
|
|||
if ps == nil then
|
||||
return nil
|
||||
end
|
||||
local pal = C.pxl8_gfx_get_palette(core.gfx)
|
||||
local pal = C.pxl8_gfx_palette(core.gfx)
|
||||
if pal ~= nil then
|
||||
C.pxl8_particles_set_palette(ps, pal)
|
||||
end
|
||||
|
|
|
|||
99
src/lua/pxl8/procgen.lua
Normal file
99
src/lua/pxl8/procgen.lua
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
local ffi = require("ffi")
|
||||
local C = ffi.C
|
||||
local core = require("pxl8.core")
|
||||
|
||||
local procgen = {}
|
||||
|
||||
procgen.OP_CONST = C.PXL8_OP_CONST
|
||||
procgen.OP_INPUT_AGE = C.PXL8_OP_INPUT_AGE
|
||||
procgen.OP_INPUT_SEED = C.PXL8_OP_INPUT_SEED
|
||||
procgen.OP_INPUT_TIME = C.PXL8_OP_INPUT_TIME
|
||||
procgen.OP_INPUT_X = C.PXL8_OP_INPUT_X
|
||||
procgen.OP_INPUT_Y = C.PXL8_OP_INPUT_Y
|
||||
|
||||
procgen.OP_ABS = C.PXL8_OP_ABS
|
||||
procgen.OP_ADD = C.PXL8_OP_ADD
|
||||
procgen.OP_CEIL = C.PXL8_OP_CEIL
|
||||
procgen.OP_CLAMP = C.PXL8_OP_CLAMP
|
||||
procgen.OP_COS = C.PXL8_OP_COS
|
||||
procgen.OP_DIV = C.PXL8_OP_DIV
|
||||
procgen.OP_FLOOR = C.PXL8_OP_FLOOR
|
||||
procgen.OP_FRACT = C.PXL8_OP_FRACT
|
||||
procgen.OP_GRADIENT_LINEAR = C.PXL8_OP_GRADIENT_LINEAR
|
||||
procgen.OP_GRADIENT_RADIAL = C.PXL8_OP_GRADIENT_RADIAL
|
||||
procgen.OP_LERP = C.PXL8_OP_LERP
|
||||
procgen.OP_MAX = C.PXL8_OP_MAX
|
||||
procgen.OP_MIN = C.PXL8_OP_MIN
|
||||
procgen.OP_MOD = C.PXL8_OP_MOD
|
||||
procgen.OP_MUL = C.PXL8_OP_MUL
|
||||
procgen.OP_NEGATE = C.PXL8_OP_NEGATE
|
||||
procgen.OP_NOISE_FBM = C.PXL8_OP_NOISE_FBM
|
||||
procgen.OP_NOISE_PERLIN = C.PXL8_OP_NOISE_PERLIN
|
||||
procgen.OP_NOISE_RIDGED = C.PXL8_OP_NOISE_RIDGED
|
||||
procgen.OP_NOISE_TURBULENCE = C.PXL8_OP_NOISE_TURBULENCE
|
||||
procgen.OP_NOISE_VALUE = C.PXL8_OP_NOISE_VALUE
|
||||
procgen.OP_POW = C.PXL8_OP_POW
|
||||
procgen.OP_QUANTIZE = C.PXL8_OP_QUANTIZE
|
||||
procgen.OP_SELECT = C.PXL8_OP_SELECT
|
||||
procgen.OP_SIN = C.PXL8_OP_SIN
|
||||
procgen.OP_SMOOTHSTEP = C.PXL8_OP_SMOOTHSTEP
|
||||
procgen.OP_SQRT = C.PXL8_OP_SQRT
|
||||
procgen.OP_SUB = C.PXL8_OP_SUB
|
||||
procgen.OP_VORONOI_CELL = C.PXL8_OP_VORONOI_CELL
|
||||
procgen.OP_VORONOI_EDGE = C.PXL8_OP_VORONOI_EDGE
|
||||
procgen.OP_VORONOI_ID = C.PXL8_OP_VORONOI_ID
|
||||
|
||||
local Graph = {}
|
||||
Graph.__index = Graph
|
||||
|
||||
function Graph.new(capacity)
|
||||
capacity = capacity or 64
|
||||
local ptr = C.pxl8_graph_create(capacity)
|
||||
if ptr == nil then
|
||||
return nil
|
||||
end
|
||||
return setmetatable({ _ptr = ptr }, Graph)
|
||||
end
|
||||
|
||||
function Graph:destroy()
|
||||
if self._ptr then
|
||||
C.pxl8_graph_destroy(self._ptr)
|
||||
self._ptr = nil
|
||||
end
|
||||
end
|
||||
|
||||
function Graph:clear()
|
||||
C.pxl8_graph_clear(self._ptr)
|
||||
end
|
||||
|
||||
function Graph:add_node(op, in0, in1, in2, in3, param)
|
||||
return C.pxl8_graph_add_node(self._ptr, op, in0 or 0, in1 or 0, in2 or 0, in3 or 0, param or 0)
|
||||
end
|
||||
|
||||
function Graph:set_output(reg)
|
||||
C.pxl8_graph_set_output(self._ptr, reg)
|
||||
end
|
||||
|
||||
function Graph:set_seed(seed)
|
||||
C.pxl8_graph_set_seed(self._ptr, seed)
|
||||
end
|
||||
|
||||
function Graph:eval_texture(width, height)
|
||||
width = width or 64
|
||||
height = height or 64
|
||||
local buffer = ffi.new("u8[?]", width * height)
|
||||
C.pxl8_graph_eval_texture(self._ptr, buffer, width, height)
|
||||
local tex_id = C.pxl8_gfx_create_texture(core.gfx, buffer, width, height)
|
||||
if tex_id < 0 then
|
||||
return nil
|
||||
end
|
||||
return tex_id
|
||||
end
|
||||
|
||||
procgen.Graph = Graph
|
||||
|
||||
function procgen.create_graph(capacity)
|
||||
return Graph.new(capacity)
|
||||
end
|
||||
|
||||
return procgen
|
||||
|
|
@ -85,36 +85,14 @@ function World:resolve_collision(from_x, from_y, from_z, to_x, to_y, to_z, radiu
|
|||
return result.x, result.y, result.z
|
||||
end
|
||||
|
||||
function World:set_wireframe(enabled)
|
||||
C.pxl8_world_set_wireframe(self._ptr, enabled)
|
||||
end
|
||||
|
||||
function World:unload()
|
||||
C.pxl8_world_unload(self._ptr)
|
||||
end
|
||||
|
||||
world.World = World
|
||||
|
||||
function world.procgen_tex(params)
|
||||
local width = params.width or 64
|
||||
local height = params.height or 64
|
||||
local buffer = ffi.new("u8[?]", width * height)
|
||||
local tex_params = ffi.new("pxl8_procgen_tex_params")
|
||||
|
||||
local name = params.name or ""
|
||||
ffi.copy(tex_params.name, name, math.min(#name, 15))
|
||||
|
||||
tex_params.seed = params.seed or 0
|
||||
tex_params.width = width
|
||||
tex_params.height = height
|
||||
tex_params.scale = params.scale or 1.0
|
||||
tex_params.roughness = params.roughness or 0.0
|
||||
tex_params.base_color = params.base_color or 0
|
||||
tex_params.variation = params.variation or 0
|
||||
|
||||
C.pxl8_procgen_tex(buffer, tex_params)
|
||||
|
||||
local tex_id = C.pxl8_gfx_create_texture(core.gfx, buffer, width, height)
|
||||
if tex_id < 0 then
|
||||
return nil
|
||||
end
|
||||
return tex_id
|
||||
end
|
||||
|
||||
return world
|
||||
|
|
|
|||
|
|
@ -1,5 +1,14 @@
|
|||
#include "pxl8_math.h"
|
||||
|
||||
u32 pxl8_hash32(u32 x) {
|
||||
x ^= x >> 16;
|
||||
x *= 0x85EBCA6Bu;
|
||||
x ^= x >> 13;
|
||||
x *= 0xC2B2AE35u;
|
||||
x ^= x >> 16;
|
||||
return x;
|
||||
}
|
||||
|
||||
pxl8_vec2 pxl8_vec2_add(pxl8_vec2 a, pxl8_vec2 b) {
|
||||
return (pxl8_vec2){
|
||||
.x = a.x + b.x,
|
||||
|
|
@ -101,7 +110,7 @@ pxl8_mat4 pxl8_mat4_identity(void) {
|
|||
return mat;
|
||||
}
|
||||
|
||||
pxl8_mat4 pxl8_mat4_mul(pxl8_mat4 a, pxl8_mat4 b) {
|
||||
pxl8_mat4 pxl8_mat4_multiply(pxl8_mat4 a, pxl8_mat4 b) {
|
||||
pxl8_mat4 mat = {0};
|
||||
|
||||
for (i32 col = 0; col < 4; col++) {
|
||||
|
|
@ -117,7 +126,7 @@ pxl8_mat4 pxl8_mat4_mul(pxl8_mat4 a, pxl8_mat4 b) {
|
|||
return mat;
|
||||
}
|
||||
|
||||
pxl8_vec4 pxl8_mat4_mul_vec4(pxl8_mat4 m, pxl8_vec4 v) {
|
||||
pxl8_vec4 pxl8_mat4_multiply_vec4(pxl8_mat4 m, pxl8_vec4 v) {
|
||||
return (pxl8_vec4){
|
||||
.x = m.m[0] * v.x + m.m[4] * v.y + m.m[8] * v.z + m.m[12] * v.w,
|
||||
.y = m.m[1] * v.x + m.m[5] * v.y + m.m[9] * v.z + m.m[13] * v.w,
|
||||
|
|
@ -126,7 +135,7 @@ pxl8_vec4 pxl8_mat4_mul_vec4(pxl8_mat4 m, pxl8_vec4 v) {
|
|||
};
|
||||
}
|
||||
|
||||
pxl8_vec3 pxl8_mat4_mul_vec3(pxl8_mat4 m, pxl8_vec3 v) {
|
||||
pxl8_vec3 pxl8_mat4_multiply_vec3(pxl8_mat4 m, pxl8_vec3 v) {
|
||||
return (pxl8_vec3){
|
||||
.x = m.m[0] * v.x + m.m[4] * v.y + m.m[8] * v.z,
|
||||
.y = m.m[1] * v.x + m.m[5] * v.y + m.m[9] * v.z,
|
||||
|
|
@ -193,7 +202,7 @@ pxl8_mat4 pxl8_mat4_scale(f32 x, f32 y, f32 z) {
|
|||
return mat;
|
||||
}
|
||||
|
||||
pxl8_mat4 pxl8_mat4_ortho(f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far) {
|
||||
pxl8_mat4 pxl8_mat4_orthographic(f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far) {
|
||||
pxl8_mat4 mat = {0};
|
||||
|
||||
mat.m[0] = 2.0f / (right - left);
|
||||
|
|
@ -246,15 +255,15 @@ pxl8_frustum pxl8_frustum_from_matrix(pxl8_mat4 vp) {
|
|||
pxl8_frustum frustum;
|
||||
const f32* m = vp.m;
|
||||
|
||||
frustum.planes[0].normal.x = m[3] - m[0];
|
||||
frustum.planes[0].normal.y = m[7] - m[4];
|
||||
frustum.planes[0].normal.z = m[11] - m[8];
|
||||
frustum.planes[0].distance = m[15] - m[12];
|
||||
frustum.planes[0].normal.x = m[3] + m[0];
|
||||
frustum.planes[0].normal.y = m[7] + m[4];
|
||||
frustum.planes[0].normal.z = m[11] + m[8];
|
||||
frustum.planes[0].distance = m[15] + m[12];
|
||||
|
||||
frustum.planes[1].normal.x = m[3] + m[0];
|
||||
frustum.planes[1].normal.y = m[7] + m[4];
|
||||
frustum.planes[1].normal.z = m[11] + m[8];
|
||||
frustum.planes[1].distance = m[15] + m[12];
|
||||
frustum.planes[1].normal.x = m[3] - m[0];
|
||||
frustum.planes[1].normal.y = m[7] - m[4];
|
||||
frustum.planes[1].normal.z = m[11] - m[8];
|
||||
frustum.planes[1].distance = m[15] - m[12];
|
||||
|
||||
frustum.planes[2].normal.x = m[3] + m[1];
|
||||
frustum.planes[2].normal.y = m[7] + m[5];
|
||||
|
|
@ -266,15 +275,15 @@ pxl8_frustum pxl8_frustum_from_matrix(pxl8_mat4 vp) {
|
|||
frustum.planes[3].normal.z = m[11] - m[9];
|
||||
frustum.planes[3].distance = m[15] - m[13];
|
||||
|
||||
frustum.planes[4].normal.x = m[3] - m[2];
|
||||
frustum.planes[4].normal.y = m[7] - m[6];
|
||||
frustum.planes[4].normal.z = m[11] - m[10];
|
||||
frustum.planes[4].distance = m[15] - m[14];
|
||||
frustum.planes[4].normal.x = m[3] + m[2];
|
||||
frustum.planes[4].normal.y = m[7] + m[6];
|
||||
frustum.planes[4].normal.z = m[11] + m[10];
|
||||
frustum.planes[4].distance = m[15] + m[14];
|
||||
|
||||
frustum.planes[5].normal.x = m[3] + m[2];
|
||||
frustum.planes[5].normal.y = m[7] + m[6];
|
||||
frustum.planes[5].normal.z = m[11] + m[10];
|
||||
frustum.planes[5].distance = m[15] + m[14];
|
||||
frustum.planes[5].normal.x = m[3] - m[2];
|
||||
frustum.planes[5].normal.y = m[7] - m[6];
|
||||
frustum.planes[5].normal.z = m[11] - m[10];
|
||||
frustum.planes[5].distance = m[15] - m[14];
|
||||
|
||||
for (i32 i = 0; i < 6; i++) {
|
||||
f32 len = pxl8_vec3_length(frustum.planes[i].normal);
|
||||
|
|
@ -289,19 +298,21 @@ pxl8_frustum pxl8_frustum_from_matrix(pxl8_mat4 vp) {
|
|||
}
|
||||
|
||||
bool pxl8_frustum_test_aabb(const pxl8_frustum* frustum, pxl8_vec3 min, pxl8_vec3 max) {
|
||||
const f32 FRUSTUM_EPSILON = -75.0f;
|
||||
|
||||
for (i32 i = 0; i < 6; i++) {
|
||||
pxl8_vec3 normal = frustum->planes[i].normal;
|
||||
f32 d = frustum->planes[i].distance;
|
||||
|
||||
pxl8_vec3 p_vertex = {
|
||||
(normal.x >= 0.0f) ? max.x : min.x,
|
||||
(normal.y >= 0.0f) ? max.y : min.y,
|
||||
(normal.z >= 0.0f) ? max.z : min.z
|
||||
(normal.x > 0.0f) ? max.x : min.x,
|
||||
(normal.y > 0.0f) ? max.y : min.y,
|
||||
(normal.z > 0.0f) ? max.z : min.z
|
||||
};
|
||||
|
||||
f32 p_dist = pxl8_vec3_dot(normal, p_vertex) + d;
|
||||
|
||||
if (p_dist < 0.0f) {
|
||||
if (p_dist < FRUSTUM_EPSILON) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,16 +4,49 @@
|
|||
|
||||
#include "pxl8_types.h"
|
||||
|
||||
#if defined(__x86_64__) || defined(_M_X64)
|
||||
#include <xmmintrin.h>
|
||||
#elif defined(__aarch64__) || defined(_M_ARM64)
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
#define PXL8_PI 3.14159265358979323846f
|
||||
#define PXL8_TAU (PXL8_PI * 2.0f)
|
||||
|
||||
static inline f32 pxl8_fast_inv_sqrt(f32 x) {
|
||||
#if defined(__x86_64__) || defined(_M_X64)
|
||||
__m128 v = _mm_set_ss(x);
|
||||
v = _mm_rsqrt_ss(v);
|
||||
return _mm_cvtss_f32(v);
|
||||
#elif defined(__aarch64__) || defined(_M_ARM64)
|
||||
float32x2_t v = vdup_n_f32(x);
|
||||
float32x2_t est = vrsqrte_f32(v);
|
||||
est = vmul_f32(est, vrsqrts_f32(vmul_f32(v, est), est));
|
||||
return vget_lane_f32(est, 0);
|
||||
#else
|
||||
f32 half = 0.5f * x;
|
||||
i32 i = *(i32*)&x;
|
||||
i = 0x5f3759df - (i >> 1);
|
||||
x = *(f32*)&i;
|
||||
x = x * (1.5f - half * x * x);
|
||||
return x;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline f32 pxl8_fast_rcp(f32 x) {
|
||||
#if defined(__x86_64__) || defined(_M_X64)
|
||||
__m128 v = _mm_set_ss(x);
|
||||
__m128 rcp = _mm_rcp_ss(v);
|
||||
rcp = _mm_add_ss(rcp, _mm_mul_ss(rcp, _mm_sub_ss(_mm_set_ss(1.0f), _mm_mul_ss(v, rcp))));
|
||||
return _mm_cvtss_f32(rcp);
|
||||
#elif defined(__aarch64__) || defined(_M_ARM64)
|
||||
float32x2_t v = vdup_n_f32(x);
|
||||
float32x2_t est = vrecpe_f32(v);
|
||||
est = vmul_f32(est, vrecps_f32(v, est));
|
||||
return vget_lane_f32(est, 0);
|
||||
#else
|
||||
return 1.0f / x;
|
||||
#endif
|
||||
}
|
||||
|
||||
typedef struct pxl8_vec2 {
|
||||
|
|
@ -33,18 +66,27 @@ typedef struct pxl8_mat4 {
|
|||
} pxl8_mat4;
|
||||
|
||||
typedef struct pxl8_plane {
|
||||
pxl8_vec3 normal;
|
||||
f32 distance;
|
||||
pxl8_vec3 normal;
|
||||
} pxl8_plane;
|
||||
|
||||
typedef struct pxl8_frustum {
|
||||
pxl8_plane planes[6];
|
||||
} pxl8_frustum;
|
||||
|
||||
typedef struct pxl8_projected_point {
|
||||
f32 depth;
|
||||
i32 x;
|
||||
i32 y;
|
||||
bool visible;
|
||||
} pxl8_projected_point;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
u32 pxl8_hash32(u32 x);
|
||||
|
||||
pxl8_vec2 pxl8_vec2_add(pxl8_vec2 a, pxl8_vec2 b);
|
||||
f32 pxl8_vec2_dot(pxl8_vec2 a, pxl8_vec2 b);
|
||||
f32 pxl8_vec2_length(pxl8_vec2 v);
|
||||
|
|
@ -63,10 +105,10 @@ pxl8_vec3 pxl8_vec3_sub(pxl8_vec3 a, pxl8_vec3 b);
|
|||
|
||||
pxl8_mat4 pxl8_mat4_identity(void);
|
||||
pxl8_mat4 pxl8_mat4_lookat(pxl8_vec3 eye, pxl8_vec3 center, pxl8_vec3 up);
|
||||
pxl8_mat4 pxl8_mat4_mul(pxl8_mat4 a, pxl8_mat4 b);
|
||||
pxl8_vec3 pxl8_mat4_mul_vec3(pxl8_mat4 m, pxl8_vec3 v);
|
||||
pxl8_vec4 pxl8_mat4_mul_vec4(pxl8_mat4 m, pxl8_vec4 v);
|
||||
pxl8_mat4 pxl8_mat4_ortho(f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far);
|
||||
pxl8_mat4 pxl8_mat4_multiply(pxl8_mat4 a, pxl8_mat4 b);
|
||||
pxl8_vec3 pxl8_mat4_multiply_vec3(pxl8_mat4 m, pxl8_vec3 v);
|
||||
pxl8_vec4 pxl8_mat4_multiply_vec4(pxl8_mat4 m, pxl8_vec4 v);
|
||||
pxl8_mat4 pxl8_mat4_orthographic(f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far);
|
||||
pxl8_mat4 pxl8_mat4_perspective(f32 fov, f32 aspect, f32 near, f32 far);
|
||||
pxl8_mat4 pxl8_mat4_rotate_x(f32 angle);
|
||||
pxl8_mat4 pxl8_mat4_rotate_y(f32 angle);
|
||||
|
|
|
|||
|
|
@ -1,291 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_types.h"
|
||||
|
||||
#if defined(__x86_64__) || defined(_M_X64)
|
||||
#define PXL8_SIMD_SSE2 1
|
||||
#include <emmintrin.h>
|
||||
#elif defined(__aarch64__) || defined(_M_ARM64)
|
||||
#define PXL8_SIMD_NEON 1
|
||||
#include <arm_neon.h>
|
||||
#else
|
||||
#define PXL8_SIMD_SCALAR 1
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(PXL8_SIMD_SSE2)
|
||||
|
||||
typedef struct { __m128 v; } pxl8_f32x4;
|
||||
typedef struct { __m128i v; } pxl8_i32x4;
|
||||
typedef struct { __m128i v; } pxl8_u16x8;
|
||||
|
||||
static inline pxl8_f32x4 pxl8_f32x4_splat(f32 x) {
|
||||
return (pxl8_f32x4){ _mm_set1_ps(x) };
|
||||
}
|
||||
|
||||
static inline pxl8_f32x4 pxl8_f32x4_new(f32 a, f32 b, f32 c, f32 d) {
|
||||
return (pxl8_f32x4){ _mm_set_ps(d, c, b, a) };
|
||||
}
|
||||
|
||||
static inline pxl8_f32x4 pxl8_f32x4_add(pxl8_f32x4 a, pxl8_f32x4 b) {
|
||||
return (pxl8_f32x4){ _mm_add_ps(a.v, b.v) };
|
||||
}
|
||||
|
||||
static inline pxl8_f32x4 pxl8_f32x4_sub(pxl8_f32x4 a, pxl8_f32x4 b) {
|
||||
return (pxl8_f32x4){ _mm_sub_ps(a.v, b.v) };
|
||||
}
|
||||
|
||||
static inline pxl8_f32x4 pxl8_f32x4_mul(pxl8_f32x4 a, pxl8_f32x4 b) {
|
||||
return (pxl8_f32x4){ _mm_mul_ps(a.v, b.v) };
|
||||
}
|
||||
|
||||
static inline pxl8_f32x4 pxl8_f32x4_div(pxl8_f32x4 a, pxl8_f32x4 b) {
|
||||
return (pxl8_f32x4){ _mm_div_ps(a.v, b.v) };
|
||||
}
|
||||
|
||||
static inline pxl8_f32x4 pxl8_f32x4_min(pxl8_f32x4 a, pxl8_f32x4 b) {
|
||||
return (pxl8_f32x4){ _mm_min_ps(a.v, b.v) };
|
||||
}
|
||||
|
||||
static inline pxl8_f32x4 pxl8_f32x4_max(pxl8_f32x4 a, pxl8_f32x4 b) {
|
||||
return (pxl8_f32x4){ _mm_max_ps(a.v, b.v) };
|
||||
}
|
||||
|
||||
static inline pxl8_f32x4 pxl8_f32x4_cmplt(pxl8_f32x4 a, pxl8_f32x4 b) {
|
||||
return (pxl8_f32x4){ _mm_cmplt_ps(a.v, b.v) };
|
||||
}
|
||||
|
||||
static inline i32 pxl8_f32x4_movemask(pxl8_f32x4 a) {
|
||||
return _mm_movemask_ps(a.v);
|
||||
}
|
||||
|
||||
static inline pxl8_i32x4 pxl8_f32x4_to_i32x4(pxl8_f32x4 a) {
|
||||
return (pxl8_i32x4){ _mm_cvttps_epi32(a.v) };
|
||||
}
|
||||
|
||||
static inline void pxl8_f32x4_store(pxl8_f32x4 a, f32* out) {
|
||||
_mm_storeu_ps(out, a.v);
|
||||
}
|
||||
|
||||
static inline pxl8_i32x4 pxl8_i32x4_splat(i32 x) {
|
||||
return (pxl8_i32x4){ _mm_set1_epi32(x) };
|
||||
}
|
||||
|
||||
static inline pxl8_i32x4 pxl8_i32x4_slli(pxl8_i32x4 a, i32 n) {
|
||||
return (pxl8_i32x4){ _mm_slli_epi32(a.v, n) };
|
||||
}
|
||||
|
||||
static inline pxl8_i32x4 pxl8_i32x4_srai(pxl8_i32x4 a, i32 n) {
|
||||
return (pxl8_i32x4){ _mm_srai_epi32(a.v, n) };
|
||||
}
|
||||
|
||||
static inline pxl8_i32x4 pxl8_i32x4_and(pxl8_i32x4 a, pxl8_i32x4 b) {
|
||||
return (pxl8_i32x4){ _mm_and_si128(a.v, b.v) };
|
||||
}
|
||||
|
||||
static inline pxl8_i32x4 pxl8_i32x4_or(pxl8_i32x4 a, pxl8_i32x4 b) {
|
||||
return (pxl8_i32x4){ _mm_or_si128(a.v, b.v) };
|
||||
}
|
||||
|
||||
static inline void pxl8_i32x4_store(pxl8_i32x4 a, i32* out) {
|
||||
_mm_storeu_si128((__m128i*)out, a.v);
|
||||
}
|
||||
|
||||
static inline pxl8_u16x8 pxl8_u16x8_cmplt(pxl8_u16x8 a, pxl8_u16x8 b) {
|
||||
return (pxl8_u16x8){ _mm_cmplt_epi16(a.v, b.v) };
|
||||
}
|
||||
|
||||
static inline pxl8_u16x8 pxl8_u16x8_blend(pxl8_u16x8 a, pxl8_u16x8 b, pxl8_u16x8 mask) {
|
||||
__m128i not_mask = _mm_andnot_si128(mask.v, a.v);
|
||||
__m128i and_mask = _mm_and_si128(mask.v, b.v);
|
||||
return (pxl8_u16x8){ _mm_or_si128(not_mask, and_mask) };
|
||||
}
|
||||
|
||||
static inline i32 pxl8_u16x8_movemask(pxl8_u16x8 a) {
|
||||
return _mm_movemask_epi8(a.v);
|
||||
}
|
||||
|
||||
#elif defined(PXL8_SIMD_NEON)
|
||||
|
||||
typedef struct { float32x4_t v; } pxl8_f32x4;
|
||||
typedef struct { int32x4_t v; } pxl8_i32x4;
|
||||
typedef struct { uint16x8_t v; } pxl8_u16x8;
|
||||
|
||||
static inline pxl8_f32x4 pxl8_f32x4_splat(f32 x) {
|
||||
return (pxl8_f32x4){ vdupq_n_f32(x) };
|
||||
}
|
||||
|
||||
static inline pxl8_f32x4 pxl8_f32x4_new(f32 a, f32 b, f32 c, f32 d) {
|
||||
f32 arr[4] = {a, b, c, d};
|
||||
return (pxl8_f32x4){ vld1q_f32(arr) };
|
||||
}
|
||||
|
||||
static inline pxl8_f32x4 pxl8_f32x4_add(pxl8_f32x4 a, pxl8_f32x4 b) {
|
||||
return (pxl8_f32x4){ vaddq_f32(a.v, b.v) };
|
||||
}
|
||||
|
||||
static inline pxl8_f32x4 pxl8_f32x4_sub(pxl8_f32x4 a, pxl8_f32x4 b) {
|
||||
return (pxl8_f32x4){ vsubq_f32(a.v, b.v) };
|
||||
}
|
||||
|
||||
static inline pxl8_f32x4 pxl8_f32x4_mul(pxl8_f32x4 a, pxl8_f32x4 b) {
|
||||
return (pxl8_f32x4){ vmulq_f32(a.v, b.v) };
|
||||
}
|
||||
|
||||
static inline pxl8_f32x4 pxl8_f32x4_div(pxl8_f32x4 a, pxl8_f32x4 b) {
|
||||
return (pxl8_f32x4){ vdivq_f32(a.v, b.v) };
|
||||
}
|
||||
|
||||
static inline pxl8_f32x4 pxl8_f32x4_min(pxl8_f32x4 a, pxl8_f32x4 b) {
|
||||
return (pxl8_f32x4){ vminq_f32(a.v, b.v) };
|
||||
}
|
||||
|
||||
static inline pxl8_f32x4 pxl8_f32x4_max(pxl8_f32x4 a, pxl8_f32x4 b) {
|
||||
return (pxl8_f32x4){ vmaxq_f32(a.v, b.v) };
|
||||
}
|
||||
|
||||
static inline pxl8_f32x4 pxl8_f32x4_cmplt(pxl8_f32x4 a, pxl8_f32x4 b) {
|
||||
uint32x4_t cmp = vcltq_f32(a.v, b.v);
|
||||
return (pxl8_f32x4){ vreinterpretq_f32_u32(cmp) };
|
||||
}
|
||||
|
||||
static inline i32 pxl8_f32x4_movemask(pxl8_f32x4 a) {
|
||||
uint32x4_t input = vreinterpretq_u32_f32(a.v);
|
||||
static const i32 shifts[4] = {0, 1, 2, 3};
|
||||
uint32x4_t shifted = vshrq_n_u32(input, 31);
|
||||
return vgetq_lane_u32(shifted, 0) | (vgetq_lane_u32(shifted, 1) << 1) |
|
||||
(vgetq_lane_u32(shifted, 2) << 2) | (vgetq_lane_u32(shifted, 3) << 3);
|
||||
}
|
||||
|
||||
static inline pxl8_i32x4 pxl8_f32x4_to_i32x4(pxl8_f32x4 a) {
|
||||
return (pxl8_i32x4){ vcvtq_s32_f32(a.v) };
|
||||
}
|
||||
|
||||
static inline void pxl8_f32x4_store(pxl8_f32x4 a, f32* out) {
|
||||
vst1q_f32(out, a.v);
|
||||
}
|
||||
|
||||
static inline pxl8_i32x4 pxl8_i32x4_splat(i32 x) {
|
||||
return (pxl8_i32x4){ vdupq_n_s32(x) };
|
||||
}
|
||||
|
||||
static inline pxl8_i32x4 pxl8_i32x4_slli(pxl8_i32x4 a, i32 n) {
|
||||
return (pxl8_i32x4){ vshlq_s32(a.v, vdupq_n_s32(n)) };
|
||||
}
|
||||
|
||||
static inline pxl8_i32x4 pxl8_i32x4_srai(pxl8_i32x4 a, i32 n) {
|
||||
return (pxl8_i32x4){ vshlq_s32(a.v, vdupq_n_s32(-n)) };
|
||||
}
|
||||
|
||||
static inline pxl8_i32x4 pxl8_i32x4_and(pxl8_i32x4 a, pxl8_i32x4 b) {
|
||||
return (pxl8_i32x4){ vandq_s32(a.v, b.v) };
|
||||
}
|
||||
|
||||
static inline pxl8_i32x4 pxl8_i32x4_or(pxl8_i32x4 a, pxl8_i32x4 b) {
|
||||
return (pxl8_i32x4){ vorrq_s32(a.v, b.v) };
|
||||
}
|
||||
|
||||
static inline void pxl8_i32x4_store(pxl8_i32x4 a, i32* out) {
|
||||
vst1q_s32(out, a.v);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
typedef struct { f32 v[4]; } pxl8_f32x4;
|
||||
typedef struct { i32 v[4]; } pxl8_i32x4;
|
||||
typedef struct { u16 v[8]; } pxl8_u16x8;
|
||||
|
||||
static inline pxl8_f32x4 pxl8_f32x4_splat(f32 x) {
|
||||
return (pxl8_f32x4){{ x, x, x, x }};
|
||||
}
|
||||
|
||||
static inline pxl8_f32x4 pxl8_f32x4_new(f32 a, f32 b, f32 c, f32 d) {
|
||||
return (pxl8_f32x4){{ a, b, c, d }};
|
||||
}
|
||||
|
||||
static inline pxl8_f32x4 pxl8_f32x4_add(pxl8_f32x4 a, pxl8_f32x4 b) {
|
||||
return (pxl8_f32x4){{ a.v[0]+b.v[0], a.v[1]+b.v[1], a.v[2]+b.v[2], a.v[3]+b.v[3] }};
|
||||
}
|
||||
|
||||
static inline pxl8_f32x4 pxl8_f32x4_sub(pxl8_f32x4 a, pxl8_f32x4 b) {
|
||||
return (pxl8_f32x4){{ a.v[0]-b.v[0], a.v[1]-b.v[1], a.v[2]-b.v[2], a.v[3]-b.v[3] }};
|
||||
}
|
||||
|
||||
static inline pxl8_f32x4 pxl8_f32x4_mul(pxl8_f32x4 a, pxl8_f32x4 b) {
|
||||
return (pxl8_f32x4){{ a.v[0]*b.v[0], a.v[1]*b.v[1], a.v[2]*b.v[2], a.v[3]*b.v[3] }};
|
||||
}
|
||||
|
||||
static inline pxl8_f32x4 pxl8_f32x4_div(pxl8_f32x4 a, pxl8_f32x4 b) {
|
||||
return (pxl8_f32x4){{ a.v[0]/b.v[0], a.v[1]/b.v[1], a.v[2]/b.v[2], a.v[3]/b.v[3] }};
|
||||
}
|
||||
|
||||
static inline pxl8_f32x4 pxl8_f32x4_min(pxl8_f32x4 a, pxl8_f32x4 b) {
|
||||
return (pxl8_f32x4){{
|
||||
a.v[0]<b.v[0]?a.v[0]:b.v[0], a.v[1]<b.v[1]?a.v[1]:b.v[1],
|
||||
a.v[2]<b.v[2]?a.v[2]:b.v[2], a.v[3]<b.v[3]?a.v[3]:b.v[3]
|
||||
}};
|
||||
}
|
||||
|
||||
static inline pxl8_f32x4 pxl8_f32x4_max(pxl8_f32x4 a, pxl8_f32x4 b) {
|
||||
return (pxl8_f32x4){{
|
||||
a.v[0]>b.v[0]?a.v[0]:b.v[0], a.v[1]>b.v[1]?a.v[1]:b.v[1],
|
||||
a.v[2]>b.v[2]?a.v[2]:b.v[2], a.v[3]>b.v[3]?a.v[3]:b.v[3]
|
||||
}};
|
||||
}
|
||||
|
||||
static inline pxl8_f32x4 pxl8_f32x4_cmplt(pxl8_f32x4 a, pxl8_f32x4 b) {
|
||||
pxl8_f32x4 r;
|
||||
u32* rv = (u32*)r.v;
|
||||
rv[0] = a.v[0] < b.v[0] ? 0xFFFFFFFF : 0;
|
||||
rv[1] = a.v[1] < b.v[1] ? 0xFFFFFFFF : 0;
|
||||
rv[2] = a.v[2] < b.v[2] ? 0xFFFFFFFF : 0;
|
||||
rv[3] = a.v[3] < b.v[3] ? 0xFFFFFFFF : 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline i32 pxl8_f32x4_movemask(pxl8_f32x4 a) {
|
||||
u32* av = (u32*)a.v;
|
||||
return ((av[0] >> 31) & 1) | ((av[1] >> 31) & 1) << 1 |
|
||||
((av[2] >> 31) & 1) << 2 | ((av[3] >> 31) & 1) << 3;
|
||||
}
|
||||
|
||||
static inline pxl8_i32x4 pxl8_f32x4_to_i32x4(pxl8_f32x4 a) {
|
||||
return (pxl8_i32x4){{ (i32)a.v[0], (i32)a.v[1], (i32)a.v[2], (i32)a.v[3] }};
|
||||
}
|
||||
|
||||
static inline void pxl8_f32x4_store(pxl8_f32x4 a, f32* out) {
|
||||
out[0] = a.v[0]; out[1] = a.v[1]; out[2] = a.v[2]; out[3] = a.v[3];
|
||||
}
|
||||
|
||||
static inline pxl8_i32x4 pxl8_i32x4_splat(i32 x) {
|
||||
return (pxl8_i32x4){{ x, x, x, x }};
|
||||
}
|
||||
|
||||
static inline pxl8_i32x4 pxl8_i32x4_slli(pxl8_i32x4 a, i32 n) {
|
||||
return (pxl8_i32x4){{ a.v[0]<<n, a.v[1]<<n, a.v[2]<<n, a.v[3]<<n }};
|
||||
}
|
||||
|
||||
static inline pxl8_i32x4 pxl8_i32x4_srai(pxl8_i32x4 a, i32 n) {
|
||||
return (pxl8_i32x4){{ a.v[0]>>n, a.v[1]>>n, a.v[2]>>n, a.v[3]>>n }};
|
||||
}
|
||||
|
||||
static inline pxl8_i32x4 pxl8_i32x4_and(pxl8_i32x4 a, pxl8_i32x4 b) {
|
||||
return (pxl8_i32x4){{ a.v[0]&b.v[0], a.v[1]&b.v[1], a.v[2]&b.v[2], a.v[3]&b.v[3] }};
|
||||
}
|
||||
|
||||
static inline pxl8_i32x4 pxl8_i32x4_or(pxl8_i32x4 a, pxl8_i32x4 b) {
|
||||
return (pxl8_i32x4){{ a.v[0]|b.v[0], a.v[1]|b.v[1], a.v[2]|b.v[2], a.v[3]|b.v[3] }};
|
||||
}
|
||||
|
||||
static inline void pxl8_i32x4_store(pxl8_i32x4 a, i32* out) {
|
||||
out[0] = a.v[0]; out[1] = a.v[1]; out[2] = a.v[2]; out[3] = a.v[3];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
#include "pxl8_net.h"
|
||||
#include "pxl8_mem.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
|
@ -94,7 +95,7 @@ bool pxl8_net_connected(const pxl8_net* net) {
|
|||
}
|
||||
|
||||
pxl8_net* pxl8_net_create(const pxl8_net_config* config) {
|
||||
pxl8_net* net = calloc(1, sizeof(pxl8_net));
|
||||
pxl8_net* net = pxl8_calloc(1, sizeof(pxl8_net));
|
||||
if (!net) return NULL;
|
||||
|
||||
net->mode = config->mode;
|
||||
|
|
@ -116,7 +117,7 @@ pxl8_net* pxl8_net_create(const pxl8_net_config* config) {
|
|||
void pxl8_net_destroy(pxl8_net* net) {
|
||||
if (!net) return;
|
||||
pxl8_net_disconnect(net);
|
||||
free(net);
|
||||
pxl8_free(net);
|
||||
}
|
||||
|
||||
void pxl8_net_disconnect(pxl8_net* net) {
|
||||
|
|
@ -203,11 +204,11 @@ u64 pxl8_net_player_id(const pxl8_net* net) {
|
|||
bool pxl8_net_poll(pxl8_net* net) {
|
||||
if (!net || !net->connected) return false;
|
||||
|
||||
size_t len = pxl8_net_recv(net, net->recv_buf, sizeof(net->recv_buf));
|
||||
usize len = pxl8_net_recv(net, net->recv_buf, sizeof(net->recv_buf));
|
||||
if (len < sizeof(pxl8_msg_header)) return false;
|
||||
|
||||
pxl8_msg_header hdr;
|
||||
size_t offset = pxl8_protocol_deserialize_header(net->recv_buf, len, &hdr);
|
||||
usize offset = pxl8_protocol_deserialize_header(net->recv_buf, len, &hdr);
|
||||
if (hdr.type != PXL8_MSG_SNAPSHOT) return false;
|
||||
|
||||
pxl8_snapshot_header snap;
|
||||
|
|
@ -243,17 +244,17 @@ void pxl8_net_predicted_tick_set(pxl8_net* net, u64 tick) {
|
|||
net->predicted_tick = tick;
|
||||
}
|
||||
|
||||
size_t pxl8_net_recv(pxl8_net* net, u8* buf, size_t len) {
|
||||
usize pxl8_net_recv(pxl8_net* net, u8* buf, usize len) {
|
||||
if (!net || !net->connected) return 0;
|
||||
|
||||
struct sockaddr_in from;
|
||||
socklen_t from_len = sizeof(from);
|
||||
ssize_t received = recvfrom(net->sock, (char*)buf, len, 0,
|
||||
(struct sockaddr*)&from, &from_len);
|
||||
return (received > 0) ? (size_t)received : 0;
|
||||
return (received > 0) ? (usize)received : 0;
|
||||
}
|
||||
|
||||
pxl8_result pxl8_net_send(pxl8_net* net, const u8* data, size_t len) {
|
||||
pxl8_result pxl8_net_send(pxl8_net* net, const u8* data, usize len) {
|
||||
if (!net || !net->connected) return PXL8_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
ssize_t sent = sendto(net->sock, (const char*)data, len, 0,
|
||||
|
|
@ -273,7 +274,7 @@ pxl8_result pxl8_net_send_command(pxl8_net* net, const pxl8_command_msg* cmd) {
|
|||
.sequence = 0
|
||||
};
|
||||
|
||||
size_t offset = pxl8_protocol_serialize_header(&hdr, buf, sizeof(buf));
|
||||
usize offset = pxl8_protocol_serialize_header(&hdr, buf, sizeof(buf));
|
||||
offset += pxl8_protocol_serialize_command(cmd, buf + offset, sizeof(buf) - offset);
|
||||
|
||||
return pxl8_net_send(net, buf, offset);
|
||||
|
|
@ -290,7 +291,7 @@ pxl8_result pxl8_net_send_input(pxl8_net* net, const pxl8_input_msg* input) {
|
|||
.sequence = 0
|
||||
};
|
||||
|
||||
size_t offset = pxl8_protocol_serialize_header(&hdr, buf, sizeof(buf));
|
||||
usize offset = pxl8_protocol_serialize_header(&hdr, buf, sizeof(buf));
|
||||
offset += pxl8_protocol_serialize_input(input, buf + offset, sizeof(buf) - offset);
|
||||
|
||||
return pxl8_net_send(net, buf, offset);
|
||||
|
|
|
|||
|
|
@ -41,8 +41,8 @@ u64 pxl8_net_player_id(const pxl8_net* net);
|
|||
bool pxl8_net_poll(pxl8_net* net);
|
||||
u8* pxl8_net_predicted_state(pxl8_net* net);
|
||||
void pxl8_net_predicted_tick_set(pxl8_net* net, u64 tick);
|
||||
size_t pxl8_net_recv(pxl8_net* net, u8* buf, size_t len);
|
||||
pxl8_result pxl8_net_send(pxl8_net* net, const u8* data, size_t len);
|
||||
usize pxl8_net_recv(pxl8_net* net, u8* buf, usize len);
|
||||
pxl8_result pxl8_net_send(pxl8_net* net, const u8* data, usize len);
|
||||
pxl8_result pxl8_net_send_command(pxl8_net* net, const pxl8_command_msg* cmd);
|
||||
pxl8_result pxl8_net_send_input(pxl8_net* net, const pxl8_input_msg* input);
|
||||
const pxl8_snapshot_header* pxl8_net_snapshot(const pxl8_net* net);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#include "pxl8_protocol.h"
|
||||
#include "pxl8_bytes.h"
|
||||
|
||||
size_t pxl8_protocol_serialize_header(const pxl8_msg_header* msg, u8* buf, size_t len) {
|
||||
usize pxl8_protocol_serialize_header(const pxl8_msg_header* msg, u8* buf, usize len) {
|
||||
if (len < sizeof(pxl8_msg_header)) return 0;
|
||||
pxl8_write_stream s = pxl8_write_stream_create(buf, (u32)len);
|
||||
pxl8_write_u32_be(&s, msg->sequence);
|
||||
|
|
@ -11,7 +11,7 @@ size_t pxl8_protocol_serialize_header(const pxl8_msg_header* msg, u8* buf, size_
|
|||
return s.offset;
|
||||
}
|
||||
|
||||
size_t pxl8_protocol_deserialize_header(const u8* buf, size_t len, pxl8_msg_header* msg) {
|
||||
usize pxl8_protocol_deserialize_header(const u8* buf, usize len, pxl8_msg_header* msg) {
|
||||
if (len < sizeof(pxl8_msg_header)) return 0;
|
||||
pxl8_stream s = pxl8_stream_create(buf, (u32)len);
|
||||
msg->sequence = pxl8_read_u32_be(&s);
|
||||
|
|
@ -21,7 +21,7 @@ size_t pxl8_protocol_deserialize_header(const u8* buf, size_t len, pxl8_msg_head
|
|||
return s.offset;
|
||||
}
|
||||
|
||||
size_t pxl8_protocol_serialize_input(const pxl8_input_msg* msg, u8* buf, size_t len) {
|
||||
usize pxl8_protocol_serialize_input(const pxl8_input_msg* msg, u8* buf, usize len) {
|
||||
if (len < sizeof(pxl8_input_msg)) return 0;
|
||||
pxl8_write_stream s = pxl8_write_stream_create(buf, (u32)len);
|
||||
pxl8_write_u32_be(&s, msg->buttons);
|
||||
|
|
@ -35,7 +35,7 @@ size_t pxl8_protocol_serialize_input(const pxl8_input_msg* msg, u8* buf, size_t
|
|||
return s.offset;
|
||||
}
|
||||
|
||||
size_t pxl8_protocol_deserialize_input(const u8* buf, size_t len, pxl8_input_msg* msg) {
|
||||
usize pxl8_protocol_deserialize_input(const u8* buf, usize len, pxl8_input_msg* msg) {
|
||||
if (len < sizeof(pxl8_input_msg)) return 0;
|
||||
pxl8_stream s = pxl8_stream_create(buf, (u32)len);
|
||||
msg->buttons = pxl8_read_u32_be(&s);
|
||||
|
|
@ -49,7 +49,7 @@ size_t pxl8_protocol_deserialize_input(const u8* buf, size_t len, pxl8_input_msg
|
|||
return s.offset;
|
||||
}
|
||||
|
||||
size_t pxl8_protocol_serialize_command(const pxl8_command_msg* msg, u8* buf, size_t len) {
|
||||
usize pxl8_protocol_serialize_command(const pxl8_command_msg* msg, u8* buf, usize len) {
|
||||
if (len < sizeof(pxl8_command_msg)) return 0;
|
||||
pxl8_write_stream s = pxl8_write_stream_create(buf, (u32)len);
|
||||
pxl8_write_u16_be(&s, msg->cmd_type);
|
||||
|
|
@ -59,7 +59,7 @@ size_t pxl8_protocol_serialize_command(const pxl8_command_msg* msg, u8* buf, siz
|
|||
return s.offset;
|
||||
}
|
||||
|
||||
size_t pxl8_protocol_deserialize_command(const u8* buf, size_t len, pxl8_command_msg* msg) {
|
||||
usize pxl8_protocol_deserialize_command(const u8* buf, usize len, pxl8_command_msg* msg) {
|
||||
if (len < sizeof(pxl8_command_msg)) return 0;
|
||||
pxl8_stream s = pxl8_stream_create(buf, (u32)len);
|
||||
msg->cmd_type = pxl8_read_u16_be(&s);
|
||||
|
|
@ -69,7 +69,7 @@ size_t pxl8_protocol_deserialize_command(const u8* buf, size_t len, pxl8_command
|
|||
return s.offset;
|
||||
}
|
||||
|
||||
size_t pxl8_protocol_serialize_entity_state(const pxl8_entity_state* state, u8* buf, size_t len) {
|
||||
usize pxl8_protocol_serialize_entity_state(const pxl8_entity_state* state, u8* buf, usize len) {
|
||||
if (len < sizeof(pxl8_entity_state)) return 0;
|
||||
pxl8_write_stream s = pxl8_write_stream_create(buf, (u32)len);
|
||||
pxl8_write_u64_be(&s, state->entity_id);
|
||||
|
|
@ -77,7 +77,7 @@ size_t pxl8_protocol_serialize_entity_state(const pxl8_entity_state* state, u8*
|
|||
return s.offset;
|
||||
}
|
||||
|
||||
size_t pxl8_protocol_deserialize_entity_state(const u8* buf, size_t len, pxl8_entity_state* state) {
|
||||
usize pxl8_protocol_deserialize_entity_state(const u8* buf, usize len, pxl8_entity_state* state) {
|
||||
if (len < sizeof(pxl8_entity_state)) return 0;
|
||||
pxl8_stream s = pxl8_stream_create(buf, (u32)len);
|
||||
state->entity_id = pxl8_read_u64_be(&s);
|
||||
|
|
@ -85,7 +85,7 @@ size_t pxl8_protocol_deserialize_entity_state(const u8* buf, size_t len, pxl8_en
|
|||
return s.offset;
|
||||
}
|
||||
|
||||
size_t pxl8_protocol_serialize_event(const pxl8_event_msg* msg, u8* buf, size_t len) {
|
||||
usize pxl8_protocol_serialize_event(const pxl8_event_msg* msg, u8* buf, usize len) {
|
||||
if (len < sizeof(pxl8_event_msg)) return 0;
|
||||
pxl8_write_stream s = pxl8_write_stream_create(buf, (u32)len);
|
||||
pxl8_write_u8(&s, msg->event_type);
|
||||
|
|
@ -93,7 +93,7 @@ size_t pxl8_protocol_serialize_event(const pxl8_event_msg* msg, u8* buf, size_t
|
|||
return s.offset;
|
||||
}
|
||||
|
||||
size_t pxl8_protocol_deserialize_event(const u8* buf, size_t len, pxl8_event_msg* msg) {
|
||||
usize pxl8_protocol_deserialize_event(const u8* buf, usize len, pxl8_event_msg* msg) {
|
||||
if (len < sizeof(pxl8_event_msg)) return 0;
|
||||
pxl8_stream s = pxl8_stream_create(buf, (u32)len);
|
||||
msg->event_type = pxl8_read_u8(&s);
|
||||
|
|
@ -101,7 +101,7 @@ size_t pxl8_protocol_deserialize_event(const u8* buf, size_t len, pxl8_event_msg
|
|||
return s.offset;
|
||||
}
|
||||
|
||||
size_t pxl8_protocol_serialize_snapshot_header(const pxl8_snapshot_header* hdr, u8* buf, size_t len) {
|
||||
usize pxl8_protocol_serialize_snapshot_header(const pxl8_snapshot_header* hdr, u8* buf, usize len) {
|
||||
if (len < sizeof(pxl8_snapshot_header)) return 0;
|
||||
pxl8_write_stream s = pxl8_write_stream_create(buf, (u32)len);
|
||||
pxl8_write_u16_be(&s, hdr->entity_count);
|
||||
|
|
@ -112,7 +112,7 @@ size_t pxl8_protocol_serialize_snapshot_header(const pxl8_snapshot_header* hdr,
|
|||
return s.offset;
|
||||
}
|
||||
|
||||
size_t pxl8_protocol_deserialize_snapshot_header(const u8* buf, size_t len, pxl8_snapshot_header* hdr) {
|
||||
usize pxl8_protocol_deserialize_snapshot_header(const u8* buf, usize len, pxl8_snapshot_header* hdr) {
|
||||
if (len < sizeof(pxl8_snapshot_header)) return 0;
|
||||
pxl8_stream s = pxl8_stream_create(buf, (u32)len);
|
||||
hdr->entity_count = pxl8_read_u16_be(&s);
|
||||
|
|
|
|||
|
|
@ -70,23 +70,23 @@ typedef struct pxl8_snapshot_header {
|
|||
f32 time;
|
||||
} pxl8_snapshot_header;
|
||||
|
||||
size_t pxl8_protocol_serialize_header(const pxl8_msg_header* msg, u8* buf, size_t len);
|
||||
size_t pxl8_protocol_deserialize_header(const u8* buf, size_t len, pxl8_msg_header* msg);
|
||||
usize pxl8_protocol_serialize_header(const pxl8_msg_header* msg, u8* buf, usize len);
|
||||
usize pxl8_protocol_deserialize_header(const u8* buf, usize len, pxl8_msg_header* msg);
|
||||
|
||||
size_t pxl8_protocol_serialize_input(const pxl8_input_msg* msg, u8* buf, size_t len);
|
||||
size_t pxl8_protocol_deserialize_input(const u8* buf, size_t len, pxl8_input_msg* msg);
|
||||
usize pxl8_protocol_serialize_input(const pxl8_input_msg* msg, u8* buf, usize len);
|
||||
usize pxl8_protocol_deserialize_input(const u8* buf, usize len, pxl8_input_msg* msg);
|
||||
|
||||
size_t pxl8_protocol_serialize_command(const pxl8_command_msg* msg, u8* buf, size_t len);
|
||||
size_t pxl8_protocol_deserialize_command(const u8* buf, size_t len, pxl8_command_msg* msg);
|
||||
usize pxl8_protocol_serialize_command(const pxl8_command_msg* msg, u8* buf, usize len);
|
||||
usize pxl8_protocol_deserialize_command(const u8* buf, usize len, pxl8_command_msg* msg);
|
||||
|
||||
size_t pxl8_protocol_serialize_entity_state(const pxl8_entity_state* state, u8* buf, size_t len);
|
||||
size_t pxl8_protocol_deserialize_entity_state(const u8* buf, size_t len, pxl8_entity_state* state);
|
||||
usize pxl8_protocol_serialize_entity_state(const pxl8_entity_state* state, u8* buf, usize len);
|
||||
usize pxl8_protocol_deserialize_entity_state(const u8* buf, usize len, pxl8_entity_state* state);
|
||||
|
||||
size_t pxl8_protocol_serialize_event(const pxl8_event_msg* msg, u8* buf, size_t len);
|
||||
size_t pxl8_protocol_deserialize_event(const u8* buf, size_t len, pxl8_event_msg* msg);
|
||||
usize pxl8_protocol_serialize_event(const pxl8_event_msg* msg, u8* buf, usize len);
|
||||
usize pxl8_protocol_deserialize_event(const u8* buf, usize len, pxl8_event_msg* msg);
|
||||
|
||||
size_t pxl8_protocol_serialize_snapshot_header(const pxl8_snapshot_header* hdr, u8* buf, size_t len);
|
||||
size_t pxl8_protocol_deserialize_snapshot_header(const u8* buf, size_t len, pxl8_snapshot_header* hdr);
|
||||
usize pxl8_protocol_serialize_snapshot_header(const pxl8_snapshot_header* hdr, u8* buf, usize len);
|
||||
usize pxl8_protocol_deserialize_snapshot_header(const u8* buf, usize len, pxl8_snapshot_header* hdr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
332
src/procgen/pxl8_graph.c
Normal file
332
src/procgen/pxl8_graph.c
Normal file
|
|
@ -0,0 +1,332 @@
|
|||
#include "pxl8_graph.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pxl8_math.h"
|
||||
#include "pxl8_mem.h"
|
||||
|
||||
static inline u32 hash2d(i32 x, i32 y, u32 seed) {
|
||||
return pxl8_hash32((u32)x ^ ((u32)y * 2654435769u) ^ seed);
|
||||
}
|
||||
|
||||
static inline f32 hash2d_f(i32 x, i32 y, u32 seed) {
|
||||
return (f32)hash2d(x, y, seed) / (f32)0xFFFFFFFF;
|
||||
}
|
||||
|
||||
static f32 gradient2d(i32 ix, i32 iy, f32 fx, f32 fy, u32 seed) {
|
||||
u32 h = hash2d(ix, iy, seed);
|
||||
f32 angle = (f32)h / (f32)0xFFFFFFFF * 6.28318530718f;
|
||||
f32 gx = cosf(angle);
|
||||
f32 gy = sinf(angle);
|
||||
return gx * fx + gy * fy;
|
||||
}
|
||||
|
||||
static inline f32 smoothstep(f32 t) {
|
||||
return t * t * (3.0f - 2.0f * t);
|
||||
}
|
||||
|
||||
static inline f32 lerp(f32 a, f32 b, f32 t) {
|
||||
return a + t * (b - a);
|
||||
}
|
||||
|
||||
static f32 noise_value(f32 x, f32 y, f32 scale, u32 seed) {
|
||||
f32 sx = x * scale;
|
||||
f32 sy = y * scale;
|
||||
i32 ix = (i32)floorf(sx);
|
||||
i32 iy = (i32)floorf(sy);
|
||||
f32 fx = sx - (f32)ix;
|
||||
f32 fy = sy - (f32)iy;
|
||||
f32 u = smoothstep(fx);
|
||||
f32 v = smoothstep(fy);
|
||||
|
||||
f32 n00 = hash2d_f(ix, iy, seed);
|
||||
f32 n10 = hash2d_f(ix + 1, iy, seed);
|
||||
f32 n01 = hash2d_f(ix, iy + 1, seed);
|
||||
f32 n11 = hash2d_f(ix + 1, iy + 1, seed);
|
||||
|
||||
return lerp(lerp(n00, n10, u), lerp(n01, n11, u), v);
|
||||
}
|
||||
|
||||
static f32 noise_perlin(f32 x, f32 y, f32 scale, u32 seed) {
|
||||
f32 sx = x * scale;
|
||||
f32 sy = y * scale;
|
||||
i32 ix = (i32)floorf(sx);
|
||||
i32 iy = (i32)floorf(sy);
|
||||
f32 fx = sx - (f32)ix;
|
||||
f32 fy = sy - (f32)iy;
|
||||
f32 u = smoothstep(fx);
|
||||
f32 v = smoothstep(fy);
|
||||
|
||||
f32 n00 = gradient2d(ix, iy, fx, fy, seed);
|
||||
f32 n10 = gradient2d(ix + 1, iy, fx - 1.0f, fy, seed);
|
||||
f32 n01 = gradient2d(ix, iy + 1, fx, fy - 1.0f, seed);
|
||||
f32 n11 = gradient2d(ix + 1, iy + 1, fx - 1.0f, fy - 1.0f, seed);
|
||||
|
||||
f32 result = lerp(lerp(n00, n10, u), lerp(n01, n11, u), v);
|
||||
return result * 0.5f + 0.5f;
|
||||
}
|
||||
|
||||
static f32 noise_fbm(f32 x, f32 y, i32 octaves, f32 scale, f32 persistence, u32 seed) {
|
||||
f32 value = 0.0f;
|
||||
f32 amplitude = 1.0f;
|
||||
f32 frequency = scale;
|
||||
f32 max_value = 0.0f;
|
||||
|
||||
for (i32 i = 0; i < octaves; i++) {
|
||||
value += amplitude * noise_perlin(x, y, frequency, seed + (u32)i * 1337);
|
||||
max_value += amplitude;
|
||||
amplitude *= persistence;
|
||||
frequency *= 2.0f;
|
||||
}
|
||||
|
||||
return value / max_value;
|
||||
}
|
||||
|
||||
static f32 noise_ridged(f32 x, f32 y, i32 octaves, f32 scale, f32 persistence, u32 seed) {
|
||||
f32 value = 0.0f;
|
||||
f32 amplitude = 1.0f;
|
||||
f32 frequency = scale;
|
||||
f32 max_value = 0.0f;
|
||||
|
||||
for (i32 i = 0; i < octaves; i++) {
|
||||
f32 n = noise_perlin(x, y, frequency, seed + (u32)i * 1337);
|
||||
n = 1.0f - fabsf(n * 2.0f - 1.0f);
|
||||
value += amplitude * n;
|
||||
max_value += amplitude;
|
||||
amplitude *= persistence;
|
||||
frequency *= 2.0f;
|
||||
}
|
||||
|
||||
return value / max_value;
|
||||
}
|
||||
|
||||
static f32 noise_turbulence(f32 x, f32 y, i32 octaves, f32 scale, f32 persistence, u32 seed) {
|
||||
f32 value = 0.0f;
|
||||
f32 amplitude = 1.0f;
|
||||
f32 frequency = scale;
|
||||
f32 max_value = 0.0f;
|
||||
|
||||
for (i32 i = 0; i < octaves; i++) {
|
||||
f32 n = noise_perlin(x, y, frequency, seed + (u32)i * 1337);
|
||||
value += amplitude * fabsf(n * 2.0f - 1.0f);
|
||||
max_value += amplitude;
|
||||
amplitude *= persistence;
|
||||
frequency *= 2.0f;
|
||||
}
|
||||
|
||||
return value / max_value;
|
||||
}
|
||||
|
||||
static void voronoi(f32 x, f32 y, f32 scale, u32 seed, f32* cell_dist, f32* edge_dist, i32* cell_id) {
|
||||
f32 sx = x * scale;
|
||||
f32 sy = y * scale;
|
||||
i32 cx = (i32)floorf(sx);
|
||||
i32 cy = (i32)floorf(sy);
|
||||
f32 fx = sx - (f32)cx;
|
||||
f32 fy = sy - (f32)cy;
|
||||
|
||||
f32 min_dist = 1e30f;
|
||||
f32 second_dist = 1e30f;
|
||||
i32 closest_id = 0;
|
||||
|
||||
for (i32 dy = -1; dy <= 1; dy++) {
|
||||
for (i32 dx = -1; dx <= 1; dx++) {
|
||||
i32 nx = cx + dx;
|
||||
i32 ny = cy + dy;
|
||||
u32 h = hash2d(nx, ny, seed);
|
||||
f32 px = (f32)dx + (f32)(h & 0xFF) / 255.0f - 0.5f - fx;
|
||||
f32 py = (f32)dy + (f32)((h >> 8) & 0xFF) / 255.0f - 0.5f - fy;
|
||||
f32 dist = px * px + py * py;
|
||||
|
||||
if (dist < min_dist) {
|
||||
second_dist = min_dist;
|
||||
min_dist = dist;
|
||||
closest_id = (i32)h;
|
||||
} else if (dist < second_dist) {
|
||||
second_dist = dist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*cell_dist = sqrtf(min_dist);
|
||||
*edge_dist = sqrtf(second_dist) - sqrtf(min_dist);
|
||||
*cell_id = closest_id;
|
||||
}
|
||||
|
||||
static f32 gradient_linear(f32 x, f32 y, f32 angle) {
|
||||
f32 dx = cosf(angle);
|
||||
f32 dy = sinf(angle);
|
||||
f32 result = x * dx + y * dy;
|
||||
return fmaxf(0.0f, fminf(1.0f, result));
|
||||
}
|
||||
|
||||
static f32 gradient_radial(f32 x, f32 y, f32 cx, f32 cy) {
|
||||
f32 dx = x - cx;
|
||||
f32 dy = y - cy;
|
||||
return fmaxf(0.0f, fminf(1.0f, sqrtf(dx * dx + dy * dy)));
|
||||
}
|
||||
|
||||
pxl8_graph* pxl8_graph_create(u32 capacity) {
|
||||
pxl8_graph* graph = pxl8_calloc(1, sizeof(pxl8_graph));
|
||||
if (!graph) return NULL;
|
||||
|
||||
graph->nodes = pxl8_calloc(capacity, sizeof(pxl8_node));
|
||||
if (!graph->nodes) {
|
||||
pxl8_free(graph);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
graph->capacity = capacity;
|
||||
graph->count = 0;
|
||||
graph->seed = 0;
|
||||
graph->output_reg = 0;
|
||||
return graph;
|
||||
}
|
||||
|
||||
void pxl8_graph_destroy(pxl8_graph* graph) {
|
||||
if (!graph) return;
|
||||
pxl8_free(graph->nodes);
|
||||
pxl8_free(graph);
|
||||
}
|
||||
|
||||
void pxl8_graph_clear(pxl8_graph* graph) {
|
||||
if (!graph) return;
|
||||
graph->count = 0;
|
||||
graph->output_reg = 0;
|
||||
}
|
||||
|
||||
u8 pxl8_graph_add_node(pxl8_graph* graph, pxl8_graph_op op, u8 in0, u8 in1, u8 in2, u8 in3, f32 param) {
|
||||
if (!graph || graph->count >= graph->capacity) return 0;
|
||||
|
||||
u8 out = (u8)(graph->count + 4);
|
||||
pxl8_node* node = &graph->nodes[graph->count++];
|
||||
node->op = (u8)op;
|
||||
node->out = out;
|
||||
node->in[0] = in0;
|
||||
node->in[1] = in1;
|
||||
node->in[2] = in2;
|
||||
node->in[3] = in3;
|
||||
node->param = param;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void pxl8_graph_set_output(pxl8_graph* graph, u8 reg) {
|
||||
if (graph) graph->output_reg = reg;
|
||||
}
|
||||
|
||||
void pxl8_graph_set_seed(pxl8_graph* graph, u32 seed) {
|
||||
if (graph) graph->seed = seed;
|
||||
}
|
||||
|
||||
f32 pxl8_graph_eval(const pxl8_graph* graph, pxl8_graph_context* ctx) {
|
||||
if (!graph || !ctx) return 0.0f;
|
||||
|
||||
for (u32 i = 0; i < graph->count; i++) {
|
||||
const pxl8_node* n = &graph->nodes[i];
|
||||
f32 a = ctx->regs[n->in[0]];
|
||||
f32 b = ctx->regs[n->in[1]];
|
||||
f32 c = ctx->regs[n->in[2]];
|
||||
f32 d = ctx->regs[n->in[3]];
|
||||
f32 result = 0.0f;
|
||||
|
||||
switch (n->op) {
|
||||
case PXL8_OP_CONST: result = n->param; break;
|
||||
case PXL8_OP_INPUT_AGE: result = ctx->regs[3]; break;
|
||||
case PXL8_OP_INPUT_SEED: result = (f32)ctx->seed; break;
|
||||
case PXL8_OP_INPUT_TIME: result = ctx->regs[2]; break;
|
||||
case PXL8_OP_INPUT_X: result = ctx->regs[0]; break;
|
||||
case PXL8_OP_INPUT_Y: result = ctx->regs[1]; break;
|
||||
|
||||
case PXL8_OP_ABS: result = fabsf(a); break;
|
||||
case PXL8_OP_CEIL: result = ceilf(a); break;
|
||||
case PXL8_OP_COS: result = cosf(a); break;
|
||||
case PXL8_OP_FLOOR: result = floorf(a); break;
|
||||
case PXL8_OP_FRACT: result = a - floorf(a); break;
|
||||
case PXL8_OP_NEGATE: result = -a; break;
|
||||
case PXL8_OP_SIN: result = sinf(a); break;
|
||||
case PXL8_OP_SQRT: result = sqrtf(a); break;
|
||||
|
||||
case PXL8_OP_ADD: result = a + b; break;
|
||||
case PXL8_OP_DIV: result = b != 0.0f ? a / b : 0.0f; break;
|
||||
case PXL8_OP_MAX: result = fmaxf(a, b); break;
|
||||
case PXL8_OP_MIN: result = fminf(a, b); break;
|
||||
case PXL8_OP_MOD: result = b != 0.0f ? fmodf(a, b) : 0.0f; break;
|
||||
case PXL8_OP_MUL: result = a * b; break;
|
||||
case PXL8_OP_POW: result = powf(a, b); break;
|
||||
case PXL8_OP_SUB: result = a - b; break;
|
||||
|
||||
case PXL8_OP_CLAMP: result = fmaxf(b, fminf(c, a)); break;
|
||||
case PXL8_OP_LERP: result = a + c * (b - a); break;
|
||||
case PXL8_OP_SELECT: result = c > 0.0f ? a : b; break;
|
||||
case PXL8_OP_SMOOTHSTEP: {
|
||||
f32 t = fmaxf(0.0f, fminf(1.0f, (c - a) / (b - a)));
|
||||
result = t * t * (3.0f - 2.0f * t);
|
||||
break;
|
||||
}
|
||||
|
||||
case PXL8_OP_NOISE_FBM: result = noise_fbm(a, b, (i32)n->param, c, d, ctx->seed); break;
|
||||
case PXL8_OP_NOISE_PERLIN: result = noise_perlin(a, b, c, ctx->seed); break;
|
||||
case PXL8_OP_NOISE_RIDGED: result = noise_ridged(a, b, (i32)n->param, c, d, ctx->seed); break;
|
||||
case PXL8_OP_NOISE_TURBULENCE:result = noise_turbulence(a, b, (i32)n->param, c, d, ctx->seed); break;
|
||||
case PXL8_OP_NOISE_VALUE: result = noise_value(a, b, c, ctx->seed); break;
|
||||
|
||||
case PXL8_OP_VORONOI_CELL: {
|
||||
f32 cell, edge; i32 id;
|
||||
voronoi(a, b, c, ctx->seed, &cell, &edge, &id);
|
||||
result = cell;
|
||||
break;
|
||||
}
|
||||
case PXL8_OP_VORONOI_EDGE: {
|
||||
f32 cell, edge; i32 id;
|
||||
voronoi(a, b, c, ctx->seed, &cell, &edge, &id);
|
||||
result = edge;
|
||||
break;
|
||||
}
|
||||
case PXL8_OP_VORONOI_ID: {
|
||||
f32 cell, edge; i32 id;
|
||||
voronoi(a, b, c, ctx->seed, &cell, &edge, &id);
|
||||
result = (f32)(id & 0xFF) / 255.0f;
|
||||
break;
|
||||
}
|
||||
|
||||
case PXL8_OP_GRADIENT_LINEAR: result = gradient_linear(a, b, c); break;
|
||||
case PXL8_OP_GRADIENT_RADIAL: result = gradient_radial(a, b, c, d); break;
|
||||
|
||||
case PXL8_OP_QUANTIZE: {
|
||||
u8 base = (u8)n->param;
|
||||
f32 range = b;
|
||||
f32 clamped = fmaxf(0.0f, fminf(1.0f, a));
|
||||
result = (f32)(base + (u8)fminf(clamped * range, range - 1.0f));
|
||||
break;
|
||||
}
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
ctx->regs[n->out] = result;
|
||||
}
|
||||
|
||||
return ctx->regs[graph->output_reg];
|
||||
}
|
||||
|
||||
void pxl8_graph_eval_texture(const pxl8_graph* graph, u8* buffer, i32 width, i32 height) {
|
||||
if (!graph || !buffer) return;
|
||||
|
||||
pxl8_graph_context ctx = {0};
|
||||
ctx.seed = graph->seed;
|
||||
|
||||
for (i32 y = 0; y < height; y++) {
|
||||
for (i32 x = 0; x < width; x++) {
|
||||
ctx.regs[0] = (f32)x / (f32)width;
|
||||
ctx.regs[1] = (f32)y / (f32)height;
|
||||
ctx.regs[2] = 0.0f;
|
||||
ctx.regs[3] = 0.0f;
|
||||
|
||||
f32 result = pxl8_graph_eval(graph, &ctx);
|
||||
buffer[y * width + x] = (u8)fmaxf(0.0f, fminf(255.0f, result));
|
||||
}
|
||||
}
|
||||
}
|
||||
90
src/procgen/pxl8_graph.h
Normal file
90
src/procgen/pxl8_graph.h
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_types.h"
|
||||
|
||||
typedef enum pxl8_graph_op {
|
||||
PXL8_OP_CONST, // param -> out
|
||||
PXL8_OP_INPUT_AGE, // particle normalized age -> out
|
||||
PXL8_OP_INPUT_SEED, // seed -> out
|
||||
PXL8_OP_INPUT_TIME, // time -> out
|
||||
PXL8_OP_INPUT_X, // x coord -> out
|
||||
PXL8_OP_INPUT_Y, // y coord -> out
|
||||
|
||||
PXL8_OP_ABS, // |a| -> out
|
||||
PXL8_OP_CEIL, // ceil(a) -> out
|
||||
PXL8_OP_COS, // cos(a) -> out
|
||||
PXL8_OP_FLOOR, // floor(a) -> out
|
||||
PXL8_OP_FRACT, // fract(a) -> out
|
||||
PXL8_OP_NEGATE, // -a -> out
|
||||
PXL8_OP_SIN, // sin(a) -> out
|
||||
PXL8_OP_SQRT, // sqrt(a) -> out
|
||||
|
||||
PXL8_OP_ADD, // a + b -> out
|
||||
PXL8_OP_DIV, // a / b -> out
|
||||
PXL8_OP_MAX, // max(a, b) -> out
|
||||
PXL8_OP_MIN, // min(a, b) -> out
|
||||
PXL8_OP_MOD, // fmod(a, b) -> out
|
||||
PXL8_OP_MUL, // a * b -> out
|
||||
PXL8_OP_POW, // pow(a, b) -> out
|
||||
PXL8_OP_SUB, // a - b -> out
|
||||
|
||||
PXL8_OP_CLAMP, // clamp(a, min, max) -> out
|
||||
PXL8_OP_LERP, // lerp(a, b, t) -> out
|
||||
PXL8_OP_SELECT, // t > 0 ? a : b -> out
|
||||
PXL8_OP_SMOOTHSTEP, // smoothstep(edge0, edge1, x) -> out
|
||||
|
||||
PXL8_OP_NOISE_FBM, // fbm(x, y, octaves, scale, persistence) -> out
|
||||
PXL8_OP_NOISE_PERLIN, // perlin noise(x, y, scale) -> out
|
||||
PXL8_OP_NOISE_RIDGED, // ridged(x, y, octaves, scale, persistence) -> out
|
||||
PXL8_OP_NOISE_TURBULENCE,// turbulence(x, y, octaves, scale, persistence) -> out
|
||||
PXL8_OP_NOISE_VALUE, // value noise(x, y, scale) -> out
|
||||
|
||||
PXL8_OP_VORONOI_CELL, // voronoi cell distance(x, y, scale) -> out
|
||||
PXL8_OP_VORONOI_EDGE, // voronoi edge distance(x, y, scale) -> out
|
||||
PXL8_OP_VORONOI_ID, // voronoi cell id(x, y, scale) -> out
|
||||
|
||||
PXL8_OP_GRADIENT_LINEAR, // linear gradient(x, y, angle) -> out
|
||||
PXL8_OP_GRADIENT_RADIAL, // radial gradient(x, y, cx, cy) -> out
|
||||
|
||||
PXL8_OP_QUANTIZE, // quantize to palette: base + floor(a * range) -> out
|
||||
|
||||
PXL8_OP_COUNT
|
||||
} pxl8_graph_op;
|
||||
|
||||
typedef struct pxl8_node {
|
||||
u8 in[4];
|
||||
u8 op;
|
||||
u8 out;
|
||||
f32 param;
|
||||
} pxl8_node;
|
||||
|
||||
typedef struct pxl8_graph {
|
||||
u32 capacity;
|
||||
u32 count;
|
||||
pxl8_node* nodes;
|
||||
u8 output_reg;
|
||||
u32 seed;
|
||||
} pxl8_graph;
|
||||
|
||||
typedef struct pxl8_graph_context {
|
||||
f32 regs[256];
|
||||
u32 seed;
|
||||
} pxl8_graph_context;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
f32 pxl8_graph_eval(const pxl8_graph* graph, pxl8_graph_context* ctx);
|
||||
void pxl8_graph_eval_texture(const pxl8_graph* graph, u8* buffer, i32 width, i32 height);
|
||||
|
||||
u8 pxl8_graph_add_node(pxl8_graph* graph, pxl8_graph_op op, u8 in0, u8 in1, u8 in2, u8 in3, f32 param);
|
||||
void pxl8_graph_clear(pxl8_graph* graph);
|
||||
pxl8_graph* pxl8_graph_create(u32 capacity);
|
||||
void pxl8_graph_destroy(pxl8_graph* graph);
|
||||
void pxl8_graph_set_output(pxl8_graph* graph, u8 reg);
|
||||
void pxl8_graph_set_seed(pxl8_graph* graph, u32 seed);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
#include "pxl8_repl.h"
|
||||
#include "pxl8_mem.h"
|
||||
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <linenoise.h>
|
||||
|
||||
#define PXL8_MAX_REPL_COMMAND_SIZE 4096
|
||||
|
|
@ -29,7 +29,7 @@ struct pxl8_repl {
|
|||
atomic_uint log_read_idx;
|
||||
|
||||
atomic_bool should_quit;
|
||||
pthread_t thread;
|
||||
SDL_Thread* thread;
|
||||
char accumulator[PXL8_MAX_REPL_COMMAND_SIZE];
|
||||
pxl8_repl_command command;
|
||||
};
|
||||
|
|
@ -56,15 +56,15 @@ static void pxl8_repl_completion(const char* buf, linenoiseCompletions* lc) {
|
|||
"pxl8.error", "pxl8.debug", "pxl8.trace"
|
||||
};
|
||||
|
||||
size_t buf_len = strlen(buf);
|
||||
usize buf_len = strlen(buf);
|
||||
|
||||
for (size_t i = 0; i < sizeof(fennel_keywords) / sizeof(fennel_keywords[0]); i++) {
|
||||
for (usize i = 0; i < sizeof(fennel_keywords) / sizeof(fennel_keywords[0]); i++) {
|
||||
if (strncmp(buf, fennel_keywords[i], buf_len) == 0) {
|
||||
linenoiseAddCompletion(lc, fennel_keywords[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < sizeof(pxl8_functions) / sizeof(pxl8_functions[0]); i++) {
|
||||
for (usize i = 0; i < sizeof(pxl8_functions) / sizeof(pxl8_functions[0]); i++) {
|
||||
if (strncmp(buf, pxl8_functions[i], buf_len) == 0) {
|
||||
linenoiseAddCompletion(lc, pxl8_functions[i]);
|
||||
}
|
||||
|
|
@ -105,7 +105,7 @@ static void pxl8_repl_flush_logs(pxl8_repl* repl) {
|
|||
fflush(stdout);
|
||||
}
|
||||
|
||||
static void* pxl8_repl_thread(void* arg) {
|
||||
static int pxl8_repl_thread(void* arg) {
|
||||
pxl8_repl* repl = (pxl8_repl*)arg;
|
||||
|
||||
printf("[pxl8 REPL] Fennel 1.6.0 - Tab for completion, Ctrl-D to exit\n");
|
||||
|
|
@ -204,8 +204,7 @@ static void* pxl8_repl_thread(void* arg) {
|
|||
lw = atomic_load(&repl->log_write_idx);
|
||||
}
|
||||
fflush(stdout);
|
||||
struct timespec ts = {.tv_sec = 0, .tv_nsec = 1000000};
|
||||
nanosleep(&ts, NULL);
|
||||
SDL_Delay(1);
|
||||
}
|
||||
atomic_store(&repl->cmd_complete, false);
|
||||
}
|
||||
|
|
@ -214,11 +213,11 @@ static void* pxl8_repl_thread(void* arg) {
|
|||
|
||||
pxl8_repl_flush_logs(repl);
|
||||
|
||||
return NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
pxl8_repl* pxl8_repl_create(void) {
|
||||
pxl8_repl* repl = (pxl8_repl*)calloc(1, sizeof(pxl8_repl));
|
||||
pxl8_repl* repl = (pxl8_repl*)pxl8_calloc(1, sizeof(pxl8_repl));
|
||||
if (!repl) return NULL;
|
||||
|
||||
repl->accumulator[0] = '\0';
|
||||
|
|
@ -237,8 +236,9 @@ pxl8_repl* pxl8_repl_create(void) {
|
|||
|
||||
g_repl = repl;
|
||||
|
||||
if (pthread_create(&repl->thread, NULL, pxl8_repl_thread, repl) != 0) {
|
||||
free(repl);
|
||||
repl->thread = SDL_CreateThread(pxl8_repl_thread, "pxl8-repl", repl);
|
||||
if (!repl->thread) {
|
||||
pxl8_free(repl);
|
||||
g_repl = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -251,19 +251,18 @@ void pxl8_repl_destroy(pxl8_repl* repl) {
|
|||
|
||||
atomic_store(&repl->should_quit, true);
|
||||
|
||||
struct timespec ts = {.tv_sec = 0, .tv_nsec = 2000000};
|
||||
nanosleep(&ts, NULL);
|
||||
SDL_Delay(2);
|
||||
|
||||
printf("\r\033[K");
|
||||
fflush(stdout);
|
||||
|
||||
pthread_join(repl->thread, NULL);
|
||||
SDL_WaitThread(repl->thread, NULL);
|
||||
pxl8_repl_flush_logs(repl);
|
||||
|
||||
g_repl = NULL;
|
||||
|
||||
system("stty sane 2>/dev/null");
|
||||
free(repl);
|
||||
pxl8_free(repl);
|
||||
}
|
||||
|
||||
pxl8_repl_command* pxl8_repl_pop_command(pxl8_repl* repl) {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include "pxl8_gui.h"
|
||||
#include "pxl8_log.h"
|
||||
#include "pxl8_macros.h"
|
||||
#include "pxl8_mem.h"
|
||||
#include "pxl8_script_ffi.h"
|
||||
|
||||
struct pxl8_script {
|
||||
|
|
@ -37,7 +38,7 @@ struct pxl8_script {
|
|||
static int pxl8_cart_loader(lua_State* L) {
|
||||
const char* found_path = lua_tostring(L, lua_upvalueindex(1));
|
||||
const char* code = lua_tostring(L, lua_upvalueindex(2));
|
||||
size_t code_len = lua_objlen(L, lua_upvalueindex(2));
|
||||
usize code_len = lua_objlen(L, lua_upvalueindex(2));
|
||||
bool is_fennel = lua_toboolean(L, lua_upvalueindex(3));
|
||||
|
||||
if (is_fennel) {
|
||||
|
|
@ -75,9 +76,9 @@ static int pxl8_cart_searcher(lua_State* L) {
|
|||
}
|
||||
|
||||
char path[512];
|
||||
size_t len = strlen(modname);
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; i < len && j < sizeof(path) - 5; i++) {
|
||||
usize len = strlen(modname);
|
||||
usize j = 0;
|
||||
for (usize i = 0; i < len && j < sizeof(path) - 5; i++) {
|
||||
if (modname[i] == '.') {
|
||||
path[j++] = '/';
|
||||
} else {
|
||||
|
|
@ -114,9 +115,9 @@ static int pxl8_cart_searcher(lua_State* L) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
static void pxl8_script_repl_promote_locals(const char* input, char* output, size_t output_size) {
|
||||
size_t i = 0;
|
||||
size_t j = 0;
|
||||
static void pxl8_script_repl_promote_locals(const char* input, char* output, usize output_size) {
|
||||
usize i = 0;
|
||||
usize j = 0;
|
||||
bool in_string = false;
|
||||
bool in_comment = false;
|
||||
|
||||
|
|
@ -182,18 +183,18 @@ static void pxl8_script_set_error(pxl8_script* script, const char* error) {
|
|||
src += 9;
|
||||
const char* mod_start = src;
|
||||
while (*src && !(*src == '"' && *(src+1) == ']')) src++;
|
||||
size_t mod_len = src - mod_start;
|
||||
usize mod_len = src - mod_start;
|
||||
|
||||
if (mod_len > 4 && strncmp(mod_start, "pxl8", 4) == 0) {
|
||||
const char* prefix = "src/lua/";
|
||||
while (*prefix && dst < end) *dst++ = *prefix++;
|
||||
for (size_t i = 0; i < mod_len && dst < end; i++) {
|
||||
for (usize i = 0; i < mod_len && dst < end; i++) {
|
||||
*dst++ = (mod_start[i] == '.') ? '/' : mod_start[i];
|
||||
}
|
||||
const char* suffix = ".lua";
|
||||
while (*suffix && dst < end) *dst++ = *suffix++;
|
||||
} else {
|
||||
for (size_t i = 0; i < mod_len && dst < end; i++) {
|
||||
for (usize i = 0; i < mod_len && dst < end; i++) {
|
||||
*dst++ = mod_start[i];
|
||||
}
|
||||
}
|
||||
|
|
@ -244,13 +245,13 @@ static void pxl8_install_embed_searcher(lua_State* L) {
|
|||
}
|
||||
|
||||
pxl8_script* pxl8_script_create(bool repl_mode) {
|
||||
pxl8_script* script = (pxl8_script*)calloc(1, sizeof(pxl8_script));
|
||||
pxl8_script* script = (pxl8_script*)pxl8_calloc(1, sizeof(pxl8_script));
|
||||
if (!script) return NULL;
|
||||
script->repl_mode = repl_mode;
|
||||
|
||||
script->L = luaL_newstate();
|
||||
if (!script->L) {
|
||||
free(script);
|
||||
pxl8_free(script);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -349,7 +350,7 @@ void pxl8_script_destroy(pxl8_script* script) {
|
|||
}
|
||||
lua_close(script->L);
|
||||
}
|
||||
free(script);
|
||||
pxl8_free(script);
|
||||
}
|
||||
|
||||
void pxl8_script_set_gfx(pxl8_script* script, pxl8_gfx* gfx) {
|
||||
|
|
@ -395,7 +396,7 @@ void pxl8_script_set_sys(pxl8_script* script, void* sys) {
|
|||
}
|
||||
}
|
||||
|
||||
static pxl8_result pxl8_script_prepare_path(pxl8_script* script, const char* filename, char* out_basename, size_t basename_size) {
|
||||
static pxl8_result pxl8_script_prepare_path(pxl8_script* script, const char* filename, char* out_basename, usize basename_size) {
|
||||
char filename_copy[PATH_MAX];
|
||||
pxl8_strncpy(filename_copy, filename, sizeof(filename_copy));
|
||||
|
||||
|
|
@ -470,7 +471,7 @@ static time_t get_latest_script_mod_time(const char* dir_path) {
|
|||
latest = subdir_time;
|
||||
}
|
||||
} else {
|
||||
size_t len = strlen(entry->d_name);
|
||||
usize len = strlen(entry->d_name);
|
||||
bool is_script = (len > 4 && strcmp(entry->d_name + len - 4, ".fnl") == 0) ||
|
||||
(len > 4 && strcmp(entry->d_name + len - 4, ".lua") == 0);
|
||||
|
||||
|
|
@ -618,8 +619,8 @@ static pxl8_result pxl8_script_eval_internal(pxl8_script* script, const char* co
|
|||
const char* error = lua_tostring(script->L, -1);
|
||||
if (error) {
|
||||
char cleaned_error[2048];
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; error[i] && j < sizeof(cleaned_error) - 1; i++) {
|
||||
usize j = 0;
|
||||
for (usize i = 0; error[i] && j < sizeof(cleaned_error) - 1; i++) {
|
||||
if (error[i] == '\t') {
|
||||
cleaned_error[j++] = ' ';
|
||||
} else {
|
||||
|
|
@ -877,7 +878,7 @@ typedef struct {
|
|||
|
||||
static void ser_buffer_init(ser_buffer* buf) {
|
||||
buf->capacity = 1024;
|
||||
buf->data = malloc(buf->capacity);
|
||||
buf->data = pxl8_malloc(buf->capacity);
|
||||
buf->size = 0;
|
||||
}
|
||||
|
||||
|
|
@ -886,7 +887,7 @@ static void ser_buffer_grow(ser_buffer* buf, u32 needed) {
|
|||
while (buf->size + needed > buf->capacity) {
|
||||
buf->capacity *= 2;
|
||||
}
|
||||
buf->data = realloc(buf->data, buf->capacity);
|
||||
buf->data = pxl8_realloc(buf->data, buf->capacity);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -930,7 +931,7 @@ static void ser_write_value(ser_buffer* buf, lua_State* L, int idx, int depth) {
|
|||
ser_write_f64(buf, lua_tonumber(L, idx));
|
||||
break;
|
||||
case LUA_TSTRING: {
|
||||
size_t len;
|
||||
usize len;
|
||||
const char* str = lua_tolstring(L, idx, &len);
|
||||
ser_write_u8(buf, SER_STRING);
|
||||
ser_write_u32(buf, (u32)len);
|
||||
|
|
@ -1080,7 +1081,7 @@ void pxl8_script_deserialize_globals(pxl8_script* script, const u8* data, u32 si
|
|||
}
|
||||
|
||||
void pxl8_script_free_serialized(u8* data) {
|
||||
free(data);
|
||||
pxl8_free(data);
|
||||
}
|
||||
|
||||
pxl8_result pxl8_script_load_main(pxl8_script* script, const char* path) {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ static const char* pxl8_ffi_cdefs =
|
|||
"typedef int64_t i64;\n"
|
||||
"typedef float f32;\n"
|
||||
"typedef double f64;\n"
|
||||
"typedef size_t usize;\n"
|
||||
"typedef ptrdiff_t isize;\n"
|
||||
"typedef struct pxl8 pxl8;\n"
|
||||
"typedef struct pxl8_gfx pxl8_gfx;\n"
|
||||
"typedef struct { int x, y, w, h; } pxl8_bounds;\n"
|
||||
|
|
@ -27,11 +29,16 @@ static const char* pxl8_ffi_cdefs =
|
|||
"u8 pxl8_gfx_find_color(pxl8_gfx* gfx, u32 color);\n"
|
||||
"i32 pxl8_gfx_get_height(pxl8_gfx* ctx);\n"
|
||||
"typedef struct pxl8_palette pxl8_palette;\n"
|
||||
"pxl8_palette* pxl8_gfx_get_palette(pxl8_gfx* gfx);\n"
|
||||
"pxl8_palette* pxl8_gfx_palette(pxl8_gfx* gfx);\n"
|
||||
"u32 pxl8_palette_color(const pxl8_palette* pal, u8 idx);\n"
|
||||
"void pxl8_palette_set_rgb(pxl8_palette* pal, u8 idx, u8 r, u8 g, u8 b);\n"
|
||||
"void pxl8_gfx_set_palette_colors(pxl8_gfx* gfx, const u32* colors, u16 count);\n"
|
||||
"i32 pxl8_palette_index(const pxl8_palette* pal, u32 color);\n"
|
||||
"u8 pxl8_palette_ramp_index(const pxl8_palette* pal, u8 position);\n"
|
||||
"typedef struct pxl8_colormap pxl8_colormap;\n"
|
||||
"pxl8_colormap* pxl8_gfx_colormap(pxl8_gfx* gfx);\n"
|
||||
"void pxl8_set_colormap(pxl8_colormap* cm, const u8* data, u32 size);\n"
|
||||
"void pxl8_gfx_ensure_blend_tables(pxl8_gfx* gfx);\n"
|
||||
"i32 pxl8_gfx_get_width(pxl8_gfx* ctx);\n"
|
||||
"void pxl8_2d_circle(pxl8_gfx* ctx, i32 x, i32 y, i32 r, u32 color);\n"
|
||||
"void pxl8_2d_circle_fill(pxl8_gfx* ctx, i32 x, i32 y, i32 r, u32 color);\n"
|
||||
|
|
@ -192,21 +199,27 @@ static const char* pxl8_ffi_cdefs =
|
|||
"\n"
|
||||
"typedef struct pxl8_light {\n"
|
||||
" pxl8_vec3 position;\n"
|
||||
" f32 inv_radius_sq;\n"
|
||||
" u8 r, g, b;\n"
|
||||
" u8 intensity;\n"
|
||||
" f32 radius;\n"
|
||||
" f32 radius_sq;\n"
|
||||
" f32 inv_radius_sq;\n"
|
||||
"} pxl8_light;\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_lights pxl8_lights;\n"
|
||||
"pxl8_lights* pxl8_lights_create(u32 capacity);\n"
|
||||
"void pxl8_lights_destroy(pxl8_lights* lights);\n"
|
||||
"void pxl8_lights_add(pxl8_lights* lights, f32 x, f32 y, f32 z, u8 r, u8 g, u8 b, u8 intensity, f32 radius);\n"
|
||||
"void pxl8_lights_clear(pxl8_lights* lights);\n"
|
||||
"u32 pxl8_lights_count(const pxl8_lights* lights);\n"
|
||||
"const pxl8_light* pxl8_lights_data(const pxl8_lights* lights);\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_3d_uniforms {\n"
|
||||
" u8 ambient;\n"
|
||||
" pxl8_vec3 celestial_dir;\n"
|
||||
" f32 celestial_intensity;\n"
|
||||
" u8 fog_color;\n"
|
||||
" f32 fog_density;\n"
|
||||
" pxl8_light lights[16];\n"
|
||||
" u32 num_lights;\n"
|
||||
" f32 time;\n"
|
||||
"} pxl8_3d_uniforms;\n"
|
||||
"\n"
|
||||
|
|
@ -224,12 +237,14 @@ static const char* pxl8_ffi_cdefs =
|
|||
"pxl8_mat4 pxl8_3d_camera_get_view(const pxl8_3d_camera* cam);\n"
|
||||
"pxl8_mat4 pxl8_3d_camera_get_projection(const pxl8_3d_camera* cam);\n"
|
||||
"void pxl8_3d_camera_update(pxl8_3d_camera* cam, f32 dt);\n"
|
||||
"typedef struct pxl8_projected_point { i32 x; i32 y; f32 depth; bool visible; } pxl8_projected_point;\n"
|
||||
"pxl8_projected_point pxl8_3d_camera_world_to_screen(const pxl8_3d_camera* cam, pxl8_vec3 world_pos, u32 screen_width, u32 screen_height);\n"
|
||||
"\n"
|
||||
"typedef enum pxl8_gfx_effect { PXL8_GFX_EFFECT_GLOWS = 0 } pxl8_gfx_effect;\n"
|
||||
"\n"
|
||||
"typedef enum pxl8_glow_shape { PXL8_GLOW_CIRCLE = 0, PXL8_GLOW_DIAMOND = 1, PXL8_GLOW_SHAFT = 2 } pxl8_glow_shape;\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_glow_source {\n"
|
||||
"typedef struct pxl8_glow {\n"
|
||||
" u8 color;\n"
|
||||
" u16 depth;\n"
|
||||
" u8 height;\n"
|
||||
|
|
@ -238,22 +253,37 @@ static const char* pxl8_ffi_cdefs =
|
|||
" pxl8_glow_shape shape;\n"
|
||||
" i16 x;\n"
|
||||
" i16 y;\n"
|
||||
"} pxl8_glow_source;\n"
|
||||
"} pxl8_glow;\n"
|
||||
"\n"
|
||||
"void pxl8_gfx_apply_effect(pxl8_gfx* gfx, pxl8_gfx_effect effect, const void* params, u32 count);\n"
|
||||
"void pxl8_gfx_blend_tables_update(pxl8_gfx* gfx);\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_glows pxl8_glows;\n"
|
||||
"pxl8_glows* pxl8_glows_create(u32 capacity);\n"
|
||||
"void pxl8_glows_destroy(pxl8_glows* glows);\n"
|
||||
"void pxl8_glows_add(pxl8_glows* glows, i16 x, i16 y, u8 radius, u16 intensity, u8 color, u8 shape);\n"
|
||||
"void pxl8_glows_clear(pxl8_glows* glows);\n"
|
||||
"u32 pxl8_glows_count(const pxl8_glows* glows);\n"
|
||||
"void pxl8_glows_render(pxl8_glows* glows, pxl8_gfx* gfx);\n"
|
||||
"void pxl8_gfx_colormap_update(pxl8_gfx* gfx);\n"
|
||||
"\n"
|
||||
"void pxl8_3d_begin_frame(pxl8_gfx* gfx, const pxl8_3d_camera* camera, const pxl8_3d_uniforms* uniforms);\n"
|
||||
"void pxl8_3d_begin_frame(pxl8_gfx* gfx, const pxl8_3d_camera* camera, const pxl8_lights* lights, const pxl8_3d_uniforms* uniforms);\n"
|
||||
"void pxl8_3d_clear(pxl8_gfx* gfx, u8 color);\n"
|
||||
"void pxl8_3d_clear_depth(pxl8_gfx* gfx);\n"
|
||||
"void pxl8_3d_draw_line(pxl8_gfx* gfx, pxl8_vec3 p0, pxl8_vec3 p1, u8 color);\n"
|
||||
"void pxl8_3d_end_frame(pxl8_gfx* gfx);\n"
|
||||
"u32 pxl8_3d_project_points(pxl8_gfx* gfx, const pxl8_vec3* in, pxl8_vec3* out, u32 count, const pxl8_mat4* transform);\n"
|
||||
"\n"
|
||||
"typedef enum pxl8_blend_mode { PXL8_BLEND_OPAQUE = 0, PXL8_BLEND_ALPHA_TEST, PXL8_BLEND_ALPHA, PXL8_BLEND_ADDITIVE } pxl8_blend_mode;\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_material {\n"
|
||||
"typedef struct pxl8_gfx_material {\n"
|
||||
" char name[16];\n"
|
||||
" pxl8_vec3 u_axis;\n"
|
||||
" pxl8_vec3 v_axis;\n"
|
||||
" f32 u_offset;\n"
|
||||
" f32 v_offset;\n"
|
||||
" u32 texture_id;\n"
|
||||
" u32 lightmap_id;\n"
|
||||
" u8 alpha;\n"
|
||||
" u8 blend_mode;\n"
|
||||
" bool dither;\n"
|
||||
|
|
@ -261,13 +291,15 @@ static const char* pxl8_ffi_cdefs =
|
|||
" bool dynamic_lighting;\n"
|
||||
" bool per_pixel;\n"
|
||||
" bool vertex_color_passthrough;\n"
|
||||
" bool wireframe;\n"
|
||||
" f32 emissive_intensity;\n"
|
||||
"} pxl8_material;\n"
|
||||
"} pxl8_gfx_material;\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_vertex {\n"
|
||||
" pxl8_vec3 position;\n"
|
||||
" pxl8_vec3 normal;\n"
|
||||
" f32 u, v;\n"
|
||||
" f32 lu, lv;\n"
|
||||
" u8 color;\n"
|
||||
" u8 light;\n"
|
||||
" u8 _pad[2];\n"
|
||||
|
|
@ -287,12 +319,16 @@ static const char* pxl8_ffi_cdefs =
|
|||
"void pxl8_mesh_clear(pxl8_mesh* mesh);\n"
|
||||
"u16 pxl8_mesh_push_vertex(pxl8_mesh* mesh, pxl8_vertex v);\n"
|
||||
"void pxl8_mesh_push_triangle(pxl8_mesh* mesh, u16 i0, u16 i1, u16 i2);\n"
|
||||
"void pxl8_3d_draw_mesh(pxl8_gfx* gfx, const pxl8_mesh* mesh, const pxl8_mat4* model, const pxl8_material* material);\n"
|
||||
"void pxl8_3d_draw_mesh(pxl8_gfx* gfx, const pxl8_mesh* mesh, const pxl8_mat4* model, const pxl8_gfx_material* material);\n"
|
||||
"\n"
|
||||
"u32 pxl8_hash32(u32 x);\n"
|
||||
"\n"
|
||||
"pxl8_mat4 pxl8_mat4_identity(void);\n"
|
||||
"pxl8_mat4 pxl8_mat4_lookat(pxl8_vec3 eye, pxl8_vec3 center, pxl8_vec3 up);\n"
|
||||
"pxl8_mat4 pxl8_mat4_mul(pxl8_mat4 a, pxl8_mat4 b);\n"
|
||||
"pxl8_mat4 pxl8_mat4_ortho(float left, float right, float bottom, float top, float near, float far);\n"
|
||||
"pxl8_mat4 pxl8_mat4_multiply(pxl8_mat4 a, pxl8_mat4 b);\n"
|
||||
"pxl8_vec3 pxl8_mat4_multiply_vec3(pxl8_mat4 m, pxl8_vec3 v);\n"
|
||||
"pxl8_vec4 pxl8_mat4_multiply_vec4(pxl8_mat4 m, pxl8_vec4 v);\n"
|
||||
"pxl8_mat4 pxl8_mat4_orthographic(float left, float right, float bottom, float top, float near, float far);\n"
|
||||
"pxl8_mat4 pxl8_mat4_perspective(float fov, float aspect, float near, float far);\n"
|
||||
"pxl8_mat4 pxl8_mat4_rotate_x(float angle);\n"
|
||||
"pxl8_mat4 pxl8_mat4_rotate_y(float angle);\n"
|
||||
|
|
@ -316,18 +352,76 @@ static const char* pxl8_ffi_cdefs =
|
|||
" int num_rooms;\n"
|
||||
"} pxl8_procgen_params;\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_procgen_tex_params {\n"
|
||||
" char name[16];\n"
|
||||
" unsigned int seed;\n"
|
||||
" int width;\n"
|
||||
" int height;\n"
|
||||
" float scale;\n"
|
||||
" float roughness;\n"
|
||||
" unsigned char base_color;\n"
|
||||
" unsigned char variation;\n"
|
||||
"} pxl8_procgen_tex_params;\n"
|
||||
"typedef enum pxl8_graph_op {\n"
|
||||
" PXL8_OP_CONST = 0,\n"
|
||||
" PXL8_OP_INPUT_AGE,\n"
|
||||
" PXL8_OP_INPUT_SEED,\n"
|
||||
" PXL8_OP_INPUT_TIME,\n"
|
||||
" PXL8_OP_INPUT_X,\n"
|
||||
" PXL8_OP_INPUT_Y,\n"
|
||||
"\n"
|
||||
"void pxl8_procgen_tex(u8* buffer, const pxl8_procgen_tex_params* params);\n"
|
||||
" PXL8_OP_ABS,\n"
|
||||
" PXL8_OP_CEIL,\n"
|
||||
" PXL8_OP_COS,\n"
|
||||
" PXL8_OP_FLOOR,\n"
|
||||
" PXL8_OP_FRACT,\n"
|
||||
" PXL8_OP_NEGATE,\n"
|
||||
" PXL8_OP_SIN,\n"
|
||||
" PXL8_OP_SQRT,\n"
|
||||
"\n"
|
||||
" PXL8_OP_ADD,\n"
|
||||
" PXL8_OP_DIV,\n"
|
||||
" PXL8_OP_MAX,\n"
|
||||
" PXL8_OP_MIN,\n"
|
||||
" PXL8_OP_MOD,\n"
|
||||
" PXL8_OP_MUL,\n"
|
||||
" PXL8_OP_POW,\n"
|
||||
" PXL8_OP_SUB,\n"
|
||||
"\n"
|
||||
" PXL8_OP_CLAMP,\n"
|
||||
" PXL8_OP_LERP,\n"
|
||||
" PXL8_OP_SELECT,\n"
|
||||
" PXL8_OP_SMOOTHSTEP,\n"
|
||||
"\n"
|
||||
" PXL8_OP_NOISE_FBM,\n"
|
||||
" PXL8_OP_NOISE_PERLIN,\n"
|
||||
" PXL8_OP_NOISE_RIDGED,\n"
|
||||
" PXL8_OP_NOISE_TURBULENCE,\n"
|
||||
" PXL8_OP_NOISE_VALUE,\n"
|
||||
"\n"
|
||||
" PXL8_OP_VORONOI_CELL,\n"
|
||||
" PXL8_OP_VORONOI_EDGE,\n"
|
||||
" PXL8_OP_VORONOI_ID,\n"
|
||||
"\n"
|
||||
" PXL8_OP_GRADIENT_LINEAR,\n"
|
||||
" PXL8_OP_GRADIENT_RADIAL,\n"
|
||||
"\n"
|
||||
" PXL8_OP_QUANTIZE,\n"
|
||||
" PXL8_OP_COUNT\n"
|
||||
"} pxl8_graph_op;\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_node {\n"
|
||||
" u8 in[4];\n"
|
||||
" u8 op;\n"
|
||||
" u8 out;\n"
|
||||
" f32 param;\n"
|
||||
"} pxl8_node;\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_graph {\n"
|
||||
" u32 capacity;\n"
|
||||
" u32 count;\n"
|
||||
" pxl8_node* nodes;\n"
|
||||
" u8 output_reg;\n"
|
||||
" u32 seed;\n"
|
||||
"} pxl8_graph;\n"
|
||||
"\n"
|
||||
"pxl8_graph* pxl8_graph_create(u32 capacity);\n"
|
||||
"void pxl8_graph_destroy(pxl8_graph* graph);\n"
|
||||
"void pxl8_graph_clear(pxl8_graph* graph);\n"
|
||||
"u8 pxl8_graph_add_node(pxl8_graph* graph, pxl8_graph_op op, u8 in0, u8 in1, u8 in2, u8 in3, f32 param);\n"
|
||||
"void pxl8_graph_set_output(pxl8_graph* graph, u8 reg);\n"
|
||||
"void pxl8_graph_set_seed(pxl8_graph* graph, u32 seed);\n"
|
||||
"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_bsp_face pxl8_bsp_face;\n"
|
||||
|
|
@ -350,12 +444,13 @@ static const char* pxl8_ffi_cdefs =
|
|||
"bool pxl8_world_is_loaded(const pxl8_world* world);\n"
|
||||
"void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos);\n"
|
||||
"pxl8_vec3 pxl8_world_resolve_collision(const pxl8_world* world, pxl8_vec3 from, pxl8_vec3 to, float radius);\n"
|
||||
"void pxl8_world_set_wireframe(pxl8_world* world, bool enabled);\n"
|
||||
"\n"
|
||||
"typedef struct { i32 cursor_x; i32 cursor_y; bool cursor_down; bool cursor_clicked; u32 hot_id; u32 active_id; } pxl8_gui_state;\n"
|
||||
"pxl8_gui_state* pxl8_gui_state_create(void);\n"
|
||||
"void pxl8_gui_state_destroy(pxl8_gui_state* state);\n"
|
||||
"void pxl8_gui_begin_frame(pxl8_gui_state* state);\n"
|
||||
"void pxl8_gui_end_frame(pxl8_gui_state* state);\n"
|
||||
"void pxl8_gui_begin_frame(pxl8_gui_state* state, pxl8_gfx* gfx);\n"
|
||||
"void pxl8_gui_end_frame(pxl8_gui_state* state, pxl8_gfx* gfx);\n"
|
||||
"void pxl8_gui_cursor_move(pxl8_gui_state* state, i32 x, i32 y);\n"
|
||||
"void pxl8_gui_cursor_down(pxl8_gui_state* state);\n"
|
||||
"void pxl8_gui_cursor_up(pxl8_gui_state* state);\n"
|
||||
|
|
@ -486,40 +581,40 @@ static const char* pxl8_ffi_cdefs =
|
|||
"bool pxl8_bit_test(u32 val, u8 bit);\n"
|
||||
"void pxl8_bit_toggle(u32* val, u8 bit);\n"
|
||||
"\n"
|
||||
"void pxl8_pack_u8(u8* buf, size_t offset, u8 val);\n"
|
||||
"void pxl8_pack_u16_be(u8* buf, size_t offset, u16 val);\n"
|
||||
"void pxl8_pack_u16_le(u8* buf, size_t offset, u16 val);\n"
|
||||
"void pxl8_pack_u32_be(u8* buf, size_t offset, u32 val);\n"
|
||||
"void pxl8_pack_u32_le(u8* buf, size_t offset, u32 val);\n"
|
||||
"void pxl8_pack_u64_be(u8* buf, size_t offset, u64 val);\n"
|
||||
"void pxl8_pack_u64_le(u8* buf, size_t offset, u64 val);\n"
|
||||
"void pxl8_pack_i8(u8* buf, size_t offset, i8 val);\n"
|
||||
"void pxl8_pack_i16_be(u8* buf, size_t offset, i16 val);\n"
|
||||
"void pxl8_pack_i16_le(u8* buf, size_t offset, i16 val);\n"
|
||||
"void pxl8_pack_i32_be(u8* buf, size_t offset, i32 val);\n"
|
||||
"void pxl8_pack_i32_le(u8* buf, size_t offset, i32 val);\n"
|
||||
"void pxl8_pack_i64_be(u8* buf, size_t offset, i64 val);\n"
|
||||
"void pxl8_pack_i64_le(u8* buf, size_t offset, i64 val);\n"
|
||||
"void pxl8_pack_f32_be(u8* buf, size_t offset, f32 val);\n"
|
||||
"void pxl8_pack_f32_le(u8* buf, size_t offset, f32 val);\n"
|
||||
"void pxl8_pack_f64_be(u8* buf, size_t offset, f64 val);\n"
|
||||
"void pxl8_pack_f64_le(u8* buf, size_t offset, f64 val);\n"
|
||||
"void pxl8_pack_u8(u8* buf, usize offset, u8 val);\n"
|
||||
"void pxl8_pack_u16_be(u8* buf, usize offset, u16 val);\n"
|
||||
"void pxl8_pack_u16_le(u8* buf, usize offset, u16 val);\n"
|
||||
"void pxl8_pack_u32_be(u8* buf, usize offset, u32 val);\n"
|
||||
"void pxl8_pack_u32_le(u8* buf, usize offset, u32 val);\n"
|
||||
"void pxl8_pack_u64_be(u8* buf, usize offset, u64 val);\n"
|
||||
"void pxl8_pack_u64_le(u8* buf, usize offset, u64 val);\n"
|
||||
"void pxl8_pack_i8(u8* buf, usize offset, i8 val);\n"
|
||||
"void pxl8_pack_i16_be(u8* buf, usize offset, i16 val);\n"
|
||||
"void pxl8_pack_i16_le(u8* buf, usize offset, i16 val);\n"
|
||||
"void pxl8_pack_i32_be(u8* buf, usize offset, i32 val);\n"
|
||||
"void pxl8_pack_i32_le(u8* buf, usize offset, i32 val);\n"
|
||||
"void pxl8_pack_i64_be(u8* buf, usize offset, i64 val);\n"
|
||||
"void pxl8_pack_i64_le(u8* buf, usize offset, i64 val);\n"
|
||||
"void pxl8_pack_f32_be(u8* buf, usize offset, f32 val);\n"
|
||||
"void pxl8_pack_f32_le(u8* buf, usize offset, f32 val);\n"
|
||||
"void pxl8_pack_f64_be(u8* buf, usize offset, f64 val);\n"
|
||||
"void pxl8_pack_f64_le(u8* buf, usize offset, f64 val);\n"
|
||||
"\n"
|
||||
"u8 pxl8_unpack_u8(const u8* buf, size_t offset);\n"
|
||||
"u16 pxl8_unpack_u16_be(const u8* buf, size_t offset);\n"
|
||||
"u16 pxl8_unpack_u16_le(const u8* buf, size_t offset);\n"
|
||||
"u32 pxl8_unpack_u32_be(const u8* buf, size_t offset);\n"
|
||||
"u32 pxl8_unpack_u32_le(const u8* buf, size_t offset);\n"
|
||||
"u64 pxl8_unpack_u64_be(const u8* buf, size_t offset);\n"
|
||||
"u64 pxl8_unpack_u64_le(const u8* buf, size_t offset);\n"
|
||||
"i8 pxl8_unpack_i8(const u8* buf, size_t offset);\n"
|
||||
"i16 pxl8_unpack_i16_be(const u8* buf, size_t offset);\n"
|
||||
"i16 pxl8_unpack_i16_le(const u8* buf, size_t offset);\n"
|
||||
"i32 pxl8_unpack_i32_be(const u8* buf, size_t offset);\n"
|
||||
"i32 pxl8_unpack_i32_le(const u8* buf, size_t offset);\n"
|
||||
"i64 pxl8_unpack_i64_be(const u8* buf, size_t offset);\n"
|
||||
"i64 pxl8_unpack_i64_le(const u8* buf, size_t offset);\n"
|
||||
"f32 pxl8_unpack_f32_be(const u8* buf, size_t offset);\n"
|
||||
"f32 pxl8_unpack_f32_le(const u8* buf, size_t offset);\n"
|
||||
"f64 pxl8_unpack_f64_be(const u8* buf, size_t offset);\n"
|
||||
"f64 pxl8_unpack_f64_le(const u8* buf, size_t offset);\n";
|
||||
"u8 pxl8_unpack_u8(const u8* buf, usize offset);\n"
|
||||
"u16 pxl8_unpack_u16_be(const u8* buf, usize offset);\n"
|
||||
"u16 pxl8_unpack_u16_le(const u8* buf, usize offset);\n"
|
||||
"u32 pxl8_unpack_u32_be(const u8* buf, usize offset);\n"
|
||||
"u32 pxl8_unpack_u32_le(const u8* buf, usize offset);\n"
|
||||
"u64 pxl8_unpack_u64_be(const u8* buf, usize offset);\n"
|
||||
"u64 pxl8_unpack_u64_le(const u8* buf, usize offset);\n"
|
||||
"i8 pxl8_unpack_i8(const u8* buf, usize offset);\n"
|
||||
"i16 pxl8_unpack_i16_be(const u8* buf, usize offset);\n"
|
||||
"i16 pxl8_unpack_i16_le(const u8* buf, usize offset);\n"
|
||||
"i32 pxl8_unpack_i32_be(const u8* buf, usize offset);\n"
|
||||
"i32 pxl8_unpack_i32_le(const u8* buf, usize offset);\n"
|
||||
"i64 pxl8_unpack_i64_be(const u8* buf, usize offset);\n"
|
||||
"i64 pxl8_unpack_i64_le(const u8* buf, usize offset);\n"
|
||||
"f32 pxl8_unpack_f32_be(const u8* buf, usize offset);\n"
|
||||
"f32 pxl8_unpack_f32_le(const u8* buf, usize offset);\n"
|
||||
"f64 pxl8_unpack_f64_be(const u8* buf, usize offset);\n"
|
||||
"f64 pxl8_unpack_f64_le(const u8* buf, usize offset);\n";
|
||||
|
|
|
|||
|
|
@ -6,9 +6,15 @@
|
|||
#include "pxl8_hal.h"
|
||||
#include "pxl8_log.h"
|
||||
#include "pxl8_math.h"
|
||||
#include "pxl8_mem.h"
|
||||
|
||||
#define VOICE_SCALE 0.15f
|
||||
|
||||
static inline f32 sanitize_audio(f32 x) {
|
||||
union { f32 f; u32 u; } conv = { .f = x };
|
||||
return ((conv.u & 0x7F800000) == 0x7F800000) ? 0.0f : x;
|
||||
}
|
||||
|
||||
typedef enum envelope_state {
|
||||
ENV_ATTACK = 0,
|
||||
ENV_DECAY,
|
||||
|
|
@ -461,14 +467,14 @@ static void delay_process_stereo(void* state, f32* left, f32* right) {
|
|||
static void delay_destroy_state(void* state) {
|
||||
delay_state* d = (delay_state*)state;
|
||||
if (d) {
|
||||
free(d->buffer_l);
|
||||
free(d->buffer_r);
|
||||
free(d);
|
||||
pxl8_free(d->buffer_l);
|
||||
pxl8_free(d->buffer_r);
|
||||
pxl8_free(d);
|
||||
}
|
||||
}
|
||||
|
||||
static void comb_init(comb_filter* c, u32 size, f32 feedback, f32 damping) {
|
||||
c->buffer = (f32*)calloc(size, sizeof(f32));
|
||||
c->buffer = (f32*)pxl8_calloc(size, sizeof(f32));
|
||||
c->size = size;
|
||||
c->feedback = feedback;
|
||||
c->damping = damping;
|
||||
|
|
@ -477,7 +483,7 @@ static void comb_init(comb_filter* c, u32 size, f32 feedback, f32 damping) {
|
|||
}
|
||||
|
||||
static void comb_destroy(comb_filter* c) {
|
||||
free(c->buffer);
|
||||
pxl8_free(c->buffer);
|
||||
}
|
||||
|
||||
static f32 comb_process(comb_filter* c, f32 input) {
|
||||
|
|
@ -492,14 +498,14 @@ static f32 comb_process(comb_filter* c, f32 input) {
|
|||
}
|
||||
|
||||
static void allpass_init(allpass_filter* a, u32 size) {
|
||||
a->buffer = (f32*)calloc(size, sizeof(f32));
|
||||
a->buffer = (f32*)pxl8_calloc(size, sizeof(f32));
|
||||
a->size = size;
|
||||
a->feedback = 0.5f;
|
||||
a->index = 0;
|
||||
}
|
||||
|
||||
static void allpass_destroy(allpass_filter* a) {
|
||||
free(a->buffer);
|
||||
pxl8_free(a->buffer);
|
||||
}
|
||||
|
||||
static f32 allpass_process(allpass_filter* a, f32 input) {
|
||||
|
|
@ -555,7 +561,7 @@ static void reverb_destroy_state(void* state) {
|
|||
if (r) {
|
||||
for (int i = 0; i < 4; i++) comb_destroy(&r->combs[i]);
|
||||
for (int i = 0; i < 2; i++) allpass_destroy(&r->allpasses[i]);
|
||||
free(r);
|
||||
pxl8_free(r);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -601,7 +607,7 @@ static void compressor_process_stereo(void* state, f32* left, f32* right) {
|
|||
}
|
||||
|
||||
static void compressor_destroy_state(void* state) {
|
||||
free(state);
|
||||
pxl8_free(state);
|
||||
}
|
||||
|
||||
static void context_process_sample(pxl8_sfx_context* ctx, f32* out_left, f32* out_right) {
|
||||
|
|
@ -649,12 +655,12 @@ static void context_process_sample(pxl8_sfx_context* ctx, f32* out_left, f32* ou
|
|||
pxl8_sfx_mixer* pxl8_sfx_mixer_create(const pxl8_hal* hal) {
|
||||
if (!hal || !hal->audio_create) return NULL;
|
||||
|
||||
pxl8_sfx_mixer* mixer = (pxl8_sfx_mixer*)calloc(1, sizeof(pxl8_sfx_mixer));
|
||||
pxl8_sfx_mixer* mixer = (pxl8_sfx_mixer*)pxl8_calloc(1, sizeof(pxl8_sfx_mixer));
|
||||
if (!mixer) return NULL;
|
||||
|
||||
mixer->output_buffer = (f32*)calloc(PXL8_SFX_BUFFER_SIZE * 2, sizeof(f32));
|
||||
mixer->output_buffer = (f32*)pxl8_calloc(PXL8_SFX_BUFFER_SIZE * 2, sizeof(f32));
|
||||
if (!mixer->output_buffer) {
|
||||
free(mixer);
|
||||
pxl8_free(mixer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -663,8 +669,8 @@ pxl8_sfx_mixer* pxl8_sfx_mixer_create(const pxl8_hal* hal) {
|
|||
|
||||
mixer->audio_handle = hal->audio_create(PXL8_SFX_SAMPLE_RATE, 2);
|
||||
if (!mixer->audio_handle) {
|
||||
free(mixer->output_buffer);
|
||||
free(mixer);
|
||||
pxl8_free(mixer->output_buffer);
|
||||
pxl8_free(mixer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -681,8 +687,8 @@ void pxl8_sfx_mixer_destroy(pxl8_sfx_mixer* mixer) {
|
|||
mixer->hal->audio_destroy(mixer->audio_handle);
|
||||
}
|
||||
|
||||
free(mixer->output_buffer);
|
||||
free(mixer);
|
||||
pxl8_free(mixer->output_buffer);
|
||||
pxl8_free(mixer);
|
||||
}
|
||||
|
||||
void pxl8_sfx_mixer_process(pxl8_sfx_mixer* mixer) {
|
||||
|
|
@ -716,8 +722,8 @@ void pxl8_sfx_mixer_process(pxl8_sfx_mixer* mixer) {
|
|||
left = fmaxf(-1.0f, fminf(1.0f, left));
|
||||
right = fmaxf(-1.0f, fminf(1.0f, right));
|
||||
|
||||
if (!isfinite(left)) left = 0.0f;
|
||||
if (!isfinite(right)) right = 0.0f;
|
||||
left = sanitize_audio(left);
|
||||
right = sanitize_audio(right);
|
||||
|
||||
mixer->output_buffer[i * 2] = left;
|
||||
mixer->output_buffer[i * 2 + 1] = right;
|
||||
|
|
@ -785,7 +791,7 @@ f32 pxl8_sfx_mixer_get_master_volume(const pxl8_sfx_mixer* mixer) {
|
|||
}
|
||||
|
||||
pxl8_sfx_context* pxl8_sfx_context_create(void) {
|
||||
pxl8_sfx_context* ctx = (pxl8_sfx_context*)calloc(1, sizeof(pxl8_sfx_context));
|
||||
pxl8_sfx_context* ctx = (pxl8_sfx_context*)pxl8_calloc(1, sizeof(pxl8_sfx_context));
|
||||
if (!ctx) return NULL;
|
||||
|
||||
ctx->volume = 1.0f;
|
||||
|
|
@ -804,7 +810,7 @@ void pxl8_sfx_context_destroy(pxl8_sfx_context* ctx) {
|
|||
node = next;
|
||||
}
|
||||
|
||||
free(ctx);
|
||||
pxl8_free(ctx);
|
||||
}
|
||||
|
||||
void pxl8_sfx_context_set_volume(pxl8_sfx_context* ctx, f32 volume) {
|
||||
|
|
@ -871,26 +877,26 @@ void pxl8_sfx_node_destroy(pxl8_sfx_node* node) {
|
|||
if (node->destroy && node->state) {
|
||||
node->destroy(node->state);
|
||||
}
|
||||
free(node);
|
||||
pxl8_free(node);
|
||||
}
|
||||
|
||||
pxl8_sfx_node* pxl8_sfx_delay_create(pxl8_sfx_delay_config cfg) {
|
||||
pxl8_sfx_node* node = (pxl8_sfx_node*)calloc(1, sizeof(pxl8_sfx_node));
|
||||
pxl8_sfx_node* node = (pxl8_sfx_node*)pxl8_calloc(1, sizeof(pxl8_sfx_node));
|
||||
if (!node) return NULL;
|
||||
|
||||
delay_state* d = (delay_state*)calloc(1, sizeof(delay_state));
|
||||
delay_state* d = (delay_state*)pxl8_calloc(1, sizeof(delay_state));
|
||||
if (!d) {
|
||||
free(node);
|
||||
pxl8_free(node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
d->buffer_l = (f32*)calloc(PXL8_SFX_MAX_DELAY_SAMPLES, sizeof(f32));
|
||||
d->buffer_r = (f32*)calloc(PXL8_SFX_MAX_DELAY_SAMPLES, sizeof(f32));
|
||||
d->buffer_l = (f32*)pxl8_calloc(PXL8_SFX_MAX_DELAY_SAMPLES, sizeof(f32));
|
||||
d->buffer_r = (f32*)pxl8_calloc(PXL8_SFX_MAX_DELAY_SAMPLES, sizeof(f32));
|
||||
if (!d->buffer_l || !d->buffer_r) {
|
||||
free(d->buffer_l);
|
||||
free(d->buffer_r);
|
||||
free(d);
|
||||
free(node);
|
||||
pxl8_free(d->buffer_l);
|
||||
pxl8_free(d->buffer_r);
|
||||
pxl8_free(d);
|
||||
pxl8_free(node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -933,12 +939,12 @@ void pxl8_sfx_delay_set_mix(pxl8_sfx_node* node, f32 mix) {
|
|||
}
|
||||
|
||||
pxl8_sfx_node* pxl8_sfx_reverb_create(pxl8_sfx_reverb_config cfg) {
|
||||
pxl8_sfx_node* node = (pxl8_sfx_node*)calloc(1, sizeof(pxl8_sfx_node));
|
||||
pxl8_sfx_node* node = (pxl8_sfx_node*)pxl8_calloc(1, sizeof(pxl8_sfx_node));
|
||||
if (!node) return NULL;
|
||||
|
||||
reverb_state* r = (reverb_state*)calloc(1, sizeof(reverb_state));
|
||||
reverb_state* r = (reverb_state*)pxl8_calloc(1, sizeof(reverb_state));
|
||||
if (!r) {
|
||||
free(node);
|
||||
pxl8_free(node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -991,12 +997,12 @@ void pxl8_sfx_reverb_set_mix(pxl8_sfx_node* node, f32 mix) {
|
|||
}
|
||||
|
||||
pxl8_sfx_node* pxl8_sfx_compressor_create(pxl8_sfx_compressor_config cfg) {
|
||||
pxl8_sfx_node* node = (pxl8_sfx_node*)calloc(1, sizeof(pxl8_sfx_node));
|
||||
pxl8_sfx_node* node = (pxl8_sfx_node*)pxl8_calloc(1, sizeof(pxl8_sfx_node));
|
||||
if (!node) return NULL;
|
||||
|
||||
compressor_state* c = (compressor_state*)calloc(1, sizeof(compressor_state));
|
||||
compressor_state* c = (compressor_state*)pxl8_calloc(1, sizeof(compressor_state));
|
||||
if (!c) {
|
||||
free(node);
|
||||
pxl8_free(node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include "pxl8_gfx.h"
|
||||
#include "pxl8_io.h"
|
||||
#include "pxl8_log.h"
|
||||
#include "pxl8_mem.h"
|
||||
|
||||
#define BSP_VERSION 29
|
||||
|
||||
|
|
@ -40,15 +41,23 @@ typedef struct {
|
|||
pxl8_bsp_chunk chunks[CHUNK_COUNT];
|
||||
} pxl8_bsp_header;
|
||||
|
||||
typedef struct {
|
||||
f32 x0, y0, x1, y1;
|
||||
} screen_rect;
|
||||
|
||||
typedef struct {
|
||||
u32 leaf;
|
||||
screen_rect window;
|
||||
} portal_queue_entry;
|
||||
|
||||
static inline pxl8_vec3 read_vec3(pxl8_stream* stream) {
|
||||
pxl8_vec3 v;
|
||||
v.x = pxl8_read_f32(stream);
|
||||
v.y = pxl8_read_f32(stream);
|
||||
v.z = pxl8_read_f32(stream);
|
||||
return v;
|
||||
f32 x = pxl8_read_f32(stream);
|
||||
f32 y = pxl8_read_f32(stream);
|
||||
f32 z = pxl8_read_f32(stream);
|
||||
return (pxl8_vec3){x, z, y};
|
||||
}
|
||||
|
||||
static bool validate_chunk(const pxl8_bsp_chunk* chunk, u32 element_size, size_t file_size) {
|
||||
static bool validate_chunk(const pxl8_bsp_chunk* chunk, u32 element_size, usize file_size) {
|
||||
if (chunk->size == 0) return true;
|
||||
if (chunk->offset >= file_size) return false;
|
||||
if (chunk->offset + chunk->size > file_size) return false;
|
||||
|
|
@ -75,32 +84,13 @@ static inline bool pxl8_bsp_get_edge_vertex(const pxl8_bsp* bsp, i32 surfedge_id
|
|||
return *out_vert_idx < bsp->num_vertices;
|
||||
}
|
||||
|
||||
static inline bool pxl8_bsp_get_edge_vertices(const pxl8_bsp* bsp, i32 surfedge_idx, u32* out_v0_idx, u32* out_v1_idx) {
|
||||
if (surfedge_idx >= (i32)bsp->num_surfedges) return false;
|
||||
|
||||
i32 edge_idx = bsp->surfedges[surfedge_idx];
|
||||
|
||||
if (edge_idx >= 0) {
|
||||
if ((u32)edge_idx >= bsp->num_edges) return false;
|
||||
*out_v0_idx = bsp->edges[edge_idx].vertex[0];
|
||||
*out_v1_idx = bsp->edges[edge_idx].vertex[1];
|
||||
} else {
|
||||
edge_idx = -edge_idx;
|
||||
if ((u32)edge_idx >= bsp->num_edges) return false;
|
||||
*out_v0_idx = bsp->edges[edge_idx].vertex[1];
|
||||
*out_v1_idx = bsp->edges[edge_idx].vertex[0];
|
||||
}
|
||||
|
||||
return *out_v0_idx < bsp->num_vertices && *out_v1_idx < bsp->num_vertices;
|
||||
}
|
||||
|
||||
pxl8_result pxl8_bsp_load(const char* path, pxl8_bsp* bsp) {
|
||||
if (!path || !bsp) return PXL8_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
memset(bsp, 0, sizeof(*bsp));
|
||||
|
||||
u8* file_data = NULL;
|
||||
size_t file_size = 0;
|
||||
usize file_size = 0;
|
||||
pxl8_result result = pxl8_io_read_binary_file(path, &file_data, &file_size);
|
||||
if (result != PXL8_OK) {
|
||||
pxl8_error("Failed to load BSP file: %s", path);
|
||||
|
|
@ -109,7 +99,7 @@ pxl8_result pxl8_bsp_load(const char* path, pxl8_bsp* bsp) {
|
|||
|
||||
if (file_size < sizeof(pxl8_bsp_header)) {
|
||||
pxl8_error("BSP file too small: %s", path);
|
||||
free(file_data);
|
||||
pxl8_free(file_data);
|
||||
return PXL8_ERROR_INVALID_FORMAT;
|
||||
}
|
||||
|
||||
|
|
@ -120,7 +110,7 @@ pxl8_result pxl8_bsp_load(const char* path, pxl8_bsp* bsp) {
|
|||
|
||||
if (header.version != BSP_VERSION) {
|
||||
pxl8_error("Invalid BSP version: %u (expected %d)", header.version, BSP_VERSION);
|
||||
free(file_data);
|
||||
pxl8_free(file_data);
|
||||
return PXL8_ERROR_INVALID_FORMAT;
|
||||
}
|
||||
|
||||
|
|
@ -133,7 +123,7 @@ pxl8_result pxl8_bsp_load(const char* path, pxl8_bsp* bsp) {
|
|||
if (!validate_chunk(chunk, 12, file_size)) goto error_cleanup;
|
||||
bsp->num_vertices = chunk->size / 12;
|
||||
if (bsp->num_vertices > 0) {
|
||||
bsp->vertices = calloc(bsp->num_vertices, sizeof(pxl8_bsp_vertex));
|
||||
bsp->vertices = pxl8_calloc(bsp->num_vertices, sizeof(pxl8_bsp_vertex));
|
||||
if (!bsp->vertices) goto error_cleanup;
|
||||
pxl8_stream_seek(&stream, chunk->offset);
|
||||
for (u32 i = 0; i < bsp->num_vertices; i++) {
|
||||
|
|
@ -145,7 +135,7 @@ pxl8_result pxl8_bsp_load(const char* path, pxl8_bsp* bsp) {
|
|||
if (!validate_chunk(chunk, 4, file_size)) goto error_cleanup;
|
||||
bsp->num_edges = chunk->size / 4;
|
||||
if (bsp->num_edges > 0) {
|
||||
bsp->edges = calloc(bsp->num_edges, sizeof(pxl8_bsp_edge));
|
||||
bsp->edges = pxl8_calloc(bsp->num_edges, sizeof(pxl8_bsp_edge));
|
||||
pxl8_stream_seek(&stream, chunk->offset);
|
||||
for (u32 i = 0; i < bsp->num_edges; i++) {
|
||||
bsp->edges[i].vertex[0] = pxl8_read_u16(&stream);
|
||||
|
|
@ -157,7 +147,7 @@ pxl8_result pxl8_bsp_load(const char* path, pxl8_bsp* bsp) {
|
|||
if (!validate_chunk(chunk, 4, file_size)) goto error_cleanup;
|
||||
bsp->num_surfedges = chunk->size / 4;
|
||||
if (bsp->num_surfedges > 0) {
|
||||
bsp->surfedges = calloc(bsp->num_surfedges, sizeof(i32));
|
||||
bsp->surfedges = pxl8_calloc(bsp->num_surfedges, sizeof(i32));
|
||||
pxl8_stream_seek(&stream, chunk->offset);
|
||||
for (u32 i = 0; i < bsp->num_surfedges; i++) {
|
||||
bsp->surfedges[i] = pxl8_read_i32(&stream);
|
||||
|
|
@ -168,7 +158,7 @@ pxl8_result pxl8_bsp_load(const char* path, pxl8_bsp* bsp) {
|
|||
if (!validate_chunk(chunk, 20, file_size)) goto error_cleanup;
|
||||
bsp->num_planes = chunk->size / 20;
|
||||
if (bsp->num_planes > 0) {
|
||||
bsp->planes = calloc(bsp->num_planes, sizeof(pxl8_bsp_plane));
|
||||
bsp->planes = pxl8_calloc(bsp->num_planes, sizeof(pxl8_bsp_plane));
|
||||
pxl8_stream_seek(&stream, chunk->offset);
|
||||
for (u32 i = 0; i < bsp->num_planes; i++) {
|
||||
bsp->planes[i].normal = read_vec3(&stream);
|
||||
|
|
@ -179,16 +169,20 @@ pxl8_result pxl8_bsp_load(const char* path, pxl8_bsp* bsp) {
|
|||
|
||||
chunk = &header.chunks[CHUNK_TEXINFO];
|
||||
if (!validate_chunk(chunk, 40, file_size)) goto error_cleanup;
|
||||
bsp->num_texinfo = chunk->size / 40;
|
||||
if (bsp->num_texinfo > 0) {
|
||||
bsp->texinfo = calloc(bsp->num_texinfo, sizeof(pxl8_bsp_texinfo));
|
||||
bsp->num_materials = chunk->size / 40;
|
||||
if (bsp->num_materials > 0) {
|
||||
bsp->materials = pxl8_calloc(bsp->num_materials, sizeof(pxl8_gfx_material));
|
||||
pxl8_stream_seek(&stream, chunk->offset);
|
||||
for (u32 i = 0; i < bsp->num_texinfo; i++) {
|
||||
bsp->texinfo[i].u_axis = read_vec3(&stream);
|
||||
bsp->texinfo[i].u_offset = pxl8_read_f32(&stream);
|
||||
bsp->texinfo[i].v_axis = read_vec3(&stream);
|
||||
bsp->texinfo[i].v_offset = pxl8_read_f32(&stream);
|
||||
bsp->texinfo[i].miptex = pxl8_read_u32(&stream);
|
||||
for (u32 i = 0; i < bsp->num_materials; i++) {
|
||||
bsp->materials[i].u_axis = read_vec3(&stream);
|
||||
bsp->materials[i].u_offset = pxl8_read_f32(&stream);
|
||||
bsp->materials[i].v_axis = read_vec3(&stream);
|
||||
bsp->materials[i].v_offset = pxl8_read_f32(&stream);
|
||||
bsp->materials[i].texture_id = pxl8_read_u32(&stream);
|
||||
bsp->materials[i].alpha = 255;
|
||||
bsp->materials[i].dither = true;
|
||||
bsp->materials[i].dynamic_lighting = true;
|
||||
bsp->materials[i].double_sided = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -196,14 +190,14 @@ pxl8_result pxl8_bsp_load(const char* path, pxl8_bsp* bsp) {
|
|||
if (!validate_chunk(chunk, 20, file_size)) goto error_cleanup;
|
||||
bsp->num_faces = chunk->size / 20;
|
||||
if (bsp->num_faces > 0) {
|
||||
bsp->faces = calloc(bsp->num_faces, sizeof(pxl8_bsp_face));
|
||||
bsp->faces = pxl8_calloc(bsp->num_faces, sizeof(pxl8_bsp_face));
|
||||
pxl8_stream_seek(&stream, chunk->offset);
|
||||
for (u32 i = 0; i < bsp->num_faces; i++) {
|
||||
bsp->faces[i].plane_id = pxl8_read_u16(&stream);
|
||||
bsp->faces[i].side = pxl8_read_u16(&stream);
|
||||
bsp->faces[i].first_edge = pxl8_read_u32(&stream);
|
||||
bsp->faces[i].num_edges = pxl8_read_u16(&stream);
|
||||
bsp->faces[i].texinfo_id = pxl8_read_u16(&stream);
|
||||
bsp->faces[i].material_id = pxl8_read_u16(&stream);
|
||||
bsp->faces[i].styles[0] = pxl8_read_u8(&stream);
|
||||
bsp->faces[i].styles[1] = pxl8_read_u8(&stream);
|
||||
bsp->faces[i].styles[2] = pxl8_read_u8(&stream);
|
||||
|
|
@ -219,14 +213,24 @@ pxl8_result pxl8_bsp_load(const char* path, pxl8_bsp* bsp) {
|
|||
if (!validate_chunk(chunk, 24, file_size)) goto error_cleanup;
|
||||
bsp->num_nodes = chunk->size / 24;
|
||||
if (bsp->num_nodes > 0) {
|
||||
bsp->nodes = calloc(bsp->num_nodes, sizeof(pxl8_bsp_node));
|
||||
bsp->nodes = pxl8_calloc(bsp->num_nodes, sizeof(pxl8_bsp_node));
|
||||
pxl8_stream_seek(&stream, chunk->offset);
|
||||
for (u32 i = 0; i < bsp->num_nodes; i++) {
|
||||
bsp->nodes[i].plane_id = pxl8_read_u32(&stream);
|
||||
bsp->nodes[i].children[0] = pxl8_read_i16(&stream);
|
||||
bsp->nodes[i].children[1] = pxl8_read_i16(&stream);
|
||||
for (u32 j = 0; j < 3; j++) bsp->nodes[i].mins[j] = pxl8_read_i16(&stream);
|
||||
for (u32 j = 0; j < 3; j++) bsp->nodes[i].maxs[j] = pxl8_read_i16(&stream);
|
||||
i16 nx = pxl8_read_i16(&stream);
|
||||
i16 ny = pxl8_read_i16(&stream);
|
||||
i16 nz = pxl8_read_i16(&stream);
|
||||
bsp->nodes[i].mins[0] = nx;
|
||||
bsp->nodes[i].mins[1] = nz;
|
||||
bsp->nodes[i].mins[2] = ny;
|
||||
i16 mx = pxl8_read_i16(&stream);
|
||||
i16 my = pxl8_read_i16(&stream);
|
||||
i16 mz = pxl8_read_i16(&stream);
|
||||
bsp->nodes[i].maxs[0] = mx;
|
||||
bsp->nodes[i].maxs[1] = mz;
|
||||
bsp->nodes[i].maxs[2] = my;
|
||||
bsp->nodes[i].first_face = pxl8_read_u16(&stream);
|
||||
bsp->nodes[i].num_faces = pxl8_read_u16(&stream);
|
||||
}
|
||||
|
|
@ -236,13 +240,23 @@ pxl8_result pxl8_bsp_load(const char* path, pxl8_bsp* bsp) {
|
|||
if (!validate_chunk(chunk, 28, file_size)) goto error_cleanup;
|
||||
bsp->num_leafs = chunk->size / 28;
|
||||
if (bsp->num_leafs > 0) {
|
||||
bsp->leafs = calloc(bsp->num_leafs, sizeof(pxl8_bsp_leaf));
|
||||
bsp->leafs = pxl8_calloc(bsp->num_leafs, sizeof(pxl8_bsp_leaf));
|
||||
pxl8_stream_seek(&stream, chunk->offset);
|
||||
for (u32 i = 0; i < bsp->num_leafs; i++) {
|
||||
bsp->leafs[i].contents = pxl8_read_i32(&stream);
|
||||
bsp->leafs[i].visofs = pxl8_read_i32(&stream);
|
||||
for (u32 j = 0; j < 3; j++) bsp->leafs[i].mins[j] = pxl8_read_i16(&stream);
|
||||
for (u32 j = 0; j < 3; j++) bsp->leafs[i].maxs[j] = pxl8_read_i16(&stream);
|
||||
i16 nx = pxl8_read_i16(&stream);
|
||||
i16 ny = pxl8_read_i16(&stream);
|
||||
i16 nz = pxl8_read_i16(&stream);
|
||||
bsp->leafs[i].mins[0] = nx;
|
||||
bsp->leafs[i].mins[1] = nz;
|
||||
bsp->leafs[i].mins[2] = ny;
|
||||
i16 mx = pxl8_read_i16(&stream);
|
||||
i16 my = pxl8_read_i16(&stream);
|
||||
i16 mz = pxl8_read_i16(&stream);
|
||||
bsp->leafs[i].maxs[0] = mx;
|
||||
bsp->leafs[i].maxs[1] = mz;
|
||||
bsp->leafs[i].maxs[2] = my;
|
||||
bsp->leafs[i].first_marksurface = pxl8_read_u16(&stream);
|
||||
bsp->leafs[i].num_marksurfaces = pxl8_read_u16(&stream);
|
||||
for (u32 j = 0; j < 4; j++) bsp->leafs[i].ambient_level[j] = pxl8_read_u8(&stream);
|
||||
|
|
@ -253,7 +267,7 @@ pxl8_result pxl8_bsp_load(const char* path, pxl8_bsp* bsp) {
|
|||
if (!validate_chunk(chunk, 2, file_size)) goto error_cleanup;
|
||||
bsp->num_marksurfaces = chunk->size / 2;
|
||||
if (bsp->num_marksurfaces > 0) {
|
||||
bsp->marksurfaces = calloc(bsp->num_marksurfaces, sizeof(u16));
|
||||
bsp->marksurfaces = pxl8_calloc(bsp->num_marksurfaces, sizeof(u16));
|
||||
pxl8_stream_seek(&stream, chunk->offset);
|
||||
for (u32 i = 0; i < bsp->num_marksurfaces; i++) {
|
||||
bsp->marksurfaces[i] = pxl8_read_u16(&stream);
|
||||
|
|
@ -264,11 +278,21 @@ pxl8_result pxl8_bsp_load(const char* path, pxl8_bsp* bsp) {
|
|||
if (!validate_chunk(chunk, 64, file_size)) goto error_cleanup;
|
||||
bsp->num_models = chunk->size / 64;
|
||||
if (bsp->num_models > 0) {
|
||||
bsp->models = calloc(bsp->num_models, sizeof(pxl8_bsp_model));
|
||||
bsp->models = pxl8_calloc(bsp->num_models, sizeof(pxl8_bsp_model));
|
||||
pxl8_stream_seek(&stream, chunk->offset);
|
||||
for (u32 i = 0; i < bsp->num_models; i++) {
|
||||
for (u32 j = 0; j < 3; j++) bsp->models[i].mins[j] = pxl8_read_f32(&stream);
|
||||
for (u32 j = 0; j < 3; j++) bsp->models[i].maxs[j] = pxl8_read_f32(&stream);
|
||||
f32 minx = pxl8_read_f32(&stream);
|
||||
f32 miny = pxl8_read_f32(&stream);
|
||||
f32 minz = pxl8_read_f32(&stream);
|
||||
bsp->models[i].mins[0] = minx;
|
||||
bsp->models[i].mins[1] = minz;
|
||||
bsp->models[i].mins[2] = miny;
|
||||
f32 maxx = pxl8_read_f32(&stream);
|
||||
f32 maxy = pxl8_read_f32(&stream);
|
||||
f32 maxz = pxl8_read_f32(&stream);
|
||||
bsp->models[i].maxs[0] = maxx;
|
||||
bsp->models[i].maxs[1] = maxz;
|
||||
bsp->models[i].maxs[2] = maxy;
|
||||
bsp->models[i].origin = read_vec3(&stream);
|
||||
for (u32 j = 0; j < 4; j++) bsp->models[i].headnode[j] = pxl8_read_i32(&stream);
|
||||
bsp->models[i].visleafs = pxl8_read_i32(&stream);
|
||||
|
|
@ -281,7 +305,7 @@ pxl8_result pxl8_bsp_load(const char* path, pxl8_bsp* bsp) {
|
|||
if (!validate_chunk(chunk, 1, file_size)) goto error_cleanup;
|
||||
bsp->visdata_size = chunk->size;
|
||||
if (bsp->visdata_size > 0) {
|
||||
bsp->visdata = malloc(bsp->visdata_size);
|
||||
bsp->visdata = pxl8_malloc(bsp->visdata_size);
|
||||
memcpy(bsp->visdata, file_data + chunk->offset, bsp->visdata_size);
|
||||
}
|
||||
|
||||
|
|
@ -289,11 +313,11 @@ pxl8_result pxl8_bsp_load(const char* path, pxl8_bsp* bsp) {
|
|||
if (!validate_chunk(chunk, 1, file_size)) goto error_cleanup;
|
||||
bsp->lightdata_size = chunk->size;
|
||||
if (bsp->lightdata_size > 0) {
|
||||
bsp->lightdata = malloc(bsp->lightdata_size);
|
||||
bsp->lightdata = pxl8_malloc(bsp->lightdata_size);
|
||||
memcpy(bsp->lightdata, file_data + chunk->offset, bsp->lightdata_size);
|
||||
}
|
||||
|
||||
free(file_data);
|
||||
pxl8_free(file_data);
|
||||
|
||||
for (u32 i = 0; i < bsp->num_faces; i++) {
|
||||
pxl8_bsp_face* face = &bsp->faces[i];
|
||||
|
|
@ -327,7 +351,7 @@ pxl8_result pxl8_bsp_load(const char* path, pxl8_bsp* bsp) {
|
|||
|
||||
error_cleanup:
|
||||
pxl8_error("BSP chunk validation failed: %s", path);
|
||||
free(file_data);
|
||||
pxl8_free(file_data);
|
||||
pxl8_bsp_destroy(bsp);
|
||||
return PXL8_ERROR_INVALID_FORMAT;
|
||||
}
|
||||
|
|
@ -335,18 +359,21 @@ error_cleanup:
|
|||
void pxl8_bsp_destroy(pxl8_bsp* bsp) {
|
||||
if (!bsp) return;
|
||||
|
||||
free(bsp->edges);
|
||||
free(bsp->faces);
|
||||
free(bsp->leafs);
|
||||
free(bsp->lightdata);
|
||||
free(bsp->marksurfaces);
|
||||
free(bsp->models);
|
||||
free(bsp->nodes);
|
||||
free(bsp->planes);
|
||||
free(bsp->surfedges);
|
||||
free(bsp->texinfo);
|
||||
free(bsp->vertices);
|
||||
free(bsp->visdata);
|
||||
pxl8_free(bsp->cell_portals);
|
||||
pxl8_free(bsp->edges);
|
||||
pxl8_free(bsp->faces);
|
||||
pxl8_free(bsp->leafs);
|
||||
pxl8_free(bsp->lightdata);
|
||||
pxl8_free(bsp->marksurfaces);
|
||||
pxl8_free(bsp->materials);
|
||||
pxl8_free(bsp->models);
|
||||
pxl8_free(bsp->nodes);
|
||||
pxl8_free(bsp->planes);
|
||||
pxl8_free(bsp->render_face_flags);
|
||||
pxl8_free(bsp->surfedges);
|
||||
pxl8_free(bsp->vertex_lights);
|
||||
pxl8_free(bsp->vertices);
|
||||
pxl8_free(bsp->visdata);
|
||||
|
||||
memset(bsp, 0, sizeof(*bsp));
|
||||
}
|
||||
|
|
@ -375,85 +402,88 @@ bool pxl8_bsp_is_leaf_visible(const pxl8_bsp* bsp, i32 leaf_from, i32 leaf_to) {
|
|||
i32 visofs = bsp->leafs[leaf_from].visofs;
|
||||
if (visofs < 0) return true;
|
||||
|
||||
u32 target_byte = leaf_to >> 3;
|
||||
u32 target_bit = leaf_to & 7;
|
||||
u32 pvs_size = (bsp->num_leafs + 7) / 8;
|
||||
u32 row_size = (bsp->num_leafs + 7) >> 3;
|
||||
u32 byte_idx = (u32)leaf_to >> 3;
|
||||
u32 bit_idx = (u32)leaf_to & 7;
|
||||
|
||||
u32 pos = (u32)visofs;
|
||||
u32 current_byte = 0;
|
||||
u8* vis = bsp->visdata + visofs;
|
||||
u8* vis_end = bsp->visdata + bsp->visdata_size;
|
||||
u32 out = 0;
|
||||
|
||||
while (current_byte < pvs_size && pos < bsp->visdata_size) {
|
||||
u8 b = bsp->visdata[pos++];
|
||||
|
||||
if (b != 0) {
|
||||
if (current_byte == target_byte) {
|
||||
return (b & (1 << target_bit)) != 0;
|
||||
while (out < row_size && vis < vis_end) {
|
||||
if (*vis) {
|
||||
if (out == byte_idx) {
|
||||
return (*vis & (1 << bit_idx)) != 0;
|
||||
}
|
||||
current_byte++;
|
||||
out++;
|
||||
vis++;
|
||||
} else {
|
||||
if (pos >= bsp->visdata_size) return false;
|
||||
u32 count = bsp->visdata[pos++];
|
||||
if (target_byte < current_byte + count) {
|
||||
vis++;
|
||||
if (vis >= vis_end) break;
|
||||
u32 count = *vis++;
|
||||
if (out + count > byte_idx && byte_idx >= out) {
|
||||
return false;
|
||||
}
|
||||
current_byte += count;
|
||||
out += count;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return out > byte_idx ? false : true;
|
||||
}
|
||||
|
||||
pxl8_bsp_pvs pxl8_bsp_decompress_pvs(const pxl8_bsp* bsp, i32 leaf) {
|
||||
pxl8_bsp_pvs pvs = {0};
|
||||
|
||||
u32 pvs_size = (bsp->num_leafs + 7) / 8;
|
||||
pvs.data = calloc(pvs_size, 1);
|
||||
pvs.size = pvs_size;
|
||||
u32 row = (bsp->num_leafs + 7) >> 3;
|
||||
pvs.data = pxl8_malloc(row);
|
||||
pvs.size = row;
|
||||
|
||||
if (!pvs.data) return pvs;
|
||||
|
||||
if (!bsp || leaf < 0 || (u32)leaf >= bsp->num_leafs) {
|
||||
memset(pvs.data, 0xFF, pvs_size);
|
||||
memset(pvs.data, 0xFF, row);
|
||||
return pvs;
|
||||
}
|
||||
|
||||
i32 visofs = bsp->leafs[leaf].visofs;
|
||||
if (visofs < 0 || !bsp->visdata || bsp->visdata_size == 0) {
|
||||
memset(pvs.data, 0xFF, pvs_size);
|
||||
memset(pvs.data, 0xFF, row);
|
||||
return pvs;
|
||||
}
|
||||
|
||||
u32 pos = (u32)visofs;
|
||||
u32 out = 0;
|
||||
u8* in = bsp->visdata + visofs;
|
||||
u8* out = pvs.data;
|
||||
u8* out_end = pvs.data + row;
|
||||
|
||||
while (out < pvs_size && pos < bsp->visdata_size) {
|
||||
u8 b = bsp->visdata[pos++];
|
||||
|
||||
if (b != 0) {
|
||||
pvs.data[out++] = b;
|
||||
do {
|
||||
if (*in) {
|
||||
*out++ = *in++;
|
||||
} else {
|
||||
if (pos >= bsp->visdata_size) break;
|
||||
u32 count = bsp->visdata[pos++];
|
||||
out += count;
|
||||
in++;
|
||||
i32 c = *in++;
|
||||
while (c > 0 && out < out_end) {
|
||||
*out++ = 0;
|
||||
c--;
|
||||
}
|
||||
}
|
||||
} while (out < out_end);
|
||||
|
||||
return pvs;
|
||||
}
|
||||
|
||||
void pxl8_bsp_pvs_destroy(pxl8_bsp_pvs* pvs) {
|
||||
if (pvs) {
|
||||
free(pvs->data);
|
||||
pxl8_free(pvs->data);
|
||||
pvs->data = NULL;
|
||||
pvs->size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool pxl8_bsp_pvs_is_visible(const pxl8_bsp_pvs* pvs, i32 leaf) {
|
||||
if (!pvs || !pvs->data || leaf < 0) return false;
|
||||
u32 byte_idx = leaf >> 3;
|
||||
u32 bit_idx = leaf & 7;
|
||||
if (byte_idx >= pvs->size) return false;
|
||||
if (!pvs || !pvs->data || leaf < 0) return true;
|
||||
u32 byte_idx = (u32)leaf >> 3;
|
||||
u32 bit_idx = (u32)leaf & 7;
|
||||
if (byte_idx >= pvs->size) return true;
|
||||
return (pvs->data[byte_idx] & (1 << bit_idx)) != 0;
|
||||
}
|
||||
|
||||
|
|
@ -546,6 +576,89 @@ static inline bool face_in_frustum(const pxl8_bsp* bsp, u32 face_id, const pxl8_
|
|||
return pxl8_frustum_test_aabb(frustum, face->aabb_min, face->aabb_max);
|
||||
}
|
||||
|
||||
static inline bool leaf_in_frustum(const pxl8_bsp_leaf* leaf, const pxl8_frustum* frustum) {
|
||||
pxl8_vec3 mins = {(f32)leaf->mins[0], (f32)leaf->mins[1], (f32)leaf->mins[2]};
|
||||
pxl8_vec3 maxs = {(f32)leaf->maxs[0], (f32)leaf->maxs[1], (f32)leaf->maxs[2]};
|
||||
return pxl8_frustum_test_aabb(frustum, mins, maxs);
|
||||
}
|
||||
|
||||
static inline bool screen_rect_valid(screen_rect r) {
|
||||
return r.x0 < r.x1 && r.y0 < r.y1;
|
||||
}
|
||||
|
||||
static inline screen_rect screen_rect_intersect(screen_rect a, screen_rect b) {
|
||||
return (screen_rect){
|
||||
.x0 = (a.x0 > b.x0) ? a.x0 : b.x0,
|
||||
.y0 = (a.y0 > b.y0) ? a.y0 : b.y0,
|
||||
.x1 = (a.x1 < b.x1) ? a.x1 : b.x1,
|
||||
.y1 = (a.y1 < b.y1) ? a.y1 : b.y1,
|
||||
};
|
||||
}
|
||||
|
||||
static inline void expand_rect_with_ndc(screen_rect* r, f32 nx, f32 ny) {
|
||||
if (nx < r->x0) r->x0 = nx;
|
||||
if (nx > r->x1) r->x1 = nx;
|
||||
if (ny < r->y0) r->y0 = ny;
|
||||
if (ny > r->y1) r->y1 = ny;
|
||||
}
|
||||
|
||||
static screen_rect project_portal_to_screen(const pxl8_bsp_portal* portal, const pxl8_mat4* vp, f32 wall_height) {
|
||||
pxl8_vec3 world_corners[4] = {
|
||||
{portal->x0, 0, portal->z0},
|
||||
{portal->x1, 0, portal->z1},
|
||||
{portal->x1, wall_height, portal->z1},
|
||||
{portal->x0, wall_height, portal->z0},
|
||||
};
|
||||
|
||||
pxl8_vec4 clip[4];
|
||||
bool in_front[4];
|
||||
i32 front_count = 0;
|
||||
|
||||
const f32 NEAR_W = 0.001f;
|
||||
|
||||
for (i32 i = 0; i < 4; i++) {
|
||||
clip[i] = pxl8_mat4_multiply_vec4(*vp, (pxl8_vec4){world_corners[i].x, world_corners[i].y, world_corners[i].z, 1.0f});
|
||||
in_front[i] = clip[i].w > NEAR_W;
|
||||
if (in_front[i]) front_count++;
|
||||
}
|
||||
|
||||
if (front_count == 0) return (screen_rect){0, 0, 0, 0};
|
||||
|
||||
screen_rect result = {1e30f, 1e30f, -1e30f, -1e30f};
|
||||
|
||||
for (i32 i = 0; i < 4; i++) {
|
||||
i32 j = (i + 1) % 4;
|
||||
pxl8_vec4 a = clip[i];
|
||||
pxl8_vec4 b = clip[j];
|
||||
bool a_in = in_front[i];
|
||||
bool b_in = in_front[j];
|
||||
|
||||
if (a_in) {
|
||||
f32 inv_w = 1.0f / a.w;
|
||||
expand_rect_with_ndc(&result, a.x * inv_w, a.y * inv_w);
|
||||
}
|
||||
|
||||
if (a_in != b_in) {
|
||||
f32 t = (NEAR_W - a.w) / (b.w - a.w);
|
||||
pxl8_vec4 intersection = {
|
||||
a.x + t * (b.x - a.x),
|
||||
a.y + t * (b.y - a.y),
|
||||
a.z + t * (b.z - a.z),
|
||||
NEAR_W
|
||||
};
|
||||
f32 inv_w = 1.0f / intersection.w;
|
||||
expand_rect_with_ndc(&result, intersection.x * inv_w, intersection.y * inv_w);
|
||||
}
|
||||
}
|
||||
|
||||
if (result.x0 < -1.0f) result.x0 = -1.0f;
|
||||
if (result.y0 < -1.0f) result.y0 = -1.0f;
|
||||
if (result.x1 > 1.0f) result.x1 = 1.0f;
|
||||
if (result.y1 > 1.0f) result.y1 = 1.0f;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void collect_face_to_mesh(
|
||||
const pxl8_bsp* bsp,
|
||||
u32 face_id,
|
||||
|
|
@ -564,17 +677,18 @@ static void collect_face_to_mesh(
|
|||
}
|
||||
}
|
||||
|
||||
const pxl8_bsp_texinfo* texinfo = NULL;
|
||||
const pxl8_gfx_material* material = NULL;
|
||||
f32 tex_scale = 64.0f;
|
||||
if (face->texinfo_id < bsp->num_texinfo) {
|
||||
texinfo = &bsp->texinfo[face->texinfo_id];
|
||||
if (face->material_id < bsp->num_materials) {
|
||||
material = &bsp->materials[face->material_id];
|
||||
}
|
||||
|
||||
u16 base_idx = (u16)mesh->vertex_count;
|
||||
u32 num_verts = 0;
|
||||
|
||||
for (u32 i = 0; i < face->num_edges && num_verts < 64; i++) {
|
||||
i32 surfedge_idx = face->first_edge + i;
|
||||
u32 edge_i = face->side ? (face->num_edges - 1 - i) : i;
|
||||
i32 surfedge_idx = face->first_edge + edge_i;
|
||||
u32 vert_idx;
|
||||
|
||||
if (!pxl8_bsp_get_edge_vertex(bsp, surfedge_idx, &vert_idx)) {
|
||||
|
|
@ -584,9 +698,9 @@ static void collect_face_to_mesh(
|
|||
pxl8_vec3 pos = bsp->vertices[vert_idx].position;
|
||||
|
||||
f32 u = 0.0f, v = 0.0f;
|
||||
if (texinfo) {
|
||||
u = (pxl8_vec3_dot(pos, texinfo->u_axis) + texinfo->u_offset) / tex_scale;
|
||||
v = (pxl8_vec3_dot(pos, texinfo->v_axis) + texinfo->v_offset) / tex_scale;
|
||||
if (material) {
|
||||
u = (pxl8_vec3_dot(pos, material->u_axis) + material->u_offset) / tex_scale;
|
||||
v = (pxl8_vec3_dot(pos, material->v_axis) + material->v_offset) / tex_scale;
|
||||
}
|
||||
|
||||
u8 light = 255;
|
||||
|
|
@ -613,8 +727,8 @@ static void collect_face_to_mesh(
|
|||
}
|
||||
}
|
||||
|
||||
void pxl8_bsp_render_face(pxl8_gfx* gfx, const pxl8_bsp* bsp, u32 face_id, u32 texture_id) {
|
||||
if (!gfx || !bsp || face_id >= bsp->num_faces) return;
|
||||
void pxl8_bsp_render_face(pxl8_gfx* gfx, const pxl8_bsp* bsp, u32 face_id, const pxl8_gfx_material* material) {
|
||||
if (!gfx || !bsp || face_id >= bsp->num_faces || !material) return;
|
||||
|
||||
pxl8_mesh* mesh = pxl8_mesh_create(64, 192);
|
||||
if (!mesh) return;
|
||||
|
|
@ -623,109 +737,115 @@ void pxl8_bsp_render_face(pxl8_gfx* gfx, const pxl8_bsp* bsp, u32 face_id, u32 t
|
|||
|
||||
if (mesh->index_count > 0) {
|
||||
pxl8_mat4 identity = pxl8_mat4_identity();
|
||||
pxl8_material mat = pxl8_material_create(texture_id);
|
||||
pxl8_3d_draw_mesh(gfx, mesh, &identity, &mat);
|
||||
pxl8_3d_draw_mesh(gfx, mesh, &identity, material);
|
||||
}
|
||||
|
||||
pxl8_mesh_destroy(mesh);
|
||||
}
|
||||
|
||||
void pxl8_bsp_render_textured(pxl8_gfx* gfx, const pxl8_bsp* bsp, pxl8_vec3 camera_pos) {
|
||||
static int call_count = 0;
|
||||
if (!gfx || !bsp || bsp->num_faces == 0) {
|
||||
if (call_count++ < 5) {
|
||||
pxl8_debug("bsp_render_textured: early return - gfx=%p, bsp=%p, num_faces=%u",
|
||||
(void*)gfx, (void*)bsp, bsp ? bsp->num_faces : 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
void pxl8_bsp_render(pxl8_gfx* gfx, const pxl8_bsp* bsp, pxl8_vec3 camera_pos) {
|
||||
if (!gfx || !bsp || bsp->num_faces == 0) return;
|
||||
if (!bsp->cell_portals || bsp->num_cell_portals == 0) return;
|
||||
if (!bsp->materials || bsp->num_materials == 0) return;
|
||||
|
||||
const pxl8_frustum* frustum = pxl8_3d_get_frustum(gfx);
|
||||
if (!frustum) {
|
||||
if (call_count++ < 5) {
|
||||
pxl8_debug("bsp_render_textured: frustum is NULL!");
|
||||
const pxl8_mat4* vp = pxl8_3d_get_view_proj(gfx);
|
||||
if (!frustum || !vp) return;
|
||||
|
||||
i32 camera_leaf = pxl8_bsp_find_leaf(bsp, camera_pos);
|
||||
if (camera_leaf < 0 || (u32)camera_leaf >= bsp->num_cell_portals) return;
|
||||
|
||||
pxl8_bsp* bsp_mut = (pxl8_bsp*)bsp;
|
||||
if (!bsp_mut->render_face_flags) {
|
||||
bsp_mut->render_face_flags = pxl8_calloc(bsp->num_faces, 1);
|
||||
if (!bsp_mut->render_face_flags) return;
|
||||
}
|
||||
memset(bsp_mut->render_face_flags, 0, bsp->num_faces);
|
||||
|
||||
pxl8_bsp_pvs pvs = pxl8_bsp_decompress_pvs(bsp, camera_leaf);
|
||||
|
||||
u32 visited_bytes = (bsp->num_leafs + 7) / 8;
|
||||
u8* visited = pxl8_calloc(visited_bytes, 1);
|
||||
screen_rect* cell_windows = pxl8_calloc(bsp->num_leafs, sizeof(screen_rect));
|
||||
portal_queue_entry* queue = pxl8_malloc(bsp->num_leafs * 4 * sizeof(portal_queue_entry));
|
||||
if (!visited || !cell_windows || !queue) {
|
||||
pxl8_free(visited);
|
||||
pxl8_free(cell_windows);
|
||||
pxl8_free(queue);
|
||||
pxl8_bsp_pvs_destroy(&pvs);
|
||||
return;
|
||||
}
|
||||
|
||||
i32 camera_leaf = pxl8_bsp_find_leaf(bsp, camera_pos);
|
||||
u32 head = 0, tail = 0;
|
||||
screen_rect full_screen = {-1.0f, -1.0f, 1.0f, 1.0f};
|
||||
|
||||
static u8* rendered_faces = NULL;
|
||||
static u32 rendered_faces_capacity = 0;
|
||||
visited[camera_leaf >> 3] |= (1 << (camera_leaf & 7));
|
||||
cell_windows[camera_leaf] = full_screen;
|
||||
queue[tail++] = (portal_queue_entry){camera_leaf, full_screen};
|
||||
|
||||
if (rendered_faces_capacity < bsp->num_faces) {
|
||||
u8* new_buffer = realloc(rendered_faces, bsp->num_faces);
|
||||
if (!new_buffer) return;
|
||||
rendered_faces = new_buffer;
|
||||
rendered_faces_capacity = bsp->num_faces;
|
||||
f32 wall_height = 128.0f;
|
||||
|
||||
while (head < tail) {
|
||||
portal_queue_entry entry = queue[head++];
|
||||
u32 leaf_id = entry.leaf;
|
||||
screen_rect window = entry.window;
|
||||
|
||||
if (leaf_id >= bsp->num_cell_portals) continue;
|
||||
const pxl8_bsp_cell_portals* cp = &bsp->cell_portals[leaf_id];
|
||||
|
||||
for (u8 i = 0; i < cp->num_portals; i++) {
|
||||
const pxl8_bsp_portal* portal = &cp->portals[i];
|
||||
u32 target = portal->target_leaf;
|
||||
|
||||
if (target >= bsp->num_leafs) continue;
|
||||
if (bsp->leafs[target].contents == -1) continue;
|
||||
if (!pxl8_bsp_pvs_is_visible(&pvs, target)) continue;
|
||||
|
||||
screen_rect portal_rect = project_portal_to_screen(portal, vp, wall_height);
|
||||
if (!screen_rect_valid(portal_rect)) continue;
|
||||
|
||||
screen_rect new_window = screen_rect_intersect(window, portal_rect);
|
||||
if (!screen_rect_valid(new_window)) continue;
|
||||
|
||||
u32 byte = target >> 3;
|
||||
u32 bit = target & 7;
|
||||
if (visited[byte] & (1 << bit)) {
|
||||
screen_rect existing = cell_windows[target];
|
||||
bool expanded = false;
|
||||
if (new_window.x0 < existing.x0) { cell_windows[target].x0 = new_window.x0; expanded = true; }
|
||||
if (new_window.y0 < existing.y0) { cell_windows[target].y0 = new_window.y0; expanded = true; }
|
||||
if (new_window.x1 > existing.x1) { cell_windows[target].x1 = new_window.x1; expanded = true; }
|
||||
if (new_window.y1 > existing.y1) { cell_windows[target].y1 = new_window.y1; expanded = true; }
|
||||
if (expanded && tail < bsp->num_leafs * 4) {
|
||||
queue[tail++] = (portal_queue_entry){target, cell_windows[target]};
|
||||
}
|
||||
|
||||
memset(rendered_faces, 0, bsp->num_faces);
|
||||
|
||||
pxl8_mesh* mesh = pxl8_mesh_create(8192, 16384);
|
||||
if (!mesh) return;
|
||||
|
||||
u32 current_texture = 0xFFFFFFFF;
|
||||
|
||||
for (u32 leaf_id = 0; leaf_id < bsp->num_leafs; leaf_id++) {
|
||||
if (camera_leaf >= 0 && !pxl8_bsp_is_leaf_visible(bsp, camera_leaf, leaf_id)) continue;
|
||||
|
||||
const pxl8_bsp_leaf* leaf = &bsp->leafs[leaf_id];
|
||||
|
||||
for (u32 i = 0; i < leaf->num_marksurfaces; i++) {
|
||||
u32 surf_idx = leaf->first_marksurface + i;
|
||||
if (surf_idx >= bsp->num_marksurfaces) continue;
|
||||
|
||||
u32 face_id = bsp->marksurfaces[surf_idx];
|
||||
if (face_id >= bsp->num_faces) continue;
|
||||
|
||||
if (rendered_faces[face_id]) continue;
|
||||
rendered_faces[face_id] = 1;
|
||||
|
||||
if (!face_in_frustum(bsp, face_id, frustum)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const pxl8_bsp_face* face = &bsp->faces[face_id];
|
||||
u32 texture_id = 0;
|
||||
if (face->texinfo_id < bsp->num_texinfo) {
|
||||
texture_id = bsp->texinfo[face->texinfo_id].miptex;
|
||||
}
|
||||
|
||||
if (texture_id != current_texture && mesh->index_count > 0) {
|
||||
pxl8_mat4 identity = pxl8_mat4_identity();
|
||||
pxl8_material mat = pxl8_material_with_lighting(pxl8_material_with_double_sided(pxl8_material_create(current_texture)));
|
||||
pxl8_3d_draw_mesh(gfx, mesh, &identity, &mat);
|
||||
pxl8_mesh_clear(mesh);
|
||||
}
|
||||
|
||||
current_texture = texture_id;
|
||||
collect_face_to_mesh(bsp, face_id, mesh);
|
||||
visited[byte] |= (1 << bit);
|
||||
cell_windows[target] = new_window;
|
||||
queue[tail++] = (portal_queue_entry){target, new_window};
|
||||
}
|
||||
}
|
||||
|
||||
if (mesh->index_count > 0) {
|
||||
pxl8_mat4 identity = pxl8_mat4_identity();
|
||||
pxl8_material mat = pxl8_material_with_lighting(pxl8_material_with_double_sided(pxl8_material_create(current_texture)));
|
||||
pxl8_3d_draw_mesh(gfx, mesh, &identity, &mat);
|
||||
pxl8_mesh* mesh = pxl8_mesh_create(8192, 16384);
|
||||
if (!mesh) {
|
||||
pxl8_free(visited);
|
||||
pxl8_free(cell_windows);
|
||||
pxl8_free(queue);
|
||||
return;
|
||||
}
|
||||
|
||||
pxl8_mesh_destroy(mesh);
|
||||
}
|
||||
|
||||
void pxl8_bsp_render_wireframe(pxl8_gfx* gfx, const pxl8_bsp* bsp, pxl8_vec3 camera_pos, u32 color) {
|
||||
if (!gfx || !bsp) return;
|
||||
|
||||
const pxl8_frustum* frustum = pxl8_3d_get_frustum(gfx);
|
||||
if (!frustum) return;
|
||||
|
||||
i32 camera_leaf = pxl8_bsp_find_leaf(bsp, camera_pos);
|
||||
u8 line_color = (u8)(color & 0xFF);
|
||||
u32 current_material = 0xFFFFFFFF;
|
||||
u32 total_faces = 0;
|
||||
|
||||
for (u32 leaf_id = 0; leaf_id < bsp->num_leafs; leaf_id++) {
|
||||
if (camera_leaf >= 0 && !pxl8_bsp_is_leaf_visible(bsp, camera_leaf, leaf_id)) continue;
|
||||
u32 byte = leaf_id >> 3;
|
||||
u32 bit = leaf_id & 7;
|
||||
if (!(visited[byte] & (1 << bit))) continue;
|
||||
|
||||
const pxl8_bsp_leaf* leaf = &bsp->leafs[leaf_id];
|
||||
if (!leaf_in_frustum(leaf, frustum)) continue;
|
||||
|
||||
for (u32 i = 0; i < leaf->num_marksurfaces; i++) {
|
||||
u32 surf_idx = leaf->first_marksurface + i;
|
||||
|
|
@ -734,23 +854,45 @@ void pxl8_bsp_render_wireframe(pxl8_gfx* gfx, const pxl8_bsp* bsp, pxl8_vec3 cam
|
|||
u32 face_id = bsp->marksurfaces[surf_idx];
|
||||
if (face_id >= bsp->num_faces) continue;
|
||||
|
||||
if (bsp_mut->render_face_flags[face_id]) continue;
|
||||
bsp_mut->render_face_flags[face_id] = 1;
|
||||
|
||||
if (!face_in_frustum(bsp, face_id, frustum)) continue;
|
||||
|
||||
const pxl8_bsp_face* face = &bsp->faces[face_id];
|
||||
u32 material_id = face->material_id;
|
||||
if (material_id >= bsp->num_materials) continue;
|
||||
total_faces++;
|
||||
|
||||
for (u32 e = 0; e < face->num_edges; e++) {
|
||||
i32 surfedge_idx = face->first_edge + e;
|
||||
if (material_id != current_material && mesh->index_count > 0 && current_material < bsp->num_materials) {
|
||||
pxl8_mat4 identity = pxl8_mat4_identity();
|
||||
pxl8_3d_draw_mesh(gfx, mesh, &identity, &bsp->materials[current_material]);
|
||||
pxl8_mesh_clear(mesh);
|
||||
}
|
||||
|
||||
if (surfedge_idx >= (i32)bsp->num_surfedges) continue;
|
||||
|
||||
u32 v0_idx, v1_idx;
|
||||
if (!pxl8_bsp_get_edge_vertices(bsp, surfedge_idx, &v0_idx, &v1_idx)) continue;
|
||||
|
||||
pxl8_vec3 p0 = bsp->vertices[v0_idx].position;
|
||||
pxl8_vec3 p1 = bsp->vertices[v1_idx].position;
|
||||
|
||||
pxl8_3d_draw_line(gfx, p0, p1, line_color);
|
||||
current_material = material_id;
|
||||
collect_face_to_mesh(bsp, face_id, mesh);
|
||||
}
|
||||
}
|
||||
|
||||
if (mesh->index_count > 0 && current_material < bsp->num_materials) {
|
||||
pxl8_mat4 identity = pxl8_mat4_identity();
|
||||
pxl8_3d_draw_mesh(gfx, mesh, &identity, &bsp->materials[current_material]);
|
||||
}
|
||||
|
||||
static u32 debug_count = 0;
|
||||
if (debug_count++ < 5) {
|
||||
pxl8_debug("BSP render: %u faces, mesh verts=%u indices=%u", total_faces, mesh->vertex_count, mesh->index_count);
|
||||
if (mesh->vertex_count > 0) {
|
||||
pxl8_vertex* v = &mesh->vertices[0];
|
||||
pxl8_debug(" vert[0]: pos=(%.1f,%.1f,%.1f) uv=(%.2f,%.2f)",
|
||||
v->position.x, v->position.y, v->position.z, v->u, v->v);
|
||||
}
|
||||
}
|
||||
|
||||
pxl8_bsp_pvs_destroy(&pvs);
|
||||
pxl8_free(visited);
|
||||
pxl8_free(cell_windows);
|
||||
pxl8_free(queue);
|
||||
pxl8_mesh_destroy(mesh);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ typedef struct pxl8_bsp_face {
|
|||
u16 side;
|
||||
|
||||
u8 styles[4];
|
||||
u16 texinfo_id;
|
||||
u16 material_id;
|
||||
|
||||
pxl8_vec3 aabb_min;
|
||||
pxl8_vec3 aabb_max;
|
||||
|
|
@ -63,16 +63,6 @@ typedef struct pxl8_bsp_plane {
|
|||
i32 type;
|
||||
} pxl8_bsp_plane;
|
||||
|
||||
typedef struct pxl8_bsp_texinfo {
|
||||
u32 miptex;
|
||||
char name[16];
|
||||
|
||||
f32 u_offset;
|
||||
pxl8_vec3 u_axis;
|
||||
|
||||
f32 v_offset;
|
||||
pxl8_vec3 v_axis;
|
||||
} pxl8_bsp_texinfo;
|
||||
|
||||
typedef struct pxl8_bsp_vertex {
|
||||
pxl8_vec3 position;
|
||||
|
|
@ -91,47 +81,52 @@ typedef struct pxl8_bsp_lightmap_sample {
|
|||
u8 r;
|
||||
} pxl8_bsp_lightmap_sample;
|
||||
|
||||
typedef struct pxl8_bsp_material_batch {
|
||||
u16* face_indices;
|
||||
u32 face_count;
|
||||
u8 material_id;
|
||||
pxl8_mesh* mesh;
|
||||
} pxl8_bsp_material_batch;
|
||||
|
||||
typedef struct pxl8_bsp_pvs {
|
||||
u8* data;
|
||||
u32 size;
|
||||
} pxl8_bsp_pvs;
|
||||
|
||||
typedef struct pxl8_bsp_portal {
|
||||
f32 x0, z0;
|
||||
f32 x1, z1;
|
||||
u32 target_leaf;
|
||||
} pxl8_bsp_portal;
|
||||
|
||||
typedef struct pxl8_bsp_cell_portals {
|
||||
pxl8_bsp_portal portals[4];
|
||||
u8 num_portals;
|
||||
} pxl8_bsp_cell_portals;
|
||||
|
||||
typedef struct pxl8_bsp {
|
||||
pxl8_bsp_cell_portals* cell_portals;
|
||||
pxl8_bsp_edge* edges;
|
||||
pxl8_bsp_face* faces;
|
||||
pxl8_bsp_leaf* leafs;
|
||||
u8* lightdata;
|
||||
pxl8_bsp_lightmap* lightmaps;
|
||||
u16* marksurfaces;
|
||||
pxl8_bsp_material_batch* material_batches;
|
||||
pxl8_gfx_material* materials;
|
||||
pxl8_bsp_model* models;
|
||||
pxl8_bsp_node* nodes;
|
||||
pxl8_bsp_plane* planes;
|
||||
u8* render_face_flags;
|
||||
i32* surfedges;
|
||||
pxl8_bsp_texinfo* texinfo;
|
||||
u32* vertex_lights;
|
||||
pxl8_bsp_vertex* vertices;
|
||||
u8* visdata;
|
||||
|
||||
u32 lightdata_size;
|
||||
u32 num_cell_portals;
|
||||
u32 num_edges;
|
||||
u32 num_faces;
|
||||
u32 num_leafs;
|
||||
u32 num_lightmaps;
|
||||
u32 num_marksurfaces;
|
||||
u32 num_material_batches;
|
||||
u32 num_materials;
|
||||
u32 num_models;
|
||||
u32 num_nodes;
|
||||
u32 num_planes;
|
||||
u32 num_surfedges;
|
||||
u32 num_texinfo;
|
||||
u32 num_vertex_lights;
|
||||
u32 num_vertices;
|
||||
u32 visdata_size;
|
||||
|
|
@ -155,9 +150,8 @@ pxl8_bsp_lightmap pxl8_bsp_lightmap_uniform(u8 r, u8 g, u8 b);
|
|||
pxl8_bsp_lightmap pxl8_bsp_lightmap_mapped(u8 width, u8 height, u32 offset);
|
||||
pxl8_bsp_lightmap_sample pxl8_bsp_sample_lightmap(const pxl8_bsp* bsp, u32 face_idx, f32 u, f32 v);
|
||||
|
||||
void pxl8_bsp_render_face(pxl8_gfx* gfx, const pxl8_bsp* bsp, u32 face_id, u32 texture_id);
|
||||
void pxl8_bsp_render_textured(pxl8_gfx* gfx, const pxl8_bsp* bsp, pxl8_vec3 camera_pos);
|
||||
void pxl8_bsp_render_wireframe(pxl8_gfx* gfx, const pxl8_bsp* bsp, pxl8_vec3 camera_pos, u32 color);
|
||||
void pxl8_bsp_render_face(pxl8_gfx* gfx, const pxl8_bsp* bsp, u32 face_id, const pxl8_gfx_material* material);
|
||||
void pxl8_bsp_render(pxl8_gfx* gfx, const pxl8_bsp* bsp, pxl8_vec3 camera_pos);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,40 @@
|
|||
#include "pxl8_gen.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pxl8_log.h"
|
||||
#include "pxl8_mem.h"
|
||||
#include "pxl8_rng.h"
|
||||
|
||||
#define CELL_SIZE 64.0f
|
||||
#define PVS_MAX_DEPTH 64
|
||||
#define WALL_HEIGHT 128.0f
|
||||
|
||||
typedef struct room_grid {
|
||||
u8* cells;
|
||||
i32 width;
|
||||
i32 height;
|
||||
} room_grid;
|
||||
|
||||
typedef struct bsp_build_context {
|
||||
pxl8_bsp* bsp;
|
||||
const room_grid* grid;
|
||||
u32 node_count;
|
||||
u32 plane_offset;
|
||||
} bsp_build_context;
|
||||
|
||||
typedef struct light_source {
|
||||
pxl8_vec3 position;
|
||||
f32 intensity;
|
||||
f32 radius;
|
||||
} light_source;
|
||||
|
||||
static bool room_grid_init(room_grid* grid, i32 width, i32 height) {
|
||||
grid->width = width;
|
||||
grid->height = height;
|
||||
grid->cells = calloc(width * height, sizeof(u8));
|
||||
grid->cells = pxl8_calloc(width * height, sizeof(u8));
|
||||
|
||||
return grid->cells != NULL;
|
||||
}
|
||||
|
|
@ -57,6 +76,333 @@ static void room_grid_fill(room_grid* grid, u8 value) {
|
|||
}
|
||||
}
|
||||
|
||||
static f32 compute_vertex_light(
|
||||
pxl8_vec3 pos,
|
||||
pxl8_vec3 normal,
|
||||
const light_source* lights,
|
||||
u32 num_lights,
|
||||
f32 ambient
|
||||
) {
|
||||
f32 total = ambient;
|
||||
|
||||
for (u32 i = 0; i < num_lights; i++) {
|
||||
pxl8_vec3 to_light = {
|
||||
lights[i].position.x - pos.x,
|
||||
lights[i].position.y - pos.y,
|
||||
lights[i].position.z - pos.z
|
||||
};
|
||||
|
||||
f32 dist_sq = to_light.x * to_light.x + to_light.y * to_light.y + to_light.z * to_light.z;
|
||||
f32 dist = sqrtf(dist_sq);
|
||||
|
||||
if (dist > lights[i].radius) continue;
|
||||
if (dist < 1.0f) dist = 1.0f;
|
||||
|
||||
f32 inv_dist = 1.0f / dist;
|
||||
pxl8_vec3 light_dir = {
|
||||
to_light.x * inv_dist,
|
||||
to_light.y * inv_dist,
|
||||
to_light.z * inv_dist
|
||||
};
|
||||
|
||||
f32 ndotl = normal.x * light_dir.x + normal.y * light_dir.y + normal.z * light_dir.z;
|
||||
if (ndotl < 0) ndotl = 0;
|
||||
|
||||
f32 attenuation = 1.0f - (dist / lights[i].radius);
|
||||
if (attenuation < 0) attenuation = 0;
|
||||
attenuation *= attenuation;
|
||||
|
||||
total += lights[i].intensity * ndotl * attenuation;
|
||||
}
|
||||
|
||||
if (total > 1.0f) total = 1.0f;
|
||||
return total;
|
||||
}
|
||||
|
||||
static void compute_bsp_vertex_lighting(
|
||||
pxl8_bsp* bsp,
|
||||
const light_source* lights,
|
||||
u32 num_lights,
|
||||
f32 ambient
|
||||
) {
|
||||
if (!bsp || bsp->num_vertices == 0) return;
|
||||
|
||||
bsp->vertex_lights = pxl8_calloc(bsp->num_vertices, sizeof(u32));
|
||||
if (!bsp->vertex_lights) return;
|
||||
bsp->num_vertex_lights = bsp->num_vertices;
|
||||
|
||||
for (u32 f = 0; f < bsp->num_faces; f++) {
|
||||
const pxl8_bsp_face* face = &bsp->faces[f];
|
||||
pxl8_vec3 normal = {0, 1, 0};
|
||||
|
||||
if (face->plane_id < bsp->num_planes) {
|
||||
normal = bsp->planes[face->plane_id].normal;
|
||||
}
|
||||
|
||||
for (u32 e = 0; e < face->num_edges; e++) {
|
||||
i32 surfedge_idx = face->first_edge + e;
|
||||
if (surfedge_idx >= (i32)bsp->num_surfedges) continue;
|
||||
|
||||
i32 edge_idx = bsp->surfedges[surfedge_idx];
|
||||
u32 vert_idx;
|
||||
|
||||
if (edge_idx >= 0) {
|
||||
if ((u32)edge_idx >= bsp->num_edges) continue;
|
||||
vert_idx = bsp->edges[edge_idx].vertex[0];
|
||||
} else {
|
||||
edge_idx = -edge_idx;
|
||||
if ((u32)edge_idx >= bsp->num_edges) continue;
|
||||
vert_idx = bsp->edges[edge_idx].vertex[1];
|
||||
}
|
||||
|
||||
if (vert_idx >= bsp->num_vertices) continue;
|
||||
|
||||
pxl8_vec3 pos = bsp->vertices[vert_idx].position;
|
||||
f32 light = compute_vertex_light(pos, normal, lights, num_lights, ambient);
|
||||
|
||||
u8 light_byte = (u8)(light * 255.0f);
|
||||
bsp->vertex_lights[vert_idx] = ((u32)light_byte << 24) | 0x00FFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
pxl8_debug("Computed vertex lighting: %u vertices, %u lights", bsp->num_vertices, num_lights);
|
||||
}
|
||||
|
||||
static pxl8_bsp_cell_portals* build_pxl8_bsp_cell_portals(const room_grid* grid, f32 cell_size) {
|
||||
i32 total_cells = grid->width * grid->height;
|
||||
pxl8_bsp_cell_portals* portals = pxl8_calloc(total_cells, sizeof(pxl8_bsp_cell_portals));
|
||||
if (!portals) return NULL;
|
||||
|
||||
for (i32 y = 0; y < grid->height; y++) {
|
||||
for (i32 x = 0; x < grid->width; x++) {
|
||||
if (room_grid_get(grid, x, y) != 0) continue;
|
||||
|
||||
i32 c = y * grid->width + x;
|
||||
f32 cx = x * cell_size;
|
||||
f32 cz = y * cell_size;
|
||||
|
||||
if (x > 0 && room_grid_get(grid, x - 1, y) == 0) {
|
||||
pxl8_bsp_portal* p = &portals[c].portals[portals[c].num_portals++];
|
||||
p->x0 = cx;
|
||||
p->z0 = cz;
|
||||
p->x1 = cx;
|
||||
p->z1 = cz + cell_size;
|
||||
p->target_leaf = y * grid->width + (x - 1);
|
||||
}
|
||||
if (x < grid->width - 1 && room_grid_get(grid, x + 1, y) == 0) {
|
||||
pxl8_bsp_portal* p = &portals[c].portals[portals[c].num_portals++];
|
||||
p->x0 = cx + cell_size;
|
||||
p->z0 = cz + cell_size;
|
||||
p->x1 = cx + cell_size;
|
||||
p->z1 = cz;
|
||||
p->target_leaf = y * grid->width + (x + 1);
|
||||
}
|
||||
if (y > 0 && room_grid_get(grid, x, y - 1) == 0) {
|
||||
pxl8_bsp_portal* p = &portals[c].portals[portals[c].num_portals++];
|
||||
p->x0 = cx + cell_size;
|
||||
p->z0 = cz;
|
||||
p->x1 = cx;
|
||||
p->z1 = cz;
|
||||
p->target_leaf = (y - 1) * grid->width + x;
|
||||
}
|
||||
if (y < grid->height - 1 && room_grid_get(grid, x, y + 1) == 0) {
|
||||
pxl8_bsp_portal* p = &portals[c].portals[portals[c].num_portals++];
|
||||
p->x0 = cx;
|
||||
p->z0 = cz + cell_size;
|
||||
p->x1 = cx + cell_size;
|
||||
p->z1 = cz + cell_size;
|
||||
p->target_leaf = (y + 1) * grid->width + x;
|
||||
}
|
||||
}
|
||||
}
|
||||
return portals;
|
||||
}
|
||||
|
||||
typedef struct flood_entry {
|
||||
u32 leaf;
|
||||
u32 depth;
|
||||
} flood_entry;
|
||||
|
||||
static void portal_flood_bfs(
|
||||
u32 start_leaf,
|
||||
const pxl8_bsp_cell_portals* portals,
|
||||
const pxl8_bsp_leaf* leafs,
|
||||
u8* pvs,
|
||||
u32 num_leafs,
|
||||
f32 cell_size,
|
||||
i32 grid_width
|
||||
) {
|
||||
(void)cell_size;
|
||||
(void)grid_width;
|
||||
|
||||
u32 pvs_bytes = (num_leafs + 7) / 8;
|
||||
u8* visited = pxl8_calloc(pvs_bytes, 1);
|
||||
flood_entry* queue = pxl8_malloc(num_leafs * sizeof(flood_entry));
|
||||
if (!visited || !queue) {
|
||||
pxl8_free(visited);
|
||||
pxl8_free(queue);
|
||||
return;
|
||||
}
|
||||
|
||||
u32 head = 0, tail = 0;
|
||||
|
||||
pvs[start_leaf >> 3] |= (1 << (start_leaf & 7));
|
||||
visited[start_leaf >> 3] |= (1 << (start_leaf & 7));
|
||||
queue[tail++] = (flood_entry){start_leaf, 0};
|
||||
|
||||
while (head < tail) {
|
||||
flood_entry e = queue[head++];
|
||||
|
||||
if (e.depth >= PVS_MAX_DEPTH) continue;
|
||||
if (leafs[e.leaf].contents == -1) continue;
|
||||
|
||||
const pxl8_bsp_cell_portals* cp = &portals[e.leaf];
|
||||
for (u8 i = 0; i < cp->num_portals; i++) {
|
||||
u32 target = cp->portals[i].target_leaf;
|
||||
u32 byte = target >> 3;
|
||||
u32 bit = target & 7;
|
||||
|
||||
if (visited[byte] & (1 << bit)) continue;
|
||||
visited[byte] |= (1 << bit);
|
||||
|
||||
if (leafs[target].contents == -1) continue;
|
||||
|
||||
pvs[byte] |= (1 << bit);
|
||||
queue[tail++] = (flood_entry){target, e.depth + 1};
|
||||
}
|
||||
}
|
||||
|
||||
pxl8_free(visited);
|
||||
pxl8_free(queue);
|
||||
}
|
||||
|
||||
static u8* compute_leaf_pvs(u32 start_leaf, const pxl8_bsp_cell_portals* portals,
|
||||
u32 num_leafs, const pxl8_bsp_leaf* leafs,
|
||||
const room_grid* grid, f32 cell_size) {
|
||||
u32 pvs_bytes = (num_leafs + 7) / 8;
|
||||
u8* pvs = pxl8_calloc(pvs_bytes, 1);
|
||||
if (!pvs) return NULL;
|
||||
|
||||
portal_flood_bfs(start_leaf, portals, leafs, pvs, num_leafs, cell_size, grid->width);
|
||||
|
||||
return pvs;
|
||||
}
|
||||
|
||||
static u32 rle_compress_pvs(const u8* pvs, u32 pvs_bytes, u8* out) {
|
||||
u32 out_pos = 0;
|
||||
u32 i = 0;
|
||||
|
||||
while (i < pvs_bytes) {
|
||||
if (pvs[i] != 0) {
|
||||
out[out_pos++] = pvs[i++];
|
||||
} else {
|
||||
u32 count = 0;
|
||||
while (i < pvs_bytes && pvs[i] == 0 && count < 255) {
|
||||
count++;
|
||||
i++;
|
||||
}
|
||||
out[out_pos++] = 0;
|
||||
out[out_pos++] = (u8)count;
|
||||
}
|
||||
}
|
||||
return out_pos;
|
||||
}
|
||||
|
||||
static pxl8_result build_pvs_data(pxl8_bsp* bsp, const pxl8_bsp_cell_portals* portals,
|
||||
const room_grid* grid, f32 cell_size) {
|
||||
u32 num_leafs = bsp->num_leafs;
|
||||
u32 pvs_bytes = (num_leafs + 7) / 8;
|
||||
|
||||
u32 max_visdata = num_leafs * pvs_bytes * 2;
|
||||
u8* visdata = pxl8_malloc(max_visdata);
|
||||
if (!visdata) return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
u32 visdata_pos = 0;
|
||||
|
||||
u8* compressed = pxl8_malloc(pvs_bytes * 2);
|
||||
if (!compressed) {
|
||||
pxl8_free(visdata);
|
||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
u32 debug_count = 0;
|
||||
for (u32 leaf = 0; leaf < num_leafs; leaf++) {
|
||||
if (bsp->leafs[leaf].contents == -1) {
|
||||
bsp->leafs[leaf].visofs = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
u8* pvs = compute_leaf_pvs(leaf, portals, num_leafs, bsp->leafs, grid, cell_size);
|
||||
if (!pvs) {
|
||||
pxl8_free(compressed);
|
||||
pxl8_free(visdata);
|
||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (debug_count < 3) {
|
||||
u32 visible = 0;
|
||||
for (u32 b = 0; b < pvs_bytes; b++) {
|
||||
for (u32 i = 0; i < 8; i++) {
|
||||
if (pvs[b] & (1 << i)) visible++;
|
||||
}
|
||||
}
|
||||
pxl8_debug("Leaf %u PVS: %u cells visible (portals: %u)", leaf, visible, portals[leaf].num_portals);
|
||||
debug_count++;
|
||||
}
|
||||
|
||||
u32 compressed_size = rle_compress_pvs(pvs, pvs_bytes, compressed);
|
||||
|
||||
bsp->leafs[leaf].visofs = visdata_pos;
|
||||
memcpy(visdata + visdata_pos, compressed, compressed_size);
|
||||
visdata_pos += compressed_size;
|
||||
|
||||
pxl8_free(pvs);
|
||||
}
|
||||
|
||||
pxl8_free(compressed);
|
||||
bsp->visdata = pxl8_realloc(visdata, visdata_pos > 0 ? visdata_pos : 1);
|
||||
bsp->visdata_size = visdata_pos;
|
||||
|
||||
pxl8_debug("Built PVS: %u leafs, %u bytes visdata", num_leafs, visdata_pos);
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
||||
static i32 build_bsp_node(bsp_build_context* ctx, i32 x0, i32 y0, i32 x1, i32 y1, i32 depth) {
|
||||
if (x1 - x0 == 1 && y1 - y0 == 1) {
|
||||
i32 leaf_idx = y0 * ctx->grid->width + x0;
|
||||
return -(leaf_idx + 1);
|
||||
}
|
||||
|
||||
i32 node_idx = ctx->node_count++;
|
||||
pxl8_bsp_node* node = &ctx->bsp->nodes[node_idx];
|
||||
|
||||
i32 plane_idx = ctx->plane_offset++;
|
||||
pxl8_bsp_plane* plane = &ctx->bsp->planes[plane_idx];
|
||||
node->plane_id = plane_idx;
|
||||
|
||||
if (depth % 2 == 0) {
|
||||
i32 mid_x = (x0 + x1) / 2;
|
||||
f32 split_pos = mid_x * CELL_SIZE;
|
||||
|
||||
plane->normal = (pxl8_vec3){1, 0, 0};
|
||||
plane->dist = split_pos;
|
||||
|
||||
node->children[0] = build_bsp_node(ctx, mid_x, y0, x1, y1, depth + 1);
|
||||
node->children[1] = build_bsp_node(ctx, x0, y0, mid_x, y1, depth + 1);
|
||||
} else {
|
||||
i32 mid_y = (y0 + y1) / 2;
|
||||
f32 split_pos = mid_y * CELL_SIZE;
|
||||
|
||||
plane->normal = (pxl8_vec3){0, 0, 1};
|
||||
plane->dist = split_pos;
|
||||
|
||||
node->children[0] = build_bsp_node(ctx, x0, mid_y, x1, y1, depth + 1);
|
||||
node->children[1] = build_bsp_node(ctx, x0, y0, x1, mid_y, depth + 1);
|
||||
}
|
||||
|
||||
return node_idx;
|
||||
}
|
||||
|
||||
static pxl8_result grid_to_bsp(pxl8_bsp* bsp, const room_grid* grid) {
|
||||
i32 vertex_count = 0;
|
||||
i32 face_count = 0;
|
||||
|
|
@ -74,37 +420,47 @@ static pxl8_result grid_to_bsp(pxl8_bsp* bsp, const room_grid* grid) {
|
|||
}
|
||||
}
|
||||
|
||||
face_count += floor_ceiling_count * 2;
|
||||
face_count += floor_ceiling_count;
|
||||
vertex_count = face_count * 4;
|
||||
|
||||
pxl8_debug("Cave generation: %dx%d grid -> %d faces, %d vertices",
|
||||
pxl8_debug("Level generation: %dx%d grid -> %d faces, %d vertices",
|
||||
grid->width, grid->height, face_count, vertex_count);
|
||||
|
||||
bsp->vertices = calloc(vertex_count, sizeof(pxl8_bsp_vertex));
|
||||
bsp->faces = calloc(face_count, sizeof(pxl8_bsp_face));
|
||||
bsp->planes = calloc(face_count, sizeof(pxl8_bsp_plane));
|
||||
bsp->edges = calloc(vertex_count, sizeof(pxl8_bsp_edge));
|
||||
bsp->surfedges = calloc(vertex_count, sizeof(i32));
|
||||
i32 total_cells = grid->width * grid->height;
|
||||
u32 max_nodes = 2 * total_cells;
|
||||
u32 total_planes = face_count + max_nodes;
|
||||
|
||||
if (!bsp->vertices || !bsp->faces || !bsp->planes || !bsp->edges || !bsp->surfedges) {
|
||||
bsp->vertices = pxl8_calloc(vertex_count, sizeof(pxl8_bsp_vertex));
|
||||
bsp->faces = pxl8_calloc(face_count, sizeof(pxl8_bsp_face));
|
||||
bsp->planes = pxl8_calloc(total_planes, sizeof(pxl8_bsp_plane));
|
||||
bsp->edges = pxl8_calloc(vertex_count, sizeof(pxl8_bsp_edge));
|
||||
bsp->surfedges = pxl8_calloc(vertex_count, sizeof(i32));
|
||||
bsp->nodes = pxl8_calloc(max_nodes, sizeof(pxl8_bsp_node));
|
||||
|
||||
u32* face_cell = pxl8_calloc(face_count, sizeof(u32));
|
||||
|
||||
if (!bsp->vertices || !bsp->faces || !bsp->planes || !bsp->edges ||
|
||||
!bsp->surfedges || !bsp->nodes || !face_cell) {
|
||||
pxl8_free(face_cell);
|
||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
bsp->texinfo = NULL;
|
||||
bsp->num_texinfo = 0;
|
||||
bsp->materials = NULL;
|
||||
bsp->num_materials = 0;
|
||||
|
||||
i32 vert_idx = 0;
|
||||
i32 face_idx = 0;
|
||||
i32 edge_idx = 0;
|
||||
|
||||
const f32 cell_size = 64.0f;
|
||||
const f32 wall_height = 128.0f;
|
||||
const f32 cell_size = CELL_SIZE;
|
||||
const f32 wall_height = WALL_HEIGHT;
|
||||
|
||||
for (i32 y = 0; y < grid->height; y++) {
|
||||
for (i32 x = 0; x < grid->width; x++) {
|
||||
if (room_grid_get(grid, x, y) == 0) {
|
||||
f32 fx = (f32)x * cell_size;
|
||||
f32 fy = (f32)y * cell_size;
|
||||
i32 cell_idx = y * grid->width + x;
|
||||
|
||||
if (room_grid_get(grid, x - 1, y) == 1) {
|
||||
bsp->vertices[vert_idx + 0].position = (pxl8_vec3){fx, 0, fy};
|
||||
|
|
@ -112,13 +468,13 @@ static pxl8_result grid_to_bsp(pxl8_bsp* bsp, const room_grid* grid) {
|
|||
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx, wall_height, fy + cell_size};
|
||||
bsp->vertices[vert_idx + 3].position = (pxl8_vec3){fx, 0, fy + cell_size};
|
||||
|
||||
bsp->planes[face_idx].normal = (pxl8_vec3){-1, 0, 0};
|
||||
bsp->planes[face_idx].dist = -fx;
|
||||
bsp->planes[face_idx].normal = (pxl8_vec3){1, 0, 0};
|
||||
bsp->planes[face_idx].dist = fx;
|
||||
|
||||
bsp->faces[face_idx].plane_id = face_idx;
|
||||
bsp->faces[face_idx].num_edges = 4;
|
||||
bsp->faces[face_idx].first_edge = edge_idx;
|
||||
bsp->faces[face_idx].texinfo_id = 0;
|
||||
bsp->faces[face_idx].material_id = 0;
|
||||
|
||||
for (i32 i = 0; i < 4; i++) {
|
||||
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
||||
|
|
@ -127,6 +483,7 @@ static pxl8_result grid_to_bsp(pxl8_bsp* bsp, const room_grid* grid) {
|
|||
}
|
||||
|
||||
compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx);
|
||||
face_cell[face_idx] = cell_idx;
|
||||
|
||||
vert_idx += 4;
|
||||
edge_idx += 4;
|
||||
|
|
@ -139,13 +496,13 @@ static pxl8_result grid_to_bsp(pxl8_bsp* bsp, const room_grid* grid) {
|
|||
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx + cell_size, wall_height, fy + cell_size};
|
||||
bsp->vertices[vert_idx + 3].position = (pxl8_vec3){fx + cell_size, wall_height, fy};
|
||||
|
||||
bsp->planes[face_idx].normal = (pxl8_vec3){1, 0, 0};
|
||||
bsp->planes[face_idx].dist = fx + cell_size;
|
||||
bsp->planes[face_idx].normal = (pxl8_vec3){-1, 0, 0};
|
||||
bsp->planes[face_idx].dist = -(fx + cell_size);
|
||||
|
||||
bsp->faces[face_idx].plane_id = face_idx;
|
||||
bsp->faces[face_idx].num_edges = 4;
|
||||
bsp->faces[face_idx].first_edge = edge_idx;
|
||||
bsp->faces[face_idx].texinfo_id = 0;
|
||||
bsp->faces[face_idx].material_id = 0;
|
||||
|
||||
for (i32 i = 0; i < 4; i++) {
|
||||
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
||||
|
|
@ -154,6 +511,7 @@ static pxl8_result grid_to_bsp(pxl8_bsp* bsp, const room_grid* grid) {
|
|||
}
|
||||
|
||||
compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx);
|
||||
face_cell[face_idx] = cell_idx;
|
||||
|
||||
vert_idx += 4;
|
||||
edge_idx += 4;
|
||||
|
|
@ -166,13 +524,13 @@ static pxl8_result grid_to_bsp(pxl8_bsp* bsp, const room_grid* grid) {
|
|||
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx + cell_size, wall_height, fy};
|
||||
bsp->vertices[vert_idx + 3].position = (pxl8_vec3){fx, wall_height, fy};
|
||||
|
||||
bsp->planes[face_idx].normal = (pxl8_vec3){0, 0, -1};
|
||||
bsp->planes[face_idx].dist = -fy;
|
||||
bsp->planes[face_idx].normal = (pxl8_vec3){0, 0, 1};
|
||||
bsp->planes[face_idx].dist = fy;
|
||||
|
||||
bsp->faces[face_idx].plane_id = face_idx;
|
||||
bsp->faces[face_idx].num_edges = 4;
|
||||
bsp->faces[face_idx].first_edge = edge_idx;
|
||||
bsp->faces[face_idx].texinfo_id = 0;
|
||||
bsp->faces[face_idx].material_id = 0;
|
||||
|
||||
for (i32 i = 0; i < 4; i++) {
|
||||
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
||||
|
|
@ -181,6 +539,7 @@ static pxl8_result grid_to_bsp(pxl8_bsp* bsp, const room_grid* grid) {
|
|||
}
|
||||
|
||||
compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx);
|
||||
face_cell[face_idx] = cell_idx;
|
||||
|
||||
vert_idx += 4;
|
||||
edge_idx += 4;
|
||||
|
|
@ -193,13 +552,13 @@ static pxl8_result grid_to_bsp(pxl8_bsp* bsp, const room_grid* grid) {
|
|||
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx + cell_size, wall_height, fy + cell_size};
|
||||
bsp->vertices[vert_idx + 3].position = (pxl8_vec3){fx + cell_size, 0, fy + cell_size};
|
||||
|
||||
bsp->planes[face_idx].normal = (pxl8_vec3){0, 0, 1};
|
||||
bsp->planes[face_idx].dist = fy + cell_size;
|
||||
bsp->planes[face_idx].normal = (pxl8_vec3){0, 0, -1};
|
||||
bsp->planes[face_idx].dist = -(fy + cell_size);
|
||||
|
||||
bsp->faces[face_idx].plane_id = face_idx;
|
||||
bsp->faces[face_idx].num_edges = 4;
|
||||
bsp->faces[face_idx].first_edge = edge_idx;
|
||||
bsp->faces[face_idx].texinfo_id = 0;
|
||||
bsp->faces[face_idx].material_id = 0;
|
||||
|
||||
for (i32 i = 0; i < 4; i++) {
|
||||
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
||||
|
|
@ -208,6 +567,7 @@ static pxl8_result grid_to_bsp(pxl8_bsp* bsp, const room_grid* grid) {
|
|||
}
|
||||
|
||||
compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx);
|
||||
face_cell[face_idx] = cell_idx;
|
||||
|
||||
vert_idx += 4;
|
||||
edge_idx += 4;
|
||||
|
|
@ -222,6 +582,7 @@ static pxl8_result grid_to_bsp(pxl8_bsp* bsp, const room_grid* grid) {
|
|||
if (room_grid_get(grid, x, y) == 0) {
|
||||
f32 fx = (f32)x * cell_size;
|
||||
f32 fy = (f32)y * cell_size;
|
||||
i32 cell_idx = y * grid->width + x;
|
||||
|
||||
bsp->vertices[vert_idx + 0].position = (pxl8_vec3){fx, 0, fy};
|
||||
bsp->vertices[vert_idx + 1].position = (pxl8_vec3){fx, 0, fy + cell_size};
|
||||
|
|
@ -234,32 +595,7 @@ static pxl8_result grid_to_bsp(pxl8_bsp* bsp, const room_grid* grid) {
|
|||
bsp->faces[face_idx].plane_id = face_idx;
|
||||
bsp->faces[face_idx].num_edges = 4;
|
||||
bsp->faces[face_idx].first_edge = edge_idx;
|
||||
bsp->faces[face_idx].texinfo_id = 0;
|
||||
|
||||
for (i32 i = 0; i < 4; i++) {
|
||||
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
||||
bsp->edges[edge_idx + i].vertex[1] = vert_idx + ((i + 1) % 4);
|
||||
bsp->surfedges[edge_idx + i] = edge_idx + i;
|
||||
}
|
||||
|
||||
compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx);
|
||||
|
||||
vert_idx += 4;
|
||||
edge_idx += 4;
|
||||
face_idx++;
|
||||
|
||||
bsp->vertices[vert_idx + 0].position = (pxl8_vec3){fx, wall_height, fy};
|
||||
bsp->vertices[vert_idx + 1].position = (pxl8_vec3){fx + cell_size, wall_height, fy};
|
||||
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx + cell_size, wall_height, fy + cell_size};
|
||||
bsp->vertices[vert_idx + 3].position = (pxl8_vec3){fx, wall_height, fy + cell_size};
|
||||
|
||||
bsp->planes[face_idx].normal = (pxl8_vec3){0, -1, 0};
|
||||
bsp->planes[face_idx].dist = -wall_height;
|
||||
|
||||
bsp->faces[face_idx].plane_id = face_idx;
|
||||
bsp->faces[face_idx].num_edges = 4;
|
||||
bsp->faces[face_idx].first_edge = edge_idx;
|
||||
bsp->faces[face_idx].texinfo_id = 0;
|
||||
bsp->faces[face_idx].material_id = 0;
|
||||
|
||||
for (i32 i = 0; i < 4; i++) {
|
||||
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
||||
|
|
@ -268,6 +604,7 @@ static pxl8_result grid_to_bsp(pxl8_bsp* bsp, const room_grid* grid) {
|
|||
}
|
||||
|
||||
compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx);
|
||||
face_cell[face_idx] = cell_idx;
|
||||
|
||||
vert_idx += 4;
|
||||
edge_idx += 4;
|
||||
|
|
@ -278,28 +615,147 @@ static pxl8_result grid_to_bsp(pxl8_bsp* bsp, const room_grid* grid) {
|
|||
|
||||
bsp->num_vertices = vertex_count;
|
||||
bsp->num_faces = face_count;
|
||||
bsp->num_planes = face_count;
|
||||
bsp->num_edges = vertex_count;
|
||||
bsp->num_surfedges = vertex_count;
|
||||
|
||||
bsp->leafs = calloc(1, sizeof(pxl8_bsp_leaf));
|
||||
bsp->marksurfaces = calloc(face_count, sizeof(u16));
|
||||
bsp->leafs = pxl8_calloc(total_cells, sizeof(pxl8_bsp_leaf));
|
||||
bsp->marksurfaces = pxl8_calloc(face_count, sizeof(u16));
|
||||
|
||||
if (!bsp->leafs || !bsp->marksurfaces) {
|
||||
pxl8_free(face_cell);
|
||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
bsp->num_leafs = 1;
|
||||
bsp->num_leafs = total_cells;
|
||||
bsp->num_marksurfaces = face_count;
|
||||
|
||||
bsp->leafs[0].first_marksurface = 0;
|
||||
bsp->leafs[0].num_marksurfaces = face_count;
|
||||
bsp->leafs[0].contents = -2;
|
||||
u32* faces_per_cell = pxl8_calloc(total_cells, sizeof(u32));
|
||||
u32* cell_offset = pxl8_calloc(total_cells, sizeof(u32));
|
||||
u32* cell_cursor = pxl8_calloc(total_cells, sizeof(u32));
|
||||
|
||||
if (!faces_per_cell || !cell_offset || !cell_cursor) {
|
||||
pxl8_free(faces_per_cell);
|
||||
pxl8_free(cell_offset);
|
||||
pxl8_free(cell_cursor);
|
||||
pxl8_free(face_cell);
|
||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
for (i32 i = 0; i < face_count; i++) {
|
||||
bsp->marksurfaces[i] = i;
|
||||
faces_per_cell[face_cell[i]]++;
|
||||
}
|
||||
|
||||
u32 offset = 0;
|
||||
for (i32 c = 0; c < total_cells; c++) {
|
||||
cell_offset[c] = offset;
|
||||
offset += faces_per_cell[c];
|
||||
}
|
||||
|
||||
for (i32 i = 0; i < face_count; i++) {
|
||||
u32 c = face_cell[i];
|
||||
bsp->marksurfaces[cell_offset[c] + cell_cursor[c]++] = i;
|
||||
}
|
||||
|
||||
for (i32 y = 0; y < grid->height; y++) {
|
||||
for (i32 x = 0; x < grid->width; x++) {
|
||||
i32 c = y * grid->width + x;
|
||||
pxl8_bsp_leaf* leaf = &bsp->leafs[c];
|
||||
|
||||
f32 fx = (f32)x * cell_size;
|
||||
f32 fz = (f32)y * cell_size;
|
||||
|
||||
leaf->mins[0] = (i16)fx;
|
||||
leaf->mins[1] = 0;
|
||||
leaf->mins[2] = (i16)fz;
|
||||
leaf->maxs[0] = (i16)(fx + cell_size);
|
||||
leaf->maxs[1] = (i16)wall_height;
|
||||
leaf->maxs[2] = (i16)(fz + cell_size);
|
||||
|
||||
if (room_grid_get(grid, x, y) == 0) {
|
||||
leaf->contents = -2;
|
||||
leaf->first_marksurface = cell_offset[c];
|
||||
leaf->num_marksurfaces = faces_per_cell[c];
|
||||
} else {
|
||||
leaf->contents = -1;
|
||||
leaf->first_marksurface = 0;
|
||||
leaf->num_marksurfaces = 0;
|
||||
}
|
||||
leaf->visofs = -1;
|
||||
}
|
||||
}
|
||||
|
||||
pxl8_free(faces_per_cell);
|
||||
pxl8_free(cell_offset);
|
||||
pxl8_free(cell_cursor);
|
||||
pxl8_free(face_cell);
|
||||
|
||||
bsp_build_context ctx = {
|
||||
.bsp = bsp,
|
||||
.grid = grid,
|
||||
.node_count = 0,
|
||||
.plane_offset = face_count,
|
||||
};
|
||||
|
||||
build_bsp_node(&ctx, 0, 0, grid->width, grid->height, 0);
|
||||
bsp->num_nodes = ctx.node_count;
|
||||
bsp->num_planes = ctx.plane_offset;
|
||||
|
||||
pxl8_debug("Built BSP tree: %u nodes, %u leafs, %u planes",
|
||||
bsp->num_nodes, bsp->num_leafs, bsp->num_planes);
|
||||
|
||||
pxl8_bsp_cell_portals* portals = build_pxl8_bsp_cell_portals(grid, cell_size);
|
||||
if (!portals) {
|
||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
u32 walkable_cells = 0;
|
||||
u32 total_portals = 0;
|
||||
i32 first_walkable = -1;
|
||||
for (i32 c = 0; c < total_cells; c++) {
|
||||
if (bsp->leafs[c].contents == -2) {
|
||||
walkable_cells++;
|
||||
if (first_walkable < 0) first_walkable = c;
|
||||
}
|
||||
total_portals += portals[c].num_portals;
|
||||
}
|
||||
pxl8_debug("Portal stats: %u walkable cells, %u total portals (avg %.1f per cell)",
|
||||
walkable_cells, total_portals, (f32)total_portals / walkable_cells);
|
||||
|
||||
if (first_walkable >= 0) {
|
||||
u32 pvs_bytes = (total_cells + 7) / 8;
|
||||
u8* visited = pxl8_calloc(pvs_bytes, 1);
|
||||
u8* queue = pxl8_malloc(total_cells * sizeof(u32));
|
||||
u32 head = 0, tail = 0;
|
||||
((u32*)queue)[tail++] = first_walkable;
|
||||
visited[first_walkable >> 3] |= (1 << (first_walkable & 7));
|
||||
u32 reachable = 0;
|
||||
while (head < tail) {
|
||||
u32 c = ((u32*)queue)[head++];
|
||||
reachable++;
|
||||
for (u8 i = 0; i < portals[c].num_portals; i++) {
|
||||
u32 n = portals[c].portals[i].target_leaf;
|
||||
u32 nb = n >> 3, ni = n & 7;
|
||||
if (!(visited[nb] & (1 << ni))) {
|
||||
visited[nb] |= (1 << ni);
|
||||
((u32*)queue)[tail++] = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
pxl8_debug("Connectivity: %u/%u walkable cells reachable from leaf %d",
|
||||
reachable, walkable_cells, first_walkable);
|
||||
pxl8_free(visited);
|
||||
pxl8_free(queue);
|
||||
}
|
||||
|
||||
pxl8_result pvs_result = build_pvs_data(bsp, portals, grid, cell_size);
|
||||
if (pvs_result != PXL8_OK) {
|
||||
pxl8_free(portals);
|
||||
return pvs_result;
|
||||
}
|
||||
|
||||
bsp->cell_portals = portals;
|
||||
bsp->num_cell_portals = total_cells;
|
||||
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
||||
|
|
@ -348,6 +804,9 @@ static pxl8_result procgen_rooms(pxl8_bsp* bsp, const pxl8_procgen_params* param
|
|||
i32 room_count = 0;
|
||||
i32 max_attempts = params->num_rooms * 10;
|
||||
|
||||
const f32 cell_size = CELL_SIZE;
|
||||
const f32 light_height = 80.0f;
|
||||
|
||||
for (i32 attempt = 0; attempt < max_attempts && room_count < params->num_rooms && room_count < 256; attempt++) {
|
||||
i32 w = params->min_room_size + (pxl8_rng_next(&rng) % (params->max_room_size - params->min_room_size + 1));
|
||||
i32 h = params->min_room_size + (pxl8_rng_next(&rng) % (params->max_room_size - params->min_room_size + 1));
|
||||
|
|
@ -394,7 +853,27 @@ static pxl8_result procgen_rooms(pxl8_bsp* bsp, const pxl8_procgen_params* param
|
|||
params->width, params->height, room_count);
|
||||
|
||||
pxl8_result result = grid_to_bsp(bsp, &grid);
|
||||
free(grid.cells);
|
||||
pxl8_free(grid.cells);
|
||||
|
||||
if (result != PXL8_OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
light_source lights[256];
|
||||
u32 num_lights = 0;
|
||||
|
||||
for (i32 i = 0; i < room_count && num_lights < 256; i++) {
|
||||
f32 cx = (rooms[i].x + rooms[i].w / 2.0f) * cell_size;
|
||||
f32 cy = (rooms[i].y + rooms[i].h / 2.0f) * cell_size;
|
||||
|
||||
lights[num_lights++] = (light_source){
|
||||
.position = {cx, light_height, cy},
|
||||
.intensity = 0.8f,
|
||||
.radius = 300.0f,
|
||||
};
|
||||
}
|
||||
|
||||
compute_bsp_vertex_lighting(bsp, lights, num_lights, 0.1f);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -417,88 +896,3 @@ pxl8_result pxl8_procgen(pxl8_bsp* bsp, const pxl8_procgen_params* params) {
|
|||
return PXL8_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
}
|
||||
|
||||
static u32 hash2d(i32 x, i32 y) {
|
||||
u32 h = ((u32)x * 374761393u) + ((u32)y * 668265263u);
|
||||
h ^= h >> 13;
|
||||
h ^= h << 17;
|
||||
h ^= h >> 5;
|
||||
return h;
|
||||
}
|
||||
|
||||
void pxl8_procgen_tex(u8* buffer, const pxl8_procgen_tex_params* params) {
|
||||
if (!buffer || !params) return;
|
||||
|
||||
for (i32 y = 0; y < params->height; y++) {
|
||||
for (i32 x = 0; x < params->width; x++) {
|
||||
f32 u = (f32)x / (f32)params->width;
|
||||
f32 v = (f32)y / (f32)params->height;
|
||||
|
||||
u8 color = params->base_color;
|
||||
|
||||
// Tile-based pattern (floor style)
|
||||
if (params->seed == 11111) {
|
||||
i32 tile_x = (i32)floorf(u * 8.0f);
|
||||
i32 tile_y = (i32)floorf(v * 8.0f);
|
||||
u32 h = hash2d(tile_x, tile_y);
|
||||
|
||||
f32 pattern = (f32)(h & 0xFF) / 255.0f;
|
||||
i32 quantized = (pattern < 0.3f) ? 0 : (pattern < 0.7f) ? 1 : 2;
|
||||
|
||||
color = params->base_color + quantized;
|
||||
|
||||
// Checkerboard dither
|
||||
if (((tile_x + tile_y) & 1) == 0 && (h & 0x100)) {
|
||||
color = (color < 255) ? color + 1 : color;
|
||||
}
|
||||
}
|
||||
// Large tile pattern (ceiling style)
|
||||
else if (params->seed == 22222) {
|
||||
i32 coarse_x = (i32)floorf(u * 2.0f);
|
||||
i32 coarse_y = (i32)floorf(v * 2.0f);
|
||||
u32 coarse_h = hash2d(coarse_x, coarse_y);
|
||||
|
||||
i32 subdivision = (coarse_h >> 8) & 0x3;
|
||||
i32 tile_x, tile_y;
|
||||
|
||||
switch (subdivision) {
|
||||
case 0: tile_x = (i32)floorf(u * 3.0f); tile_y = (i32)floorf(v * 3.0f); break;
|
||||
case 1: tile_x = (i32)floorf(u * 5.0f); tile_y = (i32)floorf(v * 5.0f); break;
|
||||
case 2: tile_x = (i32)floorf(u * 2.0f); tile_y = (i32)floorf(v * 4.0f); break;
|
||||
default: tile_x = (i32)floorf(u * 4.0f); tile_y = (i32)floorf(v * 2.0f); break;
|
||||
}
|
||||
|
||||
u32 h = hash2d(tile_x, tile_y);
|
||||
f32 pattern = (f32)(h & 0xFF) / 255.0f;
|
||||
|
||||
if (pattern < 0.25f) color = params->base_color;
|
||||
else if (pattern < 0.50f) color = params->base_color + 1;
|
||||
else if (pattern < 0.75f) color = params->base_color + 2;
|
||||
else color = params->base_color + 3;
|
||||
}
|
||||
// Brick pattern (wall style)
|
||||
else {
|
||||
f32 brick_y = floorf(v * 4.0f);
|
||||
f32 offset = ((i32)brick_y & 1) ? 0.5f : 0.0f;
|
||||
i32 brick_x = (i32)floorf(u * 4.0f + offset);
|
||||
brick_y = (i32)brick_y;
|
||||
|
||||
f32 brick_u = fabsf((u * 4.0f + offset) - floorf(u * 4.0f + offset) - 0.5f);
|
||||
f32 brick_v = fabsf((v * 4.0f) - floorf(v * 4.0f) - 0.5f);
|
||||
|
||||
u32 h = hash2d(brick_x, (i32)brick_y);
|
||||
f32 noise = (f32)(h & 0xFF) / 255.0f;
|
||||
|
||||
// Mortar lines
|
||||
if (brick_u > 0.47f || brick_v > 0.47f) {
|
||||
color = params->base_color - 2;
|
||||
} else {
|
||||
i32 shade = (i32)(noise * 3.0f);
|
||||
color = params->base_color + shade;
|
||||
}
|
||||
}
|
||||
|
||||
buffer[y * params->width + x] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,24 +21,11 @@ typedef struct pxl8_procgen_params {
|
|||
i32 num_rooms;
|
||||
} pxl8_procgen_params;
|
||||
|
||||
typedef struct pxl8_procgen_tex_params {
|
||||
char name[16];
|
||||
u32 seed;
|
||||
i32 width;
|
||||
i32 height;
|
||||
f32 scale;
|
||||
f32 roughness;
|
||||
u8 base_color;
|
||||
u8 variation;
|
||||
u8 max_color;
|
||||
} pxl8_procgen_tex_params;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
pxl8_result pxl8_procgen(pxl8_bsp* bsp, const pxl8_procgen_params* params);
|
||||
void pxl8_procgen_tex(u8* buffer, const pxl8_procgen_tex_params* params);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,23 +7,21 @@
|
|||
#include "pxl8_gen.h"
|
||||
#include "pxl8_log.h"
|
||||
#include "pxl8_math.h"
|
||||
#include "pxl8_mem.h"
|
||||
|
||||
struct pxl8_world {
|
||||
pxl8_bsp bsp;
|
||||
bool loaded;
|
||||
bool wireframe;
|
||||
u32 wireframe_color;
|
||||
};
|
||||
|
||||
pxl8_world* pxl8_world_create(void) {
|
||||
pxl8_world* world = (pxl8_world*)calloc(1, sizeof(pxl8_world));
|
||||
pxl8_world* world = (pxl8_world*)pxl8_calloc(1, sizeof(pxl8_world));
|
||||
if (!world) {
|
||||
pxl8_error("Failed to allocate world");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
world->loaded = false;
|
||||
world->wireframe_color = 15;
|
||||
|
||||
return world;
|
||||
}
|
||||
|
|
@ -35,7 +33,7 @@ void pxl8_world_destroy(pxl8_world* world) {
|
|||
pxl8_bsp_destroy(&world->bsp);
|
||||
}
|
||||
|
||||
free(world);
|
||||
pxl8_free(world);
|
||||
}
|
||||
|
||||
pxl8_result pxl8_world_generate(pxl8_world* world, pxl8_gfx* gfx, const pxl8_procgen_params* params) {
|
||||
|
|
@ -98,12 +96,12 @@ pxl8_result pxl8_world_apply_textures(pxl8_world* world, const pxl8_world_textur
|
|||
|
||||
pxl8_bsp* bsp = &world->bsp;
|
||||
|
||||
u32 max_texinfo = count * 6;
|
||||
bsp->texinfo = calloc(max_texinfo, sizeof(pxl8_bsp_texinfo));
|
||||
if (!bsp->texinfo) {
|
||||
u32 max_materials = count * 6;
|
||||
bsp->materials = pxl8_calloc(max_materials, sizeof(pxl8_gfx_material));
|
||||
if (!bsp->materials) {
|
||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
bsp->num_texinfo = 0;
|
||||
bsp->num_materials = 0;
|
||||
|
||||
for (u32 face_idx = 0; face_idx < bsp->num_faces; face_idx++) {
|
||||
pxl8_bsp_face* face = &bsp->faces[face_idx];
|
||||
|
|
@ -136,45 +134,50 @@ pxl8_result pxl8_world_apply_textures(pxl8_world* world, const pxl8_world_textur
|
|||
v_axis = (pxl8_vec3){0.0f, 1.0f, 0.0f};
|
||||
}
|
||||
|
||||
u32 texinfo_idx = bsp->num_texinfo;
|
||||
u32 material_idx = bsp->num_materials;
|
||||
bool found_existing = false;
|
||||
for (u32 i = 0; i < bsp->num_texinfo; i++) {
|
||||
if (strcmp(bsp->texinfo[i].name, matched->name) == 0 &&
|
||||
bsp->texinfo[i].miptex == matched->texture_id &&
|
||||
bsp->texinfo[i].u_axis.x == u_axis.x &&
|
||||
bsp->texinfo[i].u_axis.y == u_axis.y &&
|
||||
bsp->texinfo[i].u_axis.z == u_axis.z &&
|
||||
bsp->texinfo[i].v_axis.x == v_axis.x &&
|
||||
bsp->texinfo[i].v_axis.y == v_axis.y &&
|
||||
bsp->texinfo[i].v_axis.z == v_axis.z) {
|
||||
texinfo_idx = i;
|
||||
for (u32 i = 0; i < bsp->num_materials; i++) {
|
||||
if (strcmp(bsp->materials[i].name, matched->name) == 0 &&
|
||||
bsp->materials[i].texture_id == matched->texture_id &&
|
||||
bsp->materials[i].u_axis.x == u_axis.x &&
|
||||
bsp->materials[i].u_axis.y == u_axis.y &&
|
||||
bsp->materials[i].u_axis.z == u_axis.z &&
|
||||
bsp->materials[i].v_axis.x == v_axis.x &&
|
||||
bsp->materials[i].v_axis.y == v_axis.y &&
|
||||
bsp->materials[i].v_axis.z == v_axis.z) {
|
||||
material_idx = i;
|
||||
found_existing = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_existing) {
|
||||
if (bsp->num_texinfo >= max_texinfo) {
|
||||
pxl8_error("Too many unique texinfo entries");
|
||||
if (bsp->num_materials >= max_materials) {
|
||||
pxl8_error("Too many unique material entries");
|
||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
memcpy(bsp->texinfo[texinfo_idx].name, matched->name, sizeof(bsp->texinfo[texinfo_idx].name));
|
||||
bsp->texinfo[texinfo_idx].name[sizeof(bsp->texinfo[texinfo_idx].name) - 1] = '\0';
|
||||
bsp->texinfo[texinfo_idx].miptex = matched->texture_id;
|
||||
bsp->texinfo[texinfo_idx].u_offset = 0.0f;
|
||||
bsp->texinfo[texinfo_idx].v_offset = 0.0f;
|
||||
bsp->texinfo[texinfo_idx].u_axis = u_axis;
|
||||
bsp->texinfo[texinfo_idx].v_axis = v_axis;
|
||||
pxl8_gfx_material* mat = &bsp->materials[material_idx];
|
||||
memcpy(mat->name, matched->name, sizeof(mat->name));
|
||||
mat->name[sizeof(mat->name) - 1] = '\0';
|
||||
mat->texture_id = matched->texture_id;
|
||||
mat->u_offset = 0.0f;
|
||||
mat->v_offset = 0.0f;
|
||||
mat->u_axis = u_axis;
|
||||
mat->v_axis = v_axis;
|
||||
mat->alpha = 255;
|
||||
mat->dither = true;
|
||||
mat->double_sided = true;
|
||||
mat->dynamic_lighting = true;
|
||||
|
||||
bsp->num_texinfo++;
|
||||
bsp->num_materials++;
|
||||
}
|
||||
|
||||
face->texinfo_id = texinfo_idx;
|
||||
face->material_id = material_idx;
|
||||
}
|
||||
|
||||
pxl8_info("Applied %u textures to %u faces, created %u texinfo entries",
|
||||
count, bsp->num_faces, bsp->num_texinfo);
|
||||
pxl8_info("Applied %u textures to %u faces, created %u materials",
|
||||
count, bsp->num_faces, bsp->num_materials);
|
||||
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
|
@ -398,9 +401,13 @@ void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (world->wireframe) {
|
||||
pxl8_bsp_render_wireframe(gfx, &world->bsp, camera_pos, world->wireframe_color);
|
||||
} else {
|
||||
pxl8_bsp_render_textured(gfx, &world->bsp, camera_pos);
|
||||
pxl8_bsp_render(gfx, &world->bsp, camera_pos);
|
||||
}
|
||||
|
||||
void pxl8_world_set_wireframe(pxl8_world* world, bool enabled) {
|
||||
if (!world || !world->loaded) return;
|
||||
|
||||
for (u32 i = 0; i < world->bsp.num_materials; i++) {
|
||||
world->bsp.materials[i].wireframe = enabled;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ bool pxl8_world_check_collision(const pxl8_world* world, pxl8_vec3 pos, f32 radi
|
|||
bool pxl8_world_is_loaded(const pxl8_world* world);
|
||||
void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos);
|
||||
pxl8_vec3 pxl8_world_resolve_collision(const pxl8_world* world, pxl8_vec3 from, pxl8_vec3 to, f32 radius);
|
||||
void pxl8_world_set_wireframe(pxl8_world* world, bool enabled);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue