add proper fnl modules to demo

This commit is contained in:
asrael 2025-10-06 18:14:07 -05:00
parent 9bb9fa5f5b
commit 47c4f2045c
14 changed files with 510 additions and 240 deletions

View file

@ -1,151 +0,0 @@
(local pxl8 (require :pxl8))
(var angle-x 0)
(var angle-y 0)
(var angle-z 0)
(var auto-rotate true)
(var orthographic true)
(var wireframe true)
(var time 0)
(var zoom 5.0)
(var texture-id nil)
(var use-texture false)
(var affine false)
(var texture-initialized false)
(fn init-texture []
(when (not texture-initialized)
(pxl8.load_palette "sprites/pxl8_logo.ase")
(set texture-id (pxl8.load_sprite "sprites/pxl8_logo.ase"))
(pxl8.upload_atlas)
(set texture-initialized true)))
(fn make-cube-vertices []
[[-1 -1 -1] [1 -1 -1] [1 1 -1] [-1 1 -1]
[-1 -1 1] [1 -1 1] [1 1 1] [-1 1 1]])
(fn make-cube-faces []
[[0 1 2] [0 2 3]
[1 5 6] [1 6 2]
[5 4 7] [5 7 6]
[4 0 3] [4 3 7]
[3 2 6] [3 6 7]
[4 5 1] [4 1 0]])
(fn make-cube-faces-with-uvs []
[{:tri [0 1 2] :uvs [[0 0] [1 0] [1 1]]}
{:tri [0 2 3] :uvs [[0 0] [1 1] [0 1]]}
{:tri [1 5 6] :uvs [[0 0] [1 0] [1 1]]}
{:tri [1 6 2] :uvs [[0 0] [1 1] [0 1]]}
{:tri [5 4 7] :uvs [[0 0] [1 0] [1 1]]}
{:tri [5 7 6] :uvs [[0 0] [1 1] [0 1]]}
{:tri [4 0 3] :uvs [[0 0] [1 0] [1 1]]}
{:tri [4 3 7] :uvs [[0 0] [1 1] [0 1]]}
{:tri [3 2 6] :uvs [[0 0] [1 0] [1 1]]}
{:tri [3 6 7] :uvs [[0 0] [1 1] [0 1]]}
{:tri [4 5 1] :uvs [[0 0] [1 0] [1 1]]}
{:tri [4 1 0] :uvs [[0 0] [1 1] [0 1]]}])
(fn get-face-color [face-idx]
(let [colors [12 22 30 16 28 20]]
(. colors (+ 1 (% face-idx 6)))))
(fn cube-update [dt]
(set time (+ time dt))
(when (pxl8.key_down "w")
(set angle-x (- angle-x (* dt 2.0))))
(when (pxl8.key_down "s")
(set angle-x (+ angle-x (* dt 2.0))))
(when (pxl8.key_down "a")
(set angle-y (- angle-y (* dt 2.0))))
(when (pxl8.key_down "d")
(set angle-y (+ angle-y (* dt 2.0))))
(when (pxl8.key_down "q")
(set angle-z (- angle-z (* dt 2.0))))
(when (pxl8.key_down "e")
(set angle-z (+ angle-z (* dt 2.0))))
(when (pxl8.key_pressed " ")
(set wireframe (not wireframe)))
(when (pxl8.key_pressed "f")
(set affine (not affine)))
(when (pxl8.key_pressed "p")
(set orthographic (not orthographic)))
(when (pxl8.key_pressed "r")
(set auto-rotate (not auto-rotate)))
(when (pxl8.key_pressed "t")
(set use-texture (not use-texture))
(when use-texture
(init-texture)))
(when (pxl8.key_down "=")
(set zoom (- zoom (* dt 2.0))))
(when (pxl8.key_down "-")
(set zoom (+ zoom (* dt 2.0))))
(set zoom (math.max 1.0 (math.min zoom 20.0)))
(when auto-rotate
(set angle-x (+ angle-x (* dt 0.7)))
(set angle-y (+ angle-y (* dt 0.5)))
(set angle-z (+ angle-z (* dt 0.3)))))
(fn cube-frame []
(pxl8.clr 0)
(pxl8.clear_zbuffer)
(pxl8.set_affine_textures affine)
(pxl8.set_backface_culling true)
(pxl8.set_wireframe wireframe)
(if orthographic
(let [size (* 2.5 (/ zoom 5.0))
aspect (/ (pxl8.get_width) (pxl8.get_height))
w (* size aspect)
h size]
(pxl8.set_projection (pxl8.mat4_ortho (- w) w (- h) h 1.0 50.0)))
(let [aspect (/ (pxl8.get_width) (pxl8.get_height))
fov (/ 3.14159 (+ 2.0 (/ zoom 5.0)))]
(pxl8.set_projection (pxl8.mat4_perspective fov aspect 1.0 50.0))))
(pxl8.set_view (pxl8.mat4_lookat [0 0 zoom] [0 0 0] [0 1 0]))
(let [model (-> (pxl8.mat4_identity)
(pxl8.mat4_multiply (pxl8.mat4_rotate_x angle-x))
(pxl8.mat4_multiply (pxl8.mat4_rotate_y angle-y))
(pxl8.mat4_multiply (pxl8.mat4_rotate_z angle-z)))]
(pxl8.set_model model))
(let [vertices (make-cube-vertices)]
(if (and use-texture texture-id)
(let [faces (make-cube-faces-with-uvs)]
(each [i face-data (ipairs faces)]
(let [tri-indices face-data.tri
tri-uvs face-data.uvs
v0 (. vertices (+ 1 (. tri-indices 1)))
v1 (. vertices (+ 1 (. tri-indices 2)))
v2 (. vertices (+ 1 (. tri-indices 3)))
uv0 (. tri-uvs 1)
uv1 (. tri-uvs 2)
uv2 (. tri-uvs 3)]
(pxl8.draw_triangle_3d_textured
v0 v1 v2
uv0 uv1 uv2
texture-id))))
(let [faces (make-cube-faces)]
(each [i face (ipairs faces)]
(let [[i0 i1 i2] face
v0 (. vertices (+ 1 i0))
v1 (. vertices (+ 1 i1))
v2 (. vertices (+ 1 i2))
color (get-face-color (math.floor (/ (- i 1) 2)))]
(pxl8.draw_triangle_3d v0 v1 v2 color))))))
(pxl8.text "WASD/QE: Rotate | +/-: Zoom | Space: Wire | R: Auto | P: Proj" 5 5 15)
(pxl8.text (.. "T: Texture | F: Affine | Mode: "
(if wireframe "Wire" "Fill")
" | Texture: " (if use-texture "On" "Off")
" | Mapping: " (if affine "Affine" "Persp")) 5 15 15))
{:update cube-update
:frame cube-frame}

