refactor some things...
This commit is contained in:
parent
3550fad638
commit
1744e689b5
25 changed files with 2396 additions and 1307 deletions
98
demo/cube3d.fnl
Normal file
98
demo/cube3d.fnl
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
(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)
|
||||
|
||||
(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 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 "r")
|
||||
(set auto-rotate (not auto-rotate)))
|
||||
(when (pxl8.key_pressed "p")
|
||||
(set orthographic (not orthographic)))
|
||||
|
||||
(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_wireframe wireframe)
|
||||
(pxl8.set_backface_culling true)
|
||||
|
||||
(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)
|
||||
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)))))
|
||||
|
||||
{:update cube-update
|
||||
:frame cube-frame}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
(local pxl8 (require :pxl8))
|
||||
(local cube3d (fennel.dofile "cube3d.fnl"))
|
||||
|
||||
(var time 0)
|
||||
(var current-effect 1)
|
||||
|
|
@ -15,8 +16,8 @@
|
|||
|
||||
(global init (fn []
|
||||
(pxl8.load_palette "palettes/gruvbox.ase")
|
||||
(set logo-sprite (pxl8.load_sprite "sprites/pxl8_logo.ase"))))
|
||||
(set particles (pxl8.particles_new 1000))
|
||||
(set logo-sprite (pxl8.load_sprite "sprites/pxl8_logo.ase"))
|
||||
(set particles (pxl8.particles_new 1000))))
|
||||
|
||||
(global update (fn [dt]
|
||||
(set time (+ time dt))
|
||||
|
|
@ -38,20 +39,24 @@
|
|||
(when (pxl8.key_pressed "7")
|
||||
(set current-effect 7)
|
||||
(set snow-init false))
|
||||
(when (pxl8.key_pressed "8")
|
||||
(set current-effect 8))
|
||||
|
||||
(when (= current-effect 1)
|
||||
(set logo-x (+ logo-x (* logo-dx dt)))
|
||||
(set logo-y (+ logo-y (* logo-dy dt)))
|
||||
|
||||
(when (or (< logo-x 0) (> logo-x 512))
|
||||
(set logo-dx (- logo-dx)))
|
||||
(when (or (< logo-y 0) (> logo-y 296))
|
||||
(set logo-dy (- logo-dy))))
|
||||
(case current-effect
|
||||
1 (do
|
||||
(set logo-x (+ logo-x (* logo-dx dt)))
|
||||
(set logo-y (+ logo-y (* logo-dy dt)))
|
||||
(when (or (< logo-x 0) (> logo-x 512))
|
||||
(set logo-dx (- logo-dx)))
|
||||
(when (or (< logo-y 0) (> logo-y 296))
|
||||
(set logo-dy (- logo-dy))))
|
||||
8 (cube3d.update dt)
|
||||
_ nil)
|
||||
|
||||
(when particles
|
||||
(pxl8.particles_update particles dt))))
|
||||
|
||||
(global draw (fn []
|
||||
(global frame (fn []
|
||||
(case current-effect
|
||||
1 (do
|
||||
(pxl8.clr 0)
|
||||
|
|
@ -64,9 +69,9 @@
|
|||
|
||||
4 (do
|
||||
(pxl8.clr 0)
|
||||
(local bars [{:base_y 40 :amplitude 20 :height 16 :speed 2.0 :phase 0 :color 14 :fade_color 11}
|
||||
{:base_y 80 :amplitude 15 :height 16 :speed 2.5 :phase 1.5 :color 20 :fade_color 11}
|
||||
{:base_y 120 :amplitude 25 :height 16 :speed 1.8 :phase 3.0 :color 26 :fade_color 11}])
|
||||
(local bars [{:base_y 60 :amplitude 30 :height 16 :speed 2.0 :phase 0 :color 20 :fade_color 10}
|
||||
{:base_y 180 :amplitude 35 :height 16 :speed 1.8 :phase 2.0 :color 26 :fade_color 10}
|
||||
{:base_y 300 :amplitude 25 :height 16 :speed 2.2 :phase 4.0 :color 14 :fade_color 10}])
|
||||
(pxl8.vfx_raster_bars bars time))
|
||||
|
||||
5 (do
|
||||
|
|
@ -95,5 +100,7 @@
|
|||
(pxl8.vfx_snow particles 320 5.0)
|
||||
(set snow-init true))
|
||||
(pxl8.particles_render particles)))
|
||||
|
||||
|
||||
8 (cube3d.frame)
|
||||
|
||||
_ (pxl8.clr 0))))
|
||||
|
|
|
|||
3
pxl8.sh
3
pxl8.sh
|
|
@ -388,7 +388,8 @@ case "$COMMAND" in
|
|||
src/pxl8_font.c
|
||||
src/pxl8_gfx.c
|
||||
src/pxl8_io.c
|
||||
src/pxl8_lua.c
|
||||
src/pxl8_math.c
|
||||
src/pxl8_script.c
|
||||
src/pxl8_tilemap.c
|
||||
src/pxl8_tilesheet.c
|
||||
src/pxl8_vfx.c
|
||||
|
|
|
|||
117
src/lua/pxl8.lua
117
src/lua/pxl8.lua
|
|
@ -1,7 +1,7 @@
|
|||
local ffi = require("ffi")
|
||||
local C = ffi.C
|
||||
local gfx = _pxl8_gfx_ctx
|
||||
local input = _pxl8_input_ctx
|
||||
local gfx = _pxl8_gfx
|
||||
local input = _pxl8_input
|
||||
|
||||
-- pxl8 lua api
|
||||
--
|
||||
|
|
@ -47,8 +47,12 @@ function pxl8.sprite(id, x, y, w, h, flip_x, flip_y)
|
|||
C.pxl8_sprite(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 pxl8.get_screen()
|
||||
return C.pxl8_get_screen(gfx)
|
||||
function pxl8.get_width()
|
||||
return C.pxl8_gfx_get_width(gfx)
|
||||
end
|
||||
|
||||
function pxl8.get_height()
|
||||
return C.pxl8_gfx_get_height(gfx)
|
||||
end
|
||||
|
||||
function pxl8.load_palette(filepath)
|
||||
|
|
@ -119,8 +123,7 @@ function pxl8.vfx_plasma(time, scale1, scale2, palette_offset)
|
|||
end
|
||||
|
||||
function pxl8.vfx_rotozoom(angle, zoom, cx, cy)
|
||||
local screen = pxl8.get_screen()
|
||||
C.pxl8_vfx_rotozoom(gfx, angle, zoom, cx or screen.width/2, cy or screen.height/2)
|
||||
C.pxl8_vfx_rotozoom(gfx, angle, zoom, cx or pxl8.get_width()/2, cy or pxl8.get_height()/2)
|
||||
end
|
||||
|
||||
function pxl8.vfx_tunnel(time, speed, twist)
|
||||
|
|
@ -128,29 +131,27 @@ function pxl8.vfx_tunnel(time, speed, twist)
|
|||
end
|
||||
|
||||
function pxl8.particles_new(max_count)
|
||||
local ps = ffi.new("pxl8_particle_system")
|
||||
C.pxl8_vfx_particles_init(ps, max_count or 1000)
|
||||
return ps
|
||||
return C.pxl8_particles_create(max_count or 1000)
|
||||
end
|
||||
|
||||
function pxl8.particles_destroy(ps)
|
||||
C.pxl8_vfx_particles_destroy(ps)
|
||||
C.pxl8_particles_destroy(ps)
|
||||
end
|
||||
|
||||
function pxl8.particles_clear(ps)
|
||||
C.pxl8_vfx_particles_clear(ps)
|
||||
C.pxl8_particles_clear(ps)
|
||||
end
|
||||
|
||||
function pxl8.particles_emit(ps, count)
|
||||
C.pxl8_vfx_particles_emit(ps, count or 1)
|
||||
C.pxl8_particles_emit(ps, count or 1)
|
||||
end
|
||||
|
||||
function pxl8.particles_update(ps, dt)
|
||||
C.pxl8_vfx_particles_update(ps, dt)
|
||||
C.pxl8_particles_update(ps, dt)
|
||||
end
|
||||
|
||||
function pxl8.particles_render(ps)
|
||||
C.pxl8_vfx_particles_render(ps, gfx)
|
||||
C.pxl8_particles_render(ps, gfx)
|
||||
end
|
||||
|
||||
function pxl8.vfx_explosion(ps, x, y, color, force)
|
||||
|
|
@ -162,7 +163,7 @@ function pxl8.vfx_fire(ps, x, y, width, palette_start)
|
|||
end
|
||||
|
||||
function pxl8.vfx_rain(ps, width, wind)
|
||||
C.pxl8_vfx_rain(ps, width or pxl8.get_screen().width, wind or 0.0)
|
||||
C.pxl8_vfx_rain(ps, width or pxl8.get_width(), wind or 0.0)
|
||||
end
|
||||
|
||||
function pxl8.vfx_smoke(ps, x, y, color)
|
||||
|
|
@ -170,7 +171,7 @@ function pxl8.vfx_smoke(ps, x, y, color)
|
|||
end
|
||||
|
||||
function pxl8.vfx_snow(ps, width, wind)
|
||||
C.pxl8_vfx_snow(ps, width or pxl8.get_screen().width, wind or 10.0)
|
||||
C.pxl8_vfx_snow(ps, width or pxl8.get_width(), wind or 10.0)
|
||||
end
|
||||
|
||||
function pxl8.vfx_sparks(ps, x, y, color)
|
||||
|
|
@ -190,7 +191,7 @@ function pxl8.gfx_fade_palette(start, count, amount, target_color)
|
|||
end
|
||||
|
||||
function pxl8.tilesheet_new(tile_size)
|
||||
return C.pxl8_tilesheet_new(tile_size or 16)
|
||||
return C.pxl8_tilesheet_create(tile_size or 16)
|
||||
end
|
||||
|
||||
function pxl8.tilesheet_destroy(tilesheet)
|
||||
|
|
@ -202,7 +203,7 @@ function pxl8.tilesheet_load(tilesheet, filepath)
|
|||
end
|
||||
|
||||
function pxl8.tilemap_new(width, height, tile_size)
|
||||
return C.pxl8_tilemap_new(width, height, tile_size or 16)
|
||||
return C.pxl8_tilemap_create(width, height, tile_size or 16)
|
||||
end
|
||||
|
||||
function pxl8.tilemap_destroy(tilemap)
|
||||
|
|
@ -246,6 +247,86 @@ pxl8.TILE_FLIP_Y = 2
|
|||
pxl8.TILE_SOLID = 4
|
||||
pxl8.TILE_TRIGGER = 8
|
||||
|
||||
function pxl8.clear_zbuffer()
|
||||
C.pxl8_3d_clear_zbuffer(gfx)
|
||||
end
|
||||
|
||||
function pxl8.set_model(mat)
|
||||
C.pxl8_3d_set_model(gfx, mat)
|
||||
end
|
||||
|
||||
function pxl8.set_view(mat)
|
||||
C.pxl8_3d_set_view(gfx, mat)
|
||||
end
|
||||
|
||||
function pxl8.set_projection(mat)
|
||||
C.pxl8_3d_set_projection(gfx, mat)
|
||||
end
|
||||
|
||||
function pxl8.set_wireframe(wireframe)
|
||||
C.pxl8_3d_set_wireframe(gfx, wireframe)
|
||||
end
|
||||
|
||||
function pxl8.set_backface_culling(culling)
|
||||
C.pxl8_3d_set_backface_culling(gfx, culling)
|
||||
end
|
||||
|
||||
function pxl8.draw_triangle_3d(v0, v1, v2, color)
|
||||
local vec0 = ffi.new("pxl8_vec3", {x = v0[1], y = v0[2], z = v0[3]})
|
||||
local vec1 = ffi.new("pxl8_vec3", {x = v1[1], y = v1[2], z = v1[3]})
|
||||
local vec2 = ffi.new("pxl8_vec3", {x = v2[1], y = v2[2], z = v2[3]})
|
||||
C.pxl8_3d_draw_triangle_raw(gfx, vec0, vec1, vec2, color)
|
||||
end
|
||||
|
||||
function pxl8.draw_line_3d(p0, p1, color)
|
||||
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]})
|
||||
C.pxl8_3d_draw_line_3d(gfx, vec0, vec1, color)
|
||||
end
|
||||
|
||||
function pxl8.mat4_identity()
|
||||
return C.pxl8_mat4_identity()
|
||||
end
|
||||
|
||||
function pxl8.mat4_multiply(a, b)
|
||||
return C.pxl8_mat4_multiply(a, b)
|
||||
end
|
||||
|
||||
function pxl8.mat4_translate(x, y, z)
|
||||
return C.pxl8_mat4_translate(x, y, z)
|
||||
end
|
||||
|
||||
function pxl8.mat4_rotate_x(angle)
|
||||
return C.pxl8_mat4_rotate_x(angle)
|
||||
end
|
||||
|
||||
function pxl8.mat4_rotate_y(angle)
|
||||
return C.pxl8_mat4_rotate_y(angle)
|
||||
end
|
||||
|
||||
function pxl8.mat4_rotate_z(angle)
|
||||
return C.pxl8_mat4_rotate_z(angle)
|
||||
end
|
||||
|
||||
function pxl8.mat4_scale(x, y, z)
|
||||
return C.pxl8_mat4_scale(x, y, z)
|
||||
end
|
||||
|
||||
function pxl8.mat4_ortho(left, right, bottom, top, near, far)
|
||||
return C.pxl8_mat4_ortho(left, right, bottom, top, near, far)
|
||||
end
|
||||
|
||||
function pxl8.mat4_perspective(fov, aspect, near, far)
|
||||
return C.pxl8_mat4_perspective(fov, aspect, near, far)
|
||||
end
|
||||
|
||||
function pxl8.mat4_lookat(eye, center, up)
|
||||
local eye_vec = ffi.new("pxl8_vec3", {x = eye[1], y = eye[2], z = eye[3]})
|
||||
local center_vec = ffi.new("pxl8_vec3", {x = center[1], y = center[2], z = center[3]})
|
||||
local up_vec = ffi.new("pxl8_vec3", {x = up[1], y = up[2], z = up[3]})
|
||||
return C.pxl8_mat4_lookat(eye_vec, center_vec, up_vec)
|
||||
end
|
||||
|
||||
pxl8.gfx = gfx
|
||||
pxl8.input = input
|
||||
|
||||
|
|
|
|||
214
src/pxl8.c
214
src/pxl8.c
|
|
@ -9,13 +9,14 @@
|
|||
#include <unistd.h>
|
||||
|
||||
#include <linenoise.h>
|
||||
|
||||
#define SDL_MAIN_USE_CALLBACKS
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_main.h>
|
||||
|
||||
#include "pxl8_cart.h"
|
||||
#include "pxl8_lua.h"
|
||||
#include "pxl8_macros.h"
|
||||
#include "pxl8_script.h"
|
||||
#include "pxl8_types.h"
|
||||
|
||||
#define PXL8_MAX_REPL_COMMANDS 4096
|
||||
|
|
@ -36,10 +37,10 @@ typedef struct pxl8_repl_state {
|
|||
typedef struct pxl8_state {
|
||||
pxl8_cart* cart;
|
||||
pxl8_color_mode color_mode;
|
||||
pxl8_gfx_ctx gfx;
|
||||
lua_State* lua;
|
||||
pxl8_gfx* gfx;
|
||||
pxl8_repl_state repl;
|
||||
pxl8_resolution resolution;
|
||||
pxl8_script* script;
|
||||
|
||||
f32 fps_timer;
|
||||
i32 frame_count;
|
||||
|
|
@ -50,8 +51,7 @@ typedef struct pxl8_state {
|
|||
bool running;
|
||||
bool script_loaded;
|
||||
char script_path[256];
|
||||
time_t script_mod_time;
|
||||
|
||||
|
||||
pxl8_input_state input;
|
||||
} pxl8_state;
|
||||
|
||||
|
|
@ -184,54 +184,6 @@ static pxl8_repl_command* pxl8_repl_pop_command(pxl8_repl_state* repl) {
|
|||
return cmd;
|
||||
}
|
||||
|
||||
static void load_script(pxl8_state* app) {
|
||||
const char* ext = strrchr(app->script_path, '.');
|
||||
|
||||
if (ext && strcmp(ext, ".fnl") == 0) {
|
||||
pxl8_result result = pxl8_lua_run_fennel_file(app->lua, app->script_path);
|
||||
if (result == PXL8_OK) {
|
||||
pxl8_info("Loaded script: %s", app->script_path);
|
||||
app->script_loaded = true;
|
||||
lua_getglobal(app->lua, "init");
|
||||
if (lua_isfunction(app->lua, -1)) {
|
||||
lua_pcall(app->lua, 0, 0, 0);
|
||||
} else {
|
||||
pxl8_warn("No init function found (type: %s)", lua_typename(app->lua, lua_type(app->lua, -1)));
|
||||
lua_pop(app->lua, 1);
|
||||
}
|
||||
} else {
|
||||
pxl8_warn("Failed to load script: %s", app->script_path);
|
||||
app->script_loaded = false;
|
||||
}
|
||||
} else if (ext && strcmp(ext, ".lua") == 0) {
|
||||
pxl8_result result = pxl8_lua_run_file(app->lua, app->script_path);
|
||||
if (result == PXL8_OK) {
|
||||
pxl8_info("Loaded script: %s", app->script_path);
|
||||
app->script_loaded = true;
|
||||
lua_getglobal(app->lua, "init");
|
||||
if (lua_isfunction(app->lua, -1)) {
|
||||
lua_pcall(app->lua, 0, 0, 0);
|
||||
} else {
|
||||
pxl8_warn("No init function found (type: %s)", lua_typename(app->lua, lua_type(app->lua, -1)));
|
||||
lua_pop(app->lua, 1);
|
||||
}
|
||||
} else {
|
||||
pxl8_warn("Failed to load script: %s", app->script_path);
|
||||
app->script_loaded = false;
|
||||
}
|
||||
} else {
|
||||
pxl8_warn("Unknown script type for: %s (expected .fnl or .lua)", app->script_path);
|
||||
app->script_loaded = false;
|
||||
}
|
||||
}
|
||||
|
||||
static time_t get_file_mod_time(const char* path) {
|
||||
struct stat file_stat;
|
||||
if (stat(path, &file_stat) == 0) {
|
||||
return file_stat.st_mtime;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) {
|
||||
static pxl8_state app = {0};
|
||||
|
|
@ -249,7 +201,7 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) {
|
|||
const char* pack_input = NULL;
|
||||
const char* pack_output = NULL;
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
for (i32 i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "--repl") == 0) {
|
||||
app.repl_mode = true;
|
||||
} else if (strcmp(argv[i], "--pack") == 0) {
|
||||
|
|
@ -277,26 +229,27 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) {
|
|||
}
|
||||
|
||||
pxl8_info("Starting up");
|
||||
|
||||
if (pxl8_gfx_init(&app.gfx, app.color_mode, app.resolution, "pxl8", 1280, 720) != PXL8_OK) {
|
||||
pxl8_error("Failed to initialize graphics context");
|
||||
|
||||
app.gfx = pxl8_gfx_create(app.color_mode, app.resolution, "pxl8", 1280, 720);
|
||||
if (!app.gfx) {
|
||||
pxl8_error("Failed to create graphics context");
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
if (pxl8_gfx_load_font_atlas(&app.gfx) != PXL8_OK) {
|
||||
|
||||
if (pxl8_gfx_load_font_atlas(app.gfx) != PXL8_OK) {
|
||||
pxl8_error("Failed to load font atlas");
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
if (pxl8_gfx_init_atlas(&app.gfx, 1024, 1024) != PXL8_OK) {
|
||||
|
||||
if (pxl8_gfx_init_atlas(app.gfx, 1024, 1024) != PXL8_OK) {
|
||||
pxl8_error("Failed to initialize sprite atlas");
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
pxl8_result lua_result = pxl8_lua_init(&app.lua);
|
||||
if (lua_result != PXL8_OK) {
|
||||
pxl8_error("Failed to initialize Lua scripting!");
|
||||
pxl8_gfx_shutdown(&app.gfx);
|
||||
|
||||
app.script = pxl8_script_create();
|
||||
if (!app.script) {
|
||||
pxl8_error("Failed to initialize scripting: %s", pxl8_script_get_last_error(app.script));
|
||||
pxl8_gfx_destroy(app.gfx);
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
|
|
@ -308,16 +261,16 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) {
|
|||
|
||||
if (is_cart) {
|
||||
char* original_cwd = getcwd(NULL, 0);
|
||||
app.cart = calloc(1, sizeof(pxl8_cart));
|
||||
app.cart = pxl8_cart_create();
|
||||
if (!app.cart) {
|
||||
pxl8_error("Failed to allocate memory for cart");
|
||||
pxl8_error("Failed to create cart");
|
||||
return false;
|
||||
}
|
||||
if (pxl8_cart_load(app.cart, cart_path) == PXL8_OK) {
|
||||
pxl8_lua_setup_cart_path(app.lua, app.cart->base_path, original_cwd);
|
||||
pxl8_script_set_cart_path(app.script, pxl8_cart_get_base_path(app.cart), original_cwd);
|
||||
pxl8_cart_mount(app.cart);
|
||||
strcpy(app.script_path, "main.fnl");
|
||||
pxl8_info("Loaded cart: %s", app.cart->name);
|
||||
pxl8_info("Loaded cart: %s", pxl8_cart_get_name(app.cart));
|
||||
} else {
|
||||
pxl8_error("Failed to load cart: %s", cart_path);
|
||||
return SDL_APP_FAILURE;
|
||||
|
|
@ -328,25 +281,20 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) {
|
|||
app.script_path[sizeof(app.script_path) - 1] = '\0';
|
||||
}
|
||||
|
||||
pxl8_lua_setup_contexts(app.lua, &app.gfx, &app.input);
|
||||
pxl8_script_set_gfx(app.script, app.gfx);
|
||||
pxl8_script_set_input(app.script, &app.input);
|
||||
|
||||
if (app.script_path[0] != '\0') {
|
||||
app.script_mod_time = get_file_mod_time(app.script_path);
|
||||
load_script(&app);
|
||||
pxl8_result result = pxl8_script_load_main(app.script, app.script_path);
|
||||
app.script_loaded = (result == PXL8_OK);
|
||||
}
|
||||
|
||||
if (app.repl_mode) {
|
||||
pxl8_repl_init(&app.repl);
|
||||
fprintf(stderr, "\033[38;2;184;187;38m[pxl8 REPL]\033[0m Fennel %s - Tab for completions, Ctrl-C to exit\n", "1.5.1");
|
||||
|
||||
lua_getglobal(app.lua, "require");
|
||||
lua_pushstring(app.lua, "pxl8");
|
||||
if (lua_pcall(app.lua, 1, 1, 0) == 0) {
|
||||
lua_setglobal(app.lua, "pxl8");
|
||||
} else {
|
||||
const char* error_msg = lua_tostring(app.lua, -1);
|
||||
fprintf(stderr, "Warning: Failed to setup pxl8 global: %s\n", error_msg);
|
||||
lua_pop(app.lua, 1);
|
||||
|
||||
if (pxl8_script_load_module(app.script, "pxl8") != PXL8_OK) {
|
||||
fprintf(stderr, "Warning: Failed to setup pxl8 global: %s\n", pxl8_script_get_last_error(app.script));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -359,110 +307,72 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) {
|
|||
|
||||
SDL_AppResult SDL_AppIterate(void* appstate) {
|
||||
pxl8_state* app = (pxl8_state*)appstate;
|
||||
int width, height;
|
||||
pxl8_bounds bounds = pxl8_gfx_get_bounds(app->gfx);
|
||||
|
||||
SDL_GetWindowSize(app->gfx.window, &width, &height);
|
||||
|
||||
u64 current_time = SDL_GetTicksNS();
|
||||
float dt = (float)(current_time - app->last_time) / 1000000000.0f;
|
||||
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 >= 3.0f) {
|
||||
if (!app->repl_mode) {
|
||||
float avg_fps = app->frame_count / app->fps_timer;
|
||||
f32 avg_fps = app->frame_count / app->fps_timer;
|
||||
pxl8_info("FPS: %.1f", avg_fps);
|
||||
}
|
||||
app->frame_count = 0;
|
||||
app->fps_timer = 0.0f;
|
||||
}
|
||||
|
||||
time_t current_mod_time = get_file_mod_time(app->script_path);
|
||||
if (current_mod_time != app->script_mod_time && current_mod_time != 0) {
|
||||
pxl8_info("Script modified, reloading: %s", app->script_path);
|
||||
app->script_mod_time = current_mod_time;
|
||||
load_script(app);
|
||||
}
|
||||
pxl8_script_check_reload(app->script);
|
||||
|
||||
if (app->repl_mode) {
|
||||
pxl8_repl_command* cmd = pxl8_repl_pop_command(&app->repl);
|
||||
if (cmd) {
|
||||
pxl8_result result = pxl8_lua_eval_fennel(app->lua, cmd->buffer);
|
||||
if (result == PXL8_OK) {
|
||||
if (lua_gettop(app->lua) > 0 && !lua_isnil(app->lua, -1)) {
|
||||
const char* result_str = lua_tostring(app->lua, -1);
|
||||
if (result_str) {
|
||||
fprintf(stdout, "%s\n", result_str);
|
||||
} else if (lua_isuserdata(app->lua, -1)) {
|
||||
fprintf(stdout, "<userdata>\n");
|
||||
} else if (lua_iscfunction(app->lua, -1)) {
|
||||
fprintf(stdout, "<function>\n");
|
||||
} else if (lua_istable(app->lua, -1)) {
|
||||
fprintf(stdout, "<table>\n");
|
||||
} else {
|
||||
fprintf(stdout, "<%s>\n", lua_typename(app->lua, lua_type(app->lua, -1)));
|
||||
}
|
||||
lua_pop(app->lua, 1);
|
||||
}
|
||||
} else {
|
||||
const char* error_msg = lua_tostring(app->lua, -1);
|
||||
pxl8_error("%s", error_msg ? error_msg : "Unknown error");
|
||||
lua_pop(app->lua, 1);
|
||||
pxl8_result result = pxl8_script_eval(app->script, cmd->buffer);
|
||||
if (result != PXL8_OK) {
|
||||
pxl8_error("%s", pxl8_script_get_last_error(app->script));
|
||||
}
|
||||
SDL_free(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (app->script_loaded) {
|
||||
lua_getglobal(app->lua, "update");
|
||||
if (lua_isfunction(app->lua, -1)) {
|
||||
lua_pushnumber(app->lua, dt);
|
||||
lua_pcall(app->lua, 1, 0, 0);
|
||||
} else {
|
||||
lua_pop(app->lua, 1);
|
||||
}
|
||||
|
||||
lua_getglobal(app->lua, "draw");
|
||||
if (lua_isfunction(app->lua, -1)) {
|
||||
int result = lua_pcall(app->lua, 0, 0, 0);
|
||||
if (result != 0) {
|
||||
pxl8_error("Error calling draw: %s", lua_tostring(app->lua, -1));
|
||||
lua_pop(app->lua, 1);
|
||||
}
|
||||
} else {
|
||||
pxl8_warn("draw is not a function, type: %s", lua_typename(app->lua, lua_type(app->lua, -1)));
|
||||
lua_pop(app->lua, 1);
|
||||
pxl8_script_call_function_f32(app->script, "update", dt);
|
||||
|
||||
pxl8_result frame_result = pxl8_script_call_function(app->script, "frame");
|
||||
if (frame_result == PXL8_ERROR_SCRIPT_ERROR) {
|
||||
pxl8_error("Error calling frame: %s", pxl8_script_get_last_error(app->script));
|
||||
}
|
||||
} else {
|
||||
pxl8_clr(&app->gfx, 32);
|
||||
pxl8_clr(app->gfx, 32);
|
||||
|
||||
i32 render_width, render_height;
|
||||
pxl8_gfx_get_resolution_dimensions(app->resolution, &render_width, &render_height);
|
||||
|
||||
for (int y = 0; y < render_height; y += 24) {
|
||||
for (int x = 0; x < render_width; x += 32) {
|
||||
u32 color = ((x / 32) + (y / 24) + (int)(app->time * 2)) % 8;
|
||||
pxl8_rect_fill(&app->gfx, x, y, 31, 23, color);
|
||||
for (i32 y = 0; y < render_height; y += 24) {
|
||||
for (i32 x = 0; x < render_width; x += 32) {
|
||||
u32 color = ((x / 32) + (y / 24) + (i32)(app->time * 2)) % 8;
|
||||
pxl8_rect_fill(app->gfx, x, y, 31, 23, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i32 render_width, render_height;
|
||||
pxl8_gfx_get_resolution_dimensions(app->resolution, &render_width, &render_height);
|
||||
|
||||
f32 scale = fminf(bounds.w / (f32)render_width, bounds.h / (f32)render_height);
|
||||
i32 scaled_width = (i32)(render_width * scale);
|
||||
i32 scaled_height = (i32)(render_height * scale);
|
||||
i32 offset_x = (bounds.w - scaled_width) / 2;
|
||||
i32 offset_y = (bounds.h - scaled_height) / 2;
|
||||
|
||||
float scale = fminf(width / (float)render_width, height / (float)render_height);
|
||||
int scaled_width = (int)(render_width * scale);
|
||||
int scaled_height = (int)(render_height * scale);
|
||||
int offset_x = (width - scaled_width) / 2;
|
||||
int offset_y = (height - scaled_height) / 2;
|
||||
|
||||
pxl8_gfx_viewport(&app->gfx, offset_x, offset_y, scaled_width, scaled_height);
|
||||
pxl8_gfx_upload_framebuffer(&app->gfx);
|
||||
pxl8_gfx_upload_atlas(&app->gfx);
|
||||
pxl8_gfx_present(&app->gfx);
|
||||
pxl8_gfx_viewport(app->gfx, offset_x, offset_y, scaled_width, scaled_height);
|
||||
pxl8_gfx_upload_framebuffer(app->gfx);
|
||||
pxl8_gfx_upload_atlas(app->gfx);
|
||||
pxl8_gfx_present(app->gfx);
|
||||
|
||||
SDL_memset(app->input.keys_pressed, 0, sizeof(app->input.keys_pressed));
|
||||
|
||||
|
|
@ -520,9 +430,9 @@ void SDL_AppQuit(void* appstate, SDL_AppResult result) {
|
|||
free(app->cart);
|
||||
app->cart = NULL;
|
||||
}
|
||||
pxl8_lua_shutdown(app->lua);
|
||||
pxl8_gfx_shutdown(&app->gfx);
|
||||
pxl8_script_destroy(app->script);
|
||||
pxl8_gfx_destroy(app->gfx);
|
||||
}
|
||||
|
||||
|
||||
SDL_Quit();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ static pxl8_result parse_cel_chunk(const u8* data, u32 chunk_size, pxl8_ase_cel*
|
|||
}
|
||||
|
||||
mz_ulong dest_len = pixel_data_size;
|
||||
int result = mz_uncompress(cel->pixel_data, &dest_len, data + 20, compressed_data_size);
|
||||
i32 result = mz_uncompress(cel->pixel_data, &dest_len, data + 20, compressed_data_size);
|
||||
if (result != MZ_OK) {
|
||||
pxl8_error("Failed to decompress cel data: miniz error %d", result);
|
||||
SDL_free(cel->pixel_data);
|
||||
|
|
@ -358,13 +358,13 @@ pxl8_result pxl8_ase_load(const char* filepath, pxl8_ase_file* ase_file) {
|
|||
pxl8_io_free_binary_data(file_data);
|
||||
|
||||
if (result != PXL8_OK) {
|
||||
pxl8_ase_free(ase_file);
|
||||
pxl8_ase_destroy(ase_file);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void pxl8_ase_free(pxl8_ase_file* ase_file) {
|
||||
void pxl8_ase_destroy(pxl8_ase_file* ase_file) {
|
||||
if (!ase_file) return;
|
||||
|
||||
if (ase_file->frames) {
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
pxl8_result pxl8_ase_load(const char* filepath, pxl8_ase_file* ase_file);
|
||||
void pxl8_ase_free(pxl8_ase_file* ase_file);
|
||||
void pxl8_ase_destroy(pxl8_ase_file* ase_file);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,14 @@
|
|||
#include "pxl8_cart.h"
|
||||
#include "pxl8_macros.h"
|
||||
|
||||
struct pxl8_cart {
|
||||
void* archive_data;
|
||||
size_t archive_size;
|
||||
char* base_path;
|
||||
bool is_folder;
|
||||
bool is_mounted;
|
||||
char* name;
|
||||
};
|
||||
|
||||
static pxl8_cart* __pxl8_current_cart = NULL;
|
||||
static char* __pxl8_original_cwd = NULL;
|
||||
|
|
@ -73,10 +81,29 @@ static bool is_pxc_file(const char* path) {
|
|||
return len > 4 && strcmp(path + len - 4, ".pxc") == 0;
|
||||
}
|
||||
|
||||
pxl8_cart* pxl8_cart_create(void) {
|
||||
pxl8_cart* cart = calloc(1, sizeof(pxl8_cart));
|
||||
return cart;
|
||||
}
|
||||
|
||||
pxl8_cart* pxl8_cart_current(void) {
|
||||
return __pxl8_current_cart;
|
||||
}
|
||||
|
||||
void pxl8_cart_destroy(pxl8_cart* cart) {
|
||||
if (!cart) return;
|
||||
pxl8_cart_unload(cart);
|
||||
free(cart);
|
||||
}
|
||||
|
||||
const char* pxl8_cart_get_base_path(const pxl8_cart* cart) {
|
||||
return cart ? cart->base_path : NULL;
|
||||
}
|
||||
|
||||
const char* pxl8_cart_get_name(const pxl8_cart* cart) {
|
||||
return cart ? cart->name : NULL;
|
||||
}
|
||||
|
||||
pxl8_result pxl8_cart_load(pxl8_cart* cart, const char* path) {
|
||||
if (!cart || !path) return PXL8_ERROR_NULL_POINTER;
|
||||
|
||||
|
|
@ -146,8 +173,8 @@ pxl8_result pxl8_cart_load(pxl8_cart* cart, const char* path) {
|
|||
return PXL8_ERROR_INVALID_FORMAT;
|
||||
}
|
||||
|
||||
int num_files = mz_zip_reader_get_num_files(&zip);
|
||||
for (int i = 0; i < num_files; i++) {
|
||||
i32 num_files = mz_zip_reader_get_num_files(&zip);
|
||||
for (i32 i = 0; i < num_files; i++) {
|
||||
mz_zip_archive_file_stat file_stat;
|
||||
if (!mz_zip_reader_file_stat(&zip, i, &file_stat)) continue;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,21 +2,18 @@
|
|||
|
||||
#include "pxl8_types.h"
|
||||
|
||||
typedef struct pxl8_cart {
|
||||
void* archive_data;
|
||||
size_t archive_size;
|
||||
char* base_path;
|
||||
bool is_folder;
|
||||
bool is_mounted;
|
||||
char* name;
|
||||
} pxl8_cart;
|
||||
typedef struct pxl8_cart pxl8_cart;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
pxl8_cart* pxl8_cart_create(void);
|
||||
pxl8_cart* pxl8_cart_current(void);
|
||||
void pxl8_cart_destroy(pxl8_cart* cart);
|
||||
bool pxl8_cart_file_exists(const pxl8_cart* cart, const char* path);
|
||||
const char* pxl8_cart_get_base_path(const pxl8_cart* cart);
|
||||
const char* pxl8_cart_get_name(const pxl8_cart* cart);
|
||||
pxl8_result pxl8_cart_load(pxl8_cart* cart, const char* path);
|
||||
pxl8_result pxl8_cart_mount(pxl8_cart* cart);
|
||||
pxl8_result pxl8_cart_pack(const char* folder_path, const char* output_path);
|
||||
|
|
|
|||
901
src/pxl8_gfx.c
901
src/pxl8_gfx.c
File diff suppressed because it is too large
Load diff
170
src/pxl8_gfx.h
170
src/pxl8_gfx.h
|
|
@ -1,143 +1,105 @@
|
|||
#pragma once
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include "pxl8_blit.h"
|
||||
#include "pxl8_math.h"
|
||||
#include "pxl8_types.h"
|
||||
|
||||
typedef struct pxl8_atlas_entry {
|
||||
char path[256];
|
||||
u32 sprite_id;
|
||||
i32 x, y, w, h;
|
||||
} pxl8_atlas_entry;
|
||||
|
||||
typedef struct pxl8_gfx_ctx {
|
||||
SDL_Renderer* renderer;
|
||||
SDL_Texture* framebuffer_texture;
|
||||
SDL_Texture* sprite_atlas_texture;
|
||||
SDL_Window* window;
|
||||
|
||||
u8* framebuffer;
|
||||
bool initialized;
|
||||
u32* palette;
|
||||
u32 palette_size;
|
||||
|
||||
i32 framebuffer_width;
|
||||
i32 framebuffer_height;
|
||||
pxl8_color_mode color_mode;
|
||||
|
||||
u8* atlas;
|
||||
bool atlas_dirty;
|
||||
pxl8_atlas_entry* atlas_entries;
|
||||
u32 atlas_entries_len;
|
||||
u32 atlas_entries_cap;
|
||||
|
||||
u32 sprite_atlas_width;
|
||||
u32 sprite_atlas_height;
|
||||
u32 sprite_frame_width;
|
||||
u32 sprite_frame_height;
|
||||
u32 sprite_frames_per_row;
|
||||
|
||||
i32 viewport_x, viewport_y;
|
||||
i32 viewport_width, viewport_height;
|
||||
} pxl8_gfx_ctx;
|
||||
typedef struct pxl8_gfx pxl8_gfx;
|
||||
|
||||
typedef enum pxl8_blend_mode {
|
||||
PXL8_BLEND_NONE,
|
||||
PXL8_BLEND_ALPHA,
|
||||
PXL8_BLEND_ADD,
|
||||
PXL8_BLEND_MULTIPLY
|
||||
PXL8_BLEND_ALPHA,
|
||||
PXL8_BLEND_MULTIPLY,
|
||||
PXL8_BLEND_NONE
|
||||
} pxl8_blend_mode;
|
||||
|
||||
typedef struct pxl8_mode7_params {
|
||||
f32 horizon;
|
||||
f32 scale_x, scale_y;
|
||||
f32 rotation;
|
||||
f32 offset_x, offset_y;
|
||||
bool active;
|
||||
f32 horizon;
|
||||
f32 offset_x, offset_y;
|
||||
f32 rotation;
|
||||
f32 scale_x, scale_y;
|
||||
} pxl8_mode7_params;
|
||||
|
||||
typedef struct pxl8_palette_cycle {
|
||||
u8 start_index;
|
||||
bool active;
|
||||
u8 end_index;
|
||||
f32 speed;
|
||||
u8 start_index;
|
||||
f32 timer;
|
||||
bool active;
|
||||
} pxl8_palette_cycle;
|
||||
|
||||
typedef struct pxl8_scanline_effect {
|
||||
void (*process)(pxl8_gfx_ctx* ctx, i32 line, f32 time);
|
||||
bool active;
|
||||
void (*process)(pxl8_gfx* gfx, i32 line, f32 time);
|
||||
} pxl8_scanline_effect;
|
||||
|
||||
typedef struct pxl8_vertex {
|
||||
u32 color;
|
||||
pxl8_vec3 normal;
|
||||
pxl8_vec3 position;
|
||||
f32 u, v;
|
||||
} pxl8_vertex;
|
||||
|
||||
typedef struct pxl8_effects {
|
||||
pxl8_palette_cycle palette_cycles[8];
|
||||
pxl8_scanline_effect scanline_effects[4];
|
||||
f32 time;
|
||||
} pxl8_effects;
|
||||
|
||||
typedef struct pxl8_triangle {
|
||||
pxl8_vertex v[3];
|
||||
} pxl8_triangle;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
pxl8_gfx* pxl8_gfx_create(pxl8_color_mode mode, pxl8_resolution resolution, const char* title, i32 window_width, i32 window_height);
|
||||
void pxl8_gfx_destroy(pxl8_gfx* gfx);
|
||||
|
||||
pxl8_bounds pxl8_gfx_get_bounds(pxl8_gfx* gfx);
|
||||
pxl8_color_mode pxl8_gfx_get_color_mode(pxl8_gfx* gfx);
|
||||
u8* pxl8_gfx_get_framebuffer(pxl8_gfx* gfx);
|
||||
i32 pxl8_gfx_get_height(const pxl8_gfx* gfx);
|
||||
void pxl8_gfx_get_resolution_dimensions(pxl8_resolution resolution, i32* width, i32* height);
|
||||
pxl8_result pxl8_gfx_init(
|
||||
pxl8_gfx_ctx* ctx,
|
||||
pxl8_color_mode mode,
|
||||
pxl8_resolution resolution,
|
||||
const char* title,
|
||||
i32 window_width,
|
||||
i32 window_height
|
||||
);
|
||||
pxl8_result pxl8_gfx_init_atlas(pxl8_gfx_ctx* ctx, u32 width, u32 height);
|
||||
pxl8_result pxl8_gfx_load_font_atlas(pxl8_gfx_ctx* ctx);
|
||||
pxl8_result pxl8_gfx_load_palette(pxl8_gfx_ctx* ctx, const char* path);
|
||||
pxl8_result pxl8_gfx_load_sprite(pxl8_gfx_ctx* ctx, const char* path);
|
||||
void pxl8_gfx_present(pxl8_gfx_ctx* ctx);
|
||||
void pxl8_gfx_project(pxl8_gfx_ctx* ctx, f32 left, f32 right, f32 top, f32 bottom);
|
||||
void pxl8_gfx_shutdown(pxl8_gfx_ctx* ctx);
|
||||
void pxl8_gfx_upload_atlas(pxl8_gfx_ctx* ctx);
|
||||
void pxl8_gfx_upload_framebuffer(pxl8_gfx_ctx* ctx);
|
||||
void pxl8_gfx_viewport(pxl8_gfx_ctx* ctx, i32 x, i32 y, i32 width, i32 height);
|
||||
i32 pxl8_gfx_get_width(const pxl8_gfx* gfx);
|
||||
pxl8_result pxl8_gfx_init_atlas(pxl8_gfx* gfx, u32 width, u32 height);
|
||||
pxl8_result pxl8_gfx_load_font_atlas(pxl8_gfx* gfx);
|
||||
pxl8_result pxl8_gfx_load_palette(pxl8_gfx* gfx, const char* path);
|
||||
pxl8_result pxl8_gfx_load_sprite(pxl8_gfx* gfx, const char* path);
|
||||
void pxl8_gfx_present(pxl8_gfx* gfx);
|
||||
void pxl8_gfx_project(pxl8_gfx* gfx, f32 left, f32 right, f32 top, f32 bottom);
|
||||
void pxl8_gfx_upload_atlas(pxl8_gfx* gfx);
|
||||
void pxl8_gfx_upload_framebuffer(pxl8_gfx* gfx);
|
||||
void pxl8_gfx_viewport(pxl8_gfx* gfx, i32 x, i32 y, i32 width, i32 height);
|
||||
|
||||
void pxl8_gfx_color_ramp(
|
||||
pxl8_gfx_ctx* ctx,
|
||||
u8 start,
|
||||
u8 count,
|
||||
u32 from_color,
|
||||
u32 to_color
|
||||
);
|
||||
void pxl8_gfx_cycle_palette(pxl8_gfx_ctx* ctx, u8 start, u8 count, i32 step);
|
||||
void pxl8_gfx_fade_palette(
|
||||
pxl8_gfx_ctx* ctx,
|
||||
u8 start,
|
||||
u8 count,
|
||||
f32 amount,
|
||||
u32 target_color
|
||||
);
|
||||
void pxl8_gfx_interpolate_palettes(
|
||||
pxl8_gfx_ctx* ctx,
|
||||
u32* palette1,
|
||||
u32* palette2,
|
||||
u8 start,
|
||||
u8 count,
|
||||
f32 t
|
||||
);
|
||||
void pxl8_gfx_process_effects(pxl8_gfx_ctx* ctx, pxl8_effects* effects, f32 dt);
|
||||
void pxl8_gfx_swap_palette(pxl8_gfx_ctx* ctx, u8 start, u8 count, u32* new_colors);
|
||||
void pxl8_gfx_color_ramp(pxl8_gfx* gfx, u8 start, u8 count, u32 from_color, u32 to_color);
|
||||
void pxl8_gfx_cycle_palette(pxl8_gfx* gfx, u8 start, u8 count, i32 step);
|
||||
void pxl8_gfx_fade_palette(pxl8_gfx* gfx, u8 start, u8 count, f32 amount, u32 target_color);
|
||||
void pxl8_gfx_interpolate_palettes(pxl8_gfx* gfx, u32* palette1, u32* palette2, u8 start, u8 count, f32 t);
|
||||
void pxl8_gfx_process_effects(pxl8_gfx* gfx, pxl8_effects* effects, f32 dt);
|
||||
void pxl8_gfx_swap_palette(pxl8_gfx* gfx, u8 start, u8 count, u32* new_colors);
|
||||
|
||||
void pxl8_circle(pxl8_gfx_ctx* ctx, i32 cx, i32 cy, i32 radius, u32 color);
|
||||
void pxl8_circle_fill(pxl8_gfx_ctx* ctx, i32 cx, i32 cy, i32 radius, u32 color);
|
||||
void pxl8_clr(pxl8_gfx_ctx* ctx, u32 color);
|
||||
u32 pxl8_get_pixel(pxl8_gfx_ctx* ctx, i32 x, i32 y);
|
||||
void pxl8_line(pxl8_gfx_ctx* ctx, i32 x0, i32 y0, i32 x1, i32 y1, u32 color);
|
||||
void pxl8_pixel(pxl8_gfx_ctx* ctx, i32 x, i32 y, u32 color);
|
||||
void pxl8_rect(pxl8_gfx_ctx* ctx, i32 x, i32 y, i32 w, i32 h, u32 color);
|
||||
void pxl8_rect_fill(pxl8_gfx_ctx* ctx, i32 x, i32 y, i32 w, i32 h, u32 color);
|
||||
void pxl8_sprite(pxl8_gfx_ctx* ctx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h);
|
||||
void pxl8_text(pxl8_gfx_ctx* ctx, const char* text, i32 x, i32 y, u32 color);
|
||||
void pxl8_circle(pxl8_gfx* gfx, i32 cx, i32 cy, i32 radius, u32 color);
|
||||
void pxl8_circle_fill(pxl8_gfx* gfx, i32 cx, i32 cy, i32 radius, u32 color);
|
||||
void pxl8_clr(pxl8_gfx* gfx, u32 color);
|
||||
u32 pxl8_get_pixel(pxl8_gfx* gfx, i32 x, i32 y);
|
||||
void pxl8_line(pxl8_gfx* gfx, i32 x0, i32 y0, i32 x1, i32 y1, u32 color);
|
||||
void pxl8_pixel(pxl8_gfx* gfx, i32 x, i32 y, u32 color);
|
||||
void pxl8_rect(pxl8_gfx* gfx, i32 x, i32 y, i32 w, i32 h, u32 color);
|
||||
void pxl8_rect_fill(pxl8_gfx* gfx, i32 x, i32 y, i32 w, i32 h, u32 color);
|
||||
void pxl8_sprite(pxl8_gfx* gfx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h);
|
||||
void pxl8_text(pxl8_gfx* gfx, const char* text, i32 x, i32 y, u32 color);
|
||||
|
||||
void pxl8_3d_clear_zbuffer(pxl8_gfx* gfx);
|
||||
void pxl8_3d_draw_line_3d(pxl8_gfx* gfx, pxl8_vec3 p0, pxl8_vec3 p1, u32 color);
|
||||
void pxl8_3d_draw_triangle(pxl8_gfx* gfx, pxl8_triangle tri);
|
||||
void pxl8_3d_draw_triangle_raw(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, pxl8_vec3 v2, u32 color);
|
||||
void pxl8_3d_set_backface_culling(pxl8_gfx* gfx, bool culling);
|
||||
void pxl8_3d_set_model(pxl8_gfx* gfx, pxl8_mat4 mat);
|
||||
void pxl8_3d_set_projection(pxl8_gfx* gfx, pxl8_mat4 mat);
|
||||
void pxl8_3d_set_view(pxl8_gfx* gfx, pxl8_mat4 mat);
|
||||
void pxl8_3d_set_wireframe(pxl8_gfx* gfx, bool wireframe);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
330
src/pxl8_lua.c
330
src/pxl8_lua.c
|
|
@ -1,330 +0,0 @@
|
|||
#include <unistd.h>
|
||||
|
||||
#include "pxl8_lua.h"
|
||||
#include "pxl8_macros.h"
|
||||
#include "pxl8_vfx.h"
|
||||
|
||||
static const char* pxl8_ffi_cdefs =
|
||||
"typedef uint8_t u8;\n"
|
||||
"typedef uint16_t u16;\n"
|
||||
"typedef uint32_t u32;\n"
|
||||
"typedef uint64_t u64;\n"
|
||||
"typedef int8_t i8;\n"
|
||||
"typedef int16_t i16;\n"
|
||||
"typedef int32_t i32;\n"
|
||||
"typedef int64_t i64;\n"
|
||||
"typedef float f32;\n"
|
||||
"typedef double f64;\n"
|
||||
"typedef struct pxl8_gfx_ctx pxl8_gfx_ctx;\n"
|
||||
"typedef struct { int x, y, w, h; } pxl8_rect;\n"
|
||||
"typedef struct { int x, y; } pxl8_point;\n"
|
||||
"typedef struct {\n"
|
||||
" int width, height;\n"
|
||||
" int color_mode;\n"
|
||||
" unsigned char* pixels;\n"
|
||||
"} pxl8_screen;\n"
|
||||
"\n"
|
||||
"void pxl8_clr(pxl8_gfx_ctx* ctx, u32 color);\n"
|
||||
"void pxl8_pixel(pxl8_gfx_ctx* ctx, i32 x, i32 y, u32 color);\n"
|
||||
"u32 pxl8_get_pixel(pxl8_gfx_ctx* ctx, i32 x, i32 y);\n"
|
||||
"void pxl8_line(pxl8_gfx_ctx* ctx, i32 x0, i32 y0, i32 x1, i32 y1, u32 color);\n"
|
||||
"void pxl8_rect(pxl8_gfx_ctx* ctx, i32 x, i32 y, i32 w, i32 h, u32 color);\n"
|
||||
"void pxl8_rect_fill(pxl8_gfx_ctx* ctx, i32 x, i32 y, i32 w, i32 h, u32 color);\n"
|
||||
"void pxl8_circle(pxl8_gfx_ctx* ctx, i32 x, i32 y, i32 r, u32 color);\n"
|
||||
"void pxl8_circle_fill(pxl8_gfx_ctx* ctx, i32 x, i32 y, i32 r, u32 color);\n"
|
||||
"void pxl8_text(pxl8_gfx_ctx* ctx, const char* str, i32 x, i32 y, u32 color);\n"
|
||||
"void pxl8_sprite(pxl8_gfx_ctx* ctx, i32 id, i32 x, i32 y, i32 w, i32 h, bool flip_x, bool flip_y);\n"
|
||||
"pxl8_screen* pxl8_get_screen(pxl8_gfx_ctx* ctx);\n"
|
||||
"i32 pxl8_gfx_load_palette(pxl8_gfx_ctx* ctx, const char* filepath);\n"
|
||||
"i32 pxl8_gfx_load_sprite(pxl8_gfx_ctx* ctx, const char* filepath, u32* sprite_id);\n"
|
||||
"void pxl8_gfx_color_ramp(pxl8_gfx_ctx* ctx, u8 start, u8 count, u32 from_color, u32 to_color);\n"
|
||||
"void pxl8_gfx_fade_palette(pxl8_gfx_ctx* ctx, u8 start, u8 count, f32 amount, u32 target_color);\n"
|
||||
"void pxl8_lua_info(const char* msg);\n"
|
||||
"void pxl8_lua_warn(const char* msg);\n"
|
||||
"void pxl8_lua_error(const char* msg);\n"
|
||||
"void pxl8_lua_debug(const char* msg);\n"
|
||||
"void pxl8_lua_trace(const char* msg);\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"
|
||||
"\n"
|
||||
"typedef struct pxl8_tilesheet pxl8_tilesheet;\n"
|
||||
"typedef struct pxl8_tilemap pxl8_tilemap;\n"
|
||||
"pxl8_tilesheet* pxl8_tilesheet_new(u32 tile_size);\n"
|
||||
"void pxl8_tilesheet_destroy(pxl8_tilesheet* tilesheet);\n"
|
||||
"i32 pxl8_tilesheet_load(pxl8_tilesheet* tilesheet, const char* filepath, pxl8_gfx_ctx* gfx);\n"
|
||||
"pxl8_tilemap* pxl8_tilemap_new(u32 width, u32 height, u32 tile_size);\n"
|
||||
"void pxl8_tilemap_destroy(pxl8_tilemap* tilemap);\n"
|
||||
"i32 pxl8_tilemap_set_tilesheet(pxl8_tilemap* tilemap, pxl8_tilesheet* tilesheet);\n"
|
||||
"void pxl8_tilemap_set_tile(pxl8_tilemap* tilemap, u32 layer, u32 x, u32 y, u16 tile_id, u8 flags);\n"
|
||||
"u16 pxl8_tilemap_get_tile_id(const pxl8_tilemap* tilemap, u32 layer, u32 x, u32 y);\n"
|
||||
"void pxl8_tilemap_set_camera(pxl8_tilemap* tilemap, i32 x, i32 y);\n"
|
||||
"void pxl8_tilemap_render(const pxl8_tilemap* tilemap, pxl8_gfx_ctx* gfx);\n"
|
||||
"void pxl8_tilemap_render_layer(const pxl8_tilemap* tilemap, pxl8_gfx_ctx* gfx, u32 layer);\n"
|
||||
"bool pxl8_tilemap_is_solid(const pxl8_tilemap* tilemap, u32 x, u32 y);\n"
|
||||
"bool pxl8_tilemap_check_collision(const pxl8_tilemap* tilemap, i32 x, i32 y, i32 w, i32 h);\n"
|
||||
"\n"
|
||||
"typedef struct {\n"
|
||||
" float x, y, z;\n"
|
||||
" float vx, vy, vz;\n"
|
||||
" float ax, ay, az;\n"
|
||||
" float life;\n"
|
||||
" float max_life;\n"
|
||||
" unsigned int color;\n"
|
||||
" unsigned int start_color;\n"
|
||||
" unsigned int end_color;\n"
|
||||
" float size;\n"
|
||||
" float angle;\n"
|
||||
" float spin;\n"
|
||||
" unsigned char flags;\n"
|
||||
"} pxl8_particle;\n"
|
||||
"\n"
|
||||
"typedef struct {\n"
|
||||
" pxl8_particle* particles;\n"
|
||||
" unsigned int count;\n"
|
||||
" unsigned int max_count;\n"
|
||||
" unsigned int alive_count;\n"
|
||||
" float spawn_rate;\n"
|
||||
" float spawn_timer;\n"
|
||||
" float x, y;\n"
|
||||
" float spread_x, spread_y;\n"
|
||||
" float gravity_x, gravity_y;\n"
|
||||
" float drag;\n"
|
||||
" float turbulence;\n"
|
||||
" void* spawn_fn;\n"
|
||||
" void* update_fn;\n"
|
||||
" void* render_fn;\n"
|
||||
" void* userdata;\n"
|
||||
"} pxl8_particle_system;\n"
|
||||
"\n"
|
||||
"typedef struct {\n"
|
||||
" float base_y;\n"
|
||||
" float amplitude;\n"
|
||||
" int height;\n"
|
||||
" float speed;\n"
|
||||
" float phase;\n"
|
||||
" unsigned int color;\n"
|
||||
" unsigned int fade_color;\n"
|
||||
"} pxl8_raster_bar;\n"
|
||||
"\n"
|
||||
"void pxl8_vfx_raster_bars(pxl8_gfx_ctx* ctx, pxl8_raster_bar* bars, u32 bar_count, f32 time);\n"
|
||||
"void pxl8_vfx_plasma(pxl8_gfx_ctx* ctx, f32 time, f32 scale1, f32 scale2, u8 palette_offset);\n"
|
||||
"void pxl8_vfx_rotozoom(pxl8_gfx_ctx* ctx, f32 angle, f32 zoom, i32 cx, i32 cy);\n"
|
||||
"void pxl8_vfx_tunnel(pxl8_gfx_ctx* ctx, f32 time, f32 speed, f32 twist);\n"
|
||||
"void pxl8_vfx_water_ripple(pxl8_gfx_ctx* ctx, f32* height_map, i32 drop_x, i32 drop_y, f32 damping);\n"
|
||||
"\n"
|
||||
"void pxl8_vfx_particles_clear(pxl8_particle_system* sys);\n"
|
||||
"void pxl8_vfx_particles_destroy(pxl8_particle_system* sys);\n"
|
||||
"void pxl8_vfx_particles_emit(pxl8_particle_system* sys, u32 count);\n"
|
||||
"void pxl8_vfx_particles_init(pxl8_particle_system* sys, unsigned int max_count);\n"
|
||||
"void pxl8_vfx_particles_render(pxl8_particle_system* sys, pxl8_gfx_ctx* ctx);\n"
|
||||
"void pxl8_vfx_particles_update(pxl8_particle_system* sys, float dt);\n"
|
||||
"\n"
|
||||
"void pxl8_vfx_explosion(pxl8_particle_system* sys, int x, int y, unsigned int color, float force);\n"
|
||||
"void pxl8_vfx_fire(pxl8_particle_system* sys, int x, int y, int width, unsigned char palette_start);\n"
|
||||
"void pxl8_vfx_rain(pxl8_particle_system* sys, int width, float wind);\n"
|
||||
"void pxl8_vfx_smoke(pxl8_particle_system* sys, int x, int y, unsigned char color);\n"
|
||||
"void pxl8_vfx_snow(pxl8_particle_system* sys, int width, float wind);\n"
|
||||
"void pxl8_vfx_sparks(pxl8_particle_system* sys, int x, int y, unsigned int color);\n"
|
||||
"void pxl8_vfx_starfield(pxl8_particle_system* sys, float speed, float spread);\n";
|
||||
|
||||
typedef struct {
|
||||
int width, height;
|
||||
int color_mode;
|
||||
unsigned char* pixels;
|
||||
} pxl8_screen;
|
||||
|
||||
pxl8_screen* pxl8_get_screen(pxl8_gfx_ctx* ctx) {
|
||||
if (!ctx) return NULL;
|
||||
|
||||
static pxl8_screen screen = {0};
|
||||
screen.width = ctx->framebuffer_width;
|
||||
screen.height = ctx->framebuffer_height;
|
||||
screen.color_mode = ctx->color_mode;
|
||||
screen.pixels = ctx->framebuffer;
|
||||
|
||||
return &screen;
|
||||
}
|
||||
|
||||
void pxl8_lua_info(const char* msg) {
|
||||
pxl8_info("%s", msg);
|
||||
}
|
||||
|
||||
void pxl8_lua_warn(const char* msg) {
|
||||
pxl8_warn("%s", msg);
|
||||
}
|
||||
|
||||
void pxl8_lua_error(const char* msg) {
|
||||
pxl8_error("%s", msg);
|
||||
}
|
||||
|
||||
void pxl8_lua_debug(const char* msg) {
|
||||
pxl8_debug("%s", msg);
|
||||
}
|
||||
|
||||
void pxl8_lua_trace(const char* msg) {
|
||||
pxl8_trace("%s", msg);
|
||||
}
|
||||
|
||||
pxl8_result pxl8_lua_init(lua_State** lua_state) {
|
||||
if (!lua_state) return PXL8_ERROR_NULL_POINTER;
|
||||
|
||||
*lua_state = luaL_newstate();
|
||||
if (!*lua_state) {
|
||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
luaL_openlibs(*lua_state);
|
||||
|
||||
lua_getglobal(*lua_state, "require");
|
||||
lua_pushstring(*lua_state, "ffi");
|
||||
if (lua_pcall(*lua_state, 1, 1, 0) != 0) {
|
||||
lua_close(*lua_state);
|
||||
*lua_state = NULL;
|
||||
return PXL8_ERROR_SYSTEM_FAILURE;
|
||||
}
|
||||
|
||||
lua_getfield(*lua_state, -1, "cdef");
|
||||
lua_pushstring(*lua_state, pxl8_ffi_cdefs);
|
||||
if (lua_pcall(*lua_state, 1, 0, 0) != 0) {
|
||||
lua_close(*lua_state);
|
||||
*lua_state = NULL;
|
||||
return PXL8_ERROR_SYSTEM_FAILURE;
|
||||
}
|
||||
|
||||
lua_pop(*lua_state, 1);
|
||||
|
||||
lua_getglobal(*lua_state, "package");
|
||||
lua_getfield(*lua_state, -1, "path");
|
||||
const char* current_path = lua_tostring(*lua_state, -1);
|
||||
lua_pop(*lua_state, 1);
|
||||
|
||||
lua_pushfstring(*lua_state, "%s;src/lua/?.lua", current_path);
|
||||
lua_setfield(*lua_state, -2, "path");
|
||||
lua_pop(*lua_state, 1);
|
||||
|
||||
if (luaL_dofile(*lua_state, "lib/fennel/fennel.lua") == 0) {
|
||||
lua_setglobal(*lua_state, "fennel");
|
||||
}
|
||||
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
||||
void pxl8_lua_shutdown(lua_State* lua_state) {
|
||||
if (lua_state) {
|
||||
lua_close(lua_state);
|
||||
}
|
||||
}
|
||||
|
||||
pxl8_result pxl8_lua_setup_contexts(lua_State* lua_state, pxl8_gfx_ctx* gfx_ctx, pxl8_input_state* input) {
|
||||
if (!lua_state || !gfx_ctx || !input) return PXL8_ERROR_NULL_POINTER;
|
||||
|
||||
lua_pushlightuserdata(lua_state, gfx_ctx);
|
||||
lua_setglobal(lua_state, "_pxl8_gfx_ctx");
|
||||
|
||||
lua_pushlightuserdata(lua_state, input);
|
||||
lua_setglobal(lua_state, "_pxl8_input_ctx");
|
||||
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
||||
pxl8_result pxl8_lua_run_file(lua_State* lua_state, const char* filename) {
|
||||
if (!lua_state || !filename) return PXL8_ERROR_NULL_POINTER;
|
||||
|
||||
if (luaL_dofile(lua_state, filename) != 0) {
|
||||
printf("Lua error: %s\n", lua_tostring(lua_state, -1));
|
||||
lua_pop(lua_state, 1);
|
||||
return PXL8_ERROR_SCRIPT_ERROR;
|
||||
}
|
||||
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
||||
pxl8_result pxl8_lua_run_string(lua_State* lua_state, const char* code) {
|
||||
if (!lua_state || !code) return PXL8_ERROR_NULL_POINTER;
|
||||
|
||||
if (luaL_dostring(lua_state, code) != 0) {
|
||||
printf("Lua error: %s\n", lua_tostring(lua_state, -1));
|
||||
lua_pop(lua_state, 1);
|
||||
return PXL8_ERROR_SCRIPT_ERROR;
|
||||
}
|
||||
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
||||
void pxl8_lua_setup_cart_path(lua_State* lua_state, const char* cart_path, const char* original_cwd) {
|
||||
if (!lua_state || !cart_path || !original_cwd) return;
|
||||
|
||||
lua_getglobal(lua_state, "package");
|
||||
lua_getfield(lua_state, -1, "path");
|
||||
const char* current_path = lua_tostring(lua_state, -1);
|
||||
|
||||
char new_path[2048];
|
||||
snprintf(new_path, sizeof(new_path),
|
||||
"%s/?.lua;%s/?/init.lua;%s/src/?.lua;%s/src/?/init.lua;%s/src/lua/?.lua;%s",
|
||||
cart_path, cart_path, cart_path, cart_path, original_cwd,
|
||||
current_path ? current_path : "");
|
||||
|
||||
lua_pushstring(lua_state, new_path);
|
||||
lua_setfield(lua_state, -3, "path");
|
||||
lua_pop(lua_state, 2);
|
||||
|
||||
lua_getglobal(lua_state, "fennel");
|
||||
if (!lua_isnil(lua_state, -1)) {
|
||||
lua_getfield(lua_state, -1, "path");
|
||||
const char* fennel_path = lua_tostring(lua_state, -1);
|
||||
|
||||
snprintf(new_path, sizeof(new_path),
|
||||
"%s/?.fnl;%s/?/init.fnl;%s/src/?.fnl;%s/src/?/init.fnl;%s",
|
||||
cart_path, cart_path, cart_path, cart_path,
|
||||
fennel_path ? fennel_path : "");
|
||||
|
||||
lua_pushstring(lua_state, new_path);
|
||||
lua_setfield(lua_state, -3, "path");
|
||||
lua_pop(lua_state, 1);
|
||||
}
|
||||
lua_pop(lua_state, 1);
|
||||
}
|
||||
|
||||
pxl8_result pxl8_lua_run_fennel_file(lua_State* lua_state, const char* filename) {
|
||||
if (!lua_state || !filename) return PXL8_ERROR_NULL_POINTER;
|
||||
|
||||
lua_getglobal(lua_state, "fennel");
|
||||
if (lua_isnil(lua_state, -1)) {
|
||||
lua_pop(lua_state, 1);
|
||||
return PXL8_ERROR_SCRIPT_ERROR;
|
||||
}
|
||||
|
||||
lua_getfield(lua_state, -1, "dofile");
|
||||
lua_pushstring(lua_state, filename);
|
||||
|
||||
if (lua_pcall(lua_state, 1, 0, 0) != 0) {
|
||||
printf("Fennel error: %s\n", lua_tostring(lua_state, -1));
|
||||
lua_pop(lua_state, 1);
|
||||
return PXL8_ERROR_SCRIPT_ERROR;
|
||||
}
|
||||
|
||||
lua_pop(lua_state, 1);
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
||||
pxl8_result pxl8_lua_eval_fennel(lua_State* lua_state, const char* code) {
|
||||
if (!lua_state || !code) return PXL8_ERROR_NULL_POINTER;
|
||||
|
||||
lua_getglobal(lua_state, "fennel");
|
||||
if (lua_isnil(lua_state, -1)) {
|
||||
lua_pop(lua_state, 1);
|
||||
return PXL8_ERROR_SCRIPT_ERROR;
|
||||
}
|
||||
|
||||
lua_getfield(lua_state, -1, "eval");
|
||||
lua_pushstring(lua_state, code);
|
||||
|
||||
if (lua_pcall(lua_state, 1, 1, 0) != 0) {
|
||||
lua_remove(lua_state, -2);
|
||||
return PXL8_ERROR_SCRIPT_ERROR;
|
||||
}
|
||||
|
||||
lua_remove(lua_state, -2);
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <lua.h>
|
||||
#include <lualib.h>
|
||||
#include <lauxlib.h>
|
||||
|
||||
#include "pxl8_types.h"
|
||||
#include "pxl8_gfx.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
pxl8_result pxl8_lua_init(lua_State** lua_state);
|
||||
void pxl8_lua_shutdown(lua_State* lua_state);
|
||||
pxl8_result pxl8_lua_setup_contexts(lua_State* lua_state, pxl8_gfx_ctx* gfx_ctx, pxl8_input_state* input);
|
||||
void pxl8_lua_setup_cart_path(lua_State* lua_state, const char* cart_path, const char* original_cwd);
|
||||
|
||||
pxl8_result pxl8_lua_eval_fennel(lua_State* lua_state, const char* code);
|
||||
pxl8_result pxl8_lua_run_fennel_file(lua_State* lua_state, const char* filename);
|
||||
pxl8_result pxl8_lua_run_file(lua_State* lua_state, const char* filename);
|
||||
pxl8_result pxl8_lua_run_string(lua_State* lua_state, const char* code);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
235
src/pxl8_math.c
Normal file
235
src/pxl8_math.c
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
#include <math.h>
|
||||
|
||||
#include "pxl8_math.h"
|
||||
#include "pxl8_simd.h"
|
||||
|
||||
pxl8_vec2 pxl8_vec2_add(pxl8_vec2 a, pxl8_vec2 b) {
|
||||
return (pxl8_vec2) {
|
||||
.x = a.x + b.x,
|
||||
.y = a.y + b.y,
|
||||
};
|
||||
}
|
||||
|
||||
pxl8_vec2 pxl8_vec2_sub(pxl8_vec2 a, pxl8_vec2 b) {
|
||||
return (pxl8_vec2) {
|
||||
.x = a.x - b.x,
|
||||
.y = a.y - b.y,
|
||||
};
|
||||
}
|
||||
|
||||
pxl8_vec2 pxl8_vec2_scale(pxl8_vec2 v, f32 s) {
|
||||
return (pxl8_vec2) {
|
||||
.x = v.x * s,
|
||||
.y = v.y * s,
|
||||
};
|
||||
}
|
||||
|
||||
f32 pxl8_vec2_dot(pxl8_vec2 a, pxl8_vec2 b) {
|
||||
return a.x * b.x + a.y * b.y;
|
||||
}
|
||||
|
||||
f32 pxl8_vec2_length(pxl8_vec2 v) {
|
||||
return sqrtf(v.x * v.x + v.y * v.y);
|
||||
}
|
||||
|
||||
pxl8_vec2 pxl8_vec2_normalize(pxl8_vec2 v) {
|
||||
f32 len = pxl8_vec2_length(v);
|
||||
if (len < 1e-6f) return (pxl8_vec2){0};
|
||||
return pxl8_vec2_scale(v, 1.0f / len);
|
||||
}
|
||||
|
||||
pxl8_vec3 pxl8_vec3_add(pxl8_vec3 a, pxl8_vec3 b) {
|
||||
pxl8_simd_vec_f32 va = pxl8_simd_set_f32(a.x, a.y, a.z, 0);
|
||||
pxl8_simd_vec_f32 vb = pxl8_simd_set_f32(b.x, b.y, b.z, 0);
|
||||
pxl8_simd_vec_f32 result = pxl8_simd_add_f32(va, vb);
|
||||
|
||||
return (pxl8_vec3) {
|
||||
.x = result.f32_array[0],
|
||||
.y = result.f32_array[1],
|
||||
.z = result.f32_array[2],
|
||||
};
|
||||
}
|
||||
|
||||
pxl8_vec3 pxl8_vec3_sub(pxl8_vec3 a, pxl8_vec3 b) {
|
||||
pxl8_simd_vec_f32 va = pxl8_simd_set_f32(a.x, a.y, a.z, 0);
|
||||
pxl8_simd_vec_f32 vb = pxl8_simd_set_f32(b.x, b.y, b.z, 0);
|
||||
pxl8_simd_vec_f32 result = pxl8_simd_sub_f32(va, vb);
|
||||
|
||||
return (pxl8_vec3) {
|
||||
.x = result.f32_array[0],
|
||||
.y = result.f32_array[1],
|
||||
.z = result.f32_array[2],
|
||||
};
|
||||
}
|
||||
|
||||
pxl8_vec3 pxl8_vec3_scale(pxl8_vec3 v, f32 s) {
|
||||
pxl8_simd_vec_f32 vv = pxl8_simd_set_f32(v.x, v.y, v.z, 0);
|
||||
pxl8_simd_vec_f32 result = pxl8_simd_scale_f32(vv, s);
|
||||
|
||||
return (pxl8_vec3) {
|
||||
.x = result.f32_array[0],
|
||||
.y = result.f32_array[1],
|
||||
.z = result.f32_array[2],
|
||||
};
|
||||
}
|
||||
|
||||
f32 pxl8_vec3_dot(pxl8_vec3 a, pxl8_vec3 b) {
|
||||
pxl8_simd_vec_f32 va = pxl8_simd_set_f32(a.x, a.y, a.z, 0);
|
||||
pxl8_simd_vec_f32 vb = pxl8_simd_set_f32(b.x, b.y, b.z, 0);
|
||||
return pxl8_simd_dot3_f32(va, vb);
|
||||
}
|
||||
|
||||
pxl8_vec3 pxl8_vec3_cross(pxl8_vec3 a, pxl8_vec3 b) {
|
||||
return (pxl8_vec3) {
|
||||
.x = a.y * b.z - a.z * b.y,
|
||||
.y = a.z * b.x - a.x * b.z,
|
||||
.z = a.x * b.y - a.y * b.x,
|
||||
};
|
||||
}
|
||||
|
||||
f32 pxl8_vec3_length(pxl8_vec3 v) {
|
||||
return sqrtf(pxl8_vec3_dot(v, v));
|
||||
}
|
||||
|
||||
pxl8_vec3 pxl8_vec3_normalize(pxl8_vec3 v) {
|
||||
f32 len = pxl8_vec3_length(v);
|
||||
if (len < 1e-6f) return (pxl8_vec3){0};
|
||||
return pxl8_vec3_scale(v, 1.0f / len);
|
||||
}
|
||||
|
||||
pxl8_mat4 pxl8_mat4_identity(void) {
|
||||
pxl8_mat4 mat = {0};
|
||||
mat.m[0] = mat.m[5] = mat.m[10] = mat.m[15] = 1.0f;
|
||||
return mat;
|
||||
}
|
||||
|
||||
pxl8_mat4 pxl8_mat4_multiply(pxl8_mat4 a, pxl8_mat4 b) {
|
||||
pxl8_mat4 result = {0};
|
||||
for (i32 i = 0; i < 4; i++) {
|
||||
for (i32 j = 0; j < 4; j++) {
|
||||
pxl8_simd_vec_f32 row = pxl8_simd_set_f32(
|
||||
a.m[i * 4 + 0], a.m[i * 4 + 1], a.m[i * 4 + 2], a.m[i * 4 + 3]
|
||||
);
|
||||
pxl8_simd_vec_f32 col = pxl8_simd_set_f32(
|
||||
b.m[0 * 4 + j], b.m[1 * 4 + j], b.m[2 * 4 + j], b.m[3 * 4 + j]
|
||||
);
|
||||
result.m[i * 4 + j] = pxl8_simd_dot4_f32(row, col);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
pxl8_vec4 pxl8_mat4_multiply_vec4(pxl8_mat4 m, pxl8_vec4 v) {
|
||||
pxl8_simd_vec_f32 vec = pxl8_simd_set_f32(v.x, v.y, v.z, v.w);
|
||||
pxl8_vec4 result;
|
||||
|
||||
pxl8_simd_vec_f32 row0 = pxl8_simd_set_f32(m.m[0], m.m[1], m.m[2], m.m[3]);
|
||||
pxl8_simd_vec_f32 row1 = pxl8_simd_set_f32(m.m[4], m.m[5], m.m[6], m.m[7]);
|
||||
pxl8_simd_vec_f32 row2 = pxl8_simd_set_f32(m.m[8], m.m[9], m.m[10], m.m[11]);
|
||||
pxl8_simd_vec_f32 row3 = pxl8_simd_set_f32(m.m[12], m.m[13], m.m[14], m.m[15]);
|
||||
|
||||
result.x = pxl8_simd_dot4_f32(row0, vec);
|
||||
result.y = pxl8_simd_dot4_f32(row1, vec);
|
||||
result.z = pxl8_simd_dot4_f32(row2, vec);
|
||||
result.w = pxl8_simd_dot4_f32(row3, vec);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
pxl8_mat4 pxl8_mat4_translate(f32 x, f32 y, f32 z) {
|
||||
pxl8_mat4 mat = pxl8_mat4_identity();
|
||||
mat.m[3] = x;
|
||||
mat.m[7] = y;
|
||||
mat.m[11] = z;
|
||||
return mat;
|
||||
}
|
||||
|
||||
pxl8_mat4 pxl8_mat4_rotate_x(f32 angle) {
|
||||
pxl8_mat4 mat = pxl8_mat4_identity();
|
||||
f32 c = cosf(angle);
|
||||
f32 s = sinf(angle);
|
||||
mat.m[5] = c;
|
||||
mat.m[6] = -s;
|
||||
mat.m[9] = s;
|
||||
mat.m[10] = c;
|
||||
return mat;
|
||||
}
|
||||
|
||||
pxl8_mat4 pxl8_mat4_rotate_y(f32 angle) {
|
||||
pxl8_mat4 mat = pxl8_mat4_identity();
|
||||
f32 c = cosf(angle);
|
||||
f32 s = sinf(angle);
|
||||
mat.m[0] = c;
|
||||
mat.m[2] = s;
|
||||
mat.m[8] = -s;
|
||||
mat.m[10] = c;
|
||||
return mat;
|
||||
}
|
||||
|
||||
pxl8_mat4 pxl8_mat4_rotate_z(f32 angle) {
|
||||
pxl8_mat4 mat = pxl8_mat4_identity();
|
||||
f32 c = cosf(angle);
|
||||
f32 s = sinf(angle);
|
||||
mat.m[0] = c;
|
||||
mat.m[1] = -s;
|
||||
mat.m[4] = s;
|
||||
mat.m[5] = c;
|
||||
return mat;
|
||||
}
|
||||
|
||||
pxl8_mat4 pxl8_mat4_scale(f32 x, f32 y, f32 z) {
|
||||
pxl8_mat4 mat = pxl8_mat4_identity();
|
||||
mat.m[0] = x;
|
||||
mat.m[5] = y;
|
||||
mat.m[10] = z;
|
||||
return mat;
|
||||
}
|
||||
|
||||
pxl8_mat4 pxl8_mat4_ortho(f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far) {
|
||||
pxl8_mat4 mat = {0};
|
||||
|
||||
mat.m[0] = 2.0f / (right - left);
|
||||
mat.m[5] = 2.0f / (top - bottom);
|
||||
mat.m[10] = -2.0f / (far - near);
|
||||
mat.m[3] = -(right + left) / (right - left);
|
||||
mat.m[7] = -(top + bottom) / (top - bottom);
|
||||
mat.m[11] = -(far + near) / (far - near);
|
||||
mat.m[15] = 1.0f;
|
||||
|
||||
return mat;
|
||||
}
|
||||
|
||||
pxl8_mat4 pxl8_mat4_perspective(f32 fov, f32 aspect, f32 near, f32 far) {
|
||||
pxl8_mat4 mat = {0};
|
||||
f32 tan_half_fov = tanf(fov / 2.0f);
|
||||
|
||||
mat.m[0] = 1.0f / (aspect * tan_half_fov);
|
||||
mat.m[5] = 1.0f / tan_half_fov;
|
||||
mat.m[10] = -(far + near) / (far - near);
|
||||
mat.m[11] = -(2.0f * far * near) / (far - near);
|
||||
mat.m[14] = -1.0f;
|
||||
|
||||
return mat;
|
||||
}
|
||||
|
||||
pxl8_mat4 pxl8_mat4_lookat(pxl8_vec3 eye, pxl8_vec3 center, pxl8_vec3 up) {
|
||||
pxl8_vec3 f = pxl8_vec3_normalize(pxl8_vec3_sub(center, eye));
|
||||
pxl8_vec3 s = pxl8_vec3_normalize(pxl8_vec3_cross(f, up));
|
||||
pxl8_vec3 u = pxl8_vec3_cross(s, f);
|
||||
|
||||
pxl8_mat4 mat = pxl8_mat4_identity();
|
||||
mat.m[0] = s.x;
|
||||
mat.m[1] = s.y;
|
||||
mat.m[2] = s.z;
|
||||
mat.m[4] = u.x;
|
||||
mat.m[5] = u.y;
|
||||
mat.m[6] = u.z;
|
||||
mat.m[8] = -f.x;
|
||||
mat.m[9] = -f.y;
|
||||
mat.m[10] = -f.z;
|
||||
mat.m[3] = -pxl8_vec3_dot(s, eye);
|
||||
mat.m[7] = -pxl8_vec3_dot(u, eye);
|
||||
mat.m[11] = pxl8_vec3_dot(f, eye);
|
||||
|
||||
return mat;
|
||||
}
|
||||
54
src/pxl8_math.h
Normal file
54
src/pxl8_math.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_types.h"
|
||||
|
||||
typedef struct pxl8_vec2 {
|
||||
f32 x, y;
|
||||
} pxl8_vec2;
|
||||
|
||||
typedef struct pxl8_vec3 {
|
||||
f32 x, y, z;
|
||||
} pxl8_vec3;
|
||||
|
||||
typedef struct pxl8_vec4 {
|
||||
f32 x, y, z, w;
|
||||
} pxl8_vec4;
|
||||
|
||||
typedef struct pxl8_mat4 {
|
||||
f32 m[16];
|
||||
} pxl8_mat4;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
pxl8_vec2 pxl8_vec2_add(pxl8_vec2 a, pxl8_vec2 b);
|
||||
f32 pxl8_vec2_dot(pxl8_vec2 a, pxl8_vec2 b);
|
||||
f32 pxl8_vec2_length(pxl8_vec2 v);
|
||||
pxl8_vec2 pxl8_vec2_normalize(pxl8_vec2 v);
|
||||
pxl8_vec2 pxl8_vec2_scale(pxl8_vec2 v, f32 s);
|
||||
pxl8_vec2 pxl8_vec2_sub(pxl8_vec2 a, pxl8_vec2 b);
|
||||
|
||||
pxl8_vec3 pxl8_vec3_add(pxl8_vec3 a, pxl8_vec3 b);
|
||||
pxl8_vec3 pxl8_vec3_cross(pxl8_vec3 a, pxl8_vec3 b);
|
||||
f32 pxl8_vec3_dot(pxl8_vec3 a, pxl8_vec3 b);
|
||||
f32 pxl8_vec3_length(pxl8_vec3 v);
|
||||
pxl8_vec3 pxl8_vec3_normalize(pxl8_vec3 v);
|
||||
pxl8_vec3 pxl8_vec3_scale(pxl8_vec3 v, f32 s);
|
||||
pxl8_vec3 pxl8_vec3_sub(pxl8_vec3 a, pxl8_vec3 b);
|
||||
|
||||
pxl8_mat4 pxl8_mat4_identity(void);
|
||||
pxl8_mat4 pxl8_mat4_lookat(pxl8_vec3 eye, pxl8_vec3 center, pxl8_vec3 up);
|
||||
pxl8_mat4 pxl8_mat4_multiply(pxl8_mat4 a, pxl8_mat4 b);
|
||||
pxl8_vec4 pxl8_mat4_multiply_vec4(pxl8_mat4 m, pxl8_vec4 v);
|
||||
pxl8_mat4 pxl8_mat4_ortho(f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far);
|
||||
pxl8_mat4 pxl8_mat4_perspective(f32 fov, f32 aspect, f32 near, f32 far);
|
||||
pxl8_mat4 pxl8_mat4_rotate_x(f32 angle);
|
||||
pxl8_mat4 pxl8_mat4_rotate_y(f32 angle);
|
||||
pxl8_mat4 pxl8_mat4_rotate_z(f32 angle);
|
||||
pxl8_mat4 pxl8_mat4_scale(f32 x, f32 y, f32 z);
|
||||
pxl8_mat4 pxl8_mat4_translate(f32 x, f32 y, f32 z);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
543
src/pxl8_script.c
Normal file
543
src/pxl8_script.c
Normal file
|
|
@ -0,0 +1,543 @@
|
|||
#include <dirent.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <lauxlib.h>
|
||||
#include <lua.h>
|
||||
#include <lualib.h>
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include "pxl8_macros.h"
|
||||
#include "pxl8_script.h"
|
||||
#include "pxl8_vfx.h"
|
||||
|
||||
struct pxl8_script {
|
||||
lua_State* L;
|
||||
pxl8_gfx* gfx;
|
||||
pxl8_input_state* input;
|
||||
char last_error[1024];
|
||||
char main_path[256];
|
||||
char watch_dir[256];
|
||||
time_t latest_mod_time;
|
||||
};
|
||||
|
||||
static const char* pxl8_ffi_cdefs =
|
||||
"typedef uint8_t u8;\n"
|
||||
"typedef uint16_t u16;\n"
|
||||
"typedef uint32_t u32;\n"
|
||||
"typedef uint64_t u64;\n"
|
||||
"typedef int8_t i8;\n"
|
||||
"typedef int16_t i16;\n"
|
||||
"typedef int32_t i32;\n"
|
||||
"typedef int64_t i64;\n"
|
||||
"typedef float f32;\n"
|
||||
"typedef double f64;\n"
|
||||
"typedef struct pxl8_gfx pxl8_gfx;\n"
|
||||
"typedef struct { int x, y, w, h; } pxl8_rect;\n"
|
||||
"typedef struct { int x, y; } pxl8_point;\n"
|
||||
"\n"
|
||||
"i32 pxl8_gfx_get_height(pxl8_gfx* ctx);\n"
|
||||
"i32 pxl8_gfx_get_width(pxl8_gfx* ctx);\n"
|
||||
"void pxl8_circle(pxl8_gfx* ctx, i32 x, i32 y, i32 r, u32 color);\n"
|
||||
"void pxl8_circle_fill(pxl8_gfx* ctx, i32 x, i32 y, i32 r, u32 color);\n"
|
||||
"void pxl8_clr(pxl8_gfx* ctx, u32 color);\n"
|
||||
"u32 pxl8_get_pixel(pxl8_gfx* ctx, i32 x, i32 y);\n"
|
||||
"void pxl8_line(pxl8_gfx* ctx, i32 x0, i32 y0, i32 x1, i32 y1, u32 color);\n"
|
||||
"void pxl8_pixel(pxl8_gfx* ctx, i32 x, i32 y, u32 color);\n"
|
||||
"void pxl8_rect(pxl8_gfx* ctx, i32 x, i32 y, i32 w, i32 h, u32 color);\n"
|
||||
"void pxl8_rect_fill(pxl8_gfx* ctx, i32 x, i32 y, i32 w, i32 h, u32 color);\n"
|
||||
"void pxl8_sprite(pxl8_gfx* ctx, i32 id, i32 x, i32 y, i32 w, i32 h, bool flip_x, bool flip_y);\n"
|
||||
"void pxl8_text(pxl8_gfx* ctx, const char* str, i32 x, i32 y, u32 color);\n"
|
||||
"void pxl8_gfx_color_ramp(pxl8_gfx* ctx, u8 start, u8 count, u32 from_color, u32 to_color);\n"
|
||||
"void pxl8_gfx_fade_palette(pxl8_gfx* ctx, u8 start, u8 count, f32 amount, u32 target_color);\n"
|
||||
"i32 pxl8_gfx_load_palette(pxl8_gfx* ctx, const char* filepath);\n"
|
||||
"i32 pxl8_gfx_load_sprite(pxl8_gfx* ctx, const char* filepath, u32* sprite_id);\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"
|
||||
"void pxl8_lua_debug(const char* msg);\n"
|
||||
"void pxl8_lua_error(const char* msg);\n"
|
||||
"void pxl8_lua_info(const char* msg);\n"
|
||||
"void pxl8_lua_trace(const char* msg);\n"
|
||||
"void pxl8_lua_warn(const char* msg);\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_tilemap pxl8_tilemap;\n"
|
||||
"typedef struct pxl8_tilesheet pxl8_tilesheet;\n"
|
||||
"pxl8_tilemap* pxl8_tilemap_create(u32 width, u32 height, u32 tile_size);\n"
|
||||
"void pxl8_tilemap_destroy(pxl8_tilemap* tilemap);\n"
|
||||
"bool pxl8_tilemap_check_collision(const pxl8_tilemap* tilemap, i32 x, i32 y, i32 w, i32 h);\n"
|
||||
"u16 pxl8_tilemap_get_tile_id(const pxl8_tilemap* tilemap, u32 layer, u32 x, u32 y);\n"
|
||||
"bool pxl8_tilemap_is_solid(const pxl8_tilemap* tilemap, u32 x, u32 y);\n"
|
||||
"void pxl8_tilemap_render(const pxl8_tilemap* tilemap, pxl8_gfx* gfx);\n"
|
||||
"void pxl8_tilemap_render_layer(const pxl8_tilemap* tilemap, pxl8_gfx* gfx, u32 layer);\n"
|
||||
"void pxl8_tilemap_set_camera(pxl8_tilemap* tilemap, i32 x, i32 y);\n"
|
||||
"void pxl8_tilemap_set_tile(pxl8_tilemap* tilemap, u32 layer, u32 x, u32 y, u16 tile_id, u8 flags);\n"
|
||||
"i32 pxl8_tilemap_set_tilesheet(pxl8_tilemap* tilemap, pxl8_tilesheet* tilesheet);\n"
|
||||
"pxl8_tilesheet* pxl8_tilesheet_create(u32 tile_size);\n"
|
||||
"void pxl8_tilesheet_destroy(pxl8_tilesheet* tilesheet);\n"
|
||||
"i32 pxl8_tilesheet_load(pxl8_tilesheet* tilesheet, const char* filepath, pxl8_gfx* gfx);\n"
|
||||
"\n"
|
||||
"typedef struct {\n"
|
||||
" float angle;\n"
|
||||
" float ax, ay, az;\n"
|
||||
" unsigned int color;\n"
|
||||
" unsigned int end_color;\n"
|
||||
" unsigned char flags;\n"
|
||||
" float life;\n"
|
||||
" float max_life;\n"
|
||||
" float size;\n"
|
||||
" float spin;\n"
|
||||
" unsigned int start_color;\n"
|
||||
" float vx, vy, vz;\n"
|
||||
" float x, y, z;\n"
|
||||
"} pxl8_particle;\n"
|
||||
"\n"
|
||||
"typedef struct {\n"
|
||||
" float amplitude;\n"
|
||||
" float base_y;\n"
|
||||
" unsigned int color;\n"
|
||||
" unsigned int fade_color;\n"
|
||||
" int height;\n"
|
||||
" float phase;\n"
|
||||
" float speed;\n"
|
||||
"} pxl8_raster_bar;\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_particles pxl8_particles;\n"
|
||||
"pxl8_particles* pxl8_particles_create(u32 max_count);\n"
|
||||
"void pxl8_particles_destroy(pxl8_particles* particles);\n"
|
||||
"void pxl8_particles_clear(pxl8_particles* particles);\n"
|
||||
"void pxl8_particles_emit(pxl8_particles* particles, u32 count);\n"
|
||||
"void pxl8_particles_render(pxl8_particles* particles, pxl8_gfx* gfx);\n"
|
||||
"void pxl8_particles_update(pxl8_particles* particles, float dt);\n"
|
||||
"void pxl8_vfx_explosion(pxl8_particles* particles, int x, int y, unsigned int color, float force);\n"
|
||||
"void pxl8_vfx_fire(pxl8_particles* particles, int x, int y, int width, unsigned char palette_start);\n"
|
||||
"void pxl8_vfx_plasma(pxl8_gfx* ctx, f32 time, f32 scale1, f32 scale2, u8 palette_offset);\n"
|
||||
"void pxl8_vfx_rain(pxl8_particles* particles, int width, float wind);\n"
|
||||
"void pxl8_vfx_raster_bars(pxl8_gfx* ctx, pxl8_raster_bar* bars, u32 bar_count, f32 time);\n"
|
||||
"void pxl8_vfx_rotozoom(pxl8_gfx* ctx, f32 angle, f32 zoom, i32 cx, i32 cy);\n"
|
||||
"void pxl8_vfx_smoke(pxl8_particles* particles, int x, int y, unsigned char color);\n"
|
||||
"void pxl8_vfx_snow(pxl8_particles* particles, int width, float wind);\n"
|
||||
"void pxl8_vfx_sparks(pxl8_particles* particles, int x, int y, unsigned int color);\n"
|
||||
"void pxl8_vfx_starfield(pxl8_particles* particles, float speed, float spread);\n"
|
||||
"void pxl8_vfx_tunnel(pxl8_gfx* ctx, f32 time, f32 speed, f32 twist);\n"
|
||||
"void pxl8_vfx_water_ripple(pxl8_gfx* ctx, f32* height_map, i32 drop_x, i32 drop_y, f32 damping);\n"
|
||||
"\n"
|
||||
"typedef struct { float x, y, z; } pxl8_vec3;\n"
|
||||
"typedef struct { float x, y, z, w; } pxl8_vec4;\n"
|
||||
"typedef struct { float m[16]; } pxl8_mat4;\n"
|
||||
"\n"
|
||||
"void pxl8_3d_clear_zbuffer(pxl8_gfx* gfx);\n"
|
||||
"void pxl8_3d_draw_line_3d(pxl8_gfx* gfx, pxl8_vec3 p0, pxl8_vec3 p1, u32 color);\n"
|
||||
"void pxl8_3d_draw_triangle_raw(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, pxl8_vec3 v2, u32 color);\n"
|
||||
"void pxl8_3d_set_backface_culling(pxl8_gfx* gfx, bool culling);\n"
|
||||
"void pxl8_3d_set_model(pxl8_gfx* gfx, pxl8_mat4 mat);\n"
|
||||
"void pxl8_3d_set_projection(pxl8_gfx* gfx, pxl8_mat4 mat);\n"
|
||||
"void pxl8_3d_set_view(pxl8_gfx* gfx, pxl8_mat4 mat);\n"
|
||||
"void pxl8_3d_set_wireframe(pxl8_gfx* gfx, bool wireframe);\n"
|
||||
"pxl8_mat4 pxl8_mat4_identity(void);\n"
|
||||
"pxl8_mat4 pxl8_mat4_lookat(pxl8_vec3 eye, pxl8_vec3 center, pxl8_vec3 up);\n"
|
||||
"pxl8_mat4 pxl8_mat4_multiply(pxl8_mat4 a, pxl8_mat4 b);\n"
|
||||
"pxl8_mat4 pxl8_mat4_ortho(float left, float right, float bottom, float top, float near, float far);\n"
|
||||
"pxl8_mat4 pxl8_mat4_perspective(float fov, float aspect, float near, float far);\n"
|
||||
"pxl8_mat4 pxl8_mat4_rotate_x(float angle);\n"
|
||||
"pxl8_mat4 pxl8_mat4_rotate_y(float angle);\n"
|
||||
"pxl8_mat4 pxl8_mat4_rotate_z(float angle);\n"
|
||||
"pxl8_mat4 pxl8_mat4_scale(float x, float y, float z);\n"
|
||||
"pxl8_mat4 pxl8_mat4_translate(float x, float y, float z);\n";
|
||||
|
||||
void pxl8_lua_info(const char* msg) {
|
||||
pxl8_info("%s", msg);
|
||||
}
|
||||
|
||||
void pxl8_lua_warn(const char* msg) {
|
||||
pxl8_warn("%s", msg);
|
||||
}
|
||||
|
||||
void pxl8_lua_error(const char* msg) {
|
||||
pxl8_error("%s", msg);
|
||||
}
|
||||
|
||||
void pxl8_lua_debug(const char* msg) {
|
||||
pxl8_debug("%s", msg);
|
||||
}
|
||||
|
||||
void pxl8_lua_trace(const char* msg) {
|
||||
pxl8_trace("%s", msg);
|
||||
}
|
||||
|
||||
static void pxl8_script_set_error(pxl8_script* script, const char* error) {
|
||||
if (!script) return;
|
||||
snprintf(script->last_error, sizeof(script->last_error), "%s", error ? error : "Unknown error");
|
||||
}
|
||||
|
||||
const char* pxl8_script_get_last_error(pxl8_script* script) {
|
||||
return script ? script->last_error : "Invalid script context";
|
||||
}
|
||||
|
||||
pxl8_script* pxl8_script_create(void) {
|
||||
pxl8_script* script = SDL_calloc(1, sizeof(pxl8_script));
|
||||
if (!script) return NULL;
|
||||
|
||||
script->L = luaL_newstate();
|
||||
if (!script->L) {
|
||||
SDL_free(script);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
luaL_openlibs(script->L);
|
||||
|
||||
lua_getglobal(script->L, "require");
|
||||
lua_pushstring(script->L, "ffi");
|
||||
if (lua_pcall(script->L, 1, 1, 0) != 0) {
|
||||
pxl8_script_set_error(script, lua_tostring(script->L, -1));
|
||||
pxl8_script_destroy(script);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lua_getfield(script->L, -1, "cdef");
|
||||
lua_pushstring(script->L, pxl8_ffi_cdefs);
|
||||
if (lua_pcall(script->L, 1, 0, 0) != 0) {
|
||||
pxl8_script_set_error(script, lua_tostring(script->L, -1));
|
||||
pxl8_script_destroy(script);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lua_pop(script->L, 1);
|
||||
|
||||
lua_getglobal(script->L, "package");
|
||||
lua_getfield(script->L, -1, "path");
|
||||
const char* current_path = lua_tostring(script->L, -1);
|
||||
lua_pop(script->L, 1);
|
||||
|
||||
lua_pushfstring(script->L, "%s;src/lua/?.lua", current_path);
|
||||
lua_setfield(script->L, -2, "path");
|
||||
lua_pop(script->L, 1);
|
||||
|
||||
if (luaL_dofile(script->L, "lib/fennel/fennel.lua") == 0) {
|
||||
lua_setglobal(script->L, "fennel");
|
||||
}
|
||||
|
||||
script->last_error[0] = '\0';
|
||||
return script;
|
||||
}
|
||||
|
||||
void pxl8_script_destroy(pxl8_script* script) {
|
||||
if (!script) return;
|
||||
if (script->L) {
|
||||
lua_close(script->L);
|
||||
}
|
||||
SDL_free(script);
|
||||
}
|
||||
|
||||
void pxl8_script_set_gfx(pxl8_script* script, pxl8_gfx* gfx) {
|
||||
if (!script) return;
|
||||
script->gfx = gfx;
|
||||
if (script->L && gfx) {
|
||||
lua_pushlightuserdata(script->L, gfx);
|
||||
lua_setglobal(script->L, "_pxl8_gfx");
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_script_set_input(pxl8_script* script, pxl8_input_state* input) {
|
||||
if (!script) return;
|
||||
script->input = input;
|
||||
if (script->L && input) {
|
||||
lua_pushlightuserdata(script->L, input);
|
||||
lua_setglobal(script->L, "_pxl8_input");
|
||||
}
|
||||
}
|
||||
|
||||
pxl8_result pxl8_script_run_file(pxl8_script* script, const char* filename) {
|
||||
if (!script || !script->L || !filename) return PXL8_ERROR_NULL_POINTER;
|
||||
|
||||
char* filename_copy = strdup(filename);
|
||||
char* last_slash = strrchr(filename_copy, '/');
|
||||
const char* basename = filename;
|
||||
|
||||
if (last_slash) {
|
||||
*last_slash = '\0';
|
||||
char* script_dir = realpath(filename_copy, NULL);
|
||||
char* original_cwd = getcwd(NULL, 0);
|
||||
if (script_dir && original_cwd) {
|
||||
chdir(script_dir);
|
||||
pxl8_script_set_cart_path(script, script_dir, original_cwd);
|
||||
basename = last_slash + 1;
|
||||
}
|
||||
free(script_dir);
|
||||
free(original_cwd);
|
||||
}
|
||||
|
||||
pxl8_result result = PXL8_OK;
|
||||
if (luaL_dofile(script->L, basename) != 0) {
|
||||
pxl8_script_set_error(script, lua_tostring(script->L, -1));
|
||||
lua_pop(script->L, 1);
|
||||
result = PXL8_ERROR_SCRIPT_ERROR;
|
||||
} else {
|
||||
script->last_error[0] = '\0';
|
||||
}
|
||||
|
||||
free(filename_copy);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void pxl8_script_set_cart_path(pxl8_script* script, const char* cart_path, const char* original_cwd) {
|
||||
if (!script || !script->L || !cart_path || !original_cwd) return;
|
||||
|
||||
lua_getglobal(script->L, "package");
|
||||
lua_getfield(script->L, -1, "path");
|
||||
const char* current_path = lua_tostring(script->L, -1);
|
||||
|
||||
char new_path[2048];
|
||||
snprintf(new_path, sizeof(new_path),
|
||||
"%s/?.lua;%s/?/init.lua;%s/src/?.lua;%s/src/?/init.lua;%s/src/lua/?.lua;%s",
|
||||
cart_path, cart_path, cart_path, cart_path, original_cwd,
|
||||
current_path ? current_path : "");
|
||||
|
||||
lua_pushstring(script->L, new_path);
|
||||
lua_setfield(script->L, -3, "path");
|
||||
lua_pop(script->L, 2);
|
||||
|
||||
lua_getglobal(script->L, "fennel");
|
||||
if (!lua_isnil(script->L, -1)) {
|
||||
lua_getfield(script->L, -1, "path");
|
||||
const char* fennel_path = lua_tostring(script->L, -1);
|
||||
|
||||
snprintf(new_path, sizeof(new_path),
|
||||
"%s/?.fnl;%s/?/init.fnl;%s/src/?.fnl;%s/src/?/init.fnl;%s",
|
||||
cart_path, cart_path, cart_path, cart_path,
|
||||
fennel_path ? fennel_path : "");
|
||||
|
||||
lua_pushstring(script->L, new_path);
|
||||
lua_setfield(script->L, -3, "path");
|
||||
lua_pop(script->L, 1);
|
||||
}
|
||||
lua_pop(script->L, 1);
|
||||
}
|
||||
|
||||
pxl8_result pxl8_script_run_fennel_file(pxl8_script* script, const char* filename) {
|
||||
if (!script || !script->L || !filename) return PXL8_ERROR_NULL_POINTER;
|
||||
|
||||
char* filename_copy = strdup(filename);
|
||||
char* last_slash = strrchr(filename_copy, '/');
|
||||
const char* basename = filename;
|
||||
|
||||
if (last_slash) {
|
||||
*last_slash = '\0';
|
||||
char* script_dir = realpath(filename_copy, NULL);
|
||||
char* original_cwd = getcwd(NULL, 0);
|
||||
if (script_dir && original_cwd) {
|
||||
chdir(script_dir);
|
||||
pxl8_script_set_cart_path(script, script_dir, original_cwd);
|
||||
basename = last_slash + 1;
|
||||
}
|
||||
free(script_dir);
|
||||
free(original_cwd);
|
||||
}
|
||||
|
||||
lua_getglobal(script->L, "fennel");
|
||||
if (lua_isnil(script->L, -1)) {
|
||||
pxl8_script_set_error(script, "Fennel not loaded");
|
||||
lua_pop(script->L, 1);
|
||||
free(filename_copy);
|
||||
return PXL8_ERROR_SCRIPT_ERROR;
|
||||
}
|
||||
|
||||
lua_getfield(script->L, -1, "dofile");
|
||||
lua_pushstring(script->L, basename);
|
||||
|
||||
pxl8_result result = PXL8_OK;
|
||||
if (lua_pcall(script->L, 1, 0, 0) != 0) {
|
||||
pxl8_script_set_error(script, lua_tostring(script->L, -1));
|
||||
lua_pop(script->L, 1);
|
||||
result = PXL8_ERROR_SCRIPT_ERROR;
|
||||
} else {
|
||||
script->last_error[0] = '\0';
|
||||
}
|
||||
|
||||
lua_pop(script->L, 1);
|
||||
free(filename_copy);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
pxl8_result pxl8_script_eval(pxl8_script* script, const char* code) {
|
||||
if (!script || !script->L || !code) return PXL8_ERROR_NULL_POINTER;
|
||||
|
||||
lua_getglobal(script->L, "fennel");
|
||||
if (lua_isnil(script->L, -1)) {
|
||||
pxl8_script_set_error(script, "Fennel not loaded");
|
||||
lua_pop(script->L, 1);
|
||||
return PXL8_ERROR_SCRIPT_ERROR;
|
||||
}
|
||||
|
||||
lua_getfield(script->L, -1, "eval");
|
||||
lua_pushstring(script->L, code);
|
||||
|
||||
if (lua_pcall(script->L, 1, 1, 0) != 0) {
|
||||
pxl8_script_set_error(script, lua_tostring(script->L, -1));
|
||||
lua_remove(script->L, -2);
|
||||
return PXL8_ERROR_SCRIPT_ERROR;
|
||||
}
|
||||
|
||||
lua_remove(script->L, -2);
|
||||
script->last_error[0] = '\0';
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
||||
pxl8_result pxl8_script_load_module(pxl8_script* script, const char* module_name) {
|
||||
if (!script || !script->L || !module_name) return PXL8_ERROR_NULL_POINTER;
|
||||
|
||||
lua_getglobal(script->L, "require");
|
||||
lua_pushstring(script->L, module_name);
|
||||
if (lua_pcall(script->L, 1, 1, 0) != 0) {
|
||||
pxl8_script_set_error(script, lua_tostring(script->L, -1));
|
||||
lua_pop(script->L, 1);
|
||||
return PXL8_ERROR_SCRIPT_ERROR;
|
||||
}
|
||||
|
||||
lua_setglobal(script->L, module_name);
|
||||
script->last_error[0] = '\0';
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
||||
pxl8_result pxl8_script_call_function(pxl8_script* script, const char* name) {
|
||||
if (!script || !script->L || !name) return PXL8_ERROR_NULL_POINTER;
|
||||
|
||||
lua_getglobal(script->L, name);
|
||||
if (!lua_isfunction(script->L, -1)) {
|
||||
lua_pop(script->L, 1);
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
||||
if (lua_pcall(script->L, 0, 0, 0) != 0) {
|
||||
pxl8_script_set_error(script, lua_tostring(script->L, -1));
|
||||
lua_pop(script->L, 1);
|
||||
return PXL8_ERROR_SCRIPT_ERROR;
|
||||
}
|
||||
|
||||
script->last_error[0] = '\0';
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
||||
pxl8_result pxl8_script_call_function_f32(pxl8_script* script, const char* name, f32 arg) {
|
||||
if (!script || !script->L || !name) return PXL8_ERROR_NULL_POINTER;
|
||||
|
||||
lua_getglobal(script->L, name);
|
||||
if (!lua_isfunction(script->L, -1)) {
|
||||
lua_pop(script->L, 1);
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
||||
lua_pushnumber(script->L, arg);
|
||||
if (lua_pcall(script->L, 1, 0, 0) != 0) {
|
||||
pxl8_script_set_error(script, lua_tostring(script->L, -1));
|
||||
lua_pop(script->L, 1);
|
||||
return PXL8_ERROR_SCRIPT_ERROR;
|
||||
}
|
||||
|
||||
script->last_error[0] = '\0';
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
||||
static time_t get_file_mod_time(const char* path) {
|
||||
struct stat file_stat;
|
||||
if (stat(path, &file_stat) == 0) {
|
||||
return file_stat.st_mtime;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static time_t get_latest_script_mod_time(const char* dir_path) {
|
||||
DIR* dir = opendir(dir_path);
|
||||
if (!dir) return 0;
|
||||
|
||||
time_t latest = 0;
|
||||
struct dirent* entry;
|
||||
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
if (entry->d_name[0] == '.') continue;
|
||||
|
||||
size_t len = strlen(entry->d_name);
|
||||
bool is_script = (len > 4 && strcmp(entry->d_name + len - 4, ".fnl") == 0) ||
|
||||
(len > 4 && strcmp(entry->d_name + len - 4, ".lua") == 0);
|
||||
|
||||
if (is_script) {
|
||||
char full_path[512];
|
||||
snprintf(full_path, sizeof(full_path), "%s/%s", dir_path, entry->d_name);
|
||||
time_t mod_time = get_file_mod_time(full_path);
|
||||
if (mod_time > latest) {
|
||||
latest = mod_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
return latest;
|
||||
}
|
||||
|
||||
pxl8_result pxl8_script_load_main(pxl8_script* script, const char* path) {
|
||||
if (!script || !path) return PXL8_ERROR_NULL_POINTER;
|
||||
|
||||
strncpy(script->main_path, path, sizeof(script->main_path) - 1);
|
||||
script->main_path[sizeof(script->main_path) - 1] = '\0';
|
||||
|
||||
char* last_slash = strrchr(script->main_path, '/');
|
||||
if (last_slash) {
|
||||
size_t dir_len = last_slash - script->main_path;
|
||||
strncpy(script->watch_dir, script->main_path, dir_len);
|
||||
script->watch_dir[dir_len] = '\0';
|
||||
} else {
|
||||
strcpy(script->watch_dir, ".");
|
||||
}
|
||||
|
||||
script->latest_mod_time = get_latest_script_mod_time(script->watch_dir);
|
||||
|
||||
const char* ext = strrchr(path, '.');
|
||||
pxl8_result result = PXL8_ERROR_INVALID_FORMAT;
|
||||
|
||||
if (ext && strcmp(ext, ".fnl") == 0) {
|
||||
result = pxl8_script_run_fennel_file(script, path);
|
||||
} else if (ext && strcmp(ext, ".lua") == 0) {
|
||||
result = pxl8_script_run_file(script, path);
|
||||
} else {
|
||||
pxl8_script_set_error(script, "Unknown script type (expected .fnl or .lua)");
|
||||
return PXL8_ERROR_INVALID_FORMAT;
|
||||
}
|
||||
|
||||
if (result == PXL8_OK) {
|
||||
pxl8_info("Loaded script: %s", path);
|
||||
pxl8_script_call_function(script, "init");
|
||||
} else {
|
||||
pxl8_warn("Failed to load script: %s", script->last_error);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool pxl8_script_check_reload(pxl8_script* script) {
|
||||
if (!script || script->main_path[0] == '\0') return false;
|
||||
|
||||
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) {
|
||||
pxl8_info("Script files modified, reloading: %s", script->main_path);
|
||||
script->latest_mod_time = current_mod_time;
|
||||
|
||||
const char* ext = strrchr(script->main_path, '.');
|
||||
if (ext && strcmp(ext, ".fnl") == 0) {
|
||||
if (pxl8_script_run_fennel_file(script, script->main_path) == PXL8_OK) {
|
||||
pxl8_script_call_function(script, "init");
|
||||
return true;
|
||||
}
|
||||
} else if (ext && strcmp(ext, ".lua") == 0) {
|
||||
if (pxl8_script_run_file(script, script->main_path) == PXL8_OK) {
|
||||
pxl8_script_call_function(script, "init");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
32
src/pxl8_script.h
Normal file
32
src/pxl8_script.h
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_gfx.h"
|
||||
#include "pxl8_types.h"
|
||||
|
||||
typedef struct pxl8_script pxl8_script;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
pxl8_script* pxl8_script_create(void);
|
||||
void pxl8_script_destroy(pxl8_script* script);
|
||||
|
||||
const char* pxl8_script_get_last_error(pxl8_script* script);
|
||||
void pxl8_script_set_cart_path(pxl8_script* script, const char* cart_path, const char* original_cwd);
|
||||
void pxl8_script_set_gfx(pxl8_script* script, pxl8_gfx* gfx);
|
||||
void pxl8_script_set_input(pxl8_script* script, pxl8_input_state* input);
|
||||
|
||||
pxl8_result pxl8_script_call_function(pxl8_script* script, const char* name);
|
||||
pxl8_result pxl8_script_call_function_f32(pxl8_script* script, const char* name, f32 arg);
|
||||
pxl8_result pxl8_script_eval(pxl8_script* script, const char* code);
|
||||
pxl8_result pxl8_script_load_module(pxl8_script* script, const char* module_name);
|
||||
pxl8_result pxl8_script_run_fennel_file(pxl8_script* script, const char* filename);
|
||||
pxl8_result pxl8_script_run_file(pxl8_script* script, const char* filename);
|
||||
|
||||
pxl8_result pxl8_script_load_main(pxl8_script* script, const char* path);
|
||||
bool pxl8_script_check_reload(pxl8_script* script);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
127
src/pxl8_simd.h
127
src/pxl8_simd.h
|
|
@ -187,3 +187,130 @@ static inline pxl8_simd_vec pxl8_simd_blendv_u32(pxl8_simd_vec src, pxl8_simd_ve
|
|||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
typedef union {
|
||||
#if defined(PXL8_SIMD_AVX2)
|
||||
__m256 avx2;
|
||||
__m128 sse;
|
||||
#elif defined(PXL8_SIMD_SSE2) || defined(__SSE__)
|
||||
__m128 sse;
|
||||
#elif defined(PXL8_SIMD_NEON)
|
||||
float32x4_t neon;
|
||||
#endif
|
||||
f32 f32_array[8];
|
||||
} pxl8_simd_vec_f32;
|
||||
|
||||
static inline pxl8_simd_vec_f32 pxl8_simd_set_f32(f32 x, f32 y, f32 z, f32 w) {
|
||||
pxl8_simd_vec_f32 result;
|
||||
#if defined(PXL8_SIMD_AVX2)
|
||||
result.avx2 = _mm256_set_ps(0, 0, 0, 0, w, z, y, x);
|
||||
#elif defined(PXL8_SIMD_SSE2) || defined(__SSE__)
|
||||
result.sse = _mm_set_ps(w, z, y, x);
|
||||
#elif defined(PXL8_SIMD_NEON)
|
||||
f32 data[4] = {x, y, z, w};
|
||||
result.neon = vld1q_f32(data);
|
||||
#else
|
||||
result.f32_array[0] = x;
|
||||
result.f32_array[1] = y;
|
||||
result.f32_array[2] = z;
|
||||
result.f32_array[3] = w;
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline pxl8_simd_vec_f32 pxl8_simd_add_f32(pxl8_simd_vec_f32 a, pxl8_simd_vec_f32 b) {
|
||||
pxl8_simd_vec_f32 result;
|
||||
#if defined(PXL8_SIMD_AVX2)
|
||||
result.avx2 = _mm256_add_ps(a.avx2, b.avx2);
|
||||
#elif defined(PXL8_SIMD_SSE2) || defined(__SSE__)
|
||||
result.sse = _mm_add_ps(a.sse, b.sse);
|
||||
#elif defined(PXL8_SIMD_NEON)
|
||||
result.neon = vaddq_f32(a.neon, b.neon);
|
||||
#else
|
||||
for (i32 i = 0; i < 4; i++) result.f32_array[i] = a.f32_array[i] + b.f32_array[i];
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline pxl8_simd_vec_f32 pxl8_simd_sub_f32(pxl8_simd_vec_f32 a, pxl8_simd_vec_f32 b) {
|
||||
pxl8_simd_vec_f32 result;
|
||||
#if defined(PXL8_SIMD_AVX2)
|
||||
result.avx2 = _mm256_sub_ps(a.avx2, b.avx2);
|
||||
#elif defined(PXL8_SIMD_SSE2) || defined(__SSE__)
|
||||
result.sse = _mm_sub_ps(a.sse, b.sse);
|
||||
#elif defined(PXL8_SIMD_NEON)
|
||||
result.neon = vsubq_f32(a.neon, b.neon);
|
||||
#else
|
||||
for (i32 i = 0; i < 4; i++) result.f32_array[i] = a.f32_array[i] - b.f32_array[i];
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline pxl8_simd_vec_f32 pxl8_simd_mul_f32(pxl8_simd_vec_f32 a, pxl8_simd_vec_f32 b) {
|
||||
pxl8_simd_vec_f32 result;
|
||||
#if defined(PXL8_SIMD_AVX2)
|
||||
result.avx2 = _mm256_mul_ps(a.avx2, b.avx2);
|
||||
#elif defined(PXL8_SIMD_SSE2) || defined(__SSE__)
|
||||
result.sse = _mm_mul_ps(a.sse, b.sse);
|
||||
#elif defined(PXL8_SIMD_NEON)
|
||||
result.neon = vmulq_f32(a.neon, b.neon);
|
||||
#else
|
||||
for (i32 i = 0; i < 4; i++) result.f32_array[i] = a.f32_array[i] * b.f32_array[i];
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline pxl8_simd_vec_f32 pxl8_simd_scale_f32(pxl8_simd_vec_f32 v, f32 s) {
|
||||
pxl8_simd_vec_f32 result;
|
||||
#if defined(PXL8_SIMD_AVX2)
|
||||
result.avx2 = _mm256_mul_ps(v.avx2, _mm256_set1_ps(s));
|
||||
#elif defined(PXL8_SIMD_SSE2) || defined(__SSE__)
|
||||
result.sse = _mm_mul_ps(v.sse, _mm_set1_ps(s));
|
||||
#elif defined(PXL8_SIMD_NEON)
|
||||
result.neon = vmulq_n_f32(v.neon, s);
|
||||
#else
|
||||
for (i32 i = 0; i < 4; i++) result.f32_array[i] = v.f32_array[i] * s;
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline f32 pxl8_simd_dot3_f32(pxl8_simd_vec_f32 a, pxl8_simd_vec_f32 b) {
|
||||
#if defined(PXL8_SIMD_SSE2) || defined(__SSE__)
|
||||
__m128 mul = _mm_mul_ps(a.sse, b.sse);
|
||||
__m128 shuf = _mm_shuffle_ps(mul, mul, _MM_SHUFFLE(2, 1, 0, 3));
|
||||
__m128 sums = _mm_add_ps(mul, shuf);
|
||||
shuf = _mm_movehl_ps(shuf, sums);
|
||||
sums = _mm_add_ss(sums, shuf);
|
||||
return _mm_cvtss_f32(sums);
|
||||
#elif defined(PXL8_SIMD_NEON)
|
||||
float32x4_t mul = vmulq_f32(a.neon, b.neon);
|
||||
float32x2_t sum = vpadd_f32(vget_low_f32(mul), vget_high_f32(mul));
|
||||
sum = vpadd_f32(sum, sum);
|
||||
return vget_lane_f32(sum, 0);
|
||||
#else
|
||||
return a.f32_array[0] * b.f32_array[0] +
|
||||
a.f32_array[1] * b.f32_array[1] +
|
||||
a.f32_array[2] * b.f32_array[2];
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline f32 pxl8_simd_dot4_f32(pxl8_simd_vec_f32 a, pxl8_simd_vec_f32 b) {
|
||||
#if defined(PXL8_SIMD_SSE2) || defined(__SSE__)
|
||||
__m128 mul = _mm_mul_ps(a.sse, b.sse);
|
||||
__m128 shuf = _mm_shuffle_ps(mul, mul, _MM_SHUFFLE(2, 3, 0, 1));
|
||||
__m128 sums = _mm_add_ps(mul, shuf);
|
||||
shuf = _mm_movehl_ps(shuf, sums);
|
||||
sums = _mm_add_ss(sums, shuf);
|
||||
return _mm_cvtss_f32(sums);
|
||||
#elif defined(PXL8_SIMD_NEON)
|
||||
float32x4_t mul = vmulq_f32(a.neon, b.neon);
|
||||
float32x2_t sum = vpadd_f32(vget_low_f32(mul), vget_high_f32(mul));
|
||||
sum = vpadd_f32(sum, sum);
|
||||
return vget_lane_f32(sum, 0);
|
||||
#else
|
||||
return a.f32_array[0] * b.f32_array[0] +
|
||||
a.f32_array[1] * b.f32_array[1] +
|
||||
a.f32_array[2] * b.f32_array[2] +
|
||||
a.f32_array[3] * b.f32_array[3];
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,27 @@
|
|||
#include "pxl8_tilemap.h"
|
||||
#include "pxl8_tilesheet.h"
|
||||
|
||||
struct pxl8_tilemap_layer {
|
||||
u32 allocated_chunks;
|
||||
u32 chunk_count;
|
||||
pxl8_tile_chunk** chunks;
|
||||
u32 chunks_high;
|
||||
u32 chunks_wide;
|
||||
u8 opacity;
|
||||
bool visible;
|
||||
};
|
||||
|
||||
struct pxl8_tilemap {
|
||||
u32 active_layers;
|
||||
i32 camera_x;
|
||||
i32 camera_y;
|
||||
u32 height;
|
||||
pxl8_tilemap_layer layers[PXL8_MAX_TILE_LAYERS];
|
||||
u32 tile_size;
|
||||
pxl8_tilesheet* tilesheet;
|
||||
u32 width;
|
||||
};
|
||||
|
||||
static inline u32 pxl8_chunk_index(u32 x, u32 y, u32 chunks_wide) {
|
||||
return (y >> 4) * chunks_wide + (x >> 4);
|
||||
}
|
||||
|
|
@ -29,13 +50,14 @@ static pxl8_tile_chunk* pxl8_get_or_create_chunk(pxl8_tilemap_layer* layer, u32
|
|||
return layer->chunks[idx];
|
||||
}
|
||||
|
||||
pxl8_result pxl8_tilemap_init(pxl8_tilemap* tilemap, u32 width, u32 height, u32 tile_size) {
|
||||
if (!tilemap) return PXL8_ERROR_NULL_POINTER;
|
||||
pxl8_tilemap* pxl8_tilemap_create(u32 width, u32 height, u32 tile_size) {
|
||||
if (width > PXL8_MAX_TILEMAP_WIDTH || height > PXL8_MAX_TILEMAP_HEIGHT) {
|
||||
return PXL8_ERROR_INVALID_SIZE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(tilemap, 0, sizeof(pxl8_tilemap));
|
||||
pxl8_tilemap* tilemap = calloc(1, sizeof(pxl8_tilemap));
|
||||
if (!tilemap) return NULL;
|
||||
|
||||
tilemap->width = width;
|
||||
tilemap->height = height;
|
||||
tilemap->tile_size = tile_size ? tile_size : PXL8_TILE_SIZE;
|
||||
|
|
@ -57,14 +79,15 @@ pxl8_result pxl8_tilemap_init(pxl8_tilemap* tilemap, u32 width, u32 height, u32
|
|||
for (u32 j = 0; j < i; j++) {
|
||||
free(tilemap->layers[j].chunks);
|
||||
}
|
||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
free(tilemap);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return PXL8_OK;
|
||||
return tilemap;
|
||||
}
|
||||
|
||||
void pxl8_tilemap_free(pxl8_tilemap* tilemap) {
|
||||
void pxl8_tilemap_destroy(pxl8_tilemap* tilemap) {
|
||||
if (!tilemap) return;
|
||||
|
||||
for (u32 i = 0; i < PXL8_MAX_TILE_LAYERS; i++) {
|
||||
|
|
@ -76,14 +99,14 @@ void pxl8_tilemap_free(pxl8_tilemap* tilemap) {
|
|||
}
|
||||
}
|
||||
free(layer->chunks);
|
||||
layer->chunks = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (tilemap->tilesheet) {
|
||||
pxl8_tilesheet_unref(tilemap->tilesheet);
|
||||
tilemap->tilesheet = NULL;
|
||||
}
|
||||
|
||||
free(tilemap);
|
||||
}
|
||||
|
||||
pxl8_result pxl8_tilemap_set_tilesheet(pxl8_tilemap* tilemap, pxl8_tilesheet* tilesheet) {
|
||||
|
|
@ -96,10 +119,11 @@ pxl8_result pxl8_tilemap_set_tilesheet(pxl8_tilemap* tilemap, pxl8_tilesheet* ti
|
|||
tilemap->tilesheet = tilesheet;
|
||||
pxl8_tilesheet_ref(tilesheet);
|
||||
|
||||
if (tilesheet->tile_size != tilemap->tile_size) {
|
||||
u32 tilesheet_size = pxl8_tilesheet_get_tile_size(tilesheet);
|
||||
if (tilesheet_size != tilemap->tile_size) {
|
||||
pxl8_warn("Tilesheet tile size (%d) differs from tilemap tile size (%d)",
|
||||
tilesheet->tile_size, tilemap->tile_size);
|
||||
tilemap->tile_size = tilesheet->tile_size;
|
||||
tilesheet_size, tilemap->tile_size);
|
||||
tilemap->tile_size = tilesheet_size;
|
||||
}
|
||||
|
||||
return PXL8_OK;
|
||||
|
|
@ -155,13 +179,13 @@ void pxl8_tilemap_set_camera(pxl8_tilemap* tilemap, i32 x, i32 y) {
|
|||
tilemap->camera_y = y;
|
||||
}
|
||||
|
||||
void pxl8_tilemap_get_view(const pxl8_tilemap* tilemap, const pxl8_gfx_ctx* gfx, pxl8_tilemap_view* view) {
|
||||
void pxl8_tilemap_get_view(const pxl8_tilemap* tilemap, const pxl8_gfx* gfx, pxl8_tilemap_view* view) {
|
||||
if (!tilemap || !gfx || !view) return;
|
||||
|
||||
view->x = -tilemap->camera_x;
|
||||
view->y = -tilemap->camera_y;
|
||||
view->width = gfx->framebuffer_width;
|
||||
view->height = gfx->framebuffer_height;
|
||||
view->width = pxl8_gfx_get_width(gfx);
|
||||
view->height = pxl8_gfx_get_height(gfx);
|
||||
|
||||
view->tile_start_x = pxl8_max(0, tilemap->camera_x / (i32)tilemap->tile_size);
|
||||
view->tile_start_y = pxl8_max(0, tilemap->camera_y / (i32)tilemap->tile_size);
|
||||
|
|
@ -171,12 +195,12 @@ void pxl8_tilemap_get_view(const pxl8_tilemap* tilemap, const pxl8_gfx_ctx* gfx,
|
|||
(tilemap->camera_y + view->height) / (i32)tilemap->tile_size + 1);
|
||||
}
|
||||
|
||||
void pxl8_tilemap_render_tile(const pxl8_tilemap* tilemap, pxl8_gfx_ctx* gfx, u16 tile_id, i32 x, i32 y, u8 flags) {
|
||||
void pxl8_tilemap_render_tile(const pxl8_tilemap* tilemap, pxl8_gfx* gfx, u16 tile_id, i32 x, i32 y, u8 flags) {
|
||||
if (!tilemap || !gfx || !tilemap->tilesheet) return;
|
||||
pxl8_tilesheet_render_tile(tilemap->tilesheet, gfx, tile_id, x, y, flags);
|
||||
}
|
||||
|
||||
void pxl8_tilemap_render_layer(const pxl8_tilemap* tilemap, pxl8_gfx_ctx* gfx, u32 layer) {
|
||||
void pxl8_tilemap_render_layer(const pxl8_tilemap* tilemap, pxl8_gfx* gfx, u32 layer) {
|
||||
if (!tilemap || !gfx || layer >= tilemap->active_layers) return;
|
||||
if (!tilemap->tilesheet) {
|
||||
pxl8_warn("No tilesheet set for tilemap");
|
||||
|
|
@ -188,8 +212,8 @@ void pxl8_tilemap_render_layer(const pxl8_tilemap* tilemap, pxl8_gfx_ctx* gfx, u
|
|||
|
||||
i32 view_left = tilemap->camera_x / tilemap->tile_size;
|
||||
i32 view_top = tilemap->camera_y / tilemap->tile_size;
|
||||
i32 view_right = (tilemap->camera_x + gfx->framebuffer_width) / tilemap->tile_size + 1;
|
||||
i32 view_bottom = (tilemap->camera_y + gfx->framebuffer_height) / tilemap->tile_size + 1;
|
||||
i32 view_right = (tilemap->camera_x + pxl8_gfx_get_width(gfx)) / tilemap->tile_size + 1;
|
||||
i32 view_bottom = (tilemap->camera_y + pxl8_gfx_get_height(gfx)) / tilemap->tile_size + 1;
|
||||
|
||||
u32 chunk_left = pxl8_max(0, view_left >> 4);
|
||||
u32 chunk_top = pxl8_max(0, view_top >> 4);
|
||||
|
|
@ -233,7 +257,7 @@ void pxl8_tilemap_render_layer(const pxl8_tilemap* tilemap, pxl8_gfx_ctx* gfx, u
|
|||
}
|
||||
}
|
||||
|
||||
void pxl8_tilemap_render(const pxl8_tilemap* tilemap, pxl8_gfx_ctx* gfx) {
|
||||
void pxl8_tilemap_render(const pxl8_tilemap* tilemap, pxl8_gfx* gfx) {
|
||||
if (!tilemap || !gfx) return;
|
||||
|
||||
for (u32 layer = 0; layer < tilemap->active_layers; layer++) {
|
||||
|
|
|
|||
|
|
@ -46,20 +46,20 @@ static inline u8 pxl8_tile_get_palette(pxl8_tile tile) {
|
|||
}
|
||||
|
||||
typedef struct pxl8_tile_animation {
|
||||
u16* frames;
|
||||
u16 frame_count;
|
||||
u16 current_frame;
|
||||
f32 frame_duration;
|
||||
u16 frame_count;
|
||||
u16* frames;
|
||||
f32 time_accumulator;
|
||||
} pxl8_tile_animation;
|
||||
|
||||
typedef struct pxl8_tile_properties {
|
||||
void* user_data;
|
||||
u32 property_flags;
|
||||
i16 collision_offset_x;
|
||||
i16 collision_offset_y;
|
||||
u16 collision_width;
|
||||
u16 collision_height;
|
||||
u16 collision_width;
|
||||
u32 property_flags;
|
||||
void* user_data;
|
||||
} pxl8_tile_properties;
|
||||
|
||||
typedef struct pxl8_autotile_rule {
|
||||
|
|
@ -68,64 +68,46 @@ typedef struct pxl8_autotile_rule {
|
|||
} pxl8_autotile_rule;
|
||||
|
||||
typedef struct pxl8_tile_chunk {
|
||||
pxl8_tile tiles[PXL8_CHUNK_SIZE * PXL8_CHUNK_SIZE];
|
||||
u32 chunk_x;
|
||||
u32 chunk_y;
|
||||
bool empty;
|
||||
pxl8_tile tiles[PXL8_CHUNK_SIZE * PXL8_CHUNK_SIZE];
|
||||
} pxl8_tile_chunk;
|
||||
|
||||
typedef struct pxl8_tilemap_layer {
|
||||
pxl8_tile_chunk** chunks;
|
||||
u32 chunks_wide;
|
||||
u32 chunks_high;
|
||||
u32 chunk_count;
|
||||
u32 allocated_chunks;
|
||||
bool visible;
|
||||
u8 opacity;
|
||||
} pxl8_tilemap_layer;
|
||||
|
||||
typedef struct pxl8_tilemap {
|
||||
pxl8_tilemap_layer layers[PXL8_MAX_TILE_LAYERS];
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 tile_size;
|
||||
u32 active_layers;
|
||||
|
||||
pxl8_tilesheet* tilesheet;
|
||||
|
||||
i32 camera_x;
|
||||
i32 camera_y;
|
||||
} pxl8_tilemap;
|
||||
typedef struct pxl8_tilemap_layer pxl8_tilemap_layer;
|
||||
typedef struct pxl8_tilemap pxl8_tilemap;
|
||||
|
||||
typedef struct pxl8_tilemap_view {
|
||||
i32 x, y;
|
||||
i32 width, height;
|
||||
i32 tile_start_x, tile_start_y;
|
||||
i32 height;
|
||||
i32 tile_end_x, tile_end_y;
|
||||
i32 tile_start_x, tile_start_y;
|
||||
i32 width;
|
||||
i32 x, y;
|
||||
} pxl8_tilemap_view;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
pxl8_tilemap* pxl8_tilemap_create(u32 width, u32 height, u32 tile_size);
|
||||
void pxl8_tilemap_destroy(pxl8_tilemap* tilemap);
|
||||
|
||||
bool pxl8_tilemap_check_collision(const pxl8_tilemap* tilemap, i32 x, i32 y, i32 w, i32 h);
|
||||
void pxl8_tilemap_compress(pxl8_tilemap* tilemap);
|
||||
void pxl8_tilemap_free(pxl8_tilemap* tilemap);
|
||||
u32 pxl8_tilemap_get_memory_usage(const pxl8_tilemap* tilemap);
|
||||
pxl8_tile pxl8_tilemap_get_tile(const pxl8_tilemap* tilemap, u32 layer, u32 x, u32 y);
|
||||
u16 pxl8_tilemap_get_tile_id(const pxl8_tilemap* tilemap, u32 layer, u32 x, u32 y);
|
||||
void pxl8_tilemap_get_view(
|
||||
const pxl8_tilemap* tilemap,
|
||||
const pxl8_gfx_ctx* gfx,
|
||||
const pxl8_gfx* gfx,
|
||||
pxl8_tilemap_view* view
|
||||
);
|
||||
pxl8_result pxl8_tilemap_init(pxl8_tilemap* tilemap, u32 width, u32 height, u32 tile_size);
|
||||
bool pxl8_tilemap_is_solid(const pxl8_tilemap* tilemap, u32 x, u32 y);
|
||||
void pxl8_tilemap_render(const pxl8_tilemap* tilemap, pxl8_gfx_ctx* gfx);
|
||||
void pxl8_tilemap_render_layer(const pxl8_tilemap* tilemap, pxl8_gfx_ctx* gfx, u32 layer);
|
||||
void pxl8_tilemap_render(const pxl8_tilemap* tilemap, pxl8_gfx* gfx);
|
||||
void pxl8_tilemap_render_layer(const pxl8_tilemap* tilemap, pxl8_gfx* gfx, u32 layer);
|
||||
void pxl8_tilemap_render_tile(
|
||||
const pxl8_tilemap* tilemap,
|
||||
pxl8_gfx_ctx* gfx,
|
||||
pxl8_gfx* gfx,
|
||||
u16 tile_id,
|
||||
i32 x,
|
||||
i32 y,
|
||||
|
|
|
|||
|
|
@ -6,17 +6,47 @@
|
|||
#include "pxl8_tilesheet.h"
|
||||
#include "pxl8_tilemap.h"
|
||||
|
||||
void pxl8_tilesheet_free(pxl8_tilesheet* tilesheet) {
|
||||
struct pxl8_tilesheet {
|
||||
u8* data;
|
||||
bool* tile_valid;
|
||||
|
||||
u32 height;
|
||||
u32 tile_size;
|
||||
u32 tiles_per_row;
|
||||
u32 total_tiles;
|
||||
u32 width;
|
||||
|
||||
pxl8_color_mode color_mode;
|
||||
u32 ref_count;
|
||||
|
||||
pxl8_tile_animation* animations;
|
||||
u32 animation_count;
|
||||
|
||||
pxl8_tile_properties* properties;
|
||||
|
||||
pxl8_autotile_rule** autotile_rules;
|
||||
u32* autotile_rule_counts;
|
||||
};
|
||||
|
||||
pxl8_tilesheet* pxl8_tilesheet_create(u32 tile_size) {
|
||||
pxl8_tilesheet* tilesheet = calloc(1, sizeof(pxl8_tilesheet));
|
||||
if (!tilesheet) return NULL;
|
||||
|
||||
tilesheet->tile_size = tile_size;
|
||||
tilesheet->ref_count = 1;
|
||||
|
||||
return tilesheet;
|
||||
}
|
||||
|
||||
void pxl8_tilesheet_destroy(pxl8_tilesheet* tilesheet) {
|
||||
if (!tilesheet) return;
|
||||
|
||||
if (tilesheet->data) {
|
||||
free(tilesheet->data);
|
||||
tilesheet->data = NULL;
|
||||
}
|
||||
|
||||
if (tilesheet->tile_valid) {
|
||||
free(tilesheet->tile_valid);
|
||||
tilesheet->tile_valid = NULL;
|
||||
}
|
||||
|
||||
if (tilesheet->animations) {
|
||||
|
|
@ -26,12 +56,10 @@ void pxl8_tilesheet_free(pxl8_tilesheet* tilesheet) {
|
|||
}
|
||||
}
|
||||
free(tilesheet->animations);
|
||||
tilesheet->animations = NULL;
|
||||
}
|
||||
|
||||
if (tilesheet->properties) {
|
||||
free(tilesheet->properties);
|
||||
tilesheet->properties = NULL;
|
||||
}
|
||||
|
||||
if (tilesheet->autotile_rules) {
|
||||
|
|
@ -42,12 +70,12 @@ void pxl8_tilesheet_free(pxl8_tilesheet* tilesheet) {
|
|||
}
|
||||
free(tilesheet->autotile_rules);
|
||||
free(tilesheet->autotile_rule_counts);
|
||||
tilesheet->autotile_rules = NULL;
|
||||
tilesheet->autotile_rule_counts = NULL;
|
||||
}
|
||||
|
||||
free(tilesheet);
|
||||
}
|
||||
|
||||
pxl8_result pxl8_tilesheet_load(pxl8_tilesheet* tilesheet, const char* filepath, pxl8_gfx_ctx* gfx) {
|
||||
pxl8_result pxl8_tilesheet_load(pxl8_tilesheet* tilesheet, const char* filepath, pxl8_gfx* gfx) {
|
||||
if (!tilesheet || !filepath || !gfx) return PXL8_ERROR_NULL_POINTER;
|
||||
|
||||
pxl8_ase_file ase_file;
|
||||
|
|
@ -74,16 +102,16 @@ pxl8_result pxl8_tilesheet_load(pxl8_tilesheet* tilesheet, const char* filepath,
|
|||
tilesheet->height = height;
|
||||
tilesheet->tiles_per_row = width / tilesheet->tile_size;
|
||||
tilesheet->total_tiles = (width / tilesheet->tile_size) * (height / tilesheet->tile_size);
|
||||
tilesheet->color_mode = gfx->color_mode;
|
||||
tilesheet->color_mode = pxl8_gfx_get_color_mode(gfx);
|
||||
|
||||
size_t data_size = width * height;
|
||||
if (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) {
|
||||
if (pxl8_gfx_get_color_mode(gfx) == PXL8_COLOR_MODE_HICOLOR) {
|
||||
data_size *= 4;
|
||||
}
|
||||
|
||||
tilesheet->data = malloc(data_size);
|
||||
if (!tilesheet->data) {
|
||||
pxl8_ase_free(&ase_file);
|
||||
pxl8_ase_destroy(&ase_file);
|
||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
|
|
@ -95,7 +123,7 @@ pxl8_result pxl8_tilesheet_load(pxl8_tilesheet* tilesheet, const char* filepath,
|
|||
if (!tilesheet->tile_valid) {
|
||||
free(tilesheet->data);
|
||||
tilesheet->data = NULL;
|
||||
pxl8_ase_free(&ase_file);
|
||||
pxl8_ase_destroy(&ase_file);
|
||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
|
|
@ -135,11 +163,11 @@ pxl8_result pxl8_tilesheet_load(pxl8_tilesheet* tilesheet, const char* filepath,
|
|||
filepath, width, height, valid_tiles, tilesheet->total_tiles,
|
||||
tilesheet->tile_size, tilesheet->tile_size);
|
||||
|
||||
pxl8_ase_free(&ase_file);
|
||||
pxl8_ase_destroy(&ase_file);
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
||||
void pxl8_tilesheet_render_tile(const pxl8_tilesheet* tilesheet, pxl8_gfx_ctx* gfx,
|
||||
void pxl8_tilesheet_render_tile(const pxl8_tilesheet* tilesheet, pxl8_gfx* gfx,
|
||||
u16 tile_id, i32 x, i32 y, u8 flags) {
|
||||
if (!tilesheet || !gfx || !tilesheet->data) return;
|
||||
if (tile_id == 0 || tile_id > tilesheet->total_tiles) return;
|
||||
|
|
@ -163,8 +191,8 @@ void pxl8_tilesheet_render_tile(const pxl8_tilesheet* tilesheet, pxl8_gfx_ctx* g
|
|||
i32 screen_x = x + px;
|
||||
i32 screen_y = y + py;
|
||||
|
||||
if (screen_x >= 0 && screen_x < gfx->framebuffer_width &&
|
||||
screen_y >= 0 && screen_y < gfx->framebuffer_height) {
|
||||
if (screen_x >= 0 && screen_x < pxl8_gfx_get_width(gfx) &&
|
||||
screen_y >= 0 && screen_y < pxl8_gfx_get_height(gfx)) {
|
||||
pxl8_pixel(gfx, screen_x, screen_y, color_idx);
|
||||
}
|
||||
}
|
||||
|
|
@ -186,7 +214,7 @@ void pxl8_tilesheet_unref(pxl8_tilesheet* tilesheet) {
|
|||
if (!tilesheet) return;
|
||||
|
||||
if (--tilesheet->ref_count == 0) {
|
||||
pxl8_tilesheet_free(tilesheet);
|
||||
pxl8_tilesheet_destroy(tilesheet);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -261,6 +289,10 @@ const pxl8_tile_properties* pxl8_tilesheet_get_tile_property(const pxl8_tileshee
|
|||
return &tilesheet->properties[tile_id];
|
||||
}
|
||||
|
||||
u32 pxl8_tilesheet_get_tile_size(const pxl8_tilesheet* tilesheet) {
|
||||
return tilesheet ? tilesheet->tile_size : 0;
|
||||
}
|
||||
|
||||
pxl8_result pxl8_tilesheet_add_autotile_rule(pxl8_tilesheet* tilesheet, u16 base_tile_id,
|
||||
u8 neighbor_mask, u16 result_tile_id) {
|
||||
if (!tilesheet || base_tile_id == 0 || base_tile_id > tilesheet->total_tiles) {
|
||||
|
|
|
|||
|
|
@ -6,31 +6,15 @@
|
|||
typedef struct pxl8_tile_animation pxl8_tile_animation;
|
||||
typedef struct pxl8_tile_properties pxl8_tile_properties;
|
||||
typedef struct pxl8_autotile_rule pxl8_autotile_rule;
|
||||
|
||||
typedef struct pxl8_tilesheet {
|
||||
u8* data;
|
||||
bool* tile_valid;
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 tile_size;
|
||||
u32 tiles_per_row;
|
||||
u32 total_tiles;
|
||||
pxl8_color_mode color_mode;
|
||||
u32 ref_count;
|
||||
|
||||
pxl8_tile_animation* animations;
|
||||
u32 animation_count;
|
||||
|
||||
pxl8_tile_properties* properties;
|
||||
|
||||
pxl8_autotile_rule** autotile_rules;
|
||||
u32* autotile_rule_counts;
|
||||
} pxl8_tilesheet;
|
||||
typedef struct pxl8_tilesheet pxl8_tilesheet;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
pxl8_tilesheet* pxl8_tilesheet_create(u32 tile_size);
|
||||
void pxl8_tilesheet_destroy(pxl8_tilesheet* tilesheet);
|
||||
|
||||
pxl8_result pxl8_tilesheet_add_animation(
|
||||
pxl8_tilesheet* tilesheet,
|
||||
u16 base_tile_id,
|
||||
|
|
@ -45,18 +29,18 @@ pxl8_result pxl8_tilesheet_add_autotile_rule(
|
|||
u16 result_tile_id
|
||||
);
|
||||
u16 pxl8_tilesheet_apply_autotile(const pxl8_tilesheet* tilesheet, u16 base_tile_id, u8 neighbors);
|
||||
void pxl8_tilesheet_free(pxl8_tilesheet* tilesheet);
|
||||
u16 pxl8_tilesheet_get_animated_frame(const pxl8_tilesheet* tilesheet, u16 tile_id);
|
||||
const pxl8_tile_properties* pxl8_tilesheet_get_tile_property(
|
||||
const pxl8_tilesheet* tilesheet,
|
||||
u16 tile_id
|
||||
);
|
||||
u32 pxl8_tilesheet_get_tile_size(const pxl8_tilesheet* tilesheet);
|
||||
bool pxl8_tilesheet_is_tile_valid(const pxl8_tilesheet* tilesheet, u16 tile_id);
|
||||
pxl8_result pxl8_tilesheet_load(pxl8_tilesheet* tilesheet, const char* filepath, pxl8_gfx_ctx* gfx);
|
||||
pxl8_result pxl8_tilesheet_load(pxl8_tilesheet* tilesheet, const char* filepath, pxl8_gfx* gfx);
|
||||
void pxl8_tilesheet_ref(pxl8_tilesheet* tilesheet);
|
||||
void pxl8_tilesheet_render_tile(
|
||||
const pxl8_tilesheet* tilesheet,
|
||||
pxl8_gfx_ctx* gfx,
|
||||
pxl8_gfx* gfx,
|
||||
u16 tile_id,
|
||||
i32 x,
|
||||
i32 y,
|
||||
|
|
|
|||
|
|
@ -4,28 +4,28 @@
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
typedef float f32;
|
||||
typedef double f64;
|
||||
typedef int8_t i8;
|
||||
typedef int16_t i16;
|
||||
typedef int32_t i32;
|
||||
typedef int64_t i64;
|
||||
typedef float f32;
|
||||
typedef double f64;
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
|
||||
#if defined(__SIZEOF_INT128__)
|
||||
typedef __uint128_t u128;
|
||||
typedef __int128_t i128;
|
||||
typedef __uint128_t u128;
|
||||
#endif
|
||||
|
||||
typedef enum pxl8_color_mode {
|
||||
PXL8_COLOR_MODE_FAMI,
|
||||
PXL8_COLOR_MODE_MEGA,
|
||||
PXL8_COLOR_MODE_GBA,
|
||||
PXL8_COLOR_MODE_SUPERFAMI,
|
||||
PXL8_COLOR_MODE_HICOLOR,
|
||||
PXL8_COLOR_MODE_MEGA,
|
||||
PXL8_COLOR_MODE_SUPERFAMI
|
||||
} pxl8_color_mode;
|
||||
|
||||
typedef enum pxl8_resolution {
|
||||
|
|
@ -35,9 +35,34 @@ typedef enum pxl8_resolution {
|
|||
PXL8_RESOLUTION_640x360,
|
||||
PXL8_RESOLUTION_640x480,
|
||||
PXL8_RESOLUTION_800x600,
|
||||
PXL8_RESOLUTION_960x540,
|
||||
PXL8_RESOLUTION_960x540
|
||||
} pxl8_resolution;
|
||||
|
||||
typedef enum pxl8_result {
|
||||
PXL8_OK = 0,
|
||||
PXL8_ERROR_ASE_INVALID_FRAME_MAGIC,
|
||||
PXL8_ERROR_ASE_INVALID_MAGIC,
|
||||
PXL8_ERROR_ASE_MALFORMED_CHUNK,
|
||||
PXL8_ERROR_ASE_TRUNCATED_FILE,
|
||||
PXL8_ERROR_FILE_NOT_FOUND,
|
||||
PXL8_ERROR_INITIALIZATION_FAILED,
|
||||
PXL8_ERROR_INVALID_ARGUMENT,
|
||||
PXL8_ERROR_INVALID_COORDINATE,
|
||||
PXL8_ERROR_INVALID_FORMAT,
|
||||
PXL8_ERROR_INVALID_SIZE,
|
||||
PXL8_ERROR_NOT_INITIALIZED,
|
||||
PXL8_ERROR_NULL_POINTER,
|
||||
PXL8_ERROR_OUT_OF_MEMORY,
|
||||
PXL8_ERROR_SCRIPT_ERROR,
|
||||
PXL8_ERROR_SYSTEM_FAILURE
|
||||
} pxl8_result;
|
||||
|
||||
typedef struct pxl8_bounds {
|
||||
i32 x, y;
|
||||
i32 w;
|
||||
i32 h;
|
||||
} pxl8_bounds;
|
||||
|
||||
typedef struct pxl8_input_state {
|
||||
bool keys[256];
|
||||
bool keys_pressed[256];
|
||||
|
|
@ -46,27 +71,3 @@ typedef struct pxl8_input_state {
|
|||
typedef struct pxl8_point {
|
||||
i32 x, y;
|
||||
} pxl8_point;
|
||||
|
||||
typedef struct pxl8_rectangle {
|
||||
i32 x, y;
|
||||
i32 width, height;
|
||||
} pxl8_rectangle;
|
||||
|
||||
typedef enum pxl8_result {
|
||||
PXL8_OK = 0,
|
||||
PXL8_ERROR_NULL_POINTER,
|
||||
PXL8_ERROR_INVALID_ARGUMENT,
|
||||
PXL8_ERROR_OUT_OF_MEMORY,
|
||||
PXL8_ERROR_FILE_NOT_FOUND,
|
||||
PXL8_ERROR_INVALID_FORMAT,
|
||||
PXL8_ERROR_SYSTEM_FAILURE,
|
||||
PXL8_ERROR_INVALID_COORDINATE,
|
||||
PXL8_ERROR_INVALID_SIZE,
|
||||
PXL8_ERROR_INITIALIZATION_FAILED,
|
||||
PXL8_ERROR_NOT_INITIALIZED,
|
||||
PXL8_ERROR_SCRIPT_ERROR,
|
||||
PXL8_ERROR_ASE_INVALID_MAGIC,
|
||||
PXL8_ERROR_ASE_INVALID_FRAME_MAGIC,
|
||||
PXL8_ERROR_ASE_TRUNCATED_FILE,
|
||||
PXL8_ERROR_ASE_MALFORMED_CHUNK,
|
||||
} pxl8_result;
|
||||
|
|
|
|||
400
src/pxl8_vfx.c
400
src/pxl8_vfx.c
|
|
@ -6,11 +6,33 @@
|
|||
#include "pxl8_macros.h"
|
||||
#include "pxl8_vfx.h"
|
||||
|
||||
void pxl8_vfx_plasma(pxl8_gfx_ctx* ctx, f32 time, f32 scale1, f32 scale2, u8 palette_offset) {
|
||||
if (!ctx || !ctx->framebuffer) return;
|
||||
struct pxl8_particles {
|
||||
pxl8_particle* particles;
|
||||
u32 alive_count;
|
||||
u32 count;
|
||||
u32 max_count;
|
||||
|
||||
f32 x, y;
|
||||
f32 spread_x, spread_y;
|
||||
|
||||
f32 drag;
|
||||
f32 gravity_x, gravity_y;
|
||||
f32 turbulence;
|
||||
|
||||
f32 spawn_rate;
|
||||
f32 spawn_timer;
|
||||
|
||||
void (*render_fn)(pxl8_gfx* gfx, pxl8_particle* p, void* userdata);
|
||||
void (*spawn_fn)(pxl8_particle* p, void* userdata);
|
||||
void (*update_fn)(pxl8_particle* p, f32 dt, void* userdata);
|
||||
void* userdata;
|
||||
};
|
||||
|
||||
void pxl8_vfx_plasma(pxl8_gfx* ctx, f32 time, f32 scale1, f32 scale2, u8 palette_offset) {
|
||||
if (!ctx || !pxl8_gfx_get_framebuffer(ctx)) return;
|
||||
|
||||
for (i32 y = 0; y < ctx->framebuffer_height; y++) {
|
||||
for (i32 x = 0; x < ctx->framebuffer_width; x++) {
|
||||
for (i32 y = 0; y < pxl8_gfx_get_height(ctx); y++) {
|
||||
for (i32 x = 0; x < pxl8_gfx_get_width(ctx); x++) {
|
||||
f32 v1 = sinf(x * scale1 + time);
|
||||
f32 v2 = sinf(y * scale1 + time * 0.7f);
|
||||
f32 v3 = sinf((x + y) * scale2 + time * 1.3f);
|
||||
|
|
@ -27,44 +49,47 @@ void pxl8_vfx_plasma(pxl8_gfx_ctx* ctx, f32 time, f32 scale1, f32 scale2, u8 pal
|
|||
}
|
||||
}
|
||||
|
||||
void pxl8_vfx_raster_bars(pxl8_gfx_ctx* ctx, pxl8_raster_bar* bars, u32 bar_count, f32 time) {
|
||||
void pxl8_vfx_raster_bars(pxl8_gfx* ctx, pxl8_raster_bar* bars, u32 bar_count, f32 time) {
|
||||
if (!ctx || !bars) return;
|
||||
|
||||
|
||||
for (u32 i = 0; i < bar_count; i++) {
|
||||
pxl8_raster_bar* bar = &bars[i];
|
||||
f32 y = bar->base_y + bar->amplitude * sinf(time * bar->speed + bar->phase);
|
||||
i32 y_int = (i32)y;
|
||||
|
||||
for (i32 dy = 0; dy <= bar->height; dy++) {
|
||||
f32 position = (f32)dy / (f32)bar->height;
|
||||
f32 gradient = 1.0f - 2.0f * fabsf(position - 0.5f);
|
||||
|
||||
|
||||
for (i32 dy = 0; dy < bar->height; dy++) {
|
||||
f32 position = (f32)dy / (f32)(bar->height - 1);
|
||||
f32 distance_from_center = fabsf(position - 0.5f) * 2.0f;
|
||||
|
||||
u8 color_idx;
|
||||
if (gradient > 0.8f) {
|
||||
if (distance_from_center < 0.3f) {
|
||||
color_idx = bar->fade_color;
|
||||
} else if (distance_from_center < 0.6f) {
|
||||
color_idx = bar->fade_color - 1;
|
||||
} else if (distance_from_center < 0.8f) {
|
||||
color_idx = bar->color;
|
||||
} else {
|
||||
u8 range = bar->fade_color - bar->color;
|
||||
color_idx = bar->color + (u8)(gradient * range);
|
||||
color_idx = bar->color - 1;
|
||||
}
|
||||
|
||||
pxl8_rect_fill(ctx, 0, y_int + dy, ctx->framebuffer_width, 1, color_idx);
|
||||
|
||||
pxl8_rect_fill(ctx, 0, y_int + dy, pxl8_gfx_get_width(ctx), 1, color_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_vfx_rotozoom(pxl8_gfx_ctx* ctx, f32 angle, f32 zoom, i32 cx, i32 cy) {
|
||||
if (!ctx || !ctx->framebuffer) return;
|
||||
void pxl8_vfx_rotozoom(pxl8_gfx* ctx, f32 angle, f32 zoom, i32 cx, i32 cy) {
|
||||
if (!ctx || !pxl8_gfx_get_framebuffer(ctx)) return;
|
||||
|
||||
f32 cos_a = cosf(angle);
|
||||
f32 sin_a = sinf(angle);
|
||||
|
||||
u8* temp_buffer = (u8*)SDL_malloc(ctx->framebuffer_width * ctx->framebuffer_height);
|
||||
u8* temp_buffer = (u8*)SDL_malloc(pxl8_gfx_get_width(ctx) * pxl8_gfx_get_height(ctx));
|
||||
if (!temp_buffer) return;
|
||||
|
||||
SDL_memcpy(temp_buffer, ctx->framebuffer, ctx->framebuffer_width * ctx->framebuffer_height);
|
||||
SDL_memcpy(temp_buffer, pxl8_gfx_get_framebuffer(ctx), pxl8_gfx_get_width(ctx) * pxl8_gfx_get_height(ctx));
|
||||
|
||||
for (i32 y = 0; y < ctx->framebuffer_height; y++) {
|
||||
for (i32 x = 0; x < ctx->framebuffer_width; x++) {
|
||||
for (i32 y = 0; y < pxl8_gfx_get_height(ctx); y++) {
|
||||
for (i32 x = 0; x < pxl8_gfx_get_width(ctx); x++) {
|
||||
f32 dx = x - cx;
|
||||
f32 dy = y - cy;
|
||||
|
||||
|
|
@ -74,8 +99,8 @@ void pxl8_vfx_rotozoom(pxl8_gfx_ctx* ctx, f32 angle, f32 zoom, i32 cx, i32 cy) {
|
|||
i32 sx = (i32)src_x;
|
||||
i32 sy = (i32)src_y;
|
||||
|
||||
if (sx >= 0 && sx < ctx->framebuffer_width && sy >= 0 && sy < ctx->framebuffer_height) {
|
||||
ctx->framebuffer[y * ctx->framebuffer_width + x] = temp_buffer[sy * ctx->framebuffer_width + sx];
|
||||
if (sx >= 0 && sx < pxl8_gfx_get_width(ctx) && sy >= 0 && sy < pxl8_gfx_get_height(ctx)) {
|
||||
pxl8_gfx_get_framebuffer(ctx)[y * pxl8_gfx_get_width(ctx) + x] = temp_buffer[sy * pxl8_gfx_get_width(ctx) + sx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -83,14 +108,14 @@ void pxl8_vfx_rotozoom(pxl8_gfx_ctx* ctx, f32 angle, f32 zoom, i32 cx, i32 cy) {
|
|||
SDL_free(temp_buffer);
|
||||
}
|
||||
|
||||
void pxl8_vfx_tunnel(pxl8_gfx_ctx* ctx, f32 time, f32 speed, f32 twist) {
|
||||
if (!ctx || !ctx->framebuffer) return;
|
||||
void pxl8_vfx_tunnel(pxl8_gfx* ctx, f32 time, f32 speed, f32 twist) {
|
||||
if (!ctx || !pxl8_gfx_get_framebuffer(ctx)) return;
|
||||
|
||||
f32 cx = ctx->framebuffer_width / 2.0f;
|
||||
f32 cy = ctx->framebuffer_height / 2.0f;
|
||||
f32 cx = pxl8_gfx_get_width(ctx) / 2.0f;
|
||||
f32 cy = pxl8_gfx_get_height(ctx) / 2.0f;
|
||||
|
||||
for (i32 y = 0; y < ctx->framebuffer_height; y++) {
|
||||
for (i32 x = 0; x < ctx->framebuffer_width; x++) {
|
||||
for (i32 y = 0; y < pxl8_gfx_get_height(ctx); y++) {
|
||||
for (i32 x = 0; x < pxl8_gfx_get_width(ctx); x++) {
|
||||
f32 dx = x - cx;
|
||||
f32 dy = y - cy;
|
||||
f32 dist = sqrtf(dx * dx + dy * dy);
|
||||
|
|
@ -110,11 +135,11 @@ void pxl8_vfx_tunnel(pxl8_gfx_ctx* ctx, f32 time, f32 speed, f32 twist) {
|
|||
}
|
||||
}
|
||||
|
||||
void pxl8_vfx_water_ripple(pxl8_gfx_ctx* ctx, f32* height_map, i32 drop_x, i32 drop_y, f32 damping) {
|
||||
void pxl8_vfx_water_ripple(pxl8_gfx* ctx, f32* height_map, i32 drop_x, i32 drop_y, f32 damping) {
|
||||
if (!ctx || !height_map) return;
|
||||
|
||||
i32 w = ctx->framebuffer_width;
|
||||
i32 h = ctx->framebuffer_height;
|
||||
i32 w = pxl8_gfx_get_width(ctx);
|
||||
i32 h = pxl8_gfx_get_height(ctx);
|
||||
|
||||
static f32* prev_height = NULL;
|
||||
if (!prev_height) {
|
||||
|
|
@ -141,44 +166,56 @@ void pxl8_vfx_water_ripple(pxl8_gfx_ctx* ctx, f32* height_map, i32 drop_x, i32 d
|
|||
prev_height = temp;
|
||||
}
|
||||
|
||||
void pxl8_vfx_particles_clear(pxl8_particle_system* sys) {
|
||||
if (!sys || !sys->particles) return;
|
||||
pxl8_particles* pxl8_particles_create(u32 max_count) {
|
||||
pxl8_particles* particles = SDL_calloc(1, sizeof(pxl8_particles));
|
||||
if (!particles) return NULL;
|
||||
|
||||
for (u32 i = 0; i < sys->max_count; i++) {
|
||||
sys->particles[i].life = 0;
|
||||
sys->particles[i].flags = 0;
|
||||
particles->particles = SDL_calloc(max_count, sizeof(pxl8_particle));
|
||||
if (!particles->particles) {
|
||||
SDL_free(particles);
|
||||
return NULL;
|
||||
}
|
||||
sys->alive_count = 0;
|
||||
sys->spawn_timer = 0;
|
||||
|
||||
particles->max_count = max_count;
|
||||
particles->drag = 0.98f;
|
||||
particles->gravity_y = 100.0f;
|
||||
particles->spawn_rate = 10.0f;
|
||||
|
||||
return particles;
|
||||
}
|
||||
|
||||
void pxl8_vfx_particles_free(pxl8_particle_system* sys) {
|
||||
if (!sys) return;
|
||||
|
||||
if (sys->particles) {
|
||||
SDL_free(sys->particles);
|
||||
sys->particles = NULL;
|
||||
}
|
||||
sys->count = 0;
|
||||
sys->max_count = 0;
|
||||
sys->alive_count = 0;
|
||||
void pxl8_particles_destroy(pxl8_particles* particles) {
|
||||
if (!particles) return;
|
||||
SDL_free(particles->particles);
|
||||
SDL_free(particles);
|
||||
}
|
||||
|
||||
void pxl8_vfx_particles_emit(pxl8_particle_system* sys, u32 count) {
|
||||
if (!sys || !sys->particles) return;
|
||||
void pxl8_particles_clear(pxl8_particles* particles) {
|
||||
if (!particles || !particles->particles) return;
|
||||
|
||||
for (u32 i = 0; i < particles->max_count; i++) {
|
||||
particles->particles[i].life = 0;
|
||||
particles->particles[i].flags = 0;
|
||||
}
|
||||
particles->alive_count = 0;
|
||||
particles->spawn_timer = 0;
|
||||
}
|
||||
|
||||
void pxl8_particles_emit(pxl8_particles* particles, u32 count) {
|
||||
if (!particles || !particles->particles) return;
|
||||
|
||||
for (u32 i = 0; i < count && sys->alive_count < sys->max_count; i++) {
|
||||
for (u32 j = 0; j < sys->max_count; j++) {
|
||||
if (sys->particles[j].life <= 0) {
|
||||
pxl8_particle* p = &sys->particles[j];
|
||||
for (u32 i = 0; i < count && particles->alive_count < particles->max_count; i++) {
|
||||
for (u32 j = 0; j < particles->max_count; j++) {
|
||||
if (particles->particles[j].life <= 0) {
|
||||
pxl8_particle* p = &particles->particles[j];
|
||||
p->life = 1.0f;
|
||||
p->max_life = 1.0f;
|
||||
p->x = sys->x + (((f32)rand() / RAND_MAX) - 0.5f) * sys->spread_x;
|
||||
p->y = sys->y + (((f32)rand() / RAND_MAX) - 0.5f) * sys->spread_y;
|
||||
p->x = particles->x + (((f32)rand() / RAND_MAX) - 0.5f) * particles->spread_x;
|
||||
p->y = particles->y + (((f32)rand() / RAND_MAX) - 0.5f) * particles->spread_y;
|
||||
p->z = 0;
|
||||
p->vx = p->vy = p->vz = 0;
|
||||
p->ax = sys->gravity_x;
|
||||
p->ay = sys->gravity_y;
|
||||
p->ax = particles->gravity_x;
|
||||
p->ay = particles->gravity_y;
|
||||
p->az = 0;
|
||||
p->color = p->start_color = p->end_color = 15;
|
||||
p->size = 1.0f;
|
||||
|
|
@ -186,76 +223,65 @@ void pxl8_vfx_particles_emit(pxl8_particle_system* sys, u32 count) {
|
|||
p->spin = 0;
|
||||
p->flags = 1;
|
||||
|
||||
if (sys->spawn_fn) {
|
||||
sys->spawn_fn(p, sys->userdata);
|
||||
if (particles->spawn_fn) {
|
||||
particles->spawn_fn(p, particles->userdata);
|
||||
}
|
||||
|
||||
sys->alive_count++;
|
||||
particles->alive_count++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_vfx_particles_init(pxl8_particle_system* sys, u32 max_count) {
|
||||
if (!sys) return;
|
||||
|
||||
SDL_memset(sys, 0, sizeof(pxl8_particle_system));
|
||||
|
||||
sys->particles = (pxl8_particle*)SDL_calloc(max_count, sizeof(pxl8_particle));
|
||||
if (!sys->particles) return;
|
||||
|
||||
sys->max_count = max_count;
|
||||
sys->count = 0;
|
||||
sys->alive_count = 0;
|
||||
sys->spawn_rate = 10.0f;
|
||||
sys->spawn_timer = 0;
|
||||
sys->drag = 0.98f;
|
||||
sys->gravity_y = 100.0f;
|
||||
}
|
||||
void pxl8_particles_render(pxl8_particles* particles, pxl8_gfx* gfx) {
|
||||
if (!particles || !particles->particles || !gfx) return;
|
||||
|
||||
void pxl8_vfx_particles_render(pxl8_particle_system* sys, pxl8_gfx_ctx* ctx) {
|
||||
if (!sys || !sys->particles || !ctx) return;
|
||||
|
||||
for (u32 i = 0; i < sys->max_count; i++) {
|
||||
pxl8_particle* p = &sys->particles[i];
|
||||
for (u32 i = 0; i < particles->max_count; i++) {
|
||||
pxl8_particle* p = &particles->particles[i];
|
||||
if (p->life > 0 && p->flags) {
|
||||
if (sys->render_fn) {
|
||||
sys->render_fn(ctx, p, sys->userdata);
|
||||
if (particles->render_fn) {
|
||||
particles->render_fn(gfx, p, particles->userdata);
|
||||
} else {
|
||||
i32 x = (i32)p->x;
|
||||
i32 y = (i32)p->y;
|
||||
if (x >= 0 && x < ctx->framebuffer_width && y >= 0 && y < ctx->framebuffer_height) {
|
||||
pxl8_pixel(ctx, x, y, p->color);
|
||||
if (x >= 0 && x < pxl8_gfx_get_width(gfx) && y >= 0 && y < pxl8_gfx_get_height(gfx)) {
|
||||
pxl8_pixel(gfx, x, y, p->color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_vfx_particles_update(pxl8_particle_system* sys, f32 dt) {
|
||||
if (!sys || !sys->particles) return;
|
||||
|
||||
sys->spawn_timer += dt;
|
||||
f32 spawn_interval = 1.0f / sys->spawn_rate;
|
||||
while (sys->spawn_timer >= spawn_interval) {
|
||||
pxl8_vfx_particles_emit(sys, 1);
|
||||
sys->spawn_timer -= spawn_interval;
|
||||
void pxl8_particles_update(pxl8_particles* particles, f32 dt) {
|
||||
if (!particles || !particles->particles) return;
|
||||
|
||||
if (particles->spawn_rate > 0.0f) {
|
||||
particles->spawn_timer += dt;
|
||||
f32 spawn_interval = 1.0f / particles->spawn_rate;
|
||||
u32 max_spawns_per_frame = particles->max_count / 10;
|
||||
if (max_spawns_per_frame < 1) max_spawns_per_frame = 1;
|
||||
u32 spawn_count = 0;
|
||||
while (particles->spawn_timer >= spawn_interval && spawn_count < max_spawns_per_frame) {
|
||||
pxl8_particles_emit(particles, 1);
|
||||
particles->spawn_timer -= spawn_interval;
|
||||
spawn_count++;
|
||||
}
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < sys->max_count; i++) {
|
||||
pxl8_particle* p = &sys->particles[i];
|
||||
for (u32 i = 0; i < particles->max_count; i++) {
|
||||
pxl8_particle* p = &particles->particles[i];
|
||||
if (p->life > 0) {
|
||||
if (sys->update_fn) {
|
||||
sys->update_fn(p, dt, sys->userdata);
|
||||
if (particles->update_fn) {
|
||||
particles->update_fn(p, dt, particles->userdata);
|
||||
} else {
|
||||
p->vx += p->ax * dt;
|
||||
p->vy += p->ay * dt;
|
||||
p->vz += p->az * dt;
|
||||
|
||||
p->vx *= sys->drag;
|
||||
p->vy *= sys->drag;
|
||||
p->vz *= sys->drag;
|
||||
p->vx *= particles->drag;
|
||||
p->vy *= particles->drag;
|
||||
p->vz *= particles->drag;
|
||||
|
||||
p->x += p->vx * dt;
|
||||
p->y += p->vy * dt;
|
||||
|
|
@ -267,25 +293,25 @@ void pxl8_vfx_particles_update(pxl8_particle_system* sys, f32 dt) {
|
|||
p->life -= dt / p->max_life;
|
||||
if (p->life <= 0) {
|
||||
p->flags = 0;
|
||||
sys->alive_count--;
|
||||
particles->alive_count--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_vfx_explosion(pxl8_particle_system* sys, i32 x, i32 y, u32 color, f32 force) {
|
||||
if (!sys) return;
|
||||
void pxl8_vfx_explosion(pxl8_particles* particles, i32 x, i32 y, u32 color, f32 force) {
|
||||
if (!particles) return;
|
||||
|
||||
sys->x = x;
|
||||
sys->y = y;
|
||||
sys->spread_x = sys->spread_y = 2.0f;
|
||||
sys->gravity_x = 0;
|
||||
sys->gravity_y = 200.0f;
|
||||
sys->drag = 0.95f;
|
||||
sys->update_fn = NULL;
|
||||
particles->x = x;
|
||||
particles->y = y;
|
||||
particles->spread_x = particles->spread_y = 2.0f;
|
||||
particles->gravity_x = 0;
|
||||
particles->gravity_y = 200.0f;
|
||||
particles->drag = 0.95f;
|
||||
particles->update_fn = NULL;
|
||||
|
||||
for (u32 i = 0; i < 50 && i < sys->max_count; i++) {
|
||||
pxl8_particle* p = &sys->particles[i];
|
||||
for (u32 i = 0; i < 50 && i < particles->max_count; i++) {
|
||||
pxl8_particle* p = &particles->particles[i];
|
||||
f32 angle = ((f32)rand() / RAND_MAX) * 6.28f;
|
||||
f32 speed = force * (0.5f + ((f32)rand() / RAND_MAX) * 0.5f);
|
||||
|
||||
|
|
@ -330,20 +356,20 @@ static void fire_update(pxl8_particle* p, f32 dt, void* userdata) {
|
|||
}
|
||||
}
|
||||
|
||||
void pxl8_vfx_fire(pxl8_particle_system* sys, i32 x, i32 y, i32 width, u8 palette_start) {
|
||||
if (!sys) return;
|
||||
void pxl8_vfx_fire(pxl8_particles* particles, i32 x, i32 y, i32 width, u8 palette_start) {
|
||||
if (!particles) return;
|
||||
|
||||
sys->x = x;
|
||||
sys->y = y;
|
||||
sys->spread_x = width;
|
||||
sys->spread_y = 4.0f;
|
||||
sys->gravity_x = 0;
|
||||
sys->gravity_y = -100.0f;
|
||||
sys->drag = 0.97f;
|
||||
sys->spawn_rate = 120.0f;
|
||||
sys->spawn_fn = fire_spawn;
|
||||
sys->update_fn = fire_update;
|
||||
sys->userdata = (void*)(uintptr_t)palette_start;
|
||||
particles->x = x;
|
||||
particles->y = y;
|
||||
particles->spread_x = width;
|
||||
particles->spread_y = 4.0f;
|
||||
particles->gravity_x = 0;
|
||||
particles->gravity_y = -100.0f;
|
||||
particles->drag = 0.97f;
|
||||
particles->spawn_rate = 120.0f;
|
||||
particles->spawn_fn = fire_spawn;
|
||||
particles->update_fn = fire_update;
|
||||
particles->userdata = (void*)(uintptr_t)palette_start;
|
||||
}
|
||||
|
||||
static void rain_spawn(pxl8_particle* p, void* userdata) {
|
||||
|
|
@ -355,19 +381,19 @@ static void rain_spawn(pxl8_particle* p, void* userdata) {
|
|||
p->vy = 200.0f + ((f32)rand() / RAND_MAX) * 100.0f;
|
||||
}
|
||||
|
||||
void pxl8_vfx_rain(pxl8_particle_system* sys, i32 width, f32 wind) {
|
||||
if (!sys) return;
|
||||
void pxl8_vfx_rain(pxl8_particles* particles, i32 width, f32 wind) {
|
||||
if (!particles) return;
|
||||
|
||||
sys->x = width / 2.0f;
|
||||
sys->y = -10;
|
||||
sys->spread_x = width;
|
||||
sys->spread_y = 0;
|
||||
sys->gravity_x = wind;
|
||||
sys->gravity_y = 300.0f;
|
||||
sys->drag = 1.0f;
|
||||
sys->spawn_rate = 100.0f;
|
||||
sys->spawn_fn = rain_spawn;
|
||||
sys->update_fn = NULL;
|
||||
particles->x = width / 2.0f;
|
||||
particles->y = -10;
|
||||
particles->spread_x = width;
|
||||
particles->spread_y = 0;
|
||||
particles->gravity_x = wind;
|
||||
particles->gravity_y = 300.0f;
|
||||
particles->drag = 1.0f;
|
||||
particles->spawn_rate = 100.0f;
|
||||
particles->spawn_fn = rain_spawn;
|
||||
particles->update_fn = NULL;
|
||||
}
|
||||
|
||||
static void smoke_spawn(pxl8_particle* p, void* userdata) {
|
||||
|
|
@ -381,20 +407,20 @@ static void smoke_spawn(pxl8_particle* p, void* userdata) {
|
|||
p->size = 1.0f + ((f32)rand() / RAND_MAX) * 2.0f;
|
||||
}
|
||||
|
||||
void pxl8_vfx_smoke(pxl8_particle_system* sys, i32 x, i32 y, u8 color) {
|
||||
if (!sys) return;
|
||||
void pxl8_vfx_smoke(pxl8_particles* particles, i32 x, i32 y, u8 color) {
|
||||
if (!particles) return;
|
||||
|
||||
sys->x = x;
|
||||
sys->y = y;
|
||||
sys->spread_x = 5.0f;
|
||||
sys->spread_y = 5.0f;
|
||||
sys->gravity_x = 0;
|
||||
sys->gravity_y = -50.0f;
|
||||
sys->drag = 0.96f;
|
||||
sys->spawn_rate = 20.0f;
|
||||
sys->spawn_fn = smoke_spawn;
|
||||
sys->update_fn = NULL;
|
||||
sys->userdata = (void*)(uintptr_t)color;
|
||||
particles->x = x;
|
||||
particles->y = y;
|
||||
particles->spread_x = 5.0f;
|
||||
particles->spread_y = 5.0f;
|
||||
particles->gravity_x = 0;
|
||||
particles->gravity_y = -50.0f;
|
||||
particles->drag = 0.96f;
|
||||
particles->spawn_rate = 20.0f;
|
||||
particles->spawn_fn = smoke_spawn;
|
||||
particles->update_fn = NULL;
|
||||
particles->userdata = (void*)(uintptr_t)color;
|
||||
}
|
||||
|
||||
static void snow_spawn(pxl8_particle* p, void* userdata) {
|
||||
|
|
@ -407,19 +433,19 @@ static void snow_spawn(pxl8_particle* p, void* userdata) {
|
|||
p->vy = 30.0f + ((f32)rand() / RAND_MAX) * 20.0f;
|
||||
}
|
||||
|
||||
void pxl8_vfx_snow(pxl8_particle_system* sys, i32 width, f32 wind) {
|
||||
if (!sys) return;
|
||||
void pxl8_vfx_snow(pxl8_particles* particles, i32 width, f32 wind) {
|
||||
if (!particles) return;
|
||||
|
||||
sys->x = width / 2.0f;
|
||||
sys->y = -10;
|
||||
sys->spread_x = width;
|
||||
sys->spread_y = 0;
|
||||
sys->gravity_x = wind;
|
||||
sys->gravity_y = 30.0f;
|
||||
sys->drag = 1.0f;
|
||||
sys->spawn_rate = 30.0f;
|
||||
sys->spawn_fn = snow_spawn;
|
||||
sys->update_fn = NULL;
|
||||
particles->x = width / 2.0f;
|
||||
particles->y = -10;
|
||||
particles->spread_x = width;
|
||||
particles->spread_y = 0;
|
||||
particles->gravity_x = wind;
|
||||
particles->gravity_y = 30.0f;
|
||||
particles->drag = 1.0f;
|
||||
particles->spawn_rate = 30.0f;
|
||||
particles->spawn_fn = snow_spawn;
|
||||
particles->update_fn = NULL;
|
||||
}
|
||||
|
||||
static void sparks_spawn(pxl8_particle* p, void* userdata) {
|
||||
|
|
@ -435,33 +461,33 @@ static void sparks_spawn(pxl8_particle* p, void* userdata) {
|
|||
p->vy = sinf(angle) * speed - 50.0f;
|
||||
}
|
||||
|
||||
void pxl8_vfx_sparks(pxl8_particle_system* sys, i32 x, i32 y, u32 color) {
|
||||
if (!sys) return;
|
||||
void pxl8_vfx_sparks(pxl8_particles* particles, i32 x, i32 y, u32 color) {
|
||||
if (!particles) return;
|
||||
|
||||
sys->x = x;
|
||||
sys->y = y;
|
||||
sys->spread_x = 2.0f;
|
||||
sys->spread_y = 2.0f;
|
||||
sys->gravity_x = 0;
|
||||
sys->gravity_y = 100.0f;
|
||||
sys->drag = 0.97f;
|
||||
sys->spawn_rate = 40.0f;
|
||||
sys->spawn_fn = sparks_spawn;
|
||||
sys->update_fn = NULL;
|
||||
sys->userdata = (void*)(uintptr_t)color;
|
||||
particles->x = x;
|
||||
particles->y = y;
|
||||
particles->spread_x = 2.0f;
|
||||
particles->spread_y = 2.0f;
|
||||
particles->gravity_x = 0;
|
||||
particles->gravity_y = 100.0f;
|
||||
particles->drag = 0.97f;
|
||||
particles->spawn_rate = 40.0f;
|
||||
particles->spawn_fn = sparks_spawn;
|
||||
particles->update_fn = NULL;
|
||||
particles->userdata = (void*)(uintptr_t)color;
|
||||
}
|
||||
|
||||
void pxl8_vfx_starfield(pxl8_particle_system* sys, f32 speed, f32 spread) {
|
||||
if (!sys) return;
|
||||
void pxl8_vfx_starfield(pxl8_particles* particles, f32 speed, f32 spread) {
|
||||
if (!particles) return;
|
||||
|
||||
sys->spread_x = sys->spread_y = spread;
|
||||
sys->gravity_x = sys->gravity_y = 0;
|
||||
sys->drag = 1.0f;
|
||||
sys->spawn_rate = 0;
|
||||
sys->update_fn = NULL;
|
||||
particles->spread_x = particles->spread_y = spread;
|
||||
particles->gravity_x = particles->gravity_y = 0;
|
||||
particles->drag = 1.0f;
|
||||
particles->spawn_rate = 0;
|
||||
particles->update_fn = NULL;
|
||||
|
||||
for (u32 i = 0; i < sys->max_count; i++) {
|
||||
pxl8_particle* p = &sys->particles[i];
|
||||
for (u32 i = 0; i < particles->max_count; i++) {
|
||||
pxl8_particle* p = &particles->particles[i];
|
||||
p->x = ((f32)rand() / RAND_MAX) * spread * 2.0f - spread;
|
||||
p->y = ((f32)rand() / RAND_MAX) * spread * 2.0f - spread;
|
||||
p->z = ((f32)rand() / RAND_MAX) * spread;
|
||||
|
|
|
|||
|
|
@ -3,66 +3,51 @@
|
|||
#include "pxl8_gfx.h"
|
||||
#include "pxl8_types.h"
|
||||
|
||||
typedef struct pxl8_particles pxl8_particles;
|
||||
|
||||
typedef struct pxl8_particle {
|
||||
f32 x, y, z;
|
||||
f32 vx, vy, vz;
|
||||
f32 angle;
|
||||
f32 ax, ay, az;
|
||||
u32 color;
|
||||
u32 end_color;
|
||||
u8 flags;
|
||||
f32 life;
|
||||
f32 max_life;
|
||||
u32 color;
|
||||
u32 start_color;
|
||||
u32 end_color;
|
||||
f32 size;
|
||||
f32 angle;
|
||||
f32 spin;
|
||||
u8 flags;
|
||||
u32 start_color;
|
||||
f32 vx, vy, vz;
|
||||
f32 x, y, z;
|
||||
} pxl8_particle;
|
||||
|
||||
typedef struct pxl8_particle_system {
|
||||
pxl8_particle* particles;
|
||||
u32 count;
|
||||
u32 max_count;
|
||||
u32 alive_count;
|
||||
f32 spawn_rate;
|
||||
f32 spawn_timer;
|
||||
f32 x, y;
|
||||
f32 spread_x, spread_y;
|
||||
f32 gravity_x, gravity_y;
|
||||
f32 drag;
|
||||
f32 turbulence;
|
||||
void (*spawn_fn)(pxl8_particle* p, void* userdata);
|
||||
void (*update_fn)(pxl8_particle* p, f32 dt, void* userdata);
|
||||
void (*render_fn)(pxl8_gfx_ctx* ctx, pxl8_particle* p, void* userdata);
|
||||
void* userdata;
|
||||
} pxl8_particle_system;
|
||||
|
||||
typedef struct pxl8_raster_bar {
|
||||
f32 base_y;
|
||||
f32 amplitude;
|
||||
i32 height;
|
||||
f32 speed;
|
||||
f32 phase;
|
||||
f32 base_y;
|
||||
u32 color;
|
||||
u32 fade_color;
|
||||
i32 height;
|
||||
f32 phase;
|
||||
f32 speed;
|
||||
} pxl8_raster_bar;
|
||||
|
||||
void pxl8_vfx_particles_clear(pxl8_particle_system* sys);
|
||||
void pxl8_vfx_particles_emit(pxl8_particle_system* sys, u32 count);
|
||||
void pxl8_vfx_particles_free(pxl8_particle_system* sys);
|
||||
void pxl8_vfx_particles_init(pxl8_particle_system* sys, u32 max_count);
|
||||
void pxl8_vfx_particles_render(pxl8_particle_system* sys, pxl8_gfx_ctx* ctx);
|
||||
void pxl8_vfx_particles_update(pxl8_particle_system* sys, f32 dt);
|
||||
pxl8_particles* pxl8_particles_create(u32 max_count);
|
||||
void pxl8_particles_destroy(pxl8_particles* particles);
|
||||
|
||||
void pxl8_vfx_explosion(pxl8_particle_system* sys, i32 x, i32 y, u32 color, f32 force);
|
||||
void pxl8_vfx_fire(pxl8_particle_system* sys, i32 x, i32 y, i32 width, u8 palette_start);
|
||||
void pxl8_vfx_rain(pxl8_particle_system* sys, i32 width, f32 wind);
|
||||
void pxl8_vfx_smoke(pxl8_particle_system* sys, i32 x, i32 y, u8 color);
|
||||
void pxl8_vfx_snow(pxl8_particle_system* sys, i32 width, f32 wind);
|
||||
void pxl8_vfx_sparks(pxl8_particle_system* sys, i32 x, i32 y, u32 color);
|
||||
void pxl8_vfx_starfield(pxl8_particle_system* sys, f32 speed, f32 spread);
|
||||
void pxl8_particles_clear(pxl8_particles* particles);
|
||||
void pxl8_particles_emit(pxl8_particles* particles, u32 count);
|
||||
void pxl8_particles_render(pxl8_particles* particles, pxl8_gfx* gfx);
|
||||
void pxl8_particles_update(pxl8_particles* particles, f32 dt);
|
||||
|
||||
void pxl8_vfx_plasma(pxl8_gfx_ctx* ctx, f32 time, f32 scale1, f32 scale2, u8 palette_offset);
|
||||
void pxl8_vfx_raster_bars(pxl8_gfx_ctx* ctx, pxl8_raster_bar* bars, u32 bar_count, f32 time);
|
||||
void pxl8_vfx_rotozoom(pxl8_gfx_ctx* ctx, f32 angle, f32 zoom, i32 cx, i32 cy);
|
||||
void pxl8_vfx_tunnel(pxl8_gfx_ctx* ctx, f32 time, f32 speed, f32 twist);
|
||||
void pxl8_vfx_water_ripple(pxl8_gfx_ctx* ctx, f32* height_map, i32 drop_x, i32 drop_y, f32 damping);
|
||||
void pxl8_vfx_explosion(pxl8_particles* particles, i32 x, i32 y, u32 color, f32 force);
|
||||
void pxl8_vfx_fire(pxl8_particles* particles, i32 x, i32 y, i32 width, u8 palette_start);
|
||||
void pxl8_vfx_rain(pxl8_particles* particles, i32 width, f32 wind);
|
||||
void pxl8_vfx_smoke(pxl8_particles* particles, i32 x, i32 y, u8 color);
|
||||
void pxl8_vfx_snow(pxl8_particles* particles, i32 width, f32 wind);
|
||||
void pxl8_vfx_sparks(pxl8_particles* particles, i32 x, i32 y, u32 color);
|
||||
void pxl8_vfx_starfield(pxl8_particles* particles, f32 speed, f32 spread);
|
||||
|
||||
void pxl8_vfx_plasma(pxl8_gfx* ctx, f32 time, f32 scale1, f32 scale2, u8 palette_offset);
|
||||
void pxl8_vfx_raster_bars(pxl8_gfx* ctx, pxl8_raster_bar* bars, u32 bar_count, f32 time);
|
||||
void pxl8_vfx_rotozoom(pxl8_gfx* ctx, f32 angle, f32 zoom, i32 cx, i32 cy);
|
||||
void pxl8_vfx_tunnel(pxl8_gfx* ctx, f32 time, f32 speed, f32 twist);
|
||||
void pxl8_vfx_water_ripple(pxl8_gfx* ctx, f32* height_map, i32 drop_x, i32 drop_y, f32 damping);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue