refactor atlas implementation

This commit is contained in:
asrael 2025-10-05 16:25:17 -05:00
parent 6008ebf5ed
commit 10fe609edc
12 changed files with 868 additions and 303 deletions

View file

@ -8,6 +8,17 @@
(var wireframe true) (var wireframe true)
(var time 0) (var time 0)
(var zoom 5.0) (var zoom 5.0)
(var texture-id nil)
(var use-texture false)
(var affine false)
(var texture-initialized false)
(fn init-texture []
(when (not texture-initialized)
(pxl8.load_palette "sprites/pxl8_logo.ase")
(set texture-id (pxl8.load_sprite "sprites/pxl8_logo.ase"))
(pxl8.upload_atlas)
(set texture-initialized true)))
(fn make-cube-vertices [] (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]
@ -21,6 +32,20 @@
[3 2 6] [3 6 7] [3 2 6] [3 6 7]
[4 5 1] [4 1 0]]) [4 5 1] [4 1 0]])
(fn make-cube-faces-with-uvs []
[{:tri [0 1 2] :uvs [[0 0] [1 0] [1 1]]}
{:tri [0 2 3] :uvs [[0 0] [1 1] [0 1]]}
{:tri [1 5 6] :uvs [[0 0] [1 0] [1 1]]}
{:tri [1 6 2] :uvs [[0 0] [1 1] [0 1]]}
{:tri [5 4 7] :uvs [[0 0] [1 0] [1 1]]}
{:tri [5 7 6] :uvs [[0 0] [1 1] [0 1]]}
{:tri [4 0 3] :uvs [[0 0] [1 0] [1 1]]}
{:tri [4 3 7] :uvs [[0 0] [1 1] [0 1]]}
{:tri [3 2 6] :uvs [[0 0] [1 0] [1 1]]}
{:tri [3 6 7] :uvs [[0 0] [1 1] [0 1]]}
{:tri [4 5 1] :uvs [[0 0] [1 0] [1 1]]}
{:tri [4 1 0] :uvs [[0 0] [1 1] [0 1]]}])
(fn get-face-color [face-idx] (fn get-face-color [face-idx]
(let [colors [12 22 30 16 28 20]] (let [colors [12 22 30 16 28 20]]
(. colors (+ 1 (% face-idx 6))))) (. colors (+ 1 (% face-idx 6)))))
@ -43,10 +68,16 @@
(when (pxl8.key_pressed " ") (when (pxl8.key_pressed " ")
(set wireframe (not wireframe))) (set wireframe (not wireframe)))
(when (pxl8.key_pressed "r") (when (pxl8.key_pressed "f")
(set auto-rotate (not auto-rotate))) (set affine (not affine)))
(when (pxl8.key_pressed "p") (when (pxl8.key_pressed "p")
(set orthographic (not orthographic))) (set orthographic (not orthographic)))
(when (pxl8.key_pressed "r")
(set auto-rotate (not auto-rotate)))
(when (pxl8.key_pressed "t")
(set use-texture (not use-texture))
(when use-texture
(init-texture)))
(when (pxl8.key_down "=") (when (pxl8.key_down "=")
(set zoom (- zoom (* dt 2.0)))) (set zoom (- zoom (* dt 2.0))))
@ -63,8 +94,9 @@
(pxl8.clr 0) (pxl8.clr 0)
(pxl8.clear_zbuffer) (pxl8.clear_zbuffer)
(pxl8.set_wireframe wireframe) (pxl8.set_affine_textures affine)
(pxl8.set_backface_culling true) (pxl8.set_backface_culling true)
(pxl8.set_wireframe wireframe)
(if orthographic (if orthographic
(let [size (* 2.5 (/ zoom 5.0)) (let [size (* 2.5 (/ zoom 5.0))
@ -84,15 +116,36 @@
(pxl8.mat4_multiply (pxl8.mat4_rotate_z angle-z)))] (pxl8.mat4_multiply (pxl8.mat4_rotate_z angle-z)))]
(pxl8.set_model model)) (pxl8.set_model model))
(let [vertices (make-cube-vertices) (let [vertices (make-cube-vertices)]
faces (make-cube-faces)] (if (and use-texture texture-id)
(each [i face (ipairs faces)] (let [faces (make-cube-faces-with-uvs)]
(let [[i0 i1 i2] face (each [i face-data (ipairs faces)]
v0 (. vertices (+ 1 i0)) (let [tri-indices face-data.tri
v1 (. vertices (+ 1 i1)) tri-uvs face-data.uvs
v2 (. vertices (+ 1 i2)) v0 (. vertices (+ 1 (. tri-indices 1)))
color (get-face-color (math.floor (/ (- i 1) 2)))] v1 (. vertices (+ 1 (. tri-indices 2)))
(pxl8.draw_triangle_3d v0 v1 v2 color))))) v2 (. vertices (+ 1 (. tri-indices 3)))
uv0 (. tri-uvs 1)
uv1 (. tri-uvs 2)
uv2 (. tri-uvs 3)]
(pxl8.draw_triangle_3d_textured
v0 v1 v2
uv0 uv1 uv2
texture-id))))
(let [faces (make-cube-faces)]
(each [i face (ipairs faces)]
(let [[i0 i1 i2] face
v0 (. vertices (+ 1 i0))
v1 (. vertices (+ 1 i1))
v2 (. vertices (+ 1 i2))
color (get-face-color (math.floor (/ (- i 1) 2)))]
(pxl8.draw_triangle_3d v0 v1 v2 color))))))
(pxl8.text "WASD/QE: Rotate | +/-: Zoom | Space: Wire | R: Auto | P: Proj" 5 5 15)
(pxl8.text (.. "T: Texture | F: Affine | Mode: "
(if wireframe "Wire" "Fill")
" | Texture: " (if use-texture "On" "Off")
" | Mapping: " (if affine "Affine" "Persp")) 5 15 15))
{:update cube-update {:update cube-update
:frame cube-frame} :frame cube-frame}

