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