View file

@ -1,5 +1,5 @@
(local pxl8 (require :pxl8))
(local cube3d (fennel.dofile "cube3d.fnl"))
(local cube3d (require :mod.cube3d))
(var time 0)
(var current-effect 1)

213
demo/mod/cube3d.fnl Normal file
View file

@ -0,0 +1,213 @@
(local pxl8 (require :pxl8))
(local debug-ui (require :mod.debug_ui))
(var angle-x 0)
(var angle-y 0)
(var angle-z 0)
(var auto-rotate true)
(var orthographic true)
(var wireframe true)
(var time 0)
(var zoom 5.0)
(var texture-id nil)
(var use-texture false)
(var affine false)
(var texture-initialized false)
(var cam-x 0)
(var cam-y 2)
(var cam-z 12)
(var cam-yaw 0)
(var cam-pitch -0.2)
(var show-debug-ui false)
(var fps 0)
(var fps-timer 0)
(var fps-accumulator 0)
(var fps-frame-count 0)
(fn init-texture []
(when (not texture-initialized)
(pxl8.load_palette "sprites/pxl8_logo.ase")
(set texture-id (pxl8.load_sprite "sprites/pxl8_logo.ase"))
(pxl8.upload_atlas)
(set texture-initialized true)))
(fn make-cube-vertices []
[[-1 -1 -1] [1 -1 -1] [1 1 -1] [-1 1 -1]
[-1 -1 1] [1 -1 1] [1 1 1] [-1 1 1]])
(fn make-cube-faces []
[[0 1 2] [0 2 3]
[1 5 6] [1 6 2]
[5 4 7] [5 7 6]
[4 0 3] [4 3 7]
[3 2 6] [3 6 7]
[4 5 1] [4 1 0]])
(fn make-cube-faces-with-uvs []
[{:tri [0 1 2] :uvs [[0 0] [1 0] [1 1]]}
{:tri [0 2 3] :uvs [[0 0] [1 1] [0 1]]}
{:tri [1 5 6] :uvs [[0 0] [1 0] [1 1]]}
{:tri [1 6 2] :uvs [[0 0] [1 1] [0 1]]}
{:tri [5 4 7] :uvs [[0 0] [1 0] [1 1]]}
{:tri [5 7 6] :uvs [[0 0] [1 1] [0 1]]}
{:tri [4 0 3] :uvs [[0 0] [1 0] [1 1]]}
{:tri [4 3 7] :uvs [[0 0] [1 1] [0 1]]}
{:tri [3 2 6] :uvs [[0 0] [1 0] [1 1]]}
{:tri [3 6 7] :uvs [[0 0] [1 1] [0 1]]}
{:tri [4 5 1] :uvs [[0 0] [1 0] [1 1]]}
{:tri [4 1 0] :uvs [[0 0] [1 1] [0 1]]}])
(fn get-face-color [face-idx]
(let [colors [12 22 30 16 28 20]]
(. colors (+ 1 (% face-idx 6)))))
(fn update [dt]
(set time (+ time dt))
(set fps-accumulator (+ fps-accumulator dt))
(set fps-frame-count (+ fps-frame-count 1))
(when (>= fps-accumulator 0.25)
(set fps (/ fps-frame-count fps-accumulator))
(set fps-accumulator 0)
(set fps-frame-count 0))
(let [wheel-y (pxl8.mouse_wheel_y)
zoom-speed 0.5]
(when (not= wheel-y 0)
(let [forward-x (* (math.sin cam-yaw) wheel-y zoom-speed)
forward-z (* (math.cos cam-yaw) wheel-y zoom-speed)]
(set cam-x (+ cam-x forward-x))
(set cam-z (- cam-z forward-z)))))
(let [move-speed 5.0
rot-speed 2.0
forward-x (* (math.sin cam-yaw) move-speed dt)
forward-z (* (math.cos cam-yaw) move-speed dt)
right-x (* (math.cos cam-yaw) move-speed dt)
right-z (* (- (math.sin cam-yaw)) move-speed dt)]
(when (pxl8.key_down "w")
(set cam-x (+ cam-x forward-x))
(set cam-z (- cam-z forward-z)))
(when (pxl8.key_down "s")
(set cam-x (- cam-x forward-x))
(set cam-z (+ cam-z forward-z)))
(when (pxl8.key_down "a")
(set cam-x (- cam-x right-x))
(set cam-z (- cam-z right-z)))
(when (pxl8.key_down "d")
(set cam-x (+ cam-x right-x))
(set cam-z (+ cam-z right-z)))
(when (pxl8.key_down "q")
(set cam-y (- cam-y (* move-speed dt))))
(when (pxl8.key_down "e")
(set cam-y (+ cam-y (* move-speed dt))))
(when (pxl8.key_down "left")
(set cam-yaw (- cam-yaw (* rot-speed dt))))
(when (pxl8.key_down "right")
(set cam-yaw (+ cam-yaw (* rot-speed dt))))
(when (pxl8.key_down "up")
(set cam-pitch (+ cam-pitch (* rot-speed dt))))
(when (pxl8.key_down "down")
(set cam-pitch (- cam-pitch (* rot-speed dt))))
(set cam-pitch (math.max -1.5 (math.min cam-pitch 1.5))))
(when (pxl8.key_pressed " ")
(set wireframe (not wireframe)))
(when (pxl8.key_pressed "f")
(set affine (not affine)))
(when (pxl8.key_pressed "p")
(set orthographic (not orthographic)))
(when (pxl8.key_pressed "r")
(set auto-rotate (not auto-rotate)))
(when (pxl8.key_pressed "t")
(set use-texture (not use-texture))
(when use-texture
(init-texture)))
(when (pxl8.key_pressed "F8")
(set show-debug-ui (not show-debug-ui))
(pxl8.ui_window_set_open "Debug Menu (F8)" show-debug-ui))
(when auto-rotate
(set angle-x (+ angle-x (* dt 0.7)))
(set angle-y (+ angle-y (* dt 0.5)))
(set angle-z (+ angle-z (* dt 0.3)))))
(fn frame []
(pxl8.clr 0)
(pxl8.clear_zbuffer)
(pxl8.set_affine_textures affine)
(pxl8.set_backface_culling true)
(pxl8.set_wireframe wireframe)
(if orthographic
(let [size 2.5
aspect (/ (pxl8.get_width) (pxl8.get_height))
w (* size aspect)
h size]
(pxl8.set_projection (pxl8.mat4_ortho (- w) w (- h) h 1.0 100.0)))
(let [aspect (/ (pxl8.get_width) (pxl8.get_height))
fov (* (/ 60.0 180.0) 3.14159)]
(pxl8.set_projection (pxl8.mat4_perspective fov aspect 0.1 100.0))))
(let [target-x (* (math.sin cam-yaw) (math.cos cam-pitch))
target-y (* (math.sin cam-pitch))
target-z (* (- (math.cos cam-yaw)) (math.cos cam-pitch))
look-x (+ cam-x target-x)
look-y (+ cam-y target-y)
look-z (+ cam-z target-z)]
(pxl8.set_view (pxl8.mat4_lookat [cam-x cam-y cam-z] [look-x look-y look-z] [0 1 0])))
(let [model (-> (pxl8.mat4_identity)
(pxl8.mat4_multiply (pxl8.mat4_rotate_x angle-x))
(pxl8.mat4_multiply (pxl8.mat4_rotate_y angle-y))
(pxl8.mat4_multiply (pxl8.mat4_rotate_z angle-z)))]
(pxl8.set_model model))
(let [vertices (make-cube-vertices)]
(if (and use-texture texture-id)
(let [faces (make-cube-faces-with-uvs)]
(each [_i face-data (ipairs faces)]
(let [tri-indices face-data.tri
tri-uvs face-data.uvs
v0 (. vertices (+ 1 (. tri-indices 1)))
v1 (. vertices (+ 1 (. tri-indices 2)))
v2 (. vertices (+ 1 (. tri-indices 3)))
uv0 (. tri-uvs 1)
uv1 (. tri-uvs 2)
uv2 (. tri-uvs 3)]
(pxl8.draw_triangle_3d_textured
v0 v1 v2
uv0 uv1 uv2
texture-id))))
(let [faces (make-cube-faces)]
(each [i face (ipairs faces)]
(let [[i0 i1 i2] face
v0 (. vertices (+ 1 i0))
v1 (. vertices (+ 1 i1))
v2 (. vertices (+ 1 i2))
color (get-face-color (math.floor (/ (- i 1) 2)))]
(pxl8.draw_triangle_3d v0 v1 v2 color))))))
(let [new-state (debug-ui.render {:show-debug-ui show-debug-ui
:fps fps
:wireframe wireframe
:auto-rotate auto-rotate
:orthographic orthographic
:use-texture use-texture
:affine affine
:init-texture init-texture})]
(when (not= new-state.show-debug-ui nil) (set show-debug-ui new-state.show-debug-ui))
(when (not= new-state.wireframe nil) (set wireframe new-state.wireframe))
(when (not= new-state.auto-rotate nil) (set auto-rotate new-state.auto-rotate))
(when (not= new-state.orthographic nil) (set orthographic new-state.orthographic))
(when (not= new-state.use-texture nil)
(set use-texture new-state.use-texture)
(when use-texture (init-texture)))
(when (not= new-state.affine nil) (set affine new-state.affine))))
{: update
: frame}

32
demo/mod/debug_ui.fnl Normal file
View file

@ -0,0 +1,32 @@
(local pxl8 (require :pxl8))
(fn render [state]
(var new-state {})
(when state.show-debug-ui
(let [window-h (if state.use-texture 210 180)
window-open (pxl8.ui_window_begin "Debug Menu (F8)" 10 10 250 window-h)]
(when window-open
(pxl8.ui_layout_row 1 0 0)
(pxl8.ui_label (string.format "FPS: %.0f" (or state.fps 0)))
(let [(changed new-val) (pxl8.ui_checkbox "Wireframe" state.wireframe)]
(when changed (set new-state.wireframe new-val)))
(let [(changed new-val) (pxl8.ui_checkbox "Auto-rotate" state.auto-rotate)]
(when changed (set new-state.auto-rotate new-val)))
(let [(changed new-val) (pxl8.ui_checkbox "Orthographic" state.orthographic)]
(when changed (set new-state.orthographic new-val)))
(let [(changed new-val) (pxl8.ui_checkbox "Texture" state.use-texture)]
(when changed
(set new-state.use-texture new-val)
(when (and new-val state.init-texture)
(state.init-texture))))
(when state.use-texture
(pxl8.ui_indent 20)
(let [(changed new-val) (pxl8.ui_checkbox "Affine mapping" state.affine)]
(when changed (set new-state.affine new-val)))
(pxl8.ui_indent -20))
(pxl8.ui_window_end))
(when (not window-open)
(set new-state.show-debug-ui false))))
new-state)
{: render}