View file

@ -7,6 +7,7 @@
(var fire-init false) (var fire-init false)
(var rain-init false) (var rain-init false)
(var snow-init false) (var snow-init false)
(var use-nes-palette false)
(var logo-x 256) (var logo-x 256)
(var logo-y 148) (var logo-y 148)
@ -15,7 +16,7 @@
(var logo-sprite nil) (var logo-sprite nil)
(global init (fn [] (global init (fn []
(pxl8.load_palette "palettes/gruvbox.ase") (pxl8.load_palette "sprites/pxl8_logo.ase")
(set logo-sprite (pxl8.load_sprite "sprites/pxl8_logo.ase")) (set logo-sprite (pxl8.load_sprite "sprites/pxl8_logo.ase"))
(set particles (pxl8.particles_new 1000)))) (set particles (pxl8.particles_new 1000))))
@ -41,6 +42,12 @@
(set snow-init false)) (set snow-init false))
(when (pxl8.key_pressed "8") (when (pxl8.key_pressed "8")
(set current-effect 8)) (set current-effect 8))
(when (pxl8.key_pressed "9")
(set use-nes-palette (not use-nes-palette))
(local palette-path (if use-nes-palette "palettes/nes.ase" "sprites/pxl8_logo.ase"))
(print (.. "Switching to palette: " palette-path))
(pxl8.load_palette palette-path)
(print "Palette loaded"))
(case current-effect (case current-effect
1 (do 1 (do
@ -63,15 +70,15 @@
(when logo-sprite (when logo-sprite
(pxl8.sprite logo-sprite logo-x logo-y 128 64))) (pxl8.sprite logo-sprite logo-x logo-y 128 64)))
2 (pxl8.vfx_plasma time 0.10 0.04 0) 2 (pxl8.vfx_plasma time 0.10 0.04 1)
3 (pxl8.vfx_tunnel time 2.0 0.25) 3 (pxl8.vfx_tunnel time 2.0 0.25)
4 (do 4 (do
(pxl8.clr 0) (pxl8.clr 0)
(local bars [{:base_y 60 :amplitude 30 :height 16 :speed 2.0 :phase 0 :color 20 :fade_color 10} (local bars [{:base_y 60 :amplitude 30 :height 16 :speed 2.0 :phase 0 :color 1 :fade_color 18}
{:base_y 180 :amplitude 35 :height 16 :speed 1.8 :phase 2.0 :color 26 :fade_color 10} {:base_y 180 :amplitude 35 :height 16 :speed 1.8 :phase 2.0 :color 1 :fade_color 27}
{:base_y 300 :amplitude 25 :height 16 :speed 2.2 :phase 4.0 :color 14 :fade_color 10}]) {:base_y 300 :amplitude 25 :height 16 :speed 2.2 :phase 4.0 :color 1 :fade_color 24}])
(pxl8.vfx_raster_bars bars time)) (pxl8.vfx_raster_bars bars time))
5 (do 5 (do

BIN
demo/palettes/nes.ase Normal file

Binary file not shown.

View file

@ -69,6 +69,22 @@ function pxl8.load_sprite(filepath)
end end
end end
function pxl8.create_texture(pixels, width, height)
local pixel_data = ffi.new("u8[?]", width * height)
for i = 0, width * height - 1 do
pixel_data[i] = pixels[i + 1] or 0
end
local result = C.pxl8_gfx_create_texture(gfx, pixel_data, width, height)
if result < 0 then
return nil
end
return result
end
function pxl8.upload_atlas()
C.pxl8_gfx_upload_atlas(gfx)
end
-- log -- log
function pxl8.info(msg) function pxl8.info(msg)
C.pxl8_lua_info(msg) C.pxl8_lua_info(msg)
@ -267,6 +283,10 @@ function pxl8.set_wireframe(wireframe)
C.pxl8_3d_set_wireframe(gfx, wireframe) C.pxl8_3d_set_wireframe(gfx, wireframe)
end end
function pxl8.set_affine_textures(affine)
C.pxl8_3d_set_affine_textures(gfx, affine)
end
function pxl8.set_backface_culling(culling) function pxl8.set_backface_culling(culling)
C.pxl8_3d_set_backface_culling(gfx, culling) C.pxl8_3d_set_backface_culling(gfx, culling)
end end
@ -278,6 +298,14 @@ function pxl8.draw_triangle_3d(v0, v1, v2, color)
C.pxl8_3d_draw_triangle_raw(gfx, vec0, vec1, vec2, color) C.pxl8_3d_draw_triangle_raw(gfx, vec0, vec1, vec2, color)
end end
function pxl8.draw_triangle_3d_textured(v0, v1, v2, uv0, uv1, uv2, texture_id)
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_textured(gfx, vec0, vec1, vec2,
uv0[1], uv0[2], uv1[1], uv1[2], uv2[1], uv2[2], texture_id)
end
function pxl8.draw_line_3d(p0, p1, color) function pxl8.draw_line_3d(p0, p1, color)
local vec0 = ffi.new("pxl8_vec3", {x = p0[1], y = p0[2], z = p0[3]}) local vec0 = ffi.new("pxl8_vec3", {x = p0[1], y = p0[2], z = p0[3]})
local vec1 = ffi.new("pxl8_vec3", {x = p1[1], y = p1[2], z = p1[3]}) local vec1 = ffi.new("pxl8_vec3", {x = p1[1], y = p1[2], z = p1[3]})

View file

@ -245,11 +245,6 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) {
return SDL_APP_FAILURE; return SDL_APP_FAILURE;
} }
if (pxl8_gfx_init_atlas(app.gfx, 1024, 1024) != PXL8_OK) {
pxl8_error("Failed to initialize sprite atlas");
return SDL_APP_FAILURE;
}
app.ui = pxl8_ui_create(app.gfx); app.ui = pxl8_ui_create(app.gfx);
if (!app.ui) { if (!app.ui) {
pxl8_error("Failed to create UI"); pxl8_error("Failed to create UI");

View file

@ -260,16 +260,11 @@ pxl8_result pxl8_ase_load(const char* filepath, pxl8_ase_file* ase_file) {
chunk_header.chunk_type = read_u16_le(chunk_data + 4); chunk_header.chunk_type = read_u16_le(chunk_data + 4);
const u8* chunk_payload = chunk_data + 6; const u8* chunk_payload = chunk_data + 6;
pxl8_debug("Found chunk: type=0x%04X, size=%d", chunk_header.chunk_type, chunk_header.chunk_size);
switch (chunk_header.chunk_type) { switch (chunk_header.chunk_type) {
case PXL8_ASE_CHUNK_OLD_PALETTE: // 0x0004 case PXL8_ASE_CHUNK_OLD_PALETTE: // 0x0004
if (!ase_file->palette.colors) { if (!ase_file->palette.colors) {
result = parse_old_palette_chunk(chunk_payload, &ase_file->palette); result = parse_old_palette_chunk(chunk_payload, &ase_file->palette);
pxl8_debug("Parsed old palette: %d colors, indices %d-%d",
ase_file->palette.entry_count, ase_file->palette.first_color, ase_file->palette.last_color);
} else {
pxl8_debug("Ignoring old palette (0x0004) - new palette (0x2019) already loaded");
} }
break; break;
@ -284,11 +279,6 @@ pxl8_result pxl8_ase_load(const char* filepath, pxl8_ase_file* ase_file) {
result = parse_layer_chunk(chunk_payload, &ase_file->layers[ase_file->layer_count]); result = parse_layer_chunk(chunk_payload, &ase_file->layers[ase_file->layer_count]);
if (result == PXL8_OK) { if (result == PXL8_OK) {
pxl8_debug("Parsed layer %d: '%s', blend_mode=%d, opacity=%d",
ase_file->layer_count,
ase_file->layers[ase_file->layer_count].name ? ase_file->layers[ase_file->layer_count].name : "(unnamed)",
ase_file->layers[ase_file->layer_count].blend_mode,
ase_file->layers[ase_file->layer_count].opacity);
ase_file->layer_count++; ase_file->layer_count++;
} }
break; break;
@ -296,14 +286,11 @@ pxl8_result pxl8_ase_load(const char* filepath, pxl8_ase_file* ase_file) {
case PXL8_ASE_CHUNK_CEL: { // 0x2005 case PXL8_ASE_CHUNK_CEL: { // 0x2005
pxl8_ase_cel cel = {0}; pxl8_ase_cel cel = {0};
pxl8_debug("Found CEL chunk: size=%d", chunk_header.chunk_size - 6);
result = parse_cel_chunk(chunk_payload, chunk_header.chunk_size - 6, &cel); result = parse_cel_chunk(chunk_payload, chunk_header.chunk_size - 6, &cel);
if (result == PXL8_OK && cel.pixel_data) { if (result == PXL8_OK && cel.pixel_data) {
pxl8_debug("CEL data loaded: %dx%d, type=%d, first pixel = %d", cel.width, cel.height, cel.cel_type, cel.pixel_data[0]);
u32 copy_width = (cel.width < frame->width) ? cel.width : frame->width; u32 copy_width = (cel.width < frame->width) ? cel.width : frame->width;
u32 copy_height = (cel.height < frame->height) ? cel.height : frame->height; u32 copy_height = (cel.height < frame->height) ? cel.height : frame->height;
pxl8_debug("Copying cel to frame: cel_pos=(%d,%d), copy_size=%dx%d", cel.x, cel.y, copy_width, copy_height);
for (u32 y = 0; y < copy_height; y++) { for (u32 y = 0; y < copy_height; y++) {
u32 src_offset = y * cel.width; u32 src_offset = y * cel.width;
u32 dst_offset = (y + cel.y) * frame->width + cel.x; u32 dst_offset = (y + cel.y) * frame->width + cel.x;
@ -311,20 +298,16 @@ pxl8_result pxl8_ase_load(const char* filepath, pxl8_ase_file* ase_file) {
for (u32 x = 0; x < copy_width; x++) { for (u32 x = 0; x < copy_width; x++) {
u8 src_pixel = cel.pixel_data[src_offset + x]; u8 src_pixel = cel.pixel_data[src_offset + x];
bool is_transparent = false; bool is_transparent = false;
if (src_pixel < ase_file->palette.entry_count && ase_file->palette.colors) { if (src_pixel < ase_file->palette.entry_count && ase_file->palette.colors) {
u32 color = ase_file->palette.colors[src_pixel]; u32 color = ase_file->palette.colors[src_pixel];
is_transparent = ((color >> 24) & 0xFF) == 0; is_transparent = ((color >> 24) & 0xFF) == 0;
} }
if (!is_transparent) { if (!is_transparent) {
frame->pixels[dst_offset + x] = src_pixel; frame->pixels[dst_offset + x] = src_pixel;
} }
} }
if (y < 3) {
pxl8_debug("Row %d: cel[%d]=%d, frame[%d]=%d",
y, src_offset, cel.pixel_data[src_offset], dst_offset, frame->pixels[dst_offset]);
}
} }
} }
SDL_free(cel.pixel_data); SDL_free(cel.pixel_data);
@ -337,8 +320,6 @@ pxl8_result pxl8_ase_load(const char* filepath, pxl8_ase_file* ase_file) {
SDL_free(ase_file->palette.colors); SDL_free(ase_file->palette.colors);
} }
result = parse_palette_chunk(chunk_payload, &ase_file->palette); result = parse_palette_chunk(chunk_payload, &ase_file->palette);
pxl8_debug("Parsed new palette: %d colors, indices %d-%d",
ase_file->palette.entry_count, ase_file->palette.first_color, ase_file->palette.last_color);
break; break;
default: default:

File diff suppressed because it is too large Load diff

View file

@ -3,6 +3,7 @@
#include "pxl8_math.h" #include "pxl8_math.h"
#include "pxl8_types.h" #include "pxl8_types.h"
typedef struct pxl8_atlas pxl8_atlas;
typedef struct pxl8_gfx pxl8_gfx; typedef struct pxl8_gfx pxl8_gfx;
typedef enum pxl8_blend_mode { typedef enum pxl8_blend_mode {
@ -48,6 +49,7 @@ typedef struct pxl8_effects {
typedef struct pxl8_triangle { typedef struct pxl8_triangle {
pxl8_vertex v[3]; pxl8_vertex v[3];
u32 texture_id;
} pxl8_triangle; } pxl8_triangle;
#ifdef __cplusplus #ifdef __cplusplus
@ -61,12 +63,13 @@ pxl8_bounds pxl8_gfx_get_bounds(pxl8_gfx* gfx);
pxl8_color_mode pxl8_gfx_get_color_mode(pxl8_gfx* gfx); pxl8_color_mode pxl8_gfx_get_color_mode(pxl8_gfx* gfx);
u8* pxl8_gfx_get_framebuffer(pxl8_gfx* gfx); u8* pxl8_gfx_get_framebuffer(pxl8_gfx* gfx);
i32 pxl8_gfx_get_height(const pxl8_gfx* gfx); i32 pxl8_gfx_get_height(const pxl8_gfx* gfx);
u32 pxl8_gfx_get_palette_size(const pxl8_gfx* gfx);
void pxl8_gfx_get_resolution_dimensions(pxl8_resolution resolution, i32* width, i32* height); void pxl8_gfx_get_resolution_dimensions(pxl8_resolution resolution, i32* width, i32* height);
i32 pxl8_gfx_get_width(const pxl8_gfx* gfx); 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_font_atlas(pxl8_gfx* gfx);
pxl8_result pxl8_gfx_load_palette(pxl8_gfx* gfx, const char* path); pxl8_result pxl8_gfx_load_palette(pxl8_gfx* gfx, const char* path);
pxl8_result pxl8_gfx_load_sprite(pxl8_gfx* gfx, const char* path); pxl8_result pxl8_gfx_load_sprite(pxl8_gfx* gfx, const char* path);
pxl8_result pxl8_gfx_create_texture(pxl8_gfx* gfx, const u8* pixels, u32 width, u32 height);
void pxl8_gfx_present(pxl8_gfx* gfx); 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_project(pxl8_gfx* gfx, f32 left, f32 right, f32 top, f32 bottom);
void pxl8_gfx_upload_atlas(pxl8_gfx* gfx); void pxl8_gfx_upload_atlas(pxl8_gfx* gfx);
@ -96,6 +99,8 @@ 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_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(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_draw_triangle_raw(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, pxl8_vec3 v2, u32 color);
void pxl8_3d_draw_triangle_textured(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, pxl8_vec3 v2, f32 u0, f32 v0f, f32 u1, f32 v1f, f32 u2, f32 v2f, u32 texture_id);
void pxl8_3d_set_affine_textures(pxl8_gfx* gfx, bool affine);
void pxl8_3d_set_backface_culling(pxl8_gfx* gfx, bool culling); 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_model(pxl8_gfx* gfx, pxl8_mat4 mat);
void pxl8_3d_set_projection(pxl8_gfx* gfx, pxl8_mat4 mat); void pxl8_3d_set_projection(pxl8_gfx* gfx, pxl8_mat4 mat);

View file

@ -4,21 +4,21 @@
#include "pxl8_simd.h" #include "pxl8_simd.h"
pxl8_vec2 pxl8_vec2_add(pxl8_vec2 a, pxl8_vec2 b) { pxl8_vec2 pxl8_vec2_add(pxl8_vec2 a, pxl8_vec2 b) {
return (pxl8_vec2) { return (pxl8_vec2){
.x = a.x + b.x, .x = a.x + b.x,
.y = a.y + b.y, .y = a.y + b.y,
}; };
} }
pxl8_vec2 pxl8_vec2_sub(pxl8_vec2 a, pxl8_vec2 b) { pxl8_vec2 pxl8_vec2_sub(pxl8_vec2 a, pxl8_vec2 b) {
return (pxl8_vec2) { return (pxl8_vec2){
.x = a.x - b.x, .x = a.x - b.x,
.y = a.y - b.y, .y = a.y - b.y,
}; };
} }
pxl8_vec2 pxl8_vec2_scale(pxl8_vec2 v, f32 s) { pxl8_vec2 pxl8_vec2_scale(pxl8_vec2 v, f32 s) {
return (pxl8_vec2) { return (pxl8_vec2){
.x = v.x * s, .x = v.x * s,
.y = v.y * s, .y = v.y * s,
}; };
@ -34,7 +34,9 @@ f32 pxl8_vec2_length(pxl8_vec2 v) {
pxl8_vec2 pxl8_vec2_normalize(pxl8_vec2 v) { pxl8_vec2 pxl8_vec2_normalize(pxl8_vec2 v) {
f32 len = pxl8_vec2_length(v); f32 len = pxl8_vec2_length(v);
if (len < 1e-6f) return (pxl8_vec2){0}; if (len < 1e-6f) return (pxl8_vec2){0};
return pxl8_vec2_scale(v, 1.0f / len); return pxl8_vec2_scale(v, 1.0f / len);
} }
@ -43,7 +45,7 @@ pxl8_vec3 pxl8_vec3_add(pxl8_vec3 a, pxl8_vec3 b) {
pxl8_simd_vec_f32 vb = pxl8_simd_set_f32(b.x, b.y, b.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); pxl8_simd_vec_f32 result = pxl8_simd_add_f32(va, vb);
return (pxl8_vec3) { return (pxl8_vec3){
.x = result.f32_array[0], .x = result.f32_array[0],
.y = result.f32_array[1], .y = result.f32_array[1],
.z = result.f32_array[2], .z = result.f32_array[2],
@ -55,7 +57,7 @@ pxl8_vec3 pxl8_vec3_sub(pxl8_vec3 a, pxl8_vec3 b) {
pxl8_simd_vec_f32 vb = pxl8_simd_set_f32(b.x, b.y, b.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); pxl8_simd_vec_f32 result = pxl8_simd_sub_f32(va, vb);
return (pxl8_vec3) { return (pxl8_vec3){
.x = result.f32_array[0], .x = result.f32_array[0],
.y = result.f32_array[1], .y = result.f32_array[1],
.z = result.f32_array[2], .z = result.f32_array[2],
@ -66,7 +68,7 @@ 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 vv = pxl8_simd_set_f32(v.x, v.y, v.z, 0);
pxl8_simd_vec_f32 result = pxl8_simd_scale_f32(vv, s); pxl8_simd_vec_f32 result = pxl8_simd_scale_f32(vv, s);
return (pxl8_vec3) { return (pxl8_vec3){
.x = result.f32_array[0], .x = result.f32_array[0],
.y = result.f32_array[1], .y = result.f32_array[1],
.z = result.f32_array[2], .z = result.f32_array[2],
@ -76,11 +78,12 @@ pxl8_vec3 pxl8_vec3_scale(pxl8_vec3 v, f32 s) {
f32 pxl8_vec3_dot(pxl8_vec3 a, pxl8_vec3 b) { 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 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 vb = pxl8_simd_set_f32(b.x, b.y, b.z, 0);
return pxl8_simd_dot3_f32(va, vb); return pxl8_simd_dot3_f32(va, vb);
} }
pxl8_vec3 pxl8_vec3_cross(pxl8_vec3 a, pxl8_vec3 b) { pxl8_vec3 pxl8_vec3_cross(pxl8_vec3 a, pxl8_vec3 b) {
return (pxl8_vec3) { return (pxl8_vec3){
.x = a.y * b.z - a.z * b.y, .x = a.y * b.z - a.z * b.y,
.y = a.z * b.x - a.x * b.z, .y = a.z * b.x - a.x * b.z,
.z = a.x * b.y - a.y * b.x, .z = a.x * b.y - a.y * b.x,
@ -93,18 +96,23 @@ f32 pxl8_vec3_length(pxl8_vec3 v) {
pxl8_vec3 pxl8_vec3_normalize(pxl8_vec3 v) { pxl8_vec3 pxl8_vec3_normalize(pxl8_vec3 v) {
f32 len = pxl8_vec3_length(v); f32 len = pxl8_vec3_length(v);
if (len < 1e-6f) return (pxl8_vec3){0}; if (len < 1e-6f) return (pxl8_vec3){0};
return pxl8_vec3_scale(v, 1.0f / len); return pxl8_vec3_scale(v, 1.0f / len);
} }
pxl8_mat4 pxl8_mat4_identity(void) { pxl8_mat4 pxl8_mat4_identity(void) {
pxl8_mat4 mat = {0}; pxl8_mat4 mat = {0};
mat.m[0] = mat.m[5] = mat.m[10] = mat.m[15] = 1.0f; mat.m[0] = mat.m[5] = mat.m[10] = mat.m[15] = 1.0f;
return mat; return mat;
} }
pxl8_mat4 pxl8_mat4_multiply(pxl8_mat4 a, pxl8_mat4 b) { pxl8_mat4 pxl8_mat4_multiply(pxl8_mat4 a, pxl8_mat4 b) {
pxl8_mat4 result = {0}; pxl8_mat4 mat = {0};
for (i32 i = 0; i < 4; i++) { for (i32 i = 0; i < 4; i++) {
for (i32 j = 0; j < 4; j++) { for (i32 j = 0; j < 4; j++) {
pxl8_simd_vec_f32 row = pxl8_simd_set_f32( pxl8_simd_vec_f32 row = pxl8_simd_set_f32(
@ -113,34 +121,35 @@ pxl8_mat4 pxl8_mat4_multiply(pxl8_mat4 a, pxl8_mat4 b) {
pxl8_simd_vec_f32 col = pxl8_simd_set_f32( 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] 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); mat.m[i * 4 + j] = pxl8_simd_dot4_f32(row, col);
} }
} }
return result;
return mat;
} }
pxl8_vec4 pxl8_mat4_multiply_vec4(pxl8_mat4 m, pxl8_vec4 v) { 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 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 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 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]); pxl8_simd_vec_f32 row3 = pxl8_simd_set_f32(m.m[12], m.m[13], m.m[14], m.m[15]);
pxl8_simd_vec_f32 vec = pxl8_simd_set_f32(v.x, v.y, v.z, v.w);
result.x = pxl8_simd_dot4_f32(row0, vec); return (pxl8_vec4){
result.y = pxl8_simd_dot4_f32(row1, vec); .x = pxl8_simd_dot4_f32(row0, vec),
result.z = pxl8_simd_dot4_f32(row2, vec); .y = pxl8_simd_dot4_f32(row1, vec),
result.w = pxl8_simd_dot4_f32(row3, vec); .z = pxl8_simd_dot4_f32(row2, vec),
.w = pxl8_simd_dot4_f32(row3, vec),
return result; };
} }
pxl8_mat4 pxl8_mat4_translate(f32 x, f32 y, f32 z) { pxl8_mat4 pxl8_mat4_translate(f32 x, f32 y, f32 z) {
pxl8_mat4 mat = pxl8_mat4_identity(); pxl8_mat4 mat = pxl8_mat4_identity();
mat.m[3] = x; mat.m[3] = x;
mat.m[7] = y; mat.m[7] = y;
mat.m[11] = z; mat.m[11] = z;
return mat; return mat;
} }
@ -148,10 +157,12 @@ pxl8_mat4 pxl8_mat4_rotate_x(f32 angle) {
pxl8_mat4 mat = pxl8_mat4_identity(); pxl8_mat4 mat = pxl8_mat4_identity();
f32 c = cosf(angle); f32 c = cosf(angle);
f32 s = sinf(angle); f32 s = sinf(angle);
mat.m[5] = c; mat.m[5] = c;
mat.m[6] = -s; mat.m[6] = -s;
mat.m[9] = s; mat.m[9] = s;
mat.m[10] = c; mat.m[10] = c;
return mat; return mat;
} }
@ -159,10 +170,12 @@ pxl8_mat4 pxl8_mat4_rotate_y(f32 angle) {
pxl8_mat4 mat = pxl8_mat4_identity(); pxl8_mat4 mat = pxl8_mat4_identity();
f32 c = cosf(angle); f32 c = cosf(angle);
f32 s = sinf(angle); f32 s = sinf(angle);
mat.m[0] = c; mat.m[0] = c;
mat.m[2] = s; mat.m[2] = s;
mat.m[8] = -s; mat.m[8] = -s;
mat.m[10] = c; mat.m[10] = c;
return mat; return mat;
} }
@ -170,18 +183,22 @@ pxl8_mat4 pxl8_mat4_rotate_z(f32 angle) {
pxl8_mat4 mat = pxl8_mat4_identity(); pxl8_mat4 mat = pxl8_mat4_identity();
f32 c = cosf(angle); f32 c = cosf(angle);
f32 s = sinf(angle); f32 s = sinf(angle);
mat.m[0] = c; mat.m[0] = c;
mat.m[1] = -s; mat.m[1] = -s;
mat.m[4] = s; mat.m[4] = s;
mat.m[5] = c; mat.m[5] = c;
return mat; return mat;
} }
pxl8_mat4 pxl8_mat4_scale(f32 x, f32 y, f32 z) { pxl8_mat4 pxl8_mat4_scale(f32 x, f32 y, f32 z) {
pxl8_mat4 mat = pxl8_mat4_identity(); pxl8_mat4 mat = pxl8_mat4_identity();
mat.m[0] = x; mat.m[0] = x;
mat.m[5] = y; mat.m[5] = y;
mat.m[10] = z; mat.m[10] = z;
return mat; return mat;
} }
@ -213,11 +230,11 @@ pxl8_mat4 pxl8_mat4_perspective(f32 fov, f32 aspect, f32 near, f32 far) {
} }
pxl8_mat4 pxl8_mat4_lookat(pxl8_vec3 eye, pxl8_vec3 center, pxl8_vec3 up) { pxl8_mat4 pxl8_mat4_lookat(pxl8_vec3 eye, pxl8_vec3 center, pxl8_vec3 up) {
pxl8_mat4 mat = pxl8_mat4_identity();
pxl8_vec3 f = pxl8_vec3_normalize(pxl8_vec3_sub(center, eye)); pxl8_vec3 f = pxl8_vec3_normalize(pxl8_vec3_sub(center, eye));
pxl8_vec3 s = pxl8_vec3_normalize(pxl8_vec3_cross(f, up)); pxl8_vec3 s = pxl8_vec3_normalize(pxl8_vec3_cross(f, up));
pxl8_vec3 u = pxl8_vec3_cross(s, f); pxl8_vec3 u = pxl8_vec3_cross(s, f);
pxl8_mat4 mat = pxl8_mat4_identity();
mat.m[0] = s.x; mat.m[0] = s.x;
mat.m[1] = s.y; mat.m[1] = s.y;
mat.m[2] = s.z; mat.m[2] = s.z;

View file

@ -56,6 +56,8 @@ static const char* pxl8_ffi_cdefs =
"void pxl8_gfx_fade_palette(pxl8_gfx* ctx, u8 start, u8 count, f32 amount, u32 target_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_palette(pxl8_gfx* ctx, const char* filepath);\n"
"i32 pxl8_gfx_load_sprite(pxl8_gfx* ctx, const char* filepath, u32* sprite_id);\n" "i32 pxl8_gfx_load_sprite(pxl8_gfx* ctx, const char* filepath, u32* sprite_id);\n"
"i32 pxl8_gfx_create_texture(pxl8_gfx* ctx, const u8* pixels, u32 width, u32 height);\n"
"void pxl8_gfx_upload_atlas(pxl8_gfx* ctx);\n"
"typedef struct pxl8_input_state pxl8_input_state;\n" "typedef struct pxl8_input_state pxl8_input_state;\n"
"bool pxl8_key_down(const pxl8_input_state* input, i32 key);\n" "bool pxl8_key_down(const pxl8_input_state* input, i32 key);\n"
"bool pxl8_key_pressed(const pxl8_input_state* input, i32 key);\n" "bool pxl8_key_pressed(const pxl8_input_state* input, i32 key);\n"
@ -133,6 +135,8 @@ static const char* pxl8_ffi_cdefs =
"void pxl8_3d_clear_zbuffer(pxl8_gfx* gfx);\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_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_draw_triangle_raw(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, pxl8_vec3 v2, u32 color);\n"
"void pxl8_3d_draw_triangle_textured(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, pxl8_vec3 v2, f32 u0, f32 v0, f32 u1, f32 v1, f32 u2, f32 v2, u32 texture_id);\n"
"void pxl8_3d_set_affine_textures(pxl8_gfx* gfx, bool affine);\n"
"void pxl8_3d_set_backface_culling(pxl8_gfx* gfx, bool culling);\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_model(pxl8_gfx* gfx, pxl8_mat4 mat);\n"
"void pxl8_3d_set_projection(pxl8_gfx* gfx, pxl8_mat4 mat);\n" "void pxl8_3d_set_projection(pxl8_gfx* gfx, pxl8_mat4 mat);\n"

View file

@ -28,11 +28,11 @@ struct pxl8_particles {
void* userdata; void* userdata;
}; };
void pxl8_vfx_plasma(pxl8_gfx* ctx, f32 time, f32 scale1, f32 scale2, u8 palette_offset) { void pxl8_vfx_plasma(pxl8_gfx* gfx, f32 time, f32 scale1, f32 scale2, u8 palette_offset) {
if (!ctx || !pxl8_gfx_get_framebuffer(ctx)) return; if (!gfx || !pxl8_gfx_get_framebuffer(gfx)) return;
for (i32 y = 0; y < pxl8_gfx_get_height(ctx); y++) { for (i32 y = 0; y < pxl8_gfx_get_height(gfx); y++) {
for (i32 x = 0; x < pxl8_gfx_get_width(ctx); x++) { for (i32 x = 0; x < pxl8_gfx_get_width(gfx); x++) {
f32 v1 = sinf(x * scale1 + time); f32 v1 = sinf(x * scale1 + time);
f32 v2 = sinf(y * scale1 + time * 0.7f); f32 v2 = sinf(y * scale1 + time * 0.7f);
f32 v3 = sinf((x + y) * scale2 + time * 1.3f); f32 v3 = sinf((x + y) * scale2 + time * 1.3f);
@ -44,13 +44,13 @@ void pxl8_vfx_plasma(pxl8_gfx* ctx, f32 time, f32 scale1, f32 scale2, u8 palette
if (normalized > 1.0f) normalized = 1.0f; if (normalized > 1.0f) normalized = 1.0f;
u8 color = palette_offset + (u8)(15.0f * normalized); u8 color = palette_offset + (u8)(15.0f * normalized);
pxl8_pixel(ctx, x, y, color); pxl8_pixel(gfx, x, y, color);
} }
} }
} }
void pxl8_vfx_raster_bars(pxl8_gfx* ctx, pxl8_raster_bar* bars, u32 bar_count, f32 time) { void pxl8_vfx_raster_bars(pxl8_gfx* gfx, pxl8_raster_bar* bars, u32 bar_count, f32 time) {
if (!ctx || !bars) return; if (!gfx || !bars) return;
for (u32 i = 0; i < bar_count; i++) { for (u32 i = 0; i < bar_count; i++) {
pxl8_raster_bar* bar = &bars[i]; pxl8_raster_bar* bar = &bars[i];
@ -72,24 +72,24 @@ void pxl8_vfx_raster_bars(pxl8_gfx* ctx, pxl8_raster_bar* bars, u32 bar_count, f
color_idx = bar->color - 1; color_idx = bar->color - 1;
} }
pxl8_rect_fill(ctx, 0, y_int + dy, pxl8_gfx_get_width(ctx), 1, color_idx); pxl8_rect_fill(gfx, 0, y_int + dy, pxl8_gfx_get_width(gfx), 1, color_idx);
} }
} }
} }
void pxl8_vfx_rotozoom(pxl8_gfx* ctx, f32 angle, f32 zoom, i32 cx, i32 cy) { void pxl8_vfx_rotozoom(pxl8_gfx* gfx, f32 angle, f32 zoom, i32 cx, i32 cy) {
if (!ctx || !pxl8_gfx_get_framebuffer(ctx)) return; if (!gfx || !pxl8_gfx_get_framebuffer(gfx)) return;
f32 cos_a = cosf(angle); f32 cos_a = cosf(angle);
f32 sin_a = sinf(angle); f32 sin_a = sinf(angle);
u8* temp_buffer = (u8*)SDL_malloc(pxl8_gfx_get_width(ctx) * pxl8_gfx_get_height(ctx)); u8* temp_buffer = (u8*)SDL_malloc(pxl8_gfx_get_width(gfx) * pxl8_gfx_get_height(gfx));
if (!temp_buffer) return; if (!temp_buffer) return;
SDL_memcpy(temp_buffer, pxl8_gfx_get_framebuffer(ctx), pxl8_gfx_get_width(ctx) * pxl8_gfx_get_height(ctx)); SDL_memcpy(temp_buffer, pxl8_gfx_get_framebuffer(gfx), pxl8_gfx_get_width(gfx) * pxl8_gfx_get_height(gfx));
for (i32 y = 0; y < pxl8_gfx_get_height(ctx); y++) { for (i32 y = 0; y < pxl8_gfx_get_height(gfx); y++) {
for (i32 x = 0; x < pxl8_gfx_get_width(ctx); x++) { for (i32 x = 0; x < pxl8_gfx_get_width(gfx); x++) {
f32 dx = x - cx; f32 dx = x - cx;
f32 dy = y - cy; f32 dy = y - cy;
@ -99,8 +99,8 @@ void pxl8_vfx_rotozoom(pxl8_gfx* ctx, f32 angle, f32 zoom, i32 cx, i32 cy) {
i32 sx = (i32)src_x; i32 sx = (i32)src_x;
i32 sy = (i32)src_y; i32 sy = (i32)src_y;
if (sx >= 0 && sx < pxl8_gfx_get_width(ctx) && sy >= 0 && sy < pxl8_gfx_get_height(ctx)) { if (sx >= 0 && sx < pxl8_gfx_get_width(gfx) && sy >= 0 && sy < pxl8_gfx_get_height(gfx)) {
pxl8_gfx_get_framebuffer(ctx)[y * pxl8_gfx_get_width(ctx) + x] = temp_buffer[sy * pxl8_gfx_get_width(ctx) + sx]; pxl8_gfx_get_framebuffer(gfx)[y * pxl8_gfx_get_width(gfx) + x] = temp_buffer[sy * pxl8_gfx_get_width(gfx) + sx];
} }
} }
} }
@ -108,38 +108,41 @@ void pxl8_vfx_rotozoom(pxl8_gfx* ctx, f32 angle, f32 zoom, i32 cx, i32 cy) {
SDL_free(temp_buffer); SDL_free(temp_buffer);
} }
void pxl8_vfx_tunnel(pxl8_gfx* ctx, f32 time, f32 speed, f32 twist) { void pxl8_vfx_tunnel(pxl8_gfx* gfx, f32 time, f32 speed, f32 twist) {
if (!ctx || !pxl8_gfx_get_framebuffer(ctx)) return; if (!gfx || !pxl8_gfx_get_framebuffer(gfx)) return;
f32 cx = pxl8_gfx_get_width(ctx) / 2.0f; f32 cx = pxl8_gfx_get_width(gfx) / 2.0f;
f32 cy = pxl8_gfx_get_height(ctx) / 2.0f; f32 cy = pxl8_gfx_get_height(gfx) / 2.0f;
u32 palette_size = pxl8_gfx_get_palette_size(gfx);
for (i32 y = 0; y < pxl8_gfx_get_height(ctx); y++) { if (palette_size == 0) palette_size = 256;
for (i32 x = 0; x < pxl8_gfx_get_width(ctx); x++) {
for (i32 y = 0; y < pxl8_gfx_get_height(gfx); y++) {
for (i32 x = 0; x < pxl8_gfx_get_width(gfx); x++) {
f32 dx = x - cx; f32 dx = x - cx;
f32 dy = y - cy; f32 dy = y - cy;
f32 dist = sqrtf(dx * dx + dy * dy); f32 dist = sqrtf(dx * dx + dy * dy);
if (dist > 1.0f) { if (dist > 1.0f) {
f32 angle = atan2f(dy, dx); f32 angle = atan2f(dy, dx);
f32 u = angle * 0.159f + twist * sinf(time * 0.5f); f32 u = angle * 0.159f + twist * sinf(time * 0.5f);
f32 v = 256.0f / dist + time * speed; f32 v = 256.0f / dist + time * speed;
u8 tx = (u8)((i32)(u * 256.0f) & 0xFF); u8 tx = (u8)((i32)(u * 256.0f) & 0xFF);
u8 ty = (u8)((i32)(v * 256.0f) & 0xFF); u8 ty = (u8)((i32)(v * 256.0f) & 0xFF);
u8 color = tx ^ ty; u8 pattern = tx ^ ty;
u8 color = (pattern / 8) % palette_size;
pxl8_pixel(ctx, x, y, color);
pxl8_pixel(gfx, x, y, color);
} }
} }
} }
} }
void pxl8_vfx_water_ripple(pxl8_gfx* ctx, f32* height_map, i32 drop_x, i32 drop_y, f32 damping) { void pxl8_vfx_water_ripple(pxl8_gfx* gfx, f32* height_map, i32 drop_x, i32 drop_y, f32 damping) {
if (!ctx || !height_map) return; if (!gfx || !height_map) return;
i32 w = pxl8_gfx_get_width(ctx); i32 w = pxl8_gfx_get_width(gfx);
i32 h = pxl8_gfx_get_height(ctx); i32 h = pxl8_gfx_get_height(gfx);
static f32* prev_height = NULL; static f32* prev_height = NULL;
if (!prev_height) { if (!prev_height) {

View file

@ -46,8 +46,8 @@ 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_sparks(pxl8_particles* particles, i32 x, i32 y, u32 color);
void pxl8_vfx_starfield(pxl8_particles* particles, f32 speed, f32 spread); 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_plasma(pxl8_gfx* gfx, 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_raster_bars(pxl8_gfx* gfx, 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_rotozoom(pxl8_gfx* gfx, f32 angle, f32 zoom, i32 cx, i32 cy);
void pxl8_vfx_tunnel(pxl8_gfx* ctx, f32 time, f32 speed, f32 twist); void pxl8_vfx_tunnel(pxl8_gfx* gfx, 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); void pxl8_vfx_water_ripple(pxl8_gfx* gfx, f32* height_map, i32 drop_x, i32 drop_y, f32 damping);