clean up gfx a bit... more to come

This commit is contained in:
asrael 2025-10-05 17:19:59 -05:00
parent c662c550df
commit 7f56e0bfe8
2 changed files with 195 additions and 61 deletions

View file

@ -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

View file

@ -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,
SDL_SetRenderLogicalPresentation(
gfx->renderer,
gfx->framebuffer_width,
gfx->framebuffer_height,
SDL_LOGICAL_PRESENTATION_INTEGER_SCALE);
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)",
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);
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;
}