View file

@ -1,20 +0,0 @@
(local pxl8 (require :pxl8))
(var button-clicks 0)
(global init (fn []
(pxl8.load_palette "palettes/gruvbox.ase")))
(global update (fn [_dt]))
(global frame (fn []
(pxl8.clr 1)
(when pxl8.ui
(when (pxl8.ui_window_begin "UI Demo" 20 20 280 150)
(pxl8.ui_layout_row 1 0 0)
(pxl8.ui_label "Welcome to some window UI!")
(pxl8.ui_label (.. "Clicks: " button-clicks))
(when (pxl8.ui_button "Click me!")
(set button-clicks (+ button-clicks 1)))
(pxl8.ui_window_end)))))

View file

@ -107,19 +107,33 @@ function pxl8.trace(msg)
end
function pxl8.key_down(key)
if type(key) == "string" then
key = string.byte(key)
end
return C.pxl8_key_down(input, key)
end
function pxl8.key_pressed(key)
if type(key) == "string" then
key = string.byte(key)
end
return C.pxl8_key_pressed(input, key)
end
function pxl8.key_released(key)
return C.pxl8_key_released(input, key)
end
function pxl8.mouse_wheel_x()
return C.pxl8_mouse_wheel_x(input)
end
function pxl8.mouse_wheel_y()
return C.pxl8_mouse_wheel_y(input)
end
function pxl8.mouse_x()
return C.pxl8_mouse_x(input)
end
function pxl8.mouse_y()
return C.pxl8_mouse_y(input)
end
function pxl8.vfx_raster_bars(bars, time)
local c_bars = ffi.new("pxl8_raster_bar[?]", #bars)
for i, bar in ipairs(bars) do
@ -363,19 +377,20 @@ function pxl8.bounds(x, y, w, h)
return ffi.new("pxl8_bounds", {x = x, y = y, w = w, h = h})
end
function pxl8.ui_window_begin(title, x, y, w, h, options)
local rect = ffi.new("pxl8_bounds", {x = x, y = y, w = w, h = h})
return C.pxl8_ui_window_begin(_pxl8_ui, title, rect, options or 0)
end
function pxl8.ui_window_end()
C.pxl8_ui_window_end(_pxl8_ui)
end
function pxl8.ui_button(label)
return C.pxl8_ui_button(_pxl8_ui, label)
end
function pxl8.ui_checkbox(label, state)
local state_ptr = ffi.new("bool[1]", state)
local changed = C.pxl8_ui_checkbox(_pxl8_ui, label, state_ptr)
return changed, state_ptr[0]
end
function pxl8.ui_indent(amount)
C.pxl8_ui_indent(_pxl8_ui, amount)
end
function pxl8.ui_label(text)
C.pxl8_ui_label(_pxl8_ui, text)
end
@ -390,4 +405,17 @@ function pxl8.ui_layout_row(item_count, widths, height)
C.pxl8_ui_layout_row(_pxl8_ui, item_count, widths_array, height)
end
function pxl8.ui_window_begin(title, x, y, w, h, options)
local rect = ffi.new("pxl8_bounds", {x = x, y = y, w = w, h = h})
return C.pxl8_ui_window_begin(_pxl8_ui, title, rect, options or 0)
end
function pxl8.ui_window_end()
C.pxl8_ui_window_end(_pxl8_ui)
end
function pxl8.ui_window_set_open(title, open)
C.pxl8_ui_window_set_open(_pxl8_ui, title, open)
end
return pxl8

View file

@ -44,8 +44,6 @@ typedef struct pxl8_state {
pxl8_script* script;
pxl8_ui* ui;
f32 current_fps;
f32 fps_timer;
i32 frame_count;
u64 last_time;
f32 time;
@ -53,7 +51,6 @@ typedef struct pxl8_state {
bool repl_mode;
bool running;
bool script_loaded;
bool show_fps;
char script_path[256];
pxl8_input_state input;
@ -319,16 +316,8 @@ SDL_AppResult SDL_AppIterate(void* appstate) {
u64 current_time = SDL_GetTicksNS();
f32 dt = (f32)(current_time - app->last_time) / 1000000000.0f;
app->frame_count++;
app->fps_timer += dt;
app->last_time = current_time;
app->time += dt;
if (app->fps_timer >= 1.0f) {
app->current_fps = app->frame_count / app->fps_timer;
app->frame_count = 0;
app->fps_timer = 0.0f;
}
pxl8_script_check_reload(app->script);
@ -344,23 +333,24 @@ SDL_AppResult SDL_AppIterate(void* appstate) {
}
if (app->ui) {
pxl8_ui_input_mousemove(app->ui, app->input.mouse_x, app->input.mouse_y);
pxl8_ui_frame_begin(app->ui);
for (i32 i = 0; i < 3; i++) {
if (app->input.mouse_buttons[i] && app->input.mouse_buttons_pressed[i]) {
if (app->input.mouse_buttons_pressed[i]) {
pxl8_ui_input_mousedown(app->ui, app->input.mouse_x, app->input.mouse_y, i + 1);
}
if (!app->input.mouse_buttons[i]) {
if (app->input.mouse_buttons_released[i]) {
pxl8_ui_input_mouseup(app->ui, app->input.mouse_x, app->input.mouse_y, i + 1);
}
}
pxl8_ui_input_mousemove(app->ui, app->input.mouse_x, app->input.mouse_y);
for (i32 key = 0; key < 256; key++) {
if (app->input.keys_pressed[key]) {
pxl8_ui_input_keydown(app->ui, key);
}
if (!app->input.keys[key] && app->input.keys_pressed[key]) {
if (!app->input.keys_down[key] && app->input.keys_pressed[key]) {
pxl8_ui_input_keyup(app->ui, key);
}
}
@ -390,12 +380,6 @@ SDL_AppResult SDL_AppIterate(void* appstate) {
i32 render_width, render_height;
pxl8_gfx_get_resolution_dimensions(app->resolution, &render_width, &render_height);
if (app->show_fps && app->current_fps > 0.0f) {
char fps_text[32];
SDL_snprintf(fps_text, sizeof(fps_text), "FPS: %d", (i32)(app->current_fps + 0.5f));
pxl8_text(app->gfx, fps_text, render_width - 80, 10, 15);
}
if (app->ui) {
pxl8_ui_frame_end(app->ui);
}
@ -406,7 +390,9 @@ SDL_AppResult SDL_AppIterate(void* appstate) {
pxl8_gfx_present(app->gfx);
SDL_memset(app->input.keys_pressed, 0, sizeof(app->input.keys_pressed));
SDL_memset(app->input.keys_released, 0, sizeof(app->input.keys_released));
SDL_memset(app->input.mouse_buttons_pressed, 0, sizeof(app->input.mouse_buttons_pressed));
SDL_memset(app->input.mouse_buttons_released, 0, sizeof(app->input.mouse_buttons_released));
app->input.mouse_wheel_x = 0;
app->input.mouse_wheel_y = 0;
@ -427,25 +413,23 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) {
return SDL_APP_SUCCESS;
}
if (event->key.key == SDLK_F3) {
app->show_fps = !app->show_fps;
}
SDL_Keycode key = event->key.key;
if (key < 256) {
if (!app->input.keys[key]) {
app->input.keys_pressed[key] = true;
SDL_Scancode scancode = event->key.scancode;
if (scancode < 256) {
if (!app->input.keys_down[scancode]) {
app->input.keys_pressed[scancode] = true;
}
app->input.keys[key] = true;
app->input.keys_down[scancode] = true;
app->input.keys_released[scancode] = false;
}
break;
}
case SDL_EVENT_KEY_UP: {
SDL_Keycode key = event->key.key;
if (key < 256) {
app->input.keys[key] = false;
app->input.keys_pressed[key] = false;
SDL_Scancode scancode = event->key.scancode;
if (scancode < 256) {
app->input.keys_down[scancode] = false;
app->input.keys_pressed[scancode] = false;
app->input.keys_released[scancode] = true;
}
break;
}
@ -453,10 +437,11 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) {
case SDL_EVENT_MOUSE_BUTTON_DOWN: {
u8 button = event->button.button - 1;
if (button < 3) {
if (!app->input.mouse_buttons[button]) {
if (!app->input.mouse_buttons_down[button]) {
app->input.mouse_buttons_pressed[button] = true;
}
app->input.mouse_buttons[button] = true;
app->input.mouse_buttons_down[button] = true;
app->input.mouse_buttons_released[button] = false;
}
break;
}
@ -464,7 +449,9 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) {
case SDL_EVENT_MOUSE_BUTTON_UP: {
u8 button = event->button.button - 1;
if (button < 3) {
app->input.mouse_buttons[button] = false;
app->input.mouse_buttons_down[button] = false;
app->input.mouse_buttons_pressed[button] = false;
app->input.mouse_buttons_released[button] = true;
}
break;
}

View file

@ -1,3 +1,4 @@
#include <ctype.h>
#include <SDL3/SDL.h>
#include "pxl8_io.h"
@ -97,12 +98,50 @@ void pxl8_io_free_binary_data(u8* data) {
}
}
bool pxl8_key_down(const pxl8_input_state* input, i32 key) {
if (!input || key < 0 || key >= 256) return false;
return input->keys[key];
static i32 pxl8_key_code(const char* key_name) {
if (!key_name || !key_name[0]) return 0;
SDL_Scancode scancode = SDL_GetScancodeFromName(key_name);
return (i32)scancode;
}
bool pxl8_key_pressed(const pxl8_input_state* input, i32 key) {
if (!input || key < 0 || key >= 256) return false;
bool pxl8_key_down(const pxl8_input_state* input, const char* key_name) {
if (!input) return false;
i32 key = pxl8_key_code(key_name);
if (key < 0 || key >= 256) return false;
return input->keys_down[key];
}
bool pxl8_key_pressed(const pxl8_input_state* input, const char* key_name) {
if (!input) return false;
i32 key = pxl8_key_code(key_name);
if (key < 0 || key >= 256) return false;
return input->keys_pressed[key];
}
bool pxl8_key_released(const pxl8_input_state* input, const char* key_name) {
if (!input) return false;
i32 key = pxl8_key_code(key_name);
if (key < 0 || key >= 256) return false;
return input->keys_released[key];
}
i32 pxl8_mouse_wheel_x(const pxl8_input_state* input) {
if (!input) return 0;
return input->mouse_wheel_x;
}
i32 pxl8_mouse_wheel_y(const pxl8_input_state* input) {
if (!input) return 0;
return input->mouse_wheel_y;
}
i32 pxl8_mouse_x(const pxl8_input_state* input) {
if (!input) return 0;
return input->mouse_x;
}
i32 pxl8_mouse_y(const pxl8_input_state* input) {
if (!input) return 0;
return input->mouse_y;
}

View file

@ -19,8 +19,14 @@ pxl8_result pxl8_io_read_file(const char* path, char** content, size_t* size);
pxl8_result pxl8_io_write_binary_file(const char* path, const u8* data, size_t size);
pxl8_result pxl8_io_write_file(const char* path, const char* content, size_t size);
bool pxl8_key_down(const pxl8_input_state* input, i32 key);
bool pxl8_key_pressed(const pxl8_input_state* input, i32 key);
bool pxl8_key_down(const pxl8_input_state* input, const char* key_name);
bool pxl8_key_pressed(const pxl8_input_state* input, const char* key_name);
bool pxl8_key_released(const pxl8_input_state* input, const char* key_name);
i32 pxl8_mouse_wheel_x(const pxl8_input_state* input);
i32 pxl8_mouse_wheel_y(const pxl8_input_state* input);
i32 pxl8_mouse_x(const pxl8_input_state* input);
i32 pxl8_mouse_y(const pxl8_input_state* input);
#ifdef __cplusplus
}

View file

@ -59,8 +59,13 @@ static const char* pxl8_ffi_cdefs =
"i32 pxl8_gfx_create_texture(pxl8_gfx* ctx, const u8* pixels, u32 width, u32 height);\n"
"void pxl8_gfx_upload_atlas(pxl8_gfx* ctx);\n"
"typedef struct pxl8_input_state pxl8_input_state;\n"
"bool pxl8_key_down(const pxl8_input_state* input, i32 key);\n"
"bool pxl8_key_pressed(const pxl8_input_state* input, i32 key);\n"
"bool pxl8_key_down(const pxl8_input_state* input, const char* key_name);\n"
"bool pxl8_key_pressed(const pxl8_input_state* input, const char* key_name);\n"
"bool pxl8_key_released(const pxl8_input_state* input, const char* key_name);\n"
"int pxl8_mouse_wheel_x(const pxl8_input_state* input);\n"
"int pxl8_mouse_wheel_y(const pxl8_input_state* input);\n"
"int pxl8_mouse_x(const pxl8_input_state* input);\n"
"int pxl8_mouse_y(const pxl8_input_state* input);\n"
"void pxl8_lua_debug(const char* msg);\n"
"void pxl8_lua_error(const char* msg);\n"
"void pxl8_lua_info(const char* msg);\n"
@ -168,12 +173,15 @@ static const char* pxl8_ffi_cdefs =
"void pxl8_ui_input_scroll(pxl8_ui* ui, int x, int y);\n"
"void pxl8_ui_input_text(pxl8_ui* ui, const char* text);\n"
"bool pxl8_ui_button(pxl8_ui* ui, const char* label);\n"
"bool pxl8_ui_checkbox(pxl8_ui* ui, const char* label, bool* state);\n"
"void pxl8_ui_indent(pxl8_ui* ui, int amount);\n"
"void pxl8_ui_label(pxl8_ui* ui, const char* text);\n"
"void pxl8_ui_layout_row(pxl8_ui* ui, int item_count, const int* widths, int height);\n"
"int pxl8_ui_menu(pxl8_ui* ui, pxl8_menu_item* items, int item_count);\n"
"void pxl8_ui_panel(pxl8_ui* ui, pxl8_bounds rect, pxl8_frame_theme* theme);\n"
"bool pxl8_ui_window_begin(pxl8_ui* ui, const char* title, pxl8_bounds rect, int options);\n"
"void pxl8_ui_window_end(pxl8_ui* ui);\n"
"void pxl8_ui_window_set_open(pxl8_ui* ui, const char* title, bool open);\n"
"pxl8_frame_theme pxl8_ui_theme_default(void);\n";
void pxl8_lua_info(const char* msg) {
@ -246,6 +254,18 @@ pxl8_script* pxl8_script_create(void) {
if (luaL_dofile(script->L, "lib/fennel/fennel.lua") == 0) {
lua_setglobal(script->L, "fennel");
lua_getglobal(script->L, "fennel");
lua_getfield(script->L, -1, "install");
if (lua_isfunction(script->L, -1)) {
if (lua_pcall(script->L, 0, 0, 0) != 0) {
pxl8_warn("Failed to install fennel searcher: %s", lua_tostring(script->L, -1));
lua_pop(script->L, 1);
}
} else {
lua_pop(script->L, 1);
}
lua_pop(script->L, 1);
}
script->last_error[0] = '\0';

View file

@ -64,11 +64,13 @@ typedef struct pxl8_bounds {
} pxl8_bounds;
typedef struct pxl8_input_state {
bool keys[256];
bool keys_down[256];
bool keys_pressed[256];
bool keys_released[256];
bool mouse_buttons[3];
bool mouse_buttons_down[3];
bool mouse_buttons_pressed[3];
bool mouse_buttons_released[3];
i32 mouse_wheel_x;
i32 mouse_wheel_y;
i32 mouse_x;

View file

@ -38,26 +38,47 @@ static int mu_text_height(mu_Font font) {
return f->default_height;
}
static void pxl8_ui_render_icon(pxl8_gfx* gfx, i32 id, i32 x, i32 y, i32 w, i32 h, u8 color) {
switch (id) {
case 2: {
i32 cx = x + w / 2;
i32 cy = y + h / 2;
i32 size = (w < h ? w : h) / 3;
pxl8_line(gfx, cx - size, cy, cx, cy + size, color);
pxl8_line(gfx, cx, cy + size, cx + size, cy - size, color);
break;
}
case 1: {
i32 cx = x + w / 2;
i32 cy = y + h / 2;
i32 size = (w < h ? w : h) / 4;
pxl8_line(gfx, cx - size, cy - size, cx + size, cy + size, color);
pxl8_line(gfx, cx + size, cy - size, cx - size, cy + size, color);
break;
}
}
}
static void pxl8_ui_render_commands(pxl8_ui* ui) {
mu_Command* cmd = NULL;
while (mu_next_command(&ui->mu_ctx, &cmd)) {
switch (cmd->type) {
case MU_COMMAND_RECT: {
mu_RectCommand* rc = (mu_RectCommand*)cmd;
u8 color = rc->color.r;
pxl8_rect_fill(ui->gfx, rc->rect.x, rc->rect.y, rc->rect.w, rc->rect.h, color);
pxl8_rect_fill(ui->gfx, rc->rect.x, rc->rect.y, rc->rect.w, rc->rect.h, rc->color.r);
break;
}
case MU_COMMAND_TEXT: {
mu_TextCommand* tc = (mu_TextCommand*)cmd;
u8 color = tc->color.r;
pxl8_text(ui->gfx, tc->str, tc->pos.x, tc->pos.y, color);
pxl8_text(ui->gfx, tc->str, tc->pos.x, tc->pos.y, tc->color.r);
break;
}
case MU_COMMAND_CLIP: {
break;
}
case MU_COMMAND_ICON: {
mu_IconCommand* ic = (mu_IconCommand*)cmd;
pxl8_ui_render_icon(ui->gfx, ic->id, ic->rect.x, ic->rect.y, ic->rect.w, ic->rect.h, ic->color.r);
break;
}
}
@ -104,6 +125,21 @@ pxl8_ui* pxl8_ui_create(pxl8_gfx* gfx) {
ui->mu_ctx.text_height = mu_text_height;
ui->mu_ctx.text_width = mu_text_width;
ui->mu_ctx.style->colors[0] = (mu_Color){15, 0, 0, 255};
ui->mu_ctx.style->colors[1] = (mu_Color){8, 0, 0, 255};
ui->mu_ctx.style->colors[2] = (mu_Color){1, 0, 0, 255};
ui->mu_ctx.style->colors[3] = (mu_Color){2, 0, 0, 255};
ui->mu_ctx.style->colors[4] = (mu_Color){15, 0, 0, 255};
ui->mu_ctx.style->colors[5] = (mu_Color){0, 0, 0, 0};
ui->mu_ctx.style->colors[6] = (mu_Color){7, 0, 0, 255};
ui->mu_ctx.style->colors[7] = (mu_Color){8, 0, 0, 255};
ui->mu_ctx.style->colors[8] = (mu_Color){10, 0, 0, 255};
ui->mu_ctx.style->colors[9] = (mu_Color){2, 0, 0, 255};
ui->mu_ctx.style->colors[10] = (mu_Color){3, 0, 0, 255};
ui->mu_ctx.style->colors[11] = (mu_Color){10, 0, 0, 255};
ui->mu_ctx.style->colors[12] = (mu_Color){7, 0, 0, 255};
ui->mu_ctx.style->colors[13] = (mu_Color){8, 0, 0, 255};
return ui;
}
@ -163,6 +199,48 @@ bool pxl8_ui_button(pxl8_ui* ui, const char* label) {
return mu_button(&ui->mu_ctx, label) & MU_RES_SUBMIT;
}
bool pxl8_ui_checkbox(pxl8_ui* ui, const char* label, bool* state) {
if (!ui || !state || !label) return false;
mu_Context* ctx = &ui->mu_ctx;
mu_push_id(ctx, label, (int)strlen(label));
mu_Id id = mu_get_id(ctx, label, (int)strlen(label));
mu_Rect r = mu_layout_next(ctx);
mu_Rect box = mu_rect(r.x, r.y, r.h, r.h);
int had_focus_before = (ctx->focus == id);
mu_update_control(ctx, id, r, 0);
int has_focus_after = (ctx->focus == id);
int mouseover = mu_mouse_over(ctx, r);
int res = 0;
int int_state = *state ? 1 : 0;
if (had_focus_before && !has_focus_after && !ctx->mouse_down && mouseover) {
res |= MU_RES_CHANGE;
int_state = !int_state;
}
mu_draw_control_frame(ctx, id, box, MU_COLOR_BASE, 0);
if (int_state) {
mu_draw_icon(ctx, MU_ICON_CHECK, box, ctx->style->colors[MU_COLOR_TEXT]);
}
r = mu_rect(r.x + box.w, r.y, r.w - box.w, r.h);
mu_draw_control_text(ctx, label, r, MU_COLOR_TEXT, 0);
mu_pop_id(ctx);
*state = int_state != 0;
return res & MU_RES_CHANGE;
}
void pxl8_ui_indent(pxl8_ui* ui, i32 amount) {
if (!ui) return;
mu_Layout* layout = &ui->mu_ctx.layout_stack.items[ui->mu_ctx.layout_stack.idx - 1];
layout->indent += amount;
}
void pxl8_ui_label(pxl8_ui* ui, const char* text) {
if (!ui || !text) return;
mu_label(&ui->mu_ctx, text);
@ -205,6 +283,14 @@ void pxl8_ui_window_end(pxl8_ui* ui) {
mu_end_window(&ui->mu_ctx);
}
void pxl8_ui_window_set_open(pxl8_ui* ui, const char* title, bool open) {
if (!ui || !title) return;
mu_Container* win = mu_get_container(&ui->mu_ctx, title);
if (win) {
win->open = open ? 1 : 0;
}
}
pxl8_frame_theme pxl8_ui_theme_default(void) {
pxl8_frame_theme theme = {0};
theme.bg_color = 0;

View file

@ -5,6 +5,23 @@
typedef struct pxl8_ui pxl8_ui;
typedef struct pxl8_ui_theme {
u8 text;
u8 border;
u8 window_bg;
u8 title_bg;
u8 title_text;
u8 panel_bg;
u8 button;
u8 button_hover;
u8 button_focus;
u8 base;
u8 base_hover;
u8 base_focus;
u8 scroll_base;
u8 scroll_thumb;
} pxl8_ui_theme;
typedef struct pxl8_frame_theme {
u8 bg_color;
u32 sprite_id;
@ -44,12 +61,15 @@ void pxl8_ui_input_scroll(pxl8_ui* ui, i32 x, i32 y);
void pxl8_ui_input_text(pxl8_ui* ui, const char* text);
bool pxl8_ui_button(pxl8_ui* ui, const char* label);
bool pxl8_ui_checkbox(pxl8_ui* ui, const char* label, bool* state);
void pxl8_ui_indent(pxl8_ui* ui, i32 amount);
void pxl8_ui_label(pxl8_ui* ui, const char* text);
void pxl8_ui_layout_row(pxl8_ui* ui, i32 item_count, const i32* widths, i32 height);
i32 pxl8_ui_menu(pxl8_ui* ui, pxl8_menu_item* items, i32 item_count);
void pxl8_ui_panel(pxl8_ui* ui, pxl8_bounds rect, pxl8_frame_theme* theme);
bool pxl8_ui_window_begin(pxl8_ui* ui, const char* title, pxl8_bounds rect, i32 options);
void pxl8_ui_window_end(pxl8_ui* ui);
void pxl8_ui_window_set_open(pxl8_ui* ui, const char* title, bool open);
pxl8_frame_theme pxl8_ui_theme_default(void);

View file

@ -30,6 +30,10 @@ typedef struct pxl8_raster_bar {
f32 speed;
} pxl8_raster_bar;
#ifdef __cplusplus
extern "C" {
#endif
pxl8_particles* pxl8_particles_create(u32 max_count);
void pxl8_particles_destroy(pxl8_particles* particles);
@ -51,3 +55,7 @@ void pxl8_vfx_raster_bars(pxl8_gfx* gfx, pxl8_raster_bar* bars, u32 bar_count, f
void pxl8_vfx_rotozoom(pxl8_gfx* gfx, f32 angle, f32 zoom, i32 cx, i32 cy);
void pxl8_vfx_tunnel(pxl8_gfx* gfx, f32 time, f32 speed, f32 twist);
void pxl8_vfx_water_ripple(pxl8_gfx* gfx, f32* height_map, i32 drop_x, i32 drop_y, f32 damping);
#ifdef __cplusplus
}
#endif