From 7f56e0bfe8932b5ce90a3df7918fe87e0a408747 Mon Sep 17 00:00:00 2001 From: asrael Date: Sun, 5 Oct 2025 17:19:59 -0500 Subject: [PATCH] clean up gfx a bit... more to come --- demo/main.fnl | 4 +- src/pxl8_gfx.c | 252 +++++++++++++++++++++++++++++++++++++------------ 2 files changed, 195 insertions(+), 61 deletions(-) diff --git a/demo/main.fnl b/demo/main.fnl index d002ac7..87655c1 100644 --- a/demo/main.fnl +++ b/demo/main.fnl @@ -45,9 +45,7 @@ (when (pxl8.key_pressed "9") (set use-nes-palette (not use-nes-palette)) (local palette-path (if use-nes-palette "palettes/nes.ase" "sprites/pxl8_logo.ase")) - (print (.. "Switching to palette: " palette-path)) - (pxl8.load_palette palette-path) - (print "Palette loaded")) + (pxl8.load_palette palette-path)) (case current-effect 1 (do diff --git a/src/pxl8_gfx.c b/src/pxl8_gfx.c index 1930762..b801339 100644 --- a/src/pxl8_gfx.c +++ b/src/pxl8_gfx.c @@ -82,7 +82,13 @@ struct pxl8_gfx { bool affine_textures; }; -static pxl8_skyline_fit pxl8_skyline_find_position(const pxl8_skyline* skyline, u32 atlas_w, u32 atlas_h, u32 rect_w, u32 rect_h) { +static pxl8_skyline_fit pxl8_skyline_find_position( + const pxl8_skyline* skyline, + u32 atlas_w, + u32 atlas_h, + u32 rect_w, + u32 rect_h +) { pxl8_skyline_fit result = {.found = false}; i32 best_y = INT32_MAX; i32 best_x = 0; @@ -129,8 +135,6 @@ static void pxl8_skyline_add_rect(pxl8_skyline* skyline, pxl8_point pos, u32 w, } } - pxl8_skyline_node new_node = {pos.x, pos.y + (i32)h, (i32)w}; - u32 nodes_to_remove = 0; for (u32 i = node_idx; i < skyline->count; i++) { if (skyline->nodes[i].x < pos.x + (i32)w) { @@ -142,15 +146,21 @@ 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*)SDL_realloc(skyline->nodes, skyline->capacity * sizeof(pxl8_skyline_node)); + skyline->nodes = (pxl8_skyline_node*)SDL_realloc( + skyline->nodes, + skyline->capacity * sizeof(pxl8_skyline_node) + ); } if (nodes_to_remove > 0) { - SDL_memmove(&skyline->nodes[node_idx + 1], &skyline->nodes[node_idx + nodes_to_remove], - (skyline->count - node_idx - nodes_to_remove) * sizeof(pxl8_skyline_node)); + SDL_memmove( + &skyline->nodes[node_idx + 1], + &skyline->nodes[node_idx + nodes_to_remove], + (skyline->count - node_idx - nodes_to_remove) * sizeof(pxl8_skyline_node) + ); } - skyline->nodes[node_idx] = new_node; + skyline->nodes[node_idx] = (pxl8_skyline_node){pos.x, pos.y + (i32)h, (i32)w}; skyline->count = skyline->count - nodes_to_remove + 1; } @@ -158,8 +168,11 @@ static void pxl8_skyline_compact(pxl8_skyline* skyline) { for (u32 i = 0; i < skyline->count - 1; ) { if (skyline->nodes[i].y == skyline->nodes[i + 1].y) { skyline->nodes[i].width += skyline->nodes[i + 1].width; - SDL_memmove(&skyline->nodes[i + 1], &skyline->nodes[i + 2], - (skyline->count - i - 2) * sizeof(pxl8_skyline_node)); + SDL_memmove( + &skyline->nodes[i + 1], + &skyline->nodes[i + 2], + (skyline->count - i - 2) * sizeof(pxl8_skyline_node) + ); skyline->count--; } else { i++; @@ -167,7 +180,12 @@ static void pxl8_skyline_compact(pxl8_skyline* skyline) { } } -static pxl8_atlas* pxl8_atlas_create(SDL_Renderer* renderer, u32 width, u32 height, pxl8_color_mode color_mode) { +static pxl8_atlas* pxl8_atlas_create( + SDL_Renderer* renderer, + u32 width, + u32 height, + pxl8_color_mode color_mode +) { pxl8_atlas* atlas = (pxl8_atlas*)SDL_calloc(1, sizeof(pxl8_atlas)); if (!atlas) return NULL; @@ -181,8 +199,14 @@ static pxl8_atlas* pxl8_atlas_create(SDL_Renderer* renderer, u32 width, u32 heig return NULL; } - atlas->texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, - SDL_TEXTUREACCESS_STREAMING, width, height); + atlas->texture = SDL_CreateTexture( + renderer, + SDL_PIXELFORMAT_RGBA32, + SDL_TEXTUREACCESS_STREAMING, + width, + height + ); + if (!atlas->texture) { SDL_free(atlas->pixels); SDL_free(atlas); @@ -209,7 +233,8 @@ static pxl8_atlas* pxl8_atlas_create(SDL_Renderer* renderer, u32 width, u32 heig } atlas->skyline.capacity = 16; - atlas->skyline.nodes = (pxl8_skyline_node*)SDL_calloc(atlas->skyline.capacity, sizeof(pxl8_skyline_node)); + atlas->skyline.nodes = + (pxl8_skyline_node*)SDL_calloc(atlas->skyline.capacity, sizeof(pxl8_skyline_node)); if (!atlas->skyline.nodes) { SDL_free(atlas->free_list); SDL_free(atlas->entries); @@ -228,15 +253,19 @@ static pxl8_atlas* pxl8_atlas_create(SDL_Renderer* renderer, u32 width, u32 heig static void pxl8_atlas_destroy(pxl8_atlas* atlas) { if (!atlas) return; - SDL_free(atlas->free_list); SDL_free(atlas->entries); + SDL_free(atlas->free_list); + SDL_free(atlas->pixels); SDL_free(atlas->skyline.nodes); if (atlas->texture) SDL_DestroyTexture(atlas->texture); - SDL_free(atlas->pixels); SDL_free(atlas); } -static bool pxl8_atlas_expand(pxl8_atlas* atlas, SDL_Renderer* renderer, pxl8_color_mode color_mode) { +static bool pxl8_atlas_expand( + pxl8_atlas* atlas, + SDL_Renderer* renderer, + pxl8_color_mode color_mode +) { if (!atlas || atlas->width >= 4096) return false; u32 new_size = atlas->width * 2; @@ -246,8 +275,14 @@ static bool pxl8_atlas_expand(pxl8_atlas* atlas, SDL_Renderer* renderer, pxl8_co u8* new_pixels = (u8*)SDL_calloc(new_size * new_size, bytes_per_pixel); if (!new_pixels) return false; - SDL_Texture* new_texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, - SDL_TEXTUREACCESS_STREAMING, new_size, new_size); + SDL_Texture* new_texture = SDL_CreateTexture( + renderer, + SDL_PIXELFORMAT_RGBA32, + SDL_TEXTUREACCESS_STREAMING, + new_size, + new_size + ); + if (!new_texture) { SDL_free(new_pixels); return false; @@ -268,8 +303,14 @@ static bool pxl8_atlas_expand(pxl8_atlas* atlas, SDL_Renderer* renderer, pxl8_co for (u32 i = 0; i < atlas->entry_count; i++) { if (!atlas->entries[i].active) continue; - pxl8_skyline_fit fit = pxl8_skyline_find_position(&new_skyline, new_size, new_size, - atlas->entries[i].w, atlas->entries[i].h); + pxl8_skyline_fit fit = pxl8_skyline_find_position( + &new_skyline, + new_size, + new_size, + atlas->entries[i].w, + atlas->entries[i].h + ); + if (!fit.found) { SDL_free(new_skyline.nodes); SDL_DestroyTexture(new_texture); @@ -311,15 +352,27 @@ static bool pxl8_atlas_expand(pxl8_atlas* atlas, SDL_Renderer* renderer, pxl8_co return true; } -static u32 pxl8_atlas_add_texture(pxl8_atlas* atlas, const u8* pixels, u32 w, u32 h, const char* path, pxl8_color_mode color_mode) { +static u32 pxl8_atlas_add_texture( + pxl8_atlas* atlas, + const u8* pixels, + u32 w, + u32 h, + const char* path, + pxl8_color_mode color_mode +) { if (!atlas || !pixels) return UINT32_MAX; - pxl8_skyline_fit fit = pxl8_skyline_find_position(&atlas->skyline, atlas->width, atlas->height, w, h); + pxl8_skyline_fit fit = + pxl8_skyline_find_position(&atlas->skyline, atlas->width, atlas->height, w, h); if (!fit.found) { - if (!pxl8_atlas_expand(atlas, atlas->texture ? SDL_GetRendererFromTexture(atlas->texture) : NULL, color_mode)) { + SDL_Renderer* renderer = atlas->texture ? SDL_GetRendererFromTexture(atlas->texture) : NULL; + + if (!pxl8_atlas_expand(atlas, renderer, color_mode)) { return UINT32_MAX; } + fit = pxl8_skyline_find_position(&atlas->skyline, atlas->width, atlas->height, w, h); + if (!fit.found) return UINT32_MAX; } @@ -329,8 +382,10 @@ static u32 pxl8_atlas_add_texture(pxl8_atlas* atlas, const u8* pixels, u32 w, u3 } else { if (atlas->entry_count >= atlas->entry_capacity) { atlas->entry_capacity *= 2; - atlas->entries = (pxl8_atlas_entry*)SDL_realloc(atlas->entries, - atlas->entry_capacity * sizeof(pxl8_atlas_entry)); + atlas->entries = (pxl8_atlas_entry*)SDL_realloc( + atlas->entries, + atlas->entry_capacity * sizeof(pxl8_atlas_entry) + ); } texture_id = atlas->entry_count++; } @@ -449,7 +504,13 @@ u32 pxl8_gfx_get_palette_size(const pxl8_gfx* gfx) { return gfx ? gfx->palette_size : 0; } -pxl8_gfx* pxl8_gfx_create(pxl8_color_mode mode, pxl8_resolution resolution, const char* title, i32 window_width, i32 window_height) { +pxl8_gfx* pxl8_gfx_create( + pxl8_color_mode mode, + pxl8_resolution resolution, + const char* title, + i32 window_width, + i32 window_height +) { pxl8_gfx* gfx = (pxl8_gfx*)SDL_calloc(1, sizeof(*gfx)); if (!gfx) { pxl8_error("Failed to allocate graphics context"); @@ -457,7 +518,11 @@ pxl8_gfx* pxl8_gfx_create(pxl8_color_mode mode, pxl8_resolution resolution, cons } gfx->color_mode = mode; - pxl8_gfx_get_resolution_dimensions(resolution, &gfx->framebuffer_width, &gfx->framebuffer_height); + pxl8_gfx_get_resolution_dimensions( + resolution, + &gfx->framebuffer_width, + &gfx->framebuffer_height + ); gfx->window = SDL_CreateWindow( title, @@ -478,10 +543,12 @@ pxl8_gfx* pxl8_gfx_create(pxl8_color_mode mode, pxl8_resolution resolution, cons return NULL; } - SDL_SetRenderLogicalPresentation(gfx->renderer, - gfx->framebuffer_width, - gfx->framebuffer_height, - SDL_LOGICAL_PRESENTATION_INTEGER_SCALE); + SDL_SetRenderLogicalPresentation( + gfx->renderer, + gfx->framebuffer_width, + gfx->framebuffer_height, + SDL_LOGICAL_PRESENTATION_INTEGER_SCALE + ); i32 bytes_per_pixel = (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) ? 4 : 1; i32 fb_size = gfx->framebuffer_width * gfx->framebuffer_height * bytes_per_pixel; @@ -579,16 +646,19 @@ pxl8_result pxl8_gfx_create_texture(pxl8_gfx* gfx, const u8* pixels, u32 width, if (!gfx->atlas) return PXL8_ERROR_OUT_OF_MEMORY; } - u32 texture_id = pxl8_atlas_add_texture(gfx->atlas, pixels, width, height, NULL, gfx->color_mode); + u32 texture_id = + pxl8_atlas_add_texture(gfx->atlas, pixels, width, height, NULL, gfx->color_mode); if (texture_id == UINT32_MAX) { pxl8_error("Texture doesn't fit in atlas"); return PXL8_ERROR_INVALID_SIZE; } - pxl8_debug("Created texture %u: %ux%u at (%d,%d)", - texture_id, width, height, - gfx->atlas->entries[texture_id].x, - gfx->atlas->entries[texture_id].y); + pxl8_debug( + "Created texture %u: %ux%u at (%d,%d)", + texture_id, width, height, + gfx->atlas->entries[texture_id].x, + gfx->atlas->entries[texture_id].y + ); return texture_id; } @@ -623,7 +693,14 @@ pxl8_result pxl8_gfx_load_sprite(pxl8_gfx* gfx, const char* path) { u32 sprite_w = ase_file.header.width; u32 sprite_h = ase_file.header.height; - u32 texture_id = pxl8_atlas_add_texture(gfx->atlas, ase_file.frames[0].pixels, sprite_w, sprite_h, path, gfx->color_mode); + u32 texture_id = pxl8_atlas_add_texture( + gfx->atlas, + ase_file.frames[0].pixels, + sprite_w, + sprite_h, + path, + gfx->color_mode + ); pxl8_ase_destroy(&ase_file); @@ -659,7 +736,10 @@ pxl8_result pxl8_gfx_load_palette(pxl8_gfx* gfx, const char* path) { return PXL8_ERROR_INVALID_FORMAT; } - u32 copy_size = (ase_file.palette.entry_count < gfx->palette_size) ? ase_file.palette.entry_count : gfx->palette_size; + u32 copy_size = ase_file.palette.entry_count < gfx->palette_size + ? ase_file.palette.entry_count + : gfx->palette_size; + memcpy(gfx->palette, ase_file.palette.colors, copy_size * sizeof(u32)); u32 last_color = (copy_size > 0) ? gfx->palette[copy_size - 1] : 0xFF000000; @@ -678,7 +758,15 @@ pxl8_result pxl8_gfx_load_font_atlas(pxl8_gfx* gfx) { return PXL8_OK; } -static void pxl8_upload_indexed_texture(SDL_Texture* texture, const u8* indexed, const u32* palette, u32 palette_size, i32 width, i32 height, u32 default_color) { +static void pxl8_upload_indexed_texture( + SDL_Texture* texture, + const u8* indexed, + const u32* palette, + u32 palette_size, + i32 width, + i32 height, + u32 default_color +) { static u32* rgba_buffer = NULL; static size_t buffer_size = 0; size_t needed_size = width * height; @@ -702,13 +790,22 @@ void pxl8_gfx_upload_framebuffer(pxl8_gfx* gfx) { if (!gfx || !gfx->initialized || !gfx->framebuffer_texture) return; if (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) { - SDL_UpdateTexture(gfx->framebuffer_texture, NULL, gfx->framebuffer, - gfx->framebuffer_width * 4); + SDL_UpdateTexture( + gfx->framebuffer_texture, + NULL, + gfx->framebuffer, + gfx->framebuffer_width * 4 + ); } else { - pxl8_upload_indexed_texture(gfx->framebuffer_texture, gfx->framebuffer, - gfx->palette, gfx->palette_size, - gfx->framebuffer_width, gfx->framebuffer_height, - 0xFF000000); + pxl8_upload_indexed_texture( + gfx->framebuffer_texture, + gfx->framebuffer, + gfx->palette, + gfx->palette_size, + gfx->framebuffer_width, + gfx->framebuffer_height, + 0xFF000000 + ); } } @@ -716,13 +813,22 @@ void pxl8_gfx_upload_atlas(pxl8_gfx* gfx) { if (!gfx || !gfx->initialized || !gfx->atlas || !gfx->atlas->texture || !gfx->atlas->dirty) return; if (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) { - SDL_UpdateTexture(gfx->atlas->texture, NULL, gfx->atlas->pixels, - gfx->atlas->width * 4); + SDL_UpdateTexture( + gfx->atlas->texture, + NULL, + gfx->atlas->pixels, + gfx->atlas->width * 4 + ); } else { - pxl8_upload_indexed_texture(gfx->atlas->texture, gfx->atlas->pixels, - gfx->palette, gfx->palette_size, - gfx->atlas->width, gfx->atlas->height, - 0x00000000); + pxl8_upload_indexed_texture( + gfx->atlas->texture, + gfx->atlas->pixels, + gfx->palette, + gfx->palette_size, + gfx->atlas->width, + gfx->atlas->height, + 0x00000000 + ); } gfx->atlas->dirty = false; @@ -976,11 +1082,21 @@ void pxl8_sprite(pxl8_gfx* gfx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h) { const u8* sprite_data = gfx->atlas->pixels + entry->y * gfx->atlas->width + entry->x; if (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) { - pxl8_blit_simd_hicolor((u32*)gfx->framebuffer, gfx->framebuffer_width, - (const u32*)sprite_data, gfx->atlas->width, x, y, w, h); + pxl8_blit_simd_hicolor( + (u32*)gfx->framebuffer, + gfx->framebuffer_width, + (const u32*)sprite_data, + gfx->atlas->width, + x, y, w, h + ); } else { - pxl8_blit_simd_indexed(gfx->framebuffer, gfx->framebuffer_width, - sprite_data, gfx->atlas->width, x, y, w, h); + pxl8_blit_simd_indexed( + gfx->framebuffer, + gfx->framebuffer_width, + sprite_data, + gfx->atlas->width, + x, y, w, h + ); } } else { for (i32 py = 0; py < draw_height; py++) { @@ -1051,7 +1167,14 @@ void pxl8_gfx_fade_palette(pxl8_gfx* gfx, u8 start, u8 count, f32 amount, u32 ta } } -void pxl8_gfx_interpolate_palettes(pxl8_gfx* gfx, u32* palette1, u32* palette2, u8 start, u8 count, f32 t) { +void pxl8_gfx_interpolate_palettes( + pxl8_gfx* gfx, + u32* palette1, + u32* palette2, + u8 start, + u8 count, + f32 t +) { if (!gfx || !gfx->palette || !palette1 || !palette2 || count == 0) return; if (t < 0.0f) t = 0.0f; @@ -1496,7 +1619,19 @@ static void pxl8_draw_flat_top_triangle_textured( } } -void pxl8_3d_draw_triangle_textured(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, pxl8_vec3 v2, f32 u0, f32 v0f, f32 u1, f32 v1f, f32 u2, f32 v2f, u32 texture_id) { +void pxl8_3d_draw_triangle_textured( + pxl8_gfx* gfx, + pxl8_vec3 v0, + pxl8_vec3 v1, + pxl8_vec3 v2, + f32 u0, + f32 v0f, + f32 u1, + f32 v1f, + f32 u2, + f32 v2f, + u32 texture_id +) { if (!gfx || !pxl8_3d_init_zbuffer(gfx)) return; pxl8_vec4 cv0 = pxl8_transform_vertex(gfx, v0); @@ -1526,7 +1661,8 @@ void pxl8_3d_draw_triangle_textured(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, p tv[2].w = cv2.w; if (gfx->backface_culling) { - i32 cross = (tv[1].x - tv[0].x) * (tv[2].y - tv[0].y) - (tv[1].y - tv[0].y) * (tv[2].x - tv[0].x); + i32 cross = + (tv[1].x - tv[0].x) * (tv[2].y - tv[0].y) - (tv[1].y - tv[0].y) * (tv[2].x - tv[0].x); if (cross >= 0) return; }