Compare commits

..

No commits in common. "71787869e06101f0d47c6ee01f7147260cacf1b4" and "39b604b333c1f410c40b63c5d0d748614385fd8e" have entirely different histories.

136 changed files with 1915 additions and 13442 deletions

View file

@ -8,67 +8,55 @@ static const char embed_fennel[] = {
, 0 }; , 0 };
static const char embed_pxl8[] = { static const char embed_pxl8[] = {
#embed "src/lua/pxl8.lua" #embed "client/src/lua/pxl8.lua"
, 0 }; , 0 };
static const char embed_pxl8_anim[] = { static const char embed_pxl8_anim[] = {
#embed "src/lua/pxl8/anim.lua" #embed "client/src/lua/pxl8/anim.lua"
, 0 };
static const char embed_pxl8_bytes[] = {
#embed "src/lua/pxl8/bytes.lua"
, 0 }; , 0 };
static const char embed_pxl8_core[] = { static const char embed_pxl8_core[] = {
#embed "src/lua/pxl8/core.lua" #embed "client/src/lua/pxl8/core.lua"
, 0 };
static const char embed_pxl8_effects[] = {
#embed "src/lua/pxl8/effects.lua"
, 0 }; , 0 };
static const char embed_pxl8_gfx2d[] = { static const char embed_pxl8_gfx2d[] = {
#embed "src/lua/pxl8/gfx2d.lua" #embed "client/src/lua/pxl8/gfx2d.lua"
, 0 }; , 0 };
static const char embed_pxl8_gfx3d[] = { static const char embed_pxl8_gfx3d[] = {
#embed "src/lua/pxl8/gfx3d.lua" #embed "client/src/lua/pxl8/gfx3d.lua"
, 0 }; , 0 };
static const char embed_pxl8_gui[] = { static const char embed_pxl8_gui[] = {
#embed "src/lua/pxl8/gui.lua" #embed "client/src/lua/pxl8/gui.lua"
, 0 }; , 0 };
static const char embed_pxl8_input[] = { static const char embed_pxl8_input[] = {
#embed "src/lua/pxl8/input.lua" #embed "client/src/lua/pxl8/input.lua"
, 0 }; , 0 };
static const char embed_pxl8_math[] = { static const char embed_pxl8_math[] = {
#embed "src/lua/pxl8/math.lua" #embed "client/src/lua/pxl8/math.lua"
, 0 };
static const char embed_pxl8_net[] = {
#embed "src/lua/pxl8/net.lua"
, 0 }; , 0 };
static const char embed_pxl8_particles[] = { static const char embed_pxl8_particles[] = {
#embed "src/lua/pxl8/particles.lua" #embed "client/src/lua/pxl8/particles.lua"
, 0 }; , 0 };
static const char embed_pxl8_sfx[] = { static const char embed_pxl8_sfx[] = {
#embed "src/lua/pxl8/sfx.lua" #embed "client/src/lua/pxl8/sfx.lua"
, 0 }; , 0 };
static const char embed_pxl8_tilemap[] = { static const char embed_pxl8_tilemap[] = {
#embed "src/lua/pxl8/tilemap.lua" #embed "client/src/lua/pxl8/tilemap.lua"
, 0 }; , 0 };
static const char embed_pxl8_transition[] = { static const char embed_pxl8_transition[] = {
#embed "src/lua/pxl8/transition.lua" #embed "client/src/lua/pxl8/transition.lua"
, 0 }; , 0 };
static const char embed_pxl8_world[] = { static const char embed_pxl8_world[] = {
#embed "src/lua/pxl8/world.lua" #embed "client/src/lua/pxl8/world.lua"
, 0 }; , 0 };
#define PXL8_EMBED_ENTRY(var, name) {name, var, sizeof(var) - 1} #define PXL8_EMBED_ENTRY(var, name) {name, var, sizeof(var) - 1}
@ -79,15 +67,12 @@ static const pxl8_embed pxl8_embeds[] = {
PXL8_EMBED_ENTRY(embed_fennel, "fennel"), PXL8_EMBED_ENTRY(embed_fennel, "fennel"),
PXL8_EMBED_ENTRY(embed_pxl8, "pxl8"), PXL8_EMBED_ENTRY(embed_pxl8, "pxl8"),
PXL8_EMBED_ENTRY(embed_pxl8_anim, "pxl8.anim"), PXL8_EMBED_ENTRY(embed_pxl8_anim, "pxl8.anim"),
PXL8_EMBED_ENTRY(embed_pxl8_bytes, "pxl8.bytes"),
PXL8_EMBED_ENTRY(embed_pxl8_core, "pxl8.core"), PXL8_EMBED_ENTRY(embed_pxl8_core, "pxl8.core"),
PXL8_EMBED_ENTRY(embed_pxl8_effects, "pxl8.effects"),
PXL8_EMBED_ENTRY(embed_pxl8_gfx2d, "pxl8.gfx2d"), PXL8_EMBED_ENTRY(embed_pxl8_gfx2d, "pxl8.gfx2d"),
PXL8_EMBED_ENTRY(embed_pxl8_gfx3d, "pxl8.gfx3d"), PXL8_EMBED_ENTRY(embed_pxl8_gfx3d, "pxl8.gfx3d"),
PXL8_EMBED_ENTRY(embed_pxl8_gui, "pxl8.gui"), PXL8_EMBED_ENTRY(embed_pxl8_gui, "pxl8.gui"),
PXL8_EMBED_ENTRY(embed_pxl8_input, "pxl8.input"), PXL8_EMBED_ENTRY(embed_pxl8_input, "pxl8.input"),
PXL8_EMBED_ENTRY(embed_pxl8_math, "pxl8.math"), PXL8_EMBED_ENTRY(embed_pxl8_math, "pxl8.math"),
PXL8_EMBED_ENTRY(embed_pxl8_net, "pxl8.net"),
PXL8_EMBED_ENTRY(embed_pxl8_particles, "pxl8.particles"), PXL8_EMBED_ENTRY(embed_pxl8_particles, "pxl8.particles"),
PXL8_EMBED_ENTRY(embed_pxl8_sfx, "pxl8.sfx"), PXL8_EMBED_ENTRY(embed_pxl8_sfx, "pxl8.sfx"),
PXL8_EMBED_ENTRY(embed_pxl8_tilemap, "pxl8.tilemap"), PXL8_EMBED_ENTRY(embed_pxl8_tilemap, "pxl8.tilemap"),

View file

@ -6,6 +6,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <pthread.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
@ -309,7 +310,7 @@ pxl8_result pxl8_update(pxl8* sys) {
if (pxl8_script_load_module(game->script, "pxl8") != PXL8_OK) { if (pxl8_script_load_module(game->script, "pxl8") != PXL8_OK) {
const char* err_msg = pxl8_script_get_last_error(game->script); const char* err_msg = pxl8_script_get_last_error(game->script);
pxl8_error("failed to load pxl8 global: %s", err_msg); pxl8_error("failed to setup pxl8 global: %s", err_msg);
} }
sys->repl = pxl8_repl_create(); sys->repl = pxl8_repl_create();

126
client/src/core/pxl8_io.h Normal file
View file

@ -0,0 +1,126 @@
#pragma once
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include "pxl8_types.h"
typedef struct {
const u8* bytes;
u32 offset;
u32 size;
bool overflow;
} pxl8_stream;
static inline pxl8_stream pxl8_stream_create(const u8* bytes, u32 size) {
return (pxl8_stream){
.bytes = bytes,
.offset = 0,
.size = size,
.overflow = false
};
}
static inline bool pxl8_stream_can_read(const pxl8_stream* stream, u32 count) {
return !stream->overflow && stream->offset + count <= stream->size;
}
static inline bool pxl8_stream_has_overflow(const pxl8_stream* stream) {
return stream->overflow;
}
static inline void pxl8_stream_seek(pxl8_stream* stream, u32 offset) {
stream->offset = offset;
}
static inline u32 pxl8_stream_position(const pxl8_stream* stream) {
return stream->offset;
}
static inline u8 pxl8_read_u8(pxl8_stream* stream) {
if (stream->offset + 1 > stream->size) { stream->overflow = true; return 0; }
return stream->bytes[stream->offset++];
}
static inline u16 pxl8_read_u16(pxl8_stream* stream) {
if (stream->offset + 2 > stream->size) { stream->overflow = true; return 0; }
u16 val = (u16)stream->bytes[stream->offset] | ((u16)stream->bytes[stream->offset + 1] << 8);
stream->offset += 2;
return val;
}
static inline u32 pxl8_read_u32(pxl8_stream* stream) {
if (stream->offset + 4 > stream->size) { stream->overflow = true; return 0; }
u32 val = (u32)stream->bytes[stream->offset] |
((u32)stream->bytes[stream->offset + 1] << 8) |
((u32)stream->bytes[stream->offset + 2] << 16) |
((u32)stream->bytes[stream->offset + 3] << 24);
stream->offset += 4;
return val;
}
static inline i16 pxl8_read_i16(pxl8_stream* stream) {
return (i16)pxl8_read_u16(stream);
}
static inline i32 pxl8_read_i32(pxl8_stream* stream) {
return (i32)pxl8_read_u32(stream);
}
static inline f32 pxl8_read_f32(pxl8_stream* stream) {
u32 val = pxl8_read_u32(stream);
f32 result;
memcpy(&result, &val, sizeof(f32));
return result;
}
static inline void pxl8_read_bytes(pxl8_stream* stream, void* dest, u32 count) {
if (stream->offset + count > stream->size) { stream->overflow = true; return; }
for (u32 i = 0; i < count; i++) {
((u8*)dest)[i] = stream->bytes[stream->offset++];
}
}
static inline void pxl8_skip_bytes(pxl8_stream* stream, u32 count) {
if (stream->offset + count > stream->size) { stream->overflow = true; return; }
stream->offset += count;
}
static inline const u8* pxl8_read_ptr(pxl8_stream* stream, u32 count) {
if (stream->offset + count > stream->size) { stream->overflow = true; return NULL; }
const u8* ptr = &stream->bytes[stream->offset];
stream->offset += count;
return ptr;
}
#ifdef __cplusplus
extern "C" {
#endif
pxl8_result pxl8_io_create_directory(const char* path);
bool pxl8_io_file_exists(const char* path);
void pxl8_io_free_binary_data(u8* data);
void pxl8_io_free_file_content(char* content);
f64 pxl8_io_get_file_modified_time(const char* path);
pxl8_result pxl8_io_read_binary_file(const char* path, u8** data, size_t* size);
pxl8_result pxl8_io_read_file(const char* path, char** content, size_t* size);
pxl8_result pxl8_io_write_binary_file(const char* path, const u8* data, size_t size);
pxl8_result pxl8_io_write_file(const char* path, const char* content, size_t size);
bool pxl8_key_down(const pxl8_input_state* input, const char* key_name);
bool pxl8_key_pressed(const pxl8_input_state* input, const char* key_name);
bool pxl8_key_released(const pxl8_input_state* input, const char* key_name);
i32 pxl8_mouse_dx(const pxl8_input_state* input);
i32 pxl8_mouse_dy(const pxl8_input_state* input);
bool pxl8_mouse_pressed(const pxl8_input_state* input, i32 button);
bool pxl8_mouse_released(const pxl8_input_state* input, i32 button);
i32 pxl8_mouse_wheel_x(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_y(const pxl8_input_state* input);
#ifdef __cplusplus
}
#endif

View file

@ -29,9 +29,8 @@ typedef __uint128_t u128;
#endif #endif
typedef enum pxl8_pixel_mode { typedef enum pxl8_pixel_mode {
PXL8_PIXEL_INDEXED = 1, PXL8_PIXEL_INDEXED,
PXL8_PIXEL_HICOLOR = 2, PXL8_PIXEL_HICOLOR
PXL8_PIXEL_RGBA = 4,
} pxl8_pixel_mode; } pxl8_pixel_mode;
typedef enum pxl8_cursor { typedef enum pxl8_cursor {

View file

@ -18,20 +18,18 @@ void pxl8_gui_state_destroy(pxl8_gui_state* state) {
free(state); free(state);
} }
void pxl8_gui_begin_frame(pxl8_gui_state* state, pxl8_gfx* gfx) { void pxl8_gui_begin_frame(pxl8_gui_state* state) {
if (!state) return; if (!state) return;
state->hot_id = 0; state->hot_id = 0;
if (gfx) pxl8_gfx_push_target(gfx);
} }
void pxl8_gui_end_frame(pxl8_gui_state* state, pxl8_gfx* gfx) { void pxl8_gui_end_frame(pxl8_gui_state* state) {
if (!state) return; if (!state) return;
if (!state->cursor_down) { if (!state->cursor_down) {
state->active_id = 0; state->active_id = 0;
} }
state->cursor_clicked = false; state->cursor_clicked = false;
if (gfx) pxl8_gfx_pop_target(gfx);
} }
void pxl8_gui_cursor_move(pxl8_gui_state* state, i32 x, i32 y) { void pxl8_gui_cursor_move(pxl8_gui_state* state, i32 x, i32 y) {
@ -82,16 +80,16 @@ bool pxl8_gui_button(pxl8_gui_state* state, pxl8_gfx* gfx, u32 id, i32 x, i32 y,
i32 offset_y = 0; i32 offset_y = 0;
if (is_active) { if (is_active) {
bg_color = pxl8_gfx_ui_color(gfx, PXL8_UI_BG3); bg_color = 4;
border_color = pxl8_gfx_ui_color(gfx, PXL8_UI_BG2); border_color = 3;
offset_x = 1; offset_x = 1;
offset_y = 1; offset_y = 1;
} else if (is_hot || cursor_over) { } else if (is_hot || cursor_over) {
bg_color = pxl8_gfx_ui_color(gfx, PXL8_UI_BG3); bg_color = 4;
border_color = pxl8_gfx_ui_color(gfx, PXL8_UI_FG0); border_color = 8;
} else { } else {
bg_color = pxl8_gfx_ui_color(gfx, PXL8_UI_BG2); bg_color = 3;
border_color = pxl8_gfx_ui_color(gfx, PXL8_UI_BG3); border_color = 4;
} }
pxl8_2d_rect_fill(gfx, x, y, w, h, bg_color); pxl8_2d_rect_fill(gfx, x, y, w, h, bg_color);
@ -100,7 +98,7 @@ bool pxl8_gui_button(pxl8_gui_state* state, pxl8_gfx* gfx, u32 id, i32 x, i32 y,
i32 text_len = (i32)strlen(label); i32 text_len = (i32)strlen(label);
i32 text_x = x + (w / 2) - ((text_len * 8) / 2) + offset_x; i32 text_x = x + (w / 2) - ((text_len * 8) / 2) + offset_x;
i32 text_y = y + (h / 2) - 5 + offset_y; i32 text_y = y + (h / 2) - 5 + offset_y;
pxl8_2d_text(gfx, label, text_x, text_y, pxl8_gfx_ui_color(gfx, PXL8_UI_FG1)); pxl8_2d_text(gfx, label, text_x, text_y, 6);
return clicked; return clicked;
} }
@ -108,19 +106,14 @@ bool pxl8_gui_button(pxl8_gui_state* state, pxl8_gfx* gfx, u32 id, i32 x, i32 y,
void pxl8_gui_window(pxl8_gfx* gfx, i32 x, i32 y, i32 w, i32 h, const char* title) { void pxl8_gui_window(pxl8_gfx* gfx, i32 x, i32 y, i32 w, i32 h, const char* title) {
if (!gfx || !title) return; if (!gfx || !title) return;
u8 title_bg = pxl8_gfx_ui_color(gfx, PXL8_UI_BG1); pxl8_2d_rect_fill(gfx, x, y, w, 28, 1);
u8 body_bg = pxl8_gfx_ui_color(gfx, PXL8_UI_BG2); pxl8_2d_rect_fill(gfx, x, y + 28, w, h - 28, 2);
u8 border = pxl8_gfx_ui_color(gfx, PXL8_UI_BG3); pxl8_2d_rect(gfx, x, y, w, h, 4);
u8 title_fg = pxl8_gfx_ui_color(gfx, PXL8_UI_FG0); pxl8_2d_rect_fill(gfx, x, y + 28, w, 1, 4);
pxl8_2d_rect_fill(gfx, x, y, w, 28, title_bg);
pxl8_2d_rect_fill(gfx, x, y + 28, w, h - 28, body_bg);
pxl8_2d_rect(gfx, x, y, w, h, border);
pxl8_2d_rect_fill(gfx, x, y + 28, w, 1, border);
i32 title_x = x + 10; i32 title_x = x + 10;
i32 title_y = y + (28 / 2) - 5; i32 title_y = y + (28 / 2) - 5;
pxl8_2d_text(gfx, title, title_x, title_y, title_fg); pxl8_2d_text(gfx, title, title_x, title_y, 8);
} }
void pxl8_gui_label(pxl8_gfx* gfx, i32 x, i32 y, const char* text, u8 color) { void pxl8_gui_label(pxl8_gfx* gfx, i32 x, i32 y, const char* text, u8 color) {

View file

@ -22,8 +22,8 @@ void pxl8_gui_state_destroy(pxl8_gui_state* state);
void pxl8_gui_get_cursor_pos(const pxl8_gui_state* state, i32* x, i32* y); void pxl8_gui_get_cursor_pos(const pxl8_gui_state* state, i32* x, i32* y);
bool pxl8_gui_is_hovering(const pxl8_gui_state* state); bool pxl8_gui_is_hovering(const pxl8_gui_state* state);
void pxl8_gui_begin_frame(pxl8_gui_state* state, pxl8_gfx* gfx); void pxl8_gui_begin_frame(pxl8_gui_state* state);
void pxl8_gui_end_frame(pxl8_gui_state* state, pxl8_gfx* gfx); void pxl8_gui_end_frame(pxl8_gui_state* state);
void pxl8_gui_cursor_down(pxl8_gui_state* state); void pxl8_gui_cursor_down(pxl8_gui_state* state);
void pxl8_gui_cursor_move(pxl8_gui_state* state, i32 x, i32 y); void pxl8_gui_cursor_move(pxl8_gui_state* state, i32 x, i32 y);

View file

@ -56,8 +56,8 @@ void pxl8_3d_camera_lookat(pxl8_3d_camera* cam, pxl8_vec3 eye, pxl8_vec3 target,
pxl8_vec3 forward = pxl8_vec3_normalize(pxl8_vec3_sub(target, eye)); pxl8_vec3 forward = pxl8_vec3_normalize(pxl8_vec3_sub(target, eye));
cam->pitch = asinf(forward.y); cam->pitch = asinf(-forward.y);
cam->yaw = atan2f(-forward.x, -forward.z); cam->yaw = atan2f(forward.x, forward.z);
cam->roll = 0; cam->roll = 0;
(void)up; (void)up;
@ -104,9 +104,9 @@ pxl8_vec3 pxl8_3d_camera_get_forward(const pxl8_3d_camera* cam) {
f32 sy = sinf(cam->yaw); f32 sy = sinf(cam->yaw);
return (pxl8_vec3){ return (pxl8_vec3){
-sy * cp, cp * sy,
sp, -sp,
-cy * cp cp * cy
}; };
} }
@ -183,8 +183,8 @@ void pxl8_3d_camera_follow(pxl8_3d_camera* cam, pxl8_vec3 target, pxl8_vec3 offs
cam->position = pxl8_vec3_lerp(cam->position, desired, t); cam->position = pxl8_vec3_lerp(cam->position, desired, t);
pxl8_vec3 forward = pxl8_vec3_normalize(pxl8_vec3_sub(target, cam->position)); pxl8_vec3 forward = pxl8_vec3_normalize(pxl8_vec3_sub(target, cam->position));
cam->pitch = asinf(forward.y); cam->pitch = asinf(-forward.y);
cam->yaw = atan2f(-forward.x, -forward.z); cam->yaw = atan2f(forward.x, forward.z);
} }
void pxl8_3d_camera_shake(pxl8_3d_camera* cam, f32 intensity, f32 duration) { void pxl8_3d_camera_shake(pxl8_3d_camera* cam, f32 intensity, f32 duration) {
@ -212,30 +212,3 @@ void pxl8_3d_camera_update(pxl8_3d_camera* cam, f32 dt) {
} }
} }
} }
pxl8_projected_point pxl8_3d_camera_world_to_screen(const pxl8_3d_camera* cam, pxl8_vec3 world_pos, u32 screen_width, u32 screen_height) {
pxl8_projected_point result = {0, 0, 0.0f, false};
if (!cam) return result;
pxl8_mat4 view = pxl8_3d_camera_get_view(cam);
pxl8_mat4 proj = pxl8_3d_camera_get_projection(cam);
pxl8_mat4 vp = pxl8_mat4_mul(proj, view);
pxl8_vec4 clip = pxl8_mat4_mul_vec4(vp, (pxl8_vec4){world_pos.x, world_pos.y, world_pos.z, 1.0f});
if (clip.w <= 0.0f) return result;
f32 inv_w = 1.0f / clip.w;
f32 ndc_x = clip.x * inv_w;
f32 ndc_y = clip.y * inv_w;
f32 ndc_z = clip.z * inv_w;
if (ndc_x < -1.0f || ndc_x > 1.0f || ndc_y < -1.0f || ndc_y > 1.0f) return result;
result.x = (i32)((ndc_x + 1.0f) * 0.5f * (f32)screen_width);
result.y = (i32)((1.0f - ndc_y) * 0.5f * (f32)screen_height);
result.depth = ndc_z;
result.visible = true;
return result;
}

View file

@ -32,7 +32,6 @@ pxl8_mat4 pxl8_3d_camera_get_view(const pxl8_3d_camera* cam);
void pxl8_3d_camera_blend(pxl8_3d_camera* dest, const pxl8_3d_camera* a, const pxl8_3d_camera* b, f32 t); void pxl8_3d_camera_blend(pxl8_3d_camera* dest, const pxl8_3d_camera* a, const pxl8_3d_camera* b, f32 t);
void pxl8_3d_camera_follow(pxl8_3d_camera* cam, pxl8_vec3 target, pxl8_vec3 offset, f32 smoothing, f32 dt); void pxl8_3d_camera_follow(pxl8_3d_camera* cam, pxl8_vec3 target, pxl8_vec3 offset, f32 smoothing, f32 dt);
pxl8_projected_point pxl8_3d_camera_world_to_screen(const pxl8_3d_camera* cam, pxl8_vec3 world_pos, u32 screen_width, u32 screen_height);
void pxl8_3d_camera_shake(pxl8_3d_camera* cam, f32 intensity, f32 duration); void pxl8_3d_camera_shake(pxl8_3d_camera* cam, f32 intensity, f32 duration);
void pxl8_3d_camera_update(pxl8_3d_camera* cam, f32 dt); void pxl8_3d_camera_update(pxl8_3d_camera* cam, f32 dt);

View file

@ -27,8 +27,6 @@ typedef struct pxl8_skyline {
struct pxl8_atlas { struct pxl8_atlas {
u32 height, width; u32 height, width;
u8* pixels; u8* pixels;
u8* pixels_tiled;
u32 tiled_capacity, tiled_size;
bool dirty; bool dirty;
@ -197,7 +195,6 @@ void pxl8_atlas_destroy(pxl8_atlas* atlas) {
free(atlas->entries); free(atlas->entries);
free(atlas->free_list); free(atlas->free_list);
free(atlas->pixels); free(atlas->pixels);
free(atlas->pixels_tiled);
free(atlas->skyline.nodes); free(atlas->skyline.nodes);
free(atlas); free(atlas);
} }
@ -212,13 +209,6 @@ void pxl8_atlas_clear(pxl8_atlas* atlas, u32 preserve_count) {
atlas->entry_count = preserve_count; atlas->entry_count = preserve_count;
atlas->free_count = 0; atlas->free_count = 0;
if (preserve_count == 0) {
atlas->tiled_size = 0;
} else {
pxl8_atlas_entry* last = &atlas->entries[preserve_count - 1];
atlas->tiled_size = last->tiled_base + (u32)(last->w * last->h);
}
atlas->skyline.nodes[0] = (pxl8_skyline_node){0, 0, (i32)atlas->width}; atlas->skyline.nodes[0] = (pxl8_skyline_node){0, 0, (i32)atlas->width};
atlas->skyline.count = 1; atlas->skyline.count = 1;
@ -344,7 +334,6 @@ u32 pxl8_atlas_add_texture(
entry->y = fit.pos.y; entry->y = fit.pos.y;
entry->w = w; entry->w = w;
entry->h = h; entry->h = h;
entry->log2_w = pxl8_log2(w);
i32 bytes_per_pixel = pxl8_bytes_per_pixel(pixel_mode); i32 bytes_per_pixel = pxl8_bytes_per_pixel(pixel_mode);
for (u32 y = 0; y < h; y++) { for (u32 y = 0; y < h; y++) {
@ -360,30 +349,6 @@ u32 pxl8_atlas_add_texture(
} }
} }
u32 tiled_tex_size = w * h;
u32 new_tiled_size = atlas->tiled_size + tiled_tex_size;
if (new_tiled_size > atlas->tiled_capacity) {
u32 new_cap = atlas->tiled_capacity ? atlas->tiled_capacity * 2 : 4096;
while (new_cap < new_tiled_size) new_cap *= 2;
u8* new_tiled = (u8*)realloc(atlas->pixels_tiled, new_cap);
if (!new_tiled) {
entry->active = false;
return UINT32_MAX;
}
atlas->pixels_tiled = new_tiled;
atlas->tiled_capacity = new_cap;
}
entry->tiled_base = atlas->tiled_size;
u8* tiled_dst = atlas->pixels_tiled + entry->tiled_base;
for (u32 ty = 0; ty < h; ty++) {
for (u32 tx = 0; tx < w; tx++) {
u32 tiled_offset = pxl8_tile_addr(tx, ty, entry->log2_w);
tiled_dst[tiled_offset] = pixels[ty * w + tx];
}
}
atlas->tiled_size = new_tiled_size;
if (!pxl8_skyline_add_rect(&atlas->skyline, fit.pos, w, h)) { if (!pxl8_skyline_add_rect(&atlas->skyline, fit.pos, w, h)) {
entry->active = false; entry->active = false;
return UINT32_MAX; return UINT32_MAX;
@ -412,10 +377,6 @@ const u8* pxl8_atlas_get_pixels(const pxl8_atlas* atlas) {
return atlas ? atlas->pixels : NULL; return atlas ? atlas->pixels : NULL;
} }
const u8* pxl8_atlas_get_pixels_tiled(const pxl8_atlas* atlas) {
return atlas ? atlas->pixels_tiled : NULL;
}
u32 pxl8_atlas_get_width(const pxl8_atlas* atlas) { u32 pxl8_atlas_get_width(const pxl8_atlas* atlas) {
return atlas ? atlas->width : 0; return atlas ? atlas->width : 0;
} }

View file

@ -8,42 +8,28 @@ typedef struct pxl8_atlas_entry {
bool active; bool active;
u32 texture_id; u32 texture_id;
i32 x, y, w, h; i32 x, y, w, h;
u32 tiled_base;
u8 log2_w;
} pxl8_atlas_entry; } pxl8_atlas_entry;
static inline u32 pxl8_tile_addr(u32 u, u32 v, u8 log2_w) {
u32 tile_y = v >> 3;
u32 tile_x = u >> 3;
u32 local_y = v & 7;
u32 local_x = u & 7;
return (tile_y << (log2_w + 3)) | (tile_x << 6) | (local_y << 3) | local_x;
}
static inline u8 pxl8_log2(u32 v) {
u8 r = 0;
while (v >>= 1) r++;
return r;
}
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
u32 pxl8_atlas_add_texture(pxl8_atlas* atlas, const u8* pixels, u32 w, u32 h, pxl8_pixel_mode pixel_mode);
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, pxl8_pixel_mode pixel_mode);
void pxl8_atlas_destroy(pxl8_atlas* atlas); void pxl8_atlas_destroy(pxl8_atlas* atlas);
bool pxl8_atlas_expand(pxl8_atlas* atlas, pxl8_pixel_mode pixel_mode);
const pxl8_atlas_entry* pxl8_atlas_get_entry(const pxl8_atlas* atlas, u32 id); 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_entry_count(const pxl8_atlas* atlas);
u32 pxl8_atlas_get_height(const pxl8_atlas* atlas); u32 pxl8_atlas_get_height(const pxl8_atlas* atlas);
const u8* pxl8_atlas_get_pixels(const pxl8_atlas* atlas); const u8* pxl8_atlas_get_pixels(const pxl8_atlas* atlas);
const u8* pxl8_atlas_get_pixels_tiled(const pxl8_atlas* atlas);
u32 pxl8_atlas_get_width(const pxl8_atlas* atlas); u32 pxl8_atlas_get_width(const pxl8_atlas* atlas);
bool pxl8_atlas_is_dirty(const pxl8_atlas* atlas); bool pxl8_atlas_is_dirty(const pxl8_atlas* atlas);
void pxl8_atlas_mark_clean(pxl8_atlas* atlas); void pxl8_atlas_mark_clean(pxl8_atlas* atlas);
u32 pxl8_atlas_add_texture(pxl8_atlas* atlas, const u8* pixels, u32 w, u32 h, pxl8_pixel_mode pixel_mode);
void pxl8_atlas_clear(pxl8_atlas* atlas, u32 preserve_count);
bool pxl8_atlas_expand(pxl8_atlas* atlas, pxl8_pixel_mode pixel_mode);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -3,7 +3,7 @@
#include "pxl8_types.h" #include "pxl8_types.h"
static inline i32 pxl8_bytes_per_pixel(pxl8_pixel_mode mode) { static inline i32 pxl8_bytes_per_pixel(pxl8_pixel_mode mode) {
return (i32)mode; return (mode == PXL8_PIXEL_HICOLOR) ? 2 : 1;
} }
static inline u16 pxl8_rgb565_pack(u8 r, u8 g, u8 b) { static inline u16 pxl8_rgb565_pack(u8 r, u8 g, u8 b) {

View file

@ -0,0 +1,79 @@
#include "pxl8_colormap.h"
#include <string.h>
static u8 find_closest_color(const u32* palette, u8 target_r, u8 target_g, u8 target_b) {
u8 best_idx = 1;
u32 best_dist = 0xFFFFFFFF;
u8 dynamic_end = PXL8_DYNAMIC_RANGE_START + PXL8_DYNAMIC_RANGE_COUNT;
for (u32 i = 1; i < PXL8_FULLBRIGHT_START; i++) {
if (i >= PXL8_DYNAMIC_RANGE_START && i < dynamic_end) {
continue;
}
u32 c = palette[i];
u8 pr = (c >> 24) & 0xFF;
u8 pg = (c >> 16) & 0xFF;
u8 pb = (c >> 8) & 0xFF;
i32 dr = (i32)target_r - (i32)pr;
i32 dg = (i32)target_g - (i32)pg;
i32 db = (i32)target_b - (i32)pb;
u32 dist = (u32)(dr * dr + dg * dg + db * db);
if (dist < best_dist) {
best_dist = dist;
best_idx = (u8)i;
if (dist == 0) break;
}
}
return best_idx;
}
void pxl8_colormap_generate(pxl8_colormap* cm, const u32* palette, const pxl8_level_tint* tint) {
if (!cm || !palette) return;
u8 dark_r, dark_g, dark_b;
if (tint && tint->tint_strength > 0.0f) {
f32 t = tint->tint_strength;
f32 inv = 1.0f - t;
dark_r = (u8)(tint->dark_r * inv + tint->tint_r * t);
dark_g = (u8)(tint->dark_g * inv + tint->tint_g * t);
dark_b = (u8)(tint->dark_b * inv + tint->tint_b * t);
} else if (tint) {
dark_r = tint->dark_r;
dark_g = tint->dark_g;
dark_b = tint->dark_b;
} else {
dark_r = dark_g = dark_b = 0;
}
for (u32 light = 0; light < PXL8_LIGHT_LEVELS; light++) {
f32 brightness = (f32)light / (f32)(PXL8_LIGHT_LEVELS - 1);
for (u32 pal_idx = 0; pal_idx < 256; pal_idx++) {
u8 result_idx;
if (pal_idx == PXL8_TRANSPARENT) {
result_idx = PXL8_TRANSPARENT;
} else if (pal_idx >= PXL8_FULLBRIGHT_START) {
result_idx = (u8)pal_idx;
} else {
u32 c = palette[pal_idx];
u8 r = (c >> 24) & 0xFF;
u8 g = (c >> 16) & 0xFF;
u8 b = (c >> 8) & 0xFF;
u8 target_r = (u8)(dark_r + (r - dark_r) * brightness);
u8 target_g = (u8)(dark_g + (g - dark_g) * brightness);
u8 target_b = (u8)(dark_b + (b - dark_b) * brightness);
result_idx = find_closest_color(palette, target_r, target_g, target_b);
}
cm->table[light * 256 + pal_idx] = result_idx;
}
}
}

View file

@ -0,0 +1,43 @@
#pragma once
#include "pxl8_dither.h"
#include "pxl8_types.h"
#ifdef __cplusplus
extern "C" {
#endif
#define PXL8_LIGHT_LEVELS 64
#define PXL8_COLORMAP_SIZE (256 * PXL8_LIGHT_LEVELS)
#define PXL8_FULLBRIGHT_START 240
#define PXL8_TRANSPARENT 0
#define PXL8_DYNAMIC_RANGE_START 144
#define PXL8_DYNAMIC_RANGE_COUNT 16
typedef struct {
u8 table[PXL8_COLORMAP_SIZE];
} pxl8_colormap;
typedef struct {
u8 dark_r, dark_g, dark_b;
u8 tint_r, tint_g, tint_b;
f32 tint_strength;
} pxl8_level_tint;
void pxl8_colormap_generate(pxl8_colormap* cm, const u32* palette, const pxl8_level_tint* tint);
static inline u8 pxl8_colormap_lookup(const pxl8_colormap* cm, u8 pal_idx, u8 light) {
u32 light_idx = light >> 2;
return cm->table[light_idx * 256 + pal_idx];
}
static inline u8 pxl8_colormap_lookup_dithered(const pxl8_colormap* cm, u8 pal_idx, u8 light, u32 x, u32 y) {
u8 dithered = pxl8_dither_light(light, x, y);
u32 light_idx = dithered >> 2;
return cm->table[light_idx * 256 + pal_idx];
}
#ifdef __cplusplus
}
#endif

View file

@ -1,116 +1,18 @@
#include "pxl8_cpu.h" #include "pxl8_cpu.h"
#include <math.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "pxl8_simd.h"
struct pxl8_cpu_render_target { struct pxl8_cpu_render_target {
u8* framebuffer; u8* framebuffer;
u32 height; u32 height;
u32 width; u32 width;
u16* zbuffer; f32* zbuffer;
u32* light_accum; u32* light_accum;
}; };
static inline u16 depth_to_u16(f32 z) {
f32 d = (z + 1.0f) * 32767.5f;
if (d < 0.0f) d = 0.0f;
if (d > 65535.0f) d = 65535.0f;
return (u16)d;
}
static inline f32 calc_light_intensity(const pxl8_light* light, pxl8_vec3 world_pos, pxl8_vec3 normal) {
pxl8_vec3 to_light = pxl8_vec3_sub(light->position, world_pos);
f32 dist_sq = pxl8_vec3_dot(to_light, to_light);
if (dist_sq >= light->radius_sq) {
return 0.0f;
}
f32 intensity_norm = light->intensity * (1.0f / 255.0f);
if (dist_sq < 0.001f) {
return intensity_norm;
}
f32 inv_dist = pxl8_fast_inv_sqrt(dist_sq);
pxl8_vec3 light_dir = pxl8_vec3_scale(to_light, inv_dist);
f32 n_dot_l = pxl8_vec3_dot(normal, light_dir);
if (n_dot_l <= 0.0f) {
return 0.0f;
}
f32 falloff = 1.0f - dist_sq * light->inv_radius_sq;
return n_dot_l * falloff * intensity_norm;
}
typedef struct pxl8_light_result {
u8 light;
u32 light_color;
} pxl8_light_result;
static pxl8_light_result calc_vertex_light(
pxl8_vec3 world_pos,
pxl8_vec3 normal,
const pxl8_3d_frame* frame
) {
f32 intensity = 0.25f + frame->uniforms.ambient * (1.0f / 255.0f);
f32 accum_r = 0.0f;
f32 accum_g = 0.0f;
f32 accum_b = 0.0f;
f32 total_dynamic = 0.0f;
f32 celestial_dot = -pxl8_vec3_dot(normal, frame->uniforms.celestial_dir);
if (celestial_dot > 0.0f) {
intensity += celestial_dot * frame->uniforms.celestial_intensity;
}
f32 sky_factor = normal.y * 0.5f + 0.5f;
if (sky_factor < 0.0f) sky_factor = 0.0f;
intensity += sky_factor * frame->uniforms.celestial_intensity * 0.3f;
for (u32 i = 0; i < frame->uniforms.num_lights; i++) {
const pxl8_light* light = &frame->uniforms.lights[i];
f32 contrib = calc_light_intensity(light, world_pos, normal);
if (contrib > 0.0f) {
intensity += contrib;
total_dynamic += contrib;
accum_r += light->r * contrib;
accum_g += light->g * contrib;
accum_b += light->b * contrib;
}
}
u32 light_color = 0;
if (total_dynamic > 0.001f) {
f32 inv_total = 1.0f / total_dynamic;
f32 tint_strength = total_dynamic * 1.5f;
if (tint_strength > 1.0f) tint_strength = 1.0f;
u32 r = (u32)(accum_r * inv_total);
u32 g = (u32)(accum_g * inv_total);
u32 b = (u32)(accum_b * inv_total);
u32 a = (u32)(tint_strength * 255.0f);
if (r > 255) r = 255;
if (g > 255) g = 255;
if (b > 255) b = 255;
light_color = r | (g << 8) | (b << 16) | (a << 24);
}
if (intensity < 0.0f) intensity = 0.0f;
if (intensity > 1.0f) intensity = 1.0f;
return (pxl8_light_result){
.light = (u8)(intensity * 255.0f),
.light_color = light_color,
};
}
#define PXL8_MAX_TARGET_STACK 8 #define PXL8_MAX_TARGET_STACK 8
struct pxl8_cpu_backend { struct pxl8_cpu_backend {
@ -118,12 +20,9 @@ struct pxl8_cpu_backend {
pxl8_cpu_render_target* target_stack[PXL8_MAX_TARGET_STACK]; pxl8_cpu_render_target* target_stack[PXL8_MAX_TARGET_STACK];
u32 target_stack_depth; u32 target_stack_depth;
const pxl8_colormap* colormap; const pxl8_colormap* colormap;
const pxl8_palette_cube* palette_cube;
const u32* palette; const u32* palette;
pxl8_3d_frame frame; pxl8_3d_frame frame;
pxl8_mat4 mvp; pxl8_mat4 mvp;
u32* output;
u32 output_size;
}; };
static inline void clip_line_2d(i32* x0, i32* y0, i32* x1, i32* y1, i32 w, i32 h, bool* visible) { static inline void clip_line_2d(i32* x0, i32* y0, i32* x1, i32* y1, i32 w, i32 h, bool* visible) {
@ -191,15 +90,6 @@ pxl8_cpu_backend* pxl8_cpu_create(u32 width, u32 height) {
cpu->target_stack[0] = base_target; cpu->target_stack[0] = base_target;
cpu->target_stack_depth = 1; cpu->target_stack_depth = 1;
cpu->current_target = base_target; cpu->current_target = base_target;
cpu->output_size = width * height;
cpu->output = calloc(cpu->output_size, sizeof(u32));
if (!cpu->output) {
pxl8_cpu_destroy_render_target(base_target);
free(cpu);
return NULL;
}
return cpu; return cpu;
} }
@ -208,7 +98,6 @@ void pxl8_cpu_destroy(pxl8_cpu_backend* cpu) {
for (u32 i = 0; i < cpu->target_stack_depth; i++) { for (u32 i = 0; i < cpu->target_stack_depth; i++) {
pxl8_cpu_destroy_render_target(cpu->target_stack[i]); pxl8_cpu_destroy_render_target(cpu->target_stack[i]);
} }
free(cpu->output);
free(cpu); free(cpu);
} }
@ -231,12 +120,11 @@ void pxl8_cpu_clear(pxl8_cpu_backend* cpu, u8 color) {
void pxl8_cpu_clear_depth(pxl8_cpu_backend* cpu) { void pxl8_cpu_clear_depth(pxl8_cpu_backend* cpu) {
if (!cpu || !cpu->current_target) return; if (!cpu || !cpu->current_target) return;
pxl8_cpu_render_target* render_target = cpu->current_target; pxl8_cpu_render_target* render_target = cpu->current_target;
u32 count = render_target->width * render_target->height;
if (render_target->zbuffer) { if (render_target->zbuffer) {
memset(render_target->zbuffer, 0xFF, count * sizeof(u16)); memset(render_target->zbuffer, 0x7F, render_target->width * render_target->height * sizeof(f32));
} }
if (render_target->light_accum) { if (render_target->light_accum) {
memset(render_target->light_accum, 0, count * sizeof(u32)); memset(render_target->light_accum, 0, render_target->width * render_target->height * sizeof(u32));
} }
} }
@ -244,10 +132,6 @@ void pxl8_cpu_set_colormap(pxl8_cpu_backend* cpu, const pxl8_colormap* cm) {
if (cpu) cpu->colormap = cm; if (cpu) cpu->colormap = cm;
} }
void pxl8_cpu_set_palette_cube(pxl8_cpu_backend* cpu, const pxl8_palette_cube* cube) {
if (cpu) cpu->palette_cube = cube;
}
void pxl8_cpu_set_palette(pxl8_cpu_backend* cpu, const u32* palette) { void pxl8_cpu_set_palette(pxl8_cpu_backend* cpu, const u32* palette) {
if (cpu) cpu->palette = palette; if (cpu) cpu->palette = palette;
} }
@ -391,44 +275,8 @@ typedef struct {
f32 u, v; f32 u, v;
u8 color; u8 color;
u8 light; u8 light;
u32 light_color;
} vertex_output; } vertex_output;
static inline u32 blend_tri_light_color(u32 lc0, u32 lc1, u32 lc2) {
u32 a0 = (lc0 >> 24) & 0xFF;
u32 a1 = (lc1 >> 24) & 0xFF;
u32 a2 = (lc2 >> 24) & 0xFF;
u32 total_a = a0 + a1 + a2;
if (total_a == 0) return 0;
u32 r = ((lc0 & 0xFF) * a0 + (lc1 & 0xFF) * a1 + (lc2 & 0xFF) * a2) / total_a;
u32 g = (((lc0 >> 8) & 0xFF) * a0 + ((lc1 >> 8) & 0xFF) * a1 + ((lc2 >> 8) & 0xFF) * a2) / total_a;
u32 b = (((lc0 >> 16) & 0xFF) * a0 + ((lc1 >> 16) & 0xFF) * a1 + ((lc2 >> 16) & 0xFF) * a2) / total_a;
u32 a = total_a / 3;
if (a > 255) a = 255;
return r | (g << 8) | (b << 16) | (a << 24);
}
static inline u32 lerp_light_color(u32 a, u32 b, f32 t) {
u32 ar = a & 0xFF;
u32 ag = (a >> 8) & 0xFF;
u32 ab = (a >> 16) & 0xFF;
u32 aa = (a >> 24) & 0xFF;
u32 br = b & 0xFF;
u32 bg = (b >> 8) & 0xFF;
u32 bb = (b >> 16) & 0xFF;
u32 ba = (b >> 24) & 0xFF;
u32 or = ar + (u32)((i32)(br - ar) * t);
u32 og = ag + (u32)((i32)(bg - ag) * t);
u32 ob = ab + (u32)((i32)(bb - ab) * t);
u32 oa = aa + (u32)((i32)(ba - aa) * t);
return or | (og << 8) | (ob << 16) | (oa << 24);
}
static vertex_output lerp_vertex(const vertex_output* a, const vertex_output* b, f32 t) { static vertex_output lerp_vertex(const vertex_output* a, const vertex_output* b, f32 t) {
vertex_output out; vertex_output out;
out.clip_pos.x = a->clip_pos.x + (b->clip_pos.x - a->clip_pos.x) * t; out.clip_pos.x = a->clip_pos.x + (b->clip_pos.x - a->clip_pos.x) * t;
@ -445,7 +293,6 @@ static vertex_output lerp_vertex(const vertex_output* a, const vertex_output* b,
out.v = a->v + (b->v - a->v) * t; out.v = a->v + (b->v - a->v) * t;
out.color = t < 0.5f ? a->color : b->color; out.color = t < 0.5f ? a->color : b->color;
out.light = (u8)(a->light + (b->light - a->light) * t); out.light = (u8)(a->light + (b->light - a->light) * t);
out.light_color = lerp_light_color(a->light_color, b->light_color, t);
return out; return out;
} }
@ -501,8 +348,6 @@ typedef struct {
f32 w0_recip, w1_recip, w2_recip; f32 w0_recip, w1_recip, w2_recip;
f32 u0_w, v0_w, u1_w, v1_w, u2_w, v2_w; f32 u0_w, v0_w, u1_w, v1_w, u2_w, v2_w;
f32 l0_w, l1_w, l2_w; f32 l0_w, l1_w, l2_w;
u32 lc0, lc1, lc2;
f32 c0_w, c1_w, c2_w;
i32 y_start, y_end, total_height; i32 y_start, y_end, total_height;
f32 inv_total; f32 inv_total;
u32 target_width, target_height; u32 target_width, target_height;
@ -574,14 +419,6 @@ static bool setup_triangle(
setup->l1_w = sorted[1]->light * setup->w1_recip; setup->l1_w = sorted[1]->light * setup->w1_recip;
setup->l2_w = sorted[2]->light * setup->w2_recip; setup->l2_w = sorted[2]->light * setup->w2_recip;
setup->lc0 = sorted[0]->light_color;
setup->lc1 = sorted[1]->light_color;
setup->lc2 = sorted[2]->light_color;
setup->c0_w = (f32)sorted[0]->color * setup->w0_recip;
setup->c1_w = (f32)sorted[1]->color * setup->w1_recip;
setup->c2_w = (f32)sorted[2]->color * setup->w2_recip;
setup->inv_total = 1.0f / (f32)setup->total_height; setup->inv_total = 1.0f / (f32)setup->total_height;
setup->target_width = width; setup->target_width = width;
setup->target_height = height; setup->target_height = height;
@ -595,8 +432,6 @@ static void rasterize_triangle_opaque(
const pxl8_atlas* textures, u32 texture_id, bool dither const pxl8_atlas* textures, u32 texture_id, bool dither
) { ) {
pxl8_cpu_render_target* render_target = cpu->current_target; pxl8_cpu_render_target* render_target = cpu->current_target;
u32* light_accum = render_target->light_accum;
u32 tri_light_color = light_accum ? blend_tri_light_color(setup->lc0, setup->lc1, setup->lc2) : 0;
const pxl8_atlas_entry* tex_entry = textures ? pxl8_atlas_get_entry(textures, texture_id) : NULL; const pxl8_atlas_entry* tex_entry = textures ? pxl8_atlas_get_entry(textures, texture_id) : NULL;
u32 atlas_width = textures ? pxl8_atlas_get_width(textures) : 1; u32 atlas_width = textures ? pxl8_atlas_get_width(textures) : 1;
f32 tex_w = tex_entry ? (f32)tex_entry->w : 1.0f; f32 tex_w = tex_entry ? (f32)tex_entry->w : 1.0f;
@ -681,7 +516,7 @@ static void rasterize_triangle_opaque(
u32 row_start = (u32)y * render_target->width; u32 row_start = (u32)y * render_target->width;
u8* prow = render_target->framebuffer + row_start; u8* prow = render_target->framebuffer + row_start;
u16* zrow = render_target->zbuffer + row_start; f32* zrow = render_target->zbuffer + row_start;
const i32 SUBDIV = 32; const i32 SUBDIV = 32;
i32 x = x_start; i32 x = x_start;
@ -701,22 +536,6 @@ static void rasterize_triangle_opaque(
f32 v_end = (vw + dvw * steps) * pw_end; f32 v_end = (vw + dvw * steps) * pw_end;
f32 l_start = lw * pw_start; f32 l_start = lw * pw_start;
f32 l_end = (lw + d_lw * steps) * pw_end; f32 l_end = (lw + d_lw * steps) * pw_end;
f32 fog_density = cpu->frame.uniforms.fog_density;
if (fog_density > 0.0f) {
f32 z_end_fog = z + dz * steps;
f32 t_start = (z + 1.0f) * 0.5f;
f32 t_end = (z_end_fog + 1.0f) * 0.5f;
if (t_start < 0.0f) t_start = 0.0f;
if (t_end < 0.0f) t_end = 0.0f;
f32 fog_start = t_start * t_start * fog_density;
f32 fog_end = t_end * t_end * fog_density;
if (fog_start > 1.0f) fog_start = 1.0f;
if (fog_end > 1.0f) fog_end = 1.0f;
l_start *= (1.0f - fog_start);
l_end *= (1.0f - fog_end);
}
if (l_start > 255.0f) l_start = 255.0f; if (l_start > 255.0f) l_start = 255.0f;
if (l_end > 255.0f) l_end = 255.0f; if (l_end > 255.0f) l_end = 255.0f;
@ -734,8 +553,7 @@ static void rasterize_triangle_opaque(
f32 z_a = z; f32 z_a = z;
for (i32 px = x; px <= span_end; px++) { for (i32 px = x; px <= span_end; px++) {
u16 z16 = depth_to_u16(z_a); if (z_a <= zrow[px]) {
if (z16 < zrow[px]) {
i32 tx = (u_fixed >> 16) & tex_mask_w; i32 tx = (u_fixed >> 16) & tex_mask_w;
i32 ty = (v_fixed >> 16) & tex_mask_h; i32 ty = (v_fixed >> 16) & tex_mask_h;
u8 tex_idx = tex_pixels ? tex_pixels[tex_base + (u32)ty * (u32)tex_stride + (u32)tx] : 1; u8 tex_idx = tex_pixels ? tex_pixels[tex_base + (u32)ty * (u32)tex_stride + (u32)tx] : 1;
@ -745,12 +563,9 @@ static void rasterize_triangle_opaque(
if (dither) { if (dither) {
light = pxl8_dither_light(light, (u32)px, (u32)y); light = pxl8_dither_light(light, (u32)px, (u32)y);
} }
u8 pal_idx = cpu->colormap ? pxl8_colormap_lookup(cpu->colormap, tex_idx, PXL8_LIGHT_WHITE, light) : tex_idx; u8 pal_idx = cpu->colormap ? pxl8_colormap_lookup(cpu->colormap, tex_idx, light) : tex_idx;
prow[px] = pal_idx; prow[px] = pal_idx;
zrow[px] = z16; zrow[px] = z_a;
if (light_accum) {
light_accum[row_start + (u32)px] = tri_light_color;
}
} }
} }
@ -772,9 +587,11 @@ static void rasterize_triangle_opaque(
static void rasterize_triangle_passthrough( static void rasterize_triangle_passthrough(
pxl8_cpu_backend* cpu, pxl8_cpu_backend* cpu,
const tri_setup* setup const tri_setup* setup,
const vertex_output* vo0
) { ) {
pxl8_cpu_render_target* render_target = cpu->current_target; pxl8_cpu_render_target* render_target = cpu->current_target;
u8 color = vo0->color;
for (i32 y = setup->y_start; y <= setup->y_end; y++) { for (i32 y = setup->y_start; y <= setup->y_end; y++) {
bool second_half = y > setup->y1 || setup->y1 == setup->y0; bool second_half = y > setup->y1 || setup->y1 == setup->y0;
@ -786,33 +603,20 @@ static void rasterize_triangle_passthrough(
f32 ax = (f32)setup->x0 + (f32)(setup->x2 - setup->x0) * alpha; f32 ax = (f32)setup->x0 + (f32)(setup->x2 - setup->x0) * alpha;
f32 az = setup->z0 + (setup->z2 - setup->z0) * alpha; f32 az = setup->z0 + (setup->z2 - setup->z0) * alpha;
f32 a_wr = setup->w0_recip + (setup->w2_recip - setup->w0_recip) * alpha; f32 bx, bz;
f32 a_cw = setup->c0_w + (setup->c2_w - setup->c0_w) * alpha;
f32 bx, bz, b_wr, b_cw;
if (second_half) { if (second_half) {
bx = (f32)setup->x1 + (f32)(setup->x2 - setup->x1) * beta; bx = (f32)setup->x1 + (f32)(setup->x2 - setup->x1) * beta;
bz = setup->z1 + (setup->z2 - setup->z1) * beta; bz = setup->z1 + (setup->z2 - setup->z1) * beta;
b_wr = setup->w1_recip + (setup->w2_recip - setup->w1_recip) * beta;
b_cw = setup->c1_w + (setup->c2_w - setup->c1_w) * beta;
} else { } else {
bx = (f32)setup->x0 + (f32)(setup->x1 - setup->x0) * beta; bx = (f32)setup->x0 + (f32)(setup->x1 - setup->x0) * beta;
bz = setup->z0 + (setup->z1 - setup->z0) * beta; bz = setup->z0 + (setup->z1 - setup->z0) * beta;
b_wr = setup->w0_recip + (setup->w1_recip - setup->w0_recip) * beta;
b_cw = setup->c0_w + (setup->c1_w - setup->c0_w) * beta;
} }
f32 x_start_f, x_end_f, z_start, z_end, wr_start, wr_end, cw_start, cw_end; f32 x_start_f, x_end_f, z_start, z_end;
if (ax <= bx) { if (ax <= bx) {
x_start_f = ax; x_end_f = bx; x_start_f = ax; x_end_f = bx; z_start = az; z_end = bz;
z_start = az; z_end = bz;
wr_start = a_wr; wr_end = b_wr;
cw_start = a_cw; cw_end = b_cw;
} else { } else {
x_start_f = bx; x_end_f = ax; x_start_f = bx; x_end_f = ax; z_start = bz; z_end = az;
z_start = bz; z_end = az;
wr_start = b_wr; wr_end = a_wr;
cw_start = b_cw; cw_end = a_cw;
} }
i32 x_start_orig = (i32)(x_start_f + 0.5f); i32 x_start_orig = (i32)(x_start_f + 0.5f);
@ -826,30 +630,19 @@ static void rasterize_triangle_passthrough(
f32 inv_width = 1.0f / (f32)width_orig; f32 inv_width = 1.0f / (f32)width_orig;
f32 dz = (z_end - z_start) * inv_width; f32 dz = (z_end - z_start) * inv_width;
f32 dwr = (wr_end - wr_start) * inv_width;
f32 dcw = (cw_end - cw_start) * inv_width;
f32 skip = (f32)(x_start - x_start_orig); f32 skip = (f32)(x_start - x_start_orig);
f32 z = z_start + dz * skip; f32 z = z_start + dz * skip;
f32 wr = wr_start + dwr * skip;
f32 cw = cw_start + dcw * skip;
u32 row_start = (u32)y * render_target->width; u32 row_start = (u32)y * render_target->width;
u8* prow = render_target->framebuffer + row_start; u8* prow = render_target->framebuffer + row_start;
u16* zrow = render_target->zbuffer + row_start; f32* zrow = render_target->zbuffer + row_start;
for (i32 px = x_start; px <= x_end; px++) { for (i32 px = x_start; px <= x_end; px++) {
u16 z16 = depth_to_u16(z); if (z <= zrow[px]) {
if (z16 < zrow[px]) {
f32 w = 1.0f / wr;
f32 color_f = cw * w;
u8 color = pxl8_dither_float(color_f, (u32)px, (u32)y);
prow[px] = color; prow[px] = color;
zrow[px] = z16; zrow[px] = z;
} }
z += dz; z += dz;
wr += dwr;
cw += dcw;
} }
} }
} }
@ -860,8 +653,6 @@ static void rasterize_triangle_alpha(
const pxl8_atlas* textures, u32 texture_id, u8 mat_alpha, bool dither const pxl8_atlas* textures, u32 texture_id, u8 mat_alpha, bool dither
) { ) {
pxl8_cpu_render_target* render_target = cpu->current_target; pxl8_cpu_render_target* render_target = cpu->current_target;
u32* light_accum = render_target->light_accum;
u32 tri_light_color = light_accum ? blend_tri_light_color(setup->lc0, setup->lc1, setup->lc2) : 0;
const pxl8_atlas_entry* tex_entry = textures ? pxl8_atlas_get_entry(textures, texture_id) : NULL; const pxl8_atlas_entry* tex_entry = textures ? pxl8_atlas_get_entry(textures, texture_id) : NULL;
u32 atlas_width = textures ? pxl8_atlas_get_width(textures) : 1; u32 atlas_width = textures ? pxl8_atlas_get_width(textures) : 1;
f32 tex_w = tex_entry ? (f32)tex_entry->w : 1.0f; f32 tex_w = tex_entry ? (f32)tex_entry->w : 1.0f;
@ -946,7 +737,7 @@ static void rasterize_triangle_alpha(
u32 row_start = (u32)y * render_target->width; u32 row_start = (u32)y * render_target->width;
u8* prow = render_target->framebuffer + row_start; u8* prow = render_target->framebuffer + row_start;
u16* zrow = render_target->zbuffer + row_start; f32* zrow = render_target->zbuffer + row_start;
const i32 SUBDIV = 32; const i32 SUBDIV = 32;
i32 x = x_start; i32 x = x_start;
@ -966,22 +757,6 @@ static void rasterize_triangle_alpha(
f32 v_end = (vw + dvw * steps) * pw_end; f32 v_end = (vw + dvw * steps) * pw_end;
f32 l_start = lw * pw_start; f32 l_start = lw * pw_start;
f32 l_end = (lw + d_lw * steps) * pw_end; f32 l_end = (lw + d_lw * steps) * pw_end;
f32 fog_density = cpu->frame.uniforms.fog_density;
if (fog_density > 0.0f) {
f32 z_end_fog = z + dz * steps;
f32 t_start = (z + 1.0f) * 0.5f;
f32 t_end = (z_end_fog + 1.0f) * 0.5f;
if (t_start < 0.0f) t_start = 0.0f;
if (t_end < 0.0f) t_end = 0.0f;
f32 fog_start = t_start * t_start * fog_density;
f32 fog_end = t_end * t_end * fog_density;
if (fog_start > 1.0f) fog_start = 1.0f;
if (fog_end > 1.0f) fog_end = 1.0f;
l_start *= (1.0f - fog_start);
l_end *= (1.0f - fog_end);
}
if (l_start > 255.0f) l_start = 255.0f; if (l_start > 255.0f) l_start = 255.0f;
if (l_end > 255.0f) l_end = 255.0f; if (l_end > 255.0f) l_end = 255.0f;
@ -1008,15 +783,11 @@ static void rasterize_triangle_alpha(
if (dither) { if (dither) {
light = pxl8_dither_light(light, (u32)px, (u32)y); light = pxl8_dither_light(light, (u32)px, (u32)y);
} }
u8 src_idx = cpu->colormap ? pxl8_colormap_lookup(cpu->colormap, tex_idx, PXL8_LIGHT_WHITE, light) : tex_idx; u8 src_idx = cpu->colormap ? pxl8_colormap_lookup(cpu->colormap, tex_idx, light) : tex_idx;
if (mat_alpha >= 128) { if (mat_alpha >= 128) {
prow[px] = src_idx; prow[px] = src_idx;
u16 z16 = depth_to_u16(z_a); if (z_a <= zrow[px]) zrow[px] = z_a;
if (z16 < zrow[px]) zrow[px] = z16;
if (light_accum) {
light_accum[row_start + (u32)px] = tri_light_color;
}
} }
} }
@ -1036,43 +807,11 @@ static void rasterize_triangle_alpha(
} }
} }
static void rasterize_triangle_wireframe(
pxl8_cpu_backend* cpu,
const vertex_output* vo0, const vertex_output* vo1, const vertex_output* vo2,
u8 color, bool double_sided
) {
pxl8_cpu_render_target* render_target = cpu->current_target;
f32 hw = (f32)render_target->width * 0.5f;
f32 hh = (f32)render_target->height * 0.5f;
i32 x0 = (i32)(hw + vo0->clip_pos.x / vo0->clip_pos.w * hw);
i32 y0 = (i32)(hh - vo0->clip_pos.y / vo0->clip_pos.w * hh);
i32 x1 = (i32)(hw + vo1->clip_pos.x / vo1->clip_pos.w * hw);
i32 y1 = (i32)(hh - vo1->clip_pos.y / vo1->clip_pos.w * hh);
i32 x2 = (i32)(hw + vo2->clip_pos.x / vo2->clip_pos.w * hw);
i32 y2 = (i32)(hh - vo2->clip_pos.y / vo2->clip_pos.w * hh);
if (!double_sided) {
i32 cross = (x1 - x0) * (y2 - y0) - (y1 - y0) * (x2 - x0);
if (cross >= 0) return;
}
pxl8_cpu_draw_line_2d(cpu, x0, y0, x1, y1, color);
pxl8_cpu_draw_line_2d(cpu, x1, y1, x2, y2, color);
pxl8_cpu_draw_line_2d(cpu, x2, y2, x0, y0, color);
}
static void dispatch_triangle( static void dispatch_triangle(
pxl8_cpu_backend* cpu, pxl8_cpu_backend* cpu,
const vertex_output* vo0, const vertex_output* vo1, const vertex_output* vo2, const vertex_output* vo0, const vertex_output* vo1, const vertex_output* vo2,
const pxl8_atlas* textures, const pxl8_gfx_material* material const pxl8_atlas* textures, const pxl8_material* material
) { ) {
if (material->wireframe) {
u8 color = (material->texture_id > 0) ? (u8)material->texture_id : 15;
rasterize_triangle_wireframe(cpu, vo0, vo1, vo2, color, material->double_sided);
return;
}
pxl8_cpu_render_target* render_target = cpu->current_target; pxl8_cpu_render_target* render_target = cpu->current_target;
tri_setup setup; tri_setup setup;
if (!setup_triangle(&setup, vo0, vo1, vo2, render_target->width, render_target->height, material->double_sided)) { if (!setup_triangle(&setup, vo0, vo1, vo2, render_target->width, render_target->height, material->double_sided)) {
@ -1085,7 +824,7 @@ static void dispatch_triangle(
if (alpha_blend) { if (alpha_blend) {
rasterize_triangle_alpha(cpu, &setup, textures, material->texture_id, material->alpha, material->dither); rasterize_triangle_alpha(cpu, &setup, textures, material->texture_id, material->alpha, material->dither);
} else if (passthrough) { } else if (passthrough) {
rasterize_triangle_passthrough(cpu, &setup); rasterize_triangle_passthrough(cpu, &setup, vo0);
} else { } else {
rasterize_triangle_opaque(cpu, &setup, textures, material->texture_id, material->dither); rasterize_triangle_opaque(cpu, &setup, textures, material->texture_id, material->dither);
} }
@ -1094,13 +833,13 @@ static void dispatch_triangle(
void pxl8_cpu_draw_mesh( void pxl8_cpu_draw_mesh(
pxl8_cpu_backend* cpu, pxl8_cpu_backend* cpu,
const pxl8_mesh* mesh, const pxl8_mesh* mesh,
const pxl8_mat4* model, pxl8_mat4 model,
const pxl8_gfx_material* material, pxl8_material material,
const pxl8_atlas* textures const pxl8_atlas* textures
) { ) {
if (!cpu || !mesh || !model || !material || mesh->index_count < 3 || !cpu->current_target) return; if (!cpu || !mesh || mesh->index_count < 3 || !cpu->current_target) return;
pxl8_mat4 mv = pxl8_mat4_mul(cpu->frame.view, *model); pxl8_mat4 mv = pxl8_mat4_mul(cpu->frame.view, model);
pxl8_mat4 mvp = pxl8_mat4_mul(cpu->frame.projection, mv); pxl8_mat4 mvp = pxl8_mat4_mul(cpu->frame.projection, mv);
f32 near = cpu->frame.near_clip > 0.0f ? cpu->frame.near_clip : 0.1f; f32 near = cpu->frame.near_clip > 0.0f ? cpu->frame.near_clip : 0.1f;
@ -1124,9 +863,9 @@ void pxl8_cpu_draw_mesh(
vo1.clip_pos = pxl8_mat4_mul_vec4(mvp, p1); vo1.clip_pos = pxl8_mat4_mul_vec4(mvp, p1);
vo2.clip_pos = pxl8_mat4_mul_vec4(mvp, p2); vo2.clip_pos = pxl8_mat4_mul_vec4(mvp, p2);
pxl8_vec4 w0 = pxl8_mat4_mul_vec4(*model, p0); pxl8_vec4 w0 = pxl8_mat4_mul_vec4(model, p0);
pxl8_vec4 w1 = pxl8_mat4_mul_vec4(*model, p1); pxl8_vec4 w1 = pxl8_mat4_mul_vec4(model, p1);
pxl8_vec4 w2 = pxl8_mat4_mul_vec4(*model, p2); pxl8_vec4 w2 = pxl8_mat4_mul_vec4(model, p2);
vo0.world_pos = (pxl8_vec3){w0.x, w0.y, w0.z}; vo0.world_pos = (pxl8_vec3){w0.x, w0.y, w0.z};
vo1.world_pos = (pxl8_vec3){w1.x, w1.y, w1.z}; vo1.world_pos = (pxl8_vec3){w1.x, w1.y, w1.z};
vo2.world_pos = (pxl8_vec3){w2.x, w2.y, w2.z}; vo2.world_pos = (pxl8_vec3){w2.x, w2.y, w2.z};
@ -1143,36 +882,15 @@ void pxl8_cpu_draw_mesh(
vo1.color = v1->color; vo1.color = v1->color;
vo2.color = v2->color; vo2.color = v2->color;
if (material->dynamic_lighting) { vo0.light = material.dynamic_lighting ? v0->light : 255;
pxl8_vec3 n0 = pxl8_vec3_normalize(pxl8_mat4_mul_vec3(*model, v0->normal)); vo1.light = material.dynamic_lighting ? v1->light : 255;
pxl8_vec3 n1 = pxl8_vec3_normalize(pxl8_mat4_mul_vec3(*model, v1->normal)); vo2.light = material.dynamic_lighting ? v2->light : 255;
pxl8_vec3 n2 = pxl8_vec3_normalize(pxl8_mat4_mul_vec3(*model, v2->normal));
pxl8_light_result lr0 = calc_vertex_light(vo0.world_pos, n0, &cpu->frame);
pxl8_light_result lr1 = calc_vertex_light(vo1.world_pos, n1, &cpu->frame);
pxl8_light_result lr2 = calc_vertex_light(vo2.world_pos, n2, &cpu->frame);
vo0.light = lr0.light;
vo1.light = lr1.light;
vo2.light = lr2.light;
vo0.light_color = lr0.light_color;
vo1.light_color = lr1.light_color;
vo2.light_color = lr2.light_color;
} else {
vo0.light = 255;
vo1.light = 255;
vo2.light = 255;
vo0.light_color = 0;
vo1.light_color = 0;
vo2.light_color = 0;
}
vertex_output clipped[6]; vertex_output clipped[6];
i32 clipped_count = clip_triangle_near(&vo0, &vo1, &vo2, near, clipped); i32 clipped_count = clip_triangle_near(&vo0, &vo1, &vo2, near, clipped);
for (i32 t = 0; t < clipped_count; t += 3) { for (i32 t = 0; t < clipped_count; t += 3) {
dispatch_triangle(cpu, &clipped[t], &clipped[t+1], &clipped[t+2], textures, material); dispatch_triangle(cpu, &clipped[t], &clipped[t+1], &clipped[t+2], textures, &material);
} }
} }
} }
@ -1246,7 +964,7 @@ pxl8_cpu_render_target* pxl8_cpu_create_render_target(const pxl8_cpu_render_targ
} }
if (desc->with_depth) { if (desc->with_depth) {
target->zbuffer = calloc(size, sizeof(u16)); target->zbuffer = calloc(size, sizeof(f32));
if (!target->zbuffer) { if (!target->zbuffer) {
free(target->framebuffer); free(target->framebuffer);
free(target); free(target);
@ -1303,12 +1021,10 @@ void pxl8_cpu_blit(pxl8_cpu_backend* cpu, pxl8_cpu_render_target* src, i32 x, i3
for (i32 row = 0; row < copy_h; row++) { for (i32 row = 0; row < copy_h; row++) {
u8* src_row = src->framebuffer + (src_y0 + row) * src->width + src_x0; u8* src_row = src->framebuffer + (src_y0 + row) * src->width + src_x0;
u8* dst_row = dst->framebuffer + (dst_y0 + row) * dst->width + dst_x0; u8* dst_row = dst->framebuffer + (dst_y0 + row) * dst->width + dst_x0;
u32* light_row = dst->light_accum ? dst->light_accum + (dst_y0 + row) * dst->width + dst_x0 : NULL;
for (i32 col = 0; col < copy_w; col++) { for (i32 col = 0; col < copy_w; col++) {
u8 pixel = src_row[col]; u8 pixel = src_row[col];
if (pixel != transparent_idx) { if (pixel != transparent_idx) {
dst_row[col] = pixel; dst_row[col] = pixel;
if (light_row) light_row[col] = 0;
} }
} }
} }
@ -1357,194 +1073,3 @@ u32 pxl8_cpu_render_target_get_height(const pxl8_cpu_render_target* target) {
u32 pxl8_cpu_render_target_get_width(const pxl8_cpu_render_target* target) { u32 pxl8_cpu_render_target_get_width(const pxl8_cpu_render_target* target) {
return target ? target->width : 0; return target ? target->width : 0;
} }
u32* pxl8_cpu_render_target_get_light_accum(pxl8_cpu_render_target* target) {
return target ? target->light_accum : NULL;
}
u32* pxl8_cpu_get_output(pxl8_cpu_backend* cpu) {
return cpu ? cpu->output : NULL;
}
void pxl8_cpu_render_glows(
pxl8_cpu_backend* cpu,
const pxl8_glow_source* glows,
u32 glow_count
) {
if (!cpu || cpu->target_stack_depth == 0) return;
pxl8_cpu_render_target* target = cpu->target_stack[cpu->target_stack_depth - 1];
if (!target || !target->framebuffer) return;
u32 width = target->width;
u32 height = target->height;
u8* pixels = target->framebuffer;
u16* zbuffer = target->zbuffer;
u32* light_accum = target->light_accum;
for (u32 gi = 0; gi < glow_count; gi++) {
const pxl8_glow_source* glow = &glows[gi];
i32 cx = glow->x;
i32 cy = glow->y;
i32 radius = glow->radius;
if (radius <= 0 || glow->intensity == 0) continue;
i32 x0 = cx - radius;
i32 y0 = cy - radius;
i32 x1 = cx + radius;
i32 y1 = cy + radius;
if (x0 < 0) x0 = 0;
if (y0 < 0) y0 = 0;
if (x1 >= (i32)width) x1 = (i32)width - 1;
if (y1 >= (i32)height) y1 = (i32)height - 1;
if (x0 >= x1 || y0 >= y1) continue;
u16 glow_depth = glow->depth;
u8 base_color = glow->color;
f32 base_intensity = glow->intensity / 255.0f;
f32 radius_f = (f32)radius;
f32 radius_sq = radius_f * radius_f;
f32 inv_radius_sq = 1.0f / radius_sq;
f32 inv_radius = 1.0f / radius_f;
for (i32 y = y0; y <= y1; y++) {
u32 row = (u32)y * width;
f32 dy = (f32)(y - cy);
for (i32 x = x0; x <= x1; x++) {
u32 idx = row + (u32)x;
if (zbuffer && glow_depth > zbuffer[idx]) continue;
f32 dx = (f32)(x - cx);
f32 intensity = 0.0f;
switch (glow->shape) {
case PXL8_GLOW_CIRCLE: {
f32 dist_sq = dx * dx + dy * dy;
if (dist_sq >= radius_sq) continue;
f32 falloff = 1.0f - dist_sq * inv_radius_sq;
intensity = base_intensity * falloff * falloff;
break;
}
case PXL8_GLOW_DIAMOND: {
f32 abs_dx = dx < 0 ? -dx : dx;
f32 abs_dy = dy < 0 ? -dy : dy;
f32 manhattan = abs_dx + abs_dy;
if (manhattan >= radius_f) continue;
f32 falloff = 1.0f - manhattan * inv_radius;
intensity = base_intensity * falloff * falloff;
break;
}
case PXL8_GLOW_SHAFT: {
f32 abs_dx = dx < 0 ? -dx : dx;
f32 abs_dy = dy < 0 ? -dy : dy;
f32 height_f = glow->height > 0 ? (f32)glow->height : radius_f;
if (abs_dx >= radius_f || abs_dy >= height_f) continue;
f32 falloff_x = 1.0f - abs_dx * inv_radius;
f32 falloff_y = 1.0f - abs_dy / height_f;
intensity = base_intensity * falloff_x * falloff_x * falloff_y;
break;
}
}
if (intensity < 0.02f) continue;
u8 int_val = intensity < 1.0f ? (u8)(intensity * 255.0f) : 255;
u8 light_level = pxl8_ordered_dither(int_val, (u32)x, (u32)y);
if (light_level < 8) continue;
u8 shaded = pxl8_colormap_lookup(cpu->colormap, base_color, PXL8_LIGHT_WHITE, light_level);
if (cpu->palette_cube) {
u8 sr, sg, sb, dr, dg, db;
pxl8_palette_cube_get_rgb(cpu->palette_cube, shaded, &sr, &sg, &sb);
pxl8_palette_cube_get_rgb(cpu->palette_cube, pixels[idx], &dr, &dg, &db);
u32 r = (u32)sr + (u32)dr;
u32 g = (u32)sg + (u32)dg;
u32 b = (u32)sb + (u32)db;
if (r > 255) r = 255;
if (g > 255) g = 255;
if (b > 255) b = 255;
pixels[idx] = pxl8_palette_cube_lookup(cpu->palette_cube, (u8)r, (u8)g, (u8)b);
} else {
pixels[idx] = shaded;
}
if (light_accum) {
light_accum[idx] = 0;
}
}
}
}
}
void pxl8_cpu_resolve(pxl8_cpu_backend* cpu) {
if (!cpu || !cpu->palette || cpu->target_stack_depth == 0) return;
pxl8_cpu_render_target* target = cpu->target_stack[0];
if (!target || !target->framebuffer) return;
u32 pixel_count = target->width * target->height;
const u8* pixels = target->framebuffer;
const u32* light_accum = target->light_accum;
const u32* palette = cpu->palette;
u32* output = cpu->output;
for (u32 i = 0; i < pixel_count; i++) {
u32 base = palette[pixels[i]];
if (light_accum && light_accum[i] != 0) {
u32 light_color = light_accum[i];
u32 tint_alpha = (light_color >> 24) & 0xFF;
if (tint_alpha > 0) {
u32 lr = light_color & 0xFF;
u32 lg = (light_color >> 8) & 0xFF;
u32 lb = (light_color >> 16) & 0xFF;
u32 br = base & 0xFF;
u32 bg = (base >> 8) & 0xFF;
u32 bb = (base >> 16) & 0xFF;
u32 ba = (base >> 24) & 0xFF;
u32 t = (tint_alpha * tint_alpha) / 255;
u32 inv_t = 255 - t;
u32 lr_norm = (lr * 255) / 240;
u32 lg_norm = (lg * 255) / 240;
u32 lb_norm = (lb * 255) / 220;
u32 or = ((br * inv_t + (br * lr_norm / 255) * t) / 255);
u32 og = ((bg * inv_t + (bg * lg_norm / 255) * t) / 255);
u32 ob = ((bb * inv_t + (bb * lb_norm / 255) * t) / 255);
if (or > 255) or = 255;
if (og > 255) og = 255;
if (ob > 255) ob = 255;
output[i] = or | (og << 8) | (ob << 16) | (ba << 24);
continue;
}
}
output[i] = base;
}
for (u32 t = 1; t < cpu->target_stack_depth; t++) {
pxl8_cpu_render_target* overlay = cpu->target_stack[t];
if (!overlay || !overlay->framebuffer) continue;
const u8* overlay_pixels = overlay->framebuffer;
for (u32 i = 0; i < pixel_count; i++) {
u8 idx = overlay_pixels[i];
if (idx != 0) {
output[i] = palette[idx];
}
}
}
}

View file

@ -2,12 +2,8 @@
#include "pxl8_atlas.h" #include "pxl8_atlas.h"
#include "pxl8_colormap.h" #include "pxl8_colormap.h"
#include "pxl8_gfx.h"
#include "pxl8_gfx3d.h"
#include "pxl8_lightmap.h"
#include "pxl8_math.h" #include "pxl8_math.h"
#include "pxl8_mesh.h" #include "pxl8_mesh.h"
#include "pxl8_palette.h"
#include "pxl8_types.h" #include "pxl8_types.h"
#ifdef __cplusplus #ifdef __cplusplus
@ -24,6 +20,19 @@ typedef struct pxl8_cpu_render_target_desc {
bool with_lighting; bool with_lighting;
} pxl8_cpu_render_target_desc; } pxl8_cpu_render_target_desc;
typedef struct pxl8_3d_frame {
u8 ambient_light;
pxl8_vec3 camera_dir;
pxl8_vec3 camera_pos;
f32 far_clip;
u8 fog_color;
f32 fog_density;
f32 near_clip;
pxl8_mat4 projection;
f32 time;
pxl8_mat4 view;
} pxl8_3d_frame;
pxl8_cpu_backend* pxl8_cpu_create(u32 width, u32 height); pxl8_cpu_backend* pxl8_cpu_create(u32 width, u32 height);
void pxl8_cpu_destroy(pxl8_cpu_backend* cpu); void pxl8_cpu_destroy(pxl8_cpu_backend* cpu);
@ -35,10 +44,6 @@ void pxl8_cpu_clear_depth(pxl8_cpu_backend* cpu);
void pxl8_cpu_set_colormap(pxl8_cpu_backend* cpu, const pxl8_colormap* cm); void pxl8_cpu_set_colormap(pxl8_cpu_backend* cpu, const pxl8_colormap* cm);
void pxl8_cpu_set_palette(pxl8_cpu_backend* cpu, const u32* palette); void pxl8_cpu_set_palette(pxl8_cpu_backend* cpu, const u32* palette);
void pxl8_cpu_set_palette_cube(pxl8_cpu_backend* cpu, const pxl8_palette_cube* cube);
u32 pxl8_cpu_add_lightmap(pxl8_cpu_backend* cpu, pxl8_lightmap* lm);
pxl8_lightmap* pxl8_cpu_get_lightmap(pxl8_cpu_backend* cpu, u32 id);
void pxl8_cpu_draw_pixel(pxl8_cpu_backend* cpu, i32 x, i32 y, u8 color); void pxl8_cpu_draw_pixel(pxl8_cpu_backend* cpu, i32 x, i32 y, u8 color);
u8 pxl8_cpu_get_pixel(pxl8_cpu_backend* cpu, i32 x, i32 y); u8 pxl8_cpu_get_pixel(pxl8_cpu_backend* cpu, i32 x, i32 y);
@ -52,8 +57,8 @@ void pxl8_cpu_draw_line_3d(pxl8_cpu_backend* cpu, pxl8_vec3 v0, pxl8_vec3 v1, u8
void pxl8_cpu_draw_mesh( void pxl8_cpu_draw_mesh(
pxl8_cpu_backend* cpu, pxl8_cpu_backend* cpu,
const pxl8_mesh* mesh, const pxl8_mesh* mesh,
const pxl8_mat4* model, pxl8_mat4 model,
const pxl8_gfx_material* material, pxl8_material material,
const pxl8_atlas* textures const pxl8_atlas* textures
); );
@ -65,18 +70,9 @@ void pxl8_cpu_draw_mesh_wireframe(
); );
u8* pxl8_cpu_get_framebuffer(pxl8_cpu_backend* cpu); u8* pxl8_cpu_get_framebuffer(pxl8_cpu_backend* cpu);
u32* pxl8_cpu_get_output(pxl8_cpu_backend* cpu);
u32 pxl8_cpu_get_height(const pxl8_cpu_backend* cpu); u32 pxl8_cpu_get_height(const pxl8_cpu_backend* cpu);
u32 pxl8_cpu_get_width(const pxl8_cpu_backend* cpu); u32 pxl8_cpu_get_width(const pxl8_cpu_backend* cpu);
void pxl8_cpu_render_glows(
pxl8_cpu_backend* cpu,
const pxl8_glow_source* glows,
u32 glow_count
);
void pxl8_cpu_resolve(pxl8_cpu_backend* cpu);
pxl8_cpu_render_target* pxl8_cpu_create_render_target(const pxl8_cpu_render_target_desc* desc); pxl8_cpu_render_target* pxl8_cpu_create_render_target(const pxl8_cpu_render_target_desc* desc);
void pxl8_cpu_destroy_render_target(pxl8_cpu_render_target* target); void pxl8_cpu_destroy_render_target(pxl8_cpu_render_target* target);
@ -92,7 +88,6 @@ u32 pxl8_cpu_get_target_depth(const pxl8_cpu_backend* cpu);
u8* pxl8_cpu_render_target_get_framebuffer(pxl8_cpu_render_target* target); u8* pxl8_cpu_render_target_get_framebuffer(pxl8_cpu_render_target* target);
u32 pxl8_cpu_render_target_get_height(const pxl8_cpu_render_target* target); u32 pxl8_cpu_render_target_get_height(const pxl8_cpu_render_target* target);
u32 pxl8_cpu_render_target_get_width(const pxl8_cpu_render_target* target); u32 pxl8_cpu_render_target_get_width(const pxl8_cpu_render_target* target);
u32* pxl8_cpu_render_target_get_light_accum(pxl8_cpu_render_target* target);
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -34,7 +34,6 @@ struct pxl8_gfx {
const pxl8_hal* hal; const pxl8_hal* hal;
bool initialized; bool initialized;
pxl8_palette* palette; pxl8_palette* palette;
pxl8_palette_cube* palette_cube;
pxl8_pixel_mode pixel_mode; pxl8_pixel_mode pixel_mode;
void* platform_data; void* platform_data;
pxl8_sprite_cache_entry* sprite_cache; pxl8_sprite_cache_entry* sprite_cache;
@ -75,14 +74,10 @@ i32 pxl8_gfx_get_width(const pxl8_gfx* gfx) {
return gfx ? gfx->framebuffer_width : 0; return gfx ? gfx->framebuffer_width : 0;
} }
pxl8_palette* pxl8_gfx_palette(pxl8_gfx* gfx) { pxl8_palette* pxl8_gfx_get_palette(pxl8_gfx* gfx) {
return gfx ? gfx->palette : NULL; return gfx ? gfx->palette : NULL;
} }
pxl8_colormap* pxl8_gfx_colormap(pxl8_gfx* gfx) {
return gfx ? gfx->colormap : NULL;
}
u8 pxl8_gfx_find_color(pxl8_gfx* gfx, u32 color) { u8 pxl8_gfx_find_color(pxl8_gfx* gfx, u32 color) {
if (!gfx || !gfx->palette) return 0; if (!gfx || !gfx->palette) return 0;
if (color <= 0xFFFFFF) color = (color << 8) | 0xFF; if (color <= 0xFFFFFF) color = (color << 8) | 0xFF;
@ -92,24 +87,37 @@ u8 pxl8_gfx_find_color(pxl8_gfx* gfx, u32 color) {
return pxl8_palette_find_closest(gfx->palette, r, g, b); return pxl8_palette_find_closest(gfx->palette, r, g, b);
} }
void pxl8_gfx_set_palette_colors(pxl8_gfx* gfx, const u32* colors, u16 count) { void pxl8_gfx_set_palette(pxl8_gfx* gfx, pxl8_palette* pal) {
if (!gfx || !gfx->palette || !colors) return; if (!gfx) return;
pxl8_set_palette(gfx->palette, colors, count); if (gfx->palette) {
if (gfx->pixel_mode != PXL8_PIXEL_HICOLOR && gfx->backend.type == PXL8_GFX_BACKEND_CPU) { pxl8_palette_destroy(gfx->palette);
pxl8_cpu_set_palette(gfx->backend.cpu, pxl8_palette_colors(gfx->palette)); }
gfx->palette = pal;
if (gfx->palette && gfx->pixel_mode != PXL8_PIXEL_HICOLOR) {
u32* colors = pxl8_palette_colors(gfx->palette);
if (gfx->colormap) {
pxl8_colormap_generate(gfx->colormap, colors, NULL);
}
if (gfx->backend.type == PXL8_GFX_BACKEND_CPU) {
pxl8_cpu_set_palette(gfx->backend.cpu, colors);
}
} }
} }
i32 pxl8_gfx_load_palette(pxl8_gfx* gfx, const char* filepath) { i32 pxl8_gfx_load_palette(pxl8_gfx* gfx, const char* filepath) {
if (!gfx || !gfx->palette || !filepath) return -1; if (!gfx || !filepath) return -1;
pxl8_result result = pxl8_palette_load_ase(gfx->palette, filepath); pxl8_palette* pal = gfx->palette;
if (!pal) return -1;
pxl8_result result = pxl8_palette_load_ase(pal, filepath);
if (result != PXL8_OK) return (i32)result; if (result != PXL8_OK) return (i32)result;
if (gfx->pixel_mode != PXL8_PIXEL_HICOLOR) { if (gfx->pixel_mode != PXL8_PIXEL_HICOLOR) {
u32* colors = pxl8_palette_colors(pal);
if (gfx->colormap) { if (gfx->colormap) {
pxl8_colormap_generate(gfx->colormap, pxl8_palette_colors(gfx->palette)); pxl8_colormap_generate(gfx->colormap, colors, NULL);
} }
if (gfx->backend.type == PXL8_GFX_BACKEND_CPU) { if (gfx->backend.type == PXL8_GFX_BACKEND_CPU) {
pxl8_cpu_set_palette(gfx->backend.cpu, pxl8_palette_colors(gfx->palette)); pxl8_cpu_set_palette(gfx->backend.cpu, colors);
} }
} }
return 0; return 0;
@ -180,7 +188,6 @@ void pxl8_gfx_destroy(pxl8_gfx* gfx) {
pxl8_cpu_destroy(gfx->backend.cpu); pxl8_cpu_destroy(gfx->backend.cpu);
} }
free(gfx->colormap); free(gfx->colormap);
pxl8_palette_cube_destroy(gfx->palette_cube);
pxl8_palette_destroy(gfx->palette); pxl8_palette_destroy(gfx->palette);
free(gfx->sprite_cache); free(gfx->sprite_cache);
@ -302,30 +309,15 @@ pxl8_result pxl8_gfx_load_font_atlas(pxl8_gfx* gfx) {
void pxl8_gfx_upload_framebuffer(pxl8_gfx* gfx) { void pxl8_gfx_upload_framebuffer(pxl8_gfx* gfx) {
if (!gfx || !gfx->initialized || !gfx->hal) return; if (!gfx || !gfx->initialized || !gfx->hal) return;
if (gfx->backend.type == PXL8_GFX_BACKEND_CPU && gfx->pixel_mode == PXL8_PIXEL_INDEXED) { u32 bpp = (gfx->pixel_mode == PXL8_PIXEL_HICOLOR) ? 2 : 1;
pxl8_cpu_resolve(gfx->backend.cpu);
u32* output = pxl8_cpu_get_output(gfx->backend.cpu);
if (output) {
gfx->hal->upload_texture(
gfx->platform_data,
output,
gfx->framebuffer_width,
gfx->framebuffer_height,
PXL8_PIXEL_RGBA,
NULL
);
return;
}
}
u32* colors = gfx->palette ? pxl8_palette_colors(gfx->palette) : NULL; u32* colors = gfx->palette ? pxl8_palette_colors(gfx->palette) : NULL;
gfx->hal->upload_texture( gfx->hal->upload_texture(
gfx->platform_data, gfx->platform_data,
gfx->framebuffer, gfx->framebuffer,
gfx->framebuffer_width, gfx->framebuffer_width,
gfx->framebuffer_height, gfx->framebuffer_height,
gfx->pixel_mode, colors,
colors bpp
); );
} }
@ -446,7 +438,6 @@ void pxl8_2d_text(pxl8_gfx* gfx, const char* text, i32 x, i32 y, u32 color) {
if (!gfx || !text) return; if (!gfx || !text) return;
u8* framebuffer = NULL; u8* framebuffer = NULL;
u32* light_accum = NULL;
i32 fb_width = 0; i32 fb_width = 0;
i32 fb_height = 0; i32 fb_height = 0;
@ -455,7 +446,6 @@ void pxl8_2d_text(pxl8_gfx* gfx, const char* text, i32 x, i32 y, u32 color) {
pxl8_cpu_render_target* render_target = pxl8_cpu_get_target(gfx->backend.cpu); pxl8_cpu_render_target* render_target = pxl8_cpu_get_target(gfx->backend.cpu);
if (!render_target) return; if (!render_target) return;
framebuffer = pxl8_cpu_render_target_get_framebuffer(render_target); framebuffer = pxl8_cpu_render_target_get_framebuffer(render_target);
light_accum = pxl8_cpu_render_target_get_light_accum(render_target);
fb_width = (i32)pxl8_cpu_render_target_get_width(render_target); fb_width = (i32)pxl8_cpu_render_target_get_width(render_target);
fb_height = (i32)pxl8_cpu_render_target_get_height(render_target); fb_height = (i32)pxl8_cpu_render_target_get_height(render_target);
break; break;
@ -493,9 +483,7 @@ void pxl8_2d_text(pxl8_gfx* gfx, const char* text, i32 x, i32 y, u32 color) {
} }
if (pixel_bit) { if (pixel_bit) {
i32 idx = py * fb_width + px; framebuffer[py * fb_width + px] = (u8)color;
framebuffer[idx] = (u8)color;
if (light_accum) light_accum[idx] = 0;
} }
} }
} }
@ -509,7 +497,6 @@ void pxl8_2d_sprite(pxl8_gfx* gfx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h, bo
if (!gfx || !gfx->atlas) return; if (!gfx || !gfx->atlas) return;
u8* framebuffer = NULL; u8* framebuffer = NULL;
u32* light_accum = NULL;
i32 fb_width = 0; i32 fb_width = 0;
i32 fb_height = 0; i32 fb_height = 0;
@ -518,7 +505,6 @@ void pxl8_2d_sprite(pxl8_gfx* gfx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h, bo
pxl8_cpu_render_target* render_target = pxl8_cpu_get_target(gfx->backend.cpu); pxl8_cpu_render_target* render_target = pxl8_cpu_get_target(gfx->backend.cpu);
if (!render_target) return; if (!render_target) return;
framebuffer = pxl8_cpu_render_target_get_framebuffer(render_target); framebuffer = pxl8_cpu_render_target_get_framebuffer(render_target);
light_accum = pxl8_cpu_render_target_get_light_accum(render_target);
fb_width = (i32)pxl8_cpu_render_target_get_width(render_target); fb_width = (i32)pxl8_cpu_render_target_get_width(render_target);
fb_height = (i32)pxl8_cpu_render_target_get_height(render_target); fb_height = (i32)pxl8_cpu_render_target_get_height(render_target);
break; break;
@ -555,11 +541,6 @@ 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) { if (is_1to1_scale && is_unclipped && !is_flipped) {
const u8* sprite_data = atlas_pixels + entry->y * atlas_width + entry->x; 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_indexed(framebuffer, fb_width, sprite_data, atlas_width, x, y, w, h);
if (light_accum) {
for (i32 py = 0; py < h; py++) {
memset(light_accum + (y + py) * fb_width + x, 0, w * sizeof(u32));
}
}
} else { } else {
for (i32 py = 0; py < draw_height; py++) { for (i32 py = 0; py < draw_height; py++) {
for (i32 px = 0; px < draw_width; px++) { for (i32 px = 0; px < draw_width; px++) {
@ -573,7 +554,6 @@ void pxl8_2d_sprite(pxl8_gfx* gfx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h, bo
i32 dest_idx = (dest_y + py) * fb_width + (dest_x + px); 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_blend_indexed(atlas_pixels[src_idx], framebuffer[dest_idx]);
if (light_accum) light_accum[dest_idx] = 0;
} }
} }
} }
@ -588,32 +568,30 @@ void pxl8_gfx_update(pxl8_gfx* gfx, f32 dt) {
} }
} }
pxl8_3d_frame pxl8_3d_frame_from_camera(const pxl8_3d_camera* camera, const pxl8_3d_uniforms* uniforms) {
pxl8_3d_frame frame = {0};
if (!camera) return frame;
frame.view = pxl8_3d_camera_get_view(camera);
frame.projection = pxl8_3d_camera_get_projection(camera);
frame.camera_pos = pxl8_3d_camera_get_position(camera);
frame.camera_dir = pxl8_3d_camera_get_forward(camera);
frame.near_clip = 1.0f;
frame.far_clip = 4096.0f;
if (uniforms) {
frame.uniforms = *uniforms;
}
return frame;
}
void pxl8_3d_begin_frame(pxl8_gfx* gfx, const pxl8_3d_camera* camera, const pxl8_3d_uniforms* uniforms) { void pxl8_3d_begin_frame(pxl8_gfx* gfx, const pxl8_3d_camera* camera, const pxl8_3d_uniforms* uniforms) {
if (!gfx || !camera) return; if (!gfx || !camera) return;
pxl8_3d_frame frame = pxl8_3d_frame_from_camera(camera, uniforms); pxl8_mat4 view = pxl8_3d_camera_get_view(camera);
pxl8_mat4 projection = pxl8_3d_camera_get_projection(camera);
pxl8_vec3 position = pxl8_3d_camera_get_position(camera);
pxl8_vec3 forward = pxl8_3d_camera_get_forward(camera);
pxl8_mat4 vp = pxl8_mat4_mul(frame.projection, frame.view); pxl8_mat4 vp = pxl8_mat4_mul(projection, view);
gfx->frustum = pxl8_frustum_from_matrix(vp); gfx->frustum = pxl8_frustum_from_matrix(vp);
pxl8_3d_frame frame = {
.ambient_light = uniforms ? uniforms->ambient : 0,
.camera_dir = forward,
.camera_pos = position,
.far_clip = 4096.0f,
.fog_color = uniforms ? uniforms->fog_color : 0,
.fog_density = uniforms ? uniforms->fog_density : 0.0f,
.near_clip = 1.0f,
.projection = projection,
.time = uniforms ? uniforms->time : 0.0f,
.view = view,
};
switch (gfx->backend.type) { switch (gfx->backend.type) {
case PXL8_GFX_BACKEND_CPU: case PXL8_GFX_BACKEND_CPU:
pxl8_cpu_begin_frame(gfx->backend.cpu, &frame); pxl8_cpu_begin_frame(gfx->backend.cpu, &frame);
@ -661,8 +639,8 @@ void pxl8_3d_draw_line(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, u8 color) {
} }
} }
void pxl8_3d_draw_mesh(pxl8_gfx* gfx, const pxl8_mesh* mesh, const pxl8_mat4* model, const pxl8_gfx_material* material) { void pxl8_3d_draw_mesh(pxl8_gfx* gfx, const pxl8_mesh* mesh, pxl8_mat4 model, pxl8_material material) {
if (!gfx || !mesh || !model || !material) return; if (!gfx || !mesh) return;
switch (gfx->backend.type) { switch (gfx->backend.type) {
case PXL8_GFX_BACKEND_CPU: case PXL8_GFX_BACKEND_CPU:
pxl8_cpu_draw_mesh(gfx->backend.cpu, mesh, model, material, gfx->atlas); pxl8_cpu_draw_mesh(gfx->backend.cpu, mesh, model, material, gfx->atlas);
@ -726,67 +704,3 @@ void pxl8_gfx_pop_target(pxl8_gfx* gfx) {
break; break;
} }
} }
void pxl8_gfx_ensure_blend_tables(pxl8_gfx* gfx) {
if (!gfx || !gfx->palette) return;
if (!gfx->palette_cube) {
gfx->palette_cube = pxl8_palette_cube_create(gfx->palette);
if (gfx->backend.cpu) {
pxl8_cpu_set_palette_cube(gfx->backend.cpu, gfx->palette_cube);
}
}
}
void pxl8_gfx_blend_tables_update(pxl8_gfx* gfx) {
if (!gfx || !gfx->palette) return;
pxl8_gfx_ensure_blend_tables(gfx);
if (gfx->palette_cube) {
pxl8_palette_cube_rebuild(gfx->palette_cube, gfx->palette);
if (gfx->backend.cpu) {
pxl8_cpu_set_palette_cube(gfx->backend.cpu, gfx->palette_cube);
}
}
}
void pxl8_gfx_colormap_update(pxl8_gfx* gfx) {
if (!gfx || !gfx->palette || !gfx->colormap) return;
u32* colors = pxl8_palette_colors(gfx->palette);
if (colors) {
pxl8_colormap_generate(gfx->colormap, colors);
}
}
u8 pxl8_gfx_find_closest_color(pxl8_gfx* gfx, u8 r, u8 g, u8 b) {
if (!gfx || !gfx->palette) return 0;
return pxl8_palette_find_closest(gfx->palette, r, g, b);
}
u8 pxl8_gfx_ui_color(pxl8_gfx* gfx, u8 index) {
if (!gfx || !gfx->palette || index >= PXL8_UI_PALETTE_SIZE) return 0;
u32 abgr = pxl8_ui_palette[index];
u8 r = (abgr >> 0) & 0xFF;
u8 g = (abgr >> 8) & 0xFF;
u8 b = (abgr >> 16) & 0xFF;
return pxl8_palette_find_closest(gfx->palette, r, g, b);
}
void pxl8_gfx_apply_effect(pxl8_gfx* gfx, pxl8_gfx_effect effect, const void* params, u32 count) {
if (!gfx || !params || count == 0) return;
switch (effect) {
case PXL8_GFX_EFFECT_GLOWS: {
const pxl8_glow_source* glows = (const pxl8_glow_source*)params;
switch (gfx->backend.type) {
case PXL8_GFX_BACKEND_CPU:
pxl8_cpu_render_glows(gfx->backend.cpu, glows, count);
break;
case PXL8_GFX_BACKEND_GPU:
break;
}
break;
}
}
}

48
client/src/gfx/pxl8_gfx.h Normal file
View file

@ -0,0 +1,48 @@
#pragma once
#include "pxl8_gfx2d.h"
#include "pxl8_gfx3d.h"
#include "pxl8_hal.h"
#include "pxl8_palette.h"
#include "pxl8_types.h"
typedef struct pxl8_gfx pxl8_gfx;
#ifdef __cplusplus
extern "C" {
#endif
pxl8_gfx* pxl8_gfx_create(const pxl8_hal* hal, void* platform_data, pxl8_pixel_mode mode, pxl8_resolution resolution);
void pxl8_gfx_destroy(pxl8_gfx* gfx);
void pxl8_gfx_present(pxl8_gfx* gfx);
void pxl8_gfx_update(pxl8_gfx* gfx, f32 dt);
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);
i32 pxl8_gfx_get_height(const pxl8_gfx* gfx);
pxl8_palette* pxl8_gfx_get_palette(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);
void pxl8_gfx_project(pxl8_gfx* gfx, f32 left, f32 right, f32 top, f32 bottom);
void pxl8_gfx_set_palette(pxl8_gfx* gfx, pxl8_palette* pal);
void pxl8_gfx_set_viewport(pxl8_gfx* gfx, pxl8_viewport vp);
pxl8_viewport pxl8_gfx_viewport(pxl8_bounds bounds, i32 width, i32 height);
void pxl8_gfx_clear_textures(pxl8_gfx* gfx);
pxl8_result pxl8_gfx_create_texture(pxl8_gfx* gfx, const u8* pixels, u32 width, u32 height);
pxl8_result pxl8_gfx_load_font_atlas(pxl8_gfx* gfx);
pxl8_result pxl8_gfx_load_sprite(pxl8_gfx* gfx, const char* path);
bool pxl8_gfx_push_target(pxl8_gfx* gfx);
void pxl8_gfx_pop_target(pxl8_gfx* gfx);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,33 @@
#pragma once
#include "pxl8_3d_camera.h"
#include "pxl8_math.h"
#include "pxl8_mesh.h"
#include "pxl8_types.h"
typedef struct pxl8_gfx pxl8_gfx;
typedef struct pxl8_3d_uniforms {
u8 ambient;
u8 fog_color;
f32 fog_density;
f32 time;
} pxl8_3d_uniforms;
#ifdef __cplusplus
extern "C" {
#endif
void pxl8_3d_begin_frame(pxl8_gfx* gfx, const pxl8_3d_camera* camera, const pxl8_3d_uniforms* uniforms);
void pxl8_3d_clear(pxl8_gfx* gfx, u8 color);
void pxl8_3d_clear_depth(pxl8_gfx* gfx);
void pxl8_3d_draw_line(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, u8 color);
void pxl8_3d_draw_mesh(pxl8_gfx* gfx, const pxl8_mesh* mesh, pxl8_mat4 model, pxl8_material material);
void pxl8_3d_draw_mesh_wireframe(pxl8_gfx* gfx, const pxl8_mesh* mesh, pxl8_mat4 model, u8 color);
void pxl8_3d_end_frame(pxl8_gfx* gfx);
u8* pxl8_3d_get_framebuffer(pxl8_gfx* gfx);
const pxl8_frustum* pxl8_3d_get_frustum(pxl8_gfx* gfx);
#ifdef __cplusplus
}
#endif

View file

@ -17,9 +17,8 @@ typedef enum pxl8_blend_mode {
PXL8_BLEND_ADDITIVE, PXL8_BLEND_ADDITIVE,
} pxl8_blend_mode; } pxl8_blend_mode;
typedef struct pxl8_gfx_material { typedef struct pxl8_material {
u32 texture_id; u32 texture_id;
u32 lightmap_id;
u8 alpha; u8 alpha;
u8 blend_mode; u8 blend_mode;
bool dither; bool dither;
@ -27,15 +26,13 @@ typedef struct pxl8_gfx_material {
bool dynamic_lighting; bool dynamic_lighting;
bool per_pixel; bool per_pixel;
bool vertex_color_passthrough; bool vertex_color_passthrough;
bool wireframe;
f32 emissive_intensity; f32 emissive_intensity;
} pxl8_gfx_material; } pxl8_material;
typedef struct pxl8_vertex { typedef struct pxl8_vertex {
pxl8_vec3 position; pxl8_vec3 position;
pxl8_vec3 normal; pxl8_vec3 normal;
f32 u, v; f32 u, v;
f32 lu, lv;
u8 color; u8 color;
u8 light; u8 light;
u8 _pad[2]; u8 _pad[2];
@ -65,6 +62,62 @@ static inline u32 pxl8_mesh_triangle_count(const pxl8_mesh* mesh) {
return mesh->index_count / 3; return mesh->index_count / 3;
} }
static inline pxl8_material pxl8_material_new(u32 texture_id) {
return (pxl8_material){
.texture_id = texture_id,
.alpha = 255,
.blend_mode = PXL8_BLEND_OPAQUE,
.dither = true,
.double_sided = false,
.dynamic_lighting = false,
.per_pixel = false,
.vertex_color_passthrough = false,
.emissive_intensity = 0.0f,
};
}
static inline pxl8_material pxl8_material_with_alpha(pxl8_material m, u8 alpha) {
m.alpha = alpha;
if (alpha < 255 && m.blend_mode == PXL8_BLEND_OPAQUE) {
m.blend_mode = PXL8_BLEND_ALPHA;
}
return m;
}
static inline pxl8_material pxl8_material_with_blend(pxl8_material m, pxl8_blend_mode mode) {
m.blend_mode = mode;
return m;
}
static inline pxl8_material pxl8_material_with_double_sided(pxl8_material m) {
m.double_sided = true;
return m;
}
static inline pxl8_material pxl8_material_with_emissive(pxl8_material m, f32 intensity) {
m.emissive_intensity = intensity;
return m;
}
static inline pxl8_material pxl8_material_with_lighting(pxl8_material m) {
m.dynamic_lighting = true;
return m;
}
static inline pxl8_material pxl8_material_with_no_dither(pxl8_material m) {
m.dither = false;
return m;
}
static inline pxl8_material pxl8_material_with_passthrough(pxl8_material m) {
m.vertex_color_passthrough = true;
return m;
}
static inline pxl8_material pxl8_material_with_per_pixel(pxl8_material m) {
m.per_pixel = true;
return m;
}
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -5,17 +5,10 @@
#include "pxl8_ase.h" #include "pxl8_ase.h"
#include "pxl8_color.h" #include "pxl8_color.h"
#include "pxl8_colormap.h"
#include "pxl8_log.h" #include "pxl8_log.h"
#define PXL8_PALETTE_HASH_SIZE 512 #define PXL8_PALETTE_HASH_SIZE 512
struct pxl8_palette_cube {
u8 colors[PXL8_PALETTE_SIZE * 3];
u8 table[PXL8_CUBE_ENTRIES];
u8 stable[PXL8_CUBE_ENTRIES];
};
typedef struct { typedef struct {
u32 color; u32 color;
i16 index; i16 index;
@ -339,6 +332,10 @@ void pxl8_palette_get_rgba(const pxl8_palette* pal, u8 idx, u8* r, u8* g, u8* b,
unpack_rgba(pal->colors[idx], r, g, b, a); unpack_rgba(pal->colors[idx], r, g, b, a);
} }
void pxl8_palette_set(pxl8_palette* pal, u8 idx, u32 color) {
if (pal) pal->colors[idx] = color;
}
void pxl8_palette_set_rgb(pxl8_palette* pal, u8 idx, u8 r, u8 g, u8 b) { void pxl8_palette_set_rgb(pxl8_palette* pal, u8 idx, u8 r, u8 g, u8 b) {
if (pal) pal->colors[idx] = pack_rgb(r, g, b); if (pal) pal->colors[idx] = pack_rgb(r, g, b);
} }
@ -347,17 +344,6 @@ void pxl8_palette_set_rgba(pxl8_palette* pal, u8 idx, u8 r, u8 g, u8 b, u8 a) {
if (pal) pal->colors[idx] = pack_rgba(r, g, b, a); if (pal) pal->colors[idx] = pack_rgba(r, g, b, a);
} }
void pxl8_set_palette(pxl8_palette* pal, const u32* colors, u16 count) {
if (!pal || !colors) return;
for (u16 i = 0; i < count; i++) {
u32 rgb = colors[i];
u8 r = (rgb >> 16) & 0xFF;
u8 g = (rgb >> 8) & 0xFF;
u8 b = rgb & 0xFF;
pal->colors[i] = pack_rgb(r, g, b);
}
}
void pxl8_palette_fill_gradient(pxl8_palette* pal, u8 start, u8 count, u32 from, u32 to) { void pxl8_palette_fill_gradient(pxl8_palette* pal, u8 start, u8 count, u32 from, u32 to) {
if (!pal || count == 0) return; if (!pal || count == 0) return;
@ -463,7 +449,7 @@ void pxl8_palette_tick(pxl8_palette* pal, u16 delta_ticks) {
} }
} }
pxl8_cycle_range pxl8_cycle_range_create(u8 start, u8 len, u16 period) { pxl8_cycle_range pxl8_cycle_range_new(u8 start, u8 len, u16 period) {
pxl8_cycle_range range = { pxl8_cycle_range range = {
.easing = PXL8_EASE_LINEAR, .easing = PXL8_EASE_LINEAR,
.interpolate = true, .interpolate = true,
@ -486,91 +472,3 @@ pxl8_cycle_range pxl8_cycle_range_disabled(void) {
}; };
return range; return range;
} }
static u8 find_closest_stable(const pxl8_palette* pal, u8 r, u8 g, u8 b) {
u8 best_idx = 1;
u32 best_dist = 0xFFFFFFFF;
u8 dynamic_end = PXL8_DYNAMIC_RANGE_START + PXL8_DYNAMIC_RANGE_COUNT;
for (u32 i = 1; i < PXL8_FULLBRIGHT_START; i++) {
if (i >= PXL8_DYNAMIC_RANGE_START && i < dynamic_end) {
continue;
}
u8 pr, pg, pb;
pxl8_palette_get_rgb(pal, (u8)i, &pr, &pg, &pb);
i32 dr = (i32)r - (i32)pr;
i32 dg = (i32)g - (i32)pg;
i32 db = (i32)b - (i32)pb;
u32 dist = (u32)(dr * dr + dg * dg + db * db);
if (dist < best_dist) {
best_dist = dist;
best_idx = (u8)i;
if (dist == 0) break;
}
}
return best_idx;
}
pxl8_palette_cube* pxl8_palette_cube_create(const pxl8_palette* pal) {
pxl8_palette_cube* cube = calloc(1, sizeof(pxl8_palette_cube));
if (!cube) return NULL;
pxl8_palette_cube_rebuild(cube, pal);
return cube;
}
void pxl8_palette_cube_destroy(pxl8_palette_cube* cube) {
free(cube);
}
void pxl8_palette_cube_rebuild(pxl8_palette_cube* cube, const pxl8_palette* pal) {
if (!cube || !pal) return;
for (u32 i = 0; i < PXL8_PALETTE_SIZE; i++) {
u8 r, g, b;
pxl8_palette_get_rgb(pal, (u8)i, &r, &g, &b);
cube->colors[i * 3 + 0] = r;
cube->colors[i * 3 + 1] = g;
cube->colors[i * 3 + 2] = b;
}
for (u32 bi = 0; bi < PXL8_CUBE_SIZE; bi++) {
for (u32 gi = 0; gi < PXL8_CUBE_SIZE; gi++) {
for (u32 ri = 0; ri < PXL8_CUBE_SIZE; ri++) {
u32 idx = (bi * PXL8_CUBE_SIZE * PXL8_CUBE_SIZE) + (gi * PXL8_CUBE_SIZE) + ri;
u8 r8 = (u8)((ri * 255) / (PXL8_CUBE_SIZE - 1));
u8 g8 = (u8)((gi * 255) / (PXL8_CUBE_SIZE - 1));
u8 b8 = (u8)((bi * 255) / (PXL8_CUBE_SIZE - 1));
cube->table[idx] = pxl8_palette_find_closest(pal, r8, g8, b8);
cube->stable[idx] = find_closest_stable(pal, r8, g8, b8);
}
}
}
}
u8 pxl8_palette_cube_lookup(const pxl8_palette_cube* cube, u8 r, u8 g, u8 b) {
u32 ri = (r * (PXL8_CUBE_SIZE - 1)) / 255;
u32 gi = (g * (PXL8_CUBE_SIZE - 1)) / 255;
u32 bi = (b * (PXL8_CUBE_SIZE - 1)) / 255;
u32 idx = (bi * PXL8_CUBE_SIZE * PXL8_CUBE_SIZE) + (gi * PXL8_CUBE_SIZE) + ri;
return cube->table[idx];
}
u8 pxl8_palette_cube_lookup_stable(const pxl8_palette_cube* cube, u8 r, u8 g, u8 b) {
u32 ri = (r * (PXL8_CUBE_SIZE - 1)) / 255;
u32 gi = (g * (PXL8_CUBE_SIZE - 1)) / 255;
u32 bi = (b * (PXL8_CUBE_SIZE - 1)) / 255;
u32 idx = (bi * PXL8_CUBE_SIZE * PXL8_CUBE_SIZE) + (gi * PXL8_CUBE_SIZE) + ri;
return cube->stable[idx];
}
void pxl8_palette_cube_get_rgb(const pxl8_palette_cube* cube, u8 idx, u8* r, u8* g, u8* b) {
*r = cube->colors[idx * 3 + 0];
*g = cube->colors[idx * 3 + 1];
*b = cube->colors[idx * 3 + 2];
}

View file

@ -5,11 +5,8 @@
#define PXL8_PALETTE_SIZE 256 #define PXL8_PALETTE_SIZE 256
#define PXL8_MAX_CYCLES 8 #define PXL8_MAX_CYCLES 8
#define PXL8_MAX_CYCLE_LEN 16 #define PXL8_MAX_CYCLE_LEN 16
#define PXL8_CUBE_SIZE 32
#define PXL8_CUBE_ENTRIES (PXL8_CUBE_SIZE * PXL8_CUBE_SIZE * PXL8_CUBE_SIZE)
typedef struct pxl8_palette pxl8_palette; typedef struct pxl8_palette pxl8_palette;
typedef struct pxl8_palette_cube pxl8_palette_cube;
typedef enum pxl8_cycle_mode { typedef enum pxl8_cycle_mode {
PXL8_CYCLE_LOOP, PXL8_CYCLE_LOOP,
@ -52,9 +49,9 @@ u32 pxl8_palette_color(const pxl8_palette* pal, u8 idx);
i32 pxl8_palette_index(const pxl8_palette* pal, u32 color); i32 pxl8_palette_index(const pxl8_palette* pal, u32 color);
void pxl8_palette_get_rgb(const pxl8_palette* pal, u8 idx, u8* r, u8* g, u8* b); void pxl8_palette_get_rgb(const pxl8_palette* pal, u8 idx, u8* r, u8* g, u8* b);
void pxl8_palette_get_rgba(const pxl8_palette* pal, u8 idx, u8* r, u8* g, u8* b, u8* a); void pxl8_palette_get_rgba(const pxl8_palette* pal, u8 idx, u8* r, u8* g, u8* b, u8* a);
void pxl8_palette_set(pxl8_palette* pal, u8 idx, u32 color);
void pxl8_palette_set_rgb(pxl8_palette* pal, u8 idx, u8 r, u8 g, u8 b); void pxl8_palette_set_rgb(pxl8_palette* pal, u8 idx, u8 r, u8 g, u8 b);
void pxl8_palette_set_rgba(pxl8_palette* pal, u8 idx, u8 r, u8 g, u8 b, u8 a); void pxl8_palette_set_rgba(pxl8_palette* pal, u8 idx, u8 r, u8 g, u8 b, u8 a);
void pxl8_set_palette(pxl8_palette* pal, const u32* colors, u16 count);
void pxl8_palette_fill_gradient(pxl8_palette* pal, u8 start, u8 count, u32 from, u32 to); void pxl8_palette_fill_gradient(pxl8_palette* pal, u8 start, u8 count, u32 from, u32 to);
void pxl8_palette_fill_gradient_rgb(pxl8_palette* pal, u8 start, u8 count, u8 r0, u8 g0, u8 b0, u8 r1, u8 g1, u8 b1); void pxl8_palette_fill_gradient_rgb(pxl8_palette* pal, u8 start, u8 count, u8 r0, u8 g0, u8 b0, u8 r1, u8 g1, u8 b1);
@ -65,16 +62,9 @@ void pxl8_palette_set_cycle_colors(pxl8_palette* pal, u8 slot, const u32* colors
void pxl8_palette_set_cycle_phase(pxl8_palette* pal, u8 slot, f32 phase); void pxl8_palette_set_cycle_phase(pxl8_palette* pal, u8 slot, f32 phase);
void pxl8_palette_tick(pxl8_palette* pal, u16 delta_ticks); void pxl8_palette_tick(pxl8_palette* pal, u16 delta_ticks);
pxl8_cycle_range pxl8_cycle_range_create(u8 start, u8 len, u16 period); pxl8_cycle_range pxl8_cycle_range_new(u8 start, u8 len, u16 period);
pxl8_cycle_range pxl8_cycle_range_disabled(void); pxl8_cycle_range pxl8_cycle_range_disabled(void);
pxl8_palette_cube* pxl8_palette_cube_create(const pxl8_palette* pal);
void pxl8_palette_cube_destroy(pxl8_palette_cube* cube);
void pxl8_palette_cube_rebuild(pxl8_palette_cube* cube, const pxl8_palette* pal);
u8 pxl8_palette_cube_lookup(const pxl8_palette_cube* cube, u8 r, u8 g, u8 b);
u8 pxl8_palette_cube_lookup_stable(const pxl8_palette_cube* cube, u8 r, u8 g, u8 b);
void pxl8_palette_cube_get_rgb(const pxl8_palette_cube* cube, u8 idx, u8* r, u8* g, u8* b);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -11,8 +11,8 @@ typedef struct pxl8_hal {
void (*present)(void* platform_data); void (*present)(void* platform_data);
void (*set_cursor)(void* platform_data, u32 cursor); void (*set_cursor)(void* platform_data, u32 cursor);
void (*set_relative_mouse_mode)(void* platform_data, bool enabled); void (*set_relative_mouse_mode)(void* platform_data, bool enabled);
void (*upload_texture)(void* platform_data, const void* pixels, u32 w, u32 h, void (*upload_texture)(void* platform_data, const u8* pixels, u32 w, u32 h,
u32 bpp, const u32* palette); const u32* palette, u32 bpp);
void* (*audio_create)(i32 sample_rate, i32 channels); void* (*audio_create)(i32 sample_rate, i32 channels);
void (*audio_destroy)(void* audio_handle); void (*audio_destroy)(void* audio_handle);

View file

@ -81,6 +81,7 @@ static u64 sdl3_get_ticks(void) {
return SDL_GetTicksNS(); return SDL_GetTicksNS();
} }
static void sdl3_present(void* platform_data) { static void sdl3_present(void* platform_data) {
if (!platform_data) return; if (!platform_data) return;
@ -96,34 +97,28 @@ static void sdl3_present(void* platform_data) {
SDL_RenderPresent(ctx->renderer); SDL_RenderPresent(ctx->renderer);
} }
static void sdl3_upload_texture(void* platform_data, const void* pixels, u32 w, u32 h, static void sdl3_upload_texture(void* platform_data, const u8* pixels, u32 w, u32 h,
u32 bpp, const u32* palette) { const u32* palette, u32 bpp) {
if (!platform_data || !pixels) return; if (!platform_data || !pixels) return;
pxl8_sdl3_context* ctx = (pxl8_sdl3_context*)platform_data; pxl8_sdl3_context* ctx = (pxl8_sdl3_context*)platform_data;
size_t pixel_count = w * h; size_t needed_size = w * h;
if (bpp == 4) { if (ctx->rgba_buffer_size < needed_size) {
SDL_UpdateTexture(ctx->framebuffer, NULL, pixels, w * 4); u32* new_buffer = (u32*)SDL_realloc(ctx->rgba_buffer, needed_size * 4);
return;
}
if (ctx->rgba_buffer_size < pixel_count) {
u32* new_buffer = (u32*)SDL_realloc(ctx->rgba_buffer, pixel_count * 4);
if (!new_buffer) return; if (!new_buffer) return;
ctx->rgba_buffer = new_buffer; ctx->rgba_buffer = new_buffer;
ctx->rgba_buffer_size = pixel_count; ctx->rgba_buffer_size = needed_size;
} }
if (bpp == 2) { if (bpp == 2) {
const u16* pixels16 = (const u16*)pixels; const u16* pixels16 = (const u16*)pixels;
for (u32 i = 0; i < pixel_count; i++) { for (u32 i = 0; i < w * h; i++) {
ctx->rgba_buffer[i] = pxl8_rgb565_to_rgba32(pixels16[i]); ctx->rgba_buffer[i] = pxl8_rgb565_to_rgba32(pixels16[i]);
} }
} else { } else {
const u8* pixels8 = (const u8*)pixels; for (u32 i = 0; i < w * h; i++) {
for (u32 i = 0; i < pixel_count; i++) { ctx->rgba_buffer[i] = palette[pixels[i]];
ctx->rgba_buffer[i] = palette[pixels8[i]];
} }
} }

View file

@ -1,14 +1,11 @@
local anim = require("pxl8.anim") local anim = require("pxl8.anim")
local bytes = require("pxl8.bytes")
local core = require("pxl8.core") local core = require("pxl8.core")
local gfx2d = require("pxl8.gfx2d") local gfx2d = require("pxl8.gfx2d")
local gfx3d = require("pxl8.gfx3d") local gfx3d = require("pxl8.gfx3d")
local gui = require("pxl8.gui") local gui = require("pxl8.gui")
local input = require("pxl8.input") local input = require("pxl8.input")
local math = require("pxl8.math") local math3d = require("pxl8.math")
local net = require("pxl8.net")
local particles = require("pxl8.particles") local particles = require("pxl8.particles")
local procgen = require("pxl8.procgen")
local sfx = require("pxl8.sfx") local sfx = require("pxl8.sfx")
local tilemap = require("pxl8.tilemap") local tilemap = require("pxl8.tilemap")
local transition = require("pxl8.transition") local transition = require("pxl8.transition")
@ -28,20 +25,15 @@ pxl8.debug = core.debug
pxl8.trace = core.trace pxl8.trace = core.trace
pxl8.quit = core.quit pxl8.quit = core.quit
pxl8.hash32 = math.hash32
pxl8.rng_f32 = core.rng_f32
pxl8.rng_next = core.rng_next
pxl8.rng_range = core.rng_range
pxl8.rng_seed = core.rng_seed pxl8.rng_seed = core.rng_seed
pxl8.rng_next = core.rng_next
pxl8.rng_f32 = core.rng_f32
pxl8.rng_range = core.rng_range
pxl8.find_color = core.find_color pxl8.find_color = core.find_color
pxl8.palette_color = core.palette_color pxl8.palette_color = core.palette_color
pxl8.palette_index = core.palette_index pxl8.palette_index = core.palette_index
pxl8.ramp_index = core.ramp_index pxl8.ramp_index = core.ramp_index
pxl8.set_colormap = core.set_colormap
pxl8.set_palette = core.set_palette
pxl8.set_palette_rgb = core.set_palette_rgb
pxl8.update_palette_deps = core.update_palette_deps
pxl8.clear = gfx2d.clear pxl8.clear = gfx2d.clear
pxl8.pixel = gfx2d.pixel pxl8.pixel = gfx2d.pixel
@ -79,96 +71,87 @@ pxl8.center_cursor = input.center_cursor
pxl8.set_cursor = input.set_cursor pxl8.set_cursor = input.set_cursor
pxl8.set_relative_mouse_mode = input.set_relative_mouse_mode pxl8.set_relative_mouse_mode = input.set_relative_mouse_mode
pxl8.Anim = anim.Anim pxl8.Particles = particles.Particles
pxl8.create_anim = anim.Anim.new pxl8.create_particles = function(max_count) return particles.Particles.new(max_count) end
pxl8.create_anim_from_ase = anim.Anim.from_ase
pxl8.bounds = math.bounds pxl8.Tilesheet = tilemap.Tilesheet
pxl8.Tilemap = tilemap.Tilemap
pxl8.create_tilesheet = function(tile_size) return tilemap.Tilesheet.new(tile_size) end
pxl8.create_tilemap = function(w, h, tile_size) return tilemap.Tilemap.new(w, h, tile_size) end
pxl8.TILE_FLIP_X = tilemap.TILE_FLIP_X
pxl8.TILE_FLIP_Y = tilemap.TILE_FLIP_Y
pxl8.TILE_SOLID = tilemap.TILE_SOLID
pxl8.TILE_TRIGGER = tilemap.TILE_TRIGGER
pxl8.Camera3D = gfx3d.Camera3D pxl8.Camera3D = gfx3d.Camera3D
pxl8.create_camera_3d = gfx3d.Camera3D.new pxl8.create_camera_3d = function() return gfx3d.Camera3D.new() end
pxl8.begin_frame_3d = gfx3d.begin_frame pxl8.begin_frame_3d = gfx3d.begin_frame
pxl8.end_frame_3d = gfx3d.end_frame
pxl8.clear_3d = gfx3d.clear pxl8.clear_3d = gfx3d.clear
pxl8.clear_depth = gfx3d.clear_depth pxl8.clear_depth = gfx3d.clear_depth
pxl8.draw_line_3d = gfx3d.draw_line pxl8.draw_line_3d = gfx3d.draw_line
pxl8.draw_mesh = gfx3d.draw_mesh
pxl8.end_frame_3d = gfx3d.end_frame
pxl8.Mesh = gfx3d.Mesh pxl8.Mesh = gfx3d.Mesh
pxl8.create_mesh = gfx3d.Mesh.new pxl8.create_mesh = function(vertices, indices) return gfx3d.Mesh.new(vertices, indices) end
pxl8.draw_mesh = gfx3d.draw_mesh
pxl8.Compressor = sfx.Compressor pxl8.mat4_identity = math3d.mat4_identity
pxl8.create_compressor = sfx.Compressor.new pxl8.mat4_multiply = math3d.mat4_multiply
pxl8.Delay = sfx.Delay pxl8.mat4_translate = math3d.mat4_translate
pxl8.create_delay = sfx.Delay.new pxl8.mat4_rotate_x = math3d.mat4_rotate_x
pxl8.Reverb = sfx.Reverb pxl8.mat4_rotate_y = math3d.mat4_rotate_y
pxl8.create_reverb = sfx.Reverb.new pxl8.mat4_rotate_z = math3d.mat4_rotate_z
pxl8.mat4_scale = math3d.mat4_scale
pxl8.mat4_ortho = math3d.mat4_ortho
pxl8.mat4_perspective = math3d.mat4_perspective
pxl8.mat4_lookat = math3d.mat4_lookat
pxl8.bounds = math3d.bounds
pxl8.Gui = gui.Gui pxl8.Gui = gui.Gui
pxl8.create_gui = gui.Gui.new pxl8.create_gui = function() return gui.Gui.new() end
pxl8.gui_label = gui.label pxl8.gui_label = gui.label
pxl8.gui_window = gui.window pxl8.gui_window = gui.window
pxl8.mat4_identity = math.mat4_identity pxl8.World = world.World
pxl8.mat4_lookat = math.mat4_lookat pxl8.create_world = function() return world.World.new() end
pxl8.mat4_multiply = math.mat4_multiply pxl8.procgen_tex = world.procgen_tex
pxl8.mat4_ortho = math.mat4_ortho
pxl8.mat4_perspective = math.mat4_perspective
pxl8.mat4_rotate_x = math.mat4_rotate_x
pxl8.mat4_rotate_y = math.mat4_rotate_y
pxl8.mat4_rotate_z = math.mat4_rotate_z
pxl8.mat4_scale = math.mat4_scale
pxl8.mat4_translate = math.mat4_translate
pxl8.Net = net.Net
pxl8.create_net = net.Net.new
pxl8.NET_MODE_LOCAL = net.MODE_LOCAL
pxl8.NET_MODE_REMOTE = net.MODE_REMOTE
pxl8.pack_f32_be = bytes.pack_f32_be
pxl8.pack_f32_le = bytes.pack_f32_le
pxl8.pack_f64_be = bytes.pack_f64_be
pxl8.pack_f64_le = bytes.pack_f64_le
pxl8.pack_i8 = bytes.pack_i8
pxl8.pack_i16_be = bytes.pack_i16_be
pxl8.pack_i16_le = bytes.pack_i16_le
pxl8.pack_i32_be = bytes.pack_i32_be
pxl8.pack_i32_le = bytes.pack_i32_le
pxl8.pack_i64_be = bytes.pack_i64_be
pxl8.pack_i64_le = bytes.pack_i64_le
pxl8.pack_u8 = bytes.pack_u8
pxl8.pack_u16_be = bytes.pack_u16_be
pxl8.pack_u16_le = bytes.pack_u16_le
pxl8.pack_u32_be = bytes.pack_u32_be
pxl8.pack_u32_le = bytes.pack_u32_le
pxl8.pack_u64_be = bytes.pack_u64_be
pxl8.pack_u64_le = bytes.pack_u64_le
pxl8.Particles = particles.Particles
pxl8.create_particles = particles.Particles.new
pxl8.Graph = procgen.Graph
pxl8.create_graph = procgen.create_graph
pxl8.PROCGEN_ROOMS = world.PROCGEN_ROOMS pxl8.PROCGEN_ROOMS = world.PROCGEN_ROOMS
pxl8.PROCGEN_TERRAIN = world.PROCGEN_TERRAIN pxl8.PROCGEN_TERRAIN = world.PROCGEN_TERRAIN
pxl8.Transition = transition.Transition
pxl8.create_transition = function(type_name, duration) return transition.Transition.new(type_name, duration) end
pxl8.TRANSITION_TYPES = transition.TYPES
pxl8.Anim = anim.Anim
pxl8.create_anim = function(frame_ids, frame_durations) return anim.Anim.new(frame_ids, frame_durations) end
pxl8.create_anim_from_ase = function(filepath) return anim.Anim.from_ase(filepath) end
pxl8.SfxContext = sfx.SfxContext pxl8.SfxContext = sfx.SfxContext
pxl8.SfxNode = sfx.SfxNode pxl8.SfxNode = sfx.SfxNode
pxl8.create_sfx_context = sfx.SfxContext.new pxl8.Compressor = sfx.Compressor
pxl8.Delay = sfx.Delay
pxl8.Reverb = sfx.Reverb
pxl8.create_sfx_context = function() return sfx.SfxContext.new() end
pxl8.create_compressor = function(opts) return sfx.Compressor.new(opts) end
pxl8.create_delay = function(opts) return sfx.Delay.new(opts) end
pxl8.create_reverb = function(opts) return sfx.Reverb.new(opts) end
pxl8.sfx_get_master_volume = sfx.get_master_volume pxl8.sfx_get_master_volume = sfx.get_master_volume
pxl8.sfx_note_to_freq = sfx.note_to_freq
pxl8.sfx_set_master_volume = sfx.set_master_volume pxl8.sfx_set_master_volume = sfx.set_master_volume
pxl8.sfx_note_to_freq = sfx.note_to_freq
pxl8.sfx_voice_params = sfx.voice_params pxl8.sfx_voice_params = sfx.voice_params
pxl8.SFX_FILTER_BANDPASS = sfx.FILTER_BANDPASS pxl8.SFX_FILTER_BANDPASS = sfx.FILTER_BANDPASS
pxl8.SFX_FILTER_HIGHPASS = sfx.FILTER_HIGHPASS pxl8.SFX_FILTER_HIGHPASS = sfx.FILTER_HIGHPASS
pxl8.SFX_FILTER_LOWPASS = sfx.FILTER_LOWPASS pxl8.SFX_FILTER_LOWPASS = sfx.FILTER_LOWPASS
pxl8.SFX_FILTER_NONE = sfx.FILTER_NONE pxl8.SFX_FILTER_NONE = sfx.FILTER_NONE
pxl8.SFX_LFO_AMPLITUDE = sfx.LFO_AMPLITUDE pxl8.SFX_LFO_AMPLITUDE = sfx.LFO_AMPLITUDE
pxl8.SFX_LFO_FILTER = sfx.LFO_FILTER pxl8.SFX_LFO_FILTER = sfx.LFO_FILTER
pxl8.SFX_LFO_PITCH = sfx.LFO_PITCH pxl8.SFX_LFO_PITCH = sfx.LFO_PITCH
pxl8.SFX_NODE_COMPRESSOR = sfx.NODE_COMPRESSOR pxl8.SFX_NODE_COMPRESSOR = sfx.NODE_COMPRESSOR
pxl8.SFX_NODE_DELAY = sfx.NODE_DELAY pxl8.SFX_NODE_DELAY = sfx.NODE_DELAY
pxl8.SFX_NODE_REVERB = sfx.NODE_REVERB pxl8.SFX_NODE_REVERB = sfx.NODE_REVERB
pxl8.SFX_WAVE_NOISE = sfx.WAVE_NOISE pxl8.SFX_WAVE_NOISE = sfx.WAVE_NOISE
pxl8.SFX_WAVE_PULSE = sfx.WAVE_PULSE pxl8.SFX_WAVE_PULSE = sfx.WAVE_PULSE
pxl8.SFX_WAVE_SAW = sfx.WAVE_SAW pxl8.SFX_WAVE_SAW = sfx.WAVE_SAW
@ -176,39 +159,4 @@ pxl8.SFX_WAVE_SINE = sfx.WAVE_SINE
pxl8.SFX_WAVE_SQUARE = sfx.WAVE_SQUARE pxl8.SFX_WAVE_SQUARE = sfx.WAVE_SQUARE
pxl8.SFX_WAVE_TRIANGLE = sfx.WAVE_TRIANGLE pxl8.SFX_WAVE_TRIANGLE = sfx.WAVE_TRIANGLE
pxl8.TILE_FLIP_X = tilemap.TILE_FLIP_X
pxl8.TILE_FLIP_Y = tilemap.TILE_FLIP_Y
pxl8.TILE_SOLID = tilemap.TILE_SOLID
pxl8.TILE_TRIGGER = tilemap.TILE_TRIGGER
pxl8.Tilemap = tilemap.Tilemap
pxl8.create_tilemap = tilemap.Tilemap.new
pxl8.Tilesheet = tilemap.Tilesheet
pxl8.create_tilesheet = tilemap.Tilesheet.new
pxl8.Transition = transition.Transition
pxl8.create_transition = transition.Transition.new
pxl8.TRANSITION_TYPES = transition.TYPES
pxl8.unpack_f32_be = bytes.unpack_f32_be
pxl8.unpack_f32_le = bytes.unpack_f32_le
pxl8.unpack_f64_be = bytes.unpack_f64_be
pxl8.unpack_f64_le = bytes.unpack_f64_le
pxl8.unpack_i8 = bytes.unpack_i8
pxl8.unpack_i16_be = bytes.unpack_i16_be
pxl8.unpack_i16_le = bytes.unpack_i16_le
pxl8.unpack_i32_be = bytes.unpack_i32_be
pxl8.unpack_i32_le = bytes.unpack_i32_le
pxl8.unpack_i64_be = bytes.unpack_i64_be
pxl8.unpack_i64_le = bytes.unpack_i64_le
pxl8.unpack_u8 = bytes.unpack_u8
pxl8.unpack_u16_be = bytes.unpack_u16_be
pxl8.unpack_u16_le = bytes.unpack_u16_le
pxl8.unpack_u32_be = bytes.unpack_u32_be
pxl8.unpack_u32_le = bytes.unpack_u32_le
pxl8.unpack_u64_be = bytes.unpack_u64_be
pxl8.unpack_u64_le = bytes.unpack_u64_le
pxl8.World = world.World
pxl8.create_world = world.World.new
return pxl8 return pxl8

View file

@ -20,40 +20,19 @@ function core.get_fps()
end end
function core.palette_color(index) function core.palette_color(index)
local pal = C.pxl8_gfx_palette(core.gfx) local pal = C.pxl8_gfx_get_palette(core.gfx)
if pal == nil then return 0 end if pal == nil then return 0 end
return C.pxl8_palette_color(pal, index) return C.pxl8_palette_color(pal, index)
end end
function core.palette_index(color) function core.palette_index(color)
local pal = C.pxl8_gfx_palette(core.gfx) local pal = C.pxl8_gfx_get_palette(core.gfx)
if pal == nil then return -1 end if pal == nil then return -1 end
return C.pxl8_palette_index(pal, color) return C.pxl8_palette_index(pal, color)
end end
function core.set_palette_rgb(index, r, g, b)
local pal = C.pxl8_gfx_palette(core.gfx)
if pal == nil then return end
C.pxl8_palette_set_rgb(pal, index, r, g, b)
end
function core.set_palette(colors, count)
C.pxl8_gfx_set_palette_colors(core.gfx, colors, count)
end
function core.set_colormap(data, size)
local cm = C.pxl8_gfx_colormap(core.gfx)
if cm == nil then return end
C.pxl8_set_colormap(cm, data, size)
end
function core.update_palette_deps()
C.pxl8_gfx_blend_tables_update(core.gfx)
C.pxl8_gfx_colormap_update(core.gfx)
end
function core.ramp_index(position) function core.ramp_index(position)
local pal = C.pxl8_gfx_palette(core.gfx) local pal = C.pxl8_gfx_get_palette(core.gfx)
if pal == nil then return 0 end if pal == nil then return 0 end
return C.pxl8_palette_ramp_index(pal, position) return C.pxl8_palette_ramp_index(pal, position)
end end

View file

@ -42,14 +42,6 @@ function Camera3D:get_up()
return {v.x, v.y, v.z} return {v.x, v.y, v.z}
end end
function Camera3D:get_view()
return C.pxl8_3d_camera_get_view(self._ptr)
end
function Camera3D:get_projection()
return C.pxl8_3d_camera_get_projection(self._ptr)
end
function Camera3D:lookat(eye, target, up) function Camera3D:lookat(eye, target, up)
up = up or {0, 1, 0} up = up or {0, 1, 0}
local eye_vec = ffi.new("pxl8_vec3", {x = eye[1], y = eye[2], z = eye[3]}) local eye_vec = ffi.new("pxl8_vec3", {x = eye[1], y = eye[2], z = eye[3]})
@ -75,15 +67,6 @@ function Camera3D:update(dt)
C.pxl8_3d_camera_update(self._ptr, dt) C.pxl8_3d_camera_update(self._ptr, dt)
end end
function Camera3D:world_to_screen(x, y, z, width, height)
local pos = ffi.new("pxl8_vec3", {x = x, y = y, z = z})
local result = C.pxl8_3d_camera_world_to_screen(self._ptr, pos, width, height)
if result.visible then
return {x = result.x, y = result.y, depth = result.depth}
end
return nil
end
gfx3d.Camera3D = Camera3D gfx3d.Camera3D = Camera3D
local Mesh = {} local Mesh = {}
@ -131,16 +114,8 @@ gfx3d.Mesh = Mesh
function gfx3d.draw_mesh(mesh, opts) function gfx3d.draw_mesh(mesh, opts)
opts = opts or {} opts = opts or {}
local model = ffi.new("pxl8_mat4") local model = C.pxl8_mat4_identity()
local s = opts.scale or 1 local material = ffi.new("pxl8_material", {
model.m[0] = s
model.m[5] = s
model.m[10] = s
model.m[15] = 1
if opts.x then model.m[12] = opts.x end
if opts.y then model.m[13] = opts.y end
if opts.z then model.m[14] = opts.z end
local material = ffi.new("pxl8_gfx_material", {
texture_id = opts.texture or 0, texture_id = opts.texture or 0,
alpha = opts.alpha or 255, alpha = opts.alpha or 255,
blend_mode = opts.blend_mode or 0, blend_mode = opts.blend_mode or 0,
@ -149,7 +124,6 @@ function gfx3d.draw_mesh(mesh, opts)
dynamic_lighting = opts.lighting or false, dynamic_lighting = opts.lighting or false,
per_pixel = opts.per_pixel or false, per_pixel = opts.per_pixel or false,
vertex_color_passthrough = opts.passthrough or false, vertex_color_passthrough = opts.passthrough or false,
wireframe = opts.wireframe or false,
emissive_intensity = opts.emissive or 0.0, emissive_intensity = opts.emissive or 0.0,
}) })
C.pxl8_3d_draw_mesh(core.gfx, mesh._ptr, model, material) C.pxl8_3d_draw_mesh(core.gfx, mesh._ptr, model, material)
@ -157,44 +131,12 @@ end
function gfx3d.begin_frame(camera, uniforms) function gfx3d.begin_frame(camera, uniforms)
uniforms = uniforms or {} uniforms = uniforms or {}
local u = ffi.new("pxl8_3d_uniforms") local u = ffi.new("pxl8_3d_uniforms", {
ambient = uniforms.ambient or 0,
u.ambient = uniforms.ambient or 0 fog_color = uniforms.fog_color or 0,
u.fog_color = uniforms.fog_color or 0 fog_density = uniforms.fog_density or 0.0,
u.fog_density = uniforms.fog_density or 0.0 time = uniforms.time or 0.0,
u.time = uniforms.time or 0.0 })
if uniforms.celestial_dir then
u.celestial_dir.x = uniforms.celestial_dir[1] or 0
u.celestial_dir.y = uniforms.celestial_dir[2] or -1
u.celestial_dir.z = uniforms.celestial_dir[3] or 0
else
u.celestial_dir.x = 0
u.celestial_dir.y = -1
u.celestial_dir.z = 0
end
u.celestial_intensity = uniforms.celestial_intensity or 0.0
u.num_lights = 0
if uniforms.lights then
for i, light in ipairs(uniforms.lights) do
if i > 16 then break end
local idx = i - 1
u.lights[idx].position.x = light.x or 0
u.lights[idx].position.y = light.y or 0
u.lights[idx].position.z = light.z or 0
u.lights[idx].r = light.r or 255
u.lights[idx].g = light.g or 255
u.lights[idx].b = light.b or 255
u.lights[idx].intensity = light.intensity or 255
u.lights[idx].radius = light.radius or 100
local radius_sq = u.lights[idx].radius * u.lights[idx].radius
u.lights[idx].radius_sq = radius_sq
u.lights[idx].inv_radius_sq = radius_sq > 0 and (1.0 / radius_sq) or 0
u.num_lights = i
end
end
C.pxl8_3d_begin_frame(core.gfx, camera._ptr, u) C.pxl8_3d_begin_frame(core.gfx, camera._ptr, u)
end end

View file

@ -16,7 +16,7 @@ function Gui.new()
end end
function Gui:begin_frame() function Gui:begin_frame()
C.pxl8_gui_begin_frame(self._ptr, core.gfx) C.pxl8_gui_begin_frame(self._ptr)
end end
function Gui:button(id, x, y, w, h, label) function Gui:button(id, x, y, w, h, label)
@ -43,7 +43,7 @@ function Gui:destroy()
end end
function Gui:end_frame() function Gui:end_frame()
C.pxl8_gui_end_frame(self._ptr, core.gfx) C.pxl8_gui_end_frame(self._ptr)
end end
function Gui:get_cursor_pos() function Gui:get_cursor_pos()

View file

@ -1,57 +1,53 @@
local ffi = require("ffi") local ffi = require("ffi")
local C = ffi.C local C = ffi.C
local math = {} local math3d = {}
function math.hash32(x) function math3d.mat4_identity()
return C.pxl8_hash32(x)
end
function math.mat4_identity()
return C.pxl8_mat4_identity() return C.pxl8_mat4_identity()
end end
function math.mat4_mul(a, b) function math3d.mat4_mul(a, b)
return C.pxl8_mat4_mul(a, b) return C.pxl8_mat4_mul(a, b)
end end
function math.mat4_translate(x, y, z) function math3d.mat4_translate(x, y, z)
return C.pxl8_mat4_translate(x, y, z) return C.pxl8_mat4_translate(x, y, z)
end end
function math.mat4_rotate_x(angle) function math3d.mat4_rotate_x(angle)
return C.pxl8_mat4_rotate_x(angle) return C.pxl8_mat4_rotate_x(angle)
end end
function math.mat4_rotate_y(angle) function math3d.mat4_rotate_y(angle)
return C.pxl8_mat4_rotate_y(angle) return C.pxl8_mat4_rotate_y(angle)
end end
function math.mat4_rotate_z(angle) function math3d.mat4_rotate_z(angle)
return C.pxl8_mat4_rotate_z(angle) return C.pxl8_mat4_rotate_z(angle)
end end
function math.mat4_scale(x, y, z) function math3d.mat4_scale(x, y, z)
return C.pxl8_mat4_scale(x, y, z) return C.pxl8_mat4_scale(x, y, z)
end end
function math.mat4_ortho(left, right, bottom, top, near, far) function math3d.mat4_ortho(left, right, bottom, top, near, far)
return C.pxl8_mat4_ortho(left, right, bottom, top, near, far) return C.pxl8_mat4_ortho(left, right, bottom, top, near, far)
end end
function math.mat4_perspective(fov, aspect, near, far) function math3d.mat4_perspective(fov, aspect, near, far)
return C.pxl8_mat4_perspective(fov, aspect, near, far) return C.pxl8_mat4_perspective(fov, aspect, near, far)
end end
function math.mat4_lookat(eye, center, up) function math3d.mat4_lookat(eye, center, up)
local eye_vec = ffi.new("pxl8_vec3", {x = eye[1], y = eye[2], z = eye[3]}) local eye_vec = ffi.new("pxl8_vec3", {x = eye[1], y = eye[2], z = eye[3]})
local center_vec = ffi.new("pxl8_vec3", {x = center[1], y = center[2], z = center[3]}) local center_vec = ffi.new("pxl8_vec3", {x = center[1], y = center[2], z = center[3]})
local up_vec = ffi.new("pxl8_vec3", {x = up[1], y = up[2], z = up[3]}) local up_vec = ffi.new("pxl8_vec3", {x = up[1], y = up[2], z = up[3]})
return C.pxl8_mat4_lookat(eye_vec, center_vec, up_vec) return C.pxl8_mat4_lookat(eye_vec, center_vec, up_vec)
end end
function math.bounds(x, y, w, h) function math3d.bounds(x, y, w, h)
return ffi.new("pxl8_bounds", {x = x, y = y, w = w, h = h}) return ffi.new("pxl8_bounds", {x = x, y = y, w = w, h = h})
end end
return math return math3d

View file

@ -12,7 +12,7 @@ function Particles.new(max_count)
if ps == nil then if ps == nil then
return nil return nil
end end
local pal = C.pxl8_gfx_palette(core.gfx) local pal = C.pxl8_gfx_get_palette(core.gfx)
if pal ~= nil then if pal ~= nil then
C.pxl8_particles_set_palette(ps, pal) C.pxl8_particles_set_palette(ps, pal)
end end

View file

@ -200,7 +200,9 @@ function sfx.get_master_volume()
return C.pxl8_sfx_mixer_get_master_volume(core.sfx) return C.pxl8_sfx_mixer_get_master_volume(core.sfx)
end end
sfx.note_to_freq = C.pxl8_sfx_note_to_freq function sfx.note_to_freq(note)
return C.pxl8_sfx_note_to_freq(note)
end
function sfx.set_master_volume(volume) function sfx.set_master_volume(volume)
C.pxl8_sfx_mixer_set_master_volume(core.sfx, volume) C.pxl8_sfx_mixer_set_master_volume(core.sfx, volume)

View file

@ -0,0 +1,65 @@
local ffi = require("ffi")
local C = ffi.C
local core = require("pxl8.core")
local vfx = {}
function vfx.raster_bars(bars, time)
local c_bars = ffi.new("pxl8_raster_bar[?]", #bars)
for i, bar in ipairs(bars) do
c_bars[i-1].base_y = bar.base_y or 0
c_bars[i-1].amplitude = bar.amplitude or 10
c_bars[i-1].height = bar.height or 5
c_bars[i-1].speed = bar.speed or 1.0
c_bars[i-1].phase = bar.phase or 0
c_bars[i-1].color = bar.color or 15
c_bars[i-1].fade_color = bar.fade_color or bar.color or 15
end
C.pxl8_vfx_raster_bars(core.gfx, c_bars, #bars, time)
end
function vfx.plasma(time, scale1, scale2, palette_offset)
C.pxl8_vfx_plasma(core.gfx, time, scale1 or 0.05, scale2 or 0.03, palette_offset or 0)
end
function vfx.rotozoom(angle, zoom, cx, cy)
local width = C.pxl8_gfx_get_width(core.gfx)
local height = C.pxl8_gfx_get_height(core.gfx)
C.pxl8_vfx_rotozoom(core.gfx, angle, zoom, cx or width/2, cy or height/2)
end
function vfx.tunnel(time, speed, twist)
C.pxl8_vfx_tunnel(core.gfx, time, speed or 2.0, twist or 0.5)
end
function vfx.explosion(ps, x, y, color, force)
C.pxl8_vfx_explosion(ps, x, y, color or 15, force or 200.0)
end
function vfx.fire(ps, x, y, width, palette_start)
C.pxl8_vfx_fire(ps, x, y, width or 50, palette_start or 64)
end
function vfx.rain(ps, width, wind)
local w = width or C.pxl8_gfx_get_width(core.gfx)
C.pxl8_vfx_rain(ps, w, wind or 0.0)
end
function vfx.smoke(ps, x, y, color)
C.pxl8_vfx_smoke(ps, x, y, color or 8)
end
function vfx.snow(ps, width, wind)
local w = width or C.pxl8_gfx_get_width(core.gfx)
C.pxl8_vfx_snow(ps, w, wind or 10.0)
end
function vfx.sparks(ps, x, y, color)
C.pxl8_vfx_sparks(ps, x, y, color or 15)
end
function vfx.starfield(ps, speed, spread)
C.pxl8_vfx_starfield(ps, speed or 5.0, spread or 500.0)
end
return vfx

View file

@ -85,14 +85,36 @@ function World:resolve_collision(from_x, from_y, from_z, to_x, to_y, to_z, radiu
return result.x, result.y, result.z return result.x, result.y, result.z
end end
function World:set_wireframe(enabled, color)
C.pxl8_world_set_wireframe(self._ptr, enabled, color or 15)
end
function World:unload() function World:unload()
C.pxl8_world_unload(self._ptr) C.pxl8_world_unload(self._ptr)
end end
world.World = World world.World = World
function world.procgen_tex(params)
local width = params.width or 64
local height = params.height or 64
local buffer = ffi.new("u8[?]", width * height)
local tex_params = ffi.new("pxl8_procgen_tex_params")
local name = params.name or ""
ffi.copy(tex_params.name, name, math.min(#name, 15))
tex_params.seed = params.seed or 0
tex_params.width = width
tex_params.height = height
tex_params.scale = params.scale or 1.0
tex_params.roughness = params.roughness or 0.0
tex_params.base_color = params.base_color or 0
tex_params.variation = params.variation or 0
C.pxl8_procgen_tex(buffer, tex_params)
local tex_id = C.pxl8_gfx_create_texture(core.gfx, buffer, width, height)
if tex_id < 0 then
return nil
end
return tex_id
end
return world return world

View file

@ -1,14 +1,5 @@
#include "pxl8_math.h" #include "pxl8_math.h"
u32 pxl8_hash32(u32 x) {
x ^= x >> 16;
x *= 0x85EBCA6Bu;
x ^= x >> 13;
x *= 0xC2B2AE35u;
x ^= x >> 16;
return x;
}
pxl8_vec2 pxl8_vec2_add(pxl8_vec2 a, pxl8_vec2 b) { pxl8_vec2 pxl8_vec2_add(pxl8_vec2 a, pxl8_vec2 b) {
return (pxl8_vec2){ return (pxl8_vec2){
.x = a.x + b.x, .x = a.x + b.x,
@ -135,14 +126,6 @@ pxl8_vec4 pxl8_mat4_mul_vec4(pxl8_mat4 m, pxl8_vec4 v) {
}; };
} }
pxl8_vec3 pxl8_mat4_mul_vec3(pxl8_mat4 m, pxl8_vec3 v) {
return (pxl8_vec3){
.x = m.m[0] * v.x + m.m[4] * v.y + m.m[8] * v.z,
.y = m.m[1] * v.x + m.m[5] * v.y + m.m[9] * v.z,
.z = m.m[2] * v.x + m.m[6] * v.y + m.m[10] * v.z,
};
}
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();

View file

@ -4,51 +4,9 @@
#include "pxl8_types.h" #include "pxl8_types.h"
#if defined(__x86_64__) || defined(_M_X64)
#include <xmmintrin.h>
#elif defined(__aarch64__) || defined(_M_ARM64)
#include <arm_neon.h>
#endif
#define PXL8_PI 3.14159265358979323846f #define PXL8_PI 3.14159265358979323846f
#define PXL8_TAU (PXL8_PI * 2.0f) #define PXL8_TAU (PXL8_PI * 2.0f)
static inline f32 pxl8_fast_inv_sqrt(f32 x) {
#if defined(__x86_64__) || defined(_M_X64)
__m128 v = _mm_set_ss(x);
v = _mm_rsqrt_ss(v);
return _mm_cvtss_f32(v);
#elif defined(__aarch64__) || defined(_M_ARM64)
float32x2_t v = vdup_n_f32(x);
float32x2_t est = vrsqrte_f32(v);
est = vmul_f32(est, vrsqrts_f32(vmul_f32(v, est), est));
return vget_lane_f32(est, 0);
#else
f32 half = 0.5f * x;
i32 i = *(i32*)&x;
i = 0x5f3759df - (i >> 1);
x = *(f32*)&i;
x = x * (1.5f - half * x * x);
return x;
#endif
}
static inline f32 pxl8_fast_rcp(f32 x) {
#if defined(__x86_64__) || defined(_M_X64)
__m128 v = _mm_set_ss(x);
__m128 rcp = _mm_rcp_ss(v);
rcp = _mm_add_ss(rcp, _mm_mul_ss(rcp, _mm_sub_ss(_mm_set_ss(1.0f), _mm_mul_ss(v, rcp))));
return _mm_cvtss_f32(rcp);
#elif defined(__aarch64__) || defined(_M_ARM64)
float32x2_t v = vdup_n_f32(x);
float32x2_t est = vrecpe_f32(v);
est = vmul_f32(est, vrecps_f32(v, est));
return vget_lane_f32(est, 0);
#else
return 1.0f / x;
#endif
}
typedef struct pxl8_vec2 { typedef struct pxl8_vec2 {
f32 x, y; f32 x, y;
} pxl8_vec2; } pxl8_vec2;
@ -66,27 +24,18 @@ typedef struct pxl8_mat4 {
} pxl8_mat4; } pxl8_mat4;
typedef struct pxl8_plane { typedef struct pxl8_plane {
f32 distance;
pxl8_vec3 normal; pxl8_vec3 normal;
f32 distance;
} pxl8_plane; } pxl8_plane;
typedef struct pxl8_frustum { typedef struct pxl8_frustum {
pxl8_plane planes[6]; pxl8_plane planes[6];
} pxl8_frustum; } pxl8_frustum;
typedef struct pxl8_projected_point {
f32 depth;
i32 x;
i32 y;
bool visible;
} pxl8_projected_point;
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
u32 pxl8_hash32(u32 x);
pxl8_vec2 pxl8_vec2_add(pxl8_vec2 a, pxl8_vec2 b); pxl8_vec2 pxl8_vec2_add(pxl8_vec2 a, pxl8_vec2 b);
f32 pxl8_vec2_dot(pxl8_vec2 a, pxl8_vec2 b); f32 pxl8_vec2_dot(pxl8_vec2 a, pxl8_vec2 b);
f32 pxl8_vec2_length(pxl8_vec2 v); f32 pxl8_vec2_length(pxl8_vec2 v);
@ -106,7 +55,6 @@ pxl8_vec3 pxl8_vec3_sub(pxl8_vec3 a, pxl8_vec3 b);
pxl8_mat4 pxl8_mat4_identity(void); pxl8_mat4 pxl8_mat4_identity(void);
pxl8_mat4 pxl8_mat4_lookat(pxl8_vec3 eye, pxl8_vec3 center, pxl8_vec3 up); pxl8_mat4 pxl8_mat4_lookat(pxl8_vec3 eye, pxl8_vec3 center, pxl8_vec3 up);
pxl8_mat4 pxl8_mat4_mul(pxl8_mat4 a, pxl8_mat4 b); pxl8_mat4 pxl8_mat4_mul(pxl8_mat4 a, pxl8_mat4 b);
pxl8_vec3 pxl8_mat4_mul_vec3(pxl8_mat4 m, pxl8_vec3 v);
pxl8_vec4 pxl8_mat4_mul_vec4(pxl8_mat4 m, pxl8_vec4 v); pxl8_vec4 pxl8_mat4_mul_vec4(pxl8_mat4 m, pxl8_vec4 v);
pxl8_mat4 pxl8_mat4_ortho(f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far); pxl8_mat4 pxl8_mat4_ortho(f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far);
pxl8_mat4 pxl8_mat4_perspective(f32 fov, f32 aspect, f32 near, f32 far); pxl8_mat4 pxl8_mat4_perspective(f32 fov, f32 aspect, f32 near, f32 far);

299
client/src/math/pxl8_simd.h Normal file
View file

@ -0,0 +1,299 @@
#pragma once
#include "pxl8_types.h"
#if defined(__x86_64__) || defined(_M_X64)
#define PXL8_SIMD_SSE2 1
#include <emmintrin.h>
#elif defined(__aarch64__) || defined(_M_ARM64)
#define PXL8_SIMD_NEON 1
#include <arm_neon.h>
#else
#define PXL8_SIMD_SCALAR 1
#endif
#ifdef __cplusplus
extern "C" {
#endif
#if defined(PXL8_SIMD_SSE2)
typedef struct { __m128 v; } pxl8_f32x4;
typedef struct { __m128i v; } pxl8_i32x4;
typedef struct { __m128i v; } pxl8_u16x8;
static inline pxl8_f32x4 pxl8_f32x4_splat(f32 x) {
return (pxl8_f32x4){ _mm_set1_ps(x) };
}
static inline pxl8_f32x4 pxl8_f32x4_new(f32 a, f32 b, f32 c, f32 d) {
return (pxl8_f32x4){ _mm_set_ps(d, c, b, a) };
}
static inline pxl8_f32x4 pxl8_f32x4_add(pxl8_f32x4 a, pxl8_f32x4 b) {
return (pxl8_f32x4){ _mm_add_ps(a.v, b.v) };
}
static inline pxl8_f32x4 pxl8_f32x4_sub(pxl8_f32x4 a, pxl8_f32x4 b) {
return (pxl8_f32x4){ _mm_sub_ps(a.v, b.v) };
}
static inline pxl8_f32x4 pxl8_f32x4_mul(pxl8_f32x4 a, pxl8_f32x4 b) {
return (pxl8_f32x4){ _mm_mul_ps(a.v, b.v) };
}
static inline pxl8_f32x4 pxl8_f32x4_div(pxl8_f32x4 a, pxl8_f32x4 b) {
return (pxl8_f32x4){ _mm_div_ps(a.v, b.v) };
}
static inline pxl8_f32x4 pxl8_f32x4_min(pxl8_f32x4 a, pxl8_f32x4 b) {
return (pxl8_f32x4){ _mm_min_ps(a.v, b.v) };
}
static inline pxl8_f32x4 pxl8_f32x4_max(pxl8_f32x4 a, pxl8_f32x4 b) {
return (pxl8_f32x4){ _mm_max_ps(a.v, b.v) };
}
static inline pxl8_f32x4 pxl8_f32x4_cmplt(pxl8_f32x4 a, pxl8_f32x4 b) {
return (pxl8_f32x4){ _mm_cmplt_ps(a.v, b.v) };
}
static inline i32 pxl8_f32x4_movemask(pxl8_f32x4 a) {
return _mm_movemask_ps(a.v);
}
static inline pxl8_i32x4 pxl8_f32x4_to_i32x4(pxl8_f32x4 a) {
return (pxl8_i32x4){ _mm_cvttps_epi32(a.v) };
}
static inline void pxl8_f32x4_store(pxl8_f32x4 a, f32* out) {
_mm_storeu_ps(out, a.v);
}
static inline pxl8_i32x4 pxl8_i32x4_splat(i32 x) {
return (pxl8_i32x4){ _mm_set1_epi32(x) };
}
static inline pxl8_i32x4 pxl8_i32x4_slli(pxl8_i32x4 a, i32 n) {
return (pxl8_i32x4){ _mm_slli_epi32(a.v, n) };
}
static inline pxl8_i32x4 pxl8_i32x4_srai(pxl8_i32x4 a, i32 n) {
return (pxl8_i32x4){ _mm_srai_epi32(a.v, n) };
}
static inline pxl8_i32x4 pxl8_i32x4_and(pxl8_i32x4 a, pxl8_i32x4 b) {
return (pxl8_i32x4){ _mm_and_si128(a.v, b.v) };
}
static inline pxl8_i32x4 pxl8_i32x4_or(pxl8_i32x4 a, pxl8_i32x4 b) {
return (pxl8_i32x4){ _mm_or_si128(a.v, b.v) };
}
static inline void pxl8_i32x4_store(pxl8_i32x4 a, i32* out) {
_mm_storeu_si128((__m128i*)out, a.v);
}
static inline pxl8_u16x8 pxl8_u16x8_cmplt(pxl8_u16x8 a, pxl8_u16x8 b) {
return (pxl8_u16x8){ _mm_cmplt_epi16(a.v, b.v) };
}
static inline pxl8_u16x8 pxl8_u16x8_blend(pxl8_u16x8 a, pxl8_u16x8 b, pxl8_u16x8 mask) {
__m128i not_mask = _mm_andnot_si128(mask.v, a.v);
__m128i and_mask = _mm_and_si128(mask.v, b.v);
return (pxl8_u16x8){ _mm_or_si128(not_mask, and_mask) };
}
static inline i32 pxl8_u16x8_movemask(pxl8_u16x8 a) {
return _mm_movemask_epi8(a.v);
}
#elif defined(PXL8_SIMD_NEON)
typedef struct { float32x4_t v; } pxl8_f32x4;
typedef struct { int32x4_t v; } pxl8_i32x4;
typedef struct { uint16x8_t v; } pxl8_u16x8;
static inline pxl8_f32x4 pxl8_f32x4_splat(f32 x) {
return (pxl8_f32x4){ vdupq_n_f32(x) };
}
static inline pxl8_f32x4 pxl8_f32x4_new(f32 a, f32 b, f32 c, f32 d) {
f32 arr[4] = {a, b, c, d};
return (pxl8_f32x4){ vld1q_f32(arr) };
}
static inline pxl8_f32x4 pxl8_f32x4_add(pxl8_f32x4 a, pxl8_f32x4 b) {
return (pxl8_f32x4){ vaddq_f32(a.v, b.v) };
}
static inline pxl8_f32x4 pxl8_f32x4_sub(pxl8_f32x4 a, pxl8_f32x4 b) {
return (pxl8_f32x4){ vsubq_f32(a.v, b.v) };
}
static inline pxl8_f32x4 pxl8_f32x4_mul(pxl8_f32x4 a, pxl8_f32x4 b) {
return (pxl8_f32x4){ vmulq_f32(a.v, b.v) };
}
static inline pxl8_f32x4 pxl8_f32x4_div(pxl8_f32x4 a, pxl8_f32x4 b) {
return (pxl8_f32x4){ vdivq_f32(a.v, b.v) };
}
static inline pxl8_f32x4 pxl8_f32x4_min(pxl8_f32x4 a, pxl8_f32x4 b) {
return (pxl8_f32x4){ vminq_f32(a.v, b.v) };
}
static inline pxl8_f32x4 pxl8_f32x4_max(pxl8_f32x4 a, pxl8_f32x4 b) {
return (pxl8_f32x4){ vmaxq_f32(a.v, b.v) };
}
static inline pxl8_f32x4 pxl8_f32x4_cmplt(pxl8_f32x4 a, pxl8_f32x4 b) {
uint32x4_t cmp = vcltq_f32(a.v, b.v);
return (pxl8_f32x4){ vreinterpretq_f32_u32(cmp) };
}
static inline i32 pxl8_f32x4_movemask(pxl8_f32x4 a) {
uint32x4_t input = vreinterpretq_u32_f32(a.v);
static const i32 shifts[4] = {0, 1, 2, 3};
uint32x4_t shifted = vshrq_n_u32(input, 31);
return vgetq_lane_u32(shifted, 0) | (vgetq_lane_u32(shifted, 1) << 1) |
(vgetq_lane_u32(shifted, 2) << 2) | (vgetq_lane_u32(shifted, 3) << 3);
}
static inline pxl8_i32x4 pxl8_f32x4_to_i32x4(pxl8_f32x4 a) {
return (pxl8_i32x4){ vcvtq_s32_f32(a.v) };
}
static inline void pxl8_f32x4_store(pxl8_f32x4 a, f32* out) {
vst1q_f32(out, a.v);
}
static inline pxl8_i32x4 pxl8_i32x4_splat(i32 x) {
return (pxl8_i32x4){ vdupq_n_s32(x) };
}
static inline pxl8_i32x4 pxl8_i32x4_slli(pxl8_i32x4 a, i32 n) {
return (pxl8_i32x4){ vshlq_s32(a.v, vdupq_n_s32(n)) };
}
static inline pxl8_i32x4 pxl8_i32x4_srai(pxl8_i32x4 a, i32 n) {
return (pxl8_i32x4){ vshlq_s32(a.v, vdupq_n_s32(-n)) };
}
static inline pxl8_i32x4 pxl8_i32x4_and(pxl8_i32x4 a, pxl8_i32x4 b) {
return (pxl8_i32x4){ vandq_s32(a.v, b.v) };
}
static inline pxl8_i32x4 pxl8_i32x4_or(pxl8_i32x4 a, pxl8_i32x4 b) {
return (pxl8_i32x4){ vorrq_s32(a.v, b.v) };
}
static inline void pxl8_i32x4_store(pxl8_i32x4 a, i32* out) {
vst1q_s32(out, a.v);
}
#else
typedef struct { f32 v[4]; } pxl8_f32x4;
typedef struct { i32 v[4]; } pxl8_i32x4;
typedef struct { u16 v[8]; } pxl8_u16x8;
static inline pxl8_f32x4 pxl8_f32x4_splat(f32 x) {
return (pxl8_f32x4){{ x, x, x, x }};
}
static inline pxl8_f32x4 pxl8_f32x4_new(f32 a, f32 b, f32 c, f32 d) {
return (pxl8_f32x4){{ a, b, c, d }};
}
static inline pxl8_f32x4 pxl8_f32x4_add(pxl8_f32x4 a, pxl8_f32x4 b) {
return (pxl8_f32x4){{ a.v[0]+b.v[0], a.v[1]+b.v[1], a.v[2]+b.v[2], a.v[3]+b.v[3] }};
}
static inline pxl8_f32x4 pxl8_f32x4_sub(pxl8_f32x4 a, pxl8_f32x4 b) {
return (pxl8_f32x4){{ a.v[0]-b.v[0], a.v[1]-b.v[1], a.v[2]-b.v[2], a.v[3]-b.v[3] }};
}
static inline pxl8_f32x4 pxl8_f32x4_mul(pxl8_f32x4 a, pxl8_f32x4 b) {
return (pxl8_f32x4){{ a.v[0]*b.v[0], a.v[1]*b.v[1], a.v[2]*b.v[2], a.v[3]*b.v[3] }};
}
static inline pxl8_f32x4 pxl8_f32x4_div(pxl8_f32x4 a, pxl8_f32x4 b) {
return (pxl8_f32x4){{ a.v[0]/b.v[0], a.v[1]/b.v[1], a.v[2]/b.v[2], a.v[3]/b.v[3] }};
}
static inline pxl8_f32x4 pxl8_f32x4_min(pxl8_f32x4 a, pxl8_f32x4 b) {
return (pxl8_f32x4){{
a.v[0]<b.v[0]?a.v[0]:b.v[0], a.v[1]<b.v[1]?a.v[1]:b.v[1],
a.v[2]<b.v[2]?a.v[2]:b.v[2], a.v[3]<b.v[3]?a.v[3]:b.v[3]
}};
}
static inline pxl8_f32x4 pxl8_f32x4_max(pxl8_f32x4 a, pxl8_f32x4 b) {
return (pxl8_f32x4){{
a.v[0]>b.v[0]?a.v[0]:b.v[0], a.v[1]>b.v[1]?a.v[1]:b.v[1],
a.v[2]>b.v[2]?a.v[2]:b.v[2], a.v[3]>b.v[3]?a.v[3]:b.v[3]
}};
}
static inline pxl8_f32x4 pxl8_f32x4_cmplt(pxl8_f32x4 a, pxl8_f32x4 b) {
pxl8_f32x4 r;
u32* rv = (u32*)r.v;
rv[0] = a.v[0] < b.v[0] ? 0xFFFFFFFF : 0;
rv[1] = a.v[1] < b.v[1] ? 0xFFFFFFFF : 0;
rv[2] = a.v[2] < b.v[2] ? 0xFFFFFFFF : 0;
rv[3] = a.v[3] < b.v[3] ? 0xFFFFFFFF : 0;
return r;
}
static inline i32 pxl8_f32x4_movemask(pxl8_f32x4 a) {
u32* av = (u32*)a.v;
return ((av[0] >> 31) & 1) | ((av[1] >> 31) & 1) << 1 |
((av[2] >> 31) & 1) << 2 | ((av[3] >> 31) & 1) << 3;
}
static inline pxl8_i32x4 pxl8_f32x4_to_i32x4(pxl8_f32x4 a) {
return (pxl8_i32x4){{ (i32)a.v[0], (i32)a.v[1], (i32)a.v[2], (i32)a.v[3] }};
}
static inline void pxl8_f32x4_store(pxl8_f32x4 a, f32* out) {
out[0] = a.v[0]; out[1] = a.v[1]; out[2] = a.v[2]; out[3] = a.v[3];
}
static inline pxl8_i32x4 pxl8_i32x4_splat(i32 x) {
return (pxl8_i32x4){{ x, x, x, x }};
}
static inline pxl8_i32x4 pxl8_i32x4_slli(pxl8_i32x4 a, i32 n) {
return (pxl8_i32x4){{ a.v[0]<<n, a.v[1]<<n, a.v[2]<<n, a.v[3]<<n }};
}
static inline pxl8_i32x4 pxl8_i32x4_srai(pxl8_i32x4 a, i32 n) {
return (pxl8_i32x4){{ a.v[0]>>n, a.v[1]>>n, a.v[2]>>n, a.v[3]>>n }};
}
static inline pxl8_i32x4 pxl8_i32x4_and(pxl8_i32x4 a, pxl8_i32x4 b) {
return (pxl8_i32x4){{ a.v[0]&b.v[0], a.v[1]&b.v[1], a.v[2]&b.v[2], a.v[3]&b.v[3] }};
}
static inline pxl8_i32x4 pxl8_i32x4_or(pxl8_i32x4 a, pxl8_i32x4 b) {
return (pxl8_i32x4){{ a.v[0]|b.v[0], a.v[1]|b.v[1], a.v[2]|b.v[2], a.v[3]|b.v[3] }};
}
static inline void pxl8_i32x4_store(pxl8_i32x4 a, i32* out) {
out[0] = a.v[0]; out[1] = a.v[1]; out[2] = a.v[2]; out[3] = a.v[3];
}
#endif
static inline f32 pxl8_fast_inv_sqrt(f32 x) {
f32 half = 0.5f * x;
i32 i = *(i32*)&x;
i = 0x5f375a86 - (i >> 1);
f32 y = *(f32*)&i;
return y * (1.5f - half * y * y);
}
#ifdef __cplusplus
}
#endif

View file

@ -1,13 +1,14 @@
#include "pxl8_repl.h" #include "pxl8_repl.h"
#include <poll.h> #include <poll.h>
#include <pthread.h>
#include <stdatomic.h> #include <stdatomic.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h>
#include <unistd.h> #include <unistd.h>
#include <SDL3/SDL.h>
#include <linenoise.h> #include <linenoise.h>
#define PXL8_MAX_REPL_COMMAND_SIZE 4096 #define PXL8_MAX_REPL_COMMAND_SIZE 4096
@ -28,7 +29,7 @@ struct pxl8_repl {
atomic_uint log_read_idx; atomic_uint log_read_idx;
atomic_bool should_quit; atomic_bool should_quit;
SDL_Thread* thread; pthread_t thread;
char accumulator[PXL8_MAX_REPL_COMMAND_SIZE]; char accumulator[PXL8_MAX_REPL_COMMAND_SIZE];
pxl8_repl_command command; pxl8_repl_command command;
}; };
@ -104,7 +105,7 @@ static void pxl8_repl_flush_logs(pxl8_repl* repl) {
fflush(stdout); fflush(stdout);
} }
static int pxl8_repl_thread(void* arg) { static void* pxl8_repl_thread(void* arg) {
pxl8_repl* repl = (pxl8_repl*)arg; pxl8_repl* repl = (pxl8_repl*)arg;
printf("[pxl8 REPL] Fennel 1.6.0 - Tab for completion, Ctrl-D to exit\n"); printf("[pxl8 REPL] Fennel 1.6.0 - Tab for completion, Ctrl-D to exit\n");
@ -203,7 +204,8 @@ static int pxl8_repl_thread(void* arg) {
lw = atomic_load(&repl->log_write_idx); lw = atomic_load(&repl->log_write_idx);
} }
fflush(stdout); fflush(stdout);
SDL_Delay(1); struct timespec ts = {.tv_sec = 0, .tv_nsec = 1000000};
nanosleep(&ts, NULL);
} }
atomic_store(&repl->cmd_complete, false); atomic_store(&repl->cmd_complete, false);
} }
@ -212,7 +214,7 @@ static int pxl8_repl_thread(void* arg) {
pxl8_repl_flush_logs(repl); pxl8_repl_flush_logs(repl);
return 0; return NULL;
} }
pxl8_repl* pxl8_repl_create(void) { pxl8_repl* pxl8_repl_create(void) {
@ -235,8 +237,7 @@ pxl8_repl* pxl8_repl_create(void) {
g_repl = repl; g_repl = repl;
repl->thread = SDL_CreateThread(pxl8_repl_thread, "pxl8-repl", repl); if (pthread_create(&repl->thread, NULL, pxl8_repl_thread, repl) != 0) {
if (!repl->thread) {
free(repl); free(repl);
g_repl = NULL; g_repl = NULL;
return NULL; return NULL;
@ -250,12 +251,13 @@ void pxl8_repl_destroy(pxl8_repl* repl) {
atomic_store(&repl->should_quit, true); atomic_store(&repl->should_quit, true);
SDL_Delay(2); struct timespec ts = {.tv_sec = 0, .tv_nsec = 2000000};
nanosleep(&ts, NULL);
printf("\r\033[K"); printf("\r\033[K");
fflush(stdout); fflush(stdout);
SDL_WaitThread(repl->thread, NULL); pthread_join(repl->thread, NULL);
pxl8_repl_flush_logs(repl); pxl8_repl_flush_logs(repl);
g_repl = NULL; g_repl = NULL;

View file

@ -27,16 +27,10 @@ static const char* pxl8_ffi_cdefs =
"u8 pxl8_gfx_find_color(pxl8_gfx* gfx, u32 color);\n" "u8 pxl8_gfx_find_color(pxl8_gfx* gfx, u32 color);\n"
"i32 pxl8_gfx_get_height(pxl8_gfx* ctx);\n" "i32 pxl8_gfx_get_height(pxl8_gfx* ctx);\n"
"typedef struct pxl8_palette pxl8_palette;\n" "typedef struct pxl8_palette pxl8_palette;\n"
"pxl8_palette* pxl8_gfx_palette(pxl8_gfx* gfx);\n" "pxl8_palette* pxl8_gfx_get_palette(pxl8_gfx* gfx);\n"
"u32 pxl8_palette_color(const pxl8_palette* pal, u8 idx);\n" "u32 pxl8_palette_color(const pxl8_palette* pal, u8 idx);\n"
"void pxl8_palette_set_rgb(pxl8_palette* pal, u8 idx, u8 r, u8 g, u8 b);\n"
"void pxl8_gfx_set_palette_colors(pxl8_gfx* gfx, const u32* colors, u16 count);\n"
"i32 pxl8_palette_index(const pxl8_palette* pal, u32 color);\n" "i32 pxl8_palette_index(const pxl8_palette* pal, u32 color);\n"
"u8 pxl8_palette_ramp_index(const pxl8_palette* pal, u8 position);\n" "u8 pxl8_palette_ramp_index(const pxl8_palette* pal, u8 position);\n"
"typedef struct pxl8_colormap pxl8_colormap;\n"
"pxl8_colormap* pxl8_gfx_colormap(pxl8_gfx* gfx);\n"
"void pxl8_set_colormap(pxl8_colormap* cm, const u8* data, u32 size);\n"
"void pxl8_gfx_ensure_blend_tables(pxl8_gfx* gfx);\n"
"i32 pxl8_gfx_get_width(pxl8_gfx* ctx);\n" "i32 pxl8_gfx_get_width(pxl8_gfx* ctx);\n"
"void pxl8_2d_circle(pxl8_gfx* ctx, i32 x, i32 y, i32 r, u32 color);\n" "void pxl8_2d_circle(pxl8_gfx* ctx, i32 x, i32 y, i32 r, u32 color);\n"
"void pxl8_2d_circle_fill(pxl8_gfx* ctx, i32 x, i32 y, i32 r, u32 color);\n" "void pxl8_2d_circle_fill(pxl8_gfx* ctx, i32 x, i32 y, i32 r, u32 color);\n"
@ -195,23 +189,10 @@ static const char* pxl8_ffi_cdefs =
"typedef struct { float x, y, z, w; } pxl8_vec4;\n" "typedef struct { float x, y, z, w; } pxl8_vec4;\n"
"typedef struct { float m[16]; } pxl8_mat4;\n" "typedef struct { float m[16]; } pxl8_mat4;\n"
"\n" "\n"
"typedef struct pxl8_light {\n"
" pxl8_vec3 position;\n"
" u8 r, g, b;\n"
" u8 intensity;\n"
" f32 radius;\n"
" f32 radius_sq;\n"
" f32 inv_radius_sq;\n"
"} pxl8_light;\n"
"\n"
"typedef struct pxl8_3d_uniforms {\n" "typedef struct pxl8_3d_uniforms {\n"
" u8 ambient;\n" " u8 ambient;\n"
" pxl8_vec3 celestial_dir;\n"
" f32 celestial_intensity;\n"
" u8 fog_color;\n" " u8 fog_color;\n"
" f32 fog_density;\n" " f32 fog_density;\n"
" pxl8_light lights[16];\n"
" u32 num_lights;\n"
" f32 time;\n" " f32 time;\n"
"} pxl8_3d_uniforms;\n" "} pxl8_3d_uniforms;\n"
"\n" "\n"
@ -229,27 +210,6 @@ static const char* pxl8_ffi_cdefs =
"pxl8_mat4 pxl8_3d_camera_get_view(const pxl8_3d_camera* cam);\n" "pxl8_mat4 pxl8_3d_camera_get_view(const pxl8_3d_camera* cam);\n"
"pxl8_mat4 pxl8_3d_camera_get_projection(const pxl8_3d_camera* cam);\n" "pxl8_mat4 pxl8_3d_camera_get_projection(const pxl8_3d_camera* cam);\n"
"void pxl8_3d_camera_update(pxl8_3d_camera* cam, f32 dt);\n" "void pxl8_3d_camera_update(pxl8_3d_camera* cam, f32 dt);\n"
"typedef struct pxl8_projected_point { i32 x; i32 y; f32 depth; bool visible; } pxl8_projected_point;\n"
"pxl8_projected_point pxl8_3d_camera_world_to_screen(const pxl8_3d_camera* cam, pxl8_vec3 world_pos, u32 screen_width, u32 screen_height);\n"
"\n"
"typedef enum pxl8_gfx_effect { PXL8_GFX_EFFECT_GLOWS = 0 } pxl8_gfx_effect;\n"
"\n"
"typedef enum pxl8_glow_shape { PXL8_GLOW_CIRCLE = 0, PXL8_GLOW_DIAMOND = 1, PXL8_GLOW_SHAFT = 2 } pxl8_glow_shape;\n"
"\n"
"typedef struct pxl8_glow_source {\n"
" u8 color;\n"
" u16 depth;\n"
" u8 height;\n"
" u16 intensity;\n"
" u8 radius;\n"
" pxl8_glow_shape shape;\n"
" i16 x;\n"
" i16 y;\n"
"} pxl8_glow_source;\n"
"\n"
"void pxl8_gfx_apply_effect(pxl8_gfx* gfx, pxl8_gfx_effect effect, const void* params, u32 count);\n"
"void pxl8_gfx_blend_tables_update(pxl8_gfx* gfx);\n"
"void pxl8_gfx_colormap_update(pxl8_gfx* gfx);\n"
"\n" "\n"
"void pxl8_3d_begin_frame(pxl8_gfx* gfx, const pxl8_3d_camera* camera, const pxl8_3d_uniforms* uniforms);\n" "void pxl8_3d_begin_frame(pxl8_gfx* gfx, const pxl8_3d_camera* camera, const pxl8_3d_uniforms* uniforms);\n"
"void pxl8_3d_clear(pxl8_gfx* gfx, u8 color);\n" "void pxl8_3d_clear(pxl8_gfx* gfx, u8 color);\n"
@ -259,9 +219,8 @@ static const char* pxl8_ffi_cdefs =
"\n" "\n"
"typedef enum pxl8_blend_mode { PXL8_BLEND_OPAQUE = 0, PXL8_BLEND_ALPHA_TEST, PXL8_BLEND_ALPHA, PXL8_BLEND_ADDITIVE } pxl8_blend_mode;\n" "typedef enum pxl8_blend_mode { PXL8_BLEND_OPAQUE = 0, PXL8_BLEND_ALPHA_TEST, PXL8_BLEND_ALPHA, PXL8_BLEND_ADDITIVE } pxl8_blend_mode;\n"
"\n" "\n"
"typedef struct pxl8_gfx_material {\n" "typedef struct pxl8_material {\n"
" u32 texture_id;\n" " u32 texture_id;\n"
" u32 lightmap_id;\n"
" u8 alpha;\n" " u8 alpha;\n"
" u8 blend_mode;\n" " u8 blend_mode;\n"
" bool dither;\n" " bool dither;\n"
@ -269,15 +228,13 @@ static const char* pxl8_ffi_cdefs =
" bool dynamic_lighting;\n" " bool dynamic_lighting;\n"
" bool per_pixel;\n" " bool per_pixel;\n"
" bool vertex_color_passthrough;\n" " bool vertex_color_passthrough;\n"
" bool wireframe;\n"
" f32 emissive_intensity;\n" " f32 emissive_intensity;\n"
"} pxl8_gfx_material;\n" "} pxl8_material;\n"
"\n" "\n"
"typedef struct pxl8_vertex {\n" "typedef struct pxl8_vertex {\n"
" pxl8_vec3 position;\n" " pxl8_vec3 position;\n"
" pxl8_vec3 normal;\n" " pxl8_vec3 normal;\n"
" f32 u, v;\n" " f32 u, v;\n"
" f32 lu, lv;\n"
" u8 color;\n" " u8 color;\n"
" u8 light;\n" " u8 light;\n"
" u8 _pad[2];\n" " u8 _pad[2];\n"
@ -297,10 +254,7 @@ static const char* pxl8_ffi_cdefs =
"void pxl8_mesh_clear(pxl8_mesh* mesh);\n" "void pxl8_mesh_clear(pxl8_mesh* mesh);\n"
"u16 pxl8_mesh_push_vertex(pxl8_mesh* mesh, pxl8_vertex v);\n" "u16 pxl8_mesh_push_vertex(pxl8_mesh* mesh, pxl8_vertex v);\n"
"void pxl8_mesh_push_triangle(pxl8_mesh* mesh, u16 i0, u16 i1, u16 i2);\n" "void pxl8_mesh_push_triangle(pxl8_mesh* mesh, u16 i0, u16 i1, u16 i2);\n"
"void pxl8_3d_draw_mesh(pxl8_gfx* gfx, const pxl8_mesh* mesh, const pxl8_mat4* model, const pxl8_gfx_material* material);\n" "void pxl8_3d_draw_mesh(pxl8_gfx* gfx, const pxl8_mesh* mesh, pxl8_mat4 model, pxl8_material material);\n"
"void pxl8_3d_draw_mesh_wireframe(pxl8_gfx* gfx, const pxl8_mesh* mesh, pxl8_mat4 model, u8 color);\n"
"\n"
"u32 pxl8_hash32(u32 x);\n"
"\n" "\n"
"pxl8_mat4 pxl8_mat4_identity(void);\n" "pxl8_mat4 pxl8_mat4_identity(void);\n"
"pxl8_mat4 pxl8_mat4_lookat(pxl8_vec3 eye, pxl8_vec3 center, pxl8_vec3 up);\n" "pxl8_mat4 pxl8_mat4_lookat(pxl8_vec3 eye, pxl8_vec3 center, pxl8_vec3 up);\n"
@ -329,76 +283,18 @@ static const char* pxl8_ffi_cdefs =
" int num_rooms;\n" " int num_rooms;\n"
"} pxl8_procgen_params;\n" "} pxl8_procgen_params;\n"
"\n" "\n"
"typedef enum pxl8_graph_op {\n" "typedef struct pxl8_procgen_tex_params {\n"
" PXL8_OP_CONST = 0,\n" " char name[16];\n"
" PXL8_OP_INPUT_AGE,\n" " unsigned int seed;\n"
" PXL8_OP_INPUT_SEED,\n" " int width;\n"
" PXL8_OP_INPUT_TIME,\n" " int height;\n"
" PXL8_OP_INPUT_X,\n" " float scale;\n"
" PXL8_OP_INPUT_Y,\n" " float roughness;\n"
" unsigned char base_color;\n"
" unsigned char variation;\n"
"} pxl8_procgen_tex_params;\n"
"\n" "\n"
" PXL8_OP_ABS,\n" "void pxl8_procgen_tex(u8* buffer, const pxl8_procgen_tex_params* params);\n"
" PXL8_OP_CEIL,\n"
" PXL8_OP_COS,\n"
" PXL8_OP_FLOOR,\n"
" PXL8_OP_FRACT,\n"
" PXL8_OP_NEGATE,\n"
" PXL8_OP_SIN,\n"
" PXL8_OP_SQRT,\n"
"\n"
" PXL8_OP_ADD,\n"
" PXL8_OP_DIV,\n"
" PXL8_OP_MAX,\n"
" PXL8_OP_MIN,\n"
" PXL8_OP_MOD,\n"
" PXL8_OP_MUL,\n"
" PXL8_OP_POW,\n"
" PXL8_OP_SUB,\n"
"\n"
" PXL8_OP_CLAMP,\n"
" PXL8_OP_LERP,\n"
" PXL8_OP_SELECT,\n"
" PXL8_OP_SMOOTHSTEP,\n"
"\n"
" PXL8_OP_NOISE_FBM,\n"
" PXL8_OP_NOISE_PERLIN,\n"
" PXL8_OP_NOISE_RIDGED,\n"
" PXL8_OP_NOISE_TURBULENCE,\n"
" PXL8_OP_NOISE_VALUE,\n"
"\n"
" PXL8_OP_VORONOI_CELL,\n"
" PXL8_OP_VORONOI_EDGE,\n"
" PXL8_OP_VORONOI_ID,\n"
"\n"
" PXL8_OP_GRADIENT_LINEAR,\n"
" PXL8_OP_GRADIENT_RADIAL,\n"
"\n"
" PXL8_OP_QUANTIZE,\n"
" PXL8_OP_COUNT\n"
"} pxl8_graph_op;\n"
"\n"
"typedef struct pxl8_node {\n"
" u8 in[4];\n"
" u8 op;\n"
" u8 out;\n"
" f32 param;\n"
"} pxl8_node;\n"
"\n"
"typedef struct pxl8_graph {\n"
" u32 capacity;\n"
" u32 count;\n"
" pxl8_node* nodes;\n"
" u8 output_reg;\n"
" u32 seed;\n"
"} pxl8_graph;\n"
"\n"
"pxl8_graph* pxl8_graph_create(u32 capacity);\n"
"void pxl8_graph_destroy(pxl8_graph* graph);\n"
"void pxl8_graph_clear(pxl8_graph* graph);\n"
"u8 pxl8_graph_add_node(pxl8_graph* graph, pxl8_graph_op op, u8 in0, u8 in1, u8 in2, u8 in3, f32 param);\n"
"void pxl8_graph_set_output(pxl8_graph* graph, u8 reg);\n"
"void pxl8_graph_set_seed(pxl8_graph* graph, u32 seed);\n"
"void pxl8_graph_eval_texture(const pxl8_graph* graph, u8* buffer, i32 width, i32 height);\n"
"\n" "\n"
"typedef struct pxl8_bsp pxl8_bsp;\n" "typedef struct pxl8_bsp pxl8_bsp;\n"
"typedef struct pxl8_bsp_face pxl8_bsp_face;\n" "typedef struct pxl8_bsp_face pxl8_bsp_face;\n"
@ -421,13 +317,12 @@ static const char* pxl8_ffi_cdefs =
"bool pxl8_world_is_loaded(const pxl8_world* world);\n" "bool pxl8_world_is_loaded(const pxl8_world* world);\n"
"void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos);\n" "void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos);\n"
"pxl8_vec3 pxl8_world_resolve_collision(const pxl8_world* world, pxl8_vec3 from, pxl8_vec3 to, float radius);\n" "pxl8_vec3 pxl8_world_resolve_collision(const pxl8_world* world, pxl8_vec3 from, pxl8_vec3 to, float radius);\n"
"void pxl8_world_set_wireframe(pxl8_world* world, bool enabled, u8 color);\n"
"\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" "typedef struct { i32 cursor_x; i32 cursor_y; bool cursor_down; bool cursor_clicked; u32 hot_id; u32 active_id; } pxl8_gui_state;\n"
"pxl8_gui_state* pxl8_gui_state_create(void);\n" "pxl8_gui_state* pxl8_gui_state_create(void);\n"
"void pxl8_gui_state_destroy(pxl8_gui_state* state);\n" "void pxl8_gui_state_destroy(pxl8_gui_state* state);\n"
"void pxl8_gui_begin_frame(pxl8_gui_state* state, pxl8_gfx* gfx);\n" "void pxl8_gui_begin_frame(pxl8_gui_state* state);\n"
"void pxl8_gui_end_frame(pxl8_gui_state* state, pxl8_gfx* gfx);\n" "void pxl8_gui_end_frame(pxl8_gui_state* state);\n"
"void pxl8_gui_cursor_move(pxl8_gui_state* state, i32 x, i32 y);\n" "void pxl8_gui_cursor_move(pxl8_gui_state* state, i32 x, i32 y);\n"
"void pxl8_gui_cursor_down(pxl8_gui_state* state);\n" "void pxl8_gui_cursor_down(pxl8_gui_state* state);\n"
"void pxl8_gui_cursor_up(pxl8_gui_state* state);\n" "void pxl8_gui_cursor_up(pxl8_gui_state* state);\n"
@ -491,107 +386,4 @@ static const char* pxl8_ffi_cdefs =
"void pxl8_sfx_reverb_set_mix(pxl8_sfx_node* node, f32 mix);\n" "void pxl8_sfx_reverb_set_mix(pxl8_sfx_node* node, f32 mix);\n"
"void pxl8_sfx_reverb_set_room(pxl8_sfx_node* node, f32 room);\n" "void pxl8_sfx_reverb_set_room(pxl8_sfx_node* node, f32 room);\n"
"void pxl8_sfx_stop_all(pxl8_sfx_context* ctx);\n" "void pxl8_sfx_stop_all(pxl8_sfx_context* ctx);\n"
"void pxl8_sfx_stop_voice(pxl8_sfx_context* ctx, u16 voice_id);\n" "void pxl8_sfx_stop_voice(pxl8_sfx_context* ctx, u16 voice_id);\n";
"\n"
"typedef struct pxl8_net pxl8_net;\n"
"typedef enum pxl8_net_mode { PXL8_NET_LOCAL = 0, PXL8_NET_REMOTE } pxl8_net_mode;\n"
"typedef struct pxl8_net_config { const char* address; pxl8_net_mode mode; u16 port; } pxl8_net_config;\n"
"typedef enum pxl8_cmd_type { PXL8_CMD_NONE = 0, PXL8_CMD_SPAWN_ENTITY } pxl8_cmd_type;\n"
"\n"
"typedef struct pxl8_command_msg {\n"
" u16 cmd_type;\n"
" u8 payload[64];\n"
" u16 payload_size;\n"
" u64 tick;\n"
"} pxl8_command_msg;\n"
"\n"
"typedef struct pxl8_input_msg {\n"
" u32 buttons;\n"
" f32 look_dx;\n"
" f32 look_dy;\n"
" f32 move_x;\n"
" f32 move_y;\n"
" f32 yaw;\n"
" u64 tick;\n"
" u64 timestamp;\n"
"} pxl8_input_msg;\n"
"\n"
"typedef struct pxl8_entity_state {\n"
" u64 entity_id;\n"
" u8 userdata[56];\n"
"} pxl8_entity_state;\n"
"\n"
"typedef struct pxl8_snapshot_header {\n"
" u16 entity_count;\n"
" u16 event_count;\n"
" u64 player_id;\n"
" u64 tick;\n"
" f32 time;\n"
"} pxl8_snapshot_header;\n"
"\n"
"i32 pxl8_net_connect(pxl8_net* net);\n"
"bool pxl8_net_connected(const pxl8_net* net);\n"
"pxl8_net* pxl8_net_create(const pxl8_net_config* config);\n"
"void pxl8_net_destroy(pxl8_net* net);\n"
"void pxl8_net_disconnect(pxl8_net* net);\n"
"const pxl8_entity_state* pxl8_net_entities(const pxl8_net* net);\n"
"const u8* pxl8_net_entity_prev_userdata(const pxl8_net* net, u64 entity_id);\n"
"const u8* pxl8_net_entity_userdata(const pxl8_net* net, u64 entity_id);\n"
"const pxl8_input_msg* pxl8_net_input_at(const pxl8_net* net, u64 tick);\n"
"u64 pxl8_net_input_oldest_tick(const pxl8_net* net);\n"
"void pxl8_net_input_push(pxl8_net* net, const pxl8_input_msg* input);\n"
"f32 pxl8_net_lerp_alpha(const pxl8_net* net);\n"
"bool pxl8_net_needs_correction(const pxl8_net* net);\n"
"u64 pxl8_net_player_id(const pxl8_net* net);\n"
"bool pxl8_net_poll(pxl8_net* net);\n"
"u8* pxl8_net_predicted_state(pxl8_net* net);\n"
"void pxl8_net_predicted_tick_set(pxl8_net* net, u64 tick);\n"
"i32 pxl8_net_send_command(pxl8_net* net, const pxl8_command_msg* cmd);\n"
"i32 pxl8_net_send_input(pxl8_net* net, const pxl8_input_msg* input);\n"
"const pxl8_snapshot_header* pxl8_net_snapshot(const pxl8_net* net);\n"
"u64 pxl8_net_tick(const pxl8_net* net);\n"
"void pxl8_net_update(pxl8_net* net, f32 dt);\n"
"\n"
"void pxl8_bit_clear(u32* val, u8 bit);\n"
"u32 pxl8_bit_count(u32 val);\n"
"void pxl8_bit_set(u32* val, u8 bit);\n"
"bool pxl8_bit_test(u32 val, u8 bit);\n"
"void pxl8_bit_toggle(u32* val, u8 bit);\n"
"\n"
"void pxl8_pack_u8(u8* buf, size_t offset, u8 val);\n"
"void pxl8_pack_u16_be(u8* buf, size_t offset, u16 val);\n"
"void pxl8_pack_u16_le(u8* buf, size_t offset, u16 val);\n"
"void pxl8_pack_u32_be(u8* buf, size_t offset, u32 val);\n"
"void pxl8_pack_u32_le(u8* buf, size_t offset, u32 val);\n"
"void pxl8_pack_u64_be(u8* buf, size_t offset, u64 val);\n"
"void pxl8_pack_u64_le(u8* buf, size_t offset, u64 val);\n"
"void pxl8_pack_i8(u8* buf, size_t offset, i8 val);\n"
"void pxl8_pack_i16_be(u8* buf, size_t offset, i16 val);\n"
"void pxl8_pack_i16_le(u8* buf, size_t offset, i16 val);\n"
"void pxl8_pack_i32_be(u8* buf, size_t offset, i32 val);\n"
"void pxl8_pack_i32_le(u8* buf, size_t offset, i32 val);\n"
"void pxl8_pack_i64_be(u8* buf, size_t offset, i64 val);\n"
"void pxl8_pack_i64_le(u8* buf, size_t offset, i64 val);\n"
"void pxl8_pack_f32_be(u8* buf, size_t offset, f32 val);\n"
"void pxl8_pack_f32_le(u8* buf, size_t offset, f32 val);\n"
"void pxl8_pack_f64_be(u8* buf, size_t offset, f64 val);\n"
"void pxl8_pack_f64_le(u8* buf, size_t offset, f64 val);\n"
"\n"
"u8 pxl8_unpack_u8(const u8* buf, size_t offset);\n"
"u16 pxl8_unpack_u16_be(const u8* buf, size_t offset);\n"
"u16 pxl8_unpack_u16_le(const u8* buf, size_t offset);\n"
"u32 pxl8_unpack_u32_be(const u8* buf, size_t offset);\n"
"u32 pxl8_unpack_u32_le(const u8* buf, size_t offset);\n"
"u64 pxl8_unpack_u64_be(const u8* buf, size_t offset);\n"
"u64 pxl8_unpack_u64_le(const u8* buf, size_t offset);\n"
"i8 pxl8_unpack_i8(const u8* buf, size_t offset);\n"
"i16 pxl8_unpack_i16_be(const u8* buf, size_t offset);\n"
"i16 pxl8_unpack_i16_le(const u8* buf, size_t offset);\n"
"i32 pxl8_unpack_i32_be(const u8* buf, size_t offset);\n"
"i32 pxl8_unpack_i32_le(const u8* buf, size_t offset);\n"
"i64 pxl8_unpack_i64_be(const u8* buf, size_t offset);\n"
"i64 pxl8_unpack_i64_le(const u8* buf, size_t offset);\n"
"f32 pxl8_unpack_f32_be(const u8* buf, size_t offset);\n"
"f32 pxl8_unpack_f32_le(const u8* buf, size_t offset);\n"
"f64 pxl8_unpack_f64_be(const u8* buf, size_t offset);\n"
"f64 pxl8_unpack_f64_le(const u8* buf, size_t offset);\n";

View file

@ -9,11 +9,6 @@
#define VOICE_SCALE 0.15f #define VOICE_SCALE 0.15f
static inline f32 sanitize_audio(f32 x) {
union { f32 f; u32 u; } conv = { .f = x };
return ((conv.u & 0x7F800000) == 0x7F800000) ? 0.0f : x;
}
typedef enum envelope_state { typedef enum envelope_state {
ENV_ATTACK = 0, ENV_ATTACK = 0,
ENV_DECAY, ENV_DECAY,
@ -721,8 +716,8 @@ void pxl8_sfx_mixer_process(pxl8_sfx_mixer* mixer) {
left = fmaxf(-1.0f, fminf(1.0f, left)); left = fmaxf(-1.0f, fminf(1.0f, left));
right = fmaxf(-1.0f, fminf(1.0f, right)); right = fmaxf(-1.0f, fminf(1.0f, right));
left = sanitize_audio(left); if (!isfinite(left)) left = 0.0f;
right = sanitize_audio(right); if (!isfinite(right)) right = 0.0f;
mixer->output_buffer[i * 2] = left; mixer->output_buffer[i * 2] = left;
mixer->output_buffer[i * 2 + 1] = right; mixer->output_buffer[i * 2 + 1] = right;

View file

@ -345,7 +345,6 @@ void pxl8_bsp_destroy(pxl8_bsp* bsp) {
free(bsp->planes); free(bsp->planes);
free(bsp->surfedges); free(bsp->surfedges);
free(bsp->texinfo); free(bsp->texinfo);
free(bsp->vertex_lights);
free(bsp->vertices); free(bsp->vertices);
free(bsp->visdata); free(bsp->visdata);
@ -378,24 +377,26 @@ bool pxl8_bsp_is_leaf_visible(const pxl8_bsp* bsp, i32 leaf_from, i32 leaf_to) {
u32 target_byte = leaf_to >> 3; u32 target_byte = leaf_to >> 3;
u32 target_bit = leaf_to & 7; u32 target_bit = leaf_to & 7;
u32 pvs_size = (bsp->num_leafs + 7) / 8;
u32 pos = (u32)visofs; u32 pos = (u32)visofs;
u32 out_pos = 0; u32 current_byte = 0;
while (out_pos <= target_byte && pos < bsp->visdata_size) { while (current_byte < pvs_size && pos < bsp->visdata_size) {
u8 b = bsp->visdata[pos++]; u8 b = bsp->visdata[pos++];
if (b != 0) { if (b != 0) {
if (out_pos == target_byte) { if (current_byte == target_byte) {
return (b & (1 << target_bit)) != 0; return (b & (1 << target_bit)) != 0;
} }
out_pos++; current_byte++;
} else { } else {
if (pos >= bsp->visdata_size) break; if (pos >= bsp->visdata_size) return false;
u32 count = bsp->visdata[pos++]; u32 count = bsp->visdata[pos++];
if (out_pos + count > target_byte) { if (target_byte < current_byte + count) {
return false; return false;
} }
out_pos += count; current_byte += count;
} }
} }
@ -545,12 +546,6 @@ static inline bool face_in_frustum(const pxl8_bsp* bsp, u32 face_id, const pxl8_
return pxl8_frustum_test_aabb(frustum, face->aabb_min, face->aabb_max); return pxl8_frustum_test_aabb(frustum, face->aabb_min, face->aabb_max);
} }
static inline bool leaf_in_frustum(const pxl8_bsp_leaf* leaf, const pxl8_frustum* frustum) {
pxl8_vec3 mins = {(f32)leaf->mins[0], (f32)leaf->mins[1], (f32)leaf->mins[2]};
pxl8_vec3 maxs = {(f32)leaf->maxs[0], (f32)leaf->maxs[1], (f32)leaf->maxs[2]};
return pxl8_frustum_test_aabb(frustum, mins, maxs);
}
static void collect_face_to_mesh( static void collect_face_to_mesh(
const pxl8_bsp* bsp, const pxl8_bsp* bsp,
u32 face_id, u32 face_id,
@ -577,12 +572,15 @@ static void collect_face_to_mesh(
u16 base_idx = (u16)mesh->vertex_count; u16 base_idx = (u16)mesh->vertex_count;
u32 num_verts = 0; u32 num_verts = 0;
static int face_debug = 0;
bool debug_this = (face_debug++ < 3);
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;
u32 vert_idx; u32 vert_idx;
if (!pxl8_bsp_get_edge_vertex(bsp, surfedge_idx, &vert_idx)) { if (!pxl8_bsp_get_edge_vertex(bsp, surfedge_idx, &vert_idx)) {
if (debug_this) pxl8_debug("face %u: edge %u failed to get vertex (surfedge_idx=%d)", face_id, i, surfedge_idx);
continue; continue;
} }
@ -611,6 +609,11 @@ static void collect_face_to_mesh(
num_verts++; num_verts++;
} }
if (debug_this) {
pxl8_debug("face %u: num_edges=%u, collected %u verts, texinfo_id=%u",
face_id, face->num_edges, num_verts, face->texinfo_id);
}
if (num_verts < 3) return; if (num_verts < 3) return;
for (u32 i = 1; i < num_verts - 1; i++) { for (u32 i = 1; i < num_verts - 1; i++) {
@ -627,14 +630,8 @@ void pxl8_bsp_render_face(pxl8_gfx* gfx, const pxl8_bsp* bsp, u32 face_id, u32 t
collect_face_to_mesh(bsp, face_id, mesh); collect_face_to_mesh(bsp, face_id, mesh);
if (mesh->index_count > 0) { if (mesh->index_count > 0) {
pxl8_mat4 identity = pxl8_mat4_identity(); pxl8_material mat = pxl8_material_new(texture_id);
pxl8_gfx_material mat = { pxl8_3d_draw_mesh(gfx, mesh, pxl8_mat4_identity(), mat);
.texture_id = texture_id,
.alpha = 255,
.dither = true,
.dynamic_lighting = true,
};
pxl8_3d_draw_mesh(gfx, mesh, &identity, &mat);
} }
pxl8_mesh_destroy(mesh); pxl8_mesh_destroy(mesh);
@ -660,32 +657,6 @@ void pxl8_bsp_render_textured(pxl8_gfx* gfx, const pxl8_bsp* bsp, pxl8_vec3 came
i32 camera_leaf = pxl8_bsp_find_leaf(bsp, camera_pos); i32 camera_leaf = pxl8_bsp_find_leaf(bsp, camera_pos);
static u32 debug_counter = 0;
bool do_debug = (debug_counter++ % 300 == 0);
u32 visible_leafs = 0;
u32 visible_faces = 0;
u32 total_faces_in_visible_leafs = 0;
static bool dumped_camera_leaf = false;
if (do_debug) {
bool self_visible = (camera_leaf < 0) || pxl8_bsp_is_leaf_visible(bsp, camera_leaf, camera_leaf);
if (!self_visible) {
pxl8_debug("WARNING: Camera leaf %d is NOT visible from itself!", camera_leaf);
}
if (!dumped_camera_leaf && camera_leaf >= 0) {
const pxl8_bsp_leaf* cl = &bsp->leafs[camera_leaf];
pxl8_debug("Camera leaf %d: contents=%d, marksurfaces=%u-%u (%u faces)",
camera_leaf, cl->contents, cl->first_marksurface,
cl->first_marksurface + cl->num_marksurfaces, cl->num_marksurfaces);
dumped_camera_leaf = true;
}
for (u32 i = 0; i < bsp->num_leafs; i++) {
if (camera_leaf < 0 || pxl8_bsp_is_leaf_visible(bsp, camera_leaf, i)) {
visible_leafs++;
total_faces_in_visible_leafs += bsp->leafs[i].num_marksurfaces;
}
}
}
static u8* rendered_faces = NULL; static u8* rendered_faces = NULL;
static u32 rendered_faces_capacity = 0; static u32 rendered_faces_capacity = 0;
@ -703,25 +674,14 @@ void pxl8_bsp_render_textured(pxl8_gfx* gfx, const pxl8_bsp* bsp, pxl8_vec3 came
u32 current_texture = 0xFFFFFFFF; u32 current_texture = 0xFFFFFFFF;
static int debug_cull = 0;
u32 faces_checked = 0, faces_culled = 0, faces_passed = 0;
for (u32 leaf_id = 0; leaf_id < bsp->num_leafs; leaf_id++) { 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; if (camera_leaf >= 0 && !pxl8_bsp_is_leaf_visible(bsp, camera_leaf, leaf_id)) continue;
const pxl8_bsp_leaf* leaf = &bsp->leafs[leaf_id]; const pxl8_bsp_leaf* leaf = &bsp->leafs[leaf_id];
bool is_camera_leaf = ((i32)leaf_id == camera_leaf);
f32 cx = ((f32)leaf->mins[0] + (f32)leaf->maxs[0]) * 0.5f;
f32 cy = ((f32)leaf->mins[1] + (f32)leaf->maxs[1]) * 0.5f;
f32 cz = ((f32)leaf->mins[2] + (f32)leaf->maxs[2]) * 0.5f;
f32 dx = camera_pos.x - cx;
f32 dy = camera_pos.y - cy;
f32 dz = camera_pos.z - cz;
f32 dist_sq = dx*dx + dy*dy + dz*dz;
bool camera_near_leaf = dist_sq < (512.0f * 512.0f);
bool skip_frustum = is_camera_leaf || camera_near_leaf;
if (!skip_frustum && !leaf_in_frustum(leaf, frustum)) continue;
for (u32 i = 0; i < leaf->num_marksurfaces; i++) { for (u32 i = 0; i < leaf->num_marksurfaces; i++) {
u32 surf_idx = leaf->first_marksurface + i; u32 surf_idx = leaf->first_marksurface + i;
if (surf_idx >= bsp->num_marksurfaces) continue; if (surf_idx >= bsp->num_marksurfaces) continue;
@ -731,11 +691,13 @@ void pxl8_bsp_render_textured(pxl8_gfx* gfx, const pxl8_bsp* bsp, pxl8_vec3 came
if (rendered_faces[face_id]) continue; if (rendered_faces[face_id]) continue;
rendered_faces[face_id] = 1; rendered_faces[face_id] = 1;
if (do_debug) visible_faces++;
if (!skip_frustum && !face_in_frustum(bsp, face_id, frustum)) { faces_checked++;
if (!face_in_frustum(bsp, face_id, frustum)) {
faces_culled++;
continue; continue;
} }
faces_passed++;
const pxl8_bsp_face* face = &bsp->faces[face_id]; const pxl8_bsp_face* face = &bsp->faces[face_id];
u32 texture_id = 0; u32 texture_id = 0;
@ -744,38 +706,39 @@ void pxl8_bsp_render_textured(pxl8_gfx* gfx, const pxl8_bsp* bsp, pxl8_vec3 came
} }
if (texture_id != current_texture && mesh->index_count > 0) { if (texture_id != current_texture && mesh->index_count > 0) {
pxl8_mat4 identity = pxl8_mat4_identity(); pxl8_material mat = pxl8_material_with_double_sided(pxl8_material_new(current_texture));
pxl8_gfx_material mat = { pxl8_3d_draw_mesh(gfx, mesh, pxl8_mat4_identity(), mat);
.texture_id = current_texture,
.alpha = 255,
.dither = true,
.dynamic_lighting = true,
};
pxl8_3d_draw_mesh(gfx, mesh, &identity, &mat);
pxl8_mesh_clear(mesh); pxl8_mesh_clear(mesh);
} }
current_texture = texture_id; current_texture = texture_id;
u32 before = mesh->index_count;
collect_face_to_mesh(bsp, face_id, mesh); collect_face_to_mesh(bsp, face_id, mesh);
if (debug_cull < 3 && mesh->index_count > before) {
pxl8_debug("Added face %u: mesh now has %u indices (was %u)", face_id, mesh->index_count, before);
}
} }
} }
static int draw_count = 0;
if (debug_cull < 3) {
pxl8_debug("Final mesh: %u indices, %u vertices", mesh->index_count, mesh->vertex_count);
}
if (mesh->index_count > 0) { if (mesh->index_count > 0) {
pxl8_mat4 identity = pxl8_mat4_identity(); pxl8_material mat = pxl8_material_with_double_sided(pxl8_material_new(current_texture));
pxl8_gfx_material mat = { pxl8_3d_draw_mesh(gfx, mesh, pxl8_mat4_identity(), mat);
.texture_id = current_texture, if (draw_count++ < 5) {
.alpha = 255, pxl8_debug("bsp_render_textured: drew mesh with %u indices, camera_leaf=%d",
.dither = true, mesh->index_count, camera_leaf);
.dynamic_lighting = true, }
}; } else if (draw_count < 5) {
pxl8_3d_draw_mesh(gfx, mesh, &identity, &mat); pxl8_debug("bsp_render_textured: mesh is empty, camera_leaf=%d, num_leafs=%u",
camera_leaf, bsp->num_leafs);
draw_count++;
} }
if (do_debug) { if (debug_cull++ < 5) {
pxl8_debug("Camera at (%.1f, %.1f, %.1f) -> leaf %d, %u/%u leafs, %u/%u faces (expected %u)", pxl8_debug("bsp_render: checked=%u, culled=%u, passed=%u", faces_checked, faces_culled, faces_passed);
camera_pos.x, camera_pos.y, camera_pos.z,
camera_leaf, visible_leafs, bsp->num_leafs,
visible_faces, bsp->num_faces, total_faces_in_visible_leafs);
} }
pxl8_mesh_destroy(mesh); pxl8_mesh_destroy(mesh);

504
client/src/world/pxl8_gen.c Normal file
View file

@ -0,0 +1,504 @@
#include "pxl8_gen.h"
#include <stdlib.h>
#include <string.h>
#include "pxl8_log.h"
#include "pxl8_rng.h"
typedef struct room_grid {
u8* cells;
i32 width;
i32 height;
} room_grid;
static bool room_grid_init(room_grid* grid, i32 width, i32 height) {
grid->width = width;
grid->height = height;
grid->cells = calloc(width * height, sizeof(u8));
return grid->cells != NULL;
}
static u8 room_grid_get(const room_grid* grid, i32 x, i32 y) {
if (x < 0 || x >= grid->width || y < 0 || y >= grid->height) {
return 1;
}
return grid->cells[y * grid->width + x];
}
static void room_grid_set(room_grid* grid, i32 x, i32 y, u8 value) {
if (x < 0 || x >= grid->width || y < 0 || y >= grid->height) {
return;
}
grid->cells[y * grid->width + x] = value;
}
static inline void compute_face_aabb(pxl8_bsp_face* face, const pxl8_bsp_vertex* verts, u32 vert_idx) {
face->aabb_min = (pxl8_vec3){1e30f, 1e30f, 1e30f};
face->aabb_max = (pxl8_vec3){-1e30f, -1e30f, -1e30f};
for (u32 i = 0; i < 4; i++) {
pxl8_vec3 v = verts[vert_idx + i].position;
if (v.x < face->aabb_min.x) face->aabb_min.x = v.x;
if (v.x > face->aabb_max.x) face->aabb_max.x = v.x;
if (v.y < face->aabb_min.y) face->aabb_min.y = v.y;
if (v.y > face->aabb_max.y) face->aabb_max.y = v.y;
if (v.z < face->aabb_min.z) face->aabb_min.z = v.z;
if (v.z > face->aabb_max.z) face->aabb_max.z = v.z;
}
}
static void room_grid_fill(room_grid* grid, u8 value) {
for (i32 y = 0; y < grid->height; y++) {
for (i32 x = 0; x < grid->width; x++) {
room_grid_set(grid, x, y, value);
}
}
}
static pxl8_result grid_to_bsp(pxl8_bsp* bsp, const room_grid* grid) {
i32 vertex_count = 0;
i32 face_count = 0;
i32 floor_ceiling_count = 0;
for (i32 y = 0; y < grid->height; y++) {
for (i32 x = 0; x < grid->width; x++) {
if (room_grid_get(grid, x, y) == 0) {
if (room_grid_get(grid, x - 1, y) == 1) face_count++;
if (room_grid_get(grid, x + 1, y) == 1) face_count++;
if (room_grid_get(grid, x, y - 1) == 1) face_count++;
if (room_grid_get(grid, x, y + 1) == 1) face_count++;
floor_ceiling_count++;
}
}
}
face_count += floor_ceiling_count * 2;
vertex_count = face_count * 4;
pxl8_debug("Cave generation: %dx%d grid -> %d faces, %d vertices",
grid->width, grid->height, face_count, vertex_count);
bsp->vertices = calloc(vertex_count, sizeof(pxl8_bsp_vertex));
bsp->faces = calloc(face_count, sizeof(pxl8_bsp_face));
bsp->planes = calloc(face_count, sizeof(pxl8_bsp_plane));
bsp->edges = calloc(vertex_count, sizeof(pxl8_bsp_edge));
bsp->surfedges = calloc(vertex_count, sizeof(i32));
if (!bsp->vertices || !bsp->faces || !bsp->planes || !bsp->edges || !bsp->surfedges) {
return PXL8_ERROR_OUT_OF_MEMORY;
}
bsp->texinfo = NULL;
bsp->num_texinfo = 0;
i32 vert_idx = 0;
i32 face_idx = 0;
i32 edge_idx = 0;
const f32 cell_size = 64.0f;
const f32 wall_height = 128.0f;
for (i32 y = 0; y < grid->height; y++) {
for (i32 x = 0; x < grid->width; x++) {
if (room_grid_get(grid, x, y) == 0) {
f32 fx = (f32)x * cell_size;
f32 fy = (f32)y * cell_size;
if (room_grid_get(grid, x - 1, y) == 1) {
bsp->vertices[vert_idx + 0].position = (pxl8_vec3){fx, 0, fy};
bsp->vertices[vert_idx + 1].position = (pxl8_vec3){fx, wall_height, fy};
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx, wall_height, fy + cell_size};
bsp->vertices[vert_idx + 3].position = (pxl8_vec3){fx, 0, fy + cell_size};
bsp->planes[face_idx].normal = (pxl8_vec3){-1, 0, 0};
bsp->planes[face_idx].dist = -fx;
bsp->faces[face_idx].plane_id = face_idx;
bsp->faces[face_idx].num_edges = 4;
bsp->faces[face_idx].first_edge = edge_idx;
bsp->faces[face_idx].texinfo_id = 0;
for (i32 i = 0; i < 4; i++) {
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
bsp->edges[edge_idx + i].vertex[1] = vert_idx + ((i + 1) % 4);
bsp->surfedges[edge_idx + i] = edge_idx + i;
}
compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx);
vert_idx += 4;
edge_idx += 4;
face_idx++;
}
if (room_grid_get(grid, x + 1, y) == 1) {
bsp->vertices[vert_idx + 0].position = (pxl8_vec3){fx + cell_size, 0, fy};
bsp->vertices[vert_idx + 1].position = (pxl8_vec3){fx + cell_size, 0, fy + cell_size};
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx + cell_size, wall_height, fy + cell_size};
bsp->vertices[vert_idx + 3].position = (pxl8_vec3){fx + cell_size, wall_height, fy};
bsp->planes[face_idx].normal = (pxl8_vec3){1, 0, 0};
bsp->planes[face_idx].dist = fx + cell_size;
bsp->faces[face_idx].plane_id = face_idx;
bsp->faces[face_idx].num_edges = 4;
bsp->faces[face_idx].first_edge = edge_idx;
bsp->faces[face_idx].texinfo_id = 0;
for (i32 i = 0; i < 4; i++) {
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
bsp->edges[edge_idx + i].vertex[1] = vert_idx + ((i + 1) % 4);
bsp->surfedges[edge_idx + i] = edge_idx + i;
}
compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx);
vert_idx += 4;
edge_idx += 4;
face_idx++;
}
if (room_grid_get(grid, x, y - 1) == 1) {
bsp->vertices[vert_idx + 0].position = (pxl8_vec3){fx, 0, fy};
bsp->vertices[vert_idx + 1].position = (pxl8_vec3){fx + cell_size, 0, fy};
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx + cell_size, wall_height, fy};
bsp->vertices[vert_idx + 3].position = (pxl8_vec3){fx, wall_height, fy};
bsp->planes[face_idx].normal = (pxl8_vec3){0, 0, -1};
bsp->planes[face_idx].dist = -fy;
bsp->faces[face_idx].plane_id = face_idx;
bsp->faces[face_idx].num_edges = 4;
bsp->faces[face_idx].first_edge = edge_idx;
bsp->faces[face_idx].texinfo_id = 0;
for (i32 i = 0; i < 4; i++) {
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
bsp->edges[edge_idx + i].vertex[1] = vert_idx + ((i + 1) % 4);
bsp->surfedges[edge_idx + i] = edge_idx + i;
}
compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx);
vert_idx += 4;
edge_idx += 4;
face_idx++;
}
if (room_grid_get(grid, x, y + 1) == 1) {
bsp->vertices[vert_idx + 0].position = (pxl8_vec3){fx, 0, fy + cell_size};
bsp->vertices[vert_idx + 1].position = (pxl8_vec3){fx, wall_height, fy + cell_size};
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx + cell_size, wall_height, fy + cell_size};
bsp->vertices[vert_idx + 3].position = (pxl8_vec3){fx + cell_size, 0, fy + cell_size};
bsp->planes[face_idx].normal = (pxl8_vec3){0, 0, 1};
bsp->planes[face_idx].dist = fy + cell_size;
bsp->faces[face_idx].plane_id = face_idx;
bsp->faces[face_idx].num_edges = 4;
bsp->faces[face_idx].first_edge = edge_idx;
bsp->faces[face_idx].texinfo_id = 0;
for (i32 i = 0; i < 4; i++) {
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
bsp->edges[edge_idx + i].vertex[1] = vert_idx + ((i + 1) % 4);
bsp->surfedges[edge_idx + i] = edge_idx + i;
}
compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx);
vert_idx += 4;
edge_idx += 4;
face_idx++;
}
}
}
}
for (i32 y = 0; y < grid->height; y++) {
for (i32 x = 0; x < grid->width; x++) {
if (room_grid_get(grid, x, y) == 0) {
f32 fx = (f32)x * cell_size;
f32 fy = (f32)y * cell_size;
bsp->vertices[vert_idx + 0].position = (pxl8_vec3){fx, 0, fy};
bsp->vertices[vert_idx + 1].position = (pxl8_vec3){fx, 0, fy + cell_size};
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx + cell_size, 0, fy + cell_size};
bsp->vertices[vert_idx + 3].position = (pxl8_vec3){fx + cell_size, 0, fy};
bsp->planes[face_idx].normal = (pxl8_vec3){0, 1, 0};
bsp->planes[face_idx].dist = 0;
bsp->faces[face_idx].plane_id = face_idx;
bsp->faces[face_idx].num_edges = 4;
bsp->faces[face_idx].first_edge = edge_idx;
bsp->faces[face_idx].texinfo_id = 0;
for (i32 i = 0; i < 4; i++) {
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
bsp->edges[edge_idx + i].vertex[1] = vert_idx + ((i + 1) % 4);
bsp->surfedges[edge_idx + i] = edge_idx + i;
}
compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx);
vert_idx += 4;
edge_idx += 4;
face_idx++;
bsp->vertices[vert_idx + 0].position = (pxl8_vec3){fx, wall_height, fy};
bsp->vertices[vert_idx + 1].position = (pxl8_vec3){fx + cell_size, wall_height, fy};
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx + cell_size, wall_height, fy + cell_size};
bsp->vertices[vert_idx + 3].position = (pxl8_vec3){fx, wall_height, fy + cell_size};
bsp->planes[face_idx].normal = (pxl8_vec3){0, -1, 0};
bsp->planes[face_idx].dist = -wall_height;
bsp->faces[face_idx].plane_id = face_idx;
bsp->faces[face_idx].num_edges = 4;
bsp->faces[face_idx].first_edge = edge_idx;
bsp->faces[face_idx].texinfo_id = 0;
for (i32 i = 0; i < 4; i++) {
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
bsp->edges[edge_idx + i].vertex[1] = vert_idx + ((i + 1) % 4);
bsp->surfedges[edge_idx + i] = edge_idx + i;
}
compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx);
vert_idx += 4;
edge_idx += 4;
face_idx++;
}
}
}
bsp->num_vertices = vertex_count;
bsp->num_faces = face_count;
bsp->num_planes = face_count;
bsp->num_edges = vertex_count;
bsp->num_surfedges = vertex_count;
bsp->leafs = calloc(1, sizeof(pxl8_bsp_leaf));
bsp->marksurfaces = calloc(face_count, sizeof(u16));
if (!bsp->leafs || !bsp->marksurfaces) {
return PXL8_ERROR_OUT_OF_MEMORY;
}
bsp->num_leafs = 1;
bsp->num_marksurfaces = face_count;
bsp->leafs[0].first_marksurface = 0;
bsp->leafs[0].num_marksurfaces = face_count;
bsp->leafs[0].contents = -2;
for (i32 i = 0; i < face_count; i++) {
bsp->marksurfaces[i] = i;
}
return PXL8_OK;
}
static bool bounds_intersects(const pxl8_bounds* a, const pxl8_bounds* b) {
return !(a->x + a->w <= b->x || b->x + b->w <= a->x ||
a->y + a->h <= b->y || b->y + b->h <= a->y);
}
static void carve_corridor_h(room_grid* grid, i32 x1, i32 x2, i32 y) {
i32 start = (x1 < x2) ? x1 : x2;
i32 end = (x1 > x2) ? x1 : x2;
for (i32 x = start; x <= end; x++) {
room_grid_set(grid, x, y, 0);
room_grid_set(grid, x, y - 1, 0);
room_grid_set(grid, x, y + 1, 0);
}
}
static void carve_corridor_v(room_grid* grid, i32 y1, i32 y2, i32 x) {
i32 start = (y1 < y2) ? y1 : y2;
i32 end = (y1 > y2) ? y1 : y2;
for (i32 y = start; y <= end; y++) {
room_grid_set(grid, x, y, 0);
room_grid_set(grid, x - 1, y, 0);
room_grid_set(grid, x + 1, y, 0);
}
}
static pxl8_result procgen_rooms(pxl8_bsp* bsp, const pxl8_procgen_params* params) {
pxl8_debug("procgen_rooms called: %dx%d, seed=%u, min=%d, max=%d, num=%d",
params->width, params->height, params->seed,
params->min_room_size, params->max_room_size, params->num_rooms);
pxl8_rng rng;
pxl8_rng_seed(&rng, params->seed);
room_grid grid;
if (!room_grid_init(&grid, params->width, params->height)) {
pxl8_error("Failed to allocate room grid");
return PXL8_ERROR_OUT_OF_MEMORY;
}
room_grid_fill(&grid, 1);
pxl8_bounds rooms[256];
i32 room_count = 0;
i32 max_attempts = params->num_rooms * 10;
for (i32 attempt = 0; attempt < max_attempts && room_count < params->num_rooms && room_count < 256; attempt++) {
i32 w = params->min_room_size + (pxl8_rng_next(&rng) % (params->max_room_size - params->min_room_size + 1));
i32 h = params->min_room_size + (pxl8_rng_next(&rng) % (params->max_room_size - params->min_room_size + 1));
i32 x = 1 + (pxl8_rng_next(&rng) % (params->width - w - 2));
i32 y = 1 + (pxl8_rng_next(&rng) % (params->height - h - 2));
pxl8_bounds new_room = {x, y, w, h};
bool overlaps = false;
for (i32 i = 0; i < room_count; i++) {
if (bounds_intersects(&new_room, &rooms[i])) {
overlaps = true;
break;
}
}
if (!overlaps) {
for (i32 ry = y; ry < y + h; ry++) {
for (i32 rx = x; rx < x + w; rx++) {
room_grid_set(&grid, rx, ry, 0);
}
}
if (room_count > 0) {
i32 new_cx = x + w / 2;
i32 new_cy = y + h / 2;
i32 prev_cx = rooms[room_count - 1].x + rooms[room_count - 1].w / 2;
i32 prev_cy = rooms[room_count - 1].y + rooms[room_count - 1].h / 2;
if (pxl8_rng_next(&rng) % 2 == 0) {
carve_corridor_h(&grid, prev_cx, new_cx, prev_cy);
carve_corridor_v(&grid, prev_cy, new_cy, new_cx);
} else {
carve_corridor_v(&grid, prev_cy, new_cy, prev_cx);
carve_corridor_h(&grid, prev_cx, new_cx, new_cy);
}
}
rooms[room_count++] = new_room;
}
}
pxl8_debug("Room generation: %dx%d grid -> %d rooms created",
params->width, params->height, room_count);
pxl8_result result = grid_to_bsp(bsp, &grid);
free(grid.cells);
return result;
}
pxl8_result pxl8_procgen(pxl8_bsp* bsp, const pxl8_procgen_params* params) {
if (!bsp || !params) {
return PXL8_ERROR_NULL_POINTER;
}
switch (params->type) {
case PXL8_PROCGEN_ROOMS:
return procgen_rooms(bsp, params);
case PXL8_PROCGEN_TERRAIN:
pxl8_error("Terrain generation not yet implemented");
return PXL8_ERROR_NOT_INITIALIZED;
default:
pxl8_error("Unknown procgen type: %d", params->type);
return PXL8_ERROR_INVALID_ARGUMENT;
}
}
static u32 hash2d(i32 x, i32 y) {
u32 h = ((u32)x * 374761393u) + ((u32)y * 668265263u);
h ^= h >> 13;
h ^= h << 17;
h ^= h >> 5;
return h;
}
void pxl8_procgen_tex(u8* buffer, const pxl8_procgen_tex_params* params) {
if (!buffer || !params) return;
for (i32 y = 0; y < params->height; y++) {
for (i32 x = 0; x < params->width; x++) {
f32 u = (f32)x / (f32)params->width;
f32 v = (f32)y / (f32)params->height;
u8 color = params->base_color;
// Tile-based pattern (floor style)
if (params->seed == 11111) {
i32 tile_x = (i32)floorf(u * 8.0f);
i32 tile_y = (i32)floorf(v * 8.0f);
u32 h = hash2d(tile_x, tile_y);
f32 pattern = (f32)(h & 0xFF) / 255.0f;
i32 quantized = (pattern < 0.3f) ? 0 : (pattern < 0.7f) ? 1 : 2;
color = params->base_color + quantized;
// Checkerboard dither
if (((tile_x + tile_y) & 1) == 0 && (h & 0x100)) {
color = (color < 255) ? color + 1 : color;
}
}
// Large tile pattern (ceiling style)
else if (params->seed == 22222) {
i32 coarse_x = (i32)floorf(u * 2.0f);
i32 coarse_y = (i32)floorf(v * 2.0f);
u32 coarse_h = hash2d(coarse_x, coarse_y);
i32 subdivision = (coarse_h >> 8) & 0x3;
i32 tile_x, tile_y;
switch (subdivision) {
case 0: tile_x = (i32)floorf(u * 3.0f); tile_y = (i32)floorf(v * 3.0f); break;
case 1: tile_x = (i32)floorf(u * 5.0f); tile_y = (i32)floorf(v * 5.0f); break;
case 2: tile_x = (i32)floorf(u * 2.0f); tile_y = (i32)floorf(v * 4.0f); break;
default: tile_x = (i32)floorf(u * 4.0f); tile_y = (i32)floorf(v * 2.0f); break;
}
u32 h = hash2d(tile_x, tile_y);
f32 pattern = (f32)(h & 0xFF) / 255.0f;
if (pattern < 0.25f) color = params->base_color;
else if (pattern < 0.50f) color = params->base_color + 1;
else if (pattern < 0.75f) color = params->base_color + 2;
else color = params->base_color + 3;
}
// Brick pattern (wall style)
else {
f32 brick_y = floorf(v * 4.0f);
f32 offset = ((i32)brick_y & 1) ? 0.5f : 0.0f;
i32 brick_x = (i32)floorf(u * 4.0f + offset);
brick_y = (i32)brick_y;
f32 brick_u = fabsf((u * 4.0f + offset) - floorf(u * 4.0f + offset) - 0.5f);
f32 brick_v = fabsf((v * 4.0f) - floorf(v * 4.0f) - 0.5f);
u32 h = hash2d(brick_x, (i32)brick_y);
f32 noise = (f32)(h & 0xFF) / 255.0f;
// Mortar lines
if (brick_u > 0.47f || brick_v > 0.47f) {
color = params->base_color - 2;
} else {
i32 shade = (i32)(noise * 3.0f);
color = params->base_color + shade;
}
}
buffer[y * params->width + x] = color;
}
}
}

View file

@ -21,11 +21,24 @@ typedef struct pxl8_procgen_params {
i32 num_rooms; i32 num_rooms;
} pxl8_procgen_params; } pxl8_procgen_params;
typedef struct pxl8_procgen_tex_params {
char name[16];
u32 seed;
i32 width;
i32 height;
f32 scale;
f32 roughness;
u8 base_color;
u8 variation;
u8 max_color;
} pxl8_procgen_tex_params;
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
pxl8_result pxl8_procgen(pxl8_bsp* bsp, const pxl8_procgen_params* params); pxl8_result pxl8_procgen(pxl8_bsp* bsp, const pxl8_procgen_params* params);
void pxl8_procgen_tex(u8* buffer, const pxl8_procgen_tex_params* params);
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -398,15 +398,15 @@ void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos) {
return; return;
} }
static int render_count = 0;
if (render_count++ < 5) {
pxl8_debug("world_render: camera_pos=(%.1f, %.1f, %.1f), num_faces=%u",
camera_pos.x, camera_pos.y, camera_pos.z, world->bsp.num_faces);
}
if (world->wireframe) { if (world->wireframe) {
pxl8_bsp_render_wireframe(gfx, &world->bsp, camera_pos, world->wireframe_color); pxl8_bsp_render_wireframe(gfx, &world->bsp, camera_pos, world->wireframe_color);
} else { } else {
pxl8_bsp_render_textured(gfx, &world->bsp, camera_pos); pxl8_bsp_render_textured(gfx, &world->bsp, camera_pos);
} }
} }
void pxl8_world_set_wireframe(pxl8_world* world, bool enabled, u8 color) {
if (!world) return;
world->wireframe = enabled;
world->wireframe_color = color;
}

View file

@ -32,7 +32,6 @@ bool pxl8_world_check_collision(const pxl8_world* world, pxl8_vec3 pos, f32 radi
bool pxl8_world_is_loaded(const pxl8_world* world); bool pxl8_world_is_loaded(const pxl8_world* world);
void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos); void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos);
pxl8_vec3 pxl8_world_resolve_collision(const pxl8_world* world, pxl8_vec3 from, pxl8_vec3 to, f32 radius); pxl8_vec3 pxl8_world_resolve_collision(const pxl8_world* world, pxl8_vec3 from, pxl8_vec3 to, f32 radius);
void pxl8_world_set_wireframe(pxl8_world* world, bool enabled, u8 color);
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -1,15 +1,16 @@
(local pxl8 (require :pxl8)) (local pxl8 (require :pxl8))
(local menu (require :mod.menu)) (local menu (require :mod.menu))
(local music (require :mod.music)) (local music (require :mod.music))
(local first_person3d (require :mod.first_person3d)) (local worldgen (require :mod.worldgen))
(var time 0) (var time 0)
(var active-demo :logo) (var active-demo :logo)
(var particles nil) (var particles nil)
(var particles2 nil)
(var fire-init? false) (var fire-init? false)
(var rain-init? false) (var rain-init? false)
(var snow-init? false) (var snow-init? false)
(var first_person3d-init? false) (var snow-init2? false)
(var use-famicube-palette? false) (var use-famicube-palette? false)
(var logo-x 256) (var logo-x 256)
@ -31,12 +32,15 @@
(pxl8.load_palette "res/sprites/pxl8_logo.ase") (pxl8.load_palette "res/sprites/pxl8_logo.ase")
(set logo-sprite (pxl8.load_sprite "res/sprites/pxl8_logo.ase")) (set logo-sprite (pxl8.load_sprite "res/sprites/pxl8_logo.ase"))
(set particles (pxl8.create_particles 1000)) (set particles (pxl8.create_particles 1000))
(music.init))) (set particles2 (pxl8.create_particles 500))
(music.init)
(music.start)
(worldgen.init)))
(global update (fn [dt] (global update (fn [dt]
(when (pxl8.key_pressed "escape") (when (pxl8.key_pressed "escape")
(menu.toggle) (menu.toggle)
(when (= active-demo :first_person3d) (when (= active-demo :worldgen)
(pxl8.set_relative_mouse_mode (not (menu.is-paused))))) (pxl8.set_relative_mouse_mode (not (menu.is-paused)))))
(when (not (menu.is-paused)) (when (not (menu.is-paused))
@ -46,16 +50,15 @@
(transition:update dt) (transition:update dt)
(when (transition:is_complete) (when (transition:is_complete)
(when transition-pending (when transition-pending
(when (and (= active-demo :first_person3d) (not= transition-pending :first_person3d)) (when (and (= active-demo :worldgen) (not= transition-pending :worldgen))
(pxl8.set_relative_mouse_mode false)) (pxl8.set_relative_mouse_mode false))
(when (and (not= active-demo :first_person3d) (= transition-pending :first_person3d)) (when (and (not= active-demo :worldgen) (= transition-pending :worldgen))
(pxl8.set_relative_mouse_mode true)) (pxl8.set_relative_mouse_mode true))
(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))
(when (= active-demo :rain) (set rain-init? false)) (when (= active-demo :rain) (set rain-init? false))
(when (= active-demo :snow) (set snow-init? false)) (when (= active-demo :snow) (set snow-init? false) (set snow-init2? false)))
(when (= active-demo :first_person3d) (set first_person3d-init? false)))
(transition:destroy) (transition:destroy)
(set transition nil))) (set transition nil)))
@ -66,14 +69,12 @@
(when (pxl8.key_pressed "5") (switch-demo :fire)) (when (pxl8.key_pressed "5") (switch-demo :fire))
(when (pxl8.key_pressed "6") (switch-demo :rain)) (when (pxl8.key_pressed "6") (switch-demo :rain))
(when (pxl8.key_pressed "7") (switch-demo :snow)) (when (pxl8.key_pressed "7") (switch-demo :snow))
(when (pxl8.key_pressed "8") (switch-demo :first_person3d)) (when (pxl8.key_pressed "8") (switch-demo :worldgen))
(when (pxl8.key_pressed "=") (when (pxl8.key_pressed "=")
(set use-famicube-palette? (not use-famicube-palette?)) (set use-famicube-palette? (not use-famicube-palette?))
(local palette-path (if use-famicube-palette? "res/palettes/famicube.ase" "res/sprites/pxl8_logo.ase")) (local palette-path (if use-famicube-palette? "res/palettes/famicube.ase" "res/sprites/pxl8_logo.ase"))
(pxl8.load_palette palette-path)) (pxl8.load_palette palette-path))
(music.update dt)
(case active-demo (case active-demo
:logo (do :logo (do
(set logo-x (+ logo-x (* logo-dx dt))) (set logo-x (+ logo-x (* logo-dx dt)))
@ -90,14 +91,14 @@
(when (> logo-y 296) (when (> logo-y 296)
(set logo-y 296) (set logo-y 296)
(set logo-dy (- (math.abs logo-dy))))) (set logo-dy (- (math.abs logo-dy)))))
:first_person3d (do :worldgen (worldgen.update dt))
(when (not first_person3d-init?)
(first_person3d.init) (music.update dt)
(set first_person3d-init? true))
(first_person3d.update dt)))
(when particles (when particles
(particles:update dt))) (particles:update dt))
(when particles2
(particles2:update dt)))
(when (menu.is-paused) (when (menu.is-paused)
(menu.update)))) (menu.update))))
@ -172,8 +173,7 @@
(set snow-init? true)) (set snow-init? true))
(particles:render))) (particles:render)))
:first_person3d (first_person3d.frame) :worldgen (worldgen.frame)
_ (pxl8.clear 0)) _ (pxl8.clear 0))

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,516 +0,0 @@
(local pxl8 (require :pxl8))
(local effects (require :pxl8.effects))
(local net (require :pxl8.net))
(local colormap (require :mod.colormap))
(local menu (require :mod.menu))
(local palette (require :mod.palette))
(local sky (require :mod.sky))
(local textures (require :mod.textures))
(local bob-amount 4.0)
(local bob-speed 8.0)
(local cam-smoothing 0.25)
(local cell-size 64)
(local cursor-sensitivity 0.010)
(local gravity -800)
(local grid-size 64)
(local ground-y 64)
(local jump-force 175)
(local land-recovery-speed 20)
(local land-squash-amount -4)
(local max-pitch 1.5)
(local move-speed 200)
(local turn-speed 4.0)
(local sim-tick-rate 60)
(local sim-dt (/ 1.0 sim-tick-rate))
(local history-size 128)
(local correction-threshold 1.0)
(var auto-run? false)
(var auto-run-cancel-key nil)
(var bob-time 0)
(var cam-pitch 0)
(var cam-x 1000)
(var cam-y 64)
(var cam-yaw 0)
(var cam-z 1000)
(var camera nil)
(var grounded? true)
(var land-squash 0)
(var light-time 0)
(var real-time 0)
(var network nil)
(var smooth-cam-x 1000)
(var smooth-cam-z 1000)
(var velocity-y 0)
(var world nil)
(var fps-avg 0)
(var fps-sample-count 0)
(var fireball-mesh nil)
(var last-dt 0.016)
(local cursor-look? true)
(local FIREBALL_COLOR 218)
(local STONE_FLOOR_START 37)
(local STONE_WALL_START 2)
(local MOSS_COLOR 200)
(local trail-positions [])
(local TRAIL_LENGTH 8)
(fn create-fireball-mesh []
(let [verts []
indices []
radius 5
rings 4
segments 6
core-color (+ FIREBALL_COLOR 6)
spike-color (+ FIREBALL_COLOR 2)]
;; top pole
(table.insert verts {:x 0 :y radius :z 0 :nx 0 :ny 1 :nz 0 :color core-color :light 255})
;; sphere rings
(for [ring 1 (- rings 1)]
(let [phi (* (/ ring rings) math.pi)
sin-phi (math.sin phi)
cos-phi (math.cos phi)
y (* radius cos-phi)
ring-radius (* radius sin-phi)]
(for [seg 0 (- segments 1)]
(let [theta (* (/ seg segments) math.pi 2)
x (* ring-radius (math.cos theta))
z (* ring-radius (math.sin theta))
nx (* sin-phi (math.cos theta))
nz (* sin-phi (math.sin theta))]
(table.insert verts {:x x :y y :z z :nx nx :ny cos-phi :nz nz :color core-color :light 255})))))
;; bottom pole
(let [bottom-idx (length verts)]
(table.insert verts {:x 0 :y (- radius) :z 0 :nx 0 :ny -1 :nz 0 :color core-color :light 255})
;; top cap triangles
(for [seg 0 (- segments 1)]
(let [next-seg (% (+ seg 1) segments)]
(table.insert indices 0)
(table.insert indices (+ 1 next-seg))
(table.insert indices (+ 1 seg))))
;; middle quads
(for [ring 0 (- rings 3)]
(for [seg 0 (- segments 1)]
(let [next-seg (% (+ seg 1) segments)
curr-row (+ 1 (* ring segments))
next-row (+ 1 (* (+ ring 1) segments))]
(table.insert indices (+ curr-row seg))
(table.insert indices (+ curr-row next-seg))
(table.insert indices (+ next-row seg))
(table.insert indices (+ curr-row next-seg))
(table.insert indices (+ next-row next-seg))
(table.insert indices (+ next-row seg)))))
;; bottom cap triangles
(let [last-ring-start (+ 1 (* (- rings 2) segments))]
(for [seg 0 (- segments 1)]
(let [next-seg (% (+ seg 1) segments)]
(table.insert indices bottom-idx)
(table.insert indices (+ last-ring-start seg))
(table.insert indices (+ last-ring-start next-seg))))))
;; add spikes - evenly distributed using golden ratio
(let [num-spikes 12
spike-len 8
base-size 1.2
golden-ratio (/ (+ 1 (math.sqrt 5)) 2)]
(for [i 0 (- num-spikes 1)]
(let [;; fibonacci sphere distribution
y (- 1 (* (/ i (- num-spikes 1)) 2))
r-at-y (math.sqrt (- 1 (* y y)))
theta (* math.pi 2 i golden-ratio)
nx (* r-at-y (math.cos theta))
ny y
nz (* r-at-y (math.sin theta))
;; tangent vectors for base
tx (if (> (math.abs ny) 0.9) 1 0)
ty (if (> (math.abs ny) 0.9) 0 1)
tz 0
;; cross product for perpendicular
px (- (* ty nz) (* tz ny))
py (- (* tz nx) (* tx nz))
pz (- (* tx ny) (* ty nx))
pl (math.sqrt (+ (* px px) (* py py) (* pz pz)))
px (/ px pl) py (/ py pl) pz (/ pz pl)
;; second perpendicular
qx (- (* ny pz) (* nz py))
qy (- (* nz px) (* nx pz))
qz (- (* nx py) (* ny px))
;; base center inside sphere
bx (* radius 0.8 nx)
by (* radius 0.8 ny)
bz (* radius 0.8 nz)
;; spike tip
sx (* (+ radius spike-len) nx)
sy (* (+ radius spike-len) ny)
sz (* (+ radius spike-len) nz)
base-idx (length verts)]
;; 4 base vertices forming a square
(table.insert verts {:x (+ bx (* base-size px) (* base-size qx))
:y (+ by (* base-size py) (* base-size qy))
:z (+ bz (* base-size pz) (* base-size qz))
:nx nx :ny ny :nz nz :color core-color :light 255})
(table.insert verts {:x (+ bx (* base-size px) (* (- base-size) qx))
:y (+ by (* base-size py) (* (- base-size) qy))
:z (+ bz (* base-size pz) (* (- base-size) qz))
:nx nx :ny ny :nz nz :color core-color :light 255})
(table.insert verts {:x (+ bx (* (- base-size) px) (* (- base-size) qx))
:y (+ by (* (- base-size) py) (* (- base-size) qy))
:z (+ bz (* (- base-size) pz) (* (- base-size) qz))
:nx nx :ny ny :nz nz :color core-color :light 255})
(table.insert verts {:x (+ bx (* (- base-size) px) (* base-size qx))
:y (+ by (* (- base-size) py) (* base-size qy))
:z (+ bz (* (- base-size) pz) (* base-size qz))
:nx nx :ny ny :nz nz :color core-color :light 255})
;; spike tip
(table.insert verts {:x sx :y sy :z sz :nx nx :ny ny :nz nz :color spike-color :light 255})
;; 4 triangular faces of pyramid
(table.insert indices base-idx)
(table.insert indices (+ base-idx 1))
(table.insert indices (+ base-idx 4))
(table.insert indices (+ base-idx 1))
(table.insert indices (+ base-idx 2))
(table.insert indices (+ base-idx 4))
(table.insert indices (+ base-idx 2))
(table.insert indices (+ base-idx 3))
(table.insert indices (+ base-idx 4))
(table.insert indices (+ base-idx 3))
(table.insert indices base-idx)
(table.insert indices (+ base-idx 4)))))
(set fireball-mesh (pxl8.create_mesh verts indices))))
(var client-tick 0)
(var last-processed-tick 0)
(var time-accumulator 0)
(var position-history {})
(var pending-inputs {})
(fn history-idx [tick]
(+ 1 (% tick history-size)))
(fn store-position [tick x z yaw]
(tset position-history (history-idx tick) {:tick tick :x x :z z :yaw yaw}))
(fn get-position [tick]
(let [entry (. position-history (history-idx tick))]
(when (and entry (= entry.tick tick))
entry)))
(fn store-pending-input [tick input]
(tset pending-inputs (history-idx tick) {:tick tick :input input}))
(fn get-pending-input [tick]
(let [entry (. pending-inputs (history-idx tick))]
(when (and entry (= entry.tick tick))
entry.input)))
(fn apply-movement [x z yaw input]
(var new-x x)
(var new-z z)
(let [move-forward (or input.move_y 0)
move-right (or input.move_x 0)]
(when (or (not= move-forward 0) (not= move-right 0))
(let [forward-x (- (math.sin yaw))
forward-z (- (math.cos yaw))
right-x (math.cos yaw)
right-z (- (math.sin yaw))
len (math.sqrt (+ (* move-forward move-forward) (* move-right move-right)))
norm-forward (/ move-forward len)
norm-right (/ move-right len)
move-delta (* move-speed sim-dt)]
(set new-x (+ new-x (* move-delta (+ (* forward-x norm-forward) (* right-x norm-right)))))
(set new-z (+ new-z (* move-delta (+ (* forward-z norm-forward) (* right-z norm-right))))))))
(values new-x new-z))
(fn init []
(pxl8.set_relative_mouse_mode true)
(pxl8.set_palette palette 256)
(pxl8.set_colormap colormap 16384)
(for [i 0 7]
(let [t (/ i 7)
r 0xFF
g (math.floor (+ 0x60 (* t (- 0xE0 0x60))))
b (math.floor (+ 0x10 (* t (- 0x80 0x10))))]
(pxl8.set_palette_rgb (+ FIREBALL_COLOR i) r g b)))
(sky.update-gradient 1 2 6 6 10 18)
(pxl8.update_palette_deps)
(set camera (pxl8.create_camera_3d))
(set world (pxl8.create_world))
(sky.generate-stars 12345)
(create-fireball-mesh)
(set network (net.Net.new {:port 7777}))
(when network
(network:connect)
(network:spawn cam-x cam-y cam-z cam-yaw cam-pitch))
(let [result (world:generate {
:type pxl8.PROCGEN_ROOMS
:width 64
:height 64
:seed 42
:min_room_size 5
:max_room_size 10
:num_rooms 20})]
(if (< result 0)
(pxl8.error (.. "Failed to generate rooms - result: " result))
(let [floor-tex (textures.mossy-cobblestone 44444 STONE_FLOOR_START MOSS_COLOR)
wall-tex (textures.ashlar-wall 55555 STONE_WALL_START MOSS_COLOR)
sky-tex (pxl8.create_texture [0] 1 1)]
(let [result (world:apply_textures [
{:name "floor"
:texture_id floor-tex
:rule (fn [normal] (> normal.y 0.7))}
{:name "ceiling"
:texture_id sky-tex
:rule (fn [normal] (< normal.y -0.7))}
{:name "wall"
:texture_id wall-tex
:rule (fn [normal] (and (<= normal.y 0.7) (>= normal.y -0.7)))}])]
(when (< result 0)
(pxl8.error (.. "Failed to apply textures - result: " result))))))))
(fn sample-input []
(var move-forward 0)
(var move-right 0)
(when (pxl8.key_pressed "`")
(set auto-run? (not auto-run?))
(when (and auto-run? (pxl8.key_down "w"))
(set auto-run-cancel-key "w")))
(when (and auto-run? (not auto-run-cancel-key) (or (pxl8.key_down "w") (pxl8.key_down "s")))
(set auto-run? false)
(when (pxl8.key_down "s")
(set auto-run-cancel-key "s")))
(when (and auto-run-cancel-key (not (pxl8.key_down auto-run-cancel-key)))
(set auto-run-cancel-key nil))
(when (or (pxl8.key_down "w") auto-run?)
(set move-forward (+ move-forward 1)))
(when (and (pxl8.key_down "s") (not= auto-run-cancel-key "s"))
(set move-forward (- move-forward 1)))
(when (pxl8.key_down "a")
(set move-right (- move-right 1)))
(when (pxl8.key_down "d")
(set move-right (+ move-right 1)))
{:move_x move-right
:move_y move-forward
:look_dx (pxl8.mouse_dx)
:look_dy (pxl8.mouse_dy)})
(fn reconcile [server-tick server-x server-z]
(let [predicted (get-position server-tick)]
(when predicted
(let [dx (- predicted.x server-x)
dz (- predicted.z server-z)
error (math.sqrt (+ (* dx dx) (* dz dz)))]
(when (> error correction-threshold)
(set cam-x server-x)
(set cam-z server-z)
(for [t (+ server-tick 1) client-tick]
(let [input (get-pending-input t)
hist (get-position t)]
(when (and input hist)
(let [(new-x new-z) (apply-movement cam-x cam-z hist.yaw input)]
(set cam-x new-x)
(set cam-z new-z)
(store-position t cam-x cam-z hist.yaw))))))))))
(fn update [dt]
(set last-dt dt)
(let [fps (pxl8.get_fps)]
(set fps-sample-count (+ fps-sample-count 1))
(set fps-avg (+ (* fps-avg (/ (- fps-sample-count 1) fps-sample-count))
(/ fps fps-sample-count)))
(when (>= fps-sample-count 120)
(set fps-sample-count 0)
(set fps-avg 0)))
(when (world:is_loaded)
(let [input (sample-input)
grid-max (* grid-size cell-size)
movement-yaw cam-yaw]
(set time-accumulator (+ time-accumulator dt))
(while (>= time-accumulator sim-dt)
(set time-accumulator (- time-accumulator sim-dt))
(set client-tick (+ client-tick 1))
(store-pending-input client-tick input)
(let [(new-x new-z) (apply-movement cam-x cam-z movement-yaw input)]
(when (and (>= new-x 0) (<= new-x grid-max)
(>= new-z 0) (<= new-z grid-max))
(let [(resolved-x _ resolved-z) (world:resolve_collision cam-x cam-y cam-z new-x cam-y new-z 5)]
(set cam-x resolved-x)
(set cam-z resolved-z)))
(store-position client-tick cam-x cam-z movement-yaw)))
(when cursor-look?
(set cam-yaw (- cam-yaw (* input.look_dx cursor-sensitivity)))
(set cam-pitch (math.max (- max-pitch)
(math.min max-pitch
(- cam-pitch (* input.look_dy cursor-sensitivity))))))
(when (and (not cursor-look?) (pxl8.key_down "up"))
(set cam-pitch (math.min max-pitch (+ cam-pitch (* turn-speed dt)))))
(when (and (not cursor-look?) (pxl8.key_down "down"))
(set cam-pitch (math.max (- max-pitch) (- cam-pitch (* turn-speed dt)))))
(when (and (not cursor-look?) (pxl8.key_down "left"))
(set cam-yaw (- cam-yaw (* turn-speed dt))))
(when (and (not cursor-look?) (pxl8.key_down "right"))
(set cam-yaw (+ cam-yaw (* turn-speed dt))))
(when network
(let [(ok err) (pcall (fn []
(network:send_input {:move_x input.move_x
:move_y input.move_y
:look_dx input.look_dx
:look_dy input.look_dy
:yaw movement-yaw
:tick client-tick})
(network:update dt)
(when (network:poll)
(let [snapshot (network:snapshot)]
(when (and snapshot (> snapshot.tick last-processed-tick))
(set last-processed-tick snapshot.tick)
(let [player-id (network:player_id)]
(when (> player-id 0)
(let [curr (network:entity_userdata player-id)]
(when curr
(let [srv-x (pxl8.unpack_f32_be curr 0)
srv-z (pxl8.unpack_f32_be curr 8)]
(reconcile snapshot.tick srv-x srv-z)))))))))))]
(when (not ok)
(pxl8.error (.. "Network error: " err)))))
(set smooth-cam-x (+ (* smooth-cam-x (- 1 cam-smoothing)) (* cam-x cam-smoothing)))
(set smooth-cam-z (+ (* smooth-cam-z (- 1 cam-smoothing)) (* cam-z cam-smoothing)))
(when (and (pxl8.key_pressed "space") grounded?)
(set velocity-y jump-force)
(set grounded? false))
(set velocity-y (+ velocity-y (* gravity dt)))
(set cam-y (+ cam-y (* velocity-y dt)))
(when (<= cam-y ground-y)
(when (not grounded?)
(set land-squash land-squash-amount))
(set cam-y ground-y)
(set velocity-y 0)
(set grounded? true))
(when (< land-squash 0)
(set land-squash (math.min 0 (+ land-squash (* land-recovery-speed dt)))))
(let [moving (or (not= input.move_x 0) (not= input.move_y 0))]
(if (and moving grounded?)
(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))))))
(set light-time (+ light-time (* dt 0.5)))
(set real-time (+ real-time dt)))))
(fn frame []
(pxl8.clear 1)
(when (not camera)
(pxl8.error "camera is nil!"))
(when (not world)
(pxl8.error "world is nil!"))
(when (and world (not (world:is_loaded)))
(pxl8.text "World not loaded yet..." 5 30 12))
(when (and camera world (world:is_loaded))
(let [bob-offset (* (math.sin bob-time) bob-amount)
eye-y (+ cam-y bob-offset land-squash)
forward-x (- (math.sin cam-yaw))
forward-z (- (math.cos cam-yaw))
target-x (+ smooth-cam-x forward-x)
target-y (+ eye-y (math.sin cam-pitch))
target-z (+ smooth-cam-z forward-z)
aspect (/ (pxl8.get_width) (pxl8.get_height))]
(camera:lookat [smooth-cam-x eye-y smooth-cam-z]
[target-x target-y target-z]
[0 1 0])
(camera:set_perspective 1.047 aspect 1.0 4096.0)
(let [light-x (+ 1000 (* 50 (math.cos light-time)))
light-z (+ 940 (* 50 (math.sin light-time)))
light-y 80
phase (+ (* light-x 0.01) 1.7)
f1 (* 0.08 (math.sin (+ (* real-time 2.5) phase)))
f2 (* 0.05 (math.sin (+ (* real-time 4.1) (* phase 0.7))))
f3 (* 0.03 (math.sin (+ (* real-time 7.3) (* phase 1.2))))
flicker (+ 0.92 f1 f2 f3)
light-intensity (math.floor (math.max 0 (math.min 255 (* 255 flicker))))
r1 (* 0.06 (math.sin (+ (* real-time 1.8) (* phase 0.5))))
r2 (* 0.04 (math.sin (+ (* real-time 3.2) phase)))
light-radius (* 150 (+ 0.95 r1 r2))]
(pxl8.begin_frame_3d camera {
:ambient 30
:fog_density 0.0
:celestial_dir [0.5 -0.8 0.3]
:celestial_intensity 0.5
:lights [{:x light-x :y light-y :z light-z
:r 255 :g 200 :b 150
:intensity light-intensity
:radius light-radius}]})
(pxl8.clear_depth)
(sky.update-gradient 1 2 6 6 10 18)
(sky.render smooth-cam-x eye-y smooth-cam-z (menu.is-wireframe))
(pxl8.clear_depth)
(world:set_wireframe (menu.is-wireframe) 15)
(world:render [smooth-cam-x eye-y smooth-cam-z])
(when fireball-mesh
(let [wire (menu.is-wireframe)]
(pxl8.draw_mesh fireball-mesh {:x light-x :y light-y :z light-z
:passthrough true
:wireframe wire
:emissive 1.0})))
(pxl8.end_frame_3d))
(sky.render-stars cam-yaw cam-pitch 1.0 last-dt)
(let [cx (/ (pxl8.get_width) 2)
cy (/ (pxl8.get_height) 2)
crosshair-size 4
crosshair-color 240
text-color 251]
(pxl8.line (- cx crosshair-size) cy (+ cx crosshair-size) cy crosshair-color)
(pxl8.line cx (- cy crosshair-size) cx (+ cy crosshair-size) crosshair-color)
(pxl8.text (.. "fps: " (string.format "%.0f" fps-avg)) 5 5 text-color)
(pxl8.text (.. "pos: " (string.format "%.0f" cam-x) ","
(string.format "%.0f" cam-y) ","
(string.format "%.0f" cam-z)) 5 15 text-color)))))
{:init init
:update update
:frame frame}

View file

@ -1,8 +1,6 @@
(local pxl8 (require :pxl8)) (local pxl8 (require :pxl8))
(local music (require :mod.music))
(var paused false) (var paused false)
(var wireframe false)
(var gui nil) (var gui nil)
(fn init [] (fn init []
@ -38,22 +36,12 @@
(when gui (when gui
(gui:begin_frame) (gui:begin_frame)
(pxl8.gui_window 200 100 240 200 "pxl8 demo") (pxl8.gui_window 200 100 240 140 "pxl8 demo")
(when (gui:button 1 215 140 210 30 "Resume") (when (gui:button 1 215 145 210 32 "Resume")
(hide)) (hide))
(let [music-label (if (music.is-playing) "Music: On" "Music: Off")] (when (gui:button 2 215 185 210 32 "Quit")
(when (gui:button 3 215 175 210 30 music-label)
(if (music.is-playing)
(music.stop)
(music.start))))
(let [wire-label (if wireframe "Wireframe: On" "Wireframe: Off")]
(when (gui:button 4 215 210 210 30 wire-label)
(set wireframe (not wireframe))))
(when (gui:button 2 215 245 210 30 "Quit")
(pxl8.quit)) (pxl8.quit))
(if (gui:is_hovering) (if (gui:is_hovering)
@ -63,7 +51,6 @@
(gui:end_frame))) (gui:end_frame)))
{:is-paused (fn [] paused) {:is-paused (fn [] paused)
:is-wireframe (fn [] wireframe)
:toggle toggle :toggle toggle
:show show :show show
:hide hide :hide hide

View file

@ -101,7 +101,7 @@
(fn update [dt] (fn update [dt]
(when playing (when playing
(set time (+ time dt)) (set time (+ time dt))
(while (>= time step-duration) (when (>= time step-duration)
(set time (- time step-duration)) (set time (- time step-duration))
(local melody-idx (+ 1 (% step (length melody)))) (local melody-idx (+ 1 (% step (length melody))))
@ -125,5 +125,4 @@
:start start :start start
:stop stop :stop stop
:update update :update update
:is-playing (fn [] playing) :is-playing (fn [] playing)}
:get-step (fn [] step)}

View file

@ -1,263 +0,0 @@
(require :pxl8)
(local ffi (require :ffi))
(local data (ffi.new "u32[256]" [
0x080602
0x14120E
0x23211E
0x31302C
0x403E3B
0x4B4946
0x595755
0x676664
0x767573
0x858382
0x939290
0xA2A09F
0xB0AFAE
0xBEBDBC
0xCDCCCC
0xDADAD9
0x594625
0x544023
0x4F3C24
0x4C3A22
0x453821
0x40321F
0x3E2F20
0x382D1D
0x33291E
0x30271F
0x2F251D
0x2D231E
0x28211C
0x251F1D
0x23201A
0x221F1B
0x646269
0x5F5C61
0x5C545A
0x584F55
0x5B514F
0x554A47
0x4B413F
0x423C36
0x463D31
0x3E352A
0x362E25
0x2D2922
0x26221D
0x1C1916
0x151310
0x100F0D
0x8C6F52
0x7E6045
0x73553B
0x715134
0xBA8346
0xA1723B
0x815C2E
0x745226
0xCC8926
0xBB7E22
0x9F6B1F
0x875A1C
0x6E4918
0x553712
0x3B250D
0x24180A
0xA34331
0x9B3728
0x923220
0x882E18
0x842B16
0x772312
0x69200D
0x5A1C06
0x541C04
0x4C1A03
0x411701
0x371000
0x2E0D00
0x250B00
0x1B0600
0x130500
0x7D5741
0x76503A
0x6E4C37
0x684833
0x5D3F2F
0x553A2C
0x4F3628
0x483024
0x4A3126
0x483025
0x432D22
0x3C2C22
0x352922
0x2C241F
0x221C1B
0x1A1916
0x6E4626
0x5F4025
0x523924
0x433322
0x352B1E
0x28231A
0x1A1A14
0x1C1815
0x96544B
0xAC7369
0xB48C86
0xBCA7A4
0xB1BCC2
0x9DB0B9
0x8A9FAA
0x77929F
0x738995
0x5E7C8B
0x4A6C7D
0x345E72
0x1F4C64
0x19445C
0x143C51
0x10384B
0x183748
0x1A3341
0x192F39
0x152B34
0x13262E
0x101E23
0x0E1519
0x0B0E10
0x896463
0x815C5B
0x785352
0x6F4C4D
0x664444
0x5F3C3D
0x573738
0x523233
0x442929
0x392324
0x2D1D1D
0x241414
0x1A0E0E
0x100909
0x070403
0x000000
0x98936F
0x918B68
0x887F60
0x807759
0x797055
0x73684D
0x6B6146
0x63593F
0x5B523A
0x504834
0x423D2D
0x373226
0x2E2B1F
0x222018
0x161511
0x0E0F0A
0x9A554F
0x904D48
0x87453F
0x7D4037
0x743831
0x693329
0x612C24
0x572720
0x4F231A
0x441E16
0x391914
0x2D150F
0x22110D
0x1A0B06
0x0D0403
0x040202
0x7F77C0
0x7770B5
0x6E68A8
0x686099
0x60588C
0x575381
0x4E4C72
0x454263
0x3D3957
0x34324A
0x2C2940
0x242135
0x1E1928
0x16121D
0x0C0A12
0x050306
0x88AF7B
0x81A473
0x7B9A67
0x728E5D
0x6D8553
0x61794A
0x5B7144
0x61734B
0x586A3D
0x4D5E2D
0x465422
0x3F4D17
0x36420E
0x2F3507
0x272804
0x211F02
0x1EF708
0x3CE10D
0x51CC1B
0x64B621
0x6DA12C
0x69882B
0x727F3B
0xE4DDCE
0xEEE6BA
0xEAE290
0xE9E26D
0xE5DE43
0xE3DB20
0xE1CC18
0xDFB911
0xDCA60B
0xE8A306
0xDF9312
0xE17B05
0xC86815
0xBC5908
0xB14805
0xA63D07
0xB6431E
0xAA381A
0x9A2E12
0x8C270F
0x892B17
0x762311
0x5F1F0D
0x491B09
0x3B1809
0xE50F19
0x6A34C4
0xE00B28
0x2B08C8
0x322A33
0x281C0E
0x2F1E15
0xD48067
0xC26B4C
0x974928
0x814123
0xD5B3A9
0xBE9D93
0x9A7B6C
0x7F5F51
0x8E504C
]))
data

View file

@ -1,271 +0,0 @@
(local ffi (require :ffi))
(local pxl8 (require :pxl8))
(local effects (require :pxl8.effects))
(local SKY_GRADIENT_START 144)
(local SKY_GRADIENT_COUNT 16)
(local sky-radius 900)
(local sky-segments 16)
(local sky-rings 16)
(local NUM_RANDOM_STARS 300)
(local NUM_TINY_STARS 7000)
(local STAR_SEED 0xDEADBEEF)
(local STAR_CYCLE_PERIOD 7200)
;; Use existing bright palette colors
;; Silver/white: indices 14-15 (brightest grays)
(local IDX_SILVER 14)
;; Warm/torch: indices 216-218 (bright creams/yellows)
(local IDX_TORCH 216)
;; Blue/magic: indices 176-178 (purples - brightest of the range)
(local IDX_MAGIC 176)
(var sky-mesh nil)
(var star-time 0)
(var last-gradient-key nil)
(var random-stars [])
(var tiny-stars [])
(fn generate-sky-gradient [zenith-r zenith-g zenith-b horizon-r horizon-g horizon-b]
(for [i 0 (- SKY_GRADIENT_COUNT 1)]
(let [t (/ i (- SKY_GRADIENT_COUNT 1))
r (math.floor (+ zenith-r (* t (- horizon-r zenith-r))))
g (math.floor (+ zenith-g (* t (- horizon-g zenith-g))))
b (math.floor (+ zenith-b (* t (- horizon-b zenith-b))))]
(pxl8.set_palette_rgb (+ SKY_GRADIENT_START i) r g b))))
(fn create-sky-dome []
(let [verts []
indices []]
(for [i 0 (- sky-rings 1)]
(let [theta0 (* (/ i sky-rings) math.pi 0.5)
theta1 (* (/ (+ i 1) sky-rings) math.pi 0.5)
sin-t0 (math.sin theta0)
cos-t0 (math.cos theta0)
sin-t1 (math.sin theta1)
cos-t1 (math.cos theta1)
y0 (* sky-radius cos-t0)
y1 (* sky-radius cos-t1)
r0 (* sky-radius sin-t0)
r1 (* sky-radius sin-t1)
t0 (/ i sky-rings)
t1 (/ (+ i 1) sky-rings)
c0 (math.floor (+ SKY_GRADIENT_START (* t0 (- SKY_GRADIENT_COUNT 1)) 0.5))
c1 (math.floor (+ SKY_GRADIENT_START (* t1 (- SKY_GRADIENT_COUNT 1)) 0.5))]
(for [j 0 (- sky-segments 1)]
(let [phi0 (* (/ j sky-segments) math.pi 2)
phi1 (* (/ (+ j 1) sky-segments) math.pi 2)
cos-p0 (math.cos phi0)
sin-p0 (math.sin phi0)
cos-p1 (math.cos phi1)
sin-p1 (math.sin phi1)
x00 (* r0 cos-p0) z00 (* r0 sin-p0)
x01 (* r0 cos-p1) z01 (* r0 sin-p1)
x10 (* r1 cos-p0) z10 (* r1 sin-p0)
x11 (* r1 cos-p1) z11 (* r1 sin-p1)
nx00 (- (* sin-t0 cos-p0)) ny00 (- cos-t0) nz00 (- (* sin-t0 sin-p0))
nx01 (- (* sin-t0 cos-p1)) ny01 (- cos-t0) nz01 (- (* sin-t0 sin-p1))
nx10 (- (* sin-t1 cos-p0)) ny10 (- cos-t1) nz10 (- (* sin-t1 sin-p0))
nx11 (- (* sin-t1 cos-p1)) ny11 (- cos-t1) nz11 (- (* sin-t1 sin-p1))
base-idx (# verts)]
(if (= i 0)
(do
(table.insert verts {:x x00 :y y0 :z z00 :nx nx00 :ny ny00 :nz nz00 :color c0 :light 255})
(table.insert verts {:x x11 :y y1 :z z11 :nx nx11 :ny ny11 :nz nz11 :color c1 :light 255})
(table.insert verts {:x x10 :y y1 :z z10 :nx nx10 :ny ny10 :nz nz10 :color c1 :light 255})
(table.insert indices base-idx)
(table.insert indices (+ base-idx 2))
(table.insert indices (+ base-idx 1)))
(do
(table.insert verts {:x x00 :y y0 :z z00 :nx nx00 :ny ny00 :nz nz00 :color c0 :light 255})
(table.insert verts {:x x01 :y y0 :z z01 :nx nx01 :ny ny01 :nz nz01 :color c0 :light 255})
(table.insert verts {:x x11 :y y1 :z z11 :nx nx11 :ny ny11 :nz nz11 :color c1 :light 255})
(table.insert verts {:x x10 :y y1 :z z10 :nx nx10 :ny ny10 :nz nz10 :color c1 :light 255})
(table.insert indices base-idx)
(table.insert indices (+ base-idx 3))
(table.insert indices (+ base-idx 2))
(table.insert indices base-idx)
(table.insert indices (+ base-idx 2))
(table.insert indices (+ base-idx 1))))))))
(set sky-mesh (pxl8.create_mesh verts indices))))
(fn update-gradient [zenith-r zenith-g zenith-b horizon-r horizon-g horizon-b]
(let [key (.. zenith-r "," zenith-g "," zenith-b "," horizon-r "," horizon-g "," horizon-b)]
(when (not= key last-gradient-key)
(generate-sky-gradient zenith-r zenith-g zenith-b horizon-r horizon-g horizon-b)
(set last-gradient-key key))))
(fn galactic-band-factor [dx dy dz]
(let [band-len (math.sqrt (+ (* 0.6 0.6) (* 0.3 0.3) (* 0.742 0.742)))
bx (/ 0.6 band-len)
by (/ 0.3 band-len)
bz (/ 0.742 band-len)
dist-from-band (math.abs (+ (* dx bx) (* dy by) (* dz bz)))
in-band (- 1 (math.min (* dist-from-band 3) 1))]
(* in-band in-band)))
(fn generate-stars []
(set random-stars [])
(set tiny-stars [])
;; Generate random stars - use full upper hemisphere (dy > -0.1)
(for [i 0 (- NUM_RANDOM_STARS 1)]
(let [h1 (pxl8.hash32 (+ STAR_SEED (* i 5)))
h2 (pxl8.hash32 (+ STAR_SEED (* i 5) 1))
h3 (pxl8.hash32 (+ STAR_SEED (* i 5) 2))
h4 (pxl8.hash32 (+ STAR_SEED (* i 5) 3))
theta (* (/ h1 0xFFFFFFFF) math.pi 2)
phi (math.acos (- 1 (* (/ h2 0xFFFFFFFF) 1.0)))
sin-phi (math.sin phi)
cos-phi (math.cos phi)
dx (* sin-phi (math.cos theta))
dy cos-phi
dz (* sin-phi (math.sin theta))
brightness-raw (/ (% h3 256) 255)
brightness (math.floor (+ 60 (* brightness-raw brightness-raw 195)))
color-type (% h4 100)
color (if (< color-type 8) (+ IDX_TORCH (% (bit.rshift h4 8) 2))
(< color-type 16) (+ IDX_MAGIC (% (bit.rshift h4 8) 2))
(+ IDX_SILVER (% (bit.rshift h4 8) 2)))]
(when (> dy -0.1)
(table.insert random-stars {:dx dx :dy dy :dz dz
:brightness brightness
:color color}))))
(let [tiny-seed (+ STAR_SEED 0xCAFEBABE)]
(for [i 0 (- NUM_TINY_STARS 1)]
(let [h1 (pxl8.hash32 (+ tiny-seed (* i 4)))
h2 (pxl8.hash32 (+ tiny-seed (* i 4) 1))
h3 (pxl8.hash32 (+ tiny-seed (* i 4) 2))
h4 (pxl8.hash32 (+ tiny-seed (* i 4) 3))
theta (* (/ h1 0xFFFFFFFF) math.pi 2)
phi (math.acos (- 1 (* (/ h2 0xFFFFFFFF) 1.0)))
sin-phi (math.sin phi)
cos-phi (math.cos phi)
dx (* sin-phi (math.cos theta))
dy cos-phi
dz (* sin-phi (math.sin theta))
band-boost (galactic-band-factor dx dy dz)
base-bright (+ 40 (% h3 50))
brightness (+ base-bright (math.floor (* band-boost 40)))
color-shift (% h4 100)
color (if (< color-shift 3) (+ IDX_TORCH (% (bit.rshift h4 8) 2))
(< color-shift 12) (+ IDX_MAGIC (% (bit.rshift h4 8) 2))
(+ IDX_SILVER (% (bit.rshift h4 8) 2)))]
(when (> dy -0.1)
(table.insert tiny-stars {:dx dx :dy dy :dz dz
:brightness brightness
:color color}))))))
(fn project-direction [dir-x dir-y dir-z yaw pitch cos-rot sin-rot width height]
(let [rot-x (- (* dir-x cos-rot) (* dir-z sin-rot))
rot-z (+ (* dir-x sin-rot) (* dir-z cos-rot))
cos-yaw (math.cos yaw)
sin-yaw (math.sin yaw)
cos-pitch (math.cos pitch)
sin-pitch (math.sin pitch)
rotated-x (+ (* rot-x cos-yaw) (* rot-z sin-yaw))
rotated-z (+ (* (- rot-x) sin-yaw) (* rot-z cos-yaw))
rotated-y (- (* dir-y cos-pitch) (* rotated-z sin-pitch))
final-z (+ (* dir-y sin-pitch) (* rotated-z cos-pitch))]
(when (> final-z 0.01)
(let [fov 1.047
aspect (/ width height)
half-fov-tan (math.tan (* fov 0.5))
ndc-x (/ rotated-x (* final-z half-fov-tan aspect))
ndc-y (/ rotated-y (* final-z half-fov-tan))]
(when (and (>= ndc-x -1) (<= ndc-x 1) (>= ndc-y -1) (<= ndc-y 1))
{:x (math.floor (* (+ 1 ndc-x) 0.5 width))
:y (math.floor (* (- 1 ndc-y) 0.5 height))})))))
(fn render-stars [yaw pitch intensity dt]
(set star-time (+ star-time (or dt 0)))
(when (> intensity 0)
(let [width (pxl8.get_width)
height (pxl8.get_height)
glows []
fade-in (* intensity intensity)
time-factor (/ star-time 60)
star-rotation (/ (* star-time math.pi 2) STAR_CYCLE_PERIOD)
cos-rot (math.cos star-rotation)
sin-rot (math.sin star-rotation)]
(each [i star (ipairs tiny-stars)]
(let [screen (project-direction star.dx star.dy star.dz yaw pitch cos-rot sin-rot width height)]
(when screen
(let [int (math.floor (* star.brightness fade-in))]
(when (> int 8)
(table.insert glows {:x screen.x :y screen.y
:radius 1
:intensity int
:color star.color
:shape effects.GLOW_CIRCLE}))))))
(each [i star (ipairs random-stars)]
(let [screen (project-direction star.dx star.dy star.dz yaw pitch cos-rot sin-rot width height)]
(when screen
(let [phase (+ (* i 2.137) (* time-factor 3.0))
twinkle (+ 0.75 (* 0.25 (math.sin (* phase 6.28))))
int (math.floor (* star.brightness fade-in twinkle))]
(if (> star.brightness 220)
(do
(table.insert glows {:x screen.x :y screen.y
:radius 3
:intensity (math.floor (* int 1.5))
:color star.color
:shape effects.GLOW_DIAMOND})
(table.insert glows {:x screen.x :y screen.y
:radius 5
:intensity (math.floor (/ int 2))
:color star.color
:shape effects.GLOW_CIRCLE}))
(> star.brightness 180)
(do
(table.insert glows {:x screen.x :y screen.y
:radius 2
:intensity int
:color star.color
:shape effects.GLOW_DIAMOND})
(table.insert glows {:x screen.x :y screen.y
:radius 4
:intensity (math.floor (/ int 3))
:color star.color
:shape effects.GLOW_CIRCLE}))
(> star.brightness 120)
(do
(table.insert glows {:x screen.x :y screen.y
:radius 2
:intensity (math.floor (* int 0.67))
:color star.color
:shape effects.GLOW_DIAMOND})
(table.insert glows {:x screen.x :y screen.y
:radius 3
:intensity (math.floor (/ int 4))
:color star.color
:shape effects.GLOW_CIRCLE}))
(table.insert glows {:x screen.x :y screen.y
:radius 2
:intensity (math.floor (* int 0.5))
:color star.color
:shape effects.GLOW_CIRCLE}))))))
(when (> (length glows) 0)
(effects.glows glows)))))
(fn render [cam-x cam-y cam-z wireframe]
(when (not sky-mesh) (create-sky-dome))
(when sky-mesh
(pxl8.draw_mesh sky-mesh {:x cam-x :y cam-y :z cam-z :passthrough true :wireframe wireframe})))
{:render render
:render-stars render-stars
:generate-stars generate-stars
:update-gradient update-gradient
:SKY_GRADIENT_START SKY_GRADIENT_START
:SKY_GRADIENT_COUNT SKY_GRADIENT_COUNT}

View file

@ -1,168 +0,0 @@
(local pxl8 (require :pxl8))
(local procgen (require :pxl8.procgen))
(local textures {})
(fn build-graph [seed builder]
(let [g (pxl8.create_graph 128)
x (g:add_node procgen.OP_INPUT_X 0 0 0 0 0)
y (g:add_node procgen.OP_INPUT_Y 0 0 0 0 0)
ctx {:graph g :x x :y y}]
(g:set_seed seed)
(let [output (builder ctx)]
(g:set_output output)
g)))
(fn const [ctx val]
(ctx.graph:add_node procgen.OP_CONST 0 0 0 0 val))
(fn add [ctx a b]
(ctx.graph:add_node procgen.OP_ADD a b 0 0 0))
(fn sub [ctx a b]
(ctx.graph:add_node procgen.OP_SUB a b 0 0 0))
(fn mul [ctx a b]
(ctx.graph:add_node procgen.OP_MUL a b 0 0 0))
(fn div [ctx a b]
(ctx.graph:add_node procgen.OP_DIV a b 0 0 0))
(fn min-op [ctx a b]
(ctx.graph:add_node procgen.OP_MIN a b 0 0 0))
(fn max-op [ctx a b]
(ctx.graph:add_node procgen.OP_MAX a b 0 0 0))
(fn abs [ctx a]
(ctx.graph:add_node procgen.OP_ABS a 0 0 0 0))
(fn floor [ctx a]
(ctx.graph:add_node procgen.OP_FLOOR a 0 0 0 0))
(fn fract [ctx a]
(ctx.graph:add_node procgen.OP_FRACT a 0 0 0 0))
(fn lerp [ctx a b t]
(ctx.graph:add_node procgen.OP_LERP a b t 0 0))
(fn clamp [ctx val lo hi]
(ctx.graph:add_node procgen.OP_CLAMP val lo hi 0 0))
(fn select [ctx cond a b]
(ctx.graph:add_node procgen.OP_SELECT a b cond 0 0))
(fn smoothstep [ctx edge0 edge1 x]
(ctx.graph:add_node procgen.OP_SMOOTHSTEP edge0 edge1 x 0 0))
(fn noise-value [ctx scale]
(let [s (const ctx scale)]
(ctx.graph:add_node procgen.OP_NOISE_VALUE ctx.x ctx.y s 0 0)))
(fn noise-perlin [ctx scale]
(let [s (const ctx scale)]
(ctx.graph:add_node procgen.OP_NOISE_PERLIN ctx.x ctx.y s 0 0)))
(fn noise-fbm [ctx octaves scale persistence]
(let [s (const ctx scale)
p (const ctx persistence)]
(ctx.graph:add_node procgen.OP_NOISE_FBM ctx.x ctx.y s p octaves)))
(fn noise-ridged [ctx octaves scale persistence]
(let [s (const ctx scale)
p (const ctx persistence)]
(ctx.graph:add_node procgen.OP_NOISE_RIDGED ctx.x ctx.y s p octaves)))
(fn noise-turbulence [ctx octaves scale persistence]
(let [s (const ctx scale)
p (const ctx persistence)]
(ctx.graph:add_node procgen.OP_NOISE_TURBULENCE ctx.x ctx.y s p octaves)))
(fn voronoi-cell [ctx scale]
(let [s (const ctx scale)]
(ctx.graph:add_node procgen.OP_VORONOI_CELL ctx.x ctx.y s 0 0)))
(fn voronoi-edge [ctx scale]
(let [s (const ctx scale)]
(ctx.graph:add_node procgen.OP_VORONOI_EDGE ctx.x ctx.y s 0 0)))
(fn voronoi-id [ctx scale]
(let [s (const ctx scale)]
(ctx.graph:add_node procgen.OP_VORONOI_ID ctx.x ctx.y s 0 0)))
(fn gradient-linear [ctx angle]
(let [a (const ctx angle)]
(ctx.graph:add_node procgen.OP_GRADIENT_LINEAR ctx.x ctx.y a 0 0)))
(fn gradient-radial [ctx cx cy]
(let [center-x (const ctx cx)
center-y (const ctx cy)]
(ctx.graph:add_node procgen.OP_GRADIENT_RADIAL ctx.x ctx.y center-x center-y 0)))
(fn quantize [ctx val base range]
(let [r (const ctx range)]
(ctx.graph:add_node procgen.OP_QUANTIZE val r 0 0 base)))
(fn textures.mossy-cobblestone [seed base-color moss-color]
(let [g (build-graph seed
(fn [ctx]
(let [cell (voronoi-cell ctx 6)
edge (voronoi-edge ctx 6)
mortar-threshold (const ctx 0.05)
is-mortar (sub ctx mortar-threshold edge)
mortar-color (const ctx (- base-color 2))
stone-detail (noise-value ctx 48)
stone-base (mul ctx cell (const ctx 0.6))
stone-combined (add ctx stone-base (mul ctx stone-detail (const ctx 0.4)))
stone-quant (quantize ctx stone-combined base-color 8)
moss-pattern (noise-fbm ctx 4 10 0.5)
moss-detail (noise-value ctx 64)
moss-var (add ctx (mul ctx moss-pattern (const ctx 0.7))
(mul ctx moss-detail (const ctx 0.3)))
moss-threshold (const ctx 0.55)
has-moss (sub ctx moss-pattern moss-threshold)
moss-quant (quantize ctx moss-var moss-color 6)
stone-or-moss (select ctx has-moss moss-quant stone-quant)]
(select ctx is-mortar mortar-color stone-or-moss))))]
(let [tex-id (g:eval_texture 64 64)]
(g:destroy)
tex-id)))
(fn textures.ashlar-wall [seed base-color moss-color]
(let [g (build-graph seed
(fn [ctx]
(let [cell (voronoi-cell ctx 5)
edge (voronoi-edge ctx 5)
cell-id (voronoi-id ctx 5)
mortar-threshold (const ctx 0.12)
is-mortar (sub ctx mortar-threshold edge)
stone-detail (noise-fbm ctx 3 32 0.5)
stone-tint (mul ctx cell-id (const ctx 0.4))
stone-shade (mul ctx cell (const ctx 0.3))
stone-combined (add ctx stone-detail (add ctx stone-tint stone-shade))
stone-quant (quantize ctx stone-combined base-color 10)
crack-moss (noise-fbm ctx 3 16 0.5)
moss-in-crack (mul ctx crack-moss (sub ctx (const ctx 0.2) edge))
moss-threshold (const ctx 0.06)
has-moss (sub ctx moss-in-crack moss-threshold)
moss-quant (quantize ctx crack-moss moss-color 4)
mortar-color (const ctx (+ base-color 1))
stone-or-moss (select ctx has-moss moss-quant stone-quant)]
(select ctx is-mortar mortar-color stone-or-moss))))]
(let [tex-id (g:eval_texture 64 64)]
(g:destroy)
tex-id)))
(fn textures.gradient-sky [seed zenith-color horizon-color]
(let [g (build-graph seed
(fn [ctx]
(let [grad (gradient-linear ctx (* math.pi 0.5))
zenith (const ctx zenith-color)
horizon (const ctx horizon-color)
range (const ctx (- horizon-color zenith-color))]
(quantize ctx grad zenith-color (- horizon-color zenith-color)))))]
(let [tex-id (g:eval_texture 64 64)]
(g:destroy)
tex-id)))
textures

View file

@ -1,75 +0,0 @@
(local vfx {})
(fn vfx.explosion [ps x y ?opts]
(let [opts (or ?opts {})
color (or opts.color 208)
force (or opts.force 200)]
(ps:set_position x y)
(ps:set_colors color (+ color 15))
(ps:set_velocity (- force) force (- force) force)
(ps:set_gravity 0 100)
(ps:set_life 0.3 0.8)
(ps:set_size 1 3)
(ps:set_drag 0.98)
(ps:set_spawn_rate 0)
(ps:emit (or opts.count 50))))
(fn vfx.fire [ps x y ?opts]
(let [opts (or ?opts {})
width (or opts.width 50)
color (or opts.color 208)]
(ps:set_position x y)
(ps:set_spread width 5)
(ps:set_colors color (+ color 15))
(ps:set_velocity -20 20 -80 -40)
(ps:set_gravity 0 -30)
(ps:set_life 0.5 1.5)
(ps:set_size 1 2)
(ps:set_turbulence 30)
(ps:set_drag 0.95)
(ps:set_spawn_rate (or opts.rate 50))))
(fn vfx.rain [ps width ?opts]
(let [opts (or ?opts {})
wind (or opts.wind 0)
color (or opts.color 153)]
(ps:set_position (/ width 2) -10)
(ps:set_spread width 0)
(ps:set_colors color (+ color 3))
(ps:set_velocity (- wind 10) (+ wind 10) 300 400)
(ps:set_gravity 0 200)
(ps:set_life 1 2)
(ps:set_size 1 1)
(ps:set_drag 1)
(ps:set_spawn_rate (or opts.rate 100))))
(fn vfx.smoke [ps x y ?opts]
(let [opts (or ?opts {})
color (or opts.color 248)]
(ps:set_position x y)
(ps:set_spread 10 5)
(ps:set_colors color (+ color 7))
(ps:set_velocity -15 15 -30 -10)
(ps:set_gravity 0 -20)
(ps:set_life 1 3)
(ps:set_size 2 4)
(ps:set_turbulence 20)
(ps:set_drag 0.98)
(ps:set_spawn_rate (or opts.rate 20))))
(fn vfx.snow [ps width ?opts]
(let [opts (or ?opts {})
wind (or opts.wind 10)
color (or opts.color 15)]
(ps:set_position (/ width 2) -10)
(ps:set_spread width 0)
(ps:set_colors color color)
(ps:set_velocity (- wind 20) (+ wind 20) 30 60)
(ps:set_gravity 0 10)
(ps:set_life 3 6)
(ps:set_size 1 2)
(ps:set_turbulence 15)
(ps:set_drag 0.99)
(ps:set_spawn_rate (or opts.rate 30))))
vfx

224
demo/mod/worldgen.fnl Normal file
View file

@ -0,0 +1,224 @@
(local pxl8 (require :pxl8))
(local bob-amount 4.0)
(local bob-speed 8.0)
(local cam-smoothing 0.25)
(local cell-size 64)
(local cursor-sensitivity 0.008)
(local gravity -800)
(local grid-size 64)
(local ground-y 64)
(local jump-force 175)
(local land-recovery-speed 20)
(local land-squash-amount -4)
(local max-pitch 1.5)
(local move-speed 200)
(local turn-speed 2.0)
(var auto-run? false)
(var auto-run-cancel-key nil)
(var bob-time 0)
(var cam-pitch 0)
(var cam-x 1000)
(var cam-y 64)
(var cam-yaw 0)
(var cam-z 1000)
(var camera nil)
(var cursor-look? true)
(var grounded? true)
(var land-squash 0)
(var smooth-cam-x 1000)
(var smooth-cam-z 1000)
(var velocity-y 0)
(var world nil)
(fn init []
(set camera (pxl8.create_camera_3d))
(set world (pxl8.create_world))
(let [result (world:generate {
:type pxl8.PROCGEN_ROOMS
:width 64
:height 64
:seed 42
:min_room_size 5
:max_room_size 10
:num_rooms 20})]
(if (< result 0)
(pxl8.error (.. "Failed to generate rooms - result: " result))
(let [floor-tex (pxl8.procgen_tex {:name "floor"
:seed 11111
:width 64
:height 64
:base_color 19})
ceiling-tex (pxl8.procgen_tex {:name "ceiling"
:seed 22222
:width 64
:height 64
:base_color 1})
wall-tex (pxl8.procgen_tex {:name "wall"
:seed 12345
:width 64
:height 64
:base_color 4})]
(let [result (world:apply_textures [
{:name "floor"
:texture_id floor-tex
:rule (fn [normal] (> normal.y 0.7))}
{:name "ceiling"
:texture_id ceiling-tex
:rule (fn [normal] (< normal.y -0.7))}
{:name "wall"
:texture_id wall-tex
:rule (fn [normal] (and (<= normal.y 0.7) (>= normal.y -0.7)))}])]
(when (< result 0)
(pxl8.error (.. "Failed to apply textures - result: " result))))))))
(fn update [dt]
(when (pxl8.key_pressed "`")
(set auto-run? (not auto-run?))
(when (and auto-run? (pxl8.key_down "w"))
(set auto-run-cancel-key "w")))
(when (and auto-run? (not auto-run-cancel-key) (or (pxl8.key_down "w") (pxl8.key_down "s")))
(set auto-run? false)
(when (pxl8.key_down "s")
(set auto-run-cancel-key "s")))
(when (and auto-run-cancel-key (not (pxl8.key_down auto-run-cancel-key)))
(set auto-run-cancel-key nil))
(when (world:is_loaded)
(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))
grid-max (* grid-size cell-size)
move-delta (* move-speed dt)]
(var moving false)
(var move-forward 0)
(var move-right 0)
(when (or (pxl8.key_down "w") auto-run?)
(set move-forward (+ move-forward 1)))
(when (and (pxl8.key_down "s") (not= auto-run-cancel-key "s"))
(set move-forward (- move-forward 1)))
(when (pxl8.key_down "a")
(set move-right (- move-right 1)))
(when (pxl8.key_down "d")
(set move-right (+ move-right 1)))
(set moving (or (not= move-forward 0) (not= move-right 0)))
(var new-x cam-x)
(var new-z cam-z)
(when moving
(let [len (math.sqrt (+ (* move-forward move-forward) (* move-right move-right)))
norm-forward (/ move-forward len)
norm-right (/ move-right len)]
(set new-x (+ new-x (* move-delta (+ (* forward-x norm-forward) (* right-x norm-right)))))
(set new-z (+ new-z (* move-delta (+ (* forward-z norm-forward) (* right-z norm-right)))))))
(when (and (>= new-x 0) (<= new-x grid-max)
(>= new-z 0) (<= new-z grid-max))
(let [(resolved-x resolved-y resolved-z) (world:resolve_collision cam-x cam-y cam-z new-x cam-y new-z 5)]
(set cam-x resolved-x)
(set cam-z resolved-z)))
(set smooth-cam-x (+ (* smooth-cam-x (- 1 cam-smoothing)) (* cam-x cam-smoothing)))
(set smooth-cam-z (+ (* smooth-cam-z (- 1 cam-smoothing)) (* cam-z cam-smoothing)))
(when cursor-look?
(let [dx (pxl8.mouse_dx)
dy (pxl8.mouse_dy)]
(set cam-yaw (- cam-yaw (* dx cursor-sensitivity)))
(set cam-pitch (math.max (- max-pitch)
(math.min max-pitch
(- cam-pitch (* dy cursor-sensitivity)))))))
(when (and (not cursor-look?) (pxl8.key_down "left"))
(set cam-yaw (+ cam-yaw (* turn-speed dt))))
(when (and (not cursor-look?) (pxl8.key_down "right"))
(set cam-yaw (- cam-yaw (* turn-speed dt))))
(when (and (not cursor-look?) (pxl8.key_down "up"))
(set cam-pitch (math.min max-pitch (+ cam-pitch (* turn-speed dt)))))
(when (and (not cursor-look?) (pxl8.key_down "down"))
(set cam-pitch (math.max (- max-pitch) (- cam-pitch (* turn-speed dt)))))
(when (and (pxl8.key_pressed "space") grounded?)
(set velocity-y jump-force)
(set grounded? false))
(set velocity-y (+ velocity-y (* gravity dt)))
(set cam-y (+ cam-y (* velocity-y dt)))
(when (<= cam-y ground-y)
(when (not grounded?)
(set land-squash land-squash-amount))
(set cam-y ground-y)
(set velocity-y 0)
(set grounded? true))
(when (< land-squash 0)
(set land-squash (math.min 0 (+ land-squash (* land-recovery-speed dt)))))
(if (and moving grounded?)
(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))))))))
(fn frame []
(pxl8.clear 0)
(when (not camera)
(pxl8.error "camera is nil!"))
(when (not world)
(pxl8.error "world is nil!"))
(when (and world (not (world:is_loaded)))
(pxl8.text "World not loaded yet..." 5 30 12))
(when (and camera world (world:is_loaded))
(let [bob-offset (* (math.sin bob-time) bob-amount)
eye-y (+ cam-y bob-offset land-squash)
forward-x (- (math.sin cam-yaw))
forward-z (- (math.cos cam-yaw))
target-x (+ smooth-cam-x forward-x)
target-y (+ eye-y (math.sin cam-pitch))
target-z (+ smooth-cam-z forward-z)
aspect (/ (pxl8.get_width) (pxl8.get_height))]
(camera:lookat [smooth-cam-x eye-y smooth-cam-z]
[target-x target-y target-z]
[0 1 0])
(camera:set_perspective 1.047 aspect 1.0 4096.0)
(pxl8.begin_frame_3d camera)
(pxl8.clear_depth)
(world:render [smooth-cam-x eye-y smooth-cam-z])
(pxl8.end_frame_3d)
(let [cx (/ (pxl8.get_width) 2)
cy (/ (pxl8.get_height) 2)
crosshair-size 4
red-color 18]
(pxl8.line (- cx crosshair-size) cy (+ cx crosshair-size) cy red-color)
(pxl8.line cx (- cy crosshair-size) cx (+ cy crosshair-size) red-color))
(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-z)) 5 15 12))))
{:init init
:update update
:frame frame}

Some files were not shown because too many files have changed in this diff Show more