diff --git a/demo/main.fnl b/demo/main.fnl index 51b77fe..c6c8d41 100644 --- a/demo/main.fnl +++ b/demo/main.fnl @@ -3,44 +3,76 @@ (var time 0) (var current-effect 1) (var particles nil) -(var starfield-init false) (var fire-init false) (var rain-init false) (var snow-init false) +(var logo-x 256) +(var logo-y 148) +(var logo-dx 100) +(var logo-dy 80) +(var logo-sprite nil) + (global init (fn [] (pxl8.load_palette "palettes/gruvbox.ase") - (set particles (pxl8.particles_new 1000)))) + (set particles (pxl8.particles_new 1000)) + (set logo-sprite (pxl8.load_sprite "sprites/pxl8.ase")))) (global update (fn [dt] (set time (+ time dt)) - - (when (pxl8.key_pressed "1") + + (when (pxl8.key_pressed "1") (set current-effect 1)) - (when (pxl8.key_pressed "2") + (when (pxl8.key_pressed "2") (set current-effect 2)) - (when (pxl8.key_pressed "3") - (set current-effect 3) - (set fire-init false)) - (when (pxl8.key_pressed "4") + (when (pxl8.key_pressed "3") + (set current-effect 3)) + (when (pxl8.key_pressed "4") (set current-effect 4)) - (when (pxl8.key_pressed "5") + (when (pxl8.key_pressed "5") (set current-effect 5) - (set rain-init false)) - (when (pxl8.key_pressed "6") + (set fire-init false)) + (when (pxl8.key_pressed "6") (set current-effect 6) + (set rain-init false)) + (when (pxl8.key_pressed "7") + (set current-effect 7) (set snow-init false)) - + + (when (= current-effect 1) + (set logo-x (+ logo-x (* logo-dx dt))) + (set logo-y (+ logo-y (* logo-dy dt))) + + (when (or (< logo-x 0) (> logo-x 512)) + (set logo-dx (- logo-dx))) + (when (or (< logo-y 0) (> logo-y 296)) + (set logo-dy (- logo-dy)))) + (when particles (pxl8.particles_update particles dt)))) (global draw (fn [] (match current-effect - 1 (pxl8.vfx_plasma time 0.10 0.04 0) - - 2 (pxl8.vfx_tunnel time 2.0 0.25) - - 3 (do + 1 (do + (pxl8.clr 0) + (when logo-sprite + (pxl8.sprite logo-sprite logo-x logo-y 128 64)) + (pxl8.text "Bouncing Logo" 200 10 15) + (pxl8.text "Press 1-7 for effects" 10 150 7)) + + 2 (pxl8.vfx_plasma time 0.10 0.04 0) + + 3 (pxl8.vfx_tunnel time 2.0 0.25) + + 4 (do + (pxl8.clr 0) + (local bars [{:base_y 40 :amplitude 20 :height 16 :speed 2.0 :phase 0 :color 14 :fade_color 11} + {:base_y 80 :amplitude 15 :height 16 :speed 2.5 :phase 1.5 :color 20 :fade_color 11} + {:base_y 120 :amplitude 25 :height 16 :speed 1.8 :phase 3.0 :color 26 :fade_color 11}]) + (pxl8.vfx_raster_bars bars time) + (pxl8.text "Raster Bars" 200 10 15)) + + 5 (do (pxl8.clr 0) (when particles (when (not fire-init) @@ -49,16 +81,8 @@ (set fire-init true)) (pxl8.particles_render particles)) (pxl8.text "Fire Effect Test" 200 10 15)) - - 4 (do - (pxl8.clr 0) - (local bars [{:base_y 40 :amplitude 20 :height 16 :speed 2.0 :phase 0 :color 14 :fade_color 11} - {:base_y 80 :amplitude 15 :height 16 :speed 2.5 :phase 1.5 :color 20 :fade_color 11} - {:base_y 120 :amplitude 25 :height 16 :speed 1.8 :phase 3.0 :color 26 :fade_color 11}]) - (pxl8.vfx_copper_bars bars time) - (pxl8.text "Copper Bars" 200 10 15)) - - 5 (do + + 6 (do (pxl8.clr 0) (when particles (when (not rain-init) @@ -67,8 +91,8 @@ (set rain-init true)) (pxl8.particles_render particles)) (pxl8.text "Rain" 200 10 15)) - - 6 (do + + 7 (do (pxl8.clr 0) (when particles (when (not snow-init) diff --git a/demo/palettes/sweaft-64.ase b/demo/palettes/sweaft-64.ase deleted file mode 100644 index 2a48de0..0000000 Binary files a/demo/palettes/sweaft-64.ase and /dev/null differ diff --git a/demo/sprites/pxl8.png b/demo/sprites/pxl8.png deleted file mode 100755 index ad85c8e..0000000 Binary files a/demo/sprites/pxl8.png and /dev/null differ diff --git a/demo/sprites/trees.ase b/demo/sprites/trees.ase deleted file mode 100644 index 95c65d7..0000000 Binary files a/demo/sprites/trees.ase and /dev/null differ diff --git a/demo/tiles/tilesheet-dungeon.ase b/demo/tiles/tilesheet-dungeon.ase deleted file mode 100644 index 573598a..0000000 Binary files a/demo/tiles/tilesheet-dungeon.ase and /dev/null differ diff --git a/demo/tiles/tilesheet-world.ase b/demo/tiles/tilesheet-world.ase deleted file mode 100644 index e28fab2..0000000 Binary files a/demo/tiles/tilesheet-world.ase and /dev/null differ diff --git a/pxl8.sh b/pxl8.sh index 90f2523..98e78f0 100755 --- a/pxl8.sh +++ b/pxl8.sh @@ -39,21 +39,21 @@ if [[ "$(uname)" == "Linux" ]]; then CFLAGS="$CFLAGS -D_GNU_SOURCE" fi -print_info() { - echo -e "${BLUE}${BOLD}info:${NC} $1" -} +compile_source_file() { + local src_file="$1" + local obj_file="$2" + local compile_flags="$3" -print_success() { - echo -e "${GREEN}${BOLD}✓${NC} $1" -} - -print_error() { - echo -e "${RED}${BOLD}error:${NC} $1" >&2 + print_info "Compiling: $src_file" + if ! $CC -c $compile_flags "$src_file" -o "$obj_file"; then + print_error "Compilation failed for $src_file" + exit 1 + fi } detect_simd() { local simd_flags="" - + case "$(uname -m)" in x86_64|amd64) if $CC -mavx2 -c -x c /dev/null -o /dev/null 2>/dev/null; then @@ -68,10 +68,26 @@ detect_simd() { fi ;; esac - + echo "$simd_flags" } +make_lib_dirs() { + mkdir -p lib/linenoise lib/fennel lib/microui/src lib/miniz +} + +print_error() { + echo -e "${RED}${BOLD}error:${NC} $1" >&2 +} + +print_info() { + echo -e "${BLUE}${BOLD}info:${NC} $1" +} + +print_success() { + echo -e "${GREEN}${BOLD}✓${NC} $1" +} + print_usage() { echo -e "${BOLD}pxl8${NC} - framework build tool" echo @@ -80,15 +96,18 @@ print_usage() { echo echo -e "${BOLD}COMMANDS:${NC}" echo " build Build pxl8" - echo " run Build and run pxl8 [script.fnl|script.lua]" echo " clean Remove build artifacts" + echo " run Build and run pxl8 (optional: cart.pxc or folder)" + echo echo " update Download/update all dependencies" - echo " vendor Fetch source for dependencies (ex. sdl3)" + echo " vendor Fetch source for dependencies (ex. SDL3)" + echo echo " help Show this help message" echo echo -e "${BOLD}OPTIONS:${NC}" - echo " --all Remove all artifacts including dependencies when cleaning" - echo " --release Build/run in release mode" + echo " --all Clean both build artifacts and dependencies" + echo " --deps Clean only dependencies" + echo " --release Build/run/clean in release mode (default: debug)" } COMMAND="$1" @@ -115,6 +134,34 @@ else BINDIR="$BINDIR/debug" fi +build_luajit() { + if [[ ! -f "lib/luajit/src/libluajit.a" ]]; then + print_info "Building LuaJIT" + cd lib/luajit + + if [[ "$(uname)" == "Darwin" ]]; then + export MACOSX_DEPLOYMENT_TARGET="10.11" + fi + + make clean >/dev/null 2>&1 || true + make -j$(nproc 2>/dev/null || echo 4) > /dev/null 2>&1 + cd - > /dev/null + print_success "Built LuaJIT" + fi +} + +build_sdl() { + if [[ ! -f "lib/SDL/build/libSDL3.so" ]] && [[ ! -f "lib/SDL/build/libSDL3.a" ]] && [[ ! -f "lib/SDL/build/libSDL3.dylib" ]]; then + print_info "Building SDL3" + mkdir -p lib/SDL/build + cd lib/SDL/build + cmake .. -DCMAKE_BUILD_TYPE=Release > /dev/null 2>&1 + cmake --build . --parallel $(nproc 2>/dev/null || echo 4) > /dev/null 2>&1 + cd - > /dev/null + print_success "Built SDL3" + fi +} + setup_sdl3() { if [[ -d "lib/SDL/build" ]]; then SDL3_CFLAGS="-Ilib/SDL/include" @@ -125,49 +172,52 @@ setup_sdl3() { SDL3_CFLAGS=$(pkg-config --cflags sdl3 2>/dev/null || echo "") SDL3_LIBS=$(pkg-config --libs sdl3 2>/dev/null || echo "") SDL3_RPATH="" - + if [[ -z "$SDL3_LIBS" ]]; then case "$(uname)" in Darwin) - SDL3_CFLAGS="-I/usr/local/include/SDL3" - SDL3_LIBS="-L/usr/local/lib -lSDL3" + if [[ -f "/usr/local/include/SDL3/SDL.h" ]]; then + SDL3_CFLAGS="-I/usr/local/include/SDL3" + SDL3_LIBS="-L/usr/local/lib -lSDL3" + fi ;; Linux) - SDL3_CFLAGS="-I/usr/include/SDL3" - SDL3_LIBS="-lSDL3" + if [[ -f "/usr/include/SDL3/SDL.h" ]]; then + SDL3_CFLAGS="-I/usr/include/SDL3" + SDL3_LIBS="-lSDL3" + fi ;; MINGW*|MSYS*|CYGWIN*) - SDL3_CFLAGS="-I/mingw64/include/SDL3" - SDL3_LIBS="-lSDL3" + if [[ -f "/mingw64/include/SDL3/SDL.h" ]]; then + SDL3_CFLAGS="-I/mingw64/include/SDL3" + SDL3_LIBS="-lSDL3" + fi ;; esac fi - print_info "Using system SDL3" + + if [[ -z "$SDL3_LIBS" ]]; then + print_info "System SDL3 not found, vendoring SDL3..." + update_sdl + build_sdl + SDL3_CFLAGS="-Ilib/SDL/include" + SDL3_LIBS="-Llib/SDL/build -lSDL3" + SDL3_RPATH="-Wl,-rpath,$(pwd)/lib/SDL/build" + print_info "Using vendored SDL3" + else + print_info "Using system SDL3" + fi fi CFLAGS="$CFLAGS $SDL3_CFLAGS" LIBS="$LIBS $SDL3_LIBS $SDL3_RPATH" } -update_linenoise() { - print_info "Fetching linenoise" - mkdir -p lib/linenoise - - if curl -s --max-time 5 -o lib/linenoise/linenoise.c https://raw.githubusercontent.com/antirez/linenoise/master/linenoise.c 2>/dev/null && \ - curl -s --max-time 5 -o lib/linenoise/linenoise.h https://raw.githubusercontent.com/antirez/linenoise/master/linenoise.h 2>/dev/null; then - print_success "Updated linenoise" - else - print_error "Failed to download linenoise" - return 1 - fi -} - update_fennel() { print_info "Fetching Fennel" - mkdir -p lib/fennel - + local version="1.5.3" - + if curl -s --max-time 5 -o lib/fennel/fennel.lua "https://fennel-lang.org/downloads/fennel-${version}.lua" 2>/dev/null; then if [[ -f "lib/fennel/fennel.lua" ]] && [[ -s "lib/fennel/fennel.lua" ]]; then print_success "Updated Fennel (${version})" @@ -181,6 +231,18 @@ update_fennel() { fi } +update_linenoise() { + print_info "Fetching linenoise" + + if curl -s --max-time 5 -o lib/linenoise/linenoise.c https://raw.githubusercontent.com/antirez/linenoise/master/linenoise.c 2>/dev/null && \ + curl -s --max-time 5 -o lib/linenoise/linenoise.h https://raw.githubusercontent.com/antirez/linenoise/master/linenoise.h 2>/dev/null; then + print_success "Updated linenoise" + else + print_error "Failed to download linenoise" + return 1 + fi +} + update_luajit() { print_info "Fetching LuaJIT" @@ -198,25 +260,8 @@ update_luajit() { print_success "Updated LuaJIT (${version})" } -build_luajit() { - if [[ ! -f "lib/luajit/src/libluajit.a" ]]; then - print_info "Building LuaJIT" - cd lib/luajit - - if [[ "$(uname)" == "Darwin" ]]; then - export MACOSX_DEPLOYMENT_TARGET="10.11" - fi - - make clean >/dev/null 2>&1 || true - make -j$(nproc 2>/dev/null || echo 4) > /dev/null 2>&1 - cd - > /dev/null - print_success "Built LuaJIT" - fi -} - update_microui() { print_info "Fetching microui" - mkdir -p lib/microui/src if curl -s --max-time 5 -o lib/microui/src/microui.c https://raw.githubusercontent.com/rxi/microui/master/src/microui.c 2>/dev/null && \ curl -s --max-time 5 -o lib/microui/src/microui.h https://raw.githubusercontent.com/rxi/microui/master/src/microui.h 2>/dev/null; then @@ -229,11 +274,10 @@ update_microui() { update_miniz() { print_info "Fetching miniz" - mkdir -p lib/miniz local version="3.0.2" - if curl -sL --max-time 10 -o /tmp/miniz.zip "https://github.com/richgel999/miniz/releases/download/${version}/miniz-${version}.zip" 2>/dev/null; then + if curl -sL --max-time 5 -o /tmp/miniz.zip "https://github.com/richgel999/miniz/releases/download/${version}/miniz-${version}.zip" 2>/dev/null; then unzip -qjo /tmp/miniz.zip miniz.c miniz.h -d lib/miniz/ 2>/dev/null rm -f /tmp/miniz.zip @@ -249,7 +293,7 @@ update_miniz() { fi } -vendor_sdl() { +update_sdl() { print_info "Fetching SDL3" if [[ -d "lib/SDL/.git" ]]; then @@ -263,18 +307,6 @@ vendor_sdl() { print_success "Updated SDL3" } -build_sdl() { - if [[ ! -f "lib/SDL/build/libSDL3.so" ]] && [[ ! -f "lib/SDL/build/libSDL3.a" ]] && [[ ! -f "lib/SDL/build/libSDL3.dylib" ]]; then - print_info "Building SDL3" - mkdir -p lib/SDL/build - cd lib/SDL/build - cmake .. -DCMAKE_BUILD_TYPE=Release > /dev/null 2>&1 - cmake --build . --parallel $(nproc 2>/dev/null || echo 4) > /dev/null 2>&1 - cd - > /dev/null - print_success "Built SDL3" - fi -} - case "$COMMAND" in build) mkdir -p "$BUILDDIR" @@ -286,9 +318,10 @@ case "$COMMAND" in [[ ! -f "lib/miniz/miniz.c" ]] || \ [[ ! -f "lib/fennel/fennel.lua" ]]; then print_info "Missing dependencies, fetching..." - - update_linenoise + + make_lib_dirs update_fennel + update_linenoise update_luajit update_microui update_miniz @@ -341,7 +374,7 @@ case "$COMMAND" in LIB_SOURCE_FILES="lib/linenoise/linenoise.c lib/miniz/miniz.c" - SRC_SOURCE_FILES=" + PXL8_SOURCE_FILES=" src/pxl8.c src/pxl8_ase.c src/pxl8_blit.c @@ -367,32 +400,24 @@ case "$COMMAND" in obj_name=$(basename "$src_file" .c).o obj_file="$OBJECT_DIR/$obj_name" OBJECTS="$OBJECTS $obj_file" - + if [[ "$src_file" -nt "$obj_file" ]]; then NEED_LINK=true - print_info "Compiling: $src_file" - if ! $CC -c $COMPILE_FLAGS "$src_file" -o "$obj_file"; then - print_error "Compilation failed for $src_file" - exit 1 - fi + compile_source_file "$src_file" "$obj_file" "$COMPILE_FLAGS" SOURCES_COMPILED="yes" fi done - for src_file in $SRC_SOURCE_FILES; do + for src_file in $PXL8_SOURCE_FILES; do obj_name=$(basename "$src_file" .c).o obj_file="$OBJECT_DIR/$obj_name" OBJECTS="$OBJECTS $obj_file" - + if [[ "$src_file" -nt "$obj_file" ]] || \ [[ "src/pxl8_types.h" -nt "$obj_file" ]] || \ [[ "src/pxl8_macros.h" -nt "$obj_file" ]]; then NEED_LINK=true - print_info "Compiling: $src_file" - if ! $CC -c $COMPILE_FLAGS "$src_file" -o "$obj_file"; then - print_error "Compilation failed for $src_file" - exit 1 - fi + compile_source_file "$src_file" "$obj_file" "$COMPILE_FLAGS" SOURCES_COMPILED="yes" fi done @@ -413,31 +438,58 @@ case "$COMMAND" in run) "$0" build "$@" || exit 1 - - SCRIPT="" + + CART="" for arg in "$@"; do if [[ "$arg" != "--release" ]]; then - SCRIPT="$arg" + CART="$arg" break fi done - - if [[ -z "$SCRIPT" ]]; then - SCRIPT="src/fnl/demo.fnl" + + if [[ -z "$CART" ]]; then + "$BINDIR/pxl8" + else + "$BINDIR/pxl8" "$CART" fi - - "$BINDIR/pxl8" "$SCRIPT" ;; clean) - if [[ "$1" == "--all" ]] || [[ "$1" == "--deps" ]]; then - print_info "Removing all build artifacts and dependencies" - rm -rf "$BUILDDIR" "$BINDIR" lib + CLEAN_ALL=false + CLEAN_DEPS=false + CLEAN_RELEASE=false + + for arg in "$@"; do + case "$arg" in + --all) CLEAN_ALL=true ;; + --deps) CLEAN_DEPS=true ;; + --release) CLEAN_RELEASE=true ;; + esac + done + + if [[ "$CLEAN_RELEASE" == true ]]; then + BUILD_PATH=".build/release" + BIN_PATH="bin/release" + MODE="release" + else + BUILD_PATH=".build/debug" + BIN_PATH="bin/debug" + MODE="debug" + fi + + if [[ "$CLEAN_ALL" == true ]]; then + print_info "Removing build artifacts and dependencies" + rm -rf "$BUILD_PATH" "$BIN_PATH" lib + print_success "Cleaned all" + elif [[ "$CLEAN_DEPS" == true ]]; then + print_info "Removing dependencies" + rm -rf lib + print_success "Cleaned dependencies" else print_info "Removing build artifacts" - rm -rf "$BUILDDIR" "$BINDIR" + rm -rf "$BUILD_PATH" "$BIN_PATH" + print_success "Cleaned" fi - print_success "Cleaned" ;; update) @@ -450,7 +502,7 @@ case "$COMMAND" in ;; vendor) - vendor_sdl + update_sdl ;; help|--help|-h|"") diff --git a/src/lua/pxl8.lua b/src/lua/pxl8.lua index 30e422b..553403b 100644 --- a/src/lua/pxl8.lua +++ b/src/lua/pxl8.lua @@ -100,8 +100,8 @@ function pxl8.key_pressed(key) return C.pxl8_key_pressed(input, key) end -function pxl8.vfx_copper_bars(bars, time) - local c_bars = ffi.new("pxl8_copper_bar[?]", #bars) +function pxl8.vfx_raster_bars(bars, time) + local c_bars = ffi.new("pxl8_raster_bar[?]", #bars) for i, bar in ipairs(bars) do c_bars[i-1].base_y = bar.base_y or 0 c_bars[i-1].amplitude = bar.amplitude or 10 @@ -111,7 +111,7 @@ function pxl8.vfx_copper_bars(bars, time) c_bars[i-1].color = bar.color or 15 c_bars[i-1].fade_color = bar.fade_color or bar.color or 15 end - C.pxl8_vfx_copper_bars(gfx, c_bars, #bars, time) + C.pxl8_vfx_raster_bars(gfx, c_bars, #bars, time) end function pxl8.vfx_plasma(time, scale1, scale2, palette_offset) diff --git a/src/pxl8.c b/src/pxl8.c index 83d76f2..e721826 100644 --- a/src/pxl8.c +++ b/src/pxl8.c @@ -32,13 +32,13 @@ typedef struct pxl8_repl_state { bool running; } pxl8_repl_state; -typedef struct pxl8_app_state { +typedef struct pxl8_state { + pxl8_cart* cart; pxl8_color_mode color_mode; pxl8_gfx_ctx gfx; lua_State* lua; pxl8_repl_state repl; pxl8_resolution resolution; - pxl8_cart* cart; f32 fps_timer; i32 frame_count; @@ -52,7 +52,7 @@ typedef struct pxl8_app_state { time_t script_mod_time; pxl8_input_state input; -} pxl8_app_state; +} pxl8_state; static void pxl8_repl_completion(const char* buf, linenoiseCompletions* lc) { const char* fennel_keywords[] = { @@ -183,7 +183,7 @@ static pxl8_repl_command* pxl8_repl_pop_command(pxl8_repl_state* repl) { return cmd; } -static void load_script(pxl8_app_state* app) { +static void load_script(pxl8_state* app) { const char* ext = strrchr(app->script_path, '.'); if (ext && strcmp(ext, ".fnl") == 0) { @@ -233,7 +233,7 @@ static time_t get_file_mod_time(const char* path) { } SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) { - static pxl8_app_state app = {0}; + static pxl8_state app = {0}; if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS)) { pxl8_error("SDL_Init failed: %s", SDL_GetError()); @@ -299,36 +299,40 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) { return SDL_APP_FAILURE; } - if (script_arg) { - struct stat st; - bool is_cart = (stat(script_arg, &st) == 0 && S_ISDIR(st.st_mode)) || - strstr(script_arg, ".pxc"); + const char* cart_path = script_arg ? script_arg : "demo"; - if (is_cart) { - char* original_cwd = getcwd(NULL, 0); - app.cart = pxl8_cart_new(); - if (pxl8_cart_load(app.cart, script_arg) == PXL8_OK) { - pxl8_lua_setup_cart_path(app.lua, app.cart->base_path, original_cwd); - pxl8_cart_mount(app.cart); - strcpy(app.script_path, "main.fnl"); - pxl8_info("Loaded cart: %s", app.cart->name); - } else { - pxl8_error("Failed to load cart: %s", script_arg); - return SDL_APP_FAILURE; - } - free(original_cwd); - } else { - strncpy(app.script_path, script_arg, sizeof(app.script_path) - 1); - app.script_path[sizeof(app.script_path) - 1] = '\0'; + struct stat st; + bool is_cart = (stat(cart_path, &st) == 0 && S_ISDIR(st.st_mode)) || + (cart_path && strstr(cart_path, ".pxc")); + + if (is_cart) { + char* original_cwd = getcwd(NULL, 0); + app.cart = calloc(1, sizeof(pxl8_cart)); + if (!app.cart) { + pxl8_error("Failed to allocate memory for cart"); + return false; } - } else { - strcpy(app.script_path, "src/fnl/demo.fnl"); + if (pxl8_cart_load(app.cart, cart_path) == PXL8_OK) { + pxl8_lua_setup_cart_path(app.lua, app.cart->base_path, original_cwd); + pxl8_cart_mount(app.cart); + strcpy(app.script_path, "main.fnl"); + pxl8_info("Loaded cart: %s", app.cart->name); + } else { + pxl8_error("Failed to load cart: %s", cart_path); + return SDL_APP_FAILURE; + } + free(original_cwd); + } else if (script_arg) { + strncpy(app.script_path, script_arg, sizeof(app.script_path) - 1); + app.script_path[sizeof(app.script_path) - 1] = '\0'; } - + pxl8_lua_setup_contexts(app.lua, &app.gfx, &app.input); - - app.script_mod_time = get_file_mod_time(app.script_path); - load_script(&app); + + if (app.script_path[0] != '\0') { + app.script_mod_time = get_file_mod_time(app.script_path); + load_script(&app); + } if (app.repl_mode) { pxl8_repl_init(&app.repl); @@ -353,7 +357,7 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) { } SDL_AppResult SDL_AppIterate(void* appstate) { - pxl8_app_state* app = (pxl8_app_state*)appstate; + pxl8_state* app = (pxl8_state*)appstate; int width, height; SDL_GetWindowSize(app->gfx.window, &width, &height); @@ -465,7 +469,7 @@ SDL_AppResult SDL_AppIterate(void* appstate) { } SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) { - pxl8_app_state* app = (pxl8_app_state*)appstate; + pxl8_state* app = (pxl8_state*)appstate; switch (event->type) { case SDL_EVENT_QUIT: @@ -502,7 +506,7 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) { } void SDL_AppQuit(void* appstate, SDL_AppResult result) { - pxl8_app_state* app = (pxl8_app_state*)appstate; + pxl8_state* app = (pxl8_state*)appstate; (void)result; if (app) { @@ -511,7 +515,8 @@ void SDL_AppQuit(void* appstate, SDL_AppResult result) { pxl8_repl_shutdown(&app->repl); } if (app->cart) { - pxl8_cart_destroy(app->cart); + pxl8_cart_unload(app->cart); + free(app->cart); app->cart = NULL; } pxl8_lua_shutdown(app->lua); diff --git a/src/pxl8_ase.c b/src/pxl8_ase.c index 186230f..359b6e4 100644 --- a/src/pxl8_ase.c +++ b/src/pxl8_ase.c @@ -1,6 +1,6 @@ +#include #include #include -#include #define MINIZ_NO_STDIO #define MINIZ_NO_TIME @@ -89,8 +89,7 @@ static pxl8_result parse_old_palette_chunk(const u8* data, pxl8_ase_palette* pal u8 r = packet_data[0]; u8 g = packet_data[1]; u8 b = packet_data[2]; - - // Store in ABGR format for GPU + palette->colors[color_index] = 0xFF000000 | (b << 16) | (g << 8) | r; packet_data += 3; } @@ -100,16 +99,12 @@ static pxl8_result parse_old_palette_chunk(const u8* data, pxl8_ase_palette* pal } static pxl8_result parse_layer_chunk(const u8* data, pxl8_ase_layer* layer) { - layer->flags = read_u16_le(data); // Offset 0: flags (2 bytes) - layer->layer_type = read_u16_le(data + 2); // Offset 2: layer_type (2 bytes) - layer->child_level = read_u16_le(data + 4); // Offset 4: child_level (2 bytes) - // Offset 6: default_width (2 bytes) - skip - // Offset 8: default_height (2 bytes) - skip - layer->blend_mode = read_u16_le(data + 10); // Offset 10: blend_mode (2 bytes) - layer->opacity = data[12]; // Offset 12: opacity (1 byte) - // Offset 13-15: reserved (3 bytes) - skip - - // Offset 16: name length (2 bytes), then name string at offset 18 + layer->flags = read_u16_le(data); + layer->layer_type = read_u16_le(data + 2); + layer->child_level = read_u16_le(data + 4); + layer->blend_mode = read_u16_le(data + 10); + layer->opacity = data[12]; + u16 name_len = read_u16_le(data + 16); if (name_len > 0) { layer->name = (char*)SDL_malloc(name_len + 1); @@ -124,9 +119,9 @@ static pxl8_result parse_layer_chunk(const u8* data, pxl8_ase_layer* layer) { } static pxl8_result parse_palette_chunk(const u8* data, pxl8_ase_palette* palette) { - palette->entry_count = read_u32_le(data); // Offset 0: entry_count (4 bytes) - palette->first_color = read_u32_le(data + 4); // Offset 4: first_color (4 bytes) - palette->last_color = read_u32_le(data + 8); // Offset 8: last_color (4 bytes) + palette->entry_count = read_u32_le(data); + palette->first_color = read_u32_le(data + 4); + palette->last_color = read_u32_le(data + 8); u32 color_count = palette->entry_count; palette->colors = (u32*)SDL_malloc(color_count * sizeof(u32)); @@ -134,15 +129,14 @@ static pxl8_result parse_palette_chunk(const u8* data, pxl8_ase_palette* palette return PXL8_ERROR_OUT_OF_MEMORY; } - const u8* color_data = data + 20; // Skip palette header (20 bytes) + const u8* color_data = data + 20; for (u32 i = 0; i < color_count; i++) { - u16 flags = read_u16_le(color_data); // Offset 0: flags (2 bytes) - u8 r = color_data[2]; // Offset 2: red (1 byte) - u8 g = color_data[3]; // Offset 3: green (1 byte) - u8 b = color_data[4]; // Offset 4: blue (1 byte) - u8 a = color_data[5]; // Offset 5: alpha (1 byte) - - // Store in ABGR format for GPU + u16 flags = read_u16_le(color_data); + u8 r = color_data[2]; + u8 g = color_data[3]; + u8 b = color_data[4]; + u8 a = color_data[5]; + palette->colors[i] = (a << 24) | (b << 16) | (g << 8) | r; color_data += 6; @@ -160,21 +154,19 @@ static pxl8_result parse_cel_chunk(const u8* data, u32 chunk_size, pxl8_ase_cel* return PXL8_ERROR_ASE_MALFORMED_CHUNK; } - cel->layer_index = read_u16_le(data); // Offset 0: layer_index (2 bytes) - cel->x = read_i16_le(data + 2); // Offset 2: x (2 bytes) - cel->y = read_i16_le(data + 4); // Offset 4: y (2 bytes) - cel->opacity = data[6]; // Offset 6: opacity (1 byte) - cel->cel_type = read_u16_le(data + 7); // Offset 7: cel_type (2 bytes) + cel->layer_index = read_u16_le(data); + cel->x = read_i16_le(data + 2); + cel->y = read_i16_le(data + 4); + cel->opacity = data[6]; + cel->cel_type = read_u16_le(data + 7); if (cel->cel_type == 2) { if (chunk_size < 20) { return PXL8_ERROR_ASE_MALFORMED_CHUNK; } - // Offset 9: Z-Index (2 bytes) - skip - // Offset 11: Reserved (5 bytes) - skip - cel->width = read_u16_le(data + 16); // Offset 16: width (2 bytes) - cel->height = read_u16_le(data + 18); // Offset 18: height (2 bytes) + cel->width = read_u16_le(data + 16); + cel->height = read_u16_le(data + 18); u32 pixel_data_size = cel->width * cel->height; u32 compressed_data_size = chunk_size - 20; @@ -183,8 +175,7 @@ static pxl8_result parse_cel_chunk(const u8* data, u32 chunk_size, pxl8_ase_cel* if (!cel->pixel_data) { return PXL8_ERROR_OUT_OF_MEMORY; } - - // Decompress ZLIB data + mz_ulong dest_len = pixel_data_size; int result = mz_uncompress(cel->pixel_data, &dest_len, data + 20, compressed_data_size); if (result != MZ_OK) { @@ -274,16 +265,17 @@ pxl8_result pxl8_ase_load(const char* filepath, pxl8_ase_file* ase_file) { case PXL8_ASE_CHUNK_OLD_PALETTE: // 0x0004 if (!ase_file->palette.colors) { 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); + 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; case PXL8_ASE_CHUNK_LAYER: { // 0x2004 - // Need to allocate or reallocate layers array - ase_file->layers = (pxl8_ase_layer*)SDL_realloc(ase_file->layers, - (ase_file->layer_count + 1) * sizeof(pxl8_ase_layer)); + ase_file->layers = + (pxl8_ase_layer*)SDL_realloc(ase_file->layers, + (ase_file->layer_count + 1) * sizeof(pxl8_ase_layer)); if (!ase_file->layers) { result = PXL8_ERROR_OUT_OF_MEMORY; break; @@ -315,15 +307,12 @@ pxl8_result pxl8_ase_load(const char* filepath, pxl8_ase_file* ase_file) { u32 src_offset = y * cel.width; u32 dst_offset = (y + cel.y) * frame->width + cel.x; if (dst_offset + copy_width <= pixel_count) { - // Composite layers: only copy non-transparent pixels - // Check if palette color is transparent (#00000000) for (u32 x = 0; x < copy_width; x++) { u8 src_pixel = cel.pixel_data[src_offset + x]; bool is_transparent = false; if (src_pixel < ase_file->palette.entry_count && ase_file->palette.colors) { u32 color = ase_file->palette.colors[src_pixel]; - // Check if color is fully transparent (alpha = 0) is_transparent = ((color >> 24) & 0xFF) == 0; } @@ -331,9 +320,9 @@ pxl8_result pxl8_ase_load(const char* filepath, pxl8_ase_file* ase_file) { frame->pixels[dst_offset + x] = src_pixel; } } - // Debug: check first few pixels of each row 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]); + pxl8_debug("Row %d: cel[%d]=%d, frame[%d]=%d", + y, src_offset, cel.pixel_data[src_offset], dst_offset, frame->pixels[dst_offset]); } } } @@ -347,7 +336,8 @@ pxl8_result pxl8_ase_load(const char* filepath, pxl8_ase_file* ase_file) { SDL_free(ase_file->palette.colors); } 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); + 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; default: @@ -355,10 +345,12 @@ pxl8_result pxl8_ase_load(const char* filepath, pxl8_ase_file* ase_file) { } if (result != PXL8_OK) break; + chunk_data += chunk_header.chunk_size; } if (result != PXL8_OK) break; + frame_data += frame_header.frame_bytes; } diff --git a/src/pxl8_ase.h b/src/pxl8_ase.h index 2ff3460..efaf9ed 100644 --- a/src/pxl8_ase.h +++ b/src/pxl8_ase.h @@ -1,5 +1,4 @@ #pragma once - #include "pxl8_types.h" #define PXL8_ASE_MAGIC 0xA5E0 diff --git a/src/pxl8_blit.c b/src/pxl8_blit.c index 23e6682..f56ee50 100644 --- a/src/pxl8_blit.c +++ b/src/pxl8_blit.c @@ -1,33 +1,6 @@ #include "pxl8_blit.h" #include "pxl8_simd.h" -void pxl8_blit_simd_indexed(u8* fb, u32 fb_width, const u8* sprite, u32 atlas_width, - i32 x, i32 y, u32 w, u32 h) { - u8* dest_base = fb + y * fb_width + x; - const u8* src_base = sprite; - - for (u32 row = 0; row < h; row++) { - u8* dest_row = dest_base + row * fb_width; - const u8* src_row = src_base + row * atlas_width; - - u32 col = 0; - for (; col + PXL8_SIMD_WIDTH_U8 <= w; col += PXL8_SIMD_WIDTH_U8) { - pxl8_simd_vec src_vec = pxl8_simd_load_u8(src_row + col); - pxl8_simd_vec dest_vec = pxl8_simd_load_u8(dest_row + col); - pxl8_simd_vec zero = pxl8_simd_zero_u8(); - pxl8_simd_vec mask = pxl8_simd_cmpeq_u8(src_vec, zero); - pxl8_simd_vec result = pxl8_simd_blendv_u8(src_vec, dest_vec, mask); - pxl8_simd_store_u8(dest_row + col, result); - } - - for (; col < w; col++) { - if (src_row[col] != 0) { - dest_row[col] = src_row[col]; - } - } - } -} - void pxl8_blit_simd_hicolor(u32* fb, u32 fb_width, const u32* sprite, u32 atlas_width, i32 x, i32 y, u32 w, u32 h) { u32* dest_base = fb + y * fb_width + x; @@ -56,3 +29,30 @@ void pxl8_blit_simd_hicolor(u32* fb, u32 fb_width, const u32* sprite, u32 atlas_ } } } + +void pxl8_blit_simd_indexed(u8* fb, u32 fb_width, const u8* sprite, u32 atlas_width, + i32 x, i32 y, u32 w, u32 h) { + u8* dest_base = fb + y * fb_width + x; + const u8* src_base = sprite; + + for (u32 row = 0; row < h; row++) { + u8* dest_row = dest_base + row * fb_width; + const u8* src_row = src_base + row * atlas_width; + + u32 col = 0; + for (; col + PXL8_SIMD_WIDTH_U8 <= w; col += PXL8_SIMD_WIDTH_U8) { + pxl8_simd_vec src_vec = pxl8_simd_load_u8(src_row + col); + pxl8_simd_vec dest_vec = pxl8_simd_load_u8(dest_row + col); + pxl8_simd_vec zero = pxl8_simd_zero_u8(); + pxl8_simd_vec mask = pxl8_simd_cmpeq_u8(src_vec, zero); + pxl8_simd_vec result = pxl8_simd_blendv_u8(src_vec, dest_vec, mask); + pxl8_simd_store_u8(dest_row + col, result); + } + + for (; col < w; col++) { + if (src_row[col] != 0) { + dest_row[col] = src_row[col]; + } + } + } +} diff --git a/src/pxl8_blit.h b/src/pxl8_blit.h index 39c8004..26576a8 100644 --- a/src/pxl8_blit.h +++ b/src/pxl8_blit.h @@ -7,13 +7,21 @@ static inline bool pxl8_is_simd_aligned(u32 w) { return w >= PXL8_SIMD_WIDTH_U8 && (w % PXL8_SIMD_WIDTH_U8 == 0); } -void pxl8_blit_simd_indexed( - u8* fb, u32 fb_width, - const u8* sprite, u32 atlas_width, - i32 x, i32 y, u32 w, u32 h -); +#ifdef __cplusplus +extern "C" { +#endif + void pxl8_blit_simd_hicolor( u32* fb, u32 fb_width, const u32* sprite, u32 atlas_width, i32 x, i32 y, u32 w, u32 h ); +void pxl8_blit_simd_indexed( + u8* fb, u32 fb_width, + const u8* sprite, u32 atlas_width, + i32 x, i32 y, u32 w, u32 h +); + +#ifdef __cplusplus +} +#endif diff --git a/src/pxl8_cart.c b/src/pxl8_cart.c index 9501e2f..c7bf437 100644 --- a/src/pxl8_cart.c +++ b/src/pxl8_cart.c @@ -8,17 +8,38 @@ #include #include "../lib/miniz/miniz.h" -static pxl8_cart* s_current_cart = NULL; -static char* s_original_cwd = NULL; +static pxl8_cart* __pxl8_current_cart = NULL; +static char* __pxl8_original_cwd = NULL; -static bool is_directory(const char* path) { - struct stat st; - return stat(path, &st) == 0 && S_ISDIR(st.st_mode); -} +static void pxl8_add_file_recursive(mz_zip_archive* zip, const char* dir_path, const char* prefix) { + DIR* dir = opendir(dir_path); + if (!dir) return; -static bool is_pxc_file(const char* path) { - size_t len = strlen(path); - return len > 4 && strcmp(path + len - 4, ".pxc") == 0; + struct dirent* entry; + while ((entry = readdir(dir)) != NULL) { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue; + + char full_path[1024]; + char zip_path[1024]; + snprintf(full_path, sizeof(full_path), "%s/%s", dir_path, entry->d_name); + snprintf(zip_path, sizeof(zip_path), "%s%s", prefix, entry->d_name); + + struct stat st; + if (stat(full_path, &st) == 0) { + if (S_ISDIR(st.st_mode)) { + char new_prefix[1025]; + snprintf(new_prefix, sizeof(new_prefix), "%s/", zip_path); + pxl8_add_file_recursive(zip, full_path, new_prefix); + } else { + pxl8_info("Adding: %s", zip_path); + if (!mz_zip_writer_add_file(zip, zip_path, full_path, NULL, 0, MZ_BEST_COMPRESSION)) { + pxl8_warn("Failed to add file: %s", zip_path); + } + } + } + } + + closedir(dir); } static char* get_cart_name(const char* path) { @@ -39,19 +60,18 @@ static char* get_cart_name(const char* path) { return name; } -pxl8_cart* pxl8_cart_new(void) { - pxl8_cart* cart = calloc(1, sizeof(pxl8_cart)); - if (!cart) { - pxl8_error("Failed to allocate cart"); - return NULL; - } - return cart; +static bool is_directory(const char* path) { + struct stat st; + return stat(path, &st) == 0 && S_ISDIR(st.st_mode); } -void pxl8_cart_destroy(pxl8_cart* cart) { - if (!cart) return; - pxl8_cart_unload(cart); - free(cart); +static bool is_pxc_file(const char* path) { + size_t len = strlen(path); + return len > 4 && strcmp(path + len - 4, ".pxc") == 0; +} + +pxl8_cart* pxl8_cart_current(void) { + return __pxl8_current_cart; } pxl8_result pxl8_cart_load(pxl8_cart* cart, const char* path) { @@ -128,7 +148,7 @@ pxl8_result pxl8_cart_load(pxl8_cart* cart, const char* path) { mz_zip_archive_file_stat file_stat; if (!mz_zip_reader_file_stat(&zip, i, &file_stat)) continue; - char extract_path[512]; + char extract_path[1024]; snprintf(extract_path, sizeof(extract_path), "%s/%s", temp_dir, file_stat.m_filename); if (file_stat.m_is_directory) { @@ -171,6 +191,8 @@ void pxl8_cart_unload(pxl8_cart* cart) { system(cmd); } + char* cart_name = cart->name ? strdup(cart->name) : NULL; + if (cart->base_path) { free(cart->base_path); cart->base_path = NULL; @@ -189,26 +211,31 @@ void pxl8_cart_unload(pxl8_cart* cart) { cart->archive_size = 0; cart->is_folder = false; cart->is_mounted = false; + + if (cart_name) { + pxl8_info("Unloaded cart: %s", cart_name); + free(cart_name); + } } 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 (s_current_cart) { - pxl8_cart_unmount(s_current_cart); + if (__pxl8_current_cart) { + pxl8_cart_unmount(__pxl8_current_cart); } - s_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(s_original_cwd); - s_original_cwd = NULL; + free(__pxl8_original_cwd); + __pxl8_original_cwd = NULL; return PXL8_ERROR_FILE_NOT_FOUND; } cart->is_mounted = true; - s_current_cart = cart; + __pxl8_current_cart = cart; pxl8_info("Mounted cart: %s", cart->name); return PXL8_OK; @@ -217,15 +244,15 @@ pxl8_result pxl8_cart_mount(pxl8_cart* cart) { void pxl8_cart_unmount(pxl8_cart* cart) { if (!cart || !cart->is_mounted) return; - if (s_original_cwd) { - chdir(s_original_cwd); - free(s_original_cwd); - s_original_cwd = NULL; + if (__pxl8_original_cwd) { + chdir(__pxl8_original_cwd); + free(__pxl8_original_cwd); + __pxl8_original_cwd = NULL; } cart->is_mounted = false; - if (s_current_cart == cart) { - s_current_cart = NULL; + if (__pxl8_current_cart == cart) { + __pxl8_current_cart = NULL; } pxl8_info("Unmounted cart: %s", cart->name); @@ -252,37 +279,6 @@ bool pxl8_cart_file_exists(const pxl8_cart* cart, const char* path) { return exists; } -static void pxl8_add_files_recursive(mz_zip_archive* zip, const char* dir_path, const char* prefix) { - DIR* dir = opendir(dir_path); - if (!dir) return; - - struct dirent* entry; - while ((entry = readdir(dir)) != NULL) { - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue; - - char full_path[512]; - char zip_path[512]; - snprintf(full_path, sizeof(full_path), "%s/%s", dir_path, entry->d_name); - snprintf(zip_path, sizeof(zip_path), "%s%s", prefix, entry->d_name); - - struct stat st; - if (stat(full_path, &st) == 0) { - if (S_ISDIR(st.st_mode)) { - char new_prefix[512]; - snprintf(new_prefix, sizeof(new_prefix), "%s/", zip_path); - pxl8_add_files_recursive(zip, full_path, new_prefix); - } else { - pxl8_info("Adding: %s", zip_path); - if (!mz_zip_writer_add_file(zip, zip_path, full_path, NULL, 0, MZ_BEST_COMPRESSION)) { - pxl8_warn("Failed to add file: %s", zip_path); - } - } - } - } - - closedir(dir); -} - pxl8_result pxl8_cart_pack(const char* folder_path, const char* output_path) { if (!folder_path || !output_path) return PXL8_ERROR_NULL_POINTER; @@ -308,7 +304,7 @@ pxl8_result pxl8_cart_pack(const char* folder_path, const char* output_path) { } pxl8_info("Packing cart: %s -> %s", folder_path, output_path); - pxl8_add_files_recursive(&zip, folder_path, ""); + pxl8_add_file_recursive(&zip, folder_path, ""); if (!mz_zip_writer_finalize_archive(&zip)) { pxl8_error("Failed to finalize archive"); @@ -320,7 +316,3 @@ pxl8_result pxl8_cart_pack(const char* folder_path, const char* output_path) { pxl8_info("Cart packed successfully!"); return PXL8_OK; } - -pxl8_cart* pxl8_cart_current(void) { - return s_current_cart; -} diff --git a/src/pxl8_cart.h b/src/pxl8_cart.h index 25e2880..52b101c 100644 --- a/src/pxl8_cart.h +++ b/src/pxl8_cart.h @@ -3,33 +3,26 @@ #include "pxl8_types.h" typedef struct pxl8_cart { - char* base_path; - char* name; void* archive_data; size_t archive_size; + char* base_path; bool is_folder; bool is_mounted; + char* name; } pxl8_cart; #ifdef __cplusplus extern "C" { #endif -pxl8_result pxl8_cart_load(pxl8_cart* cart, const char* path); -void pxl8_cart_unload(pxl8_cart* cart); - -pxl8_result pxl8_cart_mount(pxl8_cart* cart); -void pxl8_cart_unmount(pxl8_cart* cart); - -char* pxl8_cart_resolve_path(const pxl8_cart* cart, const char* relative_path); -bool pxl8_cart_file_exists(const pxl8_cart* cart, const char* path); - -pxl8_cart* pxl8_cart_new(void); -void pxl8_cart_destroy(pxl8_cart* cart); - -pxl8_result pxl8_cart_pack(const char* folder_path, const char* output_path); - pxl8_cart* pxl8_cart_current(void); +bool pxl8_cart_file_exists(const pxl8_cart* cart, const char* path); +pxl8_result pxl8_cart_load(pxl8_cart* cart, const char* path); +pxl8_result pxl8_cart_mount(pxl8_cart* cart); +pxl8_result pxl8_cart_pack(const char* folder_path, const char* output_path); +char* pxl8_cart_resolve_path(const pxl8_cart* cart, const char* relative_path); +void pxl8_cart_unload(pxl8_cart* cart); +void pxl8_cart_unmount(pxl8_cart* cart); #ifdef __cplusplus } diff --git a/src/pxl8_font.c b/src/pxl8_font.c index d3b7ab2..5ae5466 100644 --- a/src/pxl8_font.c +++ b/src/pxl8_font.c @@ -2,18 +2,6 @@ #include #include -const pxl8_glyph* pxl8_font_find_glyph(const pxl8_font* font, u32 codepoint) { - if (!font || !font->glyphs) return NULL; - - for (u32 i = 0; i < font->glyph_count; i++) { - if (font->glyphs[i].codepoint == codepoint) { - return &font->glyphs[i]; - } - } - - return NULL; -} - pxl8_result pxl8_font_create_atlas(const pxl8_font* font, u8** atlas_data, i32* atlas_width, i32* atlas_height) { if (!font || !atlas_data || !atlas_width || !atlas_height) { return PXL8_ERROR_NULL_POINTER; @@ -58,3 +46,15 @@ pxl8_result pxl8_font_create_atlas(const pxl8_font* font, u8** atlas_data, i32* return PXL8_OK; } + +const pxl8_glyph* pxl8_font_find_glyph(const pxl8_font* font, u32 codepoint) { + if (!font || !font->glyphs) return NULL; + + for (u32 i = 0; i < font->glyph_count; i++) { + if (font->glyphs[i].codepoint == codepoint) { + return &font->glyphs[i]; + } + } + + return NULL; +} diff --git a/src/pxl8_font.h b/src/pxl8_font.h index dec8429..82d1c20 100644 --- a/src/pxl8_font.h +++ b/src/pxl8_font.h @@ -122,8 +122,8 @@ static const pxl8_font pxl8_default_font = { extern "C" { #endif -const pxl8_glyph* pxl8_font_find_glyph(const pxl8_font* font, u32 codepoint); pxl8_result pxl8_font_create_atlas(const pxl8_font* font, u8** atlas_data, i32* atlas_width, i32* atlas_height); +const pxl8_glyph* pxl8_font_find_glyph(const pxl8_font* font, u32 codepoint); #ifdef __cplusplus } diff --git a/src/pxl8_gfx.c b/src/pxl8_gfx.c index 084a154..ab0cf62 100644 --- a/src/pxl8_gfx.c +++ b/src/pxl8_gfx.c @@ -153,6 +153,7 @@ void pxl8_gfx_shutdown(pxl8_gfx_ctx* ctx) { ctx->initialized = false; } +// resource loading pxl8_result pxl8_gfx_init_atlas(pxl8_gfx_ctx* ctx, u32 width, u32 height) { if (!ctx || !ctx->initialized) return PXL8_ERROR_INVALID_ARGUMENT; @@ -328,6 +329,7 @@ pxl8_result pxl8_gfx_load_font_atlas(pxl8_gfx_ctx* ctx) { return PXL8_OK; } +// rendering pipeline void pxl8_gfx_upload_framebuffer(pxl8_gfx_ctx* ctx) { if (!ctx || !ctx->initialized || !ctx->framebuffer_texture) return; @@ -405,6 +407,7 @@ void pxl8_gfx_project(pxl8_gfx_ctx* ctx, f32 left, f32 right, f32 top, f32 botto (void)ctx; (void)left; (void)right; (void)top; (void)bottom; } +// drawing primitives void pxl8_clr(pxl8_gfx_ctx* ctx, u32 color) { if (!ctx || !ctx->framebuffer) return; @@ -590,6 +593,7 @@ void pxl8_sprite(pxl8_gfx_ctx* ctx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h) { } } +// palette effects void pxl8_gfx_cycle_palette(pxl8_gfx_ctx* ctx, u8 start, u8 count, i32 step) { if (!ctx || !ctx->palette || count == 0) return; diff --git a/src/pxl8_gfx.h b/src/pxl8_gfx.h index bc3b2cd..5b33ab2 100644 --- a/src/pxl8_gfx.h +++ b/src/pxl8_gfx.h @@ -17,32 +17,30 @@ typedef struct pxl8_gfx_ctx { SDL_Texture* framebuffer_texture; SDL_Texture* sprite_atlas_texture; SDL_Window* window; - + u8* framebuffer; + bool initialized; u32* palette; u32 palette_size; - + i32 framebuffer_width; i32 framebuffer_height; pxl8_color_mode color_mode; - + + u8* atlas; + bool atlas_dirty; + pxl8_atlas_entry* atlas_entries; + u32 atlas_entries_len; + u32 atlas_entries_cap; + u32 sprite_atlas_width; u32 sprite_atlas_height; u32 sprite_frame_width; u32 sprite_frame_height; u32 sprite_frames_per_row; - - pxl8_atlas_entry* atlas_entries; - u32 atlas_entries_len; - u32 atlas_entries_cap; - - u8* atlas; - bool atlas_dirty; - + i32 viewport_x, viewport_y; i32 viewport_width, viewport_height; - - bool initialized; } pxl8_gfx_ctx; typedef enum pxl8_blend_mode { @@ -52,6 +50,14 @@ typedef enum pxl8_blend_mode { PXL8_BLEND_MULTIPLY } pxl8_blend_mode; +typedef struct pxl8_mode7_params { + f32 horizon; + f32 scale_x, scale_y; + f32 rotation; + f32 offset_x, offset_y; + bool active; +} pxl8_mode7_params; + typedef struct pxl8_palette_cycle { u8 start_index; u8 end_index; @@ -65,21 +71,17 @@ typedef struct pxl8_scanline_effect { bool active; } pxl8_scanline_effect; -typedef struct pxl8_mode7_params { - f32 horizon; - f32 scale_x, scale_y; - f32 rotation; - f32 offset_x, offset_y; - bool active; -} pxl8_mode7_params; - typedef struct pxl8_effects { pxl8_palette_cycle palette_cycles[8]; pxl8_scanline_effect scanline_effects[4]; - f32 time; } pxl8_effects; +#ifdef __cplusplus +extern "C" { +#endif + +void pxl8_gfx_get_resolution_dimensions(pxl8_resolution resolution, i32* width, i32* height); pxl8_result pxl8_gfx_init( pxl8_gfx_ctx* ctx, pxl8_color_mode mode, @@ -88,36 +90,54 @@ pxl8_result pxl8_gfx_init( i32 window_width, i32 window_height ); - -void pxl8_gfx_shutdown(pxl8_gfx_ctx* ctx); - pxl8_result pxl8_gfx_init_atlas(pxl8_gfx_ctx* ctx, u32 width, u32 height); -pxl8_result pxl8_gfx_load_sprite(pxl8_gfx_ctx* ctx, const char* path); -pxl8_result pxl8_gfx_load_palette(pxl8_gfx_ctx* ctx, const char* path); pxl8_result pxl8_gfx_load_font_atlas(pxl8_gfx_ctx* ctx); - -void pxl8_gfx_get_resolution_dimensions(pxl8_resolution resolution, i32* width, i32* height); -void pxl8_gfx_upload_framebuffer(pxl8_gfx_ctx* ctx); -void pxl8_gfx_upload_atlas(pxl8_gfx_ctx* ctx); +pxl8_result pxl8_gfx_load_palette(pxl8_gfx_ctx* ctx, const char* path); +pxl8_result pxl8_gfx_load_sprite(pxl8_gfx_ctx* ctx, const char* path); void pxl8_gfx_present(pxl8_gfx_ctx* ctx); - -void pxl8_gfx_viewport(pxl8_gfx_ctx* ctx, i32 x, i32 y, i32 width, i32 height); void pxl8_gfx_project(pxl8_gfx_ctx* ctx, f32 left, f32 right, f32 top, f32 bottom); +void pxl8_gfx_shutdown(pxl8_gfx_ctx* ctx); +void pxl8_gfx_upload_atlas(pxl8_gfx_ctx* ctx); +void pxl8_gfx_upload_framebuffer(pxl8_gfx_ctx* ctx); +void pxl8_gfx_viewport(pxl8_gfx_ctx* ctx, i32 x, i32 y, i32 width, i32 height); -void pxl8_clr(pxl8_gfx_ctx* ctx, u32 color); -void pxl8_pixel(pxl8_gfx_ctx* ctx, i32 x, i32 y, u32 color); -u32 pxl8_get_pixel(pxl8_gfx_ctx* ctx, i32 x, i32 y); -void pxl8_line(pxl8_gfx_ctx* ctx, i32 x0, i32 y0, i32 x1, i32 y1, u32 color); -void pxl8_rect(pxl8_gfx_ctx* ctx, i32 x, i32 y, i32 w, i32 h, u32 color); -void pxl8_rect_fill(pxl8_gfx_ctx* ctx, i32 x, i32 y, i32 w, i32 h, u32 color); -void pxl8_circle(pxl8_gfx_ctx* ctx, i32 cx, i32 cy, i32 radius, u32 color); -void pxl8_circle_fill(pxl8_gfx_ctx* ctx, i32 cx, i32 cy, i32 radius, u32 color); -void pxl8_text(pxl8_gfx_ctx* ctx, const char* text, i32 x, i32 y, u32 color); -void pxl8_sprite(pxl8_gfx_ctx* ctx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h); - -void pxl8_gfx_color_ramp(pxl8_gfx_ctx* ctx, u8 start, u8 count, u32 from_color, u32 to_color); +void pxl8_gfx_color_ramp( + pxl8_gfx_ctx* ctx, + u8 start, + u8 count, + u32 from_color, + u32 to_color +); void pxl8_gfx_cycle_palette(pxl8_gfx_ctx* ctx, u8 start, u8 count, i32 step); -void pxl8_gfx_fade_palette(pxl8_gfx_ctx* ctx, u8 start, u8 count, f32 amount, u32 target_color); -void pxl8_gfx_interpolate_palettes(pxl8_gfx_ctx* ctx, u32* palette1, u32* palette2, u8 start, u8 count, f32 t); +void pxl8_gfx_fade_palette( + pxl8_gfx_ctx* ctx, + u8 start, + u8 count, + f32 amount, + u32 target_color +); +void pxl8_gfx_interpolate_palettes( + pxl8_gfx_ctx* ctx, + u32* palette1, + u32* palette2, + u8 start, + u8 count, + f32 t +); void pxl8_gfx_process_effects(pxl8_gfx_ctx* ctx, pxl8_effects* effects, f32 dt); void pxl8_gfx_swap_palette(pxl8_gfx_ctx* ctx, u8 start, u8 count, u32* new_colors); + +void pxl8_circle(pxl8_gfx_ctx* ctx, i32 cx, i32 cy, i32 radius, u32 color); +void pxl8_circle_fill(pxl8_gfx_ctx* ctx, i32 cx, i32 cy, i32 radius, u32 color); +void pxl8_clr(pxl8_gfx_ctx* ctx, u32 color); +u32 pxl8_get_pixel(pxl8_gfx_ctx* ctx, i32 x, i32 y); +void pxl8_line(pxl8_gfx_ctx* ctx, i32 x0, i32 y0, i32 x1, i32 y1, u32 color); +void pxl8_pixel(pxl8_gfx_ctx* ctx, i32 x, i32 y, u32 color); +void pxl8_rect(pxl8_gfx_ctx* ctx, i32 x, i32 y, i32 w, i32 h, u32 color); +void pxl8_rect_fill(pxl8_gfx_ctx* ctx, i32 x, i32 y, i32 w, i32 h, u32 color); +void pxl8_sprite(pxl8_gfx_ctx* ctx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h); +void pxl8_text(pxl8_gfx_ctx* ctx, const char* text, i32 x, i32 y, u32 color); + +#ifdef __cplusplus +} +#endif diff --git a/src/pxl8_io.h b/src/pxl8_io.h index fe9557f..c15045c 100644 --- a/src/pxl8_io.h +++ b/src/pxl8_io.h @@ -11,17 +11,15 @@ extern "C" { #endif -pxl8_result pxl8_io_read_file(const char* path, char** content, size_t* size); -pxl8_result pxl8_io_write_file(const char* path, const char* content, size_t size); -pxl8_result pxl8_io_read_binary_file(const char* path, u8** data, size_t* size); -pxl8_result pxl8_io_write_binary_file(const char* path, const u8* data, size_t size); - -bool pxl8_io_file_exists(const char* path); -f64 pxl8_io_get_file_modified_time(const char* path); pxl8_result pxl8_io_create_directory(const char* path); - -void pxl8_io_free_file_content(char* content); +bool pxl8_io_file_exists(const char* path); void pxl8_io_free_binary_data(u8* data); +void pxl8_io_free_file_content(char* content); +f64 pxl8_io_get_file_modified_time(const char* path); +pxl8_result pxl8_io_read_binary_file(const char* path, u8** data, size_t* size); +pxl8_result pxl8_io_read_file(const char* path, char** content, size_t* size); +pxl8_result pxl8_io_write_binary_file(const char* path, const u8* data, size_t size); +pxl8_result pxl8_io_write_file(const char* path, const char* content, size_t size); bool pxl8_key_down(const pxl8_input_state* input, i32 key); bool pxl8_key_pressed(const pxl8_input_state* input, i32 key); diff --git a/src/pxl8_lua.c b/src/pxl8_lua.c index fe87043..8a18ac2 100644 --- a/src/pxl8_lua.c +++ b/src/pxl8_lua.c @@ -104,9 +104,9 @@ static const char* pxl8_ffi_cdefs = " float phase;\n" " unsigned int color;\n" " unsigned int fade_color;\n" -"} pxl8_copper_bar;\n" +"} pxl8_raster_bar;\n" "\n" -"void pxl8_vfx_copper_bars(pxl8_gfx_ctx* ctx, pxl8_copper_bar* bars, u32 bar_count, f32 time);\n" +"void pxl8_vfx_raster_bars(pxl8_gfx_ctx* ctx, pxl8_raster_bar* bars, u32 bar_count, f32 time);\n" "void pxl8_vfx_plasma(pxl8_gfx_ctx* ctx, f32 time, f32 scale1, f32 scale2, u8 palette_offset);\n" "void pxl8_vfx_rotozoom(pxl8_gfx_ctx* ctx, f32 angle, f32 zoom, i32 cx, i32 cy);\n" "void pxl8_vfx_tunnel(pxl8_gfx_ctx* ctx, f32 time, f32 speed, f32 twist);\n" @@ -327,4 +327,3 @@ pxl8_result pxl8_lua_eval_fennel(lua_State* lua_state, const char* code) { lua_remove(lua_state, -2); return PXL8_OK; } - diff --git a/src/pxl8_lua.h b/src/pxl8_lua.h index d7d872a..90a3667 100644 --- a/src/pxl8_lua.h +++ b/src/pxl8_lua.h @@ -13,12 +13,13 @@ extern "C" { pxl8_result pxl8_lua_init(lua_State** lua_state); void pxl8_lua_shutdown(lua_State* lua_state); +pxl8_result pxl8_lua_setup_contexts(lua_State* lua_state, pxl8_gfx_ctx* gfx_ctx, pxl8_input_state* input); +void pxl8_lua_setup_cart_path(lua_State* lua_state, const char* cart_path, const char* original_cwd); + pxl8_result pxl8_lua_eval_fennel(lua_State* lua_state, const char* code); pxl8_result pxl8_lua_run_fennel_file(lua_State* lua_state, const char* filename); pxl8_result pxl8_lua_run_file(lua_State* lua_state, const char* filename); pxl8_result pxl8_lua_run_string(lua_State* lua_state, const char* code); -pxl8_result pxl8_lua_setup_contexts(lua_State* lua_state, pxl8_gfx_ctx* gfx_ctx, pxl8_input_state* input); -void pxl8_lua_setup_cart_path(lua_State* lua_state, const char* cart_path, const char* original_cwd); #ifdef __cplusplus } diff --git a/src/pxl8_tilemap.c b/src/pxl8_tilemap.c index f3ecf1e..ced7c65 100644 --- a/src/pxl8_tilemap.c +++ b/src/pxl8_tilemap.c @@ -192,8 +192,8 @@ void pxl8_tilemap_render_layer(const pxl8_tilemap* tilemap, pxl8_gfx_ctx* gfx, u u32 chunk_left = pxl8_max(0, view_left >> 4); u32 chunk_top = pxl8_max(0, view_top >> 4); - u32 chunk_right = pxl8_min((tilemap->width + 15) >> 4, (view_right >> 4) + 1); - u32 chunk_bottom = pxl8_min((tilemap->height + 15) >> 4, (view_bottom >> 4) + 1); + u32 chunk_right = pxl8_min((tilemap->width + 15) >> 4, (u32)((view_right >> 4) + 1)); + u32 chunk_bottom = pxl8_min((tilemap->height + 15) >> 4, (u32)((view_bottom >> 4) + 1)); for (u32 cy = chunk_top; cy < chunk_bottom; cy++) { for (u32 cx = chunk_left; cx < chunk_right; cx++) { @@ -277,29 +277,6 @@ bool pxl8_tilemap_check_collision(const pxl8_tilemap* tilemap, i32 x, i32 y, i32 return false; } -pxl8_tilemap* pxl8_tilemap_new(u32 width, u32 height, u32 tile_size) { - pxl8_tilemap* tilemap = calloc(1, sizeof(pxl8_tilemap)); - if (!tilemap) { - pxl8_error("Failed to allocate tilemap"); - return NULL; - } - - pxl8_result result = pxl8_tilemap_init(tilemap, width, height, tile_size); - if (result != PXL8_OK) { - pxl8_error("Failed to initialize tilemap"); - free(tilemap); - return NULL; - } - - return tilemap; -} - -void pxl8_tilemap_destroy(pxl8_tilemap* tilemap) { - if (!tilemap) return; - pxl8_tilemap_free(tilemap); - free(tilemap); -} - u16 pxl8_tilemap_get_tile_id(const pxl8_tilemap* tilemap, u32 layer, u32 x, u32 y) { if (!tilemap) return 0; pxl8_tile tile = pxl8_tilemap_get_tile(tilemap, layer, x, y); diff --git a/src/pxl8_tilemap.h b/src/pxl8_tilemap.h index d612cc5..2bc08b5 100644 --- a/src/pxl8_tilemap.h +++ b/src/pxl8_tilemap.h @@ -108,35 +108,56 @@ typedef struct pxl8_tilemap_view { extern "C" { #endif -pxl8_result pxl8_tilemap_init(pxl8_tilemap* tilemap, u32 width, u32 height, u32 tile_size); -pxl8_result pxl8_tilemap_set_tilesheet(pxl8_tilemap* tilemap, pxl8_tilesheet* tilesheet); +bool pxl8_tilemap_check_collision(const pxl8_tilemap* tilemap, i32 x, i32 y, i32 w, i32 h); +void pxl8_tilemap_compress(pxl8_tilemap* tilemap); void pxl8_tilemap_free(pxl8_tilemap* tilemap); - -pxl8_result pxl8_tilemap_set_tile(pxl8_tilemap* tilemap, u32 layer, u32 x, u32 y, u16 tile_id, u8 flags); +u32 pxl8_tilemap_get_memory_usage(const pxl8_tilemap* tilemap); pxl8_tile pxl8_tilemap_get_tile(const pxl8_tilemap* tilemap, u32 layer, u32 x, u32 y); - -void pxl8_tilemap_set_camera(pxl8_tilemap* tilemap, i32 x, i32 y); -void pxl8_tilemap_get_view(const pxl8_tilemap* tilemap, const pxl8_gfx_ctx* gfx, pxl8_tilemap_view* view); - +u16 pxl8_tilemap_get_tile_id(const pxl8_tilemap* tilemap, u32 layer, u32 x, u32 y); +void pxl8_tilemap_get_view( + const pxl8_tilemap* tilemap, + const pxl8_gfx_ctx* gfx, + pxl8_tilemap_view* view +); +pxl8_result pxl8_tilemap_init(pxl8_tilemap* tilemap, u32 width, u32 height, u32 tile_size); +bool pxl8_tilemap_is_solid(const pxl8_tilemap* tilemap, u32 x, u32 y); void pxl8_tilemap_render(const pxl8_tilemap* tilemap, pxl8_gfx_ctx* gfx); void pxl8_tilemap_render_layer(const pxl8_tilemap* tilemap, pxl8_gfx_ctx* gfx, u32 layer); -void pxl8_tilemap_render_tile(const pxl8_tilemap* tilemap, pxl8_gfx_ctx* gfx, u16 tile_id, i32 x, i32 y, u8 flags); - -bool pxl8_tilemap_is_solid(const pxl8_tilemap* tilemap, u32 x, u32 y); -bool pxl8_tilemap_check_collision(const pxl8_tilemap* tilemap, i32 x, i32 y, i32 w, i32 h); - -pxl8_tilemap* pxl8_tilemap_new(u32 width, u32 height, u32 tile_size); -void pxl8_tilemap_destroy(pxl8_tilemap* tilemap); -u16 pxl8_tilemap_get_tile_id(const pxl8_tilemap* tilemap, u32 layer, u32 x, u32 y); - +void pxl8_tilemap_render_tile( + const pxl8_tilemap* tilemap, + pxl8_gfx_ctx* gfx, + u16 tile_id, + i32 x, + i32 y, + u8 flags +); +void pxl8_tilemap_set_camera(pxl8_tilemap* tilemap, i32 x, i32 y); +pxl8_result pxl8_tilemap_set_tile( + pxl8_tilemap* tilemap, + u32 layer, + u32 x, + u32 y, + u16 tile_id, + u8 flags +); +pxl8_result pxl8_tilemap_set_tile_auto( + pxl8_tilemap* tilemap, + u32 layer, + u32 x, + u32 y, + u16 base_tile_id, + u8 flags +); +pxl8_result pxl8_tilemap_set_tilesheet(pxl8_tilemap* tilemap, pxl8_tilesheet* tilesheet); void pxl8_tilemap_update(pxl8_tilemap* tilemap, f32 delta_time); - -pxl8_result pxl8_tilemap_set_tile_auto(pxl8_tilemap* tilemap, u32 layer, u32 x, u32 y, - u16 base_tile_id, u8 flags); -void pxl8_tilemap_update_autotiles(pxl8_tilemap* tilemap, u32 layer, u32 x, u32 y, u32 w, u32 h); - -u32 pxl8_tilemap_get_memory_usage(const pxl8_tilemap* tilemap); -void pxl8_tilemap_compress(pxl8_tilemap* tilemap); +void pxl8_tilemap_update_autotiles( + pxl8_tilemap* tilemap, + u32 layer, + u32 x, + u32 y, + u32 w, + u32 h +); #ifdef __cplusplus } diff --git a/src/pxl8_tilesheet.c b/src/pxl8_tilesheet.c index a9ecb46..1bbc686 100644 --- a/src/pxl8_tilesheet.c +++ b/src/pxl8_tilesheet.c @@ -5,16 +5,6 @@ #include #include -pxl8_result pxl8_tilesheet_init(pxl8_tilesheet* tilesheet, u32 tile_size) { - if (!tilesheet) return PXL8_ERROR_NULL_POINTER; - - memset(tilesheet, 0, sizeof(pxl8_tilesheet)); - tilesheet->tile_size = tile_size ? tile_size : PXL8_TILE_SIZE; - tilesheet->ref_count = 1; - - return PXL8_OK; -} - void pxl8_tilesheet_free(pxl8_tilesheet* tilesheet) { if (!tilesheet) return; @@ -186,28 +176,6 @@ bool pxl8_tilesheet_is_tile_valid(const pxl8_tilesheet* tilesheet, u16 tile_id) return tilesheet->tile_valid ? tilesheet->tile_valid[tile_id] : true; } -pxl8_tilesheet* pxl8_tilesheet_new(u32 tile_size) { - pxl8_tilesheet* tilesheet = calloc(1, sizeof(pxl8_tilesheet)); - if (!tilesheet) { - pxl8_error("Failed to allocate tilesheet"); - return NULL; - } - - pxl8_result result = pxl8_tilesheet_init(tilesheet, tile_size); - if (result != PXL8_OK) { - pxl8_error("Failed to initialize tilesheet"); - free(tilesheet); - return NULL; - } - - return tilesheet; -} - -void pxl8_tilesheet_destroy(pxl8_tilesheet* tilesheet) { - if (!tilesheet) return; - pxl8_tilesheet_unref(tilesheet); -} - void pxl8_tilesheet_ref(pxl8_tilesheet* tilesheet) { if (!tilesheet) return; tilesheet->ref_count++; @@ -218,7 +186,6 @@ void pxl8_tilesheet_unref(pxl8_tilesheet* tilesheet) { if (--tilesheet->ref_count == 0) { pxl8_tilesheet_free(tilesheet); - free(tilesheet); } } diff --git a/src/pxl8_tilesheet.h b/src/pxl8_tilesheet.h index b7432df..fd94bf2 100644 --- a/src/pxl8_tilesheet.h +++ b/src/pxl8_tilesheet.h @@ -31,33 +31,44 @@ typedef struct pxl8_tilesheet { extern "C" { #endif -pxl8_result pxl8_tilesheet_init(pxl8_tilesheet* tilesheet, u32 tile_size); -pxl8_result pxl8_tilesheet_load(pxl8_tilesheet* tilesheet, const char* filepath, pxl8_gfx_ctx* gfx); -void pxl8_tilesheet_free(pxl8_tilesheet* tilesheet); - -void pxl8_tilesheet_render_tile(const pxl8_tilesheet* tilesheet, pxl8_gfx_ctx* gfx, - u16 tile_id, i32 x, i32 y, u8 flags); - -bool pxl8_tilesheet_is_tile_valid(const pxl8_tilesheet* tilesheet, u16 tile_id); - -pxl8_tilesheet* pxl8_tilesheet_new(u32 tile_size); -void pxl8_tilesheet_destroy(pxl8_tilesheet* tilesheet); - -void pxl8_tilesheet_ref(pxl8_tilesheet* tilesheet); -void pxl8_tilesheet_unref(pxl8_tilesheet* tilesheet); - -pxl8_result pxl8_tilesheet_add_animation(pxl8_tilesheet* tilesheet, u16 base_tile_id, - const u16* frames, u16 frame_count, f32 frame_duration); -void pxl8_tilesheet_update_animations(pxl8_tilesheet* tilesheet, f32 delta_time); -u16 pxl8_tilesheet_get_animated_frame(const pxl8_tilesheet* tilesheet, u16 tile_id); - -void pxl8_tilesheet_set_tile_property(pxl8_tilesheet* tilesheet, u16 tile_id, - const pxl8_tile_properties* props); -const pxl8_tile_properties* pxl8_tilesheet_get_tile_property(const pxl8_tilesheet* tilesheet, u16 tile_id); - -pxl8_result pxl8_tilesheet_add_autotile_rule(pxl8_tilesheet* tilesheet, u16 base_tile_id, - u8 neighbor_mask, u16 result_tile_id); +pxl8_result pxl8_tilesheet_add_animation( + pxl8_tilesheet* tilesheet, + u16 base_tile_id, + const u16* frames, + u16 frame_count, + f32 frame_duration +); +pxl8_result pxl8_tilesheet_add_autotile_rule( + pxl8_tilesheet* tilesheet, + u16 base_tile_id, + u8 neighbor_mask, + u16 result_tile_id +); u16 pxl8_tilesheet_apply_autotile(const pxl8_tilesheet* tilesheet, u16 base_tile_id, u8 neighbors); +void pxl8_tilesheet_free(pxl8_tilesheet* tilesheet); +u16 pxl8_tilesheet_get_animated_frame(const pxl8_tilesheet* tilesheet, u16 tile_id); +const pxl8_tile_properties* pxl8_tilesheet_get_tile_property( + const pxl8_tilesheet* tilesheet, + u16 tile_id +); +bool pxl8_tilesheet_is_tile_valid(const pxl8_tilesheet* tilesheet, u16 tile_id); +pxl8_result pxl8_tilesheet_load(pxl8_tilesheet* tilesheet, const char* filepath, pxl8_gfx_ctx* gfx); +void pxl8_tilesheet_ref(pxl8_tilesheet* tilesheet); +void pxl8_tilesheet_render_tile( + const pxl8_tilesheet* tilesheet, + pxl8_gfx_ctx* gfx, + u16 tile_id, + i32 x, + i32 y, + u8 flags +); +void pxl8_tilesheet_set_tile_property( + pxl8_tilesheet* tilesheet, + u16 tile_id, + const pxl8_tile_properties* props +); +void pxl8_tilesheet_unref(pxl8_tilesheet* tilesheet); +void pxl8_tilesheet_update_animations(pxl8_tilesheet* tilesheet, f32 delta_time); #ifdef __cplusplus } diff --git a/src/pxl8_types.h b/src/pxl8_types.h index b1a8edb..60d4022 100644 --- a/src/pxl8_types.h +++ b/src/pxl8_types.h @@ -1,8 +1,8 @@ #pragma once +#include #include #include -#include typedef uint8_t u8; typedef uint16_t u16; @@ -21,13 +21,28 @@ typedef __int128_t i128; #endif typedef enum pxl8_color_mode { - PXL8_COLOR_MODE_FAMI, // NES-style 4 colors per sprite, 64 color palette - PXL8_COLOR_MODE_MEGA, // Genesis-style 64 colors, 512 color palette - PXL8_COLOR_MODE_GBA, // GBA-style 64 colors, 32K color palette - PXL8_COLOR_MODE_SUPERFAMI, // SNES-style 256 colors, 32K color palette - PXL8_COLOR_MODE_HICOLOR, // 16-bit high color mode + PXL8_COLOR_MODE_FAMI, + PXL8_COLOR_MODE_MEGA, + PXL8_COLOR_MODE_GBA, + PXL8_COLOR_MODE_SUPERFAMI, + PXL8_COLOR_MODE_HICOLOR, } pxl8_color_mode; +typedef enum pxl8_resolution { + PXL8_RESOLUTION_240x160, + PXL8_RESOLUTION_320x180, + PXL8_RESOLUTION_320x240, + PXL8_RESOLUTION_640x360, + PXL8_RESOLUTION_640x480, + PXL8_RESOLUTION_800x600, + PXL8_RESOLUTION_960x540, +} pxl8_resolution; + +typedef struct pxl8_input_state { + bool keys[256]; + bool keys_pressed[256]; +} pxl8_input_state; + typedef struct pxl8_point { i32 x, y; } pxl8_point; @@ -55,18 +70,3 @@ typedef enum pxl8_result { PXL8_ERROR_ASE_TRUNCATED_FILE, PXL8_ERROR_ASE_MALFORMED_CHUNK, } pxl8_result; - -typedef enum pxl8_resolution { - PXL8_RESOLUTION_240x160, - PXL8_RESOLUTION_320x180, - PXL8_RESOLUTION_320x240, - PXL8_RESOLUTION_640x360, - PXL8_RESOLUTION_640x480, - PXL8_RESOLUTION_800x600, - PXL8_RESOLUTION_960x540, -} pxl8_resolution; - -typedef struct pxl8_input_state { - bool keys[256]; - bool keys_pressed[256]; -} pxl8_input_state; diff --git a/src/pxl8_vfx.c b/src/pxl8_vfx.c index 119bf06..2b598a6 100644 --- a/src/pxl8_vfx.c +++ b/src/pxl8_vfx.c @@ -4,31 +4,6 @@ #include #include -void pxl8_vfx_copper_bars(pxl8_gfx_ctx* ctx, pxl8_copper_bar* bars, u32 bar_count, f32 time) { - if (!ctx || !bars) return; - - for (u32 i = 0; i < bar_count; i++) { - pxl8_copper_bar* bar = &bars[i]; - f32 y = bar->base_y + bar->amplitude * sinf(time * bar->speed + bar->phase); - i32 y_int = (i32)y; - - for (i32 dy = 0; dy <= bar->height; dy++) { - f32 position = (f32)dy / (f32)bar->height; - f32 gradient = 1.0f - 2.0f * fabsf(position - 0.5f); - - u8 color_idx; - if (gradient > 0.8f) { - color_idx = bar->fade_color; - } else { - u8 range = bar->fade_color - bar->color; - color_idx = bar->color + (u8)(gradient * range); - } - - pxl8_rect_fill(ctx, 0, y_int + dy, ctx->framebuffer_width, 1, color_idx); - } - } -} - void pxl8_vfx_plasma(pxl8_gfx_ctx* ctx, f32 time, f32 scale1, f32 scale2, u8 palette_offset) { if (!ctx || !ctx->framebuffer) return; @@ -50,6 +25,31 @@ void pxl8_vfx_plasma(pxl8_gfx_ctx* ctx, f32 time, f32 scale1, f32 scale2, u8 pal } } +void pxl8_vfx_raster_bars(pxl8_gfx_ctx* ctx, pxl8_raster_bar* bars, u32 bar_count, f32 time) { + if (!ctx || !bars) return; + + for (u32 i = 0; i < bar_count; i++) { + pxl8_raster_bar* bar = &bars[i]; + f32 y = bar->base_y + bar->amplitude * sinf(time * bar->speed + bar->phase); + i32 y_int = (i32)y; + + for (i32 dy = 0; dy <= bar->height; dy++) { + f32 position = (f32)dy / (f32)bar->height; + f32 gradient = 1.0f - 2.0f * fabsf(position - 0.5f); + + u8 color_idx; + if (gradient > 0.8f) { + color_idx = bar->fade_color; + } else { + u8 range = bar->fade_color - bar->color; + color_idx = bar->color + (u8)(gradient * range); + } + + pxl8_rect_fill(ctx, 0, y_int + dy, ctx->framebuffer_width, 1, color_idx); + } + } +} + void pxl8_vfx_rotozoom(pxl8_gfx_ctx* ctx, f32 angle, f32 zoom, i32 cx, i32 cy) { if (!ctx || !ctx->framebuffer) return; @@ -141,15 +141,16 @@ void pxl8_vfx_water_ripple(pxl8_gfx_ctx* ctx, f32* height_map, i32 drop_x, i32 d void pxl8_vfx_particles_clear(pxl8_particle_system* sys) { if (!sys || !sys->particles) return; - + for (u32 i = 0; i < sys->max_count; i++) { sys->particles[i].life = 0; sys->particles[i].flags = 0; } sys->alive_count = 0; + sys->spawn_timer = 0; } -void pxl8_vfx_particles_destroy(pxl8_particle_system* sys) { +void pxl8_vfx_particles_free(pxl8_particle_system* sys) { if (!sys) return; if (sys->particles) { @@ -272,13 +273,14 @@ void pxl8_vfx_particles_update(pxl8_particle_system* sys, f32 dt) { void pxl8_vfx_explosion(pxl8_particle_system* sys, i32 x, i32 y, u32 color, f32 force) { if (!sys) return; - + sys->x = x; sys->y = y; sys->spread_x = sys->spread_y = 2.0f; sys->gravity_x = 0; sys->gravity_y = 200.0f; sys->drag = 0.95f; + sys->update_fn = NULL; for (u32 i = 0; i < 50 && i < sys->max_count; i++) { pxl8_particle* p = &sys->particles[i]; @@ -353,7 +355,7 @@ static void rain_spawn(pxl8_particle* p, void* userdata) { void pxl8_vfx_rain(pxl8_particle_system* sys, i32 width, f32 wind) { if (!sys) return; - + sys->x = width / 2.0f; sys->y = -10; sys->spread_x = width; @@ -363,6 +365,7 @@ void pxl8_vfx_rain(pxl8_particle_system* sys, i32 width, f32 wind) { sys->drag = 1.0f; sys->spawn_rate = 100.0f; sys->spawn_fn = rain_spawn; + sys->update_fn = NULL; } static void smoke_spawn(pxl8_particle* p, void* userdata) { @@ -388,12 +391,13 @@ void pxl8_vfx_smoke(pxl8_particle_system* sys, i32 x, i32 y, u8 color) { sys->drag = 0.96f; sys->spawn_rate = 20.0f; sys->spawn_fn = smoke_spawn; + sys->update_fn = NULL; sys->userdata = (void*)(uintptr_t)color; } static void snow_spawn(pxl8_particle* p, void* userdata) { (void)userdata; - p->start_color = 15 + (rand() % 2); + p->start_color = 8 + (rand() % 3); p->end_color = 10; p->color = p->start_color; p->max_life = 4.0f; @@ -403,16 +407,17 @@ static void snow_spawn(pxl8_particle* p, void* userdata) { void pxl8_vfx_snow(pxl8_particle_system* sys, i32 width, f32 wind) { if (!sys) return; - + sys->x = width / 2.0f; sys->y = -10; sys->spread_x = width; sys->spread_y = 0; sys->gravity_x = wind; sys->gravity_y = 30.0f; - sys->drag = 0.99f; + sys->drag = 1.0f; sys->spawn_rate = 30.0f; sys->spawn_fn = snow_spawn; + sys->update_fn = NULL; } static void sparks_spawn(pxl8_particle* p, void* userdata) { @@ -440,6 +445,7 @@ void pxl8_vfx_sparks(pxl8_particle_system* sys, i32 x, i32 y, u32 color) { sys->drag = 0.97f; sys->spawn_rate = 40.0f; sys->spawn_fn = sparks_spawn; + sys->update_fn = NULL; sys->userdata = (void*)(uintptr_t)color; } @@ -450,6 +456,7 @@ void pxl8_vfx_starfield(pxl8_particle_system* sys, f32 speed, f32 spread) { sys->gravity_x = sys->gravity_y = 0; sys->drag = 1.0f; sys->spawn_rate = 0; + sys->update_fn = NULL; for (u32 i = 0; i < sys->max_count; i++) { pxl8_particle* p = &sys->particles[i]; diff --git a/src/pxl8_vfx.h b/src/pxl8_vfx.h index 05aaa39..ab870a4 100644 --- a/src/pxl8_vfx.h +++ b/src/pxl8_vfx.h @@ -3,16 +3,6 @@ #include "pxl8_types.h" #include "pxl8_gfx.h" -typedef struct pxl8_copper_bar { - f32 base_y; - f32 amplitude; - i32 height; - f32 speed; - f32 phase; - u32 color; - u32 fade_color; -} pxl8_copper_bar; - typedef struct pxl8_particle { f32 x, y, z; f32 vx, vy, vz; @@ -46,15 +36,19 @@ typedef struct pxl8_particle_system { void* userdata; } pxl8_particle_system; -void pxl8_vfx_copper_bars(pxl8_gfx_ctx* ctx, pxl8_copper_bar* bars, u32 bar_count, f32 time); -void pxl8_vfx_plasma(pxl8_gfx_ctx* ctx, f32 time, f32 scale1, f32 scale2, u8 palette_offset); -void pxl8_vfx_rotozoom(pxl8_gfx_ctx* ctx, f32 angle, f32 zoom, i32 cx, i32 cy); -void pxl8_vfx_tunnel(pxl8_gfx_ctx* ctx, f32 time, f32 speed, f32 twist); -void pxl8_vfx_water_ripple(pxl8_gfx_ctx* ctx, f32* height_map, i32 drop_x, i32 drop_y, f32 damping); +typedef struct pxl8_raster_bar { + f32 base_y; + f32 amplitude; + i32 height; + f32 speed; + f32 phase; + u32 color; + u32 fade_color; +} pxl8_raster_bar; void pxl8_vfx_particles_clear(pxl8_particle_system* sys); -void pxl8_vfx_particles_destroy(pxl8_particle_system* sys); void pxl8_vfx_particles_emit(pxl8_particle_system* sys, u32 count); +void pxl8_vfx_particles_free(pxl8_particle_system* sys); void pxl8_vfx_particles_init(pxl8_particle_system* sys, u32 max_count); void pxl8_vfx_particles_render(pxl8_particle_system* sys, pxl8_gfx_ctx* ctx); void pxl8_vfx_particles_update(pxl8_particle_system* sys, f32 dt); @@ -66,3 +60,9 @@ void pxl8_vfx_smoke(pxl8_particle_system* sys, i32 x, i32 y, u8 color); void pxl8_vfx_snow(pxl8_particle_system* sys, i32 width, f32 wind); void pxl8_vfx_sparks(pxl8_particle_system* sys, i32 x, i32 y, u32 color); void pxl8_vfx_starfield(pxl8_particle_system* sys, f32 speed, f32 spread); + +void pxl8_vfx_plasma(pxl8_gfx_ctx* ctx, f32 time, f32 scale1, f32 scale2, u8 palette_offset); +void pxl8_vfx_raster_bars(pxl8_gfx_ctx* ctx, pxl8_raster_bar* bars, u32 bar_count, f32 time); +void pxl8_vfx_rotozoom(pxl8_gfx_ctx* ctx, f32 angle, f32 zoom, i32 cx, i32 cy); +void pxl8_vfx_tunnel(pxl8_gfx_ctx* ctx, f32 time, f32 speed, f32 twist); +void pxl8_vfx_water_ripple(pxl8_gfx_ctx* ctx, f32* height_map, i32 drop_x, i32 drop_y, f32 damping);