major gfx refactor
This commit is contained in:
parent
0c0aa792c1
commit
3c3e961995
58 changed files with 3681 additions and 2982 deletions
|
|
@ -6,10 +6,10 @@ typedef struct pxl8_atlas pxl8_atlas;
|
|||
|
||||
typedef struct pxl8_atlas_entry {
|
||||
bool active;
|
||||
i32 h, w, x, y;
|
||||
u8 log2_h, log2_w;
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
#ifndef PXL8_BACKEND_H
|
||||
#define PXL8_BACKEND_H
|
||||
|
||||
#include "pxl8_cpu.h"
|
||||
|
||||
typedef enum {
|
||||
PXL8_GFX_BACKEND_CPU,
|
||||
PXL8_GFX_BACKEND_GPU,
|
||||
} pxl8_gfx_backend_type;
|
||||
|
||||
typedef struct {
|
||||
pxl8_gfx_backend_type type;
|
||||
union {
|
||||
pxl8_cpu_backend* cpu;
|
||||
void* gpu;
|
||||
};
|
||||
} pxl8_gfx_backend;
|
||||
|
||||
#endif
|
||||
|
|
@ -58,7 +58,7 @@ static inline u8 pxl8_colormap_lookup(const pxl8_colormap* cm, u8 pal_idx, pxl8_
|
|||
}
|
||||
|
||||
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);
|
||||
u8 dithered = pxl8_gfx_dither((f32)intensity + 0.5f, x, y);
|
||||
u32 light_row = ((u32)light_color << 3) + (dithered >> 5);
|
||||
return cm->table[(light_row << 8) + pal_idx];
|
||||
}
|
||||
|
|
|
|||
1669
src/gfx/pxl8_cpu.c
1669
src/gfx/pxl8_cpu.c
File diff suppressed because it is too large
Load diff
|
|
@ -1,94 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_atlas.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
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct pxl8_cpu_backend pxl8_cpu_backend;
|
||||
typedef struct pxl8_cpu_render_target pxl8_cpu_render_target;
|
||||
|
||||
typedef struct pxl8_cpu_render_target_desc {
|
||||
u32 width;
|
||||
u32 height;
|
||||
bool with_depth;
|
||||
bool with_lighting;
|
||||
} pxl8_cpu_render_target_desc;
|
||||
|
||||
pxl8_cpu_backend* pxl8_cpu_create(u32 width, u32 height);
|
||||
void pxl8_cpu_destroy(pxl8_cpu_backend* cpu);
|
||||
|
||||
void pxl8_cpu_begin_frame(pxl8_cpu_backend* cpu, const pxl8_3d_frame* frame);
|
||||
void pxl8_cpu_end_frame(pxl8_cpu_backend* cpu);
|
||||
|
||||
void pxl8_cpu_clear(pxl8_cpu_backend* cpu, u8 color);
|
||||
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);
|
||||
void pxl8_cpu_draw_line_2d(pxl8_cpu_backend* cpu, i32 x0, i32 y0, i32 x1, i32 y1, u8 color);
|
||||
void pxl8_cpu_draw_rect(pxl8_cpu_backend* cpu, i32 x, i32 y, i32 w, i32 h, u8 color);
|
||||
void pxl8_cpu_draw_rect_fill(pxl8_cpu_backend* cpu, i32 x, i32 y, i32 w, i32 h, u8 color);
|
||||
void pxl8_cpu_draw_circle(pxl8_cpu_backend* cpu, i32 cx, i32 cy, i32 radius, u8 color);
|
||||
void pxl8_cpu_draw_circle_fill(pxl8_cpu_backend* cpu, i32 cx, i32 cy, i32 radius, u8 color);
|
||||
void pxl8_cpu_draw_line_3d(pxl8_cpu_backend* cpu, pxl8_vec3 v0, pxl8_vec3 v1, u8 color);
|
||||
|
||||
void pxl8_cpu_draw_mesh(
|
||||
pxl8_cpu_backend* cpu,
|
||||
const pxl8_mesh* mesh,
|
||||
const pxl8_mat4* model,
|
||||
const pxl8_gfx_material* material,
|
||||
const pxl8_atlas* textures
|
||||
);
|
||||
|
||||
u8* pxl8_cpu_get_framebuffer(pxl8_cpu_backend* cpu);
|
||||
u32* pxl8_cpu_get_light_accum(pxl8_cpu_backend* cpu);
|
||||
u32* pxl8_cpu_get_output(pxl8_cpu_backend* cpu);
|
||||
u16* pxl8_cpu_get_zbuffer(pxl8_cpu_backend* cpu);
|
||||
u32 pxl8_cpu_get_height(const pxl8_cpu_backend* cpu);
|
||||
u32 pxl8_cpu_get_width(const pxl8_cpu_backend* cpu);
|
||||
|
||||
void pxl8_cpu_render_glows(
|
||||
pxl8_cpu_backend* cpu,
|
||||
const pxl8_glow* glows,
|
||||
u32 glow_count
|
||||
);
|
||||
|
||||
void pxl8_cpu_resolve(pxl8_cpu_backend* cpu);
|
||||
|
||||
pxl8_cpu_render_target* pxl8_cpu_create_render_target(const pxl8_cpu_render_target_desc* desc);
|
||||
void pxl8_cpu_destroy_render_target(pxl8_cpu_render_target* target);
|
||||
|
||||
pxl8_cpu_render_target* pxl8_cpu_get_target(pxl8_cpu_backend* cpu);
|
||||
void pxl8_cpu_set_target(pxl8_cpu_backend* cpu, pxl8_cpu_render_target* target);
|
||||
|
||||
void pxl8_cpu_blit(pxl8_cpu_backend* cpu, pxl8_cpu_render_target* src, i32 x, i32 y, u8 transparent_idx);
|
||||
|
||||
bool pxl8_cpu_push_target(pxl8_cpu_backend* cpu);
|
||||
void pxl8_cpu_pop_target(pxl8_cpu_backend* cpu);
|
||||
u32 pxl8_cpu_get_target_depth(const pxl8_cpu_backend* cpu);
|
||||
|
||||
u8* pxl8_cpu_render_target_get_framebuffer(pxl8_cpu_render_target* target);
|
||||
u32 pxl8_cpu_render_target_get_height(const pxl8_cpu_render_target* target);
|
||||
u32 pxl8_cpu_render_target_get_width(const pxl8_cpu_render_target* target);
|
||||
u32* pxl8_cpu_render_target_get_light_accum(pxl8_cpu_render_target* target);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
@ -8,8 +8,16 @@ extern "C" {
|
|||
|
||||
extern const u8 PXL8_BAYER_4X4[16];
|
||||
|
||||
static inline i8 pxl8_bayer_offset(u32 x, u32 y) {
|
||||
return (i8)(PXL8_BAYER_4X4[(y & 3) * 4 + (x & 3)]) - 8;
|
||||
static inline u8 pxl8_gfx_dither(f32 value, u32 x, u32 y) {
|
||||
if (value <= 0.0f) return 0;
|
||||
if (value >= 255.0f) return 255;
|
||||
u8 base = (u8)value;
|
||||
f32 frac = value - (f32)base;
|
||||
f32 threshold = (PXL8_BAYER_4X4[(y & 3) * 4 + (x & 3)] + 0.5f) * (1.0f / 16.0f);
|
||||
if (frac > threshold) {
|
||||
return base < 255 ? (u8)(base + 1) : 255;
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
static inline u8 pxl8_ordered_dither(u8 value, u32 x, u32 y) {
|
||||
|
|
@ -18,24 +26,6 @@ static inline u8 pxl8_ordered_dither(u8 value, u32 x, u32 y) {
|
|||
return result > 255 ? 255 : (u8)result;
|
||||
}
|
||||
|
||||
static inline u8 pxl8_dither_light(u8 light, u32 x, u32 y) {
|
||||
i8 offset = pxl8_bayer_offset(x, y) >> 1;
|
||||
i16 result = (i16)light + offset;
|
||||
if (result < 0) return 0;
|
||||
if (result > 255) return 255;
|
||||
return (u8)result;
|
||||
}
|
||||
|
||||
static inline u8 pxl8_dither_float(f32 value, u32 x, u32 y) {
|
||||
u8 floor_val = (u8)value;
|
||||
f32 frac = value - (f32)floor_val;
|
||||
f32 threshold = (PXL8_BAYER_4X4[(y & 3) * 4 + (x & 3)] + 0.5f) * (1.0f / 16.0f);
|
||||
if (frac > threshold) {
|
||||
return floor_val < 255 ? floor_val + 1 : 255;
|
||||
}
|
||||
return floor_val;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
#include "pxl8_ase.h"
|
||||
#include "pxl8_atlas.h"
|
||||
#include "pxl8_backend.h"
|
||||
#include "pxl8_blit.h"
|
||||
#include "pxl8_color.h"
|
||||
#include "pxl8_colormap.h"
|
||||
|
|
@ -15,36 +14,77 @@
|
|||
#include "pxl8_macros.h"
|
||||
#include "pxl8_math.h"
|
||||
#include "pxl8_mem.h"
|
||||
#include "pxl8_render.h"
|
||||
#include "pxl8_shader_registry.h"
|
||||
#include "pxl8_sys.h"
|
||||
#include "pxl8_types.h"
|
||||
|
||||
#define PXL8_MAX_TARGET_STACK 8
|
||||
|
||||
typedef struct pxl8_sprite_cache_entry {
|
||||
char path[256];
|
||||
u32 sprite_id;
|
||||
bool active;
|
||||
} pxl8_sprite_cache_entry;
|
||||
|
||||
typedef struct pxl8_target_entry {
|
||||
pxl8_gfx_texture color;
|
||||
pxl8_gfx_texture depth;
|
||||
pxl8_gfx_texture light;
|
||||
} pxl8_target_entry;
|
||||
|
||||
#define PXL8_MAX_FRAME_RESOURCES 512
|
||||
#define PXL8_STREAM_VB_SIZE (256 * 1024)
|
||||
#define PXL8_STREAM_IB_SIZE (512 * 1024)
|
||||
|
||||
typedef struct pxl8_frame_resources {
|
||||
pxl8_gfx_texture textures[PXL8_MAX_FRAME_RESOURCES];
|
||||
pxl8_gfx_buffer buffers[PXL8_MAX_FRAME_RESOURCES];
|
||||
pxl8_gfx_pipeline pipelines[PXL8_MAX_FRAME_RESOURCES];
|
||||
pxl8_gfx_bindings bindings[PXL8_MAX_FRAME_RESOURCES];
|
||||
u32 texture_count;
|
||||
u32 buffer_count;
|
||||
u32 pipeline_count;
|
||||
u32 bindings_count;
|
||||
} pxl8_frame_resources;
|
||||
|
||||
struct pxl8_gfx {
|
||||
pxl8_atlas* atlas;
|
||||
pxl8_gfx_backend backend;
|
||||
pxl8_renderer* renderer;
|
||||
pxl8_gfx_texture color_target;
|
||||
pxl8_gfx_texture depth_target;
|
||||
pxl8_gfx_texture light_target;
|
||||
pxl8_gfx_cmdbuf* cmdbuf;
|
||||
pxl8_target_entry target_stack[PXL8_MAX_TARGET_STACK];
|
||||
u32 target_stack_depth;
|
||||
const pxl8_bsp* bsp;
|
||||
pxl8_colormap* colormap;
|
||||
u8* framebuffer;
|
||||
i32 framebuffer_height;
|
||||
i32 framebuffer_width;
|
||||
pxl8_frustum frustum;
|
||||
const pxl8_hal* hal;
|
||||
bool initialized;
|
||||
u32* output;
|
||||
pxl8_palette* palette;
|
||||
pxl8_palette_cube* palette_cube;
|
||||
pxl8_gfx_pass frame_pass;
|
||||
pxl8_frame_resources frame_res;
|
||||
pxl8_pixel_mode pixel_mode;
|
||||
void* platform_data;
|
||||
const pxl8_sdf* sdf;
|
||||
pxl8_sprite_cache_entry* sprite_cache;
|
||||
u32 sprite_cache_capacity;
|
||||
u32 sprite_cache_count;
|
||||
pxl8_viewport viewport;
|
||||
pxl8_mat4 view_proj;
|
||||
pxl8_3d_frame frame;
|
||||
|
||||
pxl8_gfx_buffer stream_vb;
|
||||
pxl8_gfx_buffer stream_ib;
|
||||
u32 stream_vb_capacity;
|
||||
u32 stream_ib_capacity;
|
||||
u32 stream_vb_offset;
|
||||
u32 stream_ib_offset;
|
||||
bool wireframe;
|
||||
};
|
||||
|
||||
pxl8_bounds pxl8_gfx_get_bounds(pxl8_gfx* gfx) {
|
||||
|
|
@ -63,15 +103,12 @@ pxl8_pixel_mode pxl8_gfx_get_pixel_mode(pxl8_gfx* gfx) {
|
|||
|
||||
u8* pxl8_gfx_get_framebuffer_indexed(pxl8_gfx* gfx) {
|
||||
if (!gfx || gfx->pixel_mode == PXL8_PIXEL_HICOLOR) return NULL;
|
||||
if (gfx->backend.type == PXL8_GFX_BACKEND_CPU) {
|
||||
return pxl8_cpu_get_framebuffer(gfx->backend.cpu);
|
||||
}
|
||||
return gfx->framebuffer;
|
||||
return (u8*)pxl8_texture_get_data(gfx->renderer, gfx->color_target);
|
||||
}
|
||||
|
||||
u16* pxl8_gfx_get_framebuffer_hicolor(pxl8_gfx* gfx) {
|
||||
if (!gfx || gfx->pixel_mode != PXL8_PIXEL_HICOLOR) return NULL;
|
||||
return (u16*)gfx->framebuffer;
|
||||
return (u16*)pxl8_texture_get_data(gfx->renderer, gfx->color_target);
|
||||
}
|
||||
|
||||
i32 pxl8_gfx_get_height(const pxl8_gfx* gfx) {
|
||||
|
|
@ -80,10 +117,30 @@ i32 pxl8_gfx_get_height(const pxl8_gfx* gfx) {
|
|||
|
||||
u32* pxl8_gfx_get_light_accum(pxl8_gfx* gfx) {
|
||||
if (!gfx) return NULL;
|
||||
if (gfx->backend.type == PXL8_GFX_BACKEND_CPU) {
|
||||
return pxl8_cpu_get_light_accum(gfx->backend.cpu);
|
||||
return (u32*)pxl8_texture_get_data(gfx->renderer, gfx->light_target);
|
||||
}
|
||||
|
||||
u32* pxl8_gfx_get_output(pxl8_gfx* gfx) {
|
||||
if (!gfx) return NULL;
|
||||
return gfx->output;
|
||||
}
|
||||
|
||||
void pxl8_gfx_resolve(pxl8_gfx* gfx) {
|
||||
if (!gfx || !gfx->initialized) return;
|
||||
if (gfx->pixel_mode != PXL8_PIXEL_INDEXED) return;
|
||||
|
||||
const u32* pal = gfx->palette ? pxl8_palette_colors(gfx->palette) : NULL;
|
||||
if (!pal) {
|
||||
pxl8_error("resolve: no palette!");
|
||||
return;
|
||||
}
|
||||
return NULL;
|
||||
pxl8_resolve_to_rgba(
|
||||
gfx->renderer,
|
||||
gfx->color_target,
|
||||
gfx->light_target,
|
||||
pal,
|
||||
gfx->output
|
||||
);
|
||||
}
|
||||
|
||||
const u32* pxl8_gfx_palette_colors(pxl8_gfx* gfx) {
|
||||
|
|
@ -93,10 +150,7 @@ const u32* pxl8_gfx_palette_colors(pxl8_gfx* gfx) {
|
|||
|
||||
u16* pxl8_gfx_get_zbuffer(pxl8_gfx* gfx) {
|
||||
if (!gfx) return NULL;
|
||||
if (gfx->backend.type == PXL8_GFX_BACKEND_CPU) {
|
||||
return pxl8_cpu_get_zbuffer(gfx->backend.cpu);
|
||||
}
|
||||
return NULL;
|
||||
return (u16*)pxl8_texture_get_data(gfx->renderer, gfx->depth_target);
|
||||
}
|
||||
|
||||
i32 pxl8_gfx_get_width(const pxl8_gfx* gfx) {
|
||||
|
|
@ -123,9 +177,6 @@ u8 pxl8_gfx_find_color(pxl8_gfx* gfx, u32 color) {
|
|||
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) {
|
||||
|
|
@ -136,9 +187,6 @@ i32 pxl8_gfx_load_palette(pxl8_gfx* gfx, const char* filepath) {
|
|||
if (gfx->colormap) {
|
||||
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, pxl8_palette_colors(gfx->palette));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -149,6 +197,8 @@ pxl8_gfx* pxl8_gfx_create(
|
|||
pxl8_pixel_mode mode,
|
||||
pxl8_resolution resolution
|
||||
) {
|
||||
pxl8_shader_registry_init();
|
||||
|
||||
pxl8_gfx* gfx = (pxl8_gfx*)pxl8_calloc(1, sizeof(pxl8_gfx));
|
||||
if (!gfx) {
|
||||
pxl8_error("Failed to allocate graphics context");
|
||||
|
|
@ -173,29 +223,87 @@ pxl8_gfx* pxl8_gfx_create(
|
|||
gfx->palette = pxl8_palette_create();
|
||||
}
|
||||
|
||||
gfx->backend.type = PXL8_GFX_BACKEND_CPU;
|
||||
gfx->backend.cpu = pxl8_cpu_create(gfx->framebuffer_width, gfx->framebuffer_height);
|
||||
if (!gfx->backend.cpu) {
|
||||
pxl8_error("Failed to create CPU backend");
|
||||
gfx->renderer = pxl8_renderer_create(gfx->framebuffer_width, gfx->framebuffer_height);
|
||||
if (!gfx->renderer) {
|
||||
pxl8_error("Failed to create renderer");
|
||||
pxl8_gfx_destroy(gfx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gfx->framebuffer = pxl8_cpu_get_framebuffer(gfx->backend.cpu);
|
||||
pxl8_gfx_texture_desc color_desc = {
|
||||
.width = (u32)gfx->framebuffer_width,
|
||||
.height = (u32)gfx->framebuffer_height,
|
||||
.format = PXL8_GFX_FORMAT_INDEXED8,
|
||||
.render_target = true,
|
||||
};
|
||||
gfx->color_target = pxl8_create_texture(gfx->renderer, &color_desc);
|
||||
|
||||
pxl8_gfx_texture_desc depth_desc = {
|
||||
.width = (u32)gfx->framebuffer_width,
|
||||
.height = (u32)gfx->framebuffer_height,
|
||||
.format = PXL8_GFX_FORMAT_DEPTH16,
|
||||
.render_target = true,
|
||||
};
|
||||
gfx->depth_target = pxl8_create_texture(gfx->renderer, &depth_desc);
|
||||
|
||||
pxl8_gfx_texture_desc light_desc = {
|
||||
.width = (u32)gfx->framebuffer_width,
|
||||
.height = (u32)gfx->framebuffer_height,
|
||||
.format = PXL8_GFX_FORMAT_LIGHT_ACCUM,
|
||||
.render_target = true,
|
||||
};
|
||||
gfx->light_target = pxl8_create_texture(gfx->renderer, &light_desc);
|
||||
|
||||
u32 pixel_count = (u32)gfx->framebuffer_width * (u32)gfx->framebuffer_height;
|
||||
gfx->output = pxl8_calloc(pixel_count, sizeof(u32));
|
||||
if (!gfx->output) {
|
||||
pxl8_error("Failed to allocate output buffer");
|
||||
pxl8_gfx_destroy(gfx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gfx->cmdbuf = pxl8_cmdbuf_create(1024);
|
||||
if (!gfx->cmdbuf) {
|
||||
pxl8_error("Failed to create command buffer");
|
||||
pxl8_gfx_destroy(gfx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (mode != PXL8_PIXEL_HICOLOR) {
|
||||
gfx->colormap = pxl8_calloc(1, sizeof(pxl8_colormap));
|
||||
if (gfx->colormap) {
|
||||
pxl8_cpu_set_colormap(gfx->backend.cpu, gfx->colormap);
|
||||
}
|
||||
}
|
||||
|
||||
gfx->target_stack[0] = (pxl8_target_entry){
|
||||
.color = gfx->color_target,
|
||||
.depth = gfx->depth_target,
|
||||
.light = gfx->light_target,
|
||||
};
|
||||
gfx->target_stack_depth = 1;
|
||||
|
||||
gfx->viewport.offset_x = 0;
|
||||
gfx->viewport.offset_y = 0;
|
||||
gfx->viewport.scaled_width = gfx->framebuffer_width;
|
||||
gfx->viewport.scaled_height = gfx->framebuffer_height;
|
||||
gfx->viewport.scale = 1.0f;
|
||||
|
||||
pxl8_gfx_buffer_desc vb_desc = {
|
||||
.capacity = PXL8_STREAM_VB_SIZE,
|
||||
.type = PXL8_GFX_BUFFER_VERTEX,
|
||||
.usage = PXL8_GFX_USAGE_STREAM,
|
||||
};
|
||||
gfx->stream_vb = pxl8_create_buffer(gfx->renderer, &vb_desc);
|
||||
gfx->stream_vb_capacity = PXL8_STREAM_VB_SIZE;
|
||||
gfx->stream_vb_offset = 0;
|
||||
|
||||
pxl8_gfx_buffer_desc ib_desc = {
|
||||
.capacity = PXL8_STREAM_IB_SIZE,
|
||||
.type = PXL8_GFX_BUFFER_INDEX,
|
||||
.usage = PXL8_GFX_USAGE_STREAM,
|
||||
};
|
||||
gfx->stream_ib = pxl8_create_buffer(gfx->renderer, &ib_desc);
|
||||
gfx->stream_ib_capacity = PXL8_STREAM_IB_SIZE;
|
||||
gfx->stream_ib_offset = 0;
|
||||
|
||||
gfx->initialized = true;
|
||||
return gfx;
|
||||
}
|
||||
|
|
@ -204,13 +312,13 @@ void pxl8_gfx_destroy(pxl8_gfx* gfx) {
|
|||
if (!gfx) return;
|
||||
|
||||
pxl8_atlas_destroy(gfx->atlas);
|
||||
if (gfx->backend.type == PXL8_GFX_BACKEND_CPU) {
|
||||
pxl8_cpu_destroy(gfx->backend.cpu);
|
||||
}
|
||||
pxl8_cmdbuf_destroy(gfx->cmdbuf);
|
||||
pxl8_free(gfx->colormap);
|
||||
pxl8_free(gfx->output);
|
||||
pxl8_palette_cube_destroy(gfx->palette_cube);
|
||||
pxl8_palette_destroy(gfx->palette);
|
||||
pxl8_free(gfx->sprite_cache);
|
||||
pxl8_renderer_destroy(gfx->renderer);
|
||||
|
||||
pxl8_free(gfx);
|
||||
}
|
||||
|
|
@ -330,26 +438,24 @@ pxl8_result pxl8_gfx_load_font_atlas(pxl8_gfx* gfx) {
|
|||
void pxl8_gfx_upload_framebuffer(pxl8_gfx* gfx) {
|
||||
if (!gfx || !gfx->initialized || !gfx->hal) return;
|
||||
|
||||
if (gfx->backend.type == PXL8_GFX_BACKEND_CPU && gfx->pixel_mode == PXL8_PIXEL_INDEXED) {
|
||||
pxl8_cpu_resolve(gfx->backend.cpu);
|
||||
u32* output = pxl8_cpu_get_output(gfx->backend.cpu);
|
||||
if (output) {
|
||||
gfx->hal->upload_texture(
|
||||
gfx->platform_data,
|
||||
output,
|
||||
gfx->framebuffer_width,
|
||||
gfx->framebuffer_height,
|
||||
PXL8_PIXEL_RGBA,
|
||||
NULL
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (gfx->pixel_mode == PXL8_PIXEL_INDEXED) {
|
||||
pxl8_gfx_resolve(gfx);
|
||||
gfx->hal->upload_texture(
|
||||
gfx->platform_data,
|
||||
gfx->output,
|
||||
gfx->framebuffer_width,
|
||||
gfx->framebuffer_height,
|
||||
PXL8_PIXEL_RGBA,
|
||||
NULL
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
u32* colors = gfx->palette ? pxl8_palette_colors(gfx->palette) : NULL;
|
||||
u8* framebuffer = (u8*)pxl8_texture_get_data(gfx->renderer, gfx->color_target);
|
||||
gfx->hal->upload_texture(
|
||||
gfx->platform_data,
|
||||
gfx->framebuffer,
|
||||
framebuffer,
|
||||
gfx->framebuffer_width,
|
||||
gfx->framebuffer_height,
|
||||
gfx->pixel_mode,
|
||||
|
|
@ -363,6 +469,16 @@ void pxl8_gfx_present(pxl8_gfx* gfx) {
|
|||
gfx->hal->present(gfx->platform_data);
|
||||
}
|
||||
|
||||
void pxl8_gfx_reset_stats(pxl8_gfx* gfx) {
|
||||
if (!gfx || !gfx->renderer) return;
|
||||
pxl8_renderer_reset_stats(gfx->renderer);
|
||||
}
|
||||
|
||||
const pxl8_gfx_stats* pxl8_gfx_get_stats(pxl8_gfx* gfx) {
|
||||
if (!gfx || !gfx->renderer) return NULL;
|
||||
return pxl8_renderer_get_stats(gfx->renderer);
|
||||
}
|
||||
|
||||
pxl8_viewport pxl8_gfx_viewport(pxl8_bounds bounds, i32 width, i32 height) {
|
||||
pxl8_viewport vp = {0};
|
||||
vp.scale = fminf(bounds.w / (f32)width, bounds.h / (f32)height);
|
||||
|
|
@ -382,115 +498,99 @@ void pxl8_gfx_project(pxl8_gfx* gfx, f32 left, f32 right, f32 top, f32 bottom) {
|
|||
(void)gfx; (void)left; (void)right; (void)top; (void)bottom;
|
||||
}
|
||||
|
||||
static pxl8_gfx_texture gfx_current_color(pxl8_gfx* gfx) {
|
||||
if (gfx->target_stack_depth == 0) return gfx->color_target;
|
||||
return gfx->target_stack[gfx->target_stack_depth - 1].color;
|
||||
}
|
||||
|
||||
static pxl8_gfx_texture gfx_current_depth(pxl8_gfx* gfx) {
|
||||
if (gfx->target_stack_depth == 0) return gfx->depth_target;
|
||||
return gfx->target_stack[gfx->target_stack_depth - 1].depth;
|
||||
}
|
||||
|
||||
static pxl8_gfx_texture gfx_current_light(pxl8_gfx* gfx) {
|
||||
if (gfx->target_stack_depth == 0) return gfx->light_target;
|
||||
return gfx->target_stack[gfx->target_stack_depth - 1].light;
|
||||
}
|
||||
|
||||
static void gfx_composite_over(pxl8_gfx* gfx, const pxl8_target_entry* src, pxl8_target_entry* dst) {
|
||||
if (!gfx || !src || !dst) return;
|
||||
|
||||
u8* src_color = (u8*)pxl8_texture_get_data(gfx->renderer, src->color);
|
||||
u8* dst_color = (u8*)pxl8_texture_get_data(gfx->renderer, dst->color);
|
||||
if (!src_color || !dst_color) return;
|
||||
|
||||
u32 width = pxl8_texture_get_width(gfx->renderer, src->color);
|
||||
u32 height = pxl8_texture_get_height(gfx->renderer, src->color);
|
||||
if (width == 0 || height == 0) return;
|
||||
|
||||
u32 dst_width = pxl8_texture_get_width(gfx->renderer, dst->color);
|
||||
u32 dst_height = pxl8_texture_get_height(gfx->renderer, dst->color);
|
||||
if (dst_width != width || dst_height != height) return;
|
||||
|
||||
u32* src_light = (u32*)pxl8_texture_get_data(gfx->renderer, src->light);
|
||||
u32* dst_light = (u32*)pxl8_texture_get_data(gfx->renderer, dst->light);
|
||||
|
||||
u32 count = width * height;
|
||||
for (u32 i = 0; i < count; i++) {
|
||||
u8 sc = src_color[i];
|
||||
if (sc == 0) continue;
|
||||
|
||||
dst_color[i] = sc;
|
||||
if (dst_light) {
|
||||
dst_light[i] = src_light ? src_light[i] : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_2d_clear(pxl8_gfx* gfx, u32 color) {
|
||||
if (!gfx) return;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
pxl8_cpu_clear(gfx->backend.cpu, (u8)color);
|
||||
break;
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
break;
|
||||
}
|
||||
pxl8_clear(gfx->renderer, gfx_current_color(gfx), (u8)color);
|
||||
pxl8_clear_light(gfx->renderer, gfx_current_light(gfx));
|
||||
}
|
||||
|
||||
void pxl8_2d_pixel(pxl8_gfx* gfx, i32 x, i32 y, u32 color) {
|
||||
if (!gfx) return;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
pxl8_cpu_draw_pixel(gfx->backend.cpu, x, y, (u8)color);
|
||||
break;
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
break;
|
||||
}
|
||||
pxl8_draw_pixel(gfx->renderer, gfx_current_color(gfx), x, y, (u8)color);
|
||||
}
|
||||
|
||||
u32 pxl8_2d_get_pixel(pxl8_gfx* gfx, i32 x, i32 y) {
|
||||
if (!gfx) return 0;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
return pxl8_cpu_get_pixel(gfx->backend.cpu, x, y);
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
return pxl8_get_pixel(gfx->renderer, gfx_current_color(gfx), x, y);
|
||||
}
|
||||
|
||||
void pxl8_2d_line(pxl8_gfx* gfx, i32 x0, i32 y0, i32 x1, i32 y1, u32 color) {
|
||||
if (!gfx) return;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
pxl8_cpu_draw_line_2d(gfx->backend.cpu, x0, y0, x1, y1, (u8)color);
|
||||
break;
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
break;
|
||||
}
|
||||
pxl8_draw_line(gfx->renderer, gfx_current_color(gfx), x0, y0, x1, y1, (u8)color);
|
||||
}
|
||||
|
||||
void pxl8_2d_rect(pxl8_gfx* gfx, i32 x, i32 y, i32 w, i32 h, u32 color) {
|
||||
if (!gfx) return;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
pxl8_cpu_draw_rect(gfx->backend.cpu, x, y, w, h, (u8)color);
|
||||
break;
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
break;
|
||||
}
|
||||
pxl8_draw_rect(gfx->renderer, gfx_current_color(gfx), x, y, w, h, (u8)color);
|
||||
}
|
||||
|
||||
void pxl8_2d_rect_fill(pxl8_gfx* gfx, i32 x, i32 y, i32 w, i32 h, u32 color) {
|
||||
if (!gfx) return;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
pxl8_cpu_draw_rect_fill(gfx->backend.cpu, x, y, w, h, (u8)color);
|
||||
break;
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
break;
|
||||
}
|
||||
pxl8_draw_rect_fill(gfx->renderer, gfx_current_color(gfx), x, y, w, h, (u8)color);
|
||||
}
|
||||
|
||||
void pxl8_2d_circle(pxl8_gfx* gfx, i32 cx, i32 cy, i32 radius, u32 color) {
|
||||
if (!gfx) return;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
pxl8_cpu_draw_circle(gfx->backend.cpu, cx, cy, radius, (u8)color);
|
||||
break;
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
break;
|
||||
}
|
||||
pxl8_draw_circle(gfx->renderer, gfx_current_color(gfx), cx, cy, radius, (u8)color);
|
||||
}
|
||||
|
||||
void pxl8_2d_circle_fill(pxl8_gfx* gfx, i32 cx, i32 cy, i32 radius, u32 color) {
|
||||
if (!gfx) return;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
pxl8_cpu_draw_circle_fill(gfx->backend.cpu, cx, cy, radius, (u8)color);
|
||||
break;
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
break;
|
||||
}
|
||||
pxl8_draw_circle_fill(gfx->renderer, gfx_current_color(gfx), cx, cy, radius, (u8)color);
|
||||
}
|
||||
|
||||
void pxl8_2d_text(pxl8_gfx* gfx, const char* text, i32 x, i32 y, u32 color) {
|
||||
if (!gfx || !text) return;
|
||||
|
||||
u8* framebuffer = NULL;
|
||||
u32* light_accum = NULL;
|
||||
i32 fb_width = 0;
|
||||
i32 fb_height = 0;
|
||||
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU: {
|
||||
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;
|
||||
}
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
return;
|
||||
}
|
||||
pxl8_gfx_texture target = gfx_current_color(gfx);
|
||||
u8* framebuffer = (u8*)pxl8_texture_get_data(gfx->renderer, target);
|
||||
i32 fb_width = (i32)pxl8_texture_get_width(gfx->renderer, target);
|
||||
i32 fb_height = (i32)pxl8_texture_get_height(gfx->renderer, target);
|
||||
|
||||
if (!framebuffer) return;
|
||||
|
||||
|
|
@ -523,7 +623,6 @@ void pxl8_2d_text(pxl8_gfx* gfx, const char* text, i32 x, i32 y, u32 color) {
|
|||
if (pixel_bit) {
|
||||
i32 idx = py * fb_width + px;
|
||||
framebuffer[idx] = (u8)color;
|
||||
if (light_accum) light_accum[idx] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -536,24 +635,10 @@ void pxl8_2d_text(pxl8_gfx* gfx, const char* text, i32 x, i32 y, u32 color) {
|
|||
void pxl8_2d_sprite(pxl8_gfx* gfx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h, bool flip_x, bool flip_y) {
|
||||
if (!gfx || !gfx->atlas) return;
|
||||
|
||||
u8* framebuffer = NULL;
|
||||
u32* light_accum = NULL;
|
||||
i32 fb_width = 0;
|
||||
i32 fb_height = 0;
|
||||
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU: {
|
||||
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;
|
||||
}
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
return;
|
||||
}
|
||||
pxl8_gfx_texture target = gfx_current_color(gfx);
|
||||
u8* framebuffer = (u8*)pxl8_texture_get_data(gfx->renderer, target);
|
||||
i32 fb_width = (i32)pxl8_texture_get_width(gfx->renderer, target);
|
||||
i32 fb_height = (i32)pxl8_texture_get_height(gfx->renderer, target);
|
||||
|
||||
if (!framebuffer) return;
|
||||
|
||||
|
|
@ -583,11 +668,6 @@ 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++) {
|
||||
|
|
@ -601,7 +681,6 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -616,7 +695,7 @@ void pxl8_gfx_update(pxl8_gfx* gfx, f32 dt) {
|
|||
}
|
||||
}
|
||||
|
||||
pxl8_3d_frame pxl8_3d_frame_from_camera(const pxl8_3d_camera* camera, const pxl8_3d_uniforms* uniforms) {
|
||||
pxl8_3d_frame pxl8_3d_frame_from_camera(const pxl8_3d_camera* camera, const pxl8_shader_uniforms* uniforms) {
|
||||
pxl8_3d_frame frame = {0};
|
||||
if (!camera) return frame;
|
||||
|
||||
|
|
@ -639,32 +718,37 @@ void pxl8_3d_set_bsp(pxl8_gfx* gfx, const pxl8_bsp* bsp) {
|
|||
gfx->bsp = bsp;
|
||||
}
|
||||
|
||||
void pxl8_3d_set_sdf(pxl8_gfx* gfx, const pxl8_sdf* sdf) {
|
||||
if (!gfx) return;
|
||||
gfx->sdf = sdf;
|
||||
}
|
||||
|
||||
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_begin_frame(pxl8_gfx* gfx, const pxl8_3d_camera* camera, const pxl8_lights* lights, const pxl8_shader_uniforms* uniforms) {
|
||||
if (!gfx || !camera) return;
|
||||
|
||||
pxl8_3d_frame frame = pxl8_3d_frame_from_camera(camera, uniforms);
|
||||
frame.bsp = gfx->bsp;
|
||||
frame.lights = lights ? pxl8_lights_data(lights) : NULL;
|
||||
frame.lights_count = lights ? pxl8_lights_count(lights) : 0;
|
||||
frame.sdf = gfx->sdf;
|
||||
frame.uniforms.lights = lights ? pxl8_lights_data(lights) : NULL;
|
||||
frame.uniforms.lights_count = lights ? pxl8_lights_count(lights) : 0;
|
||||
|
||||
pxl8_mat4 vp = pxl8_mat4_multiply(frame.projection, frame.view);
|
||||
|
||||
gfx->frustum = pxl8_frustum_from_matrix(vp);
|
||||
gfx->view_proj = vp;
|
||||
gfx->frame = frame;
|
||||
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
pxl8_cpu_begin_frame(gfx->backend.cpu, &frame);
|
||||
break;
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
break;
|
||||
}
|
||||
gfx->frame_res.texture_count = 0;
|
||||
gfx->frame_res.buffer_count = 0;
|
||||
gfx->frame_res.pipeline_count = 0;
|
||||
gfx->frame_res.bindings_count = 0;
|
||||
|
||||
gfx->stream_vb_offset = 0;
|
||||
gfx->stream_ib_offset = 0;
|
||||
|
||||
pxl8_cmdbuf_reset(gfx->cmdbuf);
|
||||
|
||||
pxl8_gfx_pass_desc pass_desc = {
|
||||
.color = { .texture = gfx_current_color(gfx), .load = PXL8_GFX_LOAD_CLEAR, .clear_value = 0 },
|
||||
.depth = { .texture = gfx_current_depth(gfx), .load = PXL8_GFX_LOAD_CLEAR, .clear_value = 0xFFFF },
|
||||
.light_accum = { .texture = gfx_current_light(gfx), .load = PXL8_GFX_LOAD_CLEAR },
|
||||
};
|
||||
gfx->frame_pass = pxl8_create_pass(gfx->renderer, &pass_desc);
|
||||
pxl8_begin_pass(gfx->cmdbuf, gfx->frame_pass);
|
||||
}
|
||||
|
||||
const pxl8_frustum* pxl8_3d_get_frustum(pxl8_gfx* gfx) {
|
||||
|
|
@ -706,90 +790,249 @@ u32 pxl8_3d_project_points(pxl8_gfx* gfx, const pxl8_vec3* in, pxl8_vec3* out, u
|
|||
|
||||
void pxl8_3d_clear(pxl8_gfx* gfx, u8 color) {
|
||||
if (!gfx) return;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
pxl8_cpu_clear(gfx->backend.cpu, color);
|
||||
break;
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
break;
|
||||
}
|
||||
pxl8_clear(gfx->renderer, gfx_current_color(gfx), color);
|
||||
pxl8_clear_light(gfx->renderer, gfx_current_light(gfx));
|
||||
}
|
||||
|
||||
void pxl8_3d_clear_depth(pxl8_gfx* gfx) {
|
||||
if (!gfx) return;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
pxl8_cpu_clear_depth(gfx->backend.cpu);
|
||||
break;
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
break;
|
||||
}
|
||||
pxl8_clear_depth(gfx->renderer, gfx_current_depth(gfx));
|
||||
}
|
||||
|
||||
void pxl8_3d_draw_line(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, u8 color) {
|
||||
if (!gfx) return;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
pxl8_cpu_draw_line_3d(gfx->backend.cpu, v0, v1, color);
|
||||
break;
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
break;
|
||||
}
|
||||
|
||||
pxl8_vec4 c0 = pxl8_mat4_multiply_vec4(gfx->view_proj, (pxl8_vec4){v0.x, v0.y, v0.z, 1.0f});
|
||||
pxl8_vec4 c1 = pxl8_mat4_multiply_vec4(gfx->view_proj, (pxl8_vec4){v1.x, v1.y, v1.z, 1.0f});
|
||||
|
||||
if (c0.w <= 0.0f && c1.w <= 0.0f) return;
|
||||
|
||||
f32 hw = (f32)gfx->framebuffer_width * 0.5f;
|
||||
f32 hh = (f32)gfx->framebuffer_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);
|
||||
|
||||
pxl8_draw_line(gfx->renderer, gfx_current_color(gfx), x0, y0, x1, y1, color);
|
||||
}
|
||||
|
||||
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:
|
||||
pxl8_cpu_draw_mesh(gfx->backend.cpu, mesh, model, material, gfx->atlas);
|
||||
if (!pxl8_gfx_handle_valid(gfx->frame_pass)) return;
|
||||
|
||||
pxl8_frame_resources* res = &gfx->frame_res;
|
||||
bool is_wireframe = gfx->wireframe;
|
||||
|
||||
const char* shader_name = "lit";
|
||||
if (material->emissive || (!material->dynamic_lighting && !material->per_pixel)) {
|
||||
shader_name = "unlit";
|
||||
}
|
||||
|
||||
pxl8_gfx_pipeline_desc pipe_desc = {
|
||||
.blend = {
|
||||
.enabled = false,
|
||||
.src = PXL8_GFX_BLEND_ONE,
|
||||
.dst = PXL8_GFX_BLEND_ZERO,
|
||||
.alpha_test = false,
|
||||
.alpha_ref = 0,
|
||||
},
|
||||
.depth = { .test = true, .write = true, .compare = PXL8_GFX_COMPARE_LESS },
|
||||
.dither = material->dither,
|
||||
.double_sided = material->double_sided,
|
||||
.emissive = material->emissive,
|
||||
.rasterizer = { .cull = PXL8_GFX_CULL_BACK, .fill = is_wireframe ? PXL8_GFX_FILL_WIREFRAME : PXL8_GFX_FILL_SOLID },
|
||||
.shader = pxl8_shader_registry_get(shader_name),
|
||||
};
|
||||
|
||||
switch (material->blend_mode) {
|
||||
case PXL8_BLEND_ALPHA_TEST:
|
||||
pipe_desc.blend.alpha_test = true;
|
||||
pipe_desc.blend.alpha_ref = material->alpha;
|
||||
break;
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
case PXL8_BLEND_ALPHA:
|
||||
pipe_desc.blend.enabled = true;
|
||||
pipe_desc.blend.src = PXL8_GFX_BLEND_SRC_ALPHA;
|
||||
pipe_desc.blend.dst = PXL8_GFX_BLEND_INV_SRC_ALPHA;
|
||||
break;
|
||||
case PXL8_BLEND_ADDITIVE:
|
||||
pipe_desc.blend.enabled = true;
|
||||
pipe_desc.blend.src = PXL8_GFX_BLEND_ONE;
|
||||
pipe_desc.blend.dst = PXL8_GFX_BLEND_ONE;
|
||||
break;
|
||||
case PXL8_BLEND_OPAQUE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
pxl8_gfx_pipeline pipeline = pxl8_create_pipeline(gfx->renderer, &pipe_desc);
|
||||
if (res->pipeline_count < PXL8_MAX_FRAME_RESOURCES) {
|
||||
res->pipelines[res->pipeline_count++] = pipeline;
|
||||
}
|
||||
|
||||
pxl8_gfx_bindings_desc bind_desc = {
|
||||
.atlas = gfx->atlas,
|
||||
.colormap = gfx->colormap,
|
||||
.palette = gfx->palette ? pxl8_palette_colors(gfx->palette) : NULL,
|
||||
.texture_id = material->texture_id,
|
||||
};
|
||||
pxl8_gfx_bindings bindings = pxl8_create_bindings(gfx->renderer, &bind_desc);
|
||||
if (res->bindings_count < PXL8_MAX_FRAME_RESOURCES) {
|
||||
res->bindings[res->bindings_count++] = bindings;
|
||||
}
|
||||
|
||||
u32 vb_size = mesh->vertex_count * sizeof(pxl8_vertex);
|
||||
u32 ib_size = mesh->index_count * sizeof(u16);
|
||||
|
||||
u32 base_vertex = 0;
|
||||
u32 first_index = 0;
|
||||
pxl8_gfx_buffer vb, ib;
|
||||
|
||||
bool use_stream = pxl8_gfx_handle_valid(gfx->stream_vb) &&
|
||||
pxl8_gfx_handle_valid(gfx->stream_ib) &&
|
||||
(gfx->stream_vb_offset + vb_size <= gfx->stream_vb_capacity) &&
|
||||
(gfx->stream_ib_offset + ib_size <= gfx->stream_ib_capacity);
|
||||
|
||||
if (use_stream) {
|
||||
pxl8_gfx_range vb_data = { .ptr = mesh->vertices, .size = vb_size };
|
||||
pxl8_gfx_range ib_data = { .ptr = mesh->indices, .size = ib_size };
|
||||
i32 vb_offset = pxl8_append_buffer(gfx->renderer, gfx->stream_vb, &vb_data);
|
||||
i32 ib_offset = pxl8_append_buffer(gfx->renderer, gfx->stream_ib, &ib_data);
|
||||
|
||||
if (vb_offset >= 0 && ib_offset >= 0) {
|
||||
gfx->stream_vb_offset += vb_size;
|
||||
gfx->stream_ib_offset += ib_size;
|
||||
|
||||
base_vertex = (u32)vb_offset / sizeof(pxl8_vertex);
|
||||
first_index = (u32)ib_offset / sizeof(u16);
|
||||
|
||||
vb = gfx->stream_vb;
|
||||
ib = gfx->stream_ib;
|
||||
} else {
|
||||
use_stream = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!use_stream) {
|
||||
pxl8_gfx_buffer_desc vb_desc = {
|
||||
.type = PXL8_GFX_BUFFER_VERTEX,
|
||||
.data = { .ptr = mesh->vertices, .size = vb_size },
|
||||
};
|
||||
vb = pxl8_create_buffer(gfx->renderer, &vb_desc);
|
||||
if (res->buffer_count < PXL8_MAX_FRAME_RESOURCES) {
|
||||
res->buffers[res->buffer_count++] = vb;
|
||||
}
|
||||
|
||||
pxl8_gfx_buffer_desc ib_desc = {
|
||||
.type = PXL8_GFX_BUFFER_INDEX,
|
||||
.data = { .ptr = mesh->indices, .size = ib_size },
|
||||
};
|
||||
ib = pxl8_create_buffer(gfx->renderer, &ib_desc);
|
||||
if (res->buffer_count < PXL8_MAX_FRAME_RESOURCES) {
|
||||
res->buffers[res->buffer_count++] = ib;
|
||||
}
|
||||
}
|
||||
|
||||
pxl8_gfx_cmd_draw_params draw_params = {
|
||||
.model = *model,
|
||||
.view = gfx->frame.view,
|
||||
.projection = gfx->frame.projection,
|
||||
.shader = gfx->frame.uniforms,
|
||||
};
|
||||
|
||||
pxl8_set_pipeline(gfx->cmdbuf, pipeline);
|
||||
pxl8_set_bindings(gfx->cmdbuf, bindings);
|
||||
pxl8_set_draw_params(gfx->cmdbuf, &draw_params);
|
||||
pxl8_draw(gfx->cmdbuf, vb, ib, first_index, mesh->index_count, base_vertex);
|
||||
}
|
||||
|
||||
void pxl8_3d_end_frame(pxl8_gfx* gfx) {
|
||||
if (!gfx) return;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
pxl8_cpu_end_frame(gfx->backend.cpu);
|
||||
break;
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
break;
|
||||
if (!gfx) { pxl8_error("end_frame: gfx is NULL"); return; }
|
||||
if (!pxl8_gfx_handle_valid(gfx->frame_pass)) { pxl8_error("end_frame: frame_pass invalid (id=%u)", gfx->frame_pass.id); return; }
|
||||
|
||||
pxl8_end_pass(gfx->cmdbuf);
|
||||
pxl8_gfx_submit(gfx->renderer, gfx->cmdbuf);
|
||||
|
||||
pxl8_frame_resources* res = &gfx->frame_res;
|
||||
|
||||
for (u32 i = 0; i < res->buffer_count; i++) {
|
||||
pxl8_destroy_buffer(gfx->renderer, res->buffers[i]);
|
||||
}
|
||||
for (u32 i = 0; i < res->pipeline_count; i++) {
|
||||
pxl8_destroy_pipeline(gfx->renderer, res->pipelines[i]);
|
||||
}
|
||||
for (u32 i = 0; i < res->bindings_count; i++) {
|
||||
pxl8_destroy_bindings(gfx->renderer, res->bindings[i]);
|
||||
}
|
||||
for (u32 i = 0; i < res->texture_count; i++) {
|
||||
pxl8_destroy_texture(gfx->renderer, res->textures[i]);
|
||||
}
|
||||
|
||||
pxl8_destroy_pass(gfx->renderer, gfx->frame_pass);
|
||||
gfx->frame_pass = (pxl8_gfx_pass){PXL8_GFX_INVALID_ID};
|
||||
|
||||
res->buffer_count = 0;
|
||||
res->pipeline_count = 0;
|
||||
res->bindings_count = 0;
|
||||
res->texture_count = 0;
|
||||
}
|
||||
|
||||
u8* pxl8_3d_get_framebuffer(pxl8_gfx* gfx) {
|
||||
if (!gfx) return NULL;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
return pxl8_cpu_get_framebuffer(gfx->backend.cpu);
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
return (u8*)pxl8_texture_get_data(gfx->renderer, gfx_current_color(gfx));
|
||||
}
|
||||
|
||||
bool pxl8_gfx_push_target(pxl8_gfx* gfx) {
|
||||
if (!gfx) return false;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
return pxl8_cpu_push_target(gfx->backend.cpu);
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
if (!gfx || gfx->target_stack_depth >= PXL8_MAX_TARGET_STACK) return false;
|
||||
|
||||
pxl8_gfx_texture_desc color_desc = {
|
||||
.width = (u32)gfx->framebuffer_width,
|
||||
.height = (u32)gfx->framebuffer_height,
|
||||
.format = PXL8_GFX_FORMAT_INDEXED8,
|
||||
.render_target = true,
|
||||
};
|
||||
pxl8_gfx_texture new_color = pxl8_create_texture(gfx->renderer, &color_desc);
|
||||
|
||||
pxl8_gfx_texture_desc depth_desc = {
|
||||
.width = (u32)gfx->framebuffer_width,
|
||||
.height = (u32)gfx->framebuffer_height,
|
||||
.format = PXL8_GFX_FORMAT_DEPTH16,
|
||||
.render_target = true,
|
||||
};
|
||||
pxl8_gfx_texture new_depth = pxl8_create_texture(gfx->renderer, &depth_desc);
|
||||
|
||||
pxl8_gfx_texture_desc light_desc = {
|
||||
.width = (u32)gfx->framebuffer_width,
|
||||
.height = (u32)gfx->framebuffer_height,
|
||||
.format = PXL8_GFX_FORMAT_LIGHT_ACCUM,
|
||||
.render_target = true,
|
||||
};
|
||||
pxl8_gfx_texture new_light = pxl8_create_texture(gfx->renderer, &light_desc);
|
||||
|
||||
gfx->target_stack[gfx->target_stack_depth] = (pxl8_target_entry){
|
||||
.color = new_color,
|
||||
.depth = new_depth,
|
||||
.light = new_light,
|
||||
};
|
||||
gfx->target_stack_depth++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void pxl8_gfx_pop_target(pxl8_gfx* gfx) {
|
||||
if (!gfx) return;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
pxl8_cpu_pop_target(gfx->backend.cpu);
|
||||
break;
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
break;
|
||||
}
|
||||
if (!gfx || gfx->target_stack_depth <= 1) return;
|
||||
|
||||
u32 top = gfx->target_stack_depth - 1;
|
||||
pxl8_target_entry* entry = &gfx->target_stack[top];
|
||||
pxl8_target_entry* parent = &gfx->target_stack[top - 1];
|
||||
|
||||
gfx_composite_over(gfx, entry, parent);
|
||||
|
||||
gfx->target_stack_depth--;
|
||||
|
||||
pxl8_destroy_texture(gfx->renderer, entry->color);
|
||||
pxl8_destroy_texture(gfx->renderer, entry->depth);
|
||||
pxl8_destroy_texture(gfx->renderer, entry->light);
|
||||
}
|
||||
|
||||
void pxl8_gfx_ensure_blend_tables(pxl8_gfx* gfx) {
|
||||
|
|
@ -797,9 +1040,6 @@ void pxl8_gfx_ensure_blend_tables(pxl8_gfx* gfx) {
|
|||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -810,9 +1050,6 @@ void pxl8_gfx_blend_tables_update(pxl8_gfx* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -838,20 +1075,14 @@ u8 pxl8_gfx_ui_color(pxl8_gfx* gfx, u8 index) {
|
|||
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: {
|
||||
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);
|
||||
break;
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
void pxl8_gfx_set_wireframe(pxl8_gfx* gfx, bool enabled) {
|
||||
if (gfx) gfx->wireframe = enabled;
|
||||
}
|
||||
|
||||
bool pxl8_gfx_get_wireframe(const pxl8_gfx* gfx) {
|
||||
return gfx ? gfx->wireframe : false;
|
||||
}
|
||||
|
||||
u8 pxl8_gfx_get_ambient(const pxl8_gfx* gfx) {
|
||||
return gfx ? gfx->frame.uniforms.ambient : 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include "pxl8_gui_palette.h"
|
||||
|
||||
typedef struct pxl8_gfx pxl8_gfx;
|
||||
typedef struct pxl8_gfx_stats pxl8_gfx_stats;
|
||||
|
||||
typedef enum pxl8_gfx_effect {
|
||||
PXL8_GFX_EFFECT_GLOWS = 0,
|
||||
|
|
@ -25,6 +26,8 @@ void pxl8_gfx_destroy(pxl8_gfx* gfx);
|
|||
void pxl8_gfx_present(pxl8_gfx* gfx);
|
||||
void pxl8_gfx_update(pxl8_gfx* gfx, f32 dt);
|
||||
void pxl8_gfx_upload_framebuffer(pxl8_gfx* gfx);
|
||||
void pxl8_gfx_reset_stats(pxl8_gfx* gfx);
|
||||
const pxl8_gfx_stats* pxl8_gfx_get_stats(pxl8_gfx* gfx);
|
||||
|
||||
u8 pxl8_gfx_find_color(pxl8_gfx* gfx, u32 color);
|
||||
|
||||
|
|
@ -62,6 +65,11 @@ 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);
|
||||
|
||||
void pxl8_gfx_set_wireframe(pxl8_gfx* gfx, bool enabled);
|
||||
bool pxl8_gfx_get_wireframe(const pxl8_gfx* gfx);
|
||||
|
||||
u8 pxl8_gfx_get_ambient(const pxl8_gfx* gfx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -4,39 +4,20 @@
|
|||
#include "pxl8_lights.h"
|
||||
#include "pxl8_math.h"
|
||||
#include "pxl8_mesh.h"
|
||||
#include "pxl8_shader.h"
|
||||
#include "pxl8_types.h"
|
||||
|
||||
typedef struct pxl8_bsp pxl8_bsp;
|
||||
typedef struct pxl8_gfx pxl8_gfx;
|
||||
|
||||
typedef struct pxl8_3d_uniforms {
|
||||
u8 ambient;
|
||||
pxl8_vec3 celestial_dir;
|
||||
f32 celestial_intensity;
|
||||
u8 fog_color;
|
||||
f32 fog_density;
|
||||
f32 time;
|
||||
} pxl8_3d_uniforms;
|
||||
|
||||
typedef struct pxl8_3d_frame_desc {
|
||||
const pxl8_bsp* bsp;
|
||||
const pxl8_3d_camera* camera;
|
||||
const pxl8_lights* lights;
|
||||
const pxl8_sdf* sdf;
|
||||
pxl8_3d_uniforms uniforms;
|
||||
} pxl8_3d_frame_desc;
|
||||
|
||||
typedef struct pxl8_3d_frame {
|
||||
const pxl8_bsp* bsp;
|
||||
pxl8_vec3 camera_dir;
|
||||
pxl8_vec3 camera_pos;
|
||||
f32 far_clip;
|
||||
const pxl8_light* lights;
|
||||
u32 lights_count;
|
||||
f32 near_clip;
|
||||
pxl8_mat4 projection;
|
||||
const pxl8_sdf* sdf;
|
||||
pxl8_3d_uniforms uniforms;
|
||||
pxl8_shader_uniforms uniforms;
|
||||
pxl8_mat4 view;
|
||||
} pxl8_3d_frame;
|
||||
|
||||
|
|
@ -45,8 +26,7 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
void pxl8_3d_set_bsp(pxl8_gfx* gfx, const pxl8_bsp* bsp);
|
||||
void pxl8_3d_set_sdf(pxl8_gfx* gfx, const pxl8_sdf* sdf);
|
||||
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_begin_frame(pxl8_gfx* gfx, const pxl8_3d_camera* camera, const pxl8_lights* lights, const pxl8_shader_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);
|
||||
|
|
@ -57,7 +37,7 @@ 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);
|
||||
pxl8_3d_frame pxl8_3d_frame_from_camera(const pxl8_3d_camera* camera, const pxl8_shader_uniforms* uniforms);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,36 @@ void pxl8_lights_add(pxl8_lights* lights, f32 x, f32 y, f32 z, u8 r, u8 g, u8 b,
|
|||
l->radius = radius;
|
||||
l->radius_sq = radius_sq;
|
||||
l->inv_radius_sq = radius_sq > 0.0f ? 1.0f / radius_sq : 0.0f;
|
||||
|
||||
l->constant = 1.0f;
|
||||
if (radius <= 7.0f) {
|
||||
l->linear = 0.7f;
|
||||
l->quadratic = 1.8f;
|
||||
} else if (radius <= 13.0f) {
|
||||
l->linear = 0.35f;
|
||||
l->quadratic = 0.44f;
|
||||
} else if (radius <= 20.0f) {
|
||||
l->linear = 0.22f;
|
||||
l->quadratic = 0.20f;
|
||||
} else if (radius <= 32.0f) {
|
||||
l->linear = 0.14f;
|
||||
l->quadratic = 0.07f;
|
||||
} else if (radius <= 50.0f) {
|
||||
l->linear = 0.09f;
|
||||
l->quadratic = 0.032f;
|
||||
} else if (radius <= 65.0f) {
|
||||
l->linear = 0.07f;
|
||||
l->quadratic = 0.017f;
|
||||
} else if (radius <= 100.0f) {
|
||||
l->linear = 0.045f;
|
||||
l->quadratic = 0.0075f;
|
||||
} else if (radius <= 160.0f) {
|
||||
l->linear = 0.027f;
|
||||
l->quadratic = 0.0028f;
|
||||
} else {
|
||||
l->linear = 0.022f;
|
||||
l->quadratic = 0.0019f;
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_lights_clear(pxl8_lights* lights) {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@ typedef struct pxl8_light {
|
|||
u8 intensity;
|
||||
f32 radius;
|
||||
f32 radius_sq;
|
||||
f32 constant;
|
||||
f32 linear;
|
||||
f32 quadratic;
|
||||
} pxl8_light;
|
||||
|
||||
typedef struct pxl8_lights pxl8_lights;
|
||||
|
|
|
|||
|
|
@ -31,10 +31,8 @@ typedef struct pxl8_gfx_material {
|
|||
bool dither;
|
||||
bool double_sided;
|
||||
bool dynamic_lighting;
|
||||
bool emissive;
|
||||
bool per_pixel;
|
||||
bool vertex_color_passthrough;
|
||||
bool wireframe;
|
||||
f32 emissive_intensity;
|
||||
} pxl8_gfx_material;
|
||||
|
||||
typedef struct pxl8_vertex {
|
||||
|
|
|
|||
1502
src/gfx/pxl8_render.c
Normal file
1502
src/gfx/pxl8_render.c
Normal file
File diff suppressed because it is too large
Load diff
98
src/gfx/pxl8_render.h
Normal file
98
src/gfx/pxl8_render.h
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_colormap.h"
|
||||
#include "pxl8_render_types.h"
|
||||
#include "pxl8_shader.h"
|
||||
|
||||
#ifndef PXL8_GFX_ENABLE_STATS
|
||||
#define PXL8_GFX_ENABLE_STATS 1
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct pxl8_renderer pxl8_renderer;
|
||||
typedef struct pxl8_gfx_cmdbuf pxl8_gfx_cmdbuf;
|
||||
|
||||
typedef struct pxl8_gfx_stats {
|
||||
u64 draw_calls;
|
||||
u64 triangles;
|
||||
u64 clipped_triangles;
|
||||
u64 depth_tests;
|
||||
u64 depth_passes;
|
||||
u64 shader_calls;
|
||||
u64 pixels_written;
|
||||
u64 light_writes;
|
||||
u64 submit_ns;
|
||||
u64 execute_draw_ns;
|
||||
u64 raster_ns;
|
||||
} pxl8_gfx_stats;
|
||||
|
||||
pxl8_renderer* pxl8_renderer_create(u32 width, u32 height);
|
||||
void pxl8_renderer_destroy(pxl8_renderer* r);
|
||||
void pxl8_renderer_set_shader(pxl8_renderer* r, pxl8_shader_fn fn);
|
||||
void pxl8_renderer_reset_stats(pxl8_renderer* r);
|
||||
const pxl8_gfx_stats* pxl8_renderer_get_stats(const pxl8_renderer* r);
|
||||
|
||||
u32 pxl8_renderer_get_width(const pxl8_renderer* r);
|
||||
u32 pxl8_renderer_get_height(const pxl8_renderer* r);
|
||||
|
||||
pxl8_gfx_bindings pxl8_create_bindings(pxl8_renderer* r, const pxl8_gfx_bindings_desc* desc);
|
||||
pxl8_gfx_buffer pxl8_create_buffer(pxl8_renderer* r, const pxl8_gfx_buffer_desc* desc);
|
||||
pxl8_gfx_pass pxl8_create_pass(pxl8_renderer* r, const pxl8_gfx_pass_desc* desc);
|
||||
pxl8_gfx_pipeline pxl8_create_pipeline(pxl8_renderer* r, const pxl8_gfx_pipeline_desc* desc);
|
||||
pxl8_gfx_texture pxl8_create_texture(pxl8_renderer* r, const pxl8_gfx_texture_desc* desc);
|
||||
|
||||
void pxl8_destroy_bindings(pxl8_renderer* r, pxl8_gfx_bindings bnd);
|
||||
void pxl8_destroy_buffer(pxl8_renderer* r, pxl8_gfx_buffer buf);
|
||||
void pxl8_destroy_pass(pxl8_renderer* r, pxl8_gfx_pass pass);
|
||||
void pxl8_destroy_pipeline(pxl8_renderer* r, pxl8_gfx_pipeline pip);
|
||||
void pxl8_destroy_texture(pxl8_renderer* r, pxl8_gfx_texture tex);
|
||||
|
||||
void pxl8_update_buffer(pxl8_renderer* r, pxl8_gfx_buffer buf, const pxl8_gfx_range* data);
|
||||
i32 pxl8_append_buffer(pxl8_renderer* r, pxl8_gfx_buffer buf, const pxl8_gfx_range* data);
|
||||
void pxl8_update_texture(pxl8_renderer* r, pxl8_gfx_texture tex, const pxl8_gfx_range* data, u32 x, u32 y, u32 w, u32 h);
|
||||
|
||||
void* pxl8_buffer_ptr(pxl8_renderer* r, pxl8_gfx_buffer buf);
|
||||
u32 pxl8_buffer_size(pxl8_renderer* r, pxl8_gfx_buffer buf);
|
||||
|
||||
void* pxl8_texture_get_data(pxl8_renderer* r, pxl8_gfx_texture tex);
|
||||
u32 pxl8_texture_get_width(pxl8_renderer* r, pxl8_gfx_texture tex);
|
||||
u32 pxl8_texture_get_height(pxl8_renderer* r, pxl8_gfx_texture tex);
|
||||
pxl8_gfx_texture_format pxl8_texture_get_format(pxl8_renderer* r, pxl8_gfx_texture tex);
|
||||
|
||||
pxl8_gfx_cmdbuf* pxl8_cmdbuf_create(u32 capacity);
|
||||
void pxl8_cmdbuf_destroy(pxl8_gfx_cmdbuf* cb);
|
||||
void pxl8_cmdbuf_reset(pxl8_gfx_cmdbuf* cb);
|
||||
|
||||
void pxl8_begin_pass(pxl8_gfx_cmdbuf* cb, pxl8_gfx_pass pass);
|
||||
void pxl8_end_pass(pxl8_gfx_cmdbuf* cb);
|
||||
void pxl8_set_bindings(pxl8_gfx_cmdbuf* cb, pxl8_gfx_bindings bindings);
|
||||
void pxl8_set_draw_params(pxl8_gfx_cmdbuf* cb, const pxl8_gfx_cmd_draw_params* p);
|
||||
void pxl8_set_pipeline(pxl8_gfx_cmdbuf* cb, pxl8_gfx_pipeline pipeline);
|
||||
void pxl8_set_scissor(pxl8_gfx_cmdbuf* cb, i32 x, i32 y, u32 w, u32 h);
|
||||
void pxl8_set_viewport(pxl8_gfx_cmdbuf* cb, i32 x, i32 y, u32 w, u32 h);
|
||||
|
||||
void pxl8_draw(pxl8_gfx_cmdbuf* cb, pxl8_gfx_buffer vb, pxl8_gfx_buffer ib, u32 first, u32 count, u32 base_vertex);
|
||||
|
||||
void pxl8_gfx_submit(pxl8_renderer* r, pxl8_gfx_cmdbuf* cb);
|
||||
|
||||
void pxl8_clear(pxl8_renderer* r, pxl8_gfx_texture target, u8 color);
|
||||
void pxl8_clear_depth(pxl8_renderer* r, pxl8_gfx_texture target);
|
||||
void pxl8_clear_light(pxl8_renderer* r, pxl8_gfx_texture target);
|
||||
|
||||
void pxl8_draw_pixel(pxl8_renderer* r, pxl8_gfx_texture target, i32 x, i32 y, u8 color);
|
||||
u8 pxl8_get_pixel(pxl8_renderer* r, pxl8_gfx_texture target, i32 x, i32 y);
|
||||
void pxl8_draw_line(pxl8_renderer* r, pxl8_gfx_texture target, i32 x0, i32 y0, i32 x1, i32 y1, u8 color);
|
||||
void pxl8_draw_rect(pxl8_renderer* r, pxl8_gfx_texture target, i32 x, i32 y, i32 w, i32 h, u8 color);
|
||||
void pxl8_draw_rect_fill(pxl8_renderer* r, pxl8_gfx_texture target, i32 x, i32 y, i32 w, i32 h, u8 color);
|
||||
void pxl8_draw_circle(pxl8_renderer* r, pxl8_gfx_texture target, i32 cx, i32 cy, i32 radius, u8 color);
|
||||
void pxl8_draw_circle_fill(pxl8_renderer* r, pxl8_gfx_texture target, i32 cx, i32 cy, i32 radius, u8 color);
|
||||
|
||||
void pxl8_resolve_to_rgba(pxl8_renderer* r, pxl8_gfx_texture color, pxl8_gfx_texture light_accum,
|
||||
const u32* palette, u32* output);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
242
src/gfx/pxl8_render_types.h
Normal file
242
src/gfx/pxl8_render_types.h
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_atlas.h"
|
||||
#include "pxl8_colormap.h"
|
||||
#include "pxl8_lights.h"
|
||||
#include "pxl8_math.h"
|
||||
#include "pxl8_shader.h"
|
||||
#include "pxl8_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define PXL8_GFX_MAX_BINDINGS 256
|
||||
#define PXL8_GFX_MAX_BUFFERS 512
|
||||
#define PXL8_GFX_MAX_PASSES 32
|
||||
#define PXL8_GFX_MAX_PIPELINES 128
|
||||
#define PXL8_GFX_MAX_TEXTURES 256
|
||||
|
||||
typedef struct { u32 id; } pxl8_gfx_buffer;
|
||||
typedef struct { u32 id; } pxl8_gfx_texture;
|
||||
typedef struct { u32 id; } pxl8_gfx_pipeline;
|
||||
typedef struct { u32 id; } pxl8_gfx_bindings;
|
||||
typedef struct { u32 id; } pxl8_gfx_pass;
|
||||
|
||||
#define PXL8_GFX_INVALID_ID (0)
|
||||
#define pxl8_gfx_handle_valid(h) ((h).id != PXL8_GFX_INVALID_ID)
|
||||
|
||||
typedef struct pxl8_gfx_range {
|
||||
const void* ptr;
|
||||
u32 size;
|
||||
} pxl8_gfx_range;
|
||||
|
||||
#define PXL8_GFX_RANGE(x) ((pxl8_gfx_range){ .ptr = &(x), .size = sizeof(x) })
|
||||
#define PXL8_GFX_RANGE_REF(p, sz) ((pxl8_gfx_range){ .ptr = (p), .size = (sz) })
|
||||
|
||||
typedef enum pxl8_gfx_buffer_type {
|
||||
PXL8_GFX_BUFFER_VERTEX,
|
||||
PXL8_GFX_BUFFER_INDEX,
|
||||
} pxl8_gfx_buffer_type;
|
||||
|
||||
typedef enum pxl8_gfx_usage {
|
||||
PXL8_GFX_USAGE_IMMUTABLE,
|
||||
PXL8_GFX_USAGE_DYNAMIC,
|
||||
PXL8_GFX_USAGE_STREAM,
|
||||
} pxl8_gfx_usage;
|
||||
|
||||
typedef enum pxl8_gfx_texture_format {
|
||||
PXL8_GFX_FORMAT_INDEXED8,
|
||||
PXL8_GFX_FORMAT_DEPTH16,
|
||||
PXL8_GFX_FORMAT_LIGHT_ACCUM,
|
||||
} pxl8_gfx_texture_format;
|
||||
|
||||
typedef enum pxl8_gfx_cull_mode {
|
||||
PXL8_GFX_CULL_NONE,
|
||||
PXL8_GFX_CULL_BACK,
|
||||
PXL8_GFX_CULL_FRONT,
|
||||
} pxl8_gfx_cull_mode;
|
||||
|
||||
typedef enum pxl8_gfx_fill_mode {
|
||||
PXL8_GFX_FILL_SOLID,
|
||||
PXL8_GFX_FILL_WIREFRAME,
|
||||
} pxl8_gfx_fill_mode;
|
||||
|
||||
typedef enum pxl8_gfx_compare_func {
|
||||
PXL8_GFX_COMPARE_NEVER,
|
||||
PXL8_GFX_COMPARE_LESS,
|
||||
PXL8_GFX_COMPARE_EQUAL,
|
||||
PXL8_GFX_COMPARE_LEQUAL,
|
||||
PXL8_GFX_COMPARE_GREATER,
|
||||
PXL8_GFX_COMPARE_NOTEQUAL,
|
||||
PXL8_GFX_COMPARE_GEQUAL,
|
||||
PXL8_GFX_COMPARE_ALWAYS,
|
||||
} pxl8_gfx_compare_func;
|
||||
|
||||
typedef enum pxl8_gfx_blend_factor {
|
||||
PXL8_GFX_BLEND_ZERO,
|
||||
PXL8_GFX_BLEND_ONE,
|
||||
PXL8_GFX_BLEND_SRC_ALPHA,
|
||||
PXL8_GFX_BLEND_INV_SRC_ALPHA,
|
||||
PXL8_GFX_BLEND_DST_ALPHA,
|
||||
PXL8_GFX_BLEND_INV_DST_ALPHA,
|
||||
} pxl8_gfx_blend_factor;
|
||||
|
||||
typedef enum pxl8_gfx_load_op {
|
||||
PXL8_GFX_LOAD_CLEAR,
|
||||
PXL8_GFX_LOAD_LOAD,
|
||||
PXL8_GFX_LOAD_DONT_CARE,
|
||||
} pxl8_gfx_load_op;
|
||||
|
||||
typedef enum pxl8_gfx_store_op {
|
||||
PXL8_GFX_STORE_STORE,
|
||||
PXL8_GFX_STORE_DONT_CARE,
|
||||
} pxl8_gfx_store_op;
|
||||
|
||||
typedef struct pxl8_gfx_buffer_desc {
|
||||
pxl8_gfx_range data;
|
||||
u32 capacity;
|
||||
pxl8_gfx_buffer_type type;
|
||||
pxl8_gfx_usage usage;
|
||||
} pxl8_gfx_buffer_desc;
|
||||
|
||||
typedef struct pxl8_gfx_texture_desc {
|
||||
u32 width;
|
||||
u32 height;
|
||||
pxl8_gfx_texture_format format;
|
||||
pxl8_gfx_range data;
|
||||
pxl8_gfx_usage usage;
|
||||
bool render_target;
|
||||
} pxl8_gfx_texture_desc;
|
||||
|
||||
typedef pxl8_shader_fn pxl8_gfx_shader;
|
||||
|
||||
typedef struct pxl8_gfx_pipeline_desc {
|
||||
struct {
|
||||
bool enabled;
|
||||
pxl8_gfx_blend_factor src;
|
||||
pxl8_gfx_blend_factor dst;
|
||||
bool alpha_test;
|
||||
u8 alpha_ref;
|
||||
} blend;
|
||||
|
||||
struct {
|
||||
bool test;
|
||||
bool write;
|
||||
pxl8_gfx_compare_func compare;
|
||||
} depth;
|
||||
|
||||
bool dither;
|
||||
bool double_sided;
|
||||
bool emissive;
|
||||
|
||||
struct {
|
||||
pxl8_gfx_cull_mode cull;
|
||||
pxl8_gfx_fill_mode fill;
|
||||
} rasterizer;
|
||||
|
||||
pxl8_gfx_shader shader;
|
||||
} pxl8_gfx_pipeline_desc;
|
||||
|
||||
typedef struct pxl8_gfx_bindings_desc {
|
||||
const pxl8_atlas* atlas;
|
||||
const pxl8_colormap* colormap;
|
||||
const u32* palette;
|
||||
u32 texture_id;
|
||||
} pxl8_gfx_bindings_desc;
|
||||
|
||||
typedef struct pxl8_gfx_pass_color_attachment {
|
||||
pxl8_gfx_texture texture;
|
||||
pxl8_gfx_load_op load;
|
||||
pxl8_gfx_store_op store;
|
||||
u8 clear_value;
|
||||
} pxl8_gfx_pass_color_attachment;
|
||||
|
||||
typedef struct pxl8_gfx_pass_depth_attachment {
|
||||
pxl8_gfx_texture texture;
|
||||
pxl8_gfx_load_op load;
|
||||
u16 clear_value;
|
||||
} pxl8_gfx_pass_depth_attachment;
|
||||
|
||||
typedef struct pxl8_gfx_pass_light_attachment {
|
||||
pxl8_gfx_texture texture;
|
||||
pxl8_gfx_load_op load;
|
||||
} pxl8_gfx_pass_light_attachment;
|
||||
|
||||
typedef struct pxl8_gfx_pass_desc {
|
||||
pxl8_gfx_pass_color_attachment color;
|
||||
pxl8_gfx_pass_depth_attachment depth;
|
||||
pxl8_gfx_pass_light_attachment light_accum;
|
||||
} pxl8_gfx_pass_desc;
|
||||
|
||||
typedef struct pxl8_gfx_cmd_draw_params {
|
||||
pxl8_mat4 model;
|
||||
pxl8_mat4 projection;
|
||||
pxl8_shader_uniforms shader;
|
||||
pxl8_mat4 view;
|
||||
} pxl8_gfx_cmd_draw_params;
|
||||
|
||||
typedef enum pxl8_gfx_cmd_type {
|
||||
PXL8_GFX_CMD_BEGIN_PASS,
|
||||
PXL8_GFX_CMD_END_PASS,
|
||||
PXL8_GFX_CMD_SET_PIPELINE,
|
||||
PXL8_GFX_CMD_SET_BINDINGS,
|
||||
PXL8_GFX_CMD_SET_VIEWPORT,
|
||||
PXL8_GFX_CMD_SET_SCISSOR,
|
||||
PXL8_GFX_CMD_SET_DRAW_PARAMS,
|
||||
PXL8_GFX_CMD_DRAW,
|
||||
PXL8_GFX_CMD_RESOLVE,
|
||||
} pxl8_gfx_cmd_type;
|
||||
|
||||
typedef struct pxl8_gfx_cmd_begin_pass {
|
||||
pxl8_gfx_pass pass;
|
||||
} pxl8_gfx_cmd_begin_pass;
|
||||
|
||||
typedef struct pxl8_gfx_cmd_set_pipeline {
|
||||
pxl8_gfx_pipeline pipeline;
|
||||
} pxl8_gfx_cmd_set_pipeline;
|
||||
|
||||
typedef struct pxl8_gfx_cmd_set_bindings {
|
||||
pxl8_gfx_bindings bindings;
|
||||
} pxl8_gfx_cmd_set_bindings;
|
||||
|
||||
typedef struct pxl8_gfx_cmd_set_viewport {
|
||||
i32 x, y;
|
||||
u32 w, h;
|
||||
} pxl8_gfx_cmd_set_viewport;
|
||||
|
||||
typedef struct pxl8_gfx_cmd_set_scissor {
|
||||
i32 x, y;
|
||||
u32 w, h;
|
||||
} pxl8_gfx_cmd_set_scissor;
|
||||
|
||||
typedef struct pxl8_gfx_cmd_draw {
|
||||
pxl8_gfx_buffer vertex_buffer;
|
||||
pxl8_gfx_buffer index_buffer;
|
||||
u32 base_vertex;
|
||||
u32 first_index;
|
||||
u32 index_count;
|
||||
} pxl8_gfx_cmd_draw;
|
||||
|
||||
typedef struct pxl8_gfx_cmd_resolve {
|
||||
pxl8_gfx_texture src;
|
||||
u32* output;
|
||||
} pxl8_gfx_cmd_resolve;
|
||||
|
||||
typedef struct pxl8_gfx_cmd {
|
||||
pxl8_gfx_cmd_type type;
|
||||
union {
|
||||
pxl8_gfx_cmd_begin_pass begin_pass;
|
||||
pxl8_gfx_cmd_set_pipeline set_pipeline;
|
||||
pxl8_gfx_cmd_set_bindings set_bindings;
|
||||
pxl8_gfx_cmd_set_viewport set_viewport;
|
||||
pxl8_gfx_cmd_set_scissor set_scissor;
|
||||
pxl8_gfx_cmd_draw draw;
|
||||
pxl8_gfx_cmd_draw_params draw_params;
|
||||
pxl8_gfx_cmd_resolve resolve;
|
||||
};
|
||||
} pxl8_gfx_cmd;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
81
src/gfx/pxl8_shader.h
Normal file
81
src/gfx/pxl8_shader.h
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_lights.h"
|
||||
#include "pxl8_math.h"
|
||||
#include "pxl8_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct pxl8_vertex_in {
|
||||
pxl8_vec3 a_position;
|
||||
pxl8_vec3 a_normal;
|
||||
pxl8_vec2 a_uv;
|
||||
u8 a_color;
|
||||
u8 a_light;
|
||||
} pxl8_vertex_in;
|
||||
|
||||
typedef struct pxl8_vertex_out {
|
||||
pxl8_vec4 gl_Position;
|
||||
pxl8_vec2 v_uv;
|
||||
pxl8_vec3 v_world;
|
||||
pxl8_vec3 v_normal;
|
||||
f32 v_light;
|
||||
f32 v_depth;
|
||||
} pxl8_vertex_out;
|
||||
|
||||
typedef struct pxl8_shader_uniforms {
|
||||
u8 ambient;
|
||||
bool baked_lighting;
|
||||
pxl8_vec3 celestial_dir;
|
||||
f32 celestial_intensity;
|
||||
bool dither;
|
||||
bool dynamic_lighting;
|
||||
bool emissive;
|
||||
u8 fog_color;
|
||||
f32 fog_density;
|
||||
const pxl8_light* lights;
|
||||
u32 lights_count;
|
||||
bool textures;
|
||||
f32 time;
|
||||
} pxl8_shader_uniforms;
|
||||
|
||||
typedef struct pxl8_shader_bindings {
|
||||
const u8* colormap;
|
||||
const u32* palette;
|
||||
const u8* atlas;
|
||||
u32 width, height;
|
||||
bool use_tiled;
|
||||
union {
|
||||
struct {
|
||||
u32 stride;
|
||||
u32 x, y;
|
||||
} linear;
|
||||
struct {
|
||||
u32 base;
|
||||
u8 log2_w;
|
||||
} tiled;
|
||||
};
|
||||
} pxl8_shader_bindings;
|
||||
|
||||
typedef struct pxl8_shader_ctx {
|
||||
i32 x, y;
|
||||
pxl8_vec2 v_uv;
|
||||
pxl8_vec3 v_world;
|
||||
pxl8_vec3 v_normal;
|
||||
f32 v_color;
|
||||
f32 v_light;
|
||||
f32 v_depth;
|
||||
u32 out_light_color;
|
||||
} pxl8_shader_ctx;
|
||||
|
||||
typedef u8 (*pxl8_shader_fn)(
|
||||
pxl8_shader_ctx* ctx,
|
||||
const pxl8_shader_bindings* bindings,
|
||||
const pxl8_shader_uniforms* uniforms
|
||||
);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
80
src/gfx/pxl8_shader_builtins.h
Normal file
80
src/gfx/pxl8_shader_builtins.h
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_dither.h"
|
||||
#include "pxl8_math.h"
|
||||
#include "pxl8_shader.h"
|
||||
#include "pxl8_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static inline pxl8_vec2 pxl8_swizzle_xy(pxl8_vec4 v) { return (pxl8_vec2){v.x, v.y}; }
|
||||
static inline pxl8_vec2 pxl8_swizzle_xz(pxl8_vec4 v) { return (pxl8_vec2){v.x, v.z}; }
|
||||
static inline pxl8_vec2 pxl8_swizzle_xw(pxl8_vec4 v) { return (pxl8_vec2){v.x, v.w}; }
|
||||
static inline pxl8_vec2 pxl8_swizzle_yz(pxl8_vec4 v) { return (pxl8_vec2){v.y, v.z}; }
|
||||
static inline pxl8_vec2 pxl8_swizzle_yw(pxl8_vec4 v) { return (pxl8_vec2){v.y, v.w}; }
|
||||
static inline pxl8_vec2 pxl8_swizzle_zw(pxl8_vec4 v) { return (pxl8_vec2){v.z, v.w}; }
|
||||
|
||||
static inline pxl8_vec3 pxl8_swizzle_xyz(pxl8_vec4 v) { return (pxl8_vec3){v.x, v.y, v.z}; }
|
||||
static inline pxl8_vec3 pxl8_swizzle_xyw(pxl8_vec4 v) { return (pxl8_vec3){v.x, v.y, v.w}; }
|
||||
static inline pxl8_vec3 pxl8_swizzle_xzw(pxl8_vec4 v) { return (pxl8_vec3){v.x, v.z, v.w}; }
|
||||
static inline pxl8_vec3 pxl8_swizzle_yzw(pxl8_vec4 v) { return (pxl8_vec3){v.y, v.z, v.w}; }
|
||||
|
||||
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_sample_indexed(const pxl8_shader_bindings* b, pxl8_vec2 uv) {
|
||||
if (!b || !b->atlas) return 0;
|
||||
u32 w = b->width;
|
||||
u32 h = b->height;
|
||||
if (w == 0 || h == 0) return 0;
|
||||
u32 u = (u32)(uv.x * (f32)w) & (w - 1);
|
||||
u32 v = (u32)(uv.y * (f32)h) & (h - 1);
|
||||
if (b->use_tiled) {
|
||||
return b->atlas[b->tiled.base + pxl8_tile_addr(u, v, b->tiled.log2_w)];
|
||||
} else {
|
||||
return b->atlas[(b->linear.y + v) * b->linear.stride + b->linear.x + u];
|
||||
}
|
||||
}
|
||||
|
||||
static inline u8 pxl8_colormap_lookup(const pxl8_shader_bindings* b, u8 color, u8 light) {
|
||||
if (!b) return color;
|
||||
const u8* cm = b->colormap;
|
||||
if (!cm) return color;
|
||||
u32 row = light >> 5;
|
||||
return cm[(row << 8) | (u32)color];
|
||||
}
|
||||
|
||||
static inline f32 pxl8_light_falloff(const pxl8_shader_ctx* ctx, const pxl8_shader_uniforms* u, u32 light_idx) {
|
||||
if (!u || light_idx >= u->lights_count) return 0.0f;
|
||||
const pxl8_light* light = &u->lights[light_idx];
|
||||
f32 dx = light->position.x - ctx->v_world.x;
|
||||
f32 dy = light->position.y - ctx->v_world.y;
|
||||
f32 dz = light->position.z - ctx->v_world.z;
|
||||
f32 dist_sq = dx * dx + dy * dy + dz * dz;
|
||||
if (dist_sq >= light->radius_sq) return 0.0f;
|
||||
return 1.0f - dist_sq * light->inv_radius_sq;
|
||||
}
|
||||
|
||||
static inline u32 pxl8_light_color(const pxl8_shader_uniforms* u, u32 light_idx) {
|
||||
if (!u || light_idx >= u->lights_count) return 0;
|
||||
const pxl8_light* light = &u->lights[light_idx];
|
||||
return (u32)light->r | ((u32)light->g << 8) | ((u32)light->b << 16);
|
||||
}
|
||||
|
||||
static inline void pxl8_set_light_tint(pxl8_shader_ctx* ctx, u32 color, f32 strength) {
|
||||
if (strength <= 0.0f) return;
|
||||
if (strength > 1.0f) strength = 1.0f;
|
||||
u32 alpha = (u32)(strength * 255.0f);
|
||||
ctx->out_light_color = (color & 0x00FFFFFF) | (alpha << 24);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
15
src/gfx/pxl8_shader_registry.c
Normal file
15
src/gfx/pxl8_shader_registry.c
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#include "pxl8_shader_registry.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
extern u8 pxl8_shader_lit(pxl8_shader_ctx* ctx, const pxl8_shader_bindings* bindings, const pxl8_shader_uniforms* uniforms);
|
||||
extern u8 pxl8_shader_unlit(pxl8_shader_ctx* ctx, const pxl8_shader_bindings* bindings, const pxl8_shader_uniforms* uniforms);
|
||||
|
||||
void pxl8_shader_registry_init(void) {}
|
||||
void pxl8_shader_registry_reload(void) {}
|
||||
|
||||
pxl8_shader_fn pxl8_shader_registry_get(const char* name) {
|
||||
if (strcmp(name, "lit") == 0) return (pxl8_shader_fn)pxl8_shader_lit;
|
||||
if (strcmp(name, "unlit") == 0) return (pxl8_shader_fn)pxl8_shader_unlit;
|
||||
return NULL;
|
||||
}
|
||||
7
src/gfx/pxl8_shader_registry.h
Normal file
7
src/gfx/pxl8_shader_registry.h
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_shader.h"
|
||||
|
||||
void pxl8_shader_registry_init(void);
|
||||
void pxl8_shader_registry_reload(void);
|
||||
pxl8_shader_fn pxl8_shader_registry_get(const char* name);
|
||||
66
src/gfx/pxl8_shader_runtime.c
Normal file
66
src/gfx/pxl8_shader_runtime.c
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
#include "pxl8_shader_runtime.h"
|
||||
#include "pxl8_log.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
struct pxl8_shader_lib {
|
||||
void* handle;
|
||||
char path[256];
|
||||
};
|
||||
|
||||
pxl8_shader_lib* pxl8_shader_lib_load(const char* path) {
|
||||
void* handle = dlopen(path, RTLD_NOW);
|
||||
if (!handle) {
|
||||
pxl8_error("Failed to load shader library: %s - %s", path, dlerror());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pxl8_shader_lib* lib = malloc(sizeof(pxl8_shader_lib));
|
||||
lib->handle = handle;
|
||||
strncpy(lib->path, path, sizeof(lib->path) - 1);
|
||||
lib->path[sizeof(lib->path) - 1] = '\0';
|
||||
|
||||
return lib;
|
||||
}
|
||||
|
||||
void pxl8_shader_lib_unload(pxl8_shader_lib* lib) {
|
||||
if (!lib) return;
|
||||
if (lib->handle) {
|
||||
dlclose(lib->handle);
|
||||
}
|
||||
free(lib);
|
||||
}
|
||||
|
||||
bool pxl8_shader_lib_reload(pxl8_shader_lib* lib) {
|
||||
if (!lib) return false;
|
||||
|
||||
if (lib->handle) {
|
||||
dlclose(lib->handle);
|
||||
}
|
||||
|
||||
lib->handle = dlopen(lib->path, RTLD_NOW);
|
||||
if (!lib->handle) {
|
||||
pxl8_error("Failed to reload shader library: %s - %s", lib->path, dlerror());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pxl8_shader_fn pxl8_shader_lib_get(pxl8_shader_lib* lib, const char* name) {
|
||||
if (!lib || !lib->handle) return NULL;
|
||||
|
||||
char symbol[128];
|
||||
snprintf(symbol, sizeof(symbol), "pxl8_shader_%s", name);
|
||||
|
||||
void* fn = dlsym(lib->handle, symbol);
|
||||
if (!fn) {
|
||||
pxl8_error("Failed to find shader: %s - %s", symbol, dlerror());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (pxl8_shader_fn)fn;
|
||||
}
|
||||
20
src/gfx/pxl8_shader_runtime.h
Normal file
20
src/gfx/pxl8_shader_runtime.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_shader.h"
|
||||
#include "pxl8_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct pxl8_shader_lib pxl8_shader_lib;
|
||||
|
||||
pxl8_shader_lib* pxl8_shader_lib_load(const char* path);
|
||||
void pxl8_shader_lib_unload(pxl8_shader_lib* lib);
|
||||
bool pxl8_shader_lib_reload(pxl8_shader_lib* lib);
|
||||
|
||||
pxl8_shader_fn pxl8_shader_lib_get(pxl8_shader_lib* lib, const char* name);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
106
src/gfx/shaders/cpu/lit.c
Normal file
106
src/gfx/shaders/cpu/lit.c
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
#include "pxl8_macros.h"
|
||||
#include "pxl8_shader.h"
|
||||
#include "pxl8_shader_builtins.h"
|
||||
|
||||
u8 pxl8_shader_lit(
|
||||
pxl8_shader_ctx* ctx,
|
||||
const pxl8_shader_bindings* bindings,
|
||||
const pxl8_shader_uniforms* uniforms
|
||||
) {
|
||||
u8 tex_idx = 0;
|
||||
if (bindings && bindings->atlas) {
|
||||
tex_idx = pxl8_sample_indexed(bindings, ctx->v_uv);
|
||||
if (pxl8_unlikely(tex_idx == 0)) return 0;
|
||||
} else {
|
||||
if (uniforms && uniforms->dither) {
|
||||
tex_idx = pxl8_gfx_dither(ctx->v_color, (u32)ctx->x, (u32)ctx->y);
|
||||
} else {
|
||||
f32 clamped = pxl8_clamp(ctx->v_color, 0.0f, 255.0f);
|
||||
tex_idx = (u8)(clamped);
|
||||
}
|
||||
}
|
||||
|
||||
f32 light = ctx->v_light;
|
||||
|
||||
if (uniforms) {
|
||||
f32 ambient = (f32)uniforms->ambient / 255.0f;
|
||||
if (ambient > light) light = ambient;
|
||||
|
||||
if (uniforms->celestial_intensity > 0.0f) {
|
||||
f32 ndotl = -(ctx->v_normal.x * uniforms->celestial_dir.x +
|
||||
ctx->v_normal.y * uniforms->celestial_dir.y +
|
||||
ctx->v_normal.z * uniforms->celestial_dir.z);
|
||||
if (ndotl > 0.0f) {
|
||||
light += ndotl * uniforms->celestial_intensity;
|
||||
}
|
||||
}
|
||||
|
||||
f32 dyn_strength = 0.0f;
|
||||
f32 dyn_r = 0.0f;
|
||||
f32 dyn_g = 0.0f;
|
||||
f32 dyn_b = 0.0f;
|
||||
|
||||
for (u32 i = 0; i < uniforms->lights_count; i++) {
|
||||
const pxl8_light* l = &uniforms->lights[i];
|
||||
f32 lx = l->position.x - ctx->v_world.x;
|
||||
f32 ly = l->position.y - ctx->v_world.y;
|
||||
f32 lz = l->position.z - ctx->v_world.z;
|
||||
f32 dist_sq = lx * lx + ly * ly + lz * lz;
|
||||
if (dist_sq >= l->radius_sq) continue;
|
||||
|
||||
f32 inv_dist = pxl8_fast_inv_sqrt(dist_sq);
|
||||
f32 nx = lx * inv_dist;
|
||||
f32 ny = ly * inv_dist;
|
||||
f32 nz = lz * inv_dist;
|
||||
|
||||
f32 ndotl = ctx->v_normal.x * nx + ctx->v_normal.y * ny + ctx->v_normal.z * nz;
|
||||
if (ndotl <= 0.0f) continue;
|
||||
|
||||
f32 falloff = 1.0f - dist_sq * l->inv_radius_sq;
|
||||
if (falloff <= 0.0f) continue;
|
||||
if (uniforms->dither && falloff < 0.33f) {
|
||||
f32 threshold = (PXL8_BAYER_4X4[((u32)ctx->y & 3) * 4 + ((u32)ctx->x & 3)] + 0.5f) * (1.0f / 16.0f);
|
||||
if (falloff < threshold * 0.33f) continue;
|
||||
}
|
||||
|
||||
f32 strength = ((f32)l->intensity / 255.0f) * falloff * ndotl;
|
||||
if (strength <= 0.0f) continue;
|
||||
|
||||
dyn_strength += strength;
|
||||
dyn_r += strength * (f32)l->r;
|
||||
dyn_g += strength * (f32)l->g;
|
||||
dyn_b += strength * (f32)l->b;
|
||||
}
|
||||
|
||||
if (dyn_strength > 0.0f) {
|
||||
f32 inv = pxl8_fast_rcp(dyn_strength);
|
||||
u8 r = (u8)pxl8_clamp(dyn_r * inv, 0.0f, 255.0f);
|
||||
u8 g = (u8)pxl8_clamp(dyn_g * inv, 0.0f, 255.0f);
|
||||
u8 b = (u8)pxl8_clamp(dyn_b * inv, 0.0f, 255.0f);
|
||||
u8 a = (u8)pxl8_clamp(dyn_strength * 255.0f, 0.0f, 255.0f);
|
||||
ctx->out_light_color = (u32)r | ((u32)g << 8) | ((u32)b << 16) | ((u32)a << 24);
|
||||
light += dyn_strength;
|
||||
}
|
||||
}
|
||||
|
||||
if (light > 1.0f) light = 1.0f;
|
||||
if (light < 0.0f) light = 0.0f;
|
||||
|
||||
f32 light_f = light * 255.0f;
|
||||
u8 light_u8 = (u8)light_f;
|
||||
if (uniforms && uniforms->dither) {
|
||||
light_u8 = pxl8_gfx_dither(light_f, (u32)ctx->x, (u32)ctx->y);
|
||||
}
|
||||
|
||||
u8 shaded = pxl8_colormap_lookup(bindings, tex_idx, light_u8);
|
||||
|
||||
if (uniforms && uniforms->emissive) {
|
||||
u32 rgb = 0x00FFFFFF;
|
||||
if (bindings && bindings->palette) {
|
||||
rgb = bindings->palette[tex_idx] & 0x00FFFFFF;
|
||||
}
|
||||
pxl8_set_light_tint(ctx, rgb, 1.0f);
|
||||
}
|
||||
|
||||
return shaded;
|
||||
}
|
||||
31
src/gfx/shaders/cpu/unlit.c
Normal file
31
src/gfx/shaders/cpu/unlit.c
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#include "pxl8_shader.h"
|
||||
#include "pxl8_shader_builtins.h"
|
||||
|
||||
u8 pxl8_shader_unlit(
|
||||
pxl8_shader_ctx* ctx,
|
||||
const pxl8_shader_bindings* bindings,
|
||||
const pxl8_shader_uniforms* uniforms
|
||||
) {
|
||||
u8 tex_idx = 0;
|
||||
if (bindings && bindings->atlas) {
|
||||
tex_idx = pxl8_sample_indexed(bindings, ctx->v_uv);
|
||||
if (tex_idx == 0) return 0;
|
||||
} else {
|
||||
if (uniforms && uniforms->dither) {
|
||||
tex_idx = pxl8_gfx_dither(ctx->v_color, (u32)ctx->x, (u32)ctx->y);
|
||||
} else {
|
||||
f32 clamped = pxl8_clamp(ctx->v_color, 0.0f, 255.0f);
|
||||
tex_idx = (u8)(clamped);
|
||||
}
|
||||
}
|
||||
|
||||
if (uniforms && uniforms->emissive) {
|
||||
u32 rgb = 0x00FFFFFF;
|
||||
if (bindings && bindings->palette) {
|
||||
rgb = bindings->palette[tex_idx] & 0x00FFFFFF;
|
||||
}
|
||||
pxl8_set_light_tint(ctx, rgb, 1.0f);
|
||||
}
|
||||
|
||||
return tex_idx;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue