From 9550d34e6548b4f17e75c211360068310f66f659 Mon Sep 17 00:00:00 2001 From: asrael Date: Mon, 10 Nov 2025 09:39:33 -0600 Subject: [PATCH] add more checks in triangle raster code --- pxl8.sh | 1 + src/pxl8_bsp.c | 77 +++++----- src/pxl8_bsp.h | 3 + src/pxl8_gfx.c | 363 +++++++++++++++++++++++++-------------------- src/pxl8_macros.h | 131 ++++++++-------- src/pxl8_math.c | 15 +- src/pxl8_procgen.c | 27 ++++ 7 files changed, 357 insertions(+), 260 deletions(-) diff --git a/pxl8.sh b/pxl8.sh index 5106411..739b43c 100755 --- a/pxl8.sh +++ b/pxl8.sh @@ -389,6 +389,7 @@ case "$COMMAND" in src/pxl8_gfx.c src/pxl8_io.c src/pxl8_math.c + src/pxl8_procgen.c src/pxl8_script.c src/pxl8_sdl3.c src/pxl8_tilemap.c diff --git a/src/pxl8_bsp.c b/src/pxl8_bsp.c index 6b22818..7006e3d 100644 --- a/src/pxl8_bsp.c +++ b/src/pxl8_bsp.c @@ -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[3] = pxl8_read_u8(&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); + 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", 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) { const pxl8_bsp_face* face = &bsp->faces[face_id]; - - 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); + return pxl8_frustum_test_aabb(frustum, face->aabb_min, face->aabb_max); } void pxl8_bsp_render_face(pxl8_gfx* gfx, const pxl8_bsp* bsp, u32 face_id, u32 texture_id) { diff --git a/src/pxl8_bsp.h b/src/pxl8_bsp.h index 74cfbc1..363d12b 100644 --- a/src/pxl8_bsp.h +++ b/src/pxl8_bsp.h @@ -37,6 +37,9 @@ typedef struct pxl8_bsp_face { u8 styles[4]; u16 texinfo_id; + + pxl8_vec3 aabb_min; + pxl8_vec3 aabb_max; } pxl8_bsp_face; typedef struct pxl8_bsp_node { diff --git a/src/pxl8_gfx.c b/src/pxl8_gfx.c index 1089c64..b553d9b 100644 --- a/src/pxl8_gfx.c +++ b/src/pxl8_gfx.c @@ -416,7 +416,7 @@ void pxl8_clr(pxl8_gfx* gfx, u32 color) { if (frame_count % 60 == 0) { i32 fb_pixels = gfx->framebuffer_width * gfx->framebuffer_height; 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); } 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, f32 z0, f32 z1, f32 u0, f32 v0, f32 w0, @@ -1062,8 +1062,7 @@ static inline void pxl8_fill_scanline_textured( v /= w0; } u32 color = pxl8_sample_texture(gfx, texture_id, u, v); - if ((gfx->color_mode == PXL8_COLOR_MODE_HICOLOR && (color & 0xFF000000)) || - (gfx->color_mode != PXL8_COLOR_MODE_HICOLOR && color != 0)) { + if (color & 0xFF000000) { gfx->zbuffer[idx] = z0; pxl8_pixel(gfx, xs, y, color); } @@ -1083,122 +1082,147 @@ static inline void pxl8_fill_scanline_textured( f32 v = v0; f32 w = w0; - if (gfx->affine_textures) { - 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++; - u32 color = pxl8_sample_texture(gfx, texture_id, u, 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); - } + 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 & 0xFF000000) { + gfx->zbuffer[idx] = z; + pxl8_pixel(gfx, x, y, color); } } - z += dz; - u += du; - v += dv; } - } else { - i32 x = xs; - while (x <= xe) { - f32 w_inv = (fabsf(w) > 1e-6f) ? (1.0f / w) : 0.0f; - f32 u_corrected = u * w_inv; - f32 v_corrected = v * w_inv; + z += dz; + u += du; + v += dv; + w += dw; + } +} - i32 span_end = (x + 16 <= xe) ? (x + 16) : (xe + 1); - i32 span_len = span_end - x; +static inline void pxl8_fill_scanline_textured_indexed( + 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; - if (span_len > 1) { - f32 next_w = w + span_len * dw; - f32 next_w_inv = (fabsf(next_w) > 1e-6f) ? (1.0f / next_w) : 0.0f; - next_u = (u + span_len * du) * next_w_inv; - next_v = (v + span_len * dv) * next_w_inv; - f32 inv_span = 1.0f / (f32)span_len; - affine_du = (next_u - u_corrected) * inv_span; - affine_dv = (next_v - v_corrected) * inv_span; - } else { - affine_du = 0; - affine_dv = 0; - } - - f32 affine_u = u_corrected; - 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); - } - } + i32 span = xe - xs; + if (span <= 0) { + if (xs >= 0 && xs < gfx->framebuffer_width) { + i32 idx = y * gfx->zbuffer_width + xs; + if (z0 <= gfx->zbuffer[idx]) { + gfx->frame_pixel_count++; + f32 u = u0, v = v0; + if (!gfx->affine_textures && fabsf(w0) > 1e-6f) { + u /= w0; + v /= w0; + } + u32 color = pxl8_sample_texture(gfx, texture_id, u, v); + if (color != 0) { + gfx->zbuffer[idx] = z0; + pxl8_pixel(gfx, xs, 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_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, i32 x0, i32 y0, f32 z0, i32 x1, i32 y1, f32 z1, i32 x2, i32 y2, f32 z2, u32 color, - pxl8_scanline_func fill_scanline + pxl8_scanline_func fill_scanline, + bool is_bottom ) { (void)z2; - if (y1 == y0) return; - f32 inv_slope_1 = (f32)(x1 - x0) / (f32)(y1 - y0); - f32 inv_slope_2 = (f32)(x2 - x0) / (f32)(y2 - y0); + i32 y_start, y_end, y_step; + f32 x_start1, x_start2, inv_slope_1, inv_slope_2; - f32 cur_x1 = (f32)x0; - f32 cur_x2 = (f32)x0; - - for (i32 y = y0; y <= y1; y++) { - fill_scanline(gfx, y, (i32)cur_x1, (i32)cur_x2, z0, z1, color); - cur_x1 += inv_slope_1; - cur_x2 += inv_slope_2; + if (is_bottom) { + if (y1 == y0 || y1 < y0 || y1 - y0 > gfx->framebuffer_height) return; + y_start = y0; + y_end = y1; + y_step = 1; + x_start1 = x_start2 = (f32)x0; + 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( - pxl8_gfx* gfx, - i32 x0, i32 y0, f32 z0, - i32 x1, i32 y1, f32 z1, - i32 x2, i32 y2, f32 z2, - u32 color, - pxl8_scanline_func fill_scanline -) { - (void)z2; - if (y2 == y0) return; + f32 cur_x1 = x_start1; + f32 cur_x2 = x_start2; + f32 slope_step1 = inv_slope_1 * y_step; + f32 slope_step2 = inv_slope_2 * y_step; - f32 inv_slope_1 = (f32)(x2 - x0) / (f32)(y2 - y0); - 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--) { + for (i32 y = y_start; y != y_end + y_step; y += y_step) { fill_scanline(gfx, y, (i32)cur_x1, (i32)cur_x2, z0, z1, color); - cur_x1 -= inv_slope_1; - cur_x2 -= inv_slope_2; + cur_x1 += slope_step1; + cur_x2 += slope_step2; } } @@ -1266,16 +1290,16 @@ void pxl8_3d_draw_triangle(pxl8_gfx* gfx, pxl8_triangle tri) { : pxl8_fill_scanline_indexed; 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) { - 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 { i32 x3 = x0 + (i32)(((f32)(y1 - y0) / (f32)(y2 - y0)) * (x2 - x0)); i32 y3 = y1; 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_draw_flat_top_triangle(gfx, x1, y1, z1, x3, y3, z3, x2, y2, z2, color, fill_scanline); + pxl8_scan_triangle(gfx, x0, y0, z0, x1, y1, z1, x3, y3, z3, color, fill_scanline, true); + 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; } pxl8_textured_vertex; -static void pxl8_draw_flat_bottom_triangle_textured( +static void pxl8_scan_triangle_textured( pxl8_gfx* gfx, pxl8_textured_vertex v0, pxl8_textured_vertex v1, 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); - f32 inv_slope_2 = (f32)(v2.x - v0.x) / (f32)(v2.y - v0.y); - f32 inv_slope_z1 = (v1.z - v0.z) / (f32)(v1.y - v0.y); - f32 inv_slope_z2 = (v2.z - v0.z) / (f32)(v2.y - v0.y); - f32 inv_slope_u1 = (v1.u - v0.u) / (f32)(v1.y - v0.y); - f32 inv_slope_u2 = (v2.u - v0.u) / (f32)(v2.y - v0.y); - f32 inv_slope_v1 = (v1.v - v0.v) / (f32)(v1.y - v0.y); - f32 inv_slope_v2 = (v2.v - v0.v) / (f32)(v2.y - v0.y); - f32 inv_slope_w1 = (v1.w - v0.w) / (f32)(v1.y - v0.y); - f32 inv_slope_w2 = (v2.w - v0.w) / (f32)(v2.y - v0.y); - - f32 cur_x1 = (f32)v0.x, cur_x2 = (f32)v0.x; - f32 cur_z1 = v0.z, cur_z2 = v0.z; - f32 cur_u1 = v0.u, cur_u2 = v0.u; - f32 cur_v1 = v0.v, cur_v2 = v0.v; - f32 cur_w1 = v0.w, cur_w2 = v0.w; - - for (i32 y = v0.y; y <= v1.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_x1 += inv_slope_1; cur_x2 += inv_slope_2; - cur_z1 += inv_slope_z1; cur_z2 += inv_slope_z2; - cur_u1 += inv_slope_u1; cur_u2 += inv_slope_u2; - cur_v1 += inv_slope_v1; cur_v2 += inv_slope_v2; - cur_w1 += inv_slope_w1; cur_w2 += inv_slope_w2; + if (is_bottom) { + if (v1.y == v0.y || v1.y < v0.y || v1.y - v0.y > gfx->framebuffer_height) return; + y_start = v0.y; + y_end = v1.y; + y_step = 1; + x_start1 = x_start2 = (f32)v0.x; + z_start1 = z_start2 = v0.z; + u_start1 = u_start2 = v0.u; + v_start1 = v_start2 = v0.v; + w_start1 = w_start2 = v0.w; + inv_slope_1 = (f32)(v1.x - v0.x) / (f32)(v1.y - v0.y); + inv_slope_2 = (f32)(v2.x - v0.x) / (f32)(v2.y - v0.y); + inv_slope_z1 = (v1.z - v0.z) / (f32)(v1.y - v0.y); + inv_slope_z2 = (v2.z - v0.z) / (f32)(v2.y - v0.y); + inv_slope_u1 = (v1.u - v0.u) / (f32)(v1.y - v0.y); + inv_slope_u2 = (v2.u - v0.u) / (f32)(v2.y - v0.y); + inv_slope_v1 = (v1.v - v0.v) / (f32)(v1.y - v0.y); + inv_slope_v2 = (v2.v - v0.v) / (f32)(v2.y - v0.y); + inv_slope_w1 = (v1.w - v0.w) / (f32)(v1.y - v0.y); + inv_slope_w2 = (v2.w - v0.w) / (f32)(v2.y - v0.y); + } else { + if (v2.y == v0.y || v2.y < v0.y || v2.y - v0.y > gfx->framebuffer_height) return; + y_start = v2.y; + y_end = v0.y; + 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( - pxl8_gfx* gfx, - pxl8_textured_vertex v0, - pxl8_textured_vertex v1, - pxl8_textured_vertex v2, - u32 texture_id -) { - if (v2.y == v0.y) return; + f32 cur_x1 = x_start1, cur_x2 = x_start2; + f32 cur_z1 = z_start1, cur_z2 = z_start2; + f32 cur_u1 = u_start1, cur_u2 = u_start2; + f32 cur_v1 = v_start1, cur_v2 = v_start2; + f32 cur_w1 = w_start1, cur_w2 = w_start2; - f32 inv_slope_1 = (f32)(v2.x - v0.x) / (f32)(v2.y - v0.y); - f32 inv_slope_2 = (f32)(v2.x - v1.x) / (f32)(v2.y - v1.y); - f32 inv_slope_z1 = (v2.z - v0.z) / (f32)(v2.y - v0.y); - f32 inv_slope_z2 = (v2.z - v1.z) / (f32)(v2.y - v1.y); - f32 inv_slope_u1 = (v2.u - v0.u) / (f32)(v2.y - v0.y); - f32 inv_slope_u2 = (v2.u - v1.u) / (f32)(v2.y - v1.y); - f32 inv_slope_v1 = (v2.v - v0.v) / (f32)(v2.y - v0.y); - f32 inv_slope_v2 = (v2.v - v1.v) / (f32)(v2.y - v1.y); - f32 inv_slope_w1 = (v2.w - v0.w) / (f32)(v2.y - v0.y); - f32 inv_slope_w2 = (v2.w - v1.w) / (f32)(v2.y - v1.y); + f32 slope_step_x1 = inv_slope_1 * y_step; + f32 slope_step_x2 = inv_slope_2 * y_step; + f32 slope_step_z1 = inv_slope_z1 * y_step; + f32 slope_step_z2 = inv_slope_z2 * y_step; + f32 slope_step_u1 = inv_slope_u1 * y_step; + f32 slope_step_u2 = inv_slope_u2 * y_step; + f32 slope_step_v1 = inv_slope_v1 * y_step; + f32 slope_step_v2 = inv_slope_v2 * y_step; + f32 slope_step_w1 = inv_slope_w1 * y_step; + f32 slope_step_w2 = inv_slope_w2 * y_step; - f32 cur_x1 = (f32)v2.x, cur_x2 = (f32)v2.x; - f32 cur_z1 = v2.z, cur_z2 = v2.z; - 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, + for (i32 y = y_start; y != y_end + y_step; y += y_step) { + fill_scanline(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_x1 -= inv_slope_1; cur_x2 -= inv_slope_2; - cur_z1 -= inv_slope_z1; cur_z2 -= inv_slope_z2; - cur_u1 -= inv_slope_u1; cur_u2 -= inv_slope_u2; - cur_v1 -= inv_slope_v1; cur_v2 -= inv_slope_v2; - cur_w1 -= inv_slope_w1; cur_w2 -= inv_slope_w2; + cur_x1 += slope_step_x1; cur_x2 += slope_step_x2; + cur_z1 += slope_step_z1; cur_z2 += slope_step_z2; + cur_u1 += slope_step_u1; cur_u2 += slope_step_u2; + cur_v1 += slope_step_v1; cur_v2 += slope_step_v2; + 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_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) { - 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) { - 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 { f32 t = (f32)(tv[1].y - tv[0].y) / (f32)(tv[2].y - tv[0].y); 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.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_draw_flat_top_triangle_textured(gfx, tv[1], v3, tv[2], texture_id); + pxl8_scan_triangle_textured(gfx, tv[0], tv[1], v3, texture_id, fill_scanline, true); + pxl8_scan_triangle_textured(gfx, tv[1], v3, tv[2], texture_id, fill_scanline, false); } } diff --git a/src/pxl8_macros.h b/src/pxl8_macros.h index e379c6e..9056178 100644 --- a/src/pxl8_macros.h +++ b/src/pxl8_macros.h @@ -3,86 +3,97 @@ #include #include -#define PXL8_LOG_ERROR "\033[38;2;251;73;52m" -#define PXL8_LOG_SUCCESS "\033[38;2;254;128;25m" -#define PXL8_LOG_WARN "\033[38;2;250;189;47m" -#define PXL8_LOG_INFO "\033[38;2;184;187;38m" -#define PXL8_LOG_DEBUG "\033[38;2;131;165;152m" -#define PXL8_LOG_TRACE "\033[38;2;211;134;155m" -#define PXL8_LOG_MUTED "\033[38;2;168;153;132m" +#define PXL8_LOG_ERROR "\033[38;2;251;73;52m" +#define PXL8_LOG_SUCCESS "\033[38;2;254;128;25m" +#define PXL8_LOG_WARN "\033[38;2;250;189;47m" +#define PXL8_LOG_INFO "\033[38;2;184;187;38m" +#define PXL8_LOG_DEBUG "\033[38;2;131;165;152m" +#define PXL8_LOG_TRACE "\033[38;2;211;134;155m" +#define PXL8_LOG_MUTED "\033[38;2;168;153;132m" #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) { time_t now = time(NULL); struct tm* tm_info = localtime(&now); strftime(buffer, size, "%H:%M:%S", tm_info); } -#ifdef DEBUG - #ifndef PXL8_ENABLE_DEBUG_LOGS - #define PXL8_ENABLE_DEBUG_LOGS 1 - #endif - - #if PXL8_ENABLE_DEBUG_LOGS - #define pxl8_debug(...) \ - do { \ - char timestamp[16]; \ - pxl8_log_timestamp(timestamp, sizeof(timestamp)); \ - fprintf(stderr, "\r\033[K" PXL8_LOG_DEBUG "[%s DEBUG]" PXL8_LOG_RESET \ - " %s:%d: ", timestamp, __FILE__, __LINE__); \ - fprintf(stderr, __VA_ARGS__); \ - fprintf(stderr, "\n"); \ - } while(0) +#define pxl8_trace(...) \ + do { \ + if (pxl8_current_log_level <= PXL8_LOG_LEVEL_TRACE) { \ + char timestamp[16]; \ + pxl8_log_timestamp(timestamp, sizeof(timestamp)); \ + fprintf(stderr, "\r\033[K" PXL8_LOG_TRACE "[%s TRACE]" PXL8_LOG_RESET \ + " %s:%d: ", timestamp, __FILE__, __LINE__); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + } \ + } while(0) - #define pxl8_trace(...) \ - do { \ - char timestamp[16]; \ - pxl8_log_timestamp(timestamp, sizeof(timestamp)); \ - fprintf(stderr, "\r\033[K" PXL8_LOG_TRACE "[%s TRACE]" PXL8_LOG_RESET \ - " %s:%d: ", timestamp, __FILE__, __LINE__); \ - fprintf(stderr, __VA_ARGS__); \ - fprintf(stderr, "\n"); \ - } while(0) - #else - #define pxl8_debug(...) - #define pxl8_trace(...) - #endif -#else - #define pxl8_debug(...) - #define pxl8_trace(...) -#endif +#define pxl8_debug(...) \ + do { \ + if (pxl8_current_log_level <= PXL8_LOG_LEVEL_DEBUG) { \ + char timestamp[16]; \ + pxl8_log_timestamp(timestamp, sizeof(timestamp)); \ + fprintf(stderr, "\r\033[K" PXL8_LOG_DEBUG "[%s DEBUG]" PXL8_LOG_RESET \ + " %s:%d: ", timestamp, __FILE__, __LINE__); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + } \ + } while(0) #define pxl8_error(...) \ do { \ - char timestamp[16]; \ - pxl8_log_timestamp(timestamp, sizeof(timestamp)); \ - fprintf(stderr, "\r\033[K" PXL8_LOG_ERROR "[%s ERROR]" PXL8_LOG_RESET \ - " %s:%d: ", timestamp, __FILE__, __LINE__); \ - fprintf(stderr, __VA_ARGS__); \ - fprintf(stderr, "\n"); \ - \ + if (pxl8_current_log_level <= PXL8_LOG_LEVEL_ERROR) { \ + char timestamp[16]; \ + pxl8_log_timestamp(timestamp, sizeof(timestamp)); \ + fprintf(stderr, "\r\033[K" PXL8_LOG_ERROR "[%s ERROR]" PXL8_LOG_RESET \ + " %s:%d: ", timestamp, __FILE__, __LINE__); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + } \ } while(0) #define pxl8_warn(...) \ do { \ - char timestamp[16]; \ - pxl8_log_timestamp(timestamp, sizeof(timestamp)); \ - fprintf(stderr, "\r\033[K" PXL8_LOG_WARN "[%s WARN]" PXL8_LOG_RESET \ - " %s:%d: ", timestamp, __FILE__, __LINE__); \ - fprintf(stderr, __VA_ARGS__); \ - fprintf(stderr, "\n"); \ - \ + if (pxl8_current_log_level <= PXL8_LOG_LEVEL_WARN) { \ + char timestamp[16]; \ + pxl8_log_timestamp(timestamp, sizeof(timestamp)); \ + fprintf(stderr, "\r\033[K" PXL8_LOG_WARN "[%s WARN]" PXL8_LOG_RESET \ + " %s:%d: ", timestamp, __FILE__, __LINE__); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + } \ } while(0) #define pxl8_info(...) \ do { \ - char timestamp[16]; \ - pxl8_log_timestamp(timestamp, sizeof(timestamp)); \ - fprintf(stdout, "\r\033[K" PXL8_LOG_INFO "[%s INFO]" PXL8_LOG_RESET \ - " ", timestamp); \ - fprintf(stdout, __VA_ARGS__); \ - fprintf(stdout, "\n"); \ - \ + if (pxl8_current_log_level <= PXL8_LOG_LEVEL_INFO) { \ + char timestamp[16]; \ + pxl8_log_timestamp(timestamp, sizeof(timestamp)); \ + fprintf(stdout, "\r\033[K" PXL8_LOG_INFO "[%s INFO]" PXL8_LOG_RESET \ + " ", timestamp); \ + fprintf(stdout, __VA_ARGS__); \ + fprintf(stdout, "\n"); \ + } \ } while(0) #ifndef pxl8_min diff --git a/src/pxl8_math.c b/src/pxl8_math.c index 37b8006..4935cf6 100644 --- a/src/pxl8_math.c +++ b/src/pxl8_math.c @@ -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 }; - 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; } + + if (n_dist > 0.1f) { + continue; + } } return true; diff --git a/src/pxl8_procgen.c b/src/pxl8_procgen.c index 38ff452..2397a03 100644 --- a/src/pxl8_procgen.c +++ b/src/pxl8_procgen.c @@ -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) { for (i32 y = 0; y < grid->height; y++) { 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; } + compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx); + vert_idx += 4; edge_idx += 4; 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; } + compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx); + vert_idx += 4; edge_idx += 4; 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; } + compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx); + vert_idx += 4; edge_idx += 4; 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; } + compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx); + vert_idx += 4; edge_idx += 4; 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; } + compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx); + vert_idx += 4; edge_idx += 4; 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; } + compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx); + vert_idx += 4; edge_idx += 4; face_idx++;