From b1e8525c3e1ec8f1ac019306fbb3a8f6dba5d18a Mon Sep 17 00:00:00 2001 From: asrael Date: Fri, 28 Nov 2025 14:41:35 -0600 Subject: [PATCH] true 16-bit color... glorious --- README.md | 6 + demo/mod/worldgen.fnl | 13 +- pxl8.sh | 7 +- src/pxl8.c | 5 +- src/pxl8_ase.c | 20 +- src/pxl8_atlas.c | 49 +++-- src/pxl8_blit.c | 12 +- src/pxl8_blit.h | 4 +- src/pxl8_bsp.c | 5 +- src/pxl8_cart.c | 30 +-- src/pxl8_color.h | 42 ++++ src/pxl8_embed.h | 88 +++++++++ src/{pxl8_procgen.c => pxl8_gen.c} | 4 +- src/{pxl8_procgen.h => pxl8_gen.h} | 0 src/pxl8_gfx.c | 155 +++++++-------- src/pxl8_gfx.h | 3 +- src/pxl8_io.c | 8 +- src/pxl8_io.h | 16 +- src/pxl8_rec.c | 137 +++++++++++++ src/pxl8_rec.h | 31 +++ src/pxl8_script.c | 74 +++++-- src/pxl8_sdl3.c | 62 +++--- src/pxl8_tilesheet.c | 59 ++++-- src/pxl8_transition.c | 7 +- src/pxl8_types.h | 14 +- src/pxl8_ui.c | 308 ----------------------------- src/pxl8_ui.h | 80 -------- src/pxl8_vfx.c | 56 +++--- src/pxl8_world.c | 33 +--- src/pxl8_world.h | 2 +- 30 files changed, 678 insertions(+), 652 deletions(-) create mode 100644 src/pxl8_color.h create mode 100644 src/pxl8_embed.h rename src/{pxl8_procgen.c => pxl8_gen.c} (99%) rename src/{pxl8_procgen.h => pxl8_gen.h} (100%) create mode 100644 src/pxl8_rec.c create mode 100644 src/pxl8_rec.h delete mode 100644 src/pxl8_ui.c delete mode 100644 src/pxl8_ui.h diff --git a/README.md b/README.md index c7cba97..87dc7e0 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,15 @@ # pxl8 +### Requirements + +- **Clang 19+** (or GCC 15+) - Required for C23 `#embed` support +- **SDL3** - System package or auto-vendored + ### Quick Start ```bash ./pxl8.sh build # Build pxl8 system +./pxl8.sh install # Install pxl8 binary to ~/.local/bin ./pxl8.sh run [game.cart | project_dir] # Run pxl8 demo (or a specific game) ./pxl8.sh run [game.cart | project_dir] --repl # Run pxl8 demo (or a specific game) with a REPL ``` diff --git a/demo/mod/worldgen.fnl b/demo/mod/worldgen.fnl index 170b58f..04a2fe4 100644 --- a/demo/mod/worldgen.fnl +++ b/demo/mod/worldgen.fnl @@ -3,6 +3,7 @@ (var world nil) (var auto-run? false) +(var auto-run-cancel-key nil) (var cam-pitch 0) (var cam-x 1000) (var cam-y 64) @@ -77,7 +78,15 @@ (fn update [dt] (when (pxl8.key_pressed "`") - (set auto-run? (not auto-run?))) + (set auto-run? (not auto-run?)) + (when (and auto-run? (pxl8.key_down "w")) + (set auto-run-cancel-key "w"))) + (when (and auto-run? (not auto-run-cancel-key) (or (pxl8.key_down "w") (pxl8.key_down "s"))) + (set auto-run? false) + (when (pxl8.key_down "s") + (set auto-run-cancel-key "s"))) + (when (and auto-run-cancel-key (not (pxl8.key_down auto-run-cancel-key))) + (set auto-run-cancel-key nil)) (when (pxl8.world_is_loaded world) (let [forward-x (- (math.sin cam-yaw)) @@ -94,7 +103,7 @@ (when (or (pxl8.key_down "w") auto-run?) (set move-forward (+ move-forward 1))) - (when (pxl8.key_down "s") + (when (and (pxl8.key_down "s") (not= auto-run-cancel-key "s")) (set move-forward (- move-forward 1))) (when (pxl8.key_down "a") diff --git a/pxl8.sh b/pxl8.sh index c7bb611..299de1a 100755 --- a/pxl8.sh +++ b/pxl8.sh @@ -2,7 +2,7 @@ set -e -CC="${CC:-gcc}" +CC="${CC:-clang}" if command -v ccache >/dev/null 2>&1; then CC="ccache $CC" @@ -338,16 +338,17 @@ case "$COMMAND" in src/pxl8_bsp.c src/pxl8_cart.c src/pxl8_font.c + src/pxl8_gen.c src/pxl8_gfx.c + src/pxl8_gui.c src/pxl8_io.c src/pxl8_math.c - src/pxl8_procgen.c + src/pxl8_rec.c src/pxl8_script.c src/pxl8_sdl3.c src/pxl8_tilemap.c src/pxl8_tilesheet.c src/pxl8_transition.c - src/pxl8_gui.c src/pxl8_vfx.c src/pxl8_world.c " diff --git a/src/pxl8.c b/src/pxl8.c index d8b92f4..f075145 100644 --- a/src/pxl8.c +++ b/src/pxl8.c @@ -148,7 +148,8 @@ pxl8_result pxl8_init(pxl8* sys, i32 argc, char* argv[]) { if (pxl8_cart_load(sys->cart, cart_path) == PXL8_OK) { pxl8_script_set_cart_path(game->script, pxl8_cart_get_base_path(sys->cart), original_cwd); pxl8_cart_mount(sys->cart); - strcpy(game->script_path, "main.fnl"); + strncpy(game->script_path, "main.fnl", sizeof(game->script_path) - 1); + game->script_path[sizeof(game->script_path) - 1] = '\0'; pxl8_info("Loaded cart: %s", pxl8_cart_get_name(sys->cart)); } else { pxl8_error("Failed to load cart: %s", cart_path); @@ -369,7 +370,7 @@ u32 pxl8_get_palette_size(pxl8_color_mode mode) { case PXL8_COLOR_MODE_HICOLOR: return 0; case PXL8_COLOR_MODE_FAMI: return 64; case PXL8_COLOR_MODE_MEGA: return 512; - case PXL8_COLOR_MODE_GBA: return 32768; + case PXL8_COLOR_MODE_GBA: case PXL8_COLOR_MODE_SNES: return 32768; default: return 256; } diff --git a/src/pxl8_ase.c b/src/pxl8_ase.c index 4d6a224..3c2c31e 100644 --- a/src/pxl8_ase.c +++ b/src/pxl8_ase.c @@ -242,10 +242,7 @@ static pxl8_result parse_tileset_chunk(pxl8_stream* stream, pxl8_ase_tileset* ti } if (tileset->flags & 1) { - u32 external_file_id = pxl8_read_u32(stream); - u32 tileset_id = pxl8_read_u32(stream); - (void)external_file_id; - (void)tileset_id; + pxl8_skip_bytes(stream, 8); // external_file_id + tileset_id } if (tileset->flags & 2) { @@ -458,13 +455,14 @@ pxl8_result pxl8_ase_load(const char* filepath, pxl8_ase_file* ase_file) { break; case PXL8_ASE_CHUNK_LAYER: { - ase_file->layers = + pxl8_ase_layer* new_layers = (pxl8_ase_layer*)realloc(ase_file->layers, (ase_file->layer_count + 1) * sizeof(pxl8_ase_layer)); - if (!ase_file->layers) { + if (!new_layers) { result = PXL8_ERROR_OUT_OF_MEMORY; break; } + ase_file->layers = new_layers; result = parse_layer_chunk(&stream, &ase_file->layers[ase_file->layer_count]); if (result == PXL8_OK) { @@ -477,11 +475,12 @@ pxl8_result pxl8_ase_load(const char* filepath, pxl8_ase_file* ase_file) { pxl8_ase_cel cel = {0}; result = parse_cel_chunk(&stream, chunk_header.chunk_size - 6, &cel); if (result == PXL8_OK) { - frame->cels = (pxl8_ase_cel*)realloc(frame->cels, (frame->cel_count + 1) * sizeof(pxl8_ase_cel)); - if (!frame->cels) { + pxl8_ase_cel* new_cels = (pxl8_ase_cel*)realloc(frame->cels, (frame->cel_count + 1) * sizeof(pxl8_ase_cel)); + if (!new_cels) { result = PXL8_ERROR_OUT_OF_MEMORY; break; } + frame->cels = new_cels; frame->cels[frame->cel_count] = cel; frame->cel_count++; @@ -522,12 +521,13 @@ pxl8_result pxl8_ase_load(const char* filepath, pxl8_ase_file* ase_file) { break; case PXL8_ASE_CHUNK_TILESET: { - ase_file->tilesets = (pxl8_ase_tileset*)realloc(ase_file->tilesets, + pxl8_ase_tileset* new_tilesets = (pxl8_ase_tileset*)realloc(ase_file->tilesets, (ase_file->tileset_count + 1) * sizeof(pxl8_ase_tileset)); - if (!ase_file->tilesets) { + if (!new_tilesets) { result = PXL8_ERROR_OUT_OF_MEMORY; break; } + ase_file->tilesets = new_tilesets; memset(&ase_file->tilesets[ase_file->tileset_count], 0, sizeof(pxl8_ase_tileset)); result = parse_tileset_chunk(&stream, &ase_file->tilesets[ase_file->tileset_count]); diff --git a/src/pxl8_atlas.c b/src/pxl8_atlas.c index f88bea5..e561540 100644 --- a/src/pxl8_atlas.c +++ b/src/pxl8_atlas.c @@ -5,6 +5,7 @@ #include #include +#include "pxl8_color.h" #include "pxl8_macros.h" typedef struct pxl8_skyline_fit { @@ -82,7 +83,7 @@ static pxl8_skyline_fit pxl8_skyline_find_position( return result; } -static void pxl8_skyline_add_rect(pxl8_skyline* skyline, pxl8_point pos, u32 w, u32 h) { +static bool pxl8_skyline_add_rect(pxl8_skyline* skyline, pxl8_point pos, u32 w, u32 h) { u32 node_idx = 0; for (u32 i = 0; i < skyline->count; i++) { if (skyline->nodes[i].x == pos.x) { @@ -101,11 +102,14 @@ static void pxl8_skyline_add_rect(pxl8_skyline* skyline, pxl8_point pos, u32 w, } if (skyline->count - nodes_to_remove + 1 > skyline->capacity) { - skyline->capacity = (skyline->count - nodes_to_remove + 1) * 2; - skyline->nodes = (pxl8_skyline_node*)realloc( + u32 new_capacity = (skyline->count - nodes_to_remove + 1) * 2; + pxl8_skyline_node* new_nodes = (pxl8_skyline_node*)realloc( skyline->nodes, - skyline->capacity * sizeof(pxl8_skyline_node) + new_capacity * sizeof(pxl8_skyline_node) ); + if (!new_nodes) return false; + skyline->nodes = new_nodes; + skyline->capacity = new_capacity; } if (nodes_to_remove > 0) { @@ -118,6 +122,7 @@ static void pxl8_skyline_add_rect(pxl8_skyline* skyline, pxl8_point pos, u32 w, skyline->nodes[node_idx] = (pxl8_skyline_node){pos.x, pos.y + (i32)h, (i32)w}; skyline->count = skyline->count - nodes_to_remove + 1; + return true; } static void pxl8_skyline_compact(pxl8_skyline* skyline) { @@ -143,14 +148,14 @@ pxl8_atlas* pxl8_atlas_create(u32 width, u32 height, pxl8_color_mode color_mode) atlas->height = height; atlas->width = width; - i32 bytes_per_pixel = (color_mode == PXL8_COLOR_MODE_HICOLOR) ? 4 : 1; + i32 bytes_per_pixel = pxl8_bytes_per_pixel(color_mode); atlas->pixels = (u8*)calloc(width * height, bytes_per_pixel); if (!atlas->pixels) { free(atlas); return NULL; } - atlas->entry_capacity = 64; + atlas->entry_capacity = PXL8_DEFAULT_ATLAS_ENTRY_CAPACITY; atlas->entries = (pxl8_atlas_entry*)calloc(atlas->entry_capacity, sizeof(pxl8_atlas_entry)); if (!atlas->entries) { free(atlas->pixels); @@ -197,7 +202,7 @@ void pxl8_atlas_destroy(pxl8_atlas* atlas) { bool pxl8_atlas_expand(pxl8_atlas* atlas, pxl8_color_mode color_mode) { if (!atlas || atlas->width >= 4096) return false; - i32 bytes_per_pixel = (color_mode == PXL8_COLOR_MODE_HICOLOR) ? 4 : 1; + i32 bytes_per_pixel = pxl8_bytes_per_pixel(color_mode); u32 new_size = atlas->width * 2; u32 old_width = atlas->width; @@ -236,8 +241,8 @@ bool pxl8_atlas_expand(pxl8_atlas* atlas, pxl8_color_mode color_mode) { for (u32 x = 0; x < (u32)atlas->entries[i].w; x++) { u32 src_idx = (atlas->entries[i].y + y) * old_width + (atlas->entries[i].x + x); u32 dst_idx = (fit.pos.y + y) * new_size + (fit.pos.x + x); - if (bytes_per_pixel == 4) { - ((u32*)new_pixels)[dst_idx] = ((u32*)atlas->pixels)[src_idx]; + if (bytes_per_pixel == 2) { + ((u16*)new_pixels)[dst_idx] = ((u16*)atlas->pixels)[src_idx]; } else { new_pixels[dst_idx] = atlas->pixels[src_idx]; } @@ -247,7 +252,11 @@ bool pxl8_atlas_expand(pxl8_atlas* atlas, pxl8_color_mode color_mode) { atlas->entries[i].x = fit.pos.x; atlas->entries[i].y = fit.pos.y; - pxl8_skyline_add_rect(&new_skyline, fit.pos, atlas->entries[i].w, atlas->entries[i].h); + if (!pxl8_skyline_add_rect(&new_skyline, fit.pos, atlas->entries[i].w, atlas->entries[i].h)) { + free(new_skyline.nodes); + free(new_pixels); + return false; + } pxl8_skyline_compact(&new_skyline); } @@ -290,11 +299,14 @@ u32 pxl8_atlas_add_texture( texture_id = atlas->free_list[--atlas->free_count]; } else { if (atlas->entry_count >= atlas->entry_capacity) { - atlas->entry_capacity *= 2; - atlas->entries = (pxl8_atlas_entry*)realloc( + u32 new_capacity = atlas->entry_capacity * 2; + pxl8_atlas_entry* new_entries = (pxl8_atlas_entry*)realloc( atlas->entries, - atlas->entry_capacity * sizeof(pxl8_atlas_entry) + new_capacity * sizeof(pxl8_atlas_entry) ); + if (!new_entries) return UINT32_MAX; + atlas->entries = new_entries; + atlas->entry_capacity = new_capacity; } texture_id = atlas->entry_count++; } @@ -307,21 +319,24 @@ u32 pxl8_atlas_add_texture( entry->w = w; entry->h = h; - i32 bytes_per_pixel = (color_mode == PXL8_COLOR_MODE_HICOLOR) ? 4 : 1; + i32 bytes_per_pixel = pxl8_bytes_per_pixel(color_mode); for (u32 y = 0; y < h; y++) { for (u32 x = 0; x < w; x++) { u32 src_idx = y * w + x; u32 dst_idx = (fit.pos.y + y) * atlas->width + (fit.pos.x + x); - if (bytes_per_pixel == 4) { - ((u32*)atlas->pixels)[dst_idx] = ((const u32*)pixels)[src_idx]; + if (bytes_per_pixel == 2) { + ((u16*)atlas->pixels)[dst_idx] = ((const u16*)pixels)[src_idx]; } else { atlas->pixels[dst_idx] = pixels[src_idx]; } } } - pxl8_skyline_add_rect(&atlas->skyline, fit.pos, w, h); + if (!pxl8_skyline_add_rect(&atlas->skyline, fit.pos, w, h)) { + entry->active = false; + return UINT32_MAX; + } pxl8_skyline_compact(&atlas->skyline); atlas->dirty = true; diff --git a/src/pxl8_blit.c b/src/pxl8_blit.c index 5c9647f..e8bdc88 100644 --- a/src/pxl8_blit.c +++ b/src/pxl8_blit.c @@ -1,16 +1,16 @@ #include "pxl8_blit.h" -void pxl8_blit_hicolor(u32* fb, u32 fb_width, const u32* sprite, u32 atlas_width, +void pxl8_blit_hicolor(u16* fb, u32 fb_width, const u16* sprite, u32 atlas_width, i32 x, i32 y, u32 w, u32 h) { - u32* dest_base = fb + y * fb_width + x; - const u32* src_base = sprite; + u16* dest_base = fb + y * fb_width + x; + const u16* src_base = sprite; for (u32 row = 0; row < h; row++) { - u32* dest_row = dest_base + row * fb_width; - const u32* src_row = src_base + row * atlas_width; + u16* dest_row = dest_base + row * fb_width; + const u16* src_row = src_base + row * atlas_width; for (u32 col = 0; col < w; col++) { - if (src_row[col] & 0xFF000000) { + if (src_row[col] != 0) { dest_row[col] = src_row[col]; } } diff --git a/src/pxl8_blit.h b/src/pxl8_blit.h index 75354b3..09a940e 100644 --- a/src/pxl8_blit.h +++ b/src/pxl8_blit.h @@ -7,8 +7,8 @@ extern "C" { #endif void pxl8_blit_hicolor( - u32* fb, u32 fb_width, - const u32* sprite, u32 atlas_width, + u16* fb, u32 fb_width, + const u16* sprite, u32 atlas_width, i32 x, i32 y, u32 w, u32 h ); void pxl8_blit_indexed( diff --git a/src/pxl8_bsp.c b/src/pxl8_bsp.c index 46be7d4..f335799 100644 --- a/src/pxl8_bsp.c +++ b/src/pxl8_bsp.c @@ -468,8 +468,9 @@ void pxl8_bsp_render_textured( static u32 rendered_faces_capacity = 0; if (rendered_faces_capacity < bsp->num_faces) { - rendered_faces = realloc(rendered_faces, bsp->num_faces); - if (!rendered_faces) return; + u8* new_buffer = realloc(rendered_faces, bsp->num_faces); + if (!new_buffer) return; + rendered_faces = new_buffer; rendered_faces_capacity = bsp->num_faces; pxl8_debug("Allocated face tracking buffer: %u bytes", bsp->num_faces); } diff --git a/src/pxl8_cart.c b/src/pxl8_cart.c index ed7d957..7986285 100644 --- a/src/pxl8_cart.c +++ b/src/pxl8_cart.c @@ -21,8 +21,8 @@ struct pxl8_cart { char* name; }; -static pxl8_cart* __pxl8_current_cart = NULL; -static char* __pxl8_original_cwd = NULL; +static pxl8_cart* pxl8_current_cart = NULL; +static char* pxl8_original_cwd = NULL; static void pxl8_add_file_recursive(mz_zip_archive* zip, const char* dir_path, const char* prefix) { DIR* dir = opendir(dir_path); @@ -89,7 +89,7 @@ pxl8_cart* pxl8_cart_create(void) { } pxl8_cart* pxl8_cart_current(void) { - return __pxl8_current_cart; + return pxl8_current_cart; } void pxl8_cart_destroy(pxl8_cart* cart) { @@ -254,20 +254,20 @@ pxl8_result pxl8_cart_mount(pxl8_cart* cart) { if (!cart || !cart->base_path) return PXL8_ERROR_NULL_POINTER; if (cart->is_mounted) return PXL8_OK; - if (__pxl8_current_cart) { - pxl8_cart_unmount(__pxl8_current_cart); + if (pxl8_current_cart) { + pxl8_cart_unmount(pxl8_current_cart); } - __pxl8_original_cwd = getcwd(NULL, 0); + pxl8_original_cwd = getcwd(NULL, 0); if (chdir(cart->base_path) != 0) { pxl8_error("Failed to change to cart directory: %s", cart->base_path); - free(__pxl8_original_cwd); - __pxl8_original_cwd = NULL; + free(pxl8_original_cwd); + pxl8_original_cwd = NULL; return PXL8_ERROR_FILE_NOT_FOUND; } cart->is_mounted = true; - __pxl8_current_cart = cart; + pxl8_current_cart = cart; pxl8_info("Mounted cart: %s", cart->name); return PXL8_OK; @@ -276,15 +276,15 @@ pxl8_result pxl8_cart_mount(pxl8_cart* cart) { void pxl8_cart_unmount(pxl8_cart* cart) { if (!cart || !cart->is_mounted) return; - if (__pxl8_original_cwd) { - chdir(__pxl8_original_cwd); - free(__pxl8_original_cwd); - __pxl8_original_cwd = NULL; + if (pxl8_original_cwd) { + chdir(pxl8_original_cwd); + free(pxl8_original_cwd); + pxl8_original_cwd = NULL; } cart->is_mounted = false; - if (__pxl8_current_cart == cart) { - __pxl8_current_cart = NULL; + if (pxl8_current_cart == cart) { + pxl8_current_cart = NULL; } pxl8_info("Unmounted cart: %s", cart->name); diff --git a/src/pxl8_color.h b/src/pxl8_color.h new file mode 100644 index 0000000..128bfc4 --- /dev/null +++ b/src/pxl8_color.h @@ -0,0 +1,42 @@ +#pragma once + +#include "pxl8_types.h" + +static inline i32 pxl8_bytes_per_pixel(pxl8_color_mode mode) { + return (mode == PXL8_COLOR_MODE_HICOLOR) ? 2 : 1; +} + +static inline u16 pxl8_rgb565_pack(u8 r, u8 g, u8 b) { + return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3); +} + +static inline void pxl8_rgb565_unpack(u16 color, u8* r, u8* g, u8* b) { + *r = (color >> 11) << 3; + *g = ((color >> 5) & 0x3F) << 2; + *b = (color & 0x1F) << 3; +} + +static inline u16 pxl8_rgba32_to_rgb565(u32 rgba) { + return pxl8_rgb565_pack(rgba & 0xFF, (rgba >> 8) & 0xFF, (rgba >> 16) & 0xFF); +} + +static inline u32 pxl8_rgb565_to_rgba32(u16 color) { + u8 r, g, b; + pxl8_rgb565_unpack(color, &r, &g, &b); + return r | (g << 8) | (b << 16) | 0xFF000000; +} + +static inline void pxl8_rgba32_unpack(u32 color, u8* r, u8* g, u8* b, u8* a) { + *r = color & 0xFF; + *g = (color >> 8) & 0xFF; + *b = (color >> 16) & 0xFF; + *a = (color >> 24) & 0xFF; +} + +static inline u32 pxl8_rgba32_pack(u8 r, u8 g, u8 b, u8 a) { + return r | (g << 8) | (b << 16) | (a << 24); +} + +static inline u8 pxl8_color_lerp_channel(u8 c1, u8 c2, f32 t) { + return c1 + (i32)((c2 - c1) * t); +} diff --git a/src/pxl8_embed.h b/src/pxl8_embed.h new file mode 100644 index 0000000..0bc95c2 --- /dev/null +++ b/src/pxl8_embed.h @@ -0,0 +1,88 @@ +#pragma once + +#include "pxl8_types.h" +#include + +static const char embed_fennel[] = { +#embed "lib/fennel/fennel.lua" +, 0 }; + +static const char embed_pxl8[] = { +#embed "src/lua/pxl8.lua" +, 0 }; + +static const char embed_pxl8_anim[] = { +#embed "src/lua/pxl8/anim.lua" +, 0 }; + +static const char embed_pxl8_core[] = { +#embed "src/lua/pxl8/core.lua" +, 0 }; + +static const char embed_pxl8_gfx2d[] = { +#embed "src/lua/pxl8/gfx2d.lua" +, 0 }; + +static const char embed_pxl8_gfx3d[] = { +#embed "src/lua/pxl8/gfx3d.lua" +, 0 }; + +static const char embed_pxl8_gui[] = { +#embed "src/lua/pxl8/gui.lua" +, 0 }; + +static const char embed_pxl8_input[] = { +#embed "src/lua/pxl8/input.lua" +, 0 }; + +static const char embed_pxl8_math[] = { +#embed "src/lua/pxl8/math.lua" +, 0 }; + +static const char embed_pxl8_particles[] = { +#embed "src/lua/pxl8/particles.lua" +, 0 }; + +static const char embed_pxl8_tilemap[] = { +#embed "src/lua/pxl8/tilemap.lua" +, 0 }; + +static const char embed_pxl8_transition[] = { +#embed "src/lua/pxl8/transition.lua" +, 0 }; + +static const char embed_pxl8_vfx[] = { +#embed "src/lua/pxl8/vfx.lua" +, 0 }; + +static const char embed_pxl8_world[] = { +#embed "src/lua/pxl8/world.lua" +, 0 }; + +#define PXL8_EMBED_ENTRY(var, name) {name, var, sizeof(var) - 1} + +typedef struct { const char* name; const char* data; u32 size; } pxl8_embed; + +static const pxl8_embed pxl8_embeds[] = { + PXL8_EMBED_ENTRY(embed_fennel, "fennel"), + PXL8_EMBED_ENTRY(embed_pxl8, "pxl8"), + PXL8_EMBED_ENTRY(embed_pxl8_anim, "pxl8.anim"), + PXL8_EMBED_ENTRY(embed_pxl8_core, "pxl8.core"), + PXL8_EMBED_ENTRY(embed_pxl8_gfx2d, "pxl8.gfx2d"), + PXL8_EMBED_ENTRY(embed_pxl8_gfx3d, "pxl8.gfx3d"), + PXL8_EMBED_ENTRY(embed_pxl8_gui, "pxl8.gui"), + PXL8_EMBED_ENTRY(embed_pxl8_input, "pxl8.input"), + PXL8_EMBED_ENTRY(embed_pxl8_math, "pxl8.math"), + PXL8_EMBED_ENTRY(embed_pxl8_particles, "pxl8.particles"), + PXL8_EMBED_ENTRY(embed_pxl8_tilemap, "pxl8.tilemap"), + PXL8_EMBED_ENTRY(embed_pxl8_transition, "pxl8.transition"), + PXL8_EMBED_ENTRY(embed_pxl8_vfx, "pxl8.vfx"), + PXL8_EMBED_ENTRY(embed_pxl8_world, "pxl8.world"), + {0} +}; + +static inline const pxl8_embed* pxl8_embed_find(const char* name) { + for (const pxl8_embed* e = pxl8_embeds; e->name; e++) + if (strcmp(e->name, name) == 0) return e; + return NULL; +} diff --git a/src/pxl8_procgen.c b/src/pxl8_gen.c similarity index 99% rename from src/pxl8_procgen.c rename to src/pxl8_gen.c index 96f5e01..ec6f083 100644 --- a/src/pxl8_procgen.c +++ b/src/pxl8_gen.c @@ -1,4 +1,4 @@ -#include "pxl8_procgen.h" +#include "pxl8_gen.h" #include #include @@ -487,7 +487,7 @@ void pxl8_procgen_tex(u8* buffer, const pxl8_procgen_tex_params* params) { if (pattern < 0.25f) color = params->base_color; else if (pattern < 0.50f) color = params->base_color + 1; else if (pattern < 0.75f) color = params->base_color + 2; - else color = params->base_color + 1; + else color = params->base_color + 3; } // Brick pattern (wall style) else { diff --git a/src/pxl8_procgen.h b/src/pxl8_gen.h similarity index 100% rename from src/pxl8_procgen.h rename to src/pxl8_gen.h diff --git a/src/pxl8_gfx.c b/src/pxl8_gfx.c index 3f87743..06be282 100644 --- a/src/pxl8_gfx.c +++ b/src/pxl8_gfx.c @@ -6,6 +6,7 @@ #include "pxl8_ase.h" #include "pxl8_atlas.h" #include "pxl8_blit.h" +#include "pxl8_color.h" #include "pxl8_font.h" #include "pxl8_hal.h" #include "pxl8_macros.h" @@ -55,21 +56,6 @@ struct pxl8_gfx { }; -static inline void pxl8_color_unpack(u32 color, u8* r, u8* g, u8* b, u8* a) { - *r = color & 0xFF; - *g = (color >> 8) & 0xFF; - *b = (color >> 16) & 0xFF; - *a = (color >> 24) & 0xFF; -} - -static inline u32 pxl8_color_pack(u8 r, u8 g, u8 b, u8 a) { - return r | (g << 8) | (b << 16) | (a << 24); -} - -static inline u8 pxl8_color_lerp_channel(u8 c1, u8 c2, f32 t) { - return c1 + (i32)((c2 - c1) * t); -} - pxl8_bounds pxl8_gfx_get_bounds(pxl8_gfx* gfx) { pxl8_bounds bounds = {0}; if (!gfx) { @@ -84,8 +70,14 @@ pxl8_color_mode pxl8_gfx_get_color_mode(pxl8_gfx* gfx) { return gfx ? gfx->color_mode : PXL8_COLOR_MODE_FAMI; } -u8* pxl8_gfx_get_framebuffer(pxl8_gfx* gfx) { - return gfx ? gfx->framebuffer : NULL; +u8* pxl8_gfx_get_framebuffer_indexed(pxl8_gfx* gfx) { + if (!gfx || gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) return NULL; + return gfx->framebuffer; +} + +u16* pxl8_gfx_get_framebuffer_hicolor(pxl8_gfx* gfx) { + if (!gfx || gfx->color_mode != PXL8_COLOR_MODE_HICOLOR) return NULL; + return (u16*)gfx->framebuffer; } i32 pxl8_gfx_get_height(const pxl8_gfx* gfx) { @@ -126,7 +118,7 @@ pxl8_gfx* pxl8_gfx_create( return NULL; } - i32 bytes_per_pixel = (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) ? 4 : 1; + i32 bytes_per_pixel = pxl8_bytes_per_pixel(gfx->color_mode); i32 fb_size = gfx->framebuffer_width * gfx->framebuffer_height * bytes_per_pixel; gfx->framebuffer = (u8*)calloc(1, fb_size); if (!gfx->framebuffer) { @@ -183,14 +175,17 @@ void pxl8_gfx_destroy(pxl8_gfx* gfx) { free(gfx); } +static pxl8_result pxl8_gfx_ensure_atlas(pxl8_gfx* gfx) { + if (gfx->atlas) return PXL8_OK; + gfx->atlas = pxl8_atlas_create(PXL8_DEFAULT_ATLAS_SIZE, PXL8_DEFAULT_ATLAS_SIZE, gfx->color_mode); + return gfx->atlas ? PXL8_OK : PXL8_ERROR_OUT_OF_MEMORY; +} pxl8_result pxl8_gfx_create_texture(pxl8_gfx* gfx, const u8* pixels, u32 width, u32 height) { if (!gfx || !gfx->initialized || !pixels) return PXL8_ERROR_INVALID_ARGUMENT; - if (!gfx->atlas) { - gfx->atlas = pxl8_atlas_create(1024, 1024, gfx->color_mode); - if (!gfx->atlas) return PXL8_ERROR_OUT_OF_MEMORY; - } + pxl8_result result = pxl8_gfx_ensure_atlas(gfx); + if (result != PXL8_OK) return result; u32 texture_id = pxl8_atlas_add_texture(gfx->atlas, pixels, width, height, gfx->color_mode); if (texture_id == UINT32_MAX) { @@ -205,7 +200,7 @@ pxl8_result pxl8_gfx_load_sprite(pxl8_gfx* gfx, const char* path) { if (!gfx || !gfx->initialized || !path) return PXL8_ERROR_INVALID_ARGUMENT; if (!gfx->sprite_cache) { - gfx->sprite_cache_capacity = 64; + gfx->sprite_cache_capacity = PXL8_DEFAULT_SPRITE_CACHE_CAPACITY; gfx->sprite_cache = (pxl8_sprite_cache_entry*)calloc( gfx->sprite_cache_capacity, sizeof(pxl8_sprite_cache_entry) ); @@ -218,13 +213,11 @@ pxl8_result pxl8_gfx_load_sprite(pxl8_gfx* gfx, const char* path) { } } - if (!gfx->atlas) { - gfx->atlas = pxl8_atlas_create(1024, 1024, gfx->color_mode); - if (!gfx->atlas) return PXL8_ERROR_OUT_OF_MEMORY; - } + pxl8_result result = pxl8_gfx_ensure_atlas(gfx); + if (result != PXL8_OK) return result; pxl8_ase_file ase_file; - pxl8_result result = pxl8_ase_load(path, &ase_file); + result = pxl8_ase_load(path, &ase_file); if (result != PXL8_OK) { pxl8_error("Failed to load ASE file: %s", path); return result; @@ -252,11 +245,14 @@ pxl8_result pxl8_gfx_load_sprite(pxl8_gfx* gfx, const char* path) { } if (gfx->sprite_cache_count >= gfx->sprite_cache_capacity) { - gfx->sprite_cache_capacity *= 2; - gfx->sprite_cache = (pxl8_sprite_cache_entry*)realloc( + u32 new_capacity = gfx->sprite_cache_capacity * 2; + pxl8_sprite_cache_entry* new_cache = (pxl8_sprite_cache_entry*)realloc( gfx->sprite_cache, - gfx->sprite_cache_capacity * sizeof(pxl8_sprite_cache_entry) + new_capacity * sizeof(pxl8_sprite_cache_entry) ); + if (!new_cache) return PXL8_ERROR_OUT_OF_MEMORY; + gfx->sprite_cache = new_cache; + gfx->sprite_cache_capacity = new_capacity; } pxl8_sprite_cache_entry* entry = &gfx->sprite_cache[gfx->sprite_cache_count++]; @@ -271,10 +267,7 @@ pxl8_result pxl8_gfx_load_sprite(pxl8_gfx* gfx, const char* path) { pxl8_atlas* pxl8_gfx_get_atlas(pxl8_gfx* gfx) { if (!gfx || !gfx->initialized) return NULL; - if (!gfx->atlas) { - gfx->atlas = pxl8_atlas_create(1024, 1024, gfx->color_mode); - } - + if (pxl8_gfx_ensure_atlas(gfx) != PXL8_OK) return NULL; return gfx->atlas; } @@ -369,14 +362,13 @@ void pxl8_gfx_project(pxl8_gfx* gfx, f32 left, f32 right, f32 top, f32 bottom) { void pxl8_clear(pxl8_gfx* gfx, u32 color) { if (!gfx || !gfx->framebuffer) return; - - i32 bytes_per_pixel = (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) ? 4 : 1; i32 size = gfx->framebuffer_width * gfx->framebuffer_height; - if (bytes_per_pixel == 4) { - u32* fb32 = (u32*)gfx->framebuffer; + if (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) { + u16* fb16 = (u16*)gfx->framebuffer; + u16 color16 = pxl8_rgba32_to_rgb565(color); for (i32 i = 0; i < size; i++) { - fb32[i] = color; + fb16[i] = color16; } } else { memset(gfx->framebuffer, color & 0xFF, size); @@ -386,10 +378,10 @@ void pxl8_clear(pxl8_gfx* gfx, u32 color) { void pxl8_pixel(pxl8_gfx* gfx, i32 x, i32 y, u32 color) { if (!gfx || !gfx->framebuffer) return; if (x < 0 || x >= gfx->framebuffer_width || y < 0 || y >= gfx->framebuffer_height) return; - + i32 idx = y * gfx->framebuffer_width + x; if (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) { - ((u32*)gfx->framebuffer)[idx] = color; + ((u16*)gfx->framebuffer)[idx] = pxl8_rgba32_to_rgb565(color); } else { gfx->framebuffer[idx] = color & 0xFF; } @@ -398,10 +390,10 @@ void pxl8_pixel(pxl8_gfx* gfx, i32 x, i32 y, u32 color) { u32 pxl8_get_pixel(pxl8_gfx* gfx, i32 x, i32 y) { if (!gfx || !gfx->framebuffer) return 0; if (x < 0 || x >= gfx->framebuffer_width || y < 0 || y >= gfx->framebuffer_height) return 0; - + i32 idx = y * gfx->framebuffer_width + x; if (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) { - return ((u32*)gfx->framebuffer)[idx]; + return pxl8_rgb565_to_rgba32(((u16*)gfx->framebuffer)[idx]); } else { return gfx->framebuffer[idx]; } @@ -445,7 +437,7 @@ void pxl8_rect(pxl8_gfx* gfx, i32 x, i32 y, i32 w, i32 h, u32 color) { static inline void pxl8_pixel_unchecked(pxl8_gfx* gfx, i32 x, i32 y, u32 color) { i32 idx = y * gfx->framebuffer_width + x; if (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) { - ((u32*)gfx->framebuffer)[idx] = color; + ((u16*)gfx->framebuffer)[idx] = pxl8_rgba32_to_rgb565(color); } else { gfx->framebuffer[idx] = color & 0xFF; } @@ -543,7 +535,7 @@ void pxl8_text(pxl8_gfx* gfx, const char* text, i32 x, i32 y, u32 color) { if (pixel_bit) { i32 fb_idx = py * gfx->framebuffer_width + px; if (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) { - ((u32*)gfx->framebuffer)[fb_idx] = color; + ((u16*)gfx->framebuffer)[fb_idx] = pxl8_rgba32_to_rgb565(color); } else { gfx->framebuffer[fb_idx] = (u8)color; } @@ -582,17 +574,17 @@ void pxl8_sprite(pxl8_gfx* gfx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h) { const u8* atlas_pixels = pxl8_atlas_get_pixels(gfx->atlas); if (is_1to1_scale && is_unclipped) { - const u8* sprite_data = atlas_pixels + entry->y * atlas_width + entry->x; - if (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) { + const u16* sprite_data = (const u16*)atlas_pixels + entry->y * atlas_width + entry->x; pxl8_blit_hicolor( - (u32*)gfx->framebuffer, + (u16*)gfx->framebuffer, gfx->framebuffer_width, - (const u32*)sprite_data, + sprite_data, atlas_width, x, y, w, h ); } else { + const u8* sprite_data = atlas_pixels + entry->y * atlas_width + entry->x; pxl8_blit_indexed( gfx->framebuffer, gfx->framebuffer_width, @@ -610,9 +602,9 @@ void pxl8_sprite(pxl8_gfx* gfx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h) { i32 dest_idx = (dest_y + py) * gfx->framebuffer_width + (dest_x + px); if (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) { - u32 pixel = ((const u32*)atlas_pixels)[src_idx]; - if (pixel & 0xFF000000) { - ((u32*)gfx->framebuffer)[dest_idx] = pixel; + u16 pixel = ((const u16*)atlas_pixels)[src_idx]; + if (pixel != 0) { + ((u16*)gfx->framebuffer)[dest_idx] = pixel; } } else { u8 pixel = atlas_pixels[src_idx]; @@ -655,18 +647,18 @@ void pxl8_gfx_fade_palette(pxl8_gfx* gfx, u8 start, u8 count, f32 amount, u32 ta if (amount > 1.0f) amount = 1.0f; u8 target_r, target_g, target_b, target_a; - pxl8_color_unpack(target_color, &target_r, &target_g, &target_b, &target_a); + pxl8_rgba32_unpack(target_color, &target_r, &target_g, &target_b, &target_a); for (u8 i = 0; i < count && (start + i) < gfx->palette_size; i++) { u8 cur_r, cur_g, cur_b, cur_a; - pxl8_color_unpack(gfx->palette[start + i], &cur_r, &cur_g, &cur_b, &cur_a); + pxl8_rgba32_unpack(gfx->palette[start + i], &cur_r, &cur_g, &cur_b, &cur_a); u8 new_r = pxl8_color_lerp_channel(cur_r, target_r, amount); u8 new_g = pxl8_color_lerp_channel(cur_g, target_g, amount); u8 new_b = pxl8_color_lerp_channel(cur_b, target_b, amount); u8 new_a = pxl8_color_lerp_channel(cur_a, target_a, amount); - gfx->palette[start + i] = pxl8_color_pack(new_r, new_g, new_b, new_a); + gfx->palette[start + i] = pxl8_rgba32_pack(new_r, new_g, new_b, new_a); } } @@ -685,15 +677,15 @@ void pxl8_gfx_interpolate_palettes( for (u8 i = 0; i < count && (start + i) < gfx->palette_size; i++) { u8 r1, g1, b1, a1, r2, g2, b2, a2; - pxl8_color_unpack(palette1[i], &r1, &g1, &b1, &a1); - pxl8_color_unpack(palette2[i], &r2, &g2, &b2, &a2); + pxl8_rgba32_unpack(palette1[i], &r1, &g1, &b1, &a1); + pxl8_rgba32_unpack(palette2[i], &r2, &g2, &b2, &a2); u8 r = pxl8_color_lerp_channel(r1, r2, t); u8 g = pxl8_color_lerp_channel(g1, g2, t); u8 b = pxl8_color_lerp_channel(b1, b2, t); u8 a = pxl8_color_lerp_channel(a1, a2, t); - gfx->palette[start + i] = pxl8_color_pack(r, g, b, a); + gfx->palette[start + i] = pxl8_rgba32_pack(r, g, b, a); } } @@ -702,8 +694,8 @@ void pxl8_gfx_color_ramp(pxl8_gfx* gfx, u8 start, u8 count, u32 from_color, u32 u8 from_r, from_g, from_b, from_a; u8 to_r, to_g, to_b, to_a; - pxl8_color_unpack(from_color, &from_r, &from_g, &from_b, &from_a); - pxl8_color_unpack(to_color, &to_r, &to_g, &to_b, &to_a); + pxl8_rgba32_unpack(from_color, &from_r, &from_g, &from_b, &from_a); + pxl8_rgba32_unpack(to_color, &to_r, &to_g, &to_b, &to_a); for (u8 i = 0; i < count && (start + i) < gfx->palette_size; i++) { f32 t = (f32)i / (f32)(count - 1); @@ -713,7 +705,7 @@ void pxl8_gfx_color_ramp(pxl8_gfx* gfx, u8 start, u8 count, u32 from_color, u32 u8 b = pxl8_color_lerp_channel(from_b, to_b, t); u8 a = pxl8_color_lerp_channel(from_a, to_a, t); - gfx->palette[start + i] = pxl8_color_pack(r, g, b, a); + gfx->palette[start + i] = pxl8_rgba32_pack(r, g, b, a); } } @@ -872,12 +864,13 @@ static inline void pxl8_fill_scanline(pxl8_gfx* gfx, i32 y, i32 xs, i32 xe, f32 i32 fb_offset = y * gfx->framebuffer_width; if (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) { - u32* fb = (u32*)gfx->framebuffer; + u16* fb = (u16*)gfx->framebuffer; + u16 color16 = pxl8_rgba32_to_rgb565(color); if (width == 0) { i32 idx = zbuf_offset + xs; if (z0 <= gfx->zbuffer[idx]) { gfx->zbuffer[idx] = z0; - fb[fb_offset + xs] = color; + fb[fb_offset + xs] = color16; } return; } @@ -889,7 +882,7 @@ static inline void pxl8_fill_scanline(pxl8_gfx* gfx, i32 y, i32 xs, i32 xe, f32 i32 idx = zbuf_offset + x; if (z <= gfx->zbuffer[idx]) { gfx->zbuffer[idx] = z; - fb[fb_offset + x] = color; + fb[fb_offset + x] = color16; } z += dz; } @@ -946,7 +939,7 @@ static inline void pxl8_fill_scanline_textured( i32 tex_mask = tex_w - 1; i32 atlas_x_base = entry->x; i32 atlas_y_base = entry->y; - bool is_hicolor = (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR); + bool is_hicolor = gfx->color_mode == PXL8_COLOR_MODE_HICOLOR; bool affine = gfx->affine_textures; i32 span = xe - xs; @@ -975,17 +968,18 @@ static inline void pxl8_fill_scanline_textured( } i32 atlas_idx = (atlas_y_base + ty) * atlas_width + (atlas_x_base + tx); - u32 color = is_hicolor ? ((const u32*)atlas_pixels)[atlas_idx] : atlas_pixels[atlas_idx]; if (is_hicolor) { - if (color & 0xFF000000) { - gfx->zbuffer[idx] = z0; - ((u32*)gfx->framebuffer)[y * gfx->framebuffer_width + xs] = color; - } - } else { + u16 color = ((const u16*)atlas_pixels)[atlas_idx]; if (color != 0) { gfx->zbuffer[idx] = z0; - gfx->framebuffer[y * gfx->framebuffer_width + xs] = (u8)color; + ((u16*)gfx->framebuffer)[y * gfx->framebuffer_width + xs] = color; + } + } else { + u8 color = atlas_pixels[atlas_idx]; + if (color != 0) { + gfx->zbuffer[idx] = z0; + gfx->framebuffer[y * gfx->framebuffer_width + xs] = color; } } } @@ -1030,17 +1024,18 @@ static inline void pxl8_fill_scanline_textured( } i32 atlas_idx = (atlas_y_base + ty) * atlas_width + (atlas_x_base + tx); - u32 color = is_hicolor ? ((const u32*)atlas_pixels)[atlas_idx] : atlas_pixels[atlas_idx]; if (is_hicolor) { - if (color & 0xFF000000) { - gfx->zbuffer[idx] = z; - ((u32*)gfx->framebuffer)[y * gfx->framebuffer_width + x] = color; - } - } else { + u16 color = ((const u16*)atlas_pixels)[atlas_idx]; if (color != 0) { gfx->zbuffer[idx] = z; - gfx->framebuffer[y * gfx->framebuffer_width + x] = (u8)color; + ((u16*)gfx->framebuffer)[y * gfx->framebuffer_width + x] = color; + } + } else { + u8 color = atlas_pixels[atlas_idx]; + if (color != 0) { + gfx->zbuffer[idx] = z; + gfx->framebuffer[y * gfx->framebuffer_width + x] = color; } } } diff --git a/src/pxl8_gfx.h b/src/pxl8_gfx.h index 82d919b..1b8e9a2 100644 --- a/src/pxl8_gfx.h +++ b/src/pxl8_gfx.h @@ -54,7 +54,8 @@ 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); +u8* pxl8_gfx_get_framebuffer_indexed(pxl8_gfx* gfx); +u16* pxl8_gfx_get_framebuffer_hicolor(pxl8_gfx* gfx); i32 pxl8_gfx_get_height(const pxl8_gfx* gfx); u32 pxl8_gfx_get_palette_size(const pxl8_gfx* gfx); i32 pxl8_gfx_get_width(const pxl8_gfx* gfx); diff --git a/src/pxl8_io.c b/src/pxl8_io.c index 1353f5b..b9b78c2 100644 --- a/src/pxl8_io.c +++ b/src/pxl8_io.c @@ -120,6 +120,8 @@ static i32 pxl8_key_code(const char* key_name) { {"left", 80}, {"right", 79}, {"up", 82}, {"down", 81}, {"f1", 58}, {"f2", 59}, {"f3", 60}, {"f4", 61}, {"f5", 62}, {"f6", 63}, {"f7", 64}, {"f8", 65}, {"f9", 66}, {"f10", 67}, {"f11", 68}, {"f12", 69}, + {"capslock", 57}, {"lshift", 225}, {"rshift", 229}, + {"lctrl", 224}, {"rctrl", 228}, {"lalt", 226}, {"ralt", 230}, {NULL, 0} }; @@ -142,21 +144,21 @@ static i32 pxl8_key_code(const char* key_name) { bool pxl8_key_down(const pxl8_input_state* input, const char* key_name) { if (!input) return false; i32 key = pxl8_key_code(key_name); - if (key < 0 || key >= 256) return false; + if (key < 0 || key >= PXL8_MAX_KEYS) return false; return input->keys_down[key]; } bool pxl8_key_pressed(const pxl8_input_state* input, const char* key_name) { if (!input) return false; i32 key = pxl8_key_code(key_name); - if (key < 0 || key >= 256) return false; + if (key < 0 || key >= PXL8_MAX_KEYS) return false; return input->keys_pressed[key]; } bool pxl8_key_released(const pxl8_input_state* input, const char* key_name) { if (!input) return false; i32 key = pxl8_key_code(key_name); - if (key < 0 || key >= 256) return false; + if (key < 0 || key >= PXL8_MAX_KEYS) return false; return input->keys_released[key]; } diff --git a/src/pxl8_io.h b/src/pxl8_io.h index 23500f8..f4e73bb 100644 --- a/src/pxl8_io.h +++ b/src/pxl8_io.h @@ -10,18 +10,24 @@ typedef struct { const u8* bytes; u32 offset; u32 size; + bool overflow; } pxl8_stream; static inline pxl8_stream pxl8_stream_create(const u8* bytes, u32 size) { return (pxl8_stream){ .bytes = bytes, .offset = 0, - .size = size + .size = size, + .overflow = false }; } static inline bool pxl8_stream_can_read(const pxl8_stream* stream, u32 count) { - return stream->offset + count <= stream->size; + return !stream->overflow && stream->offset + count <= stream->size; +} + +static inline bool pxl8_stream_has_overflow(const pxl8_stream* stream) { + return stream->overflow; } static inline void pxl8_stream_seek(pxl8_stream* stream, u32 offset) { @@ -33,16 +39,19 @@ static inline u32 pxl8_stream_position(const pxl8_stream* stream) { } static inline u8 pxl8_read_u8(pxl8_stream* stream) { + if (stream->offset + 1 > stream->size) { stream->overflow = true; return 0; } return stream->bytes[stream->offset++]; } static inline u16 pxl8_read_u16(pxl8_stream* stream) { + if (stream->offset + 2 > stream->size) { stream->overflow = true; return 0; } u16 val = (u16)stream->bytes[stream->offset] | ((u16)stream->bytes[stream->offset + 1] << 8); stream->offset += 2; return val; } static inline u32 pxl8_read_u32(pxl8_stream* stream) { + if (stream->offset + 4 > stream->size) { stream->overflow = true; return 0; } u32 val = (u32)stream->bytes[stream->offset] | ((u32)stream->bytes[stream->offset + 1] << 8) | ((u32)stream->bytes[stream->offset + 2] << 16) | @@ -67,16 +76,19 @@ static inline f32 pxl8_read_f32(pxl8_stream* stream) { } static inline void pxl8_read_bytes(pxl8_stream* stream, void* dest, u32 count) { + if (stream->offset + count > stream->size) { stream->overflow = true; return; } for (u32 i = 0; i < count; i++) { ((u8*)dest)[i] = stream->bytes[stream->offset++]; } } static inline void pxl8_skip_bytes(pxl8_stream* stream, u32 count) { + if (stream->offset + count > stream->size) { stream->overflow = true; return; } stream->offset += count; } static inline const u8* pxl8_read_ptr(pxl8_stream* stream, u32 count) { + if (stream->offset + count > stream->size) { stream->overflow = true; return NULL; } const u8* ptr = &stream->bytes[stream->offset]; stream->offset += count; return ptr; diff --git a/src/pxl8_rec.c b/src/pxl8_rec.c new file mode 100644 index 0000000..3538640 --- /dev/null +++ b/src/pxl8_rec.c @@ -0,0 +1,137 @@ +#include "pxl8_rec.h" + +#include "pxl8_macros.h" + +#include +#include +#include +#include + +struct pxl8_recorder { + i32 height; + i32 width; + + pxl8_recorder_format format; + i32 framerate; + char output_path[512]; + + FILE* ffmpeg_pipe; + u32 frame_count; + bool recording; +}; + +static void generate_default_output_path(pxl8_recorder* rec) { + time_t now = time(NULL); + struct tm* t = localtime(&now); + const char* ext = (rec->format == PXL8_RECORDER_GIF) ? "gif" : "mp4"; + + snprintf(rec->output_path, sizeof(rec->output_path), + "recording_%04d%02d%02d_%02d%02d%02d.%s", + t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, + t->tm_hour, t->tm_min, t->tm_sec, ext); +} + +pxl8_recorder* pxl8_recorder_create(i32 width, i32 height) { + pxl8_recorder* rec = (pxl8_recorder*)calloc(1, sizeof(pxl8_recorder)); + if (!rec) return NULL; + + rec->height = height; + rec->width = width; + + rec->format = PXL8_RECORDER_MP4; + rec->framerate = 60; + rec->output_path[0] = '\0'; + + rec->ffmpeg_pipe = NULL; + rec->frame_count = 0; + rec->recording = false; + + return rec; +} + +void pxl8_recorder_destroy(pxl8_recorder* rec) { + if (!rec) return; + if (rec->recording) pxl8_recorder_stop(rec); + free(rec); +} + +void pxl8_recorder_set_format(pxl8_recorder* rec, pxl8_recorder_format format) { + if (rec && !rec->recording) rec->format = format; +} + +void pxl8_recorder_set_framerate(pxl8_recorder* rec, i32 fps) { + if (rec && !rec->recording) rec->framerate = fps > 0 ? fps : 60; +} + +void pxl8_recorder_set_output_path(pxl8_recorder* rec, const char* path) { + if (rec && !rec->recording && path) strncpy(rec->output_path, path, sizeof(rec->output_path) - 1); +} + +void pxl8_recorder_start(pxl8_recorder* rec) { + if (!rec || rec->recording) return; + if (rec->output_path[0] == '\0') generate_default_output_path(rec); + + char cmd[1024]; + + if (rec->format == PXL8_RECORDER_GIF) { + snprintf(cmd, sizeof(cmd), + "ffmpeg -y -f rawvideo -pix_fmt rgba -s %dx%d -r %d -i - " + "-vf \"split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse\" " + "\"%s\" 2>/dev/null", + rec->width, rec->height, rec->framerate, rec->output_path); + } else { + snprintf(cmd, sizeof(cmd), + "ffmpeg -y -f rawvideo -pix_fmt rgba -s %dx%d -r %d -i - " + "-c:v libx264 -pix_fmt yuv420p -crf 18 -preset fast " + "\"%s\" 2>/dev/null", + rec->width, rec->height, rec->framerate, rec->output_path); + } + + rec->ffmpeg_pipe = popen(cmd, "w"); + if (!rec->ffmpeg_pipe) { + pxl8_error("Failed to start ffmpeg. Is ffmpeg installed?"); + return; + } + + rec->frame_count = 0; + rec->recording = true; + + pxl8_info("Recording started: %s (%dx%d @ %dfps)", + rec->output_path, rec->width, rec->height, rec->framerate); +} + +void pxl8_recorder_stop(pxl8_recorder* rec) { + if (!rec || !rec->recording) return; + + rec->recording = false; + + if (rec->ffmpeg_pipe) { + pclose(rec->ffmpeg_pipe); + rec->ffmpeg_pipe = NULL; + } + + pxl8_info("Recording stopped: %u frames -> %s", rec->frame_count, rec->output_path); +} + +bool pxl8_recorder_is_recording(pxl8_recorder* rec) { + return rec && rec->recording; +} + +void pxl8_recorder_capture_frame(pxl8_recorder* rec, const u32* rgba_pixels) { + if (!rec || !rec->recording || !rgba_pixels || !rec->ffmpeg_pipe) return; + + size_t written = fwrite(rgba_pixels, 4, rec->width * rec->height, rec->ffmpeg_pipe); + if (written == (size_t)(rec->width * rec->height)) { + rec->frame_count++; + } else { + pxl8_error("Failed to write frame %u", rec->frame_count); + } +} + +u32 pxl8_recorder_get_frame_count(pxl8_recorder* rec) { + return rec ? rec->frame_count : 0; +} + +const char* pxl8_recorder_get_output_path(pxl8_recorder* rec) { + return rec ? rec->output_path : NULL; +} diff --git a/src/pxl8_rec.h b/src/pxl8_rec.h new file mode 100644 index 0000000..4ba5dca --- /dev/null +++ b/src/pxl8_rec.h @@ -0,0 +1,31 @@ +#pragma once + +#include "pxl8_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum pxl8_recorder_format { + PXL8_RECORDER_GIF, + PXL8_RECORDER_MP4 +} pxl8_recorder_format; + +typedef struct pxl8_recorder pxl8_recorder; + +pxl8_recorder* pxl8_recorder_create(i32 width, i32 height); +void pxl8_recorder_destroy(pxl8_recorder* rec); + +void pxl8_recorder_capture_frame(pxl8_recorder* rec, const u32* rgba_pixels); +u32 pxl8_recorder_get_frame_count(pxl8_recorder* rec); +const char* pxl8_recorder_get_output_path(pxl8_recorder* rec); +bool pxl8_recorder_is_recording(pxl8_recorder* rec); +void pxl8_recorder_set_format(pxl8_recorder* rec, pxl8_recorder_format format); +void pxl8_recorder_set_framerate(pxl8_recorder* rec, i32 fps); +void pxl8_recorder_set_output_path(pxl8_recorder* rec, const char* path); +void pxl8_recorder_start(pxl8_recorder* rec); +void pxl8_recorder_stop(pxl8_recorder* rec); + +#ifdef __cplusplus +} +#endif diff --git a/src/pxl8_script.c b/src/pxl8_script.c index a5b83d2..36bf563 100644 --- a/src/pxl8_script.c +++ b/src/pxl8_script.c @@ -12,6 +12,7 @@ #include #include +#include "pxl8_embed.h" #include "pxl8_macros.h" #include "pxl8_gui.h" @@ -19,9 +20,9 @@ struct pxl8_script { lua_State* L; pxl8_gfx* gfx; pxl8_input_state* input; - char last_error[2048]; - char main_path[256]; - char watch_dir[256]; + char last_error[PXL8_MAX_ERROR_SIZE]; + char main_path[PXL8_MAX_PATH]; + char watch_dir[PXL8_MAX_PATH]; time_t latest_mod_time; }; @@ -353,6 +354,39 @@ const char* pxl8_script_get_last_error(pxl8_script* script) { return script ? script->last_error : "Invalid script context"; } +static int pxl8_embed_searcher(lua_State* L) { + const char* name = luaL_checkstring(L, 1); + const pxl8_embed* e = pxl8_embed_find(name); + if (!e) { + lua_pushfstring(L, "\n\tno embedded module '%s'", name); + return 1; + } + if (luaL_loadbuffer(L, e->data, e->size, name) != 0) { + return lua_error(L); + } + lua_pushstring(L, name); + return 2; +} + +static void pxl8_install_embed_searcher(lua_State* L) { + lua_getglobal(L, "package"); + lua_getfield(L, -1, "searchers"); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + lua_getfield(L, -1, "loaders"); + } + + int len = (int)lua_objlen(L, -1); + for (int i = len; i >= 2; i--) { + lua_rawgeti(L, -1, i); + lua_rawseti(L, -2, i + 1); + } + + lua_pushcfunction(L, pxl8_embed_searcher); + lua_rawseti(L, -2, 2); + lua_pop(L, 2); +} + pxl8_script* pxl8_script_create(void) { pxl8_script* script = (pxl8_script*)calloc(1, sizeof(pxl8_script)); if (!script) return NULL; @@ -383,29 +417,28 @@ pxl8_script* pxl8_script_create(void) { 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); + pxl8_install_embed_searcher(script->L); - lua_pushfstring(script->L, "%s;src/lua/?.lua", current_path); - lua_setfield(script->L, -2, "path"); - lua_pop(script->L, 1); + const pxl8_embed* fennel = pxl8_embed_find("fennel"); + if (fennel && luaL_loadbuffer(script->L, fennel->data, fennel->size, "fennel") == 0) { + if (lua_pcall(script->L, 0, 1, 0) == 0) { + lua_setglobal(script->L, "fennel"); - if (luaL_dofile(script->L, "lib/fennel/fennel.lua") == 0) { - lua_setglobal(script->L, "fennel"); - - lua_getglobal(script->L, "fennel"); - lua_getfield(script->L, -1, "install"); - if (lua_isfunction(script->L, -1)) { - if (lua_pcall(script->L, 0, 0, 0) != 0) { - pxl8_warn("Failed to install fennel searcher: %s", lua_tostring(script->L, -1)); + lua_getglobal(script->L, "fennel"); + lua_getfield(script->L, -1, "install"); + if (lua_isfunction(script->L, -1)) { + if (lua_pcall(script->L, 0, 0, 0) != 0) { + pxl8_warn("Failed to install fennel searcher: %s", lua_tostring(script->L, -1)); + lua_pop(script->L, 1); + } + } else { lua_pop(script->L, 1); } + lua_pop(script->L, 1); } else { + pxl8_warn("Failed to execute fennel: %s", lua_tostring(script->L, -1)); lua_pop(script->L, 1); } - lua_pop(script->L, 1); } script->last_error[0] = '\0'; @@ -746,7 +779,8 @@ pxl8_result pxl8_script_load_main(pxl8_script* script, const char* path) { strncpy(script->watch_dir, script->main_path, dir_len); script->watch_dir[dir_len] = '\0'; } else { - strcpy(script->watch_dir, "."); + script->watch_dir[0] = '.'; + script->watch_dir[1] = '\0'; } script->latest_mod_time = get_latest_script_mod_time(script->watch_dir); diff --git a/src/pxl8_sdl3.c b/src/pxl8_sdl3.c index f51b617..1f8cf2c 100644 --- a/src/pxl8_sdl3.c +++ b/src/pxl8_sdl3.c @@ -5,7 +5,9 @@ #include #include "pxl8_atlas.h" +#include "pxl8_color.h" #include "pxl8_macros.h" +#include "pxl8_rec.h" #include "pxl8_sys.h" typedef struct pxl8_sdl3_context { @@ -122,28 +124,29 @@ static void sdl3_upload_framebuffer(void* platform_data, const u8* fb, if (!platform_data || !fb) return; pxl8_sdl3_context* ctx = (pxl8_sdl3_context*)platform_data; + size_t needed_size = w * h; + + if (ctx->rgba_buffer_size < needed_size) { + u32* new_buffer = (u32*)SDL_realloc(ctx->rgba_buffer, needed_size * 4); + if (!new_buffer) return; + ctx->rgba_buffer = new_buffer; + ctx->rgba_buffer_size = needed_size; + } if (mode == PXL8_COLOR_MODE_HICOLOR) { - SDL_UpdateTexture(ctx->framebuffer_texture, NULL, fb, w * 4); - } else { - size_t needed_size = w * h; - - if (ctx->rgba_buffer_size < needed_size) { - ctx->rgba_buffer = (u32*)SDL_realloc(ctx->rgba_buffer, needed_size * 4); - ctx->rgba_buffer_size = needed_size; + const u16* fb16 = (const u16*)fb; + for (i32 i = 0; i < w * h; i++) { + ctx->rgba_buffer[i] = pxl8_rgb565_to_rgba32(fb16[i]); } - - if (!ctx->rgba_buffer) return; - + } else { u32 palette_size = pxl8_get_palette_size(mode); - for (i32 i = 0; i < w * h; i++) { u8 index = fb[i]; ctx->rgba_buffer[i] = (index < palette_size) ? palette[index] : 0xFF000000; } - - SDL_UpdateTexture(ctx->framebuffer_texture, NULL, ctx->rgba_buffer, w * 4); } + + SDL_UpdateTexture(ctx->framebuffer_texture, NULL, ctx->rgba_buffer, w * 4); } static void sdl3_upload_atlas(void* platform_data, const pxl8_atlas* atlas, @@ -183,27 +186,30 @@ static void sdl3_upload_atlas(void* platform_data, const pxl8_atlas* atlas, ctx->atlas_height = atlas_h; } + size_t needed_size = atlas_w * atlas_h; + + if (ctx->rgba_buffer_size < needed_size) { + u32* new_buffer = (u32*)SDL_realloc(ctx->rgba_buffer, needed_size * 4); + if (!new_buffer) return; + ctx->rgba_buffer = new_buffer; + ctx->rgba_buffer_size = needed_size; + } + if (mode == PXL8_COLOR_MODE_HICOLOR) { - SDL_UpdateTexture(ctx->atlas_texture, NULL, atlas_pixels, atlas_w * 4); - } else { - size_t needed_size = atlas_w * atlas_h; - - if (ctx->rgba_buffer_size < needed_size) { - ctx->rgba_buffer = (u32*)SDL_realloc(ctx->rgba_buffer, needed_size * 4); - ctx->rgba_buffer_size = needed_size; + const u16* atlas16 = (const u16*)atlas_pixels; + for (u32 i = 0; i < atlas_w * atlas_h; i++) { + u16 pixel = atlas16[i]; + ctx->rgba_buffer[i] = (pixel != 0) ? pxl8_rgb565_to_rgba32(pixel) : 0x00000000; } - - if (!ctx->rgba_buffer) return; - + } else { u32 palette_size = pxl8_get_palette_size(mode); - for (u32 i = 0; i < atlas_w * atlas_h; i++) { u8 index = atlas_pixels[i]; ctx->rgba_buffer[i] = (index < palette_size) ? palette[index] : 0x00000000; } - - SDL_UpdateTexture(ctx->atlas_texture, NULL, ctx->rgba_buffer, atlas_w * 4); } + + SDL_UpdateTexture(ctx->atlas_texture, NULL, ctx->rgba_buffer, atlas_w * 4); } SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) { @@ -267,7 +273,7 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) { case SDL_EVENT_KEY_DOWN: { SDL_Scancode scancode = event->key.scancode; - if (scancode < 256) { + if (scancode < PXL8_MAX_KEYS) { if (!input->keys_down[scancode]) { input->keys_pressed[scancode] = true; } @@ -279,7 +285,7 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) { case SDL_EVENT_KEY_UP: { SDL_Scancode scancode = event->key.scancode; - if (scancode < 256) { + if (scancode < PXL8_MAX_KEYS) { input->keys_down[scancode] = false; input->keys_pressed[scancode] = false; input->keys_released[scancode] = true; diff --git a/src/pxl8_tilesheet.c b/src/pxl8_tilesheet.c index fc29618..83b813e 100644 --- a/src/pxl8_tilesheet.c +++ b/src/pxl8_tilesheet.c @@ -4,6 +4,7 @@ #include #include "pxl8_ase.h" +#include "pxl8_color.h" #include "pxl8_macros.h" #include "pxl8_tilemap.h" @@ -105,11 +106,11 @@ pxl8_result pxl8_tilesheet_load(pxl8_tilesheet* tilesheet, const char* filepath, tilesheet->total_tiles = (width / tilesheet->tile_size) * (height / tilesheet->tile_size); tilesheet->color_mode = pxl8_gfx_get_color_mode(gfx); - size_t data_size = width * height; - if (pxl8_gfx_get_color_mode(gfx) == PXL8_COLOR_MODE_HICOLOR) { - data_size *= 4; - } + u32 pixel_count = width * height; + u16 ase_depth = ase_file.header.color_depth; + bool gfx_hicolor = (tilesheet->color_mode == PXL8_COLOR_MODE_HICOLOR); + size_t data_size = pixel_count * pxl8_bytes_per_pixel(tilesheet->color_mode); tilesheet->data = malloc(data_size); if (!tilesheet->data) { pxl8_ase_destroy(&ase_file); @@ -117,7 +118,41 @@ pxl8_result pxl8_tilesheet_load(pxl8_tilesheet* tilesheet, const char* filepath, } if (ase_file.frame_count > 0 && ase_file.frames[0].pixels) { - memcpy(tilesheet->data, ase_file.frames[0].pixels, data_size); + const u8* src = ase_file.frames[0].pixels; + + if (ase_depth == 8 && !gfx_hicolor) { + memcpy(tilesheet->data, src, pixel_count); + } else if (ase_depth == 32 && gfx_hicolor) { + u16* dst = (u16*)tilesheet->data; + const u32* rgba = (const u32*)src; + for (u32 i = 0; i < pixel_count; i++) { + u32 c = rgba[i]; + u8 a = (c >> 24) & 0xFF; + if (a == 0) { + dst[i] = 0; + } else { + dst[i] = pxl8_rgba32_to_rgb565(c); + } + } + } else if (ase_depth == 8 && gfx_hicolor) { + pxl8_warn("Indexed ASE with hicolor gfx - storing as indexed"); + tilesheet->color_mode = PXL8_COLOR_MODE_FAMI; + u8* new_data = realloc(tilesheet->data, pixel_count); + if (!new_data) { + free(tilesheet->data); + tilesheet->data = NULL; + pxl8_ase_destroy(&ase_file); + return PXL8_ERROR_OUT_OF_MEMORY; + } + tilesheet->data = new_data; + memcpy(tilesheet->data, src, pixel_count); + } else { + pxl8_error("Unsupported ASE color depth %d for gfx mode", ase_depth); + free(tilesheet->data); + tilesheet->data = NULL; + pxl8_ase_destroy(&ase_file); + return PXL8_ERROR_INVALID_ARGUMENT; + } } tilesheet->tile_valid = calloc(tilesheet->total_tiles + 1, sizeof(bool)); @@ -138,15 +173,13 @@ pxl8_result pxl8_tilesheet_load(pxl8_tilesheet* tilesheet, const char* filepath, bool has_content = false; for (u32 py = 0; py < tilesheet->tile_size && !has_content; py++) { for (u32 px = 0; px < tilesheet->tile_size; px++) { + u32 idx = (tile_y + py) * width + (tile_x + px); if (is_hicolor) { - u32 idx = ((tile_y + py) * width + (tile_x + px)) * 4; - u8 alpha = tilesheet->data[idx + 3]; - if (alpha > 0) { + if (((u16*)tilesheet->data)[idx] != 0) { has_content = true; break; } } else { - u32 idx = (tile_y + py) * width + (tile_x + px); if (tilesheet->data[idx] != 0) { has_content = true; break; @@ -277,17 +310,17 @@ pxl8_result pxl8_tilesheet_set_tile_pixels(pxl8_tilesheet* tilesheet, u16 tile_i u32 tile_x = (tile_id - 1) % tilesheet->tiles_per_row; u32 tile_y = (tile_id - 1) / tilesheet->tiles_per_row; - u32 bytes_per_pixel = (tilesheet->color_mode == PXL8_COLOR_MODE_HICOLOR) ? 4 : 1; + u32 bytes_per_pixel = pxl8_bytes_per_pixel(tilesheet->color_mode); for (u32 py = 0; py < tilesheet->tile_size; py++) { for (u32 px = 0; px < tilesheet->tile_size; px++) { u32 src_idx = py * tilesheet->tile_size + px; u32 dst_x = tile_x * tilesheet->tile_size + px; u32 dst_y = tile_y * tilesheet->tile_size + py; - u32 dst_idx = (dst_y * tilesheet->width + dst_x) * bytes_per_pixel; + u32 dst_idx = dst_y * tilesheet->width + dst_x; - if (bytes_per_pixel == 4) { - ((u32*)tilesheet->data)[dst_idx / 4] = pixels[src_idx]; + if (bytes_per_pixel == 2) { + ((u16*)tilesheet->data)[dst_idx] = ((const u16*)pixels)[src_idx]; } else { tilesheet->data[dst_idx] = pixels[src_idx]; } diff --git a/src/pxl8_transition.c b/src/pxl8_transition.c index 64c8e81..8e7eff2 100644 --- a/src/pxl8_transition.c +++ b/src/pxl8_transition.c @@ -168,8 +168,11 @@ void pxl8_transition_render(const pxl8_transition* transition, pxl8_gfx* gfx) { i32 block_size = (i32)(max_block_size * progress); if (block_size < 1) block_size = 1; - u8* fb = pxl8_gfx_get_framebuffer(gfx); - if (!fb) break; + pxl8_color_mode mode = pxl8_gfx_get_color_mode(gfx); + bool has_fb = (mode == PXL8_COLOR_MODE_HICOLOR) + ? (pxl8_gfx_get_framebuffer_hicolor(gfx) != NULL) + : (pxl8_gfx_get_framebuffer_indexed(gfx) != NULL); + if (!has_fb) break; for (i32 y = 0; y < height; y += block_size) { for (i32 x = 0; x < width; x += block_size) { diff --git a/src/pxl8_types.h b/src/pxl8_types.h index 5aa6a38..3f8032c 100644 --- a/src/pxl8_types.h +++ b/src/pxl8_types.h @@ -4,6 +4,14 @@ #include #include +#define PXL8_DEFAULT_ATLAS_ENTRY_CAPACITY 64 +#define PXL8_DEFAULT_ATLAS_SIZE 1024 +#define PXL8_DEFAULT_SPRITE_CACHE_CAPACITY 64 +#define PXL8_MAX_ERROR_SIZE 2048 +#define PXL8_MAX_KEYS 256 +#define PXL8_MAX_PALETTE_SIZE 256 +#define PXL8_MAX_PATH 256 + typedef float f32; typedef double f64; typedef int8_t i8; @@ -69,9 +77,9 @@ typedef struct pxl8_bounds { } pxl8_bounds; typedef struct pxl8_input_state { - bool keys_down[256]; - bool keys_pressed[256]; - bool keys_released[256]; + bool keys_down[PXL8_MAX_KEYS]; + bool keys_pressed[PXL8_MAX_KEYS]; + bool keys_released[PXL8_MAX_KEYS]; bool mouse_buttons_down[3]; bool mouse_buttons_pressed[3]; diff --git a/src/pxl8_ui.c b/src/pxl8_ui.c deleted file mode 100644 index 77ab29a..0000000 --- a/src/pxl8_ui.c +++ /dev/null @@ -1,308 +0,0 @@ -#include "pxl8_ui.h" - -#include -#include - -#include - -#include "pxl8_font.h" - -struct pxl8_ui { - pxl8_gfx* gfx; - mu_Context mu_ctx; - i32 font_height; - i32 font_width; -}; - -static int mu_text_width(mu_Font font, const char* str, int len) { - const pxl8_font* f = (const pxl8_font*)font; - if (!f) return 0; - - if (len < 0) { - len = (int)strlen(str); - } - - i32 width = 0; - for (int i = 0; i < len; i++) { - const pxl8_glyph* glyph = pxl8_font_find_glyph(f, (u32)str[i]); - if (glyph) { - width += f->default_width; - } - } - - return width; -} - -static int mu_text_height(mu_Font font) { - const pxl8_font* f = (const pxl8_font*)font; - if (!f) return 8; - return f->default_height; -} - -static void pxl8_ui_render_icon(pxl8_gfx* gfx, i32 id, i32 x, i32 y, i32 w, i32 h, u8 color) { - switch (id) { - case 2: { - i32 cx = x + w / 2; - i32 cy = y + h / 2; - i32 size = (w < h ? w : h) / 3; - pxl8_line(gfx, cx - size, cy, cx, cy + size, color); - pxl8_line(gfx, cx, cy + size, cx + size, cy - size, color); - break; - } - case 1: { - i32 cx = x + w / 2; - i32 cy = y + h / 2; - i32 size = (w < h ? w : h) / 4; - pxl8_line(gfx, cx - size, cy - size, cx + size, cy + size, color); - pxl8_line(gfx, cx + size, cy - size, cx - size, cy + size, color); - break; - } - } -} - -static void pxl8_ui_render_commands(pxl8_ui* ui) { - mu_Command* cmd = NULL; - while (mu_next_command(&ui->mu_ctx, &cmd)) { - switch (cmd->type) { - case MU_COMMAND_RECT: { - mu_RectCommand* rc = (mu_RectCommand*)cmd; - pxl8_rect_fill(ui->gfx, rc->rect.x, rc->rect.y, rc->rect.w, rc->rect.h, rc->color.r); - break; - } - case MU_COMMAND_TEXT: { - mu_TextCommand* tc = (mu_TextCommand*)cmd; - pxl8_text(ui->gfx, tc->str, tc->pos.x, tc->pos.y, tc->color.r); - break; - } - case MU_COMMAND_CLIP: { - break; - } - case MU_COMMAND_ICON: { - mu_IconCommand* ic = (mu_IconCommand*)cmd; - pxl8_ui_render_icon(ui->gfx, ic->id, ic->rect.x, ic->rect.y, ic->rect.w, ic->rect.h, ic->color.r); - break; - } - } - } -} - -static void pxl8_ui_draw_9slice(pxl8_gfx* gfx, pxl8_bounds rect, pxl8_frame_theme* theme) { - if (theme->sprite_id == 0) return; - - i32 cs = theme->corner_size; - i32 es = theme->edge_size; - - pxl8_sprite(gfx, theme->sprite_id, rect.x, rect.y, cs, cs); - pxl8_sprite(gfx, theme->sprite_id, rect.x + rect.w - cs, rect.y, cs, cs); - pxl8_sprite(gfx, theme->sprite_id, rect.x, rect.y + rect.h - cs, cs, cs); - pxl8_sprite(gfx, theme->sprite_id, rect.x + rect.w - cs, rect.y + rect.h - cs, cs, cs); - - for (i32 x = cs; x < rect.w - cs; x += es) { - i32 w = (x + es > rect.w - cs) ? (rect.w - cs - x) : es; - pxl8_sprite(gfx, theme->sprite_id, rect.x + x, rect.y, w, es); - pxl8_sprite(gfx, theme->sprite_id, rect.x + x, rect.y + rect.h - es, w, es); - } - - for (i32 y = cs; y < rect.h - cs; y += es) { - i32 h = (y + es > rect.h - cs) ? (rect.h - cs - y) : es; - pxl8_sprite(gfx, theme->sprite_id, rect.x, rect.y + y, es, h); - pxl8_sprite(gfx, theme->sprite_id, rect.x + rect.w - es, rect.y + y, es, h); - } -} - -pxl8_ui* pxl8_ui_create(pxl8_gfx* gfx) { - if (!gfx) return NULL; - - pxl8_ui* ui = (pxl8_ui*)malloc(sizeof(pxl8_ui)); - if (!ui) return NULL; - - memset(ui, 0, sizeof(pxl8_ui)); - ui->gfx = gfx; - ui->font_width = 8; - ui->font_height = 8; - - mu_init(&ui->mu_ctx); - ui->mu_ctx.style->font = (mu_Font)&pxl8_default_font; - ui->mu_ctx.text_height = mu_text_height; - ui->mu_ctx.text_width = mu_text_width; - - ui->mu_ctx.style->colors[0] = (mu_Color){15, 0, 0, 255}; - ui->mu_ctx.style->colors[1] = (mu_Color){8, 0, 0, 255}; - ui->mu_ctx.style->colors[2] = (mu_Color){1, 0, 0, 255}; - ui->mu_ctx.style->colors[3] = (mu_Color){2, 0, 0, 255}; - ui->mu_ctx.style->colors[4] = (mu_Color){15, 0, 0, 255}; - ui->mu_ctx.style->colors[5] = (mu_Color){0, 0, 0, 0}; - ui->mu_ctx.style->colors[6] = (mu_Color){7, 0, 0, 255}; - ui->mu_ctx.style->colors[7] = (mu_Color){8, 0, 0, 255}; - ui->mu_ctx.style->colors[8] = (mu_Color){10, 0, 0, 255}; - ui->mu_ctx.style->colors[9] = (mu_Color){2, 0, 0, 255}; - ui->mu_ctx.style->colors[10] = (mu_Color){3, 0, 0, 255}; - ui->mu_ctx.style->colors[11] = (mu_Color){10, 0, 0, 255}; - ui->mu_ctx.style->colors[12] = (mu_Color){1, 0, 0, 255}; - ui->mu_ctx.style->colors[13] = (mu_Color){3, 0, 0, 255}; - - return ui; -} - -void pxl8_ui_destroy(pxl8_ui* ui) { - if (!ui) return; - free(ui); -} - -void pxl8_ui_frame_begin(pxl8_ui* ui) { - if (!ui) return; - mu_begin(&ui->mu_ctx); -} - -void pxl8_ui_frame_end(pxl8_ui* ui) { - if (!ui) return; - mu_end(&ui->mu_ctx); - pxl8_ui_render_commands(ui); -} - -void pxl8_ui_input_keydown(pxl8_ui* ui, i32 key) { - if (!ui) return; - mu_input_keydown(&ui->mu_ctx, key); -} - -void pxl8_ui_input_keyup(pxl8_ui* ui, i32 key) { - if (!ui) return; - mu_input_keyup(&ui->mu_ctx, key); -} - -void pxl8_ui_input_mousedown(pxl8_ui* ui, i32 x, i32 y, i32 button) { - if (!ui) return; - mu_input_mousedown(&ui->mu_ctx, x, y, button); -} - -void pxl8_ui_input_mousemove(pxl8_ui* ui, i32 x, i32 y) { - if (!ui) return; - mu_input_mousemove(&ui->mu_ctx, x, y); -} - -void pxl8_ui_input_mouseup(pxl8_ui* ui, i32 x, i32 y, i32 button) { - if (!ui) return; - mu_input_mouseup(&ui->mu_ctx, x, y, button); -} - -void pxl8_ui_input_scroll(pxl8_ui* ui, i32 x, i32 y) { - if (!ui) return; - mu_input_scroll(&ui->mu_ctx, x, y); -} - -void pxl8_ui_input_text(pxl8_ui* ui, const char* text) { - if (!ui || !text) return; - mu_input_text(&ui->mu_ctx, text); -} - -bool pxl8_ui_has_mouse_focus(pxl8_ui* ui) { - if (!ui) return false; - return ui->mu_ctx.hover_root != NULL; -} - -bool pxl8_ui_button(pxl8_ui* ui, const char* label) { - if (!ui) return false; - return mu_button(&ui->mu_ctx, label) & MU_RES_SUBMIT; -} - -bool pxl8_ui_checkbox(pxl8_ui* ui, const char* label, bool* state) { - if (!ui || !state || !label) return false; - - mu_Context* ctx = &ui->mu_ctx; - mu_push_id(ctx, label, (int)strlen(label)); - - mu_Id id = mu_get_id(ctx, label, (int)strlen(label)); - mu_Rect r = mu_layout_next(ctx); - mu_Rect box = mu_rect(r.x, r.y, r.h, r.h); - - int had_focus_before = (ctx->focus == id); - mu_update_control(ctx, id, r, 0); - int has_focus_after = (ctx->focus == id); - int mouseover = mu_mouse_over(ctx, r); - - int res = 0; - int int_state = *state ? 1 : 0; - - if (had_focus_before && !has_focus_after && !ctx->mouse_down && mouseover) { - res |= MU_RES_CHANGE; - int_state = !int_state; - } - - mu_draw_control_frame(ctx, id, box, MU_COLOR_BASE, 0); - if (int_state) { - mu_draw_icon(ctx, MU_ICON_CHECK, box, ctx->style->colors[MU_COLOR_TEXT]); - } - r = mu_rect(r.x + box.w, r.y, r.w - box.w, r.h); - mu_draw_control_text(ctx, label, r, MU_COLOR_TEXT, 0); - - mu_pop_id(ctx); - - *state = int_state != 0; - return res & MU_RES_CHANGE; -} - -void pxl8_ui_indent(pxl8_ui* ui, i32 amount) { - if (!ui) return; - mu_Layout* layout = &ui->mu_ctx.layout_stack.items[ui->mu_ctx.layout_stack.idx - 1]; - layout->indent += amount; -} - -void pxl8_ui_label(pxl8_ui* ui, const char* text) { - if (!ui || !text) return; - mu_label(&ui->mu_ctx, text); -} - -void pxl8_ui_layout_row(pxl8_ui* ui, i32 item_count, const i32* widths, i32 height) { - if (!ui) return; - mu_layout_row(&ui->mu_ctx, item_count, widths, height); -} - -i32 pxl8_ui_menu(pxl8_ui* ui, pxl8_menu_item* items, i32 item_count) { - if (!ui || !items) return -1; - - i32 selected = -1; - for (i32 i = 0; i < item_count; i++) { - if (!items[i].enabled) { - mu_label(&ui->mu_ctx, items[i].label); - } else if (mu_button(&ui->mu_ctx, items[i].label) & MU_RES_SUBMIT) { - selected = i; - } - } - return selected; -} - -void pxl8_ui_panel(pxl8_ui* ui, pxl8_bounds rect, pxl8_frame_theme* theme) { - if (!ui || !theme) return; - - pxl8_rect_fill(ui->gfx, rect.x, rect.y, rect.w, rect.h, theme->bg_color); - pxl8_ui_draw_9slice(ui->gfx, rect, theme); -} - -bool pxl8_ui_window_begin(pxl8_ui* ui, const char* title, pxl8_bounds rect, i32 options) { - if (!ui) return false; - mu_Rect r = { rect.x, rect.y, rect.w, rect.h }; - return mu_begin_window_ex(&ui->mu_ctx, title, r, options); -} - -void pxl8_ui_window_end(pxl8_ui* ui) { - if (!ui) return; - mu_end_window(&ui->mu_ctx); -} - -void pxl8_ui_window_set_open(pxl8_ui* ui, const char* title, bool open) { - if (!ui || !title) return; - mu_Container* win = mu_get_container(&ui->mu_ctx, title); - if (win) { - win->open = open ? 1 : 0; - } -} - -pxl8_frame_theme pxl8_ui_theme_default(void) { - pxl8_frame_theme theme = {0}; - theme.bg_color = 0; - theme.corner_size = 8; - theme.edge_size = 8; - theme.padding = 4; - theme.sprite_id = 0; - return theme; -} diff --git a/src/pxl8_ui.h b/src/pxl8_ui.h deleted file mode 100644 index 81428c0..0000000 --- a/src/pxl8_ui.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include "pxl8_gfx.h" -#include "pxl8_types.h" - -typedef struct pxl8_ui pxl8_ui; - -typedef struct pxl8_ui_theme { - u8 text; - u8 border; - u8 window_bg; - u8 title_bg; - u8 title_text; - u8 panel_bg; - u8 button; - u8 button_hover; - u8 button_focus; - u8 base; - u8 base_hover; - u8 base_focus; - u8 scroll_base; - u8 scroll_thumb; -} pxl8_ui_theme; - -typedef struct pxl8_frame_theme { - u8 bg_color; - u32 sprite_id; - i32 corner_size; - i32 edge_size; - i32 padding; -} pxl8_frame_theme; - -typedef struct pxl8_menu_item { - bool enabled; - const char* label; -} pxl8_menu_item; - -typedef enum pxl8_ui_options { - PXL8_UI_NOCLOSE = 1 << 0, - PXL8_UI_NOFRAME = 1 << 1, - PXL8_UI_NORESIZE = 1 << 2, - PXL8_UI_NOTITLE = 1 << 3 -} pxl8_ui_options; - -#ifdef __cplusplus -extern "C" { -#endif - -pxl8_ui* pxl8_ui_create(pxl8_gfx* gfx); -void pxl8_ui_destroy(pxl8_ui* ui); - -bool pxl8_ui_has_mouse_focus(pxl8_ui* ui); - -void pxl8_ui_frame_begin(pxl8_ui* ui); -void pxl8_ui_frame_end(pxl8_ui* ui); - -void pxl8_ui_input_keydown(pxl8_ui* ui, i32 key); -void pxl8_ui_input_keyup(pxl8_ui* ui, i32 key); -void pxl8_ui_input_mousedown(pxl8_ui* ui, i32 x, i32 y, i32 button); -void pxl8_ui_input_mousemove(pxl8_ui* ui, i32 x, i32 y); -void pxl8_ui_input_mouseup(pxl8_ui* ui, i32 x, i32 y, i32 button); -void pxl8_ui_input_scroll(pxl8_ui* ui, i32 x, i32 y); -void pxl8_ui_input_text(pxl8_ui* ui, const char* text); - -bool pxl8_ui_button(pxl8_ui* ui, const char* label); -bool pxl8_ui_checkbox(pxl8_ui* ui, const char* label, bool* state); -void pxl8_ui_indent(pxl8_ui* ui, i32 amount); -void pxl8_ui_label(pxl8_ui* ui, const char* text); -void pxl8_ui_layout_row(pxl8_ui* ui, i32 item_count, const i32* widths, i32 height); -i32 pxl8_ui_menu(pxl8_ui* ui, pxl8_menu_item* items, i32 item_count); -void pxl8_ui_panel(pxl8_ui* ui, pxl8_bounds rect, pxl8_frame_theme* theme); -bool pxl8_ui_window_begin(pxl8_ui* ui, const char* title, pxl8_bounds rect, i32 options); -void pxl8_ui_window_end(pxl8_ui* ui); -void pxl8_ui_window_set_open(pxl8_ui* ui, const char* title, bool open); - -pxl8_frame_theme pxl8_ui_theme_default(void); - -#ifdef __cplusplus -} -#endif diff --git a/src/pxl8_vfx.c b/src/pxl8_vfx.c index 430da50..032d3cb 100644 --- a/src/pxl8_vfx.c +++ b/src/pxl8_vfx.c @@ -3,6 +3,8 @@ #include #include +#define PXL8_RANDF() ((f32)rand() / (f32)RAND_MAX) + struct pxl8_particles { pxl8_particle* particles; u32 alive_count; @@ -26,7 +28,7 @@ struct pxl8_particles { }; void pxl8_vfx_plasma(pxl8_gfx* gfx, f32 time, f32 scale1, f32 scale2, u8 palette_offset) { - if (!gfx || !pxl8_gfx_get_framebuffer(gfx)) return; + if (!gfx || !pxl8_gfx_get_framebuffer_indexed(gfx)) return; for (i32 y = 0; y < pxl8_gfx_get_height(gfx); y++) { for (i32 x = 0; x < pxl8_gfx_get_width(gfx); x++) { @@ -51,6 +53,8 @@ void pxl8_vfx_raster_bars(pxl8_gfx* gfx, pxl8_raster_bar* bars, u32 bar_count, f for (u32 i = 0; i < bar_count; i++) { pxl8_raster_bar* bar = &bars[i]; + if (bar->height <= 1) continue; + f32 y = bar->base_y + bar->amplitude * sinf(time * bar->speed + bar->phase); i32 y_int = (i32)y; @@ -75,7 +79,7 @@ void pxl8_vfx_raster_bars(pxl8_gfx* gfx, pxl8_raster_bar* bars, u32 bar_count, f } void pxl8_vfx_rotozoom(pxl8_gfx* gfx, f32 angle, f32 zoom, i32 cx, i32 cy) { - if (!gfx || !pxl8_gfx_get_framebuffer(gfx)) return; + if (!gfx || !pxl8_gfx_get_framebuffer_indexed(gfx)) return; f32 cos_a = cosf(angle); f32 sin_a = sinf(angle); @@ -83,7 +87,7 @@ void pxl8_vfx_rotozoom(pxl8_gfx* gfx, f32 angle, f32 zoom, i32 cx, i32 cy) { u8* temp_buffer = (u8*)malloc(pxl8_gfx_get_width(gfx) * pxl8_gfx_get_height(gfx)); if (!temp_buffer) return; - memcpy(temp_buffer, pxl8_gfx_get_framebuffer(gfx), pxl8_gfx_get_width(gfx) * pxl8_gfx_get_height(gfx)); + memcpy(temp_buffer, pxl8_gfx_get_framebuffer_indexed(gfx), pxl8_gfx_get_width(gfx) * pxl8_gfx_get_height(gfx)); for (i32 y = 0; y < pxl8_gfx_get_height(gfx); y++) { for (i32 x = 0; x < pxl8_gfx_get_width(gfx); x++) { @@ -97,7 +101,7 @@ void pxl8_vfx_rotozoom(pxl8_gfx* gfx, f32 angle, f32 zoom, i32 cx, i32 cy) { i32 sy = (i32)src_y; if (sx >= 0 && sx < pxl8_gfx_get_width(gfx) && sy >= 0 && sy < pxl8_gfx_get_height(gfx)) { - pxl8_gfx_get_framebuffer(gfx)[y * pxl8_gfx_get_width(gfx) + x] = temp_buffer[sy * pxl8_gfx_get_width(gfx) + sx]; + pxl8_gfx_get_framebuffer_indexed(gfx)[y * pxl8_gfx_get_width(gfx) + x] = temp_buffer[sy * pxl8_gfx_get_width(gfx) + sx]; } } } @@ -106,7 +110,7 @@ void pxl8_vfx_rotozoom(pxl8_gfx* gfx, f32 angle, f32 zoom, i32 cx, i32 cy) { } void pxl8_vfx_tunnel(pxl8_gfx* gfx, f32 time, f32 speed, f32 twist) { - if (!gfx || !pxl8_gfx_get_framebuffer(gfx)) return; + if (!gfx || !pxl8_gfx_get_framebuffer_indexed(gfx)) return; f32 cx = pxl8_gfx_get_width(gfx) / 2.0f; f32 cy = pxl8_gfx_get_height(gfx) / 2.0f; @@ -210,8 +214,8 @@ void pxl8_particles_emit(pxl8_particles* particles, u32 count) { pxl8_particle* p = &particles->particles[j]; p->life = 1.0f; p->max_life = 1.0f; - 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->x = particles->x + ((PXL8_RANDF()) - 0.5f) * particles->spread_x; + p->y = particles->y + ((PXL8_RANDF()) - 0.5f) * particles->spread_y; p->z = 0; p->vx = p->vy = p->vz = 0; p->ax = particles->gravity_x; @@ -312,15 +316,15 @@ void pxl8_vfx_explosion(pxl8_particles* particles, i32 x, i32 y, u32 color, f32 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); + f32 angle = (PXL8_RANDF()) * 6.28f; + f32 speed = force * (0.5f + (PXL8_RANDF()) * 0.5f); p->x = x; p->y = y; p->vx = cosf(angle) * speed; p->vy = sinf(angle) * speed; p->life = 1.0f; - p->max_life = 1.0f + ((f32)rand() / RAND_MAX); + p->max_life = 1.0f + (PXL8_RANDF()); p->color = color; p->flags = 1; } @@ -331,9 +335,9 @@ static void fire_spawn(pxl8_particle* p, void* userdata) { p->start_color = palette_start + 6 + (rand() % 3); p->end_color = palette_start; p->color = p->start_color; - p->max_life = 1.5f + ((f32)rand() / RAND_MAX) * 1.5f; - p->vy = -80.0f - ((f32)rand() / RAND_MAX) * 120.0f; - p->vx = (((f32)rand() / RAND_MAX) - 0.5f) * 40.0f; + p->max_life = 1.5f + (PXL8_RANDF()) * 1.5f; + p->vy = -80.0f - (PXL8_RANDF()) * 120.0f; + p->vx = ((PXL8_RANDF()) - 0.5f) * 40.0f; } static void fire_update(pxl8_particle* p, f32 dt, void* userdata) { @@ -378,7 +382,7 @@ static void rain_spawn(pxl8_particle* p, void* userdata) { p->end_color = 29; p->color = p->start_color; p->max_life = 2.0f; - p->vy = 200.0f + ((f32)rand() / RAND_MAX) * 100.0f; + p->vy = 200.0f + (PXL8_RANDF()) * 100.0f; } void pxl8_vfx_rain(pxl8_particles* particles, i32 width, f32 wind) { @@ -401,10 +405,10 @@ static void smoke_spawn(pxl8_particle* p, void* userdata) { p->start_color = base_color; p->end_color = base_color + 4; p->color = p->start_color; - p->max_life = 3.0f + ((f32)rand() / RAND_MAX) * 2.0f; - p->vy = -20.0f - ((f32)rand() / RAND_MAX) * 30.0f; - p->vx = (((f32)rand() / RAND_MAX) - 0.5f) * 20.0f; - p->size = 1.0f + ((f32)rand() / RAND_MAX) * 2.0f; + p->max_life = 3.0f + (PXL8_RANDF()) * 2.0f; + p->vy = -20.0f - (PXL8_RANDF()) * 30.0f; + p->vx = ((PXL8_RANDF()) - 0.5f) * 20.0f; + p->size = 1.0f + (PXL8_RANDF()) * 2.0f; } void pxl8_vfx_smoke(pxl8_particles* particles, i32 x, i32 y, u8 color) { @@ -429,8 +433,8 @@ static void snow_spawn(pxl8_particle* p, void* userdata) { p->end_color = 10; p->color = p->start_color; p->max_life = 4.0f; - p->vx = (((f32)rand() / RAND_MAX) - 0.5f) * 20.0f; - p->vy = 30.0f + ((f32)rand() / RAND_MAX) * 20.0f; + p->vx = ((PXL8_RANDF()) - 0.5f) * 20.0f; + p->vy = 30.0f + (PXL8_RANDF()) * 20.0f; } void pxl8_vfx_snow(pxl8_particles* particles, i32 width, f32 wind) { @@ -453,10 +457,10 @@ static void sparks_spawn(pxl8_particle* p, void* userdata) { p->start_color = base_color; p->end_color = base_color > 2 ? base_color - 2 : 0; p->color = p->start_color; - p->max_life = 0.5f + ((f32)rand() / RAND_MAX) * 1.0f; + p->max_life = 0.5f + (PXL8_RANDF()) * 1.0f; - f32 angle = ((f32)rand() / RAND_MAX) * 6.28f; - f32 speed = 100.0f + ((f32)rand() / RAND_MAX) * 200.0f; + f32 angle = (PXL8_RANDF()) * 6.28f; + f32 speed = 100.0f + (PXL8_RANDF()) * 200.0f; p->vx = cosf(angle) * speed; p->vy = sinf(angle) * speed - 50.0f; } @@ -488,9 +492,9 @@ void pxl8_vfx_starfield(pxl8_particles* particles, f32 speed, f32 spread) { 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; + p->x = (PXL8_RANDF()) * spread * 2.0f - spread; + p->y = (PXL8_RANDF()) * spread * 2.0f - spread; + p->z = (PXL8_RANDF()) * spread; p->vz = -speed; p->life = 1000.0f; p->max_life = 1000.0f; diff --git a/src/pxl8_world.c b/src/pxl8_world.c index 87e6689..a56b0b7 100644 --- a/src/pxl8_world.c +++ b/src/pxl8_world.c @@ -4,8 +4,9 @@ #include #include "pxl8_bsp.h" +#include "pxl8_gen.h" #include "pxl8_macros.h" -#include "pxl8_procgen.h" +#include "pxl8_math.h" struct pxl8_world { pxl8_bsp bsp; @@ -221,22 +222,6 @@ bool pxl8_world_is_loaded(const pxl8_world* world) { return world && world->loaded; } -static inline f32 vec3_dot(pxl8_vec3 a, pxl8_vec3 b) { - return a.x * b.x + a.y * b.y + a.z * b.z; -} - -static inline pxl8_vec3 vec3_cross(pxl8_vec3 a, pxl8_vec3 b) { - return (pxl8_vec3){ - a.y * b.z - a.z * b.y, - a.z * b.x - a.x * b.z, - a.x * b.y - a.y * b.x - }; -} - -static inline f32 vec3_length(pxl8_vec3 v) { - return sqrtf(v.x * v.x + v.y * v.y + v.z * v.z); -} - pxl8_vec3 pxl8_world_resolve_collision(const pxl8_world* world, pxl8_vec3 from, pxl8_vec3 to, f32 radius) { if (!world || !world->loaded) return to; @@ -294,7 +279,7 @@ pxl8_vec3 pxl8_world_resolve_collision(const pxl8_world* world, pxl8_vec3 from, bool is_new_plane = true; for (u32 p = 0; p < num_planes; p++) { - if (vec3_dot(push_dir, clip_planes[p]) > 0.99f) { + if (pxl8_vec3_dot(push_dir, clip_planes[p]) > 0.99f) { is_new_plane = false; break; } @@ -327,8 +312,8 @@ pxl8_vec3 pxl8_world_resolve_collision(const pxl8_world* world, pxl8_vec3 from, original_velocity.z * original_velocity.z; if (orig_vel_len_sq > 0.000001f) { - f32 vdot0 = vec3_dot(original_velocity, clip_planes[0]); - f32 vdot1 = vec3_dot(original_velocity, clip_planes[1]); + f32 vdot0 = pxl8_vec3_dot(original_velocity, clip_planes[0]); + f32 vdot1 = pxl8_vec3_dot(original_velocity, clip_planes[1]); f32 dot0 = fabsf(vdot0); f32 dot1 = fabsf(vdot1); @@ -350,11 +335,11 @@ pxl8_vec3 pxl8_world_resolve_collision(const pxl8_world* world, pxl8_vec3 from, pos.y += slide_vel.y; pos.z += slide_vel.z; - pxl8_vec3 crease_dir = vec3_cross(clip_planes[0], clip_planes[1]); - f32 crease_len = vec3_length(crease_dir); + pxl8_vec3 crease_dir = pxl8_vec3_cross(clip_planes[0], clip_planes[1]); + f32 crease_len = pxl8_vec3_length(crease_dir); if (crease_len > 0.01f && fabsf(crease_dir.y / crease_len) > 0.9f) { - f32 bias0 = vec3_dot(original_velocity, clip_planes[0]); - f32 bias1 = vec3_dot(original_velocity, clip_planes[1]); + f32 bias0 = pxl8_vec3_dot(original_velocity, clip_planes[0]); + f32 bias1 = pxl8_vec3_dot(original_velocity, clip_planes[1]); if (bias0 < 0 && bias1 < 0) { const f32 corner_push = 0.1f; diff --git a/src/pxl8_world.h b/src/pxl8_world.h index 129d5ec..5388a12 100644 --- a/src/pxl8_world.h +++ b/src/pxl8_world.h @@ -1,9 +1,9 @@ #pragma once #include "pxl8_bsp.h" +#include "pxl8_gen.h" #include "pxl8_gfx.h" #include "pxl8_math.h" -#include "pxl8_procgen.h" #include "pxl8_types.h" typedef struct pxl8_world pxl8_world;