only support indexed color mode, get rid of enum/branching on color modes

This commit is contained in:
asrael 2026-02-27 15:53:25 -06:00
parent 9f657ffcf9
commit e0a5d34d29
25 changed files with 142 additions and 286 deletions

View file

@ -1,4 +1,3 @@
{:title "pxl8 demo"
:pixel-mode "indexed"
:resolution "640x360"
:window-size [1280 720]}

View file

@ -18,6 +18,10 @@
(local SIM_FLAG_GROUNDED 4)
(local door-spawn-x 860)
(local door-spawn-z 416)
(local door-spawn-yaw 1.5708)
(local sim-cfg (pxl8.sim_config {:move_speed 150
:gravity 600
:jump_velocity 180
@ -48,7 +52,7 @@
(var light-time 0)
(var glows nil)
(var lights nil)
(var bsp-materials-setup false)
(var materials-for-chunk 0)
(var network nil)
(var portal-cooldown 0)
(var real-time 0)
@ -136,17 +140,25 @@
(setup-textures 1))
(fn setup-materials []
(when (and world (not bsp-materials-setup) floor-tex trim-tex wall-tex)
(when (not world) (lua "return"))
(when (not network) (lua "return"))
(let [net-chunk (network:chunk_id)]
(when (or (= net-chunk 0) (= materials-for-chunk net-chunk)) (lua "return"))
(when (not= current-bsp-id net-chunk)
(setup-textures net-chunk))
(when (not floor-tex) (lua "return"))
(when (not wall-tex) (lua "return"))
(let [chunk (world:active_chunk)]
(when (and chunk (chunk:ready))
(let [floor-mat (pxl8.create_material {:texture floor-tex :lighting true :double_sided true})
trim-mat (pxl8.create_material {:texture trim-tex :lighting true :double_sided true})
wall-mat (pxl8.create_material {:texture wall-tex :lighting true :double_sided true})]
(world:set_bsp_material 0 floor-mat)
(world:set_bsp_material 1 wall-mat)
(world:set_bsp_material 3 trim-mat)
(entities.setup-lighting (chunk:bsp) 2)
(set bsp-materials-setup true))))))
(when (not chunk) (lua "return"))
(when (not (chunk:ready)) (lua "return"))
(let [floor-mat (pxl8.create_material {:texture floor-tex :lighting true :double_sided true})
trim-mat (pxl8.create_material {:texture trim-tex :lighting true :double_sided true})
wall-mat (pxl8.create_material {:texture wall-tex :lighting true :double_sided true})]
(world:set_bsp_material 0 floor-mat)
(world:set_bsp_material 1 wall-mat)
(world:set_bsp_material 3 trim-mat)
(entities.setup-lighting (chunk:bsp) 2)
(set materials-for-chunk net-chunk)))))
(fn sample-input []
(when (pxl8.key_pressed "`")
@ -187,27 +199,31 @@
(if (= current-id 1)
(do
(pxl8.info "Door: BSP 1 -> BSP 2")
(network:enter_scene 1 2 416 0 416)
(world:init_local_player 416 0 416)
(set cam-x 416)
(network:enter_scene 1 2 door-spawn-x 0 door-spawn-z)
(world:init_local_player door-spawn-x 0 door-spawn-z)
(world:set_look door-spawn-yaw 0)
(set cam-x door-spawn-x)
(set cam-y 0)
(set cam-z 416)
(set smooth-cam-x 416)
(set smooth-cam-z 416)
(set bsp-materials-setup false)
(set cam-z door-spawn-z)
(set cam-yaw door-spawn-yaw)
(set cam-pitch 0)
(set smooth-cam-x door-spawn-x)
(set smooth-cam-z door-spawn-z)
(setup-textures 2)
(set portal-cooldown 2.0))
(= current-id 2)
(do
(pxl8.info "Door: BSP 2 -> BSP 1")
(network:enter_scene 1 1 416 0 416)
(world:init_local_player 416 0 416)
(set cam-x 416)
(network:enter_scene 1 1 door-spawn-x 0 door-spawn-z)
(world:init_local_player door-spawn-x 0 door-spawn-z)
(world:set_look door-spawn-yaw 0)
(set cam-x door-spawn-x)
(set cam-y 0)
(set cam-z 416)
(set smooth-cam-x 416)
(set smooth-cam-z 416)
(set bsp-materials-setup false)
(set cam-z door-spawn-z)
(set cam-yaw door-spawn-yaw)
(set cam-pitch 0)
(set smooth-cam-x door-spawn-x)
(set smooth-cam-z door-spawn-z)
(setup-textures 1)
(set portal-cooldown 2.0)))))))
@ -282,7 +298,8 @@
r2 (* 0.04 (math.sin (+ (* real-time 3.2) phase)))
light-radius (* 150 (+ 0.95 r1 r2))]
(lights:clear)
(lights:add light-x light-y light-z 2 light-intensity light-radius)
(when (= current-bsp-id 1)
(lights:add light-x light-y light-z 2 light-intensity light-radius))
(pxl8.push_target)
(pxl8.begin_frame_3d camera lights {
@ -300,7 +317,7 @@
(pxl8.set_wireframe (menu.is-wireframe))
(world:render [smooth-cam-x eye-y smooth-cam-z])
(when chunk
(when (and chunk (= current-bsp-id 1))
(entities.render-fireball light-x light-y light-z (menu.is-wireframe)))
(entities.render-door (menu.is-wireframe) 0)

View file

@ -174,6 +174,7 @@ pub extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 {
if let Some(pid) = player_id {
if let Some(_player) = sim.get_player_position(pid) {
let mut burst = false;
while let Some(chunk_id) = client_chunks.next_pending() {
match chunk_id {
ChunkId::Bsp(id) => {
@ -182,13 +183,15 @@ pub extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 {
let msgs = bsp_to_messages(bsp, id, chunk.version());
client_chunks.queue_messages(msgs);
client_chunks.mark_sent(chunk_id, chunk.version());
burst = true;
}
}
}
}
}
for _ in 0..8 {
let send_limit = if burst { 256 } else { 8 };
for _ in 0..send_limit {
if let Some(msg) = client_chunks.next_message() {
transport.send_chunk(&msg, sequence);
sequence = sequence.wrapping_add(1);

View file

@ -169,7 +169,15 @@ fn build_bsp_node_grid(ctx: &mut BspBuildContext, x0: i32, y0: i32, x1: i32, y1:
let plane_idx = ctx.plane_offset;
ctx.plane_offset += 1;
if depth % 2 == 0 {
let split_x = if x1 - x0 <= 1 {
false
} else if y1 - y0 <= 1 {
true
} else {
depth % 2 == 0
};
if split_x {
let mid_x = (x0 + x1) / 2;
let split_pos = mid_x as f32 * CELL_SIZE;
@ -479,6 +487,16 @@ fn compute_vertex_ao(bsp: &BspBuilder, pos: Vec3, normal: Vec3) -> f32 {
continue;
}
let cx = offset_pos.x.max(face.aabb_min.x).min(face.aabb_max.x);
let cy = offset_pos.y.max(face.aabb_min.y).min(face.aabb_max.y);
let cz = offset_pos.z.max(face.aabb_min.z).min(face.aabb_max.z);
let dx = offset_pos.x - cx;
let dy = offset_pos.y - cy;
let dz = offset_pos.z - cz;
if dx * dx + dy * dy + dz * dz > AO_RAY_LENGTH * AO_RAY_LENGTH {
continue;
}
let mut verts = [Vec3::new(0.0, 0.0, 0.0); 4];
let mut num_verts = 0usize;

View file

@ -49,7 +49,6 @@ struct pxl8_cart {
char* title;
pxl8_resolution resolution;
pxl8_size window_size;
pxl8_pixel_mode pixel_mode;
bool is_folder;
bool is_mounted;
};
@ -156,7 +155,6 @@ pxl8_cart* pxl8_cart_create(void) {
if (cart) {
cart->resolution = PXL8_RESOLUTION_640x360;
cart->window_size = (pxl8_size){1280, 720};
cart->pixel_mode = PXL8_PIXEL_INDEXED;
}
return cart;
}
@ -364,14 +362,6 @@ void pxl8_cart_set_window_size(pxl8_cart* cart, pxl8_size size) {
if (cart) cart->window_size = size;
}
pxl8_pixel_mode pxl8_cart_get_pixel_mode(const pxl8_cart* cart) {
return cart ? cart->pixel_mode : PXL8_PIXEL_INDEXED;
}
void pxl8_cart_set_pixel_mode(pxl8_cart* cart, pxl8_pixel_mode mode) {
if (cart) cart->pixel_mode = mode;
}
bool pxl8_cart_is_packed(const pxl8_cart* cart) {
return cart && !cart->is_folder;
}

View file

@ -25,11 +25,9 @@ const char* pxl8_cart_get_base_path(const pxl8_cart* cart);
const char* pxl8_cart_get_title(const pxl8_cart* cart);
pxl8_resolution pxl8_cart_get_resolution(const pxl8_cart* cart);
pxl8_size pxl8_cart_get_window_size(const pxl8_cart* cart);
pxl8_pixel_mode pxl8_cart_get_pixel_mode(const pxl8_cart* cart);
void pxl8_cart_set_title(pxl8_cart* cart, const char* title);
void pxl8_cart_set_resolution(pxl8_cart* cart, pxl8_resolution resolution);
void pxl8_cart_set_window_size(pxl8_cart* cart, pxl8_size size);
void pxl8_cart_set_pixel_mode(pxl8_cart* cart, pxl8_pixel_mode mode);
bool pxl8_cart_is_packed(const pxl8_cart* cart);
bool pxl8_cart_has_embedded(const char* exe_path);

View file

@ -239,7 +239,6 @@ pxl8_result pxl8_init(pxl8* sys, i32 argc, char* argv[]) {
const char* window_title = pxl8_cart_get_title(sys->cart);
if (!window_title) window_title = "pxl8";
pxl8_resolution resolution = pxl8_cart_get_resolution(sys->cart);
pxl8_pixel_mode pixel_mode = pxl8_cart_get_pixel_mode(sys->cart);
pxl8_size window_size = pxl8_cart_get_window_size(sys->cart);
pxl8_size render_size = pxl8_get_resolution_dimensions(resolution);
@ -249,7 +248,7 @@ pxl8_result pxl8_init(pxl8* sys, i32 argc, char* argv[]) {
return PXL8_ERROR_INITIALIZATION_FAILED;
}
game->gfx = pxl8_gfx_create(sys->hal, sys->platform_data, pixel_mode, resolution);
game->gfx = pxl8_gfx_create(sys->hal, sys->platform_data, resolution);
if (!game->gfx) {
pxl8_error("failed to create graphics context");
return PXL8_ERROR_INITIALIZATION_FAILED;

View file

@ -31,12 +31,6 @@ typedef __int128_t i128;
typedef __uint128_t u128;
#endif
typedef enum pxl8_pixel_mode {
PXL8_PIXEL_INDEXED = 1,
PXL8_PIXEL_HICOLOR = 2,
PXL8_PIXEL_RGBA = 4,
} pxl8_pixel_mode;
typedef enum pxl8_cursor {
PXL8_CURSOR_ARROW,
PXL8_CURSOR_HAND

View file

@ -1,11 +1,8 @@
#include "pxl8_atlas.h"
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pxl8_color.h"
#include "pxl8_log.h"
#include "pxl8_mem.h"
@ -144,15 +141,14 @@ static void pxl8_skyline_compact(pxl8_skyline* skyline) {
}
}
pxl8_atlas* pxl8_atlas_create(u32 width, u32 height, pxl8_pixel_mode pixel_mode) {
pxl8_atlas* pxl8_atlas_create(u32 width, u32 height) {
pxl8_atlas* atlas = (pxl8_atlas*)pxl8_calloc(1, sizeof(pxl8_atlas));
if (!atlas) return NULL;
atlas->height = height;
atlas->width = width;
i32 bytes_per_pixel = pxl8_bytes_per_pixel(pixel_mode);
atlas->pixels = (u8*)pxl8_calloc(width * height, bytes_per_pixel);
atlas->pixels = (u8*)pxl8_calloc(width * height, 1);
if (!atlas->pixels) {
pxl8_free(atlas);
return NULL;
@ -226,14 +222,13 @@ void pxl8_atlas_clear(pxl8_atlas* atlas, u32 preserve_count) {
atlas->dirty = true;
}
bool pxl8_atlas_expand(pxl8_atlas* atlas, pxl8_pixel_mode pixel_mode) {
bool pxl8_atlas_expand(pxl8_atlas* atlas) {
if (!atlas || atlas->width >= 4096) return false;
i32 bytes_per_pixel = pxl8_bytes_per_pixel(pixel_mode);
u32 new_size = atlas->width * 2;
u32 old_width = atlas->width;
u8* new_pixels = (u8*)pxl8_calloc(new_size * new_size, bytes_per_pixel);
u8* new_pixels = (u8*)pxl8_calloc(new_size * new_size, 1);
if (!new_pixels) return false;
pxl8_skyline new_skyline;
@ -268,11 +263,7 @@ bool pxl8_atlas_expand(pxl8_atlas* atlas, pxl8_pixel_mode pixel_mode) {
for (u32 x = 0; x < (u32)atlas->entries[i].w; x++) {
u32 src_idx = (atlas->entries[i].y + y) * old_width + (atlas->entries[i].x + x);
u32 dst_idx = (fit.pos.y + y) * new_size + (fit.pos.x + x);
if (bytes_per_pixel == 2) {
((u16*)new_pixels)[dst_idx] = ((u16*)atlas->pixels)[src_idx];
} else {
new_pixels[dst_idx] = atlas->pixels[src_idx];
}
new_pixels[dst_idx] = atlas->pixels[src_idx];
}
}
@ -304,15 +295,14 @@ u32 pxl8_atlas_add_texture(
pxl8_atlas* atlas,
const u8* pixels,
u32 w,
u32 h,
pxl8_pixel_mode pixel_mode
u32 h
) {
if (!atlas || !pixels) return UINT32_MAX;
pxl8_skyline_fit fit =
pxl8_skyline_find_position(&atlas->skyline, atlas->width, atlas->height, w, h);
if (!fit.found) {
if (!pxl8_atlas_expand(atlas, pixel_mode)) {
if (!pxl8_atlas_expand(atlas)) {
return UINT32_MAX;
}
@ -347,17 +337,11 @@ u32 pxl8_atlas_add_texture(
entry->h = h;
entry->log2_w = pxl8_log2(w);
i32 bytes_per_pixel = pxl8_bytes_per_pixel(pixel_mode);
for (u32 y = 0; y < h; y++) {
for (u32 x = 0; x < w; x++) {
u32 src_idx = y * w + x;
u32 dst_idx = (fit.pos.y + y) * atlas->width + (fit.pos.x + x);
if (bytes_per_pixel == 2) {
((u16*)atlas->pixels)[dst_idx] = ((const u16*)pixels)[src_idx];
} else {
atlas->pixels[dst_idx] = pixels[src_idx];
}
atlas->pixels[dst_idx] = pixels[src_idx];
}
}

View file

@ -30,11 +30,11 @@ static inline u8 pxl8_log2(u32 v) {
extern "C" {
#endif
u32 pxl8_atlas_add_texture(pxl8_atlas* atlas, const u8* pixels, u32 w, u32 h, pxl8_pixel_mode pixel_mode);
u32 pxl8_atlas_add_texture(pxl8_atlas* atlas, const u8* pixels, u32 w, u32 h);
void pxl8_atlas_clear(pxl8_atlas* atlas, u32 preserve_count);
pxl8_atlas* pxl8_atlas_create(u32 width, u32 height, pxl8_pixel_mode pixel_mode);
pxl8_atlas* pxl8_atlas_create(u32 width, u32 height);
void pxl8_atlas_destroy(pxl8_atlas* atlas);
bool pxl8_atlas_expand(pxl8_atlas* atlas, pxl8_pixel_mode pixel_mode);
bool pxl8_atlas_expand(pxl8_atlas* atlas);
const pxl8_atlas_entry* pxl8_atlas_get_entry(const pxl8_atlas* atlas, u32 id);
u32 pxl8_atlas_get_entry_count(const pxl8_atlas* atlas);
u32 pxl8_atlas_get_height(const pxl8_atlas* atlas);

View file

@ -1,34 +1,7 @@
#include "pxl8_blit.h"
void pxl8_blit_hicolor(u16* fb, u32 fb_width, const u16* sprite, u32 atlas_width,
i32 x, i32 y, u32 w, u32 h) {
u16* dest_base = fb + y * fb_width + x;
const u16* src_base = sprite;
for (u32 row = 0; row < h; row++) {
u16* dest_row = dest_base + row * fb_width;
const u16* src_row = src_base + row * atlas_width;
u32 col = 0;
u32 count2 = w / 2;
for (u32 i = 0; i < count2; i++) {
u32 pixels = ((const u32*)src_row)[i];
if (pixels == 0) {
col += 2;
continue;
}
dest_row[col] = pxl8_blend_hicolor((u16)(pixels), dest_row[col]);
dest_row[col + 1] = pxl8_blend_hicolor((u16)(pixels >> 16), dest_row[col + 1]);
col += 2;
}
if (w & 1) {
dest_row[col] = pxl8_blend_hicolor(src_row[col], dest_row[col]);
}
}
}
void pxl8_blit_indexed(u8* fb, u32 fb_width, const u8* sprite, u32 atlas_width,
i32 x, i32 y, u32 w, u32 h) {
void pxl8_blit(u8* fb, u32 fb_width, const u8* sprite, u32 atlas_width,
i32 x, i32 y, u32 w, u32 h) {
u8* dest_base = fb + y * fb_width + x;
const u8* src_base = sprite;
@ -44,14 +17,14 @@ void pxl8_blit_indexed(u8* fb, u32 fb_width, const u8* sprite, u32 atlas_width,
col += 4;
continue;
}
dest_row[col] = pxl8_blend_indexed((u8)(pixels), dest_row[col]);
dest_row[col + 1] = pxl8_blend_indexed((u8)(pixels >> 8), dest_row[col + 1]);
dest_row[col + 2] = pxl8_blend_indexed((u8)(pixels >> 16), dest_row[col + 2]);
dest_row[col + 3] = pxl8_blend_indexed((u8)(pixels >> 24), dest_row[col + 3]);
dest_row[col] = pxl8_blit_mask((u8)(pixels), dest_row[col]);
dest_row[col + 1] = pxl8_blit_mask((u8)(pixels >> 8), dest_row[col + 1]);
dest_row[col + 2] = pxl8_blit_mask((u8)(pixels >> 16), dest_row[col + 2]);
dest_row[col + 3] = pxl8_blit_mask((u8)(pixels >> 24), dest_row[col + 3]);
col += 4;
}
for (; col < w; col++) {
dest_row[col] = pxl8_blend_indexed(src_row[col], dest_row[col]);
dest_row[col] = pxl8_blit_mask(src_row[col], dest_row[col]);
}
}
}

View file

@ -6,22 +6,12 @@
extern "C" {
#endif
static inline u8 pxl8_blend_indexed(u8 src, u8 dst) {
static inline u8 pxl8_blit_mask(u8 src, u8 dst) {
u8 m = (u8)(-(src != 0));
return (src & m) | (dst & ~m);
}
static inline u16 pxl8_blend_hicolor(u16 src, u16 dst) {
u16 m = (u16)(-(src != 0));
return (src & m) | (dst & ~m);
}
void pxl8_blit_hicolor(
u16* fb, u32 fb_width,
const u16* sprite, u32 atlas_width,
i32 x, i32 y, u32 w, u32 h
);
void pxl8_blit_indexed(
void pxl8_blit(
u8* fb, u32 fb_width,
const u8* sprite, u32 atlas_width,
i32 x, i32 y, u32 w, u32 h

View file

@ -2,10 +2,6 @@
#include "pxl8_types.h"
static inline i32 pxl8_bytes_per_pixel(pxl8_pixel_mode mode) {
return (i32)mode;
}
static inline u32 pxl8_color_from_rgba(u32 rgba) {
u8 r = (rgba >> 24) & 0xFF;
u8 g = (rgba >> 16) & 0xFF;
@ -76,30 +72,10 @@ static inline void pxl8_rgb332_unpack(u8 c, u8* r, u8* g, u8* b) {
*b = (bi << 6) | (bi << 4) | (bi << 2) | bi;
}
static inline u16 pxl8_rgb565_pack(u8 r, u8 g, u8 b) {
return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
}
static inline void pxl8_rgb565_unpack(u16 color, u8* r, u8* g, u8* b) {
*r = (color >> 11) << 3;
*g = ((color >> 5) & 0x3F) << 2;
*b = (color & 0x1F) << 3;
}
static inline u32 pxl8_rgb565_to_rgba32(u16 color) {
u8 r, g, b;
pxl8_rgb565_unpack(color, &r, &g, &b);
return r | ((u32)g << 8) | ((u32)b << 16) | 0xFF000000;
}
static inline u32 pxl8_rgba32_pack(u8 r, u8 g, u8 b, u8 a) {
return r | ((u32)g << 8) | ((u32)b << 16) | ((u32)a << 24);
}
static inline u16 pxl8_rgba32_to_rgb565(u32 rgba) {
return pxl8_rgb565_pack(rgba & 0xFF, (rgba >> 8) & 0xFF, (rgba >> 16) & 0xFF);
}
static inline void pxl8_rgba32_unpack(u32 color, u8* r, u8* g, u8* b, u8* a) {
*r = color & 0xFF;
*g = (color >> 8) & 0xFF;

View file

@ -6,7 +6,6 @@
#include "pxl8_ase.h"
#include "pxl8_atlas.h"
#include "pxl8_blit.h"
#include "pxl8_color.h"
#include "pxl8_colormap.h"
#include "pxl8_font.h"
#include "pxl8_glows.h"
@ -72,7 +71,6 @@ struct pxl8_gfx {
pxl8_palette_cube* palette_cube;
pxl8_gfx_pass frame_pass;
pxl8_frame_resources frame_res;
pxl8_pixel_mode pixel_mode;
void* platform_data;
pxl8_sprite_cache_entry* sprite_cache;
u32 sprite_cache_capacity;
@ -100,20 +98,11 @@ pxl8_bounds pxl8_gfx_get_bounds(pxl8_gfx* gfx) {
return bounds;
}
pxl8_pixel_mode pxl8_gfx_get_pixel_mode(pxl8_gfx* gfx) {
return gfx ? gfx->pixel_mode : PXL8_PIXEL_INDEXED;
}
u8* pxl8_gfx_get_framebuffer_indexed(pxl8_gfx* gfx) {
if (!gfx || gfx->pixel_mode == PXL8_PIXEL_HICOLOR) return NULL;
u8* pxl8_gfx_framebuffer(pxl8_gfx* gfx) {
if (!gfx) return NULL;
return (u8*)pxl8_texture_get_data(gfx->renderer, gfx->color_target);
}
u16* pxl8_gfx_get_framebuffer_hicolor(pxl8_gfx* gfx) {
if (!gfx || gfx->pixel_mode != PXL8_PIXEL_HICOLOR) return NULL;
return (u16*)pxl8_texture_get_data(gfx->renderer, gfx->color_target);
}
i32 pxl8_gfx_get_height(const pxl8_gfx* gfx) {
return gfx ? gfx->framebuffer_height : 0;
}
@ -125,9 +114,8 @@ u32* pxl8_gfx_get_output(pxl8_gfx* gfx) {
void pxl8_gfx_resolve(pxl8_gfx* gfx) {
if (!gfx || !gfx->initialized) return;
if (gfx->pixel_mode != PXL8_PIXEL_INDEXED) return;
const u32* pal = gfx->palette ? pxl8_palette_colors(gfx->palette) : NULL;
const u32* pal = pxl8_palette_colors(gfx->palette);
if (!pal) {
pxl8_error("resolve: no palette!");
return;
@ -180,10 +168,8 @@ i32 pxl8_gfx_load_palette(pxl8_gfx* gfx, const char* filepath) {
if (!gfx || !gfx->palette || !filepath) return -1;
pxl8_result result = pxl8_palette_load_ase(gfx->palette, filepath);
if (result != PXL8_OK) return (i32)result;
if (gfx->pixel_mode != PXL8_PIXEL_HICOLOR) {
if (gfx->colormap) {
pxl8_colormap_generate(gfx->colormap, pxl8_palette_colors(gfx->palette));
}
if (gfx->colormap) {
pxl8_colormap_generate(gfx->colormap, pxl8_palette_colors(gfx->palette));
}
return 0;
}
@ -191,7 +177,6 @@ i32 pxl8_gfx_load_palette(pxl8_gfx* gfx, const char* filepath) {
pxl8_gfx* pxl8_gfx_create(
const pxl8_hal* hal,
void* platform_data,
pxl8_pixel_mode mode,
pxl8_resolution resolution
) {
pxl8_shader_registry_init();
@ -205,7 +190,6 @@ pxl8_gfx* pxl8_gfx_create(
gfx->hal = hal;
gfx->platform_data = platform_data;
gfx->pixel_mode = mode;
pxl8_size size = pxl8_get_resolution_dimensions(resolution);
gfx->framebuffer_width = size.w;
gfx->framebuffer_height = size.h;
@ -216,9 +200,7 @@ pxl8_gfx* pxl8_gfx_create(
return NULL;
}
if (mode != PXL8_PIXEL_HICOLOR) {
gfx->palette = pxl8_palette_create();
}
gfx->palette = pxl8_palette_create();
gfx->renderer = pxl8_renderer_create(gfx->framebuffer_width, gfx->framebuffer_height);
if (!gfx->renderer) {
@ -258,9 +240,7 @@ pxl8_gfx* pxl8_gfx_create(
return NULL;
}
if (mode != PXL8_PIXEL_HICOLOR) {
gfx->colormap = pxl8_calloc(1, sizeof(pxl8_colormap));
}
gfx->colormap = pxl8_calloc(1, sizeof(pxl8_colormap));
gfx->target_stack[0] = (pxl8_target_entry){
.color = gfx->color_target,
@ -313,7 +293,7 @@ void pxl8_gfx_destroy(pxl8_gfx* gfx) {
static pxl8_result pxl8_gfx_ensure_atlas(pxl8_gfx* gfx) {
if (gfx->atlas) return PXL8_OK;
gfx->atlas = pxl8_atlas_create(PXL8_DEFAULT_ATLAS_SIZE, PXL8_DEFAULT_ATLAS_SIZE, gfx->pixel_mode);
gfx->atlas = pxl8_atlas_create(PXL8_DEFAULT_ATLAS_SIZE, PXL8_DEFAULT_ATLAS_SIZE);
return gfx->atlas ? PXL8_OK : PXL8_ERROR_OUT_OF_MEMORY;
}
@ -335,7 +315,7 @@ pxl8_result pxl8_gfx_create_texture(pxl8_gfx* gfx, const u8* pixels, u32 width,
pxl8_result result = pxl8_gfx_ensure_atlas(gfx);
if (result != PXL8_OK) return result;
u32 texture_id = pxl8_atlas_add_texture(gfx->atlas, pixels, width, height, gfx->pixel_mode);
u32 texture_id = pxl8_atlas_add_texture(gfx->atlas, pixels, width, height);
if (texture_id == UINT32_MAX) {
pxl8_error("Texture doesn't fit in atlas");
return PXL8_ERROR_INVALID_SIZE;
@ -381,8 +361,7 @@ pxl8_result pxl8_gfx_load_sprite(pxl8_gfx* gfx, const char* path) {
gfx->atlas,
ase_file.frames[0].pixels,
ase_file.header.width,
ase_file.header.height,
gfx->pixel_mode
ase_file.header.height
);
pxl8_ase_destroy(&ase_file);
@ -426,28 +405,14 @@ pxl8_result pxl8_gfx_load_font_atlas(pxl8_gfx* gfx) {
void pxl8_gfx_upload_framebuffer(pxl8_gfx* gfx) {
if (!gfx || !gfx->initialized || !gfx->hal) return;
if (gfx->pixel_mode == PXL8_PIXEL_INDEXED) {
pxl8_gfx_resolve(gfx);
gfx->hal->upload_texture(
gfx->platform_data,
gfx->output,
gfx->framebuffer_width,
gfx->framebuffer_height,
PXL8_PIXEL_RGBA,
NULL
);
return;
}
u32* colors = gfx->palette ? pxl8_palette_colors(gfx->palette) : NULL;
u8* framebuffer = (u8*)pxl8_texture_get_data(gfx->renderer, gfx->color_target);
pxl8_gfx_resolve(gfx);
gfx->hal->upload_texture(
gfx->platform_data,
framebuffer,
gfx->output,
gfx->framebuffer_width,
gfx->framebuffer_height,
gfx->pixel_mode,
colors
4,
NULL
);
}
@ -639,7 +604,7 @@ void pxl8_2d_sprite(pxl8_gfx* gfx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h, bo
if (is_1to1_scale && is_unclipped && !is_flipped) {
const u8* sprite_data = atlas_pixels + entry->y * atlas_width + entry->x;
pxl8_blit_indexed(framebuffer, fb_width, sprite_data, atlas_width, x, y, w, h);
pxl8_blit(framebuffer, fb_width, sprite_data, atlas_width, x, y, w, h);
} else {
for (i32 py = 0; py < draw_height; py++) {
for (i32 px = 0; px < draw_width; px++) {
@ -652,7 +617,7 @@ void pxl8_2d_sprite(pxl8_gfx* gfx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h, bo
i32 src_idx = src_y * atlas_width + src_x;
i32 dest_idx = (dest_y + py) * fb_width + (dest_x + px);
framebuffer[dest_idx] = pxl8_blend_indexed(atlas_pixels[src_idx], framebuffer[dest_idx]);
framebuffer[dest_idx] = pxl8_blit_mask(atlas_pixels[src_idx], framebuffer[dest_idx]);
}
}
}

View file

@ -36,7 +36,7 @@ typedef enum pxl8_gfx_effect {
extern "C" {
#endif
pxl8_gfx* pxl8_gfx_create(const pxl8_hal* hal, void* platform_data, pxl8_pixel_mode mode, pxl8_resolution resolution);
pxl8_gfx* pxl8_gfx_create(const pxl8_hal* hal, void* platform_data, pxl8_resolution resolution);
void pxl8_gfx_destroy(pxl8_gfx* gfx);
void pxl8_gfx_present(pxl8_gfx* gfx);
@ -46,14 +46,12 @@ void pxl8_gfx_upload_framebuffer(pxl8_gfx* gfx);
u8 pxl8_gfx_find_color(pxl8_gfx* gfx, u32 color);
pxl8_bounds pxl8_gfx_get_bounds(pxl8_gfx* gfx);
u8* pxl8_gfx_get_framebuffer_indexed(pxl8_gfx* gfx);
u16* pxl8_gfx_get_framebuffer_hicolor(pxl8_gfx* gfx);
u8* pxl8_gfx_framebuffer(pxl8_gfx* gfx);
i32 pxl8_gfx_get_height(const pxl8_gfx* gfx);
const u32* pxl8_gfx_palette_colors(pxl8_gfx* gfx);
u16* pxl8_gfx_get_zbuffer(pxl8_gfx* gfx);
pxl8_palette* pxl8_gfx_palette(pxl8_gfx* gfx);
pxl8_colormap* pxl8_gfx_colormap(pxl8_gfx* gfx);
pxl8_pixel_mode pxl8_gfx_get_pixel_mode(pxl8_gfx* gfx);
i32 pxl8_gfx_get_width(const pxl8_gfx* gfx);
i32 pxl8_gfx_load_palette(pxl8_gfx* gfx, const char* filepath);

View file

@ -83,7 +83,7 @@ static u8 palette_find_closest(const u32* palette, u8 r, u8 g, u8 b) {
return best_idx;
}
static u8 blend_indexed(
static u8 blend_colors(
const pxl8_gfx_pipeline_desc* pipeline,
u8 src,
u8 dst,
@ -540,7 +540,7 @@ static void rasterize_triangle(
if (color != 0) {
u8 out_color = color;
if (blend_enabled) {
out_color = blend_indexed(pipeline, color, prow[px], palette);
out_color = blend_colors(pipeline, color, prow[px], palette);
}
prow[px] = out_color;
@ -586,7 +586,7 @@ static void rasterize_triangle(
if (color != 0) {
u8 out_color = color;
if (blend_enabled) {
out_color = blend_indexed(pipeline, color, prow[px], palette);
out_color = blend_colors(pipeline, color, prow[px], palette);
}
prow[px] = out_color;

View file

@ -17,7 +17,6 @@ struct pxl8_tilesheet {
u32 tiles_per_row;
u32 total_tiles;
u32 width;
pxl8_pixel_mode pixel_mode;
u32 ref_count;
pxl8_tile_animation* animations;
u32 animation_count;
@ -547,7 +546,6 @@ pxl8_result pxl8_tilemap_load_ase(pxl8_tilemap* tilemap, const char* filepath, u
tilemap->tilesheet->height = tilesheet_height;
tilemap->tilesheet->tiles_per_row = tiles_per_row;
tilemap->tilesheet->total_tiles = tileset->tile_count;
tilemap->tilesheet->pixel_mode = PXL8_PIXEL_INDEXED;
if (tilemap->tilesheet->tile_valid) pxl8_free(tilemap->tilesheet->tile_valid);
tilemap->tilesheet->tile_valid = pxl8_calloc(tileset->tile_count + 1, sizeof(bool));

View file

@ -4,7 +4,6 @@
#include <string.h>
#include "pxl8_ase.h"
#include "pxl8_color.h"
#include "pxl8_gfx.h"
#include "pxl8_log.h"
#include "pxl8_mem.h"
@ -20,7 +19,6 @@ struct pxl8_tilesheet {
u32 total_tiles;
u32 width;
pxl8_pixel_mode pixel_mode;
u32 ref_count;
pxl8_tile_animation* animations;
@ -106,14 +104,10 @@ pxl8_result pxl8_tilesheet_load(pxl8_tilesheet* tilesheet, const char* filepath,
tilesheet->height = height;
tilesheet->tiles_per_row = width / tilesheet->tile_size;
tilesheet->total_tiles = (width / tilesheet->tile_size) * (height / tilesheet->tile_size);
tilesheet->pixel_mode = pxl8_gfx_get_pixel_mode(gfx);
u32 pixel_count = width * height;
u16 ase_depth = ase_file.header.color_depth;
bool gfx_hicolor = (tilesheet->pixel_mode == PXL8_PIXEL_HICOLOR);
usize data_size = pixel_count * pxl8_bytes_per_pixel(tilesheet->pixel_mode);
tilesheet->data = pxl8_malloc(data_size);
tilesheet->data = pxl8_malloc(pixel_count);
if (!tilesheet->data) {
pxl8_ase_destroy(&ase_file);
return PXL8_ERROR_OUT_OF_MEMORY;
@ -122,34 +116,10 @@ pxl8_result pxl8_tilesheet_load(pxl8_tilesheet* tilesheet, const char* filepath,
if (ase_file.frame_count > 0 && ase_file.frames[0].pixels) {
const u8* src = ase_file.frames[0].pixels;
if (ase_depth == 8 && !gfx_hicolor) {
memcpy(tilesheet->data, src, pixel_count);
} else if (ase_depth == 32 && gfx_hicolor) {
u16* dst = (u16*)tilesheet->data;
const u32* rgba = (const u32*)src;
for (u32 i = 0; i < pixel_count; i++) {
u32 c = rgba[i];
u8 a = (c >> 24) & 0xFF;
if (a == 0) {
dst[i] = 0;
} else {
dst[i] = pxl8_rgba32_to_rgb565(c);
}
}
} else if (ase_depth == 8 && gfx_hicolor) {
pxl8_warn("Indexed ASE with hicolor gfx - storing as indexed");
tilesheet->pixel_mode = PXL8_PIXEL_INDEXED;
u8* new_data = pxl8_realloc(tilesheet->data, pixel_count);
if (!new_data) {
pxl8_free(tilesheet->data);
tilesheet->data = NULL;
pxl8_ase_destroy(&ase_file);
return PXL8_ERROR_OUT_OF_MEMORY;
}
tilesheet->data = new_data;
if (ase_depth == 8) {
memcpy(tilesheet->data, src, pixel_count);
} else {
pxl8_error("Unsupported ASE color depth %d for gfx mode", ase_depth);
pxl8_error("Unsupported ASE color depth %d (expected indexed 8-bit)", ase_depth);
pxl8_free(tilesheet->data);
tilesheet->data = NULL;
pxl8_ase_destroy(&ase_file);
@ -166,7 +136,6 @@ pxl8_result pxl8_tilesheet_load(pxl8_tilesheet* tilesheet, const char* filepath,
}
u32 valid_tiles = 0;
bool is_hicolor = (tilesheet->pixel_mode == PXL8_PIXEL_HICOLOR);
for (u32 tile_id = 1; tile_id <= tilesheet->total_tiles; tile_id++) {
u32 tile_x = ((tile_id - 1) % tilesheet->tiles_per_row) * tilesheet->tile_size;
@ -176,16 +145,9 @@ pxl8_result pxl8_tilesheet_load(pxl8_tilesheet* tilesheet, const char* filepath,
for (u32 py = 0; py < tilesheet->tile_size && !has_content; py++) {
for (u32 px = 0; px < tilesheet->tile_size; px++) {
u32 idx = (tile_y + py) * width + (tile_x + px);
if (is_hicolor) {
if (((u16*)tilesheet->data)[idx] != 0) {
has_content = true;
break;
}
} else {
if (tilesheet->data[idx] != 0) {
has_content = true;
break;
}
if (tilesheet->data[idx] != 0) {
has_content = true;
break;
}
}
}
@ -312,7 +274,6 @@ pxl8_result pxl8_tilesheet_set_tile_pixels(pxl8_tilesheet* tilesheet, u16 tile_i
u32 tile_x = (tile_id - 1) % tilesheet->tiles_per_row;
u32 tile_y = (tile_id - 1) / tilesheet->tiles_per_row;
u32 bytes_per_pixel = pxl8_bytes_per_pixel(tilesheet->pixel_mode);
for (u32 py = 0; py < tilesheet->tile_size; py++) {
for (u32 px = 0; px < tilesheet->tile_size; px++) {
@ -320,12 +281,7 @@ pxl8_result pxl8_tilesheet_set_tile_pixels(pxl8_tilesheet* tilesheet, u16 tile_i
u32 dst_x = tile_x * tilesheet->tile_size + px;
u32 dst_y = tile_y * tilesheet->tile_size + py;
u32 dst_idx = dst_y * tilesheet->width + dst_x;
if (bytes_per_pixel == 2) {
((u16*)tilesheet->data)[dst_idx] = ((const u16*)pixels)[src_idx];
} else {
tilesheet->data[dst_idx] = pixels[src_idx];
}
tilesheet->data[dst_idx] = pixels[src_idx];
}
}

View file

@ -170,11 +170,7 @@ void pxl8_transition_render(const pxl8_transition* transition, pxl8_gfx* gfx) {
i32 block_size = (i32)(max_block_size * progress);
if (block_size < 1) block_size = 1;
pxl8_pixel_mode mode = pxl8_gfx_get_pixel_mode(gfx);
bool has_fb = (mode == PXL8_PIXEL_HICOLOR)
? (pxl8_gfx_get_framebuffer_hicolor(gfx) != NULL)
: (pxl8_gfx_get_framebuffer_indexed(gfx) != NULL);
if (!has_fb) break;
if (!pxl8_gfx_framebuffer(gfx)) break;
for (i32 y = 0; y < height; y += block_size) {
for (i32 x = 0; x < width; x += block_size) {

View file

@ -125,16 +125,9 @@ static void sdl3_upload_texture(void* platform_data, const void* pixels, u32 w,
ctx->rgba_buffer_size = pixel_count;
}
if (bpp == 2) {
const u16* pixels16 = (const u16*)pixels;
for (u32 i = 0; i < pixel_count; i++) {
ctx->rgba_buffer[i] = pxl8_rgb565_to_rgba32(pixels16[i]);
}
} else {
const u8* pixels8 = (const u8*)pixels;
for (u32 i = 0; i < pixel_count; i++) {
ctx->rgba_buffer[i] = palette[pixels8[i]];
}
const u8* pixels8 = (const u8*)pixels;
for (u32 i = 0; i < pixel_count; i++) {
ctx->rgba_buffer[i] = palette[pixels8[i]];
}
SDL_UpdateTexture(ctx->framebuffer, NULL, ctx->rgba_buffer, w * 4);

View file

@ -64,6 +64,10 @@ function World:init_local_player(x, y, z)
C.pxl8_world_init_local_player(self._ptr, x, y, z)
end
function World:set_look(yaw, pitch)
C.pxl8_world_set_look(self._ptr, yaw, pitch or 0)
end
function World:local_player()
local ptr = C.pxl8_world_local_player(self._ptr)
if ptr == nil then return nil end

View file

@ -1214,12 +1214,6 @@ static pxl8_resolution parse_resolution(const char* str) {
return PXL8_RESOLUTION_640x360;
}
static pxl8_pixel_mode parse_pixel_mode(const char* str) {
if (strcmp(str, "indexed") == 0) return PXL8_PIXEL_INDEXED;
if (strcmp(str, "hicolor") == 0) return PXL8_PIXEL_HICOLOR;
return PXL8_PIXEL_INDEXED;
}
pxl8_result pxl8_script_load_cart_manifest(pxl8_script* script, pxl8_cart* cart) {
if (!script || !script->L || !cart) return PXL8_ERROR_NULL_POINTER;
@ -1267,12 +1261,6 @@ pxl8_result pxl8_script_load_cart_manifest(pxl8_script* script, pxl8_cart* cart)
}
lua_pop(script->L, 1);
lua_getfield(script->L, -1, "pixel-mode");
if (lua_isstring(script->L, -1)) {
pxl8_cart_set_pixel_mode(cart, parse_pixel_mode(lua_tostring(script->L, -1)));
}
lua_pop(script->L, 1);
lua_getfield(script->L, -1, "window-size");
if (lua_istable(script->L, -1)) {
lua_rawgeti(script->L, -1, 1);

View file

@ -27,7 +27,7 @@ static const char* pxl8_ffi_cdefs =
"f32 pxl8_get_fps(const pxl8* sys);\n"
"\n"
"u8 pxl8_gfx_find_color(pxl8_gfx* gfx, u32 color);\n"
"u8* pxl8_gfx_get_framebuffer_indexed(pxl8_gfx* gfx);\n"
"u8* pxl8_gfx_framebuffer(pxl8_gfx* gfx);\n"
"i32 pxl8_gfx_get_height(pxl8_gfx* ctx);\n"
"u32* pxl8_gfx_get_light_accum(pxl8_gfx* gfx);\n"
"const u32* pxl8_gfx_palette_colors(pxl8_gfx* gfx);\n"
@ -437,6 +437,7 @@ static const char* pxl8_ffi_cdefs =
"} pxl8_sim_entity;\n"
"\n"
"void pxl8_world_init_local_player(pxl8_world* world, f32 x, f32 y, f32 z);\n"
"void pxl8_world_set_look(pxl8_world* world, f32 yaw, f32 pitch);\n"
"pxl8_sim_entity* pxl8_world_local_player(pxl8_world* world);\n"
"\n"
"typedef struct { i32 cursor_x; i32 cursor_y; bool cursor_down; bool cursor_clicked; u32 hot_id; u32 active_id; } pxl8_gui_state;\n"

View file

@ -316,6 +316,7 @@ void pxl8_world_init_local_player(pxl8_world* world, f32 x, f32 y, f32 z) {
world->local_player.flags = PXL8_SIM_FLAG_ALIVE | PXL8_SIM_FLAG_PLAYER | PXL8_SIM_FLAG_GROUNDED;
world->local_player.kind = 0;
world->client_tick = 0;
world->pointer_motion = (pxl8_vec2){0};
#ifdef PXL8_ASYNC_THREADS
world->render_state[0] = world->local_player;
@ -323,6 +324,20 @@ void pxl8_world_init_local_player(pxl8_world* world, f32 x, f32 y, f32 z) {
#endif
}
void pxl8_world_set_look(pxl8_world* world, f32 yaw, f32 pitch) {
if (!world) return;
world->local_player.yaw = yaw;
world->local_player.pitch = pitch;
world->pointer_motion = (pxl8_vec2){.yaw = yaw, .pitch = pitch};
#ifdef PXL8_ASYNC_THREADS
world->render_state[0].yaw = yaw;
world->render_state[0].pitch = pitch;
world->render_state[1].yaw = yaw;
world->render_state[1].pitch = pitch;
#endif
}
pxl8_sim_entity* pxl8_world_local_player(pxl8_world* world) {
if (!world) return NULL;
#ifdef PXL8_ASYNC_THREADS

View file

@ -37,6 +37,7 @@ void pxl8_world_set_bsp_material(pxl8_world* world, u16 material_id, const pxl8_
void pxl8_world_set_sim_config(pxl8_world* world, const pxl8_sim_config* config);
void pxl8_world_init_local_player(pxl8_world* world, f32 x, f32 y, f32 z);
void pxl8_world_set_look(pxl8_world* world, f32 yaw, f32 pitch);
pxl8_sim_entity* pxl8_world_local_player(pxl8_world* world);
pxl8_sim_world pxl8_world_sim_world(const pxl8_world* world, pxl8_vec3 pos);
void pxl8_world_predict(pxl8_world* world, pxl8_net* net, const pxl8_input_msg* input, f32 dt);