diff --git a/demo/mod/worldgen.fnl b/demo/mod/worldgen.fnl index 0519d88..42fe7d1 100644 --- a/demo/mod/worldgen.fnl +++ b/demo/mod/worldgen.fnl @@ -2,9 +2,9 @@ (local debug-ui (require :mod.debug_ui)) (var world nil) -(var cam-x 1000) +(var cam-x 512) (var cam-y 64) -(var cam-z 1000) +(var cam-z 512) (var cam-yaw 0) (var cam-pitch 0) (var bob-time 0) @@ -23,8 +23,8 @@ (set world (pxl8.world_new)) (let [result (pxl8.world_generate world { :type pxl8.PROCGEN_CAVE - :width 32 - :height 32 + :width 16 + :height 16 :seed 42 :density 0.45 :iterations 4})] @@ -33,118 +33,123 @@ (pxl8.info "Generated procedural cave")))) (fn update [dt] - (set fps-accumulator (+ fps-accumulator dt)) - (set fps-frame-count (+ fps-frame-count 1)) + (let [clamped-dt (math.min dt 0.1)] + (when (> dt 0.05) + (pxl8.debug (.. "Large dt: " (string.format "%.3f" dt) "s"))) - (when (>= fps-accumulator 0.25) - (set fps (/ fps-frame-count fps-accumulator)) - (set fps-accumulator 0) - (set fps-frame-count 0)) + (set fps-accumulator (+ fps-accumulator clamped-dt)) + (set fps-frame-count (+ fps-frame-count 1)) - (when (pxl8.key_pressed "F8") - (set show-debug-ui (not show-debug-ui)) - (pxl8.ui_window_set_open "Debug Menu (F8)" show-debug-ui)) + (when (>= fps-accumulator 0.25) + (set fps (/ fps-frame-count fps-accumulator)) + (set fps-accumulator 0) + (set fps-frame-count 0)) - (when (pxl8.world_is_loaded world) - (let [forward-x (- (math.sin cam-yaw)) - forward-z (- (math.cos cam-yaw)) - right-x (math.cos cam-yaw) - right-z (- (math.sin cam-yaw)) - cell-size 64 - grid-min 0 - grid-max (* 32 cell-size)] + (when (pxl8.key_pressed "F8") + (set show-debug-ui (not show-debug-ui)) + (pxl8.ui_window_set_open "Debug Menu (F8)" show-debug-ui)) - (var moving false) - (var new-x cam-x) - (var new-z cam-z) + (when (pxl8.world_is_loaded world) + (let [forward-x (- (math.sin cam-yaw)) + forward-z (- (math.cos cam-yaw)) + right-x (math.cos cam-yaw) + right-z (- (math.sin cam-yaw)) + cell-size 64 + grid-min 0 + grid-max (* 16 cell-size)] - (when (pxl8.key_down "w") - (set new-x (+ new-x (* forward-x move-speed dt))) - (set new-z (+ new-z (* forward-z move-speed dt))) - (set moving true)) + (var moving false) + (var new-x cam-x) + (var new-z cam-z) - (when (pxl8.key_down "s") - (set new-x (- new-x (* forward-x move-speed dt))) - (set new-z (- new-z (* forward-z move-speed dt))) - (set moving true)) + (when (pxl8.key_down "w") + (set new-x (+ new-x (* forward-x move-speed clamped-dt))) + (set new-z (+ new-z (* forward-z move-speed clamped-dt))) + (set moving true)) - (when (pxl8.key_down "q") - (set new-x (- new-x (* right-x move-speed dt))) - (set new-z (- new-z (* right-z move-speed dt))) - (set moving true)) + (when (pxl8.key_down "s") + (set new-x (- new-x (* forward-x move-speed clamped-dt))) + (set new-z (- new-z (* forward-z move-speed clamped-dt))) + (set moving true)) - (when (pxl8.key_down "e") - (set new-x (+ new-x (* right-x move-speed dt))) - (set new-z (+ new-z (* right-z move-speed dt))) - (set moving true)) + (when (pxl8.key_down "q") + (set new-x (- new-x (* right-x move-speed clamped-dt))) + (set new-z (- new-z (* right-z move-speed clamped-dt))) + (set moving true)) - (when (and (>= new-x grid-min) (<= new-x grid-max) - (>= new-z grid-min) (<= new-z grid-max)) - (set cam-x new-x) - (set cam-z new-z)) + (when (pxl8.key_down "e") + (set new-x (+ new-x (* right-x move-speed clamped-dt))) + (set new-z (+ new-z (* right-z move-speed clamped-dt))) + (set moving true)) - (when (or (pxl8.key_down "left") (pxl8.key_down "a")) - (set cam-yaw (+ cam-yaw (* turn-speed dt)))) + (when (and (>= new-x grid-min) (<= new-x grid-max) + (>= new-z grid-min) (<= new-z grid-max)) + (set cam-x new-x) + (set cam-z new-z)) - (when (or (pxl8.key_down "right") (pxl8.key_down "d")) - (set cam-yaw (- cam-yaw (* turn-speed dt)))) + (when (or (pxl8.key_down "left") (pxl8.key_down "a")) + (set cam-yaw (+ cam-yaw (* turn-speed clamped-dt)))) - (when (pxl8.key_down "up") - (set cam-pitch (+ cam-pitch (* turn-speed dt))) - (when (> cam-pitch 1.5) (set cam-pitch 1.5))) + (when (or (pxl8.key_down "right") (pxl8.key_down "d")) + (set cam-yaw (- cam-yaw (* turn-speed clamped-dt)))) - (when (pxl8.key_down "down") - (set cam-pitch (- cam-pitch (* turn-speed dt))) - (when (< cam-pitch -1.5) (set cam-pitch -1.5))) + (when (pxl8.key_down "up") + (set cam-pitch (+ cam-pitch (* turn-speed clamped-dt))) + (when (> cam-pitch 1.5) (set cam-pitch 1.5))) - (if moving - (set bob-time (+ bob-time (* dt bob-speed))) - (let [target-phase (* (math.floor (/ bob-time math.pi)) math.pi)] - (set bob-time (+ (* bob-time 0.8) (* target-phase 0.2)))))))) + (when (pxl8.key_down "down") + (set cam-pitch (- cam-pitch (* turn-speed clamped-dt))) + (when (< cam-pitch -1.5) (set cam-pitch -1.5))) + (if moving + (set bob-time (+ bob-time (* clamped-dt bob-speed))) + (let [target-phase (* (math.floor (/ bob-time math.pi)) math.pi)] + (set bob-time (+ (* bob-time 0.8) (* target-phase 0.2))))))))) (fn frame [] (pxl8.clr 0) - (when (pxl8.world_is_loaded world) - (let [bob-offset (* (math.sin bob-time) bob-amount) - eye-y (+ cam-y bob-offset) - forward-x (- (math.sin cam-yaw)) - forward-z (- (math.cos cam-yaw)) - target-x (+ cam-x forward-x) - target-y (+ eye-y (math.sin cam-pitch)) - target-z (+ cam-z forward-z)] + (when (pxl8.world_is_loaded world) + (let [bob-offset (* (math.sin bob-time) bob-amount) + eye-y (+ cam-y bob-offset) + forward-x (- (math.sin cam-yaw)) + forward-z (- (math.cos cam-yaw)) + target-x (+ cam-x forward-x) + target-y (+ eye-y (math.sin cam-pitch)) + target-z (+ cam-z forward-z)] - (pxl8.text (.. "Pos: " (string.format "%.0f" cam-x) "," - (string.format "%.0f" cam-y) "," - (string.format "%.0f" cam-z)) 10 25 12) + (pxl8.text (.. "Pos: " (string.format "%.0f" cam-x) "," + (string.format "%.0f" cam-y) "," + (string.format "%.0f" cam-z)) 10 25 12) - (pxl8.clear_zbuffer) - (pxl8.set_backface_culling true) - (pxl8.set_wireframe false) + (pxl8.clear_zbuffer) - (let [aspect (/ (pxl8.get_width) (pxl8.get_height)) - fov 1.047] - (pxl8.set_projection (pxl8.mat4_perspective fov aspect 1.0 4096.0))) + (pxl8.set_backface_culling true) + (pxl8.set_wireframe false) - (pxl8.set_view (pxl8.mat4_lookat - [cam-x eye-y cam-z] - [target-x target-y target-z] - [0 1 0])) + (let [aspect (/ (pxl8.get_width) (pxl8.get_height)) + fov 1.047] + (pxl8.set_projection (pxl8.mat4_perspective fov aspect 1.0 4096.0))) - (pxl8.set_model (pxl8.mat4_identity)) + (pxl8.set_view (pxl8.mat4_lookat + [cam-x eye-y cam-z] + [target-x target-y target-z] + [0 1 0])) - (pxl8.set_affine_textures affine) - (pxl8.world_render world [cam-x eye-y cam-z])) + (pxl8.set_model (pxl8.mat4_identity)) - (let [new-state (debug-ui.render {:show-debug-ui show-debug-ui - :fps fps - :wireframe false - :auto-rotate false - :orthographic false - :use-texture true - :affine affine})] - (when (not= new-state.show-debug-ui nil) (set show-debug-ui new-state.show-debug-ui)) - (when (not= new-state.affine nil) (set affine new-state.affine))))) + (pxl8.set_affine_textures affine) + + (pxl8.world_render world [cam-x eye-y cam-z]))) + + (let [new-state (debug-ui.render {:show-debug-ui show-debug-ui + :fps fps + :wireframe false + :auto-rotate false + :orthographic false + :use-texture true + :affine affine})] + (when (not= new-state.show-debug-ui nil) (set show-debug-ui new-state.show-debug-ui)) + (when (not= new-state.affine nil) (set affine new-state.affine)))) {:init init :update update diff --git a/src/pxl8.c b/src/pxl8.c index 6f7e5ca..9fb682b 100644 --- a/src/pxl8.c +++ b/src/pxl8.c @@ -8,6 +8,7 @@ #include #include +#include "pxl8.h" #include "pxl8_cart.h" #include "pxl8_game.h" #include "pxl8_hal.h" @@ -16,16 +17,18 @@ #include "pxl8_types.h" #include "pxl8_ui.h" -pxl8_game_result pxl8_init(pxl8_game* game, i32 argc, char* argv[]) { - if (!game) { - return PXL8_GAME_FAILURE; +pxl8_result pxl8_init(pxl8* system, i32 argc, char* argv[]) { + if (!system) { + return PXL8_FAILURE; } - if (!game->hal) { + if (!system->hal) { pxl8_error("HAL must be set before calling pxl8_init"); - return PXL8_GAME_FAILURE; + return PXL8_FAILURE; } + pxl8_game* game = &system->game; + game->color_mode = PXL8_COLOR_MODE_MEGA; game->resolution = PXL8_RESOLUTION_640x360; @@ -44,7 +47,7 @@ pxl8_game_result pxl8_init(pxl8_game* game, i32 argc, char* argv[]) { pack_output = argv[++i]; } else { pxl8_error("--pack requires "); - return PXL8_GAME_FAILURE; + return PXL8_FAILURE; } } else if (!script_arg) { script_arg = argv[i]; @@ -53,7 +56,7 @@ pxl8_game_result pxl8_init(pxl8_game* game, i32 argc, char* argv[]) { if (pack_mode) { pxl8_result result = pxl8_cart_pack(pack_input, pack_output); - return (result == PXL8_OK) ? PXL8_GAME_SUCCESS : PXL8_GAME_FAILURE; + return (result == PXL8_OK) ? PXL8_SUCCESS : PXL8_FAILURE; } @@ -63,29 +66,29 @@ pxl8_game_result pxl8_init(pxl8_game* game, i32 argc, char* argv[]) { pxl8_info("Starting up"); - game->gfx = pxl8_gfx_create(game->hal, game->color_mode, game->resolution, "pxl8", 1280, 720); + game->gfx = pxl8_gfx_create(system->hal, game->color_mode, game->resolution, "pxl8", 1280, 720); if (!game->gfx) { pxl8_error("Failed to create graphics context"); - return PXL8_GAME_FAILURE; + return PXL8_FAILURE; } if (pxl8_gfx_load_font_atlas(game->gfx) != PXL8_OK) { pxl8_error("Failed to load font atlas"); - return PXL8_GAME_FAILURE; + return PXL8_FAILURE; } game->ui = pxl8_ui_create(game->gfx); if (!game->ui) { pxl8_error("Failed to create UI"); pxl8_gfx_destroy(game->gfx); - return PXL8_GAME_FAILURE; + return PXL8_FAILURE; } game->script = pxl8_script_create(); if (!game->script) { pxl8_error("Failed to initialize scripting: %s", pxl8_script_get_last_error(game->script)); pxl8_gfx_destroy(game->gfx); - return PXL8_GAME_FAILURE; + return PXL8_FAILURE; } const char* cart_path = script_arg ? script_arg : "demo"; @@ -99,7 +102,7 @@ pxl8_game_result pxl8_init(pxl8_game* game, i32 argc, char* argv[]) { game->cart = pxl8_cart_create(); if (!game->cart) { pxl8_error("Failed to create cart"); - return PXL8_GAME_FAILURE; + return PXL8_FAILURE; } if (pxl8_cart_load(game->cart, cart_path) == PXL8_OK) { pxl8_script_set_cart_path(game->script, pxl8_cart_get_base_path(game->cart), original_cwd); @@ -108,7 +111,7 @@ pxl8_game_result pxl8_init(pxl8_game* game, i32 argc, char* argv[]) { pxl8_info("Loaded cart: %s", pxl8_cart_get_name(game->cart)); } else { pxl8_error("Failed to load cart: %s", cart_path); - return PXL8_GAME_FAILURE; + return PXL8_FAILURE; } free(original_cwd); } else if (script_arg) { @@ -129,19 +132,21 @@ pxl8_game_result pxl8_init(pxl8_game* game, i32 argc, char* argv[]) { } } - game->last_time = game->hal->get_ticks(); + game->last_time = pxl8_get_ticks(system); game->running = true; - return PXL8_GAME_CONTINUE; + return PXL8_CONTINUE; } -pxl8_game_result pxl8_update(pxl8_game* game) { - if (!game) { - return PXL8_GAME_FAILURE; +pxl8_result pxl8_update(pxl8* system) { + if (!system) { + return PXL8_FAILURE; } - u64 current_time = game->hal->get_ticks(); + pxl8_game* game = &system->game; + + u64 current_time = pxl8_get_ticks(system); f32 dt = (f32)(current_time - game->last_time) / 1000000000.0f; game->last_time = current_time; @@ -234,14 +239,16 @@ pxl8_game_result pxl8_update(pxl8_game* game) { pxl8_script_call_function_f32(game->script, "update", dt); } - return PXL8_GAME_CONTINUE; + return PXL8_CONTINUE; } -pxl8_game_result pxl8_frame(pxl8_game* game) { - if (!game) { - return PXL8_GAME_FAILURE; +pxl8_result pxl8_frame(pxl8* system) { + if (!system) { + return PXL8_FAILURE; } + pxl8_game* game = &system->game; + pxl8_bounds bounds = pxl8_gfx_get_bounds(game->gfx); if (game->script_loaded) { @@ -282,11 +289,13 @@ pxl8_game_result pxl8_frame(pxl8_game* game) { game->input.mouse_wheel_x = 0; game->input.mouse_wheel_y = 0; - return game->running ? PXL8_GAME_CONTINUE : PXL8_GAME_SUCCESS; + return game->running ? PXL8_CONTINUE : PXL8_SUCCESS; } -void pxl8_quit(pxl8_game* game) { - if (!game) return; +void pxl8_quit(pxl8* system) { + if (!system) return; + + pxl8_game* game = &system->game; if (game->repl_mode && game->repl) { fprintf(stderr, "\r\033[K"); @@ -307,3 +316,11 @@ void pxl8_quit(pxl8_game* game) { pxl8_script_destroy(game->script); if (game->ui) pxl8_ui_destroy(game->ui); } + +u64 pxl8_get_ticks(const pxl8* system) { + if (!system || !system->hal) { + pxl8_error("Invalid pxl8 system"); + return 0; + } + return system->hal->get_ticks(); +} diff --git a/src/pxl8.h b/src/pxl8.h new file mode 100644 index 0000000..74e3931 --- /dev/null +++ b/src/pxl8.h @@ -0,0 +1,24 @@ +#pragma once + +#include "pxl8_game.h" +#include "pxl8_hal.h" +#include "pxl8_types.h" + +typedef struct pxl8 { + const pxl8_hal* hal; + pxl8_game game; +} pxl8; + +typedef struct pxl8_callbacks { + pxl8_result (*init)(pxl8* system, i32 argc, char* argv[]); + pxl8_result (*update)(pxl8* system); + pxl8_result (*frame)(pxl8* system); + void (*quit)(pxl8* system); +} pxl8_callbacks; + +pxl8_result pxl8_init(pxl8* system, i32 argc, char* argv[]); +pxl8_result pxl8_update(pxl8* system); +pxl8_result pxl8_frame(pxl8* system); +void pxl8_quit(pxl8* system); + +u64 pxl8_get_ticks(const pxl8* system); diff --git a/src/pxl8_bsp.c b/src/pxl8_bsp.c index 6b22818..37fdd7f 100644 --- a/src/pxl8_bsp.c +++ b/src/pxl8_bsp.c @@ -302,7 +302,8 @@ void pxl8_bsp_destroy(pxl8_bsp* bsp) { } i32 pxl8_bsp_find_leaf(const pxl8_bsp* bsp, pxl8_vec3 pos) { - if (!bsp || bsp->num_nodes == 0) return -1; + if (!bsp) return -1; + if (bsp->num_nodes == 0) return 0; i32 node_id = 0; @@ -334,42 +335,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->bounds_min, face->bounds_max); } void pxl8_bsp_render_face(pxl8_gfx* gfx, const pxl8_bsp* bsp, u32 face_id, u32 texture_id) { @@ -449,23 +415,41 @@ void pxl8_bsp_render_solid( ) { if (!gfx || !bsp || bsp->num_faces == 0) return; + static bool first_call = true; + static u32 frame_count = 0; + frame_count++; + + if (first_call) { + pxl8_debug("BSP render solid FIRST CALL - frame %u", frame_count); + first_call = false; + } + const pxl8_frustum* frustum = pxl8_3d_get_frustum(gfx); if (!frustum) return; + pxl8_debug("Finding camera leaf START"); i32 camera_leaf = pxl8_bsp_find_leaf(bsp, camera_pos); + pxl8_debug("Finding camera leaf DONE - leaf: %d", camera_leaf); static u8* rendered_faces = NULL; static u32 rendered_faces_capacity = 0; if (rendered_faces_capacity < bsp->num_faces) { + pxl8_debug("Face buffer realloc START - %u bytes", bsp->num_faces); rendered_faces = realloc(rendered_faces, bsp->num_faces); if (!rendered_faces) return; rendered_faces_capacity = bsp->num_faces; - pxl8_debug("Allocated face tracking buffer: %u bytes", bsp->num_faces); + pxl8_debug("Face buffer realloc DONE"); } memset(rendered_faces, 0, bsp->num_faces); + const f32 max_draw_distance = 768.0f; + const f32 max_draw_distance_sq = max_draw_distance * max_draw_distance; + + pxl8_debug("Leaf visibility check START - %u leafs", bsp->num_leafs); + u32 visible_face_count = 0; + for (u32 leaf_id = 0; leaf_id < bsp->num_leafs; leaf_id++) { if (camera_leaf >= 0 && !pxl8_bsp_is_leaf_visible(bsp, camera_leaf, leaf_id)) continue; @@ -479,12 +463,32 @@ void pxl8_bsp_render_solid( if (face_id >= bsp->num_faces) continue; if (rendered_faces[face_id]) continue; + + const pxl8_bsp_face* face = &bsp->faces[face_id]; + + pxl8_vec3 face_center = { + (face->bounds_min.x + face->bounds_max.x) * 0.5f, + (face->bounds_min.y + face->bounds_max.y) * 0.5f, + (face->bounds_min.z + face->bounds_max.z) * 0.5f + }; + + f32 dx = face_center.x - camera_pos.x; + f32 dy = face_center.y - camera_pos.y; + f32 dz = face_center.z - camera_pos.z; + f32 dist_sq = dx * dx + dy * dy + dz * dz; + + if (dist_sq > max_draw_distance_sq) { + continue; + } + rendered_faces[face_id] = 1; + visible_face_count++; pxl8_bsp_render_face(gfx, bsp, face_id, texture_id); } } + pxl8_debug("Leaf visibility check DONE - rendered %u faces", visible_face_count); } void pxl8_bsp_render_wireframe( diff --git a/src/pxl8_bsp.h b/src/pxl8_bsp.h index 74cfbc1..9b1e8c8 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 bounds_min; + pxl8_vec3 bounds_max; } pxl8_bsp_face; typedef struct pxl8_bsp_node { diff --git a/src/pxl8_game.h b/src/pxl8_game.h index 0077915..5a2a1ee 100644 --- a/src/pxl8_game.h +++ b/src/pxl8_game.h @@ -7,15 +7,7 @@ #include "pxl8_types.h" #include "pxl8_ui.h" -typedef enum pxl8_game_result { - PXL8_GAME_CONTINUE, - PXL8_GAME_SUCCESS, - PXL8_GAME_FAILURE -} pxl8_game_result; - typedef struct pxl8_game { - const pxl8_hal* hal; - pxl8_cart* cart; pxl8_color_mode color_mode; pxl8_gfx* gfx; @@ -40,15 +32,3 @@ typedef struct pxl8_game { pxl8_input_state input; pxl8_script_repl* repl; } pxl8_game; - -typedef struct pxl8_game_callbacks { - pxl8_game_result (*init)(pxl8_game* game, i32 argc, char* argv[]); - pxl8_game_result (*update)(pxl8_game* game); - pxl8_game_result (*frame)(pxl8_game* game); - void (*quit)(pxl8_game* game); -} pxl8_game_callbacks; - -pxl8_game_result pxl8_init(pxl8_game* game, i32 argc, char* argv[]); -pxl8_game_result pxl8_update(pxl8_game* game); -pxl8_game_result pxl8_frame(pxl8_game* game); -void pxl8_quit(pxl8_game* game); diff --git a/src/pxl8_gfx.c b/src/pxl8_gfx.c index 1089c64..2de852c 100644 --- a/src/pxl8_gfx.c +++ b/src/pxl8_gfx.c @@ -810,6 +810,14 @@ void pxl8_3d_clear_zbuffer(pxl8_gfx* gfx) { i32 count = gfx->zbuffer_width * gfx->zbuffer_height; const f32 far_z = 1e30f; + static u32 clear_count = 0; + f32 sample_before = gfx->zbuffer[count / 2]; + + if ((clear_count++ % 240) == 0) { + pxl8_debug("Z-buffer clear #%u: %d pixels, sample_before=%.0f, far_z=%.0f", + clear_count, count, sample_before, far_z); + } + #if defined(PXL8_SIMD_NEON) float32x4_t far_vec = vdupq_n_f32(far_z); i32 i = 0; @@ -833,6 +841,11 @@ void pxl8_3d_clear_zbuffer(pxl8_gfx* gfx) { gfx->zbuffer[i] = far_z; } #endif + + if ((clear_count % 240) == 0) { + f32 sample_after = gfx->zbuffer[count / 2]; + pxl8_debug("Z-buffer verified: sample_after=%.0f (should be %.0f)", sample_after, far_z); + } } void pxl8_3d_set_backface_culling(pxl8_gfx* gfx, bool culling) { @@ -1093,7 +1106,7 @@ static inline void pxl8_fill_scanline_textured( 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); + pxl8_pixel_unchecked(gfx, x, y, color); } } } @@ -1137,7 +1150,7 @@ static inline void pxl8_fill_scanline_textured( 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); + pxl8_pixel_unchecked(gfx, x, y, color); } } } diff --git a/src/pxl8_io.h b/src/pxl8_io.h index a0e3635..1198cc7 100644 --- a/src/pxl8_io.h +++ b/src/pxl8_io.h @@ -60,7 +60,9 @@ static inline i32 pxl8_read_i32(pxl8_stream* stream) { static inline f32 pxl8_read_f32(pxl8_stream* stream) { u32 val = pxl8_read_u32(stream); - return *(f32*)&val; + f32 result; + memcpy(&result, &val, sizeof(f32)); + return result; } static inline void pxl8_read_bytes(pxl8_stream* stream, void* dest, u32 count) { diff --git a/src/pxl8_procgen.c b/src/pxl8_procgen.c index 38ff452..414259b 100644 --- a/src/pxl8_procgen.c +++ b/src/pxl8_procgen.c @@ -91,6 +91,15 @@ static void calculate_texture_axes(const pxl8_vec3 normal, pxl8_vec3* u_axis, px } } +static inline void compute_face_bounds(pxl8_bsp_face* face, const pxl8_bsp_vertex* verts, u32 v0, u32 v1, u32 v2, u32 v3) { + face->bounds_min.x = fminf(fminf(verts[v0].position.x, verts[v1].position.x), fminf(verts[v2].position.x, verts[v3].position.x)); + face->bounds_min.y = fminf(fminf(verts[v0].position.y, verts[v1].position.y), fminf(verts[v2].position.y, verts[v3].position.y)); + face->bounds_min.z = fminf(fminf(verts[v0].position.z, verts[v1].position.z), fminf(verts[v2].position.z, verts[v3].position.z)); + face->bounds_max.x = fmaxf(fmaxf(verts[v0].position.x, verts[v1].position.x), fmaxf(verts[v2].position.x, verts[v3].position.x)); + face->bounds_max.y = fmaxf(fmaxf(verts[v0].position.y, verts[v1].position.y), fmaxf(verts[v2].position.y, verts[v3].position.y)); + face->bounds_max.z = fmaxf(fmaxf(verts[v0].position.z, verts[v1].position.z), fmaxf(verts[v2].position.z, verts[v3].position.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 +198,8 @@ static pxl8_result cave_to_bsp(pxl8_bsp* bsp, const cave_grid* grid) { bsp->surfedges[edge_idx + i] = edge_idx + i; } + compute_face_bounds(&bsp->faces[face_idx], bsp->vertices, vert_idx, vert_idx + 1, vert_idx + 2, vert_idx + 3); + vert_idx += 4; edge_idx += 4; face_idx++; @@ -220,6 +231,8 @@ static pxl8_result cave_to_bsp(pxl8_bsp* bsp, const cave_grid* grid) { bsp->surfedges[edge_idx + i] = edge_idx + i; } + compute_face_bounds(&bsp->faces[face_idx], bsp->vertices, vert_idx, vert_idx + 1, vert_idx + 2, vert_idx + 3); + vert_idx += 4; edge_idx += 4; face_idx++; @@ -251,6 +264,8 @@ static pxl8_result cave_to_bsp(pxl8_bsp* bsp, const cave_grid* grid) { bsp->surfedges[edge_idx + i] = edge_idx + i; } + compute_face_bounds(&bsp->faces[face_idx], bsp->vertices, vert_idx, vert_idx + 1, vert_idx + 2, vert_idx + 3); + vert_idx += 4; edge_idx += 4; face_idx++; @@ -282,6 +297,8 @@ static pxl8_result cave_to_bsp(pxl8_bsp* bsp, const cave_grid* grid) { bsp->surfedges[edge_idx + i] = edge_idx + i; } + compute_face_bounds(&bsp->faces[face_idx], bsp->vertices, vert_idx, vert_idx + 1, vert_idx + 2, vert_idx + 3); + vert_idx += 4; edge_idx += 4; face_idx++; @@ -321,6 +338,8 @@ static pxl8_result cave_to_bsp(pxl8_bsp* bsp, const cave_grid* grid) { bsp->surfedges[edge_idx + i] = edge_idx + i; } + compute_face_bounds(&bsp->faces[face_idx], bsp->vertices, vert_idx, vert_idx + 1, vert_idx + 2, vert_idx + 3); + vert_idx += 4; edge_idx += 4; face_idx++; @@ -350,6 +369,8 @@ static pxl8_result cave_to_bsp(pxl8_bsp* bsp, const cave_grid* grid) { bsp->surfedges[edge_idx + i] = edge_idx + i; } + compute_face_bounds(&bsp->faces[face_idx], bsp->vertices, vert_idx, vert_idx + 1, vert_idx + 2, vert_idx + 3); + vert_idx += 4; edge_idx += 4; face_idx++; @@ -440,12 +461,12 @@ void pxl8_procgen_tex(u8* buffer, const pxl8_procgen_tex_params* params) { i32 ix = (i32)((f32)x * params->scale); i32 iy = (i32)((f32)y * params->scale); - u32 block_hash = (ix * 374761393 + iy * 668265263) ^ params->seed; + u32 block_hash = ((u32)ix * 374761393U + (u32)iy * 668265263U) ^ params->seed; block_hash ^= block_hash >> 13; block_hash ^= block_hash << 17; block_hash ^= block_hash >> 5; - u32 pixel_hash = (x * 1597334677 + y * 3812015801) ^ params->seed; + u32 pixel_hash = ((u32)x * 1597334677U + (u32)y * 3812015801U) ^ params->seed; pixel_hash ^= pixel_hash >> 13; pixel_hash ^= pixel_hash << 17; pixel_hash ^= pixel_hash >> 5; diff --git a/src/pxl8_sdl3.c b/src/pxl8_sdl3.c index 6a72f23..ec8d027 100644 --- a/src/pxl8_sdl3.c +++ b/src/pxl8_sdl3.c @@ -1,4 +1,5 @@ #include "pxl8_sdl3.h" +#include "pxl8.h" #include "pxl8_atlas.h" #include "pxl8_game.h" #include "pxl8_macros.h" @@ -238,56 +239,58 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) { return SDL_APP_FAILURE; } - pxl8_game* game = (pxl8_game*)SDL_calloc(1, sizeof(pxl8_game)); - if (!game) { - pxl8_error("Failed to allocate game instance"); + pxl8* system = (pxl8*)SDL_calloc(1, sizeof(pxl8)); + if (!system) { + pxl8_error("Failed to allocate pxl8 instance"); SDL_Quit(); return SDL_APP_FAILURE; } - game->hal = &pxl8_hal_sdl3; + system->hal = &pxl8_hal_sdl3; - pxl8_game_result result = pxl8_init(game, argc, argv); - if (result != PXL8_GAME_CONTINUE) { - SDL_free(game); + pxl8_result result = pxl8_init(system, argc, argv); + if (result != PXL8_CONTINUE) { + SDL_free(system); SDL_Quit(); return SDL_APP_FAILURE; } - *appstate = game; + *appstate = system; return SDL_APP_CONTINUE; } SDL_AppResult SDL_AppIterate(void* appstate) { - pxl8_game* game = (pxl8_game*)appstate; + pxl8* system = (pxl8*)appstate; - if (!game) { + if (!system) { return SDL_APP_FAILURE; } - pxl8_game_result update_result = pxl8_update(game); - if (update_result == PXL8_GAME_FAILURE) { + pxl8_result update_result = pxl8_update(system); + if (update_result == PXL8_FAILURE) { return SDL_APP_FAILURE; } - if (update_result == PXL8_GAME_SUCCESS) { + if (update_result == PXL8_SUCCESS) { return SDL_APP_SUCCESS; } - pxl8_game_result frame_result = pxl8_frame(game); - if (frame_result == PXL8_GAME_FAILURE) { + pxl8_result frame_result = pxl8_frame(system); + if (frame_result == PXL8_FAILURE) { return SDL_APP_FAILURE; } - return (frame_result == PXL8_GAME_SUCCESS) ? SDL_APP_SUCCESS : SDL_APP_CONTINUE; + return (frame_result == PXL8_SUCCESS) ? SDL_APP_SUCCESS : SDL_APP_CONTINUE; } SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) { - pxl8_game* game = (pxl8_game*)appstate; + pxl8* system = (pxl8*)appstate; - if (!game) { + if (!system) { return SDL_APP_CONTINUE; } + pxl8_game* game = &system->game; + switch (event->type) { case SDL_EVENT_QUIT: game->running = false; @@ -379,10 +382,10 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) { void SDL_AppQuit(void* appstate, SDL_AppResult result) { (void)result; - pxl8_game* game = (pxl8_game*)appstate; - if (game) { - pxl8_quit(game); - SDL_free(game); + pxl8* system = (pxl8*)appstate; + if (system) { + pxl8_quit(system); + SDL_free(system); } SDL_Quit(); diff --git a/src/pxl8_types.h b/src/pxl8_types.h index e07536f..a013962 100644 --- a/src/pxl8_types.h +++ b/src/pxl8_types.h @@ -40,6 +40,9 @@ typedef enum pxl8_resolution { typedef enum pxl8_result { PXL8_OK = 0, + PXL8_CONTINUE, + PXL8_SUCCESS, + PXL8_FAILURE, PXL8_ERROR_ASE_INVALID_FRAME_MAGIC, PXL8_ERROR_ASE_INVALID_MAGIC, PXL8_ERROR_ASE_MALFORMED_CHUNK, diff --git a/src/pxl8_world.c b/src/pxl8_world.c index dd0ca41..00b472e 100644 --- a/src/pxl8_world.c +++ b/src/pxl8_world.c @@ -86,7 +86,15 @@ void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos) { static bool texture_generated = false; static u32 wall_texture_id = 0; + static bool first_render = true; + + if (first_render) { + pxl8_debug("World render ENTRY - first render"); + first_render = false; + } + if (!texture_generated) { + pxl8_debug("Texture generation START"); u8 texture_data[64 * 64]; pxl8_procgen_tex_params tex_params = { @@ -100,11 +108,14 @@ void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos) { }; pxl8_procgen_tex(texture_data, &tex_params); + pxl8_debug("Texture generation DONE, creating GPU texture"); pxl8_result result = pxl8_gfx_create_texture(gfx, texture_data, 64, 64); if (result >= 0) { wall_texture_id = (u32)result; + pxl8_debug("Uploading atlas START"); pxl8_gfx_upload_atlas(gfx); + pxl8_debug("Uploading atlas DONE"); texture_generated = true; pxl8_info("Generated stone texture with ID: %u", wall_texture_id); } else { @@ -112,11 +123,13 @@ void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos) { } } + pxl8_debug("BSP render START"); if (world->wireframe) { pxl8_bsp_render_wireframe(gfx, &world->bsp, camera_pos, world->wireframe_color); } else { pxl8_bsp_render_solid(gfx, &world->bsp, camera_pos, wall_texture_id); } + pxl8_debug("BSP render DONE"); } void pxl8_world_unload(pxl8_world* world) {