Compare commits
No commits in common. "71787869e06101f0d47c6ee01f7147260cacf1b4" and "39b604b333c1f410c40b63c5d0d748614385fd8e" have entirely different histories.
71787869e0
...
39b604b333
136 changed files with 1915 additions and 13442 deletions
|
|
@ -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"),
|
||||||
|
|
@ -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
126
client/src/core/pxl8_io.h
Normal 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
|
||||||
|
|
@ -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 {
|
||||||
|
|
@ -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) {
|
||||||
|
|
@ -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);
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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) {
|
||||||
79
client/src/gfx/pxl8_colormap.c
Normal file
79
client/src/gfx/pxl8_colormap.c
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
43
client/src/gfx/pxl8_colormap.h
Normal file
43
client/src/gfx/pxl8_colormap.h
Normal 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
|
||||||
|
|
@ -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];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
@ -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
48
client/src/gfx/pxl8_gfx.h
Normal 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
|
||||||
33
client/src/gfx/pxl8_gfx3d.h
Normal file
33
client/src/gfx/pxl8_gfx3d.h
Normal 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
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
@ -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];
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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);
|
||||||
|
|
@ -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]];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
@ -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()
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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)
|
||||||
65
client/src/lua/pxl8/vfx.lua
Normal file
65
client/src/lua/pxl8/vfx.lua
Normal 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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
|
|
@ -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
299
client/src/math/pxl8_simd.h
Normal 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
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -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";
|
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -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
504
client/src/world/pxl8_gen.c
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
@ -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
|
|
@ -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}
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)}
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
271
demo/mod/sky.fnl
271
demo/mod/sky.fnl
|
|
@ -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}
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
224
demo/mod/worldgen.fnl
Normal 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
Loading…
Add table
Add a link
Reference in a new issue