improve 3d renderer

This commit is contained in:
asrael 2025-11-19 22:18:08 -06:00
parent f19b06d705
commit 59be43d80e
17 changed files with 556 additions and 486 deletions

View file

@ -39,6 +39,8 @@
(pxl8.transition_update transition dt) (pxl8.transition_update transition dt)
(when (pxl8.transition_is_complete transition) (when (pxl8.transition_is_complete transition)
(when transition-pending (when transition-pending
(when (and (= active-demo :worldgen) (not= transition-pending :worldgen))
(pxl8.set_relative_mouse_mode false))
(set active-demo transition-pending) (set active-demo transition-pending)
(set transition-pending nil) (set transition-pending nil)
(when (= active-demo :fire) (set fire-init? false)) (when (= active-demo :fire) (set fire-init? false))

View file

@ -5,7 +5,7 @@
(var angle-y 0) (var angle-y 0)
(var angle-z 0) (var angle-z 0)
(var auto-rotate? true) (var auto-rotate? true)
(var orthographic? true) (var orthographic? false)
(var wireframe? true) (var wireframe? true)
(var time 0) (var time 0)
(var zoom 5.0) (var zoom 5.0)
@ -27,7 +27,7 @@
(set angle-y 0) (set angle-y 0)
(set angle-z 0) (set angle-z 0)
(set auto-rotate? true) (set auto-rotate? true)
(set orthographic? true) (set orthographic? false)
(set wireframe? true) (set wireframe? true)
(set time 0) (set time 0)
(set zoom 5.0) (set zoom 5.0)
@ -169,12 +169,11 @@
(fn draw-cube [pos scale rotation-offset] (fn draw-cube [pos scale rotation-offset]
(let [[x y z] pos (let [[x y z] pos
model (-> (pxl8.mat4_identity) model (-> (pxl8.mat4_translate x y z)
(pxl8.mat4_multiply (pxl8.mat4_scale scale scale scale))
(pxl8.mat4_multiply (pxl8.mat4_rotate_x (+ angle-x rotation-offset)))
(pxl8.mat4_multiply (pxl8.mat4_rotate_y (+ angle-y (* rotation-offset 1.3))))
(pxl8.mat4_multiply (pxl8.mat4_rotate_z (+ angle-z (* rotation-offset 0.7)))) (pxl8.mat4_multiply (pxl8.mat4_rotate_z (+ angle-z (* rotation-offset 0.7))))
(pxl8.mat4_multiply (pxl8.mat4_translate x y z)))] (pxl8.mat4_multiply (pxl8.mat4_rotate_y (+ angle-y (* rotation-offset 1.3))))
(pxl8.mat4_multiply (pxl8.mat4_rotate_x (+ angle-x rotation-offset)))
(pxl8.mat4_multiply (pxl8.mat4_scale scale scale scale)))]
(pxl8.set_model model)) (pxl8.set_model model))
(let [vertices (make-cube-vertices)] (let [vertices (make-cube-vertices)]

View file

@ -3,21 +3,23 @@
(local bob-amount 4.0) (local bob-amount 4.0)
(local bob-speed 8.0) (local bob-speed 8.0)
(var bob-time 0) (var bob-time 0)
(var cam-pitch 0)
(var cam-x 1000) (var cam-x 1000)
(var cam-y 64) (var cam-y 64)
(var cam-z 1000)
(var cam-pitch 0)
(var cam-yaw 0) (var cam-yaw 0)
(var cam-z 1000)
(local cell-size 64) (local cell-size 64)
(local gravity -800) (local gravity -800)
(local grid-size 32) (local grid-size 32)
(var grounded? true) (var grounded? true)
(local ground-y 64) (local ground-y 64)
(local jump-force 175) (local jump-force 175)
(var land-squash 0)
(local land-squash-amount -4)
(local land-recovery-speed 20) (local land-recovery-speed 20)
(local land-squash-amount -4)
(var land-squash 0)
(local max-pitch 1.5) (local max-pitch 1.5)
(var mouse-look? true)
(local mouse-sensitivity 0.008)
(local move-speed 200) (local move-speed 200)
(local turn-speed 2.0) (local turn-speed 2.0)
(var velocity-y 0) (var velocity-y 0)
@ -66,6 +68,11 @@
(pxl8.error (.. "Failed to apply textures - result: " result)))))))) (pxl8.error (.. "Failed to apply textures - result: " result))))))))
(fn update [dt] (fn update [dt]
(pxl8.set_relative_mouse_mode mouse-look?)
(when (pxl8.key_pressed "escape")
(set mouse-look? (not mouse-look?)))
(when (pxl8.world_is_loaded world) (when (pxl8.world_is_loaded world)
(let [forward-x (- (math.sin cam-yaw)) (let [forward-x (- (math.sin cam-yaw))
forward-z (- (math.cos cam-yaw)) forward-z (- (math.cos cam-yaw))
@ -84,10 +91,10 @@
(when (pxl8.key_down "s") (when (pxl8.key_down "s")
(set move-forward (- move-forward 1))) (set move-forward (- move-forward 1)))
(when (pxl8.key_down "q") (when (pxl8.key_down "a")
(set move-right (- move-right 1))) (set move-right (- move-right 1)))
(when (pxl8.key_down "e") (when (pxl8.key_down "d")
(set move-right (+ move-right 1))) (set move-right (+ move-right 1)))
(set moving (or (not= move-forward 0) (not= move-right 0))) (set moving (or (not= move-forward 0) (not= move-right 0)))
@ -107,16 +114,24 @@
(set cam-x new-x) (set cam-x new-x)
(set cam-z new-z)) (set cam-z new-z))
(when (or (pxl8.key_down "left") (pxl8.key_down "a")) (when mouse-look?
(let [dx (pxl8.mouse_dx)
dy (pxl8.mouse_dy)]
(set cam-yaw (- cam-yaw (* dx mouse-sensitivity)))
(set cam-pitch (math.max (- max-pitch)
(math.min max-pitch
(- cam-pitch (* dy mouse-sensitivity)))))))
(when (and (not mouse-look?) (pxl8.key_down "left"))
(set cam-yaw (+ cam-yaw (* turn-speed dt)))) (set cam-yaw (+ cam-yaw (* turn-speed dt))))
(when (or (pxl8.key_down "right") (pxl8.key_down "d")) (when (and (not mouse-look?) (pxl8.key_down "right"))
(set cam-yaw (- cam-yaw (* turn-speed dt)))) (set cam-yaw (- cam-yaw (* turn-speed dt))))
(when (pxl8.key_down "up") (when (and (not mouse-look?) (pxl8.key_down "up"))
(set cam-pitch (math.min max-pitch (+ cam-pitch (* turn-speed dt))))) (set cam-pitch (math.min max-pitch (+ cam-pitch (* turn-speed dt)))))
(when (pxl8.key_down "down") (when (and (not mouse-look?) (pxl8.key_down "down"))
(set cam-pitch (math.max (- max-pitch) (- cam-pitch (* turn-speed dt))))) (set cam-pitch (math.max (- max-pitch) (- cam-pitch (* turn-speed dt)))))
(when (and (pxl8.key_pressed "space") grounded?) (when (and (pxl8.key_pressed "space") grounded?)
@ -170,10 +185,10 @@
(pxl8.world_render world [cam-x eye-y cam-z]) (pxl8.world_render world [cam-x eye-y cam-z])
(pxl8.text (.. "Pos: " (string.format "%.0f" cam-x) "," (pxl8.text (.. "fps: " (string.format "%.1f" (pxl8.get_fps))) 5 5 12)
(pxl8.text (.. "pos: " (string.format "%.0f" cam-x) ","
(string.format "%.0f" cam-y) "," (string.format "%.0f" cam-y) ","
(string.format "%.0f" cam-z)) 10 25 12) (string.format "%.0f" cam-z)) 5 15 12))))
(pxl8.text (.. "FPS: " (string.format "%.1f" (pxl8.get_fps))) 10 40 12))))
{:init init {:init init
:update update :update update

View file

@ -45,10 +45,13 @@ pxl8.gfx_fade_palette = gfx2d.fade_palette
pxl8.key_down = input.key_down pxl8.key_down = input.key_down
pxl8.key_pressed = input.key_pressed pxl8.key_pressed = input.key_pressed
pxl8.key_released = input.key_released pxl8.key_released = input.key_released
pxl8.mouse_dx = input.mouse_dx
pxl8.mouse_dy = input.mouse_dy
pxl8.mouse_wheel_x = input.mouse_wheel_x pxl8.mouse_wheel_x = input.mouse_wheel_x
pxl8.mouse_wheel_y = input.mouse_wheel_y pxl8.mouse_wheel_y = input.mouse_wheel_y
pxl8.mouse_x = input.mouse_x pxl8.mouse_x = input.mouse_x
pxl8.mouse_y = input.mouse_y pxl8.mouse_y = input.mouse_y
pxl8.set_relative_mouse_mode = input.set_relative_mouse_mode
pxl8.vfx_raster_bars = vfx.raster_bars pxl8.vfx_raster_bars = vfx.raster_bars
pxl8.vfx_plasma = vfx.plasma pxl8.vfx_plasma = vfx.plasma

View file

@ -32,4 +32,16 @@ function input.mouse_y()
return C.pxl8_mouse_y(core.input) return C.pxl8_mouse_y(core.input)
end end
function input.mouse_dx()
return C.pxl8_mouse_dx(core.input)
end
function input.mouse_dy()
return C.pxl8_mouse_dy(core.input)
end
function input.set_relative_mouse_mode(enabled)
C.pxl8_set_relative_mouse_mode(core.sys, enabled)
end
return input return input

View file

@ -253,9 +253,6 @@ pxl8_result pxl8_update(pxl8* sys) {
} }
if (game->ui) { if (game->ui) {
i32 render_width, render_height;
pxl8_gfx_get_resolution_dimensions(game->resolution, &render_width, &render_height);
pxl8_ui_input_mousemove(game->ui, game->input.mouse_x, game->input.mouse_y); pxl8_ui_input_mousemove(game->ui, game->input.mouse_x, game->input.mouse_y);
if (game->input.mouse_wheel_x != 0 || game->input.mouse_wheel_y != 0) { if (game->input.mouse_wheel_x != 0 || game->input.mouse_wheel_y != 0) {
@ -306,25 +303,23 @@ pxl8_result pxl8_frame(pxl8* sys) {
} else { } else {
pxl8_clear(game->gfx, 32); pxl8_clear(game->gfx, 32);
i32 render_width, render_height; pxl8_size render_size = pxl8_get_resolution_dimensions(game->resolution);
pxl8_gfx_get_resolution_dimensions(game->resolution, &render_width, &render_height);
for (i32 y = 0; y < render_height; y += 24) { for (i32 y = 0; y < render_size.h; y += 24) {
for (i32 x = 0; x < render_width; x += 32) { for (i32 x = 0; x < render_size.w; x += 32) {
u32 color = ((x / 32) + (y / 24) + (i32)(game->time * 2)) % 8; u32 color = ((x / 32) + (y / 24) + (i32)(game->time * 2)) % 8;
pxl8_rect_fill(game->gfx, x, y, 31, 23, color); pxl8_rect_fill(game->gfx, x, y, 31, 23, color);
} }
} }
} }
i32 render_width, render_height; pxl8_size render_size = pxl8_get_resolution_dimensions(game->resolution);
pxl8_gfx_get_resolution_dimensions(game->resolution, &render_width, &render_height);
if (game->ui) { if (game->ui) {
pxl8_ui_frame_end(game->ui); pxl8_ui_frame_end(game->ui);
} }
pxl8_gfx_set_viewport(game->gfx, pxl8_gfx_viewport(bounds, render_width, render_height)); pxl8_gfx_set_viewport(game->gfx, pxl8_gfx_viewport(bounds, render_size.w, render_size.h));
pxl8_gfx_upload_framebuffer(game->gfx); pxl8_gfx_upload_framebuffer(game->gfx);
pxl8_gfx_upload_atlas(game->gfx); pxl8_gfx_upload_atlas(game->gfx);
pxl8_gfx_present(game->gfx); pxl8_gfx_present(game->gfx);
@ -333,8 +328,12 @@ pxl8_result pxl8_frame(pxl8* sys) {
memset(game->input.keys_released, 0, sizeof(game->input.keys_released)); memset(game->input.keys_released, 0, sizeof(game->input.keys_released));
memset(game->input.mouse_buttons_pressed, 0, sizeof(game->input.mouse_buttons_pressed)); memset(game->input.mouse_buttons_pressed, 0, sizeof(game->input.mouse_buttons_pressed));
memset(game->input.mouse_buttons_released, 0, sizeof(game->input.mouse_buttons_released)); memset(game->input.mouse_buttons_released, 0, sizeof(game->input.mouse_buttons_released));
game->input.mouse_dx = 0;
game->input.mouse_dy = 0;
game->input.mouse_wheel_x = 0; game->input.mouse_wheel_x = 0;
game->input.mouse_wheel_y = 0; game->input.mouse_wheel_y = 0;
game->input.mouse_x = 0;
game->input.mouse_y = 0;
game->frame_count++; game->frame_count++;
@ -378,14 +377,46 @@ f32 pxl8_get_fps(const pxl8* sys) {
return (sys && sys->game) ? sys->game->fps : 0.0f; return (sys && sys->game) ? sys->game->fps : 0.0f;
} }
pxl8_gfx* pxl8_get_gfx(pxl8* sys) { pxl8_gfx* pxl8_get_gfx(const pxl8* sys) {
return (sys && sys->game) ? sys->game->gfx : NULL; return (sys && sys->game) ? sys->game->gfx : NULL;
} }
pxl8_input_state* pxl8_get_input(pxl8* sys) { pxl8_input_state* pxl8_get_input(const pxl8* sys) {
return (sys && sys->game) ? &sys->game->input : NULL; return (sys && sys->game) ? &sys->game->input : NULL;
} }
pxl8_resolution pxl8_get_resolution(pxl8* sys) { pxl8_resolution pxl8_get_resolution(const pxl8* sys) {
return (sys && sys->game) ? sys->game->resolution : PXL8_RESOLUTION_640x360; return (sys && sys->game) ? sys->game->resolution : PXL8_RESOLUTION_640x360;
} }
void pxl8_set_relative_mouse_mode(pxl8* sys, bool enabled) {
if (!sys || !sys->hal || !sys->hal->set_relative_mouse_mode) return;
sys->hal->set_relative_mouse_mode(sys->platform_data, enabled);
if (sys->game) {
sys->game->input.mouse_relative_mode = enabled;
}
}
u32 pxl8_get_palette_size(pxl8_color_mode mode) {
switch (mode) {
case PXL8_COLOR_MODE_HICOLOR: return 0;
case PXL8_COLOR_MODE_FAMI: return 64;
case PXL8_COLOR_MODE_MEGA: return 512;
case PXL8_COLOR_MODE_GBA: return 32768;
case PXL8_COLOR_MODE_SNES: return 32768;
default: return 256;
}
}
pxl8_size pxl8_get_resolution_dimensions(pxl8_resolution resolution) {
switch (resolution) {
case PXL8_RESOLUTION_240x160: return (pxl8_size){240, 160};
case PXL8_RESOLUTION_320x180: return (pxl8_size){320, 180};
case PXL8_RESOLUTION_320x240: return (pxl8_size){320, 240};
case PXL8_RESOLUTION_640x360: return (pxl8_size){640, 360};
case PXL8_RESOLUTION_640x480: return (pxl8_size){640, 480};
case PXL8_RESOLUTION_800x600: return (pxl8_size){800, 600};
case PXL8_RESOLUTION_960x540: return (pxl8_size){960, 540};
default: return (pxl8_size){640, 360};
}
}

View file

@ -55,6 +55,44 @@ static bool validate_chunk(const pxl8_bsp_chunk* chunk, u32 element_size, size_t
return true; return true;
} }
static inline bool pxl8_bsp_get_edge_vertex(const pxl8_bsp* bsp, i32 surfedge_idx, u32* out_vert_idx) {
if (surfedge_idx >= (i32)bsp->num_surfedges) return false;
i32 edge_idx = bsp->surfedges[surfedge_idx];
u32 vertex_index;
if (edge_idx >= 0) {
if ((u32)edge_idx >= bsp->num_edges) return false;
vertex_index = 0;
} else {
edge_idx = -edge_idx;
if ((u32)edge_idx >= bsp->num_edges) return false;
vertex_index = 1;
}
*out_vert_idx = bsp->edges[edge_idx].vertex[vertex_index];
return *out_vert_idx < bsp->num_vertices;
}
static inline bool pxl8_bsp_get_edge_vertices(const pxl8_bsp* bsp, i32 surfedge_idx, u32* out_v0_idx, u32* out_v1_idx) {
if (surfedge_idx >= (i32)bsp->num_surfedges) return false;
i32 edge_idx = bsp->surfedges[surfedge_idx];
if (edge_idx >= 0) {
if ((u32)edge_idx >= bsp->num_edges) return false;
*out_v0_idx = bsp->edges[edge_idx].vertex[0];
*out_v1_idx = bsp->edges[edge_idx].vertex[1];
} else {
edge_idx = -edge_idx;
if ((u32)edge_idx >= bsp->num_edges) return false;
*out_v0_idx = bsp->edges[edge_idx].vertex[1];
*out_v1_idx = bsp->edges[edge_idx].vertex[0];
}
return *out_v0_idx < bsp->num_vertices && *out_v1_idx < bsp->num_vertices;
}
pxl8_result pxl8_bsp_load(const char* path, pxl8_bsp* bsp) { pxl8_result pxl8_bsp_load(const char* path, pxl8_bsp* bsp) {
if (!path || !bsp) return PXL8_ERROR_INVALID_ARGUMENT; if (!path || !bsp) return PXL8_ERROR_INVALID_ARGUMENT;
@ -280,21 +318,9 @@ pxl8_result pxl8_bsp_load(const char* path, pxl8_bsp* bsp) {
for (u32 j = 0; j < face->num_edges; j++) { for (u32 j = 0; j < face->num_edges; j++) {
i32 surfedge_idx = face->first_edge + 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; u32 vert_idx;
if (edge_idx >= 0) { if (!pxl8_bsp_get_edge_vertex(bsp, surfedge_idx, &vert_idx)) continue;
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; pxl8_vec3 v = bsp->vertices[vert_idx].position;
@ -392,21 +418,10 @@ void pxl8_bsp_render_face(pxl8_gfx* gfx, const pxl8_bsp* bsp, u32 face_id, u32 t
for (u32 i = 0; i < face->num_edges && num_verts < 64; i++) { for (u32 i = 0; i < face->num_edges && num_verts < 64; i++) {
i32 surfedge_idx = face->first_edge + 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; u32 vert_idx;
if (edge_idx >= 0) { if (!pxl8_bsp_get_edge_vertex(bsp, surfedge_idx, &vert_idx)) continue;
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;
verts[num_verts++] = bsp->vertices[vert_idx].position; verts[num_verts++] = bsp->vertices[vert_idx].position;
} }
@ -494,6 +509,7 @@ void pxl8_bsp_render_textured(
pxl8_bsp_render_face(gfx, bsp, face_id, texture_id); pxl8_bsp_render_face(gfx, bsp, face_id, texture_id);
} }
} }
} }
void pxl8_bsp_render_wireframe( void pxl8_bsp_render_wireframe(
@ -532,21 +548,9 @@ void pxl8_bsp_render_wireframe(
if (surfedge_idx >= (i32)bsp->num_surfedges || if (surfedge_idx >= (i32)bsp->num_surfedges ||
next_surfedge_idx >= (i32)bsp->num_surfedges) continue; next_surfedge_idx >= (i32)bsp->num_surfedges) continue;
i32 edge_idx = bsp->surfedges[surfedge_idx];
u32 v0_idx, v1_idx; u32 v0_idx, v1_idx;
if (edge_idx >= 0) { if (!pxl8_bsp_get_edge_vertices(bsp, surfedge_idx, &v0_idx, &v1_idx)) continue;
if ((u32)edge_idx >= bsp->num_edges) continue;
v0_idx = bsp->edges[edge_idx].vertex[0];
v1_idx = bsp->edges[edge_idx].vertex[1];
} else {
edge_idx = -edge_idx;
if ((u32)edge_idx >= bsp->num_edges) continue;
v0_idx = bsp->edges[edge_idx].vertex[1];
v1_idx = bsp->edges[edge_idx].vertex[0];
}
if (v0_idx >= bsp->num_vertices || v1_idx >= bsp->num_vertices) continue;
pxl8_vec3 p0 = bsp->vertices[v0_idx].position; pxl8_vec3 p0 = bsp->vertices[v0_idx].position;
pxl8_vec3 p1 = bsp->vertices[v1_idx].position; pxl8_vec3 p1 = bsp->vertices[v1_idx].position;

View file

@ -10,6 +10,7 @@
#include "pxl8_hal.h" #include "pxl8_hal.h"
#include "pxl8_macros.h" #include "pxl8_macros.h"
#include "pxl8_math.h" #include "pxl8_math.h"
#include "pxl8_sys.h"
#include "pxl8_types.h" #include "pxl8_types.h"
typedef struct pxl8_sprite_cache_entry { typedef struct pxl8_sprite_cache_entry {
@ -52,8 +53,6 @@ struct pxl8_gfx {
bool affine_textures; bool affine_textures;
u32 frame_triangle_count;
u32 frame_pixel_count;
}; };
static inline void pxl8_color_unpack(u32 color, u8* r, u8* g, u8* b, u8* a) { static inline void pxl8_color_unpack(u32 color, u8* r, u8* g, u8* b, u8* a) {
@ -71,38 +70,6 @@ static inline u8 pxl8_color_lerp_channel(u8 c1, u8 c2, f32 t) {
return c1 + (i32)((c2 - c1) * t); return c1 + (i32)((c2 - c1) * t);
} }
static u32 pxl8_get_palette_size(pxl8_color_mode mode) {
switch (mode) {
case PXL8_COLOR_MODE_HICOLOR: return 0;
case PXL8_COLOR_MODE_FAMI: return 64;
case PXL8_COLOR_MODE_MEGA: return 512;
case PXL8_COLOR_MODE_GBA: return 32768;
case PXL8_COLOR_MODE_SNES: return 32768;
default: return 256;
}
}
void pxl8_gfx_get_resolution_dimensions(pxl8_resolution resolution, i32* width, i32* height) {
switch (resolution) {
case PXL8_RESOLUTION_240x160:
*width = 240; *height = 160; break;
case PXL8_RESOLUTION_320x180:
*width = 320; *height = 180; break;
case PXL8_RESOLUTION_320x240:
*width = 320; *height = 240; break;
case PXL8_RESOLUTION_640x360:
*width = 640; *height = 360; break;
case PXL8_RESOLUTION_640x480:
*width = 640; *height = 480; break;
case PXL8_RESOLUTION_800x600:
*width = 800; *height = 600; break;
case PXL8_RESOLUTION_960x540:
*width = 960; *height = 540; break;
default:
*width = 640; *height = 360; break;
}
}
pxl8_bounds pxl8_gfx_get_bounds(pxl8_gfx* gfx) { pxl8_bounds pxl8_gfx_get_bounds(pxl8_gfx* gfx) {
pxl8_bounds bounds = {0}; pxl8_bounds bounds = {0};
if (!gfx) { if (!gfx) {
@ -149,11 +116,9 @@ pxl8_gfx* pxl8_gfx_create(
gfx->platform_data = platform_data; gfx->platform_data = platform_data;
gfx->color_mode = mode; gfx->color_mode = mode;
pxl8_gfx_get_resolution_dimensions( pxl8_size size = pxl8_get_resolution_dimensions(resolution);
resolution, gfx->framebuffer_width = size.w;
&gfx->framebuffer_width, gfx->framebuffer_height = size.h;
&gfx->framebuffer_height
);
if (!gfx->platform_data) { if (!gfx->platform_data) {
pxl8_error("Platform data cannot be NULL"); pxl8_error("Platform data cannot be NULL");
@ -404,18 +369,6 @@ void pxl8_gfx_project(pxl8_gfx* gfx, f32 left, f32 right, f32 top, f32 bottom) {
void pxl8_clear(pxl8_gfx* gfx, u32 color) { void pxl8_clear(pxl8_gfx* gfx, u32 color) {
if (!gfx || !gfx->framebuffer) return; if (!gfx || !gfx->framebuffer) return;
static u32 frame_count = 0;
if (gfx->frame_triangle_count > 0) {
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_trace("Frame triangles: %u, pixels: %u, overdraw: %.2fx",
gfx->frame_triangle_count, gfx->frame_pixel_count, overdraw);
}
frame_count++;
}
gfx->frame_triangle_count = 0;
gfx->frame_pixel_count = 0;
i32 bytes_per_pixel = (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) ? 4 : 1; i32 bytes_per_pixel = (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) ? 4 : 1;
i32 size = gfx->framebuffer_width * gfx->framebuffer_height; i32 size = gfx->framebuffer_width * gfx->framebuffer_height;
@ -903,7 +856,7 @@ void pxl8_3d_draw_line_3d(pxl8_gfx* gfx, pxl8_vec3 p0, pxl8_vec3 p1, u32 color)
pxl8_line(gfx, x0, y0, x1, y1, color); pxl8_line(gfx, x0, y0, x1, y1, color);
} }
static inline void pxl8_fill_scanline_hicolor(pxl8_gfx* gfx, i32 y, i32 xs, i32 xe, f32 z0, f32 z1, u32 color) { static inline void pxl8_fill_scanline(pxl8_gfx* gfx, i32 y, i32 xs, i32 xe, f32 z0, f32 z1, u32 color) {
if (y < 0 || y >= gfx->framebuffer_height) return; if (y < 0 || y >= gfx->framebuffer_height) return;
if (xs > xe) { if (xs > xe) {
i32 tmp = xs; xs = xe; xe = tmp; i32 tmp = xs; xs = xe; xe = tmp;
@ -915,109 +868,57 @@ static inline void pxl8_fill_scanline_hicolor(pxl8_gfx* gfx, i32 y, i32 xs, i32
if (xs > xe) return; if (xs > xe) return;
i32 width = xe - xs; i32 width = xe - xs;
if (width == 0) {
i32 idx = y * gfx->zbuffer_width + xs;
if (z0 <= gfx->zbuffer[idx]) {
gfx->zbuffer[idx] = z0;
i32 fb_idx = y * gfx->framebuffer_width + xs;
((u32*)gfx->framebuffer)[fb_idx] = color;
}
return;
}
f32 dz = (z1 - z0) / (f32)width;
f32 z = z0;
i32 zbuf_offset = y * gfx->zbuffer_width; i32 zbuf_offset = y * gfx->zbuffer_width;
i32 fb_offset = y * gfx->framebuffer_width; i32 fb_offset = y * gfx->framebuffer_width;
u32* fb = (u32*)gfx->framebuffer;
for (i32 x = xs; x <= xe; x++) {
i32 idx = zbuf_offset + x;
if (z <= gfx->zbuffer[idx]) {
gfx->zbuffer[idx] = z;
fb[fb_offset + x] = color;
}
z += dz;
}
}
static inline void pxl8_fill_scanline_indexed(pxl8_gfx* gfx, i32 y, i32 xs, i32 xe, f32 z0, f32 z1, u32 color) {
if (y < 0 || y >= gfx->framebuffer_height) return;
if (xs > xe) {
i32 tmp = xs; xs = xe; xe = tmp;
f32 tmpz = z0; z0 = z1; z1 = tmpz;
}
if (xs < 0) xs = 0;
if (xe >= gfx->framebuffer_width) xe = gfx->framebuffer_width - 1;
if (xs > xe) return;
i32 width = xe - xs;
u8 idx_color = color & 0xFF;
if (width == 0) {
i32 idx = y * gfx->zbuffer_width + xs;
if (z0 <= gfx->zbuffer[idx]) {
gfx->zbuffer[idx] = z0;
i32 fb_idx = y * gfx->framebuffer_width + xs;
gfx->framebuffer[fb_idx] = idx_color;
}
return;
}
f32 dz = (z1 - z0) / (f32)width;
f32 z = z0;
i32 zbuf_offset = y * gfx->zbuffer_width;
i32 fb_offset = y * gfx->framebuffer_width;
for (i32 x = xs; x <= xe; x++) {
i32 idx = zbuf_offset + x;
if (z <= gfx->zbuffer[idx]) {
gfx->zbuffer[idx] = z;
gfx->framebuffer[fb_offset + x] = idx_color;
}
z += dz;
}
}
static inline u32 pxl8_sample_texture(pxl8_gfx* gfx, u32 texture_id, f32 u, f32 v) {
if (!gfx->atlas) return 0;
const pxl8_atlas_entry* entry = pxl8_atlas_get_entry(gfx->atlas, texture_id);
if (!entry || !entry->active) {
static bool warned = false;
if (!warned) {
pxl8_warn("Texture sampling failed: texture_id=%u entry=%p active=%d",
texture_id, (void*)entry, entry ? entry->active : 0);
warned = true;
}
return 0;
}
i32 tex_u = (i32)(u * entry->w);
i32 tex_v = (i32)(v * entry->h);
i32 tx = tex_u % entry->w;
if (tx < 0) tx += entry->w;
i32 ty = tex_v % entry->h;
if (ty < 0) ty += entry->h;
i32 atlas_x = entry->x + tx;
i32 atlas_y = entry->y + ty;
u32 atlas_width = pxl8_atlas_get_width(gfx->atlas);
const u8* atlas_pixels = pxl8_atlas_get_pixels(gfx->atlas);
i32 idx = atlas_y * atlas_width + atlas_x;
if (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) { if (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) {
return ((const u32*)atlas_pixels)[idx]; u32* fb = (u32*)gfx->framebuffer;
if (width == 0) {
i32 idx = zbuf_offset + xs;
if (z0 <= gfx->zbuffer[idx]) {
gfx->zbuffer[idx] = z0;
fb[fb_offset + xs] = color;
}
return;
}
f32 dz = (z1 - z0) / (f32)width;
f32 z = z0;
for (i32 x = xs; x <= xe; x++) {
i32 idx = zbuf_offset + x;
if (z <= gfx->zbuffer[idx]) {
gfx->zbuffer[idx] = z;
fb[fb_offset + x] = color;
}
z += dz;
}
} else { } else {
return atlas_pixels[idx]; u8 idx_color = color & 0xFF;
if (width == 0) {
i32 idx = zbuf_offset + xs;
if (z0 <= gfx->zbuffer[idx]) {
gfx->zbuffer[idx] = z0;
gfx->framebuffer[fb_offset + xs] = idx_color;
}
return;
}
f32 dz = (z1 - z0) / (f32)width;
f32 z = z0;
for (i32 x = xs; x <= xe; x++) {
i32 idx = zbuf_offset + x;
if (z <= gfx->zbuffer[idx]) {
gfx->zbuffer[idx] = z;
gfx->framebuffer[fb_offset + x] = idx_color;
}
z += dz;
}
} }
} }
static inline void pxl8_fill_scanline_textured_hicolor( static inline void pxl8_fill_scanline_textured(
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,
@ -1034,22 +935,58 @@ static inline void pxl8_fill_scanline_textured_hicolor(
tmpf = w0; w0 = w1; w1 = tmpf; tmpf = w0; w0 = w1; w1 = tmpf;
} }
const pxl8_atlas_entry* entry = pxl8_atlas_get_entry(gfx->atlas, texture_id);
if (!entry || !entry->active) return;
const u8* atlas_pixels = pxl8_atlas_get_pixels(gfx->atlas);
u32 atlas_width = pxl8_atlas_get_width(gfx->atlas);
i32 tex_w = entry->w;
i32 tex_h = entry->h;
bool is_pow2 = ((tex_w & (tex_w - 1)) == 0) && ((tex_h & (tex_h - 1)) == 0) && (tex_w == tex_h);
i32 tex_mask = tex_w - 1;
i32 atlas_x_base = entry->x;
i32 atlas_y_base = entry->y;
bool is_hicolor = (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR);
bool affine = gfx->affine_textures;
i32 span = xe - xs; i32 span = xe - xs;
if (span <= 0) { if (span <= 0) {
if (xs >= 0 && xs < gfx->framebuffer_width) { if (xs >= 0 && xs < gfx->framebuffer_width) {
i32 idx = y * gfx->zbuffer_width + xs; i32 idx = y * gfx->zbuffer_width + xs;
if (z0 <= gfx->zbuffer[idx]) { if (z0 <= gfx->zbuffer[idx]) {
gfx->frame_pixel_count++; f32 tex_u = u0, tex_v = v0;
f32 u = u0, v = v0; if (!affine && fabsf(w0) > 1e-6f) {
if (!gfx->affine_textures && fabsf(w0) > 1e-6f) {
f32 perspective_w = 1.0f / w0; f32 perspective_w = 1.0f / w0;
u *= perspective_w; tex_u *= perspective_w;
v *= perspective_w; tex_v *= perspective_w;
} }
u32 color = pxl8_sample_texture(gfx, texture_id, u, v);
if (color & 0xFF000000) { i32 tu = (i32)(tex_u * tex_w);
gfx->zbuffer[idx] = z0; i32 tv = (i32)(tex_v * tex_h);
pxl8_pixel(gfx, xs, y, color); i32 tx, ty;
if (is_pow2) {
tx = tu & tex_mask;
ty = tv & tex_mask;
} else {
tx = tu % tex_w;
if (tx < 0) tx += tex_w;
ty = tv % tex_h;
if (ty < 0) ty += tex_h;
}
i32 atlas_idx = (atlas_y_base + ty) * atlas_width + (atlas_x_base + tx);
u32 color = is_hicolor ? ((const u32*)atlas_pixels)[atlas_idx] : atlas_pixels[atlas_idx];
if (is_hicolor) {
if (color & 0xFF000000) {
gfx->zbuffer[idx] = z0;
((u32*)gfx->framebuffer)[y * gfx->framebuffer_width + xs] = color;
}
} else {
if (color != 0) {
gfx->zbuffer[idx] = z0;
gfx->framebuffer[y * gfx->framebuffer_width + xs] = (u8)color;
}
} }
} }
} }
@ -1071,92 +1008,40 @@ static inline void pxl8_fill_scanline_textured_hicolor(
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++;
f32 tex_u = u, tex_v = v; f32 tex_u = u, tex_v = v;
if (!gfx->affine_textures && fabsf(w) > 1e-6f) { if (!affine && fabsf(w) > 1e-6f) {
f32 perspective_w = 1.0f / w; f32 perspective_w = 1.0f / w;
tex_u *= perspective_w; tex_u *= perspective_w;
tex_v *= perspective_w; tex_v *= perspective_w;
} }
u32 color = pxl8_sample_texture(gfx, texture_id, tex_u, tex_v);
if (color & 0xFF000000) { i32 tu = (i32)(tex_u * tex_w);
gfx->zbuffer[idx] = z; i32 tv = (i32)(tex_v * tex_h);
pxl8_pixel(gfx, x, y, color); i32 tx, ty;
if (is_pow2) {
tx = tu & tex_mask;
ty = tv & tex_mask;
} else {
tx = tu % tex_w;
if (tx < 0) tx += tex_w;
ty = tv % tex_h;
if (ty < 0) ty += tex_h;
} }
}
}
z += dz;
u += du;
v += dv;
w += dw;
}
}
static inline void pxl8_fill_scanline_textured_indexed( i32 atlas_idx = (atlas_y_base + ty) * atlas_width + (atlas_x_base + tx);
pxl8_gfx* gfx, i32 y, i32 xs, i32 xe, u32 color = is_hicolor ? ((const u32*)atlas_pixels)[atlas_idx] : atlas_pixels[atlas_idx];
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;
}
i32 span = xe - xs; if (is_hicolor) {
if (span <= 0) { if (color & 0xFF000000) {
if (xs >= 0 && xs < gfx->framebuffer_width) { gfx->zbuffer[idx] = z;
i32 idx = y * gfx->zbuffer_width + xs; ((u32*)gfx->framebuffer)[y * gfx->framebuffer_width + x] = color;
if (z0 <= gfx->zbuffer[idx]) { }
gfx->frame_pixel_count++; } else {
f32 u = u0, v = v0; if (color != 0) {
if (!gfx->affine_textures && fabsf(w0) > 1e-6f) { gfx->zbuffer[idx] = z;
f32 perspective_w = 1.0f / w0; gfx->framebuffer[y * gfx->framebuffer_width + x] = (u8)color;
u *= perspective_w; }
v *= perspective_w;
}
u32 color = pxl8_sample_texture(gfx, texture_id, u, v);
if (color != 0) {
gfx->zbuffer[idx] = z0;
pxl8_pixel(gfx, xs, y, color);
}
}
}
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) {
f32 perspective_w = 1.0f / w;
tex_u *= perspective_w;
tex_v *= perspective_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);
} }
} }
} }
@ -1228,19 +1113,21 @@ void pxl8_3d_draw_triangle_raw(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, pxl8_v
void pxl8_3d_draw_triangle(pxl8_gfx* gfx, pxl8_triangle tri) { void pxl8_3d_draw_triangle(pxl8_gfx* gfx, pxl8_triangle tri) {
if (!gfx || !pxl8_3d_init_zbuffer(gfx)) return; if (!gfx || !pxl8_3d_init_zbuffer(gfx)) return;
gfx->frame_triangle_count++;
pxl8_vec4 v0 = pxl8_transform_vertex(gfx, tri.v[0].position); pxl8_vec4 v0 = pxl8_transform_vertex(gfx, tri.v[0].position);
pxl8_vec4 v1 = pxl8_transform_vertex(gfx, tri.v[1].position); pxl8_vec4 v1 = pxl8_transform_vertex(gfx, tri.v[1].position);
pxl8_vec4 v2 = pxl8_transform_vertex(gfx, tri.v[2].position); pxl8_vec4 v2 = pxl8_transform_vertex(gfx, tri.v[2].position);
if (v0.w <= 0 || v1.w <= 0 || v2.w <= 0) return; pxl8_vec4 tv0 = v0;
pxl8_vec4 tv1 = v1;
pxl8_vec4 tv2 = v2;
u32 tc0 = tri.v[0].color;
i32 x0, y0, x1, y1, x2, y2; i32 x0, y0, x1, y1, x2, y2;
f32 z0, z1, z2; f32 z0, z1, z2;
pxl8_project_to_screen(gfx, v0, &x0, &y0, &z0); pxl8_project_to_screen(gfx, tv0, &x0, &y0, &z0);
pxl8_project_to_screen(gfx, v1, &x1, &y1, &z1); pxl8_project_to_screen(gfx, tv1, &x1, &y1, &z1);
pxl8_project_to_screen(gfx, v2, &x2, &y2, &z2); pxl8_project_to_screen(gfx, tv2, &x2, &y2, &z2);
if (gfx->backface_culling) { if (gfx->backface_culling) {
i32 cross = (x1 - x0) * (y2 - y0) - (y1 - y0) * (x2 - x0); i32 cross = (x1 - x0) * (y2 - y0) - (y1 - y0) * (x2 - x0);
@ -1248,10 +1135,9 @@ void pxl8_3d_draw_triangle(pxl8_gfx* gfx, pxl8_triangle tri) {
} }
if (gfx->wireframe) { if (gfx->wireframe) {
u32 color = tri.v[0].color; pxl8_line(gfx, x0, y0, x1, y1, tc0);
pxl8_line(gfx, x0, y0, x1, y1, color); pxl8_line(gfx, x1, y1, x2, y2, tc0);
pxl8_line(gfx, x1, y1, x2, y2, color); pxl8_line(gfx, x2, y2, x0, y0, tc0);
pxl8_line(gfx, x2, y2, x0, y0, color);
return; return;
} }
@ -1271,23 +1157,19 @@ void pxl8_3d_draw_triangle(pxl8_gfx* gfx, pxl8_triangle tri) {
f32 tmp_f = z1; z1 = z2; z2 = tmp_f; f32 tmp_f = z1; z1 = z2; z2 = tmp_f;
} }
u32 color = tri.v[0].color; pxl8_scanline_func fill_scanline = pxl8_fill_scanline;
pxl8_scanline_func fill_scanline = (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR)
? pxl8_fill_scanline_hicolor
: pxl8_fill_scanline_indexed;
if (y1 == y2) { if (y1 == y2) {
pxl8_scan_triangle(gfx, x0, y0, z0, x1, y1, z1, x2, y2, z2, color, fill_scanline, true); pxl8_scan_triangle(gfx, x0, y0, z0, x1, y1, z1, x2, y2, z2, tc0, fill_scanline, true);
} else if (y0 == y1) { } else if (y0 == y1) {
pxl8_scan_triangle(gfx, x0, y0, z0, x1, y1, z1, x2, y2, z2, color, fill_scanline, false); pxl8_scan_triangle(gfx, x0, y0, z0, x1, y1, z1, x2, y2, z2, tc0, 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_scan_triangle(gfx, x0, y0, z0, x1, y1, z1, x3, y3, z3, color, fill_scanline, true); pxl8_scan_triangle(gfx, x0, y0, z0, x1, y1, z1, x3, y3, z3, tc0, fill_scanline, true);
pxl8_scan_triangle(gfx, x1, y1, z1, x3, y3, z3, x2, y2, z2, color, fill_scanline, false); pxl8_scan_triangle(gfx, x1, y1, z1, x3, y3, z3, x2, y2, z2, tc0, fill_scanline, false);
} }
} }
@ -1312,7 +1194,7 @@ static void pxl8_scan_triangle_textured(
f32 inv_slope_u1, inv_slope_u2, inv_slope_v1, inv_slope_v2, inv_slope_w1, inv_slope_w2; f32 inv_slope_u1, inv_slope_u2, inv_slope_v1, inv_slope_v2, inv_slope_w1, inv_slope_w2;
if (is_bottom) { if (is_bottom) {
if (v1.y == v0.y || v1.y < v0.y || v1.y - v0.y > gfx->framebuffer_height) return; if (v1.y == v0.y || v1.y < v0.y) return;
y_start = v0.y; y_start = v0.y;
y_end = v1.y; y_end = v1.y;
y_step = 1; y_step = 1;
@ -1332,7 +1214,7 @@ static void pxl8_scan_triangle_textured(
inv_slope_w1 = (v1.w - v0.w) / (f32)(v1.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); inv_slope_w2 = (v2.w - v0.w) / (f32)(v2.y - v0.y);
} else { } else {
if (v2.y == v0.y || v2.y < v0.y || v2.y - v0.y > gfx->framebuffer_height) return; if (v2.y == v0.y || v2.y < v0.y) return;
y_start = v2.y; y_start = v2.y;
y_end = v0.y; y_end = v0.y;
y_step = -1; y_step = -1;
@ -1381,6 +1263,83 @@ static void pxl8_scan_triangle_textured(
} }
} }
typedef struct {
pxl8_vec4 clip;
f32 u, v;
} pxl8_clip_vert;
typedef struct {
i32 x, y;
f32 z, u, v, w;
} pxl8_screen_vert;
static void pxl8_clip_screen_axis(pxl8_screen_vert* in, i32 in_count, pxl8_screen_vert* out, i32* out_count, i32 plane_pos, bool is_x, bool is_min) {
*out_count = 0;
for (i32 i = 0; i < in_count; i++) {
pxl8_screen_vert curr = in[i];
pxl8_screen_vert next = in[(i + 1) % in_count];
i32 curr_val = is_x ? curr.x : curr.y;
i32 next_val = is_x ? next.x : next.y;
bool curr_inside = is_min ? (curr_val >= plane_pos) : (curr_val <= plane_pos);
bool next_inside = is_min ? (next_val >= plane_pos) : (next_val <= plane_pos);
if (curr_inside) {
out[(*out_count)++] = curr;
}
if (curr_inside != next_inside) {
i32 denom = next_val - curr_val;
if (denom != 0) {
f32 t = (f32)(plane_pos - curr_val) / (f32)denom;
out[*out_count] = (pxl8_screen_vert){
.x = curr.x + (i32)(t * (next.x - curr.x)),
.y = curr.y + (i32)(t * (next.y - curr.y)),
.z = curr.z + t * (next.z - curr.z),
.u = curr.u + t * (next.u - curr.u),
.v = curr.v + t * (next.v - curr.v),
.w = curr.w + t * (next.w - curr.w)
};
(*out_count)++;
}
}
}
}
static void pxl8_clip_near_plane(pxl8_clip_vert* in, i32 in_count, pxl8_clip_vert* out, i32* out_count, f32 near) {
*out_count = 0;
for (i32 i = 0; i < in_count; i++) {
pxl8_clip_vert curr = in[i];
pxl8_clip_vert next = in[(i + 1) % in_count];
bool curr_inside = curr.clip.w >= near;
bool next_inside = next.clip.w >= near;
if (curr_inside) {
out[(*out_count)++] = curr;
}
if (curr_inside != next_inside) {
f32 t = (near - curr.clip.w) / (next.clip.w - curr.clip.w);
out[*out_count] = (pxl8_clip_vert){
.clip = {
curr.clip.x + t * (next.clip.x - curr.clip.x),
curr.clip.y + t * (next.clip.y - curr.clip.y),
curr.clip.z + t * (next.clip.z - curr.clip.z),
near
},
.u = curr.u + t * (next.u - curr.u),
.v = curr.v + t * (next.v - curr.v)
};
(*out_count)++;
}
}
}
void pxl8_3d_draw_triangle_textured( void pxl8_3d_draw_triangle_textured(
pxl8_gfx* gfx, pxl8_gfx* gfx,
pxl8_vec3 v0, pxl8_vec3 v0,
@ -1396,67 +1355,114 @@ void pxl8_3d_draw_triangle_textured(
) { ) {
if (!gfx || !pxl8_3d_init_zbuffer(gfx)) return; if (!gfx || !pxl8_3d_init_zbuffer(gfx)) return;
pxl8_vec4 cv0 = pxl8_transform_vertex(gfx, v0); pxl8_vec4 cv0 = pxl8_transform_vertex(gfx, v0);
pxl8_vec4 cv1 = pxl8_transform_vertex(gfx, v1); pxl8_vec4 cv1 = pxl8_transform_vertex(gfx, v1);
pxl8_vec4 cv2 = pxl8_transform_vertex(gfx, v2); pxl8_vec4 cv2 = pxl8_transform_vertex(gfx, v2);
if (cv0.w <= 0 || cv1.w <= 0 || cv2.w <= 0) return; pxl8_clip_vert input[3] = {
{cv0, u0, v0f},
{cv1, u1, v1f},
{cv2, u2, v2f}
};
pxl8_textured_vertex tv[3]; pxl8_clip_vert clipped[8];
f32 z_tmp; i32 clipped_count = 0;
pxl8_project_to_screen(gfx, cv0, &tv[0].x, &tv[0].y, &z_tmp);
tv[0].z = z_tmp;
tv[0].u = gfx->affine_textures ? u0 : u0 / cv0.w;
tv[0].v = gfx->affine_textures ? v0f : v0f / cv0.w;
tv[0].w = gfx->affine_textures ? 1.0f : 1.0f / cv0.w;
pxl8_project_to_screen(gfx, cv1, &tv[1].x, &tv[1].y, &z_tmp); f32 near_clip = 0.1f;
tv[1].z = z_tmp; pxl8_clip_near_plane(input, 3, clipped, &clipped_count, near_clip);
tv[1].u = gfx->affine_textures ? u1 : u1 / cv1.w;
tv[1].v = gfx->affine_textures ? v1f : v1f / cv1.w;
tv[1].w = gfx->affine_textures ? 1.0f : 1.0f / cv1.w;
pxl8_project_to_screen(gfx, cv2, &tv[2].x, &tv[2].y, &z_tmp); if (clipped_count < 3) return;
tv[2].z = z_tmp;
tv[2].u = gfx->affine_textures ? u2 : u2 / cv2.w;
tv[2].v = gfx->affine_textures ? v2f : v2f / cv2.w;
tv[2].w = gfx->affine_textures ? 1.0f : 1.0f / cv2.w;
if (gfx->backface_culling) { i32 guard_band = (gfx->framebuffer_width > gfx->framebuffer_height ? gfx->framebuffer_width : gfx->framebuffer_height) * 2;
i32 cross =
(tv[1].x - tv[0].x) * (tv[2].y - tv[0].y) - (tv[1].y - tv[0].y) * (tv[2].x - tv[0].x);
if (cross >= 0) return;
}
if (tv[0].y > tv[1].y) { for (i32 tri = 0; tri < clipped_count - 2; tri++) {
pxl8_textured_vertex tmp = tv[0]; tv[0] = tv[1]; tv[1] = tmp; pxl8_vec4 c0 = clipped[0].clip;
} pxl8_vec4 c1 = clipped[tri + 1].clip;
if (tv[0].y > tv[2].y) { pxl8_vec4 c2 = clipped[tri + 2].clip;
pxl8_textured_vertex tmp = tv[0]; tv[0] = tv[2]; tv[2] = tmp; f32 tu0 = clipped[0].u;
} f32 tv0 = clipped[0].v;
if (tv[1].y > tv[2].y) { f32 tu1 = clipped[tri + 1].u;
pxl8_textured_vertex tmp = tv[1]; tv[1] = tv[2]; tv[2] = tmp; f32 tv1 = clipped[tri + 1].v;
} f32 tu2 = clipped[tri + 2].u;
f32 tv2 = clipped[tri + 2].v;
pxl8_scanline_textured_func fill_scanline = (gfx->color_mode == PXL8_COLOR_MODE_HICOLOR) i32 sx0, sy0, sx1, sy1, sx2, sy2;
? pxl8_fill_scanline_textured_hicolor f32 z0, z1, z2;
: pxl8_fill_scanline_textured_indexed; pxl8_project_to_screen(gfx, c0, &sx0, &sy0, &z0);
pxl8_project_to_screen(gfx, c1, &sx1, &sy1, &z1);
pxl8_project_to_screen(gfx, c2, &sx2, &sy2, &z2);
if (tv[1].y == tv[2].y) { f32 w0_recip = 1.0f / c0.w;
pxl8_scan_triangle_textured(gfx, tv[0], tv[1], tv[2], texture_id, fill_scanline, true); f32 w1_recip = 1.0f / c1.w;
} else if (tv[0].y == tv[1].y) { f32 w2_recip = 1.0f / c2.w;
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;
v3.x = tv[0].x + (i32)(t * (tv[2].x - tv[0].x));
v3.y = tv[1].y;
v3.z = tv[0].z + t * (tv[2].z - tv[0].z);
v3.u = tv[0].u + t * (tv[2].u - tv[0].u);
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_scan_triangle_textured(gfx, tv[0], tv[1], v3, texture_id, fill_scanline, true); pxl8_screen_vert screen_tri[3] = {
pxl8_scan_triangle_textured(gfx, tv[1], v3, tv[2], texture_id, fill_scanline, false); {sx0, sy0, z0, tu0 * w0_recip, tv0 * w0_recip, w0_recip},
{sx1, sy1, z1, tu1 * w1_recip, tv1 * w1_recip, w1_recip},
{sx2, sy2, z2, tu2 * w2_recip, tv2 * w2_recip, w2_recip}
};
pxl8_screen_vert temp1[8], temp2[8];
i32 count1, count2;
pxl8_clip_screen_axis(screen_tri, 3, temp1, &count1, -guard_band, true, true);
if (count1 < 3) continue;
pxl8_clip_screen_axis(temp1, count1, temp2, &count2, guard_band, true, false);
if (count2 < 3) continue;
pxl8_clip_screen_axis(temp2, count2, temp1, &count1, -guard_band, false, true);
if (count1 < 3) continue;
pxl8_clip_screen_axis(temp1, count1, temp2, &count2, guard_band, false, false);
if (count2 < 3) continue;
for (i32 i = 1; i < count2 - 1; i++) {
pxl8_screen_vert v0 = temp2[0];
pxl8_screen_vert v1 = temp2[i];
pxl8_screen_vert v2 = temp2[i + 1];
pxl8_textured_vertex tv[3];
tv[0].x = v0.x; tv[0].y = v0.y; tv[0].z = v0.z;
tv[0].u = v0.u; tv[0].v = v0.v; tv[0].w = v0.w;
tv[1].x = v1.x; tv[1].y = v1.y; tv[1].z = v1.z;
tv[1].u = v1.u; tv[1].v = v1.v; tv[1].w = v1.w;
tv[2].x = v2.x; tv[2].y = v2.y; tv[2].z = v2.z;
tv[2].u = v2.u; tv[2].v = v2.v; tv[2].w = v2.w;
if (gfx->backface_culling) {
i32 cross =
(tv[1].x - tv[0].x) * (tv[2].y - tv[0].y) - (tv[1].y - tv[0].y) * (tv[2].x - tv[0].x);
if (cross >= 0) continue;
}
if (tv[0].y > tv[1].y) {
pxl8_textured_vertex tmp = tv[0]; tv[0] = tv[1]; tv[1] = tmp;
}
if (tv[0].y > tv[2].y) {
pxl8_textured_vertex tmp = tv[0]; tv[0] = tv[2]; tv[2] = tmp;
}
if (tv[1].y > tv[2].y) {
pxl8_textured_vertex tmp = tv[1]; tv[1] = tv[2]; tv[2] = tmp;
}
pxl8_scanline_textured_func fill_scanline = pxl8_fill_scanline_textured;
if (tv[1].y == tv[2].y) {
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_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;
v3.x = tv[0].x + (i32)(t * (tv[2].x - tv[0].x));
v3.y = tv[1].y;
v3.z = tv[0].z + t * (tv[2].z - tv[0].z);
v3.u = tv[0].u + t * (tv[2].u - tv[0].u);
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_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);
}
}
} }
} }

View file

@ -57,7 +57,6 @@ pxl8_color_mode pxl8_gfx_get_color_mode(pxl8_gfx* gfx);
u8* pxl8_gfx_get_framebuffer(pxl8_gfx* gfx); u8* pxl8_gfx_get_framebuffer(pxl8_gfx* gfx);
i32 pxl8_gfx_get_height(const pxl8_gfx* gfx); i32 pxl8_gfx_get_height(const pxl8_gfx* gfx);
u32 pxl8_gfx_get_palette_size(const pxl8_gfx* gfx); u32 pxl8_gfx_get_palette_size(const pxl8_gfx* gfx);
void pxl8_gfx_get_resolution_dimensions(pxl8_resolution resolution, i32* width, i32* height);
i32 pxl8_gfx_get_width(const pxl8_gfx* gfx); i32 pxl8_gfx_get_width(const pxl8_gfx* gfx);
pxl8_result pxl8_gfx_load_font_atlas(pxl8_gfx* gfx); pxl8_result pxl8_gfx_load_font_atlas(pxl8_gfx* gfx);
pxl8_result pxl8_gfx_load_palette(pxl8_gfx* gfx, const char* path); pxl8_result pxl8_gfx_load_palette(pxl8_gfx* gfx, const char* path);

View file

@ -13,6 +13,7 @@ typedef struct pxl8_hal {
u64 (*get_ticks)(void); u64 (*get_ticks)(void);
void (*present)(void* platform_data); void (*present)(void* platform_data);
void (*set_relative_mouse_mode)(void* platform_data, bool enabled);
void (*upload_atlas)(void* platform_data, const pxl8_atlas* atlas, void (*upload_atlas)(void* platform_data, const pxl8_atlas* atlas,
const u32* palette, pxl8_color_mode mode); const u32* palette, pxl8_color_mode mode);
void (*upload_framebuffer)(void* platform_data, const u8* fb, void (*upload_framebuffer)(void* platform_data, const u8* fb,

View file

@ -179,3 +179,13 @@ i32 pxl8_mouse_y(const pxl8_input_state* input) {
if (!input) return 0; if (!input) return 0;
return input->mouse_y; return input->mouse_y;
} }
i32 pxl8_mouse_dx(const pxl8_input_state* input) {
if (!input) return 0;
return input->mouse_dx;
}
i32 pxl8_mouse_dy(const pxl8_input_state* input) {
if (!input) return 0;
return input->mouse_dy;
}

View file

@ -104,6 +104,8 @@ i32 pxl8_mouse_wheel_x(const pxl8_input_state* input);
i32 pxl8_mouse_wheel_y(const pxl8_input_state* input); i32 pxl8_mouse_wheel_y(const pxl8_input_state* input);
i32 pxl8_mouse_x(const pxl8_input_state* input); i32 pxl8_mouse_x(const pxl8_input_state* input);
i32 pxl8_mouse_y(const pxl8_input_state* input); i32 pxl8_mouse_y(const pxl8_input_state* input);
i32 pxl8_mouse_dx(const pxl8_input_state* input);
i32 pxl8_mouse_dy(const pxl8_input_state* input);
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -96,13 +96,13 @@ pxl8_mat4 pxl8_mat4_identity(void) {
pxl8_mat4 pxl8_mat4_multiply(pxl8_mat4 a, pxl8_mat4 b) { pxl8_mat4 pxl8_mat4_multiply(pxl8_mat4 a, pxl8_mat4 b) {
pxl8_mat4 mat = {0}; pxl8_mat4 mat = {0};
for (i32 i = 0; i < 4; i++) { for (i32 col = 0; col < 4; col++) {
for (i32 j = 0; j < 4; j++) { for (i32 row = 0; row < 4; row++) {
mat.m[i * 4 + j] = mat.m[col * 4 + row] =
a.m[i * 4 + 0] * b.m[0 * 4 + j] + a.m[0 * 4 + row] * b.m[col * 4 + 0] +
a.m[i * 4 + 1] * b.m[1 * 4 + j] + a.m[1 * 4 + row] * b.m[col * 4 + 1] +
a.m[i * 4 + 2] * b.m[2 * 4 + j] + a.m[2 * 4 + row] * b.m[col * 4 + 2] +
a.m[i * 4 + 3] * b.m[3 * 4 + j]; a.m[3 * 4 + row] * b.m[col * 4 + 3];
} }
} }
@ -111,19 +111,19 @@ pxl8_mat4 pxl8_mat4_multiply(pxl8_mat4 a, pxl8_mat4 b) {
pxl8_vec4 pxl8_mat4_multiply_vec4(pxl8_mat4 m, pxl8_vec4 v) { pxl8_vec4 pxl8_mat4_multiply_vec4(pxl8_mat4 m, pxl8_vec4 v) {
return (pxl8_vec4){ return (pxl8_vec4){
.x = m.m[0] * v.x + m.m[1] * v.y + m.m[2] * v.z + m.m[3] * v.w, .x = m.m[0] * v.x + m.m[4] * v.y + m.m[8] * v.z + m.m[12] * v.w,
.y = m.m[4] * v.x + m.m[5] * v.y + m.m[6] * v.z + m.m[7] * 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[8] * v.x + m.m[9] * v.y + m.m[10] * v.z + m.m[11] * 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[12] * v.x + m.m[13] * v.y + m.m[14] * v.z + m.m[15] * v.w, .w = m.m[3] * v.x + m.m[7] * v.y + m.m[11] * v.z + m.m[15] * v.w,
}; };
} }
pxl8_mat4 pxl8_mat4_translate(f32 x, f32 y, f32 z) { pxl8_mat4 pxl8_mat4_translate(f32 x, f32 y, f32 z) {
pxl8_mat4 mat = pxl8_mat4_identity(); pxl8_mat4 mat = pxl8_mat4_identity();
mat.m[3] = x; mat.m[12] = x;
mat.m[7] = y; mat.m[13] = y;
mat.m[11] = z; mat.m[14] = z;
return mat; return mat;
} }
@ -134,8 +134,8 @@ pxl8_mat4 pxl8_mat4_rotate_x(f32 angle) {
f32 s = sinf(angle); f32 s = sinf(angle);
mat.m[5] = c; mat.m[5] = c;
mat.m[6] = -s; mat.m[9] = -s;
mat.m[9] = s; mat.m[6] = s;
mat.m[10] = c; mat.m[10] = c;
return mat; return mat;
@ -147,8 +147,8 @@ pxl8_mat4 pxl8_mat4_rotate_y(f32 angle) {
f32 s = sinf(angle); f32 s = sinf(angle);
mat.m[0] = c; mat.m[0] = c;
mat.m[2] = s; mat.m[8] = s;
mat.m[8] = -s; mat.m[2] = -s;
mat.m[10] = c; mat.m[10] = c;
return mat; return mat;
@ -160,8 +160,8 @@ pxl8_mat4 pxl8_mat4_rotate_z(f32 angle) {
f32 s = sinf(angle); f32 s = sinf(angle);
mat.m[0] = c; mat.m[0] = c;
mat.m[1] = -s; mat.m[4] = -s;
mat.m[4] = s; mat.m[1] = s;
mat.m[5] = c; mat.m[5] = c;
return mat; return mat;
@ -183,9 +183,9 @@ pxl8_mat4 pxl8_mat4_ortho(f32 left, f32 right, f32 bottom, f32 top, f32 near, f3
mat.m[0] = 2.0f / (right - left); mat.m[0] = 2.0f / (right - left);
mat.m[5] = 2.0f / (top - bottom); mat.m[5] = 2.0f / (top - bottom);
mat.m[10] = -2.0f / (far - near); mat.m[10] = -2.0f / (far - near);
mat.m[3] = -(right + left) / (right - left); mat.m[12] = -(right + left) / (right - left);
mat.m[7] = -(top + bottom) / (top - bottom); mat.m[13] = -(top + bottom) / (top - bottom);
mat.m[11] = -(far + near) / (far - near); mat.m[14] = -(far + near) / (far - near);
mat.m[15] = 1.0f; mat.m[15] = 1.0f;
return mat; return mat;
@ -198,8 +198,8 @@ pxl8_mat4 pxl8_mat4_perspective(f32 fov, f32 aspect, f32 near, f32 far) {
mat.m[0] = 1.0f / (aspect * tan_half_fov); mat.m[0] = 1.0f / (aspect * tan_half_fov);
mat.m[5] = 1.0f / tan_half_fov; mat.m[5] = 1.0f / tan_half_fov;
mat.m[10] = -(far + near) / (far - near); mat.m[10] = -(far + near) / (far - near);
mat.m[11] = -(2.0f * far * near) / (far - near); mat.m[14] = -(2.0f * far * near) / (far - near);
mat.m[14] = -1.0f; mat.m[11] = -1.0f;
return mat; return mat;
} }
@ -211,17 +211,17 @@ pxl8_mat4 pxl8_mat4_lookat(pxl8_vec3 eye, pxl8_vec3 center, pxl8_vec3 up) {
pxl8_vec3 u = pxl8_vec3_cross(s, f); pxl8_vec3 u = pxl8_vec3_cross(s, f);
mat.m[0] = s.x; mat.m[0] = s.x;
mat.m[1] = s.y; mat.m[4] = s.y;
mat.m[2] = s.z; mat.m[8] = s.z;
mat.m[4] = u.x; mat.m[1] = u.x;
mat.m[5] = u.y; mat.m[5] = u.y;
mat.m[6] = u.z; mat.m[9] = u.z;
mat.m[8] = -f.x; mat.m[2] = -f.x;
mat.m[9] = -f.y; mat.m[6] = -f.y;
mat.m[10] = -f.z; mat.m[10] = -f.z;
mat.m[3] = -pxl8_vec3_dot(s, eye); mat.m[12] = -pxl8_vec3_dot(s, eye);
mat.m[7] = -pxl8_vec3_dot(u, eye); mat.m[13] = -pxl8_vec3_dot(u, eye);
mat.m[11] = pxl8_vec3_dot(f, eye); mat.m[14] = pxl8_vec3_dot(f, eye);
return mat; return mat;
} }
@ -230,35 +230,35 @@ pxl8_frustum pxl8_frustum_from_matrix(pxl8_mat4 vp) {
pxl8_frustum frustum; pxl8_frustum frustum;
const f32* m = vp.m; const f32* m = vp.m;
frustum.planes[0].normal.x = m[12] - m[0]; frustum.planes[0].normal.x = m[3] - m[0];
frustum.planes[0].normal.y = m[13] - m[1]; frustum.planes[0].normal.y = m[7] - m[4];
frustum.planes[0].normal.z = m[14] - m[2]; frustum.planes[0].normal.z = m[11] - m[8];
frustum.planes[0].distance = m[15] - m[3]; frustum.planes[0].distance = m[15] - m[12];
frustum.planes[1].normal.x = m[12] + m[0]; frustum.planes[1].normal.x = m[3] + m[0];
frustum.planes[1].normal.y = m[13] + m[1]; frustum.planes[1].normal.y = m[7] + m[4];
frustum.planes[1].normal.z = m[14] + m[2]; frustum.planes[1].normal.z = m[11] + m[8];
frustum.planes[1].distance = m[15] + m[3]; frustum.planes[1].distance = m[15] + m[12];
frustum.planes[2].normal.x = m[12] + m[4]; frustum.planes[2].normal.x = m[3] + m[1];
frustum.planes[2].normal.y = m[13] + m[5]; frustum.planes[2].normal.y = m[7] + m[5];
frustum.planes[2].normal.z = m[14] + m[6]; frustum.planes[2].normal.z = m[11] + m[9];
frustum.planes[2].distance = m[15] + m[7]; frustum.planes[2].distance = m[15] + m[13];
frustum.planes[3].normal.x = m[12] - m[4]; frustum.planes[3].normal.x = m[3] - m[1];
frustum.planes[3].normal.y = m[13] - m[5]; frustum.planes[3].normal.y = m[7] - m[5];
frustum.planes[3].normal.z = m[14] - m[6]; frustum.planes[3].normal.z = m[11] - m[9];
frustum.planes[3].distance = m[15] - m[7]; frustum.planes[3].distance = m[15] - m[13];
frustum.planes[4].normal.x = m[12] - m[8]; frustum.planes[4].normal.x = m[3] - m[2];
frustum.planes[4].normal.y = m[13] - m[9]; frustum.planes[4].normal.y = m[7] - m[6];
frustum.planes[4].normal.z = m[14] - m[10]; frustum.planes[4].normal.z = m[11] - m[10];
frustum.planes[4].distance = m[15] - m[11]; frustum.planes[4].distance = m[15] - m[14];
frustum.planes[5].normal.x = m[12] + m[8]; frustum.planes[5].normal.x = m[3] + m[2];
frustum.planes[5].normal.y = m[13] + m[9]; frustum.planes[5].normal.y = m[7] + m[6];
frustum.planes[5].normal.z = m[14] + m[10]; frustum.planes[5].normal.z = m[11] + m[10];
frustum.planes[5].distance = m[15] + m[11]; frustum.planes[5].distance = m[15] + m[14];
for (i32 i = 0; i < 6; i++) { for (i32 i = 0; i < 6; i++) {
f32 len = pxl8_vec3_length(frustum.planes[i].normal); f32 len = pxl8_vec3_length(frustum.planes[i].normal);
@ -283,22 +283,11 @@ 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
}; };
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
};
f32 p_dist = pxl8_vec3_dot(normal, p_vertex) + d; 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) { if (p_dist < 0.0f) {
return false; return false;
} }
if (n_dist > 0.1f) {
continue;
}
} }
return true; return true;

View file

@ -113,6 +113,9 @@ static const char* pxl8_ffi_cdefs =
"int pxl8_mouse_wheel_y(const pxl8_input_state* input);\n" "int pxl8_mouse_wheel_y(const pxl8_input_state* input);\n"
"int pxl8_mouse_x(const pxl8_input_state* input);\n" "int pxl8_mouse_x(const pxl8_input_state* input);\n"
"int pxl8_mouse_y(const pxl8_input_state* input);\n" "int pxl8_mouse_y(const pxl8_input_state* input);\n"
"int pxl8_mouse_dx(const pxl8_input_state* input);\n"
"int pxl8_mouse_dy(const pxl8_input_state* input);\n"
"void pxl8_set_relative_mouse_mode(pxl8* sys, bool enabled);\n"
"void pxl8_lua_debug(const char* msg);\n" "void pxl8_lua_debug(const char* msg);\n"
"void pxl8_lua_error(const char* msg);\n" "void pxl8_lua_error(const char* msg);\n"
"void pxl8_lua_info(const char* msg);\n" "void pxl8_lua_info(const char* msg);\n"

View file

@ -31,17 +31,7 @@ static void* sdl3_create(pxl8_color_mode mode, pxl8_resolution resolution,
return NULL; return NULL;
} }
i32 fb_w, fb_h; pxl8_size fb_size = pxl8_get_resolution_dimensions(resolution);
switch (resolution) {
case PXL8_RESOLUTION_240x160: fb_w = 240; fb_h = 160; break;
case PXL8_RESOLUTION_320x180: fb_w = 320; fb_h = 180; break;
case PXL8_RESOLUTION_320x240: fb_w = 320; fb_h = 240; break;
case PXL8_RESOLUTION_640x360: fb_w = 640; fb_h = 360; break;
case PXL8_RESOLUTION_640x480: fb_w = 640; fb_h = 480; break;
case PXL8_RESOLUTION_800x600: fb_w = 800; fb_h = 600; break;
case PXL8_RESOLUTION_960x540: fb_w = 960; fb_h = 540; break;
default: fb_w = 640; fb_h = 360; break;
}
ctx->window = SDL_CreateWindow(title, win_w, win_h, SDL_WINDOW_RESIZABLE); ctx->window = SDL_CreateWindow(title, win_w, win_h, SDL_WINDOW_RESIZABLE);
if (!ctx->window) { if (!ctx->window) {
@ -65,7 +55,7 @@ static void* sdl3_create(pxl8_color_mode mode, pxl8_resolution resolution,
ctx->framebuffer_texture = SDL_CreateTexture(ctx->renderer, ctx->framebuffer_texture = SDL_CreateTexture(ctx->renderer,
SDL_PIXELFORMAT_RGBA32, SDL_PIXELFORMAT_RGBA32,
SDL_TEXTUREACCESS_STREAMING, SDL_TEXTUREACCESS_STREAMING,
fb_w, fb_h); fb_size.w, fb_size.h);
if (!ctx->framebuffer_texture) { if (!ctx->framebuffer_texture) {
pxl8_error("Failed to create framebuffer texture: %s", SDL_GetError()); pxl8_error("Failed to create framebuffer texture: %s", SDL_GetError());
SDL_DestroyRenderer(ctx->renderer); SDL_DestroyRenderer(ctx->renderer);
@ -145,14 +135,7 @@ static void sdl3_upload_framebuffer(void* platform_data, const u8* fb,
if (!ctx->rgba_buffer) return; if (!ctx->rgba_buffer) return;
u32 palette_size; u32 palette_size = pxl8_get_palette_size(mode);
switch (mode) {
case PXL8_COLOR_MODE_FAMI: palette_size = 64; break;
case PXL8_COLOR_MODE_MEGA: palette_size = 512; break;
case PXL8_COLOR_MODE_GBA: palette_size = 32768; break;
case PXL8_COLOR_MODE_SNES: palette_size = 32768; break;
default: palette_size = 256; break;
}
for (i32 i = 0; i < w * h; i++) { for (i32 i = 0; i < w * h; i++) {
u8 index = fb[i]; u8 index = fb[i];
@ -212,14 +195,7 @@ static void sdl3_upload_atlas(void* platform_data, const pxl8_atlas* atlas,
if (!ctx->rgba_buffer) return; if (!ctx->rgba_buffer) return;
u32 palette_size; u32 palette_size = pxl8_get_palette_size(mode);
switch (mode) {
case PXL8_COLOR_MODE_FAMI: palette_size = 64; break;
case PXL8_COLOR_MODE_MEGA: palette_size = 512; break;
case PXL8_COLOR_MODE_GBA: palette_size = 32768; break;
case PXL8_COLOR_MODE_SNES: palette_size = 32768; break;
default: palette_size = 256; break;
}
for (u32 i = 0; i < atlas_w * atlas_h; i++) { for (u32 i = 0; i < atlas_w * atlas_h; i++) {
u8 index = atlas_pixels[i]; u8 index = atlas_pixels[i];
@ -290,12 +266,6 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) {
break; break;
case SDL_EVENT_KEY_DOWN: { case SDL_EVENT_KEY_DOWN: {
#ifdef DEBUG
if (event->key.key == SDLK_ESCAPE) {
pxl8_set_running(sys, false);
}
#endif
SDL_Scancode scancode = event->key.scancode; SDL_Scancode scancode = event->key.scancode;
if (scancode < 256) { if (scancode < 256) {
if (!input->keys_down[scancode]) { if (!input->keys_down[scancode]) {
@ -343,6 +313,9 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) {
pxl8_gfx* gfx = pxl8_get_gfx(sys); pxl8_gfx* gfx = pxl8_get_gfx(sys);
if (!gfx) break; if (!gfx) break;
input->mouse_dx = (i32)event->motion.xrel;
input->mouse_dy = (i32)event->motion.yrel;
i32 window_mouse_x = (i32)event->motion.x; i32 window_mouse_x = (i32)event->motion.x;
i32 window_mouse_y = (i32)event->motion.y; i32 window_mouse_y = (i32)event->motion.y;
@ -352,12 +325,11 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) {
i32 window_width, window_height; i32 window_width, window_height;
SDL_GetWindowSize(window, &window_width, &window_height); SDL_GetWindowSize(window, &window_width, &window_height);
i32 render_width, render_height;
pxl8_resolution resolution = pxl8_get_resolution(sys); pxl8_resolution resolution = pxl8_get_resolution(sys);
pxl8_gfx_get_resolution_dimensions(resolution, &render_width, &render_height); pxl8_size render_size = pxl8_get_resolution_dimensions(resolution);
pxl8_bounds window_bounds = {0, 0, window_width, window_height}; pxl8_bounds window_bounds = {0, 0, window_width, window_height};
pxl8_viewport vp = pxl8_gfx_viewport(window_bounds, render_width, render_height); pxl8_viewport vp = pxl8_gfx_viewport(window_bounds, render_size.w, render_size.h);
input->mouse_x = (i32)((window_mouse_x - vp.offset_x) / vp.scale); input->mouse_x = (i32)((window_mouse_x - vp.offset_x) / vp.scale);
input->mouse_y = (i32)((window_mouse_y - vp.offset_y) / vp.scale); input->mouse_y = (i32)((window_mouse_y - vp.offset_y) / vp.scale);
@ -386,6 +358,15 @@ void SDL_AppQuit(void* appstate, SDL_AppResult result) {
SDL_Quit(); SDL_Quit();
} }
static void sdl3_set_relative_mouse_mode(void* platform_data, bool enabled) {
if (!platform_data) return;
pxl8_sdl3_context* ctx = (pxl8_sdl3_context*)platform_data;
if (!SDL_SetWindowRelativeMouseMode(ctx->window, enabled)) {
pxl8_error("Failed to set relative mouse mode: %s", SDL_GetError());
}
}
const pxl8_hal pxl8_hal_sdl3 = { const pxl8_hal pxl8_hal_sdl3 = {
.create = sdl3_create, .create = sdl3_create,
.destroy = sdl3_destroy, .destroy = sdl3_destroy,
@ -393,4 +374,5 @@ const pxl8_hal pxl8_hal_sdl3 = {
.present = sdl3_present, .present = sdl3_present,
.upload_atlas = sdl3_upload_atlas, .upload_atlas = sdl3_upload_atlas,
.upload_framebuffer = sdl3_upload_framebuffer, .upload_framebuffer = sdl3_upload_framebuffer,
.set_relative_mouse_mode = sdl3_set_relative_mouse_mode,
}; };

View file

@ -14,11 +14,16 @@ bool pxl8_is_running(const pxl8* sys);
void pxl8_set_running(pxl8* sys, bool running); void pxl8_set_running(pxl8* sys, bool running);
f32 pxl8_get_fps(const pxl8* sys); f32 pxl8_get_fps(const pxl8* sys);
pxl8_gfx* pxl8_get_gfx(pxl8* sys); pxl8_gfx* pxl8_get_gfx(const pxl8* sys);
pxl8_input_state* pxl8_get_input(pxl8* sys); pxl8_input_state* pxl8_get_input(const pxl8* sys);
pxl8_resolution pxl8_get_resolution(pxl8* sys); pxl8_resolution pxl8_get_resolution(const pxl8* sys);
void pxl8_set_relative_mouse_mode(pxl8* sys, bool enabled);
pxl8_result pxl8_init(pxl8* sys, i32 argc, char* argv[]); pxl8_result pxl8_init(pxl8* sys, i32 argc, char* argv[]);
pxl8_result pxl8_update(pxl8* sys); pxl8_result pxl8_update(pxl8* sys);
pxl8_result pxl8_frame(pxl8* sys); pxl8_result pxl8_frame(pxl8* sys);
void pxl8_quit(pxl8* sys); void pxl8_quit(pxl8* sys);
u32 pxl8_get_palette_size(pxl8_color_mode mode);
pxl8_size pxl8_get_resolution_dimensions(pxl8_resolution resolution);

View file

@ -75,12 +75,19 @@ typedef struct pxl8_input_state {
i32 mouse_wheel_y; i32 mouse_wheel_y;
i32 mouse_x; i32 mouse_x;
i32 mouse_y; i32 mouse_y;
i32 mouse_dx;
i32 mouse_dy;
bool mouse_relative_mode;
} pxl8_input_state; } pxl8_input_state;
typedef struct pxl8_point { typedef struct pxl8_point {
i32 x, y; i32 x, y;
} pxl8_point; } pxl8_point;
typedef struct pxl8_size {
i32 w, h;
} pxl8_size;
typedef struct pxl8_viewport { typedef struct pxl8_viewport {
i32 offset_x, offset_y; i32 offset_x, offset_y;
i32 scaled_width, scaled_height; i32 scaled_width, scaled_height;