add more checks in triangle raster code

This commit is contained in:
asrael 2025-11-10 09:39:33 -06:00
parent 79a678f162
commit 9550d34e65
No known key found for this signature in database
GPG key ID: 2786557804DFAE24
7 changed files with 357 additions and 260 deletions

View file

@ -389,6 +389,7 @@ case "$COMMAND" in
src/pxl8_gfx.c src/pxl8_gfx.c
src/pxl8_io.c src/pxl8_io.c
src/pxl8_math.c src/pxl8_math.c
src/pxl8_procgen.c
src/pxl8_script.c src/pxl8_script.c
src/pxl8_sdl3.c src/pxl8_sdl3.c
src/pxl8_tilemap.c src/pxl8_tilemap.c

View file

@ -187,6 +187,9 @@ pxl8_result pxl8_bsp_load(const char* path, pxl8_bsp* bsp) {
bsp->faces[i].styles[2] = pxl8_read_u8(&stream); bsp->faces[i].styles[2] = pxl8_read_u8(&stream);
bsp->faces[i].styles[3] = pxl8_read_u8(&stream); bsp->faces[i].styles[3] = pxl8_read_u8(&stream);
bsp->faces[i].lightmap_offset = pxl8_read_u32(&stream); bsp->faces[i].lightmap_offset = pxl8_read_u32(&stream);
bsp->faces[i].aabb_min = (pxl8_vec3){1e30f, 1e30f, 1e30f};
bsp->faces[i].aabb_max = (pxl8_vec3){-1e30f, -1e30f, -1e30f};
} }
} }
@ -270,6 +273,43 @@ pxl8_result pxl8_bsp_load(const char* path, pxl8_bsp* bsp) {
free(file_data); free(file_data);
for (u32 i = 0; i < bsp->num_faces; i++) {
pxl8_bsp_face* face = &bsp->faces[i];
f32 min_x = 1e30f, min_y = 1e30f, min_z = 1e30f;
f32 max_x = -1e30f, max_y = -1e30f, max_z = -1e30f;
for (u32 j = 0; j < face->num_edges; j++) {
i32 surfedge_idx = face->first_edge + j;
if (surfedge_idx >= (i32)bsp->num_surfedges) continue;
i32 edge_idx = bsp->surfedges[surfedge_idx];
u32 vert_idx;
if (edge_idx >= 0) {
if ((u32)edge_idx >= bsp->num_edges) continue;
vert_idx = bsp->edges[edge_idx].vertex[0];
} else {
edge_idx = -edge_idx;
if ((u32)edge_idx >= bsp->num_edges) continue;
vert_idx = bsp->edges[edge_idx].vertex[1];
}
if (vert_idx >= bsp->num_vertices) continue;
pxl8_vec3 v = bsp->vertices[vert_idx].position;
if (v.x < min_x) min_x = v.x;
if (v.x > max_x) max_x = v.x;
if (v.y < min_y) min_y = v.y;
if (v.y > max_y) max_y = v.y;
if (v.z < min_z) min_z = v.z;
if (v.z > max_z) max_z = v.z;
}
face->aabb_min = (pxl8_vec3){min_x, min_y, min_z};
face->aabb_max = (pxl8_vec3){max_x, max_y, max_z};
}
pxl8_debug("Loaded BSP: %u verts, %u faces, %u nodes, %u leafs", pxl8_debug("Loaded BSP: %u verts, %u faces, %u nodes, %u leafs",
bsp->num_vertices, bsp->num_faces, bsp->num_nodes, bsp->num_leafs); bsp->num_vertices, bsp->num_faces, bsp->num_nodes, bsp->num_leafs);
@ -334,42 +374,7 @@ bool pxl8_bsp_is_leaf_visible(const pxl8_bsp* bsp, i32 leaf_from, i32 leaf_to) {
static inline bool face_in_frustum(const pxl8_bsp* bsp, u32 face_id, const pxl8_frustum* frustum) { static inline bool face_in_frustum(const pxl8_bsp* bsp, u32 face_id, const pxl8_frustum* frustum) {
const pxl8_bsp_face* face = &bsp->faces[face_id]; const pxl8_bsp_face* face = &bsp->faces[face_id];
return pxl8_frustum_test_aabb(frustum, face->aabb_min, face->aabb_max);
f32 min_x = 1e30f, min_y = 1e30f, min_z = 1e30f;
f32 max_x = -1e30f, max_y = -1e30f, max_z = -1e30f;
for (u32 i = 0; i < face->num_edges; i++) {
i32 surfedge_idx = face->first_edge + i;
if (surfedge_idx >= (i32)bsp->num_surfedges) continue;
i32 edge_idx = bsp->surfedges[surfedge_idx];
u32 vert_idx;
if (edge_idx >= 0) {
if ((u32)edge_idx >= bsp->num_edges) continue;
vert_idx = bsp->edges[edge_idx].vertex[0];
} else {
edge_idx = -edge_idx;
if ((u32)edge_idx >= bsp->num_edges) continue;
vert_idx = bsp->edges[edge_idx].vertex[1];
}
if (vert_idx >= bsp->num_vertices) continue;
pxl8_vec3 v = bsp->vertices[vert_idx].position;
if (v.x < min_x) min_x = v.x;
if (v.x > max_x) max_x = v.x;
if (v.y < min_y) min_y = v.y;
if (v.y > max_y) max_y = v.y;
if (v.z < min_z) min_z = v.z;
if (v.z > max_z) max_z = v.z;
}
pxl8_vec3 aabb_min = {min_x, min_y, min_z};
pxl8_vec3 aabb_max = {max_x, max_y, max_z};
return pxl8_frustum_test_aabb(frustum, aabb_min, aabb_max);
} }
void pxl8_bsp_render_face(pxl8_gfx* gfx, const pxl8_bsp* bsp, u32 face_id, u32 texture_id) { void pxl8_bsp_render_face(pxl8_gfx* gfx, const pxl8_bsp* bsp, u32 face_id, u32 texture_id) {

View file

@ -37,6 +37,9 @@ typedef struct pxl8_bsp_face {
u8 styles[4]; u8 styles[4];
u16 texinfo_id; u16 texinfo_id;
pxl8_vec3 aabb_min;
pxl8_vec3 aabb_max;
} pxl8_bsp_face; } pxl8_bsp_face;
typedef struct pxl8_bsp_node { typedef struct pxl8_bsp_node {

View file

@ -416,7 +416,7 @@ void pxl8_clr(pxl8_gfx* gfx, u32 color) {
if (frame_count % 60 == 0) { if (frame_count % 60 == 0) {
i32 fb_pixels = gfx->framebuffer_width * gfx->framebuffer_height; i32 fb_pixels = gfx->framebuffer_width * gfx->framebuffer_height;
f32 overdraw = (f32)gfx->frame_pixel_count / (f32)fb_pixels; f32 overdraw = (f32)gfx->frame_pixel_count / (f32)fb_pixels;
pxl8_debug("Frame triangles: %u, pixels: %u, overdraw: %.2fx", pxl8_trace("Frame triangles: %u, pixels: %u, overdraw: %.2fx",
gfx->frame_triangle_count, gfx->frame_pixel_count, overdraw); gfx->frame_triangle_count, gfx->frame_pixel_count, overdraw);
} }
frame_count++; frame_count++;
@ -1033,7 +1033,7 @@ static inline u32 pxl8_sample_texture(pxl8_gfx* gfx, u32 texture_id, f32 u, f32
} }
} }
static inline void pxl8_fill_scanline_textured( static inline void pxl8_fill_scanline_textured_hicolor(
pxl8_gfx* gfx, i32 y, i32 xs, i32 xe, pxl8_gfx* gfx, i32 y, i32 xs, i32 xe,
f32 z0, f32 z1, f32 z0, f32 z1,
f32 u0, f32 v0, f32 w0, f32 u0, f32 v0, f32 w0,
@ -1062,8 +1062,7 @@ static inline void pxl8_fill_scanline_textured(
v /= w0; v /= w0;
} }
u32 color = pxl8_sample_texture(gfx, texture_id, u, v); u32 color = pxl8_sample_texture(gfx, texture_id, u, v);
if ((gfx->color_mode == PXL8_COLOR_MODE_HICOLOR && (color & 0xFF000000)) || if (color & 0xFF000000) {
(gfx->color_mode != PXL8_COLOR_MODE_HICOLOR && color != 0)) {
gfx->zbuffer[idx] = z0; gfx->zbuffer[idx] = z0;
pxl8_pixel(gfx, xs, y, color); pxl8_pixel(gfx, xs, y, color);
} }
@ -1083,122 +1082,147 @@ static inline void pxl8_fill_scanline_textured(
f32 v = v0; f32 v = v0;
f32 w = w0; f32 w = w0;
if (gfx->affine_textures) { for (i32 x = xs; x <= xe; x++) {
for (i32 x = xs; x <= xe; x++) { if (x >= 0 && x < gfx->framebuffer_width) {
if (x >= 0 && x < gfx->framebuffer_width) { i32 idx = y * gfx->zbuffer_width + x;
i32 idx = y * gfx->zbuffer_width + x; if (z <= gfx->zbuffer[idx]) {
if (z <= gfx->zbuffer[idx]) { gfx->frame_pixel_count++;
gfx->frame_pixel_count++; f32 tex_u = u, tex_v = v;
u32 color = pxl8_sample_texture(gfx, texture_id, u, v); if (!gfx->affine_textures && fabsf(w) > 1e-6f) {
if ((gfx->color_mode == PXL8_COLOR_MODE_HICOLOR && (color & 0xFF000000)) || tex_u /= w;
(gfx->color_mode != PXL8_COLOR_MODE_HICOLOR && color != 0)) { tex_v /= w;
gfx->zbuffer[idx] = z; }
pxl8_pixel(gfx, x, y, color); u32 color = pxl8_sample_texture(gfx, texture_id, tex_u, tex_v);
} if (color & 0xFF000000) {
gfx->zbuffer[idx] = z;
pxl8_pixel(gfx, x, y, color);
} }
} }
z += dz;
u += du;
v += dv;
} }
} else { z += dz;
i32 x = xs; u += du;
while (x <= xe) { v += dv;
f32 w_inv = (fabsf(w) > 1e-6f) ? (1.0f / w) : 0.0f; w += dw;
f32 u_corrected = u * w_inv; }
f32 v_corrected = v * w_inv; }
i32 span_end = (x + 16 <= xe) ? (x + 16) : (xe + 1); static inline void pxl8_fill_scanline_textured_indexed(
i32 span_len = span_end - x; pxl8_gfx* gfx, i32 y, i32 xs, i32 xe,
f32 z0, f32 z1,
f32 u0, f32 v0, f32 w0,
f32 u1, f32 v1, f32 w1,
u32 texture_id
) {
if (y < 0 || y >= gfx->framebuffer_height) return;
if (xs > xe) {
i32 tmp = xs; xs = xe; xe = tmp;
f32 tmpf;
tmpf = z0; z0 = z1; z1 = tmpf;
tmpf = u0; u0 = u1; u1 = tmpf;
tmpf = v0; v0 = v1; v1 = tmpf;
tmpf = w0; w0 = w1; w1 = tmpf;
}
f32 next_u, next_v, affine_du, affine_dv; i32 span = xe - xs;
if (span_len > 1) { if (span <= 0) {
f32 next_w = w + span_len * dw; if (xs >= 0 && xs < gfx->framebuffer_width) {
f32 next_w_inv = (fabsf(next_w) > 1e-6f) ? (1.0f / next_w) : 0.0f; i32 idx = y * gfx->zbuffer_width + xs;
next_u = (u + span_len * du) * next_w_inv; if (z0 <= gfx->zbuffer[idx]) {
next_v = (v + span_len * dv) * next_w_inv; gfx->frame_pixel_count++;
f32 inv_span = 1.0f / (f32)span_len; f32 u = u0, v = v0;
affine_du = (next_u - u_corrected) * inv_span; if (!gfx->affine_textures && fabsf(w0) > 1e-6f) {
affine_dv = (next_v - v_corrected) * inv_span; u /= w0;
} else { v /= w0;
affine_du = 0; }
affine_dv = 0; u32 color = pxl8_sample_texture(gfx, texture_id, u, v);
} if (color != 0) {
gfx->zbuffer[idx] = z0;
f32 affine_u = u_corrected; pxl8_pixel(gfx, xs, y, color);
f32 affine_v = v_corrected;
for (; x < span_end; x++) {
if (x >= 0 && x < gfx->framebuffer_width) {
i32 idx = y * gfx->zbuffer_width + x;
if (z <= gfx->zbuffer[idx]) {
gfx->frame_pixel_count++;
u32 color = pxl8_sample_texture(gfx, texture_id, affine_u, affine_v);
if ((gfx->color_mode == PXL8_COLOR_MODE_HICOLOR && (color & 0xFF000000)) ||
(gfx->color_mode != PXL8_COLOR_MODE_HICOLOR && color != 0)) {
gfx->zbuffer[idx] = z;
pxl8_pixel(gfx, x, y, color);
}
}
} }
z += dz;
u += du;
v += dv;
w += dw;
affine_u += affine_du;
affine_v += affine_dv;
} }
} }
return;
}
f32 inv_span = 1.0f / (f32)span;
f32 dz = (z1 - z0) * inv_span;
f32 du = (u1 - u0) * inv_span;
f32 dv = (v1 - v0) * inv_span;
f32 dw = (w1 - w0) * inv_span;
f32 z = z0;
f32 u = u0;
f32 v = v0;
f32 w = w0;
for (i32 x = xs; x <= xe; x++) {
if (x >= 0 && x < gfx->framebuffer_width) {
i32 idx = y * gfx->zbuffer_width + x;
if (z <= gfx->zbuffer[idx]) {
gfx->frame_pixel_count++;
f32 tex_u = u, tex_v = v;
if (!gfx->affine_textures && fabsf(w) > 1e-6f) {
tex_u /= w;
tex_v /= w;
}
u32 color = pxl8_sample_texture(gfx, texture_id, tex_u, tex_v);
if (color != 0) {
gfx->zbuffer[idx] = z;
pxl8_pixel(gfx, x, y, color);
}
}
}
z += dz;
u += du;
v += dv;
w += dw;
} }
} }
typedef void (*pxl8_scanline_func)(pxl8_gfx*, i32, i32, i32, f32, f32, u32); typedef void (*pxl8_scanline_func)(pxl8_gfx*, i32, i32, i32, f32, f32, u32);
typedef void (*pxl8_scanline_textured_func)(pxl8_gfx*, i32, i32, i32, f32, f32, f32, f32, f32, f32, f32, f32, u32);
static void pxl8_draw_flat_bottom_triangle( static void pxl8_scan_triangle(
pxl8_gfx* gfx, pxl8_gfx* gfx,
i32 x0, i32 y0, f32 z0, i32 x0, i32 y0, f32 z0,
i32 x1, i32 y1, f32 z1, i32 x1, i32 y1, f32 z1,
i32 x2, i32 y2, f32 z2, i32 x2, i32 y2, f32 z2,
u32 color, u32 color,
pxl8_scanline_func fill_scanline pxl8_scanline_func fill_scanline,
bool is_bottom
) { ) {
(void)z2; (void)z2;
if (y1 == y0) return;
f32 inv_slope_1 = (f32)(x1 - x0) / (f32)(y1 - y0); i32 y_start, y_end, y_step;
f32 inv_slope_2 = (f32)(x2 - x0) / (f32)(y2 - y0); f32 x_start1, x_start2, inv_slope_1, inv_slope_2;
f32 cur_x1 = (f32)x0; if (is_bottom) {
f32 cur_x2 = (f32)x0; if (y1 == y0 || y1 < y0 || y1 - y0 > gfx->framebuffer_height) return;
y_start = y0;
for (i32 y = y0; y <= y1; y++) { y_end = y1;
fill_scanline(gfx, y, (i32)cur_x1, (i32)cur_x2, z0, z1, color); y_step = 1;
cur_x1 += inv_slope_1; x_start1 = x_start2 = (f32)x0;
cur_x2 += inv_slope_2; inv_slope_1 = (f32)(x1 - x0) / (f32)(y1 - y0);
inv_slope_2 = (f32)(x2 - x0) / (f32)(y2 - y0);
} else {
if (y2 == y0 || y2 < y0 || y2 - y0 > gfx->framebuffer_height) return;
y_start = y2;
y_end = y0;
y_step = -1;
x_start1 = x_start2 = (f32)x2;
inv_slope_1 = (f32)(x2 - x0) / (f32)(y2 - y0);
inv_slope_2 = (f32)(x2 - x1) / (f32)(y2 - y1);
} }
}
static void pxl8_draw_flat_top_triangle( f32 cur_x1 = x_start1;
pxl8_gfx* gfx, f32 cur_x2 = x_start2;
i32 x0, i32 y0, f32 z0, f32 slope_step1 = inv_slope_1 * y_step;
i32 x1, i32 y1, f32 z1, f32 slope_step2 = inv_slope_2 * y_step;
i32 x2, i32 y2, f32 z2,
u32 color,
pxl8_scanline_func fill_scanline
) {
(void)z2;
if (y2 == y0) return;
f32 inv_slope_1 = (f32)(x2 - x0) / (f32)(y2 - y0); for (i32 y = y_start; y != y_end + y_step; y += y_step) {
f32 inv_slope_2 = (f32)(x2 - x1) / (f32)(y2 - y1);
f32 cur_x1 = (f32)x2;
f32 cur_x2 = (f32)x2;
for (i32 y = y2; y > y0; y--) {
fill_scanline(gfx, y, (i32)cur_x1, (i32)cur_x2, z0, z1, color); fill_scanline(gfx, y, (i32)cur_x1, (i32)cur_x2, z0, z1, color);
cur_x1 -= inv_slope_1; cur_x1 += slope_step1;
cur_x2 -= inv_slope_2; cur_x2 += slope_step2;
} }
} }
@ -1266,16 +1290,16 @@ void pxl8_3d_draw_triangle(pxl8_gfx* gfx, pxl8_triangle tri) {
: pxl8_fill_scanline_indexed; : pxl8_fill_scanline_indexed;
if (y1 == y2) { if (y1 == y2) {
pxl8_draw_flat_bottom_triangle(gfx, x0, y0, z0, x1, y1, z1, x2, y2, z2, color, fill_scanline); pxl8_scan_triangle(gfx, x0, y0, z0, x1, y1, z1, x2, y2, z2, color, fill_scanline, true);
} else if (y0 == y1) { } else if (y0 == y1) {
pxl8_draw_flat_top_triangle(gfx, x0, y0, z0, x1, y1, z1, x2, y2, z2, color, fill_scanline); pxl8_scan_triangle(gfx, x0, y0, z0, x1, y1, z1, x2, y2, z2, color, fill_scanline, false);
} else { } else {
i32 x3 = x0 + (i32)(((f32)(y1 - y0) / (f32)(y2 - y0)) * (x2 - x0)); i32 x3 = x0 + (i32)(((f32)(y1 - y0) / (f32)(y2 - y0)) * (x2 - x0));
i32 y3 = y1; i32 y3 = y1;
f32 z3 = z0 + ((f32)(y1 - y0) / (f32)(y2 - y0)) * (z2 - z0); f32 z3 = z0 + ((f32)(y1 - y0) / (f32)(y2 - y0)) * (z2 - z0);
pxl8_draw_flat_bottom_triangle(gfx, x0, y0, z0, x1, y1, z1, x3, y3, z3, color, fill_scanline); pxl8_scan_triangle(gfx, x0, y0, z0, x1, y1, z1, x3, y3, z3, color, fill_scanline, true);
pxl8_draw_flat_top_triangle(gfx, x1, y1, z1, x3, y3, z3, x2, y2, z2, color, fill_scanline); pxl8_scan_triangle(gfx, x1, y1, z1, x3, y3, z3, x2, y2, z2, color, fill_scanline, false);
} }
} }
@ -1284,77 +1308,88 @@ typedef struct pxl8_textured_vertex {
f32 z, u, v, w; f32 z, u, v, w;
} pxl8_textured_vertex; } pxl8_textured_vertex;
static void pxl8_draw_flat_bottom_triangle_textured( static void pxl8_scan_triangle_textured(
pxl8_gfx* gfx, pxl8_gfx* gfx,
pxl8_textured_vertex v0, pxl8_textured_vertex v0,
pxl8_textured_vertex v1, pxl8_textured_vertex v1,
pxl8_textured_vertex v2, pxl8_textured_vertex v2,
u32 texture_id u32 texture_id,
pxl8_scanline_textured_func fill_scanline,
bool is_bottom
) { ) {
if (v1.y == v0.y) return; i32 y_start, y_end, y_step;
f32 x_start1, x_start2;
f32 z_start1, z_start2, u_start1, u_start2, v_start1, v_start2, w_start1, w_start2;
f32 inv_slope_1, inv_slope_2, inv_slope_z1, inv_slope_z2;
f32 inv_slope_u1, inv_slope_u2, inv_slope_v1, inv_slope_v2, inv_slope_w1, inv_slope_w2;
f32 inv_slope_1 = (f32)(v1.x - v0.x) / (f32)(v1.y - v0.y); if (is_bottom) {
f32 inv_slope_2 = (f32)(v2.x - v0.x) / (f32)(v2.y - v0.y); if (v1.y == v0.y || v1.y < v0.y || v1.y - v0.y > gfx->framebuffer_height) return;
f32 inv_slope_z1 = (v1.z - v0.z) / (f32)(v1.y - v0.y); y_start = v0.y;
f32 inv_slope_z2 = (v2.z - v0.z) / (f32)(v2.y - v0.y); y_end = v1.y;
f32 inv_slope_u1 = (v1.u - v0.u) / (f32)(v1.y - v0.y); y_step = 1;
f32 inv_slope_u2 = (v2.u - v0.u) / (f32)(v2.y - v0.y); x_start1 = x_start2 = (f32)v0.x;
f32 inv_slope_v1 = (v1.v - v0.v) / (f32)(v1.y - v0.y); z_start1 = z_start2 = v0.z;
f32 inv_slope_v2 = (v2.v - v0.v) / (f32)(v2.y - v0.y); u_start1 = u_start2 = v0.u;
f32 inv_slope_w1 = (v1.w - v0.w) / (f32)(v1.y - v0.y); v_start1 = v_start2 = v0.v;
f32 inv_slope_w2 = (v2.w - v0.w) / (f32)(v2.y - v0.y); w_start1 = w_start2 = v0.w;
inv_slope_1 = (f32)(v1.x - v0.x) / (f32)(v1.y - v0.y);
f32 cur_x1 = (f32)v0.x, cur_x2 = (f32)v0.x; inv_slope_2 = (f32)(v2.x - v0.x) / (f32)(v2.y - v0.y);
f32 cur_z1 = v0.z, cur_z2 = v0.z; inv_slope_z1 = (v1.z - v0.z) / (f32)(v1.y - v0.y);
f32 cur_u1 = v0.u, cur_u2 = v0.u; inv_slope_z2 = (v2.z - v0.z) / (f32)(v2.y - v0.y);
f32 cur_v1 = v0.v, cur_v2 = v0.v; inv_slope_u1 = (v1.u - v0.u) / (f32)(v1.y - v0.y);
f32 cur_w1 = v0.w, cur_w2 = v0.w; inv_slope_u2 = (v2.u - v0.u) / (f32)(v2.y - v0.y);
inv_slope_v1 = (v1.v - v0.v) / (f32)(v1.y - v0.y);
for (i32 y = v0.y; y <= v1.y; y++) { inv_slope_v2 = (v2.v - v0.v) / (f32)(v2.y - v0.y);
pxl8_fill_scanline_textured(gfx, y, (i32)cur_x1, (i32)cur_x2, inv_slope_w1 = (v1.w - v0.w) / (f32)(v1.y - v0.y);
cur_z1, cur_z2, cur_u1, cur_v1, cur_w1, cur_u2, cur_v2, cur_w2, texture_id); inv_slope_w2 = (v2.w - v0.w) / (f32)(v2.y - v0.y);
cur_x1 += inv_slope_1; cur_x2 += inv_slope_2; } else {
cur_z1 += inv_slope_z1; cur_z2 += inv_slope_z2; if (v2.y == v0.y || v2.y < v0.y || v2.y - v0.y > gfx->framebuffer_height) return;
cur_u1 += inv_slope_u1; cur_u2 += inv_slope_u2; y_start = v2.y;
cur_v1 += inv_slope_v1; cur_v2 += inv_slope_v2; y_end = v0.y;
cur_w1 += inv_slope_w1; cur_w2 += inv_slope_w2; y_step = -1;
x_start1 = x_start2 = (f32)v2.x;
z_start1 = z_start2 = v2.z;
u_start1 = u_start2 = v2.u;
v_start1 = v_start2 = v2.v;
w_start1 = w_start2 = v2.w;
inv_slope_1 = (f32)(v2.x - v0.x) / (f32)(v2.y - v0.y);
inv_slope_2 = (f32)(v2.x - v1.x) / (f32)(v2.y - v1.y);
inv_slope_z1 = (v2.z - v0.z) / (f32)(v2.y - v0.y);
inv_slope_z2 = (v2.z - v1.z) / (f32)(v2.y - v1.y);
inv_slope_u1 = (v2.u - v0.u) / (f32)(v2.y - v0.y);
inv_slope_u2 = (v2.u - v1.u) / (f32)(v2.y - v1.y);
inv_slope_v1 = (v2.v - v0.v) / (f32)(v2.y - v0.y);
inv_slope_v2 = (v2.v - v1.v) / (f32)(v2.y - v1.y);
inv_slope_w1 = (v2.w - v0.w) / (f32)(v2.y - v0.y);
inv_slope_w2 = (v2.w - v1.w) / (f32)(v2.y - v1.y);
} }
}
static void pxl8_draw_flat_top_triangle_textured( f32 cur_x1 = x_start1, cur_x2 = x_start2;
pxl8_gfx* gfx, f32 cur_z1 = z_start1, cur_z2 = z_start2;
pxl8_textured_vertex v0, f32 cur_u1 = u_start1, cur_u2 = u_start2;
pxl8_textured_vertex v1, f32 cur_v1 = v_start1, cur_v2 = v_start2;
pxl8_textured_vertex v2, f32 cur_w1 = w_start1, cur_w2 = w_start2;
u32 texture_id
) {
if (v2.y == v0.y) return;
f32 inv_slope_1 = (f32)(v2.x - v0.x) / (f32)(v2.y - v0.y); f32 slope_step_x1 = inv_slope_1 * y_step;
f32 inv_slope_2 = (f32)(v2.x - v1.x) / (f32)(v2.y - v1.y); f32 slope_step_x2 = inv_slope_2 * y_step;
f32 inv_slope_z1 = (v2.z - v0.z) / (f32)(v2.y - v0.y); f32 slope_step_z1 = inv_slope_z1 * y_step;
f32 inv_slope_z2 = (v2.z - v1.z) / (f32)(v2.y - v1.y); f32 slope_step_z2 = inv_slope_z2 * y_step;
f32 inv_slope_u1 = (v2.u - v0.u) / (f32)(v2.y - v0.y); f32 slope_step_u1 = inv_slope_u1 * y_step;
f32 inv_slope_u2 = (v2.u - v1.u) / (f32)(v2.y - v1.y); f32 slope_step_u2 = inv_slope_u2 * y_step;
f32 inv_slope_v1 = (v2.v - v0.v) / (f32)(v2.y - v0.y); f32 slope_step_v1 = inv_slope_v1 * y_step;
f32 inv_slope_v2 = (v2.v - v1.v) / (f32)(v2.y - v1.y); f32 slope_step_v2 = inv_slope_v2 * y_step;
f32 inv_slope_w1 = (v2.w - v0.w) / (f32)(v2.y - v0.y); f32 slope_step_w1 = inv_slope_w1 * y_step;
f32 inv_slope_w2 = (v2.w - v1.w) / (f32)(v2.y - v1.y); f32 slope_step_w2 = inv_slope_w2 * y_step;
f32 cur_x1 = (f32)v2.x, cur_x2 = (f32)v2.x; for (i32 y = y_start; y != y_end + y_step; y += y_step) {
f32 cur_z1 = v2.z, cur_z2 = v2.z; fill_scanline(gfx, y, (i32)cur_x1, (i32)cur_x2,
f32 cur_u1 = v2.u, cur_u2 = v2.u;
f32 cur_v1 = v2.v, cur_v2 = v2.v;
f32 cur_w1 = v2.w, cur_w2 = v2.w;
for (i32 y = v2.y; y > v0.y; y--) {
pxl8_fill_scanline_textured(gfx, y, (i32)cur_x1, (i32)cur_x2,
cur_z1, cur_z2, cur_u1, cur_v1, cur_w1, cur_u2, cur_v2, cur_w2, texture_id); cur_z1, cur_z2, cur_u1, cur_v1, cur_w1, cur_u2, cur_v2, cur_w2, texture_id);
cur_x1 -= inv_slope_1; cur_x2 -= inv_slope_2; cur_x1 += slope_step_x1; cur_x2 += slope_step_x2;
cur_z1 -= inv_slope_z1; cur_z2 -= inv_slope_z2; cur_z1 += slope_step_z1; cur_z2 += slope_step_z2;
cur_u1 -= inv_slope_u1; cur_u2 -= inv_slope_u2; cur_u1 += slope_step_u1; cur_u2 += slope_step_u2;
cur_v1 -= inv_slope_v1; cur_v2 -= inv_slope_v2; cur_v1 += slope_step_v1; cur_v2 += slope_step_v2;
cur_w1 -= inv_slope_w1; cur_w2 -= inv_slope_w2; cur_w1 += slope_step_w1; cur_w2 += slope_step_w2;
} }
} }
@ -1415,10 +1450,14 @@ void pxl8_3d_draw_triangle_textured(
pxl8_textured_vertex tmp = tv[1]; tv[1] = tv[2]; tv[2] = tmp; pxl8_textured_vertex tmp = tv[1]; tv[1] = tv[2]; tv[2] = tmp;
} }
pxl8_scanline_textured_func fill_scanline = (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR)
? pxl8_fill_scanline_textured_hicolor
: pxl8_fill_scanline_textured_indexed;
if (tv[1].y == tv[2].y) { if (tv[1].y == tv[2].y) {
pxl8_draw_flat_bottom_triangle_textured(gfx, tv[0], tv[1], tv[2], texture_id); pxl8_scan_triangle_textured(gfx, tv[0], tv[1], tv[2], texture_id, fill_scanline, true);
} else if (tv[0].y == tv[1].y) { } else if (tv[0].y == tv[1].y) {
pxl8_draw_flat_top_triangle_textured(gfx, tv[0], tv[1], tv[2], texture_id); pxl8_scan_triangle_textured(gfx, tv[0], tv[1], tv[2], texture_id, fill_scanline, false);
} else { } else {
f32 t = (f32)(tv[1].y - tv[0].y) / (f32)(tv[2].y - tv[0].y); f32 t = (f32)(tv[1].y - tv[0].y) / (f32)(tv[2].y - tv[0].y);
pxl8_textured_vertex v3; pxl8_textured_vertex v3;
@ -1429,7 +1468,7 @@ void pxl8_3d_draw_triangle_textured(
v3.v = tv[0].v + t * (tv[2].v - tv[0].v); v3.v = tv[0].v + t * (tv[2].v - tv[0].v);
v3.w = tv[0].w + t * (tv[2].w - tv[0].w); v3.w = tv[0].w + t * (tv[2].w - tv[0].w);
pxl8_draw_flat_bottom_triangle_textured(gfx, tv[0], tv[1], v3, texture_id); pxl8_scan_triangle_textured(gfx, tv[0], tv[1], v3, texture_id, fill_scanline, true);
pxl8_draw_flat_top_triangle_textured(gfx, tv[1], v3, tv[2], texture_id); pxl8_scan_triangle_textured(gfx, tv[1], v3, tv[2], texture_id, fill_scanline, false);
} }
} }

View file

@ -3,86 +3,97 @@
#include <stdio.h> #include <stdio.h>
#include <time.h> #include <time.h>
#define PXL8_LOG_ERROR "\033[38;2;251;73;52m" #define PXL8_LOG_ERROR "\033[38;2;251;73;52m"
#define PXL8_LOG_SUCCESS "\033[38;2;254;128;25m" #define PXL8_LOG_SUCCESS "\033[38;2;254;128;25m"
#define PXL8_LOG_WARN "\033[38;2;250;189;47m" #define PXL8_LOG_WARN "\033[38;2;250;189;47m"
#define PXL8_LOG_INFO "\033[38;2;184;187;38m" #define PXL8_LOG_INFO "\033[38;2;184;187;38m"
#define PXL8_LOG_DEBUG "\033[38;2;131;165;152m" #define PXL8_LOG_DEBUG "\033[38;2;131;165;152m"
#define PXL8_LOG_TRACE "\033[38;2;211;134;155m" #define PXL8_LOG_TRACE "\033[38;2;211;134;155m"
#define PXL8_LOG_MUTED "\033[38;2;168;153;132m" #define PXL8_LOG_MUTED "\033[38;2;168;153;132m"
#define PXL8_LOG_RESET "\033[0m" #define PXL8_LOG_RESET "\033[0m"
typedef enum {
PXL8_LOG_LEVEL_TRACE = 0,
PXL8_LOG_LEVEL_DEBUG = 1,
PXL8_LOG_LEVEL_INFO = 2,
PXL8_LOG_LEVEL_WARN = 3,
PXL8_LOG_LEVEL_ERROR = 4,
} pxl8_log_level;
#ifndef PXL8_LOG_LEVEL
#ifdef DEBUG
#define PXL8_LOG_LEVEL PXL8_LOG_LEVEL_DEBUG
#else
#define PXL8_LOG_LEVEL PXL8_LOG_LEVEL_INFO
#endif
#endif
static pxl8_log_level pxl8_current_log_level = PXL8_LOG_LEVEL;
static inline void pxl8_log_timestamp(char* buffer, size_t size) { static inline void pxl8_log_timestamp(char* buffer, size_t size) {
time_t now = time(NULL); time_t now = time(NULL);
struct tm* tm_info = localtime(&now); struct tm* tm_info = localtime(&now);
strftime(buffer, size, "%H:%M:%S", tm_info); strftime(buffer, size, "%H:%M:%S", tm_info);
} }
#ifdef DEBUG #define pxl8_trace(...) \
#ifndef PXL8_ENABLE_DEBUG_LOGS do { \
#define PXL8_ENABLE_DEBUG_LOGS 1 if (pxl8_current_log_level <= PXL8_LOG_LEVEL_TRACE) { \
#endif char timestamp[16]; \
pxl8_log_timestamp(timestamp, sizeof(timestamp)); \
#if PXL8_ENABLE_DEBUG_LOGS fprintf(stderr, "\r\033[K" PXL8_LOG_TRACE "[%s TRACE]" PXL8_LOG_RESET \
#define pxl8_debug(...) \ " %s:%d: ", timestamp, __FILE__, __LINE__); \
do { \ fprintf(stderr, __VA_ARGS__); \
char timestamp[16]; \ fprintf(stderr, "\n"); \
pxl8_log_timestamp(timestamp, sizeof(timestamp)); \ } \
fprintf(stderr, "\r\033[K" PXL8_LOG_DEBUG "[%s DEBUG]" PXL8_LOG_RESET \ } while(0)
" %s:%d: ", timestamp, __FILE__, __LINE__); \
fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, "\n"); \
} while(0)
#define pxl8_trace(...) \ #define pxl8_debug(...) \
do { \ do { \
char timestamp[16]; \ if (pxl8_current_log_level <= PXL8_LOG_LEVEL_DEBUG) { \
pxl8_log_timestamp(timestamp, sizeof(timestamp)); \ char timestamp[16]; \
fprintf(stderr, "\r\033[K" PXL8_LOG_TRACE "[%s TRACE]" PXL8_LOG_RESET \ pxl8_log_timestamp(timestamp, sizeof(timestamp)); \
" %s:%d: ", timestamp, __FILE__, __LINE__); \ fprintf(stderr, "\r\033[K" PXL8_LOG_DEBUG "[%s DEBUG]" PXL8_LOG_RESET \
fprintf(stderr, __VA_ARGS__); \ " %s:%d: ", timestamp, __FILE__, __LINE__); \
fprintf(stderr, "\n"); \ fprintf(stderr, __VA_ARGS__); \
} while(0) fprintf(stderr, "\n"); \
#else } \
#define pxl8_debug(...) } while(0)
#define pxl8_trace(...)
#endif
#else
#define pxl8_debug(...)
#define pxl8_trace(...)
#endif
#define pxl8_error(...) \ #define pxl8_error(...) \
do { \ do { \
char timestamp[16]; \ if (pxl8_current_log_level <= PXL8_LOG_LEVEL_ERROR) { \
pxl8_log_timestamp(timestamp, sizeof(timestamp)); \ char timestamp[16]; \
fprintf(stderr, "\r\033[K" PXL8_LOG_ERROR "[%s ERROR]" PXL8_LOG_RESET \ pxl8_log_timestamp(timestamp, sizeof(timestamp)); \
" %s:%d: ", timestamp, __FILE__, __LINE__); \ fprintf(stderr, "\r\033[K" PXL8_LOG_ERROR "[%s ERROR]" PXL8_LOG_RESET \
fprintf(stderr, __VA_ARGS__); \ " %s:%d: ", timestamp, __FILE__, __LINE__); \
fprintf(stderr, "\n"); \ fprintf(stderr, __VA_ARGS__); \
\ fprintf(stderr, "\n"); \
} \
} while(0) } while(0)
#define pxl8_warn(...) \ #define pxl8_warn(...) \
do { \ do { \
char timestamp[16]; \ if (pxl8_current_log_level <= PXL8_LOG_LEVEL_WARN) { \
pxl8_log_timestamp(timestamp, sizeof(timestamp)); \ char timestamp[16]; \
fprintf(stderr, "\r\033[K" PXL8_LOG_WARN "[%s WARN]" PXL8_LOG_RESET \ pxl8_log_timestamp(timestamp, sizeof(timestamp)); \
" %s:%d: ", timestamp, __FILE__, __LINE__); \ fprintf(stderr, "\r\033[K" PXL8_LOG_WARN "[%s WARN]" PXL8_LOG_RESET \
fprintf(stderr, __VA_ARGS__); \ " %s:%d: ", timestamp, __FILE__, __LINE__); \
fprintf(stderr, "\n"); \ fprintf(stderr, __VA_ARGS__); \
\ fprintf(stderr, "\n"); \
} \
} while(0) } while(0)
#define pxl8_info(...) \ #define pxl8_info(...) \
do { \ do { \
char timestamp[16]; \ if (pxl8_current_log_level <= PXL8_LOG_LEVEL_INFO) { \
pxl8_log_timestamp(timestamp, sizeof(timestamp)); \ char timestamp[16]; \
fprintf(stdout, "\r\033[K" PXL8_LOG_INFO "[%s INFO]" PXL8_LOG_RESET \ pxl8_log_timestamp(timestamp, sizeof(timestamp)); \
" ", timestamp); \ fprintf(stdout, "\r\033[K" PXL8_LOG_INFO "[%s INFO]" PXL8_LOG_RESET \
fprintf(stdout, __VA_ARGS__); \ " ", timestamp); \
fprintf(stdout, "\n"); \ fprintf(stdout, __VA_ARGS__); \
\ fprintf(stdout, "\n"); \
} \
} while(0) } while(0)
#ifndef pxl8_min #ifndef pxl8_min

View file

@ -308,11 +308,22 @@ bool pxl8_frustum_test_aabb(const pxl8_frustum* frustum, pxl8_vec3 min, pxl8_vec
(normal.z >= 0.0f) ? max.z : min.z (normal.z >= 0.0f) ? max.z : min.z
}; };
f32 dist = pxl8_vec3_dot(normal, p_vertex) + d; pxl8_vec3 n_vertex = {
(normal.x >= 0.0f) ? min.x : max.x,
(normal.y >= 0.0f) ? min.y : max.y,
(normal.z >= 0.0f) ? min.z : max.z
};
if (dist < 0.0f) { f32 p_dist = pxl8_vec3_dot(normal, p_vertex) + d;
f32 n_dist = pxl8_vec3_dot(normal, n_vertex) + d;
if (p_dist < -0.1f) {
return false; return false;
} }
if (n_dist > 0.1f) {
continue;
}
} }
return true; return true;

View file

@ -91,6 +91,21 @@ static void calculate_texture_axes(const pxl8_vec3 normal, pxl8_vec3* u_axis, px
} }
} }
static inline void compute_face_aabb(pxl8_bsp_face* face, const pxl8_bsp_vertex* verts, u32 vert_idx) {
face->aabb_min = (pxl8_vec3){1e30f, 1e30f, 1e30f};
face->aabb_max = (pxl8_vec3){-1e30f, -1e30f, -1e30f};
for (u32 i = 0; i < 4; i++) {
pxl8_vec3 v = verts[vert_idx + i].position;
if (v.x < face->aabb_min.x) face->aabb_min.x = v.x;
if (v.x > face->aabb_max.x) face->aabb_max.x = v.x;
if (v.y < face->aabb_min.y) face->aabb_min.y = v.y;
if (v.y > face->aabb_max.y) face->aabb_max.y = v.y;
if (v.z < face->aabb_min.z) face->aabb_min.z = v.z;
if (v.z > face->aabb_max.z) face->aabb_max.z = v.z;
}
}
static void cave_grid_initialize(cave_grid* grid, f32 density) { static void cave_grid_initialize(cave_grid* grid, f32 density) {
for (i32 y = 0; y < grid->height; y++) { for (i32 y = 0; y < grid->height; y++) {
for (i32 x = 0; x < grid->width; x++) { for (i32 x = 0; x < grid->width; x++) {
@ -189,6 +204,8 @@ static pxl8_result cave_to_bsp(pxl8_bsp* bsp, const cave_grid* grid) {
bsp->surfedges[edge_idx + i] = edge_idx + i; bsp->surfedges[edge_idx + i] = edge_idx + i;
} }
compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx);
vert_idx += 4; vert_idx += 4;
edge_idx += 4; edge_idx += 4;
face_idx++; face_idx++;
@ -220,6 +237,8 @@ static pxl8_result cave_to_bsp(pxl8_bsp* bsp, const cave_grid* grid) {
bsp->surfedges[edge_idx + i] = edge_idx + i; bsp->surfedges[edge_idx + i] = edge_idx + i;
} }
compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx);
vert_idx += 4; vert_idx += 4;
edge_idx += 4; edge_idx += 4;
face_idx++; face_idx++;
@ -251,6 +270,8 @@ static pxl8_result cave_to_bsp(pxl8_bsp* bsp, const cave_grid* grid) {
bsp->surfedges[edge_idx + i] = edge_idx + i; bsp->surfedges[edge_idx + i] = edge_idx + i;
} }
compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx);
vert_idx += 4; vert_idx += 4;
edge_idx += 4; edge_idx += 4;
face_idx++; face_idx++;
@ -282,6 +303,8 @@ static pxl8_result cave_to_bsp(pxl8_bsp* bsp, const cave_grid* grid) {
bsp->surfedges[edge_idx + i] = edge_idx + i; bsp->surfedges[edge_idx + i] = edge_idx + i;
} }
compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx);
vert_idx += 4; vert_idx += 4;
edge_idx += 4; edge_idx += 4;
face_idx++; face_idx++;
@ -321,6 +344,8 @@ static pxl8_result cave_to_bsp(pxl8_bsp* bsp, const cave_grid* grid) {
bsp->surfedges[edge_idx + i] = edge_idx + i; bsp->surfedges[edge_idx + i] = edge_idx + i;
} }
compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx);
vert_idx += 4; vert_idx += 4;
edge_idx += 4; edge_idx += 4;
face_idx++; face_idx++;
@ -350,6 +375,8 @@ static pxl8_result cave_to_bsp(pxl8_bsp* bsp, const cave_grid* grid) {
bsp->surfedges[edge_idx + i] = edge_idx + i; bsp->surfedges[edge_idx + i] = edge_idx + i;
} }
compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx);
vert_idx += 4; vert_idx += 4;
edge_idx += 4; edge_idx += 4;
face_idx++; face_idx++;