improve sw renderer
This commit is contained in:
parent
415d424057
commit
39ee0fefb7
89 changed files with 9380 additions and 2307 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
62
src/gfx/pxl8_glows.c
Normal 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
42
src/gfx/pxl8_glows.h
Normal 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
114
src/gfx/pxl8_lightmap.c
Normal 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
48
src/gfx/pxl8_lightmap.h
Normal 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
64
src/gfx/pxl8_lights.c
Normal 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
33
src/gfx/pxl8_lights.h
Normal 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
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue