major gfx refactor from fixed function to shader based
This commit is contained in:
parent
0c0aa792c1
commit
40b385607d
58 changed files with 3681 additions and 2982 deletions
|
|
@ -107,4 +107,6 @@
|
||||||
(transition:render))
|
(transition:render))
|
||||||
|
|
||||||
(when (menu.is-paused)
|
(when (menu.is-paused)
|
||||||
(menu.draw))))
|
(pxl8.push_target)
|
||||||
|
(menu.draw)
|
||||||
|
(pxl8.pop_target))))
|
||||||
|
|
|
||||||
|
|
@ -168,9 +168,8 @@
|
||||||
(fn render-fireball [x y z wireframe]
|
(fn render-fireball [x y z wireframe]
|
||||||
(when fireball-mesh
|
(when fireball-mesh
|
||||||
(pxl8.draw_mesh fireball-mesh {:x x :y y :z z
|
(pxl8.draw_mesh fireball-mesh {:x x :y y :z z
|
||||||
:passthrough true
|
:emissive true
|
||||||
:wireframe wireframe
|
:wireframe wireframe})))
|
||||||
:emissive 1.0})))
|
|
||||||
|
|
||||||
{:FIREBALL_COLOR FIREBALL_COLOR
|
{:FIREBALL_COLOR FIREBALL_COLOR
|
||||||
:get-door-position get-door-position
|
:get-door-position get-door-position
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
(local pxl8 (require :pxl8))
|
(local pxl8 (require :pxl8))
|
||||||
(local effects (require :pxl8.effects))
|
(local effects (require :pxl8.effects))
|
||||||
(local net (require :pxl8.net))
|
(local net (require :pxl8.net))
|
||||||
(local shader (require :pxl8.shader))
|
|
||||||
|
|
||||||
(local colormap (require :mod.colormap))
|
(local colormap (require :mod.colormap))
|
||||||
(local entities (require :mod.entities))
|
(local entities (require :mod.entities))
|
||||||
|
|
@ -124,9 +123,7 @@
|
||||||
(set lights (pxl8.create_lights)))
|
(set lights (pxl8.create_lights)))
|
||||||
|
|
||||||
(entities.init textures)
|
(entities.init textures)
|
||||||
|
(sky.generate-stars)
|
||||||
(sky.generate-stars 12345)
|
|
||||||
|
|
||||||
(preload)
|
(preload)
|
||||||
|
|
||||||
(when (not ceiling-tex)
|
(when (not ceiling-tex)
|
||||||
|
|
@ -136,22 +133,17 @@
|
||||||
(when (not trim-tex)
|
(when (not trim-tex)
|
||||||
(set trim-tex (textures.wood-trim 77777 WOOD_COLOR)))
|
(set trim-tex (textures.wood-trim 77777 WOOD_COLOR)))
|
||||||
(when (not wall-tex)
|
(when (not wall-tex)
|
||||||
(set wall-tex (textures.cobble-timber 55555 STONE_WALL_START MOSS_COLOR WOOD_COLOR)))
|
(set wall-tex (textures.cobble-timber 55555 STONE_WALL_START MOSS_COLOR WOOD_COLOR))))
|
||||||
|
|
||||||
)
|
|
||||||
|
|
||||||
(fn setup-materials []
|
(fn setup-materials []
|
||||||
(when (and world (not bsp-materials-setup) ceiling-tex floor-tex trim-tex wall-tex)
|
(when (and world (not bsp-materials-setup) floor-tex trim-tex wall-tex)
|
||||||
(let [chunk (world:active_chunk)]
|
(let [chunk (world:active_chunk)]
|
||||||
(when (and chunk (chunk:ready))
|
(when (and chunk (chunk:ready))
|
||||||
(let [ceiling-mat (pxl8.create_material {:texture ceiling-tex})
|
(let [floor-mat (pxl8.create_material {:texture floor-tex :lighting true :double_sided true})
|
||||||
floor-mat (pxl8.create_material {:texture floor-tex :lighting true})
|
trim-mat (pxl8.create_material {:texture trim-tex :lighting true :double_sided true})
|
||||||
trim-mat (pxl8.create_material {:texture trim-tex :lighting true})
|
wall-mat (pxl8.create_material {:texture wall-tex :lighting true :double_sided true})]
|
||||||
wall-mat (pxl8.create_material {:texture wall-tex :lighting true})]
|
|
||||||
|
|
||||||
(world:set_bsp_material 0 floor-mat)
|
(world:set_bsp_material 0 floor-mat)
|
||||||
(world:set_bsp_material 1 wall-mat)
|
(world:set_bsp_material 1 wall-mat)
|
||||||
(world:set_bsp_material 2 ceiling-mat)
|
|
||||||
(world:set_bsp_material 3 trim-mat)
|
(world:set_bsp_material 3 trim-mat)
|
||||||
(set bsp-materials-setup true))))))
|
(set bsp-materials-setup true))))))
|
||||||
|
|
||||||
|
|
@ -199,7 +191,7 @@
|
||||||
(when (> portal-cooldown 0)
|
(when (> portal-cooldown 0)
|
||||||
(set portal-cooldown (- portal-cooldown dt)))
|
(set portal-cooldown (- portal-cooldown dt)))
|
||||||
|
|
||||||
(when (and network (<= portal-cooldown 0))
|
(when (and world network (<= portal-cooldown 0))
|
||||||
(let [chunk (world:active_chunk)
|
(let [chunk (world:active_chunk)
|
||||||
in-bsp (not= chunk nil)
|
in-bsp (not= chunk nil)
|
||||||
(door-x door-z) (entities.get-door-position)
|
(door-x door-z) (entities.get-door-position)
|
||||||
|
|
@ -240,7 +232,7 @@
|
||||||
expecting-bsp (> chunk-id 0)
|
expecting-bsp (> chunk-id 0)
|
||||||
voxel-space (and (not chunk) (= chunk-id 0))
|
voxel-space (and (not chunk) (= chunk-id 0))
|
||||||
ready (or voxel-space (and chunk (chunk:ready)))]
|
ready (or voxel-space (and chunk (chunk:ready)))]
|
||||||
(when ready
|
(when world
|
||||||
(let [input (sample-input)
|
(let [input (sample-input)
|
||||||
grid-max (if voxel-space 100000 (* grid-size chunk-size))
|
grid-max (if voxel-space 100000 (* grid-size chunk-size))
|
||||||
movement-yaw cam-yaw]
|
movement-yaw cam-yaw]
|
||||||
|
|
@ -322,10 +314,14 @@
|
||||||
voxel-space (and world (not chunk) (not expecting-bsp))
|
voxel-space (and world (not chunk) (not expecting-bsp))
|
||||||
ready (or voxel-space (and chunk (chunk:ready)))]
|
ready (or voxel-space (and chunk (chunk:ready)))]
|
||||||
|
|
||||||
|
(when (not camera)
|
||||||
|
(pxl8.text "camera nil" 5 30 12))
|
||||||
|
(when (not world)
|
||||||
|
(pxl8.text "world nil" 5 40 12))
|
||||||
(when (and world (not ready))
|
(when (and world (not ready))
|
||||||
(pxl8.text "Waiting for world data..." 5 30 12))
|
(pxl8.text (.. "Waiting... chunk=" (tostring chunk) " voxel=" (tostring voxel-space)) 5 50 12))
|
||||||
|
|
||||||
(when (and camera world ready)
|
(when (and camera world)
|
||||||
(let [bob-offset (* (math.sin bob-time) bob-amount)
|
(let [bob-offset (* (math.sin bob-time) bob-amount)
|
||||||
eye-y (+ cam-y player-eye-height bob-offset land-squash)
|
eye-y (+ cam-y player-eye-height bob-offset land-squash)
|
||||||
forward-x (- (math.sin cam-yaw))
|
forward-x (- (math.sin cam-yaw))
|
||||||
|
|
@ -353,19 +349,22 @@
|
||||||
r2 (* 0.04 (math.sin (+ (* real-time 3.2) phase)))
|
r2 (* 0.04 (math.sin (+ (* real-time 3.2) phase)))
|
||||||
light-radius (* 150 (+ 0.95 r1 r2))]
|
light-radius (* 150 (+ 0.95 r1 r2))]
|
||||||
(lights:clear)
|
(lights:clear)
|
||||||
(lights:add light-x light-y light-z 0xFF8C32 light-intensity light-radius)
|
(lights:add light-x light-y light-z 0xFFB888 light-intensity light-radius)
|
||||||
|
|
||||||
|
(pxl8.push_target)
|
||||||
(pxl8.begin_frame_3d camera lights {
|
(pxl8.begin_frame_3d camera lights {
|
||||||
:ambient 30
|
:ambient 25
|
||||||
:fog_density 0.0
|
:fog_density 0.0
|
||||||
:celestial_dir [0.5 -0.8 0.3]
|
:celestial_dir [0.5 -0.8 0.3]
|
||||||
:celestial_intensity 0.5})
|
:celestial_intensity 0.3})
|
||||||
(pxl8.clear_depth)
|
(pxl8.clear_depth)
|
||||||
|
|
||||||
(sky.update-gradient 1 2 6 6 10 18)
|
(sky.update-gradient 1 2 6 6 10 18)
|
||||||
(sky.render smooth-cam-x eye-y smooth-cam-z (menu.is-wireframe))
|
(sky.render smooth-cam-x eye-y smooth-cam-z (menu.is-wireframe))
|
||||||
|
(sky.render-stars smooth-cam-x eye-y smooth-cam-z 1.0 last-dt)
|
||||||
(pxl8.clear_depth)
|
(pxl8.clear_depth)
|
||||||
|
|
||||||
(world:set_wireframe (menu.is-wireframe))
|
(pxl8.set_wireframe (menu.is-wireframe))
|
||||||
(world:render [smooth-cam-x eye-y smooth-cam-z])
|
(world:render [smooth-cam-x eye-y smooth-cam-z])
|
||||||
|
|
||||||
(when chunk
|
(when chunk
|
||||||
|
|
@ -373,16 +372,10 @@
|
||||||
|
|
||||||
(entities.render-door (menu.is-wireframe) (if voxel-space 128 0))
|
(entities.render-door (menu.is-wireframe) (if voxel-space 128 0))
|
||||||
|
|
||||||
(pxl8.end_frame_3d))
|
(pxl8.end_frame_3d)
|
||||||
|
(pxl8.pop_target))
|
||||||
;; TODO: shader needs to run at present time, not mid-frame
|
|
||||||
;; (shader.begin_frame)
|
|
||||||
;; (shader.run shader.presets.light_with_fog
|
|
||||||
;; {:fog_r 6 :fog_g 6 :fog_b 12
|
|
||||||
;; :fog_start 0.1 :fog_end 0.85})
|
|
||||||
|
|
||||||
(sky.render-stars smooth-cam-x eye-y smooth-cam-z 1.0 last-dt)
|
|
||||||
|
|
||||||
|
(pxl8.push_target)
|
||||||
(let [cx (/ (pxl8.get_width) 2)
|
(let [cx (/ (pxl8.get_width) 2)
|
||||||
cy (/ (pxl8.get_height) 2)
|
cy (/ (pxl8.get_height) 2)
|
||||||
crosshair-size 4
|
crosshair-size 4
|
||||||
|
|
@ -394,7 +387,8 @@
|
||||||
(pxl8.text (.. "fps: " (string.format "%.0f" fps-avg)) 5 5 text-color)
|
(pxl8.text (.. "fps: " (string.format "%.0f" fps-avg)) 5 5 text-color)
|
||||||
(pxl8.text (.. "pos: " (string.format "%.0f" cam-x) ","
|
(pxl8.text (.. "pos: " (string.format "%.0f" cam-x) ","
|
||||||
(string.format "%.0f" cam-y) ","
|
(string.format "%.0f" cam-y) ","
|
||||||
(string.format "%.0f" cam-z)) 5 15 text-color))))))
|
(string.format "%.0f" cam-z)) 5 15 text-color))
|
||||||
|
(pxl8.pop_target)))))
|
||||||
|
|
||||||
{:preload preload
|
{:preload preload
|
||||||
:is-connected is-connected
|
:is-connected is-connected
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
(local net-mod (require :pxl8.net))
|
(local net-mod (require :pxl8.net))
|
||||||
|
|
||||||
(var paused false)
|
(var paused false)
|
||||||
(var wireframe false)
|
|
||||||
(var gui nil)
|
(var gui nil)
|
||||||
(var render-distance 3)
|
(var render-distance 3)
|
||||||
(var sim-distance 4)
|
(var sim-distance 4)
|
||||||
|
|
@ -13,6 +12,11 @@
|
||||||
(var current-items [])
|
(var current-items [])
|
||||||
(var pending-action nil)
|
(var pending-action nil)
|
||||||
|
|
||||||
|
(var baked-lighting true)
|
||||||
|
(var dynamic-lighting true)
|
||||||
|
(var textures true)
|
||||||
|
(var wireframe false)
|
||||||
|
|
||||||
(fn init []
|
(fn init []
|
||||||
(set gui (pxl8.create_gui))
|
(set gui (pxl8.create_gui))
|
||||||
(let [w (world-mod.World.get)]
|
(let [w (world-mod.World.get)]
|
||||||
|
|
@ -100,24 +104,20 @@
|
||||||
(or clicked (and is-selected (= pending-action label))))))
|
(or clicked (and is-selected (= pending-action label))))))
|
||||||
|
|
||||||
(fn draw-main-menu []
|
(fn draw-main-menu []
|
||||||
(pxl8.gui_window 200 80 240 235 "pxl8 demo")
|
(pxl8.gui_window 200 80 240 200 "pxl8 demo")
|
||||||
|
|
||||||
(when (menu-button 1 215 127 210 30 "Resume")
|
(when (menu-button 1 215 127 210 30 "Resume")
|
||||||
(hide))
|
(hide))
|
||||||
|
|
||||||
(let [wire-label (if wireframe "Wireframe: On" "Wireframe: Off")]
|
(when (menu-button 5 215 162 210 30 "GFX")
|
||||||
(when (menu-button 4 215 162 210 30 wire-label)
|
|
||||||
(set wireframe (not wireframe))))
|
|
||||||
|
|
||||||
(when (menu-button 5 215 197 210 30 "GFX")
|
|
||||||
(set current-panel :gfx)
|
(set current-panel :gfx)
|
||||||
(set selected-item nil))
|
(set selected-item nil))
|
||||||
|
|
||||||
(when (menu-button 6 215 232 210 30 "SFX")
|
(when (menu-button 6 215 197 210 30 "SFX")
|
||||||
(set current-panel :sfx)
|
(set current-panel :sfx)
|
||||||
(set selected-item nil))
|
(set selected-item nil))
|
||||||
|
|
||||||
(when (menu-button 2 215 267 210 30 "Quit")
|
(when (menu-button 2 215 232 210 30 "Quit")
|
||||||
(pxl8.quit)))
|
(pxl8.quit)))
|
||||||
|
|
||||||
(fn draw-sfx-panel []
|
(fn draw-sfx-panel []
|
||||||
|
|
@ -136,10 +136,18 @@
|
||||||
(set selected-item nil)))
|
(set selected-item nil)))
|
||||||
|
|
||||||
(fn draw-gfx-panel []
|
(fn draw-gfx-panel []
|
||||||
(pxl8.gui_window 200 100 240 180 "GFX")
|
(pxl8.gui_window 200 60 240 220 "GFX")
|
||||||
|
|
||||||
(pxl8.gui_label 215 150 (.. "Render: " render-distance) 15)
|
(let [baked-label (if baked-lighting "Baked Lighting: On" "Baked Lighting: Off")]
|
||||||
(let [(changed new-val) (gui:slider_int 30 215 165 210 16 render-distance 1 8)]
|
(when (menu-button 40 215 107 210 24 baked-label)
|
||||||
|
(set baked-lighting (not baked-lighting))))
|
||||||
|
|
||||||
|
(let [dynamic-label (if dynamic-lighting "Dynamic Lighting: On" "Dynamic Lighting: Off")]
|
||||||
|
(when (menu-button 41 215 134 210 24 dynamic-label)
|
||||||
|
(set dynamic-lighting (not dynamic-lighting))))
|
||||||
|
|
||||||
|
(pxl8.gui_label 215 162 (.. "Render: " render-distance) 15)
|
||||||
|
(let [(changed new-val) (gui:slider_int 30 215 175 210 14 render-distance 1 8)]
|
||||||
(when changed
|
(when changed
|
||||||
(set render-distance new-val)
|
(set render-distance new-val)
|
||||||
(let [w (world-mod.World.get)
|
(let [w (world-mod.World.get)
|
||||||
|
|
@ -147,16 +155,15 @@
|
||||||
(when w (w:set_render_distance new-val))
|
(when w (w:set_render_distance new-val))
|
||||||
(when n (n:set_chunk_settings new-val sim-distance)))))
|
(when n (n:set_chunk_settings new-val sim-distance)))))
|
||||||
|
|
||||||
(pxl8.gui_label 215 190 (.. "Sim: " sim-distance) 15)
|
(let [tex-label (if textures "Textures: On" "Textures: Off")]
|
||||||
(let [(changed new-val) (gui:slider_int 31 215 205 210 16 sim-distance 1 8)]
|
(when (menu-button 42 215 194 210 24 tex-label)
|
||||||
(when changed
|
(set textures (not textures))))
|
||||||
(set sim-distance new-val)
|
|
||||||
(let [w (world-mod.World.get)
|
|
||||||
n (net-mod.get)]
|
|
||||||
(when w (w:set_sim_distance new-val))
|
|
||||||
(when n (n:set_chunk_settings render-distance new-val)))))
|
|
||||||
|
|
||||||
(when (menu-button 32 215 240 210 30 "Back")
|
(let [wire-label (if wireframe "Wireframe: On" "Wireframe: Off")]
|
||||||
|
(when (menu-button 43 215 221 210 24 wire-label)
|
||||||
|
(set wireframe (not wireframe))))
|
||||||
|
|
||||||
|
(when (menu-button 32 215 248 210 24 "Back")
|
||||||
(set current-panel :main)
|
(set current-panel :main)
|
||||||
(set selected-item nil)))
|
(set selected-item nil)))
|
||||||
|
|
||||||
|
|
@ -178,6 +185,9 @@
|
||||||
|
|
||||||
{:init init
|
{:init init
|
||||||
:is-paused (fn [] paused)
|
:is-paused (fn [] paused)
|
||||||
|
:is-baked-lighting (fn [] baked-lighting)
|
||||||
|
:is-dynamic-lighting (fn [] dynamic-lighting)
|
||||||
|
:is-textures (fn [] textures)
|
||||||
:is-wireframe (fn [] wireframe)
|
:is-wireframe (fn [] wireframe)
|
||||||
:toggle toggle
|
:toggle toggle
|
||||||
:show show
|
:show show
|
||||||
|
|
|
||||||
|
|
@ -176,7 +176,8 @@
|
||||||
|
|
||||||
(set star-count (+ (length tiny-stars) (length random-stars)))
|
(set star-count (+ (length tiny-stars) (length random-stars)))
|
||||||
(set star-directions (pxl8.create_vec3_array star-count))
|
(set star-directions (pxl8.create_vec3_array star-count))
|
||||||
(set star-glows (pxl8.create_glows 10000))
|
(when pxl8.create_glows
|
||||||
|
(set star-glows (pxl8.create_glows 10000)))
|
||||||
(set star-projected (pxl8.create_vec3_array star-count))
|
(set star-projected (pxl8.create_vec3_array star-count))
|
||||||
|
|
||||||
(var idx 0)
|
(var idx 0)
|
||||||
|
|
@ -247,7 +248,7 @@
|
||||||
(fn render [cam-x cam-y cam-z wireframe]
|
(fn render [cam-x cam-y cam-z wireframe]
|
||||||
(when (not sky-mesh) (create-sky-dome))
|
(when (not sky-mesh) (create-sky-dome))
|
||||||
(when sky-mesh
|
(when sky-mesh
|
||||||
(pxl8.draw_mesh sky-mesh {:x cam-x :y cam-y :z cam-z :passthrough true :wireframe wireframe})))
|
(pxl8.draw_mesh sky-mesh {:x cam-x :y cam-y :z cam-z :wireframe wireframe})))
|
||||||
|
|
||||||
{:render render
|
{:render render
|
||||||
:render-stars render-stars
|
:render-stars render-stars
|
||||||
|
|
|
||||||
BIN
demo/res/textures/door.ase
Normal file
BIN
demo/res/textures/door.ase
Normal file
Binary file not shown.
192
pxl8.sh
192
pxl8.sh
|
|
@ -8,7 +8,7 @@ if command -v ccache >/dev/null 2>&1; then
|
||||||
CC="ccache $CC"
|
CC="ccache $CC"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
CFLAGS="-std=c23 -Wall -Wextra"
|
CFLAGS="-std=c23 -Wall -Wextra -Wno-missing-braces"
|
||||||
LIBS="-lm"
|
LIBS="-lm"
|
||||||
MODE="debug"
|
MODE="debug"
|
||||||
BUILDDIR=".build"
|
BUILDDIR=".build"
|
||||||
|
|
@ -23,6 +23,7 @@ fi
|
||||||
case "$(uname)" in
|
case "$(uname)" in
|
||||||
Linux)
|
Linux)
|
||||||
LINKER_FLAGS="$LINKER_FLAGS -rdynamic"
|
LINKER_FLAGS="$LINKER_FLAGS -rdynamic"
|
||||||
|
LIBS="$LIBS -ldl"
|
||||||
;;
|
;;
|
||||||
Darwin)
|
Darwin)
|
||||||
export MACOSX_DEPLOYMENT_TARGET="$(sw_vers -productVersion | cut -d '.' -f 1)"
|
export MACOSX_DEPLOYMENT_TARGET="$(sw_vers -productVersion | cut -d '.' -f 1)"
|
||||||
|
|
@ -32,6 +33,7 @@ case "$(uname)" in
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
LINKER_FLAGS="$LINKER_FLAGS -rdynamic"
|
LINKER_FLAGS="$LINKER_FLAGS -rdynamic"
|
||||||
|
LIBS="$LIBS -ldl"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
|
@ -59,6 +61,12 @@ build_luajit() {
|
||||||
|
|
||||||
build_server() {
|
build_server() {
|
||||||
local mode="$1"
|
local mode="$1"
|
||||||
|
local server_bin
|
||||||
|
if [[ "$mode" == "release" ]]; then
|
||||||
|
server_bin="pxl8d/target/release/pxl8d"
|
||||||
|
else
|
||||||
|
server_bin="pxl8d/target/debug/pxl8d"
|
||||||
|
fi
|
||||||
if [[ -d "pxl8d" ]]; then
|
if [[ -d "pxl8d" ]]; then
|
||||||
print_info "Building pxl8d ($mode mode)"
|
print_info "Building pxl8d ($mode mode)"
|
||||||
cd pxl8d
|
cd pxl8d
|
||||||
|
|
@ -70,6 +78,8 @@ build_server() {
|
||||||
local status=$?
|
local status=$?
|
||||||
cd - > /dev/null
|
cd - > /dev/null
|
||||||
if [[ $status -eq 0 ]]; then
|
if [[ $status -eq 0 ]]; then
|
||||||
|
mkdir -p "$BINDIR"
|
||||||
|
cp "$server_bin" "$BINDIR/pxl8d"
|
||||||
print_info "Built pxl8d"
|
print_info "Built pxl8d"
|
||||||
else
|
else
|
||||||
print_error "pxl8d build failed"
|
print_error "pxl8d build failed"
|
||||||
|
|
@ -78,14 +88,7 @@ build_server() {
|
||||||
}
|
}
|
||||||
|
|
||||||
start_server() {
|
start_server() {
|
||||||
local mode="$1"
|
local server_bin="$BINDIR/pxl8d"
|
||||||
local server_bin
|
|
||||||
if [[ "$mode" == "release" ]]; then
|
|
||||||
server_bin="pxl8d/target/release/pxl8d"
|
|
||||||
else
|
|
||||||
server_bin="pxl8d/target/debug/pxl8d"
|
|
||||||
fi
|
|
||||||
print_info "Server mode: $mode, binary: $server_bin"
|
|
||||||
if [[ -f "$server_bin" ]]; then
|
if [[ -f "$server_bin" ]]; then
|
||||||
print_info "Starting server..."
|
print_info "Starting server..."
|
||||||
./$server_bin &
|
./$server_bin &
|
||||||
|
|
@ -94,7 +97,7 @@ start_server() {
|
||||||
sleep 0.5
|
sleep 0.5
|
||||||
else
|
else
|
||||||
print_error "pxl8d binary not found: $server_bin"
|
print_error "pxl8d binary not found: $server_bin"
|
||||||
print_error "Build pxl8d first with: cd pxl8d && cargo build"
|
print_error "Build first with: ./pxl8.sh build"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -128,8 +131,17 @@ build_sdl() {
|
||||||
}
|
}
|
||||||
|
|
||||||
prefix_output() {
|
prefix_output() {
|
||||||
|
local in_warning=false
|
||||||
while IFS= read -r line; do
|
while IFS= read -r line; do
|
||||||
if [[ "$line" == *": warning:"* ]] || [[ "$line" == *": note:"* ]]; then
|
if [[ "$line" == *": warning:"* ]] || [[ "$line" == *": note:"* ]]; then
|
||||||
|
in_warning=true
|
||||||
|
echo -e "${YELLOW}${BOLD}[$(timestamp) WARN]${NC} $line" >&2
|
||||||
|
elif [[ "$line" == *": error:"* ]] || [[ "$line" == *": fatal error:"* ]]; then
|
||||||
|
in_warning=false
|
||||||
|
echo -e "${RED}${BOLD}[$(timestamp) ERROR]${NC} $line" >&2
|
||||||
|
elif [[ "$line" =~ ^[0-9]+\ (warning|error)s?\ generated ]] || [[ -z "$line" ]]; then
|
||||||
|
echo -e "${YELLOW}${BOLD}[$(timestamp) WARN]${NC} $line" >&2
|
||||||
|
elif $in_warning; then
|
||||||
echo -e "${YELLOW}${BOLD}[$(timestamp) WARN]${NC} $line" >&2
|
echo -e "${YELLOW}${BOLD}[$(timestamp) WARN]${NC} $line" >&2
|
||||||
else
|
else
|
||||||
echo -e "${RED}${BOLD}[$(timestamp) ERROR]${NC} $line" >&2
|
echo -e "${RED}${BOLD}[$(timestamp) ERROR]${NC} $line" >&2
|
||||||
|
|
@ -155,6 +167,54 @@ compile_source_file() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
compile_shaders() {
|
||||||
|
local build_mode="$1"
|
||||||
|
local shader_dir="src/gfx/shaders/cpu"
|
||||||
|
local so_dir=".build/$build_mode/shaders/cpu"
|
||||||
|
local obj_dir=".build/$build_mode/shaders/cpu/obj"
|
||||||
|
|
||||||
|
if [[ ! -d "$shader_dir" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
[[ "$build_mode" == "debug" ]] && mkdir -p "$so_dir"
|
||||||
|
mkdir -p "$obj_dir"
|
||||||
|
|
||||||
|
local SHADER_INCLUDES="-Isrc/core -Isrc/gfx -Isrc/math"
|
||||||
|
case "$(uname)" in
|
||||||
|
Darwin) SO_EXT="dylib" ;;
|
||||||
|
*) SO_EXT="so" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Compile C shaders directly
|
||||||
|
for shader in $(find "$shader_dir" -maxdepth 1 -name "*.c" 2>/dev/null); do
|
||||||
|
local shader_name=$(basename "${shader%.c}")
|
||||||
|
local so_file="$so_dir/${shader_name}.$SO_EXT"
|
||||||
|
local obj_file="$obj_dir/${shader_name}.o"
|
||||||
|
|
||||||
|
# Debug: compile to .so for hot-reload
|
||||||
|
if [[ "$build_mode" == "debug" ]]; then
|
||||||
|
if [[ "$shader" -nt "$so_file" ]] || [[ ! -f "$so_file" ]]; then
|
||||||
|
$CC -shared -fPIC -O2 $SHADER_INCLUDES "$shader" -o "$so_file" 2>&1 | prefix_output || {
|
||||||
|
print_error "Shader build failed: $shader_name"
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Compile to .o for release static linking
|
||||||
|
if [[ "$shader" -nt "$obj_file" ]] || [[ ! -f "$obj_file" ]]; then
|
||||||
|
$CC -c -O2 $SHADER_INCLUDES "$shader" -o "$obj_file" 2>&1 | prefix_output || {
|
||||||
|
print_error "Shader compile failed: $shader_name"
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Export shader object files for linking
|
||||||
|
SHADER_OBJECTS=""
|
||||||
|
for obj in $(find "$obj_dir" -name "*.o" 2>/dev/null); do
|
||||||
|
SHADER_OBJECTS="$SHADER_OBJECTS $obj"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
make_lib_dirs() {
|
make_lib_dirs() {
|
||||||
mkdir -p lib/linenoise lib/fennel lib/miniz
|
mkdir -p lib/linenoise lib/fennel lib/miniz
|
||||||
|
|
@ -180,15 +240,17 @@ print_usage() {
|
||||||
echo " clean Remove build artifacts"
|
echo " clean Remove build artifacts"
|
||||||
echo " help Show this help message"
|
echo " help Show this help message"
|
||||||
echo " install Install pxl8 to ~/.local/bin"
|
echo " install Install pxl8 to ~/.local/bin"
|
||||||
|
echo " profile Profile with perf and generate flamegraph (Linux)"
|
||||||
echo " run Build and run pxl8 (optional: cart.pxc or folder)"
|
echo " run Build and run pxl8 (optional: cart.pxc or folder)"
|
||||||
echo " update Download/update all dependencies"
|
echo " update Download/update all dependencies"
|
||||||
echo " vendor Fetch source for dependencies (ex. SDL3)"
|
echo " vendor Fetch source for dependencies (ex. SDL3)"
|
||||||
echo
|
echo
|
||||||
echo -e "${BOLD}OPTIONS:${NC}"
|
echo -e "${BOLD}OPTIONS:${NC}"
|
||||||
echo " --all Clean both build artifacts and dependencies"
|
echo " --all Clean both build artifacts and dependencies"
|
||||||
echo " --cache Clear ccache (use with clean)"
|
echo " --cache Clear ccache (use with clean)"
|
||||||
echo " --deps Clean only dependencies"
|
echo " --deps Clean only dependencies"
|
||||||
echo " --release Build/run/clean in release mode (default: debug)"
|
echo " --duration=N Profile duration in seconds (default: 30)"
|
||||||
|
echo " --release Build/run/clean in release mode (default: debug)"
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_sdl3() {
|
setup_sdl3() {
|
||||||
|
|
@ -264,6 +326,20 @@ update_fennel() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
update_flamegraph() {
|
||||||
|
print_info "Fetching FlameGraph"
|
||||||
|
|
||||||
|
if [[ -d "lib/FlameGraph/.git" ]]; then
|
||||||
|
cd lib/FlameGraph && git pull --quiet origin master
|
||||||
|
cd - > /dev/null
|
||||||
|
else
|
||||||
|
rm -rf lib/FlameGraph
|
||||||
|
git clone --quiet https://github.com/brendangregg/FlameGraph.git lib/FlameGraph
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_info "Updated FlameGraph"
|
||||||
|
}
|
||||||
|
|
||||||
update_linenoise() {
|
update_linenoise() {
|
||||||
print_info "Fetching linenoise"
|
print_info "Fetching linenoise"
|
||||||
|
|
||||||
|
|
@ -328,6 +404,7 @@ update_sdl() {
|
||||||
print_info "Updated SDL3"
|
print_info "Updated SDL3"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
COMMAND="$1"
|
COMMAND="$1"
|
||||||
shift || true
|
shift || true
|
||||||
|
|
||||||
|
|
@ -399,7 +476,7 @@ case "$COMMAND" in
|
||||||
print_info "Compiler cache: ccache enabled"
|
print_info "Compiler cache: ccache enabled"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
INCLUDES="-Isrc/asset -Isrc/bsp -Isrc/core -Isrc/gfx -Isrc/gui -Isrc/hal -Isrc/math -Isrc/net -Isrc/procgen -Isrc/script -Isrc/sfx -Isrc/sim -Isrc/vxl -Isrc/world -Ilib/linenoise -Ilib/luajit/src -Ilib/miniz"
|
INCLUDES="-Isrc/asset -Isrc/bsp -Isrc/core -Isrc/gfx -Isrc/gui -Isrc/hal -Isrc/math -Isrc/net -Isrc/procgen -Isrc/script -Isrc/shader -Isrc/sfx -Isrc/sim -Isrc/vxl -Isrc/world -Ilib/linenoise -Ilib/luajit/src -Ilib/miniz -I.build/shaders/c"
|
||||||
COMPILE_FLAGS="$CFLAGS $INCLUDES"
|
COMPILE_FLAGS="$CFLAGS $INCLUDES"
|
||||||
DEP_COMPILE_FLAGS="$DEP_CFLAGS $INCLUDES"
|
DEP_COMPILE_FLAGS="$DEP_CFLAGS $INCLUDES"
|
||||||
|
|
||||||
|
|
@ -424,11 +501,12 @@ case "$COMMAND" in
|
||||||
src/gfx/pxl8_atlas.c
|
src/gfx/pxl8_atlas.c
|
||||||
src/gfx/pxl8_blit.c
|
src/gfx/pxl8_blit.c
|
||||||
src/gfx/pxl8_colormap.c
|
src/gfx/pxl8_colormap.c
|
||||||
src/gfx/pxl8_cpu.c
|
|
||||||
src/gfx/pxl8_dither.c
|
src/gfx/pxl8_dither.c
|
||||||
|
src/gfx/pxl8_render.c
|
||||||
|
src/gfx/pxl8_shader_registry.c
|
||||||
|
src/gfx/pxl8_shader_runtime.c
|
||||||
src/gfx/pxl8_font.c
|
src/gfx/pxl8_font.c
|
||||||
src/gfx/pxl8_gfx.c
|
src/gfx/pxl8_gfx.c
|
||||||
src/gfx/pxl8_glows.c
|
|
||||||
src/gfx/pxl8_lightmap.c
|
src/gfx/pxl8_lightmap.c
|
||||||
src/gfx/pxl8_lights.c
|
src/gfx/pxl8_lights.c
|
||||||
src/gfx/pxl8_mesh.c
|
src/gfx/pxl8_mesh.c
|
||||||
|
|
@ -461,11 +539,11 @@ case "$COMMAND" in
|
||||||
LUAJIT_LIB="lib/luajit/src/libluajit.a"
|
LUAJIT_LIB="lib/luajit/src/libluajit.a"
|
||||||
OBJECT_DIR="$BUILDDIR/obj"
|
OBJECT_DIR="$BUILDDIR/obj"
|
||||||
mkdir -p "$OBJECT_DIR"
|
mkdir -p "$OBJECT_DIR"
|
||||||
|
|
||||||
OBJECTS=""
|
OBJECTS=""
|
||||||
NEED_LINK=false
|
NEED_LINK=false
|
||||||
SOURCES_COMPILED=""
|
SOURCES_COMPILED=""
|
||||||
|
|
||||||
for src_file in $LIB_SOURCE_FILES; do
|
for src_file in $LIB_SOURCE_FILES; do
|
||||||
obj_name=$(basename "$src_file" .c).o
|
obj_name=$(basename "$src_file" .c).o
|
||||||
obj_file="$OBJECT_DIR/$obj_name"
|
obj_file="$OBJECT_DIR/$obj_name"
|
||||||
|
|
@ -477,7 +555,9 @@ case "$COMMAND" in
|
||||||
SOURCES_COMPILED="yes"
|
SOURCES_COMPILED="yes"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
compile_shaders "$MODE"
|
||||||
|
|
||||||
for src_file in $PXL8_SOURCE_FILES; do
|
for src_file in $PXL8_SOURCE_FILES; do
|
||||||
obj_name=$(basename "$src_file" .c).o
|
obj_name=$(basename "$src_file" .c).o
|
||||||
obj_file="$OBJECT_DIR/$obj_name"
|
obj_file="$OBJECT_DIR/$obj_name"
|
||||||
|
|
@ -509,7 +589,7 @@ case "$COMMAND" in
|
||||||
|
|
||||||
if [[ "$LUAJIT_LIB" -nt "$EXECUTABLE" ]] || [[ "$NEED_LINK" == true ]]; then
|
if [[ "$LUAJIT_LIB" -nt "$EXECUTABLE" ]] || [[ "$NEED_LINK" == true ]]; then
|
||||||
print_info "Linking executable"
|
print_info "Linking executable"
|
||||||
if ! $CC $LINKER_FLAGS $OBJECTS $LUAJIT_LIB $LIBS -o "$EXECUTABLE"; then
|
if ! $CC $LINKER_FLAGS $OBJECTS $SHADER_OBJECTS $LUAJIT_LIB $LIBS -o "$EXECUTABLE"; then
|
||||||
print_error "Linking failed"
|
print_error "Linking failed"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
@ -578,7 +658,7 @@ case "$COMMAND" in
|
||||||
|
|
||||||
if [[ "$CLEAN_ALL" == true ]]; then
|
if [[ "$CLEAN_ALL" == true ]]; then
|
||||||
print_info "Removing build artifacts and dependencies"
|
print_info "Removing build artifacts and dependencies"
|
||||||
rm -rf "$BUILD_PATH" "$BIN_PATH" lib
|
rm -rf "$BUILD_PATH" "$BIN_PATH" .build/shaders lib
|
||||||
clean_server
|
clean_server
|
||||||
print_info "Cleaned all"
|
print_info "Cleaned all"
|
||||||
elif [[ "$CLEAN_DEPS" == true ]]; then
|
elif [[ "$CLEAN_DEPS" == true ]]; then
|
||||||
|
|
@ -587,7 +667,7 @@ case "$COMMAND" in
|
||||||
print_info "Cleaned dependencies"
|
print_info "Cleaned dependencies"
|
||||||
else
|
else
|
||||||
print_info "Removing build artifacts"
|
print_info "Removing build artifacts"
|
||||||
rm -rf "$BUILD_PATH" "$BIN_PATH"
|
rm -rf "$BUILD_PATH" "$BIN_PATH" .build/shaders
|
||||||
clean_server
|
clean_server
|
||||||
print_info "Cleaned"
|
print_info "Cleaned"
|
||||||
fi
|
fi
|
||||||
|
|
@ -626,6 +706,72 @@ case "$COMMAND" in
|
||||||
bash tools/aseprite/pxl8-ase.sh "$@"
|
bash tools/aseprite/pxl8-ase.sh "$@"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
profile)
|
||||||
|
if [[ "$(uname)" != "Linux" ]]; then
|
||||||
|
print_error "Profiling with perf is only supported on Linux"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v perf >/dev/null 2>&1; then
|
||||||
|
print_error "perf not found. Install linux-tools or perf package."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -d "lib/FlameGraph" ]]; then
|
||||||
|
mkdir -p lib
|
||||||
|
update_flamegraph
|
||||||
|
fi
|
||||||
|
|
||||||
|
"$0" build || exit 1
|
||||||
|
|
||||||
|
PROFILE_DIR=".build/debug/profile"
|
||||||
|
mkdir -p "$PROFILE_DIR"
|
||||||
|
|
||||||
|
CART=""
|
||||||
|
PERF_DURATION=30
|
||||||
|
for arg in "$@"; do
|
||||||
|
if [[ "$arg" =~ ^--duration=([0-9]+)$ ]]; then
|
||||||
|
PERF_DURATION="${BASH_REMATCH[1]}"
|
||||||
|
elif [[ "$arg" != "--release" ]] && [[ -z "$CART" ]]; then
|
||||||
|
CART="$arg"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
[[ -z "$CART" ]] && CART="demo"
|
||||||
|
|
||||||
|
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
|
||||||
|
PERF_DATA="$PROFILE_DIR/perf_${TIMESTAMP}.data"
|
||||||
|
PERF_SCRIPT="$PROFILE_DIR/perf_${TIMESTAMP}.perf"
|
||||||
|
FOLDED="$PROFILE_DIR/perf_${TIMESTAMP}.folded"
|
||||||
|
SVG="$PROFILE_DIR/flamegraph_${TIMESTAMP}.svg"
|
||||||
|
|
||||||
|
print_info "Starting server..."
|
||||||
|
./bin/debug/pxl8d &
|
||||||
|
SERVER_PID=$!
|
||||||
|
sleep 0.5
|
||||||
|
|
||||||
|
trap "kill $SERVER_PID 2>/dev/null; wait $SERVER_PID 2>/dev/null" EXIT
|
||||||
|
|
||||||
|
print_info "Profiling pxl8 for ${PERF_DURATION}s (Ctrl+C to stop early)..."
|
||||||
|
perf record -F 99 -g --call-graph dwarf -o "$PERF_DATA" -- \
|
||||||
|
timeout "${PERF_DURATION}s" ./bin/debug/pxl8 "$CART" 2>/dev/null || true
|
||||||
|
|
||||||
|
print_info "Processing profile data..."
|
||||||
|
perf script -i "$PERF_DATA" > "$PERF_SCRIPT"
|
||||||
|
|
||||||
|
print_info "Generating flamegraph..."
|
||||||
|
lib/FlameGraph/stackcollapse-perf.pl "$PERF_SCRIPT" > "$FOLDED"
|
||||||
|
lib/FlameGraph/flamegraph.pl --cp --colors orange --title "pxl8 profile" "$FOLDED" > "$SVG"
|
||||||
|
|
||||||
|
rm -f "$PERF_DATA" "$PERF_SCRIPT" "$FOLDED"
|
||||||
|
|
||||||
|
print_info "Flamegraph: $SVG"
|
||||||
|
|
||||||
|
if command -v xdg-open >/dev/null 2>&1; then
|
||||||
|
xdg-open "$SVG" 2>/dev/null &
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
help|--help|-h|"")
|
help|--help|-h|"")
|
||||||
print_usage
|
print_usage
|
||||||
;;
|
;;
|
||||||
|
|
|
||||||
|
|
@ -36,9 +36,8 @@ fn main() {
|
||||||
|
|
||||||
let bindings = bindgen::Builder::default()
|
let bindings = bindgen::Builder::default()
|
||||||
.header(pxl8_src.join("core/pxl8_log.h").to_str().unwrap())
|
.header(pxl8_src.join("core/pxl8_log.h").to_str().unwrap())
|
||||||
.header(pxl8_src.join("sim/pxl8_sim.h").to_str().unwrap())
|
|
||||||
.header(pxl8_src.join("vxl/pxl8_vxl.h").to_str().unwrap())
|
|
||||||
.header(pxl8_src.join("math/pxl8_noise.h").to_str().unwrap())
|
.header(pxl8_src.join("math/pxl8_noise.h").to_str().unwrap())
|
||||||
|
.header(pxl8_src.join("sim/pxl8_sim.h").to_str().unwrap())
|
||||||
.clang_arg(format!("-I{}", pxl8_src.join("bsp").display()))
|
.clang_arg(format!("-I{}", pxl8_src.join("bsp").display()))
|
||||||
.clang_arg(format!("-I{}", pxl8_src.join("core").display()))
|
.clang_arg(format!("-I{}", pxl8_src.join("core").display()))
|
||||||
.clang_arg(format!("-I{}", pxl8_src.join("math").display()))
|
.clang_arg(format!("-I{}", pxl8_src.join("math").display()))
|
||||||
|
|
@ -50,6 +49,11 @@ fn main() {
|
||||||
.blocklist_item("FP_ZERO")
|
.blocklist_item("FP_ZERO")
|
||||||
.blocklist_item("FP_SUBNORMAL")
|
.blocklist_item("FP_SUBNORMAL")
|
||||||
.blocklist_item("FP_NORMAL")
|
.blocklist_item("FP_NORMAL")
|
||||||
|
.blocklist_type("pxl8_vec2")
|
||||||
|
.blocklist_type("pxl8_vec3")
|
||||||
|
.blocklist_type("pxl8_vec4")
|
||||||
|
.blocklist_type("pxl8_mat4")
|
||||||
|
.raw_line("pub use crate::math::{pxl8_vec2, pxl8_vec3, pxl8_vec4, pxl8_mat4};")
|
||||||
.clang_arg("-DPXL8_NO_SIMD")
|
.clang_arg("-DPXL8_NO_SIMD")
|
||||||
.use_core()
|
.use_core()
|
||||||
.rustified_enum(".*")
|
.rustified_enum(".*")
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,38 @@ impl Default for CellPortals {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Clone for Face {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
first_edge: self.first_edge,
|
||||||
|
lightmap_offset: self.lightmap_offset,
|
||||||
|
num_edges: self.num_edges,
|
||||||
|
plane_id: self.plane_id,
|
||||||
|
side: self.side,
|
||||||
|
styles: self.styles,
|
||||||
|
material_id: self.material_id,
|
||||||
|
aabb_min: self.aabb_min,
|
||||||
|
aabb_max: self.aabb_max,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for Plane {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
dist: self.dist,
|
||||||
|
normal: self.normal,
|
||||||
|
type_: self.type_,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for Vertex {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self { position: self.position }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Bsp {
|
pub struct Bsp {
|
||||||
inner: pxl8_bsp,
|
inner: pxl8_bsp,
|
||||||
pub cell_portals: Box<[CellPortals]>,
|
pub cell_portals: Box<[CellPortals]>,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,43 @@
|
||||||
use core::ops::{Add, Mul, Sub};
|
use core::ops::{Add, Mul, Sub};
|
||||||
|
|
||||||
use crate::pxl8::pxl8_vec3;
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Vec2 {
|
||||||
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
|
}
|
||||||
|
|
||||||
pub type Vec3 = pxl8_vec3;
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Vec3 {
|
||||||
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
|
pub z: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Vec4 {
|
||||||
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
|
pub z: f32,
|
||||||
|
pub w: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Mat4 {
|
||||||
|
pub m: [f32; 16],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub type pxl8_vec2 = Vec2;
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub type pxl8_vec3 = Vec3;
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub type pxl8_vec4 = Vec4;
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub type pxl8_mat4 = Mat4;
|
||||||
|
|
||||||
pub const VEC3_ZERO: Vec3 = Vec3 { x: 0.0, y: 0.0, z: 0.0 };
|
pub const VEC3_ZERO: Vec3 = Vec3 { x: 0.0, y: 0.0, z: 0.0 };
|
||||||
pub const VEC3_Y: Vec3 = Vec3 { x: 0.0, y: 1.0, z: 0.0 };
|
pub const VEC3_Y: Vec3 = Vec3 { x: 0.0, y: 1.0, z: 0.0 };
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ use libm::sqrtf;
|
||||||
|
|
||||||
use crate::bsp::{Bsp, BspBuilder, CellPortals, Edge, Face, Leaf, Node, Plane, Portal, Vertex};
|
use crate::bsp::{Bsp, BspBuilder, CellPortals, Edge, Face, Leaf, Node, Plane, Portal, Vertex};
|
||||||
use crate::math::{Vec3, Vec3Ext};
|
use crate::math::{Vec3, Vec3Ext};
|
||||||
|
use crate::pxl8::{pxl8_vec3_cross, pxl8_vec3_dot, pxl8_vec3_normalize, pxl8_vec3_scale, pxl8_vec3_add, pxl8_vec3_sub};
|
||||||
|
|
||||||
pub const CELL_SIZE: f32 = 64.0;
|
pub const CELL_SIZE: f32 = 64.0;
|
||||||
pub const WALL_HEIGHT: f32 = 128.0;
|
pub const WALL_HEIGHT: f32 = 128.0;
|
||||||
|
|
@ -402,36 +403,139 @@ fn build_pvs_data(bsp: &mut BspBuilder, portals: &[CellPortals]) {
|
||||||
bsp.visdata = visdata;
|
bsp.visdata = visdata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const AO_NUM_SAMPLES: usize = 16;
|
||||||
|
const AO_RAY_LENGTH: f32 = 48.0;
|
||||||
|
|
||||||
|
fn generate_hemisphere_samples(normal: Vec3) -> [Vec3; AO_NUM_SAMPLES] {
|
||||||
|
let tangent = if normal.y.abs() < 0.9 {
|
||||||
|
unsafe { pxl8_vec3_normalize(pxl8_vec3_cross(normal, Vec3::new(0.0, 1.0, 0.0))) }
|
||||||
|
} else {
|
||||||
|
unsafe { pxl8_vec3_normalize(pxl8_vec3_cross(normal, Vec3::new(1.0, 0.0, 0.0))) }
|
||||||
|
};
|
||||||
|
let bitangent = unsafe { pxl8_vec3_cross(normal, tangent) };
|
||||||
|
|
||||||
|
let mut samples = [Vec3::new(0.0, 0.0, 0.0); AO_NUM_SAMPLES];
|
||||||
|
for i in 0..AO_NUM_SAMPLES {
|
||||||
|
let phi = (i as f32 / AO_NUM_SAMPLES as f32) * core::f32::consts::TAU;
|
||||||
|
let theta = ((i as f32 + 0.5) / AO_NUM_SAMPLES as f32) * (core::f32::consts::PI * 0.45);
|
||||||
|
let (sin_phi, cos_phi) = (libm::sinf(phi), libm::cosf(phi));
|
||||||
|
let (sin_theta, cos_theta) = (libm::sinf(theta), libm::cosf(theta));
|
||||||
|
|
||||||
|
let local_x = sin_theta * cos_phi;
|
||||||
|
let local_y = cos_theta;
|
||||||
|
let local_z = sin_theta * sin_phi;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let t_contrib = pxl8_vec3_scale(tangent, local_x);
|
||||||
|
let n_contrib = pxl8_vec3_scale(normal, local_y);
|
||||||
|
let b_contrib = pxl8_vec3_scale(bitangent, local_z);
|
||||||
|
samples[i] = pxl8_vec3_normalize(pxl8_vec3_add(pxl8_vec3_add(t_contrib, n_contrib), b_contrib));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
samples
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ray_triangle_intersect(origin: Vec3, dir: Vec3, v0: Vec3, v1: Vec3, v2: Vec3, max_dist: f32) -> bool {
|
||||||
|
let edge1 = unsafe { pxl8_vec3_sub(v1, v0) };
|
||||||
|
let edge2 = unsafe { pxl8_vec3_sub(v2, v0) };
|
||||||
|
let h = unsafe { pxl8_vec3_cross(dir, edge2) };
|
||||||
|
let a = unsafe { pxl8_vec3_dot(edge1, h) };
|
||||||
|
|
||||||
|
if a > -0.0001 && a < 0.0001 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let f = 1.0 / a;
|
||||||
|
let s = unsafe { pxl8_vec3_sub(origin, v0) };
|
||||||
|
let u = f * unsafe { pxl8_vec3_dot(s, h) };
|
||||||
|
if u < 0.0 || u > 1.0 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let q = unsafe { pxl8_vec3_cross(s, edge1) };
|
||||||
|
let v = f * unsafe { pxl8_vec3_dot(dir, q) };
|
||||||
|
if v < 0.0 || u + v > 1.0 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let t = f * unsafe { pxl8_vec3_dot(edge2, q) };
|
||||||
|
t > 0.001 && t < max_dist
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_vertex_ao(bsp: &BspBuilder, pos: Vec3, normal: Vec3) -> f32 {
|
||||||
|
let samples = generate_hemisphere_samples(normal);
|
||||||
|
let offset_pos = unsafe { pxl8_vec3_add(pos, pxl8_vec3_scale(normal, 0.5)) };
|
||||||
|
|
||||||
|
let mut occluded = 0;
|
||||||
|
|
||||||
|
for sample_dir in &samples {
|
||||||
|
'face_loop: for face in &bsp.faces {
|
||||||
|
if face.num_edges < 3 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut verts = [Vec3::new(0.0, 0.0, 0.0); 4];
|
||||||
|
let mut num_verts = 0usize;
|
||||||
|
|
||||||
|
for e in 0..face.num_edges.min(4) {
|
||||||
|
let surfedge_idx = (face.first_edge + e as u32) as usize;
|
||||||
|
if surfedge_idx >= bsp.surfedges.len() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let edge_idx = bsp.surfedges[surfedge_idx];
|
||||||
|
let vert_idx = if edge_idx >= 0 {
|
||||||
|
let ei = edge_idx as usize;
|
||||||
|
if ei >= bsp.edges.len() { continue; }
|
||||||
|
bsp.edges[ei].vertex[0] as usize
|
||||||
|
} else {
|
||||||
|
let ei = (-edge_idx) as usize;
|
||||||
|
if ei >= bsp.edges.len() { continue; }
|
||||||
|
bsp.edges[ei].vertex[1] as usize
|
||||||
|
};
|
||||||
|
|
||||||
|
if vert_idx < bsp.vertices.len() {
|
||||||
|
verts[num_verts] = bsp.vertices[vert_idx].position;
|
||||||
|
num_verts += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if num_verts >= 3 {
|
||||||
|
if ray_triangle_intersect(offset_pos, *sample_dir, verts[0], verts[1], verts[2], AO_RAY_LENGTH) {
|
||||||
|
occluded += 1;
|
||||||
|
break 'face_loop;
|
||||||
|
}
|
||||||
|
if num_verts == 4 {
|
||||||
|
if ray_triangle_intersect(offset_pos, *sample_dir, verts[0], verts[2], verts[3], AO_RAY_LENGTH) {
|
||||||
|
occluded += 1;
|
||||||
|
break 'face_loop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
1.0 - (occluded as f32 / AO_NUM_SAMPLES as f32)
|
||||||
|
}
|
||||||
|
|
||||||
fn compute_vertex_light(
|
fn compute_vertex_light(
|
||||||
pos: Vec3,
|
pos: Vec3,
|
||||||
normal: Vec3,
|
normal: Vec3,
|
||||||
lights: &[LightSource],
|
lights: &[LightSource],
|
||||||
ambient: f32,
|
|
||||||
) -> f32 {
|
) -> f32 {
|
||||||
let mut total = ambient;
|
let mut total = 0.0;
|
||||||
|
|
||||||
for light in lights {
|
for light in lights {
|
||||||
let to_light = Vec3::new(
|
let to_light = unsafe { pxl8_vec3_sub(light.position, pos) };
|
||||||
light.position.x - pos.x,
|
let dist = unsafe { pxl8_vec3_dot(to_light, to_light) };
|
||||||
light.position.y - pos.y,
|
let dist = sqrtf(dist).max(1.0);
|
||||||
light.position.z - pos.z,
|
|
||||||
);
|
|
||||||
|
|
||||||
let dist_sq = to_light.x * to_light.x + to_light.y * to_light.y + to_light.z * to_light.z;
|
|
||||||
let dist = sqrtf(dist_sq).max(1.0);
|
|
||||||
|
|
||||||
if dist > light.radius {
|
if dist > light.radius {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let inv_dist = 1.0 / dist;
|
let light_dir = unsafe { pxl8_vec3_normalize(to_light) };
|
||||||
let light_dir = Vec3::new(
|
let ndotl = unsafe { pxl8_vec3_dot(normal, light_dir) }.max(0.0);
|
||||||
to_light.x * inv_dist,
|
|
||||||
to_light.y * inv_dist,
|
|
||||||
to_light.z * inv_dist,
|
|
||||||
);
|
|
||||||
|
|
||||||
let ndotl = (normal.x * light_dir.x + normal.y * light_dir.y + normal.z * light_dir.z).max(0.0);
|
|
||||||
|
|
||||||
let attenuation = (1.0 - dist / light.radius).max(0.0);
|
let attenuation = (1.0 - dist / light.radius).max(0.0);
|
||||||
let attenuation = attenuation * attenuation;
|
let attenuation = attenuation * attenuation;
|
||||||
|
|
@ -442,12 +546,13 @@ fn compute_vertex_light(
|
||||||
total.min(1.0)
|
total.min(1.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_bsp_vertex_lighting(bsp: &mut BspBuilder, lights: &[LightSource], ambient: f32) {
|
fn compute_bsp_vertex_lighting(bsp: &mut BspBuilder, lights: &[LightSource]) {
|
||||||
if bsp.vertices.is_empty() {
|
if bsp.vertices.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bsp.vertex_lights = vec![0u32; bsp.vertices.len()];
|
bsp.vertex_lights = vec![0u32; bsp.vertices.len()];
|
||||||
|
let mut vertex_normals: Vec<Option<Vec3>> = vec![None; bsp.vertices.len()];
|
||||||
|
|
||||||
for f in 0..bsp.faces.len() {
|
for f in 0..bsp.faces.len() {
|
||||||
let face = &bsp.faces[f];
|
let face = &bsp.faces[f];
|
||||||
|
|
@ -478,13 +583,27 @@ fn compute_bsp_vertex_lighting(bsp: &mut BspBuilder, lights: &[LightSource], amb
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let pos = bsp.vertices[vert_idx].position;
|
vertex_normals[vert_idx] = Some(normal);
|
||||||
let light = compute_vertex_light(pos, normal, lights, ambient);
|
|
||||||
|
|
||||||
let light_byte = (light * 255.0) as u8;
|
let pos = bsp.vertices[vert_idx].position;
|
||||||
bsp.vertex_lights[vert_idx] = ((light_byte as u32) << 24) | 0x00FFFFFF;
|
let direct = compute_vertex_light(pos, normal, lights);
|
||||||
|
let direct_byte = ((direct * 255.0).min(255.0)) as u8;
|
||||||
|
|
||||||
|
bsp.vertex_lights[vert_idx] = (bsp.vertex_lights[vert_idx] & 0x00FFFFFF) | ((direct_byte as u32) << 24);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for vert_idx in 0..bsp.vertices.len() {
|
||||||
|
let normal = match vertex_normals[vert_idx] {
|
||||||
|
Some(n) => n,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
let pos = bsp.vertices[vert_idx].position;
|
||||||
|
let ao = compute_vertex_ao(bsp, pos, normal);
|
||||||
|
let ao_byte = ((ao * 255.0).min(255.0)) as u8;
|
||||||
|
|
||||||
|
bsp.vertex_lights[vert_idx] = (bsp.vertex_lights[vert_idx] & 0xFF00FFFF) | ((ao_byte as u32) << 16);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn grid_to_bsp(bsp: &mut BspBuilder, grid: &RoomGrid) {
|
fn grid_to_bsp(bsp: &mut BspBuilder, grid: &RoomGrid) {
|
||||||
|
|
@ -926,17 +1045,27 @@ pub fn generate_rooms(params: &ProcgenParams) -> Bsp {
|
||||||
grid_to_bsp(&mut bsp, &grid);
|
grid_to_bsp(&mut bsp, &grid);
|
||||||
|
|
||||||
let light_height = 80.0;
|
let light_height = 80.0;
|
||||||
let lights: Vec<LightSource> = rooms.iter().map(|room| {
|
let fireball_pos = Vec3::new(384.0, light_height, 324.0);
|
||||||
|
let fireball_exclusion_radius = 150.0;
|
||||||
|
|
||||||
|
let lights: Vec<LightSource> = rooms.iter().filter_map(|room| {
|
||||||
let cx = (room.x as f32 + room.w as f32 / 2.0) * CELL_SIZE;
|
let cx = (room.x as f32 + room.w as f32 / 2.0) * CELL_SIZE;
|
||||||
let cy = (room.y as f32 + room.h as f32 / 2.0) * CELL_SIZE;
|
let cz = (room.y as f32 + room.h as f32 / 2.0) * CELL_SIZE;
|
||||||
LightSource {
|
|
||||||
position: Vec3::new(cx, light_height, cy),
|
let dx = cx - fireball_pos.x;
|
||||||
intensity: 0.8,
|
let dz = cz - fireball_pos.z;
|
||||||
radius: 300.0,
|
if dx * dx + dz * dz < fireball_exclusion_radius * fireball_exclusion_radius {
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some(LightSource {
|
||||||
|
position: Vec3::new(cx, light_height, cz),
|
||||||
|
intensity: 1.8,
|
||||||
|
radius: 160.0,
|
||||||
|
})
|
||||||
}).collect();
|
}).collect();
|
||||||
|
|
||||||
compute_bsp_vertex_lighting(&mut bsp, &lights, 0.1);
|
compute_bsp_vertex_lighting(&mut bsp, &lights);
|
||||||
|
|
||||||
bsp.into()
|
bsp.into()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,20 @@ impl Default for Entity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Clone for Entity {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
pos: self.pos,
|
||||||
|
vel: self.vel,
|
||||||
|
yaw: self.yaw,
|
||||||
|
pitch: self.pitch,
|
||||||
|
flags: self.flags,
|
||||||
|
kind: self.kind,
|
||||||
|
_pad: self._pad,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Simulation {
|
pub struct Simulation {
|
||||||
pub entities: Vec<Entity>,
|
pub entities: Vec<Entity>,
|
||||||
pub free_list: Vec<u32>,
|
pub free_list: Vec<u32>,
|
||||||
|
|
|
||||||
|
|
@ -27,12 +27,8 @@ static const char embed_pxl8_effects[] = {
|
||||||
#embed "src/lua/pxl8/effects.lua"
|
#embed "src/lua/pxl8/effects.lua"
|
||||||
, 0 };
|
, 0 };
|
||||||
|
|
||||||
static const char embed_pxl8_gfx2d[] = {
|
static const char embed_pxl8_gfx[] = {
|
||||||
#embed "src/lua/pxl8/gfx2d.lua"
|
#embed "src/lua/pxl8/gfx.lua"
|
||||||
, 0 };
|
|
||||||
|
|
||||||
static const char embed_pxl8_gfx3d[] = {
|
|
||||||
#embed "src/lua/pxl8/gfx3d.lua"
|
|
||||||
, 0 };
|
, 0 };
|
||||||
|
|
||||||
static const char embed_pxl8_gui[] = {
|
static const char embed_pxl8_gui[] = {
|
||||||
|
|
@ -86,8 +82,7 @@ static const pxl8_embed pxl8_embeds[] = {
|
||||||
PXL8_EMBED_ENTRY(embed_pxl8_bytes, "pxl8.bytes"),
|
PXL8_EMBED_ENTRY(embed_pxl8_bytes, "pxl8.bytes"),
|
||||||
PXL8_EMBED_ENTRY(embed_pxl8_core, "pxl8.core"),
|
PXL8_EMBED_ENTRY(embed_pxl8_core, "pxl8.core"),
|
||||||
PXL8_EMBED_ENTRY(embed_pxl8_effects, "pxl8.effects"),
|
PXL8_EMBED_ENTRY(embed_pxl8_effects, "pxl8.effects"),
|
||||||
PXL8_EMBED_ENTRY(embed_pxl8_gfx2d, "pxl8.gfx2d"),
|
PXL8_EMBED_ENTRY(embed_pxl8_gfx, "pxl8.gfx"),
|
||||||
PXL8_EMBED_ENTRY(embed_pxl8_gfx3d, "pxl8.gfx3d"),
|
|
||||||
PXL8_EMBED_ENTRY(embed_pxl8_gui, "pxl8.gui"),
|
PXL8_EMBED_ENTRY(embed_pxl8_gui, "pxl8.gui"),
|
||||||
PXL8_EMBED_ENTRY(embed_pxl8_input, "pxl8.input"),
|
PXL8_EMBED_ENTRY(embed_pxl8_input, "pxl8.input"),
|
||||||
PXL8_EMBED_ENTRY(embed_pxl8_math, "pxl8.math"),
|
PXL8_EMBED_ENTRY(embed_pxl8_math, "pxl8.math"),
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,7 @@ static screen_rect project_portal_to_screen(const pxl8_bsp_portal* portal, const
|
||||||
}
|
}
|
||||||
|
|
||||||
static void collect_face_to_mesh(const pxl8_bsp* bsp, const pxl8_bsp_render_state* state,
|
static void collect_face_to_mesh(const pxl8_bsp* bsp, const pxl8_bsp_render_state* state,
|
||||||
u32 face_id, pxl8_mesh* mesh) {
|
u32 face_id, pxl8_mesh* mesh, u8 ambient) {
|
||||||
const pxl8_bsp_face* face = &bsp->faces[face_id];
|
const pxl8_bsp_face* face = &bsp->faces[face_id];
|
||||||
if (face->num_edges < 3) return;
|
if (face->num_edges < 3) return;
|
||||||
|
|
||||||
|
|
@ -179,7 +179,11 @@ static void collect_face_to_mesh(const pxl8_bsp* bsp, const pxl8_bsp_render_stat
|
||||||
|
|
||||||
u8 light = 255;
|
u8 light = 255;
|
||||||
if (bsp->vertex_lights && vert_idx < bsp->num_vertex_lights) {
|
if (bsp->vertex_lights && vert_idx < bsp->num_vertex_lights) {
|
||||||
light = (bsp->vertex_lights[vert_idx] >> 24) & 0xFF;
|
u32 packed = bsp->vertex_lights[vert_idx];
|
||||||
|
u8 direct = (packed >> 24) & 0xFF;
|
||||||
|
u8 ao = (packed >> 16) & 0xFF;
|
||||||
|
f32 combined = (f32)direct + ((f32)ambient / 255.0f) * (f32)ao;
|
||||||
|
light = (u8)(combined > 255.0f ? 255.0f : combined);
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_vertex vtx = {
|
pxl8_vertex vtx = {
|
||||||
|
|
@ -230,7 +234,7 @@ void pxl8_bsp_render_face(pxl8_gfx* gfx, const pxl8_bsp* bsp, u32 face_id, const
|
||||||
pxl8_mesh* mesh = pxl8_mesh_create(64, 192);
|
pxl8_mesh* mesh = pxl8_mesh_create(64, 192);
|
||||||
if (!mesh) return;
|
if (!mesh) return;
|
||||||
|
|
||||||
collect_face_to_mesh(bsp, NULL, face_id, mesh);
|
collect_face_to_mesh(bsp, NULL, face_id, mesh, pxl8_gfx_get_ambient(gfx));
|
||||||
|
|
||||||
if (mesh->index_count > 0) {
|
if (mesh->index_count > 0) {
|
||||||
pxl8_mat4 identity = pxl8_mat4_identity();
|
pxl8_mat4 identity = pxl8_mat4_identity();
|
||||||
|
|
@ -242,15 +246,8 @@ void pxl8_bsp_render_face(pxl8_gfx* gfx, const pxl8_bsp* bsp, u32 face_id, const
|
||||||
|
|
||||||
void pxl8_bsp_render(pxl8_gfx* gfx, const pxl8_bsp* bsp,
|
void pxl8_bsp_render(pxl8_gfx* gfx, const pxl8_bsp* bsp,
|
||||||
pxl8_bsp_render_state* state, pxl8_vec3 camera_pos) {
|
pxl8_bsp_render_state* state, pxl8_vec3 camera_pos) {
|
||||||
if (!gfx || !bsp || !state || bsp->num_faces == 0) {
|
if (!gfx || !bsp || !state || bsp->num_faces == 0) return;
|
||||||
return;
|
if (!state->materials || state->num_materials == 0) return;
|
||||||
}
|
|
||||||
if (!bsp->cell_portals || bsp->num_cell_portals == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!state->materials || state->num_materials == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pxl8_frustum* frustum = pxl8_3d_get_frustum(gfx);
|
const pxl8_frustum* frustum = pxl8_3d_get_frustum(gfx);
|
||||||
const pxl8_mat4* vp = pxl8_3d_get_view_proj(gfx);
|
const pxl8_mat4* vp = pxl8_3d_get_view_proj(gfx);
|
||||||
|
|
@ -259,6 +256,38 @@ void pxl8_bsp_render(pxl8_gfx* gfx, const pxl8_bsp* bsp,
|
||||||
i32 camera_leaf = pxl8_bsp_find_leaf(bsp, camera_pos);
|
i32 camera_leaf = pxl8_bsp_find_leaf(bsp, camera_pos);
|
||||||
if (camera_leaf < 0 || (u32)camera_leaf >= bsp->num_leafs) return;
|
if (camera_leaf < 0 || (u32)camera_leaf >= bsp->num_leafs) return;
|
||||||
|
|
||||||
|
if (!bsp->cell_portals || bsp->num_cell_portals == 0) {
|
||||||
|
pxl8_mesh* mesh = pxl8_mesh_create(8192, 16384);
|
||||||
|
if (!mesh) return;
|
||||||
|
|
||||||
|
u32 current_material = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
for (u32 face_id = 0; face_id < bsp->num_faces; face_id++) {
|
||||||
|
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 >= state->num_materials) continue;
|
||||||
|
|
||||||
|
if (material_id != current_material && mesh->index_count > 0 && current_material < state->num_materials) {
|
||||||
|
pxl8_mat4 identity = pxl8_mat4_identity();
|
||||||
|
pxl8_3d_draw_mesh(gfx, mesh, &identity, &state->materials[current_material]);
|
||||||
|
pxl8_mesh_clear(mesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
current_material = material_id;
|
||||||
|
collect_face_to_mesh(bsp, state, face_id, mesh, pxl8_gfx_get_ambient(gfx));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mesh->index_count > 0 && current_material < state->num_materials) {
|
||||||
|
pxl8_mat4 identity = pxl8_mat4_identity();
|
||||||
|
pxl8_3d_draw_mesh(gfx, mesh, &identity, &state->materials[current_material]);
|
||||||
|
}
|
||||||
|
|
||||||
|
pxl8_mesh_destroy(mesh);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!state->render_face_flags && state->num_faces > 0) {
|
if (!state->render_face_flags && state->num_faces > 0) {
|
||||||
state->render_face_flags = pxl8_calloc(state->num_faces, 1);
|
state->render_face_flags = pxl8_calloc(state->num_faces, 1);
|
||||||
if (!state->render_face_flags) return;
|
if (!state->render_face_flags) return;
|
||||||
|
|
@ -370,7 +399,7 @@ void pxl8_bsp_render(pxl8_gfx* gfx, const pxl8_bsp* bsp,
|
||||||
}
|
}
|
||||||
|
|
||||||
current_material = material_id;
|
current_material = material_id;
|
||||||
collect_face_to_mesh(bsp, state, face_id, mesh);
|
collect_face_to_mesh(bsp, state, face_id, mesh, pxl8_gfx_get_ambient(gfx));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -420,10 +449,3 @@ void pxl8_bsp_set_material(pxl8_bsp_render_state* state, u16 material_id, const
|
||||||
state->materials[material_id].u_offset = u_offset;
|
state->materials[material_id].u_offset = u_offset;
|
||||||
state->materials[material_id].v_offset = v_offset;
|
state->materials[material_id].v_offset = v_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_bsp_set_wireframe(pxl8_bsp_render_state* state, bool wireframe) {
|
|
||||||
if (!state || !state->materials) return;
|
|
||||||
for (u32 i = 0; i < state->num_materials; i++) {
|
|
||||||
state->materials[i].wireframe = wireframe;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ void pxl8_bsp_render_face(pxl8_gfx* gfx, const pxl8_bsp* bsp, u32 face_id,
|
||||||
const pxl8_gfx_material* material);
|
const pxl8_gfx_material* material);
|
||||||
void pxl8_bsp_set_material(pxl8_bsp_render_state* state, u16 material_id,
|
void pxl8_bsp_set_material(pxl8_bsp_render_state* state, u16 material_id,
|
||||||
const pxl8_gfx_material* material);
|
const pxl8_gfx_material* material);
|
||||||
void pxl8_bsp_set_wireframe(pxl8_bsp_render_state* state, bool wireframe);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -393,13 +393,16 @@ pxl8_result pxl8_update(pxl8* sys) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef PXL8_ASYNC_THREADS
|
#ifdef PXL8_ASYNC_THREADS
|
||||||
if (game->world && (pxl8_world_local_player(game->world))) {
|
if (game->world) {
|
||||||
pxl8_input_msg msg = {0};
|
pxl8_input_msg msg = {0};
|
||||||
msg.move_x = (pxl8_key_down(&game->input, "d") ? 1.0f : 0.0f) - (pxl8_key_down(&game->input, "a") ? 1.0f : 0.0f);
|
msg.move_x = (pxl8_key_down(&game->input, "d") ? 1.0f : 0.0f) - (pxl8_key_down(&game->input, "a") ? 1.0f : 0.0f);
|
||||||
msg.move_y = (pxl8_key_down(&game->input, "w") ? 1.0f : 0.0f) - (pxl8_key_down(&game->input, "s") ? 1.0f : 0.0f);
|
msg.move_y = (pxl8_key_down(&game->input, "w") ? 1.0f : 0.0f) - (pxl8_key_down(&game->input, "s") ? 1.0f : 0.0f);
|
||||||
msg.look_dx = (f32)pxl8_mouse_dx(&game->input);
|
if (game->input.mouse_relative_mode) {
|
||||||
msg.look_dy = (f32)pxl8_mouse_dy(&game->input);
|
msg.look_dx = (f32)pxl8_mouse_dx(&game->input);
|
||||||
|
msg.look_dy = (f32)pxl8_mouse_dy(&game->input);
|
||||||
|
}
|
||||||
msg.buttons = pxl8_key_down(&game->input, "space") ? 1 : 0;
|
msg.buttons = pxl8_key_down(&game->input, "space") ? 1 : 0;
|
||||||
|
|
||||||
pxl8_world_push_input(game->world, &msg);
|
pxl8_world_push_input(game->world, &msg);
|
||||||
}
|
}
|
||||||
pxl8_net_update(game->net, dt);
|
pxl8_net_update(game->net, dt);
|
||||||
|
|
@ -409,7 +412,6 @@ pxl8_result pxl8_update(pxl8* sys) {
|
||||||
pxl8_net_update(game->net, dt);
|
pxl8_net_update(game->net, dt);
|
||||||
pxl8_world_sync(game->world, game->net);
|
pxl8_world_sync(game->world, game->net);
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_world_update(game->world, &game->input, dt);
|
pxl8_world_update(game->world, &game->input, dt);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -563,9 +565,6 @@ void pxl8_set_relative_mouse_mode(pxl8* sys, bool enabled) {
|
||||||
sys->hal->set_relative_mouse_mode(sys->platform_data, enabled);
|
sys->hal->set_relative_mouse_mode(sys->platform_data, enabled);
|
||||||
if (sys->game) {
|
if (sys->game) {
|
||||||
sys->game->input.mouse_relative_mode = enabled;
|
sys->game->input.mouse_relative_mode = enabled;
|
||||||
#ifdef PXL8_ASYNC_THREADS
|
|
||||||
pxl8_world_pause_sim(sys->game->world, !enabled);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,14 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
#define pxl8_likely(x) __builtin_expect(!!(x), 1)
|
||||||
|
#define pxl8_unlikely(x) __builtin_expect(!!(x), 0)
|
||||||
|
#else
|
||||||
|
#define pxl8_likely(x) (x)
|
||||||
|
#define pxl8_unlikely(x) (x)
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef pxl8_min
|
#ifndef pxl8_min
|
||||||
#define pxl8_min(a, b) ((a) < (b) ? (a) : (b))
|
#define pxl8_min(a, b) ((a) < (b) ? (a) : (b))
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,10 @@ typedef struct pxl8_atlas pxl8_atlas;
|
||||||
|
|
||||||
typedef struct pxl8_atlas_entry {
|
typedef struct pxl8_atlas_entry {
|
||||||
bool active;
|
bool active;
|
||||||
|
i32 h, w, x, y;
|
||||||
|
u8 log2_h, log2_w;
|
||||||
u32 texture_id;
|
u32 texture_id;
|
||||||
i32 x, y, w, h;
|
|
||||||
u32 tiled_base;
|
u32 tiled_base;
|
||||||
u8 log2_w;
|
|
||||||
} pxl8_atlas_entry;
|
} pxl8_atlas_entry;
|
||||||
|
|
||||||
static inline u32 pxl8_tile_addr(u32 u, u32 v, u8 log2_w) {
|
static inline u32 pxl8_tile_addr(u32 u, u32 v, u8 log2_w) {
|
||||||
|
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
#ifndef PXL8_BACKEND_H
|
|
||||||
#define PXL8_BACKEND_H
|
|
||||||
|
|
||||||
#include "pxl8_cpu.h"
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
PXL8_GFX_BACKEND_CPU,
|
|
||||||
PXL8_GFX_BACKEND_GPU,
|
|
||||||
} pxl8_gfx_backend_type;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
pxl8_gfx_backend_type type;
|
|
||||||
union {
|
|
||||||
pxl8_cpu_backend* cpu;
|
|
||||||
void* gpu;
|
|
||||||
};
|
|
||||||
} pxl8_gfx_backend;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -58,7 +58,7 @@ static inline u8 pxl8_colormap_lookup(const pxl8_colormap* cm, u8 pal_idx, pxl8_
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
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);
|
u8 dithered = pxl8_gfx_dither((f32)intensity + 0.5f, x, y);
|
||||||
u32 light_row = ((u32)light_color << 3) + (dithered >> 5);
|
u32 light_row = ((u32)light_color << 3) + (dithered >> 5);
|
||||||
return cm->table[(light_row << 8) + pal_idx];
|
return cm->table[(light_row << 8) + pal_idx];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1669
src/gfx/pxl8_cpu.c
1669
src/gfx/pxl8_cpu.c
File diff suppressed because it is too large
Load diff
|
|
@ -1,94 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "pxl8_atlas.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
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct pxl8_cpu_backend pxl8_cpu_backend;
|
|
||||||
typedef struct pxl8_cpu_render_target pxl8_cpu_render_target;
|
|
||||||
|
|
||||||
typedef struct pxl8_cpu_render_target_desc {
|
|
||||||
u32 width;
|
|
||||||
u32 height;
|
|
||||||
bool with_depth;
|
|
||||||
bool with_lighting;
|
|
||||||
} pxl8_cpu_render_target_desc;
|
|
||||||
|
|
||||||
pxl8_cpu_backend* pxl8_cpu_create(u32 width, u32 height);
|
|
||||||
void pxl8_cpu_destroy(pxl8_cpu_backend* cpu);
|
|
||||||
|
|
||||||
void pxl8_cpu_begin_frame(pxl8_cpu_backend* cpu, const pxl8_3d_frame* frame);
|
|
||||||
void pxl8_cpu_end_frame(pxl8_cpu_backend* cpu);
|
|
||||||
|
|
||||||
void pxl8_cpu_clear(pxl8_cpu_backend* cpu, u8 color);
|
|
||||||
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);
|
|
||||||
void pxl8_cpu_draw_line_2d(pxl8_cpu_backend* cpu, i32 x0, i32 y0, i32 x1, i32 y1, u8 color);
|
|
||||||
void pxl8_cpu_draw_rect(pxl8_cpu_backend* cpu, i32 x, i32 y, i32 w, i32 h, u8 color);
|
|
||||||
void pxl8_cpu_draw_rect_fill(pxl8_cpu_backend* cpu, i32 x, i32 y, i32 w, i32 h, u8 color);
|
|
||||||
void pxl8_cpu_draw_circle(pxl8_cpu_backend* cpu, i32 cx, i32 cy, i32 radius, u8 color);
|
|
||||||
void pxl8_cpu_draw_circle_fill(pxl8_cpu_backend* cpu, i32 cx, i32 cy, i32 radius, u8 color);
|
|
||||||
void pxl8_cpu_draw_line_3d(pxl8_cpu_backend* cpu, pxl8_vec3 v0, pxl8_vec3 v1, u8 color);
|
|
||||||
|
|
||||||
void pxl8_cpu_draw_mesh(
|
|
||||||
pxl8_cpu_backend* cpu,
|
|
||||||
const pxl8_mesh* mesh,
|
|
||||||
const pxl8_mat4* model,
|
|
||||||
const pxl8_gfx_material* material,
|
|
||||||
const pxl8_atlas* textures
|
|
||||||
);
|
|
||||||
|
|
||||||
u8* pxl8_cpu_get_framebuffer(pxl8_cpu_backend* cpu);
|
|
||||||
u32* pxl8_cpu_get_light_accum(pxl8_cpu_backend* cpu);
|
|
||||||
u32* pxl8_cpu_get_output(pxl8_cpu_backend* cpu);
|
|
||||||
u16* pxl8_cpu_get_zbuffer(pxl8_cpu_backend* cpu);
|
|
||||||
u32 pxl8_cpu_get_height(const pxl8_cpu_backend* cpu);
|
|
||||||
u32 pxl8_cpu_get_width(const pxl8_cpu_backend* cpu);
|
|
||||||
|
|
||||||
void pxl8_cpu_render_glows(
|
|
||||||
pxl8_cpu_backend* cpu,
|
|
||||||
const pxl8_glow* glows,
|
|
||||||
u32 glow_count
|
|
||||||
);
|
|
||||||
|
|
||||||
void pxl8_cpu_resolve(pxl8_cpu_backend* cpu);
|
|
||||||
|
|
||||||
pxl8_cpu_render_target* pxl8_cpu_create_render_target(const pxl8_cpu_render_target_desc* desc);
|
|
||||||
void pxl8_cpu_destroy_render_target(pxl8_cpu_render_target* target);
|
|
||||||
|
|
||||||
pxl8_cpu_render_target* pxl8_cpu_get_target(pxl8_cpu_backend* cpu);
|
|
||||||
void pxl8_cpu_set_target(pxl8_cpu_backend* cpu, pxl8_cpu_render_target* target);
|
|
||||||
|
|
||||||
void pxl8_cpu_blit(pxl8_cpu_backend* cpu, pxl8_cpu_render_target* src, i32 x, i32 y, u8 transparent_idx);
|
|
||||||
|
|
||||||
bool pxl8_cpu_push_target(pxl8_cpu_backend* cpu);
|
|
||||||
void pxl8_cpu_pop_target(pxl8_cpu_backend* cpu);
|
|
||||||
u32 pxl8_cpu_get_target_depth(const pxl8_cpu_backend* cpu);
|
|
||||||
|
|
||||||
u8* pxl8_cpu_render_target_get_framebuffer(pxl8_cpu_render_target* target);
|
|
||||||
u32 pxl8_cpu_render_target_get_height(const pxl8_cpu_render_target* target);
|
|
||||||
u32 pxl8_cpu_render_target_get_width(const pxl8_cpu_render_target* target);
|
|
||||||
u32* pxl8_cpu_render_target_get_light_accum(pxl8_cpu_render_target* target);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
@ -8,8 +8,16 @@ extern "C" {
|
||||||
|
|
||||||
extern const u8 PXL8_BAYER_4X4[16];
|
extern const u8 PXL8_BAYER_4X4[16];
|
||||||
|
|
||||||
static inline i8 pxl8_bayer_offset(u32 x, u32 y) {
|
static inline u8 pxl8_gfx_dither(f32 value, u32 x, u32 y) {
|
||||||
return (i8)(PXL8_BAYER_4X4[(y & 3) * 4 + (x & 3)]) - 8;
|
if (value <= 0.0f) return 0;
|
||||||
|
if (value >= 255.0f) return 255;
|
||||||
|
u8 base = (u8)value;
|
||||||
|
f32 frac = value - (f32)base;
|
||||||
|
f32 threshold = (PXL8_BAYER_4X4[(y & 3) * 4 + (x & 3)] + 0.5f) * (1.0f / 16.0f);
|
||||||
|
if (frac > threshold) {
|
||||||
|
return base < 255 ? (u8)(base + 1) : 255;
|
||||||
|
}
|
||||||
|
return base;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u8 pxl8_ordered_dither(u8 value, u32 x, u32 y) {
|
static inline u8 pxl8_ordered_dither(u8 value, u32 x, u32 y) {
|
||||||
|
|
@ -18,24 +26,6 @@ static inline u8 pxl8_ordered_dither(u8 value, u32 x, u32 y) {
|
||||||
return result > 255 ? 255 : (u8)result;
|
return result > 255 ? 255 : (u8)result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u8 pxl8_dither_light(u8 light, u32 x, u32 y) {
|
|
||||||
i8 offset = pxl8_bayer_offset(x, y) >> 1;
|
|
||||||
i16 result = (i16)light + offset;
|
|
||||||
if (result < 0) return 0;
|
|
||||||
if (result > 255) return 255;
|
|
||||||
return (u8)result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline u8 pxl8_dither_float(f32 value, u32 x, u32 y) {
|
|
||||||
u8 floor_val = (u8)value;
|
|
||||||
f32 frac = value - (f32)floor_val;
|
|
||||||
f32 threshold = (PXL8_BAYER_4X4[(y & 3) * 4 + (x & 3)] + 0.5f) * (1.0f / 16.0f);
|
|
||||||
if (frac > threshold) {
|
|
||||||
return floor_val < 255 ? floor_val + 1 : 255;
|
|
||||||
}
|
|
||||||
return floor_val;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
|
|
||||||
#include "pxl8_ase.h"
|
#include "pxl8_ase.h"
|
||||||
#include "pxl8_atlas.h"
|
#include "pxl8_atlas.h"
|
||||||
#include "pxl8_backend.h"
|
|
||||||
#include "pxl8_blit.h"
|
#include "pxl8_blit.h"
|
||||||
#include "pxl8_color.h"
|
#include "pxl8_color.h"
|
||||||
#include "pxl8_colormap.h"
|
#include "pxl8_colormap.h"
|
||||||
|
|
@ -15,36 +14,77 @@
|
||||||
#include "pxl8_macros.h"
|
#include "pxl8_macros.h"
|
||||||
#include "pxl8_math.h"
|
#include "pxl8_math.h"
|
||||||
#include "pxl8_mem.h"
|
#include "pxl8_mem.h"
|
||||||
|
#include "pxl8_render.h"
|
||||||
|
#include "pxl8_shader_registry.h"
|
||||||
#include "pxl8_sys.h"
|
#include "pxl8_sys.h"
|
||||||
#include "pxl8_types.h"
|
#include "pxl8_types.h"
|
||||||
|
|
||||||
|
#define PXL8_MAX_TARGET_STACK 8
|
||||||
|
|
||||||
typedef struct pxl8_sprite_cache_entry {
|
typedef struct pxl8_sprite_cache_entry {
|
||||||
char path[256];
|
char path[256];
|
||||||
u32 sprite_id;
|
u32 sprite_id;
|
||||||
bool active;
|
bool active;
|
||||||
} pxl8_sprite_cache_entry;
|
} pxl8_sprite_cache_entry;
|
||||||
|
|
||||||
|
typedef struct pxl8_target_entry {
|
||||||
|
pxl8_gfx_texture color;
|
||||||
|
pxl8_gfx_texture depth;
|
||||||
|
pxl8_gfx_texture light;
|
||||||
|
} pxl8_target_entry;
|
||||||
|
|
||||||
|
#define PXL8_MAX_FRAME_RESOURCES 512
|
||||||
|
#define PXL8_STREAM_VB_SIZE (256 * 1024)
|
||||||
|
#define PXL8_STREAM_IB_SIZE (512 * 1024)
|
||||||
|
|
||||||
|
typedef struct pxl8_frame_resources {
|
||||||
|
pxl8_gfx_texture textures[PXL8_MAX_FRAME_RESOURCES];
|
||||||
|
pxl8_gfx_buffer buffers[PXL8_MAX_FRAME_RESOURCES];
|
||||||
|
pxl8_gfx_pipeline pipelines[PXL8_MAX_FRAME_RESOURCES];
|
||||||
|
pxl8_gfx_bindings bindings[PXL8_MAX_FRAME_RESOURCES];
|
||||||
|
u32 texture_count;
|
||||||
|
u32 buffer_count;
|
||||||
|
u32 pipeline_count;
|
||||||
|
u32 bindings_count;
|
||||||
|
} pxl8_frame_resources;
|
||||||
|
|
||||||
struct pxl8_gfx {
|
struct pxl8_gfx {
|
||||||
pxl8_atlas* atlas;
|
pxl8_atlas* atlas;
|
||||||
pxl8_gfx_backend backend;
|
pxl8_renderer* renderer;
|
||||||
|
pxl8_gfx_texture color_target;
|
||||||
|
pxl8_gfx_texture depth_target;
|
||||||
|
pxl8_gfx_texture light_target;
|
||||||
|
pxl8_gfx_cmdbuf* cmdbuf;
|
||||||
|
pxl8_target_entry target_stack[PXL8_MAX_TARGET_STACK];
|
||||||
|
u32 target_stack_depth;
|
||||||
const pxl8_bsp* bsp;
|
const pxl8_bsp* bsp;
|
||||||
pxl8_colormap* colormap;
|
pxl8_colormap* colormap;
|
||||||
u8* framebuffer;
|
|
||||||
i32 framebuffer_height;
|
i32 framebuffer_height;
|
||||||
i32 framebuffer_width;
|
i32 framebuffer_width;
|
||||||
pxl8_frustum frustum;
|
pxl8_frustum frustum;
|
||||||
const pxl8_hal* hal;
|
const pxl8_hal* hal;
|
||||||
bool initialized;
|
bool initialized;
|
||||||
|
u32* output;
|
||||||
pxl8_palette* palette;
|
pxl8_palette* palette;
|
||||||
pxl8_palette_cube* palette_cube;
|
pxl8_palette_cube* palette_cube;
|
||||||
|
pxl8_gfx_pass frame_pass;
|
||||||
|
pxl8_frame_resources frame_res;
|
||||||
pxl8_pixel_mode pixel_mode;
|
pxl8_pixel_mode pixel_mode;
|
||||||
void* platform_data;
|
void* platform_data;
|
||||||
const pxl8_sdf* sdf;
|
|
||||||
pxl8_sprite_cache_entry* sprite_cache;
|
pxl8_sprite_cache_entry* sprite_cache;
|
||||||
u32 sprite_cache_capacity;
|
u32 sprite_cache_capacity;
|
||||||
u32 sprite_cache_count;
|
u32 sprite_cache_count;
|
||||||
pxl8_viewport viewport;
|
pxl8_viewport viewport;
|
||||||
pxl8_mat4 view_proj;
|
pxl8_mat4 view_proj;
|
||||||
|
pxl8_3d_frame frame;
|
||||||
|
|
||||||
|
pxl8_gfx_buffer stream_vb;
|
||||||
|
pxl8_gfx_buffer stream_ib;
|
||||||
|
u32 stream_vb_capacity;
|
||||||
|
u32 stream_ib_capacity;
|
||||||
|
u32 stream_vb_offset;
|
||||||
|
u32 stream_ib_offset;
|
||||||
|
bool wireframe;
|
||||||
};
|
};
|
||||||
|
|
||||||
pxl8_bounds pxl8_gfx_get_bounds(pxl8_gfx* gfx) {
|
pxl8_bounds pxl8_gfx_get_bounds(pxl8_gfx* gfx) {
|
||||||
|
|
@ -63,15 +103,12 @@ pxl8_pixel_mode pxl8_gfx_get_pixel_mode(pxl8_gfx* gfx) {
|
||||||
|
|
||||||
u8* pxl8_gfx_get_framebuffer_indexed(pxl8_gfx* gfx) {
|
u8* pxl8_gfx_get_framebuffer_indexed(pxl8_gfx* gfx) {
|
||||||
if (!gfx || gfx->pixel_mode == PXL8_PIXEL_HICOLOR) return NULL;
|
if (!gfx || gfx->pixel_mode == PXL8_PIXEL_HICOLOR) return NULL;
|
||||||
if (gfx->backend.type == PXL8_GFX_BACKEND_CPU) {
|
return (u8*)pxl8_texture_get_data(gfx->renderer, gfx->color_target);
|
||||||
return pxl8_cpu_get_framebuffer(gfx->backend.cpu);
|
|
||||||
}
|
|
||||||
return gfx->framebuffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u16* pxl8_gfx_get_framebuffer_hicolor(pxl8_gfx* gfx) {
|
u16* pxl8_gfx_get_framebuffer_hicolor(pxl8_gfx* gfx) {
|
||||||
if (!gfx || gfx->pixel_mode != PXL8_PIXEL_HICOLOR) return NULL;
|
if (!gfx || gfx->pixel_mode != PXL8_PIXEL_HICOLOR) return NULL;
|
||||||
return (u16*)gfx->framebuffer;
|
return (u16*)pxl8_texture_get_data(gfx->renderer, gfx->color_target);
|
||||||
}
|
}
|
||||||
|
|
||||||
i32 pxl8_gfx_get_height(const pxl8_gfx* gfx) {
|
i32 pxl8_gfx_get_height(const pxl8_gfx* gfx) {
|
||||||
|
|
@ -80,10 +117,30 @@ i32 pxl8_gfx_get_height(const pxl8_gfx* gfx) {
|
||||||
|
|
||||||
u32* pxl8_gfx_get_light_accum(pxl8_gfx* gfx) {
|
u32* pxl8_gfx_get_light_accum(pxl8_gfx* gfx) {
|
||||||
if (!gfx) return NULL;
|
if (!gfx) return NULL;
|
||||||
if (gfx->backend.type == PXL8_GFX_BACKEND_CPU) {
|
return (u32*)pxl8_texture_get_data(gfx->renderer, gfx->light_target);
|
||||||
return pxl8_cpu_get_light_accum(gfx->backend.cpu);
|
}
|
||||||
|
|
||||||
|
u32* pxl8_gfx_get_output(pxl8_gfx* gfx) {
|
||||||
|
if (!gfx) return NULL;
|
||||||
|
return gfx->output;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_gfx_resolve(pxl8_gfx* gfx) {
|
||||||
|
if (!gfx || !gfx->initialized) return;
|
||||||
|
if (gfx->pixel_mode != PXL8_PIXEL_INDEXED) return;
|
||||||
|
|
||||||
|
const u32* pal = gfx->palette ? pxl8_palette_colors(gfx->palette) : NULL;
|
||||||
|
if (!pal) {
|
||||||
|
pxl8_error("resolve: no palette!");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return NULL;
|
pxl8_resolve_to_rgba(
|
||||||
|
gfx->renderer,
|
||||||
|
gfx->color_target,
|
||||||
|
gfx->light_target,
|
||||||
|
pal,
|
||||||
|
gfx->output
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const u32* pxl8_gfx_palette_colors(pxl8_gfx* gfx) {
|
const u32* pxl8_gfx_palette_colors(pxl8_gfx* gfx) {
|
||||||
|
|
@ -93,10 +150,7 @@ const u32* pxl8_gfx_palette_colors(pxl8_gfx* gfx) {
|
||||||
|
|
||||||
u16* pxl8_gfx_get_zbuffer(pxl8_gfx* gfx) {
|
u16* pxl8_gfx_get_zbuffer(pxl8_gfx* gfx) {
|
||||||
if (!gfx) return NULL;
|
if (!gfx) return NULL;
|
||||||
if (gfx->backend.type == PXL8_GFX_BACKEND_CPU) {
|
return (u16*)pxl8_texture_get_data(gfx->renderer, gfx->depth_target);
|
||||||
return pxl8_cpu_get_zbuffer(gfx->backend.cpu);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
i32 pxl8_gfx_get_width(const pxl8_gfx* gfx) {
|
i32 pxl8_gfx_get_width(const pxl8_gfx* gfx) {
|
||||||
|
|
@ -123,9 +177,6 @@ u8 pxl8_gfx_find_color(pxl8_gfx* gfx, u32 color) {
|
||||||
void pxl8_gfx_set_palette_colors(pxl8_gfx* gfx, const u32* colors, u16 count) {
|
void pxl8_gfx_set_palette_colors(pxl8_gfx* gfx, const u32* colors, u16 count) {
|
||||||
if (!gfx || !gfx->palette || !colors) return;
|
if (!gfx || !gfx->palette || !colors) return;
|
||||||
pxl8_set_palette(gfx->palette, colors, count);
|
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) {
|
i32 pxl8_gfx_load_palette(pxl8_gfx* gfx, const char* filepath) {
|
||||||
|
|
@ -136,9 +187,6 @@ i32 pxl8_gfx_load_palette(pxl8_gfx* gfx, const char* filepath) {
|
||||||
if (gfx->colormap) {
|
if (gfx->colormap) {
|
||||||
pxl8_colormap_generate(gfx->colormap, pxl8_palette_colors(gfx->palette));
|
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, pxl8_palette_colors(gfx->palette));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -149,6 +197,8 @@ pxl8_gfx* pxl8_gfx_create(
|
||||||
pxl8_pixel_mode mode,
|
pxl8_pixel_mode mode,
|
||||||
pxl8_resolution resolution
|
pxl8_resolution resolution
|
||||||
) {
|
) {
|
||||||
|
pxl8_shader_registry_init();
|
||||||
|
|
||||||
pxl8_gfx* gfx = (pxl8_gfx*)pxl8_calloc(1, sizeof(pxl8_gfx));
|
pxl8_gfx* gfx = (pxl8_gfx*)pxl8_calloc(1, sizeof(pxl8_gfx));
|
||||||
if (!gfx) {
|
if (!gfx) {
|
||||||
pxl8_error("Failed to allocate graphics context");
|
pxl8_error("Failed to allocate graphics context");
|
||||||
|
|
@ -173,29 +223,87 @@ pxl8_gfx* pxl8_gfx_create(
|
||||||
gfx->palette = pxl8_palette_create();
|
gfx->palette = pxl8_palette_create();
|
||||||
}
|
}
|
||||||
|
|
||||||
gfx->backend.type = PXL8_GFX_BACKEND_CPU;
|
gfx->renderer = pxl8_renderer_create(gfx->framebuffer_width, gfx->framebuffer_height);
|
||||||
gfx->backend.cpu = pxl8_cpu_create(gfx->framebuffer_width, gfx->framebuffer_height);
|
if (!gfx->renderer) {
|
||||||
if (!gfx->backend.cpu) {
|
pxl8_error("Failed to create renderer");
|
||||||
pxl8_error("Failed to create CPU backend");
|
|
||||||
pxl8_gfx_destroy(gfx);
|
pxl8_gfx_destroy(gfx);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
gfx->framebuffer = pxl8_cpu_get_framebuffer(gfx->backend.cpu);
|
pxl8_gfx_texture_desc color_desc = {
|
||||||
|
.width = (u32)gfx->framebuffer_width,
|
||||||
|
.height = (u32)gfx->framebuffer_height,
|
||||||
|
.format = PXL8_GFX_FORMAT_INDEXED8,
|
||||||
|
.render_target = true,
|
||||||
|
};
|
||||||
|
gfx->color_target = pxl8_create_texture(gfx->renderer, &color_desc);
|
||||||
|
|
||||||
|
pxl8_gfx_texture_desc depth_desc = {
|
||||||
|
.width = (u32)gfx->framebuffer_width,
|
||||||
|
.height = (u32)gfx->framebuffer_height,
|
||||||
|
.format = PXL8_GFX_FORMAT_DEPTH16,
|
||||||
|
.render_target = true,
|
||||||
|
};
|
||||||
|
gfx->depth_target = pxl8_create_texture(gfx->renderer, &depth_desc);
|
||||||
|
|
||||||
|
pxl8_gfx_texture_desc light_desc = {
|
||||||
|
.width = (u32)gfx->framebuffer_width,
|
||||||
|
.height = (u32)gfx->framebuffer_height,
|
||||||
|
.format = PXL8_GFX_FORMAT_LIGHT_ACCUM,
|
||||||
|
.render_target = true,
|
||||||
|
};
|
||||||
|
gfx->light_target = pxl8_create_texture(gfx->renderer, &light_desc);
|
||||||
|
|
||||||
|
u32 pixel_count = (u32)gfx->framebuffer_width * (u32)gfx->framebuffer_height;
|
||||||
|
gfx->output = pxl8_calloc(pixel_count, sizeof(u32));
|
||||||
|
if (!gfx->output) {
|
||||||
|
pxl8_error("Failed to allocate output buffer");
|
||||||
|
pxl8_gfx_destroy(gfx);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
gfx->cmdbuf = pxl8_cmdbuf_create(1024);
|
||||||
|
if (!gfx->cmdbuf) {
|
||||||
|
pxl8_error("Failed to create command buffer");
|
||||||
|
pxl8_gfx_destroy(gfx);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (mode != PXL8_PIXEL_HICOLOR) {
|
if (mode != PXL8_PIXEL_HICOLOR) {
|
||||||
gfx->colormap = pxl8_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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gfx->target_stack[0] = (pxl8_target_entry){
|
||||||
|
.color = gfx->color_target,
|
||||||
|
.depth = gfx->depth_target,
|
||||||
|
.light = gfx->light_target,
|
||||||
|
};
|
||||||
|
gfx->target_stack_depth = 1;
|
||||||
|
|
||||||
gfx->viewport.offset_x = 0;
|
gfx->viewport.offset_x = 0;
|
||||||
gfx->viewport.offset_y = 0;
|
gfx->viewport.offset_y = 0;
|
||||||
gfx->viewport.scaled_width = gfx->framebuffer_width;
|
gfx->viewport.scaled_width = gfx->framebuffer_width;
|
||||||
gfx->viewport.scaled_height = gfx->framebuffer_height;
|
gfx->viewport.scaled_height = gfx->framebuffer_height;
|
||||||
gfx->viewport.scale = 1.0f;
|
gfx->viewport.scale = 1.0f;
|
||||||
|
|
||||||
|
pxl8_gfx_buffer_desc vb_desc = {
|
||||||
|
.capacity = PXL8_STREAM_VB_SIZE,
|
||||||
|
.type = PXL8_GFX_BUFFER_VERTEX,
|
||||||
|
.usage = PXL8_GFX_USAGE_STREAM,
|
||||||
|
};
|
||||||
|
gfx->stream_vb = pxl8_create_buffer(gfx->renderer, &vb_desc);
|
||||||
|
gfx->stream_vb_capacity = PXL8_STREAM_VB_SIZE;
|
||||||
|
gfx->stream_vb_offset = 0;
|
||||||
|
|
||||||
|
pxl8_gfx_buffer_desc ib_desc = {
|
||||||
|
.capacity = PXL8_STREAM_IB_SIZE,
|
||||||
|
.type = PXL8_GFX_BUFFER_INDEX,
|
||||||
|
.usage = PXL8_GFX_USAGE_STREAM,
|
||||||
|
};
|
||||||
|
gfx->stream_ib = pxl8_create_buffer(gfx->renderer, &ib_desc);
|
||||||
|
gfx->stream_ib_capacity = PXL8_STREAM_IB_SIZE;
|
||||||
|
gfx->stream_ib_offset = 0;
|
||||||
|
|
||||||
gfx->initialized = true;
|
gfx->initialized = true;
|
||||||
return gfx;
|
return gfx;
|
||||||
}
|
}
|
||||||
|
|
@ -204,13 +312,13 @@ void pxl8_gfx_destroy(pxl8_gfx* gfx) {
|
||||||
if (!gfx) return;
|
if (!gfx) return;
|
||||||
|
|
||||||
pxl8_atlas_destroy(gfx->atlas);
|
pxl8_atlas_destroy(gfx->atlas);
|
||||||
if (gfx->backend.type == PXL8_GFX_BACKEND_CPU) {
|
pxl8_cmdbuf_destroy(gfx->cmdbuf);
|
||||||
pxl8_cpu_destroy(gfx->backend.cpu);
|
|
||||||
}
|
|
||||||
pxl8_free(gfx->colormap);
|
pxl8_free(gfx->colormap);
|
||||||
|
pxl8_free(gfx->output);
|
||||||
pxl8_palette_cube_destroy(gfx->palette_cube);
|
pxl8_palette_cube_destroy(gfx->palette_cube);
|
||||||
pxl8_palette_destroy(gfx->palette);
|
pxl8_palette_destroy(gfx->palette);
|
||||||
pxl8_free(gfx->sprite_cache);
|
pxl8_free(gfx->sprite_cache);
|
||||||
|
pxl8_renderer_destroy(gfx->renderer);
|
||||||
|
|
||||||
pxl8_free(gfx);
|
pxl8_free(gfx);
|
||||||
}
|
}
|
||||||
|
|
@ -330,26 +438,24 @@ pxl8_result pxl8_gfx_load_font_atlas(pxl8_gfx* gfx) {
|
||||||
void pxl8_gfx_upload_framebuffer(pxl8_gfx* gfx) {
|
void pxl8_gfx_upload_framebuffer(pxl8_gfx* gfx) {
|
||||||
if (!gfx || !gfx->initialized || !gfx->hal) return;
|
if (!gfx || !gfx->initialized || !gfx->hal) return;
|
||||||
|
|
||||||
if (gfx->backend.type == PXL8_GFX_BACKEND_CPU && gfx->pixel_mode == PXL8_PIXEL_INDEXED) {
|
if (gfx->pixel_mode == PXL8_PIXEL_INDEXED) {
|
||||||
pxl8_cpu_resolve(gfx->backend.cpu);
|
pxl8_gfx_resolve(gfx);
|
||||||
u32* output = pxl8_cpu_get_output(gfx->backend.cpu);
|
gfx->hal->upload_texture(
|
||||||
if (output) {
|
gfx->platform_data,
|
||||||
gfx->hal->upload_texture(
|
gfx->output,
|
||||||
gfx->platform_data,
|
gfx->framebuffer_width,
|
||||||
output,
|
gfx->framebuffer_height,
|
||||||
gfx->framebuffer_width,
|
PXL8_PIXEL_RGBA,
|
||||||
gfx->framebuffer_height,
|
NULL
|
||||||
PXL8_PIXEL_RGBA,
|
);
|
||||||
NULL
|
return;
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32* colors = gfx->palette ? pxl8_palette_colors(gfx->palette) : NULL;
|
u32* colors = gfx->palette ? pxl8_palette_colors(gfx->palette) : NULL;
|
||||||
|
u8* framebuffer = (u8*)pxl8_texture_get_data(gfx->renderer, gfx->color_target);
|
||||||
gfx->hal->upload_texture(
|
gfx->hal->upload_texture(
|
||||||
gfx->platform_data,
|
gfx->platform_data,
|
||||||
gfx->framebuffer,
|
framebuffer,
|
||||||
gfx->framebuffer_width,
|
gfx->framebuffer_width,
|
||||||
gfx->framebuffer_height,
|
gfx->framebuffer_height,
|
||||||
gfx->pixel_mode,
|
gfx->pixel_mode,
|
||||||
|
|
@ -363,6 +469,16 @@ void pxl8_gfx_present(pxl8_gfx* gfx) {
|
||||||
gfx->hal->present(gfx->platform_data);
|
gfx->hal->present(gfx->platform_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pxl8_gfx_reset_stats(pxl8_gfx* gfx) {
|
||||||
|
if (!gfx || !gfx->renderer) return;
|
||||||
|
pxl8_renderer_reset_stats(gfx->renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
const pxl8_gfx_stats* pxl8_gfx_get_stats(pxl8_gfx* gfx) {
|
||||||
|
if (!gfx || !gfx->renderer) return NULL;
|
||||||
|
return pxl8_renderer_get_stats(gfx->renderer);
|
||||||
|
}
|
||||||
|
|
||||||
pxl8_viewport pxl8_gfx_viewport(pxl8_bounds bounds, i32 width, i32 height) {
|
pxl8_viewport pxl8_gfx_viewport(pxl8_bounds bounds, i32 width, i32 height) {
|
||||||
pxl8_viewport vp = {0};
|
pxl8_viewport vp = {0};
|
||||||
vp.scale = fminf(bounds.w / (f32)width, bounds.h / (f32)height);
|
vp.scale = fminf(bounds.w / (f32)width, bounds.h / (f32)height);
|
||||||
|
|
@ -382,115 +498,99 @@ void pxl8_gfx_project(pxl8_gfx* gfx, f32 left, f32 right, f32 top, f32 bottom) {
|
||||||
(void)gfx; (void)left; (void)right; (void)top; (void)bottom;
|
(void)gfx; (void)left; (void)right; (void)top; (void)bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static pxl8_gfx_texture gfx_current_color(pxl8_gfx* gfx) {
|
||||||
|
if (gfx->target_stack_depth == 0) return gfx->color_target;
|
||||||
|
return gfx->target_stack[gfx->target_stack_depth - 1].color;
|
||||||
|
}
|
||||||
|
|
||||||
|
static pxl8_gfx_texture gfx_current_depth(pxl8_gfx* gfx) {
|
||||||
|
if (gfx->target_stack_depth == 0) return gfx->depth_target;
|
||||||
|
return gfx->target_stack[gfx->target_stack_depth - 1].depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
static pxl8_gfx_texture gfx_current_light(pxl8_gfx* gfx) {
|
||||||
|
if (gfx->target_stack_depth == 0) return gfx->light_target;
|
||||||
|
return gfx->target_stack[gfx->target_stack_depth - 1].light;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gfx_composite_over(pxl8_gfx* gfx, const pxl8_target_entry* src, pxl8_target_entry* dst) {
|
||||||
|
if (!gfx || !src || !dst) return;
|
||||||
|
|
||||||
|
u8* src_color = (u8*)pxl8_texture_get_data(gfx->renderer, src->color);
|
||||||
|
u8* dst_color = (u8*)pxl8_texture_get_data(gfx->renderer, dst->color);
|
||||||
|
if (!src_color || !dst_color) return;
|
||||||
|
|
||||||
|
u32 width = pxl8_texture_get_width(gfx->renderer, src->color);
|
||||||
|
u32 height = pxl8_texture_get_height(gfx->renderer, src->color);
|
||||||
|
if (width == 0 || height == 0) return;
|
||||||
|
|
||||||
|
u32 dst_width = pxl8_texture_get_width(gfx->renderer, dst->color);
|
||||||
|
u32 dst_height = pxl8_texture_get_height(gfx->renderer, dst->color);
|
||||||
|
if (dst_width != width || dst_height != height) return;
|
||||||
|
|
||||||
|
u32* src_light = (u32*)pxl8_texture_get_data(gfx->renderer, src->light);
|
||||||
|
u32* dst_light = (u32*)pxl8_texture_get_data(gfx->renderer, dst->light);
|
||||||
|
|
||||||
|
u32 count = width * height;
|
||||||
|
for (u32 i = 0; i < count; i++) {
|
||||||
|
u8 sc = src_color[i];
|
||||||
|
if (sc == 0) continue;
|
||||||
|
|
||||||
|
dst_color[i] = sc;
|
||||||
|
if (dst_light) {
|
||||||
|
dst_light[i] = src_light ? src_light[i] : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void pxl8_2d_clear(pxl8_gfx* gfx, u32 color) {
|
void pxl8_2d_clear(pxl8_gfx* gfx, u32 color) {
|
||||||
if (!gfx) return;
|
if (!gfx) return;
|
||||||
switch (gfx->backend.type) {
|
pxl8_clear(gfx->renderer, gfx_current_color(gfx), (u8)color);
|
||||||
case PXL8_GFX_BACKEND_CPU:
|
pxl8_clear_light(gfx->renderer, gfx_current_light(gfx));
|
||||||
pxl8_cpu_clear(gfx->backend.cpu, (u8)color);
|
|
||||||
break;
|
|
||||||
case PXL8_GFX_BACKEND_GPU:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_2d_pixel(pxl8_gfx* gfx, i32 x, i32 y, u32 color) {
|
void pxl8_2d_pixel(pxl8_gfx* gfx, i32 x, i32 y, u32 color) {
|
||||||
if (!gfx) return;
|
if (!gfx) return;
|
||||||
switch (gfx->backend.type) {
|
pxl8_draw_pixel(gfx->renderer, gfx_current_color(gfx), x, y, (u8)color);
|
||||||
case PXL8_GFX_BACKEND_CPU:
|
|
||||||
pxl8_cpu_draw_pixel(gfx->backend.cpu, x, y, (u8)color);
|
|
||||||
break;
|
|
||||||
case PXL8_GFX_BACKEND_GPU:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 pxl8_2d_get_pixel(pxl8_gfx* gfx, i32 x, i32 y) {
|
u32 pxl8_2d_get_pixel(pxl8_gfx* gfx, i32 x, i32 y) {
|
||||||
if (!gfx) return 0;
|
if (!gfx) return 0;
|
||||||
switch (gfx->backend.type) {
|
return pxl8_get_pixel(gfx->renderer, gfx_current_color(gfx), x, y);
|
||||||
case PXL8_GFX_BACKEND_CPU:
|
|
||||||
return pxl8_cpu_get_pixel(gfx->backend.cpu, x, y);
|
|
||||||
case PXL8_GFX_BACKEND_GPU:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_2d_line(pxl8_gfx* gfx, i32 x0, i32 y0, i32 x1, i32 y1, u32 color) {
|
void pxl8_2d_line(pxl8_gfx* gfx, i32 x0, i32 y0, i32 x1, i32 y1, u32 color) {
|
||||||
if (!gfx) return;
|
if (!gfx) return;
|
||||||
switch (gfx->backend.type) {
|
pxl8_draw_line(gfx->renderer, gfx_current_color(gfx), x0, y0, x1, y1, (u8)color);
|
||||||
case PXL8_GFX_BACKEND_CPU:
|
|
||||||
pxl8_cpu_draw_line_2d(gfx->backend.cpu, x0, y0, x1, y1, (u8)color);
|
|
||||||
break;
|
|
||||||
case PXL8_GFX_BACKEND_GPU:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_2d_rect(pxl8_gfx* gfx, i32 x, i32 y, i32 w, i32 h, u32 color) {
|
void pxl8_2d_rect(pxl8_gfx* gfx, i32 x, i32 y, i32 w, i32 h, u32 color) {
|
||||||
if (!gfx) return;
|
if (!gfx) return;
|
||||||
switch (gfx->backend.type) {
|
pxl8_draw_rect(gfx->renderer, gfx_current_color(gfx), x, y, w, h, (u8)color);
|
||||||
case PXL8_GFX_BACKEND_CPU:
|
|
||||||
pxl8_cpu_draw_rect(gfx->backend.cpu, x, y, w, h, (u8)color);
|
|
||||||
break;
|
|
||||||
case PXL8_GFX_BACKEND_GPU:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_2d_rect_fill(pxl8_gfx* gfx, i32 x, i32 y, i32 w, i32 h, u32 color) {
|
void pxl8_2d_rect_fill(pxl8_gfx* gfx, i32 x, i32 y, i32 w, i32 h, u32 color) {
|
||||||
if (!gfx) return;
|
if (!gfx) return;
|
||||||
switch (gfx->backend.type) {
|
pxl8_draw_rect_fill(gfx->renderer, gfx_current_color(gfx), x, y, w, h, (u8)color);
|
||||||
case PXL8_GFX_BACKEND_CPU:
|
|
||||||
pxl8_cpu_draw_rect_fill(gfx->backend.cpu, x, y, w, h, (u8)color);
|
|
||||||
break;
|
|
||||||
case PXL8_GFX_BACKEND_GPU:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_2d_circle(pxl8_gfx* gfx, i32 cx, i32 cy, i32 radius, u32 color) {
|
void pxl8_2d_circle(pxl8_gfx* gfx, i32 cx, i32 cy, i32 radius, u32 color) {
|
||||||
if (!gfx) return;
|
if (!gfx) return;
|
||||||
switch (gfx->backend.type) {
|
pxl8_draw_circle(gfx->renderer, gfx_current_color(gfx), cx, cy, radius, (u8)color);
|
||||||
case PXL8_GFX_BACKEND_CPU:
|
|
||||||
pxl8_cpu_draw_circle(gfx->backend.cpu, cx, cy, radius, (u8)color);
|
|
||||||
break;
|
|
||||||
case PXL8_GFX_BACKEND_GPU:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_2d_circle_fill(pxl8_gfx* gfx, i32 cx, i32 cy, i32 radius, u32 color) {
|
void pxl8_2d_circle_fill(pxl8_gfx* gfx, i32 cx, i32 cy, i32 radius, u32 color) {
|
||||||
if (!gfx) return;
|
if (!gfx) return;
|
||||||
switch (gfx->backend.type) {
|
pxl8_draw_circle_fill(gfx->renderer, gfx_current_color(gfx), cx, cy, radius, (u8)color);
|
||||||
case PXL8_GFX_BACKEND_CPU:
|
|
||||||
pxl8_cpu_draw_circle_fill(gfx->backend.cpu, cx, cy, radius, (u8)color);
|
|
||||||
break;
|
|
||||||
case PXL8_GFX_BACKEND_GPU:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_2d_text(pxl8_gfx* gfx, const char* text, i32 x, i32 y, u32 color) {
|
void pxl8_2d_text(pxl8_gfx* gfx, const char* text, i32 x, i32 y, u32 color) {
|
||||||
if (!gfx || !text) return;
|
if (!gfx || !text) return;
|
||||||
|
|
||||||
u8* framebuffer = NULL;
|
pxl8_gfx_texture target = gfx_current_color(gfx);
|
||||||
u32* light_accum = NULL;
|
u8* framebuffer = (u8*)pxl8_texture_get_data(gfx->renderer, target);
|
||||||
i32 fb_width = 0;
|
i32 fb_width = (i32)pxl8_texture_get_width(gfx->renderer, target);
|
||||||
i32 fb_height = 0;
|
i32 fb_height = (i32)pxl8_texture_get_height(gfx->renderer, target);
|
||||||
|
|
||||||
switch (gfx->backend.type) {
|
|
||||||
case PXL8_GFX_BACKEND_CPU: {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
case PXL8_GFX_BACKEND_GPU:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!framebuffer) return;
|
if (!framebuffer) return;
|
||||||
|
|
||||||
|
|
@ -523,7 +623,6 @@ void pxl8_2d_text(pxl8_gfx* gfx, const char* text, i32 x, i32 y, u32 color) {
|
||||||
if (pixel_bit) {
|
if (pixel_bit) {
|
||||||
i32 idx = py * fb_width + px;
|
i32 idx = py * fb_width + px;
|
||||||
framebuffer[idx] = (u8)color;
|
framebuffer[idx] = (u8)color;
|
||||||
if (light_accum) light_accum[idx] = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -536,24 +635,10 @@ void pxl8_2d_text(pxl8_gfx* gfx, const char* text, i32 x, i32 y, u32 color) {
|
||||||
void pxl8_2d_sprite(pxl8_gfx* gfx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h, bool flip_x, bool flip_y) {
|
void pxl8_2d_sprite(pxl8_gfx* gfx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h, bool flip_x, bool flip_y) {
|
||||||
if (!gfx || !gfx->atlas) return;
|
if (!gfx || !gfx->atlas) return;
|
||||||
|
|
||||||
u8* framebuffer = NULL;
|
pxl8_gfx_texture target = gfx_current_color(gfx);
|
||||||
u32* light_accum = NULL;
|
u8* framebuffer = (u8*)pxl8_texture_get_data(gfx->renderer, target);
|
||||||
i32 fb_width = 0;
|
i32 fb_width = (i32)pxl8_texture_get_width(gfx->renderer, target);
|
||||||
i32 fb_height = 0;
|
i32 fb_height = (i32)pxl8_texture_get_height(gfx->renderer, target);
|
||||||
|
|
||||||
switch (gfx->backend.type) {
|
|
||||||
case PXL8_GFX_BACKEND_CPU: {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
case PXL8_GFX_BACKEND_GPU:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!framebuffer) return;
|
if (!framebuffer) return;
|
||||||
|
|
||||||
|
|
@ -583,11 +668,6 @@ 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) {
|
if (is_1to1_scale && is_unclipped && !is_flipped) {
|
||||||
const u8* sprite_data = atlas_pixels + entry->y * atlas_width + entry->x;
|
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);
|
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 {
|
} else {
|
||||||
for (i32 py = 0; py < draw_height; py++) {
|
for (i32 py = 0; py < draw_height; py++) {
|
||||||
for (i32 px = 0; px < draw_width; px++) {
|
for (i32 px = 0; px < draw_width; px++) {
|
||||||
|
|
@ -601,7 +681,6 @@ 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);
|
i32 dest_idx = (dest_y + py) * fb_width + (dest_x + px);
|
||||||
|
|
||||||
framebuffer[dest_idx] = pxl8_blend_indexed(atlas_pixels[src_idx], framebuffer[dest_idx]);
|
framebuffer[dest_idx] = pxl8_blend_indexed(atlas_pixels[src_idx], framebuffer[dest_idx]);
|
||||||
if (light_accum) light_accum[dest_idx] = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -616,7 +695,7 @@ void pxl8_gfx_update(pxl8_gfx* gfx, f32 dt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_3d_frame pxl8_3d_frame_from_camera(const pxl8_3d_camera* camera, const pxl8_3d_uniforms* uniforms) {
|
pxl8_3d_frame pxl8_3d_frame_from_camera(const pxl8_3d_camera* camera, const pxl8_shader_uniforms* uniforms) {
|
||||||
pxl8_3d_frame frame = {0};
|
pxl8_3d_frame frame = {0};
|
||||||
if (!camera) return frame;
|
if (!camera) return frame;
|
||||||
|
|
||||||
|
|
@ -639,32 +718,37 @@ void pxl8_3d_set_bsp(pxl8_gfx* gfx, const pxl8_bsp* bsp) {
|
||||||
gfx->bsp = bsp;
|
gfx->bsp = bsp;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_3d_set_sdf(pxl8_gfx* gfx, const pxl8_sdf* sdf) {
|
void pxl8_3d_begin_frame(pxl8_gfx* gfx, const pxl8_3d_camera* camera, const pxl8_lights* lights, const pxl8_shader_uniforms* uniforms) {
|
||||||
if (!gfx) return;
|
|
||||||
gfx->sdf = sdf;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
if (!gfx || !camera) return;
|
||||||
|
|
||||||
pxl8_3d_frame frame = pxl8_3d_frame_from_camera(camera, uniforms);
|
pxl8_3d_frame frame = pxl8_3d_frame_from_camera(camera, uniforms);
|
||||||
frame.bsp = gfx->bsp;
|
frame.bsp = gfx->bsp;
|
||||||
frame.lights = lights ? pxl8_lights_data(lights) : NULL;
|
frame.uniforms.lights = lights ? pxl8_lights_data(lights) : NULL;
|
||||||
frame.lights_count = lights ? pxl8_lights_count(lights) : 0;
|
frame.uniforms.lights_count = lights ? pxl8_lights_count(lights) : 0;
|
||||||
frame.sdf = gfx->sdf;
|
|
||||||
|
|
||||||
pxl8_mat4 vp = pxl8_mat4_multiply(frame.projection, frame.view);
|
pxl8_mat4 vp = pxl8_mat4_multiply(frame.projection, frame.view);
|
||||||
|
|
||||||
gfx->frustum = pxl8_frustum_from_matrix(vp);
|
gfx->frustum = pxl8_frustum_from_matrix(vp);
|
||||||
gfx->view_proj = vp;
|
gfx->view_proj = vp;
|
||||||
|
gfx->frame = frame;
|
||||||
|
|
||||||
switch (gfx->backend.type) {
|
gfx->frame_res.texture_count = 0;
|
||||||
case PXL8_GFX_BACKEND_CPU:
|
gfx->frame_res.buffer_count = 0;
|
||||||
pxl8_cpu_begin_frame(gfx->backend.cpu, &frame);
|
gfx->frame_res.pipeline_count = 0;
|
||||||
break;
|
gfx->frame_res.bindings_count = 0;
|
||||||
case PXL8_GFX_BACKEND_GPU:
|
|
||||||
break;
|
gfx->stream_vb_offset = 0;
|
||||||
}
|
gfx->stream_ib_offset = 0;
|
||||||
|
|
||||||
|
pxl8_cmdbuf_reset(gfx->cmdbuf);
|
||||||
|
|
||||||
|
pxl8_gfx_pass_desc pass_desc = {
|
||||||
|
.color = { .texture = gfx_current_color(gfx), .load = PXL8_GFX_LOAD_CLEAR, .clear_value = 0 },
|
||||||
|
.depth = { .texture = gfx_current_depth(gfx), .load = PXL8_GFX_LOAD_CLEAR, .clear_value = 0xFFFF },
|
||||||
|
.light_accum = { .texture = gfx_current_light(gfx), .load = PXL8_GFX_LOAD_CLEAR },
|
||||||
|
};
|
||||||
|
gfx->frame_pass = pxl8_create_pass(gfx->renderer, &pass_desc);
|
||||||
|
pxl8_begin_pass(gfx->cmdbuf, gfx->frame_pass);
|
||||||
}
|
}
|
||||||
|
|
||||||
const pxl8_frustum* pxl8_3d_get_frustum(pxl8_gfx* gfx) {
|
const pxl8_frustum* pxl8_3d_get_frustum(pxl8_gfx* gfx) {
|
||||||
|
|
@ -706,90 +790,249 @@ u32 pxl8_3d_project_points(pxl8_gfx* gfx, const pxl8_vec3* in, pxl8_vec3* out, u
|
||||||
|
|
||||||
void pxl8_3d_clear(pxl8_gfx* gfx, u8 color) {
|
void pxl8_3d_clear(pxl8_gfx* gfx, u8 color) {
|
||||||
if (!gfx) return;
|
if (!gfx) return;
|
||||||
switch (gfx->backend.type) {
|
pxl8_clear(gfx->renderer, gfx_current_color(gfx), color);
|
||||||
case PXL8_GFX_BACKEND_CPU:
|
pxl8_clear_light(gfx->renderer, gfx_current_light(gfx));
|
||||||
pxl8_cpu_clear(gfx->backend.cpu, color);
|
|
||||||
break;
|
|
||||||
case PXL8_GFX_BACKEND_GPU:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_3d_clear_depth(pxl8_gfx* gfx) {
|
void pxl8_3d_clear_depth(pxl8_gfx* gfx) {
|
||||||
if (!gfx) return;
|
if (!gfx) return;
|
||||||
switch (gfx->backend.type) {
|
pxl8_clear_depth(gfx->renderer, gfx_current_depth(gfx));
|
||||||
case PXL8_GFX_BACKEND_CPU:
|
|
||||||
pxl8_cpu_clear_depth(gfx->backend.cpu);
|
|
||||||
break;
|
|
||||||
case PXL8_GFX_BACKEND_GPU:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_3d_draw_line(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, u8 color) {
|
void pxl8_3d_draw_line(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, u8 color) {
|
||||||
if (!gfx) return;
|
if (!gfx) return;
|
||||||
switch (gfx->backend.type) {
|
|
||||||
case PXL8_GFX_BACKEND_CPU:
|
pxl8_vec4 c0 = pxl8_mat4_multiply_vec4(gfx->view_proj, (pxl8_vec4){v0.x, v0.y, v0.z, 1.0f});
|
||||||
pxl8_cpu_draw_line_3d(gfx->backend.cpu, v0, v1, color);
|
pxl8_vec4 c1 = pxl8_mat4_multiply_vec4(gfx->view_proj, (pxl8_vec4){v1.x, v1.y, v1.z, 1.0f});
|
||||||
break;
|
|
||||||
case PXL8_GFX_BACKEND_GPU:
|
if (c0.w <= 0.0f && c1.w <= 0.0f) return;
|
||||||
break;
|
|
||||||
}
|
f32 hw = (f32)gfx->framebuffer_width * 0.5f;
|
||||||
|
f32 hh = (f32)gfx->framebuffer_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);
|
||||||
|
|
||||||
|
pxl8_draw_line(gfx->renderer, gfx_current_color(gfx), x0, y0, x1, y1, 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_draw_mesh(pxl8_gfx* gfx, const pxl8_mesh* mesh, const pxl8_mat4* model, const pxl8_gfx_material* material) {
|
||||||
if (!gfx || !mesh || !model || !material) return;
|
if (!gfx || !mesh || !model || !material) return;
|
||||||
switch (gfx->backend.type) {
|
if (!pxl8_gfx_handle_valid(gfx->frame_pass)) return;
|
||||||
case PXL8_GFX_BACKEND_CPU:
|
|
||||||
pxl8_cpu_draw_mesh(gfx->backend.cpu, mesh, model, material, gfx->atlas);
|
pxl8_frame_resources* res = &gfx->frame_res;
|
||||||
|
bool is_wireframe = gfx->wireframe;
|
||||||
|
|
||||||
|
const char* shader_name = "lit";
|
||||||
|
if (material->emissive || (!material->dynamic_lighting && !material->per_pixel)) {
|
||||||
|
shader_name = "unlit";
|
||||||
|
}
|
||||||
|
|
||||||
|
pxl8_gfx_pipeline_desc pipe_desc = {
|
||||||
|
.blend = {
|
||||||
|
.enabled = false,
|
||||||
|
.src = PXL8_GFX_BLEND_ONE,
|
||||||
|
.dst = PXL8_GFX_BLEND_ZERO,
|
||||||
|
.alpha_test = false,
|
||||||
|
.alpha_ref = 0,
|
||||||
|
},
|
||||||
|
.depth = { .test = true, .write = true, .compare = PXL8_GFX_COMPARE_LESS },
|
||||||
|
.dither = material->dither,
|
||||||
|
.double_sided = material->double_sided,
|
||||||
|
.emissive = material->emissive,
|
||||||
|
.rasterizer = { .cull = PXL8_GFX_CULL_BACK, .fill = is_wireframe ? PXL8_GFX_FILL_WIREFRAME : PXL8_GFX_FILL_SOLID },
|
||||||
|
.shader = pxl8_shader_registry_get(shader_name),
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (material->blend_mode) {
|
||||||
|
case PXL8_BLEND_ALPHA_TEST:
|
||||||
|
pipe_desc.blend.alpha_test = true;
|
||||||
|
pipe_desc.blend.alpha_ref = material->alpha;
|
||||||
break;
|
break;
|
||||||
case PXL8_GFX_BACKEND_GPU:
|
case PXL8_BLEND_ALPHA:
|
||||||
|
pipe_desc.blend.enabled = true;
|
||||||
|
pipe_desc.blend.src = PXL8_GFX_BLEND_SRC_ALPHA;
|
||||||
|
pipe_desc.blend.dst = PXL8_GFX_BLEND_INV_SRC_ALPHA;
|
||||||
|
break;
|
||||||
|
case PXL8_BLEND_ADDITIVE:
|
||||||
|
pipe_desc.blend.enabled = true;
|
||||||
|
pipe_desc.blend.src = PXL8_GFX_BLEND_ONE;
|
||||||
|
pipe_desc.blend.dst = PXL8_GFX_BLEND_ONE;
|
||||||
|
break;
|
||||||
|
case PXL8_BLEND_OPAQUE:
|
||||||
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
pxl8_gfx_pipeline pipeline = pxl8_create_pipeline(gfx->renderer, &pipe_desc);
|
||||||
|
if (res->pipeline_count < PXL8_MAX_FRAME_RESOURCES) {
|
||||||
|
res->pipelines[res->pipeline_count++] = pipeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
pxl8_gfx_bindings_desc bind_desc = {
|
||||||
|
.atlas = gfx->atlas,
|
||||||
|
.colormap = gfx->colormap,
|
||||||
|
.palette = gfx->palette ? pxl8_palette_colors(gfx->palette) : NULL,
|
||||||
|
.texture_id = material->texture_id,
|
||||||
|
};
|
||||||
|
pxl8_gfx_bindings bindings = pxl8_create_bindings(gfx->renderer, &bind_desc);
|
||||||
|
if (res->bindings_count < PXL8_MAX_FRAME_RESOURCES) {
|
||||||
|
res->bindings[res->bindings_count++] = bindings;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 vb_size = mesh->vertex_count * sizeof(pxl8_vertex);
|
||||||
|
u32 ib_size = mesh->index_count * sizeof(u16);
|
||||||
|
|
||||||
|
u32 base_vertex = 0;
|
||||||
|
u32 first_index = 0;
|
||||||
|
pxl8_gfx_buffer vb, ib;
|
||||||
|
|
||||||
|
bool use_stream = pxl8_gfx_handle_valid(gfx->stream_vb) &&
|
||||||
|
pxl8_gfx_handle_valid(gfx->stream_ib) &&
|
||||||
|
(gfx->stream_vb_offset + vb_size <= gfx->stream_vb_capacity) &&
|
||||||
|
(gfx->stream_ib_offset + ib_size <= gfx->stream_ib_capacity);
|
||||||
|
|
||||||
|
if (use_stream) {
|
||||||
|
pxl8_gfx_range vb_data = { .ptr = mesh->vertices, .size = vb_size };
|
||||||
|
pxl8_gfx_range ib_data = { .ptr = mesh->indices, .size = ib_size };
|
||||||
|
i32 vb_offset = pxl8_append_buffer(gfx->renderer, gfx->stream_vb, &vb_data);
|
||||||
|
i32 ib_offset = pxl8_append_buffer(gfx->renderer, gfx->stream_ib, &ib_data);
|
||||||
|
|
||||||
|
if (vb_offset >= 0 && ib_offset >= 0) {
|
||||||
|
gfx->stream_vb_offset += vb_size;
|
||||||
|
gfx->stream_ib_offset += ib_size;
|
||||||
|
|
||||||
|
base_vertex = (u32)vb_offset / sizeof(pxl8_vertex);
|
||||||
|
first_index = (u32)ib_offset / sizeof(u16);
|
||||||
|
|
||||||
|
vb = gfx->stream_vb;
|
||||||
|
ib = gfx->stream_ib;
|
||||||
|
} else {
|
||||||
|
use_stream = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!use_stream) {
|
||||||
|
pxl8_gfx_buffer_desc vb_desc = {
|
||||||
|
.type = PXL8_GFX_BUFFER_VERTEX,
|
||||||
|
.data = { .ptr = mesh->vertices, .size = vb_size },
|
||||||
|
};
|
||||||
|
vb = pxl8_create_buffer(gfx->renderer, &vb_desc);
|
||||||
|
if (res->buffer_count < PXL8_MAX_FRAME_RESOURCES) {
|
||||||
|
res->buffers[res->buffer_count++] = vb;
|
||||||
|
}
|
||||||
|
|
||||||
|
pxl8_gfx_buffer_desc ib_desc = {
|
||||||
|
.type = PXL8_GFX_BUFFER_INDEX,
|
||||||
|
.data = { .ptr = mesh->indices, .size = ib_size },
|
||||||
|
};
|
||||||
|
ib = pxl8_create_buffer(gfx->renderer, &ib_desc);
|
||||||
|
if (res->buffer_count < PXL8_MAX_FRAME_RESOURCES) {
|
||||||
|
res->buffers[res->buffer_count++] = ib;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pxl8_gfx_cmd_draw_params draw_params = {
|
||||||
|
.model = *model,
|
||||||
|
.view = gfx->frame.view,
|
||||||
|
.projection = gfx->frame.projection,
|
||||||
|
.shader = gfx->frame.uniforms,
|
||||||
|
};
|
||||||
|
|
||||||
|
pxl8_set_pipeline(gfx->cmdbuf, pipeline);
|
||||||
|
pxl8_set_bindings(gfx->cmdbuf, bindings);
|
||||||
|
pxl8_set_draw_params(gfx->cmdbuf, &draw_params);
|
||||||
|
pxl8_draw(gfx->cmdbuf, vb, ib, first_index, mesh->index_count, base_vertex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_3d_end_frame(pxl8_gfx* gfx) {
|
void pxl8_3d_end_frame(pxl8_gfx* gfx) {
|
||||||
if (!gfx) return;
|
if (!gfx) { pxl8_error("end_frame: gfx is NULL"); return; }
|
||||||
switch (gfx->backend.type) {
|
if (!pxl8_gfx_handle_valid(gfx->frame_pass)) { pxl8_error("end_frame: frame_pass invalid (id=%u)", gfx->frame_pass.id); return; }
|
||||||
case PXL8_GFX_BACKEND_CPU:
|
|
||||||
pxl8_cpu_end_frame(gfx->backend.cpu);
|
pxl8_end_pass(gfx->cmdbuf);
|
||||||
break;
|
pxl8_gfx_submit(gfx->renderer, gfx->cmdbuf);
|
||||||
case PXL8_GFX_BACKEND_GPU:
|
|
||||||
break;
|
pxl8_frame_resources* res = &gfx->frame_res;
|
||||||
|
|
||||||
|
for (u32 i = 0; i < res->buffer_count; i++) {
|
||||||
|
pxl8_destroy_buffer(gfx->renderer, res->buffers[i]);
|
||||||
}
|
}
|
||||||
|
for (u32 i = 0; i < res->pipeline_count; i++) {
|
||||||
|
pxl8_destroy_pipeline(gfx->renderer, res->pipelines[i]);
|
||||||
|
}
|
||||||
|
for (u32 i = 0; i < res->bindings_count; i++) {
|
||||||
|
pxl8_destroy_bindings(gfx->renderer, res->bindings[i]);
|
||||||
|
}
|
||||||
|
for (u32 i = 0; i < res->texture_count; i++) {
|
||||||
|
pxl8_destroy_texture(gfx->renderer, res->textures[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
pxl8_destroy_pass(gfx->renderer, gfx->frame_pass);
|
||||||
|
gfx->frame_pass = (pxl8_gfx_pass){PXL8_GFX_INVALID_ID};
|
||||||
|
|
||||||
|
res->buffer_count = 0;
|
||||||
|
res->pipeline_count = 0;
|
||||||
|
res->bindings_count = 0;
|
||||||
|
res->texture_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u8* pxl8_3d_get_framebuffer(pxl8_gfx* gfx) {
|
u8* pxl8_3d_get_framebuffer(pxl8_gfx* gfx) {
|
||||||
if (!gfx) return NULL;
|
if (!gfx) return NULL;
|
||||||
switch (gfx->backend.type) {
|
return (u8*)pxl8_texture_get_data(gfx->renderer, gfx_current_color(gfx));
|
||||||
case PXL8_GFX_BACKEND_CPU:
|
|
||||||
return pxl8_cpu_get_framebuffer(gfx->backend.cpu);
|
|
||||||
case PXL8_GFX_BACKEND_GPU:
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pxl8_gfx_push_target(pxl8_gfx* gfx) {
|
bool pxl8_gfx_push_target(pxl8_gfx* gfx) {
|
||||||
if (!gfx) return false;
|
if (!gfx || gfx->target_stack_depth >= PXL8_MAX_TARGET_STACK) return false;
|
||||||
switch (gfx->backend.type) {
|
|
||||||
case PXL8_GFX_BACKEND_CPU:
|
pxl8_gfx_texture_desc color_desc = {
|
||||||
return pxl8_cpu_push_target(gfx->backend.cpu);
|
.width = (u32)gfx->framebuffer_width,
|
||||||
case PXL8_GFX_BACKEND_GPU:
|
.height = (u32)gfx->framebuffer_height,
|
||||||
return false;
|
.format = PXL8_GFX_FORMAT_INDEXED8,
|
||||||
}
|
.render_target = true,
|
||||||
return false;
|
};
|
||||||
|
pxl8_gfx_texture new_color = pxl8_create_texture(gfx->renderer, &color_desc);
|
||||||
|
|
||||||
|
pxl8_gfx_texture_desc depth_desc = {
|
||||||
|
.width = (u32)gfx->framebuffer_width,
|
||||||
|
.height = (u32)gfx->framebuffer_height,
|
||||||
|
.format = PXL8_GFX_FORMAT_DEPTH16,
|
||||||
|
.render_target = true,
|
||||||
|
};
|
||||||
|
pxl8_gfx_texture new_depth = pxl8_create_texture(gfx->renderer, &depth_desc);
|
||||||
|
|
||||||
|
pxl8_gfx_texture_desc light_desc = {
|
||||||
|
.width = (u32)gfx->framebuffer_width,
|
||||||
|
.height = (u32)gfx->framebuffer_height,
|
||||||
|
.format = PXL8_GFX_FORMAT_LIGHT_ACCUM,
|
||||||
|
.render_target = true,
|
||||||
|
};
|
||||||
|
pxl8_gfx_texture new_light = pxl8_create_texture(gfx->renderer, &light_desc);
|
||||||
|
|
||||||
|
gfx->target_stack[gfx->target_stack_depth] = (pxl8_target_entry){
|
||||||
|
.color = new_color,
|
||||||
|
.depth = new_depth,
|
||||||
|
.light = new_light,
|
||||||
|
};
|
||||||
|
gfx->target_stack_depth++;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_gfx_pop_target(pxl8_gfx* gfx) {
|
void pxl8_gfx_pop_target(pxl8_gfx* gfx) {
|
||||||
if (!gfx) return;
|
if (!gfx || gfx->target_stack_depth <= 1) return;
|
||||||
switch (gfx->backend.type) {
|
|
||||||
case PXL8_GFX_BACKEND_CPU:
|
u32 top = gfx->target_stack_depth - 1;
|
||||||
pxl8_cpu_pop_target(gfx->backend.cpu);
|
pxl8_target_entry* entry = &gfx->target_stack[top];
|
||||||
break;
|
pxl8_target_entry* parent = &gfx->target_stack[top - 1];
|
||||||
case PXL8_GFX_BACKEND_GPU:
|
|
||||||
break;
|
gfx_composite_over(gfx, entry, parent);
|
||||||
}
|
|
||||||
|
gfx->target_stack_depth--;
|
||||||
|
|
||||||
|
pxl8_destroy_texture(gfx->renderer, entry->color);
|
||||||
|
pxl8_destroy_texture(gfx->renderer, entry->depth);
|
||||||
|
pxl8_destroy_texture(gfx->renderer, entry->light);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_gfx_ensure_blend_tables(pxl8_gfx* gfx) {
|
void pxl8_gfx_ensure_blend_tables(pxl8_gfx* gfx) {
|
||||||
|
|
@ -797,9 +1040,6 @@ void pxl8_gfx_ensure_blend_tables(pxl8_gfx* gfx) {
|
||||||
|
|
||||||
if (!gfx->palette_cube) {
|
if (!gfx->palette_cube) {
|
||||||
gfx->palette_cube = pxl8_palette_cube_create(gfx->palette);
|
gfx->palette_cube = pxl8_palette_cube_create(gfx->palette);
|
||||||
if (gfx->backend.cpu) {
|
|
||||||
pxl8_cpu_set_palette_cube(gfx->backend.cpu, gfx->palette_cube);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -810,9 +1050,6 @@ void pxl8_gfx_blend_tables_update(pxl8_gfx* gfx) {
|
||||||
|
|
||||||
if (gfx->palette_cube) {
|
if (gfx->palette_cube) {
|
||||||
pxl8_palette_cube_rebuild(gfx->palette_cube, gfx->palette);
|
pxl8_palette_cube_rebuild(gfx->palette_cube, gfx->palette);
|
||||||
if (gfx->backend.cpu) {
|
|
||||||
pxl8_cpu_set_palette_cube(gfx->backend.cpu, gfx->palette_cube);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -838,20 +1075,14 @@ u8 pxl8_gfx_ui_color(pxl8_gfx* gfx, u8 index) {
|
||||||
return pxl8_palette_find_closest(gfx->palette, r, g, b);
|
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) {
|
void pxl8_gfx_set_wireframe(pxl8_gfx* gfx, bool enabled) {
|
||||||
if (!gfx || !params || count == 0) return;
|
if (gfx) gfx->wireframe = enabled;
|
||||||
|
}
|
||||||
switch (effect) {
|
|
||||||
case PXL8_GFX_EFFECT_GLOWS: {
|
bool pxl8_gfx_get_wireframe(const pxl8_gfx* gfx) {
|
||||||
const pxl8_glow* glows = (const pxl8_glow*)params;
|
return gfx ? gfx->wireframe : false;
|
||||||
switch (gfx->backend.type) {
|
}
|
||||||
case PXL8_GFX_BACKEND_CPU:
|
|
||||||
pxl8_cpu_render_glows(gfx->backend.cpu, glows, count);
|
u8 pxl8_gfx_get_ambient(const pxl8_gfx* gfx) {
|
||||||
break;
|
return gfx ? gfx->frame.uniforms.ambient : 0;
|
||||||
case PXL8_GFX_BACKEND_GPU:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
#include "pxl8_gui_palette.h"
|
#include "pxl8_gui_palette.h"
|
||||||
|
|
||||||
typedef struct pxl8_gfx pxl8_gfx;
|
typedef struct pxl8_gfx pxl8_gfx;
|
||||||
|
typedef struct pxl8_gfx_stats pxl8_gfx_stats;
|
||||||
|
|
||||||
typedef enum pxl8_gfx_effect {
|
typedef enum pxl8_gfx_effect {
|
||||||
PXL8_GFX_EFFECT_GLOWS = 0,
|
PXL8_GFX_EFFECT_GLOWS = 0,
|
||||||
|
|
@ -25,6 +26,8 @@ void pxl8_gfx_destroy(pxl8_gfx* gfx);
|
||||||
void pxl8_gfx_present(pxl8_gfx* gfx);
|
void pxl8_gfx_present(pxl8_gfx* gfx);
|
||||||
void pxl8_gfx_update(pxl8_gfx* gfx, f32 dt);
|
void pxl8_gfx_update(pxl8_gfx* gfx, f32 dt);
|
||||||
void pxl8_gfx_upload_framebuffer(pxl8_gfx* gfx);
|
void pxl8_gfx_upload_framebuffer(pxl8_gfx* gfx);
|
||||||
|
void pxl8_gfx_reset_stats(pxl8_gfx* gfx);
|
||||||
|
const pxl8_gfx_stats* pxl8_gfx_get_stats(pxl8_gfx* gfx);
|
||||||
|
|
||||||
u8 pxl8_gfx_find_color(pxl8_gfx* gfx, u32 color);
|
u8 pxl8_gfx_find_color(pxl8_gfx* gfx, u32 color);
|
||||||
|
|
||||||
|
|
@ -62,6 +65,11 @@ 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_find_closest_color(pxl8_gfx* gfx, u8 r, u8 g, u8 b);
|
||||||
u8 pxl8_gfx_ui_color(pxl8_gfx* gfx, u8 index);
|
u8 pxl8_gfx_ui_color(pxl8_gfx* gfx, u8 index);
|
||||||
|
|
||||||
|
void pxl8_gfx_set_wireframe(pxl8_gfx* gfx, bool enabled);
|
||||||
|
bool pxl8_gfx_get_wireframe(const pxl8_gfx* gfx);
|
||||||
|
|
||||||
|
u8 pxl8_gfx_get_ambient(const pxl8_gfx* gfx);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -4,39 +4,20 @@
|
||||||
#include "pxl8_lights.h"
|
#include "pxl8_lights.h"
|
||||||
#include "pxl8_math.h"
|
#include "pxl8_math.h"
|
||||||
#include "pxl8_mesh.h"
|
#include "pxl8_mesh.h"
|
||||||
|
#include "pxl8_shader.h"
|
||||||
#include "pxl8_types.h"
|
#include "pxl8_types.h"
|
||||||
|
|
||||||
typedef struct pxl8_bsp pxl8_bsp;
|
typedef struct pxl8_bsp pxl8_bsp;
|
||||||
typedef struct pxl8_gfx pxl8_gfx;
|
typedef struct pxl8_gfx pxl8_gfx;
|
||||||
|
|
||||||
typedef struct pxl8_3d_uniforms {
|
|
||||||
u8 ambient;
|
|
||||||
pxl8_vec3 celestial_dir;
|
|
||||||
f32 celestial_intensity;
|
|
||||||
u8 fog_color;
|
|
||||||
f32 fog_density;
|
|
||||||
f32 time;
|
|
||||||
} pxl8_3d_uniforms;
|
|
||||||
|
|
||||||
typedef struct pxl8_3d_frame_desc {
|
|
||||||
const pxl8_bsp* bsp;
|
|
||||||
const pxl8_3d_camera* camera;
|
|
||||||
const pxl8_lights* lights;
|
|
||||||
const pxl8_sdf* sdf;
|
|
||||||
pxl8_3d_uniforms uniforms;
|
|
||||||
} pxl8_3d_frame_desc;
|
|
||||||
|
|
||||||
typedef struct pxl8_3d_frame {
|
typedef struct pxl8_3d_frame {
|
||||||
const pxl8_bsp* bsp;
|
const pxl8_bsp* bsp;
|
||||||
pxl8_vec3 camera_dir;
|
pxl8_vec3 camera_dir;
|
||||||
pxl8_vec3 camera_pos;
|
pxl8_vec3 camera_pos;
|
||||||
f32 far_clip;
|
f32 far_clip;
|
||||||
const pxl8_light* lights;
|
|
||||||
u32 lights_count;
|
|
||||||
f32 near_clip;
|
f32 near_clip;
|
||||||
pxl8_mat4 projection;
|
pxl8_mat4 projection;
|
||||||
const pxl8_sdf* sdf;
|
pxl8_shader_uniforms uniforms;
|
||||||
pxl8_3d_uniforms uniforms;
|
|
||||||
pxl8_mat4 view;
|
pxl8_mat4 view;
|
||||||
} pxl8_3d_frame;
|
} pxl8_3d_frame;
|
||||||
|
|
||||||
|
|
@ -45,8 +26,7 @@ extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void pxl8_3d_set_bsp(pxl8_gfx* gfx, const pxl8_bsp* bsp);
|
void pxl8_3d_set_bsp(pxl8_gfx* gfx, const pxl8_bsp* bsp);
|
||||||
void pxl8_3d_set_sdf(pxl8_gfx* gfx, const pxl8_sdf* sdf);
|
void pxl8_3d_begin_frame(pxl8_gfx* gfx, const pxl8_3d_camera* camera, const pxl8_lights* lights, const pxl8_shader_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(pxl8_gfx* gfx, u8 color);
|
||||||
void pxl8_3d_clear_depth(pxl8_gfx* gfx);
|
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_line(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, u8 color);
|
||||||
|
|
@ -57,7 +37,7 @@ const pxl8_frustum* pxl8_3d_get_frustum(pxl8_gfx* gfx);
|
||||||
const pxl8_mat4* pxl8_3d_get_view_proj(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);
|
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);
|
pxl8_3d_frame pxl8_3d_frame_from_camera(const pxl8_3d_camera* camera, const pxl8_shader_uniforms* uniforms);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,36 @@ void pxl8_lights_add(pxl8_lights* lights, f32 x, f32 y, f32 z, u8 r, u8 g, u8 b,
|
||||||
l->radius = radius;
|
l->radius = radius;
|
||||||
l->radius_sq = radius_sq;
|
l->radius_sq = radius_sq;
|
||||||
l->inv_radius_sq = radius_sq > 0.0f ? 1.0f / radius_sq : 0.0f;
|
l->inv_radius_sq = radius_sq > 0.0f ? 1.0f / radius_sq : 0.0f;
|
||||||
|
|
||||||
|
l->constant = 1.0f;
|
||||||
|
if (radius <= 7.0f) {
|
||||||
|
l->linear = 0.7f;
|
||||||
|
l->quadratic = 1.8f;
|
||||||
|
} else if (radius <= 13.0f) {
|
||||||
|
l->linear = 0.35f;
|
||||||
|
l->quadratic = 0.44f;
|
||||||
|
} else if (radius <= 20.0f) {
|
||||||
|
l->linear = 0.22f;
|
||||||
|
l->quadratic = 0.20f;
|
||||||
|
} else if (radius <= 32.0f) {
|
||||||
|
l->linear = 0.14f;
|
||||||
|
l->quadratic = 0.07f;
|
||||||
|
} else if (radius <= 50.0f) {
|
||||||
|
l->linear = 0.09f;
|
||||||
|
l->quadratic = 0.032f;
|
||||||
|
} else if (radius <= 65.0f) {
|
||||||
|
l->linear = 0.07f;
|
||||||
|
l->quadratic = 0.017f;
|
||||||
|
} else if (radius <= 100.0f) {
|
||||||
|
l->linear = 0.045f;
|
||||||
|
l->quadratic = 0.0075f;
|
||||||
|
} else if (radius <= 160.0f) {
|
||||||
|
l->linear = 0.027f;
|
||||||
|
l->quadratic = 0.0028f;
|
||||||
|
} else {
|
||||||
|
l->linear = 0.022f;
|
||||||
|
l->quadratic = 0.0019f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_lights_clear(pxl8_lights* lights) {
|
void pxl8_lights_clear(pxl8_lights* lights) {
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,9 @@ typedef struct pxl8_light {
|
||||||
u8 intensity;
|
u8 intensity;
|
||||||
f32 radius;
|
f32 radius;
|
||||||
f32 radius_sq;
|
f32 radius_sq;
|
||||||
|
f32 constant;
|
||||||
|
f32 linear;
|
||||||
|
f32 quadratic;
|
||||||
} pxl8_light;
|
} pxl8_light;
|
||||||
|
|
||||||
typedef struct pxl8_lights pxl8_lights;
|
typedef struct pxl8_lights pxl8_lights;
|
||||||
|
|
|
||||||
|
|
@ -31,10 +31,8 @@ typedef struct pxl8_gfx_material {
|
||||||
bool dither;
|
bool dither;
|
||||||
bool double_sided;
|
bool double_sided;
|
||||||
bool dynamic_lighting;
|
bool dynamic_lighting;
|
||||||
|
bool emissive;
|
||||||
bool per_pixel;
|
bool per_pixel;
|
||||||
bool vertex_color_passthrough;
|
|
||||||
bool wireframe;
|
|
||||||
f32 emissive_intensity;
|
|
||||||
} pxl8_gfx_material;
|
} pxl8_gfx_material;
|
||||||
|
|
||||||
typedef struct pxl8_vertex {
|
typedef struct pxl8_vertex {
|
||||||
|
|
|
||||||
1502
src/gfx/pxl8_render.c
Normal file
1502
src/gfx/pxl8_render.c
Normal file
File diff suppressed because it is too large
Load diff
98
src/gfx/pxl8_render.h
Normal file
98
src/gfx/pxl8_render.h
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "pxl8_colormap.h"
|
||||||
|
#include "pxl8_render_types.h"
|
||||||
|
#include "pxl8_shader.h"
|
||||||
|
|
||||||
|
#ifndef PXL8_GFX_ENABLE_STATS
|
||||||
|
#define PXL8_GFX_ENABLE_STATS 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct pxl8_renderer pxl8_renderer;
|
||||||
|
typedef struct pxl8_gfx_cmdbuf pxl8_gfx_cmdbuf;
|
||||||
|
|
||||||
|
typedef struct pxl8_gfx_stats {
|
||||||
|
u64 draw_calls;
|
||||||
|
u64 triangles;
|
||||||
|
u64 clipped_triangles;
|
||||||
|
u64 depth_tests;
|
||||||
|
u64 depth_passes;
|
||||||
|
u64 shader_calls;
|
||||||
|
u64 pixels_written;
|
||||||
|
u64 light_writes;
|
||||||
|
u64 submit_ns;
|
||||||
|
u64 execute_draw_ns;
|
||||||
|
u64 raster_ns;
|
||||||
|
} pxl8_gfx_stats;
|
||||||
|
|
||||||
|
pxl8_renderer* pxl8_renderer_create(u32 width, u32 height);
|
||||||
|
void pxl8_renderer_destroy(pxl8_renderer* r);
|
||||||
|
void pxl8_renderer_set_shader(pxl8_renderer* r, pxl8_shader_fn fn);
|
||||||
|
void pxl8_renderer_reset_stats(pxl8_renderer* r);
|
||||||
|
const pxl8_gfx_stats* pxl8_renderer_get_stats(const pxl8_renderer* r);
|
||||||
|
|
||||||
|
u32 pxl8_renderer_get_width(const pxl8_renderer* r);
|
||||||
|
u32 pxl8_renderer_get_height(const pxl8_renderer* r);
|
||||||
|
|
||||||
|
pxl8_gfx_bindings pxl8_create_bindings(pxl8_renderer* r, const pxl8_gfx_bindings_desc* desc);
|
||||||
|
pxl8_gfx_buffer pxl8_create_buffer(pxl8_renderer* r, const pxl8_gfx_buffer_desc* desc);
|
||||||
|
pxl8_gfx_pass pxl8_create_pass(pxl8_renderer* r, const pxl8_gfx_pass_desc* desc);
|
||||||
|
pxl8_gfx_pipeline pxl8_create_pipeline(pxl8_renderer* r, const pxl8_gfx_pipeline_desc* desc);
|
||||||
|
pxl8_gfx_texture pxl8_create_texture(pxl8_renderer* r, const pxl8_gfx_texture_desc* desc);
|
||||||
|
|
||||||
|
void pxl8_destroy_bindings(pxl8_renderer* r, pxl8_gfx_bindings bnd);
|
||||||
|
void pxl8_destroy_buffer(pxl8_renderer* r, pxl8_gfx_buffer buf);
|
||||||
|
void pxl8_destroy_pass(pxl8_renderer* r, pxl8_gfx_pass pass);
|
||||||
|
void pxl8_destroy_pipeline(pxl8_renderer* r, pxl8_gfx_pipeline pip);
|
||||||
|
void pxl8_destroy_texture(pxl8_renderer* r, pxl8_gfx_texture tex);
|
||||||
|
|
||||||
|
void pxl8_update_buffer(pxl8_renderer* r, pxl8_gfx_buffer buf, const pxl8_gfx_range* data);
|
||||||
|
i32 pxl8_append_buffer(pxl8_renderer* r, pxl8_gfx_buffer buf, const pxl8_gfx_range* data);
|
||||||
|
void pxl8_update_texture(pxl8_renderer* r, pxl8_gfx_texture tex, const pxl8_gfx_range* data, u32 x, u32 y, u32 w, u32 h);
|
||||||
|
|
||||||
|
void* pxl8_buffer_ptr(pxl8_renderer* r, pxl8_gfx_buffer buf);
|
||||||
|
u32 pxl8_buffer_size(pxl8_renderer* r, pxl8_gfx_buffer buf);
|
||||||
|
|
||||||
|
void* pxl8_texture_get_data(pxl8_renderer* r, pxl8_gfx_texture tex);
|
||||||
|
u32 pxl8_texture_get_width(pxl8_renderer* r, pxl8_gfx_texture tex);
|
||||||
|
u32 pxl8_texture_get_height(pxl8_renderer* r, pxl8_gfx_texture tex);
|
||||||
|
pxl8_gfx_texture_format pxl8_texture_get_format(pxl8_renderer* r, pxl8_gfx_texture tex);
|
||||||
|
|
||||||
|
pxl8_gfx_cmdbuf* pxl8_cmdbuf_create(u32 capacity);
|
||||||
|
void pxl8_cmdbuf_destroy(pxl8_gfx_cmdbuf* cb);
|
||||||
|
void pxl8_cmdbuf_reset(pxl8_gfx_cmdbuf* cb);
|
||||||
|
|
||||||
|
void pxl8_begin_pass(pxl8_gfx_cmdbuf* cb, pxl8_gfx_pass pass);
|
||||||
|
void pxl8_end_pass(pxl8_gfx_cmdbuf* cb);
|
||||||
|
void pxl8_set_bindings(pxl8_gfx_cmdbuf* cb, pxl8_gfx_bindings bindings);
|
||||||
|
void pxl8_set_draw_params(pxl8_gfx_cmdbuf* cb, const pxl8_gfx_cmd_draw_params* p);
|
||||||
|
void pxl8_set_pipeline(pxl8_gfx_cmdbuf* cb, pxl8_gfx_pipeline pipeline);
|
||||||
|
void pxl8_set_scissor(pxl8_gfx_cmdbuf* cb, i32 x, i32 y, u32 w, u32 h);
|
||||||
|
void pxl8_set_viewport(pxl8_gfx_cmdbuf* cb, i32 x, i32 y, u32 w, u32 h);
|
||||||
|
|
||||||
|
void pxl8_draw(pxl8_gfx_cmdbuf* cb, pxl8_gfx_buffer vb, pxl8_gfx_buffer ib, u32 first, u32 count, u32 base_vertex);
|
||||||
|
|
||||||
|
void pxl8_gfx_submit(pxl8_renderer* r, pxl8_gfx_cmdbuf* cb);
|
||||||
|
|
||||||
|
void pxl8_clear(pxl8_renderer* r, pxl8_gfx_texture target, u8 color);
|
||||||
|
void pxl8_clear_depth(pxl8_renderer* r, pxl8_gfx_texture target);
|
||||||
|
void pxl8_clear_light(pxl8_renderer* r, pxl8_gfx_texture target);
|
||||||
|
|
||||||
|
void pxl8_draw_pixel(pxl8_renderer* r, pxl8_gfx_texture target, i32 x, i32 y, u8 color);
|
||||||
|
u8 pxl8_get_pixel(pxl8_renderer* r, pxl8_gfx_texture target, i32 x, i32 y);
|
||||||
|
void pxl8_draw_line(pxl8_renderer* r, pxl8_gfx_texture target, i32 x0, i32 y0, i32 x1, i32 y1, u8 color);
|
||||||
|
void pxl8_draw_rect(pxl8_renderer* r, pxl8_gfx_texture target, i32 x, i32 y, i32 w, i32 h, u8 color);
|
||||||
|
void pxl8_draw_rect_fill(pxl8_renderer* r, pxl8_gfx_texture target, i32 x, i32 y, i32 w, i32 h, u8 color);
|
||||||
|
void pxl8_draw_circle(pxl8_renderer* r, pxl8_gfx_texture target, i32 cx, i32 cy, i32 radius, u8 color);
|
||||||
|
void pxl8_draw_circle_fill(pxl8_renderer* r, pxl8_gfx_texture target, i32 cx, i32 cy, i32 radius, u8 color);
|
||||||
|
|
||||||
|
void pxl8_resolve_to_rgba(pxl8_renderer* r, pxl8_gfx_texture color, pxl8_gfx_texture light_accum,
|
||||||
|
const u32* palette, u32* output);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
242
src/gfx/pxl8_render_types.h
Normal file
242
src/gfx/pxl8_render_types.h
Normal file
|
|
@ -0,0 +1,242 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "pxl8_atlas.h"
|
||||||
|
#include "pxl8_colormap.h"
|
||||||
|
#include "pxl8_lights.h"
|
||||||
|
#include "pxl8_math.h"
|
||||||
|
#include "pxl8_shader.h"
|
||||||
|
#include "pxl8_types.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define PXL8_GFX_MAX_BINDINGS 256
|
||||||
|
#define PXL8_GFX_MAX_BUFFERS 512
|
||||||
|
#define PXL8_GFX_MAX_PASSES 32
|
||||||
|
#define PXL8_GFX_MAX_PIPELINES 128
|
||||||
|
#define PXL8_GFX_MAX_TEXTURES 256
|
||||||
|
|
||||||
|
typedef struct { u32 id; } pxl8_gfx_buffer;
|
||||||
|
typedef struct { u32 id; } pxl8_gfx_texture;
|
||||||
|
typedef struct { u32 id; } pxl8_gfx_pipeline;
|
||||||
|
typedef struct { u32 id; } pxl8_gfx_bindings;
|
||||||
|
typedef struct { u32 id; } pxl8_gfx_pass;
|
||||||
|
|
||||||
|
#define PXL8_GFX_INVALID_ID (0)
|
||||||
|
#define pxl8_gfx_handle_valid(h) ((h).id != PXL8_GFX_INVALID_ID)
|
||||||
|
|
||||||
|
typedef struct pxl8_gfx_range {
|
||||||
|
const void* ptr;
|
||||||
|
u32 size;
|
||||||
|
} pxl8_gfx_range;
|
||||||
|
|
||||||
|
#define PXL8_GFX_RANGE(x) ((pxl8_gfx_range){ .ptr = &(x), .size = sizeof(x) })
|
||||||
|
#define PXL8_GFX_RANGE_REF(p, sz) ((pxl8_gfx_range){ .ptr = (p), .size = (sz) })
|
||||||
|
|
||||||
|
typedef enum pxl8_gfx_buffer_type {
|
||||||
|
PXL8_GFX_BUFFER_VERTEX,
|
||||||
|
PXL8_GFX_BUFFER_INDEX,
|
||||||
|
} pxl8_gfx_buffer_type;
|
||||||
|
|
||||||
|
typedef enum pxl8_gfx_usage {
|
||||||
|
PXL8_GFX_USAGE_IMMUTABLE,
|
||||||
|
PXL8_GFX_USAGE_DYNAMIC,
|
||||||
|
PXL8_GFX_USAGE_STREAM,
|
||||||
|
} pxl8_gfx_usage;
|
||||||
|
|
||||||
|
typedef enum pxl8_gfx_texture_format {
|
||||||
|
PXL8_GFX_FORMAT_INDEXED8,
|
||||||
|
PXL8_GFX_FORMAT_DEPTH16,
|
||||||
|
PXL8_GFX_FORMAT_LIGHT_ACCUM,
|
||||||
|
} pxl8_gfx_texture_format;
|
||||||
|
|
||||||
|
typedef enum pxl8_gfx_cull_mode {
|
||||||
|
PXL8_GFX_CULL_NONE,
|
||||||
|
PXL8_GFX_CULL_BACK,
|
||||||
|
PXL8_GFX_CULL_FRONT,
|
||||||
|
} pxl8_gfx_cull_mode;
|
||||||
|
|
||||||
|
typedef enum pxl8_gfx_fill_mode {
|
||||||
|
PXL8_GFX_FILL_SOLID,
|
||||||
|
PXL8_GFX_FILL_WIREFRAME,
|
||||||
|
} pxl8_gfx_fill_mode;
|
||||||
|
|
||||||
|
typedef enum pxl8_gfx_compare_func {
|
||||||
|
PXL8_GFX_COMPARE_NEVER,
|
||||||
|
PXL8_GFX_COMPARE_LESS,
|
||||||
|
PXL8_GFX_COMPARE_EQUAL,
|
||||||
|
PXL8_GFX_COMPARE_LEQUAL,
|
||||||
|
PXL8_GFX_COMPARE_GREATER,
|
||||||
|
PXL8_GFX_COMPARE_NOTEQUAL,
|
||||||
|
PXL8_GFX_COMPARE_GEQUAL,
|
||||||
|
PXL8_GFX_COMPARE_ALWAYS,
|
||||||
|
} pxl8_gfx_compare_func;
|
||||||
|
|
||||||
|
typedef enum pxl8_gfx_blend_factor {
|
||||||
|
PXL8_GFX_BLEND_ZERO,
|
||||||
|
PXL8_GFX_BLEND_ONE,
|
||||||
|
PXL8_GFX_BLEND_SRC_ALPHA,
|
||||||
|
PXL8_GFX_BLEND_INV_SRC_ALPHA,
|
||||||
|
PXL8_GFX_BLEND_DST_ALPHA,
|
||||||
|
PXL8_GFX_BLEND_INV_DST_ALPHA,
|
||||||
|
} pxl8_gfx_blend_factor;
|
||||||
|
|
||||||
|
typedef enum pxl8_gfx_load_op {
|
||||||
|
PXL8_GFX_LOAD_CLEAR,
|
||||||
|
PXL8_GFX_LOAD_LOAD,
|
||||||
|
PXL8_GFX_LOAD_DONT_CARE,
|
||||||
|
} pxl8_gfx_load_op;
|
||||||
|
|
||||||
|
typedef enum pxl8_gfx_store_op {
|
||||||
|
PXL8_GFX_STORE_STORE,
|
||||||
|
PXL8_GFX_STORE_DONT_CARE,
|
||||||
|
} pxl8_gfx_store_op;
|
||||||
|
|
||||||
|
typedef struct pxl8_gfx_buffer_desc {
|
||||||
|
pxl8_gfx_range data;
|
||||||
|
u32 capacity;
|
||||||
|
pxl8_gfx_buffer_type type;
|
||||||
|
pxl8_gfx_usage usage;
|
||||||
|
} pxl8_gfx_buffer_desc;
|
||||||
|
|
||||||
|
typedef struct pxl8_gfx_texture_desc {
|
||||||
|
u32 width;
|
||||||
|
u32 height;
|
||||||
|
pxl8_gfx_texture_format format;
|
||||||
|
pxl8_gfx_range data;
|
||||||
|
pxl8_gfx_usage usage;
|
||||||
|
bool render_target;
|
||||||
|
} pxl8_gfx_texture_desc;
|
||||||
|
|
||||||
|
typedef pxl8_shader_fn pxl8_gfx_shader;
|
||||||
|
|
||||||
|
typedef struct pxl8_gfx_pipeline_desc {
|
||||||
|
struct {
|
||||||
|
bool enabled;
|
||||||
|
pxl8_gfx_blend_factor src;
|
||||||
|
pxl8_gfx_blend_factor dst;
|
||||||
|
bool alpha_test;
|
||||||
|
u8 alpha_ref;
|
||||||
|
} blend;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool test;
|
||||||
|
bool write;
|
||||||
|
pxl8_gfx_compare_func compare;
|
||||||
|
} depth;
|
||||||
|
|
||||||
|
bool dither;
|
||||||
|
bool double_sided;
|
||||||
|
bool emissive;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
pxl8_gfx_cull_mode cull;
|
||||||
|
pxl8_gfx_fill_mode fill;
|
||||||
|
} rasterizer;
|
||||||
|
|
||||||
|
pxl8_gfx_shader shader;
|
||||||
|
} pxl8_gfx_pipeline_desc;
|
||||||
|
|
||||||
|
typedef struct pxl8_gfx_bindings_desc {
|
||||||
|
const pxl8_atlas* atlas;
|
||||||
|
const pxl8_colormap* colormap;
|
||||||
|
const u32* palette;
|
||||||
|
u32 texture_id;
|
||||||
|
} pxl8_gfx_bindings_desc;
|
||||||
|
|
||||||
|
typedef struct pxl8_gfx_pass_color_attachment {
|
||||||
|
pxl8_gfx_texture texture;
|
||||||
|
pxl8_gfx_load_op load;
|
||||||
|
pxl8_gfx_store_op store;
|
||||||
|
u8 clear_value;
|
||||||
|
} pxl8_gfx_pass_color_attachment;
|
||||||
|
|
||||||
|
typedef struct pxl8_gfx_pass_depth_attachment {
|
||||||
|
pxl8_gfx_texture texture;
|
||||||
|
pxl8_gfx_load_op load;
|
||||||
|
u16 clear_value;
|
||||||
|
} pxl8_gfx_pass_depth_attachment;
|
||||||
|
|
||||||
|
typedef struct pxl8_gfx_pass_light_attachment {
|
||||||
|
pxl8_gfx_texture texture;
|
||||||
|
pxl8_gfx_load_op load;
|
||||||
|
} pxl8_gfx_pass_light_attachment;
|
||||||
|
|
||||||
|
typedef struct pxl8_gfx_pass_desc {
|
||||||
|
pxl8_gfx_pass_color_attachment color;
|
||||||
|
pxl8_gfx_pass_depth_attachment depth;
|
||||||
|
pxl8_gfx_pass_light_attachment light_accum;
|
||||||
|
} pxl8_gfx_pass_desc;
|
||||||
|
|
||||||
|
typedef struct pxl8_gfx_cmd_draw_params {
|
||||||
|
pxl8_mat4 model;
|
||||||
|
pxl8_mat4 projection;
|
||||||
|
pxl8_shader_uniforms shader;
|
||||||
|
pxl8_mat4 view;
|
||||||
|
} pxl8_gfx_cmd_draw_params;
|
||||||
|
|
||||||
|
typedef enum pxl8_gfx_cmd_type {
|
||||||
|
PXL8_GFX_CMD_BEGIN_PASS,
|
||||||
|
PXL8_GFX_CMD_END_PASS,
|
||||||
|
PXL8_GFX_CMD_SET_PIPELINE,
|
||||||
|
PXL8_GFX_CMD_SET_BINDINGS,
|
||||||
|
PXL8_GFX_CMD_SET_VIEWPORT,
|
||||||
|
PXL8_GFX_CMD_SET_SCISSOR,
|
||||||
|
PXL8_GFX_CMD_SET_DRAW_PARAMS,
|
||||||
|
PXL8_GFX_CMD_DRAW,
|
||||||
|
PXL8_GFX_CMD_RESOLVE,
|
||||||
|
} pxl8_gfx_cmd_type;
|
||||||
|
|
||||||
|
typedef struct pxl8_gfx_cmd_begin_pass {
|
||||||
|
pxl8_gfx_pass pass;
|
||||||
|
} pxl8_gfx_cmd_begin_pass;
|
||||||
|
|
||||||
|
typedef struct pxl8_gfx_cmd_set_pipeline {
|
||||||
|
pxl8_gfx_pipeline pipeline;
|
||||||
|
} pxl8_gfx_cmd_set_pipeline;
|
||||||
|
|
||||||
|
typedef struct pxl8_gfx_cmd_set_bindings {
|
||||||
|
pxl8_gfx_bindings bindings;
|
||||||
|
} pxl8_gfx_cmd_set_bindings;
|
||||||
|
|
||||||
|
typedef struct pxl8_gfx_cmd_set_viewport {
|
||||||
|
i32 x, y;
|
||||||
|
u32 w, h;
|
||||||
|
} pxl8_gfx_cmd_set_viewport;
|
||||||
|
|
||||||
|
typedef struct pxl8_gfx_cmd_set_scissor {
|
||||||
|
i32 x, y;
|
||||||
|
u32 w, h;
|
||||||
|
} pxl8_gfx_cmd_set_scissor;
|
||||||
|
|
||||||
|
typedef struct pxl8_gfx_cmd_draw {
|
||||||
|
pxl8_gfx_buffer vertex_buffer;
|
||||||
|
pxl8_gfx_buffer index_buffer;
|
||||||
|
u32 base_vertex;
|
||||||
|
u32 first_index;
|
||||||
|
u32 index_count;
|
||||||
|
} pxl8_gfx_cmd_draw;
|
||||||
|
|
||||||
|
typedef struct pxl8_gfx_cmd_resolve {
|
||||||
|
pxl8_gfx_texture src;
|
||||||
|
u32* output;
|
||||||
|
} pxl8_gfx_cmd_resolve;
|
||||||
|
|
||||||
|
typedef struct pxl8_gfx_cmd {
|
||||||
|
pxl8_gfx_cmd_type type;
|
||||||
|
union {
|
||||||
|
pxl8_gfx_cmd_begin_pass begin_pass;
|
||||||
|
pxl8_gfx_cmd_set_pipeline set_pipeline;
|
||||||
|
pxl8_gfx_cmd_set_bindings set_bindings;
|
||||||
|
pxl8_gfx_cmd_set_viewport set_viewport;
|
||||||
|
pxl8_gfx_cmd_set_scissor set_scissor;
|
||||||
|
pxl8_gfx_cmd_draw draw;
|
||||||
|
pxl8_gfx_cmd_draw_params draw_params;
|
||||||
|
pxl8_gfx_cmd_resolve resolve;
|
||||||
|
};
|
||||||
|
} pxl8_gfx_cmd;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
81
src/gfx/pxl8_shader.h
Normal file
81
src/gfx/pxl8_shader.h
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "pxl8_lights.h"
|
||||||
|
#include "pxl8_math.h"
|
||||||
|
#include "pxl8_types.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct pxl8_vertex_in {
|
||||||
|
pxl8_vec3 a_position;
|
||||||
|
pxl8_vec3 a_normal;
|
||||||
|
pxl8_vec2 a_uv;
|
||||||
|
u8 a_color;
|
||||||
|
u8 a_light;
|
||||||
|
} pxl8_vertex_in;
|
||||||
|
|
||||||
|
typedef struct pxl8_vertex_out {
|
||||||
|
pxl8_vec4 gl_Position;
|
||||||
|
pxl8_vec2 v_uv;
|
||||||
|
pxl8_vec3 v_world;
|
||||||
|
pxl8_vec3 v_normal;
|
||||||
|
f32 v_light;
|
||||||
|
f32 v_depth;
|
||||||
|
} pxl8_vertex_out;
|
||||||
|
|
||||||
|
typedef struct pxl8_shader_uniforms {
|
||||||
|
u8 ambient;
|
||||||
|
bool baked_lighting;
|
||||||
|
pxl8_vec3 celestial_dir;
|
||||||
|
f32 celestial_intensity;
|
||||||
|
bool dither;
|
||||||
|
bool dynamic_lighting;
|
||||||
|
bool emissive;
|
||||||
|
u8 fog_color;
|
||||||
|
f32 fog_density;
|
||||||
|
const pxl8_light* lights;
|
||||||
|
u32 lights_count;
|
||||||
|
bool textures;
|
||||||
|
f32 time;
|
||||||
|
} pxl8_shader_uniforms;
|
||||||
|
|
||||||
|
typedef struct pxl8_shader_bindings {
|
||||||
|
const u8* colormap;
|
||||||
|
const u32* palette;
|
||||||
|
const u8* atlas;
|
||||||
|
u32 width, height;
|
||||||
|
bool use_tiled;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
u32 stride;
|
||||||
|
u32 x, y;
|
||||||
|
} linear;
|
||||||
|
struct {
|
||||||
|
u32 base;
|
||||||
|
u8 log2_w;
|
||||||
|
} tiled;
|
||||||
|
};
|
||||||
|
} pxl8_shader_bindings;
|
||||||
|
|
||||||
|
typedef struct pxl8_shader_ctx {
|
||||||
|
i32 x, y;
|
||||||
|
pxl8_vec2 v_uv;
|
||||||
|
pxl8_vec3 v_world;
|
||||||
|
pxl8_vec3 v_normal;
|
||||||
|
f32 v_color;
|
||||||
|
f32 v_light;
|
||||||
|
f32 v_depth;
|
||||||
|
u32 out_light_color;
|
||||||
|
} pxl8_shader_ctx;
|
||||||
|
|
||||||
|
typedef u8 (*pxl8_shader_fn)(
|
||||||
|
pxl8_shader_ctx* ctx,
|
||||||
|
const pxl8_shader_bindings* bindings,
|
||||||
|
const pxl8_shader_uniforms* uniforms
|
||||||
|
);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
80
src/gfx/pxl8_shader_builtins.h
Normal file
80
src/gfx/pxl8_shader_builtins.h
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "pxl8_dither.h"
|
||||||
|
#include "pxl8_math.h"
|
||||||
|
#include "pxl8_shader.h"
|
||||||
|
#include "pxl8_types.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static inline pxl8_vec2 pxl8_swizzle_xy(pxl8_vec4 v) { return (pxl8_vec2){v.x, v.y}; }
|
||||||
|
static inline pxl8_vec2 pxl8_swizzle_xz(pxl8_vec4 v) { return (pxl8_vec2){v.x, v.z}; }
|
||||||
|
static inline pxl8_vec2 pxl8_swizzle_xw(pxl8_vec4 v) { return (pxl8_vec2){v.x, v.w}; }
|
||||||
|
static inline pxl8_vec2 pxl8_swizzle_yz(pxl8_vec4 v) { return (pxl8_vec2){v.y, v.z}; }
|
||||||
|
static inline pxl8_vec2 pxl8_swizzle_yw(pxl8_vec4 v) { return (pxl8_vec2){v.y, v.w}; }
|
||||||
|
static inline pxl8_vec2 pxl8_swizzle_zw(pxl8_vec4 v) { return (pxl8_vec2){v.z, v.w}; }
|
||||||
|
|
||||||
|
static inline pxl8_vec3 pxl8_swizzle_xyz(pxl8_vec4 v) { return (pxl8_vec3){v.x, v.y, v.z}; }
|
||||||
|
static inline pxl8_vec3 pxl8_swizzle_xyw(pxl8_vec4 v) { return (pxl8_vec3){v.x, v.y, v.w}; }
|
||||||
|
static inline pxl8_vec3 pxl8_swizzle_xzw(pxl8_vec4 v) { return (pxl8_vec3){v.x, v.z, v.w}; }
|
||||||
|
static inline pxl8_vec3 pxl8_swizzle_yzw(pxl8_vec4 v) { return (pxl8_vec3){v.y, v.z, v.w}; }
|
||||||
|
|
||||||
|
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_sample_indexed(const pxl8_shader_bindings* b, pxl8_vec2 uv) {
|
||||||
|
if (!b || !b->atlas) return 0;
|
||||||
|
u32 w = b->width;
|
||||||
|
u32 h = b->height;
|
||||||
|
if (w == 0 || h == 0) return 0;
|
||||||
|
u32 u = (u32)(uv.x * (f32)w) & (w - 1);
|
||||||
|
u32 v = (u32)(uv.y * (f32)h) & (h - 1);
|
||||||
|
if (b->use_tiled) {
|
||||||
|
return b->atlas[b->tiled.base + pxl8_tile_addr(u, v, b->tiled.log2_w)];
|
||||||
|
} else {
|
||||||
|
return b->atlas[(b->linear.y + v) * b->linear.stride + b->linear.x + u];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u8 pxl8_colormap_lookup(const pxl8_shader_bindings* b, u8 color, u8 light) {
|
||||||
|
if (!b) return color;
|
||||||
|
const u8* cm = b->colormap;
|
||||||
|
if (!cm) return color;
|
||||||
|
u32 row = light >> 5;
|
||||||
|
return cm[(row << 8) | (u32)color];
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline f32 pxl8_light_falloff(const pxl8_shader_ctx* ctx, const pxl8_shader_uniforms* u, u32 light_idx) {
|
||||||
|
if (!u || light_idx >= u->lights_count) return 0.0f;
|
||||||
|
const pxl8_light* light = &u->lights[light_idx];
|
||||||
|
f32 dx = light->position.x - ctx->v_world.x;
|
||||||
|
f32 dy = light->position.y - ctx->v_world.y;
|
||||||
|
f32 dz = light->position.z - ctx->v_world.z;
|
||||||
|
f32 dist_sq = dx * dx + dy * dy + dz * dz;
|
||||||
|
if (dist_sq >= light->radius_sq) return 0.0f;
|
||||||
|
return 1.0f - dist_sq * light->inv_radius_sq;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 pxl8_light_color(const pxl8_shader_uniforms* u, u32 light_idx) {
|
||||||
|
if (!u || light_idx >= u->lights_count) return 0;
|
||||||
|
const pxl8_light* light = &u->lights[light_idx];
|
||||||
|
return (u32)light->r | ((u32)light->g << 8) | ((u32)light->b << 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void pxl8_set_light_tint(pxl8_shader_ctx* ctx, u32 color, f32 strength) {
|
||||||
|
if (strength <= 0.0f) return;
|
||||||
|
if (strength > 1.0f) strength = 1.0f;
|
||||||
|
u32 alpha = (u32)(strength * 255.0f);
|
||||||
|
ctx->out_light_color = (color & 0x00FFFFFF) | (alpha << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
15
src/gfx/pxl8_shader_registry.c
Normal file
15
src/gfx/pxl8_shader_registry.c
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
#include "pxl8_shader_registry.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
extern u8 pxl8_shader_lit(pxl8_shader_ctx* ctx, const pxl8_shader_bindings* bindings, const pxl8_shader_uniforms* uniforms);
|
||||||
|
extern u8 pxl8_shader_unlit(pxl8_shader_ctx* ctx, const pxl8_shader_bindings* bindings, const pxl8_shader_uniforms* uniforms);
|
||||||
|
|
||||||
|
void pxl8_shader_registry_init(void) {}
|
||||||
|
void pxl8_shader_registry_reload(void) {}
|
||||||
|
|
||||||
|
pxl8_shader_fn pxl8_shader_registry_get(const char* name) {
|
||||||
|
if (strcmp(name, "lit") == 0) return (pxl8_shader_fn)pxl8_shader_lit;
|
||||||
|
if (strcmp(name, "unlit") == 0) return (pxl8_shader_fn)pxl8_shader_unlit;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
7
src/gfx/pxl8_shader_registry.h
Normal file
7
src/gfx/pxl8_shader_registry.h
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "pxl8_shader.h"
|
||||||
|
|
||||||
|
void pxl8_shader_registry_init(void);
|
||||||
|
void pxl8_shader_registry_reload(void);
|
||||||
|
pxl8_shader_fn pxl8_shader_registry_get(const char* name);
|
||||||
66
src/gfx/pxl8_shader_runtime.c
Normal file
66
src/gfx/pxl8_shader_runtime.c
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
#include "pxl8_shader_runtime.h"
|
||||||
|
#include "pxl8_log.h"
|
||||||
|
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
struct pxl8_shader_lib {
|
||||||
|
void* handle;
|
||||||
|
char path[256];
|
||||||
|
};
|
||||||
|
|
||||||
|
pxl8_shader_lib* pxl8_shader_lib_load(const char* path) {
|
||||||
|
void* handle = dlopen(path, RTLD_NOW);
|
||||||
|
if (!handle) {
|
||||||
|
pxl8_error("Failed to load shader library: %s - %s", path, dlerror());
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pxl8_shader_lib* lib = malloc(sizeof(pxl8_shader_lib));
|
||||||
|
lib->handle = handle;
|
||||||
|
strncpy(lib->path, path, sizeof(lib->path) - 1);
|
||||||
|
lib->path[sizeof(lib->path) - 1] = '\0';
|
||||||
|
|
||||||
|
return lib;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_shader_lib_unload(pxl8_shader_lib* lib) {
|
||||||
|
if (!lib) return;
|
||||||
|
if (lib->handle) {
|
||||||
|
dlclose(lib->handle);
|
||||||
|
}
|
||||||
|
free(lib);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pxl8_shader_lib_reload(pxl8_shader_lib* lib) {
|
||||||
|
if (!lib) return false;
|
||||||
|
|
||||||
|
if (lib->handle) {
|
||||||
|
dlclose(lib->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
lib->handle = dlopen(lib->path, RTLD_NOW);
|
||||||
|
if (!lib->handle) {
|
||||||
|
pxl8_error("Failed to reload shader library: %s - %s", lib->path, dlerror());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pxl8_shader_fn pxl8_shader_lib_get(pxl8_shader_lib* lib, const char* name) {
|
||||||
|
if (!lib || !lib->handle) return NULL;
|
||||||
|
|
||||||
|
char symbol[128];
|
||||||
|
snprintf(symbol, sizeof(symbol), "pxl8_shader_%s", name);
|
||||||
|
|
||||||
|
void* fn = dlsym(lib->handle, symbol);
|
||||||
|
if (!fn) {
|
||||||
|
pxl8_error("Failed to find shader: %s - %s", symbol, dlerror());
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (pxl8_shader_fn)fn;
|
||||||
|
}
|
||||||
20
src/gfx/pxl8_shader_runtime.h
Normal file
20
src/gfx/pxl8_shader_runtime.h
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "pxl8_shader.h"
|
||||||
|
#include "pxl8_types.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct pxl8_shader_lib pxl8_shader_lib;
|
||||||
|
|
||||||
|
pxl8_shader_lib* pxl8_shader_lib_load(const char* path);
|
||||||
|
void pxl8_shader_lib_unload(pxl8_shader_lib* lib);
|
||||||
|
bool pxl8_shader_lib_reload(pxl8_shader_lib* lib);
|
||||||
|
|
||||||
|
pxl8_shader_fn pxl8_shader_lib_get(pxl8_shader_lib* lib, const char* name);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
106
src/gfx/shaders/cpu/lit.c
Normal file
106
src/gfx/shaders/cpu/lit.c
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
#include "pxl8_macros.h"
|
||||||
|
#include "pxl8_shader.h"
|
||||||
|
#include "pxl8_shader_builtins.h"
|
||||||
|
|
||||||
|
u8 pxl8_shader_lit(
|
||||||
|
pxl8_shader_ctx* ctx,
|
||||||
|
const pxl8_shader_bindings* bindings,
|
||||||
|
const pxl8_shader_uniforms* uniforms
|
||||||
|
) {
|
||||||
|
u8 tex_idx = 0;
|
||||||
|
if (bindings && bindings->atlas) {
|
||||||
|
tex_idx = pxl8_sample_indexed(bindings, ctx->v_uv);
|
||||||
|
if (pxl8_unlikely(tex_idx == 0)) return 0;
|
||||||
|
} else {
|
||||||
|
if (uniforms && uniforms->dither) {
|
||||||
|
tex_idx = pxl8_gfx_dither(ctx->v_color, (u32)ctx->x, (u32)ctx->y);
|
||||||
|
} else {
|
||||||
|
f32 clamped = pxl8_clamp(ctx->v_color, 0.0f, 255.0f);
|
||||||
|
tex_idx = (u8)(clamped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f32 light = ctx->v_light;
|
||||||
|
|
||||||
|
if (uniforms) {
|
||||||
|
f32 ambient = (f32)uniforms->ambient / 255.0f;
|
||||||
|
if (ambient > light) light = ambient;
|
||||||
|
|
||||||
|
if (uniforms->celestial_intensity > 0.0f) {
|
||||||
|
f32 ndotl = -(ctx->v_normal.x * uniforms->celestial_dir.x +
|
||||||
|
ctx->v_normal.y * uniforms->celestial_dir.y +
|
||||||
|
ctx->v_normal.z * uniforms->celestial_dir.z);
|
||||||
|
if (ndotl > 0.0f) {
|
||||||
|
light += ndotl * uniforms->celestial_intensity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f32 dyn_strength = 0.0f;
|
||||||
|
f32 dyn_r = 0.0f;
|
||||||
|
f32 dyn_g = 0.0f;
|
||||||
|
f32 dyn_b = 0.0f;
|
||||||
|
|
||||||
|
for (u32 i = 0; i < uniforms->lights_count; i++) {
|
||||||
|
const pxl8_light* l = &uniforms->lights[i];
|
||||||
|
f32 lx = l->position.x - ctx->v_world.x;
|
||||||
|
f32 ly = l->position.y - ctx->v_world.y;
|
||||||
|
f32 lz = l->position.z - ctx->v_world.z;
|
||||||
|
f32 dist_sq = lx * lx + ly * ly + lz * lz;
|
||||||
|
if (dist_sq >= l->radius_sq) continue;
|
||||||
|
|
||||||
|
f32 inv_dist = pxl8_fast_inv_sqrt(dist_sq);
|
||||||
|
f32 nx = lx * inv_dist;
|
||||||
|
f32 ny = ly * inv_dist;
|
||||||
|
f32 nz = lz * inv_dist;
|
||||||
|
|
||||||
|
f32 ndotl = ctx->v_normal.x * nx + ctx->v_normal.y * ny + ctx->v_normal.z * nz;
|
||||||
|
if (ndotl <= 0.0f) continue;
|
||||||
|
|
||||||
|
f32 falloff = 1.0f - dist_sq * l->inv_radius_sq;
|
||||||
|
if (falloff <= 0.0f) continue;
|
||||||
|
if (uniforms->dither && falloff < 0.33f) {
|
||||||
|
f32 threshold = (PXL8_BAYER_4X4[((u32)ctx->y & 3) * 4 + ((u32)ctx->x & 3)] + 0.5f) * (1.0f / 16.0f);
|
||||||
|
if (falloff < threshold * 0.33f) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
f32 strength = ((f32)l->intensity / 255.0f) * falloff * ndotl;
|
||||||
|
if (strength <= 0.0f) continue;
|
||||||
|
|
||||||
|
dyn_strength += strength;
|
||||||
|
dyn_r += strength * (f32)l->r;
|
||||||
|
dyn_g += strength * (f32)l->g;
|
||||||
|
dyn_b += strength * (f32)l->b;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dyn_strength > 0.0f) {
|
||||||
|
f32 inv = pxl8_fast_rcp(dyn_strength);
|
||||||
|
u8 r = (u8)pxl8_clamp(dyn_r * inv, 0.0f, 255.0f);
|
||||||
|
u8 g = (u8)pxl8_clamp(dyn_g * inv, 0.0f, 255.0f);
|
||||||
|
u8 b = (u8)pxl8_clamp(dyn_b * inv, 0.0f, 255.0f);
|
||||||
|
u8 a = (u8)pxl8_clamp(dyn_strength * 255.0f, 0.0f, 255.0f);
|
||||||
|
ctx->out_light_color = (u32)r | ((u32)g << 8) | ((u32)b << 16) | ((u32)a << 24);
|
||||||
|
light += dyn_strength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (light > 1.0f) light = 1.0f;
|
||||||
|
if (light < 0.0f) light = 0.0f;
|
||||||
|
|
||||||
|
f32 light_f = light * 255.0f;
|
||||||
|
u8 light_u8 = (u8)light_f;
|
||||||
|
if (uniforms && uniforms->dither) {
|
||||||
|
light_u8 = pxl8_gfx_dither(light_f, (u32)ctx->x, (u32)ctx->y);
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 shaded = pxl8_colormap_lookup(bindings, tex_idx, light_u8);
|
||||||
|
|
||||||
|
if (uniforms && uniforms->emissive) {
|
||||||
|
u32 rgb = 0x00FFFFFF;
|
||||||
|
if (bindings && bindings->palette) {
|
||||||
|
rgb = bindings->palette[tex_idx] & 0x00FFFFFF;
|
||||||
|
}
|
||||||
|
pxl8_set_light_tint(ctx, rgb, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
return shaded;
|
||||||
|
}
|
||||||
31
src/gfx/shaders/cpu/unlit.c
Normal file
31
src/gfx/shaders/cpu/unlit.c
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
#include "pxl8_shader.h"
|
||||||
|
#include "pxl8_shader_builtins.h"
|
||||||
|
|
||||||
|
u8 pxl8_shader_unlit(
|
||||||
|
pxl8_shader_ctx* ctx,
|
||||||
|
const pxl8_shader_bindings* bindings,
|
||||||
|
const pxl8_shader_uniforms* uniforms
|
||||||
|
) {
|
||||||
|
u8 tex_idx = 0;
|
||||||
|
if (bindings && bindings->atlas) {
|
||||||
|
tex_idx = pxl8_sample_indexed(bindings, ctx->v_uv);
|
||||||
|
if (tex_idx == 0) return 0;
|
||||||
|
} else {
|
||||||
|
if (uniforms && uniforms->dither) {
|
||||||
|
tex_idx = pxl8_gfx_dither(ctx->v_color, (u32)ctx->x, (u32)ctx->y);
|
||||||
|
} else {
|
||||||
|
f32 clamped = pxl8_clamp(ctx->v_color, 0.0f, 255.0f);
|
||||||
|
tex_idx = (u8)(clamped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uniforms && uniforms->emissive) {
|
||||||
|
u32 rgb = 0x00FFFFFF;
|
||||||
|
if (bindings && bindings->palette) {
|
||||||
|
rgb = bindings->palette[tex_idx] & 0x00FFFFFF;
|
||||||
|
}
|
||||||
|
pxl8_set_light_tint(ctx, rgb, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tex_idx;
|
||||||
|
}
|
||||||
|
|
@ -20,19 +20,19 @@ void pxl8_gui_state_destroy(pxl8_gui_state* state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_gui_begin_frame(pxl8_gui_state* state, pxl8_gfx* gfx) {
|
void pxl8_gui_begin_frame(pxl8_gui_state* state, pxl8_gfx* gfx) {
|
||||||
|
(void)gfx;
|
||||||
if (!state) return;
|
if (!state) return;
|
||||||
state->hot_id = 0;
|
state->hot_id = 0;
|
||||||
if (gfx) pxl8_gfx_push_target(gfx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_gui_end_frame(pxl8_gui_state* state, pxl8_gfx* gfx) {
|
void pxl8_gui_end_frame(pxl8_gui_state* state, pxl8_gfx* gfx) {
|
||||||
|
(void)gfx;
|
||||||
if (!state) return;
|
if (!state) return;
|
||||||
|
|
||||||
if (!state->cursor_down) {
|
if (!state->cursor_down) {
|
||||||
state->active_id = 0;
|
state->active_id = 0;
|
||||||
}
|
}
|
||||||
state->cursor_clicked = false;
|
state->cursor_clicked = false;
|
||||||
if (gfx) pxl8_gfx_pop_target(gfx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_gui_cursor_move(pxl8_gui_state* state, i32 x, i32 y) {
|
void pxl8_gui_cursor_move(pxl8_gui_state* state, i32 x, i32 y) {
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,7 @@ local anim = require("pxl8.anim")
|
||||||
local bytes = require("pxl8.bytes")
|
local bytes = require("pxl8.bytes")
|
||||||
local core = require("pxl8.core")
|
local core = require("pxl8.core")
|
||||||
local effects = require("pxl8.effects")
|
local effects = require("pxl8.effects")
|
||||||
local gfx2d = require("pxl8.gfx2d")
|
local gfx = require("pxl8.gfx")
|
||||||
local gfx3d = require("pxl8.gfx3d")
|
|
||||||
local gui = require("pxl8.gui")
|
local gui = require("pxl8.gui")
|
||||||
local input = require("pxl8.input")
|
local input = require("pxl8.input")
|
||||||
local math = require("pxl8.math")
|
local math = require("pxl8.math")
|
||||||
|
|
@ -44,25 +43,27 @@ pxl8.set_palette = core.set_palette
|
||||||
pxl8.set_palette_rgb = core.set_palette_rgb
|
pxl8.set_palette_rgb = core.set_palette_rgb
|
||||||
pxl8.update_palette_deps = core.update_palette_deps
|
pxl8.update_palette_deps = core.update_palette_deps
|
||||||
|
|
||||||
pxl8.clear = gfx2d.clear
|
pxl8.clear = gfx.clear
|
||||||
pxl8.pixel = gfx2d.pixel
|
pxl8.pixel = gfx.pixel
|
||||||
pxl8.line = gfx2d.line
|
pxl8.line = gfx.line
|
||||||
pxl8.rect = gfx2d.rect
|
pxl8.rect = gfx.rect
|
||||||
pxl8.rect_fill = gfx2d.rect_fill
|
pxl8.rect_fill = gfx.rect_fill
|
||||||
pxl8.circle = gfx2d.circle
|
pxl8.circle = gfx.circle
|
||||||
pxl8.circle_fill = gfx2d.circle_fill
|
pxl8.circle_fill = gfx.circle_fill
|
||||||
pxl8.text = gfx2d.text
|
pxl8.text = gfx.text
|
||||||
pxl8.sprite = gfx2d.sprite
|
pxl8.sprite = gfx.sprite
|
||||||
pxl8.load_palette = gfx2d.load_palette
|
pxl8.load_palette = gfx.load_palette
|
||||||
pxl8.load_sprite = gfx2d.load_sprite
|
pxl8.load_sprite = gfx.load_sprite
|
||||||
pxl8.create_texture = gfx2d.create_texture
|
pxl8.create_texture = gfx.create_texture
|
||||||
pxl8.gfx_color_ramp = gfx2d.color_ramp
|
pxl8.gfx_color_ramp = gfx.color_ramp
|
||||||
pxl8.gfx_fade_palette = gfx2d.fade_palette
|
pxl8.gfx_fade_palette = gfx.fade_palette
|
||||||
pxl8.gfx_cycle_palette = gfx2d.cycle_palette
|
pxl8.gfx_cycle_palette = gfx.cycle_palette
|
||||||
pxl8.add_palette_cycle = gfx2d.add_palette_cycle
|
pxl8.add_palette_cycle = gfx.add_palette_cycle
|
||||||
pxl8.remove_palette_cycle = gfx2d.remove_palette_cycle
|
pxl8.remove_palette_cycle = gfx.remove_palette_cycle
|
||||||
pxl8.set_palette_cycle_speed = gfx2d.set_palette_cycle_speed
|
pxl8.set_palette_cycle_speed = gfx.set_palette_cycle_speed
|
||||||
pxl8.clear_palette_cycles = gfx2d.clear_palette_cycles
|
pxl8.clear_palette_cycles = gfx.clear_palette_cycles
|
||||||
|
pxl8.push_target = gfx.push_target
|
||||||
|
pxl8.pop_target = gfx.pop_target
|
||||||
|
|
||||||
pxl8.key_down = input.key_down
|
pxl8.key_down = input.key_down
|
||||||
pxl8.key_pressed = input.key_pressed
|
pxl8.key_pressed = input.key_pressed
|
||||||
|
|
@ -86,26 +87,23 @@ pxl8.create_anim_from_ase = anim.Anim.from_ase
|
||||||
|
|
||||||
pxl8.bounds = math.bounds
|
pxl8.bounds = math.bounds
|
||||||
|
|
||||||
pxl8.Camera3D = gfx3d.Camera3D
|
pxl8.Camera3D = gfx.Camera3D
|
||||||
pxl8.Material = gfx3d.Material
|
pxl8.Material = gfx.Material
|
||||||
pxl8.Mesh = gfx3d.Mesh
|
pxl8.Mesh = gfx.Mesh
|
||||||
pxl8.begin_frame_3d = gfx3d.begin_frame
|
pxl8.begin_frame_3d = gfx.begin_frame_3d
|
||||||
pxl8.clear_3d = gfx3d.clear
|
pxl8.clear_3d = gfx.clear_3d
|
||||||
pxl8.clear_depth = gfx3d.clear_depth
|
pxl8.clear_depth = gfx.clear_depth
|
||||||
pxl8.create_camera_3d = gfx3d.Camera3D.new
|
pxl8.create_camera_3d = gfx.Camera3D.new
|
||||||
pxl8.create_material = gfx3d.create_material
|
pxl8.create_material = gfx.create_material
|
||||||
pxl8.create_mesh = gfx3d.Mesh.new
|
pxl8.create_mesh = gfx.Mesh.new
|
||||||
pxl8.create_vec3_array = gfx3d.create_vec3_array
|
pxl8.create_vec3_array = gfx.create_vec3_array
|
||||||
pxl8.draw_line_3d = gfx3d.draw_line
|
pxl8.draw_line_3d = gfx.draw_line_3d
|
||||||
pxl8.draw_mesh = gfx3d.draw_mesh
|
pxl8.draw_mesh = gfx.draw_mesh
|
||||||
pxl8.end_frame_3d = gfx3d.end_frame
|
pxl8.end_frame_3d = gfx.end_frame_3d
|
||||||
pxl8.project_points = gfx3d.project_points
|
pxl8.project_points = gfx.project_points
|
||||||
|
pxl8.set_wireframe = gfx.set_wireframe
|
||||||
|
pxl8.get_wireframe = gfx.get_wireframe
|
||||||
|
|
||||||
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.Lights = effects.Lights
|
||||||
pxl8.create_lights = effects.Lights.new
|
pxl8.create_lights = effects.Lights.new
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,49 +1,8 @@
|
||||||
local ffi = require("ffi")
|
local ffi = require("ffi")
|
||||||
local C = ffi.C
|
local C = ffi.C
|
||||||
local core = require("pxl8.core")
|
|
||||||
|
|
||||||
local effects = {}
|
local effects = {}
|
||||||
|
|
||||||
effects.GLOW_CIRCLE = 0
|
|
||||||
effects.GLOW_DIAMOND = 1
|
|
||||||
effects.GLOW_SHAFT = 2
|
|
||||||
|
|
||||||
local Glows = {}
|
|
||||||
Glows.__index = Glows
|
|
||||||
|
|
||||||
function Glows.new(capacity)
|
|
||||||
local ptr = C.pxl8_glows_create(capacity or 1000)
|
|
||||||
if ptr == nil then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
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 = {}
|
local Lights = {}
|
||||||
Lights.__index = Lights
|
Lights.__index = Lights
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,108 @@ local ffi = require("ffi")
|
||||||
local C = ffi.C
|
local C = ffi.C
|
||||||
local core = require("pxl8.core")
|
local core = require("pxl8.core")
|
||||||
|
|
||||||
local gfx3d = {}
|
local gfx = {}
|
||||||
|
|
||||||
|
function gfx.clear(color)
|
||||||
|
C.pxl8_2d_clear(core.gfx, color or 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
function gfx.pixel(x, y, color)
|
||||||
|
if color then
|
||||||
|
C.pxl8_2d_pixel(core.gfx, x, y, color)
|
||||||
|
else
|
||||||
|
return C.pxl8_2d_get_pixel(core.gfx, x, y)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function gfx.line(x0, y0, x1, y1, color)
|
||||||
|
C.pxl8_2d_line(core.gfx, x0, y0, x1, y1, color)
|
||||||
|
end
|
||||||
|
|
||||||
|
function gfx.rect(x, y, w, h, color)
|
||||||
|
C.pxl8_2d_rect(core.gfx, x, y, w, h, color)
|
||||||
|
end
|
||||||
|
|
||||||
|
function gfx.rect_fill(x, y, w, h, color)
|
||||||
|
C.pxl8_2d_rect_fill(core.gfx, x, y, w, h, color)
|
||||||
|
end
|
||||||
|
|
||||||
|
function gfx.circle(x, y, r, color)
|
||||||
|
C.pxl8_2d_circle(core.gfx, x, y, r, color)
|
||||||
|
end
|
||||||
|
|
||||||
|
function gfx.circle_fill(x, y, r, color)
|
||||||
|
C.pxl8_2d_circle_fill(core.gfx, x, y, r, color)
|
||||||
|
end
|
||||||
|
|
||||||
|
function gfx.text(str, x, y, color)
|
||||||
|
C.pxl8_2d_text(core.gfx, str, x or 0, y or 0, color or 15)
|
||||||
|
end
|
||||||
|
|
||||||
|
function gfx.sprite(id, x, y, w, h, flip_x, flip_y)
|
||||||
|
C.pxl8_2d_sprite(core.gfx, id or 0, x or 0, y or 0, w or 16, h or 16, flip_x or false, flip_y or false)
|
||||||
|
end
|
||||||
|
|
||||||
|
function gfx.load_palette(filepath)
|
||||||
|
return C.pxl8_gfx_load_palette(core.gfx, filepath)
|
||||||
|
end
|
||||||
|
|
||||||
|
function gfx.load_sprite(filepath)
|
||||||
|
local result = C.pxl8_gfx_load_sprite(core.gfx, filepath)
|
||||||
|
if result >= 0 and result < 100 then
|
||||||
|
return result
|
||||||
|
else
|
||||||
|
return nil, result
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function gfx.create_texture(pixels, width, height)
|
||||||
|
local pixel_data = ffi.new("u8[?]", width * height)
|
||||||
|
for i = 0, width * height - 1 do
|
||||||
|
pixel_data[i] = pixels[i + 1] or 0
|
||||||
|
end
|
||||||
|
local result = C.pxl8_gfx_create_texture(core.gfx, pixel_data, width, height)
|
||||||
|
if result < 0 then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
function gfx.color_ramp(start, count, from_color, to_color)
|
||||||
|
C.pxl8_gfx_color_ramp(core.gfx, start, count, from_color, to_color)
|
||||||
|
end
|
||||||
|
|
||||||
|
function gfx.fade_palette(start, count, amount, target_color)
|
||||||
|
C.pxl8_gfx_fade_palette(core.gfx, start, count, amount, target_color)
|
||||||
|
end
|
||||||
|
|
||||||
|
function gfx.cycle_palette(start, count, step)
|
||||||
|
C.pxl8_gfx_cycle_palette(core.gfx, start, count, step or 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
function gfx.add_palette_cycle(start_index, end_index, speed)
|
||||||
|
return C.pxl8_gfx_add_palette_cycle(core.gfx, start_index, end_index, speed or 1.0)
|
||||||
|
end
|
||||||
|
|
||||||
|
function gfx.remove_palette_cycle(cycle_id)
|
||||||
|
C.pxl8_gfx_remove_palette_cycle(core.gfx, cycle_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
function gfx.set_palette_cycle_speed(cycle_id, speed)
|
||||||
|
C.pxl8_gfx_set_palette_cycle_speed(core.gfx, cycle_id, speed)
|
||||||
|
end
|
||||||
|
|
||||||
|
function gfx.clear_palette_cycles()
|
||||||
|
C.pxl8_gfx_clear_palette_cycles(core.gfx)
|
||||||
|
end
|
||||||
|
|
||||||
|
function gfx.push_target()
|
||||||
|
return C.pxl8_gfx_push_target(core.gfx)
|
||||||
|
end
|
||||||
|
|
||||||
|
function gfx.pop_target()
|
||||||
|
C.pxl8_gfx_pop_target(core.gfx)
|
||||||
|
end
|
||||||
|
|
||||||
local Camera3D = {}
|
local Camera3D = {}
|
||||||
Camera3D.__index = Camera3D
|
Camera3D.__index = Camera3D
|
||||||
|
|
@ -84,8 +185,6 @@ function Camera3D:world_to_screen(x, y, z, width, height)
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
gfx3d.Camera3D = Camera3D
|
|
||||||
|
|
||||||
local Mesh = {}
|
local Mesh = {}
|
||||||
Mesh.__index = Mesh
|
Mesh.__index = Mesh
|
||||||
|
|
||||||
|
|
@ -127,35 +226,43 @@ function Mesh:clear()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
gfx3d.Mesh = Mesh
|
gfx.Camera3D = Camera3D
|
||||||
|
|
||||||
function gfx3d.draw_mesh(mesh, opts)
|
gfx.Mesh = Mesh
|
||||||
|
|
||||||
|
function gfx.draw_mesh(mesh, opts)
|
||||||
|
if not mesh or not mesh._ptr then
|
||||||
|
return
|
||||||
|
end
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
local model = ffi.new("pxl8_mat4")
|
local model
|
||||||
local s = opts.scale or 1
|
if opts.transform then
|
||||||
model.m[0] = s
|
model = opts.transform
|
||||||
model.m[5] = s
|
else
|
||||||
model.m[10] = s
|
model = ffi.new("pxl8_mat4")
|
||||||
model.m[15] = 1
|
local s = opts.scale or 1
|
||||||
if opts.x then model.m[12] = opts.x end
|
model.m[0] = s
|
||||||
if opts.y then model.m[13] = opts.y end
|
model.m[5] = s
|
||||||
if opts.z then model.m[14] = opts.z end
|
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
|
||||||
|
end
|
||||||
local material = ffi.new("pxl8_gfx_material", {
|
local material = ffi.new("pxl8_gfx_material", {
|
||||||
texture_id = opts.texture or 0,
|
|
||||||
alpha = opts.alpha or 255,
|
alpha = opts.alpha or 255,
|
||||||
blend_mode = opts.blend_mode or 0,
|
blend_mode = opts.blend_mode or 0,
|
||||||
dither = opts.dither ~= false,
|
dither = opts.dither ~= false,
|
||||||
double_sided = opts.double_sided or false,
|
double_sided = opts.double_sided or false,
|
||||||
dynamic_lighting = opts.lighting or false,
|
dynamic_lighting = opts.lighting or false,
|
||||||
|
emissive = opts.emissive or false,
|
||||||
per_pixel = opts.per_pixel or false,
|
per_pixel = opts.per_pixel or false,
|
||||||
vertex_color_passthrough = opts.passthrough or false,
|
texture_id = opts.texture or 0xFFFFFFFF,
|
||||||
wireframe = opts.wireframe or false,
|
|
||||||
emissive_intensity = opts.emissive or 0.0,
|
|
||||||
})
|
})
|
||||||
C.pxl8_3d_draw_mesh(core.gfx, mesh._ptr, model, material)
|
C.pxl8_3d_draw_mesh(core.gfx, mesh._ptr, model, material)
|
||||||
end
|
end
|
||||||
|
|
||||||
function gfx3d.begin_frame(camera, lights, uniforms)
|
function gfx.begin_frame_3d(camera, lights, uniforms)
|
||||||
uniforms = uniforms or {}
|
uniforms = uniforms or {}
|
||||||
local u = ffi.new("pxl8_3d_uniforms")
|
local u = ffi.new("pxl8_3d_uniforms")
|
||||||
|
|
||||||
|
|
@ -164,68 +271,82 @@ function gfx3d.begin_frame(camera, lights, uniforms)
|
||||||
u.fog_density = uniforms.fog_density or 0.0
|
u.fog_density = uniforms.fog_density or 0.0
|
||||||
u.time = uniforms.time or 0.0
|
u.time = uniforms.time or 0.0
|
||||||
|
|
||||||
|
local cx, cy, cz
|
||||||
if uniforms.celestial_dir then
|
if uniforms.celestial_dir then
|
||||||
u.celestial_dir.x = uniforms.celestial_dir[1] or 0
|
cx = uniforms.celestial_dir[1] or 0
|
||||||
u.celestial_dir.y = uniforms.celestial_dir[2] or -1
|
cy = uniforms.celestial_dir[2] or -1
|
||||||
u.celestial_dir.z = uniforms.celestial_dir[3] or 0
|
cz = uniforms.celestial_dir[3] or 0
|
||||||
else
|
else
|
||||||
u.celestial_dir.x = 0
|
cx, cy, cz = 0, -1, 0
|
||||||
u.celestial_dir.y = -1
|
|
||||||
u.celestial_dir.z = 0
|
|
||||||
end
|
end
|
||||||
|
local len = math.sqrt(cx * cx + cy * cy + cz * cz)
|
||||||
|
if len > 0.0001 then
|
||||||
|
local inv = 1.0 / len
|
||||||
|
cx, cy, cz = cx * inv, cy * inv, cz * inv
|
||||||
|
end
|
||||||
|
u.celestial_dir.x = cx
|
||||||
|
u.celestial_dir.y = cy
|
||||||
|
u.celestial_dir.z = cz
|
||||||
u.celestial_intensity = uniforms.celestial_intensity or 0.0
|
u.celestial_intensity = uniforms.celestial_intensity or 0.0
|
||||||
|
|
||||||
local lights_ptr = lights and lights._ptr or nil
|
local lights_ptr = lights and lights._ptr or nil
|
||||||
C.pxl8_3d_begin_frame(core.gfx, camera._ptr, lights_ptr, u)
|
C.pxl8_3d_begin_frame(core.gfx, camera._ptr, lights_ptr, u)
|
||||||
end
|
end
|
||||||
|
|
||||||
function gfx3d.clear(color)
|
function gfx.clear_3d(color)
|
||||||
C.pxl8_3d_clear(core.gfx, color or 0)
|
C.pxl8_3d_clear(core.gfx, color or 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
function gfx3d.clear_depth()
|
function gfx.clear_depth()
|
||||||
C.pxl8_3d_clear_depth(core.gfx)
|
C.pxl8_3d_clear_depth(core.gfx)
|
||||||
end
|
end
|
||||||
|
|
||||||
function gfx3d.draw_line(p0, p1, color)
|
function gfx.draw_line_3d(p0, p1, color)
|
||||||
local vec0 = ffi.new("pxl8_vec3", {x = p0[1], y = p0[2], z = p0[3]})
|
local vec0 = ffi.new("pxl8_vec3", {x = p0[1], y = p0[2], z = p0[3]})
|
||||||
local vec1 = ffi.new("pxl8_vec3", {x = p1[1], y = p1[2], z = p1[3]})
|
local vec1 = ffi.new("pxl8_vec3", {x = p1[1], y = p1[2], z = p1[3]})
|
||||||
C.pxl8_3d_draw_line(core.gfx, vec0, vec1, color)
|
C.pxl8_3d_draw_line(core.gfx, vec0, vec1, color)
|
||||||
end
|
end
|
||||||
|
|
||||||
function gfx3d.end_frame()
|
function gfx.end_frame_3d()
|
||||||
C.pxl8_3d_end_frame(core.gfx)
|
C.pxl8_3d_end_frame(core.gfx)
|
||||||
end
|
end
|
||||||
|
|
||||||
function gfx3d.project_points(input, output, count, transform)
|
function gfx.project_points(input, output, count, transform)
|
||||||
C.pxl8_3d_project_points(core.gfx, input, output, count, transform)
|
return C.pxl8_3d_project_points(core.gfx, input, output, count, transform)
|
||||||
end
|
end
|
||||||
|
|
||||||
function gfx3d.create_vec3_array(count)
|
function gfx.create_vec3_array(count)
|
||||||
return ffi.new("pxl8_vec3[?]", count)
|
return ffi.new("pxl8_vec3[?]", count)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function gfx.set_wireframe(enabled)
|
||||||
|
C.pxl8_gfx_set_wireframe(core.gfx, enabled)
|
||||||
|
end
|
||||||
|
|
||||||
|
function gfx.get_wireframe()
|
||||||
|
return C.pxl8_gfx_get_wireframe(core.gfx)
|
||||||
|
end
|
||||||
|
|
||||||
local Material = {}
|
local Material = {}
|
||||||
Material.__index = Material
|
Material.__index = Material
|
||||||
|
|
||||||
function Material.new(opts)
|
function Material.new(opts)
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
local mat = ffi.new("pxl8_gfx_material", {
|
local mat = ffi.new("pxl8_gfx_material", {
|
||||||
texture_id = opts.texture or 0,
|
|
||||||
alpha = opts.alpha or 255,
|
alpha = opts.alpha or 255,
|
||||||
blend_mode = opts.blend_mode or 0,
|
blend_mode = opts.blend_mode or 0,
|
||||||
dither = opts.dither ~= false,
|
dither = opts.dither ~= false,
|
||||||
double_sided = opts.double_sided or false,
|
double_sided = opts.double_sided or false,
|
||||||
dynamic_lighting = opts.lighting or false,
|
dynamic_lighting = opts.lighting or false,
|
||||||
|
emissive = opts.emissive or false,
|
||||||
per_pixel = opts.per_pixel or false,
|
per_pixel = opts.per_pixel or false,
|
||||||
vertex_color_passthrough = opts.passthrough or false,
|
texture_id = opts.texture or 0xFFFFFFFF,
|
||||||
wireframe = opts.wireframe or false,
|
|
||||||
emissive_intensity = opts.emissive or 0.0,
|
|
||||||
})
|
})
|
||||||
return setmetatable({ _ptr = mat }, Material)
|
return setmetatable({ _ptr = mat }, Material)
|
||||||
end
|
end
|
||||||
|
|
||||||
gfx3d.Material = Material
|
gfx.Material = Material
|
||||||
gfx3d.create_material = Material.new
|
|
||||||
|
|
||||||
return gfx3d
|
gfx.create_material = Material.new
|
||||||
|
|
||||||
|
return gfx
|
||||||
|
|
@ -1,108 +0,0 @@
|
||||||
local ffi = require("ffi")
|
|
||||||
local C = ffi.C
|
|
||||||
local core = require("pxl8.core")
|
|
||||||
|
|
||||||
local graphics = {}
|
|
||||||
|
|
||||||
function graphics.clear(color)
|
|
||||||
C.pxl8_2d_clear(core.gfx, color or 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
function graphics.pixel(x, y, color)
|
|
||||||
if color then
|
|
||||||
C.pxl8_2d_pixel(core.gfx, x, y, color)
|
|
||||||
else
|
|
||||||
return C.pxl8_2d_get_pixel(core.gfx, x, y)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function graphics.line(x0, y0, x1, y1, color)
|
|
||||||
C.pxl8_2d_line(core.gfx, x0, y0, x1, y1, color)
|
|
||||||
end
|
|
||||||
|
|
||||||
function graphics.rect(x, y, w, h, color)
|
|
||||||
C.pxl8_2d_rect(core.gfx, x, y, w, h, color)
|
|
||||||
end
|
|
||||||
|
|
||||||
function graphics.rect_fill(x, y, w, h, color)
|
|
||||||
C.pxl8_2d_rect_fill(core.gfx, x, y, w, h, color)
|
|
||||||
end
|
|
||||||
|
|
||||||
function graphics.circle(x, y, r, color)
|
|
||||||
C.pxl8_2d_circle(core.gfx, x, y, r, color)
|
|
||||||
end
|
|
||||||
|
|
||||||
function graphics.circle_fill(x, y, r, color)
|
|
||||||
C.pxl8_2d_circle_fill(core.gfx, x, y, r, color)
|
|
||||||
end
|
|
||||||
|
|
||||||
function graphics.text(str, x, y, color)
|
|
||||||
C.pxl8_2d_text(core.gfx, str, x or 0, y or 0, color or 15)
|
|
||||||
end
|
|
||||||
|
|
||||||
function graphics.sprite(id, x, y, w, h, flip_x, flip_y)
|
|
||||||
C.pxl8_2d_sprite(core.gfx, id or 0, x or 0, y or 0, w or 16, h or 16, flip_x or false, flip_y or false)
|
|
||||||
end
|
|
||||||
|
|
||||||
function graphics.load_palette(filepath)
|
|
||||||
return C.pxl8_gfx_load_palette(core.gfx, filepath)
|
|
||||||
end
|
|
||||||
|
|
||||||
function graphics.load_sprite(filepath)
|
|
||||||
local result = C.pxl8_gfx_load_sprite(core.gfx, filepath)
|
|
||||||
if result >= 0 and result < 100 then
|
|
||||||
return result
|
|
||||||
else
|
|
||||||
return nil, result
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function graphics.create_texture(pixels, width, height)
|
|
||||||
local pixel_data = ffi.new("u8[?]", width * height)
|
|
||||||
for i = 0, width * height - 1 do
|
|
||||||
pixel_data[i] = pixels[i + 1] or 0
|
|
||||||
end
|
|
||||||
local result = C.pxl8_gfx_create_texture(core.gfx, pixel_data, width, height)
|
|
||||||
if result < 0 then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
|
|
||||||
function graphics.color_ramp(start, count, from_color, to_color)
|
|
||||||
C.pxl8_gfx_color_ramp(core.gfx, start, count, from_color, to_color)
|
|
||||||
end
|
|
||||||
|
|
||||||
function graphics.fade_palette(start, count, amount, target_color)
|
|
||||||
C.pxl8_gfx_fade_palette(core.gfx, start, count, amount, target_color)
|
|
||||||
end
|
|
||||||
|
|
||||||
function graphics.cycle_palette(start, count, step)
|
|
||||||
C.pxl8_gfx_cycle_palette(core.gfx, start, count, step or 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
function graphics.add_palette_cycle(start_index, end_index, speed)
|
|
||||||
return C.pxl8_gfx_add_palette_cycle(core.gfx, start_index, end_index, speed or 1.0)
|
|
||||||
end
|
|
||||||
|
|
||||||
function graphics.remove_palette_cycle(cycle_id)
|
|
||||||
C.pxl8_gfx_remove_palette_cycle(core.gfx, cycle_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
function graphics.set_palette_cycle_speed(cycle_id, speed)
|
|
||||||
C.pxl8_gfx_set_palette_cycle_speed(core.gfx, cycle_id, speed)
|
|
||||||
end
|
|
||||||
|
|
||||||
function graphics.clear_palette_cycles()
|
|
||||||
C.pxl8_gfx_clear_palette_cycles(core.gfx)
|
|
||||||
end
|
|
||||||
|
|
||||||
function graphics.push_target()
|
|
||||||
return C.pxl8_gfx_push_target(core.gfx)
|
|
||||||
end
|
|
||||||
|
|
||||||
function graphics.pop_target()
|
|
||||||
C.pxl8_gfx_pop_target(core.gfx)
|
|
||||||
end
|
|
||||||
|
|
||||||
return graphics
|
|
||||||
|
|
@ -1,281 +0,0 @@
|
||||||
local ffi = require("ffi")
|
|
||||||
local bit = require("bit")
|
|
||||||
local core = require("pxl8.core")
|
|
||||||
|
|
||||||
ffi.cdef[[
|
|
||||||
u8* pxl8_gfx_get_framebuffer_indexed(pxl8_gfx* gfx);
|
|
||||||
u32* pxl8_gfx_get_light_accum(pxl8_gfx* gfx);
|
|
||||||
const u32* pxl8_gfx_palette_colors(pxl8_gfx* gfx);
|
|
||||||
u16* pxl8_gfx_get_zbuffer(pxl8_gfx* gfx);
|
|
||||||
]]
|
|
||||||
|
|
||||||
local C = ffi.C
|
|
||||||
local band, bor, rshift, lshift = bit.band, bit.bor, bit.rshift, bit.lshift
|
|
||||||
local floor, sqrt, max, min, abs = math.floor, math.sqrt, math.max, math.min, math.abs
|
|
||||||
local sin, cos, fmod = math.sin, math.cos, math.fmod
|
|
||||||
|
|
||||||
local shader = {}
|
|
||||||
|
|
||||||
local fb = ffi.new("u8*")
|
|
||||||
local light = ffi.new("u32*")
|
|
||||||
local pal = ffi.new("const u32*")
|
|
||||||
local zbuf = ffi.new("u16*")
|
|
||||||
local w, h, count = 0, 0, 0
|
|
||||||
|
|
||||||
function shader.begin_frame()
|
|
||||||
fb = C.pxl8_gfx_get_framebuffer_indexed(core.gfx)
|
|
||||||
light = C.pxl8_gfx_get_light_accum(core.gfx)
|
|
||||||
pal = C.pxl8_gfx_palette_colors(core.gfx)
|
|
||||||
zbuf = C.pxl8_gfx_get_zbuffer(core.gfx)
|
|
||||||
w = C.pxl8_gfx_get_width(core.gfx)
|
|
||||||
h = C.pxl8_gfx_get_height(core.gfx)
|
|
||||||
count = w * h
|
|
||||||
end
|
|
||||||
|
|
||||||
function shader.get_buffers()
|
|
||||||
return fb, light, pal, zbuf, w, h
|
|
||||||
end
|
|
||||||
|
|
||||||
local function clamp(x, lo, hi)
|
|
||||||
if x < lo then return lo end
|
|
||||||
if x > hi then return hi end
|
|
||||||
return x
|
|
||||||
end
|
|
||||||
|
|
||||||
shader.resolve_tint = function()
|
|
||||||
if fb == nil or light == nil or pal == nil then return end
|
|
||||||
local fb_l, light_l, pal_l = fb, light, pal
|
|
||||||
local count_l = count
|
|
||||||
local band_l, rshift_l, bor_l, lshift_l = band, rshift, bor, lshift
|
|
||||||
local floor_l = floor
|
|
||||||
|
|
||||||
for i = 0, count_l - 1 do
|
|
||||||
local lv = light_l[i]
|
|
||||||
if lv ~= 0 then
|
|
||||||
local a = rshift_l(lv, 24)
|
|
||||||
if a > 0 then
|
|
||||||
local base = pal_l[fb_l[i]]
|
|
||||||
local br = band_l(base, 0xFF)
|
|
||||||
local bg = band_l(rshift_l(base, 8), 0xFF)
|
|
||||||
local bb = band_l(rshift_l(base, 16), 0xFF)
|
|
||||||
|
|
||||||
local lr = band_l(lv, 0xFF)
|
|
||||||
local lg = band_l(rshift_l(lv, 8), 0xFF)
|
|
||||||
local lb = band_l(rshift_l(lv, 16), 0xFF)
|
|
||||||
|
|
||||||
local t = a * 0.00392156862
|
|
||||||
local r = floor_l(br + (lr - 128) * t * 2)
|
|
||||||
local g = floor_l(bg + (lg - 128) * t * 2)
|
|
||||||
local b = floor_l(bb + (lb - 128) * t * 2)
|
|
||||||
|
|
||||||
if r < 0 then r = 0 elseif r > 255 then r = 255 end
|
|
||||||
if g < 0 then g = 0 elseif g > 255 then g = 255 end
|
|
||||||
if b < 0 then b = 0 elseif b > 255 then b = 255 end
|
|
||||||
|
|
||||||
light_l[i] = bor_l(r, lshift_l(g, 8), lshift_l(b, 16), 0xFF000000)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
shader.compile = function(source)
|
|
||||||
local header = [[
|
|
||||||
local ffi = require("ffi")
|
|
||||||
local bit = require("bit")
|
|
||||||
local band, bor, rshift, lshift = bit.band, bit.bor, bit.rshift, bit.lshift
|
|
||||||
local floor, sqrt, max, min, abs = math.floor, math.sqrt, math.max, math.min, math.abs
|
|
||||||
local sin, cos, fmod = math.sin, math.cos, math.fmod
|
|
||||||
local clamp = function(x, lo, hi) if x < lo then return lo elseif x > hi then return hi else return x end end
|
|
||||||
local mix = function(a, b, t) return a + (b - a) * t end
|
|
||||||
local smoothstep = function(e0, e1, x) local t = clamp((x - e0) / (e1 - e0), 0, 1); return t * t * (3 - 2 * t) end
|
|
||||||
local saturate = function(x) if x < 0 then return 0 elseif x > 1 then return 1 else return x end end
|
|
||||||
local length2 = function(x, y) return sqrt(x*x + y*y) end
|
|
||||||
local length3 = function(x, y, z) return sqrt(x*x + y*y + z*z) end
|
|
||||||
local dot2 = function(ax, ay, bx, by) return ax*bx + ay*by end
|
|
||||||
local dot3 = function(ax, ay, az, bx, by, bz) return ax*bx + ay*by + az*bz end
|
|
||||||
local fract = function(x) return x - floor(x) end
|
|
||||||
|
|
||||||
return function(fb, light, pal, zbuf, w, h, uniforms)
|
|
||||||
uniforms = uniforms or {}
|
|
||||||
local count = w * h
|
|
||||||
local time = uniforms.time or 0
|
|
||||||
local band_l, rshift_l, bor_l, lshift_l = band, rshift, bor, lshift
|
|
||||||
local floor_l, sqrt_l, max_l, min_l = floor, sqrt, max, min
|
|
||||||
|
|
||||||
for i = 0, count - 1 do
|
|
||||||
local x = i % w
|
|
||||||
local y = floor_l(i / w)
|
|
||||||
local uv_x = x / w
|
|
||||||
local uv_y = y / h
|
|
||||||
|
|
||||||
local idx = fb[i]
|
|
||||||
local base = pal[idx]
|
|
||||||
local br = band_l(base, 0xFF)
|
|
||||||
local bg = band_l(rshift_l(base, 8), 0xFF)
|
|
||||||
local bb = band_l(rshift_l(base, 16), 0xFF)
|
|
||||||
|
|
||||||
local lv = light[i]
|
|
||||||
local lr = band_l(lv, 0xFF)
|
|
||||||
local lg = band_l(rshift_l(lv, 8), 0xFF)
|
|
||||||
local lb = band_l(rshift_l(lv, 16), 0xFF)
|
|
||||||
local la = rshift_l(lv, 24)
|
|
||||||
|
|
||||||
local depth = zbuf and zbuf[i] or 0
|
|
||||||
local depth_n = depth / 65535.0
|
|
||||||
|
|
||||||
local r, g, b = br, bg, bb
|
|
||||||
|
|
||||||
]]
|
|
||||||
|
|
||||||
local footer = [[
|
|
||||||
|
|
||||||
r = floor_l(clamp(r, 0, 255))
|
|
||||||
g = floor_l(clamp(g, 0, 255))
|
|
||||||
b = floor_l(clamp(b, 0, 255))
|
|
||||||
light[i] = bor_l(r, lshift_l(g, 8), lshift_l(b, 16), 0xFF000000)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
]]
|
|
||||||
|
|
||||||
local code = header .. source .. footer
|
|
||||||
local fn, err = loadstring(code)
|
|
||||||
if not fn then
|
|
||||||
error("Shader compile error: " .. tostring(err))
|
|
||||||
end
|
|
||||||
return fn()
|
|
||||||
end
|
|
||||||
|
|
||||||
shader.run = function(compiled_shader, uniforms)
|
|
||||||
if fb == nil or light == nil or pal == nil then return end
|
|
||||||
compiled_shader(fb, light, pal, zbuf, w, h, uniforms)
|
|
||||||
end
|
|
||||||
|
|
||||||
shader.presets = {}
|
|
||||||
|
|
||||||
shader.presets.passthrough = shader.compile([[
|
|
||||||
-- passthrough: just use base color
|
|
||||||
]])
|
|
||||||
|
|
||||||
shader.presets.light_tint = shader.compile([[
|
|
||||||
if la > 0 then
|
|
||||||
local t = la / 255.0
|
|
||||||
r = br + (lr - 128) * t * 2
|
|
||||||
g = bg + (lg - 128) * t * 2
|
|
||||||
b = bb + (lb - 128) * t * 2
|
|
||||||
end
|
|
||||||
]])
|
|
||||||
|
|
||||||
shader.presets.vignette = shader.compile([[
|
|
||||||
local cx = uv_x - 0.5
|
|
||||||
local cy = uv_y - 0.5
|
|
||||||
local dist = sqrt_l(cx*cx + cy*cy)
|
|
||||||
local vig = 1.0 - saturate(dist * 1.5)
|
|
||||||
vig = vig * vig
|
|
||||||
r = br * vig
|
|
||||||
g = bg * vig
|
|
||||||
b = bb * vig
|
|
||||||
]])
|
|
||||||
|
|
||||||
shader.presets.scanlines = shader.compile([[
|
|
||||||
local scan = 0.8 + 0.2 * (y % 2)
|
|
||||||
r = br * scan
|
|
||||||
g = bg * scan
|
|
||||||
b = bb * scan
|
|
||||||
]])
|
|
||||||
|
|
||||||
shader.presets.crt = shader.compile([[
|
|
||||||
-- Scanlines
|
|
||||||
local scan = 0.85 + 0.15 * (y % 2)
|
|
||||||
-- Vignette
|
|
||||||
local cx = uv_x - 0.5
|
|
||||||
local cy = uv_y - 0.5
|
|
||||||
local dist = sqrt_l(cx*cx + cy*cy)
|
|
||||||
local vig = 1.0 - saturate(dist * 1.2)
|
|
||||||
-- RGB shift based on x position
|
|
||||||
local shift = (uv_x - 0.5) * 0.02
|
|
||||||
local mult = scan * vig
|
|
||||||
r = br * mult * (1.0 + shift)
|
|
||||||
g = bg * mult
|
|
||||||
b = bb * mult * (1.0 - shift)
|
|
||||||
]])
|
|
||||||
|
|
||||||
shader.presets.dither_fade = shader.compile([[
|
|
||||||
local threshold = fract(sin(x * 12.9898 + y * 78.233) * 43758.5453)
|
|
||||||
local fade = uniforms.fade or 0.5
|
|
||||||
if threshold > fade then
|
|
||||||
r, g, b = 0, 0, 0
|
|
||||||
else
|
|
||||||
r, g, b = br, bg, bb
|
|
||||||
end
|
|
||||||
]])
|
|
||||||
|
|
||||||
shader.presets.fog = shader.compile([[
|
|
||||||
local fog_color_r = uniforms.fog_r or 32
|
|
||||||
local fog_color_g = uniforms.fog_g or 32
|
|
||||||
local fog_color_b = uniforms.fog_b or 48
|
|
||||||
local fog_start = uniforms.fog_start or 0.3
|
|
||||||
local fog_end = uniforms.fog_end or 0.9
|
|
||||||
local fog_t = saturate((depth_n - fog_start) / (fog_end - fog_start))
|
|
||||||
fog_t = fog_t * fog_t
|
|
||||||
r = mix(br, fog_color_r, fog_t)
|
|
||||||
g = mix(bg, fog_color_g, fog_t)
|
|
||||||
b = mix(bb, fog_color_b, fog_t)
|
|
||||||
]])
|
|
||||||
|
|
||||||
shader.presets.light_with_fog = shader.compile([[
|
|
||||||
-- Apply light tint first
|
|
||||||
if la > 0 then
|
|
||||||
local t = la / 255.0
|
|
||||||
r = br + (lr - 128) * t * 2
|
|
||||||
g = bg + (lg - 128) * t * 2
|
|
||||||
b = bb + (lb - 128) * t * 2
|
|
||||||
else
|
|
||||||
r, g, b = br, bg, bb
|
|
||||||
end
|
|
||||||
-- Then fog
|
|
||||||
local fog_r = uniforms.fog_r or 16
|
|
||||||
local fog_g = uniforms.fog_g or 16
|
|
||||||
local fog_b = uniforms.fog_b or 24
|
|
||||||
local fog_start = uniforms.fog_start or 0.2
|
|
||||||
local fog_end = uniforms.fog_end or 0.95
|
|
||||||
local fog_t = saturate((depth_n - fog_start) / (fog_end - fog_start))
|
|
||||||
fog_t = fog_t * fog_t
|
|
||||||
r = mix(r, fog_r, fog_t)
|
|
||||||
g = mix(g, fog_g, fog_t)
|
|
||||||
b = mix(b, fog_b, fog_t)
|
|
||||||
]])
|
|
||||||
|
|
||||||
shader.presets.posterize = shader.compile([[
|
|
||||||
local levels = uniforms.levels or 4
|
|
||||||
local step = 255 / levels
|
|
||||||
r = floor_l(br / step) * step
|
|
||||||
g = floor_l(bg / step) * step
|
|
||||||
b = floor_l(bb / step) * step
|
|
||||||
]])
|
|
||||||
|
|
||||||
shader.presets.chromatic = shader.compile([=[
|
|
||||||
local amount = uniforms.amount or 2
|
|
||||||
local ox = floor_l(amount * (uv_x - 0.5))
|
|
||||||
local r_i = clamp(i - ox, 0, count - 1)
|
|
||||||
local b_i = clamp(i + ox, 0, count - 1)
|
|
||||||
local r_base = pal[fb[r_i]]
|
|
||||||
local b_base = pal[fb[b_i]]
|
|
||||||
r = band_l(r_base, 0xFF)
|
|
||||||
g = bg
|
|
||||||
b = band_l(rshift_l(b_base, 16), 0xFF)
|
|
||||||
]=])
|
|
||||||
|
|
||||||
shader.clear_light = function()
|
|
||||||
if light == nil then return end
|
|
||||||
ffi.fill(light, count * 4, 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
shader.fill_output = function(r, g, b)
|
|
||||||
if light == nil then return end
|
|
||||||
local color = bor(r, lshift(g, 8), lshift(b, 16), 0xFF000000)
|
|
||||||
for i = 0, count - 1 do
|
|
||||||
light[i] = color
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return shader
|
|
||||||
|
|
@ -117,10 +117,6 @@ function World:set_sim_distance(distance)
|
||||||
C.pxl8_world_set_sim_distance(self._ptr, distance)
|
C.pxl8_world_set_sim_distance(self._ptr, distance)
|
||||||
end
|
end
|
||||||
|
|
||||||
function World:set_wireframe(enabled)
|
|
||||||
C.pxl8_world_set_wireframe(self._ptr, enabled)
|
|
||||||
end
|
|
||||||
|
|
||||||
function World:sweep(from_x, from_y, from_z, to_x, to_y, to_z, radius)
|
function World:sweep(from_x, from_y, from_z, to_x, to_y, to_z, radius)
|
||||||
local from = ffi.new("pxl8_vec3", {x = from_x, y = from_y, z = from_z})
|
local from = ffi.new("pxl8_vec3", {x = from_x, y = from_y, z = from_z})
|
||||||
local to = ffi.new("pxl8_vec3", {x = to_x, y = to_y, z = to_z})
|
local to = ffi.new("pxl8_vec3", {x = to_x, y = to_y, z = to_z})
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,50 @@
|
||||||
#include "pxl8_math.h"
|
#include "pxl8_math.h"
|
||||||
|
|
||||||
|
f32 pxl8_fast_inv_sqrt(f32 x) {
|
||||||
|
#if defined(PXL8_NO_SIMD)
|
||||||
|
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;
|
||||||
|
#elif 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
|
||||||
|
}
|
||||||
|
|
||||||
|
f32 pxl8_fast_rcp(f32 x) {
|
||||||
|
#if defined(PXL8_NO_SIMD)
|
||||||
|
return 1.0f / x;
|
||||||
|
#elif 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
|
||||||
|
}
|
||||||
|
|
||||||
u32 pxl8_hash32(u32 x) {
|
u32 pxl8_hash32(u32 x) {
|
||||||
x ^= x >> 16;
|
x ^= x >> 16;
|
||||||
x *= 0x85EBCA6Bu;
|
x *= 0x85EBCA6Bu;
|
||||||
|
|
@ -18,10 +63,6 @@ u64 pxl8_hash64(u64 x) {
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
f32 pxl8_lerp_f32(f32 a, f32 b, f32 t) {
|
|
||||||
return a + (b - a) * t;
|
|
||||||
}
|
|
||||||
|
|
||||||
f32 pxl8_smoothstep(f32 t) {
|
f32 pxl8_smoothstep(f32 t) {
|
||||||
return t * t * (3.0f - 2.0f * t);
|
return t * t * (3.0f - 2.0f * t);
|
||||||
}
|
}
|
||||||
|
|
@ -56,11 +97,11 @@ f32 pxl8_vec2_length(pxl8_vec2 v) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_vec2 pxl8_vec2_normalize(pxl8_vec2 v) {
|
pxl8_vec2 pxl8_vec2_normalize(pxl8_vec2 v) {
|
||||||
f32 len = pxl8_vec2_length(v);
|
f32 len_sq = pxl8_vec2_dot(v, v);
|
||||||
|
|
||||||
if (len < 1e-6f) return (pxl8_vec2){0};
|
if (len_sq < 1e-12f) return (pxl8_vec2){0};
|
||||||
|
|
||||||
return pxl8_vec2_scale(v, 1.0f / len);
|
return pxl8_vec2_scale(v, pxl8_fast_inv_sqrt(len_sq));
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_vec3 pxl8_vec3_add(pxl8_vec3 a, pxl8_vec3 b) {
|
pxl8_vec3 pxl8_vec3_add(pxl8_vec3 a, pxl8_vec3 b) {
|
||||||
|
|
@ -112,11 +153,11 @@ pxl8_vec3 pxl8_vec3_lerp(pxl8_vec3 a, pxl8_vec3 b, f32 t) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_vec3 pxl8_vec3_normalize(pxl8_vec3 v) {
|
pxl8_vec3 pxl8_vec3_normalize(pxl8_vec3 v) {
|
||||||
f32 len = pxl8_vec3_length(v);
|
f32 len_sq = pxl8_vec3_dot(v, v);
|
||||||
|
|
||||||
if (len < 1e-6f) return (pxl8_vec3){0};
|
if (len_sq < 1e-12f) return (pxl8_vec3){0};
|
||||||
|
|
||||||
return pxl8_vec3_scale(v, 1.0f / len);
|
return pxl8_vec3_scale(v, pxl8_fast_inv_sqrt(len_sq));
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_mat4 pxl8_mat4_identity(void) {
|
pxl8_mat4 pxl8_mat4_identity(void) {
|
||||||
|
|
|
||||||
|
|
@ -15,64 +15,43 @@
|
||||||
#define PXL8_PI 3.14159265358979323846f
|
#define PXL8_PI 3.14159265358979323846f
|
||||||
#define PXL8_TAU (PXL8_PI * 2.0f)
|
#define PXL8_TAU (PXL8_PI * 2.0f)
|
||||||
|
|
||||||
static inline f32 pxl8_fast_inv_sqrt(f32 x) {
|
#define pxl8_min(a, b) ((a) < (b) ? (a) : (b))
|
||||||
#if defined(PXL8_NO_SIMD)
|
#define pxl8_max(a, b) ((a) > (b) ? (a) : (b))
|
||||||
f32 half = 0.5f * x;
|
#define pxl8_clamp(x, lo, hi) ((x) < (lo) ? (lo) : ((x) > (hi) ? (hi) : (x)))
|
||||||
i32 i = *(i32*)&x;
|
#define pxl8_clamp_byte(x) pxl8_clamp(x, 0, 255)
|
||||||
i = 0x5f3759df - (i >> 1);
|
|
||||||
x = *(f32*)&i;
|
|
||||||
x = x * (1.5f - half * x * x);
|
|
||||||
return x;
|
|
||||||
#elif 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) {
|
#define pxl8_abs(x) fabsf(x)
|
||||||
#if defined(PXL8_NO_SIMD)
|
#define pxl8_ceil(x) ceilf(x)
|
||||||
return 1.0f / x;
|
#define pxl8_floor(x) floorf(x)
|
||||||
#elif defined(__x86_64__) || defined(_M_X64)
|
#define pxl8_sqrt(x) sqrtf(x)
|
||||||
__m128 v = _mm_set_ss(x);
|
#define pxl8_sin(x) sinf(x)
|
||||||
__m128 rcp = _mm_rcp_ss(v);
|
#define pxl8_cos(x) cosf(x)
|
||||||
rcp = _mm_add_ss(rcp, _mm_mul_ss(rcp, _mm_sub_ss(_mm_set_ss(1.0f), _mm_mul_ss(v, rcp))));
|
#define pxl8_tan(x) tanf(x)
|
||||||
return _mm_cvtss_f32(rcp);
|
#define pxl8_exp(x) expf(x)
|
||||||
#elif defined(__aarch64__) || defined(_M_ARM64)
|
#define pxl8_log(x) logf(x)
|
||||||
float32x2_t v = vdup_n_f32(x);
|
#define pxl8_pow(x, y) powf(x, y)
|
||||||
float32x2_t est = vrecpe_f32(v);
|
#define pxl8_lerp(a, b, t) ((a) + ((b) - (a)) * (t))
|
||||||
est = vmul_f32(est, vrecps_f32(v, est));
|
|
||||||
return vget_lane_f32(est, 0);
|
|
||||||
#else
|
|
||||||
return 1.0f / x;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct pxl8_vec2 {
|
f32 pxl8_fast_inv_sqrt(f32 x);
|
||||||
f32 x, y;
|
f32 pxl8_fast_rcp(f32 x);
|
||||||
|
|
||||||
|
typedef union pxl8_vec2 {
|
||||||
|
struct { f32 x, y; };
|
||||||
|
struct { f32 yaw, pitch; };
|
||||||
|
f32 v[2];
|
||||||
} pxl8_vec2;
|
} pxl8_vec2;
|
||||||
|
|
||||||
typedef struct pxl8_vec3 {
|
typedef union pxl8_vec3 {
|
||||||
f32 x, y, z;
|
struct { f32 x, y, z; };
|
||||||
|
f32 v[3];
|
||||||
} pxl8_vec3;
|
} pxl8_vec3;
|
||||||
|
|
||||||
typedef struct pxl8_vec4 {
|
typedef union pxl8_vec4 {
|
||||||
f32 x, y, z, w;
|
struct { f32 x, y, z, w; };
|
||||||
|
f32 v[4];
|
||||||
} pxl8_vec4;
|
} pxl8_vec4;
|
||||||
|
|
||||||
typedef struct pxl8_mat4 {
|
typedef union pxl8_mat4 {
|
||||||
f32 m[16];
|
f32 m[16];
|
||||||
} pxl8_mat4;
|
} pxl8_mat4;
|
||||||
|
|
||||||
|
|
@ -99,18 +78,6 @@ typedef struct pxl8_ray {
|
||||||
bool hit;
|
bool hit;
|
||||||
} pxl8_ray;
|
} pxl8_ray;
|
||||||
|
|
||||||
#define PXL8_SDF_X 32
|
|
||||||
#define PXL8_SDF_Y 16
|
|
||||||
#define PXL8_SDF_Z 32
|
|
||||||
#define PXL8_SDF_SIZE (PXL8_SDF_X * PXL8_SDF_Y * PXL8_SDF_Z)
|
|
||||||
#define PXL8_SDF_CELL 16.0f
|
|
||||||
|
|
||||||
typedef struct pxl8_sdf {
|
|
||||||
i8 data[PXL8_SDF_SIZE];
|
|
||||||
pxl8_vec3 origin;
|
|
||||||
f32 cell;
|
|
||||||
} pxl8_sdf;
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -118,7 +85,6 @@ extern "C" {
|
||||||
u32 pxl8_hash32(u32 x);
|
u32 pxl8_hash32(u32 x);
|
||||||
u64 pxl8_hash64(u64 x);
|
u64 pxl8_hash64(u64 x);
|
||||||
|
|
||||||
f32 pxl8_lerp_f32(f32 a, f32 b, f32 t);
|
|
||||||
f32 pxl8_smoothstep(f32 t);
|
f32 pxl8_smoothstep(f32 t);
|
||||||
|
|
||||||
pxl8_vec2 pxl8_vec2_add(pxl8_vec2 a, pxl8_vec2 b);
|
pxl8_vec2 pxl8_vec2_add(pxl8_vec2 a, pxl8_vec2 b);
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,9 @@ f32 pxl8_value_noise(f32 x, f32 z, u64 seed) {
|
||||||
f32 c01 = pxl8_noise2d(x0, z1, seed);
|
f32 c01 = pxl8_noise2d(x0, z1, seed);
|
||||||
f32 c11 = pxl8_noise2d(x1, z1, seed);
|
f32 c11 = pxl8_noise2d(x1, z1, seed);
|
||||||
|
|
||||||
f32 a = pxl8_lerp_f32(c00, c10, tx);
|
f32 a = pxl8_lerp(c00, c10, tx);
|
||||||
f32 b = pxl8_lerp_f32(c01, c11, tx);
|
f32 b = pxl8_lerp(c01, c11, tx);
|
||||||
return pxl8_lerp_f32(a, b, tz);
|
return pxl8_lerp(a, b, tz);
|
||||||
}
|
}
|
||||||
|
|
||||||
f32 pxl8_value_noise3d(f32 x, f32 y, f32 z, u64 seed) {
|
f32 pxl8_value_noise3d(f32 x, f32 y, f32 z, u64 seed) {
|
||||||
|
|
@ -50,15 +50,15 @@ f32 pxl8_value_noise3d(f32 x, f32 y, f32 z, u64 seed) {
|
||||||
f32 c011 = pxl8_noise3d(x0, y1, z1, seed);
|
f32 c011 = pxl8_noise3d(x0, y1, z1, seed);
|
||||||
f32 c111 = pxl8_noise3d(x1, y1, z1, seed);
|
f32 c111 = pxl8_noise3d(x1, y1, z1, seed);
|
||||||
|
|
||||||
f32 a00 = pxl8_lerp_f32(c000, c100, tx);
|
f32 a00 = pxl8_lerp(c000, c100, tx);
|
||||||
f32 a10 = pxl8_lerp_f32(c010, c110, tx);
|
f32 a10 = pxl8_lerp(c010, c110, tx);
|
||||||
f32 a01 = pxl8_lerp_f32(c001, c101, tx);
|
f32 a01 = pxl8_lerp(c001, c101, tx);
|
||||||
f32 a11 = pxl8_lerp_f32(c011, c111, tx);
|
f32 a11 = pxl8_lerp(c011, c111, tx);
|
||||||
|
|
||||||
f32 b0 = pxl8_lerp_f32(a00, a10, ty);
|
f32 b0 = pxl8_lerp(a00, a10, ty);
|
||||||
f32 b1 = pxl8_lerp_f32(a01, a11, ty);
|
f32 b1 = pxl8_lerp(a01, a11, ty);
|
||||||
|
|
||||||
return pxl8_lerp_f32(b0, b1, tz);
|
return pxl8_lerp(b0, b1, tz);
|
||||||
}
|
}
|
||||||
|
|
||||||
f32 pxl8_fbm(f32 x, f32 z, u64 seed, u32 octaves) {
|
f32 pxl8_fbm(f32 x, f32 z, u64 seed, u32 octaves) {
|
||||||
|
|
|
||||||
|
|
@ -1152,6 +1152,11 @@ bool pxl8_script_check_reload(pxl8_script* script) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u32 frame_counter = 0;
|
||||||
|
if (++frame_counter % 60 != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
time_t current_mod_time = get_latest_script_mod_time(script->watch_dir);
|
time_t current_mod_time = get_latest_script_mod_time(script->watch_dir);
|
||||||
if (current_mod_time > script->latest_mod_time && current_mod_time != 0) {
|
if (current_mod_time > script->latest_mod_time && current_mod_time != 0) {
|
||||||
pxl8_info("Script files modified, reloading: %s", script->main_path);
|
pxl8_info("Script files modified, reloading: %s", script->main_path);
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,8 @@ static const char* pxl8_ffi_cdefs =
|
||||||
"void pxl8_set_colormap(pxl8_colormap* cm, const u8* data, u32 size);\n"
|
"void pxl8_set_colormap(pxl8_colormap* cm, const u8* data, u32 size);\n"
|
||||||
"void pxl8_gfx_ensure_blend_tables(pxl8_gfx* gfx);\n"
|
"void pxl8_gfx_ensure_blend_tables(pxl8_gfx* gfx);\n"
|
||||||
"i32 pxl8_gfx_get_width(pxl8_gfx* ctx);\n"
|
"i32 pxl8_gfx_get_width(pxl8_gfx* ctx);\n"
|
||||||
|
"void pxl8_gfx_set_wireframe(pxl8_gfx* gfx, bool enabled);\n"
|
||||||
|
"bool pxl8_gfx_get_wireframe(const pxl8_gfx* gfx);\n"
|
||||||
"void pxl8_2d_circle(pxl8_gfx* ctx, i32 x, i32 y, i32 r, u32 color);\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"
|
"void pxl8_2d_circle_fill(pxl8_gfx* ctx, i32 x, i32 y, i32 r, u32 color);\n"
|
||||||
"void pxl8_2d_clear(pxl8_gfx* ctx, u32 color);\n"
|
"void pxl8_2d_clear(pxl8_gfx* ctx, u32 color);\n"
|
||||||
|
|
@ -197,6 +199,7 @@ static const char* pxl8_ffi_cdefs =
|
||||||
"void pxl8_anim_stop(pxl8_anim* anim);\n"
|
"void pxl8_anim_stop(pxl8_anim* anim);\n"
|
||||||
"void pxl8_anim_update(pxl8_anim* anim, f32 dt);\n"
|
"void pxl8_anim_update(pxl8_anim* anim, f32 dt);\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
"typedef union { struct { float x, y; }; struct { float yaw, pitch; }; float v[2]; } pxl8_vec2;\n"
|
||||||
"typedef struct { float x, y, z; } pxl8_vec3;\n"
|
"typedef struct { float x, y, z; } pxl8_vec3;\n"
|
||||||
"typedef struct { float x, y, z, w; } pxl8_vec4;\n"
|
"typedef struct { float x, y, z, w; } pxl8_vec4;\n"
|
||||||
"typedef struct { float m[16]; } pxl8_mat4;\n"
|
"typedef struct { float m[16]; } pxl8_mat4;\n"
|
||||||
|
|
@ -244,31 +247,7 @@ static const char* pxl8_ffi_cdefs =
|
||||||
"typedef struct pxl8_projected_point { i32 x; i32 y; f32 depth; bool visible; } pxl8_projected_point;\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"
|
"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"
|
"\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 {\n"
|
|
||||||
" u8 color;\n"
|
|
||||||
" u16 depth;\n"
|
|
||||||
" u8 height;\n"
|
|
||||||
" u16 intensity;\n"
|
|
||||||
" u8 radius;\n"
|
|
||||||
" pxl8_glow_shape shape;\n"
|
|
||||||
" i16 x;\n"
|
|
||||||
" i16 y;\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"
|
"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"
|
"void pxl8_gfx_colormap_update(pxl8_gfx* gfx);\n"
|
||||||
"\n"
|
"\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_begin_frame(pxl8_gfx* gfx, const pxl8_3d_camera* camera, const pxl8_lights* lights, const pxl8_3d_uniforms* uniforms);\n"
|
||||||
|
|
@ -293,10 +272,8 @@ static const char* pxl8_ffi_cdefs =
|
||||||
" bool dither;\n"
|
" bool dither;\n"
|
||||||
" bool double_sided;\n"
|
" bool double_sided;\n"
|
||||||
" bool dynamic_lighting;\n"
|
" bool dynamic_lighting;\n"
|
||||||
|
" bool emissive;\n"
|
||||||
" bool per_pixel;\n"
|
" bool per_pixel;\n"
|
||||||
" bool vertex_color_passthrough;\n"
|
|
||||||
" bool wireframe;\n"
|
|
||||||
" f32 emissive_intensity;\n"
|
|
||||||
"} pxl8_gfx_material;\n"
|
"} pxl8_gfx_material;\n"
|
||||||
"\n"
|
"\n"
|
||||||
"typedef struct pxl8_vertex {\n"
|
"typedef struct pxl8_vertex {\n"
|
||||||
|
|
@ -446,7 +423,6 @@ static const char* pxl8_ffi_cdefs =
|
||||||
"pxl8_ray pxl8_world_sweep(const pxl8_world* world, pxl8_vec3 from, pxl8_vec3 to, float radius);\n"
|
"pxl8_ray pxl8_world_sweep(const pxl8_world* world, pxl8_vec3 from, pxl8_vec3 to, float radius);\n"
|
||||||
"void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos);\n"
|
"void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos);\n"
|
||||||
"void pxl8_world_set_bsp_material(pxl8_world* world, u16 material_id, const pxl8_gfx_material* material);\n"
|
"void pxl8_world_set_bsp_material(pxl8_world* world, u16 material_id, const pxl8_gfx_material* material);\n"
|
||||||
"void pxl8_world_set_wireframe(pxl8_world* world, bool enabled);\n"
|
|
||||||
"i32 pxl8_world_get_render_distance(const pxl8_world* world);\n"
|
"i32 pxl8_world_get_render_distance(const pxl8_world* world);\n"
|
||||||
"void pxl8_world_set_render_distance(pxl8_world* world, i32 distance);\n"
|
"void pxl8_world_set_render_distance(pxl8_world* world, i32 distance);\n"
|
||||||
"i32 pxl8_world_get_sim_distance(const pxl8_world* world);\n"
|
"i32 pxl8_world_get_sim_distance(const pxl8_world* world);\n"
|
||||||
|
|
|
||||||
|
|
@ -569,8 +569,3 @@ pxl8_vxl_render_state* pxl8_vxl_render_state_create(void) {
|
||||||
void pxl8_vxl_render_state_destroy(pxl8_vxl_render_state* state) {
|
void pxl8_vxl_render_state_destroy(pxl8_vxl_render_state* state) {
|
||||||
pxl8_free(state);
|
pxl8_free(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_vxl_set_wireframe(pxl8_vxl_render_state* state, bool wireframe) {
|
|
||||||
if (!state) return;
|
|
||||||
state->wireframe = wireframe;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,11 @@ extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct pxl8_vxl_render_state {
|
typedef struct pxl8_vxl_render_state {
|
||||||
bool wireframe;
|
u8 _unused;
|
||||||
} pxl8_vxl_render_state;
|
} pxl8_vxl_render_state;
|
||||||
|
|
||||||
pxl8_vxl_render_state* pxl8_vxl_render_state_create(void);
|
pxl8_vxl_render_state* pxl8_vxl_render_state_create(void);
|
||||||
void pxl8_vxl_render_state_destroy(pxl8_vxl_render_state* state);
|
void pxl8_vxl_render_state_destroy(pxl8_vxl_render_state* state);
|
||||||
void pxl8_vxl_set_wireframe(pxl8_vxl_render_state* state, bool wireframe);
|
|
||||||
|
|
||||||
typedef struct pxl8_vxl_mesh_config {
|
typedef struct pxl8_vxl_mesh_config {
|
||||||
bool ambient_occlusion;
|
bool ambient_occlusion;
|
||||||
|
|
|
||||||
|
|
@ -30,13 +30,14 @@ struct pxl8_world {
|
||||||
pxl8_entity_pool* entities;
|
pxl8_entity_pool* entities;
|
||||||
pxl8_bsp_render_state* bsp_render_state;
|
pxl8_bsp_render_state* bsp_render_state;
|
||||||
pxl8_vxl_render_state* vxl_render_state;
|
pxl8_vxl_render_state* vxl_render_state;
|
||||||
pxl8_sdf sdf;
|
|
||||||
i32 render_distance;
|
i32 render_distance;
|
||||||
i32 sim_distance;
|
i32 sim_distance;
|
||||||
|
|
||||||
pxl8_sim_entity local_player;
|
pxl8_sim_entity local_player;
|
||||||
u64 client_tick;
|
u64 client_tick;
|
||||||
|
|
||||||
|
pxl8_vec2 pointer_motion;
|
||||||
|
|
||||||
#ifdef PXL8_ASYNC_THREADS
|
#ifdef PXL8_ASYNC_THREADS
|
||||||
pxl8_sim_entity render_state[2];
|
pxl8_sim_entity render_state[2];
|
||||||
atomic_uint active_buffer;
|
atomic_uint active_buffer;
|
||||||
|
|
@ -187,100 +188,6 @@ static void userdata_to_entity(const u8* userdata, pxl8_sim_entity* ent) {
|
||||||
memcpy(&ent->kind, p, 2);
|
memcpy(&ent->kind, p, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void update_sdf(pxl8_world* world, pxl8_vec3 center, f32 cell_size) {
|
|
||||||
if (!world) return;
|
|
||||||
|
|
||||||
world->sdf.cell = cell_size;
|
|
||||||
world->sdf.origin.x = center.x - (PXL8_SDF_X / 2) * cell_size;
|
|
||||||
world->sdf.origin.y = center.y - (PXL8_SDF_Y / 2) * cell_size;
|
|
||||||
world->sdf.origin.z = center.z - (PXL8_SDF_Z / 2) * cell_size;
|
|
||||||
|
|
||||||
i16 seed_x[PXL8_SDF_SIZE];
|
|
||||||
i16 seed_y[PXL8_SDF_SIZE];
|
|
||||||
i16 seed_z[PXL8_SDF_SIZE];
|
|
||||||
|
|
||||||
for (i32 iy = 0; iy < PXL8_SDF_Y; iy++) {
|
|
||||||
f32 wy = world->sdf.origin.y + (iy + 0.5f) * cell_size;
|
|
||||||
for (i32 iz = 0; iz < PXL8_SDF_Z; iz++) {
|
|
||||||
f32 wz = world->sdf.origin.z + (iz + 0.5f) * cell_size;
|
|
||||||
for (i32 ix = 0; ix < PXL8_SDF_X; ix++) {
|
|
||||||
f32 wx = world->sdf.origin.x + (ix + 0.5f) * cell_size;
|
|
||||||
i32 idx = iy * PXL8_SDF_Z * PXL8_SDF_X + iz * PXL8_SDF_X + ix;
|
|
||||||
|
|
||||||
if (pxl8_world_point_solid(world, wx, wy, wz)) {
|
|
||||||
seed_x[idx] = (i16)ix;
|
|
||||||
seed_y[idx] = (i16)iy;
|
|
||||||
seed_z[idx] = (i16)iz;
|
|
||||||
} else {
|
|
||||||
seed_x[idx] = -1;
|
|
||||||
seed_y[idx] = -1;
|
|
||||||
seed_z[idx] = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i32 step = 16; step >= 1; step /= 2) {
|
|
||||||
for (i32 iy = 0; iy < PXL8_SDF_Y; iy++) {
|
|
||||||
for (i32 iz = 0; iz < PXL8_SDF_Z; iz++) {
|
|
||||||
for (i32 ix = 0; ix < PXL8_SDF_X; ix++) {
|
|
||||||
i32 idx = iy * PXL8_SDF_Z * PXL8_SDF_X + iz * PXL8_SDF_X + ix;
|
|
||||||
i32 best_dist_sq = (seed_x[idx] >= 0)
|
|
||||||
? (ix - seed_x[idx]) * (ix - seed_x[idx]) +
|
|
||||||
(iy - seed_y[idx]) * (iy - seed_y[idx]) +
|
|
||||||
(iz - seed_z[idx]) * (iz - seed_z[idx])
|
|
||||||
: 0x7FFFFFFF;
|
|
||||||
|
|
||||||
for (i32 dy = -step; dy <= step; dy += step) {
|
|
||||||
i32 ny = iy + dy;
|
|
||||||
if (ny < 0 || ny >= PXL8_SDF_Y) continue;
|
|
||||||
for (i32 dz = -step; dz <= step; dz += step) {
|
|
||||||
i32 nz = iz + dz;
|
|
||||||
if (nz < 0 || nz >= PXL8_SDF_Z) continue;
|
|
||||||
for (i32 dx = -step; dx <= step; dx += step) {
|
|
||||||
if (dx == 0 && dy == 0 && dz == 0) continue;
|
|
||||||
i32 nx = ix + dx;
|
|
||||||
if (nx < 0 || nx >= PXL8_SDF_X) continue;
|
|
||||||
|
|
||||||
i32 nidx = ny * PXL8_SDF_Z * PXL8_SDF_X + nz * PXL8_SDF_X + nx;
|
|
||||||
if (seed_x[nidx] < 0) continue;
|
|
||||||
|
|
||||||
i32 dist_sq = (ix - seed_x[nidx]) * (ix - seed_x[nidx]) +
|
|
||||||
(iy - seed_y[nidx]) * (iy - seed_y[nidx]) +
|
|
||||||
(iz - seed_z[nidx]) * (iz - seed_z[nidx]);
|
|
||||||
|
|
||||||
if (dist_sq < best_dist_sq) {
|
|
||||||
best_dist_sq = dist_sq;
|
|
||||||
seed_x[idx] = seed_x[nidx];
|
|
||||||
seed_y[idx] = seed_y[nidx];
|
|
||||||
seed_z[idx] = seed_z[nidx];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i32 i = 0; i < PXL8_SDF_SIZE; i++) {
|
|
||||||
if (seed_x[i] < 0) {
|
|
||||||
world->sdf.data[i] = 127;
|
|
||||||
} else {
|
|
||||||
i32 idx_x = i % PXL8_SDF_X;
|
|
||||||
i32 idx_z = (i / PXL8_SDF_X) % PXL8_SDF_Z;
|
|
||||||
i32 idx_y = i / (PXL8_SDF_X * PXL8_SDF_Z);
|
|
||||||
i32 dx = idx_x - seed_x[i];
|
|
||||||
i32 dy = idx_y - seed_y[i];
|
|
||||||
i32 dz = idx_z - seed_z[i];
|
|
||||||
f32 dist = sqrtf((f32)(dx * dx + dy * dy + dz * dz));
|
|
||||||
i32 d = (i32)(dist * cell_size);
|
|
||||||
if (d > 127) d = 127;
|
|
||||||
world->sdf.data[i] = (i8)d;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool pxl8_world_point_solid(const pxl8_world* world, f32 x, f32 y, f32 z) {
|
bool pxl8_world_point_solid(const pxl8_world* world, f32 x, f32 y, f32 z) {
|
||||||
if (!world) return false;
|
if (!world) return false;
|
||||||
|
|
||||||
|
|
@ -395,18 +302,22 @@ void pxl8_world_update(pxl8_world* world, const pxl8_input_state* input, f32 dt)
|
||||||
msg.look_dx = (f32)pxl8_mouse_dx(input);
|
msg.look_dx = (f32)pxl8_mouse_dx(input);
|
||||||
msg.look_dy = (f32)pxl8_mouse_dy(input);
|
msg.look_dy = (f32)pxl8_mouse_dy(input);
|
||||||
msg.buttons = pxl8_key_down(input, "space") ? 1 : 0;
|
msg.buttons = pxl8_key_down(input, "space") ? 1 : 0;
|
||||||
|
msg.tick = world->client_tick;
|
||||||
|
msg.timestamp = pxl8_get_ticks_ns();
|
||||||
|
|
||||||
|
if (world->net) {
|
||||||
|
pxl8_net_send_input(world->net, &msg);
|
||||||
|
}
|
||||||
|
|
||||||
pxl8_sim_world sim = make_sim_world(world, world->local_player.pos);
|
pxl8_sim_world sim = make_sim_world(world, world->local_player.pos);
|
||||||
pxl8_sim_move_player(&world->local_player, &msg, &sim, dt);
|
pxl8_sim_move_player(&world->local_player, &msg, &sim, dt);
|
||||||
|
world->client_tick++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos) {
|
void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos) {
|
||||||
if (!world || !gfx) return;
|
if (!world || !gfx) return;
|
||||||
|
|
||||||
update_sdf(world, camera_pos, PXL8_SDF_CELL);
|
|
||||||
pxl8_3d_set_sdf(gfx, &world->sdf);
|
|
||||||
|
|
||||||
if (world->active_chunk) {
|
if (world->active_chunk) {
|
||||||
if (world->active_chunk->type == PXL8_WORLD_CHUNK_BSP && world->active_chunk->bsp) {
|
if (world->active_chunk->type == PXL8_WORLD_CHUNK_BSP && world->active_chunk->bsp) {
|
||||||
pxl8_3d_set_bsp(gfx, world->active_chunk->bsp);
|
pxl8_3d_set_bsp(gfx, world->active_chunk->bsp);
|
||||||
|
|
@ -421,13 +332,10 @@ void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos) {
|
||||||
|
|
||||||
const pxl8_frustum* frustum = pxl8_3d_get_frustum(gfx);
|
const pxl8_frustum* frustum = pxl8_3d_get_frustum(gfx);
|
||||||
|
|
||||||
bool wireframe = world->vxl_render_state && world->vxl_render_state->wireframe;
|
|
||||||
pxl8_gfx_material mat = {
|
pxl8_gfx_material mat = {
|
||||||
.texture_id = 0,
|
.texture_id = 0,
|
||||||
.dynamic_lighting = true,
|
.dynamic_lighting = true,
|
||||||
.vertex_color_passthrough = true,
|
|
||||||
.alpha = 255,
|
.alpha = 255,
|
||||||
.wireframe = wireframe,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
i32 r = world->render_distance;
|
i32 r = world->render_distance;
|
||||||
|
|
@ -481,16 +389,27 @@ void pxl8_world_sync(pxl8_world* world, pxl8_net* net) {
|
||||||
world->active_chunk = chunk;
|
world->active_chunk = chunk;
|
||||||
pxl8_debug("[CLIENT] Synced BSP chunk id=%u as active (verts=%u faces=%u)",
|
pxl8_debug("[CLIENT] Synced BSP chunk id=%u as active (verts=%u faces=%u)",
|
||||||
chunk_id, chunk->bsp->num_vertices, chunk->bsp->num_faces);
|
chunk_id, chunk->bsp->num_vertices, chunk->bsp->num_faces);
|
||||||
|
|
||||||
|
if (world->bsp_render_state) {
|
||||||
|
pxl8_bsp_render_state_destroy(world->bsp_render_state);
|
||||||
|
world->bsp_render_state = NULL;
|
||||||
|
}
|
||||||
|
world->bsp_render_state = pxl8_bsp_render_state_create(chunk->bsp->num_faces);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (chunk_id == 0 && world->active_chunk != NULL) {
|
} else if (chunk_id == 0 && world->active_chunk != NULL) {
|
||||||
world->active_chunk = NULL;
|
world->active_chunk = NULL;
|
||||||
|
if (world->bsp_render_state) {
|
||||||
|
pxl8_bsp_render_state_destroy(world->bsp_render_state);
|
||||||
|
world->bsp_render_state = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ensure_bsp_render_state(pxl8_world* world) {
|
static void ensure_bsp_render_state(pxl8_world* world) {
|
||||||
if (!world || world->bsp_render_state) return;
|
if (!world || world->bsp_render_state) return;
|
||||||
if (!world->active_chunk || world->active_chunk->type != PXL8_WORLD_CHUNK_BSP) return;
|
if (!world->active_chunk) return;
|
||||||
|
if (world->active_chunk->type != PXL8_WORLD_CHUNK_BSP) return;
|
||||||
if (!world->active_chunk->bsp) return;
|
if (!world->active_chunk->bsp) return;
|
||||||
|
|
||||||
world->bsp_render_state = pxl8_bsp_render_state_create(world->active_chunk->bsp->num_faces);
|
world->bsp_render_state = pxl8_bsp_render_state_create(world->active_chunk->bsp->num_faces);
|
||||||
|
|
@ -505,18 +424,6 @@ void pxl8_world_set_bsp_material(pxl8_world* world, u16 material_id, const pxl8_
|
||||||
pxl8_bsp_set_material(world->bsp_render_state, material_id, material);
|
pxl8_bsp_set_material(world->bsp_render_state, material_id, material);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_world_set_wireframe(pxl8_world* world, bool enabled) {
|
|
||||||
if (!world) return;
|
|
||||||
|
|
||||||
ensure_bsp_render_state(world);
|
|
||||||
if (world->bsp_render_state) {
|
|
||||||
pxl8_bsp_set_wireframe(world->bsp_render_state, enabled);
|
|
||||||
}
|
|
||||||
if (world->vxl_render_state) {
|
|
||||||
pxl8_vxl_set_wireframe(world->vxl_render_state, enabled);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
i32 pxl8_world_get_render_distance(const pxl8_world* world) {
|
i32 pxl8_world_get_render_distance(const pxl8_world* world) {
|
||||||
if (!world) return 3;
|
if (!world) return 3;
|
||||||
return world->render_distance;
|
return world->render_distance;
|
||||||
|
|
@ -559,8 +466,15 @@ void pxl8_world_init_local_player(pxl8_world* world, f32 x, f32 y, f32 z) {
|
||||||
|
|
||||||
pxl8_sim_entity* pxl8_world_local_player(pxl8_world* world) {
|
pxl8_sim_entity* pxl8_world_local_player(pxl8_world* world) {
|
||||||
if (!world) return NULL;
|
if (!world) return NULL;
|
||||||
|
#ifdef PXL8_ASYNC_THREADS
|
||||||
|
const pxl8_sim_entity* state = pxl8_world_get_render_state(world);
|
||||||
|
if (!state) return NULL;
|
||||||
|
if (!(state->flags & PXL8_SIM_FLAG_ALIVE)) return NULL;
|
||||||
|
return (pxl8_sim_entity*)state;
|
||||||
|
#else
|
||||||
if (!(world->local_player.flags & PXL8_SIM_FLAG_ALIVE)) return NULL;
|
if (!(world->local_player.flags & PXL8_SIM_FLAG_ALIVE)) return NULL;
|
||||||
return &world->local_player;
|
return &world->local_player;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_world_predict(pxl8_world* world, pxl8_net* net, const pxl8_input_msg* input, f32 dt) {
|
void pxl8_world_predict(pxl8_world* world, pxl8_net* net, const pxl8_input_msg* input, f32 dt) {
|
||||||
|
|
@ -585,7 +499,14 @@ void pxl8_world_reconcile(pxl8_world* world, pxl8_net* net, f32 dt) {
|
||||||
const u8* server_state = pxl8_net_entity_userdata(net, player_id);
|
const u8* server_state = pxl8_net_entity_userdata(net, player_id);
|
||||||
if (!server_state) return;
|
if (!server_state) return;
|
||||||
|
|
||||||
userdata_to_entity(server_state, &world->local_player);
|
pxl8_sim_entity server_player = {0};
|
||||||
|
userdata_to_entity(server_state, &server_player);
|
||||||
|
|
||||||
|
if (!(server_player.flags & PXL8_SIM_FLAG_ALIVE)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
world->local_player = server_player;
|
||||||
|
|
||||||
const pxl8_snapshot_header* snap = pxl8_net_snapshot(net);
|
const pxl8_snapshot_header* snap = pxl8_net_snapshot(net);
|
||||||
u64 server_tick = snap ? snap->tick : 0;
|
u64 server_tick = snap ? snap->tick : 0;
|
||||||
|
|
@ -606,11 +527,9 @@ void pxl8_world_reconcile(pxl8_world* world, pxl8_net* net, f32 dt) {
|
||||||
#define SIM_TIMESTEP (1.0f / 60.0f)
|
#define SIM_TIMESTEP (1.0f / 60.0f)
|
||||||
|
|
||||||
static void pxl8_world_sim_tick(pxl8_world* world, f32 dt) {
|
static void pxl8_world_sim_tick(pxl8_world* world, f32 dt) {
|
||||||
if (!(world->local_player.flags & PXL8_SIM_FLAG_ALIVE)) return;
|
bool alive = (world->local_player.flags & PXL8_SIM_FLAG_ALIVE) != 0;
|
||||||
|
|
||||||
pxl8_input_msg merged = {0};
|
pxl8_input_msg merged = {0};
|
||||||
pxl8_input_msg* input = NULL;
|
pxl8_input_msg* input = NULL;
|
||||||
bool has_input = false;
|
|
||||||
|
|
||||||
while ((input = pxl8_queue_pop(&world->input_queue))) {
|
while ((input = pxl8_queue_pop(&world->input_queue))) {
|
||||||
merged.look_dx += input->look_dx;
|
merged.look_dx += input->look_dx;
|
||||||
|
|
@ -618,16 +537,31 @@ static void pxl8_world_sim_tick(pxl8_world* world, f32 dt) {
|
||||||
merged.move_x = input->move_x;
|
merged.move_x = input->move_x;
|
||||||
merged.move_y = input->move_y;
|
merged.move_y = input->move_y;
|
||||||
merged.buttons |= input->buttons;
|
merged.buttons |= input->buttons;
|
||||||
has_input = true;
|
|
||||||
pxl8_free(input);
|
pxl8_free(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!alive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const f32 MAX_LOOK_DELTA = 100.0f;
|
const f32 MAX_LOOK_DELTA = 100.0f;
|
||||||
if (merged.look_dx > MAX_LOOK_DELTA) merged.look_dx = MAX_LOOK_DELTA;
|
if (merged.look_dx > MAX_LOOK_DELTA) merged.look_dx = MAX_LOOK_DELTA;
|
||||||
if (merged.look_dx < -MAX_LOOK_DELTA) merged.look_dx = -MAX_LOOK_DELTA;
|
if (merged.look_dx < -MAX_LOOK_DELTA) merged.look_dx = -MAX_LOOK_DELTA;
|
||||||
if (merged.look_dy > MAX_LOOK_DELTA) merged.look_dy = MAX_LOOK_DELTA;
|
if (merged.look_dy > MAX_LOOK_DELTA) merged.look_dy = MAX_LOOK_DELTA;
|
||||||
if (merged.look_dy < -MAX_LOOK_DELTA) merged.look_dy = -MAX_LOOK_DELTA;
|
if (merged.look_dy < -MAX_LOOK_DELTA) merged.look_dy = -MAX_LOOK_DELTA;
|
||||||
|
|
||||||
|
merged.tick = world->client_tick;
|
||||||
|
merged.timestamp = pxl8_get_ticks_ns();
|
||||||
|
merged.yaw = world->pointer_motion.yaw;
|
||||||
|
if (world->net) {
|
||||||
|
pxl8_net_send_input(world->net, &merged);
|
||||||
|
}
|
||||||
|
|
||||||
|
world->local_player.yaw = world->pointer_motion.yaw;
|
||||||
|
world->local_player.pitch = world->pointer_motion.pitch;
|
||||||
|
merged.look_dx = 0;
|
||||||
|
merged.look_dy = 0;
|
||||||
|
|
||||||
pxl8_sim_world sim = make_sim_world(world, world->local_player.pos);
|
pxl8_sim_world sim = make_sim_world(world, world->local_player.pos);
|
||||||
pxl8_sim_move_player(&world->local_player, &merged, &sim, dt);
|
pxl8_sim_move_player(&world->local_player, &merged, &sim, dt);
|
||||||
|
|
||||||
|
|
@ -637,7 +571,6 @@ static void pxl8_world_sim_tick(pxl8_world* world, f32 dt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
world->client_tick++;
|
world->client_tick++;
|
||||||
(void)has_input;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pxl8_world_swap_buffers(pxl8_world* world) {
|
static void pxl8_world_swap_buffers(pxl8_world* world) {
|
||||||
|
|
@ -737,6 +670,12 @@ void pxl8_world_pause_sim(pxl8_world* world, bool paused) {
|
||||||
void pxl8_world_push_input(pxl8_world* world, const pxl8_input_msg* input) {
|
void pxl8_world_push_input(pxl8_world* world, const pxl8_input_msg* input) {
|
||||||
if (!world || !input) return;
|
if (!world || !input) return;
|
||||||
|
|
||||||
|
world->pointer_motion.yaw -= input->look_dx * 0.008f;
|
||||||
|
f32 pitch = world->pointer_motion.pitch - input->look_dy * 0.008f;
|
||||||
|
if (pitch > PXL8_SIM_MAX_PITCH) pitch = PXL8_SIM_MAX_PITCH;
|
||||||
|
if (pitch < -PXL8_SIM_MAX_PITCH) pitch = -PXL8_SIM_MAX_PITCH;
|
||||||
|
world->pointer_motion.pitch = pitch;
|
||||||
|
|
||||||
pxl8_input_msg* copy = pxl8_malloc(sizeof(pxl8_input_msg));
|
pxl8_input_msg* copy = pxl8_malloc(sizeof(pxl8_input_msg));
|
||||||
if (copy) {
|
if (copy) {
|
||||||
*copy = *input;
|
*copy = *input;
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,6 @@ void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos);
|
||||||
void pxl8_world_sync(pxl8_world* world, pxl8_net* net);
|
void pxl8_world_sync(pxl8_world* world, pxl8_net* net);
|
||||||
|
|
||||||
void pxl8_world_set_bsp_material(pxl8_world* world, u16 material_id, const pxl8_gfx_material* material);
|
void pxl8_world_set_bsp_material(pxl8_world* world, u16 material_id, const pxl8_gfx_material* material);
|
||||||
void pxl8_world_set_wireframe(pxl8_world* world, bool enabled);
|
|
||||||
|
|
||||||
i32 pxl8_world_get_render_distance(const pxl8_world* world);
|
i32 pxl8_world_get_render_distance(const pxl8_world* world);
|
||||||
void pxl8_world_set_render_distance(pxl8_world* world, i32 distance);
|
void pxl8_world_set_render_distance(pxl8_world* world, i32 distance);
|
||||||
|
|
|
||||||
|
|
@ -409,8 +409,7 @@ pxl8_result pxl8_world_chunk_cache_receive(pxl8_world_chunk_cache* cache,
|
||||||
(hdr->chunk_type == PXL8_CHUNK_TYPE_BSP && a->id != hdr->id) ||
|
(hdr->chunk_type == PXL8_CHUNK_TYPE_BSP && a->id != hdr->id) ||
|
||||||
(hdr->chunk_type == PXL8_CHUNK_TYPE_VXL &&
|
(hdr->chunk_type == PXL8_CHUNK_TYPE_VXL &&
|
||||||
(a->cx != hdr->cx || a->cy != hdr->cy || a->cz != hdr->cz)) ||
|
(a->cx != hdr->cx || a->cy != hdr->cy || a->cz != hdr->cz)) ||
|
||||||
a->version != hdr->version ||
|
a->version != hdr->version;
|
||||||
hdr->fragment_idx == 0;
|
|
||||||
|
|
||||||
if (new_assembly) {
|
if (new_assembly) {
|
||||||
assembly_init(a, hdr);
|
assembly_init(a, hdr);
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define PXL8_WORLD_CHUNK_CACHE_SIZE 512
|
#define PXL8_WORLD_CHUNK_CACHE_SIZE 512
|
||||||
#define PXL8_WORLD_CHUNK_MAX_FRAGMENTS 64
|
#define PXL8_WORLD_CHUNK_MAX_FRAGMENTS 255
|
||||||
#define PXL8_WORLD_CHUNK_MAX_DATA_SIZE 131072
|
#define PXL8_WORLD_CHUNK_MAX_DATA_SIZE 131072
|
||||||
|
|
||||||
typedef struct pxl8_world_chunk_cache_entry {
|
typedef struct pxl8_world_chunk_cache_entry {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue