wip sw renderer...bsp is borked, also lots of other things
This commit is contained in:
parent
415d424057
commit
c771fa665d
56 changed files with 8151 additions and 1261 deletions
|
|
@ -56,8 +56,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 +104,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
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -183,8 +183,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 +212,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_mul(proj, view);
|
||||
|
||||
pxl8_vec4 clip = pxl8_mat4_mul_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);
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ typedef struct pxl8_skyline {
|
|||
struct pxl8_atlas {
|
||||
u32 height, width;
|
||||
u8* pixels;
|
||||
u8* pixels_tiled;
|
||||
u32 tiled_capacity, tiled_size;
|
||||
|
||||
bool dirty;
|
||||
|
||||
|
|
@ -195,6 +197,7 @@ void pxl8_atlas_destroy(pxl8_atlas* atlas) {
|
|||
free(atlas->entries);
|
||||
free(atlas->free_list);
|
||||
free(atlas->pixels);
|
||||
free(atlas->pixels_tiled);
|
||||
free(atlas->skyline.nodes);
|
||||
free(atlas);
|
||||
}
|
||||
|
|
@ -209,6 +212,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;
|
||||
|
||||
|
|
@ -334,6 +344,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 +360,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*)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 +412,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,9 @@
|
|||
#include "pxl8_cpu.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pxl8_simd.h"
|
||||
|
||||
struct pxl8_cpu_render_target {
|
||||
u8* framebuffer;
|
||||
u32 height;
|
||||
|
|
@ -119,6 +118,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;
|
||||
|
|
@ -231,14 +231,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 +244,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;
|
||||
}
|
||||
|
|
@ -699,6 +701,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 +745,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 +966,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 +1008,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 +1036,43 @@ 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) {
|
||||
u8 color = (material->texture_id > 0) ? (u8)material->texture_id : 15;
|
||||
rasterize_triangle_wireframe(cpu, vo0, vo1, vo2, color, 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,7 +1095,7 @@ 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;
|
||||
|
|
@ -1237,10 +1303,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1301,12 +1369,8 @@ 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
|
||||
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];
|
||||
|
|
@ -1392,25 +1456,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,7 +53,7 @@ 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
|
||||
);
|
||||
|
||||
|
|
@ -67,10 +72,7 @@ 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
|
||||
u32 glow_count
|
||||
);
|
||||
|
||||
void pxl8_cpu_resolve(pxl8_cpu_backend* cpu);
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
@ -25,7 +24,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 +33,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;
|
||||
|
|
@ -78,10 +75,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 +92,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;
|
||||
|
|
@ -187,13 +175,11 @@ 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_palette_cube_destroy(gfx->palette_cube);
|
||||
pxl8_palette_destroy(gfx->palette);
|
||||
free(gfx->sprite_cache);
|
||||
|
|
@ -523,6 +509,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 +518,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 +555,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 +573,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -667,7 +661,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:
|
||||
|
|
@ -733,31 +727,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 +755,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;
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@
|
|||
#include "pxl8_gfx2d.h"
|
||||
#include "pxl8_gfx3d.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;
|
||||
|
||||
|
|
@ -76,13 +78,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 +100,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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ void pxl8_3d_begin_frame(pxl8_gfx* gfx, const pxl8_3d_camera* camera, const pxl8
|
|||
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(pxl8_gfx* gfx, const pxl8_mesh* mesh, const pxl8_mat4* model, const pxl8_gfx_material* material);
|
||||
void pxl8_3d_draw_mesh_wireframe(pxl8_gfx* gfx, const pxl8_mesh* mesh, pxl8_mat4 model, u8 color);
|
||||
void pxl8_3d_end_frame(pxl8_gfx* gfx);
|
||||
u8* pxl8_3d_get_framebuffer(pxl8_gfx* gfx);
|
||||
|
|
|
|||
113
src/gfx/pxl8_lightmap.c
Normal file
113
src/gfx/pxl8_lightmap.c
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
#include "pxl8_lightmap.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
pxl8_lightmap* pxl8_lightmap_create(u32 width, u32 height, u32 scale) {
|
||||
pxl8_lightmap* lm = calloc(1, sizeof(pxl8_lightmap));
|
||||
if (!lm) return NULL;
|
||||
|
||||
lm->width = width;
|
||||
lm->height = height;
|
||||
lm->scale = scale;
|
||||
lm->data = calloc(width * height * 3, sizeof(u8));
|
||||
if (!lm->data) {
|
||||
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;
|
||||
free(lm->data);
|
||||
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
|
||||
|
|
@ -17,8 +17,9 @@ typedef enum pxl8_blend_mode {
|
|||
PXL8_BLEND_ADDITIVE,
|
||||
} pxl8_blend_mode;
|
||||
|
||||
typedef struct pxl8_material {
|
||||
typedef struct pxl8_gfx_material {
|
||||
u32 texture_id;
|
||||
u32 lightmap_id;
|
||||
u8 alpha;
|
||||
u8 blend_mode;
|
||||
bool dither;
|
||||
|
|
@ -26,13 +27,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 +65,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,17 @@
|
|||
|
||||
#include "pxl8_ase.h"
|
||||
#include "pxl8_color.h"
|
||||
#include "pxl8_colormap.h"
|
||||
#include "pxl8_log.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;
|
||||
|
|
@ -332,10 +339,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 +347,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 +486,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 = 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];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue