This commit is contained in:
asrael 2025-09-28 13:10:29 -05:00
parent 9f96626ea7
commit 6a02b24ae6
29 changed files with 653 additions and 583 deletions

View file

@ -3,14 +3,20 @@
(var time 0) (var time 0)
(var current-effect 1) (var current-effect 1)
(var particles nil) (var particles nil)
(var starfield-init false)
(var fire-init false) (var fire-init false)
(var rain-init false) (var rain-init false)
(var snow-init false) (var snow-init false)
(var logo-x 256)
(var logo-y 148)
(var logo-dx 100)
(var logo-dy 80)
(var logo-sprite nil)
(global init (fn [] (global init (fn []
(pxl8.load_palette "palettes/gruvbox.ase") (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] (global update (fn [dt]
(set time (+ time dt)) (set time (+ time dt))
@ -20,27 +26,53 @@
(when (pxl8.key_pressed "2") (when (pxl8.key_pressed "2")
(set current-effect 2)) (set current-effect 2))
(when (pxl8.key_pressed "3") (when (pxl8.key_pressed "3")
(set current-effect 3) (set current-effect 3))
(set fire-init false))
(when (pxl8.key_pressed "4") (when (pxl8.key_pressed "4")
(set current-effect 4)) (set current-effect 4))
(when (pxl8.key_pressed "5") (when (pxl8.key_pressed "5")
(set current-effect 5) (set current-effect 5)
(set rain-init false)) (set fire-init false))
(when (pxl8.key_pressed "6") (when (pxl8.key_pressed "6")
(set current-effect 6) (set current-effect 6)
(set rain-init false))
(when (pxl8.key_pressed "7")
(set current-effect 7)
(set snow-init false)) (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 (when particles
(pxl8.particles_update particles dt)))) (pxl8.particles_update particles dt))))
(global draw (fn [] (global draw (fn []
(match current-effect (match current-effect
1 (pxl8.vfx_plasma time 0.10 0.04 0) 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_tunnel time 2.0 0.25) 2 (pxl8.vfx_plasma time 0.10 0.04 0)
3 (do 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) (pxl8.clr 0)
(when particles (when particles
(when (not fire-init) (when (not fire-init)
@ -50,15 +82,7 @@
(pxl8.particles_render particles)) (pxl8.particles_render particles))
(pxl8.text "Fire Effect Test" 200 10 15)) (pxl8.text "Fire Effect Test" 200 10 15))
4 (do 6 (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
(pxl8.clr 0) (pxl8.clr 0)
(when particles (when particles
(when (not rain-init) (when (not rain-init)
@ -68,7 +92,7 @@
(pxl8.particles_render particles)) (pxl8.particles_render particles))
(pxl8.text "Rain" 200 10 15)) (pxl8.text "Rain" 200 10 15))
6 (do 7 (do
(pxl8.clr 0) (pxl8.clr 0)
(when particles (when particles
(when (not snow-init) (when (not snow-init)

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

220
pxl8.sh
View file

@ -39,16 +39,16 @@ if [[ "$(uname)" == "Linux" ]]; then
CFLAGS="$CFLAGS -D_GNU_SOURCE" CFLAGS="$CFLAGS -D_GNU_SOURCE"
fi fi
print_info() { compile_source_file() {
echo -e "${BLUE}${BOLD}info:${NC} $1" local src_file="$1"
} local obj_file="$2"
local compile_flags="$3"
print_success() { print_info "Compiling: $src_file"
echo -e "${GREEN}${BOLD}${NC} $1" if ! $CC -c $compile_flags "$src_file" -o "$obj_file"; then
} print_error "Compilation failed for $src_file"
exit 1
print_error() { fi
echo -e "${RED}${BOLD}error:${NC} $1" >&2
} }
detect_simd() { detect_simd() {
@ -72,6 +72,22 @@ detect_simd() {
echo "$simd_flags" 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() { print_usage() {
echo -e "${BOLD}pxl8${NC} - framework build tool" echo -e "${BOLD}pxl8${NC} - framework build tool"
echo echo
@ -80,15 +96,18 @@ print_usage() {
echo echo
echo -e "${BOLD}COMMANDS:${NC}" echo -e "${BOLD}COMMANDS:${NC}"
echo " build Build pxl8" echo " build Build pxl8"
echo " run Build and run pxl8 [script.fnl|script.lua]"
echo " clean Remove build artifacts" echo " clean Remove build artifacts"
echo " run Build and run pxl8 (optional: cart.pxc or folder)"
echo
echo " update Download/update all dependencies" 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 " help Show this help message"
echo echo
echo -e "${BOLD}OPTIONS:${NC}" echo -e "${BOLD}OPTIONS:${NC}"
echo " --all Remove all artifacts including dependencies when cleaning" echo " --all Clean both build artifacts and dependencies"
echo " --release Build/run in release mode" echo " --deps Clean only dependencies"
echo " --release Build/run/clean in release mode (default: debug)"
} }
COMMAND="$1" COMMAND="$1"
@ -115,6 +134,34 @@ else
BINDIR="$BINDIR/debug" BINDIR="$BINDIR/debug"
fi 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() { setup_sdl3() {
if [[ -d "lib/SDL/build" ]]; then if [[ -d "lib/SDL/build" ]]; then
SDL3_CFLAGS="-Ilib/SDL/include" SDL3_CFLAGS="-Ilib/SDL/include"
@ -129,42 +176,45 @@ setup_sdl3() {
if [[ -z "$SDL3_LIBS" ]]; then if [[ -z "$SDL3_LIBS" ]]; then
case "$(uname)" in case "$(uname)" in
Darwin) Darwin)
if [[ -f "/usr/local/include/SDL3/SDL.h" ]]; then
SDL3_CFLAGS="-I/usr/local/include/SDL3" SDL3_CFLAGS="-I/usr/local/include/SDL3"
SDL3_LIBS="-L/usr/local/lib -lSDL3" SDL3_LIBS="-L/usr/local/lib -lSDL3"
fi
;; ;;
Linux) Linux)
if [[ -f "/usr/include/SDL3/SDL.h" ]]; then
SDL3_CFLAGS="-I/usr/include/SDL3" SDL3_CFLAGS="-I/usr/include/SDL3"
SDL3_LIBS="-lSDL3" SDL3_LIBS="-lSDL3"
fi
;; ;;
MINGW*|MSYS*|CYGWIN*) MINGW*|MSYS*|CYGWIN*)
if [[ -f "/mingw64/include/SDL3/SDL.h" ]]; then
SDL3_CFLAGS="-I/mingw64/include/SDL3" SDL3_CFLAGS="-I/mingw64/include/SDL3"
SDL3_LIBS="-lSDL3" SDL3_LIBS="-lSDL3"
fi
;; ;;
esac esac
fi fi
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" print_info "Using system SDL3"
fi fi
fi
CFLAGS="$CFLAGS $SDL3_CFLAGS" CFLAGS="$CFLAGS $SDL3_CFLAGS"
LIBS="$LIBS $SDL3_LIBS $SDL3_RPATH" 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() { update_fennel() {
print_info "Fetching Fennel" print_info "Fetching Fennel"
mkdir -p lib/fennel
local version="1.5.3" local version="1.5.3"
@ -181,6 +231,18 @@ update_fennel() {
fi 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() { update_luajit() {
print_info "Fetching LuaJIT" print_info "Fetching LuaJIT"
@ -198,25 +260,8 @@ update_luajit() {
print_success "Updated LuaJIT (${version})" 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() { update_microui() {
print_info "Fetching 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 && \ 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 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() { update_miniz() {
print_info "Fetching miniz" print_info "Fetching miniz"
mkdir -p lib/miniz
local version="3.0.2" 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 unzip -qjo /tmp/miniz.zip miniz.c miniz.h -d lib/miniz/ 2>/dev/null
rm -f /tmp/miniz.zip rm -f /tmp/miniz.zip
@ -249,7 +293,7 @@ update_miniz() {
fi fi
} }
vendor_sdl() { update_sdl() {
print_info "Fetching SDL3" print_info "Fetching SDL3"
if [[ -d "lib/SDL/.git" ]]; then if [[ -d "lib/SDL/.git" ]]; then
@ -263,18 +307,6 @@ vendor_sdl() {
print_success "Updated SDL3" 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 case "$COMMAND" in
build) build)
mkdir -p "$BUILDDIR" mkdir -p "$BUILDDIR"
@ -287,8 +319,9 @@ case "$COMMAND" in
[[ ! -f "lib/fennel/fennel.lua" ]]; then [[ ! -f "lib/fennel/fennel.lua" ]]; then
print_info "Missing dependencies, fetching..." print_info "Missing dependencies, fetching..."
update_linenoise make_lib_dirs
update_fennel update_fennel
update_linenoise
update_luajit update_luajit
update_microui update_microui
update_miniz update_miniz
@ -341,7 +374,7 @@ case "$COMMAND" in
LIB_SOURCE_FILES="lib/linenoise/linenoise.c lib/miniz/miniz.c" LIB_SOURCE_FILES="lib/linenoise/linenoise.c lib/miniz/miniz.c"
SRC_SOURCE_FILES=" PXL8_SOURCE_FILES="
src/pxl8.c src/pxl8.c
src/pxl8_ase.c src/pxl8_ase.c
src/pxl8_blit.c src/pxl8_blit.c
@ -370,16 +403,12 @@ case "$COMMAND" in
if [[ "$src_file" -nt "$obj_file" ]]; then if [[ "$src_file" -nt "$obj_file" ]]; then
NEED_LINK=true NEED_LINK=true
print_info "Compiling: $src_file" compile_source_file "$src_file" "$obj_file" "$COMPILE_FLAGS"
if ! $CC -c $COMPILE_FLAGS "$src_file" -o "$obj_file"; then
print_error "Compilation failed for $src_file"
exit 1
fi
SOURCES_COMPILED="yes" SOURCES_COMPILED="yes"
fi fi
done 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_name=$(basename "$src_file" .c).o
obj_file="$OBJECT_DIR/$obj_name" obj_file="$OBJECT_DIR/$obj_name"
OBJECTS="$OBJECTS $obj_file" OBJECTS="$OBJECTS $obj_file"
@ -388,11 +417,7 @@ case "$COMMAND" in
[[ "src/pxl8_types.h" -nt "$obj_file" ]] || \ [[ "src/pxl8_types.h" -nt "$obj_file" ]] || \
[[ "src/pxl8_macros.h" -nt "$obj_file" ]]; then [[ "src/pxl8_macros.h" -nt "$obj_file" ]]; then
NEED_LINK=true NEED_LINK=true
print_info "Compiling: $src_file" compile_source_file "$src_file" "$obj_file" "$COMPILE_FLAGS"
if ! $CC -c $COMPILE_FLAGS "$src_file" -o "$obj_file"; then
print_error "Compilation failed for $src_file"
exit 1
fi
SOURCES_COMPILED="yes" SOURCES_COMPILED="yes"
fi fi
done done
@ -414,30 +439,57 @@ case "$COMMAND" in
run) run)
"$0" build "$@" || exit 1 "$0" build "$@" || exit 1
SCRIPT="" CART=""
for arg in "$@"; do for arg in "$@"; do
if [[ "$arg" != "--release" ]]; then if [[ "$arg" != "--release" ]]; then
SCRIPT="$arg" CART="$arg"
break break
fi fi
done done
if [[ -z "$SCRIPT" ]]; then if [[ -z "$CART" ]]; then
SCRIPT="src/fnl/demo.fnl" "$BINDIR/pxl8"
else
"$BINDIR/pxl8" "$CART"
fi fi
"$BINDIR/pxl8" "$SCRIPT"
;; ;;
clean) clean)
if [[ "$1" == "--all" ]] || [[ "$1" == "--deps" ]]; then CLEAN_ALL=false
print_info "Removing all build artifacts and dependencies" CLEAN_DEPS=false
rm -rf "$BUILDDIR" "$BINDIR" lib 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 else
print_info "Removing build artifacts" print_info "Removing build artifacts"
rm -rf "$BUILDDIR" "$BINDIR" rm -rf "$BUILD_PATH" "$BIN_PATH"
fi
print_success "Cleaned" print_success "Cleaned"
fi
;; ;;
update) update)
@ -450,7 +502,7 @@ case "$COMMAND" in
;; ;;
vendor) vendor)
vendor_sdl update_sdl
;; ;;
help|--help|-h|"") help|--help|-h|"")

View file

@ -100,8 +100,8 @@ function pxl8.key_pressed(key)
return C.pxl8_key_pressed(input, key) return C.pxl8_key_pressed(input, key)
end end
function pxl8.vfx_copper_bars(bars, time) function pxl8.vfx_raster_bars(bars, time)
local c_bars = ffi.new("pxl8_copper_bar[?]", #bars) local c_bars = ffi.new("pxl8_raster_bar[?]", #bars)
for i, bar in ipairs(bars) do for i, bar in ipairs(bars) do
c_bars[i-1].base_y = bar.base_y or 0 c_bars[i-1].base_y = bar.base_y or 0
c_bars[i-1].amplitude = bar.amplitude or 10 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].color = bar.color or 15
c_bars[i-1].fade_color = bar.fade_color or bar.color or 15 c_bars[i-1].fade_color = bar.fade_color or bar.color or 15
end end
C.pxl8_vfx_copper_bars(gfx, c_bars, #bars, time) C.pxl8_vfx_raster_bars(gfx, c_bars, #bars, time)
end end
function pxl8.vfx_plasma(time, scale1, scale2, palette_offset) function pxl8.vfx_plasma(time, scale1, scale2, palette_offset)

View file

@ -32,13 +32,13 @@ typedef struct pxl8_repl_state {
bool running; bool running;
} pxl8_repl_state; } pxl8_repl_state;
typedef struct pxl8_app_state { typedef struct pxl8_state {
pxl8_cart* cart;
pxl8_color_mode color_mode; pxl8_color_mode color_mode;
pxl8_gfx_ctx gfx; pxl8_gfx_ctx gfx;
lua_State* lua; lua_State* lua;
pxl8_repl_state repl; pxl8_repl_state repl;
pxl8_resolution resolution; pxl8_resolution resolution;
pxl8_cart* cart;
f32 fps_timer; f32 fps_timer;
i32 frame_count; i32 frame_count;
@ -52,7 +52,7 @@ typedef struct pxl8_app_state {
time_t script_mod_time; time_t script_mod_time;
pxl8_input_state input; pxl8_input_state input;
} pxl8_app_state; } pxl8_state;
static void pxl8_repl_completion(const char* buf, linenoiseCompletions* lc) { static void pxl8_repl_completion(const char* buf, linenoiseCompletions* lc) {
const char* fennel_keywords[] = { const char* fennel_keywords[] = {
@ -183,7 +183,7 @@ static pxl8_repl_command* pxl8_repl_pop_command(pxl8_repl_state* repl) {
return cmd; return cmd;
} }
static void load_script(pxl8_app_state* app) { static void load_script(pxl8_state* app) {
const char* ext = strrchr(app->script_path, '.'); const char* ext = strrchr(app->script_path, '.');
if (ext && strcmp(ext, ".fnl") == 0) { 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[]) { 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)) { if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS)) {
pxl8_error("SDL_Init failed: %s", SDL_GetError()); 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; return SDL_APP_FAILURE;
} }
if (script_arg) { const char* cart_path = script_arg ? script_arg : "demo";
struct stat st; struct stat st;
bool is_cart = (stat(script_arg, &st) == 0 && S_ISDIR(st.st_mode)) || bool is_cart = (stat(cart_path, &st) == 0 && S_ISDIR(st.st_mode)) ||
strstr(script_arg, ".pxc"); (cart_path && strstr(cart_path, ".pxc"));
if (is_cart) { if (is_cart) {
char* original_cwd = getcwd(NULL, 0); char* original_cwd = getcwd(NULL, 0);
app.cart = pxl8_cart_new(); app.cart = calloc(1, sizeof(pxl8_cart));
if (pxl8_cart_load(app.cart, script_arg) == PXL8_OK) { if (!app.cart) {
pxl8_error("Failed to allocate memory for cart");
return false;
}
if (pxl8_cart_load(app.cart, cart_path) == PXL8_OK) {
pxl8_lua_setup_cart_path(app.lua, app.cart->base_path, original_cwd); pxl8_lua_setup_cart_path(app.lua, app.cart->base_path, original_cwd);
pxl8_cart_mount(app.cart); pxl8_cart_mount(app.cart);
strcpy(app.script_path, "main.fnl"); strcpy(app.script_path, "main.fnl");
pxl8_info("Loaded cart: %s", app.cart->name); pxl8_info("Loaded cart: %s", app.cart->name);
} else { } else {
pxl8_error("Failed to load cart: %s", script_arg); pxl8_error("Failed to load cart: %s", cart_path);
return SDL_APP_FAILURE; return SDL_APP_FAILURE;
} }
free(original_cwd); free(original_cwd);
} else { } else if (script_arg) {
strncpy(app.script_path, script_arg, sizeof(app.script_path) - 1); strncpy(app.script_path, script_arg, sizeof(app.script_path) - 1);
app.script_path[sizeof(app.script_path) - 1] = '\0'; app.script_path[sizeof(app.script_path) - 1] = '\0';
} }
} else {
strcpy(app.script_path, "src/fnl/demo.fnl");
}
pxl8_lua_setup_contexts(app.lua, &app.gfx, &app.input); pxl8_lua_setup_contexts(app.lua, &app.gfx, &app.input);
if (app.script_path[0] != '\0') {
app.script_mod_time = get_file_mod_time(app.script_path); app.script_mod_time = get_file_mod_time(app.script_path);
load_script(&app); load_script(&app);
}
if (app.repl_mode) { if (app.repl_mode) {
pxl8_repl_init(&app.repl); 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) { SDL_AppResult SDL_AppIterate(void* appstate) {
pxl8_app_state* app = (pxl8_app_state*)appstate; pxl8_state* app = (pxl8_state*)appstate;
int width, height; int width, height;
SDL_GetWindowSize(app->gfx.window, &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) { 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) { switch (event->type) {
case SDL_EVENT_QUIT: 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) { void SDL_AppQuit(void* appstate, SDL_AppResult result) {
pxl8_app_state* app = (pxl8_app_state*)appstate; pxl8_state* app = (pxl8_state*)appstate;
(void)result; (void)result;
if (app) { if (app) {
@ -511,7 +515,8 @@ void SDL_AppQuit(void* appstate, SDL_AppResult result) {
pxl8_repl_shutdown(&app->repl); pxl8_repl_shutdown(&app->repl);
} }
if (app->cart) { if (app->cart) {
pxl8_cart_destroy(app->cart); pxl8_cart_unload(app->cart);
free(app->cart);
app->cart = NULL; app->cart = NULL;
} }
pxl8_lua_shutdown(app->lua); pxl8_lua_shutdown(app->lua);

View file

@ -1,6 +1,6 @@
#include <SDL3/SDL.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <SDL3/SDL.h>
#define MINIZ_NO_STDIO #define MINIZ_NO_STDIO
#define MINIZ_NO_TIME #define MINIZ_NO_TIME
@ -90,7 +90,6 @@ static pxl8_result parse_old_palette_chunk(const u8* data, pxl8_ase_palette* pal
u8 g = packet_data[1]; u8 g = packet_data[1];
u8 b = packet_data[2]; u8 b = packet_data[2];
// Store in ABGR format for GPU
palette->colors[color_index] = 0xFF000000 | (b << 16) | (g << 8) | r; palette->colors[color_index] = 0xFF000000 | (b << 16) | (g << 8) | r;
packet_data += 3; 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) { 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->flags = read_u16_le(data);
layer->layer_type = read_u16_le(data + 2); // Offset 2: layer_type (2 bytes) layer->layer_type = read_u16_le(data + 2);
layer->child_level = read_u16_le(data + 4); // Offset 4: child_level (2 bytes) layer->child_level = read_u16_le(data + 4);
// Offset 6: default_width (2 bytes) - skip layer->blend_mode = read_u16_le(data + 10);
// Offset 8: default_height (2 bytes) - skip layer->opacity = data[12];
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
u16 name_len = read_u16_le(data + 16); u16 name_len = read_u16_le(data + 16);
if (name_len > 0) { if (name_len > 0) {
layer->name = (char*)SDL_malloc(name_len + 1); 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) { 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->entry_count = read_u32_le(data);
palette->first_color = read_u32_le(data + 4); // Offset 4: first_color (4 bytes) palette->first_color = read_u32_le(data + 4);
palette->last_color = read_u32_le(data + 8); // Offset 8: last_color (4 bytes) palette->last_color = read_u32_le(data + 8);
u32 color_count = palette->entry_count; u32 color_count = palette->entry_count;
palette->colors = (u32*)SDL_malloc(color_count * sizeof(u32)); 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; 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++) { for (u32 i = 0; i < color_count; i++) {
u16 flags = read_u16_le(color_data); // Offset 0: flags (2 bytes) u16 flags = read_u16_le(color_data);
u8 r = color_data[2]; // Offset 2: red (1 byte) u8 r = color_data[2];
u8 g = color_data[3]; // Offset 3: green (1 byte) u8 g = color_data[3];
u8 b = color_data[4]; // Offset 4: blue (1 byte) u8 b = color_data[4];
u8 a = color_data[5]; // Offset 5: alpha (1 byte) u8 a = color_data[5];
// Store in ABGR format for GPU
palette->colors[i] = (a << 24) | (b << 16) | (g << 8) | r; palette->colors[i] = (a << 24) | (b << 16) | (g << 8) | r;
color_data += 6; 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; return PXL8_ERROR_ASE_MALFORMED_CHUNK;
} }
cel->layer_index = read_u16_le(data); // Offset 0: layer_index (2 bytes) cel->layer_index = read_u16_le(data);
cel->x = read_i16_le(data + 2); // Offset 2: x (2 bytes) cel->x = read_i16_le(data + 2);
cel->y = read_i16_le(data + 4); // Offset 4: y (2 bytes) cel->y = read_i16_le(data + 4);
cel->opacity = data[6]; // Offset 6: opacity (1 byte) cel->opacity = data[6];
cel->cel_type = read_u16_le(data + 7); // Offset 7: cel_type (2 bytes) cel->cel_type = read_u16_le(data + 7);
if (cel->cel_type == 2) { if (cel->cel_type == 2) {
if (chunk_size < 20) { if (chunk_size < 20) {
return PXL8_ERROR_ASE_MALFORMED_CHUNK; return PXL8_ERROR_ASE_MALFORMED_CHUNK;
} }
// Offset 9: Z-Index (2 bytes) - skip cel->width = read_u16_le(data + 16);
// Offset 11: Reserved (5 bytes) - skip cel->height = read_u16_le(data + 18);
cel->width = read_u16_le(data + 16); // Offset 16: width (2 bytes)
cel->height = read_u16_le(data + 18); // Offset 18: height (2 bytes)
u32 pixel_data_size = cel->width * cel->height; u32 pixel_data_size = cel->width * cel->height;
u32 compressed_data_size = chunk_size - 20; u32 compressed_data_size = chunk_size - 20;
@ -184,7 +176,6 @@ static pxl8_result parse_cel_chunk(const u8* data, u32 chunk_size, pxl8_ase_cel*
return PXL8_ERROR_OUT_OF_MEMORY; return PXL8_ERROR_OUT_OF_MEMORY;
} }
// Decompress ZLIB data
mz_ulong dest_len = pixel_data_size; mz_ulong dest_len = pixel_data_size;
int result = mz_uncompress(cel->pixel_data, &dest_len, data + 20, compressed_data_size); int result = mz_uncompress(cel->pixel_data, &dest_len, data + 20, compressed_data_size);
if (result != MZ_OK) { if (result != MZ_OK) {
@ -274,15 +265,16 @@ pxl8_result pxl8_ase_load(const char* filepath, pxl8_ase_file* ase_file) {
case PXL8_ASE_CHUNK_OLD_PALETTE: // 0x0004 case PXL8_ASE_CHUNK_OLD_PALETTE: // 0x0004
if (!ase_file->palette.colors) { if (!ase_file->palette.colors) {
result = parse_old_palette_chunk(chunk_payload, &ase_file->palette); result = parse_old_palette_chunk(chunk_payload, &ase_file->palette);
pxl8_debug("Parsed old palette: %d colors, indices %d-%d", ase_file->palette.entry_count, ase_file->palette.first_color, ase_file->palette.last_color); 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 { } else {
pxl8_debug("Ignoring old palette (0x0004) - new palette (0x2019) already loaded"); pxl8_debug("Ignoring old palette (0x0004) - new palette (0x2019) already loaded");
} }
break; break;
case PXL8_ASE_CHUNK_LAYER: { // 0x2004 case PXL8_ASE_CHUNK_LAYER: { // 0x2004
// Need to allocate or reallocate layers array ase_file->layers =
ase_file->layers = (pxl8_ase_layer*)SDL_realloc(ase_file->layers, (pxl8_ase_layer*)SDL_realloc(ase_file->layers,
(ase_file->layer_count + 1) * sizeof(pxl8_ase_layer)); (ase_file->layer_count + 1) * sizeof(pxl8_ase_layer));
if (!ase_file->layers) { if (!ase_file->layers) {
result = PXL8_ERROR_OUT_OF_MEMORY; result = PXL8_ERROR_OUT_OF_MEMORY;
@ -315,15 +307,12 @@ pxl8_result pxl8_ase_load(const char* filepath, pxl8_ase_file* ase_file) {
u32 src_offset = y * cel.width; u32 src_offset = y * cel.width;
u32 dst_offset = (y + cel.y) * frame->width + cel.x; u32 dst_offset = (y + cel.y) * frame->width + cel.x;
if (dst_offset + copy_width <= pixel_count) { 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++) { for (u32 x = 0; x < copy_width; x++) {
u8 src_pixel = cel.pixel_data[src_offset + x]; u8 src_pixel = cel.pixel_data[src_offset + x];
bool is_transparent = false; bool is_transparent = false;
if (src_pixel < ase_file->palette.entry_count && ase_file->palette.colors) { if (src_pixel < ase_file->palette.entry_count && ase_file->palette.colors) {
u32 color = ase_file->palette.colors[src_pixel]; u32 color = ase_file->palette.colors[src_pixel];
// Check if color is fully transparent (alpha = 0)
is_transparent = ((color >> 24) & 0xFF) == 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; frame->pixels[dst_offset + x] = src_pixel;
} }
} }
// Debug: check first few pixels of each row
if (y < 3) { 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); SDL_free(ase_file->palette.colors);
} }
result = parse_palette_chunk(chunk_payload, &ase_file->palette); result = parse_palette_chunk(chunk_payload, &ase_file->palette);
pxl8_debug("Parsed new palette: %d colors, indices %d-%d", ase_file->palette.entry_count, ase_file->palette.first_color, ase_file->palette.last_color); pxl8_debug("Parsed new palette: %d colors, indices %d-%d",
ase_file->palette.entry_count, ase_file->palette.first_color, ase_file->palette.last_color);
break; break;
default: default:
@ -355,10 +345,12 @@ pxl8_result pxl8_ase_load(const char* filepath, pxl8_ase_file* ase_file) {
} }
if (result != PXL8_OK) break; if (result != PXL8_OK) break;
chunk_data += chunk_header.chunk_size; chunk_data += chunk_header.chunk_size;
} }
if (result != PXL8_OK) break; if (result != PXL8_OK) break;
frame_data += frame_header.frame_bytes; frame_data += frame_header.frame_bytes;
} }

View file

@ -1,5 +1,4 @@
#pragma once #pragma once
#include "pxl8_types.h" #include "pxl8_types.h"
#define PXL8_ASE_MAGIC 0xA5E0 #define PXL8_ASE_MAGIC 0xA5E0

View file

@ -1,33 +1,6 @@
#include "pxl8_blit.h" #include "pxl8_blit.h"
#include "pxl8_simd.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, void pxl8_blit_simd_hicolor(u32* fb, u32 fb_width, const u32* sprite, u32 atlas_width,
i32 x, i32 y, u32 w, u32 h) { i32 x, i32 y, u32 w, u32 h) {
u32* dest_base = fb + y * fb_width + x; 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];
}
}
}
}

View file

@ -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); return w >= PXL8_SIMD_WIDTH_U8 && (w % PXL8_SIMD_WIDTH_U8 == 0);
} }
void pxl8_blit_simd_indexed( #ifdef __cplusplus
u8* fb, u32 fb_width, extern "C" {
const u8* sprite, u32 atlas_width, #endif
i32 x, i32 y, u32 w, u32 h
);
void pxl8_blit_simd_hicolor( void pxl8_blit_simd_hicolor(
u32* fb, u32 fb_width, u32* fb, u32 fb_width,
const u32* sprite, u32 atlas_width, const u32* sprite, u32 atlas_width,
i32 x, i32 y, u32 w, u32 h 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

View file

@ -8,17 +8,38 @@
#include <unistd.h> #include <unistd.h>
#include "../lib/miniz/miniz.h" #include "../lib/miniz/miniz.h"
static pxl8_cart* s_current_cart = NULL; static pxl8_cart* __pxl8_current_cart = NULL;
static char* s_original_cwd = 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);
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[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);
static bool is_directory(const char* path) {
struct stat st; struct stat st;
return stat(path, &st) == 0 && S_ISDIR(st.st_mode); 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);
}
}
}
} }
static bool is_pxc_file(const char* path) { closedir(dir);
size_t len = strlen(path);
return len > 4 && strcmp(path + len - 4, ".pxc") == 0;
} }
static char* get_cart_name(const char* path) { static char* get_cart_name(const char* path) {
@ -39,19 +60,18 @@ static char* get_cart_name(const char* path) {
return name; return name;
} }
pxl8_cart* pxl8_cart_new(void) { static bool is_directory(const char* path) {
pxl8_cart* cart = calloc(1, sizeof(pxl8_cart)); struct stat st;
if (!cart) { return stat(path, &st) == 0 && S_ISDIR(st.st_mode);
pxl8_error("Failed to allocate cart");
return NULL;
}
return cart;
} }
void pxl8_cart_destroy(pxl8_cart* cart) { static bool is_pxc_file(const char* path) {
if (!cart) return; size_t len = strlen(path);
pxl8_cart_unload(cart); return len > 4 && strcmp(path + len - 4, ".pxc") == 0;
free(cart); }
pxl8_cart* pxl8_cart_current(void) {
return __pxl8_current_cart;
} }
pxl8_result pxl8_cart_load(pxl8_cart* cart, const char* path) { 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; mz_zip_archive_file_stat file_stat;
if (!mz_zip_reader_file_stat(&zip, i, &file_stat)) continue; 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); snprintf(extract_path, sizeof(extract_path), "%s/%s", temp_dir, file_stat.m_filename);
if (file_stat.m_is_directory) { if (file_stat.m_is_directory) {
@ -171,6 +191,8 @@ void pxl8_cart_unload(pxl8_cart* cart) {
system(cmd); system(cmd);
} }
char* cart_name = cart->name ? strdup(cart->name) : NULL;
if (cart->base_path) { if (cart->base_path) {
free(cart->base_path); free(cart->base_path);
cart->base_path = NULL; cart->base_path = NULL;
@ -189,26 +211,31 @@ void pxl8_cart_unload(pxl8_cart* cart) {
cart->archive_size = 0; cart->archive_size = 0;
cart->is_folder = false; cart->is_folder = false;
cart->is_mounted = 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) { pxl8_result pxl8_cart_mount(pxl8_cart* cart) {
if (!cart || !cart->base_path) return PXL8_ERROR_NULL_POINTER; if (!cart || !cart->base_path) return PXL8_ERROR_NULL_POINTER;
if (cart->is_mounted) return PXL8_OK; if (cart->is_mounted) return PXL8_OK;
if (s_current_cart) { if (__pxl8_current_cart) {
pxl8_cart_unmount(s_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) { if (chdir(cart->base_path) != 0) {
pxl8_error("Failed to change to cart directory: %s", cart->base_path); pxl8_error("Failed to change to cart directory: %s", cart->base_path);
free(s_original_cwd); free(__pxl8_original_cwd);
s_original_cwd = NULL; __pxl8_original_cwd = NULL;
return PXL8_ERROR_FILE_NOT_FOUND; return PXL8_ERROR_FILE_NOT_FOUND;
} }
cart->is_mounted = true; cart->is_mounted = true;
s_current_cart = cart; __pxl8_current_cart = cart;
pxl8_info("Mounted cart: %s", cart->name); pxl8_info("Mounted cart: %s", cart->name);
return PXL8_OK; return PXL8_OK;
@ -217,15 +244,15 @@ pxl8_result pxl8_cart_mount(pxl8_cart* cart) {
void pxl8_cart_unmount(pxl8_cart* cart) { void pxl8_cart_unmount(pxl8_cart* cart) {
if (!cart || !cart->is_mounted) return; if (!cart || !cart->is_mounted) return;
if (s_original_cwd) { if (__pxl8_original_cwd) {
chdir(s_original_cwd); chdir(__pxl8_original_cwd);
free(s_original_cwd); free(__pxl8_original_cwd);
s_original_cwd = NULL; __pxl8_original_cwd = NULL;
} }
cart->is_mounted = false; cart->is_mounted = false;
if (s_current_cart == cart) { if (__pxl8_current_cart == cart) {
s_current_cart = NULL; __pxl8_current_cart = NULL;
} }
pxl8_info("Unmounted cart: %s", cart->name); 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; 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) { pxl8_result pxl8_cart_pack(const char* folder_path, const char* output_path) {
if (!folder_path || !output_path) return PXL8_ERROR_NULL_POINTER; 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_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)) { if (!mz_zip_writer_finalize_archive(&zip)) {
pxl8_error("Failed to finalize archive"); 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!"); pxl8_info("Cart packed successfully!");
return PXL8_OK; return PXL8_OK;
} }
pxl8_cart* pxl8_cart_current(void) {
return s_current_cart;
}

View file

@ -3,33 +3,26 @@
#include "pxl8_types.h" #include "pxl8_types.h"
typedef struct pxl8_cart { typedef struct pxl8_cart {
char* base_path;
char* name;
void* archive_data; void* archive_data;
size_t archive_size; size_t archive_size;
char* base_path;
bool is_folder; bool is_folder;
bool is_mounted; bool is_mounted;
char* name;
} pxl8_cart; } pxl8_cart;
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #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); 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 #ifdef __cplusplus
} }

View file

@ -2,18 +2,6 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <string.h> #include <string.h>
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) { 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) { if (!font || !atlas_data || !atlas_width || !atlas_height) {
return PXL8_ERROR_NULL_POINTER; 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; 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;
}

View file

@ -122,8 +122,8 @@ static const pxl8_font pxl8_default_font = {
extern "C" { extern "C" {
#endif #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); 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 #ifdef __cplusplus
} }

View file

@ -153,6 +153,7 @@ void pxl8_gfx_shutdown(pxl8_gfx_ctx* ctx) {
ctx->initialized = false; ctx->initialized = false;
} }
// resource loading
pxl8_result pxl8_gfx_init_atlas(pxl8_gfx_ctx* ctx, u32 width, u32 height) { pxl8_result pxl8_gfx_init_atlas(pxl8_gfx_ctx* ctx, u32 width, u32 height) {
if (!ctx || !ctx->initialized) return PXL8_ERROR_INVALID_ARGUMENT; 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; return PXL8_OK;
} }
// rendering pipeline
void pxl8_gfx_upload_framebuffer(pxl8_gfx_ctx* ctx) { void pxl8_gfx_upload_framebuffer(pxl8_gfx_ctx* ctx) {
if (!ctx || !ctx->initialized || !ctx->framebuffer_texture) return; 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; (void)ctx; (void)left; (void)right; (void)top; (void)bottom;
} }
// drawing primitives
void pxl8_clr(pxl8_gfx_ctx* ctx, u32 color) { void pxl8_clr(pxl8_gfx_ctx* ctx, u32 color) {
if (!ctx || !ctx->framebuffer) return; 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) { void pxl8_gfx_cycle_palette(pxl8_gfx_ctx* ctx, u8 start, u8 count, i32 step) {
if (!ctx || !ctx->palette || count == 0) return; if (!ctx || !ctx->palette || count == 0) return;

View file

@ -19,6 +19,7 @@ typedef struct pxl8_gfx_ctx {
SDL_Window* window; SDL_Window* window;
u8* framebuffer; u8* framebuffer;
bool initialized;
u32* palette; u32* palette;
u32 palette_size; u32 palette_size;
@ -26,23 +27,20 @@ typedef struct pxl8_gfx_ctx {
i32 framebuffer_height; i32 framebuffer_height;
pxl8_color_mode color_mode; 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_width;
u32 sprite_atlas_height; u32 sprite_atlas_height;
u32 sprite_frame_width; u32 sprite_frame_width;
u32 sprite_frame_height; u32 sprite_frame_height;
u32 sprite_frames_per_row; 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_x, viewport_y;
i32 viewport_width, viewport_height; i32 viewport_width, viewport_height;
bool initialized;
} pxl8_gfx_ctx; } pxl8_gfx_ctx;
typedef enum pxl8_blend_mode { typedef enum pxl8_blend_mode {
@ -52,6 +50,14 @@ typedef enum pxl8_blend_mode {
PXL8_BLEND_MULTIPLY PXL8_BLEND_MULTIPLY
} pxl8_blend_mode; } 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 { typedef struct pxl8_palette_cycle {
u8 start_index; u8 start_index;
u8 end_index; u8 end_index;
@ -65,21 +71,17 @@ typedef struct pxl8_scanline_effect {
bool active; bool active;
} pxl8_scanline_effect; } 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 { typedef struct pxl8_effects {
pxl8_palette_cycle palette_cycles[8]; pxl8_palette_cycle palette_cycles[8];
pxl8_scanline_effect scanline_effects[4]; pxl8_scanline_effect scanline_effects[4];
f32 time; f32 time;
} pxl8_effects; } 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_result pxl8_gfx_init(
pxl8_gfx_ctx* ctx, pxl8_gfx_ctx* ctx,
pxl8_color_mode mode, pxl8_color_mode mode,
@ -88,36 +90,54 @@ pxl8_result pxl8_gfx_init(
i32 window_width, i32 window_width,
i32 window_height 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_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); pxl8_result pxl8_gfx_load_font_atlas(pxl8_gfx_ctx* ctx);
pxl8_result pxl8_gfx_load_palette(pxl8_gfx_ctx* ctx, const char* path);
void pxl8_gfx_get_resolution_dimensions(pxl8_resolution resolution, i32* width, i32* height); pxl8_result pxl8_gfx_load_sprite(pxl8_gfx_ctx* ctx, const char* path);
void pxl8_gfx_upload_framebuffer(pxl8_gfx_ctx* ctx);
void pxl8_gfx_upload_atlas(pxl8_gfx_ctx* ctx);
void pxl8_gfx_present(pxl8_gfx_ctx* ctx); 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_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_gfx_color_ramp(
void pxl8_pixel(pxl8_gfx_ctx* ctx, i32 x, i32 y, u32 color); pxl8_gfx_ctx* ctx,
u32 pxl8_get_pixel(pxl8_gfx_ctx* ctx, i32 x, i32 y); u8 start,
void pxl8_line(pxl8_gfx_ctx* ctx, i32 x0, i32 y0, i32 x1, i32 y1, u32 color); u8 count,
void pxl8_rect(pxl8_gfx_ctx* ctx, i32 x, i32 y, i32 w, i32 h, u32 color); u32 from_color,
void pxl8_rect_fill(pxl8_gfx_ctx* ctx, i32 x, i32 y, i32 w, i32 h, u32 color); u32 to_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_cycle_palette(pxl8_gfx_ctx* ctx, u8 start, u8 count, i32 step); 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_fade_palette(
void pxl8_gfx_interpolate_palettes(pxl8_gfx_ctx* ctx, u32* palette1, u32* palette2, u8 start, u8 count, f32 t); 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_process_effects(pxl8_gfx_ctx* ctx, pxl8_effects* effects, f32 dt);
void pxl8_gfx_swap_palette(pxl8_gfx_ctx* ctx, u8 start, u8 count, u32* new_colors); void pxl8_gfx_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

View file

@ -11,17 +11,15 @@
extern "C" { extern "C" {
#endif #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); pxl8_result pxl8_io_create_directory(const char* path);
bool pxl8_io_file_exists(const char* path);
void pxl8_io_free_file_content(char* content);
void pxl8_io_free_binary_data(u8* data); 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_down(const pxl8_input_state* input, i32 key);
bool pxl8_key_pressed(const pxl8_input_state* input, i32 key); bool pxl8_key_pressed(const pxl8_input_state* input, i32 key);

View file

@ -104,9 +104,9 @@ static const char* pxl8_ffi_cdefs =
" float phase;\n" " float phase;\n"
" unsigned int color;\n" " unsigned int color;\n"
" unsigned int fade_color;\n" " unsigned int fade_color;\n"
"} pxl8_copper_bar;\n" "} pxl8_raster_bar;\n"
"\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_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_rotozoom(pxl8_gfx_ctx* ctx, f32 angle, f32 zoom, i32 cx, i32 cy);\n"
"void pxl8_vfx_tunnel(pxl8_gfx_ctx* ctx, f32 time, f32 speed, f32 twist);\n" "void pxl8_vfx_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); lua_remove(lua_state, -2);
return PXL8_OK; return PXL8_OK;
} }

View file

@ -13,12 +13,13 @@ extern "C" {
pxl8_result pxl8_lua_init(lua_State** lua_state); pxl8_result pxl8_lua_init(lua_State** lua_state);
void pxl8_lua_shutdown(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_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_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_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_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 #ifdef __cplusplus
} }

View file

@ -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_left = pxl8_max(0, view_left >> 4);
u32 chunk_top = pxl8_max(0, view_top >> 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_right = pxl8_min((tilemap->width + 15) >> 4, (u32)((view_right >> 4) + 1));
u32 chunk_bottom = pxl8_min((tilemap->height + 15) >> 4, (view_bottom >> 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 cy = chunk_top; cy < chunk_bottom; cy++) {
for (u32 cx = chunk_left; cx < chunk_right; cx++) { 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; 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) { u16 pxl8_tilemap_get_tile_id(const pxl8_tilemap* tilemap, u32 layer, u32 x, u32 y) {
if (!tilemap) return 0; if (!tilemap) return 0;
pxl8_tile tile = pxl8_tilemap_get_tile(tilemap, layer, x, y); pxl8_tile tile = pxl8_tilemap_get_tile(tilemap, layer, x, y);

View file

@ -108,35 +108,56 @@ typedef struct pxl8_tilemap_view {
extern "C" { extern "C" {
#endif #endif
pxl8_result pxl8_tilemap_init(pxl8_tilemap* tilemap, u32 width, u32 height, u32 tile_size); bool pxl8_tilemap_check_collision(const pxl8_tilemap* tilemap, i32 x, i32 y, i32 w, i32 h);
pxl8_result pxl8_tilemap_set_tilesheet(pxl8_tilemap* tilemap, pxl8_tilesheet* tilesheet); void pxl8_tilemap_compress(pxl8_tilemap* tilemap);
void pxl8_tilemap_free(pxl8_tilemap* tilemap); void pxl8_tilemap_free(pxl8_tilemap* tilemap);
u32 pxl8_tilemap_get_memory_usage(const pxl8_tilemap* tilemap);
pxl8_result pxl8_tilemap_set_tile(pxl8_tilemap* tilemap, u32 layer, u32 x, u32 y, u16 tile_id, u8 flags);
pxl8_tile pxl8_tilemap_get_tile(const pxl8_tilemap* tilemap, u32 layer, u32 x, u32 y); pxl8_tile pxl8_tilemap_get_tile(const pxl8_tilemap* tilemap, u32 layer, u32 x, u32 y);
u16 pxl8_tilemap_get_tile_id(const pxl8_tilemap* tilemap, u32 layer, u32 x, u32 y);
void pxl8_tilemap_set_camera(pxl8_tilemap* tilemap, i32 x, i32 y); void pxl8_tilemap_get_view(
void pxl8_tilemap_get_view(const pxl8_tilemap* tilemap, const pxl8_gfx_ctx* gfx, pxl8_tilemap_view* 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(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_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); void pxl8_tilemap_render_tile(
const pxl8_tilemap* tilemap,
bool pxl8_tilemap_is_solid(const pxl8_tilemap* tilemap, u32 x, u32 y); pxl8_gfx_ctx* gfx,
bool pxl8_tilemap_check_collision(const pxl8_tilemap* tilemap, i32 x, i32 y, i32 w, i32 h); u16 tile_id,
i32 x,
pxl8_tilemap* pxl8_tilemap_new(u32 width, u32 height, u32 tile_size); i32 y,
void pxl8_tilemap_destroy(pxl8_tilemap* tilemap); u8 flags
u16 pxl8_tilemap_get_tile_id(const pxl8_tilemap* tilemap, u32 layer, u32 x, u32 y); );
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); void pxl8_tilemap_update(pxl8_tilemap* tilemap, f32 delta_time);
void pxl8_tilemap_update_autotiles(
pxl8_result pxl8_tilemap_set_tile_auto(pxl8_tilemap* tilemap, u32 layer, u32 x, u32 y, pxl8_tilemap* tilemap,
u16 base_tile_id, u8 flags); u32 layer,
void pxl8_tilemap_update_autotiles(pxl8_tilemap* tilemap, u32 layer, u32 x, u32 y, u32 w, u32 h); u32 x,
u32 y,
u32 pxl8_tilemap_get_memory_usage(const pxl8_tilemap* tilemap); u32 w,
void pxl8_tilemap_compress(pxl8_tilemap* tilemap); u32 h
);
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -5,16 +5,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
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) { void pxl8_tilesheet_free(pxl8_tilesheet* tilesheet) {
if (!tilesheet) return; 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; 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) { void pxl8_tilesheet_ref(pxl8_tilesheet* tilesheet) {
if (!tilesheet) return; if (!tilesheet) return;
tilesheet->ref_count++; tilesheet->ref_count++;
@ -218,7 +186,6 @@ void pxl8_tilesheet_unref(pxl8_tilesheet* tilesheet) {
if (--tilesheet->ref_count == 0) { if (--tilesheet->ref_count == 0) {
pxl8_tilesheet_free(tilesheet); pxl8_tilesheet_free(tilesheet);
free(tilesheet);
} }
} }

View file

@ -31,33 +31,44 @@ typedef struct pxl8_tilesheet {
extern "C" { extern "C" {
#endif #endif
pxl8_result pxl8_tilesheet_init(pxl8_tilesheet* tilesheet, u32 tile_size); pxl8_result pxl8_tilesheet_add_animation(
pxl8_result pxl8_tilesheet_load(pxl8_tilesheet* tilesheet, const char* filepath, pxl8_gfx_ctx* gfx); pxl8_tilesheet* tilesheet,
void pxl8_tilesheet_free(pxl8_tilesheet* tilesheet); u16 base_tile_id,
const u16* frames,
void pxl8_tilesheet_render_tile(const pxl8_tilesheet* tilesheet, pxl8_gfx_ctx* gfx, u16 frame_count,
u16 tile_id, i32 x, i32 y, u8 flags); f32 frame_duration
);
bool pxl8_tilesheet_is_tile_valid(const pxl8_tilesheet* tilesheet, u16 tile_id); pxl8_result pxl8_tilesheet_add_autotile_rule(
pxl8_tilesheet* tilesheet,
pxl8_tilesheet* pxl8_tilesheet_new(u32 tile_size); u16 base_tile_id,
void pxl8_tilesheet_destroy(pxl8_tilesheet* tilesheet); u8 neighbor_mask,
u16 result_tile_id
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);
u16 pxl8_tilesheet_apply_autotile(const pxl8_tilesheet* tilesheet, u16 base_tile_id, u8 neighbors); 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 #ifdef __cplusplus
} }

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include <SDL3/SDL.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <SDL3/SDL.h>
typedef uint8_t u8; typedef uint8_t u8;
typedef uint16_t u16; typedef uint16_t u16;
@ -21,13 +21,28 @@ typedef __int128_t i128;
#endif #endif
typedef enum pxl8_color_mode { typedef enum pxl8_color_mode {
PXL8_COLOR_MODE_FAMI, // NES-style 4 colors per sprite, 64 color palette PXL8_COLOR_MODE_FAMI,
PXL8_COLOR_MODE_MEGA, // Genesis-style 64 colors, 512 color palette PXL8_COLOR_MODE_MEGA,
PXL8_COLOR_MODE_GBA, // GBA-style 64 colors, 32K color palette PXL8_COLOR_MODE_GBA,
PXL8_COLOR_MODE_SUPERFAMI, // SNES-style 256 colors, 32K color palette PXL8_COLOR_MODE_SUPERFAMI,
PXL8_COLOR_MODE_HICOLOR, // 16-bit high color mode PXL8_COLOR_MODE_HICOLOR,
} pxl8_color_mode; } 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 { typedef struct pxl8_point {
i32 x, y; i32 x, y;
} pxl8_point; } pxl8_point;
@ -55,18 +70,3 @@ typedef enum pxl8_result {
PXL8_ERROR_ASE_TRUNCATED_FILE, PXL8_ERROR_ASE_TRUNCATED_FILE,
PXL8_ERROR_ASE_MALFORMED_CHUNK, PXL8_ERROR_ASE_MALFORMED_CHUNK,
} pxl8_result; } 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;

View file

@ -4,31 +4,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
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) { void pxl8_vfx_plasma(pxl8_gfx_ctx* ctx, f32 time, f32 scale1, f32 scale2, u8 palette_offset) {
if (!ctx || !ctx->framebuffer) return; 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) { void pxl8_vfx_rotozoom(pxl8_gfx_ctx* ctx, f32 angle, f32 zoom, i32 cx, i32 cy) {
if (!ctx || !ctx->framebuffer) return; if (!ctx || !ctx->framebuffer) return;
@ -147,9 +147,10 @@ void pxl8_vfx_particles_clear(pxl8_particle_system* sys) {
sys->particles[i].flags = 0; sys->particles[i].flags = 0;
} }
sys->alive_count = 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) return;
if (sys->particles) { if (sys->particles) {
@ -279,6 +280,7 @@ void pxl8_vfx_explosion(pxl8_particle_system* sys, i32 x, i32 y, u32 color, f32
sys->gravity_x = 0; sys->gravity_x = 0;
sys->gravity_y = 200.0f; sys->gravity_y = 200.0f;
sys->drag = 0.95f; sys->drag = 0.95f;
sys->update_fn = NULL;
for (u32 i = 0; i < 50 && i < sys->max_count; i++) { for (u32 i = 0; i < 50 && i < sys->max_count; i++) {
pxl8_particle* p = &sys->particles[i]; pxl8_particle* p = &sys->particles[i];
@ -363,6 +365,7 @@ void pxl8_vfx_rain(pxl8_particle_system* sys, i32 width, f32 wind) {
sys->drag = 1.0f; sys->drag = 1.0f;
sys->spawn_rate = 100.0f; sys->spawn_rate = 100.0f;
sys->spawn_fn = rain_spawn; sys->spawn_fn = rain_spawn;
sys->update_fn = NULL;
} }
static void smoke_spawn(pxl8_particle* p, void* userdata) { 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->drag = 0.96f;
sys->spawn_rate = 20.0f; sys->spawn_rate = 20.0f;
sys->spawn_fn = smoke_spawn; sys->spawn_fn = smoke_spawn;
sys->update_fn = NULL;
sys->userdata = (void*)(uintptr_t)color; sys->userdata = (void*)(uintptr_t)color;
} }
static void snow_spawn(pxl8_particle* p, void* userdata) { static void snow_spawn(pxl8_particle* p, void* userdata) {
(void)userdata; (void)userdata;
p->start_color = 15 + (rand() % 2); p->start_color = 8 + (rand() % 3);
p->end_color = 10; p->end_color = 10;
p->color = p->start_color; p->color = p->start_color;
p->max_life = 4.0f; p->max_life = 4.0f;
@ -410,9 +414,10 @@ void pxl8_vfx_snow(pxl8_particle_system* sys, i32 width, f32 wind) {
sys->spread_y = 0; sys->spread_y = 0;
sys->gravity_x = wind; sys->gravity_x = wind;
sys->gravity_y = 30.0f; sys->gravity_y = 30.0f;
sys->drag = 0.99f; sys->drag = 1.0f;
sys->spawn_rate = 30.0f; sys->spawn_rate = 30.0f;
sys->spawn_fn = snow_spawn; sys->spawn_fn = snow_spawn;
sys->update_fn = NULL;
} }
static void sparks_spawn(pxl8_particle* p, void* userdata) { 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->drag = 0.97f;
sys->spawn_rate = 40.0f; sys->spawn_rate = 40.0f;
sys->spawn_fn = sparks_spawn; sys->spawn_fn = sparks_spawn;
sys->update_fn = NULL;
sys->userdata = (void*)(uintptr_t)color; 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->gravity_x = sys->gravity_y = 0;
sys->drag = 1.0f; sys->drag = 1.0f;
sys->spawn_rate = 0; sys->spawn_rate = 0;
sys->update_fn = NULL;
for (u32 i = 0; i < sys->max_count; i++) { for (u32 i = 0; i < sys->max_count; i++) {
pxl8_particle* p = &sys->particles[i]; pxl8_particle* p = &sys->particles[i];

View file

@ -3,16 +3,6 @@
#include "pxl8_types.h" #include "pxl8_types.h"
#include "pxl8_gfx.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 { typedef struct pxl8_particle {
f32 x, y, z; f32 x, y, z;
f32 vx, vy, vz; f32 vx, vy, vz;
@ -46,15 +36,19 @@ typedef struct pxl8_particle_system {
void* userdata; void* userdata;
} pxl8_particle_system; } pxl8_particle_system;
void pxl8_vfx_copper_bars(pxl8_gfx_ctx* ctx, pxl8_copper_bar* bars, u32 bar_count, f32 time); typedef struct pxl8_raster_bar {
void pxl8_vfx_plasma(pxl8_gfx_ctx* ctx, f32 time, f32 scale1, f32 scale2, u8 palette_offset); f32 base_y;
void pxl8_vfx_rotozoom(pxl8_gfx_ctx* ctx, f32 angle, f32 zoom, i32 cx, i32 cy); f32 amplitude;
void pxl8_vfx_tunnel(pxl8_gfx_ctx* ctx, f32 time, f32 speed, f32 twist); i32 height;
void pxl8_vfx_water_ripple(pxl8_gfx_ctx* ctx, f32* height_map, i32 drop_x, i32 drop_y, f32 damping); 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_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_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_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_render(pxl8_particle_system* sys, pxl8_gfx_ctx* ctx);
void pxl8_vfx_particles_update(pxl8_particle_system* sys, f32 dt); 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_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_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_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);