improve sw renderer

This commit is contained in:
asrael 2026-01-21 23:19:50 -06:00
parent 415d424057
commit 39ee0fefb7
89 changed files with 9380 additions and 2307 deletions

View file

@ -1,4 +1,5 @@
#include "pxl8_3d_camera.h"
#include "pxl8_mem.h"
#include <math.h>
#include <stdlib.h>
@ -28,7 +29,7 @@ struct pxl8_3d_camera {
};
pxl8_3d_camera* pxl8_3d_camera_create(void) {
pxl8_3d_camera* cam = calloc(1, sizeof(pxl8_3d_camera));
pxl8_3d_camera* cam = pxl8_calloc(1, sizeof(pxl8_3d_camera));
if (!cam) return NULL;
cam->position = (pxl8_vec3){0, 0, 0};
@ -46,7 +47,7 @@ pxl8_3d_camera* pxl8_3d_camera_create(void) {
}
void pxl8_3d_camera_destroy(pxl8_3d_camera* cam) {
free(cam);
pxl8_free(cam);
}
void pxl8_3d_camera_lookat(pxl8_3d_camera* cam, pxl8_vec3 eye, pxl8_vec3 target, pxl8_vec3 up) {
@ -56,8 +57,8 @@ void pxl8_3d_camera_lookat(pxl8_3d_camera* cam, pxl8_vec3 eye, pxl8_vec3 target,
pxl8_vec3 forward = pxl8_vec3_normalize(pxl8_vec3_sub(target, eye));
cam->pitch = asinf(-forward.y);
cam->yaw = atan2f(forward.x, forward.z);
cam->pitch = asinf(forward.y);
cam->yaw = atan2f(-forward.x, -forward.z);
cam->roll = 0;
(void)up;
@ -104,9 +105,9 @@ pxl8_vec3 pxl8_3d_camera_get_forward(const pxl8_3d_camera* cam) {
f32 sy = sinf(cam->yaw);
return (pxl8_vec3){
cp * sy,
-sp,
cp * cy
-sy * cp,
sp,
-cy * cp
};
}
@ -121,7 +122,7 @@ pxl8_mat4 pxl8_3d_camera_get_projection(const pxl8_3d_camera* cam) {
if (cam->mode == PXL8_3D_CAMERA_PERSPECTIVE) {
return pxl8_mat4_perspective(cam->fov, cam->aspect, cam->near, cam->far);
} else {
return pxl8_mat4_ortho(
return pxl8_mat4_orthographic(
cam->ortho_left, cam->ortho_right,
cam->ortho_bottom, cam->ortho_top,
cam->near, cam->far
@ -183,8 +184,8 @@ void pxl8_3d_camera_follow(pxl8_3d_camera* cam, pxl8_vec3 target, pxl8_vec3 offs
cam->position = pxl8_vec3_lerp(cam->position, desired, t);
pxl8_vec3 forward = pxl8_vec3_normalize(pxl8_vec3_sub(target, cam->position));
cam->pitch = asinf(-forward.y);
cam->yaw = atan2f(forward.x, forward.z);
cam->pitch = asinf(forward.y);
cam->yaw = atan2f(-forward.x, -forward.z);
}
void pxl8_3d_camera_shake(pxl8_3d_camera* cam, f32 intensity, f32 duration) {
@ -212,3 +213,30 @@ void pxl8_3d_camera_update(pxl8_3d_camera* cam, f32 dt) {
}
}
}
pxl8_projected_point pxl8_3d_camera_world_to_screen(const pxl8_3d_camera* cam, pxl8_vec3 world_pos, u32 screen_width, u32 screen_height) {
pxl8_projected_point result = {0, 0, 0.0f, false};
if (!cam) return result;
pxl8_mat4 view = pxl8_3d_camera_get_view(cam);
pxl8_mat4 proj = pxl8_3d_camera_get_projection(cam);
pxl8_mat4 vp = pxl8_mat4_multiply(proj, view);
pxl8_vec4 clip = pxl8_mat4_multiply_vec4(vp, (pxl8_vec4){world_pos.x, world_pos.y, world_pos.z, 1.0f});
if (clip.w <= 0.0f) return result;
f32 inv_w = 1.0f / clip.w;
f32 ndc_x = clip.x * inv_w;
f32 ndc_y = clip.y * inv_w;
f32 ndc_z = clip.z * inv_w;
if (ndc_x < -1.0f || ndc_x > 1.0f || ndc_y < -1.0f || ndc_y > 1.0f) return result;
result.x = (i32)((ndc_x + 1.0f) * 0.5f * (f32)screen_width);
result.y = (i32)((1.0f - ndc_y) * 0.5f * (f32)screen_height);
result.depth = ndc_z;
result.visible = true;
return result;
}

View file

@ -32,6 +32,7 @@ pxl8_mat4 pxl8_3d_camera_get_view(const pxl8_3d_camera* cam);
void pxl8_3d_camera_blend(pxl8_3d_camera* dest, const pxl8_3d_camera* a, const pxl8_3d_camera* b, f32 t);
void pxl8_3d_camera_follow(pxl8_3d_camera* cam, pxl8_vec3 target, pxl8_vec3 offset, f32 smoothing, f32 dt);
pxl8_projected_point pxl8_3d_camera_world_to_screen(const pxl8_3d_camera* cam, pxl8_vec3 world_pos, u32 screen_width, u32 screen_height);
void pxl8_3d_camera_shake(pxl8_3d_camera* cam, f32 intensity, f32 duration);
void pxl8_3d_camera_update(pxl8_3d_camera* cam, f32 dt);

View file

@ -7,6 +7,7 @@
#include "pxl8_atlas.h"
#include "pxl8_gfx.h"
#include "pxl8_log.h"
#include "pxl8_mem.h"
#define PXL8_ANIM_MAX_STATES 32
@ -36,36 +37,36 @@ pxl8_anim* pxl8_anim_create(const u32* frame_ids, const u16* frame_durations, u1
return NULL;
}
pxl8_anim* anim = (pxl8_anim*)calloc(1, sizeof(pxl8_anim));
pxl8_anim* anim = (pxl8_anim*)pxl8_calloc(1, sizeof(pxl8_anim));
if (!anim) {
pxl8_error("Failed to allocate animation");
return NULL;
}
anim->frame_ids = (u32*)malloc(frame_count * sizeof(u32));
anim->frame_ids = (u32*)pxl8_malloc(frame_count * sizeof(u32));
if (!anim->frame_ids) {
pxl8_error("Failed to allocate frame IDs");
free(anim);
pxl8_free(anim);
return NULL;
}
memcpy(anim->frame_ids, frame_ids, frame_count * sizeof(u32));
if (frame_durations) {
anim->frame_durations = (u16*)malloc(frame_count * sizeof(u16));
anim->frame_durations = (u16*)pxl8_malloc(frame_count * sizeof(u16));
if (!anim->frame_durations) {
pxl8_error("Failed to allocate frame durations");
free(anim->frame_ids);
free(anim);
pxl8_free(anim->frame_ids);
pxl8_free(anim);
return NULL;
}
memcpy(anim->frame_durations, frame_durations, frame_count * sizeof(u16));
} else {
anim->frame_durations = (u16*)calloc(frame_count, sizeof(u16));
anim->frame_durations = (u16*)pxl8_calloc(frame_count, sizeof(u16));
if (!anim->frame_durations) {
pxl8_error("Failed to allocate frame durations");
free(anim->frame_ids);
free(anim);
pxl8_free(anim->frame_ids);
pxl8_free(anim);
return NULL;
}
for (u16 i = 0; i < frame_count; i++) {
@ -107,12 +108,12 @@ pxl8_anim* pxl8_anim_create_from_ase(pxl8_gfx* gfx, const char* path) {
return NULL;
}
u32* frame_ids = (u32*)malloc(ase_file.frame_count * sizeof(u32));
u16* frame_durations = (u16*)malloc(ase_file.frame_count * sizeof(u16));
u32* frame_ids = (u32*)pxl8_malloc(ase_file.frame_count * sizeof(u32));
u16* frame_durations = (u16*)pxl8_malloc(ase_file.frame_count * sizeof(u16));
if (!frame_ids || !frame_durations) {
pxl8_error("Failed to allocate frame arrays");
free(frame_ids);
free(frame_durations);
pxl8_free(frame_ids);
pxl8_free(frame_durations);
pxl8_ase_destroy(&ase_file);
return NULL;
}
@ -122,8 +123,8 @@ pxl8_anim* pxl8_anim_create_from_ase(pxl8_gfx* gfx, const char* path) {
result = pxl8_gfx_create_texture(gfx, frame->pixels, frame->width, frame->height);
if (result != PXL8_OK) {
pxl8_error("Failed to create texture for frame %u", i);
free(frame_ids);
free(frame_durations);
pxl8_free(frame_ids);
pxl8_free(frame_durations);
pxl8_ase_destroy(&ase_file);
return NULL;
}
@ -133,8 +134,8 @@ pxl8_anim* pxl8_anim_create_from_ase(pxl8_gfx* gfx, const char* path) {
pxl8_anim* anim = pxl8_anim_create(frame_ids, frame_durations, ase_file.frame_count);
free(frame_ids);
free(frame_durations);
pxl8_free(frame_ids);
pxl8_free(frame_durations);
pxl8_ase_destroy(&ase_file);
return anim;
@ -145,15 +146,15 @@ void pxl8_anim_destroy(pxl8_anim* anim) {
if (anim->state_machine) {
for (u16 i = 0; i < anim->state_machine->state_count; i++) {
free(anim->state_machine->states[i].name);
pxl8_free(anim->state_machine->states[i].name);
pxl8_anim_destroy(anim->state_machine->states[i].anim);
}
free(anim->state_machine);
pxl8_free(anim->state_machine);
}
free(anim->frame_ids);
free(anim->frame_durations);
free(anim);
pxl8_free(anim->frame_ids);
pxl8_free(anim->frame_durations);
pxl8_free(anim);
}
pxl8_result pxl8_anim_add_state(pxl8_anim* anim, const char* name, pxl8_anim* state_anim) {
@ -162,7 +163,7 @@ pxl8_result pxl8_anim_add_state(pxl8_anim* anim, const char* name, pxl8_anim* st
}
if (!anim->state_machine) {
anim->state_machine = (pxl8_anim_state_machine*)calloc(1, sizeof(pxl8_anim_state_machine));
anim->state_machine = (pxl8_anim_state_machine*)pxl8_calloc(1, sizeof(pxl8_anim_state_machine));
if (!anim->state_machine) {
return PXL8_ERROR_OUT_OF_MEMORY;
}

View file

@ -7,6 +7,7 @@
#include "pxl8_color.h"
#include "pxl8_log.h"
#include "pxl8_mem.h"
typedef struct pxl8_skyline_fit {
bool found;
@ -27,6 +28,8 @@ typedef struct pxl8_skyline {
struct pxl8_atlas {
u32 height, width;
u8* pixels;
u8* pixels_tiled;
u32 tiled_capacity, tiled_size;
bool dirty;
@ -103,7 +106,7 @@ static bool pxl8_skyline_add_rect(pxl8_skyline* skyline, pxl8_point pos, u32 w,
if (skyline->count - nodes_to_remove + 1 > skyline->capacity) {
u32 new_capacity = (skyline->count - nodes_to_remove + 1) * 2;
pxl8_skyline_node* new_nodes = (pxl8_skyline_node*)realloc(
pxl8_skyline_node* new_nodes = (pxl8_skyline_node*)pxl8_realloc(
skyline->nodes,
new_capacity * sizeof(pxl8_skyline_node)
);
@ -142,44 +145,44 @@ static void pxl8_skyline_compact(pxl8_skyline* skyline) {
}
pxl8_atlas* pxl8_atlas_create(u32 width, u32 height, pxl8_pixel_mode pixel_mode) {
pxl8_atlas* atlas = (pxl8_atlas*)calloc(1, sizeof(pxl8_atlas));
pxl8_atlas* atlas = (pxl8_atlas*)pxl8_calloc(1, sizeof(pxl8_atlas));
if (!atlas) return NULL;
atlas->height = height;
atlas->width = width;
i32 bytes_per_pixel = pxl8_bytes_per_pixel(pixel_mode);
atlas->pixels = (u8*)calloc(width * height, bytes_per_pixel);
atlas->pixels = (u8*)pxl8_calloc(width * height, bytes_per_pixel);
if (!atlas->pixels) {
free(atlas);
pxl8_free(atlas);
return NULL;
}
atlas->entry_capacity = PXL8_DEFAULT_ATLAS_ENTRY_CAPACITY;
atlas->entries = (pxl8_atlas_entry*)calloc(atlas->entry_capacity, sizeof(pxl8_atlas_entry));
atlas->entries = (pxl8_atlas_entry*)pxl8_calloc(atlas->entry_capacity, sizeof(pxl8_atlas_entry));
if (!atlas->entries) {
free(atlas->pixels);
free(atlas);
pxl8_free(atlas->pixels);
pxl8_free(atlas);
return NULL;
}
atlas->free_capacity = 16;
atlas->free_list = (u32*)calloc(atlas->free_capacity, sizeof(u32));
atlas->free_list = (u32*)pxl8_calloc(atlas->free_capacity, sizeof(u32));
if (!atlas->free_list) {
free(atlas->entries);
free(atlas->pixels);
free(atlas);
pxl8_free(atlas->entries);
pxl8_free(atlas->pixels);
pxl8_free(atlas);
return NULL;
}
atlas->skyline.capacity = 16;
atlas->skyline.nodes =
(pxl8_skyline_node*)calloc(atlas->skyline.capacity, sizeof(pxl8_skyline_node));
(pxl8_skyline_node*)pxl8_calloc(atlas->skyline.capacity, sizeof(pxl8_skyline_node));
if (!atlas->skyline.nodes) {
free(atlas->free_list);
free(atlas->entries);
free(atlas->pixels);
free(atlas);
pxl8_free(atlas->free_list);
pxl8_free(atlas->entries);
pxl8_free(atlas->pixels);
pxl8_free(atlas);
return NULL;
}
@ -192,11 +195,12 @@ pxl8_atlas* pxl8_atlas_create(u32 width, u32 height, pxl8_pixel_mode pixel_mode)
void pxl8_atlas_destroy(pxl8_atlas* atlas) {
if (!atlas) return;
free(atlas->entries);
free(atlas->free_list);
free(atlas->pixels);
free(atlas->skyline.nodes);
free(atlas);
pxl8_free(atlas->entries);
pxl8_free(atlas->free_list);
pxl8_free(atlas->pixels);
pxl8_free(atlas->pixels_tiled);
pxl8_free(atlas->skyline.nodes);
pxl8_free(atlas);
}
void pxl8_atlas_clear(pxl8_atlas* atlas, u32 preserve_count) {
@ -209,6 +213,13 @@ void pxl8_atlas_clear(pxl8_atlas* atlas, u32 preserve_count) {
atlas->entry_count = preserve_count;
atlas->free_count = 0;
if (preserve_count == 0) {
atlas->tiled_size = 0;
} else {
pxl8_atlas_entry* last = &atlas->entries[preserve_count - 1];
atlas->tiled_size = last->tiled_base + (u32)(last->w * last->h);
}
atlas->skyline.nodes[0] = (pxl8_skyline_node){0, 0, (i32)atlas->width};
atlas->skyline.count = 1;
@ -222,13 +233,13 @@ bool pxl8_atlas_expand(pxl8_atlas* atlas, pxl8_pixel_mode pixel_mode) {
u32 new_size = atlas->width * 2;
u32 old_width = atlas->width;
u8* new_pixels = (u8*)calloc(new_size * new_size, bytes_per_pixel);
u8* new_pixels = (u8*)pxl8_calloc(new_size * new_size, bytes_per_pixel);
if (!new_pixels) return false;
pxl8_skyline new_skyline;
new_skyline.nodes = (pxl8_skyline_node*)calloc(16, sizeof(pxl8_skyline_node));
new_skyline.nodes = (pxl8_skyline_node*)pxl8_calloc(16, sizeof(pxl8_skyline_node));
if (!new_skyline.nodes) {
free(new_pixels);
pxl8_free(new_pixels);
return false;
}
@ -248,8 +259,8 @@ bool pxl8_atlas_expand(pxl8_atlas* atlas, pxl8_pixel_mode pixel_mode) {
);
if (!fit.found) {
free(new_skyline.nodes);
free(new_pixels);
pxl8_free(new_skyline.nodes);
pxl8_free(new_pixels);
return false;
}
@ -269,15 +280,15 @@ bool pxl8_atlas_expand(pxl8_atlas* atlas, pxl8_pixel_mode pixel_mode) {
atlas->entries[i].y = fit.pos.y;
if (!pxl8_skyline_add_rect(&new_skyline, fit.pos, atlas->entries[i].w, atlas->entries[i].h)) {
free(new_skyline.nodes);
free(new_pixels);
pxl8_free(new_skyline.nodes);
pxl8_free(new_pixels);
return false;
}
pxl8_skyline_compact(&new_skyline);
}
free(atlas->pixels);
free(atlas->skyline.nodes);
pxl8_free(atlas->pixels);
pxl8_free(atlas->skyline.nodes);
atlas->pixels = new_pixels;
atlas->skyline = new_skyline;
@ -316,7 +327,7 @@ u32 pxl8_atlas_add_texture(
} else {
if (atlas->entry_count >= atlas->entry_capacity) {
u32 new_capacity = atlas->entry_capacity * 2;
pxl8_atlas_entry* new_entries = (pxl8_atlas_entry*)realloc(
pxl8_atlas_entry* new_entries = (pxl8_atlas_entry*)pxl8_realloc(
atlas->entries,
new_capacity * sizeof(pxl8_atlas_entry)
);
@ -334,6 +345,7 @@ u32 pxl8_atlas_add_texture(
entry->y = fit.pos.y;
entry->w = w;
entry->h = h;
entry->log2_w = pxl8_log2(w);
i32 bytes_per_pixel = pxl8_bytes_per_pixel(pixel_mode);
for (u32 y = 0; y < h; y++) {
@ -349,6 +361,30 @@ u32 pxl8_atlas_add_texture(
}
}
u32 tiled_tex_size = w * h;
u32 new_tiled_size = atlas->tiled_size + tiled_tex_size;
if (new_tiled_size > atlas->tiled_capacity) {
u32 new_cap = atlas->tiled_capacity ? atlas->tiled_capacity * 2 : 4096;
while (new_cap < new_tiled_size) new_cap *= 2;
u8* new_tiled = (u8*)pxl8_realloc(atlas->pixels_tiled, new_cap);
if (!new_tiled) {
entry->active = false;
return UINT32_MAX;
}
atlas->pixels_tiled = new_tiled;
atlas->tiled_capacity = new_cap;
}
entry->tiled_base = atlas->tiled_size;
u8* tiled_dst = atlas->pixels_tiled + entry->tiled_base;
for (u32 ty = 0; ty < h; ty++) {
for (u32 tx = 0; tx < w; tx++) {
u32 tiled_offset = pxl8_tile_addr(tx, ty, entry->log2_w);
tiled_dst[tiled_offset] = pixels[ty * w + tx];
}
}
atlas->tiled_size = new_tiled_size;
if (!pxl8_skyline_add_rect(&atlas->skyline, fit.pos, w, h)) {
entry->active = false;
return UINT32_MAX;
@ -377,6 +413,10 @@ const u8* pxl8_atlas_get_pixels(const pxl8_atlas* atlas) {
return atlas ? atlas->pixels : NULL;
}
const u8* pxl8_atlas_get_pixels_tiled(const pxl8_atlas* atlas) {
return atlas ? atlas->pixels_tiled : NULL;
}
u32 pxl8_atlas_get_width(const pxl8_atlas* atlas) {
return atlas ? atlas->width : 0;
}

View file

@ -8,28 +8,42 @@ typedef struct pxl8_atlas_entry {
bool active;
u32 texture_id;
i32 x, y, w, h;
u32 tiled_base;
u8 log2_w;
} pxl8_atlas_entry;
static inline u32 pxl8_tile_addr(u32 u, u32 v, u8 log2_w) {
u32 tile_y = v >> 3;
u32 tile_x = u >> 3;
u32 local_y = v & 7;
u32 local_x = u & 7;
return (tile_y << (log2_w + 3)) | (tile_x << 6) | (local_y << 3) | local_x;
}
static inline u8 pxl8_log2(u32 v) {
u8 r = 0;
while (v >>= 1) r++;
return r;
}
#ifdef __cplusplus
extern "C" {
#endif
u32 pxl8_atlas_add_texture(pxl8_atlas* atlas, const u8* pixels, u32 w, u32 h, pxl8_pixel_mode pixel_mode);
void pxl8_atlas_clear(pxl8_atlas* atlas, u32 preserve_count);
pxl8_atlas* pxl8_atlas_create(u32 width, u32 height, pxl8_pixel_mode pixel_mode);
void pxl8_atlas_destroy(pxl8_atlas* atlas);
bool pxl8_atlas_expand(pxl8_atlas* atlas, pxl8_pixel_mode pixel_mode);
const pxl8_atlas_entry* pxl8_atlas_get_entry(const pxl8_atlas* atlas, u32 id);
u32 pxl8_atlas_get_entry_count(const pxl8_atlas* atlas);
u32 pxl8_atlas_get_height(const pxl8_atlas* atlas);
const u8* pxl8_atlas_get_pixels(const pxl8_atlas* atlas);
const u8* pxl8_atlas_get_pixels_tiled(const pxl8_atlas* atlas);
u32 pxl8_atlas_get_width(const pxl8_atlas* atlas);
bool pxl8_atlas_is_dirty(const pxl8_atlas* atlas);
void pxl8_atlas_mark_clean(pxl8_atlas* atlas);
u32 pxl8_atlas_add_texture(pxl8_atlas* atlas, const u8* pixels, u32 w, u32 h, pxl8_pixel_mode pixel_mode);
void pxl8_atlas_clear(pxl8_atlas* atlas, u32 preserve_count);
bool pxl8_atlas_expand(pxl8_atlas* atlas, pxl8_pixel_mode pixel_mode);
#ifdef __cplusplus
}
#endif

View file

@ -1,194 +0,0 @@
#include "pxl8_blend.h"
#include "pxl8_colormap.h"
#include <stdlib.h>
struct pxl8_palette_cube {
u8 colors[PXL8_PALETTE_SIZE * 3];
u8 table[PXL8_CUBE_ENTRIES];
u8 stable[PXL8_CUBE_ENTRIES];
};
struct pxl8_additive_table {
u8 table[PXL8_BLEND_TABLE_SIZE];
};
struct pxl8_overbright_table {
u8 table[PXL8_OVERBRIGHT_TABLE_SIZE];
};
static u8 find_closest_stable(const pxl8_palette* pal, u8 r, u8 g, u8 b) {
u8 best_idx = 1;
u32 best_dist = 0xFFFFFFFF;
u8 dynamic_end = PXL8_DYNAMIC_RANGE_START + PXL8_DYNAMIC_RANGE_COUNT;
for (u32 i = 1; i < PXL8_FULLBRIGHT_START; i++) {
if (i >= PXL8_DYNAMIC_RANGE_START && i < dynamic_end) {
continue;
}
u8 pr, pg, pb;
pxl8_palette_get_rgb(pal, (u8)i, &pr, &pg, &pb);
i32 dr = (i32)r - (i32)pr;
i32 dg = (i32)g - (i32)pg;
i32 db = (i32)b - (i32)pb;
u32 dist = (u32)(dr * dr + dg * dg + db * db);
if (dist < best_dist) {
best_dist = dist;
best_idx = (u8)i;
if (dist == 0) break;
}
}
return best_idx;
}
pxl8_palette_cube* pxl8_palette_cube_create(const pxl8_palette* pal) {
pxl8_palette_cube* cube = calloc(1, sizeof(pxl8_palette_cube));
if (!cube) return NULL;
pxl8_palette_cube_rebuild(cube, pal);
return cube;
}
void pxl8_palette_cube_destroy(pxl8_palette_cube* cube) {
free(cube);
}
void pxl8_palette_cube_rebuild(pxl8_palette_cube* cube, const pxl8_palette* pal) {
if (!cube || !pal) return;
for (u32 i = 0; i < PXL8_PALETTE_SIZE; i++) {
u8 r, g, b;
pxl8_palette_get_rgb(pal, (u8)i, &r, &g, &b);
cube->colors[i * 3 + 0] = r;
cube->colors[i * 3 + 1] = g;
cube->colors[i * 3 + 2] = b;
}
for (u32 bi = 0; bi < PXL8_CUBE_SIZE; bi++) {
for (u32 gi = 0; gi < PXL8_CUBE_SIZE; gi++) {
for (u32 ri = 0; ri < PXL8_CUBE_SIZE; ri++) {
u32 idx = (bi * PXL8_CUBE_SIZE * PXL8_CUBE_SIZE) + (gi * PXL8_CUBE_SIZE) + ri;
u8 r8 = (u8)((ri * 255) / (PXL8_CUBE_SIZE - 1));
u8 g8 = (u8)((gi * 255) / (PXL8_CUBE_SIZE - 1));
u8 b8 = (u8)((bi * 255) / (PXL8_CUBE_SIZE - 1));
cube->table[idx] = pxl8_palette_find_closest(pal, r8, g8, b8);
cube->stable[idx] = find_closest_stable(pal, r8, g8, b8);
}
}
}
}
u8 pxl8_palette_cube_lookup(const pxl8_palette_cube* cube, u8 r, u8 g, u8 b) {
u32 ri = (r * (PXL8_CUBE_SIZE - 1)) / 255;
u32 gi = (g * (PXL8_CUBE_SIZE - 1)) / 255;
u32 bi = (b * (PXL8_CUBE_SIZE - 1)) / 255;
u32 idx = (bi * PXL8_CUBE_SIZE * PXL8_CUBE_SIZE) + (gi * PXL8_CUBE_SIZE) + ri;
return cube->table[idx];
}
u8 pxl8_palette_cube_lookup_stable(const pxl8_palette_cube* cube, u8 r, u8 g, u8 b) {
u32 ri = (r * (PXL8_CUBE_SIZE - 1)) / 255;
u32 gi = (g * (PXL8_CUBE_SIZE - 1)) / 255;
u32 bi = (b * (PXL8_CUBE_SIZE - 1)) / 255;
u32 idx = (bi * PXL8_CUBE_SIZE * PXL8_CUBE_SIZE) + (gi * PXL8_CUBE_SIZE) + ri;
return cube->stable[idx];
}
void pxl8_palette_cube_get_rgb(const pxl8_palette_cube* cube, u8 idx, u8* r, u8* g, u8* b) {
*r = cube->colors[idx * 3 + 0];
*g = cube->colors[idx * 3 + 1];
*b = cube->colors[idx * 3 + 2];
}
pxl8_additive_table* pxl8_additive_table_create(const pxl8_palette* pal) {
pxl8_additive_table* table = calloc(1, sizeof(pxl8_additive_table));
if (!table) return NULL;
pxl8_additive_table_rebuild(table, pal);
return table;
}
void pxl8_additive_table_destroy(pxl8_additive_table* table) {
free(table);
}
void pxl8_additive_table_rebuild(pxl8_additive_table* table, const pxl8_palette* pal) {
if (!table || !pal) return;
for (u32 src = 0; src < 256; src++) {
u8 sr, sg, sb;
pxl8_palette_get_rgb(pal, (u8)src, &sr, &sg, &sb);
for (u32 dst = 0; dst < 256; dst++) {
u32 idx = src * 256 + dst;
if (src == PXL8_TRANSPARENT) {
table->table[idx] = (u8)dst;
continue;
}
u8 dr, dg, db;
pxl8_palette_get_rgb(pal, (u8)dst, &dr, &dg, &db);
u16 ar = (u16)sr + (u16)dr;
u16 ag = (u16)sg + (u16)dg;
u16 ab = (u16)sb + (u16)db;
if (ar > 255) ar = 255;
if (ag > 255) ag = 255;
if (ab > 255) ab = 255;
table->table[idx] = find_closest_stable(pal, (u8)ar, (u8)ag, (u8)ab);
}
}
}
u8 pxl8_additive_blend(const pxl8_additive_table* table, u8 src, u8 dst) {
return table->table[(u32)src * 256 + dst];
}
pxl8_overbright_table* pxl8_overbright_table_create(const pxl8_palette* pal) {
pxl8_overbright_table* table = calloc(1, sizeof(pxl8_overbright_table));
if (!table) return NULL;
pxl8_overbright_table_rebuild(table, pal);
return table;
}
void pxl8_overbright_table_destroy(pxl8_overbright_table* table) {
free(table);
}
void pxl8_overbright_table_rebuild(pxl8_overbright_table* table, const pxl8_palette* pal) {
if (!table || !pal) return;
for (u32 level = 0; level < PXL8_OVERBRIGHT_LEVELS; level++) {
f32 overbright = (f32)level / (f32)(PXL8_OVERBRIGHT_LEVELS - 1);
for (u32 pal_idx = 0; pal_idx < 256; pal_idx++) {
u32 idx = level * 256 + pal_idx;
if (pal_idx == PXL8_TRANSPARENT) {
table->table[idx] = PXL8_TRANSPARENT;
continue;
}
u8 r, g, b;
pxl8_palette_get_rgb(pal, (u8)pal_idx, &r, &g, &b);
u8 or = (u8)(r + (255 - r) * overbright);
u8 og = (u8)(g + (255 - g) * overbright);
u8 ob = (u8)(b + (255 - b) * overbright);
table->table[idx] = find_closest_stable(pal, or, og, ob);
}
}
}
u8 pxl8_overbright_lookup(const pxl8_overbright_table* table, u8 pal_idx, f32 emissive) {
u32 level = (u32)(emissive * (PXL8_OVERBRIGHT_LEVELS - 1));
if (level >= PXL8_OVERBRIGHT_LEVELS) level = PXL8_OVERBRIGHT_LEVELS - 1;
return table->table[level * 256 + pal_idx];
}

View file

@ -1,39 +0,0 @@
#pragma once
#include "pxl8_palette.h"
#include "pxl8_types.h"
#ifdef __cplusplus
extern "C" {
#endif
#define PXL8_CUBE_SIZE 32
#define PXL8_CUBE_ENTRIES (PXL8_CUBE_SIZE * PXL8_CUBE_SIZE * PXL8_CUBE_SIZE)
#define PXL8_BLEND_TABLE_SIZE (256 * 256)
#define PXL8_OVERBRIGHT_LEVELS 16
#define PXL8_OVERBRIGHT_TABLE_SIZE (256 * PXL8_OVERBRIGHT_LEVELS)
typedef struct pxl8_additive_table pxl8_additive_table;
typedef struct pxl8_overbright_table pxl8_overbright_table;
typedef struct pxl8_palette_cube pxl8_palette_cube;
pxl8_additive_table* pxl8_additive_table_create(const pxl8_palette* pal);
void pxl8_additive_table_destroy(pxl8_additive_table* table);
void pxl8_additive_table_rebuild(pxl8_additive_table* table, const pxl8_palette* pal);
u8 pxl8_additive_blend(const pxl8_additive_table* table, u8 src, u8 dst);
pxl8_overbright_table* pxl8_overbright_table_create(const pxl8_palette* pal);
void pxl8_overbright_table_destroy(pxl8_overbright_table* table);
void pxl8_overbright_table_rebuild(pxl8_overbright_table* table, const pxl8_palette* pal);
u8 pxl8_overbright_lookup(const pxl8_overbright_table* table, u8 pal_idx, f32 emissive);
pxl8_palette_cube* pxl8_palette_cube_create(const pxl8_palette* pal);
void pxl8_palette_cube_destroy(pxl8_palette_cube* cube);
void pxl8_palette_cube_rebuild(pxl8_palette_cube* cube, const pxl8_palette* pal);
u8 pxl8_palette_cube_lookup(const pxl8_palette_cube* cube, u8 r, u8 g, u8 b);
u8 pxl8_palette_cube_lookup_stable(const pxl8_palette_cube* cube, u8 r, u8 g, u8 b);
void pxl8_palette_cube_get_rgb(const pxl8_palette_cube* cube, u8 idx, u8* r, u8* g, u8* b);
#ifdef __cplusplus
}
#endif

View file

@ -1,42 +1,12 @@
#include "pxl8_colormap.h"
#include <string.h>
static void rgb_to_hsl(u8 r, u8 g, u8 b, i32* h, i32* s, i32* l) {
i32 max = r > g ? (r > b ? r : b) : (g > b ? g : b);
i32 min = r < g ? (r < b ? r : b) : (g < b ? g : b);
i32 chroma = max - min;
*l = (max + min) / 2;
if (chroma == 0) {
*h = 0;
*s = 0;
} else {
i32 denom = 255 - (*l > 127 ? 2 * (*l) - 255 : 255 - 2 * (*l));
if (denom <= 0) denom = 1;
*s = (chroma * 255) / denom;
if (*s > 255) *s = 255;
if (max == (i32)r) {
*h = (((i32)g - (i32)b) * 60) / chroma;
if (*h < 0) *h += 360;
} else if (max == (i32)g) {
*h = 120 + (((i32)b - (i32)r) * 60) / chroma;
} else {
*h = 240 + (((i32)r - (i32)g) * 60) / chroma;
}
}
}
static u8 find_closest_color(const u32* palette, u8 target_r, u8 target_g, u8 target_b) {
u8 best_idx = 1;
u32 best_dist = 0xFFFFFFFF;
u8 dynamic_end = PXL8_DYNAMIC_RANGE_START + PXL8_DYNAMIC_RANGE_COUNT;
i32 th, ts, tl;
rgb_to_hsl(target_r, target_g, target_b, &th, &ts, &tl);
for (u32 i = 1; i < PXL8_FULLBRIGHT_START; i++) {
if (i >= PXL8_DYNAMIC_RANGE_START && i < dynamic_end) {
continue;
@ -47,17 +17,10 @@ static u8 find_closest_color(const u32* palette, u8 target_r, u8 target_g, u8 ta
u8 pg = (c >> 8) & 0xFF;
u8 pb = (c >> 16) & 0xFF;
i32 ph, ps, pl;
rgb_to_hsl(pr, pg, pb, &ph, &ps, &pl);
i32 dh = th - ph;
if (dh > 180) dh -= 360;
if (dh < -180) dh += 360;
i32 ds = ts - ps;
i32 dl = tl - pl;
u32 dist = (u32)(dh * dh * 4 + ds * ds + dl * dl);
i32 dr = (i32)target_r - (i32)pr;
i32 dg = (i32)target_g - (i32)pg;
i32 db = (i32)target_b - (i32)pb;
u32 dist = (u32)(dr * dr + dg * dg + db * db);
if (dist < best_dist) {
best_dist = dist;
@ -69,26 +32,19 @@ static u8 find_closest_color(const u32* palette, u8 target_r, u8 target_g, u8 ta
return best_idx;
}
void pxl8_colormap_generate(pxl8_colormap* cm, const u32* palette, const pxl8_level_tint* tint) {
if (!cm || !palette) return;
void pxl8_set_colormap(pxl8_colormap* cm, const u8* data, u32 size) {
if (!cm || !data || size == 0) return;
u32 copy_size = size > PXL8_COLORMAP_SIZE ? PXL8_COLORMAP_SIZE : size;
memcpy(cm->table, data, copy_size);
}
u8 dark_r, dark_g, dark_b;
if (tint && tint->tint_strength > 0.0f) {
f32 t = tint->tint_strength;
f32 inv = 1.0f - t;
dark_r = (u8)(tint->dark_r * inv + tint->tint_r * t);
dark_g = (u8)(tint->dark_g * inv + tint->tint_g * t);
dark_b = (u8)(tint->dark_b * inv + tint->tint_b * t);
} else if (tint) {
dark_r = tint->dark_r;
dark_g = tint->dark_g;
dark_b = tint->dark_b;
} else {
dark_r = dark_g = dark_b = 0;
}
static void generate_light_table(pxl8_colormap* cm, const u32* palette, pxl8_light_color light_color) {
pxl8_rgb light = pxl8_light_colors[light_color];
u32 base_row = (u32)light_color * PXL8_LIGHT_LEVELS;
for (u32 light = 0; light < PXL8_LIGHT_LEVELS; light++) {
f32 brightness = (f32)light / (f32)(PXL8_LIGHT_LEVELS - 1);
for (u32 level = 0; level < PXL8_LIGHT_LEVELS; level++) {
f32 brightness = (f32)level / (f32)(PXL8_LIGHT_LEVELS - 1);
u32 row = base_row + level;
for (u32 pal_idx = 0; pal_idx < 256; pal_idx++) {
u8 result_idx;
@ -103,14 +59,65 @@ void pxl8_colormap_generate(pxl8_colormap* cm, const u32* palette, const pxl8_le
u8 g = (c >> 8) & 0xFF;
u8 b = (c >> 16) & 0xFF;
u8 target_r = (u8)(dark_r + (r - dark_r) * brightness);
u8 target_g = (u8)(dark_g + (g - dark_g) * brightness);
u8 target_b = (u8)(dark_b + (b - dark_b) * brightness);
f32 lr = (f32)light.r / 255.0f;
f32 lg = (f32)light.g / 255.0f;
f32 lb = (f32)light.b / 255.0f;
u8 target_r = (u8)(r * brightness * lr);
u8 target_g = (u8)(g * brightness * lg);
u8 target_b = (u8)(b * brightness * lb);
result_idx = find_closest_color(palette, target_r, target_g, target_b);
}
cm->table[light * 256 + pal_idx] = result_idx;
cm->table[row * 256 + pal_idx] = result_idx;
}
}
}
static void generate_blend_table(pxl8_colormap* cm, const u32* palette) {
for (u32 src = 0; src < 256; src++) {
u32 row = PXL8_LIGHT_ROWS + src;
u8 sr, sg, sb;
if (src == PXL8_TRANSPARENT) {
sr = sg = sb = 0;
} else {
u32 sc = palette[src];
sr = sc & 0xFF;
sg = (sc >> 8) & 0xFF;
sb = (sc >> 16) & 0xFF;
}
for (u32 dst = 0; dst < 256; dst++) {
u8 result_idx;
if (src == PXL8_TRANSPARENT) {
result_idx = (u8)dst;
} else {
u32 dc = palette[dst];
u8 dr = dc & 0xFF;
u8 dg = (dc >> 8) & 0xFF;
u8 db = (dc >> 16) & 0xFF;
u8 blend_r = (u8)((sr + dr) / 2);
u8 blend_g = (u8)((sg + dg) / 2);
u8 blend_b = (u8)((sb + db) / 2);
result_idx = find_closest_color(palette, blend_r, blend_g, blend_b);
}
cm->table[row * 256 + dst] = result_idx;
}
}
}
void pxl8_colormap_generate(pxl8_colormap* cm, const u32* palette) {
if (!cm || !palette) return;
for (u32 light = 0; light < PXL8_LIGHT_COLORS; light++) {
generate_light_table(cm, palette, (pxl8_light_color)light);
}
generate_blend_table(cm, palette);
}

View file

@ -7,35 +7,65 @@
extern "C" {
#endif
#define PXL8_LIGHT_LEVELS 64
#define PXL8_COLORMAP_SIZE (256 * PXL8_LIGHT_LEVELS)
#define PXL8_LIGHT_COLORS 8
#define PXL8_LIGHT_LEVELS 8
#define PXL8_LIGHT_ROWS (PXL8_LIGHT_COLORS * PXL8_LIGHT_LEVELS)
#define PXL8_BLEND_ROWS 256
#define PXL8_COLORMAP_ROWS (PXL8_LIGHT_ROWS + PXL8_BLEND_ROWS)
#define PXL8_COLORMAP_SIZE (256 * PXL8_COLORMAP_ROWS)
#define PXL8_FULLBRIGHT_START 240
#define PXL8_TRANSPARENT 0
#define PXL8_DYNAMIC_RANGE_START 144
#define PXL8_DYNAMIC_RANGE_COUNT 16
typedef enum {
PXL8_LIGHT_WHITE = 0,
PXL8_LIGHT_RED = 1,
PXL8_LIGHT_ORANGE = 2,
PXL8_LIGHT_YELLOW = 3,
PXL8_LIGHT_GREEN = 4,
PXL8_LIGHT_CYAN = 5,
PXL8_LIGHT_BLUE = 6,
PXL8_LIGHT_PURPLE = 7,
} pxl8_light_color;
typedef struct {
u8 r, g, b;
} pxl8_rgb;
static const pxl8_rgb pxl8_light_colors[PXL8_LIGHT_COLORS] = {
{255, 255, 255},
{255, 64, 64},
{255, 160, 64},
{255, 255, 64},
{64, 255, 64},
{64, 255, 255},
{64, 64, 255},
{255, 64, 255},
};
typedef struct {
u8 table[PXL8_COLORMAP_SIZE];
} pxl8_colormap;
typedef struct {
u8 dark_r, dark_g, dark_b;
u8 tint_r, tint_g, tint_b;
f32 tint_strength;
} pxl8_level_tint;
void pxl8_colormap_generate(pxl8_colormap* cm, const u32* palette);
void pxl8_set_colormap(pxl8_colormap* cm, const u8* data, u32 size);
void pxl8_colormap_generate(pxl8_colormap* cm, const u32* palette, const pxl8_level_tint* tint);
static inline u8 pxl8_colormap_lookup(const pxl8_colormap* cm, u8 pal_idx, u8 light) {
u32 light_idx = light >> 2;
return cm->table[light_idx * 256 + pal_idx];
static inline u8 pxl8_colormap_lookup(const pxl8_colormap* cm, u8 pal_idx, pxl8_light_color light_color, u8 intensity) {
u32 light_row = ((u32)light_color << 3) + (intensity >> 5);
return cm->table[(light_row << 8) + pal_idx];
}
static inline u8 pxl8_colormap_lookup_dithered(const pxl8_colormap* cm, u8 pal_idx, u8 light, u32 x, u32 y) {
u8 dithered = pxl8_dither_light(light, x, y);
u32 light_idx = dithered >> 2;
return cm->table[light_idx * 256 + pal_idx];
static inline u8 pxl8_colormap_lookup_dithered(const pxl8_colormap* cm, u8 pal_idx, pxl8_light_color light_color, u8 intensity, u32 x, u32 y) {
u8 dithered = pxl8_dither_light(intensity, x, y);
u32 light_row = ((u32)light_color << 3) + (dithered >> 5);
return cm->table[(light_row << 8) + pal_idx];
}
static inline u8 pxl8_colormap_blend(const pxl8_colormap* cm, u8 src, u8 dst) {
u32 blend_row = PXL8_LIGHT_ROWS + src;
return cm->table[(blend_row << 8) + dst];
}
#ifdef __cplusplus

View file

@ -1,10 +1,10 @@
#include "pxl8_cpu.h"
#include "pxl8_mem.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "pxl8_simd.h"
struct pxl8_cpu_render_target {
u8* framebuffer;
u32 height;
@ -72,8 +72,8 @@ static pxl8_light_result calc_vertex_light(
if (sky_factor < 0.0f) sky_factor = 0.0f;
intensity += sky_factor * frame->uniforms.celestial_intensity * 0.3f;
for (u32 i = 0; i < frame->uniforms.num_lights; i++) {
const pxl8_light* light = &frame->uniforms.lights[i];
for (u32 i = 0; i < frame->lights_count; i++) {
const pxl8_light* light = &frame->lights[i];
f32 contrib = calc_light_intensity(light, world_pos, normal);
if (contrib > 0.0f) {
intensity += contrib;
@ -119,6 +119,7 @@ struct pxl8_cpu_backend {
pxl8_cpu_render_target* target_stack[PXL8_MAX_TARGET_STACK];
u32 target_stack_depth;
const pxl8_colormap* colormap;
const pxl8_palette_cube* palette_cube;
const u32* palette;
pxl8_3d_frame frame;
pxl8_mat4 mvp;
@ -173,7 +174,7 @@ static inline void clip_line_2d(i32* x0, i32* y0, i32* x1, i32* y1, i32 w, i32 h
}
pxl8_cpu_backend* pxl8_cpu_create(u32 width, u32 height) {
pxl8_cpu_backend* cpu = calloc(1, sizeof(pxl8_cpu_backend));
pxl8_cpu_backend* cpu = pxl8_calloc(1, sizeof(pxl8_cpu_backend));
if (!cpu) return NULL;
pxl8_cpu_render_target_desc desc = {
@ -184,7 +185,7 @@ pxl8_cpu_backend* pxl8_cpu_create(u32 width, u32 height) {
};
pxl8_cpu_render_target* base_target = pxl8_cpu_create_render_target(&desc);
if (!base_target) {
free(cpu);
pxl8_free(cpu);
return NULL;
}
@ -193,10 +194,10 @@ pxl8_cpu_backend* pxl8_cpu_create(u32 width, u32 height) {
cpu->current_target = base_target;
cpu->output_size = width * height;
cpu->output = calloc(cpu->output_size, sizeof(u32));
cpu->output = pxl8_calloc(cpu->output_size, sizeof(u32));
if (!cpu->output) {
pxl8_cpu_destroy_render_target(base_target);
free(cpu);
pxl8_free(cpu);
return NULL;
}
@ -208,14 +209,14 @@ void pxl8_cpu_destroy(pxl8_cpu_backend* cpu) {
for (u32 i = 0; i < cpu->target_stack_depth; i++) {
pxl8_cpu_destroy_render_target(cpu->target_stack[i]);
}
free(cpu->output);
free(cpu);
pxl8_free(cpu->output);
pxl8_free(cpu);
}
void pxl8_cpu_begin_frame(pxl8_cpu_backend* cpu, const pxl8_3d_frame* frame) {
if (!cpu || !frame) return;
cpu->frame = *frame;
cpu->mvp = pxl8_mat4_mul(frame->projection, frame->view);
cpu->mvp = pxl8_mat4_multiply(frame->projection, frame->view);
}
void pxl8_cpu_end_frame(pxl8_cpu_backend* cpu) {
@ -231,14 +232,12 @@ void pxl8_cpu_clear(pxl8_cpu_backend* cpu, u8 color) {
void pxl8_cpu_clear_depth(pxl8_cpu_backend* cpu) {
if (!cpu || !cpu->current_target) return;
pxl8_cpu_render_target* render_target = cpu->current_target;
u32 count = render_target->width * render_target->height;
if (render_target->zbuffer) {
u32 count = render_target->width * render_target->height;
for (u32 i = 0; i < count; i++) {
render_target->zbuffer[i] = 0xFFFF;
}
memset(render_target->zbuffer, 0xFF, count * sizeof(u16));
}
if (render_target->light_accum) {
memset(render_target->light_accum, 0, render_target->width * render_target->height * sizeof(u32));
memset(render_target->light_accum, 0, count * sizeof(u32));
}
}
@ -246,6 +245,10 @@ void pxl8_cpu_set_colormap(pxl8_cpu_backend* cpu, const pxl8_colormap* cm) {
if (cpu) cpu->colormap = cm;
}
void pxl8_cpu_set_palette_cube(pxl8_cpu_backend* cpu, const pxl8_palette_cube* cube) {
if (cpu) cpu->palette_cube = cube;
}
void pxl8_cpu_set_palette(pxl8_cpu_backend* cpu, const u32* palette) {
if (cpu) cpu->palette = palette;
}
@ -366,8 +369,8 @@ void pxl8_cpu_draw_line_3d(pxl8_cpu_backend* cpu, pxl8_vec3 v0, pxl8_vec3 v1, u8
if (!cpu || !cpu->current_target) return;
pxl8_cpu_render_target* render_target = cpu->current_target;
pxl8_vec4 c0 = pxl8_mat4_mul_vec4(cpu->mvp, (pxl8_vec4){v0.x, v0.y, v0.z, 1.0f});
pxl8_vec4 c1 = pxl8_mat4_mul_vec4(cpu->mvp, (pxl8_vec4){v1.x, v1.y, v1.z, 1.0f});
pxl8_vec4 c0 = pxl8_mat4_multiply_vec4(cpu->mvp, (pxl8_vec4){v0.x, v0.y, v0.z, 1.0f});
pxl8_vec4 c1 = pxl8_mat4_multiply_vec4(cpu->mvp, (pxl8_vec4){v1.x, v1.y, v1.z, 1.0f});
if (c0.w <= 0.0f || c1.w <= 0.0f) return;
@ -699,6 +702,22 @@ static void rasterize_triangle_opaque(
f32 v_end = (vw + dvw * steps) * pw_end;
f32 l_start = lw * pw_start;
f32 l_end = (lw + d_lw * steps) * pw_end;
f32 fog_density = cpu->frame.uniforms.fog_density;
if (fog_density > 0.0f) {
f32 z_end_fog = z + dz * steps;
f32 t_start = (z + 1.0f) * 0.5f;
f32 t_end = (z_end_fog + 1.0f) * 0.5f;
if (t_start < 0.0f) t_start = 0.0f;
if (t_end < 0.0f) t_end = 0.0f;
f32 fog_start = t_start * t_start * fog_density;
f32 fog_end = t_end * t_end * fog_density;
if (fog_start > 1.0f) fog_start = 1.0f;
if (fog_end > 1.0f) fog_end = 1.0f;
l_start *= (1.0f - fog_start);
l_end *= (1.0f - fog_end);
}
if (l_start > 255.0f) l_start = 255.0f;
if (l_end > 255.0f) l_end = 255.0f;
@ -727,7 +746,7 @@ static void rasterize_triangle_opaque(
if (dither) {
light = pxl8_dither_light(light, (u32)px, (u32)y);
}
u8 pal_idx = cpu->colormap ? pxl8_colormap_lookup(cpu->colormap, tex_idx, light) : tex_idx;
u8 pal_idx = cpu->colormap ? pxl8_colormap_lookup(cpu->colormap, tex_idx, PXL8_LIGHT_WHITE, light) : tex_idx;
prow[px] = pal_idx;
zrow[px] = z16;
if (light_accum) {
@ -948,6 +967,22 @@ static void rasterize_triangle_alpha(
f32 v_end = (vw + dvw * steps) * pw_end;
f32 l_start = lw * pw_start;
f32 l_end = (lw + d_lw * steps) * pw_end;
f32 fog_density = cpu->frame.uniforms.fog_density;
if (fog_density > 0.0f) {
f32 z_end_fog = z + dz * steps;
f32 t_start = (z + 1.0f) * 0.5f;
f32 t_end = (z_end_fog + 1.0f) * 0.5f;
if (t_start < 0.0f) t_start = 0.0f;
if (t_end < 0.0f) t_end = 0.0f;
f32 fog_start = t_start * t_start * fog_density;
f32 fog_end = t_end * t_end * fog_density;
if (fog_start > 1.0f) fog_start = 1.0f;
if (fog_end > 1.0f) fog_end = 1.0f;
l_start *= (1.0f - fog_start);
l_end *= (1.0f - fog_end);
}
if (l_start > 255.0f) l_start = 255.0f;
if (l_end > 255.0f) l_end = 255.0f;
@ -974,7 +1009,7 @@ static void rasterize_triangle_alpha(
if (dither) {
light = pxl8_dither_light(light, (u32)px, (u32)y);
}
u8 src_idx = cpu->colormap ? pxl8_colormap_lookup(cpu->colormap, tex_idx, light) : tex_idx;
u8 src_idx = cpu->colormap ? pxl8_colormap_lookup(cpu->colormap, tex_idx, PXL8_LIGHT_WHITE, light) : tex_idx;
if (mat_alpha >= 128) {
prow[px] = src_idx;
@ -1002,11 +1037,42 @@ static void rasterize_triangle_alpha(
}
}
static void rasterize_triangle_wireframe(
pxl8_cpu_backend* cpu,
const vertex_output* vo0, const vertex_output* vo1, const vertex_output* vo2,
u8 color, bool double_sided
) {
pxl8_cpu_render_target* render_target = cpu->current_target;
f32 hw = (f32)render_target->width * 0.5f;
f32 hh = (f32)render_target->height * 0.5f;
i32 x0 = (i32)(hw + vo0->clip_pos.x / vo0->clip_pos.w * hw);
i32 y0 = (i32)(hh - vo0->clip_pos.y / vo0->clip_pos.w * hh);
i32 x1 = (i32)(hw + vo1->clip_pos.x / vo1->clip_pos.w * hw);
i32 y1 = (i32)(hh - vo1->clip_pos.y / vo1->clip_pos.w * hh);
i32 x2 = (i32)(hw + vo2->clip_pos.x / vo2->clip_pos.w * hw);
i32 y2 = (i32)(hh - vo2->clip_pos.y / vo2->clip_pos.w * hh);
if (!double_sided) {
i32 cross = (x1 - x0) * (y2 - y0) - (y1 - y0) * (x2 - x0);
if (cross >= 0) return;
}
pxl8_cpu_draw_line_2d(cpu, x0, y0, x1, y1, color);
pxl8_cpu_draw_line_2d(cpu, x1, y1, x2, y2, color);
pxl8_cpu_draw_line_2d(cpu, x2, y2, x0, y0, color);
}
static void dispatch_triangle(
pxl8_cpu_backend* cpu,
const vertex_output* vo0, const vertex_output* vo1, const vertex_output* vo2,
const pxl8_atlas* textures, const pxl8_material* material
const pxl8_atlas* textures, const pxl8_gfx_material* material
) {
if (material->wireframe) {
rasterize_triangle_wireframe(cpu, vo0, vo1, vo2, 15, material->double_sided);
return;
}
pxl8_cpu_render_target* render_target = cpu->current_target;
tri_setup setup;
if (!setup_triangle(&setup, vo0, vo1, vo2, render_target->width, render_target->height, material->double_sided)) {
@ -1029,13 +1095,13 @@ void pxl8_cpu_draw_mesh(
pxl8_cpu_backend* cpu,
const pxl8_mesh* mesh,
const pxl8_mat4* model,
const pxl8_material* material,
const pxl8_gfx_material* material,
const pxl8_atlas* textures
) {
if (!cpu || !mesh || !model || !material || mesh->index_count < 3 || !cpu->current_target) return;
pxl8_mat4 mv = pxl8_mat4_mul(cpu->frame.view, *model);
pxl8_mat4 mvp = pxl8_mat4_mul(cpu->frame.projection, mv);
pxl8_mat4 mv = pxl8_mat4_multiply(cpu->frame.view, *model);
pxl8_mat4 mvp = pxl8_mat4_multiply(cpu->frame.projection, mv);
f32 near = cpu->frame.near_clip > 0.0f ? cpu->frame.near_clip : 0.1f;
@ -1054,13 +1120,13 @@ void pxl8_cpu_draw_mesh(
pxl8_vec4 p1 = {v1->position.x, v1->position.y, v1->position.z, 1.0f};
pxl8_vec4 p2 = {v2->position.x, v2->position.y, v2->position.z, 1.0f};
vo0.clip_pos = pxl8_mat4_mul_vec4(mvp, p0);
vo1.clip_pos = pxl8_mat4_mul_vec4(mvp, p1);
vo2.clip_pos = pxl8_mat4_mul_vec4(mvp, p2);
vo0.clip_pos = pxl8_mat4_multiply_vec4(mvp, p0);
vo1.clip_pos = pxl8_mat4_multiply_vec4(mvp, p1);
vo2.clip_pos = pxl8_mat4_multiply_vec4(mvp, p2);
pxl8_vec4 w0 = pxl8_mat4_mul_vec4(*model, p0);
pxl8_vec4 w1 = pxl8_mat4_mul_vec4(*model, p1);
pxl8_vec4 w2 = pxl8_mat4_mul_vec4(*model, p2);
pxl8_vec4 w0 = pxl8_mat4_multiply_vec4(*model, p0);
pxl8_vec4 w1 = pxl8_mat4_multiply_vec4(*model, p1);
pxl8_vec4 w2 = pxl8_mat4_multiply_vec4(*model, p2);
vo0.world_pos = (pxl8_vec3){w0.x, w0.y, w0.z};
vo1.world_pos = (pxl8_vec3){w1.x, w1.y, w1.z};
vo2.world_pos = (pxl8_vec3){w2.x, w2.y, w2.z};
@ -1078,9 +1144,9 @@ void pxl8_cpu_draw_mesh(
vo2.color = v2->color;
if (material->dynamic_lighting) {
pxl8_vec3 n0 = pxl8_vec3_normalize(pxl8_mat4_mul_vec3(*model, v0->normal));
pxl8_vec3 n1 = pxl8_vec3_normalize(pxl8_mat4_mul_vec3(*model, v1->normal));
pxl8_vec3 n2 = pxl8_vec3_normalize(pxl8_mat4_mul_vec3(*model, v2->normal));
pxl8_vec3 n0 = pxl8_vec3_normalize(pxl8_mat4_multiply_vec3(*model, v0->normal));
pxl8_vec3 n1 = pxl8_vec3_normalize(pxl8_mat4_multiply_vec3(*model, v1->normal));
pxl8_vec3 n2 = pxl8_vec3_normalize(pxl8_mat4_multiply_vec3(*model, v2->normal));
pxl8_light_result lr0 = calc_vertex_light(vo0.world_pos, n0, &cpu->frame);
pxl8_light_result lr1 = calc_vertex_light(vo1.world_pos, n1, &cpu->frame);
@ -1111,44 +1177,6 @@ void pxl8_cpu_draw_mesh(
}
}
void pxl8_cpu_draw_mesh_wireframe(
pxl8_cpu_backend* cpu,
const pxl8_mesh* mesh,
pxl8_mat4 model,
u8 color
) {
if (!cpu || !cpu->current_target || !mesh || mesh->index_count < 3) return;
pxl8_cpu_render_target* render_target = cpu->current_target;
pxl8_mat4 mvp = pxl8_mat4_mul(cpu->frame.projection, pxl8_mat4_mul(cpu->frame.view, model));
for (u32 i = 0; i < mesh->index_count; i += 3) {
const pxl8_vertex* v0 = &mesh->vertices[mesh->indices[i]];
const pxl8_vertex* v1 = &mesh->vertices[mesh->indices[i + 1]];
const pxl8_vertex* v2 = &mesh->vertices[mesh->indices[i + 2]];
pxl8_vec4 c0 = pxl8_mat4_mul_vec4(mvp, (pxl8_vec4){v0->position.x, v0->position.y, v0->position.z, 1.0f});
pxl8_vec4 c1 = pxl8_mat4_mul_vec4(mvp, (pxl8_vec4){v1->position.x, v1->position.y, v1->position.z, 1.0f});
pxl8_vec4 c2 = pxl8_mat4_mul_vec4(mvp, (pxl8_vec4){v2->position.x, v2->position.y, v2->position.z, 1.0f});
if (c0.w <= 0.0f || c1.w <= 0.0f || c2.w <= 0.0f) continue;
f32 hw = (f32)render_target->width * 0.5f;
f32 hh = (f32)render_target->height * 0.5f;
i32 x0 = (i32)(hw + c0.x / c0.w * hw);
i32 y0 = (i32)(hh - c0.y / c0.w * hh);
i32 x1 = (i32)(hw + c1.x / c1.w * hw);
i32 y1 = (i32)(hh - c1.y / c1.w * hh);
i32 x2 = (i32)(hw + c2.x / c2.w * hw);
i32 y2 = (i32)(hh - c2.y / c2.w * hh);
pxl8_cpu_draw_line_2d(cpu, x0, y0, x1, y1, color);
pxl8_cpu_draw_line_2d(cpu, x1, y1, x2, y2, color);
pxl8_cpu_draw_line_2d(cpu, x2, y2, x0, y0, color);
}
}
u8* pxl8_cpu_get_framebuffer(pxl8_cpu_backend* cpu) {
if (!cpu || cpu->target_stack_depth == 0) return NULL;
return cpu->target_stack[0]->framebuffer;
@ -1167,33 +1195,33 @@ u32 pxl8_cpu_get_width(const pxl8_cpu_backend* cpu) {
pxl8_cpu_render_target* pxl8_cpu_create_render_target(const pxl8_cpu_render_target_desc* desc) {
if (!desc) return NULL;
pxl8_cpu_render_target* target = calloc(1, sizeof(pxl8_cpu_render_target));
pxl8_cpu_render_target* target = pxl8_calloc(1, sizeof(pxl8_cpu_render_target));
if (!target) return NULL;
u32 size = desc->width * desc->height;
target->width = desc->width;
target->height = desc->height;
target->framebuffer = calloc(size, sizeof(u8));
target->framebuffer = pxl8_calloc(size, sizeof(u8));
if (!target->framebuffer) {
free(target);
pxl8_free(target);
return NULL;
}
if (desc->with_depth) {
target->zbuffer = calloc(size, sizeof(u16));
target->zbuffer = pxl8_calloc(size, sizeof(u16));
if (!target->zbuffer) {
free(target->framebuffer);
free(target);
pxl8_free(target->framebuffer);
pxl8_free(target);
return NULL;
}
}
if (desc->with_lighting) {
target->light_accum = calloc(size, sizeof(u32));
target->light_accum = pxl8_calloc(size, sizeof(u32));
if (!target->light_accum) {
free(target->zbuffer);
free(target->framebuffer);
free(target);
pxl8_free(target->zbuffer);
pxl8_free(target->framebuffer);
pxl8_free(target);
return NULL;
}
}
@ -1203,10 +1231,10 @@ pxl8_cpu_render_target* pxl8_cpu_create_render_target(const pxl8_cpu_render_targ
void pxl8_cpu_destroy_render_target(pxl8_cpu_render_target* target) {
if (!target) return;
free(target->light_accum);
free(target->zbuffer);
free(target->framebuffer);
free(target);
pxl8_free(target->light_accum);
pxl8_free(target->zbuffer);
pxl8_free(target->framebuffer);
pxl8_free(target);
}
pxl8_cpu_render_target* pxl8_cpu_get_target(pxl8_cpu_backend* cpu) {
@ -1237,10 +1265,12 @@ void pxl8_cpu_blit(pxl8_cpu_backend* cpu, pxl8_cpu_render_target* src, i32 x, i3
for (i32 row = 0; row < copy_h; row++) {
u8* src_row = src->framebuffer + (src_y0 + row) * src->width + src_x0;
u8* dst_row = dst->framebuffer + (dst_y0 + row) * dst->width + dst_x0;
u32* light_row = dst->light_accum ? dst->light_accum + (dst_y0 + row) * dst->width + dst_x0 : NULL;
for (i32 col = 0; col < copy_w; col++) {
u8 pixel = src_row[col];
if (pixel != transparent_idx) {
dst_row[col] = pixel;
if (light_row) light_row[col] = 0;
}
}
}
@ -1300,13 +1330,9 @@ u32* pxl8_cpu_get_output(pxl8_cpu_backend* cpu) {
void pxl8_cpu_render_glows(
pxl8_cpu_backend* cpu,
const pxl8_glow_source* glows,
u32 glow_count,
const pxl8_additive_table* additive,
const pxl8_palette_cube* palette_cube,
const pxl8_overbright_table* overbright
const pxl8_glow* glows,
u32 glow_count
) {
(void)overbright;
if (!cpu || cpu->target_stack_depth == 0) return;
pxl8_cpu_render_target* target = cpu->target_stack[cpu->target_stack_depth - 1];
@ -1319,7 +1345,7 @@ void pxl8_cpu_render_glows(
u32* light_accum = target->light_accum;
for (u32 gi = 0; gi < glow_count; gi++) {
const pxl8_glow_source* glow = &glows[gi];
const pxl8_glow* glow = &glows[gi];
i32 cx = glow->x;
i32 cy = glow->y;
i32 radius = glow->radius;
@ -1392,25 +1418,21 @@ void pxl8_cpu_render_glows(
u8 int_val = intensity < 1.0f ? (u8)(intensity * 255.0f) : 255;
u8 light_level = pxl8_ordered_dither(int_val, (u32)x, (u32)y);
// Skip very dark pixels to create stippled transparency effect
if (light_level < 8) continue;
u8 shaded = pxl8_colormap_lookup(cpu->colormap, base_color, light_level);
u8 shaded = pxl8_colormap_lookup(cpu->colormap, base_color, PXL8_LIGHT_WHITE, light_level);
if (intensity > 1.0f && palette_cube) {
f32 over = intensity - 1.0f;
if (over > 1.0f) over = 1.0f;
u8 r, g, b;
pxl8_palette_cube_get_rgb(palette_cube, shaded, &r, &g, &b);
u8 or = (u8)(r + (255 - r) * over);
u8 og = (u8)(g + (255 - g) * over);
u8 ob = (u8)(b + (255 - b) * over);
shaded = pxl8_palette_cube_lookup_stable(palette_cube, or, og, ob);
}
u8 dst = pixels[idx];
if (additive) {
pixels[idx] = pxl8_additive_blend(additive, shaded, dst);
if (cpu->palette_cube) {
u8 sr, sg, sb, dr, dg, db;
pxl8_palette_cube_get_rgb(cpu->palette_cube, shaded, &sr, &sg, &sb);
pxl8_palette_cube_get_rgb(cpu->palette_cube, pixels[idx], &dr, &dg, &db);
u32 r = (u32)sr + (u32)dr;
u32 g = (u32)sg + (u32)dg;
u32 b = (u32)sb + (u32)db;
if (r > 255) r = 255;
if (g > 255) g = 255;
if (b > 255) b = 255;
pixels[idx] = pxl8_palette_cube_lookup(cpu->palette_cube, (u8)r, (u8)g, (u8)b);
} else {
pixels[idx] = shaded;
}

View file

@ -1,12 +1,13 @@
#pragma once
#include "pxl8_atlas.h"
#include "pxl8_blend.h"
#include "pxl8_colormap.h"
#include "pxl8_gfx.h"
#include "pxl8_gfx3d.h"
#include "pxl8_lightmap.h"
#include "pxl8_math.h"
#include "pxl8_mesh.h"
#include "pxl8_palette.h"
#include "pxl8_types.h"
#ifdef __cplusplus
@ -34,6 +35,10 @@ void pxl8_cpu_clear_depth(pxl8_cpu_backend* cpu);
void pxl8_cpu_set_colormap(pxl8_cpu_backend* cpu, const pxl8_colormap* cm);
void pxl8_cpu_set_palette(pxl8_cpu_backend* cpu, const u32* palette);
void pxl8_cpu_set_palette_cube(pxl8_cpu_backend* cpu, const pxl8_palette_cube* cube);
u32 pxl8_cpu_add_lightmap(pxl8_cpu_backend* cpu, pxl8_lightmap* lm);
pxl8_lightmap* pxl8_cpu_get_lightmap(pxl8_cpu_backend* cpu, u32 id);
void pxl8_cpu_draw_pixel(pxl8_cpu_backend* cpu, i32 x, i32 y, u8 color);
u8 pxl8_cpu_get_pixel(pxl8_cpu_backend* cpu, i32 x, i32 y);
@ -48,17 +53,10 @@ void pxl8_cpu_draw_mesh(
pxl8_cpu_backend* cpu,
const pxl8_mesh* mesh,
const pxl8_mat4* model,
const pxl8_material* material,
const pxl8_gfx_material* material,
const pxl8_atlas* textures
);
void pxl8_cpu_draw_mesh_wireframe(
pxl8_cpu_backend* cpu,
const pxl8_mesh* mesh,
pxl8_mat4 model,
u8 color
);
u8* pxl8_cpu_get_framebuffer(pxl8_cpu_backend* cpu);
u32* pxl8_cpu_get_output(pxl8_cpu_backend* cpu);
u32 pxl8_cpu_get_height(const pxl8_cpu_backend* cpu);
@ -66,11 +64,8 @@ u32 pxl8_cpu_get_width(const pxl8_cpu_backend* cpu);
void pxl8_cpu_render_glows(
pxl8_cpu_backend* cpu,
const pxl8_glow_source* glows,
u32 glow_count,
const pxl8_additive_table* additive,
const pxl8_palette_cube* palette_cube,
const pxl8_overbright_table* overbright
const pxl8_glow* glows,
u32 glow_count
);
void pxl8_cpu_resolve(pxl8_cpu_backend* cpu);

View file

@ -1,4 +1,5 @@
#include "pxl8_font.h"
#include "pxl8_mem.h"
#include <stdlib.h>
#include <string.h>
@ -15,7 +16,7 @@ pxl8_result pxl8_font_create_atlas(const pxl8_font* font, u8** atlas_data, i32*
*atlas_height = rows_needed * font->default_height;
i32 atlas_size = (*atlas_width) * (*atlas_height);
*atlas_data = (u8*)malloc(atlas_size);
*atlas_data = (u8*)pxl8_malloc(atlas_size);
if (!*atlas_data) {
return PXL8_ERROR_OUT_OF_MEMORY;
}

View file

@ -6,7 +6,6 @@
#include "pxl8_ase.h"
#include "pxl8_atlas.h"
#include "pxl8_backend.h"
#include "pxl8_blend.h"
#include "pxl8_blit.h"
#include "pxl8_color.h"
#include "pxl8_colormap.h"
@ -15,6 +14,7 @@
#include "pxl8_log.h"
#include "pxl8_macros.h"
#include "pxl8_math.h"
#include "pxl8_mem.h"
#include "pxl8_sys.h"
#include "pxl8_types.h"
@ -25,7 +25,6 @@ typedef struct pxl8_sprite_cache_entry {
} pxl8_sprite_cache_entry;
struct pxl8_gfx {
pxl8_additive_table* additive_table;
pxl8_atlas* atlas;
pxl8_gfx_backend backend;
pxl8_colormap* colormap;
@ -35,7 +34,6 @@ struct pxl8_gfx {
pxl8_frustum frustum;
const pxl8_hal* hal;
bool initialized;
pxl8_overbright_table* overbright_table;
pxl8_palette* palette;
pxl8_palette_cube* palette_cube;
pxl8_pixel_mode pixel_mode;
@ -44,6 +42,7 @@ struct pxl8_gfx {
u32 sprite_cache_capacity;
u32 sprite_cache_count;
pxl8_viewport viewport;
pxl8_mat4 view_proj;
};
pxl8_bounds pxl8_gfx_get_bounds(pxl8_gfx* gfx) {
@ -78,10 +77,14 @@ i32 pxl8_gfx_get_width(const pxl8_gfx* gfx) {
return gfx ? gfx->framebuffer_width : 0;
}
pxl8_palette* pxl8_gfx_get_palette(pxl8_gfx* gfx) {
pxl8_palette* pxl8_gfx_palette(pxl8_gfx* gfx) {
return gfx ? gfx->palette : NULL;
}
pxl8_colormap* pxl8_gfx_colormap(pxl8_gfx* gfx) {
return gfx ? gfx->colormap : NULL;
}
u8 pxl8_gfx_find_color(pxl8_gfx* gfx, u32 color) {
if (!gfx || !gfx->palette) return 0;
if (color <= 0xFFFFFF) color = (color << 8) | 0xFF;
@ -91,37 +94,24 @@ u8 pxl8_gfx_find_color(pxl8_gfx* gfx, u32 color) {
return pxl8_palette_find_closest(gfx->palette, r, g, b);
}
void pxl8_gfx_set_palette(pxl8_gfx* gfx, pxl8_palette* pal) {
if (!gfx) return;
if (gfx->palette) {
pxl8_palette_destroy(gfx->palette);
}
gfx->palette = pal;
if (gfx->palette && gfx->pixel_mode != PXL8_PIXEL_HICOLOR) {
u32* colors = pxl8_palette_colors(gfx->palette);
if (gfx->colormap) {
pxl8_colormap_generate(gfx->colormap, colors, NULL);
}
if (gfx->backend.type == PXL8_GFX_BACKEND_CPU) {
pxl8_cpu_set_palette(gfx->backend.cpu, colors);
}
void pxl8_gfx_set_palette_colors(pxl8_gfx* gfx, const u32* colors, u16 count) {
if (!gfx || !gfx->palette || !colors) return;
pxl8_set_palette(gfx->palette, colors, count);
if (gfx->pixel_mode != PXL8_PIXEL_HICOLOR && gfx->backend.type == PXL8_GFX_BACKEND_CPU) {
pxl8_cpu_set_palette(gfx->backend.cpu, pxl8_palette_colors(gfx->palette));
}
}
i32 pxl8_gfx_load_palette(pxl8_gfx* gfx, const char* filepath) {
if (!gfx || !filepath) return -1;
pxl8_palette* pal = gfx->palette;
if (!pal) return -1;
pxl8_result result = pxl8_palette_load_ase(pal, filepath);
if (!gfx || !gfx->palette || !filepath) return -1;
pxl8_result result = pxl8_palette_load_ase(gfx->palette, filepath);
if (result != PXL8_OK) return (i32)result;
if (gfx->pixel_mode != PXL8_PIXEL_HICOLOR) {
u32* colors = pxl8_palette_colors(pal);
if (gfx->colormap) {
pxl8_colormap_generate(gfx->colormap, colors, NULL);
pxl8_colormap_generate(gfx->colormap, pxl8_palette_colors(gfx->palette));
}
if (gfx->backend.type == PXL8_GFX_BACKEND_CPU) {
pxl8_cpu_set_palette(gfx->backend.cpu, colors);
pxl8_cpu_set_palette(gfx->backend.cpu, pxl8_palette_colors(gfx->palette));
}
}
return 0;
@ -133,7 +123,7 @@ pxl8_gfx* pxl8_gfx_create(
pxl8_pixel_mode mode,
pxl8_resolution resolution
) {
pxl8_gfx* gfx = (pxl8_gfx*)calloc(1, sizeof(pxl8_gfx));
pxl8_gfx* gfx = (pxl8_gfx*)pxl8_calloc(1, sizeof(pxl8_gfx));
if (!gfx) {
pxl8_error("Failed to allocate graphics context");
return NULL;
@ -149,7 +139,7 @@ pxl8_gfx* pxl8_gfx_create(
if (!gfx->platform_data) {
pxl8_error("Platform data cannot be NULL");
free(gfx);
pxl8_free(gfx);
return NULL;
}
@ -168,7 +158,7 @@ pxl8_gfx* pxl8_gfx_create(
gfx->framebuffer = pxl8_cpu_get_framebuffer(gfx->backend.cpu);
if (mode != PXL8_PIXEL_HICOLOR) {
gfx->colormap = calloc(1, sizeof(pxl8_colormap));
gfx->colormap = pxl8_calloc(1, sizeof(pxl8_colormap));
if (gfx->colormap) {
pxl8_cpu_set_colormap(gfx->backend.cpu, gfx->colormap);
}
@ -187,18 +177,16 @@ pxl8_gfx* pxl8_gfx_create(
void pxl8_gfx_destroy(pxl8_gfx* gfx) {
if (!gfx) return;
pxl8_additive_table_destroy(gfx->additive_table);
pxl8_atlas_destroy(gfx->atlas);
if (gfx->backend.type == PXL8_GFX_BACKEND_CPU) {
pxl8_cpu_destroy(gfx->backend.cpu);
}
free(gfx->colormap);
pxl8_overbright_table_destroy(gfx->overbright_table);
pxl8_free(gfx->colormap);
pxl8_palette_cube_destroy(gfx->palette_cube);
pxl8_palette_destroy(gfx->palette);
free(gfx->sprite_cache);
pxl8_free(gfx->sprite_cache);
free(gfx);
pxl8_free(gfx);
}
static pxl8_result pxl8_gfx_ensure_atlas(pxl8_gfx* gfx) {
@ -239,7 +227,7 @@ pxl8_result pxl8_gfx_load_sprite(pxl8_gfx* gfx, const char* path) {
if (!gfx->sprite_cache) {
gfx->sprite_cache_capacity = PXL8_DEFAULT_SPRITE_CACHE_CAPACITY;
gfx->sprite_cache = (pxl8_sprite_cache_entry*)calloc(
gfx->sprite_cache = (pxl8_sprite_cache_entry*)pxl8_calloc(
gfx->sprite_cache_capacity, sizeof(pxl8_sprite_cache_entry)
);
if (!gfx->sprite_cache) return PXL8_ERROR_OUT_OF_MEMORY;
@ -284,7 +272,7 @@ pxl8_result pxl8_gfx_load_sprite(pxl8_gfx* gfx, const char* path) {
if (gfx->sprite_cache_count >= gfx->sprite_cache_capacity) {
u32 new_capacity = gfx->sprite_cache_capacity * 2;
pxl8_sprite_cache_entry* new_cache = (pxl8_sprite_cache_entry*)realloc(
pxl8_sprite_cache_entry* new_cache = (pxl8_sprite_cache_entry*)pxl8_realloc(
gfx->sprite_cache,
new_capacity * sizeof(pxl8_sprite_cache_entry)
);
@ -523,6 +511,7 @@ void pxl8_2d_sprite(pxl8_gfx* gfx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h, bo
if (!gfx || !gfx->atlas) return;
u8* framebuffer = NULL;
u32* light_accum = NULL;
i32 fb_width = 0;
i32 fb_height = 0;
@ -531,6 +520,7 @@ void pxl8_2d_sprite(pxl8_gfx* gfx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h, bo
pxl8_cpu_render_target* render_target = pxl8_cpu_get_target(gfx->backend.cpu);
if (!render_target) return;
framebuffer = pxl8_cpu_render_target_get_framebuffer(render_target);
light_accum = pxl8_cpu_render_target_get_light_accum(render_target);
fb_width = (i32)pxl8_cpu_render_target_get_width(render_target);
fb_height = (i32)pxl8_cpu_render_target_get_height(render_target);
break;
@ -567,6 +557,11 @@ void pxl8_2d_sprite(pxl8_gfx* gfx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h, bo
if (is_1to1_scale && is_unclipped && !is_flipped) {
const u8* sprite_data = atlas_pixels + entry->y * atlas_width + entry->x;
pxl8_blit_indexed(framebuffer, fb_width, sprite_data, atlas_width, x, y, w, h);
if (light_accum) {
for (i32 py = 0; py < h; py++) {
memset(light_accum + (y + py) * fb_width + x, 0, w * sizeof(u32));
}
}
} else {
for (i32 py = 0; py < draw_height; py++) {
for (i32 px = 0; px < draw_width; px++) {
@ -580,6 +575,7 @@ void pxl8_2d_sprite(pxl8_gfx* gfx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h, bo
i32 dest_idx = (dest_y + py) * fb_width + (dest_x + px);
framebuffer[dest_idx] = pxl8_blend_indexed(atlas_pixels[src_idx], framebuffer[dest_idx]);
if (light_accum) light_accum[dest_idx] = 0;
}
}
}
@ -612,13 +608,17 @@ pxl8_3d_frame pxl8_3d_frame_from_camera(const pxl8_3d_camera* camera, const pxl8
return frame;
}
void pxl8_3d_begin_frame(pxl8_gfx* gfx, const pxl8_3d_camera* camera, const pxl8_3d_uniforms* uniforms) {
void pxl8_3d_begin_frame(pxl8_gfx* gfx, const pxl8_3d_camera* camera, const pxl8_lights* lights, const pxl8_3d_uniforms* uniforms) {
if (!gfx || !camera) return;
pxl8_3d_frame frame = pxl8_3d_frame_from_camera(camera, uniforms);
frame.lights = lights ? pxl8_lights_data(lights) : NULL;
frame.lights_count = lights ? pxl8_lights_count(lights) : 0;
pxl8_mat4 vp = pxl8_mat4_multiply(frame.projection, frame.view);
pxl8_mat4 vp = pxl8_mat4_mul(frame.projection, frame.view);
gfx->frustum = pxl8_frustum_from_matrix(vp);
gfx->view_proj = vp;
switch (gfx->backend.type) {
case PXL8_GFX_BACKEND_CPU:
@ -634,6 +634,38 @@ const pxl8_frustum* pxl8_3d_get_frustum(pxl8_gfx* gfx) {
return &gfx->frustum;
}
const pxl8_mat4* pxl8_3d_get_view_proj(pxl8_gfx* gfx) {
if (!gfx) return NULL;
return &gfx->view_proj;
}
u32 pxl8_3d_project_points(pxl8_gfx* gfx, const pxl8_vec3* in, pxl8_vec3* out, u32 count, const pxl8_mat4* transform) {
if (!gfx || !in || !out) return 0;
pxl8_mat4 mvp = transform ? pxl8_mat4_multiply(gfx->view_proj, *transform) : gfx->view_proj;
f32 hw = (f32)gfx->framebuffer_width * 0.5f;
f32 hh = (f32)gfx->framebuffer_height * 0.5f;
u32 visible = 0;
for (u32 i = 0; i < count; i++) {
pxl8_vec4 clip = pxl8_mat4_multiply_vec4(mvp, (pxl8_vec4){in[i].x, in[i].y, in[i].z, 1.0f});
if (clip.w <= 0.0f) {
out[i].z = -1.0f;
continue;
}
f32 inv_w = 1.0f / clip.w;
out[i].x = hw + clip.x * inv_w * hw;
out[i].y = hh - clip.y * inv_w * hh;
out[i].z = clip.w;
visible++;
}
return visible;
}
void pxl8_3d_clear(pxl8_gfx* gfx, u8 color) {
if (!gfx) return;
switch (gfx->backend.type) {
@ -667,7 +699,7 @@ void pxl8_3d_draw_line(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, u8 color) {
}
}
void pxl8_3d_draw_mesh(pxl8_gfx* gfx, const pxl8_mesh* mesh, const pxl8_mat4* model, const pxl8_material* material) {
void pxl8_3d_draw_mesh(pxl8_gfx* gfx, const pxl8_mesh* mesh, const pxl8_mat4* model, const pxl8_gfx_material* material) {
if (!gfx || !mesh || !model || !material) return;
switch (gfx->backend.type) {
case PXL8_GFX_BACKEND_CPU:
@ -678,17 +710,6 @@ void pxl8_3d_draw_mesh(pxl8_gfx* gfx, const pxl8_mesh* mesh, const pxl8_mat4* mo
}
}
void pxl8_3d_draw_mesh_wireframe(pxl8_gfx* gfx, const pxl8_mesh* mesh, pxl8_mat4 model, u8 color) {
if (!gfx || !mesh) return;
switch (gfx->backend.type) {
case PXL8_GFX_BACKEND_CPU:
pxl8_cpu_draw_mesh_wireframe(gfx->backend.cpu, mesh, model, color);
break;
case PXL8_GFX_BACKEND_GPU:
break;
}
}
void pxl8_3d_end_frame(pxl8_gfx* gfx) {
if (!gfx) return;
switch (gfx->backend.type) {
@ -733,31 +754,27 @@ void pxl8_gfx_pop_target(pxl8_gfx* gfx) {
}
}
static void pxl8_gfx_ensure_blend_tables(pxl8_gfx* gfx) {
void pxl8_gfx_ensure_blend_tables(pxl8_gfx* gfx) {
if (!gfx || !gfx->palette) return;
if (!gfx->additive_table) {
gfx->additive_table = pxl8_additive_table_create(gfx->palette);
}
if (!gfx->overbright_table) {
gfx->overbright_table = pxl8_overbright_table_create(gfx->palette);
}
if (!gfx->palette_cube) {
gfx->palette_cube = pxl8_palette_cube_create(gfx->palette);
if (gfx->backend.cpu) {
pxl8_cpu_set_palette_cube(gfx->backend.cpu, gfx->palette_cube);
}
}
}
void pxl8_gfx_blend_tables_update(pxl8_gfx* gfx) {
if (!gfx || !gfx->palette) return;
if (gfx->additive_table) {
pxl8_additive_table_rebuild(gfx->additive_table, gfx->palette);
}
if (gfx->overbright_table) {
pxl8_overbright_table_rebuild(gfx->overbright_table, gfx->palette);
}
pxl8_gfx_ensure_blend_tables(gfx);
if (gfx->palette_cube) {
pxl8_palette_cube_rebuild(gfx->palette_cube, gfx->palette);
if (gfx->backend.cpu) {
pxl8_cpu_set_palette_cube(gfx->backend.cpu, gfx->palette_cube);
}
}
}
@ -765,29 +782,33 @@ void pxl8_gfx_colormap_update(pxl8_gfx* gfx) {
if (!gfx || !gfx->palette || !gfx->colormap) return;
u32* colors = pxl8_palette_colors(gfx->palette);
if (colors) {
pxl8_colormap_generate(gfx->colormap, colors, NULL);
pxl8_colormap_generate(gfx->colormap, colors);
}
}
u8 pxl8_gfx_find_closest_color(pxl8_gfx* gfx, u8 r, u8 g, u8 b) {
if (!gfx || !gfx->palette) return 0;
return pxl8_palette_find_closest(gfx->palette, r, g, b);
}
u8 pxl8_gfx_ui_color(pxl8_gfx* gfx, u8 index) {
if (!gfx || !gfx->palette || index >= PXL8_UI_PALETTE_SIZE) return 0;
u32 abgr = pxl8_ui_palette[index];
u8 r = (abgr >> 0) & 0xFF;
u8 g = (abgr >> 8) & 0xFF;
u8 b = (abgr >> 16) & 0xFF;
return pxl8_palette_find_closest(gfx->palette, r, g, b);
}
void pxl8_gfx_apply_effect(pxl8_gfx* gfx, pxl8_gfx_effect effect, const void* params, u32 count) {
if (!gfx || !params || count == 0) return;
switch (effect) {
case PXL8_GFX_EFFECT_GLOWS: {
pxl8_gfx_ensure_blend_tables(gfx);
if (!gfx->additive_table || !gfx->overbright_table || !gfx->palette_cube) return;
const pxl8_glow_source* glows = (const pxl8_glow_source*)params;
const pxl8_glow* glows = (const pxl8_glow*)params;
switch (gfx->backend.type) {
case PXL8_GFX_BACKEND_CPU:
pxl8_cpu_render_glows(
gfx->backend.cpu,
glows,
count,
gfx->additive_table,
gfx->palette_cube,
gfx->overbright_table
);
pxl8_cpu_render_glows(gfx->backend.cpu, glows, count);
break;
case PXL8_GFX_BACKEND_GPU:
break;

View file

@ -2,9 +2,12 @@
#include "pxl8_gfx2d.h"
#include "pxl8_gfx3d.h"
#include "pxl8_glows.h"
#include "pxl8_hal.h"
#include "pxl8_colormap.h"
#include "pxl8_palette.h"
#include "pxl8_types.h"
#include "pxl8_gui_palette.h"
typedef struct pxl8_gfx pxl8_gfx;
@ -12,53 +15,6 @@ typedef enum pxl8_gfx_effect {
PXL8_GFX_EFFECT_GLOWS = 0,
} pxl8_gfx_effect;
#define PXL8_MAX_GLOWS 256
typedef enum pxl8_glow_shape {
PXL8_GLOW_CIRCLE = 0,
PXL8_GLOW_DIAMOND = 1,
PXL8_GLOW_SHAFT = 2,
} pxl8_glow_shape;
typedef struct pxl8_glow_source {
u8 color;
u16 depth;
u8 height;
u16 intensity;
u8 radius;
pxl8_glow_shape shape;
i16 x;
i16 y;
} pxl8_glow_source;
static inline pxl8_glow_source pxl8_glow_create(i32 x, i32 y, u8 radius, u16 intensity, u8 color) {
return (pxl8_glow_source){
.color = color,
.depth = 0xFFFF,
.height = 0,
.intensity = intensity,
.radius = radius,
.shape = PXL8_GLOW_CIRCLE,
.x = (i16)x,
.y = (i16)y,
};
}
static inline pxl8_glow_source pxl8_glow_with_depth(pxl8_glow_source g, u16 depth) {
g.depth = depth;
return g;
}
static inline pxl8_glow_source pxl8_glow_with_shape(pxl8_glow_source g, pxl8_glow_shape shape) {
g.shape = shape;
return g;
}
static inline pxl8_glow_source pxl8_glow_with_height(pxl8_glow_source g, u8 height) {
g.height = height;
return g;
}
#ifdef __cplusplus
extern "C" {
#endif
@ -76,13 +32,14 @@ pxl8_bounds pxl8_gfx_get_bounds(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);
pxl8_palette* pxl8_gfx_get_palette(pxl8_gfx* gfx);
pxl8_palette* pxl8_gfx_palette(pxl8_gfx* gfx);
pxl8_colormap* pxl8_gfx_colormap(pxl8_gfx* gfx);
pxl8_pixel_mode pxl8_gfx_get_pixel_mode(pxl8_gfx* gfx);
i32 pxl8_gfx_get_width(const pxl8_gfx* gfx);
i32 pxl8_gfx_load_palette(pxl8_gfx* gfx, const char* filepath);
void pxl8_gfx_project(pxl8_gfx* gfx, f32 left, f32 right, f32 top, f32 bottom);
void pxl8_gfx_set_palette(pxl8_gfx* gfx, pxl8_palette* pal);
void pxl8_gfx_set_palette_colors(pxl8_gfx* gfx, const u32* colors, u16 count);
void pxl8_gfx_set_viewport(pxl8_gfx* gfx, pxl8_viewport vp);
pxl8_viewport pxl8_gfx_viewport(pxl8_bounds bounds, i32 width, i32 height);
@ -97,6 +54,10 @@ void pxl8_gfx_pop_target(pxl8_gfx* gfx);
void pxl8_gfx_apply_effect(pxl8_gfx* gfx, pxl8_gfx_effect effect, const void* params, u32 count);
void pxl8_gfx_blend_tables_update(pxl8_gfx* gfx);
void pxl8_gfx_colormap_update(pxl8_gfx* gfx);
void pxl8_gfx_ensure_blend_tables(pxl8_gfx* gfx);
u8 pxl8_gfx_find_closest_color(pxl8_gfx* gfx, u8 r, u8 g, u8 b);
u8 pxl8_gfx_ui_color(pxl8_gfx* gfx, u8 index);
#ifdef __cplusplus
}

View file

@ -1,53 +1,31 @@
#pragma once
#include "pxl8_3d_camera.h"
#include "pxl8_lights.h"
#include "pxl8_math.h"
#include "pxl8_mesh.h"
#include "pxl8_types.h"
typedef struct pxl8_gfx pxl8_gfx;
#define PXL8_MAX_LIGHTS 16
typedef struct pxl8_light {
pxl8_vec3 position;
u8 r, g, b;
u8 intensity;
f32 radius;
f32 radius_sq;
f32 inv_radius_sq;
} pxl8_light;
static inline pxl8_light pxl8_light_create(pxl8_vec3 pos, u8 r, u8 g, u8 b, u8 intensity, f32 radius) {
f32 radius_sq = radius * radius;
return (pxl8_light){
.position = pos,
.r = r, .g = g, .b = b,
.intensity = intensity,
.radius = radius,
.radius_sq = radius_sq,
.inv_radius_sq = radius_sq > 0.0f ? 1.0f / radius_sq : 0.0f,
};
}
typedef struct pxl8_3d_uniforms {
u8 ambient;
pxl8_vec3 celestial_dir;
f32 celestial_intensity;
u8 fog_color;
f32 fog_density;
pxl8_light lights[PXL8_MAX_LIGHTS];
u32 num_lights;
f32 time;
} pxl8_3d_uniforms;
typedef struct pxl8_3d_frame {
pxl8_3d_uniforms uniforms;
pxl8_vec3 camera_dir;
pxl8_vec3 camera_pos;
f32 far_clip;
const pxl8_light* lights;
u32 lights_count;
f32 near_clip;
pxl8_mat4 projection;
pxl8_3d_uniforms uniforms;
pxl8_mat4 view;
} pxl8_3d_frame;
@ -55,15 +33,16 @@ typedef struct pxl8_3d_frame {
extern "C" {
#endif
void pxl8_3d_begin_frame(pxl8_gfx* gfx, const pxl8_3d_camera* camera, const pxl8_3d_uniforms* uniforms);
void pxl8_3d_begin_frame(pxl8_gfx* gfx, const pxl8_3d_camera* camera, const pxl8_lights* lights, const pxl8_3d_uniforms* uniforms);
void pxl8_3d_clear(pxl8_gfx* gfx, u8 color);
void pxl8_3d_clear_depth(pxl8_gfx* gfx);
void pxl8_3d_draw_line(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, u8 color);
void pxl8_3d_draw_mesh(pxl8_gfx* gfx, const pxl8_mesh* mesh, const pxl8_mat4* model, const pxl8_material* material);
void pxl8_3d_draw_mesh_wireframe(pxl8_gfx* gfx, const pxl8_mesh* mesh, pxl8_mat4 model, u8 color);
void pxl8_3d_draw_mesh(pxl8_gfx* gfx, const pxl8_mesh* mesh, const pxl8_mat4* model, const pxl8_gfx_material* material);
void pxl8_3d_end_frame(pxl8_gfx* gfx);
u8* pxl8_3d_get_framebuffer(pxl8_gfx* gfx);
const pxl8_frustum* pxl8_3d_get_frustum(pxl8_gfx* gfx);
const pxl8_mat4* pxl8_3d_get_view_proj(pxl8_gfx* gfx);
u32 pxl8_3d_project_points(pxl8_gfx* gfx, const pxl8_vec3* in, pxl8_vec3* out, u32 count, const pxl8_mat4* transform);
pxl8_3d_frame pxl8_3d_frame_from_camera(const pxl8_3d_camera* camera, const pxl8_3d_uniforms* uniforms);

62
src/gfx/pxl8_glows.c Normal file
View file

@ -0,0 +1,62 @@
#include "pxl8_glows.h"
#include "pxl8_gfx.h"
#include "pxl8_mem.h"
#include <stdlib.h>
struct pxl8_glows {
pxl8_glow* data;
u32 capacity;
u32 count;
};
pxl8_glows* pxl8_glows_create(u32 capacity) {
pxl8_glows* glows = pxl8_calloc(1, sizeof(pxl8_glows));
if (!glows) return NULL;
glows->data = pxl8_calloc(capacity, sizeof(pxl8_glow));
if (!glows->data) {
pxl8_free(glows);
return NULL;
}
glows->capacity = capacity;
glows->count = 0;
return glows;
}
void pxl8_glows_destroy(pxl8_glows* glows) {
if (!glows) return;
pxl8_free(glows->data);
pxl8_free(glows);
}
void pxl8_glows_add(pxl8_glows* glows, i16 x, i16 y, u8 radius, u16 intensity, u8 color, u8 shape) {
if (!glows || glows->count >= glows->capacity) return;
pxl8_glow* g = &glows->data[glows->count++];
g->x = x;
g->y = y;
g->radius = radius;
g->intensity = intensity;
g->color = color;
g->shape = shape;
g->depth = 0xFFFF;
g->height = 0;
}
void pxl8_glows_clear(pxl8_glows* glows) {
if (!glows) return;
glows->count = 0;
}
u32 pxl8_glows_count(const pxl8_glows* glows) {
return glows ? glows->count : 0;
}
void pxl8_glows_render(pxl8_glows* glows, pxl8_gfx* gfx) {
if (!glows || !gfx || glows->count == 0) return;
pxl8_gfx_apply_effect(gfx, PXL8_GFX_EFFECT_GLOWS, glows->data, glows->count);
}

42
src/gfx/pxl8_glows.h Normal file
View file

@ -0,0 +1,42 @@
#pragma once
#include "pxl8_types.h"
#define PXL8_GLOWS_MAX 16384
typedef enum pxl8_glow_shape {
PXL8_GLOW_CIRCLE = 0,
PXL8_GLOW_DIAMOND = 1,
PXL8_GLOW_SHAFT = 2,
} pxl8_glow_shape;
typedef struct pxl8_glow {
u8 color;
u16 depth;
u8 height;
u16 intensity;
u8 radius;
pxl8_glow_shape shape;
i16 x;
i16 y;
} pxl8_glow;
typedef struct pxl8_gfx pxl8_gfx;
typedef struct pxl8_glows pxl8_glows;
#ifdef __cplusplus
extern "C" {
#endif
pxl8_glows* pxl8_glows_create(u32 capacity);
void pxl8_glows_destroy(pxl8_glows* glows);
void pxl8_glows_add(pxl8_glows* glows, i16 x, i16 y, u8 radius, u16 intensity, u8 color, u8 shape);
void pxl8_glows_clear(pxl8_glows* glows);
u32 pxl8_glows_count(const pxl8_glows* glows);
const pxl8_glow* pxl8_glows_data(const pxl8_glows* glows);
void pxl8_glows_render(pxl8_glows* glows, pxl8_gfx* gfx);
#ifdef __cplusplus
}
#endif

114
src/gfx/pxl8_lightmap.c Normal file
View file

@ -0,0 +1,114 @@
#include "pxl8_lightmap.h"
#include "pxl8_mem.h"
#include <stdlib.h>
#include <string.h>
pxl8_lightmap* pxl8_lightmap_create(u32 width, u32 height, u32 scale) {
pxl8_lightmap* lm = pxl8_calloc(1, sizeof(pxl8_lightmap));
if (!lm) return NULL;
lm->width = width;
lm->height = height;
lm->scale = scale;
lm->data = pxl8_calloc(width * height * 3, sizeof(u8));
if (!lm->data) {
pxl8_free(lm);
return NULL;
}
pxl8_lightmap_clear(lm, PXL8_LIGHTMAP_NEUTRAL, PXL8_LIGHTMAP_NEUTRAL, PXL8_LIGHTMAP_NEUTRAL);
return lm;
}
void pxl8_lightmap_destroy(pxl8_lightmap* lm) {
if (!lm) return;
pxl8_free(lm->data);
pxl8_free(lm);
}
void pxl8_lightmap_clear(pxl8_lightmap* lm, u8 r, u8 g, u8 b) {
if (!lm || !lm->data) return;
u32 count = lm->width * lm->height;
for (u32 i = 0; i < count; i++) {
lm->data[i * 3 + 0] = r;
lm->data[i * 3 + 1] = g;
lm->data[i * 3 + 2] = b;
}
}
void pxl8_lightmap_set(pxl8_lightmap* lm, u32 x, u32 y, u8 r, u8 g, u8 b) {
if (!lm || !lm->data || x >= lm->width || y >= lm->height) return;
u32 idx = (y * lm->width + x) * 3;
lm->data[idx + 0] = r;
lm->data[idx + 1] = g;
lm->data[idx + 2] = b;
}
void pxl8_lightmap_get(const pxl8_lightmap* lm, u32 x, u32 y, u8* r, u8* g, u8* b) {
if (!lm || !lm->data || x >= lm->width || y >= lm->height) {
*r = *g = *b = PXL8_LIGHTMAP_NEUTRAL;
return;
}
u32 idx = (y * lm->width + x) * 3;
*r = lm->data[idx + 0];
*g = lm->data[idx + 1];
*b = lm->data[idx + 2];
}
void pxl8_lightmap_add_point(
pxl8_lightmap* lm,
f32 lx, f32 ly,
u8 r, u8 g, u8 b,
f32 radius,
f32 intensity
) {
if (!lm || !lm->data || radius <= 0.0f) return;
f32 radius_sq = radius * radius;
f32 inv_radius_sq = 1.0f / radius_sq;
i32 cx = (i32)(lx * (f32)lm->width);
i32 cy = (i32)(ly * (f32)lm->height);
i32 rad_pixels = (i32)(radius * (f32)lm->width) + 1;
i32 x0 = cx - rad_pixels;
i32 y0 = cy - rad_pixels;
i32 x1 = cx + rad_pixels;
i32 y1 = cy + rad_pixels;
if (x0 < 0) x0 = 0;
if (y0 < 0) y0 = 0;
if (x1 >= (i32)lm->width) x1 = (i32)lm->width - 1;
if (y1 >= (i32)lm->height) y1 = (i32)lm->height - 1;
f32 scale_x = 1.0f / (f32)lm->width;
f32 scale_y = 1.0f / (f32)lm->height;
for (i32 y = y0; y <= y1; y++) {
f32 dy = ((f32)y * scale_y) - ly;
for (i32 x = x0; x <= x1; x++) {
f32 dx = ((f32)x * scale_x) - lx;
f32 dist_sq = dx * dx + dy * dy;
if (dist_sq >= radius_sq) continue;
f32 falloff = 1.0f - dist_sq * inv_radius_sq;
f32 contrib = falloff * falloff * intensity;
u32 idx = ((u32)y * lm->width + (u32)x) * 3;
i32 nr = (i32)lm->data[idx + 0] + (i32)((f32)(r - 128) * contrib);
i32 ng = (i32)lm->data[idx + 1] + (i32)((f32)(g - 128) * contrib);
i32 nb = (i32)lm->data[idx + 2] + (i32)((f32)(b - 128) * contrib);
if (nr < 0) nr = 0; if (nr > 255) nr = 255;
if (ng < 0) ng = 0; if (ng > 255) ng = 255;
if (nb < 0) nb = 0; if (nb > 255) nb = 255;
lm->data[idx + 0] = (u8)nr;
lm->data[idx + 1] = (u8)ng;
lm->data[idx + 2] = (u8)nb;
}
}
}

48
src/gfx/pxl8_lightmap.h Normal file
View file

@ -0,0 +1,48 @@
#pragma once
#include "pxl8_types.h"
#ifdef __cplusplus
extern "C" {
#endif
#define PXL8_LIGHTMAP_MAX 16
#define PXL8_LIGHTMAP_NEUTRAL 128
typedef struct pxl8_lightmap {
u8* data;
u32 width;
u32 height;
u32 scale;
} pxl8_lightmap;
pxl8_lightmap* pxl8_lightmap_create(u32 width, u32 height, u32 scale);
void pxl8_lightmap_destroy(pxl8_lightmap* lm);
void pxl8_lightmap_clear(pxl8_lightmap* lm, u8 r, u8 g, u8 b);
void pxl8_lightmap_set(pxl8_lightmap* lm, u32 x, u32 y, u8 r, u8 g, u8 b);
void pxl8_lightmap_get(const pxl8_lightmap* lm, u32 x, u32 y, u8* r, u8* g, u8* b);
void pxl8_lightmap_add_point(
pxl8_lightmap* lm,
f32 lx, f32 ly,
u8 r, u8 g, u8 b,
f32 radius,
f32 intensity
);
static inline void pxl8_lightmap_sample(
const pxl8_lightmap* lm,
f32 u, f32 v,
u8* r, u8* g, u8* b
) {
i32 x = (i32)(u * (f32)lm->width) & (i32)(lm->width - 1);
i32 y = (i32)(v * (f32)lm->height) & (i32)(lm->height - 1);
u32 idx = ((u32)y * lm->width + (u32)x) * 3;
*r = lm->data[idx + 0];
*g = lm->data[idx + 1];
*b = lm->data[idx + 2];
}
#ifdef __cplusplus
}
#endif

64
src/gfx/pxl8_lights.c Normal file
View file

@ -0,0 +1,64 @@
#include "pxl8_lights.h"
#include "pxl8_mem.h"
#include <stdlib.h>
struct pxl8_lights {
pxl8_light* data;
u32 capacity;
u32 count;
};
pxl8_lights* pxl8_lights_create(u32 capacity) {
if (capacity > PXL8_LIGHTS_MAX) capacity = PXL8_LIGHTS_MAX;
pxl8_lights* lights = pxl8_calloc(1, sizeof(pxl8_lights));
if (!lights) return NULL;
lights->data = pxl8_calloc(capacity, sizeof(pxl8_light));
if (!lights->data) {
pxl8_free(lights);
return NULL;
}
lights->capacity = capacity;
lights->count = 0;
return lights;
}
void pxl8_lights_destroy(pxl8_lights* lights) {
if (!lights) return;
pxl8_free(lights->data);
pxl8_free(lights);
}
void pxl8_lights_add(pxl8_lights* lights, f32 x, f32 y, f32 z, u8 r, u8 g, u8 b, u8 intensity, f32 radius) {
if (!lights || lights->count >= lights->capacity) return;
f32 radius_sq = radius * radius;
pxl8_light* l = &lights->data[lights->count++];
l->position.x = x;
l->position.y = y;
l->position.z = z;
l->r = r;
l->g = g;
l->b = b;
l->intensity = intensity;
l->radius = radius;
l->radius_sq = radius_sq;
l->inv_radius_sq = radius_sq > 0.0f ? 1.0f / radius_sq : 0.0f;
}
void pxl8_lights_clear(pxl8_lights* lights) {
if (!lights) return;
lights->count = 0;
}
u32 pxl8_lights_count(const pxl8_lights* lights) {
return lights ? lights->count : 0;
}
const pxl8_light* pxl8_lights_data(const pxl8_lights* lights) {
return lights ? lights->data : NULL;
}

33
src/gfx/pxl8_lights.h Normal file
View file

@ -0,0 +1,33 @@
#pragma once
#include "pxl8_math.h"
#include "pxl8_types.h"
#define PXL8_LIGHTS_MAX 256
typedef struct pxl8_light {
pxl8_vec3 position;
f32 inv_radius_sq;
u8 r, g, b;
u8 intensity;
f32 radius;
f32 radius_sq;
} pxl8_light;
typedef struct pxl8_lights pxl8_lights;
#ifdef __cplusplus
extern "C" {
#endif
pxl8_lights* pxl8_lights_create(u32 capacity);
void pxl8_lights_destroy(pxl8_lights* lights);
void pxl8_lights_add(pxl8_lights* lights, f32 x, f32 y, f32 z, u8 r, u8 g, u8 b, u8 intensity, f32 radius);
void pxl8_lights_clear(pxl8_lights* lights);
u32 pxl8_lights_count(const pxl8_lights* lights);
const pxl8_light* pxl8_lights_data(const pxl8_lights* lights);
#ifdef __cplusplus
}
#endif

View file

@ -1,3 +1,4 @@
#include "pxl8_mem.h"
#include "pxl8_mesh.h"
#include <stdlib.h>
#include <string.h>
@ -6,16 +7,16 @@ pxl8_mesh* pxl8_mesh_create(u32 vertex_capacity, u32 index_capacity) {
if (vertex_capacity > PXL8_MESH_MAX_VERTICES) vertex_capacity = PXL8_MESH_MAX_VERTICES;
if (index_capacity > PXL8_MESH_MAX_INDICES) index_capacity = PXL8_MESH_MAX_INDICES;
pxl8_mesh* mesh = calloc(1, sizeof(pxl8_mesh));
pxl8_mesh* mesh = pxl8_calloc(1, sizeof(pxl8_mesh));
if (!mesh) return NULL;
mesh->vertices = calloc(vertex_capacity, sizeof(pxl8_vertex));
mesh->indices = calloc(index_capacity, sizeof(u16));
mesh->vertices = pxl8_calloc(vertex_capacity, sizeof(pxl8_vertex));
mesh->indices = pxl8_calloc(index_capacity, sizeof(u16));
if (!mesh->vertices || !mesh->indices) {
free(mesh->vertices);
free(mesh->indices);
free(mesh);
pxl8_free(mesh->vertices);
pxl8_free(mesh->indices);
pxl8_free(mesh);
return NULL;
}
@ -29,9 +30,9 @@ pxl8_mesh* pxl8_mesh_create(u32 vertex_capacity, u32 index_capacity) {
void pxl8_mesh_destroy(pxl8_mesh* mesh) {
if (!mesh) return;
free(mesh->vertices);
free(mesh->indices);
free(mesh);
pxl8_free(mesh->vertices);
pxl8_free(mesh->indices);
pxl8_free(mesh);
}
void pxl8_mesh_clear(pxl8_mesh* mesh) {

View file

@ -17,8 +17,15 @@ typedef enum pxl8_blend_mode {
PXL8_BLEND_ADDITIVE,
} pxl8_blend_mode;
typedef struct pxl8_material {
typedef struct pxl8_gfx_material {
char name[16];
pxl8_vec3 u_axis;
pxl8_vec3 v_axis;
f32 u_offset;
f32 v_offset;
u32 texture_id;
u32 lightmap_id;
u8 alpha;
u8 blend_mode;
bool dither;
@ -26,13 +33,15 @@ typedef struct pxl8_material {
bool dynamic_lighting;
bool per_pixel;
bool vertex_color_passthrough;
bool wireframe;
f32 emissive_intensity;
} pxl8_material;
} pxl8_gfx_material;
typedef struct pxl8_vertex {
pxl8_vec3 position;
pxl8_vec3 normal;
f32 u, v;
f32 lu, lv;
u8 color;
u8 light;
u8 _pad[2];
@ -62,62 +71,6 @@ static inline u32 pxl8_mesh_triangle_count(const pxl8_mesh* mesh) {
return mesh->index_count / 3;
}
static inline pxl8_material pxl8_material_create(u32 texture_id) {
return (pxl8_material){
.texture_id = texture_id,
.alpha = 255,
.blend_mode = PXL8_BLEND_OPAQUE,
.dither = true,
.double_sided = false,
.dynamic_lighting = false,
.per_pixel = false,
.vertex_color_passthrough = false,
.emissive_intensity = 0.0f,
};
}
static inline pxl8_material pxl8_material_with_alpha(pxl8_material m, u8 alpha) {
m.alpha = alpha;
if (alpha < 255 && m.blend_mode == PXL8_BLEND_OPAQUE) {
m.blend_mode = PXL8_BLEND_ALPHA;
}
return m;
}
static inline pxl8_material pxl8_material_with_blend(pxl8_material m, pxl8_blend_mode mode) {
m.blend_mode = mode;
return m;
}
static inline pxl8_material pxl8_material_with_double_sided(pxl8_material m) {
m.double_sided = true;
return m;
}
static inline pxl8_material pxl8_material_with_emissive(pxl8_material m, f32 intensity) {
m.emissive_intensity = intensity;
return m;
}
static inline pxl8_material pxl8_material_with_lighting(pxl8_material m) {
m.dynamic_lighting = true;
return m;
}
static inline pxl8_material pxl8_material_with_no_dither(pxl8_material m) {
m.dither = false;
return m;
}
static inline pxl8_material pxl8_material_with_passthrough(pxl8_material m) {
m.vertex_color_passthrough = true;
return m;
}
static inline pxl8_material pxl8_material_with_per_pixel(pxl8_material m) {
m.per_pixel = true;
return m;
}
#ifdef __cplusplus
}

View file

@ -5,10 +5,18 @@
#include "pxl8_ase.h"
#include "pxl8_color.h"
#include "pxl8_colormap.h"
#include "pxl8_log.h"
#include "pxl8_mem.h"
#define PXL8_PALETTE_HASH_SIZE 512
struct pxl8_palette_cube {
u8 colors[PXL8_PALETTE_SIZE * 3];
u8 table[PXL8_CUBE_ENTRIES];
u8 stable[PXL8_CUBE_ENTRIES];
};
typedef struct {
u32 color;
i16 index;
@ -200,7 +208,7 @@ static void update_cycle_colors(pxl8_palette* pal, u8 slot) {
}
pxl8_palette* pxl8_palette_create(void) {
pxl8_palette* pal = calloc(1, sizeof(pxl8_palette));
pxl8_palette* pal = pxl8_calloc(1, sizeof(pxl8_palette));
if (!pal) return NULL;
pal->colors[0] = 0x00000000;
@ -221,7 +229,7 @@ pxl8_palette* pxl8_palette_create(void) {
}
void pxl8_palette_destroy(pxl8_palette* pal) {
free(pal);
pxl8_free(pal);
}
pxl8_result pxl8_palette_load_ase(pxl8_palette* pal, const char* path) {
@ -332,10 +340,6 @@ void pxl8_palette_get_rgba(const pxl8_palette* pal, u8 idx, u8* r, u8* g, u8* b,
unpack_rgba(pal->colors[idx], r, g, b, a);
}
void pxl8_palette_set(pxl8_palette* pal, u8 idx, u32 color) {
if (pal) pal->colors[idx] = color;
}
void pxl8_palette_set_rgb(pxl8_palette* pal, u8 idx, u8 r, u8 g, u8 b) {
if (pal) pal->colors[idx] = pack_rgb(r, g, b);
}
@ -344,6 +348,17 @@ void pxl8_palette_set_rgba(pxl8_palette* pal, u8 idx, u8 r, u8 g, u8 b, u8 a) {
if (pal) pal->colors[idx] = pack_rgba(r, g, b, a);
}
void pxl8_set_palette(pxl8_palette* pal, const u32* colors, u16 count) {
if (!pal || !colors) return;
for (u16 i = 0; i < count; i++) {
u32 rgb = colors[i];
u8 r = (rgb >> 16) & 0xFF;
u8 g = (rgb >> 8) & 0xFF;
u8 b = rgb & 0xFF;
pal->colors[i] = pack_rgb(r, g, b);
}
}
void pxl8_palette_fill_gradient(pxl8_palette* pal, u8 start, u8 count, u32 from, u32 to) {
if (!pal || count == 0) return;
@ -472,3 +487,91 @@ pxl8_cycle_range pxl8_cycle_range_disabled(void) {
};
return range;
}
static u8 find_closest_stable(const pxl8_palette* pal, u8 r, u8 g, u8 b) {
u8 best_idx = 1;
u32 best_dist = 0xFFFFFFFF;
u8 dynamic_end = PXL8_DYNAMIC_RANGE_START + PXL8_DYNAMIC_RANGE_COUNT;
for (u32 i = 1; i < PXL8_FULLBRIGHT_START; i++) {
if (i >= PXL8_DYNAMIC_RANGE_START && i < dynamic_end) {
continue;
}
u8 pr, pg, pb;
pxl8_palette_get_rgb(pal, (u8)i, &pr, &pg, &pb);
i32 dr = (i32)r - (i32)pr;
i32 dg = (i32)g - (i32)pg;
i32 db = (i32)b - (i32)pb;
u32 dist = (u32)(dr * dr + dg * dg + db * db);
if (dist < best_dist) {
best_dist = dist;
best_idx = (u8)i;
if (dist == 0) break;
}
}
return best_idx;
}
pxl8_palette_cube* pxl8_palette_cube_create(const pxl8_palette* pal) {
pxl8_palette_cube* cube = pxl8_calloc(1, sizeof(pxl8_palette_cube));
if (!cube) return NULL;
pxl8_palette_cube_rebuild(cube, pal);
return cube;
}
void pxl8_palette_cube_destroy(pxl8_palette_cube* cube) {
pxl8_free(cube);
}
void pxl8_palette_cube_rebuild(pxl8_palette_cube* cube, const pxl8_palette* pal) {
if (!cube || !pal) return;
for (u32 i = 0; i < PXL8_PALETTE_SIZE; i++) {
u8 r, g, b;
pxl8_palette_get_rgb(pal, (u8)i, &r, &g, &b);
cube->colors[i * 3 + 0] = r;
cube->colors[i * 3 + 1] = g;
cube->colors[i * 3 + 2] = b;
}
for (u32 bi = 0; bi < PXL8_CUBE_SIZE; bi++) {
for (u32 gi = 0; gi < PXL8_CUBE_SIZE; gi++) {
for (u32 ri = 0; ri < PXL8_CUBE_SIZE; ri++) {
u32 idx = (bi * PXL8_CUBE_SIZE * PXL8_CUBE_SIZE) + (gi * PXL8_CUBE_SIZE) + ri;
u8 r8 = (u8)((ri * 255) / (PXL8_CUBE_SIZE - 1));
u8 g8 = (u8)((gi * 255) / (PXL8_CUBE_SIZE - 1));
u8 b8 = (u8)((bi * 255) / (PXL8_CUBE_SIZE - 1));
cube->table[idx] = pxl8_palette_find_closest(pal, r8, g8, b8);
cube->stable[idx] = find_closest_stable(pal, r8, g8, b8);
}
}
}
}
u8 pxl8_palette_cube_lookup(const pxl8_palette_cube* cube, u8 r, u8 g, u8 b) {
u32 ri = (r * (PXL8_CUBE_SIZE - 1)) / 255;
u32 gi = (g * (PXL8_CUBE_SIZE - 1)) / 255;
u32 bi = (b * (PXL8_CUBE_SIZE - 1)) / 255;
u32 idx = (bi * PXL8_CUBE_SIZE * PXL8_CUBE_SIZE) + (gi * PXL8_CUBE_SIZE) + ri;
return cube->table[idx];
}
u8 pxl8_palette_cube_lookup_stable(const pxl8_palette_cube* cube, u8 r, u8 g, u8 b) {
u32 ri = (r * (PXL8_CUBE_SIZE - 1)) / 255;
u32 gi = (g * (PXL8_CUBE_SIZE - 1)) / 255;
u32 bi = (b * (PXL8_CUBE_SIZE - 1)) / 255;
u32 idx = (bi * PXL8_CUBE_SIZE * PXL8_CUBE_SIZE) + (gi * PXL8_CUBE_SIZE) + ri;
return cube->stable[idx];
}
void pxl8_palette_cube_get_rgb(const pxl8_palette_cube* cube, u8 idx, u8* r, u8* g, u8* b) {
*r = cube->colors[idx * 3 + 0];
*g = cube->colors[idx * 3 + 1];
*b = cube->colors[idx * 3 + 2];
}

View file

@ -5,8 +5,11 @@
#define PXL8_PALETTE_SIZE 256
#define PXL8_MAX_CYCLES 8
#define PXL8_MAX_CYCLE_LEN 16
#define PXL8_CUBE_SIZE 32
#define PXL8_CUBE_ENTRIES (PXL8_CUBE_SIZE * PXL8_CUBE_SIZE * PXL8_CUBE_SIZE)
typedef struct pxl8_palette pxl8_palette;
typedef struct pxl8_palette_cube pxl8_palette_cube;
typedef enum pxl8_cycle_mode {
PXL8_CYCLE_LOOP,
@ -49,9 +52,9 @@ u32 pxl8_palette_color(const pxl8_palette* pal, u8 idx);
i32 pxl8_palette_index(const pxl8_palette* pal, u32 color);
void pxl8_palette_get_rgb(const pxl8_palette* pal, u8 idx, u8* r, u8* g, u8* b);
void pxl8_palette_get_rgba(const pxl8_palette* pal, u8 idx, u8* r, u8* g, u8* b, u8* a);
void pxl8_palette_set(pxl8_palette* pal, u8 idx, u32 color);
void pxl8_palette_set_rgb(pxl8_palette* pal, u8 idx, u8 r, u8 g, u8 b);
void pxl8_palette_set_rgba(pxl8_palette* pal, u8 idx, u8 r, u8 g, u8 b, u8 a);
void pxl8_set_palette(pxl8_palette* pal, const u32* colors, u16 count);
void pxl8_palette_fill_gradient(pxl8_palette* pal, u8 start, u8 count, u32 from, u32 to);
void pxl8_palette_fill_gradient_rgb(pxl8_palette* pal, u8 start, u8 count, u8 r0, u8 g0, u8 b0, u8 r1, u8 g1, u8 b1);
@ -65,6 +68,13 @@ void pxl8_palette_tick(pxl8_palette* pal, u16 delta_ticks);
pxl8_cycle_range pxl8_cycle_range_create(u8 start, u8 len, u16 period);
pxl8_cycle_range pxl8_cycle_range_disabled(void);
pxl8_palette_cube* pxl8_palette_cube_create(const pxl8_palette* pal);
void pxl8_palette_cube_destroy(pxl8_palette_cube* cube);
void pxl8_palette_cube_rebuild(pxl8_palette_cube* cube, const pxl8_palette* pal);
u8 pxl8_palette_cube_lookup(const pxl8_palette_cube* cube, u8 r, u8 g, u8 b);
u8 pxl8_palette_cube_lookup_stable(const pxl8_palette_cube* cube, u8 r, u8 g, u8 b);
void pxl8_palette_cube_get_rgb(const pxl8_palette_cube* cube, u8 idx, u8* r, u8* g, u8* b);
#ifdef __cplusplus
}
#endif

View file

@ -3,6 +3,7 @@
#include <stdlib.h>
#include "pxl8_gfx.h"
#include "pxl8_mem.h"
#include "pxl8_gfx2d.h"
#include "pxl8_palette.h"
#include "pxl8_rng.h"
@ -37,12 +38,12 @@ struct pxl8_particles {
};
pxl8_particles* pxl8_particles_create(u32 max_count, pxl8_rng* rng) {
pxl8_particles* ps = calloc(1, sizeof(pxl8_particles));
pxl8_particles* ps = pxl8_calloc(1, sizeof(pxl8_particles));
if (!ps) return NULL;
ps->particles = calloc(max_count, sizeof(pxl8_particle));
ps->particles = pxl8_calloc(max_count, sizeof(pxl8_particle));
if (!ps->particles) {
free(ps);
pxl8_free(ps);
return NULL;
}
@ -61,8 +62,8 @@ pxl8_particles* pxl8_particles_create(u32 max_count, pxl8_rng* rng) {
void pxl8_particles_destroy(pxl8_particles* ps) {
if (!ps) return;
free(ps->particles);
free(ps);
pxl8_free(ps->particles);
pxl8_free(ps);
}
void pxl8_particles_clear(pxl8_particles* ps) {

View file

@ -6,6 +6,7 @@
#include "pxl8_ase.h"
#include "pxl8_log.h"
#include "pxl8_macros.h"
#include "pxl8_mem.h"
#include "pxl8_tilesheet.h"
struct pxl8_tilesheet {
@ -58,7 +59,7 @@ static pxl8_tile_chunk* pxl8_get_or_create_chunk(pxl8_tilemap_layer* layer, u32
if (idx >= layer->chunks_wide * layer->chunks_high) return NULL;
if (!layer->chunks[idx]) {
layer->chunks[idx] = calloc(1, sizeof(pxl8_tile_chunk));
layer->chunks[idx] = pxl8_calloc(1, sizeof(pxl8_tile_chunk));
if (!layer->chunks[idx]) return NULL;
layer->chunks[idx]->chunk_x = chunk_x;
@ -75,7 +76,7 @@ pxl8_tilemap* pxl8_tilemap_create(u32 width, u32 height, u32 tile_size) {
return NULL;
}
pxl8_tilemap* tilemap = calloc(1, sizeof(pxl8_tilemap));
pxl8_tilemap* tilemap = pxl8_calloc(1, sizeof(pxl8_tilemap));
if (!tilemap) return NULL;
tilemap->width = width;
@ -85,7 +86,7 @@ pxl8_tilemap* pxl8_tilemap_create(u32 width, u32 height, u32 tile_size) {
tilemap->tilesheet = pxl8_tilesheet_create(tilemap->tile_size);
if (!tilemap->tilesheet) {
free(tilemap);
pxl8_free(tilemap);
return NULL;
}
@ -103,13 +104,13 @@ pxl8_tilemap* pxl8_tilemap_create(u32 width, u32 height, u32 tile_size) {
layer->visible = (i == 0);
layer->opacity = 255;
layer->chunks = calloc(layer->chunk_count, sizeof(pxl8_tile_chunk*));
layer->chunks = pxl8_calloc(layer->chunk_count, sizeof(pxl8_tile_chunk*));
if (!layer->chunks) {
for (u32 j = 0; j < i; j++) {
free(tilemap->layers[j].chunks);
pxl8_free(tilemap->layers[j].chunks);
}
if (tilemap->tilesheet) pxl8_tilesheet_destroy(tilemap->tilesheet);
free(tilemap);
pxl8_free(tilemap);
return NULL;
}
}
@ -125,17 +126,17 @@ void pxl8_tilemap_destroy(pxl8_tilemap* tilemap) {
if (layer->chunks) {
for (u32 j = 0; j < layer->chunk_count; j++) {
if (layer->chunks[j]) {
free(layer->chunks[j]);
pxl8_free(layer->chunks[j]);
}
}
free(layer->chunks);
pxl8_free(layer->chunks);
}
}
if (tilemap->tilesheet) pxl8_tilesheet_unref(tilemap->tilesheet);
if (tilemap->tile_user_data) free(tilemap->tile_user_data);
if (tilemap->tile_user_data) pxl8_free(tilemap->tile_user_data);
free(tilemap);
pxl8_free(tilemap);
}
u32 pxl8_tilemap_get_width(const pxl8_tilemap* tilemap) {
@ -155,7 +156,7 @@ void pxl8_tilemap_set_tile_user_data(pxl8_tilemap* tilemap, u16 tile_id, void* u
if (tile_id >= tilemap->tile_user_data_capacity) {
u32 new_capacity = tile_id + 64;
void** new_data = realloc(tilemap->tile_user_data, new_capacity * sizeof(void*));
void** new_data = pxl8_realloc(tilemap->tile_user_data, new_capacity * sizeof(void*));
if (!new_data) return;
for (u32 i = tilemap->tile_user_data_capacity; i < new_capacity; i++) {
@ -478,7 +479,7 @@ void pxl8_tilemap_compress(pxl8_tilemap* tilemap) {
}
if (!has_tiles) {
free(chunk);
pxl8_free(chunk);
layer->chunks[j] = NULL;
layer->allocated_chunks--;
} else {
@ -535,8 +536,8 @@ pxl8_result pxl8_tilemap_load_ase(pxl8_tilemap* tilemap, const char* filepath, u
u32 tilesheet_width = tiles_per_row * tilemap->tile_size;
u32 tilesheet_height = tilesheet_rows * tilemap->tile_size;
if (tilemap->tilesheet->data) free(tilemap->tilesheet->data);
tilemap->tilesheet->data = calloc(tilesheet_width * tilesheet_height, 1);
if (tilemap->tilesheet->data) pxl8_free(tilemap->tilesheet->data);
tilemap->tilesheet->data = pxl8_calloc(tilesheet_width * tilesheet_height, 1);
if (!tilemap->tilesheet->data) {
pxl8_ase_destroy(&ase_file);
return PXL8_ERROR_OUT_OF_MEMORY;
@ -548,8 +549,8 @@ pxl8_result pxl8_tilemap_load_ase(pxl8_tilemap* tilemap, const char* filepath, u
tilemap->tilesheet->total_tiles = tileset->tile_count;
tilemap->tilesheet->pixel_mode = PXL8_PIXEL_INDEXED;
if (tilemap->tilesheet->tile_valid) free(tilemap->tilesheet->tile_valid);
tilemap->tilesheet->tile_valid = calloc(tileset->tile_count + 1, sizeof(bool));
if (tilemap->tilesheet->tile_valid) pxl8_free(tilemap->tilesheet->tile_valid);
tilemap->tilesheet->tile_valid = pxl8_calloc(tileset->tile_count + 1, sizeof(bool));
for (u32 i = 0; i < tileset->tile_count; i++) {
u32 sheet_row = i / tiles_per_row;

View file

@ -7,6 +7,7 @@
#include "pxl8_color.h"
#include "pxl8_gfx.h"
#include "pxl8_log.h"
#include "pxl8_mem.h"
#include "pxl8_tilemap.h"
struct pxl8_tilesheet {
@ -32,7 +33,7 @@ struct pxl8_tilesheet {
};
pxl8_tilesheet* pxl8_tilesheet_create(u32 tile_size) {
pxl8_tilesheet* tilesheet = calloc(1, sizeof(pxl8_tilesheet));
pxl8_tilesheet* tilesheet = pxl8_calloc(1, sizeof(pxl8_tilesheet));
if (!tilesheet) return NULL;
tilesheet->tile_size = tile_size;
@ -45,37 +46,37 @@ void pxl8_tilesheet_destroy(pxl8_tilesheet* tilesheet) {
if (!tilesheet) return;
if (tilesheet->data) {
free(tilesheet->data);
pxl8_free(tilesheet->data);
}
if (tilesheet->tile_valid) {
free(tilesheet->tile_valid);
pxl8_free(tilesheet->tile_valid);
}
if (tilesheet->animations) {
for (u32 i = 0; i < tilesheet->animation_count; i++) {
if (tilesheet->animations[i].frames) {
free(tilesheet->animations[i].frames);
pxl8_free(tilesheet->animations[i].frames);
}
}
free(tilesheet->animations);
pxl8_free(tilesheet->animations);
}
if (tilesheet->properties) {
free(tilesheet->properties);
pxl8_free(tilesheet->properties);
}
if (tilesheet->autotile_rules) {
for (u32 i = 0; i <= tilesheet->total_tiles; i++) {
if (tilesheet->autotile_rules[i]) {
free(tilesheet->autotile_rules[i]);
pxl8_free(tilesheet->autotile_rules[i]);
}
}
free(tilesheet->autotile_rules);
free(tilesheet->autotile_rule_counts);
pxl8_free(tilesheet->autotile_rules);
pxl8_free(tilesheet->autotile_rule_counts);
}
free(tilesheet);
pxl8_free(tilesheet);
}
pxl8_result pxl8_tilesheet_load(pxl8_tilesheet* tilesheet, const char* filepath, pxl8_gfx* gfx) {
@ -89,7 +90,7 @@ pxl8_result pxl8_tilesheet_load(pxl8_tilesheet* tilesheet, const char* filepath,
}
if (tilesheet->data) {
free(tilesheet->data);
pxl8_free(tilesheet->data);
}
u32 width = ase_file.header.width;
@ -111,8 +112,8 @@ pxl8_result pxl8_tilesheet_load(pxl8_tilesheet* tilesheet, const char* filepath,
u16 ase_depth = ase_file.header.color_depth;
bool gfx_hicolor = (tilesheet->pixel_mode == PXL8_PIXEL_HICOLOR);
size_t data_size = pixel_count * pxl8_bytes_per_pixel(tilesheet->pixel_mode);
tilesheet->data = malloc(data_size);
usize data_size = pixel_count * pxl8_bytes_per_pixel(tilesheet->pixel_mode);
tilesheet->data = pxl8_malloc(data_size);
if (!tilesheet->data) {
pxl8_ase_destroy(&ase_file);
return PXL8_ERROR_OUT_OF_MEMORY;
@ -138,9 +139,9 @@ pxl8_result pxl8_tilesheet_load(pxl8_tilesheet* tilesheet, const char* filepath,
} else if (ase_depth == 8 && gfx_hicolor) {
pxl8_warn("Indexed ASE with hicolor gfx - storing as indexed");
tilesheet->pixel_mode = PXL8_PIXEL_INDEXED;
u8* new_data = realloc(tilesheet->data, pixel_count);
u8* new_data = pxl8_realloc(tilesheet->data, pixel_count);
if (!new_data) {
free(tilesheet->data);
pxl8_free(tilesheet->data);
tilesheet->data = NULL;
pxl8_ase_destroy(&ase_file);
return PXL8_ERROR_OUT_OF_MEMORY;
@ -149,16 +150,16 @@ pxl8_result pxl8_tilesheet_load(pxl8_tilesheet* tilesheet, const char* filepath,
memcpy(tilesheet->data, src, pixel_count);
} else {
pxl8_error("Unsupported ASE color depth %d for gfx mode", ase_depth);
free(tilesheet->data);
pxl8_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));
tilesheet->tile_valid = pxl8_calloc(tilesheet->total_tiles + 1, sizeof(bool));
if (!tilesheet->tile_valid) {
free(tilesheet->data);
pxl8_free(tilesheet->data);
tilesheet->data = NULL;
pxl8_ase_destroy(&ase_file);
return PXL8_ERROR_OUT_OF_MEMORY;
@ -259,14 +260,14 @@ pxl8_result pxl8_tilesheet_add_animation(pxl8_tilesheet* tilesheet, u16 base_til
if (base_tile_id == 0 || base_tile_id > tilesheet->total_tiles) return PXL8_ERROR_INVALID_ARGUMENT;
if (!tilesheet->animations) {
tilesheet->animations = calloc(tilesheet->total_tiles + 1, sizeof(pxl8_tile_animation));
tilesheet->animations = pxl8_calloc(tilesheet->total_tiles + 1, sizeof(pxl8_tile_animation));
if (!tilesheet->animations) return PXL8_ERROR_OUT_OF_MEMORY;
}
pxl8_tile_animation* anim = &tilesheet->animations[base_tile_id];
if (anim->frames) free(anim->frames);
if (anim->frames) pxl8_free(anim->frames);
anim->frames = malloc(frame_count * sizeof(u16));
anim->frames = pxl8_malloc(frame_count * sizeof(u16));
if (!anim->frames) return PXL8_ERROR_OUT_OF_MEMORY;
memcpy(anim->frames, frames, frame_count * sizeof(u16));
@ -340,7 +341,7 @@ void pxl8_tilesheet_set_tile_property(pxl8_tilesheet* tilesheet, u16 tile_id,
if (!tilesheet || !props || tile_id == 0 || tile_id > tilesheet->total_tiles) return;
if (!tilesheet->properties) {
tilesheet->properties = calloc(tilesheet->total_tiles + 1, sizeof(pxl8_tile_properties));
tilesheet->properties = pxl8_calloc(tilesheet->total_tiles + 1, sizeof(pxl8_tile_properties));
if (!tilesheet->properties) return;
}
@ -365,15 +366,15 @@ pxl8_result pxl8_tilesheet_add_autotile_rule(pxl8_tilesheet* tilesheet, u16 base
}
if (!tilesheet->autotile_rules) {
tilesheet->autotile_rules = calloc(tilesheet->total_tiles + 1, sizeof(pxl8_autotile_rule*));
tilesheet->autotile_rule_counts = calloc(tilesheet->total_tiles + 1, sizeof(u32));
tilesheet->autotile_rules = pxl8_calloc(tilesheet->total_tiles + 1, sizeof(pxl8_autotile_rule*));
tilesheet->autotile_rule_counts = pxl8_calloc(tilesheet->total_tiles + 1, sizeof(u32));
if (!tilesheet->autotile_rules || !tilesheet->autotile_rule_counts) {
return PXL8_ERROR_OUT_OF_MEMORY;
}
}
u32 count = tilesheet->autotile_rule_counts[base_tile_id];
pxl8_autotile_rule* new_rules = realloc(tilesheet->autotile_rules[base_tile_id],
pxl8_autotile_rule* new_rules = pxl8_realloc(tilesheet->autotile_rules[base_tile_id],
(count + 1) * sizeof(pxl8_autotile_rule));
if (!new_rules) return PXL8_ERROR_OUT_OF_MEMORY;

View file

@ -6,6 +6,7 @@
#include "pxl8_gfx.h"
#include "pxl8_log.h"
#include "pxl8_math.h"
#include "pxl8_mem.h"
pxl8_transition* pxl8_transition_create(pxl8_transition_type type, f32 duration) {
if (duration <= 0.0f) {
@ -13,7 +14,7 @@ pxl8_transition* pxl8_transition_create(pxl8_transition_type type, f32 duration)
return NULL;
}
pxl8_transition* transition = (pxl8_transition*)calloc(1, sizeof(pxl8_transition));
pxl8_transition* transition = (pxl8_transition*)pxl8_calloc(1, sizeof(pxl8_transition));
if (!transition) {
pxl8_error("Failed to allocate transition");
return NULL;
@ -33,7 +34,7 @@ pxl8_transition* pxl8_transition_create(pxl8_transition_type type, f32 duration)
void pxl8_transition_destroy(pxl8_transition* transition) {
if (!transition) return;
free(transition);
pxl8_free(transition);
}
f32 pxl8_transition_get_progress(const pxl8_transition* transition) {