add colored lighting back in via the colormap

This commit is contained in:
asrael 2026-02-05 03:27:35 -06:00
parent 01e6059dd1
commit a29a6018b8
16 changed files with 149 additions and 466 deletions

View file

@ -349,11 +349,12 @@
r2 (* 0.04 (math.sin (+ (* real-time 3.2) phase)))
light-radius (* 150 (+ 0.95 r1 r2))]
(lights:clear)
(lights:add light-x light-y light-z 0xFFB888 light-intensity light-radius)
(lights:add light-x light-y light-z 2 light-intensity light-radius)
(pxl8.push_target)
(pxl8.begin_frame_3d camera lights {
:ambient 25
:dither true
:fog_density 0.0
:celestial_dir [0.5 -0.8 0.3]
:celestial_intensity 0.3})

View file

@ -340,7 +340,6 @@ pxl8_result pxl8_update(pxl8* sys) {
if (game->fps_accumulator >= 1.0f) {
game->fps = (f32)game->fps_frame_count / game->fps_accumulator;
pxl8_info("FPS: %.1f", game->fps);
game->fps_accumulator = 0.0f;
game->fps_frame_count = 0;
}

View file

@ -37,7 +37,7 @@ typedef struct {
static const pxl8_rgb pxl8_light_colors[PXL8_LIGHT_COLORS] = {
{255, 255, 255},
{255, 64, 64},
{255, 160, 64},
{255, 192, 64},
{255, 255, 64},
{64, 255, 64},
{64, 255, 255},

View file

@ -53,7 +53,6 @@ struct pxl8_gfx {
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;
@ -115,11 +114,6 @@ i32 pxl8_gfx_get_height(const pxl8_gfx* gfx) {
return gfx ? gfx->framebuffer_height : 0;
}
u32* pxl8_gfx_get_light_accum(pxl8_gfx* gfx) {
if (!gfx) return NULL;
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;
@ -137,7 +131,6 @@ void pxl8_gfx_resolve(pxl8_gfx* gfx) {
pxl8_resolve_to_rgba(
gfx->renderer,
gfx->color_target,
gfx->light_target,
pal,
gfx->output
);
@ -246,14 +239,6 @@ pxl8_gfx* pxl8_gfx_create(
};
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) {
@ -276,7 +261,6 @@ pxl8_gfx* pxl8_gfx_create(
gfx->target_stack[0] = (pxl8_target_entry){
.color = gfx->color_target,
.depth = gfx->depth_target,
.light = gfx->light_target,
};
gfx->target_stack_depth = 1;
@ -469,16 +453,6 @@ 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);
@ -508,11 +482,6 @@ static pxl8_gfx_texture gfx_current_depth(pxl8_gfx* gfx) {
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;
@ -546,7 +515,6 @@ static void gfx_composite_over(pxl8_gfx* gfx, const pxl8_target_entry* src, pxl8
void pxl8_2d_clear(pxl8_gfx* gfx, u32 color) {
if (!gfx) return;
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) {
@ -745,7 +713,6 @@ void pxl8_3d_begin_frame(pxl8_gfx* gfx, const pxl8_3d_camera* camera, const pxl8
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);
@ -791,7 +758,6 @@ 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;
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) {
@ -1001,18 +967,9 @@ bool pxl8_gfx_push_target(pxl8_gfx* gfx) {
};
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++;

View file

@ -10,7 +10,19 @@
#include "pxl8_gui_palette.h"
typedef struct pxl8_gfx pxl8_gfx;
typedef struct pxl8_gfx_stats pxl8_gfx_stats;
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 submit_ns;
u64 execute_draw_ns;
u64 raster_ns;
} pxl8_gfx_stats;
typedef enum pxl8_gfx_effect {
PXL8_GFX_EFFECT_GLOWS = 0,
@ -26,8 +38,6 @@ 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);
@ -35,7 +45,6 @@ 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);
u32* pxl8_gfx_get_light_accum(pxl8_gfx* gfx);
const u32* pxl8_gfx_palette_colors(pxl8_gfx* gfx);
u16* pxl8_gfx_get_zbuffer(pxl8_gfx* gfx);
pxl8_palette* pxl8_gfx_palette(pxl8_gfx* gfx);

View file

@ -33,16 +33,16 @@ void pxl8_lights_destroy(pxl8_lights* lights) {
pxl8_free(lights);
}
void pxl8_lights_add(pxl8_lights* lights, f32 x, f32 y, f32 z, u8 r, u8 g, u8 b, u8 intensity, f32 radius) {
(void)r; (void)g; (void)b;
void pxl8_lights_add(pxl8_lights* lights, f32 x, f32 y, f32 z, u8 color, u8 intensity, f32 radius) {
if (!lights || lights->count >= lights->capacity) return;
f32 radius_sq = radius * radius;
pxl8_light* l = &lights->data[lights->count++];
l->color = color;
l->intensity = (f32)intensity / 255.0f;
l->inv_radius_sq = radius_sq > 0.0f ? 1.0f / radius_sq : 0.0f;
l->position = (pxl8_vec3){{x, y, z}};
l->radius_sq = radius_sq;
l->inv_radius_sq = radius_sq > 0.0f ? 1.0f / radius_sq : 0.0f;
l->intensity = (f32)intensity / 255.0f;
}
void pxl8_lights_clear(pxl8_lights* lights) {

View file

@ -6,10 +6,11 @@
#define PXL8_LIGHTS_MAX 256
typedef struct pxl8_light {
u8 color;
f32 intensity;
f32 inv_radius_sq;
pxl8_vec3 position;
f32 radius_sq;
f32 inv_radius_sq;
f32 intensity;
} pxl8_light;
typedef struct pxl8_lights pxl8_lights;
@ -21,7 +22,7 @@ extern "C" {
pxl8_lights* pxl8_lights_create(u32 capacity);
void pxl8_lights_destroy(pxl8_lights* lights);
void pxl8_lights_add(pxl8_lights* lights, f32 x, f32 y, f32 z, u8 r, u8 g, u8 b, u8 intensity, f32 radius);
void pxl8_lights_add(pxl8_lights* lights, f32 x, f32 y, f32 z, u8 color, u8 intensity, f32 radius);
void pxl8_lights_clear(pxl8_lights* lights);
u32 pxl8_lights_count(const pxl8_lights* lights);
const pxl8_light* pxl8_lights_data(const pxl8_lights* lights);

View file

@ -12,16 +12,6 @@
#include <stdlib.h>
#include <string.h>
#if PXL8_GFX_ENABLE_STATS
#define STATS_INC(stats, field, val) do { (stats)->field += (val); } while (0)
#define STATS_START() pxl8_get_ticks_ns()
#define STATS_ADD(stats, field, start) do { (stats)->field += pxl8_get_ticks_ns() - (start); } while (0)
#else
#define STATS_INC(stats, field, val) do { (void)(stats); } while (0)
#define STATS_START() 0
#define STATS_ADD(stats, field, start) do { (void)(stats); (void)(start); } while (0)
#endif
typedef struct {
pxl8_vec4 clip_pos;
pxl8_vec3 world_pos;
@ -300,8 +290,7 @@ static void rasterize_triangle(
pxl8_shader_fn shader,
const pxl8_gfx_pipeline_desc* pipeline,
const pxl8_shader_bindings* bindings,
const pxl8_shader_uniforms* uniforms,
pxl8_gfx_stats* stats
const pxl8_shader_uniforms* uniforms
) {
const i32 SUBDIV = 16;
@ -490,8 +479,6 @@ static void rasterize_triangle(
pxl8_i32_simd zbuf = pxl8_i32_simd_set4((i32)zrow[px], (i32)zrow[px+1], (i32)zrow[px+2], (i32)zrow[px+3]);
i32 mask = pxl8_i32_simd_movemask(pxl8_i32_simd_cmpgt(zbuf, z16_4));
STATS_INC(stats, depth_tests, 4);
if (mask) {
pxl8_shader_ctx frag_ctx = {
.color_count = 4,
@ -517,20 +504,16 @@ static void rasterize_triangle(
u8 colors[4];
shader(&frag_ctx, bindings, uniforms, colors);
STATS_INC(stats, shader_calls, 1);
i32 z16_arr[4];
pxl8_i32_simd_store(z16_arr, z16_4);
for (i32 i = 0; i < 4; i++) {
if (!(mask & (0x8 << (i * 4)))) continue;
STATS_INC(stats, depth_passes, 1);
u8 color = colors[i];
if (!(alpha_test && color <= alpha_ref) && color != 0) {
prow[px + i] = color;
if (depth_write) zrow[px + i] = (u16)z16_arr[i];
STATS_INC(stats, pixels_written, 1);
}
}
}
@ -540,16 +523,12 @@ static void rasterize_triangle(
z4 = pxl8_f32_simd_add(z4, dz4_simd);
}
}
#endif
for (; px <= span_end; px++) {
f32 depth_norm = pxl8_clamp((z_a + 1.0f) * 0.5f, 0.0f, 1.0f);
u16 z16 = (u16)(depth_norm * 65535.0f);
STATS_INC(stats, depth_tests, 1);
bool depth_pass = !depth_test || depth_test_pass(depth_compare, z16, zrow[px]);
if (depth_pass) {
STATS_INC(stats, depth_passes, 1);
pxl8_shader_ctx frag_ctx = {
.color_count = 1,
.x = pxl8_i32_simd_set(px),
@ -564,7 +543,6 @@ static void rasterize_triangle(
u8 color;
shader(&frag_ctx, bindings, uniforms, &color);
STATS_INC(stats, shader_calls, 1);
if (!(alpha_test && color <= alpha_ref)) {
if (color != 0) {
@ -577,7 +555,6 @@ static void rasterize_triangle(
if (depth_write) {
zrow[px] = z16;
}
STATS_INC(stats, pixels_written, 1);
}
}
}
@ -591,6 +568,53 @@ static void rasterize_triangle(
wy_a += dwy;
wz_a += dwz;
}
#else
for (; px <= span_end; px++) {
f32 depth_norm = pxl8_clamp((z_a + 1.0f) * 0.5f, 0.0f, 1.0f);
u16 z16 = (u16)(depth_norm * 65535.0f);
bool depth_pass = !depth_test || depth_test_pass(depth_compare, z16, zrow[px]);
if (depth_pass) {
pxl8_shader_ctx frag_ctx = {
.color_count = 1,
.x = px,
.y = y,
.v_uv = { u_a, v_a },
.v_world = { wx_a, wy_a, wz_a },
.v_normal = setup->normal,
.v_light = l_a / 255.0f,
.v_color = c_a,
.v_depth = z_a,
};
u8 color;
shader(&frag_ctx, bindings, uniforms, &color);
if (!(alpha_test && color <= alpha_ref)) {
if (color != 0) {
u8 out_color = color;
if (blend_enabled) {
out_color = blend_indexed(pipeline, color, prow[px], palette, colormap);
}
prow[px] = out_color;
if (depth_write) {
zrow[px] = z16;
}
}
}
}
u_a += du;
v_a += dv;
l_a += dl;
c_a += dc;
z_a += dz;
wx_a += dwx;
wy_a += dwy;
wz_a += dwz;
}
#endif
wr += dwr * (f32)span_len;
uw += duw * (f32)span_len;
@ -618,8 +642,7 @@ static void draw_line_clipped(
i32 clip_min_x,
i32 clip_min_y,
i32 clip_max_x,
i32 clip_max_y,
pxl8_gfx_stats* stats
i32 clip_max_y
) {
i32 dx = abs(x1 - x0);
i32 dy = -abs(y1 - y0);
@ -631,7 +654,6 @@ static void draw_line_clipped(
if (x0 >= clip_min_x && x0 <= clip_max_x && y0 >= clip_min_y && y0 <= clip_max_y) {
if (x0 >= 0 && y0 >= 0 && x0 < (i32)fb_w && y0 < (i32)fb_h) {
fb[y0 * (i32)fb_w + x0] = color;
STATS_INC(stats, pixels_written, 1);
}
}
if (x0 == x1 && y0 == y1) break;
@ -718,7 +740,6 @@ struct pxl8_renderer {
u32 scissor_w, scissor_h;
pxl8_shader_fn shader;
pxl8_gfx_stats stats;
};
struct pxl8_gfx_cmdbuf {
@ -735,7 +756,6 @@ pxl8_renderer* pxl8_renderer_create(u32 width, u32 height) {
r->viewport_h = height;
r->scissor_w = width;
r->scissor_h = height;
pxl8_renderer_reset_stats(r);
return r;
}
@ -762,20 +782,10 @@ void pxl8_renderer_set_shader(pxl8_renderer* r, pxl8_shader_fn fn) {
if (r) r->shader = fn;
}
void pxl8_renderer_reset_stats(pxl8_renderer* r) {
if (!r) return;
memset(&r->stats, 0, sizeof(r->stats));
}
const pxl8_gfx_stats* pxl8_renderer_get_stats(const pxl8_renderer* r) {
return r ? &r->stats : NULL;
}
static u32 texture_byte_size(pxl8_gfx_texture_format fmt, u32 w, u32 h) {
switch (fmt) {
case PXL8_GFX_FORMAT_INDEXED8: return w * h;
case PXL8_GFX_FORMAT_DEPTH16: return w * h * 2;
case PXL8_GFX_FORMAT_LIGHT_ACCUM: return w * h * 4;
}
return 0;
}
@ -1085,9 +1095,6 @@ static void execute_draw(
if (!VALID_PASS(r, r->current_pass)) return;
if (!VALID_PIPELINE(r, r->current_pipeline)) return;
u64 exec_start = STATS_START();
STATS_INC(&r->stats, draw_calls, 1);
buffer_slot* vb = &r->buffers[SLOT_INDEX(cmd->vertex_buffer.id)];
buffer_slot* ib = use_indices ? &r->buffers[SLOT_INDEX(cmd->index_buffer.id)] : NULL;
pass_slot* pass = &r->passes[SLOT_INDEX(r->current_pass.id)];
@ -1095,12 +1102,10 @@ static void execute_draw(
if (!VALID_TEX(r, pass->desc.color.texture)) {
pxl8_error("draw: invalid color texture");
STATS_ADD(&r->stats, execute_draw_ns, exec_start);
return;
}
if (!VALID_TEX(r, pass->desc.depth.texture)) {
pxl8_error("draw: invalid depth texture");
STATS_ADD(&r->stats, execute_draw_ns, exec_start);
return;
}
@ -1112,10 +1117,7 @@ static void execute_draw(
u32 fb_w = color_tex->width;
u32 fb_h = color_tex->height;
if (r->viewport_w == 0 || r->viewport_h == 0) {
STATS_ADD(&r->stats, execute_draw_ns, exec_start);
return;
}
if (r->viewport_w == 0 || r->viewport_h == 0) return;
i32 vp_x = r->viewport_x;
i32 vp_y = r->viewport_y;
@ -1143,10 +1145,7 @@ static void execute_draw(
if (clip_min_y < 0) clip_min_y = 0;
if (clip_max_x >= (i32)fb_w) clip_max_x = (i32)fb_w - 1;
if (clip_max_y >= (i32)fb_h) clip_max_y = (i32)fb_h - 1;
if (clip_min_x > clip_max_x || clip_min_y > clip_max_y) {
STATS_ADD(&r->stats, execute_draw_ns, exec_start);
return;
}
if (clip_min_x > clip_max_x || clip_min_y > clip_max_y) return;
const pxl8_vertex* vertices = vb->data;
const u16* indices = use_indices ? ib->data : NULL;
@ -1156,10 +1155,7 @@ static void execute_draw(
f32 near = 0.1f;
pxl8_shader_fn shader = pip->desc.shader;
if (!shader) {
STATS_ADD(&r->stats, execute_draw_ns, exec_start);
return;
}
if (!shader) return;
pxl8_shader_bindings shader_bindings = {0};
pxl8_shader_uniforms shader_uniforms = r->current_draw_params.shader;
@ -1190,8 +1186,8 @@ static void execute_draw(
bool is_wireframe = pip->desc.rasterizer.fill == PXL8_GFX_FILL_WIREFRAME;
for (u32 i = cmd->first_index; i < cmd->first_index + cmd->index_count; i += 3) {
STATS_INC(&r->stats, triangles, 1);
u16 i0, i1, i2;
if (use_indices) {
if (i + 2 >= ib->size / sizeof(u16)) break;
i0 = indices[i] + cmd->base_vertex;
@ -1262,7 +1258,6 @@ static void execute_draw(
i32 clipped_count = clip_triangle_near(&rv0, &rv1, &rv2, near, clipped);
for (i32 t = 0; t < clipped_count; t += 3) {
STATS_INC(&r->stats, clipped_triangles, 1);
if (is_wireframe) {
f32 hw = (f32)vp_w * 0.5f;
f32 hh = (f32)vp_h * 0.5f;
@ -1286,11 +1281,11 @@ static void execute_draw(
u8 wire_color = v0->color ? v0->color : 15;
draw_line_clipped(fb, fb_w, fb_h, sx0, sy0, sx1, sy1, wire_color,
clip_min_x, clip_min_y, clip_max_x, clip_max_y, &r->stats);
clip_min_x, clip_min_y, clip_max_x, clip_max_y);
draw_line_clipped(fb, fb_w, fb_h, sx1, sy1, sx2, sy2, wire_color,
clip_min_x, clip_min_y, clip_max_x, clip_max_y, &r->stats);
clip_min_x, clip_min_y, clip_max_x, clip_max_y);
draw_line_clipped(fb, fb_w, fb_h, sx2, sy2, sx0, sy0, wire_color,
clip_min_x, clip_min_y, clip_max_x, clip_max_y, &r->stats);
clip_min_x, clip_min_y, clip_max_x, clip_max_y);
} else {
tri_setup setup;
if (!setup_tri(&setup, &clipped[t], &clipped[t+1], &clipped[t+2],
@ -1300,19 +1295,14 @@ static void execute_draw(
continue;
}
u64 raster_start = STATS_START();
rasterize_triangle(&setup, fb, zb, fb_w, shader, &pip->desc,
&shader_bindings, &shader_uniforms, &r->stats);
STATS_ADD(&r->stats, raster_ns, raster_start);
&shader_bindings, &shader_uniforms);
}
}
}
STATS_ADD(&r->stats, execute_draw_ns, exec_start);
}
void pxl8_gfx_submit(pxl8_renderer* r, pxl8_gfx_cmdbuf* cb) {
u64 submit_start = STATS_START();
for (u32 i = 0; i < cb->count; i++) {
pxl8_gfx_cmd* cmd = &cb->commands[i];
switch (cmd->type) {
@ -1322,13 +1312,8 @@ void pxl8_gfx_submit(pxl8_renderer* r, pxl8_gfx_cmdbuf* cb) {
pass_slot* p = &r->passes[SLOT_INDEX(cmd->begin_pass.pass.id)];
if (p->desc.color.load == PXL8_GFX_LOAD_CLEAR) {
pxl8_clear(r, p->desc.color.texture, p->desc.color.clear_value);
}
if (p->desc.depth.load == PXL8_GFX_LOAD_CLEAR) {
pxl8_clear_depth(r, p->desc.depth.texture);
}
if (p->desc.light_accum.load == PXL8_GFX_LOAD_CLEAR) {
pxl8_clear_light(r, p->desc.light_accum.texture);
}
}
break;
case PXL8_GFX_CMD_END_PASS:
@ -1368,7 +1353,6 @@ void pxl8_gfx_submit(pxl8_renderer* r, pxl8_gfx_cmdbuf* cb) {
r->buffers[i].append_pos = 0;
}
}
STATS_ADD(&r->stats, submit_ns, submit_start);
}
void pxl8_clear(pxl8_renderer* r, pxl8_gfx_texture target, u8 color) {
@ -1387,14 +1371,6 @@ void pxl8_clear_depth(pxl8_renderer* r, pxl8_gfx_texture target) {
}
}
void pxl8_clear_light(pxl8_renderer* r, pxl8_gfx_texture target) {
if (!VALID_TEX(r, target)) return;
texture_slot* s = &r->textures[SLOT_INDEX(target.id)];
if (s->format == PXL8_GFX_FORMAT_LIGHT_ACCUM) {
memset(s->data, 0, s->width * s->height * 4);
}
}
void pxl8_draw_pixel(pxl8_renderer* r, pxl8_gfx_texture target, i32 x, i32 y, u8 color) {
if (!VALID_TEX(r, target)) return;
texture_slot* s = &r->textures[SLOT_INDEX(target.id)];
@ -1516,8 +1492,7 @@ void pxl8_draw_circle_fill(pxl8_renderer* r, pxl8_gfx_texture target, i32 cx, i3
}
}
void pxl8_resolve_to_rgba(pxl8_renderer* r, pxl8_gfx_texture color, pxl8_gfx_texture light_accum,
const u32* palette, u32* output) {
void pxl8_resolve_to_rgba(pxl8_renderer* r, pxl8_gfx_texture color, const u32* palette, u32* output) {
if (!VALID_TEX(r, color)) return;
texture_slot* cs = &r->textures[SLOT_INDEX(color.id)];
@ -1525,12 +1500,10 @@ void pxl8_resolve_to_rgba(pxl8_renderer* r, pxl8_gfx_texture color, pxl8_gfx_tex
u32 w = cs->width;
u32 h = cs->height;
u32 total = w * h;
(void)light_accum;
u32 i = 0;
#if defined(PXL8_SIMD_SSE) || defined(PXL8_SIMD_NEON)
pxl8_i32_simd alpha_mask = pxl8_i32_simd_set((i32)0xFF000000);
u32 i = 0;
for (; i + 4 <= total; i += 4) {
pxl8_i32_simd base = pxl8_i32_simd_set4(
(i32)palette[fb[i + 0]], (i32)palette[fb[i + 1]],
@ -1539,12 +1512,10 @@ void pxl8_resolve_to_rgba(pxl8_renderer* r, pxl8_gfx_texture color, pxl8_gfx_tex
base = pxl8_i32_simd_or(base, alpha_mask);
pxl8_i32_simd_store((i32*)&output[i], base);
}
#endif
for (; i < total; i++) {
output[i] = palette[fb[i]] | 0xFF000000;
}
#else
for (u32 i = 0; i < total; i++) {
output[i] = palette[fb[i]] | 0xFF000000;
}
#endif
}

View file

@ -4,10 +4,6 @@
#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
@ -15,25 +11,9 @@ extern "C" {
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);
@ -73,14 +53,12 @@ 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);
@ -90,8 +68,7 @@ void pxl8_draw_rect_fill(pxl8_renderer* r, pxl8_gfx_texture target, i32 x, i32 y
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);
void pxl8_resolve_to_rgba(pxl8_renderer* r, pxl8_gfx_texture color, const u32* palette, u32* output);
#ifdef __cplusplus
}

View file

@ -48,7 +48,6 @@ typedef enum 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 {

View file

@ -43,11 +43,22 @@ static inline u8 pxl8_sample_indexed(const pxl8_shader_bindings* b, pxl8_vec2 uv
}
}
static inline u8 pxl8_colormap_lookup(const pxl8_shader_bindings* b, u8 color, u8 light) {
static inline u8 pxl8_colormap_lookup(const pxl8_shader_bindings* b, u8 color, u8 light_color, u8 intensity) {
if (!b) return color;
const u8* cm = b->colormap;
if (!cm) return color;
u32 row = light >> 5;
u32 row = ((u32)light_color << 3) + (intensity >> 5);
return cm[(row << 8) | (u32)color];
}
static inline u8 pxl8_colormap_lookup_dithered(const pxl8_shader_bindings* b, u8 color, u8 light_color, u8 intensity, u32 x, u32 y) {
if (!b) return color;
const u8* cm = b->colormap;
if (!cm) return color;
u32 base_row = intensity >> 5;
u32 frac = intensity & 31;
u32 threshold = PXL8_BAYER_4X4[(y & 3) * 4 + (x & 3)] * 2;
u32 row = ((u32)light_color << 3) + (frac > threshold && base_row < 7 ? base_row + 1 : base_row);
return cm[(row << 8) | (u32)color];
}

View file

@ -2,14 +2,14 @@
#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);
extern void pxl8_shader_lit(const pxl8_shader_ctx* ctx, const pxl8_shader_bindings* bindings, const pxl8_shader_uniforms* uniforms, u8* colors_out);
extern void pxl8_shader_unlit(const pxl8_shader_ctx* ctx, const pxl8_shader_bindings* bindings, const pxl8_shader_uniforms* uniforms, u8* colors_out);
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;
if (strcmp(name, "lit") == 0) return pxl8_shader_lit;
if (strcmp(name, "unlit") == 0) return pxl8_shader_unlit;
return NULL;
}

View file

@ -32,6 +32,8 @@ void pxl8_shader_lit(
}
pxl8_f32_simd light = ctx->v_light;
f32 max_strength[4] = {0, 0, 0, 0};
u8 dominant_color[4] = {0, 0, 0, 0};
if (uniforms) {
pxl8_f32_simd ambient = pxl8_f32_simd_set((f32)uniforms->ambient / 255.0f);
@ -72,11 +74,32 @@ void pxl8_shader_lit(
);
falloff = pxl8_f32_simd_max(falloff, pxl8_f32_simd_zero());
if (uniforms->dither) {
f32 falloff_arr[4];
pxl8_f32_simd_store(falloff_arr, falloff);
for (u32 j = 0; j < 4; j++) {
if (falloff_arr[j] < 0.5f) {
f32 threshold = (PXL8_BAYER_4X4[((u32)py[j] & 3) * 4 + ((u32)px[j] & 3)] + 0.5f) * (1.0f / 16.0f);
if (falloff_arr[j] < threshold * 0.5f) falloff_arr[j] = 0.0f;
}
}
falloff = pxl8_f32_simd_load(falloff_arr);
}
pxl8_f32_simd strength = pxl8_f32_simd_mul(
pxl8_f32_simd_mul(pxl8_f32_simd_set(l->intensity), falloff),
ndotl
);
f32 strength_arr[4];
pxl8_f32_simd_store(strength_arr, strength);
for (u32 j = 0; j < 4; j++) {
if (strength_arr[j] > max_strength[j]) {
max_strength[j] = strength_arr[j];
dominant_color[j] = l->color;
}
}
light = pxl8_f32_simd_add(light, strength);
}
}
@ -88,13 +111,12 @@ void pxl8_shader_lit(
pxl8_f32_simd_store(light_arr, light_f);
for (u32 i = 0; i < ctx->color_count; i++) {
u8 light_u8;
u8 light_u8 = (u8)light_arr[i];
if (uniforms && uniforms->dither) {
light_u8 = pxl8_gfx_dither(light_arr[i], (u32)px[i], (u32)py[i]);
colors_out[i] = pxl8_colormap_lookup_dithered(bindings, tex_idx[i], dominant_color[i], light_u8, (u32)px[i], (u32)py[i]);
} else {
light_u8 = (u8)light_arr[i];
colors_out[i] = pxl8_colormap_lookup(bindings, tex_idx[i], dominant_color[i], light_u8);
}
colors_out[i] = pxl8_colormap_lookup(bindings, tex_idx[i], light_u8);
}
#else
@ -111,6 +133,8 @@ void pxl8_shader_lit(
}
f32 light = ctx->v_light;
f32 max_strength = 0;
u8 dominant_color = 0;
if (uniforms) {
f32 ambient = (f32)uniforms->ambient / 255.0f;
@ -143,8 +167,16 @@ void pxl8_shader_lit(
f32 falloff = 1.0f - dist_sq * l->inv_radius_sq;
if (falloff <= 0.0f) continue;
if (uniforms->dither && falloff < 0.5f) {
f32 threshold = (PXL8_BAYER_4X4[((u32)ctx->y & 3) * 4 + ((u32)ctx->x & 3)] + 0.5f) * (1.0f / 16.0f);
if (falloff < threshold * 0.5f) continue;
}
f32 strength = l->intensity * falloff * ndotl;
if (strength > max_strength) {
max_strength = strength;
dominant_color = l->color;
}
light += strength;
}
}
@ -155,9 +187,9 @@ void pxl8_shader_lit(
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);
colors_out[0] = pxl8_colormap_lookup_dithered(bindings, tex_idx, dominant_color, light_u8, (u32)ctx->x, (u32)ctx->y);
} else {
colors_out[0] = pxl8_colormap_lookup(bindings, tex_idx, dominant_color, light_u8);
}
colors_out[0] = pxl8_colormap_lookup(bindings, tex_idx, light_u8);
#endif
}

View file

@ -14,16 +14,8 @@ function Lights.new(capacity)
return setmetatable({ _ptr = ptr }, Lights)
end
function Lights:add(x, y, z, r, g, b, intensity, radius)
if r and r > 255 then
local rgb = r
intensity = g
radius = b
r = bit.band(bit.rshift(rgb, 16), 0xFF)
g = bit.band(bit.rshift(rgb, 8), 0xFF)
b = bit.band(rgb, 0xFF)
end
C.pxl8_lights_add(self._ptr, x, y, z, r or 255, g or 255, b or 255, intensity or 255, radius or 10)
function Lights:add(x, y, z, color, intensity, radius)
C.pxl8_lights_add(self._ptr, x, y, z, color or 0, intensity or 255, radius or 10)
end
function Lights:clear()

View file

@ -1,265 +0,0 @@
#pragma once
static inline f32 pxl8_fast_inv_sqrt(f32 x) {
f32 half = 0.5f * x;
i32 i = *(i32*)&x;
i = 0x5f3759df - (i >> 1);
x = *(f32*)&i;
x = x * (1.5f - half * x * x);
return x;
}
static inline u32 pxl8_hash32(u32 x) {
x ^= x >> 16;
x *= 0x85EBCA6Bu;
x ^= x >> 13;
x *= 0xC2B2AE35u;
x ^= x >> 16;
return x;
}
static inline u64 pxl8_hash64(u64 x) {
x ^= x >> 33;
x *= 0xff51afd7ed558ccdULL;
x ^= x >> 33;
x *= 0xc4ceb9fe1a85ec53ULL;
x ^= x >> 33;
return x;
}
static inline f32 pxl8_smoothstep(f32 t) {
return t * t * (3.0f - 2.0f * t);
}
static inline pxl8_vec2 pxl8_vec2_add(pxl8_vec2 a, pxl8_vec2 b) {
return (pxl8_vec2){ .x = a.x + b.x, .y = a.y + b.y };
}
static inline pxl8_vec2 pxl8_vec2_sub(pxl8_vec2 a, pxl8_vec2 b) {
return (pxl8_vec2){ .x = a.x - b.x, .y = a.y - b.y };
}
static inline pxl8_vec2 pxl8_vec2_scale(pxl8_vec2 v, f32 s) {
return (pxl8_vec2){ .x = v.x * s, .y = v.y * s };
}
static inline f32 pxl8_vec2_cross(pxl8_vec2 a, pxl8_vec2 b) {
return a.x * b.y - a.y * b.x;
}
static inline f32 pxl8_vec2_dot(pxl8_vec2 a, pxl8_vec2 b) {
return a.x * b.x + a.y * b.y;
}
static inline f32 pxl8_vec2_length(pxl8_vec2 v) {
return sqrtf(v.x * v.x + v.y * v.y);
}
static inline pxl8_vec2 pxl8_vec2_normalize(pxl8_vec2 v) {
f32 len_sq = pxl8_vec2_dot(v, v);
if (len_sq < 1e-12f) return (pxl8_vec2){0};
return pxl8_vec2_scale(v, pxl8_fast_inv_sqrt(len_sq));
}
static inline pxl8_vec3 pxl8_vec3_add(pxl8_vec3 a, pxl8_vec3 b) {
return (pxl8_vec3){ .x = a.x + b.x, .y = a.y + b.y, .z = a.z + b.z };
}
static inline pxl8_vec3 pxl8_vec3_sub(pxl8_vec3 a, pxl8_vec3 b) {
return (pxl8_vec3){ .x = a.x - b.x, .y = a.y - b.y, .z = a.z - b.z };
}
static inline pxl8_vec3 pxl8_vec3_scale(pxl8_vec3 v, f32 s) {
return (pxl8_vec3){ .x = v.x * s, .y = v.y * s, .z = v.z * s };
}
static inline f32 pxl8_vec3_dot(pxl8_vec3 a, pxl8_vec3 b) {
return a.x * b.x + a.y * b.y + a.z * b.z;
}
static inline pxl8_vec3 pxl8_vec3_cross(pxl8_vec3 a, pxl8_vec3 b) {
return (pxl8_vec3){
.x = a.y * b.z - a.z * b.y,
.y = a.z * b.x - a.x * b.z,
.z = a.x * b.y - a.y * b.x,
};
}
static inline f32 pxl8_vec3_length(pxl8_vec3 v) {
return sqrtf(pxl8_vec3_dot(v, v));
}
static inline pxl8_vec3 pxl8_vec3_lerp(pxl8_vec3 a, pxl8_vec3 b, f32 t) {
return (pxl8_vec3){
a.x + (b.x - a.x) * t,
a.y + (b.y - a.y) * t,
a.z + (b.z - a.z) * t
};
}
static inline pxl8_vec3 pxl8_vec3_normalize(pxl8_vec3 v) {
f32 len_sq = pxl8_vec3_dot(v, v);
if (len_sq < 1e-12f) return (pxl8_vec3){0};
return pxl8_vec3_scale(v, pxl8_fast_inv_sqrt(len_sq));
}
static inline pxl8_mat4 pxl8_mat4_identity(void) {
pxl8_mat4 mat = {0};
mat.m[0] = mat.m[5] = mat.m[10] = mat.m[15] = 1.0f;
return mat;
}
static inline pxl8_mat4 pxl8_mat4_multiply(pxl8_mat4 a, pxl8_mat4 b) {
pxl8_mat4 mat = {0};
for (i32 col = 0; col < 4; col++) {
for (i32 row = 0; row < 4; row++) {
mat.m[col * 4 + row] =
a.m[0 * 4 + row] * b.m[col * 4 + 0] +
a.m[1 * 4 + row] * b.m[col * 4 + 1] +
a.m[2 * 4 + row] * b.m[col * 4 + 2] +
a.m[3 * 4 + row] * b.m[col * 4 + 3];
}
}
return mat;
}
static inline pxl8_vec4 pxl8_mat4_multiply_vec4(pxl8_mat4 m, pxl8_vec4 v) {
return (pxl8_vec4){
.x = m.m[0] * v.x + m.m[4] * v.y + m.m[8] * v.z + m.m[12] * v.w,
.y = m.m[1] * v.x + m.m[5] * v.y + m.m[9] * v.z + m.m[13] * v.w,
.z = m.m[2] * v.x + m.m[6] * v.y + m.m[10] * v.z + m.m[14] * v.w,
.w = m.m[3] * v.x + m.m[7] * v.y + m.m[11] * v.z + m.m[15] * v.w,
};
}
static inline pxl8_vec3 pxl8_mat4_multiply_vec3(pxl8_mat4 m, pxl8_vec3 v) {
return (pxl8_vec3){
.x = m.m[0] * v.x + m.m[4] * v.y + m.m[8] * v.z,
.y = m.m[1] * v.x + m.m[5] * v.y + m.m[9] * v.z,
.z = m.m[2] * v.x + m.m[6] * v.y + m.m[10] * v.z,
};
}
static inline pxl8_mat4 pxl8_mat4_translate(f32 x, f32 y, f32 z) {
pxl8_mat4 mat = pxl8_mat4_identity();
mat.m[12] = x; mat.m[13] = y; mat.m[14] = z;
return mat;
}
static inline pxl8_mat4 pxl8_mat4_rotate_x(f32 angle) {
pxl8_mat4 mat = pxl8_mat4_identity();
f32 c = cosf(angle), s = sinf(angle);
mat.m[5] = c; mat.m[9] = -s; mat.m[6] = s; mat.m[10] = c;
return mat;
}
static inline pxl8_mat4 pxl8_mat4_rotate_y(f32 angle) {
pxl8_mat4 mat = pxl8_mat4_identity();
f32 c = cosf(angle), s = sinf(angle);
mat.m[0] = c; mat.m[8] = s; mat.m[2] = -s; mat.m[10] = c;
return mat;
}
static inline pxl8_mat4 pxl8_mat4_rotate_z(f32 angle) {
pxl8_mat4 mat = pxl8_mat4_identity();
f32 c = cosf(angle), s = sinf(angle);
mat.m[0] = c; mat.m[4] = -s; mat.m[1] = s; mat.m[5] = c;
return mat;
}
static inline pxl8_mat4 pxl8_mat4_scale(f32 x, f32 y, f32 z) {
pxl8_mat4 mat = pxl8_mat4_identity();
mat.m[0] = x; mat.m[5] = y; mat.m[10] = z;
return mat;
}
static inline pxl8_mat4 pxl8_mat4_orthographic(f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far) {
pxl8_mat4 mat = {0};
mat.m[0] = 2.0f / (right - left);
mat.m[5] = 2.0f / (top - bottom);
mat.m[10] = -2.0f / (far - near);
mat.m[12] = -(right + left) / (right - left);
mat.m[13] = -(top + bottom) / (top - bottom);
mat.m[14] = -(far + near) / (far - near);
mat.m[15] = 1.0f;
return mat;
}
static inline pxl8_mat4 pxl8_mat4_perspective(f32 fov, f32 aspect, f32 near, f32 far) {
pxl8_mat4 mat = {0};
f32 tan_half_fov = tanf(fov / 2.0f);
mat.m[0] = 1.0f / (aspect * tan_half_fov);
mat.m[5] = 1.0f / tan_half_fov;
mat.m[10] = -(far + near) / (far - near);
mat.m[14] = -(2.0f * far * near) / (far - near);
mat.m[11] = -1.0f;
return mat;
}
static inline pxl8_mat4 pxl8_mat4_lookat(pxl8_vec3 eye, pxl8_vec3 center, pxl8_vec3 up) {
pxl8_mat4 mat = pxl8_mat4_identity();
pxl8_vec3 f = pxl8_vec3_normalize(pxl8_vec3_sub(center, eye));
pxl8_vec3 s = pxl8_vec3_normalize(pxl8_vec3_cross(f, up));
pxl8_vec3 u = pxl8_vec3_cross(s, f);
mat.m[0] = s.x; mat.m[4] = s.y; mat.m[8] = s.z;
mat.m[1] = u.x; mat.m[5] = u.y; mat.m[9] = u.z;
mat.m[2] = -f.x; mat.m[6] = -f.y; mat.m[10] = -f.z;
mat.m[12] = -pxl8_vec3_dot(s, eye);
mat.m[13] = -pxl8_vec3_dot(u, eye);
mat.m[14] = pxl8_vec3_dot(f, eye);
return mat;
}
static inline pxl8_frustum pxl8_frustum_from_matrix(pxl8_mat4 vp) {
pxl8_frustum frustum;
const f32* m = vp.m;
frustum.planes[0].normal.x = m[3] + m[0];
frustum.planes[0].normal.y = m[7] + m[4];
frustum.planes[0].normal.z = m[11] + m[8];
frustum.planes[0].distance = m[15] + m[12];
frustum.planes[1].normal.x = m[3] - m[0];
frustum.planes[1].normal.y = m[7] - m[4];
frustum.planes[1].normal.z = m[11] - m[8];
frustum.planes[1].distance = m[15] - m[12];
frustum.planes[2].normal.x = m[3] + m[1];
frustum.planes[2].normal.y = m[7] + m[5];
frustum.planes[2].normal.z = m[11] + m[9];
frustum.planes[2].distance = m[15] + m[13];
frustum.planes[3].normal.x = m[3] - m[1];
frustum.planes[3].normal.y = m[7] - m[5];
frustum.planes[3].normal.z = m[11] - m[9];
frustum.planes[3].distance = m[15] - m[13];
frustum.planes[4].normal.x = m[3] + m[2];
frustum.planes[4].normal.y = m[7] + m[6];
frustum.planes[4].normal.z = m[11] + m[10];
frustum.planes[4].distance = m[15] + m[14];
frustum.planes[5].normal.x = m[3] - m[2];
frustum.planes[5].normal.y = m[7] - m[6];
frustum.planes[5].normal.z = m[11] - m[10];
frustum.planes[5].distance = m[15] - m[14];
for (i32 i = 0; i < 6; i++) {
f32 len = pxl8_vec3_length(frustum.planes[i].normal);
if (len > 1e-6f) {
f32 inv_len = 1.0f / len;
frustum.planes[i].normal = pxl8_vec3_scale(frustum.planes[i].normal, inv_len);
frustum.planes[i].distance *= inv_len;
}
}
return frustum;
}
static inline bool pxl8_frustum_test_aabb(const pxl8_frustum* frustum, pxl8_vec3 min, pxl8_vec3 max) {
const f32 FRUSTUM_EPSILON = -75.0f;
for (i32 i = 0; i < 6; i++) {
pxl8_vec3 normal = frustum->planes[i].normal;
f32 d = frustum->planes[i].distance;
pxl8_vec3 p_vertex = {
(normal.x > 0.0f) ? max.x : min.x,
(normal.y > 0.0f) ? max.y : min.y,
(normal.z > 0.0f) ? max.z : min.z
};
f32 p_dist = pxl8_vec3_dot(normal, p_vertex) + d;
if (p_dist < FRUSTUM_EPSILON) return false;
}
return true;
}

View file

@ -205,18 +205,17 @@ static const char* pxl8_ffi_cdefs =
"typedef struct { float m[16]; } pxl8_mat4;\n"
"\n"
"typedef struct pxl8_light {\n"
" pxl8_vec3 position;\n"
" u8 color;\n"
" f32 intensity;\n"
" f32 inv_radius_sq;\n"
" u8 r, g, b;\n"
" u8 intensity;\n"
" f32 radius;\n"
" pxl8_vec3 position;\n"
" f32 radius_sq;\n"
"} pxl8_light;\n"
"\n"
"typedef struct pxl8_lights pxl8_lights;\n"
"pxl8_lights* pxl8_lights_create(u32 capacity);\n"
"void pxl8_lights_destroy(pxl8_lights* lights);\n"
"void pxl8_lights_add(pxl8_lights* lights, f32 x, f32 y, f32 z, u8 r, u8 g, u8 b, u8 intensity, f32 radius);\n"
"void pxl8_lights_add(pxl8_lights* lights, f32 x, f32 y, f32 z, u8 color, u8 intensity, f32 radius);\n"
"void pxl8_lights_clear(pxl8_lights* lights);\n"
"u32 pxl8_lights_count(const pxl8_lights* lights);\n"
"const pxl8_light* pxl8_lights_data(const pxl8_lights* lights);\n"