refactor: reorganize pxl8 into client/src/ module structure
- core/: main entry, types, logging, I/O, RNG - asset/: ase loader, cart, save, embed - gfx/: graphics, animation, atlas, fonts, tilemap, transitions - sfx/: audio - script/: lua/fennel runtime, REPL - hal/: platform abstraction (SDL3) - world/: BSP, world, procedural gen - math/: math utilities - game/: GUI, replay - lua/: Lua API modules
This commit is contained in:
parent
272e0bc615
commit
39b604b333
106 changed files with 6078 additions and 3715 deletions
|
|
@ -79,7 +79,7 @@ static pxl8_result parse_old_palette_chunk(pxl8_stream* stream, pxl8_ase_palette
|
|||
u8 r = pxl8_read_u8(stream);
|
||||
u8 g = pxl8_read_u8(stream);
|
||||
u8 b = pxl8_read_u8(stream);
|
||||
palette->colors[color_index++] = 0xFF000000 | (b << 16) | (g << 8) | r;
|
||||
palette->colors[color_index++] = 0xFF000000 | ((u32)b << 16) | ((u32)g << 8) | r;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -134,7 +134,7 @@ static pxl8_result parse_palette_chunk(pxl8_stream* stream, pxl8_ase_palette* pa
|
|||
u8 b = pxl8_read_u8(stream);
|
||||
u8 a = pxl8_read_u8(stream);
|
||||
|
||||
palette->colors[i] = (a << 24) | (b << 16) | (g << 8) | r;
|
||||
palette->colors[i] = ((u32)a << 24) | ((u32)b << 16) | ((u32)g << 8) | r;
|
||||
|
||||
if (flags & 1) {
|
||||
u16 name_len = pxl8_read_u16(stream);
|
||||
|
|
@ -166,7 +166,7 @@ static pxl8_result parse_user_data_chunk(pxl8_stream* stream, pxl8_ase_user_data
|
|||
u8 g = pxl8_read_u8(stream);
|
||||
u8 b = pxl8_read_u8(stream);
|
||||
u8 a = pxl8_read_u8(stream);
|
||||
user_data->color = (a << 24) | (b << 16) | (g << 8) | r;
|
||||
user_data->color = ((u32)a << 24) | ((u32)b << 16) | ((u32)g << 8) | r;
|
||||
}
|
||||
|
||||
if (flags & 4) {
|
||||
|
|
@ -8,59 +8,55 @@ static const char embed_fennel[] = {
|
|||
, 0 };
|
||||
|
||||
static const char embed_pxl8[] = {
|
||||
#embed "src/lua/pxl8.lua"
|
||||
#embed "client/src/lua/pxl8.lua"
|
||||
, 0 };
|
||||
|
||||
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_core[] = {
|
||||
#embed "src/lua/pxl8/core.lua"
|
||||
#embed "client/src/lua/pxl8/core.lua"
|
||||
, 0 };
|
||||
|
||||
static const char embed_pxl8_gfx2d[] = {
|
||||
#embed "src/lua/pxl8/gfx2d.lua"
|
||||
#embed "client/src/lua/pxl8/gfx2d.lua"
|
||||
, 0 };
|
||||
|
||||
static const char embed_pxl8_gfx3d[] = {
|
||||
#embed "src/lua/pxl8/gfx3d.lua"
|
||||
#embed "client/src/lua/pxl8/gfx3d.lua"
|
||||
, 0 };
|
||||
|
||||
static const char embed_pxl8_gui[] = {
|
||||
#embed "src/lua/pxl8/gui.lua"
|
||||
#embed "client/src/lua/pxl8/gui.lua"
|
||||
, 0 };
|
||||
|
||||
static const char embed_pxl8_input[] = {
|
||||
#embed "src/lua/pxl8/input.lua"
|
||||
#embed "client/src/lua/pxl8/input.lua"
|
||||
, 0 };
|
||||
|
||||
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_particles[] = {
|
||||
#embed "src/lua/pxl8/particles.lua"
|
||||
#embed "client/src/lua/pxl8/particles.lua"
|
||||
, 0 };
|
||||
|
||||
static const char embed_pxl8_sfx[] = {
|
||||
#embed "src/lua/pxl8/sfx.lua"
|
||||
#embed "client/src/lua/pxl8/sfx.lua"
|
||||
, 0 };
|
||||
|
||||
static const char embed_pxl8_tilemap[] = {
|
||||
#embed "src/lua/pxl8/tilemap.lua"
|
||||
#embed "client/src/lua/pxl8/tilemap.lua"
|
||||
, 0 };
|
||||
|
||||
static const char embed_pxl8_transition[] = {
|
||||
#embed "src/lua/pxl8/transition.lua"
|
||||
, 0 };
|
||||
|
||||
static const char embed_pxl8_vfx[] = {
|
||||
#embed "src/lua/pxl8/vfx.lua"
|
||||
#embed "client/src/lua/pxl8/transition.lua"
|
||||
, 0 };
|
||||
|
||||
static const char embed_pxl8_world[] = {
|
||||
#embed "src/lua/pxl8/world.lua"
|
||||
#embed "client/src/lua/pxl8/world.lua"
|
||||
, 0 };
|
||||
|
||||
#define PXL8_EMBED_ENTRY(var, name) {name, var, sizeof(var) - 1}
|
||||
|
|
@ -81,7 +77,6 @@ static const pxl8_embed pxl8_embeds[] = {
|
|||
PXL8_EMBED_ENTRY(embed_pxl8_sfx, "pxl8.sfx"),
|
||||
PXL8_EMBED_ENTRY(embed_pxl8_tilemap, "pxl8.tilemap"),
|
||||
PXL8_EMBED_ENTRY(embed_pxl8_transition, "pxl8.transition"),
|
||||
PXL8_EMBED_ENTRY(embed_pxl8_vfx, "pxl8.vfx"),
|
||||
PXL8_EMBED_ENTRY(embed_pxl8_world, "pxl8.world"),
|
||||
{0}
|
||||
};
|
||||
|
|
@ -360,7 +360,7 @@ pxl8_result pxl8_frame(pxl8* sys) {
|
|||
pxl8_error("error calling frame: %s", pxl8_script_get_last_error(game->script));
|
||||
}
|
||||
} else {
|
||||
pxl8_clear(game->gfx, 32);
|
||||
pxl8_2d_clear(game->gfx, 32);
|
||||
|
||||
i32 render_w = pxl8_gfx_get_width(game->gfx);
|
||||
i32 render_h = pxl8_gfx_get_height(game->gfx);
|
||||
|
|
@ -368,7 +368,7 @@ pxl8_result pxl8_frame(pxl8* sys) {
|
|||
for (i32 y = 0; y < render_h; y += 24) {
|
||||
for (i32 x = 0; x < render_w; x += 32) {
|
||||
u32 color = ((x / 32) + (y / 24) + (i32)(game->time * 2)) % 8;
|
||||
pxl8_rect_fill(game->gfx, x, y, 31, 23, color);
|
||||
pxl8_2d_rect_fill(game->gfx, x, y, 31, 23, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,8 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pxl8_gfx.h"
|
||||
|
||||
pxl8_gui_state* pxl8_gui_state_create(void) {
|
||||
pxl8_gui_state* state = (pxl8_gui_state*)malloc(sizeof(pxl8_gui_state));
|
||||
if (!state) return NULL;
|
||||
|
|
@ -90,13 +92,13 @@ bool pxl8_gui_button(pxl8_gui_state* state, pxl8_gfx* gfx, u32 id, i32 x, i32 y,
|
|||
border_color = 4;
|
||||
}
|
||||
|
||||
pxl8_rect_fill(gfx, x, y, w, h, bg_color);
|
||||
pxl8_rect(gfx, x, y, w, h, border_color);
|
||||
pxl8_2d_rect_fill(gfx, x, y, w, h, bg_color);
|
||||
pxl8_2d_rect(gfx, x, y, w, h, border_color);
|
||||
|
||||
i32 text_len = (i32)strlen(label);
|
||||
i32 text_x = x + (w / 2) - ((text_len * 8) / 2) + offset_x;
|
||||
i32 text_y = y + (h / 2) - 5 + offset_y;
|
||||
pxl8_text(gfx, label, text_x, text_y, 6);
|
||||
pxl8_2d_text(gfx, label, text_x, text_y, 6);
|
||||
|
||||
return clicked;
|
||||
}
|
||||
|
|
@ -104,19 +106,19 @@ 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) {
|
||||
if (!gfx || !title) return;
|
||||
|
||||
pxl8_rect_fill(gfx, x, y, w, 28, 1);
|
||||
pxl8_rect_fill(gfx, x, y + 28, w, h - 28, 2);
|
||||
pxl8_rect(gfx, x, y, w, h, 4);
|
||||
pxl8_rect_fill(gfx, x, y + 28, w, 1, 4);
|
||||
pxl8_2d_rect_fill(gfx, x, y, w, 28, 1);
|
||||
pxl8_2d_rect_fill(gfx, x, y + 28, w, h - 28, 2);
|
||||
pxl8_2d_rect(gfx, x, y, w, h, 4);
|
||||
pxl8_2d_rect_fill(gfx, x, y + 28, w, 1, 4);
|
||||
|
||||
i32 title_x = x + 10;
|
||||
i32 title_y = y + (28 / 2) - 5;
|
||||
pxl8_text(gfx, title, title_x, title_y, 8);
|
||||
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) {
|
||||
if (!gfx || !text) return;
|
||||
pxl8_text(gfx, text, x, y, color);
|
||||
pxl8_2d_text(gfx, text, x, y, color);
|
||||
}
|
||||
|
||||
bool pxl8_gui_is_hovering(const pxl8_gui_state* state) {
|
||||
214
client/src/gfx/pxl8_3d_camera.c
Normal file
214
client/src/gfx/pxl8_3d_camera.c
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
#include "pxl8_3d_camera.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct pxl8_3d_camera {
|
||||
pxl8_vec3 position;
|
||||
f32 pitch;
|
||||
f32 roll;
|
||||
f32 yaw;
|
||||
|
||||
pxl8_3d_camera_mode mode;
|
||||
|
||||
f32 aspect;
|
||||
f32 far;
|
||||
f32 fov;
|
||||
f32 near;
|
||||
|
||||
f32 ortho_bottom;
|
||||
f32 ortho_left;
|
||||
f32 ortho_right;
|
||||
f32 ortho_top;
|
||||
|
||||
f32 shake_duration;
|
||||
f32 shake_intensity;
|
||||
pxl8_vec3 shake_offset;
|
||||
f32 shake_timer;
|
||||
};
|
||||
|
||||
pxl8_3d_camera* pxl8_3d_camera_create(void) {
|
||||
pxl8_3d_camera* cam = calloc(1, sizeof(pxl8_3d_camera));
|
||||
if (!cam) return NULL;
|
||||
|
||||
cam->position = (pxl8_vec3){0, 0, 0};
|
||||
cam->pitch = 0;
|
||||
cam->yaw = 0;
|
||||
cam->roll = 0;
|
||||
|
||||
cam->mode = PXL8_3D_CAMERA_PERSPECTIVE;
|
||||
cam->fov = 1.0f;
|
||||
cam->aspect = 16.0f / 9.0f;
|
||||
cam->near = 1.0f;
|
||||
cam->far = 1000.0f;
|
||||
|
||||
return cam;
|
||||
}
|
||||
|
||||
void pxl8_3d_camera_destroy(pxl8_3d_camera* cam) {
|
||||
free(cam);
|
||||
}
|
||||
|
||||
void pxl8_3d_camera_lookat(pxl8_3d_camera* cam, pxl8_vec3 eye, pxl8_vec3 target, pxl8_vec3 up) {
|
||||
if (!cam) return;
|
||||
|
||||
cam->position = eye;
|
||||
|
||||
pxl8_vec3 forward = pxl8_vec3_normalize(pxl8_vec3_sub(target, eye));
|
||||
|
||||
cam->pitch = asinf(-forward.y);
|
||||
cam->yaw = atan2f(forward.x, forward.z);
|
||||
cam->roll = 0;
|
||||
|
||||
(void)up;
|
||||
}
|
||||
|
||||
void pxl8_3d_camera_set_ortho(pxl8_3d_camera* cam, f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far) {
|
||||
if (!cam) return;
|
||||
cam->mode = PXL8_3D_CAMERA_ORTHO;
|
||||
cam->ortho_left = left;
|
||||
cam->ortho_right = right;
|
||||
cam->ortho_bottom = bottom;
|
||||
cam->ortho_top = top;
|
||||
cam->near = near;
|
||||
cam->far = far;
|
||||
}
|
||||
|
||||
void pxl8_3d_camera_set_perspective(pxl8_3d_camera* cam, f32 fov, f32 aspect, f32 near, f32 far) {
|
||||
if (!cam) return;
|
||||
cam->mode = PXL8_3D_CAMERA_PERSPECTIVE;
|
||||
cam->fov = fov;
|
||||
cam->aspect = aspect;
|
||||
cam->near = near;
|
||||
cam->far = far;
|
||||
}
|
||||
|
||||
void pxl8_3d_camera_set_position(pxl8_3d_camera* cam, pxl8_vec3 pos) {
|
||||
if (!cam) return;
|
||||
cam->position = pos;
|
||||
}
|
||||
|
||||
void pxl8_3d_camera_set_rotation(pxl8_3d_camera* cam, f32 pitch, f32 yaw, f32 roll) {
|
||||
if (!cam) return;
|
||||
cam->pitch = pitch;
|
||||
cam->yaw = yaw;
|
||||
cam->roll = roll;
|
||||
}
|
||||
|
||||
pxl8_vec3 pxl8_3d_camera_get_forward(const pxl8_3d_camera* cam) {
|
||||
if (!cam) return (pxl8_vec3){0, 0, -1};
|
||||
|
||||
f32 cp = cosf(cam->pitch);
|
||||
f32 sp = sinf(cam->pitch);
|
||||
f32 cy = cosf(cam->yaw);
|
||||
f32 sy = sinf(cam->yaw);
|
||||
|
||||
return (pxl8_vec3){
|
||||
cp * sy,
|
||||
-sp,
|
||||
cp * cy
|
||||
};
|
||||
}
|
||||
|
||||
pxl8_vec3 pxl8_3d_camera_get_position(const pxl8_3d_camera* cam) {
|
||||
if (!cam) return (pxl8_vec3){0, 0, 0};
|
||||
return pxl8_vec3_add(cam->position, cam->shake_offset);
|
||||
}
|
||||
|
||||
pxl8_mat4 pxl8_3d_camera_get_projection(const pxl8_3d_camera* cam) {
|
||||
if (!cam) return pxl8_mat4_identity();
|
||||
|
||||
if (cam->mode == PXL8_3D_CAMERA_PERSPECTIVE) {
|
||||
return pxl8_mat4_perspective(cam->fov, cam->aspect, cam->near, cam->far);
|
||||
} else {
|
||||
return pxl8_mat4_ortho(
|
||||
cam->ortho_left, cam->ortho_right,
|
||||
cam->ortho_bottom, cam->ortho_top,
|
||||
cam->near, cam->far
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pxl8_vec3 pxl8_3d_camera_get_right(const pxl8_3d_camera* cam) {
|
||||
if (!cam) return (pxl8_vec3){1, 0, 0};
|
||||
|
||||
f32 cy = cosf(cam->yaw);
|
||||
f32 sy = sinf(cam->yaw);
|
||||
|
||||
return (pxl8_vec3){cy, 0, -sy};
|
||||
}
|
||||
|
||||
pxl8_vec3 pxl8_3d_camera_get_up(const pxl8_3d_camera* cam) {
|
||||
if (!cam) return (pxl8_vec3){0, 1, 0};
|
||||
|
||||
pxl8_vec3 forward = pxl8_3d_camera_get_forward(cam);
|
||||
pxl8_vec3 right = pxl8_3d_camera_get_right(cam);
|
||||
|
||||
return pxl8_vec3_cross(forward, right);
|
||||
}
|
||||
|
||||
pxl8_mat4 pxl8_3d_camera_get_view(const pxl8_3d_camera* cam) {
|
||||
if (!cam) return pxl8_mat4_identity();
|
||||
|
||||
pxl8_vec3 pos = pxl8_3d_camera_get_position(cam);
|
||||
pxl8_vec3 forward = pxl8_3d_camera_get_forward(cam);
|
||||
pxl8_vec3 target = pxl8_vec3_add(pos, forward);
|
||||
pxl8_vec3 up = (pxl8_vec3){0, 1, 0};
|
||||
|
||||
return pxl8_mat4_lookat(pos, target, up);
|
||||
}
|
||||
|
||||
void pxl8_3d_camera_blend(pxl8_3d_camera* dest, const pxl8_3d_camera* a, const pxl8_3d_camera* b, f32 t) {
|
||||
if (!dest || !a || !b) return;
|
||||
|
||||
dest->position = pxl8_vec3_lerp(a->position, b->position, t);
|
||||
dest->pitch = a->pitch + (b->pitch - a->pitch) * t;
|
||||
dest->yaw = a->yaw + (b->yaw - a->yaw) * t;
|
||||
dest->roll = a->roll + (b->roll - a->roll) * t;
|
||||
|
||||
dest->fov = a->fov + (b->fov - a->fov) * t;
|
||||
dest->aspect = a->aspect + (b->aspect - a->aspect) * t;
|
||||
dest->near = a->near + (b->near - a->near) * t;
|
||||
dest->far = a->far + (b->far - a->far) * t;
|
||||
|
||||
dest->mode = (t < 0.5f) ? a->mode : b->mode;
|
||||
}
|
||||
|
||||
void pxl8_3d_camera_follow(pxl8_3d_camera* cam, pxl8_vec3 target, pxl8_vec3 offset, f32 smoothing, f32 dt) {
|
||||
if (!cam) return;
|
||||
|
||||
pxl8_vec3 desired = pxl8_vec3_add(target, offset);
|
||||
|
||||
f32 t = 1.0f - powf(1.0f - smoothing, dt * 60.0f);
|
||||
cam->position = pxl8_vec3_lerp(cam->position, desired, t);
|
||||
|
||||
pxl8_vec3 forward = pxl8_vec3_normalize(pxl8_vec3_sub(target, cam->position));
|
||||
cam->pitch = asinf(-forward.y);
|
||||
cam->yaw = atan2f(forward.x, forward.z);
|
||||
}
|
||||
|
||||
void pxl8_3d_camera_shake(pxl8_3d_camera* cam, f32 intensity, f32 duration) {
|
||||
if (!cam) return;
|
||||
cam->shake_intensity = intensity;
|
||||
cam->shake_duration = duration;
|
||||
cam->shake_timer = duration;
|
||||
}
|
||||
|
||||
void pxl8_3d_camera_update(pxl8_3d_camera* cam, f32 dt) {
|
||||
if (!cam) return;
|
||||
|
||||
if (cam->shake_timer > 0) {
|
||||
cam->shake_timer -= dt;
|
||||
|
||||
f32 decay = cam->shake_timer / cam->shake_duration;
|
||||
f32 intensity = cam->shake_intensity * decay;
|
||||
|
||||
cam->shake_offset.x = ((f32)rand() / (f32)RAND_MAX * 2.0f - 1.0f) * intensity;
|
||||
cam->shake_offset.y = ((f32)rand() / (f32)RAND_MAX * 2.0f - 1.0f) * intensity;
|
||||
cam->shake_offset.z = ((f32)rand() / (f32)RAND_MAX * 2.0f - 1.0f) * intensity;
|
||||
|
||||
if (cam->shake_timer <= 0) {
|
||||
cam->shake_offset = (pxl8_vec3){0, 0, 0};
|
||||
}
|
||||
}
|
||||
}
|
||||
40
client/src/gfx/pxl8_3d_camera.h
Normal file
40
client/src/gfx/pxl8_3d_camera.h
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_math.h"
|
||||
#include "pxl8_types.h"
|
||||
|
||||
typedef enum pxl8_3d_camera_mode {
|
||||
PXL8_3D_CAMERA_ORTHO,
|
||||
PXL8_3D_CAMERA_PERSPECTIVE
|
||||
} pxl8_3d_camera_mode;
|
||||
|
||||
typedef struct pxl8_3d_camera pxl8_3d_camera;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
pxl8_3d_camera* pxl8_3d_camera_create(void);
|
||||
void pxl8_3d_camera_destroy(pxl8_3d_camera* cam);
|
||||
|
||||
void pxl8_3d_camera_lookat(pxl8_3d_camera* cam, pxl8_vec3 eye, pxl8_vec3 target, pxl8_vec3 up);
|
||||
void pxl8_3d_camera_set_ortho(pxl8_3d_camera* cam, f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far);
|
||||
void pxl8_3d_camera_set_perspective(pxl8_3d_camera* cam, f32 fov, f32 aspect, f32 near, f32 far);
|
||||
void pxl8_3d_camera_set_position(pxl8_3d_camera* cam, pxl8_vec3 pos);
|
||||
void pxl8_3d_camera_set_rotation(pxl8_3d_camera* cam, f32 pitch, f32 yaw, f32 roll);
|
||||
|
||||
pxl8_vec3 pxl8_3d_camera_get_forward(const pxl8_3d_camera* cam);
|
||||
pxl8_vec3 pxl8_3d_camera_get_position(const pxl8_3d_camera* cam);
|
||||
pxl8_mat4 pxl8_3d_camera_get_projection(const pxl8_3d_camera* cam);
|
||||
pxl8_vec3 pxl8_3d_camera_get_right(const pxl8_3d_camera* cam);
|
||||
pxl8_vec3 pxl8_3d_camera_get_up(const pxl8_3d_camera* cam);
|
||||
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_follow(pxl8_3d_camera* cam, pxl8_vec3 target, pxl8_vec3 offset, f32 smoothing, f32 dt);
|
||||
void pxl8_3d_camera_shake(pxl8_3d_camera* cam, f32 intensity, f32 duration);
|
||||
void pxl8_3d_camera_update(pxl8_3d_camera* cam, f32 dt);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "pxl8_ase.h"
|
||||
#include "pxl8_atlas.h"
|
||||
#include "pxl8_gfx.h"
|
||||
#include "pxl8_log.h"
|
||||
|
||||
#define PXL8_ANIM_MAX_STATES 32
|
||||
|
|
@ -288,7 +289,7 @@ void pxl8_anim_render_sprite(const pxl8_anim* anim, pxl8_gfx* gfx, i32 x, i32 y,
|
|||
if (!anim || !gfx) return;
|
||||
|
||||
u32 sprite_id = pxl8_anim_get_current_frame_id(anim);
|
||||
pxl8_sprite(gfx, sprite_id, x, y, w, h, flip_x, flip_y);
|
||||
pxl8_2d_sprite(gfx, sprite_id, x, y, w, h, flip_x, flip_y);
|
||||
}
|
||||
|
||||
void pxl8_anim_reset(pxl8_anim* anim) {
|
||||
19
client/src/gfx/pxl8_backend.h
Normal file
19
client/src/gfx/pxl8_backend.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#ifndef PXL8_BACKEND_H
|
||||
#define PXL8_BACKEND_H
|
||||
|
||||
#include "pxl8_cpu.h"
|
||||
|
||||
typedef enum {
|
||||
PXL8_GFX_BACKEND_CPU,
|
||||
PXL8_GFX_BACKEND_GPU,
|
||||
} pxl8_gfx_backend_type;
|
||||
|
||||
typedef struct {
|
||||
pxl8_gfx_backend_type type;
|
||||
union {
|
||||
pxl8_cpu_backend* cpu;
|
||||
void* gpu;
|
||||
};
|
||||
} pxl8_gfx_backend;
|
||||
|
||||
#endif
|
||||
|
|
@ -23,7 +23,7 @@ static inline u16 pxl8_rgba32_to_rgb565(u32 rgba) {
|
|||
static inline u32 pxl8_rgb565_to_rgba32(u16 color) {
|
||||
u8 r, g, b;
|
||||
pxl8_rgb565_unpack(color, &r, &g, &b);
|
||||
return r | (g << 8) | (b << 16) | 0xFF000000;
|
||||
return r | ((u32)g << 8) | ((u32)b << 16) | 0xFF000000;
|
||||
}
|
||||
|
||||
static inline void pxl8_rgba32_unpack(u32 color, u8* r, u8* g, u8* b, u8* a) {
|
||||
|
|
@ -34,9 +34,38 @@ static inline void pxl8_rgba32_unpack(u32 color, u8* r, u8* g, u8* b, u8* a) {
|
|||
}
|
||||
|
||||
static inline u32 pxl8_rgba32_pack(u8 r, u8 g, u8 b, u8 a) {
|
||||
return r | (g << 8) | (b << 16) | (a << 24);
|
||||
return r | ((u32)g << 8) | ((u32)b << 16) | ((u32)a << 24);
|
||||
}
|
||||
|
||||
static inline u32 pxl8_color_to_rgba(u32 abgr) {
|
||||
u8 r = abgr & 0xFF;
|
||||
u8 g = (abgr >> 8) & 0xFF;
|
||||
u8 b = (abgr >> 16) & 0xFF;
|
||||
u8 a = (abgr >> 24) & 0xFF;
|
||||
return ((u32)r << 24) | ((u32)g << 16) | ((u32)b << 8) | a;
|
||||
}
|
||||
|
||||
static inline u32 pxl8_color_from_rgba(u32 rgba) {
|
||||
u8 r = (rgba >> 24) & 0xFF;
|
||||
u8 g = (rgba >> 16) & 0xFF;
|
||||
u8 b = (rgba >> 8) & 0xFF;
|
||||
u8 a = rgba & 0xFF;
|
||||
return r | ((u32)g << 8) | ((u32)b << 16) | ((u32)a << 24);
|
||||
}
|
||||
|
||||
static inline u8 pxl8_color_lerp_channel(u8 c1, u8 c2, f32 t) {
|
||||
return c1 + (i32)((c2 - c1) * t);
|
||||
}
|
||||
|
||||
static inline u8 pxl8_rgb332_pack(u8 r, u8 g, u8 b) {
|
||||
return ((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6);
|
||||
}
|
||||
|
||||
static inline void pxl8_rgb332_unpack(u8 c, u8* r, u8* g, u8* b) {
|
||||
u8 ri = (c >> 5) & 0x07;
|
||||
u8 gi = (c >> 2) & 0x07;
|
||||
u8 bi = c & 0x03;
|
||||
*r = (ri << 5) | (ri << 2) | (ri >> 1);
|
||||
*g = (gi << 5) | (gi << 2) | (gi >> 1);
|
||||
*b = (bi << 6) | (bi << 4) | (bi << 2) | bi;
|
||||
}
|
||||
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
|
||||
1075
client/src/gfx/pxl8_cpu.c
Normal file
1075
client/src/gfx/pxl8_cpu.c
Normal file
File diff suppressed because it is too large
Load diff
94
client/src/gfx/pxl8_cpu.h
Normal file
94
client/src/gfx/pxl8_cpu.h
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_atlas.h"
|
||||
#include "pxl8_colormap.h"
|
||||
#include "pxl8_math.h"
|
||||
#include "pxl8_mesh.h"
|
||||
#include "pxl8_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct pxl8_cpu_backend pxl8_cpu_backend;
|
||||
typedef struct pxl8_cpu_render_target pxl8_cpu_render_target;
|
||||
|
||||
typedef struct pxl8_cpu_render_target_desc {
|
||||
u32 width;
|
||||
u32 height;
|
||||
bool with_depth;
|
||||
bool with_lighting;
|
||||
} 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);
|
||||
void pxl8_cpu_destroy(pxl8_cpu_backend* cpu);
|
||||
|
||||
void pxl8_cpu_begin_frame(pxl8_cpu_backend* cpu, const pxl8_3d_frame* frame);
|
||||
void pxl8_cpu_end_frame(pxl8_cpu_backend* cpu);
|
||||
|
||||
void pxl8_cpu_clear(pxl8_cpu_backend* cpu, u8 color);
|
||||
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_palette(pxl8_cpu_backend* cpu, const u32* palette);
|
||||
|
||||
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);
|
||||
void pxl8_cpu_draw_line_2d(pxl8_cpu_backend* cpu, i32 x0, i32 y0, i32 x1, i32 y1, u8 color);
|
||||
void pxl8_cpu_draw_rect(pxl8_cpu_backend* cpu, i32 x, i32 y, i32 w, i32 h, u8 color);
|
||||
void pxl8_cpu_draw_rect_fill(pxl8_cpu_backend* cpu, i32 x, i32 y, i32 w, i32 h, u8 color);
|
||||
void pxl8_cpu_draw_circle(pxl8_cpu_backend* cpu, i32 cx, i32 cy, i32 radius, u8 color);
|
||||
void pxl8_cpu_draw_circle_fill(pxl8_cpu_backend* cpu, i32 cx, i32 cy, i32 radius, u8 color);
|
||||
void pxl8_cpu_draw_line_3d(pxl8_cpu_backend* cpu, pxl8_vec3 v0, pxl8_vec3 v1, u8 color);
|
||||
|
||||
void pxl8_cpu_draw_mesh(
|
||||
pxl8_cpu_backend* cpu,
|
||||
const pxl8_mesh* mesh,
|
||||
pxl8_mat4 model,
|
||||
pxl8_material material,
|
||||
const pxl8_atlas* textures
|
||||
);
|
||||
|
||||
void pxl8_cpu_draw_mesh_wireframe(
|
||||
pxl8_cpu_backend* cpu,
|
||||
const pxl8_mesh* mesh,
|
||||
pxl8_mat4 model,
|
||||
u8 color
|
||||
);
|
||||
|
||||
u8* pxl8_cpu_get_framebuffer(pxl8_cpu_backend* cpu);
|
||||
u32 pxl8_cpu_get_height(const pxl8_cpu_backend* cpu);
|
||||
u32 pxl8_cpu_get_width(const pxl8_cpu_backend* cpu);
|
||||
|
||||
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);
|
||||
|
||||
pxl8_cpu_render_target* pxl8_cpu_get_target(pxl8_cpu_backend* cpu);
|
||||
void pxl8_cpu_set_target(pxl8_cpu_backend* cpu, pxl8_cpu_render_target* target);
|
||||
|
||||
void pxl8_cpu_blit(pxl8_cpu_backend* cpu, pxl8_cpu_render_target* src, i32 x, i32 y, u8 transparent_idx);
|
||||
|
||||
bool pxl8_cpu_push_target(pxl8_cpu_backend* cpu);
|
||||
void pxl8_cpu_pop_target(pxl8_cpu_backend* cpu);
|
||||
u32 pxl8_cpu_get_target_depth(const pxl8_cpu_backend* cpu);
|
||||
|
||||
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_width(const pxl8_cpu_render_target* target);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
8
client/src/gfx/pxl8_dither.c
Normal file
8
client/src/gfx/pxl8_dither.c
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#include "pxl8_dither.h"
|
||||
|
||||
const u8 PXL8_BAYER_4X4[16] = {
|
||||
0, 8, 2, 10,
|
||||
12, 4, 14, 6,
|
||||
3, 11, 1, 9,
|
||||
15, 7, 13, 5,
|
||||
};
|
||||
41
client/src/gfx/pxl8_dither.h
Normal file
41
client/src/gfx/pxl8_dither.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern const u8 PXL8_BAYER_4X4[16];
|
||||
|
||||
static inline i8 pxl8_bayer_offset(u32 x, u32 y) {
|
||||
return (i8)(PXL8_BAYER_4X4[(y & 3) * 4 + (x & 3)]) - 8;
|
||||
}
|
||||
|
||||
static inline u8 pxl8_ordered_dither(u8 value, u32 x, u32 y) {
|
||||
u8 bayer = PXL8_BAYER_4X4[(y & 3) * 4 + (x & 3)];
|
||||
u16 result = (u16)value + (bayer >> 1);
|
||||
return result > 255 ? 255 : (u8)result;
|
||||
}
|
||||
|
||||
static inline u8 pxl8_dither_light(u8 light, u32 x, u32 y) {
|
||||
i8 offset = pxl8_bayer_offset(x, y) >> 1;
|
||||
i16 result = (i16)light + offset;
|
||||
if (result < 0) return 0;
|
||||
if (result > 255) return 255;
|
||||
return (u8)result;
|
||||
}
|
||||
|
||||
static inline u8 pxl8_dither_float(f32 value, u32 x, u32 y) {
|
||||
u8 floor_val = (u8)value;
|
||||
f32 frac = value - (f32)floor_val;
|
||||
f32 threshold = (PXL8_BAYER_4X4[(y & 3) * 4 + (x & 3)] + 0.5f) * (1.0f / 16.0f);
|
||||
if (frac > threshold) {
|
||||
return floor_val < 255 ? floor_val + 1 : 255;
|
||||
}
|
||||
return floor_val;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
706
client/src/gfx/pxl8_gfx.c
Normal file
706
client/src/gfx/pxl8_gfx.c
Normal file
|
|
@ -0,0 +1,706 @@
|
|||
#include "pxl8_gfx.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pxl8_ase.h"
|
||||
#include "pxl8_atlas.h"
|
||||
#include "pxl8_backend.h"
|
||||
#include "pxl8_blit.h"
|
||||
#include "pxl8_color.h"
|
||||
#include "pxl8_colormap.h"
|
||||
#include "pxl8_font.h"
|
||||
#include "pxl8_hal.h"
|
||||
#include "pxl8_log.h"
|
||||
#include "pxl8_macros.h"
|
||||
#include "pxl8_math.h"
|
||||
#include "pxl8_sys.h"
|
||||
#include "pxl8_types.h"
|
||||
|
||||
typedef struct pxl8_sprite_cache_entry {
|
||||
char path[256];
|
||||
u32 sprite_id;
|
||||
bool active;
|
||||
} pxl8_sprite_cache_entry;
|
||||
|
||||
struct pxl8_gfx {
|
||||
pxl8_atlas* atlas;
|
||||
pxl8_gfx_backend backend;
|
||||
pxl8_colormap* colormap;
|
||||
u8* framebuffer;
|
||||
i32 framebuffer_height;
|
||||
i32 framebuffer_width;
|
||||
pxl8_frustum frustum;
|
||||
const pxl8_hal* hal;
|
||||
bool initialized;
|
||||
pxl8_palette* palette;
|
||||
pxl8_pixel_mode pixel_mode;
|
||||
void* platform_data;
|
||||
pxl8_sprite_cache_entry* sprite_cache;
|
||||
u32 sprite_cache_capacity;
|
||||
u32 sprite_cache_count;
|
||||
pxl8_viewport viewport;
|
||||
};
|
||||
|
||||
pxl8_bounds pxl8_gfx_get_bounds(pxl8_gfx* gfx) {
|
||||
pxl8_bounds bounds = {0};
|
||||
if (!gfx) {
|
||||
return bounds;
|
||||
}
|
||||
bounds.w = gfx->framebuffer_width;
|
||||
bounds.h = gfx->framebuffer_height;
|
||||
return bounds;
|
||||
}
|
||||
|
||||
pxl8_pixel_mode pxl8_gfx_get_pixel_mode(pxl8_gfx* gfx) {
|
||||
return gfx ? gfx->pixel_mode : PXL8_PIXEL_INDEXED;
|
||||
}
|
||||
|
||||
u8* pxl8_gfx_get_framebuffer_indexed(pxl8_gfx* gfx) {
|
||||
if (!gfx || gfx->pixel_mode == PXL8_PIXEL_HICOLOR) return NULL;
|
||||
return gfx->framebuffer;
|
||||
}
|
||||
|
||||
u16* pxl8_gfx_get_framebuffer_hicolor(pxl8_gfx* gfx) {
|
||||
if (!gfx || gfx->pixel_mode != PXL8_PIXEL_HICOLOR) return NULL;
|
||||
return (u16*)gfx->framebuffer;
|
||||
}
|
||||
|
||||
i32 pxl8_gfx_get_height(const pxl8_gfx* gfx) {
|
||||
return gfx ? gfx->framebuffer_height : 0;
|
||||
}
|
||||
|
||||
i32 pxl8_gfx_get_width(const pxl8_gfx* gfx) {
|
||||
return gfx ? gfx->framebuffer_width : 0;
|
||||
}
|
||||
|
||||
pxl8_palette* pxl8_gfx_get_palette(pxl8_gfx* gfx) {
|
||||
return gfx ? gfx->palette : NULL;
|
||||
}
|
||||
|
||||
u8 pxl8_gfx_find_color(pxl8_gfx* gfx, u32 color) {
|
||||
if (!gfx || !gfx->palette) return 0;
|
||||
if (color <= 0xFFFFFF) color = (color << 8) | 0xFF;
|
||||
u8 r = (color >> 24) & 0xFF;
|
||||
u8 g = (color >> 16) & 0xFF;
|
||||
u8 b = (color >> 8) & 0xFF;
|
||||
return pxl8_palette_find_closest(gfx->palette, r, g, b);
|
||||
}
|
||||
|
||||
void pxl8_gfx_set_palette(pxl8_gfx* gfx, pxl8_palette* pal) {
|
||||
if (!gfx) return;
|
||||
if (gfx->palette) {
|
||||
pxl8_palette_destroy(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) {
|
||||
if (!gfx || !filepath) return -1;
|
||||
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 (gfx->pixel_mode != PXL8_PIXEL_HICOLOR) {
|
||||
u32* colors = pxl8_palette_colors(pal);
|
||||
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);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
pxl8_gfx* pxl8_gfx_create(
|
||||
const pxl8_hal* hal,
|
||||
void* platform_data,
|
||||
pxl8_pixel_mode mode,
|
||||
pxl8_resolution resolution
|
||||
) {
|
||||
pxl8_gfx* gfx = (pxl8_gfx*)calloc(1, sizeof(pxl8_gfx));
|
||||
if (!gfx) {
|
||||
pxl8_error("Failed to allocate graphics context");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gfx->hal = hal;
|
||||
gfx->platform_data = platform_data;
|
||||
|
||||
gfx->pixel_mode = mode;
|
||||
pxl8_size size = pxl8_get_resolution_dimensions(resolution);
|
||||
gfx->framebuffer_width = size.w;
|
||||
gfx->framebuffer_height = size.h;
|
||||
|
||||
if (!gfx->platform_data) {
|
||||
pxl8_error("Platform data cannot be NULL");
|
||||
free(gfx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (mode != PXL8_PIXEL_HICOLOR) {
|
||||
gfx->palette = pxl8_palette_create();
|
||||
}
|
||||
|
||||
gfx->backend.type = PXL8_GFX_BACKEND_CPU;
|
||||
gfx->backend.cpu = pxl8_cpu_create(gfx->framebuffer_width, gfx->framebuffer_height);
|
||||
if (!gfx->backend.cpu) {
|
||||
pxl8_error("Failed to create CPU backend");
|
||||
pxl8_gfx_destroy(gfx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gfx->framebuffer = pxl8_cpu_get_framebuffer(gfx->backend.cpu);
|
||||
|
||||
if (mode != PXL8_PIXEL_HICOLOR) {
|
||||
gfx->colormap = calloc(1, sizeof(pxl8_colormap));
|
||||
if (gfx->colormap) {
|
||||
pxl8_cpu_set_colormap(gfx->backend.cpu, gfx->colormap);
|
||||
}
|
||||
}
|
||||
|
||||
gfx->viewport.offset_x = 0;
|
||||
gfx->viewport.offset_y = 0;
|
||||
gfx->viewport.scaled_width = gfx->framebuffer_width;
|
||||
gfx->viewport.scaled_height = gfx->framebuffer_height;
|
||||
gfx->viewport.scale = 1.0f;
|
||||
|
||||
gfx->initialized = true;
|
||||
return gfx;
|
||||
}
|
||||
|
||||
void pxl8_gfx_destroy(pxl8_gfx* gfx) {
|
||||
if (!gfx) return;
|
||||
|
||||
pxl8_atlas_destroy(gfx->atlas);
|
||||
if (gfx->backend.type == PXL8_GFX_BACKEND_CPU) {
|
||||
pxl8_cpu_destroy(gfx->backend.cpu);
|
||||
}
|
||||
free(gfx->colormap);
|
||||
pxl8_palette_destroy(gfx->palette);
|
||||
free(gfx->sprite_cache);
|
||||
|
||||
free(gfx);
|
||||
}
|
||||
|
||||
static pxl8_result pxl8_gfx_ensure_atlas(pxl8_gfx* gfx) {
|
||||
if (gfx->atlas) return PXL8_OK;
|
||||
gfx->atlas = pxl8_atlas_create(PXL8_DEFAULT_ATLAS_SIZE, PXL8_DEFAULT_ATLAS_SIZE, gfx->pixel_mode);
|
||||
return gfx->atlas ? PXL8_OK : PXL8_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
void pxl8_gfx_clear_textures(pxl8_gfx* gfx) {
|
||||
if (!gfx) return;
|
||||
|
||||
if (gfx->atlas) {
|
||||
pxl8_atlas_clear(gfx->atlas, 0);
|
||||
}
|
||||
|
||||
if (gfx->sprite_cache) {
|
||||
gfx->sprite_cache_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pxl8_result pxl8_gfx_create_texture(pxl8_gfx* gfx, const u8* pixels, u32 width, u32 height) {
|
||||
if (!gfx || !gfx->initialized || !pixels) return PXL8_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
pxl8_result result = pxl8_gfx_ensure_atlas(gfx);
|
||||
if (result != PXL8_OK) return result;
|
||||
|
||||
u32 texture_id = pxl8_atlas_add_texture(gfx->atlas, pixels, width, height, gfx->pixel_mode);
|
||||
if (texture_id == UINT32_MAX) {
|
||||
pxl8_error("Texture doesn't fit in atlas");
|
||||
return PXL8_ERROR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
return texture_id;
|
||||
}
|
||||
|
||||
pxl8_result pxl8_gfx_load_sprite(pxl8_gfx* gfx, const char* path) {
|
||||
if (!gfx || !gfx->initialized || !path) return PXL8_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
if (!gfx->sprite_cache) {
|
||||
gfx->sprite_cache_capacity = PXL8_DEFAULT_SPRITE_CACHE_CAPACITY;
|
||||
gfx->sprite_cache = (pxl8_sprite_cache_entry*)calloc(
|
||||
gfx->sprite_cache_capacity, sizeof(pxl8_sprite_cache_entry)
|
||||
);
|
||||
if (!gfx->sprite_cache) return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < gfx->sprite_cache_count; i++) {
|
||||
if (gfx->sprite_cache[i].active && strcmp(gfx->sprite_cache[i].path, path) == 0) {
|
||||
return gfx->sprite_cache[i].sprite_id;
|
||||
}
|
||||
}
|
||||
|
||||
pxl8_result result = pxl8_gfx_ensure_atlas(gfx);
|
||||
if (result != PXL8_OK) return result;
|
||||
|
||||
pxl8_ase_file ase_file;
|
||||
result = pxl8_ase_load(path, &ase_file);
|
||||
if (result != PXL8_OK) {
|
||||
pxl8_error("Failed to load ASE file: %s", path);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (ase_file.frame_count == 0) {
|
||||
pxl8_error("No frames in ASE file");
|
||||
pxl8_ase_destroy(&ase_file);
|
||||
return PXL8_ERROR_INVALID_FORMAT;
|
||||
}
|
||||
|
||||
u32 sprite_id = pxl8_atlas_add_texture(
|
||||
gfx->atlas,
|
||||
ase_file.frames[0].pixels,
|
||||
ase_file.header.width,
|
||||
ase_file.header.height,
|
||||
gfx->pixel_mode
|
||||
);
|
||||
|
||||
pxl8_ase_destroy(&ase_file);
|
||||
|
||||
if (sprite_id == UINT32_MAX) {
|
||||
pxl8_error("Sprite doesn't fit in atlas");
|
||||
return PXL8_ERROR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
if (gfx->sprite_cache_count >= gfx->sprite_cache_capacity) {
|
||||
u32 new_capacity = gfx->sprite_cache_capacity * 2;
|
||||
pxl8_sprite_cache_entry* new_cache = (pxl8_sprite_cache_entry*)realloc(
|
||||
gfx->sprite_cache,
|
||||
new_capacity * sizeof(pxl8_sprite_cache_entry)
|
||||
);
|
||||
if (!new_cache) return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
gfx->sprite_cache = new_cache;
|
||||
gfx->sprite_cache_capacity = new_capacity;
|
||||
}
|
||||
|
||||
pxl8_sprite_cache_entry* entry = &gfx->sprite_cache[gfx->sprite_cache_count++];
|
||||
entry->active = true;
|
||||
entry->sprite_id = sprite_id;
|
||||
pxl8_strncpy(entry->path, path, sizeof(entry->path));
|
||||
|
||||
return sprite_id;
|
||||
}
|
||||
|
||||
pxl8_atlas* pxl8_gfx_get_atlas(pxl8_gfx* gfx) {
|
||||
if (!gfx || !gfx->initialized) return NULL;
|
||||
|
||||
if (pxl8_gfx_ensure_atlas(gfx) != PXL8_OK) return NULL;
|
||||
return gfx->atlas;
|
||||
}
|
||||
|
||||
pxl8_result pxl8_gfx_load_font_atlas(pxl8_gfx* gfx) {
|
||||
(void)gfx;
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
||||
void pxl8_gfx_upload_framebuffer(pxl8_gfx* gfx) {
|
||||
if (!gfx || !gfx->initialized || !gfx->hal) return;
|
||||
|
||||
u32 bpp = (gfx->pixel_mode == PXL8_PIXEL_HICOLOR) ? 2 : 1;
|
||||
u32* colors = gfx->palette ? pxl8_palette_colors(gfx->palette) : NULL;
|
||||
gfx->hal->upload_texture(
|
||||
gfx->platform_data,
|
||||
gfx->framebuffer,
|
||||
gfx->framebuffer_width,
|
||||
gfx->framebuffer_height,
|
||||
colors,
|
||||
bpp
|
||||
);
|
||||
}
|
||||
|
||||
void pxl8_gfx_present(pxl8_gfx* gfx) {
|
||||
if (!gfx || !gfx->initialized || !gfx->hal) return;
|
||||
|
||||
gfx->hal->present(gfx->platform_data);
|
||||
}
|
||||
|
||||
pxl8_viewport pxl8_gfx_viewport(pxl8_bounds bounds, i32 width, i32 height) {
|
||||
pxl8_viewport vp = {0};
|
||||
vp.scale = fminf(bounds.w / (f32)width, bounds.h / (f32)height);
|
||||
vp.scaled_width = (i32)(width * vp.scale);
|
||||
vp.scaled_height = (i32)(height * vp.scale);
|
||||
vp.offset_x = (bounds.w - vp.scaled_width) / 2;
|
||||
vp.offset_y = (bounds.h - vp.scaled_height) / 2;
|
||||
return vp;
|
||||
}
|
||||
|
||||
void pxl8_gfx_set_viewport(pxl8_gfx* gfx, pxl8_viewport vp) {
|
||||
if (!gfx) return;
|
||||
gfx->viewport = vp;
|
||||
}
|
||||
|
||||
void pxl8_gfx_project(pxl8_gfx* gfx, f32 left, f32 right, f32 top, f32 bottom) {
|
||||
(void)gfx; (void)left; (void)right; (void)top; (void)bottom;
|
||||
}
|
||||
|
||||
void pxl8_2d_clear(pxl8_gfx* gfx, u32 color) {
|
||||
if (!gfx) return;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
pxl8_cpu_clear(gfx->backend.cpu, (u8)color);
|
||||
break;
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_2d_pixel(pxl8_gfx* gfx, i32 x, i32 y, u32 color) {
|
||||
if (!gfx) return;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
pxl8_cpu_draw_pixel(gfx->backend.cpu, x, y, (u8)color);
|
||||
break;
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
u32 pxl8_2d_get_pixel(pxl8_gfx* gfx, i32 x, i32 y) {
|
||||
if (!gfx) return 0;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
return pxl8_cpu_get_pixel(gfx->backend.cpu, x, y);
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pxl8_2d_line(pxl8_gfx* gfx, i32 x0, i32 y0, i32 x1, i32 y1, u32 color) {
|
||||
if (!gfx) return;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
pxl8_cpu_draw_line_2d(gfx->backend.cpu, x0, y0, x1, y1, (u8)color);
|
||||
break;
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_2d_rect(pxl8_gfx* gfx, i32 x, i32 y, i32 w, i32 h, u32 color) {
|
||||
if (!gfx) return;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
pxl8_cpu_draw_rect(gfx->backend.cpu, x, y, w, h, (u8)color);
|
||||
break;
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_2d_rect_fill(pxl8_gfx* gfx, i32 x, i32 y, i32 w, i32 h, u32 color) {
|
||||
if (!gfx) return;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
pxl8_cpu_draw_rect_fill(gfx->backend.cpu, x, y, w, h, (u8)color);
|
||||
break;
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_2d_circle(pxl8_gfx* gfx, i32 cx, i32 cy, i32 radius, u32 color) {
|
||||
if (!gfx) return;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
pxl8_cpu_draw_circle(gfx->backend.cpu, cx, cy, radius, (u8)color);
|
||||
break;
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_2d_circle_fill(pxl8_gfx* gfx, i32 cx, i32 cy, i32 radius, u32 color) {
|
||||
if (!gfx) return;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
pxl8_cpu_draw_circle_fill(gfx->backend.cpu, cx, cy, radius, (u8)color);
|
||||
break;
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_2d_text(pxl8_gfx* gfx, const char* text, i32 x, i32 y, u32 color) {
|
||||
if (!gfx || !text) return;
|
||||
|
||||
u8* framebuffer = NULL;
|
||||
i32 fb_width = 0;
|
||||
i32 fb_height = 0;
|
||||
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU: {
|
||||
pxl8_cpu_render_target* render_target = pxl8_cpu_get_target(gfx->backend.cpu);
|
||||
if (!render_target) return;
|
||||
framebuffer = pxl8_cpu_render_target_get_framebuffer(render_target);
|
||||
fb_width = (i32)pxl8_cpu_render_target_get_width(render_target);
|
||||
fb_height = (i32)pxl8_cpu_render_target_get_height(render_target);
|
||||
break;
|
||||
}
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
return;
|
||||
}
|
||||
|
||||
if (!framebuffer) return;
|
||||
|
||||
const pxl8_font* font = &pxl8_default_font;
|
||||
i32 cursor_x = x;
|
||||
i32 cursor_y = y;
|
||||
|
||||
for (const char* c = text; *c; c++) {
|
||||
const pxl8_glyph* glyph = pxl8_font_find_glyph(font, (u32)*c);
|
||||
if (!glyph) continue;
|
||||
|
||||
for (i32 gy = 0; gy < glyph->height; gy++) {
|
||||
for (i32 gx = 0; gx < glyph->width; gx++) {
|
||||
i32 px = cursor_x + gx;
|
||||
i32 py = cursor_y + gy;
|
||||
|
||||
if (px < 0 || px >= fb_width || py < 0 || py >= fb_height) continue;
|
||||
|
||||
u8 pixel_bit = 0;
|
||||
|
||||
if (glyph->format == PXL8_FONT_FORMAT_INDEXED) {
|
||||
u8 pixel_byte = glyph->data.indexed[gy];
|
||||
pixel_bit = (pixel_byte >> gx) & 1;
|
||||
} else {
|
||||
i32 glyph_idx = gy * 8 + gx;
|
||||
u32 rgba_pixel = glyph->data.rgba[glyph_idx];
|
||||
pixel_bit = ((rgba_pixel >> 24) & 0xFF) > 128 ? 1 : 0;
|
||||
}
|
||||
|
||||
if (pixel_bit) {
|
||||
framebuffer[py * fb_width + px] = (u8)color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cursor_x += font->default_width;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void pxl8_2d_sprite(pxl8_gfx* gfx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h, bool flip_x, bool flip_y) {
|
||||
if (!gfx || !gfx->atlas) return;
|
||||
|
||||
u8* framebuffer = NULL;
|
||||
i32 fb_width = 0;
|
||||
i32 fb_height = 0;
|
||||
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU: {
|
||||
pxl8_cpu_render_target* render_target = pxl8_cpu_get_target(gfx->backend.cpu);
|
||||
if (!render_target) return;
|
||||
framebuffer = pxl8_cpu_render_target_get_framebuffer(render_target);
|
||||
fb_width = (i32)pxl8_cpu_render_target_get_width(render_target);
|
||||
fb_height = (i32)pxl8_cpu_render_target_get_height(render_target);
|
||||
break;
|
||||
}
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
return;
|
||||
}
|
||||
|
||||
if (!framebuffer) return;
|
||||
|
||||
const pxl8_atlas_entry* entry = pxl8_atlas_get_entry(gfx->atlas, sprite_id);
|
||||
if (!entry || !entry->active) return;
|
||||
|
||||
i32 clip_left = (x < 0) ? -x : 0;
|
||||
i32 clip_top = (y < 0) ? -y : 0;
|
||||
i32 clip_right = (x + w > fb_width) ? x + w - fb_width : 0;
|
||||
i32 clip_bottom = (y + h > fb_height) ? y + h - fb_height : 0;
|
||||
|
||||
i32 draw_width = w - clip_left - clip_right;
|
||||
i32 draw_height = h - clip_top - clip_bottom;
|
||||
|
||||
if (draw_width <= 0 || draw_height <= 0) return;
|
||||
|
||||
i32 dest_x = x + clip_left;
|
||||
i32 dest_y = y + clip_top;
|
||||
|
||||
bool is_1to1_scale = (w == entry->w && h == entry->h);
|
||||
bool is_unclipped = (clip_left == 0 && clip_top == 0 && clip_right == 0 && clip_bottom == 0);
|
||||
bool is_flipped = flip_x || flip_y;
|
||||
|
||||
u32 atlas_width = pxl8_atlas_get_width(gfx->atlas);
|
||||
const u8* atlas_pixels = pxl8_atlas_get_pixels(gfx->atlas);
|
||||
|
||||
if (is_1to1_scale && is_unclipped && !is_flipped) {
|
||||
const u8* sprite_data = atlas_pixels + entry->y * atlas_width + entry->x;
|
||||
pxl8_blit_indexed(framebuffer, fb_width, sprite_data, atlas_width, x, y, w, h);
|
||||
} else {
|
||||
for (i32 py = 0; py < draw_height; py++) {
|
||||
for (i32 px = 0; px < draw_width; px++) {
|
||||
i32 local_x = (px + clip_left) * entry->w / w;
|
||||
i32 local_y = (py + clip_top) * entry->h / h;
|
||||
|
||||
i32 src_x = flip_x ? entry->x + entry->w - 1 - local_x : entry->x + local_x;
|
||||
i32 src_y = flip_y ? entry->y + entry->h - 1 - local_y : entry->y + local_y;
|
||||
|
||||
i32 src_idx = src_y * atlas_width + src_x;
|
||||
i32 dest_idx = (dest_y + py) * fb_width + (dest_x + px);
|
||||
|
||||
framebuffer[dest_idx] = pxl8_blend_indexed(atlas_pixels[src_idx], framebuffer[dest_idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_gfx_update(pxl8_gfx* gfx, f32 dt) {
|
||||
if (!gfx) return;
|
||||
|
||||
if (gfx->palette) {
|
||||
u16 delta_ticks = (u16)(dt * 1000.0f);
|
||||
pxl8_palette_tick(gfx->palette, delta_ticks);
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_3d_begin_frame(pxl8_gfx* gfx, const pxl8_3d_camera* camera, const pxl8_3d_uniforms* uniforms) {
|
||||
if (!gfx || !camera) return;
|
||||
|
||||
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(projection, view);
|
||||
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) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
pxl8_cpu_begin_frame(gfx->backend.cpu, &frame);
|
||||
break;
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const pxl8_frustum* pxl8_3d_get_frustum(pxl8_gfx* gfx) {
|
||||
if (!gfx) return NULL;
|
||||
return &gfx->frustum;
|
||||
}
|
||||
|
||||
void pxl8_3d_clear(pxl8_gfx* gfx, u8 color) {
|
||||
if (!gfx) return;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
pxl8_cpu_clear(gfx->backend.cpu, color);
|
||||
break;
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_3d_clear_depth(pxl8_gfx* gfx) {
|
||||
if (!gfx) return;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
pxl8_cpu_clear_depth(gfx->backend.cpu);
|
||||
break;
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_3d_draw_line(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, u8 color) {
|
||||
if (!gfx) return;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
pxl8_cpu_draw_line_3d(gfx->backend.cpu, v0, v1, color);
|
||||
break;
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_3d_draw_mesh(pxl8_gfx* gfx, const pxl8_mesh* mesh, pxl8_mat4 model, pxl8_material material) {
|
||||
if (!gfx || !mesh) return;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
pxl8_cpu_draw_mesh(gfx->backend.cpu, mesh, model, material, gfx->atlas);
|
||||
break;
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_3d_draw_mesh_wireframe(pxl8_gfx* gfx, const pxl8_mesh* mesh, pxl8_mat4 model, u8 color) {
|
||||
if (!gfx || !mesh) return;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
pxl8_cpu_draw_mesh_wireframe(gfx->backend.cpu, mesh, model, color);
|
||||
break;
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_3d_end_frame(pxl8_gfx* gfx) {
|
||||
if (!gfx) return;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
pxl8_cpu_end_frame(gfx->backend.cpu);
|
||||
break;
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
u8* pxl8_3d_get_framebuffer(pxl8_gfx* gfx) {
|
||||
if (!gfx) return NULL;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
return pxl8_cpu_get_framebuffer(gfx->backend.cpu);
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool pxl8_gfx_push_target(pxl8_gfx* gfx) {
|
||||
if (!gfx) return false;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
return pxl8_cpu_push_target(gfx->backend.cpu);
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void pxl8_gfx_pop_target(pxl8_gfx* gfx) {
|
||||
if (!gfx) return;
|
||||
switch (gfx->backend.type) {
|
||||
case PXL8_GFX_BACKEND_CPU:
|
||||
pxl8_cpu_pop_target(gfx->backend.cpu);
|
||||
break;
|
||||
case PXL8_GFX_BACKEND_GPU:
|
||||
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
|
||||
24
client/src/gfx/pxl8_gfx2d.h
Normal file
24
client/src/gfx/pxl8_gfx2d.h
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_types.h"
|
||||
|
||||
typedef struct pxl8_gfx pxl8_gfx;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void pxl8_2d_circle(pxl8_gfx* gfx, i32 cx, i32 cy, i32 radius, u32 color);
|
||||
void pxl8_2d_circle_fill(pxl8_gfx* gfx, i32 cx, i32 cy, i32 radius, u32 color);
|
||||
void pxl8_2d_clear(pxl8_gfx* gfx, u32 color);
|
||||
u32 pxl8_2d_get_pixel(pxl8_gfx* gfx, i32 x, i32 y);
|
||||
void pxl8_2d_line(pxl8_gfx* gfx, i32 x0, i32 y0, i32 x1, i32 y1, u32 color);
|
||||
void pxl8_2d_pixel(pxl8_gfx* gfx, i32 x, i32 y, u32 color);
|
||||
void pxl8_2d_rect(pxl8_gfx* gfx, i32 x, i32 y, i32 w, i32 h, u32 color);
|
||||
void pxl8_2d_rect_fill(pxl8_gfx* gfx, i32 x, i32 y, i32 w, i32 h, u32 color);
|
||||
void pxl8_2d_sprite(pxl8_gfx* gfx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h, bool flip_x, bool flip_y);
|
||||
void pxl8_2d_text(pxl8_gfx* gfx, const char* text, i32 x, i32 y, u32 color);
|
||||
|
||||
#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
|
||||
122
client/src/gfx/pxl8_mesh.c
Normal file
122
client/src/gfx/pxl8_mesh.c
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
#include "pxl8_mesh.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
pxl8_mesh* pxl8_mesh_create(u32 vertex_capacity, u32 index_capacity) {
|
||||
if (vertex_capacity > PXL8_MESH_MAX_VERTICES) vertex_capacity = PXL8_MESH_MAX_VERTICES;
|
||||
if (index_capacity > PXL8_MESH_MAX_INDICES) index_capacity = PXL8_MESH_MAX_INDICES;
|
||||
|
||||
pxl8_mesh* mesh = calloc(1, sizeof(pxl8_mesh));
|
||||
if (!mesh) return NULL;
|
||||
|
||||
mesh->vertices = calloc(vertex_capacity, sizeof(pxl8_vertex));
|
||||
mesh->indices = calloc(index_capacity, sizeof(u16));
|
||||
|
||||
if (!mesh->vertices || !mesh->indices) {
|
||||
free(mesh->vertices);
|
||||
free(mesh->indices);
|
||||
free(mesh);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mesh->vertex_capacity = vertex_capacity;
|
||||
mesh->index_capacity = index_capacity;
|
||||
mesh->vertex_count = 0;
|
||||
mesh->index_count = 0;
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
void pxl8_mesh_destroy(pxl8_mesh* mesh) {
|
||||
if (!mesh) return;
|
||||
free(mesh->vertices);
|
||||
free(mesh->indices);
|
||||
free(mesh);
|
||||
}
|
||||
|
||||
void pxl8_mesh_clear(pxl8_mesh* mesh) {
|
||||
if (!mesh) return;
|
||||
mesh->vertex_count = 0;
|
||||
mesh->index_count = 0;
|
||||
}
|
||||
|
||||
u16 pxl8_mesh_push_vertex(pxl8_mesh* mesh, pxl8_vertex v) {
|
||||
if (!mesh || mesh->vertex_count >= mesh->vertex_capacity) return 0;
|
||||
u16 idx = (u16)mesh->vertex_count;
|
||||
mesh->vertices[mesh->vertex_count++] = v;
|
||||
return idx;
|
||||
}
|
||||
|
||||
void pxl8_mesh_push_triangle(pxl8_mesh* mesh, u16 i0, u16 i1, u16 i2) {
|
||||
if (!mesh || mesh->index_count + 3 > mesh->index_capacity) return;
|
||||
mesh->indices[mesh->index_count++] = i0;
|
||||
mesh->indices[mesh->index_count++] = i1;
|
||||
mesh->indices[mesh->index_count++] = i2;
|
||||
}
|
||||
|
||||
void pxl8_mesh_push_quad(pxl8_mesh* mesh, u16 i0, u16 i1, u16 i2, u16 i3) {
|
||||
pxl8_mesh_push_triangle(mesh, i0, i1, i2);
|
||||
pxl8_mesh_push_triangle(mesh, i0, i2, i3);
|
||||
}
|
||||
|
||||
void pxl8_mesh_push_box(pxl8_mesh* mesh, pxl8_vec3 center, pxl8_vec3 half, u8 color) {
|
||||
pxl8_mesh_push_box_uv(mesh, center, half, color, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
void pxl8_mesh_push_box_uv(pxl8_mesh* mesh, pxl8_vec3 center, pxl8_vec3 half, u8 color, f32 u_scale, f32 v_scale) {
|
||||
if (!mesh) return;
|
||||
|
||||
pxl8_vec3 corners[8] = {
|
||||
{center.x - half.x, center.y - half.y, center.z - half.z},
|
||||
{center.x + half.x, center.y - half.y, center.z - half.z},
|
||||
{center.x + half.x, center.y + half.y, center.z - half.z},
|
||||
{center.x - half.x, center.y + half.y, center.z - half.z},
|
||||
{center.x - half.x, center.y - half.y, center.z + half.z},
|
||||
{center.x + half.x, center.y - half.y, center.z + half.z},
|
||||
{center.x + half.x, center.y + half.y, center.z + half.z},
|
||||
{center.x - half.x, center.y + half.y, center.z + half.z},
|
||||
};
|
||||
|
||||
pxl8_vec3 normals[6] = {
|
||||
{ 0, 0, -1}, // front
|
||||
{ 0, 0, 1}, // back
|
||||
{-1, 0, 0}, // left
|
||||
{ 1, 0, 0}, // right
|
||||
{ 0, -1, 0}, // bottom
|
||||
{ 0, 1, 0}, // top
|
||||
};
|
||||
|
||||
i32 faces[6][4] = {
|
||||
{0, 1, 2, 3}, // front
|
||||
{5, 4, 7, 6}, // back
|
||||
{4, 0, 3, 7}, // left
|
||||
{1, 5, 6, 2}, // right
|
||||
{4, 5, 1, 0}, // bottom
|
||||
{3, 2, 6, 7}, // top
|
||||
};
|
||||
|
||||
f32 uvs[4][2] = {
|
||||
{0, v_scale},
|
||||
{u_scale, v_scale},
|
||||
{u_scale, 0},
|
||||
{0, 0},
|
||||
};
|
||||
|
||||
for (i32 face = 0; face < 6; face++) {
|
||||
u16 base = (u16)mesh->vertex_count;
|
||||
|
||||
for (i32 v = 0; v < 4; v++) {
|
||||
pxl8_vertex vert = {
|
||||
.position = corners[faces[face][v]],
|
||||
.normal = normals[face],
|
||||
.u = uvs[v][0],
|
||||
.v = uvs[v][1],
|
||||
.color = color,
|
||||
.light = 255,
|
||||
};
|
||||
pxl8_mesh_push_vertex(mesh, vert);
|
||||
}
|
||||
|
||||
pxl8_mesh_push_quad(mesh, base, base + 1, base + 2, base + 3);
|
||||
}
|
||||
}
|
||||
124
client/src/gfx/pxl8_mesh.h
Normal file
124
client/src/gfx/pxl8_mesh.h
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_math.h"
|
||||
#include "pxl8_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define PXL8_MESH_MAX_VERTICES 65536
|
||||
#define PXL8_MESH_MAX_INDICES 65536
|
||||
|
||||
typedef enum pxl8_blend_mode {
|
||||
PXL8_BLEND_OPAQUE = 0,
|
||||
PXL8_BLEND_ALPHA_TEST,
|
||||
PXL8_BLEND_ALPHA,
|
||||
PXL8_BLEND_ADDITIVE,
|
||||
} pxl8_blend_mode;
|
||||
|
||||
typedef struct pxl8_material {
|
||||
u32 texture_id;
|
||||
u8 alpha;
|
||||
u8 blend_mode;
|
||||
bool dither;
|
||||
bool double_sided;
|
||||
bool dynamic_lighting;
|
||||
bool per_pixel;
|
||||
bool vertex_color_passthrough;
|
||||
f32 emissive_intensity;
|
||||
} pxl8_material;
|
||||
|
||||
typedef struct pxl8_vertex {
|
||||
pxl8_vec3 position;
|
||||
pxl8_vec3 normal;
|
||||
f32 u, v;
|
||||
u8 color;
|
||||
u8 light;
|
||||
u8 _pad[2];
|
||||
} pxl8_vertex;
|
||||
|
||||
typedef struct pxl8_mesh {
|
||||
pxl8_vertex* vertices;
|
||||
u16* indices;
|
||||
u32 vertex_count;
|
||||
u32 index_count;
|
||||
u32 vertex_capacity;
|
||||
u32 index_capacity;
|
||||
} pxl8_mesh;
|
||||
|
||||
pxl8_mesh* pxl8_mesh_create(u32 vertex_capacity, u32 index_capacity);
|
||||
void pxl8_mesh_destroy(pxl8_mesh* mesh);
|
||||
void pxl8_mesh_clear(pxl8_mesh* mesh);
|
||||
|
||||
u16 pxl8_mesh_push_vertex(pxl8_mesh* mesh, pxl8_vertex v);
|
||||
void pxl8_mesh_push_triangle(pxl8_mesh* mesh, u16 i0, u16 i1, u16 i2);
|
||||
void pxl8_mesh_push_quad(pxl8_mesh* mesh, u16 i0, u16 i1, u16 i2, u16 i3);
|
||||
|
||||
void pxl8_mesh_push_box(pxl8_mesh* mesh, pxl8_vec3 center, pxl8_vec3 half_extents, u8 color);
|
||||
void pxl8_mesh_push_box_uv(pxl8_mesh* mesh, pxl8_vec3 center, pxl8_vec3 half_extents, u8 color, f32 u_scale, f32 v_scale);
|
||||
|
||||
static inline u32 pxl8_mesh_triangle_count(const pxl8_mesh* mesh) {
|
||||
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
|
||||
}
|
||||
#endif
|
||||
474
client/src/gfx/pxl8_palette.c
Normal file
474
client/src/gfx/pxl8_palette.c
Normal file
|
|
@ -0,0 +1,474 @@
|
|||
#include "pxl8_palette.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pxl8_ase.h"
|
||||
#include "pxl8_color.h"
|
||||
#include "pxl8_log.h"
|
||||
|
||||
#define PXL8_PALETTE_HASH_SIZE 512
|
||||
|
||||
typedef struct {
|
||||
u32 color;
|
||||
i16 index;
|
||||
} pxl8_palette_hash_entry;
|
||||
|
||||
struct pxl8_palette {
|
||||
u32 base_colors[PXL8_MAX_CYCLES][PXL8_MAX_CYCLE_LEN];
|
||||
u32 colors[PXL8_PALETTE_SIZE];
|
||||
u8 color_ramp[PXL8_PALETTE_SIZE];
|
||||
u16 color_count;
|
||||
pxl8_cycle_range cycles[PXL8_MAX_CYCLES];
|
||||
i8 directions[PXL8_MAX_CYCLES];
|
||||
f32 phases[PXL8_MAX_CYCLES];
|
||||
pxl8_palette_hash_entry hash[PXL8_PALETTE_HASH_SIZE];
|
||||
};
|
||||
|
||||
static inline u32 pxl8_palette_hash(u32 color) {
|
||||
color ^= color >> 16;
|
||||
color *= 0x85ebca6b;
|
||||
color ^= color >> 13;
|
||||
return color & (PXL8_PALETTE_HASH_SIZE - 1);
|
||||
}
|
||||
|
||||
static void pxl8_palette_rebuild_hash(pxl8_palette* pal) {
|
||||
for (u32 i = 0; i < PXL8_PALETTE_HASH_SIZE; i++) {
|
||||
pal->hash[i].index = -1;
|
||||
}
|
||||
for (u32 i = 0; i < PXL8_PALETTE_SIZE; i++) {
|
||||
u32 color = pal->colors[i];
|
||||
u32 slot = pxl8_palette_hash(color);
|
||||
while (pal->hash[slot].index >= 0) {
|
||||
slot = (slot + 1) & (PXL8_PALETTE_HASH_SIZE - 1);
|
||||
}
|
||||
pal->hash[slot].color = color;
|
||||
pal->hash[slot].index = (i16)i;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u32 pack_rgb(u8 r, u8 g, u8 b) {
|
||||
return 0xFF000000 | ((u32)b << 16) | ((u32)g << 8) | r;
|
||||
}
|
||||
|
||||
static inline u32 pack_rgba(u8 r, u8 g, u8 b, u8 a) {
|
||||
return ((u32)a << 24) | ((u32)b << 16) | ((u32)g << 8) | r;
|
||||
}
|
||||
|
||||
static inline void unpack_rgba(u32 c, u8* r, u8* g, u8* b, u8* a) {
|
||||
*r = c & 0xFF;
|
||||
*g = (c >> 8) & 0xFF;
|
||||
*b = (c >> 16) & 0xFF;
|
||||
*a = (c >> 24) & 0xFF;
|
||||
}
|
||||
|
||||
static f32 apply_easing(pxl8_easing easing, f32 t) {
|
||||
switch (easing) {
|
||||
case PXL8_EASE_IN:
|
||||
return t * t;
|
||||
case PXL8_EASE_IN_OUT:
|
||||
return t < 0.5f ? 2.0f * t * t : 1.0f - (-2.0f * t + 2.0f) * (-2.0f * t + 2.0f) * 0.5f;
|
||||
case PXL8_EASE_LINEAR:
|
||||
return t;
|
||||
case PXL8_EASE_OUT:
|
||||
return 1.0f - (1.0f - t) * (1.0f - t);
|
||||
default:
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
static void rgb_to_hsl(u8 r, u8 g, u8 b, f32* h, f32* s, f32* l) {
|
||||
f32 rf = r / 255.0f;
|
||||
f32 gf = g / 255.0f;
|
||||
f32 bf = b / 255.0f;
|
||||
|
||||
f32 max = rf > gf ? (rf > bf ? rf : bf) : (gf > bf ? gf : bf);
|
||||
f32 min = rf < gf ? (rf < bf ? rf : bf) : (gf < bf ? gf : bf);
|
||||
f32 delta = max - min;
|
||||
|
||||
*l = (max + min) / 2.0f;
|
||||
|
||||
if (delta < 0.001f) {
|
||||
*h = 0.0f;
|
||||
*s = 0.0f;
|
||||
} else {
|
||||
*s = *l > 0.5f ? delta / (2.0f - max - min) : delta / (max + min);
|
||||
|
||||
if (max == rf) {
|
||||
*h = (gf - bf) / delta + (gf < bf ? 6.0f : 0.0f);
|
||||
} else if (max == gf) {
|
||||
*h = (bf - rf) / delta + 2.0f;
|
||||
} else {
|
||||
*h = (rf - gf) / delta + 4.0f;
|
||||
}
|
||||
*h /= 6.0f;
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
u8 index;
|
||||
f32 hue;
|
||||
f32 sat;
|
||||
f32 lum;
|
||||
} palette_sort_entry;
|
||||
|
||||
static int palette_sort_cmp(const void* a, const void* b) {
|
||||
const palette_sort_entry* entry_a = (const palette_sort_entry*)a;
|
||||
const palette_sort_entry* entry_b = (const palette_sort_entry*)b;
|
||||
|
||||
u8 a_gray = entry_a->sat < 0.1f;
|
||||
u8 b_gray = entry_b->sat < 0.1f;
|
||||
if (a_gray != b_gray) return a_gray - b_gray;
|
||||
|
||||
if (a_gray) {
|
||||
if (entry_a->lum < entry_b->lum) return -1;
|
||||
if (entry_a->lum > entry_b->lum) return 1;
|
||||
return (int)entry_a->index - (int)entry_b->index;
|
||||
}
|
||||
|
||||
if (entry_a->hue < entry_b->hue - 0.02f) return -1;
|
||||
if (entry_a->hue > entry_b->hue + 0.02f) return 1;
|
||||
|
||||
if (entry_a->lum < entry_b->lum) return -1;
|
||||
if (entry_a->lum > entry_b->lum) return 1;
|
||||
|
||||
return (int)entry_a->index - (int)entry_b->index;
|
||||
}
|
||||
|
||||
static void pxl8_palette_sort_colors(pxl8_palette* pal) {
|
||||
if (!pal || pal->color_count < 2) return;
|
||||
|
||||
pal->color_ramp[0] = 0;
|
||||
|
||||
u8 count = pal->color_count;
|
||||
palette_sort_entry entries[PXL8_PALETTE_SIZE - 1];
|
||||
for (u32 i = 1; i < count; i++) {
|
||||
u8 r, g, b, a;
|
||||
unpack_rgba(pal->colors[i], &r, &g, &b, &a);
|
||||
|
||||
entries[i - 1].index = (u8)i;
|
||||
rgb_to_hsl(r, g, b, &entries[i - 1].hue, &entries[i - 1].sat, &entries[i - 1].lum);
|
||||
}
|
||||
|
||||
qsort(entries, count - 1, sizeof(palette_sort_entry), palette_sort_cmp);
|
||||
|
||||
for (u32 i = 1; i < count; i++) {
|
||||
pal->color_ramp[i] = entries[i - 1].index;
|
||||
}
|
||||
}
|
||||
|
||||
static u32 lerp_color(u32 c0, u32 c1, f32 t) {
|
||||
u8 r0, g0, b0, a0, r1, g1, b1, a1;
|
||||
unpack_rgba(c0, &r0, &g0, &b0, &a0);
|
||||
unpack_rgba(c1, &r1, &g1, &b1, &a1);
|
||||
|
||||
u8 r = (u8)(r0 + (r1 - r0) * t);
|
||||
u8 g = (u8)(g0 + (g1 - g0) * t);
|
||||
u8 b = (u8)(b0 + (b1 - b0) * t);
|
||||
u8 a = (u8)(a0 + (a1 - a0) * t);
|
||||
|
||||
return pack_rgba(r, g, b, a);
|
||||
}
|
||||
|
||||
static void update_cycle_colors(pxl8_palette* pal, u8 slot) {
|
||||
pxl8_cycle_range* cycle = &pal->cycles[slot];
|
||||
f32 phase = pal->phases[slot];
|
||||
f32 eased = apply_easing(cycle->easing, phase);
|
||||
u8 len = cycle->len;
|
||||
u8 start = cycle->start;
|
||||
|
||||
if (cycle->interpolate) {
|
||||
f32 pos = eased * (len - 1);
|
||||
u8 idx0 = (u8)pos;
|
||||
if (idx0 >= len - 1) idx0 = len - 1;
|
||||
f32 frac = pos - idx0;
|
||||
|
||||
for (u8 i = 0; i < len; i++) {
|
||||
u8 src0 = (i + idx0) % len;
|
||||
u8 src1 = (i + idx0 + 1) % len;
|
||||
u32 c0 = pal->base_colors[slot][src0];
|
||||
u32 c1 = pal->base_colors[slot][src1];
|
||||
pal->colors[start + i] = lerp_color(c0, c1, frac);
|
||||
}
|
||||
} else {
|
||||
u8 offset = (u8)(eased * len) % len;
|
||||
for (u8 i = 0; i < len; i++) {
|
||||
u8 src = (i + offset) % len;
|
||||
pal->colors[start + i] = pal->base_colors[slot][src];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pxl8_palette* pxl8_palette_create(void) {
|
||||
pxl8_palette* pal = calloc(1, sizeof(pxl8_palette));
|
||||
if (!pal) return NULL;
|
||||
|
||||
pal->colors[0] = 0x00000000;
|
||||
pal->colors[1] = pack_rgb(0x00, 0x00, 0x00);
|
||||
pal->colors[2] = pack_rgb(0xFF, 0xFF, 0xFF);
|
||||
pal->color_count = 3;
|
||||
pal->color_ramp[0] = 0;
|
||||
pal->color_ramp[1] = 1;
|
||||
pal->color_ramp[2] = 2;
|
||||
|
||||
for (u8 i = 0; i < PXL8_MAX_CYCLES; i++) {
|
||||
pal->cycles[i] = pxl8_cycle_range_disabled();
|
||||
pal->phases[i] = 0.0f;
|
||||
pal->directions[i] = 1;
|
||||
}
|
||||
|
||||
return pal;
|
||||
}
|
||||
|
||||
void pxl8_palette_destroy(pxl8_palette* pal) {
|
||||
free(pal);
|
||||
}
|
||||
|
||||
pxl8_result pxl8_palette_load_ase(pxl8_palette* pal, const char* path) {
|
||||
if (!pal || !path) return PXL8_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
pxl8_ase_file ase_file;
|
||||
pxl8_result result = pxl8_ase_load(path, &ase_file);
|
||||
if (result != PXL8_OK) {
|
||||
pxl8_error("Failed to load ASE file for palette: %s", path);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (ase_file.palette.entry_count == 0 || !ase_file.palette.colors) {
|
||||
pxl8_error("No palette data in ASE file");
|
||||
pxl8_ase_destroy(&ase_file);
|
||||
return PXL8_ERROR_INVALID_FORMAT;
|
||||
}
|
||||
|
||||
u32 copy_count = ase_file.palette.entry_count < PXL8_PALETTE_SIZE
|
||||
? ase_file.palette.entry_count
|
||||
: PXL8_PALETTE_SIZE;
|
||||
|
||||
memcpy(pal->colors, ase_file.palette.colors, copy_count * sizeof(u32));
|
||||
pal->color_count = (u16)copy_count;
|
||||
|
||||
pxl8_ase_destroy(&ase_file);
|
||||
pxl8_palette_sort_colors(pal);
|
||||
pxl8_palette_rebuild_hash(pal);
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
||||
u32* pxl8_palette_colors(pxl8_palette* pal) {
|
||||
return pal ? pal->colors : NULL;
|
||||
}
|
||||
|
||||
u8* pxl8_palette_color_ramp(pxl8_palette* pal) {
|
||||
return pal ? pal->color_ramp : NULL;
|
||||
}
|
||||
|
||||
u16 pxl8_palette_color_count(const pxl8_palette* pal) {
|
||||
return pal ? pal->color_count : 0;
|
||||
}
|
||||
|
||||
u8 pxl8_palette_ramp_index(const pxl8_palette* pal, u8 position) {
|
||||
if (!pal || position >= pal->color_count) return 0;
|
||||
return pal->color_ramp[position];
|
||||
}
|
||||
|
||||
u8 pxl8_palette_ramp_position(const pxl8_palette* pal, u8 index) {
|
||||
if (!pal) return 0;
|
||||
for (u32 i = 0; i < pal->color_count; i++) {
|
||||
if (pal->color_ramp[i] == index) return (u8)i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
u8 pxl8_palette_find_closest(const pxl8_palette* pal, u8 r, u8 g, u8 b) {
|
||||
if (!pal || pal->color_count < 2) return 1;
|
||||
|
||||
u8 best_idx = 1;
|
||||
u32 best_dist = 0xFFFFFFFF;
|
||||
|
||||
for (u32 i = 1; i < pal->color_count; i++) {
|
||||
u8 pr, pg, pb, pa;
|
||||
unpack_rgba(pal->colors[i], &pr, &pg, &pb, &pa);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
u32 pxl8_palette_color(const pxl8_palette* pal, u8 idx) {
|
||||
if (!pal) return 0;
|
||||
return pxl8_color_to_rgba(pal->colors[idx]);
|
||||
}
|
||||
|
||||
i32 pxl8_palette_index(const pxl8_palette* pal, u32 rgba) {
|
||||
if (!pal) return -1;
|
||||
if (rgba <= 0xFFFFFF) rgba = (rgba << 8) | 0xFF;
|
||||
u32 abgr = pxl8_color_from_rgba(rgba);
|
||||
u32 slot = pxl8_palette_hash(abgr);
|
||||
for (u32 i = 0; i < PXL8_PALETTE_HASH_SIZE; i++) {
|
||||
u32 idx = (slot + i) & (PXL8_PALETTE_HASH_SIZE - 1);
|
||||
if (pal->hash[idx].index < 0) return -1;
|
||||
if (pal->hash[idx].color == abgr) return pal->hash[idx].index;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void pxl8_palette_get_rgb(const pxl8_palette* pal, u8 idx, u8* r, u8* g, u8* b) {
|
||||
if (!pal || !r || !g || !b) return;
|
||||
u8 a;
|
||||
unpack_rgba(pal->colors[idx], r, g, b, &a);
|
||||
}
|
||||
|
||||
void pxl8_palette_get_rgba(const pxl8_palette* pal, u8 idx, u8* r, u8* g, u8* b, u8* a) {
|
||||
if (!pal || !r || !g || !b || !a) return;
|
||||
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) {
|
||||
if (pal) pal->colors[idx] = pack_rgb(r, g, b);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void pxl8_palette_fill_gradient(pxl8_palette* pal, u8 start, u8 count, u32 from, u32 to) {
|
||||
if (!pal || count == 0) return;
|
||||
|
||||
u8 r0, g0, b0, a0, r1, g1, b1, a1;
|
||||
unpack_rgba(from, &r0, &g0, &b0, &a0);
|
||||
unpack_rgba(to, &r1, &g1, &b1, &a1);
|
||||
|
||||
for (u8 i = 0; i < count; i++) {
|
||||
f32 t = (count > 1) ? (f32)i / (f32)(count - 1) : 0.0f;
|
||||
u8 r = (u8)(r0 + (r1 - r0) * t);
|
||||
u8 g = (u8)(g0 + (g1 - g0) * t);
|
||||
u8 b = (u8)(b0 + (b1 - b0) * t);
|
||||
u8 a = (u8)(a0 + (a1 - a0) * t);
|
||||
pal->colors[start + i] = pack_rgba(r, g, b, a);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
pxl8_palette_fill_gradient(pal, start, count, pack_rgb(r0, g0, b0), pack_rgb(r1, g1, b1));
|
||||
}
|
||||
|
||||
void pxl8_palette_reset_cycle(pxl8_palette* pal, u8 slot) {
|
||||
if (!pal || slot >= PXL8_MAX_CYCLES) return;
|
||||
pal->phases[slot] = 0.0f;
|
||||
pal->directions[slot] = 1;
|
||||
}
|
||||
|
||||
void pxl8_palette_set_cycle(pxl8_palette* pal, u8 slot, pxl8_cycle_range range) {
|
||||
if (!pal || slot >= PXL8_MAX_CYCLES) return;
|
||||
|
||||
pal->cycles[slot] = range;
|
||||
pal->phases[slot] = 0.0f;
|
||||
pal->directions[slot] = 1;
|
||||
|
||||
u8 start = range.start;
|
||||
u8 len = range.len;
|
||||
if (len > PXL8_MAX_CYCLE_LEN) len = PXL8_MAX_CYCLE_LEN;
|
||||
|
||||
for (u8 i = 0; i < len; i++) {
|
||||
pal->base_colors[slot][i] = pal->colors[start + i];
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_palette_set_cycle_colors(pxl8_palette* pal, u8 slot, const u32* colors, u8 count) {
|
||||
if (!pal || !colors || slot >= PXL8_MAX_CYCLES) return;
|
||||
|
||||
pxl8_cycle_range* cycle = &pal->cycles[slot];
|
||||
u8 start = cycle->start;
|
||||
u8 len = cycle->len;
|
||||
if (len > count) len = count;
|
||||
if (len > PXL8_MAX_CYCLE_LEN) len = PXL8_MAX_CYCLE_LEN;
|
||||
|
||||
for (u8 i = 0; i < len; i++) {
|
||||
pal->base_colors[slot][i] = colors[i];
|
||||
pal->colors[start + i] = colors[i];
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_palette_set_cycle_phase(pxl8_palette* pal, u8 slot, f32 phase) {
|
||||
if (!pal || slot >= PXL8_MAX_CYCLES) return;
|
||||
|
||||
if (phase < 0.0f) phase = 0.0f;
|
||||
if (phase > 1.0f) phase = 1.0f;
|
||||
|
||||
pal->phases[slot] = phase;
|
||||
update_cycle_colors(pal, slot);
|
||||
}
|
||||
|
||||
void pxl8_palette_tick(pxl8_palette* pal, u16 delta_ticks) {
|
||||
if (!pal) return;
|
||||
|
||||
for (u8 slot = 0; slot < PXL8_MAX_CYCLES; slot++) {
|
||||
pxl8_cycle_range* cycle = &pal->cycles[slot];
|
||||
if (cycle->period == 0 || cycle->len < 2) continue;
|
||||
|
||||
f32 delta = (f32)delta_ticks / (f32)cycle->period;
|
||||
f32 dir = (f32)pal->directions[slot];
|
||||
pal->phases[slot] += delta * dir;
|
||||
|
||||
switch (cycle->mode) {
|
||||
case PXL8_CYCLE_LOOP:
|
||||
while (pal->phases[slot] >= 1.0f) pal->phases[slot] -= 1.0f;
|
||||
while (pal->phases[slot] < 0.0f) pal->phases[slot] += 1.0f;
|
||||
break;
|
||||
|
||||
case PXL8_CYCLE_PINGPONG:
|
||||
if (pal->phases[slot] >= 1.0f) {
|
||||
pal->phases[slot] = 1.0f - (pal->phases[slot] - 1.0f);
|
||||
pal->directions[slot] = -1;
|
||||
} else if (pal->phases[slot] <= 0.0f) {
|
||||
pal->phases[slot] = -pal->phases[slot];
|
||||
pal->directions[slot] = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case PXL8_CYCLE_ONCE:
|
||||
if (pal->phases[slot] < 0.0f) pal->phases[slot] = 0.0f;
|
||||
if (pal->phases[slot] > 1.0f) pal->phases[slot] = 1.0f;
|
||||
break;
|
||||
}
|
||||
|
||||
update_cycle_colors(pal, slot);
|
||||
}
|
||||
}
|
||||
|
||||
pxl8_cycle_range pxl8_cycle_range_new(u8 start, u8 len, u16 period) {
|
||||
pxl8_cycle_range range = {
|
||||
.easing = PXL8_EASE_LINEAR,
|
||||
.interpolate = true,
|
||||
.len = len,
|
||||
.mode = PXL8_CYCLE_LOOP,
|
||||
.period = period,
|
||||
.start = start,
|
||||
};
|
||||
return range;
|
||||
}
|
||||
|
||||
pxl8_cycle_range pxl8_cycle_range_disabled(void) {
|
||||
pxl8_cycle_range range = {
|
||||
.easing = PXL8_EASE_LINEAR,
|
||||
.interpolate = false,
|
||||
.len = 0,
|
||||
.mode = PXL8_CYCLE_LOOP,
|
||||
.period = 0,
|
||||
.start = 0,
|
||||
};
|
||||
return range;
|
||||
}
|
||||
70
client/src/gfx/pxl8_palette.h
Normal file
70
client/src/gfx/pxl8_palette.h
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_types.h"
|
||||
|
||||
#define PXL8_PALETTE_SIZE 256
|
||||
#define PXL8_MAX_CYCLES 8
|
||||
#define PXL8_MAX_CYCLE_LEN 16
|
||||
|
||||
typedef struct pxl8_palette pxl8_palette;
|
||||
|
||||
typedef enum pxl8_cycle_mode {
|
||||
PXL8_CYCLE_LOOP,
|
||||
PXL8_CYCLE_ONCE,
|
||||
PXL8_CYCLE_PINGPONG,
|
||||
} pxl8_cycle_mode;
|
||||
|
||||
typedef enum pxl8_easing {
|
||||
PXL8_EASE_LINEAR,
|
||||
PXL8_EASE_IN,
|
||||
PXL8_EASE_IN_OUT,
|
||||
PXL8_EASE_OUT,
|
||||
} pxl8_easing;
|
||||
|
||||
typedef struct pxl8_cycle_range {
|
||||
pxl8_easing easing;
|
||||
bool interpolate;
|
||||
u8 len;
|
||||
pxl8_cycle_mode mode;
|
||||
u16 period;
|
||||
u8 start;
|
||||
} pxl8_cycle_range;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
pxl8_palette* pxl8_palette_create(void);
|
||||
void pxl8_palette_destroy(pxl8_palette* pal);
|
||||
|
||||
pxl8_result pxl8_palette_load_ase(pxl8_palette* pal, const char* path);
|
||||
|
||||
u16 pxl8_palette_color_count(const pxl8_palette* pal);
|
||||
u8* pxl8_palette_color_ramp(pxl8_palette* pal);
|
||||
u32* pxl8_palette_colors(pxl8_palette* pal);
|
||||
u8 pxl8_palette_ramp_index(const pxl8_palette* pal, u8 position);
|
||||
u8 pxl8_palette_ramp_position(const pxl8_palette* pal, u8 index);
|
||||
u8 pxl8_palette_find_closest(const pxl8_palette* pal, u8 r, u8 g, u8 b);
|
||||
u32 pxl8_palette_color(const pxl8_palette* pal, u8 idx);
|
||||
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_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_rgba(pxl8_palette* pal, u8 idx, u8 r, u8 g, u8 b, u8 a);
|
||||
|
||||
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_reset_cycle(pxl8_palette* pal, u8 slot);
|
||||
void pxl8_palette_set_cycle(pxl8_palette* pal, u8 slot, pxl8_cycle_range range);
|
||||
void pxl8_palette_set_cycle_colors(pxl8_palette* pal, u8 slot, const u32* colors, u8 count);
|
||||
void pxl8_palette_set_cycle_phase(pxl8_palette* pal, u8 slot, f32 phase);
|
||||
void pxl8_palette_tick(pxl8_palette* pal, u16 delta_ticks);
|
||||
|
||||
pxl8_cycle_range pxl8_cycle_range_new(u8 start, u8 len, u16 period);
|
||||
pxl8_cycle_range pxl8_cycle_range_disabled(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
322
client/src/gfx/pxl8_particles.c
Normal file
322
client/src/gfx/pxl8_particles.c
Normal file
|
|
@ -0,0 +1,322 @@
|
|||
#include "pxl8_particles.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "pxl8_gfx.h"
|
||||
#include "pxl8_gfx2d.h"
|
||||
#include "pxl8_palette.h"
|
||||
#include "pxl8_rng.h"
|
||||
|
||||
struct pxl8_particles {
|
||||
pxl8_particle* particles;
|
||||
pxl8_palette* palette;
|
||||
pxl8_rng* rng;
|
||||
u32 alive_count;
|
||||
u32 count;
|
||||
u32 max_count;
|
||||
|
||||
f32 x, y;
|
||||
f32 spread_x, spread_y;
|
||||
|
||||
f32 drag;
|
||||
f32 gravity_x, gravity_y;
|
||||
f32 turbulence;
|
||||
|
||||
f32 spawn_rate;
|
||||
f32 spawn_timer;
|
||||
|
||||
u8 color_min, color_max;
|
||||
f32 life_min, life_max;
|
||||
f32 size_min, size_max;
|
||||
f32 vx_min, vx_max, vy_min, vy_max;
|
||||
|
||||
pxl8_particle_render_fn render_fn;
|
||||
pxl8_particle_spawn_fn spawn_fn;
|
||||
pxl8_particle_update_fn update_fn;
|
||||
void* userdata;
|
||||
};
|
||||
|
||||
pxl8_particles* pxl8_particles_create(u32 max_count, pxl8_rng* rng) {
|
||||
pxl8_particles* ps = calloc(1, sizeof(pxl8_particles));
|
||||
if (!ps) return NULL;
|
||||
|
||||
ps->particles = calloc(max_count, sizeof(pxl8_particle));
|
||||
if (!ps->particles) {
|
||||
free(ps);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ps->rng = rng;
|
||||
ps->max_count = max_count;
|
||||
ps->drag = 0.98f;
|
||||
ps->gravity_y = 100.0f;
|
||||
ps->spawn_rate = 10.0f;
|
||||
|
||||
ps->color_min = ps->color_max = 15;
|
||||
ps->life_min = ps->life_max = 1.0f;
|
||||
ps->size_min = ps->size_max = 1.0f;
|
||||
|
||||
return ps;
|
||||
}
|
||||
|
||||
void pxl8_particles_destroy(pxl8_particles* ps) {
|
||||
if (!ps) return;
|
||||
free(ps->particles);
|
||||
free(ps);
|
||||
}
|
||||
|
||||
void pxl8_particles_clear(pxl8_particles* ps) {
|
||||
if (!ps || !ps->particles) return;
|
||||
|
||||
for (u32 i = 0; i < ps->max_count; i++) {
|
||||
ps->particles[i].life = 0;
|
||||
ps->particles[i].flags = 0;
|
||||
}
|
||||
ps->alive_count = 0;
|
||||
ps->spawn_timer = 0;
|
||||
}
|
||||
|
||||
void pxl8_particles_emit(pxl8_particles* ps, u32 count) {
|
||||
if (!ps || !ps->particles) return;
|
||||
|
||||
for (u32 i = 0; i < count && ps->alive_count < ps->max_count; i++) {
|
||||
for (u32 j = 0; j < ps->max_count; j++) {
|
||||
if (ps->particles[j].life <= 0) {
|
||||
pxl8_particle* p = &ps->particles[j];
|
||||
|
||||
f32 life = ps->life_min + pxl8_rng_f32(ps->rng) * (ps->life_max - ps->life_min);
|
||||
p->life = life;
|
||||
p->max_life = life;
|
||||
|
||||
p->x = ps->x + (pxl8_rng_f32(ps->rng) - 0.5f) * ps->spread_x;
|
||||
p->y = ps->y + (pxl8_rng_f32(ps->rng) - 0.5f) * ps->spread_y;
|
||||
p->z = 0;
|
||||
|
||||
p->vx = ps->vx_min + pxl8_rng_f32(ps->rng) * (ps->vx_max - ps->vx_min);
|
||||
p->vy = ps->vy_min + pxl8_rng_f32(ps->rng) * (ps->vy_max - ps->vy_min);
|
||||
p->vz = 0;
|
||||
|
||||
p->ax = ps->gravity_x;
|
||||
p->ay = ps->gravity_y;
|
||||
p->az = 0;
|
||||
|
||||
u8 ramp_range = ps->color_max - ps->color_min + 1;
|
||||
u8 ramp_pos = ps->color_min + (pxl8_rng_next(ps->rng) % ramp_range);
|
||||
u8 color = ps->palette
|
||||
? pxl8_palette_ramp_index(ps->palette, ramp_pos)
|
||||
: ramp_pos;
|
||||
p->color = p->start_color = p->end_color = color;
|
||||
|
||||
p->size = ps->size_min + pxl8_rng_f32(ps->rng) * (ps->size_max - ps->size_min);
|
||||
p->angle = 0;
|
||||
p->spin = 0;
|
||||
p->flags = 1;
|
||||
|
||||
if (ps->spawn_fn) {
|
||||
ps->spawn_fn(ps, p);
|
||||
}
|
||||
|
||||
ps->alive_count++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_particles_render(pxl8_particles* ps, pxl8_gfx* gfx) {
|
||||
if (!ps || !ps->particles || !gfx) return;
|
||||
|
||||
for (u32 i = 0; i < ps->max_count; i++) {
|
||||
pxl8_particle* p = &ps->particles[i];
|
||||
if (p->life > 0 && p->flags) {
|
||||
if (ps->render_fn) {
|
||||
ps->render_fn(gfx, p, ps->userdata);
|
||||
} else {
|
||||
i32 x = (i32)p->x;
|
||||
i32 y = (i32)p->y;
|
||||
if (x >= 0 && x < pxl8_gfx_get_width(gfx) && y >= 0 && y < pxl8_gfx_get_height(gfx)) {
|
||||
pxl8_2d_pixel(gfx, x, y, p->color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_particles_update(pxl8_particles* ps, f32 dt) {
|
||||
if (!ps || !ps->particles) return;
|
||||
|
||||
if (ps->spawn_rate > 0.0f) {
|
||||
ps->spawn_timer += dt;
|
||||
f32 spawn_interval = 1.0f / ps->spawn_rate;
|
||||
u32 max_spawns_per_frame = ps->max_count / 10;
|
||||
if (max_spawns_per_frame < 1) max_spawns_per_frame = 1;
|
||||
u32 spawn_count = 0;
|
||||
while (ps->spawn_timer >= spawn_interval && spawn_count < max_spawns_per_frame) {
|
||||
pxl8_particles_emit(ps, 1);
|
||||
ps->spawn_timer -= spawn_interval;
|
||||
spawn_count++;
|
||||
}
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < ps->max_count; i++) {
|
||||
pxl8_particle* p = &ps->particles[i];
|
||||
if (p->life > 0) {
|
||||
if (ps->update_fn) {
|
||||
ps->update_fn(p, dt, ps->userdata);
|
||||
} else {
|
||||
p->vx += p->ax * dt;
|
||||
p->vy += p->ay * dt;
|
||||
p->vz += p->az * dt;
|
||||
|
||||
p->vx *= ps->drag;
|
||||
p->vy *= ps->drag;
|
||||
p->vz *= ps->drag;
|
||||
|
||||
p->x += p->vx * dt;
|
||||
p->y += p->vy * dt;
|
||||
p->z += p->vz * dt;
|
||||
|
||||
p->angle += p->spin * dt;
|
||||
}
|
||||
|
||||
p->life -= dt / p->max_life;
|
||||
if (p->life <= 0) {
|
||||
p->flags = 0;
|
||||
ps->alive_count--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u32 pxl8_particles_count(const pxl8_particles* ps) {
|
||||
return ps ? ps->alive_count : 0;
|
||||
}
|
||||
|
||||
u32 pxl8_particles_max_count(const pxl8_particles* ps) {
|
||||
return ps ? ps->max_count : 0;
|
||||
}
|
||||
|
||||
pxl8_particle* pxl8_particles_get(pxl8_particles* ps, u32 index) {
|
||||
if (!ps || index >= ps->max_count) return NULL;
|
||||
return &ps->particles[index];
|
||||
}
|
||||
|
||||
pxl8_rng* pxl8_particles_rng(pxl8_particles* ps) {
|
||||
return ps ? ps->rng : NULL;
|
||||
}
|
||||
|
||||
f32 pxl8_particles_get_drag(const pxl8_particles* ps) {
|
||||
return ps ? ps->drag : 0.0f;
|
||||
}
|
||||
|
||||
f32 pxl8_particles_get_gravity_x(const pxl8_particles* ps) {
|
||||
return ps ? ps->gravity_x : 0.0f;
|
||||
}
|
||||
|
||||
f32 pxl8_particles_get_gravity_y(const pxl8_particles* ps) {
|
||||
return ps ? ps->gravity_y : 0.0f;
|
||||
}
|
||||
|
||||
f32 pxl8_particles_get_spawn_rate(const pxl8_particles* ps) {
|
||||
return ps ? ps->spawn_rate : 0.0f;
|
||||
}
|
||||
|
||||
f32 pxl8_particles_get_spread_x(const pxl8_particles* ps) {
|
||||
return ps ? ps->spread_x : 0.0f;
|
||||
}
|
||||
|
||||
f32 pxl8_particles_get_spread_y(const pxl8_particles* ps) {
|
||||
return ps ? ps->spread_y : 0.0f;
|
||||
}
|
||||
|
||||
f32 pxl8_particles_get_turbulence(const pxl8_particles* ps) {
|
||||
return ps ? ps->turbulence : 0.0f;
|
||||
}
|
||||
|
||||
void* pxl8_particles_get_userdata(const pxl8_particles* ps) {
|
||||
return ps ? ps->userdata : NULL;
|
||||
}
|
||||
|
||||
f32 pxl8_particles_get_x(const pxl8_particles* ps) {
|
||||
return ps ? ps->x : 0.0f;
|
||||
}
|
||||
|
||||
f32 pxl8_particles_get_y(const pxl8_particles* ps) {
|
||||
return ps ? ps->y : 0.0f;
|
||||
}
|
||||
|
||||
void pxl8_particles_set_colors(pxl8_particles* ps, u8 color_min, u8 color_max) {
|
||||
if (!ps) return;
|
||||
ps->color_min = color_min;
|
||||
ps->color_max = color_max;
|
||||
}
|
||||
|
||||
void pxl8_particles_set_drag(pxl8_particles* ps, f32 drag) {
|
||||
if (ps) ps->drag = drag;
|
||||
}
|
||||
|
||||
void pxl8_particles_set_gravity(pxl8_particles* ps, f32 gx, f32 gy) {
|
||||
if (!ps) return;
|
||||
ps->gravity_x = gx;
|
||||
ps->gravity_y = gy;
|
||||
}
|
||||
|
||||
void pxl8_particles_set_life(pxl8_particles* ps, f32 life_min, f32 life_max) {
|
||||
if (!ps) return;
|
||||
ps->life_min = life_min;
|
||||
ps->life_max = life_max;
|
||||
}
|
||||
|
||||
void pxl8_particles_set_palette(pxl8_particles* ps, pxl8_palette* palette) {
|
||||
if (ps) ps->palette = palette;
|
||||
}
|
||||
|
||||
void pxl8_particles_set_position(pxl8_particles* ps, f32 x, f32 y) {
|
||||
if (!ps) return;
|
||||
ps->x = x;
|
||||
ps->y = y;
|
||||
}
|
||||
|
||||
void pxl8_particles_set_render_fn(pxl8_particles* ps, pxl8_particle_render_fn fn) {
|
||||
if (ps) ps->render_fn = fn;
|
||||
}
|
||||
|
||||
void pxl8_particles_set_size(pxl8_particles* ps, f32 size_min, f32 size_max) {
|
||||
if (!ps) return;
|
||||
ps->size_min = size_min;
|
||||
ps->size_max = size_max;
|
||||
}
|
||||
|
||||
void pxl8_particles_set_spawn_fn(pxl8_particles* ps, pxl8_particle_spawn_fn fn) {
|
||||
if (ps) ps->spawn_fn = fn;
|
||||
}
|
||||
|
||||
void pxl8_particles_set_spawn_rate(pxl8_particles* ps, f32 rate) {
|
||||
if (ps) ps->spawn_rate = rate;
|
||||
}
|
||||
|
||||
void pxl8_particles_set_spread(pxl8_particles* ps, f32 spread_x, f32 spread_y) {
|
||||
if (!ps) return;
|
||||
ps->spread_x = spread_x;
|
||||
ps->spread_y = spread_y;
|
||||
}
|
||||
|
||||
void pxl8_particles_set_turbulence(pxl8_particles* ps, f32 turbulence) {
|
||||
if (ps) ps->turbulence = turbulence;
|
||||
}
|
||||
|
||||
void pxl8_particles_set_update_fn(pxl8_particles* ps, pxl8_particle_update_fn fn) {
|
||||
if (ps) ps->update_fn = fn;
|
||||
}
|
||||
|
||||
void pxl8_particles_set_userdata(pxl8_particles* ps, void* userdata) {
|
||||
if (ps) ps->userdata = userdata;
|
||||
}
|
||||
|
||||
void pxl8_particles_set_velocity(pxl8_particles* ps, f32 vx_min, f32 vx_max, f32 vy_min, f32 vy_max) {
|
||||
if (!ps) return;
|
||||
ps->vx_min = vx_min;
|
||||
ps->vx_max = vx_max;
|
||||
ps->vy_min = vy_min;
|
||||
ps->vy_max = vy_max;
|
||||
}
|
||||
75
client/src/gfx/pxl8_particles.h
Normal file
75
client/src/gfx/pxl8_particles.h
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_types.h"
|
||||
|
||||
typedef struct pxl8_gfx pxl8_gfx;
|
||||
typedef struct pxl8_palette pxl8_palette;
|
||||
typedef struct pxl8_particles pxl8_particles;
|
||||
typedef struct pxl8_rng pxl8_rng;
|
||||
|
||||
typedef struct pxl8_particle {
|
||||
f32 angle;
|
||||
f32 ax, ay, az;
|
||||
u32 color;
|
||||
u32 end_color;
|
||||
u8 flags;
|
||||
f32 life;
|
||||
f32 max_life;
|
||||
f32 size;
|
||||
f32 spin;
|
||||
u32 start_color;
|
||||
f32 vx, vy, vz;
|
||||
f32 x, y, z;
|
||||
} pxl8_particle;
|
||||
|
||||
typedef void (*pxl8_particle_render_fn)(pxl8_gfx* gfx, pxl8_particle* p, void* userdata);
|
||||
typedef void (*pxl8_particle_spawn_fn)(pxl8_particles* ps, pxl8_particle* p);
|
||||
typedef void (*pxl8_particle_update_fn)(pxl8_particle* p, f32 dt, void* userdata);
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
pxl8_particles* pxl8_particles_create(u32 max_count, pxl8_rng* rng);
|
||||
void pxl8_particles_destroy(pxl8_particles* ps);
|
||||
|
||||
void pxl8_particles_clear(pxl8_particles* ps);
|
||||
void pxl8_particles_emit(pxl8_particles* ps, u32 count);
|
||||
void pxl8_particles_render(pxl8_particles* ps, pxl8_gfx* gfx);
|
||||
void pxl8_particles_update(pxl8_particles* ps, f32 dt);
|
||||
|
||||
u32 pxl8_particles_count(const pxl8_particles* ps);
|
||||
u32 pxl8_particles_max_count(const pxl8_particles* ps);
|
||||
pxl8_particle* pxl8_particles_get(pxl8_particles* ps, u32 index);
|
||||
pxl8_rng* pxl8_particles_rng(pxl8_particles* ps);
|
||||
|
||||
f32 pxl8_particles_get_drag(const pxl8_particles* ps);
|
||||
f32 pxl8_particles_get_gravity_x(const pxl8_particles* ps);
|
||||
f32 pxl8_particles_get_gravity_y(const pxl8_particles* ps);
|
||||
f32 pxl8_particles_get_spawn_rate(const pxl8_particles* ps);
|
||||
f32 pxl8_particles_get_spread_x(const pxl8_particles* ps);
|
||||
f32 pxl8_particles_get_spread_y(const pxl8_particles* ps);
|
||||
f32 pxl8_particles_get_turbulence(const pxl8_particles* ps);
|
||||
void* pxl8_particles_get_userdata(const pxl8_particles* ps);
|
||||
f32 pxl8_particles_get_x(const pxl8_particles* ps);
|
||||
f32 pxl8_particles_get_y(const pxl8_particles* ps);
|
||||
|
||||
void pxl8_particles_set_colors(pxl8_particles* ps, u8 color_min, u8 color_max);
|
||||
void pxl8_particles_set_drag(pxl8_particles* ps, f32 drag);
|
||||
void pxl8_particles_set_gravity(pxl8_particles* ps, f32 gx, f32 gy);
|
||||
void pxl8_particles_set_life(pxl8_particles* ps, f32 life_min, f32 life_max);
|
||||
void pxl8_particles_set_palette(pxl8_particles* ps, pxl8_palette* palette);
|
||||
void pxl8_particles_set_position(pxl8_particles* ps, f32 x, f32 y);
|
||||
void pxl8_particles_set_render_fn(pxl8_particles* ps, pxl8_particle_render_fn fn);
|
||||
void pxl8_particles_set_size(pxl8_particles* ps, f32 size_min, f32 size_max);
|
||||
void pxl8_particles_set_spawn_fn(pxl8_particles* ps, pxl8_particle_spawn_fn fn);
|
||||
void pxl8_particles_set_spawn_rate(pxl8_particles* ps, f32 rate);
|
||||
void pxl8_particles_set_spread(pxl8_particles* ps, f32 spread_x, f32 spread_y);
|
||||
void pxl8_particles_set_turbulence(pxl8_particles* ps, f32 turbulence);
|
||||
void pxl8_particles_set_update_fn(pxl8_particles* ps, pxl8_particle_update_fn fn);
|
||||
void pxl8_particles_set_userdata(pxl8_particles* ps, void* userdata);
|
||||
void pxl8_particles_set_velocity(pxl8_particles* ps, f32 vx_min, f32 vx_max, f32 vy_min, f32 vy_max);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "pxl8_ase.h"
|
||||
#include "pxl8_color.h"
|
||||
#include "pxl8_gfx.h"
|
||||
#include "pxl8_log.h"
|
||||
#include "pxl8_tilemap.h"
|
||||
|
||||
|
|
@ -227,7 +228,7 @@ void pxl8_tilesheet_render_tile(const pxl8_tilesheet* tilesheet, pxl8_gfx* gfx,
|
|||
|
||||
if (screen_x >= 0 && screen_x < pxl8_gfx_get_width(gfx) &&
|
||||
screen_y >= 0 && screen_y < pxl8_gfx_get_height(gfx)) {
|
||||
pxl8_pixel(gfx, screen_x, screen_y, color_idx);
|
||||
pxl8_2d_pixel(gfx, screen_x, screen_y, color_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
#include "pxl8_transition.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "pxl8_gfx.h"
|
||||
#include "pxl8_log.h"
|
||||
#include "pxl8_math.h"
|
||||
|
||||
|
|
@ -68,7 +69,7 @@ void pxl8_transition_render(const pxl8_transition* transition, pxl8_gfx* gfx) {
|
|||
|
||||
for (i32 y = 0; y < height; y++) {
|
||||
for (i32 x = 0; x < width; x++) {
|
||||
u32 bg = pxl8_get_pixel(gfx, x, y);
|
||||
u32 bg = pxl8_2d_get_pixel(gfx, x, y);
|
||||
u32 r_bg = (bg >> 16) & 0xFF;
|
||||
u32 g_bg = (bg >> 8) & 0xFF;
|
||||
u32 b_bg = bg & 0xFF;
|
||||
|
|
@ -81,7 +82,7 @@ void pxl8_transition_render(const pxl8_transition* transition, pxl8_gfx* gfx) {
|
|||
u32 g = (g_bg * (255 - alpha) + g_fg * alpha) / 255;
|
||||
u32 b = (b_bg * (255 - alpha) + b_fg * alpha) / 255;
|
||||
|
||||
pxl8_pixel(gfx, x, y, 0xFF000000 | (r << 16) | (g << 8) | b);
|
||||
pxl8_2d_pixel(gfx, x, y, 0xFF000000 | (r << 16) | (g << 8) | b);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -89,25 +90,25 @@ void pxl8_transition_render(const pxl8_transition* transition, pxl8_gfx* gfx) {
|
|||
|
||||
case PXL8_TRANSITION_WIPE_LEFT: {
|
||||
i32 wipe_x = (i32)(width * progress);
|
||||
pxl8_rect_fill(gfx, 0, 0, wipe_x, height, transition->color);
|
||||
pxl8_2d_rect_fill(gfx, 0, 0, wipe_x, height, transition->color);
|
||||
break;
|
||||
}
|
||||
|
||||
case PXL8_TRANSITION_WIPE_RIGHT: {
|
||||
i32 wipe_x = (i32)(width * (1.0f - progress));
|
||||
pxl8_rect_fill(gfx, wipe_x, 0, width - wipe_x, height, transition->color);
|
||||
pxl8_2d_rect_fill(gfx, wipe_x, 0, width - wipe_x, height, transition->color);
|
||||
break;
|
||||
}
|
||||
|
||||
case PXL8_TRANSITION_WIPE_UP: {
|
||||
i32 wipe_y = (i32)(height * progress);
|
||||
pxl8_rect_fill(gfx, 0, 0, width, wipe_y, transition->color);
|
||||
pxl8_2d_rect_fill(gfx, 0, 0, width, wipe_y, transition->color);
|
||||
break;
|
||||
}
|
||||
|
||||
case PXL8_TRANSITION_WIPE_DOWN: {
|
||||
i32 wipe_y = (i32)(height * (1.0f - progress));
|
||||
pxl8_rect_fill(gfx, 0, wipe_y, width, height - wipe_y, transition->color);
|
||||
pxl8_2d_rect_fill(gfx, 0, wipe_y, width, height - wipe_y, transition->color);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -123,7 +124,7 @@ void pxl8_transition_render(const pxl8_transition* transition, pxl8_gfx* gfx) {
|
|||
i32 dy = y - center_y;
|
||||
i32 dist = (i32)sqrtf((f32)(dx * dx + dy * dy));
|
||||
if (dist > radius) {
|
||||
pxl8_pixel(gfx, x, y, transition->color);
|
||||
pxl8_2d_pixel(gfx, x, y, transition->color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -142,7 +143,7 @@ void pxl8_transition_render(const pxl8_transition* transition, pxl8_gfx* gfx) {
|
|||
i32 dy = y - center_y;
|
||||
i32 dist = (i32)sqrtf((f32)(dx * dx + dy * dy));
|
||||
if (dist < radius) {
|
||||
pxl8_pixel(gfx, x, y, transition->color);
|
||||
pxl8_2d_pixel(gfx, x, y, transition->color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -156,7 +157,7 @@ void pxl8_transition_render(const pxl8_transition* transition, pxl8_gfx* gfx) {
|
|||
seed = seed * 1103515245 + 12345;
|
||||
f32 noise = (f32)((seed / 65536) % 1000) / 1000.0f;
|
||||
if (noise < progress) {
|
||||
pxl8_pixel(gfx, x, y, transition->color);
|
||||
pxl8_2d_pixel(gfx, x, y, transition->color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -181,7 +182,7 @@ void pxl8_transition_render(const pxl8_transition* transition, pxl8_gfx* gfx) {
|
|||
|
||||
for (i32 by = 0; by < block_size && y + by < height; by++) {
|
||||
for (i32 bx = 0; bx < block_size && x + bx < width; bx++) {
|
||||
u32 color = pxl8_get_pixel(gfx, x + bx, y + by);
|
||||
u32 color = pxl8_2d_get_pixel(gfx, x + bx, y + by);
|
||||
color_sum_r += (color >> 16) & 0xFF;
|
||||
color_sum_g += (color >> 8) & 0xFF;
|
||||
color_sum_b += color & 0xFF;
|
||||
|
|
@ -197,7 +198,7 @@ void pxl8_transition_render(const pxl8_transition* transition, pxl8_gfx* gfx) {
|
|||
|
||||
for (i32 by = 0; by < block_size && y + by < height; by++) {
|
||||
for (i32 bx = 0; bx < block_size && x + bx < width; bx++) {
|
||||
pxl8_pixel(gfx, x + bx, y + by, avg_color);
|
||||
pxl8_2d_pixel(gfx, x + bx, y + by, avg_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
162
client/src/lua/pxl8.lua
Normal file
162
client/src/lua/pxl8.lua
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
local anim = require("pxl8.anim")
|
||||
local core = require("pxl8.core")
|
||||
local gfx2d = require("pxl8.gfx2d")
|
||||
local gfx3d = require("pxl8.gfx3d")
|
||||
local gui = require("pxl8.gui")
|
||||
local input = require("pxl8.input")
|
||||
local math3d = require("pxl8.math")
|
||||
local particles = require("pxl8.particles")
|
||||
local sfx = require("pxl8.sfx")
|
||||
local tilemap = require("pxl8.tilemap")
|
||||
local transition = require("pxl8.transition")
|
||||
local world = require("pxl8.world")
|
||||
local pxl8 = {}
|
||||
|
||||
core.init(pxl8_gfx, pxl8_input, pxl8_rng, pxl8_sfx, pxl8_sys)
|
||||
|
||||
pxl8.get_fps = core.get_fps
|
||||
pxl8.get_height = core.get_height
|
||||
pxl8.get_title = core.get_title
|
||||
pxl8.get_width = core.get_width
|
||||
pxl8.info = core.info
|
||||
pxl8.warn = core.warn
|
||||
pxl8.error = core.error
|
||||
pxl8.debug = core.debug
|
||||
pxl8.trace = core.trace
|
||||
pxl8.quit = core.quit
|
||||
|
||||
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.palette_color = core.palette_color
|
||||
pxl8.palette_index = core.palette_index
|
||||
pxl8.ramp_index = core.ramp_index
|
||||
|
||||
pxl8.clear = gfx2d.clear
|
||||
pxl8.pixel = gfx2d.pixel
|
||||
pxl8.line = gfx2d.line
|
||||
pxl8.rect = gfx2d.rect
|
||||
pxl8.rect_fill = gfx2d.rect_fill
|
||||
pxl8.circle = gfx2d.circle
|
||||
pxl8.circle_fill = gfx2d.circle_fill
|
||||
pxl8.text = gfx2d.text
|
||||
pxl8.sprite = gfx2d.sprite
|
||||
pxl8.load_palette = gfx2d.load_palette
|
||||
pxl8.load_sprite = gfx2d.load_sprite
|
||||
pxl8.create_texture = gfx2d.create_texture
|
||||
pxl8.gfx_color_ramp = gfx2d.color_ramp
|
||||
pxl8.gfx_fade_palette = gfx2d.fade_palette
|
||||
pxl8.gfx_cycle_palette = gfx2d.cycle_palette
|
||||
pxl8.add_palette_cycle = gfx2d.add_palette_cycle
|
||||
pxl8.remove_palette_cycle = gfx2d.remove_palette_cycle
|
||||
pxl8.set_palette_cycle_speed = gfx2d.set_palette_cycle_speed
|
||||
pxl8.clear_palette_cycles = gfx2d.clear_palette_cycles
|
||||
|
||||
pxl8.key_down = input.key_down
|
||||
pxl8.key_pressed = input.key_pressed
|
||||
pxl8.key_released = input.key_released
|
||||
pxl8.mouse_dx = input.mouse_dx
|
||||
pxl8.mouse_dy = input.mouse_dy
|
||||
pxl8.mouse_wheel_x = input.mouse_wheel_x
|
||||
pxl8.mouse_wheel_y = input.mouse_wheel_y
|
||||
pxl8.mouse_x = input.mouse_x
|
||||
pxl8.mouse_y = input.mouse_y
|
||||
pxl8.get_mouse_pos = input.get_mouse_pos
|
||||
pxl8.mouse_pressed = input.mouse_pressed
|
||||
pxl8.mouse_released = input.mouse_released
|
||||
pxl8.center_cursor = input.center_cursor
|
||||
pxl8.set_cursor = input.set_cursor
|
||||
pxl8.set_relative_mouse_mode = input.set_relative_mouse_mode
|
||||
|
||||
pxl8.Particles = particles.Particles
|
||||
pxl8.create_particles = function(max_count) return particles.Particles.new(max_count) end
|
||||
|
||||
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.create_camera_3d = function() return gfx3d.Camera3D.new() end
|
||||
pxl8.begin_frame_3d = gfx3d.begin_frame
|
||||
pxl8.end_frame_3d = gfx3d.end_frame
|
||||
pxl8.clear_3d = gfx3d.clear
|
||||
pxl8.clear_depth = gfx3d.clear_depth
|
||||
pxl8.draw_line_3d = gfx3d.draw_line
|
||||
pxl8.Mesh = gfx3d.Mesh
|
||||
pxl8.create_mesh = function(vertices, indices) return gfx3d.Mesh.new(vertices, indices) end
|
||||
pxl8.draw_mesh = gfx3d.draw_mesh
|
||||
|
||||
pxl8.mat4_identity = math3d.mat4_identity
|
||||
pxl8.mat4_multiply = math3d.mat4_multiply
|
||||
pxl8.mat4_translate = math3d.mat4_translate
|
||||
pxl8.mat4_rotate_x = math3d.mat4_rotate_x
|
||||
pxl8.mat4_rotate_y = math3d.mat4_rotate_y
|
||||
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.create_gui = function() return gui.Gui.new() end
|
||||
pxl8.gui_label = gui.label
|
||||
pxl8.gui_window = gui.window
|
||||
|
||||
pxl8.World = world.World
|
||||
pxl8.create_world = function() return world.World.new() end
|
||||
pxl8.procgen_tex = world.procgen_tex
|
||||
pxl8.PROCGEN_ROOMS = world.PROCGEN_ROOMS
|
||||
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.SfxNode = sfx.SfxNode
|
||||
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_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_FILTER_BANDPASS = sfx.FILTER_BANDPASS
|
||||
pxl8.SFX_FILTER_HIGHPASS = sfx.FILTER_HIGHPASS
|
||||
pxl8.SFX_FILTER_LOWPASS = sfx.FILTER_LOWPASS
|
||||
pxl8.SFX_FILTER_NONE = sfx.FILTER_NONE
|
||||
|
||||
pxl8.SFX_LFO_AMPLITUDE = sfx.LFO_AMPLITUDE
|
||||
pxl8.SFX_LFO_FILTER = sfx.LFO_FILTER
|
||||
pxl8.SFX_LFO_PITCH = sfx.LFO_PITCH
|
||||
|
||||
pxl8.SFX_NODE_COMPRESSOR = sfx.NODE_COMPRESSOR
|
||||
pxl8.SFX_NODE_DELAY = sfx.NODE_DELAY
|
||||
pxl8.SFX_NODE_REVERB = sfx.NODE_REVERB
|
||||
|
||||
pxl8.SFX_WAVE_NOISE = sfx.WAVE_NOISE
|
||||
pxl8.SFX_WAVE_PULSE = sfx.WAVE_PULSE
|
||||
pxl8.SFX_WAVE_SAW = sfx.WAVE_SAW
|
||||
pxl8.SFX_WAVE_SINE = sfx.WAVE_SINE
|
||||
pxl8.SFX_WAVE_SQUARE = sfx.WAVE_SQUARE
|
||||
pxl8.SFX_WAVE_TRIANGLE = sfx.WAVE_TRIANGLE
|
||||
|
||||
return pxl8
|
||||
126
client/src/lua/pxl8/anim.lua
Normal file
126
client/src/lua/pxl8/anim.lua
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
local ffi = require("ffi")
|
||||
local C = ffi.C
|
||||
local core = require("pxl8.core")
|
||||
|
||||
local anim = {}
|
||||
|
||||
local Anim = {}
|
||||
Anim.__index = Anim
|
||||
|
||||
function Anim.new(frame_ids, frame_durations)
|
||||
local frame_count = #frame_ids
|
||||
local c_frame_ids = ffi.new("u32[?]", frame_count)
|
||||
local c_frame_durations = nil
|
||||
|
||||
for i = 1, frame_count do
|
||||
c_frame_ids[i-1] = frame_ids[i]
|
||||
end
|
||||
|
||||
if frame_durations then
|
||||
c_frame_durations = ffi.new("u16[?]", frame_count)
|
||||
for i = 1, frame_count do
|
||||
c_frame_durations[i-1] = frame_durations[i]
|
||||
end
|
||||
end
|
||||
|
||||
local a = C.pxl8_anim_create(c_frame_ids, c_frame_durations, frame_count)
|
||||
if a == nil then
|
||||
return nil
|
||||
end
|
||||
return setmetatable({ _ptr = a }, Anim)
|
||||
end
|
||||
|
||||
function Anim.from_ase(filepath)
|
||||
local a = C.pxl8_anim_create_from_ase(core.gfx, filepath)
|
||||
if a == nil then
|
||||
return nil
|
||||
end
|
||||
return setmetatable({ _ptr = a }, Anim)
|
||||
end
|
||||
|
||||
function Anim:add_state(name, state_anim)
|
||||
return C.pxl8_anim_add_state(self._ptr, name, state_anim._ptr)
|
||||
end
|
||||
|
||||
function Anim:destroy()
|
||||
if self._ptr then
|
||||
C.pxl8_anim_destroy(self._ptr)
|
||||
self._ptr = nil
|
||||
end
|
||||
end
|
||||
|
||||
function Anim:get_current_frame()
|
||||
return C.pxl8_anim_get_current_frame(self._ptr)
|
||||
end
|
||||
|
||||
function Anim:get_current_frame_id()
|
||||
return C.pxl8_anim_get_current_frame_id(self._ptr)
|
||||
end
|
||||
|
||||
function Anim:get_state()
|
||||
local state_name = C.pxl8_anim_get_state(self._ptr)
|
||||
if state_name ~= nil then
|
||||
return ffi.string(state_name)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function Anim:has_state_machine()
|
||||
return C.pxl8_anim_has_state_machine(self._ptr)
|
||||
end
|
||||
|
||||
function Anim:is_complete()
|
||||
return C.pxl8_anim_is_complete(self._ptr)
|
||||
end
|
||||
|
||||
function Anim:is_playing()
|
||||
return C.pxl8_anim_is_playing(self._ptr)
|
||||
end
|
||||
|
||||
function Anim:pause()
|
||||
C.pxl8_anim_pause(self._ptr)
|
||||
end
|
||||
|
||||
function Anim:play()
|
||||
C.pxl8_anim_play(self._ptr)
|
||||
end
|
||||
|
||||
function Anim:render(x, y, w, h, flip_x, flip_y)
|
||||
C.pxl8_anim_render_sprite(self._ptr, core.gfx, x, y, w, h, flip_x or false, flip_y or false)
|
||||
end
|
||||
|
||||
function Anim:reset()
|
||||
C.pxl8_anim_reset(self._ptr)
|
||||
end
|
||||
|
||||
function Anim:set_frame(frame)
|
||||
C.pxl8_anim_set_frame(self._ptr, frame)
|
||||
end
|
||||
|
||||
function Anim:set_loop(loop)
|
||||
C.pxl8_anim_set_loop(self._ptr, loop)
|
||||
end
|
||||
|
||||
function Anim:set_reverse(reverse)
|
||||
C.pxl8_anim_set_reverse(self._ptr, reverse)
|
||||
end
|
||||
|
||||
function Anim:set_speed(speed)
|
||||
C.pxl8_anim_set_speed(self._ptr, speed)
|
||||
end
|
||||
|
||||
function Anim:set_state(name)
|
||||
return C.pxl8_anim_set_state(self._ptr, name)
|
||||
end
|
||||
|
||||
function Anim:stop()
|
||||
C.pxl8_anim_stop(self._ptr)
|
||||
end
|
||||
|
||||
function Anim:update(dt)
|
||||
C.pxl8_anim_update(self._ptr, dt)
|
||||
end
|
||||
|
||||
anim.Anim = Anim
|
||||
|
||||
return anim
|
||||
|
|
@ -11,10 +11,32 @@ function core.init(gfx, input, rng, sfx, sys)
|
|||
core.sys = sys
|
||||
end
|
||||
|
||||
function core.find_color(color)
|
||||
return C.pxl8_gfx_find_color(core.gfx, color)
|
||||
end
|
||||
|
||||
function core.get_fps()
|
||||
return C.pxl8_get_fps(core.sys)
|
||||
end
|
||||
|
||||
function core.palette_color(index)
|
||||
local pal = C.pxl8_gfx_get_palette(core.gfx)
|
||||
if pal == nil then return 0 end
|
||||
return C.pxl8_palette_color(pal, index)
|
||||
end
|
||||
|
||||
function core.palette_index(color)
|
||||
local pal = C.pxl8_gfx_get_palette(core.gfx)
|
||||
if pal == nil then return -1 end
|
||||
return C.pxl8_palette_index(pal, color)
|
||||
end
|
||||
|
||||
function core.ramp_index(position)
|
||||
local pal = C.pxl8_gfx_get_palette(core.gfx)
|
||||
if pal == nil then return 0 end
|
||||
return C.pxl8_palette_ramp_index(pal, position)
|
||||
end
|
||||
|
||||
function core.get_width()
|
||||
return C.pxl8_gfx_get_width(core.gfx)
|
||||
end
|
||||
|
|
@ -5,43 +5,43 @@ local core = require("pxl8.core")
|
|||
local graphics = {}
|
||||
|
||||
function graphics.clear(color)
|
||||
C.pxl8_clear(core.gfx, color or 0)
|
||||
C.pxl8_2d_clear(core.gfx, color or 0)
|
||||
end
|
||||
|
||||
function graphics.pixel(x, y, color)
|
||||
if color then
|
||||
C.pxl8_pixel(core.gfx, x, y, color)
|
||||
C.pxl8_2d_pixel(core.gfx, x, y, color)
|
||||
else
|
||||
return C.pxl8_get_pixel(core.gfx, x, y)
|
||||
return C.pxl8_2d_get_pixel(core.gfx, x, y)
|
||||
end
|
||||
end
|
||||
|
||||
function graphics.line(x0, y0, x1, y1, color)
|
||||
C.pxl8_line(core.gfx, x0, y0, x1, y1, color)
|
||||
C.pxl8_2d_line(core.gfx, x0, y0, x1, y1, color)
|
||||
end
|
||||
|
||||
function graphics.rect(x, y, w, h, color)
|
||||
C.pxl8_rect(core.gfx, x, y, w, h, color)
|
||||
C.pxl8_2d_rect(core.gfx, x, y, w, h, color)
|
||||
end
|
||||
|
||||
function graphics.rect_fill(x, y, w, h, color)
|
||||
C.pxl8_rect_fill(core.gfx, x, y, w, h, color)
|
||||
C.pxl8_2d_rect_fill(core.gfx, x, y, w, h, color)
|
||||
end
|
||||
|
||||
function graphics.circle(x, y, r, color)
|
||||
C.pxl8_circle(core.gfx, x, y, r, color)
|
||||
C.pxl8_2d_circle(core.gfx, x, y, r, color)
|
||||
end
|
||||
|
||||
function graphics.circle_fill(x, y, r, color)
|
||||
C.pxl8_circle_fill(core.gfx, x, y, r, color)
|
||||
C.pxl8_2d_circle_fill(core.gfx, x, y, r, color)
|
||||
end
|
||||
|
||||
function graphics.text(str, x, y, color)
|
||||
C.pxl8_text(core.gfx, str, x or 0, y or 0, color or 15)
|
||||
C.pxl8_2d_text(core.gfx, str, x or 0, y or 0, color or 15)
|
||||
end
|
||||
|
||||
function graphics.sprite(id, x, y, w, h, flip_x, flip_y)
|
||||
C.pxl8_sprite(core.gfx, id or 0, x or 0, y or 0, w or 16, h or 16, flip_x or false, flip_y or false)
|
||||
C.pxl8_2d_sprite(core.gfx, id or 0, x or 0, y or 0, w or 16, h or 16, flip_x or false, flip_y or false)
|
||||
end
|
||||
|
||||
function graphics.load_palette(filepath)
|
||||
|
|
@ -98,4 +98,12 @@ function graphics.clear_palette_cycles()
|
|||
C.pxl8_gfx_clear_palette_cycles(core.gfx)
|
||||
end
|
||||
|
||||
function graphics.push_target()
|
||||
return C.pxl8_gfx_push_target(core.gfx)
|
||||
end
|
||||
|
||||
function graphics.pop_target()
|
||||
C.pxl8_gfx_pop_target(core.gfx)
|
||||
end
|
||||
|
||||
return graphics
|
||||
161
client/src/lua/pxl8/gfx3d.lua
Normal file
161
client/src/lua/pxl8/gfx3d.lua
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
local ffi = require("ffi")
|
||||
local C = ffi.C
|
||||
local core = require("pxl8.core")
|
||||
|
||||
local gfx3d = {}
|
||||
|
||||
local Camera3D = {}
|
||||
Camera3D.__index = Camera3D
|
||||
|
||||
function Camera3D.new()
|
||||
local cam = C.pxl8_3d_camera_create()
|
||||
if cam == nil then
|
||||
return nil
|
||||
end
|
||||
return setmetatable({ _ptr = cam }, Camera3D)
|
||||
end
|
||||
|
||||
function Camera3D:destroy()
|
||||
if self._ptr then
|
||||
C.pxl8_3d_camera_destroy(self._ptr)
|
||||
self._ptr = nil
|
||||
end
|
||||
end
|
||||
|
||||
function Camera3D:get_forward()
|
||||
local v = C.pxl8_3d_camera_get_forward(self._ptr)
|
||||
return {v.x, v.y, v.z}
|
||||
end
|
||||
|
||||
function Camera3D:get_position()
|
||||
local v = C.pxl8_3d_camera_get_position(self._ptr)
|
||||
return {v.x, v.y, v.z}
|
||||
end
|
||||
|
||||
function Camera3D:get_right()
|
||||
local v = C.pxl8_3d_camera_get_right(self._ptr)
|
||||
return {v.x, v.y, v.z}
|
||||
end
|
||||
|
||||
function Camera3D:get_up()
|
||||
local v = C.pxl8_3d_camera_get_up(self._ptr)
|
||||
return {v.x, v.y, v.z}
|
||||
end
|
||||
|
||||
function Camera3D:lookat(eye, target, up)
|
||||
up = up or {0, 1, 0}
|
||||
local eye_vec = ffi.new("pxl8_vec3", {x = eye[1], y = eye[2], z = eye[3]})
|
||||
local target_vec = ffi.new("pxl8_vec3", {x = target[1], y = target[2], z = target[3]})
|
||||
local up_vec = ffi.new("pxl8_vec3", {x = up[1], y = up[2], z = up[3]})
|
||||
C.pxl8_3d_camera_lookat(self._ptr, eye_vec, target_vec, up_vec)
|
||||
end
|
||||
|
||||
function Camera3D:set_perspective(fov, aspect, near, far)
|
||||
C.pxl8_3d_camera_set_perspective(self._ptr, fov, aspect, near, far)
|
||||
end
|
||||
|
||||
function Camera3D:set_position(x, y, z)
|
||||
local pos = ffi.new("pxl8_vec3", {x = x, y = y, z = z})
|
||||
C.pxl8_3d_camera_set_position(self._ptr, pos)
|
||||
end
|
||||
|
||||
function Camera3D:set_rotation(pitch, yaw, roll)
|
||||
C.pxl8_3d_camera_set_rotation(self._ptr, pitch, yaw or 0, roll or 0)
|
||||
end
|
||||
|
||||
function Camera3D:update(dt)
|
||||
C.pxl8_3d_camera_update(self._ptr, dt)
|
||||
end
|
||||
|
||||
gfx3d.Camera3D = Camera3D
|
||||
|
||||
local Mesh = {}
|
||||
Mesh.__index = Mesh
|
||||
|
||||
function Mesh.new(vertices, indices)
|
||||
local vertex_count = #vertices
|
||||
local index_count = #indices
|
||||
local mesh = C.pxl8_mesh_create(vertex_count, index_count)
|
||||
if mesh == nil then
|
||||
return nil
|
||||
end
|
||||
local self = setmetatable({ _ptr = mesh }, Mesh)
|
||||
for _, v in ipairs(vertices) do
|
||||
local vert = ffi.new("pxl8_vertex", {
|
||||
position = {x = v.x or 0, y = v.y or 0, z = v.z or 0},
|
||||
normal = {x = v.nx or 0, y = v.ny or 0, z = v.nz or 0},
|
||||
u = v.u or 0,
|
||||
v = v.v or 0,
|
||||
color = v.color or 0,
|
||||
light = v.light or 255,
|
||||
})
|
||||
C.pxl8_mesh_push_vertex(mesh, vert)
|
||||
end
|
||||
for i = 1, #indices, 3 do
|
||||
C.pxl8_mesh_push_triangle(mesh, indices[i], indices[i+1], indices[i+2])
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function Mesh:destroy()
|
||||
if self._ptr then
|
||||
C.pxl8_mesh_destroy(self._ptr)
|
||||
self._ptr = nil
|
||||
end
|
||||
end
|
||||
|
||||
function Mesh:clear()
|
||||
if self._ptr then
|
||||
C.pxl8_mesh_clear(self._ptr)
|
||||
end
|
||||
end
|
||||
|
||||
gfx3d.Mesh = Mesh
|
||||
|
||||
function gfx3d.draw_mesh(mesh, opts)
|
||||
opts = opts or {}
|
||||
local model = C.pxl8_mat4_identity()
|
||||
local material = ffi.new("pxl8_material", {
|
||||
texture_id = opts.texture or 0,
|
||||
alpha = opts.alpha or 255,
|
||||
blend_mode = opts.blend_mode or 0,
|
||||
dither = opts.dither ~= false,
|
||||
double_sided = opts.double_sided or false,
|
||||
dynamic_lighting = opts.lighting or false,
|
||||
per_pixel = opts.per_pixel or false,
|
||||
vertex_color_passthrough = opts.passthrough or false,
|
||||
emissive_intensity = opts.emissive or 0.0,
|
||||
})
|
||||
C.pxl8_3d_draw_mesh(core.gfx, mesh._ptr, model, material)
|
||||
end
|
||||
|
||||
function gfx3d.begin_frame(camera, uniforms)
|
||||
uniforms = uniforms or {}
|
||||
local u = ffi.new("pxl8_3d_uniforms", {
|
||||
ambient = uniforms.ambient or 0,
|
||||
fog_color = uniforms.fog_color or 0,
|
||||
fog_density = uniforms.fog_density or 0.0,
|
||||
time = uniforms.time or 0.0,
|
||||
})
|
||||
C.pxl8_3d_begin_frame(core.gfx, camera._ptr, u)
|
||||
end
|
||||
|
||||
function gfx3d.clear(color)
|
||||
C.pxl8_3d_clear(core.gfx, color or 0)
|
||||
end
|
||||
|
||||
function gfx3d.clear_depth()
|
||||
C.pxl8_3d_clear_depth(core.gfx)
|
||||
end
|
||||
|
||||
function gfx3d.draw_line(p0, p1, color)
|
||||
local vec0 = ffi.new("pxl8_vec3", {x = p0[1], y = p0[2], z = p0[3]})
|
||||
local vec1 = ffi.new("pxl8_vec3", {x = p1[1], y = p1[2], z = p1[3]})
|
||||
C.pxl8_3d_draw_line(core.gfx, vec0, vec1, color)
|
||||
end
|
||||
|
||||
function gfx3d.end_frame()
|
||||
C.pxl8_3d_end_frame(core.gfx)
|
||||
end
|
||||
|
||||
return gfx3d
|
||||
70
client/src/lua/pxl8/gui.lua
Normal file
70
client/src/lua/pxl8/gui.lua
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
local ffi = require("ffi")
|
||||
local C = ffi.C
|
||||
local core = require("pxl8.core")
|
||||
|
||||
local gui = {}
|
||||
|
||||
local Gui = {}
|
||||
Gui.__index = Gui
|
||||
|
||||
function Gui.new()
|
||||
local s = C.pxl8_gui_state_create()
|
||||
if s == nil then
|
||||
return nil
|
||||
end
|
||||
return setmetatable({ _ptr = s }, Gui)
|
||||
end
|
||||
|
||||
function Gui:begin_frame()
|
||||
C.pxl8_gui_begin_frame(self._ptr)
|
||||
end
|
||||
|
||||
function Gui:button(id, x, y, w, h, label)
|
||||
return C.pxl8_gui_button(self._ptr, core.gfx, id, x, y, w, h, label)
|
||||
end
|
||||
|
||||
function Gui:cursor_down()
|
||||
C.pxl8_gui_cursor_down(self._ptr)
|
||||
end
|
||||
|
||||
function Gui:cursor_move(x, y)
|
||||
C.pxl8_gui_cursor_move(self._ptr, x, y)
|
||||
end
|
||||
|
||||
function Gui:cursor_up()
|
||||
C.pxl8_gui_cursor_up(self._ptr)
|
||||
end
|
||||
|
||||
function Gui:destroy()
|
||||
if self._ptr then
|
||||
C.pxl8_gui_state_destroy(self._ptr)
|
||||
self._ptr = nil
|
||||
end
|
||||
end
|
||||
|
||||
function Gui:end_frame()
|
||||
C.pxl8_gui_end_frame(self._ptr)
|
||||
end
|
||||
|
||||
function Gui:get_cursor_pos()
|
||||
local x = ffi.new("i32[1]")
|
||||
local y = ffi.new("i32[1]")
|
||||
C.pxl8_gui_get_cursor_pos(self._ptr, x, y)
|
||||
return x[0], y[0]
|
||||
end
|
||||
|
||||
function Gui:is_hovering()
|
||||
return C.pxl8_gui_is_hovering(self._ptr)
|
||||
end
|
||||
|
||||
gui.Gui = Gui
|
||||
|
||||
function gui.label(x, y, text, color)
|
||||
C.pxl8_gui_label(core.gfx, x, y, text, color)
|
||||
end
|
||||
|
||||
function gui.window(x, y, w, h, title)
|
||||
C.pxl8_gui_window(core.gfx, x, y, w, h, title)
|
||||
end
|
||||
|
||||
return gui
|
||||
|
|
@ -7,8 +7,8 @@ function math3d.mat4_identity()
|
|||
return C.pxl8_mat4_identity()
|
||||
end
|
||||
|
||||
function math3d.mat4_multiply(a, b)
|
||||
return C.pxl8_mat4_multiply(a, b)
|
||||
function math3d.mat4_mul(a, b)
|
||||
return C.pxl8_mat4_mul(a, b)
|
||||
end
|
||||
|
||||
function math3d.mat4_translate(x, y, z)
|
||||
103
client/src/lua/pxl8/particles.lua
Normal file
103
client/src/lua/pxl8/particles.lua
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
local ffi = require("ffi")
|
||||
local C = ffi.C
|
||||
local core = require("pxl8.core")
|
||||
|
||||
local particles = {}
|
||||
|
||||
local Particles = {}
|
||||
Particles.__index = Particles
|
||||
|
||||
function Particles.new(max_count)
|
||||
local ps = C.pxl8_particles_create(max_count or 1000, core.rng)
|
||||
if ps == nil then
|
||||
return nil
|
||||
end
|
||||
local pal = C.pxl8_gfx_get_palette(core.gfx)
|
||||
if pal ~= nil then
|
||||
C.pxl8_particles_set_palette(ps, pal)
|
||||
end
|
||||
return setmetatable({ _ptr = ps }, Particles)
|
||||
end
|
||||
|
||||
function Particles:clear()
|
||||
C.pxl8_particles_clear(self._ptr)
|
||||
end
|
||||
|
||||
function Particles:destroy()
|
||||
if self._ptr then
|
||||
C.pxl8_particles_destroy(self._ptr)
|
||||
self._ptr = nil
|
||||
end
|
||||
end
|
||||
|
||||
function Particles:emit(count)
|
||||
C.pxl8_particles_emit(self._ptr, count or 1)
|
||||
end
|
||||
|
||||
function Particles:render()
|
||||
C.pxl8_particles_render(self._ptr, core.gfx)
|
||||
end
|
||||
|
||||
function Particles:update(dt)
|
||||
C.pxl8_particles_update(self._ptr, dt)
|
||||
end
|
||||
|
||||
function Particles:count()
|
||||
return C.pxl8_particles_count(self._ptr)
|
||||
end
|
||||
|
||||
function Particles:get(index)
|
||||
return C.pxl8_particles_get(self._ptr, index)
|
||||
end
|
||||
|
||||
function Particles:set_position(x, y)
|
||||
C.pxl8_particles_set_position(self._ptr, x, y)
|
||||
end
|
||||
|
||||
function Particles:set_gravity(gx, gy)
|
||||
C.pxl8_particles_set_gravity(self._ptr, gx, gy)
|
||||
end
|
||||
|
||||
function Particles:set_spread(sx, sy)
|
||||
C.pxl8_particles_set_spread(self._ptr, sx, sy)
|
||||
end
|
||||
|
||||
function Particles:set_drag(drag)
|
||||
C.pxl8_particles_set_drag(self._ptr, drag)
|
||||
end
|
||||
|
||||
function Particles:set_turbulence(turbulence)
|
||||
C.pxl8_particles_set_turbulence(self._ptr, turbulence)
|
||||
end
|
||||
|
||||
function Particles:set_spawn_rate(rate)
|
||||
C.pxl8_particles_set_spawn_rate(self._ptr, rate)
|
||||
end
|
||||
|
||||
function Particles:set_colors(ramp_min, ramp_max)
|
||||
ramp_max = ramp_max or ramp_min
|
||||
if ramp_min > ramp_max then
|
||||
ramp_min, ramp_max = ramp_max, ramp_min
|
||||
end
|
||||
C.pxl8_particles_set_colors(self._ptr, ramp_min, ramp_max)
|
||||
end
|
||||
|
||||
function Particles:set_palette(palette)
|
||||
C.pxl8_particles_set_palette(self._ptr, palette)
|
||||
end
|
||||
|
||||
function Particles:set_life(life_min, life_max)
|
||||
C.pxl8_particles_set_life(self._ptr, life_min, life_max or life_min)
|
||||
end
|
||||
|
||||
function Particles:set_size(size_min, size_max)
|
||||
C.pxl8_particles_set_size(self._ptr, size_min, size_max or size_min)
|
||||
end
|
||||
|
||||
function Particles:set_velocity(vx_min, vx_max, vy_min, vy_max)
|
||||
C.pxl8_particles_set_velocity(self._ptr, vx_min, vx_max, vy_min, vy_max)
|
||||
end
|
||||
|
||||
particles.Particles = Particles
|
||||
|
||||
return particles
|
||||
241
client/src/lua/pxl8/sfx.lua
Normal file
241
client/src/lua/pxl8/sfx.lua
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
local ffi = require("ffi")
|
||||
local C = ffi.C
|
||||
local core = require("pxl8.core")
|
||||
|
||||
local sfx = {}
|
||||
|
||||
sfx.FILTER_BANDPASS = C.PXL8_SFX_FILTER_BANDPASS
|
||||
sfx.FILTER_HIGHPASS = C.PXL8_SFX_FILTER_HIGHPASS
|
||||
sfx.FILTER_LOWPASS = C.PXL8_SFX_FILTER_LOWPASS
|
||||
sfx.FILTER_NONE = C.PXL8_SFX_FILTER_NONE
|
||||
|
||||
sfx.LFO_AMPLITUDE = C.PXL8_SFX_LFO_AMPLITUDE
|
||||
sfx.LFO_FILTER = C.PXL8_SFX_LFO_FILTER
|
||||
sfx.LFO_PITCH = C.PXL8_SFX_LFO_PITCH
|
||||
|
||||
sfx.NODE_COMPRESSOR = C.PXL8_SFX_NODE_COMPRESSOR
|
||||
sfx.NODE_DELAY = C.PXL8_SFX_NODE_DELAY
|
||||
sfx.NODE_REVERB = C.PXL8_SFX_NODE_REVERB
|
||||
|
||||
sfx.WAVE_NOISE = C.PXL8_SFX_WAVE_NOISE
|
||||
sfx.WAVE_PULSE = C.PXL8_SFX_WAVE_PULSE
|
||||
sfx.WAVE_SAW = C.PXL8_SFX_WAVE_SAW
|
||||
sfx.WAVE_SINE = C.PXL8_SFX_WAVE_SINE
|
||||
sfx.WAVE_SQUARE = C.PXL8_SFX_WAVE_SQUARE
|
||||
sfx.WAVE_TRIANGLE = C.PXL8_SFX_WAVE_TRIANGLE
|
||||
|
||||
local SfxNode = {}
|
||||
SfxNode.__index = SfxNode
|
||||
|
||||
function SfxNode:destroy()
|
||||
if self._ptr then
|
||||
C.pxl8_sfx_node_destroy(self._ptr)
|
||||
self._ptr = nil
|
||||
end
|
||||
end
|
||||
|
||||
sfx.SfxNode = SfxNode
|
||||
|
||||
local SfxContext = {}
|
||||
SfxContext.__index = SfxContext
|
||||
|
||||
function SfxContext.new()
|
||||
local ctx = C.pxl8_sfx_context_create()
|
||||
if ctx == nil then
|
||||
return nil
|
||||
end
|
||||
return setmetatable({ _ptr = ctx }, SfxContext)
|
||||
end
|
||||
|
||||
function SfxContext:append_node(node)
|
||||
C.pxl8_sfx_context_append_node(self._ptr, node._ptr)
|
||||
end
|
||||
|
||||
function SfxContext:attach()
|
||||
C.pxl8_sfx_mixer_attach(core.sfx, self._ptr)
|
||||
end
|
||||
|
||||
function SfxContext:destroy()
|
||||
if self._ptr then
|
||||
C.pxl8_sfx_context_destroy(self._ptr)
|
||||
self._ptr = nil
|
||||
end
|
||||
end
|
||||
|
||||
function SfxContext:detach()
|
||||
C.pxl8_sfx_mixer_detach(core.sfx, self._ptr)
|
||||
end
|
||||
|
||||
function SfxContext:get_head()
|
||||
local ptr = C.pxl8_sfx_context_get_head(self._ptr)
|
||||
if ptr == nil then return nil end
|
||||
return setmetatable({ _ptr = ptr }, SfxNode)
|
||||
end
|
||||
|
||||
function SfxContext:get_volume()
|
||||
return C.pxl8_sfx_context_get_volume(self._ptr)
|
||||
end
|
||||
|
||||
function SfxContext:insert_node(after, node)
|
||||
C.pxl8_sfx_context_insert_node(self._ptr, after._ptr, node._ptr)
|
||||
end
|
||||
|
||||
function SfxContext:play_note(note, params, volume, duration)
|
||||
return C.pxl8_sfx_play_note(self._ptr, note, params, volume or 0.8, duration or 0)
|
||||
end
|
||||
|
||||
function SfxContext:release_voice(voice_id)
|
||||
C.pxl8_sfx_release_voice(self._ptr, voice_id)
|
||||
end
|
||||
|
||||
function SfxContext:remove_node(node)
|
||||
C.pxl8_sfx_context_remove_node(self._ptr, node._ptr)
|
||||
end
|
||||
|
||||
function SfxContext:set_volume(volume)
|
||||
C.pxl8_sfx_context_set_volume(self._ptr, volume)
|
||||
end
|
||||
|
||||
function SfxContext:stop_all()
|
||||
C.pxl8_sfx_stop_all(self._ptr)
|
||||
end
|
||||
|
||||
function SfxContext:stop_voice(voice_id)
|
||||
C.pxl8_sfx_stop_voice(self._ptr, voice_id)
|
||||
end
|
||||
|
||||
sfx.SfxContext = SfxContext
|
||||
|
||||
local Compressor = setmetatable({}, { __index = SfxNode })
|
||||
Compressor.__index = Compressor
|
||||
|
||||
function Compressor.new(opts)
|
||||
opts = opts or {}
|
||||
local cfg = ffi.new("pxl8_sfx_compressor_config")
|
||||
cfg.attack = opts.attack or 10
|
||||
cfg.ratio = opts.ratio or 4
|
||||
cfg.release = opts.release or 100
|
||||
cfg.threshold = opts.threshold or -12
|
||||
local ptr = C.pxl8_sfx_compressor_create(cfg)
|
||||
if ptr == nil then return nil end
|
||||
return setmetatable({ _ptr = ptr }, Compressor)
|
||||
end
|
||||
|
||||
function Compressor:set_attack(attack)
|
||||
C.pxl8_sfx_compressor_set_attack(self._ptr, attack)
|
||||
end
|
||||
|
||||
function Compressor:set_ratio(ratio)
|
||||
C.pxl8_sfx_compressor_set_ratio(self._ptr, ratio)
|
||||
end
|
||||
|
||||
function Compressor:set_release(release)
|
||||
C.pxl8_sfx_compressor_set_release(self._ptr, release)
|
||||
end
|
||||
|
||||
function Compressor:set_threshold(threshold)
|
||||
C.pxl8_sfx_compressor_set_threshold(self._ptr, threshold)
|
||||
end
|
||||
|
||||
sfx.Compressor = Compressor
|
||||
|
||||
local Delay = setmetatable({}, { __index = SfxNode })
|
||||
Delay.__index = Delay
|
||||
|
||||
function Delay.new(opts)
|
||||
opts = opts or {}
|
||||
local cfg = ffi.new("pxl8_sfx_delay_config")
|
||||
cfg.feedback = opts.feedback or 0.4
|
||||
cfg.mix = opts.mix or 0.25
|
||||
cfg.time_l = opts.time_l or 350
|
||||
cfg.time_r = opts.time_r or 500
|
||||
local ptr = C.pxl8_sfx_delay_create(cfg)
|
||||
if ptr == nil then return nil end
|
||||
return setmetatable({ _ptr = ptr }, Delay)
|
||||
end
|
||||
|
||||
function Delay:set_feedback(feedback)
|
||||
C.pxl8_sfx_delay_set_feedback(self._ptr, feedback)
|
||||
end
|
||||
|
||||
function Delay:set_mix(mix)
|
||||
C.pxl8_sfx_delay_set_mix(self._ptr, mix)
|
||||
end
|
||||
|
||||
function Delay:set_time(time_l, time_r)
|
||||
C.pxl8_sfx_delay_set_time(self._ptr, time_l, time_r)
|
||||
end
|
||||
|
||||
sfx.Delay = Delay
|
||||
|
||||
local Reverb = setmetatable({}, { __index = SfxNode })
|
||||
Reverb.__index = Reverb
|
||||
|
||||
function Reverb.new(opts)
|
||||
opts = opts or {}
|
||||
local cfg = ffi.new("pxl8_sfx_reverb_config")
|
||||
cfg.damping = opts.damping or 0.5
|
||||
cfg.mix = opts.mix or 0.3
|
||||
cfg.room = opts.room or 0.5
|
||||
local ptr = C.pxl8_sfx_reverb_create(cfg)
|
||||
if ptr == nil then return nil end
|
||||
return setmetatable({ _ptr = ptr }, Reverb)
|
||||
end
|
||||
|
||||
function Reverb:set_damping(damping)
|
||||
C.pxl8_sfx_reverb_set_damping(self._ptr, damping)
|
||||
end
|
||||
|
||||
function Reverb:set_mix(mix)
|
||||
C.pxl8_sfx_reverb_set_mix(self._ptr, mix)
|
||||
end
|
||||
|
||||
function Reverb:set_room(room)
|
||||
C.pxl8_sfx_reverb_set_room(self._ptr, room)
|
||||
end
|
||||
|
||||
sfx.Reverb = Reverb
|
||||
|
||||
function sfx.get_master_volume()
|
||||
return C.pxl8_sfx_mixer_get_master_volume(core.sfx)
|
||||
end
|
||||
|
||||
function sfx.note_to_freq(note)
|
||||
return C.pxl8_sfx_note_to_freq(note)
|
||||
end
|
||||
|
||||
function sfx.set_master_volume(volume)
|
||||
C.pxl8_sfx_mixer_set_master_volume(core.sfx, volume)
|
||||
end
|
||||
|
||||
function sfx.voice_params(opts)
|
||||
opts = opts or {}
|
||||
local params = ffi.new("pxl8_sfx_voice_params")
|
||||
|
||||
params.amp_env.attack = opts.attack or 0.01
|
||||
params.amp_env.decay = opts.decay or 0.1
|
||||
params.amp_env.sustain = opts.sustain or 0.5
|
||||
params.amp_env.release = opts.release or 0.2
|
||||
|
||||
params.filter_env.attack = opts.filter_attack or 0.01
|
||||
params.filter_env.decay = opts.filter_decay or 0.1
|
||||
params.filter_env.sustain = opts.filter_sustain or 0.3
|
||||
params.filter_env.release = opts.filter_release or 0.1
|
||||
|
||||
params.filter_type = opts.filter_type or C.PXL8_SFX_FILTER_NONE
|
||||
params.lfo_target = opts.lfo_target or C.PXL8_SFX_LFO_PITCH
|
||||
params.lfo_waveform = opts.lfo_waveform or C.PXL8_SFX_WAVE_SINE
|
||||
params.waveform = opts.waveform or C.PXL8_SFX_WAVE_SINE
|
||||
|
||||
params.filter_cutoff = opts.filter_cutoff or 4000
|
||||
params.filter_env_depth = opts.filter_env_depth or 0
|
||||
params.filter_resonance = opts.filter_resonance or 0
|
||||
params.fx_send = opts.fx_send or 0
|
||||
params.lfo_depth = opts.lfo_depth or 0
|
||||
params.lfo_rate = opts.lfo_rate or 0
|
||||
params.pulse_width = opts.pulse_width or 0.5
|
||||
|
||||
return params
|
||||
end
|
||||
|
||||
return sfx
|
||||
106
client/src/lua/pxl8/tilemap.lua
Normal file
106
client/src/lua/pxl8/tilemap.lua
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
local ffi = require("ffi")
|
||||
local C = ffi.C
|
||||
local core = require("pxl8.core")
|
||||
|
||||
local tilemap = {}
|
||||
|
||||
tilemap.TILE_FLIP_X = 1
|
||||
tilemap.TILE_FLIP_Y = 2
|
||||
tilemap.TILE_SOLID = 4
|
||||
tilemap.TILE_TRIGGER = 8
|
||||
|
||||
local Tilesheet = {}
|
||||
Tilesheet.__index = Tilesheet
|
||||
|
||||
function Tilesheet.new(tile_size)
|
||||
local ts = C.pxl8_tilesheet_create(tile_size or 16)
|
||||
if ts == nil then
|
||||
return nil
|
||||
end
|
||||
return setmetatable({ _ptr = ts }, Tilesheet)
|
||||
end
|
||||
|
||||
function Tilesheet:destroy()
|
||||
if self._ptr then
|
||||
C.pxl8_tilesheet_destroy(self._ptr)
|
||||
self._ptr = nil
|
||||
end
|
||||
end
|
||||
|
||||
function Tilesheet:load(filepath)
|
||||
return C.pxl8_tilesheet_load(self._ptr, filepath, core.gfx)
|
||||
end
|
||||
|
||||
tilemap.Tilesheet = Tilesheet
|
||||
|
||||
local Tilemap = {}
|
||||
Tilemap.__index = Tilemap
|
||||
|
||||
local tile_data = setmetatable({}, {__mode = "k"})
|
||||
|
||||
function Tilemap.new(width, height, tile_size)
|
||||
local tm = C.pxl8_tilemap_create(width, height, tile_size or 16)
|
||||
if tm == nil then
|
||||
return nil
|
||||
end
|
||||
return setmetatable({ _ptr = tm }, Tilemap)
|
||||
end
|
||||
|
||||
function Tilemap:check_collision(x, y, w, h)
|
||||
return C.pxl8_tilemap_check_collision(self._ptr, x, y, w, h)
|
||||
end
|
||||
|
||||
function Tilemap:destroy()
|
||||
if self._ptr then
|
||||
C.pxl8_tilemap_destroy(self._ptr)
|
||||
self._ptr = nil
|
||||
end
|
||||
end
|
||||
|
||||
function Tilemap:get_tile_data(tile_id)
|
||||
if tile_id == 0 then return nil end
|
||||
if not tile_data[self] then return nil end
|
||||
return tile_data[self][tile_id]
|
||||
end
|
||||
|
||||
function Tilemap:get_tile_id(layer, x, y)
|
||||
return C.pxl8_tilemap_get_tile_id(self._ptr, layer or 0, x, y)
|
||||
end
|
||||
|
||||
function Tilemap:is_solid(x, y)
|
||||
return C.pxl8_tilemap_is_solid(self._ptr, x, y)
|
||||
end
|
||||
|
||||
function Tilemap:load_ase(filepath, layer)
|
||||
return C.pxl8_tilemap_load_ase(self._ptr, filepath, layer or 0)
|
||||
end
|
||||
|
||||
function Tilemap:render()
|
||||
C.pxl8_tilemap_render(self._ptr, core.gfx)
|
||||
end
|
||||
|
||||
function Tilemap:render_layer(layer)
|
||||
C.pxl8_tilemap_render_layer(self._ptr, core.gfx, layer)
|
||||
end
|
||||
|
||||
function Tilemap:set_camera(x, y)
|
||||
C.pxl8_tilemap_set_camera(self._ptr, x, y)
|
||||
end
|
||||
|
||||
function Tilemap:set_tile(layer, x, y, tile_id, flags)
|
||||
C.pxl8_tilemap_set_tile(self._ptr, layer or 0, x, y, tile_id or 0, flags or 0)
|
||||
end
|
||||
|
||||
function Tilemap:set_tile_data(tile_id, data)
|
||||
if tile_id == 0 then return end
|
||||
if not tile_data[self] then tile_data[self] = {} end
|
||||
tile_data[self][tile_id] = data
|
||||
end
|
||||
|
||||
function Tilemap:set_tilesheet(tilesheet)
|
||||
return C.pxl8_tilemap_set_tilesheet(self._ptr, tilesheet._ptr)
|
||||
end
|
||||
|
||||
tilemap.Tilemap = Tilemap
|
||||
|
||||
return tilemap
|
||||
82
client/src/lua/pxl8/transition.lua
Normal file
82
client/src/lua/pxl8/transition.lua
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
local ffi = require("ffi")
|
||||
local C = ffi.C
|
||||
local core = require("pxl8.core")
|
||||
|
||||
local transition = {}
|
||||
|
||||
local TYPES = {
|
||||
fade = 0,
|
||||
wipe_left = 1,
|
||||
wipe_right = 2,
|
||||
wipe_up = 3,
|
||||
wipe_down = 4,
|
||||
circle_open = 5,
|
||||
circle_close = 6,
|
||||
dissolve = 7,
|
||||
pixelate = 8
|
||||
}
|
||||
|
||||
transition.TYPES = TYPES
|
||||
|
||||
local Transition = {}
|
||||
Transition.__index = Transition
|
||||
|
||||
function Transition.new(type_name, duration)
|
||||
local type_id = TYPES[type_name] or 0
|
||||
local t = C.pxl8_transition_create(type_id, duration or 1.0)
|
||||
if t == nil then
|
||||
return nil
|
||||
end
|
||||
return setmetatable({ _ptr = t }, Transition)
|
||||
end
|
||||
|
||||
function Transition:destroy()
|
||||
if self._ptr then
|
||||
C.pxl8_transition_destroy(self._ptr)
|
||||
self._ptr = nil
|
||||
end
|
||||
end
|
||||
|
||||
function Transition:get_progress()
|
||||
return C.pxl8_transition_get_progress(self._ptr)
|
||||
end
|
||||
|
||||
function Transition:is_active()
|
||||
return C.pxl8_transition_is_active(self._ptr)
|
||||
end
|
||||
|
||||
function Transition:is_complete()
|
||||
return C.pxl8_transition_is_complete(self._ptr)
|
||||
end
|
||||
|
||||
function Transition:render()
|
||||
C.pxl8_transition_render(self._ptr, core.gfx)
|
||||
end
|
||||
|
||||
function Transition:reset()
|
||||
C.pxl8_transition_reset(self._ptr)
|
||||
end
|
||||
|
||||
function Transition:set_color(color)
|
||||
C.pxl8_transition_set_color(self._ptr, color)
|
||||
end
|
||||
|
||||
function Transition:set_reverse(reverse)
|
||||
C.pxl8_transition_set_reverse(self._ptr, reverse)
|
||||
end
|
||||
|
||||
function Transition:start()
|
||||
C.pxl8_transition_start(self._ptr)
|
||||
end
|
||||
|
||||
function Transition:stop()
|
||||
C.pxl8_transition_stop(self._ptr)
|
||||
end
|
||||
|
||||
function Transition:update(dt)
|
||||
C.pxl8_transition_update(self._ptr, dt)
|
||||
end
|
||||
|
||||
transition.Transition = Transition
|
||||
|
||||
return transition
|
||||
|
|
@ -7,32 +7,52 @@ local world = {}
|
|||
world.PROCGEN_ROOMS = C.PXL8_PROCGEN_ROOMS
|
||||
world.PROCGEN_TERRAIN = C.PXL8_PROCGEN_TERRAIN
|
||||
|
||||
function world.new()
|
||||
return C.pxl8_world_create()
|
||||
local World = {}
|
||||
World.__index = World
|
||||
|
||||
function World.new()
|
||||
local w = C.pxl8_world_create()
|
||||
if w == nil then
|
||||
return nil
|
||||
end
|
||||
return setmetatable({ _ptr = w }, World)
|
||||
end
|
||||
|
||||
function world.destroy(w)
|
||||
C.pxl8_world_destroy(w)
|
||||
function World:apply_textures(texture_defs)
|
||||
local count = #texture_defs
|
||||
local textures = ffi.new("pxl8_world_texture[?]", count)
|
||||
|
||||
for i, def in ipairs(texture_defs) do
|
||||
local idx = i - 1
|
||||
ffi.copy(textures[idx].name, def.name or "", math.min(#(def.name or ""), 15))
|
||||
textures[idx].texture_id = def.texture_id or 0
|
||||
|
||||
if def.rule then
|
||||
textures[idx].rule = ffi.cast("bool (*)(const pxl8_vec3*, const pxl8_bsp_face*, const pxl8_bsp*)",
|
||||
function(normal, face, bsp)
|
||||
return def.rule(normal[0], face, bsp)
|
||||
end)
|
||||
else
|
||||
textures[idx].rule = nil
|
||||
end
|
||||
end
|
||||
|
||||
return C.pxl8_world_apply_textures(self._ptr, textures, count)
|
||||
end
|
||||
|
||||
function world.load(w, filepath)
|
||||
return C.pxl8_world_load(w, filepath)
|
||||
function World:check_collision(x, y, z, radius)
|
||||
local pos = ffi.new("pxl8_vec3", {x = x, y = y, z = z})
|
||||
return C.pxl8_world_check_collision(self._ptr, pos, radius)
|
||||
end
|
||||
|
||||
function world.render(w, camera_pos)
|
||||
local vec = ffi.new("pxl8_vec3", {x = camera_pos[1], y = camera_pos[2], z = camera_pos[3]})
|
||||
C.pxl8_world_render(w, core.gfx, vec)
|
||||
function World:destroy()
|
||||
if self._ptr then
|
||||
C.pxl8_world_destroy(self._ptr)
|
||||
self._ptr = nil
|
||||
end
|
||||
end
|
||||
|
||||
function world.unload(w)
|
||||
C.pxl8_world_unload(w)
|
||||
end
|
||||
|
||||
function world.is_loaded(w)
|
||||
return C.pxl8_world_is_loaded(w)
|
||||
end
|
||||
|
||||
function world.generate(w, params)
|
||||
function World:generate(params)
|
||||
local c_params = ffi.new("pxl8_procgen_params")
|
||||
c_params.type = params.type or C.PXL8_PROCGEN_ROOMS
|
||||
c_params.width = params.width or 32
|
||||
|
|
@ -42,9 +62,35 @@ function world.generate(w, params)
|
|||
c_params.min_room_size = params.min_room_size or 5
|
||||
c_params.max_room_size = params.max_room_size or 10
|
||||
c_params.num_rooms = params.num_rooms or 8
|
||||
return C.pxl8_world_generate(w, core.gfx, c_params)
|
||||
return C.pxl8_world_generate(self._ptr, core.gfx, c_params)
|
||||
end
|
||||
|
||||
function World:is_loaded()
|
||||
return C.pxl8_world_is_loaded(self._ptr)
|
||||
end
|
||||
|
||||
function World:load(filepath)
|
||||
return C.pxl8_world_load(self._ptr, filepath)
|
||||
end
|
||||
|
||||
function World:render(camera_pos)
|
||||
local vec = ffi.new("pxl8_vec3", {x = camera_pos[1], y = camera_pos[2], z = camera_pos[3]})
|
||||
C.pxl8_world_render(self._ptr, core.gfx, vec)
|
||||
end
|
||||
|
||||
function World:resolve_collision(from_x, from_y, from_z, to_x, to_y, to_z, radius)
|
||||
local from = ffi.new("pxl8_vec3", {x = from_x, y = from_y, z = from_z})
|
||||
local to = ffi.new("pxl8_vec3", {x = to_x, y = to_y, z = to_z})
|
||||
local result = C.pxl8_world_resolve_collision(self._ptr, from, to, radius)
|
||||
return result.x, result.y, result.z
|
||||
end
|
||||
|
||||
function World:unload()
|
||||
C.pxl8_world_unload(self._ptr)
|
||||
end
|
||||
|
||||
world.World = World
|
||||
|
||||
function world.procgen_tex(params)
|
||||
local width = params.width or 64
|
||||
local height = params.height or 64
|
||||
|
|
@ -71,40 +117,4 @@ function world.procgen_tex(params)
|
|||
return tex_id
|
||||
end
|
||||
|
||||
function world.apply_textures(w, texture_defs)
|
||||
local count = #texture_defs
|
||||
local textures = ffi.new("pxl8_world_texture[?]", count)
|
||||
|
||||
for i, def in ipairs(texture_defs) do
|
||||
local idx = i - 1
|
||||
ffi.copy(textures[idx].name, def.name or "", math.min(#(def.name or ""), 15))
|
||||
textures[idx].texture_id = def.texture_id or 0
|
||||
|
||||
if def.rule then
|
||||
textures[idx].rule = ffi.cast("bool (*)(const pxl8_vec3*, const pxl8_bsp_face*, const pxl8_bsp*)",
|
||||
function(normal, face, bsp)
|
||||
return def.rule(normal[0], face, bsp)
|
||||
end)
|
||||
else
|
||||
textures[idx].rule = nil
|
||||
end
|
||||
end
|
||||
|
||||
local result = C.pxl8_world_apply_textures(w, textures, count)
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
function world.check_collision(w, x, y, z, radius)
|
||||
local pos = ffi.new("pxl8_vec3", {x = x, y = y, z = z})
|
||||
return C.pxl8_world_check_collision(w, pos, radius)
|
||||
end
|
||||
|
||||
function world.resolve_collision(w, from_x, from_y, from_z, to_x, to_y, to_z, radius)
|
||||
local from = ffi.new("pxl8_vec3", {x = from_x, y = from_y, z = from_z})
|
||||
local to = ffi.new("pxl8_vec3", {x = to_x, y = to_y, z = to_z})
|
||||
local result = C.pxl8_world_resolve_collision(w, from, to, radius)
|
||||
return result.x, result.y, result.z
|
||||
end
|
||||
|
||||
return world
|
||||
|
|
@ -77,6 +77,14 @@ f32 pxl8_vec3_length(pxl8_vec3 v) {
|
|||
return sqrtf(pxl8_vec3_dot(v, v));
|
||||
}
|
||||
|
||||
pxl8_vec3 pxl8_vec3_lerp(pxl8_vec3 a, pxl8_vec3 b, f32 t) {
|
||||
return (pxl8_vec3){
|
||||
a.x + (b.x - a.x) * t,
|
||||
a.y + (b.y - a.y) * t,
|
||||
a.z + (b.z - a.z) * t
|
||||
};
|
||||
}
|
||||
|
||||
pxl8_vec3 pxl8_vec3_normalize(pxl8_vec3 v) {
|
||||
f32 len = pxl8_vec3_length(v);
|
||||
|
||||
|
|
@ -93,7 +101,7 @@ pxl8_mat4 pxl8_mat4_identity(void) {
|
|||
return mat;
|
||||
}
|
||||
|
||||
pxl8_mat4 pxl8_mat4_multiply(pxl8_mat4 a, pxl8_mat4 b) {
|
||||
pxl8_mat4 pxl8_mat4_mul(pxl8_mat4 a, pxl8_mat4 b) {
|
||||
pxl8_mat4 mat = {0};
|
||||
|
||||
for (i32 col = 0; col < 4; col++) {
|
||||
|
|
@ -109,7 +117,7 @@ pxl8_mat4 pxl8_mat4_multiply(pxl8_mat4 a, pxl8_mat4 b) {
|
|||
return mat;
|
||||
}
|
||||
|
||||
pxl8_vec4 pxl8_mat4_multiply_vec4(pxl8_mat4 m, pxl8_vec4 v) {
|
||||
pxl8_vec4 pxl8_mat4_mul_vec4(pxl8_mat4 m, pxl8_vec4 v) {
|
||||
return (pxl8_vec4){
|
||||
.x = m.m[0] * v.x + m.m[4] * v.y + m.m[8] * v.z + m.m[12] * v.w,
|
||||
.y = m.m[1] * v.x + m.m[5] * v.y + m.m[9] * v.z + m.m[13] * v.w,
|
||||
|
|
@ -47,14 +47,15 @@ pxl8_vec3 pxl8_vec3_add(pxl8_vec3 a, pxl8_vec3 b);
|
|||
pxl8_vec3 pxl8_vec3_cross(pxl8_vec3 a, pxl8_vec3 b);
|
||||
f32 pxl8_vec3_dot(pxl8_vec3 a, pxl8_vec3 b);
|
||||
f32 pxl8_vec3_length(pxl8_vec3 v);
|
||||
pxl8_vec3 pxl8_vec3_lerp(pxl8_vec3 a, pxl8_vec3 b, f32 t);
|
||||
pxl8_vec3 pxl8_vec3_normalize(pxl8_vec3 v);
|
||||
pxl8_vec3 pxl8_vec3_scale(pxl8_vec3 v, f32 s);
|
||||
pxl8_vec3 pxl8_vec3_sub(pxl8_vec3 a, pxl8_vec3 b);
|
||||
|
||||
pxl8_mat4 pxl8_mat4_identity(void);
|
||||
pxl8_mat4 pxl8_mat4_lookat(pxl8_vec3 eye, pxl8_vec3 center, pxl8_vec3 up);
|
||||
pxl8_mat4 pxl8_mat4_multiply(pxl8_mat4 a, pxl8_mat4 b);
|
||||
pxl8_vec4 pxl8_mat4_multiply_vec4(pxl8_mat4 m, pxl8_vec4 v);
|
||||
pxl8_mat4 pxl8_mat4_mul(pxl8_mat4 a, pxl8_mat4 b);
|
||||
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_perspective(f32 fov, f32 aspect, f32 near, f32 far);
|
||||
pxl8_mat4 pxl8_mat4_rotate_x(f32 angle);
|
||||
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
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
#include "pxl8_gui.h"
|
||||
#include "pxl8_log.h"
|
||||
#include "pxl8_macros.h"
|
||||
#include "pxl8_script_ffi.h"
|
||||
|
||||
struct pxl8_script {
|
||||
lua_State* L;
|
||||
|
|
@ -154,327 +155,6 @@ static void pxl8_script_repl_promote_locals(const char* input, char* output, siz
|
|||
output[j] = '\0';
|
||||
}
|
||||
|
||||
static const char* pxl8_ffi_cdefs =
|
||||
"typedef uint8_t u8;\n"
|
||||
"typedef uint16_t u16;\n"
|
||||
"typedef uint32_t u32;\n"
|
||||
"typedef uint64_t u64;\n"
|
||||
"typedef int8_t i8;\n"
|
||||
"typedef int16_t i16;\n"
|
||||
"typedef int32_t i32;\n"
|
||||
"typedef int64_t i64;\n"
|
||||
"typedef float f32;\n"
|
||||
"typedef double f64;\n"
|
||||
"typedef struct pxl8 pxl8;\n"
|
||||
"typedef struct pxl8_gfx pxl8_gfx;\n"
|
||||
"typedef struct { int x, y, w, h; } pxl8_bounds;\n"
|
||||
"typedef struct { int x, y; } pxl8_point;\n"
|
||||
"typedef struct pxl8_rng { u32 state; } pxl8_rng;\n"
|
||||
"\n"
|
||||
"void pxl8_rng_seed(pxl8_rng* rng, u32 seed);\n"
|
||||
"u32 pxl8_rng_next(pxl8_rng* rng);\n"
|
||||
"f32 pxl8_rng_f32(pxl8_rng* rng);\n"
|
||||
"i32 pxl8_rng_range(pxl8_rng* rng, i32 min, i32 max);\n"
|
||||
"\n"
|
||||
"f32 pxl8_get_fps(const pxl8* sys);\n"
|
||||
"\n"
|
||||
"i32 pxl8_gfx_get_height(pxl8_gfx* ctx);\n"
|
||||
"i32 pxl8_gfx_get_width(pxl8_gfx* ctx);\n"
|
||||
"void pxl8_circle(pxl8_gfx* ctx, i32 x, i32 y, i32 r, u32 color);\n"
|
||||
"void pxl8_circle_fill(pxl8_gfx* ctx, i32 x, i32 y, i32 r, u32 color);\n"
|
||||
"void pxl8_clear(pxl8_gfx* ctx, u32 color);\n"
|
||||
"u32 pxl8_get_pixel(pxl8_gfx* ctx, i32 x, i32 y);\n"
|
||||
"void pxl8_line(pxl8_gfx* ctx, i32 x0, i32 y0, i32 x1, i32 y1, u32 color);\n"
|
||||
"void pxl8_pixel(pxl8_gfx* ctx, i32 x, i32 y, u32 color);\n"
|
||||
"void pxl8_rect(pxl8_gfx* ctx, i32 x, i32 y, i32 w, i32 h, u32 color);\n"
|
||||
"void pxl8_rect_fill(pxl8_gfx* ctx, i32 x, i32 y, i32 w, i32 h, u32 color);\n"
|
||||
"void pxl8_sprite(pxl8_gfx* ctx, i32 id, i32 x, i32 y, i32 w, i32 h, bool flip_x, bool flip_y);\n"
|
||||
"void pxl8_text(pxl8_gfx* ctx, const char* str, i32 x, i32 y, u32 color);\n"
|
||||
"void pxl8_gfx_color_ramp(pxl8_gfx* ctx, u8 start, u8 count, u32 from_color, u32 to_color);\n"
|
||||
"void pxl8_gfx_cycle_palette(pxl8_gfx* ctx, u8 start, u8 count, i32 step);\n"
|
||||
"void pxl8_gfx_fade_palette(pxl8_gfx* ctx, u8 start, u8 count, f32 amount, u32 target_color);\n"
|
||||
"i32 pxl8_gfx_add_palette_cycle(pxl8_gfx* ctx, u8 start_index, u8 end_index, f32 speed);\n"
|
||||
"void pxl8_gfx_remove_palette_cycle(pxl8_gfx* ctx, i32 cycle_id);\n"
|
||||
"void pxl8_gfx_set_palette_cycle_speed(pxl8_gfx* ctx, i32 cycle_id, f32 speed);\n"
|
||||
"void pxl8_gfx_clear_palette_cycles(pxl8_gfx* ctx);\n"
|
||||
"void pxl8_gfx_update(pxl8_gfx* ctx, f32 dt);\n"
|
||||
"i32 pxl8_gfx_load_palette(pxl8_gfx* ctx, const char* filepath);\n"
|
||||
"i32 pxl8_gfx_load_sprite(pxl8_gfx* ctx, const char* filepath, u32* sprite_id);\n"
|
||||
"i32 pxl8_gfx_create_texture(pxl8_gfx* ctx, const u8* pixels, u32 width, u32 height);\n"
|
||||
"typedef struct pxl8_input_state pxl8_input_state;\n"
|
||||
"bool pxl8_key_down(const pxl8_input_state* input, const char* key_name);\n"
|
||||
"bool pxl8_key_pressed(const pxl8_input_state* input, const char* key_name);\n"
|
||||
"bool pxl8_key_released(const pxl8_input_state* input, const char* key_name);\n"
|
||||
"bool pxl8_mouse_pressed(const pxl8_input_state* input, i32 button);\n"
|
||||
"bool pxl8_mouse_released(const pxl8_input_state* input, i32 button);\n"
|
||||
"int pxl8_mouse_wheel_x(const pxl8_input_state* input);\n"
|
||||
"int pxl8_mouse_wheel_y(const pxl8_input_state* input);\n"
|
||||
"int pxl8_mouse_x(const pxl8_input_state* input);\n"
|
||||
"int pxl8_mouse_y(const pxl8_input_state* input);\n"
|
||||
"int pxl8_mouse_dx(const pxl8_input_state* input);\n"
|
||||
"int pxl8_mouse_dy(const pxl8_input_state* input);\n"
|
||||
"typedef enum { PXL8_CURSOR_ARROW = 0, PXL8_CURSOR_HAND = 1 } pxl8_cursor;\n"
|
||||
"void pxl8_center_cursor(pxl8* sys);\n"
|
||||
"void pxl8_set_cursor(pxl8* sys, pxl8_cursor cursor);\n"
|
||||
"void pxl8_set_relative_mouse_mode(pxl8* sys, bool enabled);\n"
|
||||
"void pxl8_set_running(pxl8* sys, bool running);\n"
|
||||
"void pxl8_lua_log(int level, const char* file, int line, const char* msg);\n"
|
||||
"typedef struct pxl8_cart pxl8_cart;\n"
|
||||
"pxl8_cart* pxl8_get_cart(void);\n"
|
||||
"const char* pxl8_cart_get_title(const pxl8_cart* cart);\n"
|
||||
"typedef u32 pxl8_tile;\n"
|
||||
"typedef struct pxl8_tilemap pxl8_tilemap;\n"
|
||||
"typedef struct pxl8_tilesheet pxl8_tilesheet;\n"
|
||||
"pxl8_tilemap* pxl8_tilemap_create(u32 width, u32 height, u32 tile_size);\n"
|
||||
"void pxl8_tilemap_destroy(pxl8_tilemap* tilemap);\n"
|
||||
"u32 pxl8_tilemap_get_height(const pxl8_tilemap* tilemap);\n"
|
||||
"pxl8_tile pxl8_tilemap_get_tile(const pxl8_tilemap* tilemap, u32 layer, u32 x, u32 y);\n"
|
||||
"u32 pxl8_tilemap_get_tile_size(const pxl8_tilemap* tilemap);\n"
|
||||
"void* pxl8_tilemap_get_tile_user_data(const pxl8_tilemap* tilemap, u16 tile_id);\n"
|
||||
"u32 pxl8_tilemap_get_width(const pxl8_tilemap* tilemap);\n"
|
||||
"void pxl8_tilemap_set_tile_user_data(pxl8_tilemap* tilemap, u16 tile_id, void* user_data);\n"
|
||||
"bool pxl8_tilemap_check_collision(const pxl8_tilemap* tilemap, i32 x, i32 y, i32 w, i32 h);\n"
|
||||
"u16 pxl8_tilemap_get_tile_id(const pxl8_tilemap* tilemap, u32 layer, u32 x, u32 y);\n"
|
||||
"bool pxl8_tilemap_is_solid(const pxl8_tilemap* tilemap, u32 x, u32 y);\n"
|
||||
"void pxl8_tilemap_render(const pxl8_tilemap* tilemap, pxl8_gfx* gfx);\n"
|
||||
"void pxl8_tilemap_render_layer(const pxl8_tilemap* tilemap, pxl8_gfx* gfx, u32 layer);\n"
|
||||
"void pxl8_tilemap_set_camera(pxl8_tilemap* tilemap, i32 x, i32 y);\n"
|
||||
"void pxl8_tilemap_set_tile(pxl8_tilemap* tilemap, u32 layer, u32 x, u32 y, u16 tile_id, u8 flags);\n"
|
||||
"i32 pxl8_tilemap_set_tilesheet(pxl8_tilemap* tilemap, pxl8_tilesheet* tilesheet);\n"
|
||||
"i32 pxl8_tilemap_load_ase(pxl8_tilemap* tilemap, const char* filepath, u32 layer);\n"
|
||||
"pxl8_tilesheet* pxl8_tilesheet_create(u32 tile_size);\n"
|
||||
"void pxl8_tilesheet_destroy(pxl8_tilesheet* tilesheet);\n"
|
||||
"i32 pxl8_tilesheet_load(pxl8_tilesheet* tilesheet, const char* filepath, pxl8_gfx* gfx);\n"
|
||||
"\n"
|
||||
"typedef struct {\n"
|
||||
" float angle;\n"
|
||||
" float ax, ay, az;\n"
|
||||
" unsigned int color;\n"
|
||||
" unsigned int end_color;\n"
|
||||
" unsigned char flags;\n"
|
||||
" float life;\n"
|
||||
" float max_life;\n"
|
||||
" float size;\n"
|
||||
" float spin;\n"
|
||||
" unsigned int start_color;\n"
|
||||
" float vx, vy, vz;\n"
|
||||
" float x, y, z;\n"
|
||||
"} pxl8_particle;\n"
|
||||
"\n"
|
||||
"typedef struct {\n"
|
||||
" float amplitude;\n"
|
||||
" float base_y;\n"
|
||||
" unsigned int color;\n"
|
||||
" unsigned int fade_color;\n"
|
||||
" int height;\n"
|
||||
" float phase;\n"
|
||||
" float speed;\n"
|
||||
"} pxl8_raster_bar;\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_particles pxl8_particles;\n"
|
||||
"pxl8_particles* pxl8_particles_create(u32 max_count, pxl8_rng* rng);\n"
|
||||
"void pxl8_particles_destroy(pxl8_particles* particles);\n"
|
||||
"void pxl8_particles_clear(pxl8_particles* particles);\n"
|
||||
"void pxl8_particles_emit(pxl8_particles* particles, u32 count);\n"
|
||||
"void pxl8_particles_render(pxl8_particles* particles, pxl8_gfx* gfx);\n"
|
||||
"void pxl8_particles_update(pxl8_particles* particles, float dt);\n"
|
||||
"void pxl8_vfx_explosion(pxl8_particles* particles, int x, int y, unsigned int color, float force);\n"
|
||||
"void pxl8_vfx_fire(pxl8_particles* particles, int x, int y, int width, unsigned char palette_start);\n"
|
||||
"void pxl8_vfx_plasma(pxl8_gfx* ctx, f32 time, f32 scale1, f32 scale2, u8 palette_offset);\n"
|
||||
"void pxl8_vfx_rain(pxl8_particles* particles, int width, float wind);\n"
|
||||
"void pxl8_vfx_raster_bars(pxl8_gfx* ctx, pxl8_raster_bar* bars, u32 bar_count, f32 time);\n"
|
||||
"void pxl8_vfx_rotozoom(pxl8_gfx* ctx, f32 angle, f32 zoom, i32 cx, i32 cy);\n"
|
||||
"void pxl8_vfx_smoke(pxl8_particles* particles, int x, int y, unsigned char color);\n"
|
||||
"void pxl8_vfx_snow(pxl8_particles* particles, int width, float wind);\n"
|
||||
"void pxl8_vfx_sparks(pxl8_particles* particles, int x, int y, unsigned int color);\n"
|
||||
"void pxl8_vfx_starfield(pxl8_particles* particles, float speed, float spread);\n"
|
||||
"void pxl8_vfx_tunnel(pxl8_gfx* ctx, f32 time, f32 speed, f32 twist);\n"
|
||||
"void pxl8_vfx_water_ripple(pxl8_gfx* ctx, f32* height_map, i32 drop_x, i32 drop_y, f32 damping);\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_transition pxl8_transition;\n"
|
||||
"typedef struct pxl8_anim pxl8_anim;\n"
|
||||
"\n"
|
||||
"pxl8_transition* pxl8_transition_create(i32 type, f32 duration);\n"
|
||||
"void pxl8_transition_destroy(pxl8_transition* transition);\n"
|
||||
"f32 pxl8_transition_get_progress(const pxl8_transition* transition);\n"
|
||||
"bool pxl8_transition_is_active(const pxl8_transition* transition);\n"
|
||||
"bool pxl8_transition_is_complete(const pxl8_transition* transition);\n"
|
||||
"void pxl8_transition_render(const pxl8_transition* transition, pxl8_gfx* gfx);\n"
|
||||
"void pxl8_transition_reset(pxl8_transition* transition);\n"
|
||||
"void pxl8_transition_set_color(pxl8_transition* transition, u32 color);\n"
|
||||
"void pxl8_transition_set_reverse(pxl8_transition* transition, bool reverse);\n"
|
||||
"void pxl8_transition_start(pxl8_transition* transition);\n"
|
||||
"void pxl8_transition_stop(pxl8_transition* transition);\n"
|
||||
"void pxl8_transition_update(pxl8_transition* transition, f32 dt);\n"
|
||||
"\n"
|
||||
"pxl8_anim* pxl8_anim_create(const u32* frame_ids, const u16* frame_durations, u16 frame_count);\n"
|
||||
"pxl8_anim* pxl8_anim_create_from_ase(pxl8_gfx* gfx, const char* path);\n"
|
||||
"void pxl8_anim_destroy(pxl8_anim* anim);\n"
|
||||
"i32 pxl8_anim_add_state(pxl8_anim* anim, const char* name, pxl8_anim* state_anim);\n"
|
||||
"u16 pxl8_anim_get_current_frame(const pxl8_anim* anim);\n"
|
||||
"u32 pxl8_anim_get_current_frame_id(const pxl8_anim* anim);\n"
|
||||
"const char* pxl8_anim_get_state(const pxl8_anim* anim);\n"
|
||||
"bool pxl8_anim_has_state_machine(const pxl8_anim* anim);\n"
|
||||
"bool pxl8_anim_is_complete(const pxl8_anim* anim);\n"
|
||||
"bool pxl8_anim_is_playing(const pxl8_anim* anim);\n"
|
||||
"void pxl8_anim_pause(pxl8_anim* anim);\n"
|
||||
"void pxl8_anim_play(pxl8_anim* anim);\n"
|
||||
"void pxl8_anim_render_sprite(const pxl8_anim* anim, pxl8_gfx* gfx, i32 x, i32 y, i32 w, i32 h, bool flip_x, bool flip_y);\n"
|
||||
"void pxl8_anim_reset(pxl8_anim* anim);\n"
|
||||
"void pxl8_anim_set_frame(pxl8_anim* anim, u16 frame);\n"
|
||||
"void pxl8_anim_set_loop(pxl8_anim* anim, bool loop);\n"
|
||||
"void pxl8_anim_set_reverse(pxl8_anim* anim, bool reverse);\n"
|
||||
"void pxl8_anim_set_speed(pxl8_anim* anim, f32 speed);\n"
|
||||
"i32 pxl8_anim_set_state(pxl8_anim* anim, const char* name);\n"
|
||||
"void pxl8_anim_stop(pxl8_anim* anim);\n"
|
||||
"void pxl8_anim_update(pxl8_anim* anim, f32 dt);\n"
|
||||
"\n"
|
||||
"typedef struct { float x, y, z; } pxl8_vec3;\n"
|
||||
"typedef struct { float x, y, z, w; } pxl8_vec4;\n"
|
||||
"typedef struct { float m[16]; } pxl8_mat4;\n"
|
||||
"\n"
|
||||
"void pxl8_3d_clear_zbuffer(pxl8_gfx* gfx);\n"
|
||||
"void pxl8_3d_draw_line_3d(pxl8_gfx* gfx, pxl8_vec3 p0, pxl8_vec3 p1, u32 color);\n"
|
||||
"void pxl8_3d_draw_triangle_raw(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, pxl8_vec3 v2, u32 color);\n"
|
||||
"void pxl8_3d_draw_triangle_textured(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, pxl8_vec3 v2, f32 u0, f32 v0, f32 u1, f32 v1, f32 u2, f32 v2, u32 texture_id);\n"
|
||||
"void pxl8_3d_set_affine_textures(pxl8_gfx* gfx, bool affine);\n"
|
||||
"void pxl8_3d_set_backface_culling(pxl8_gfx* gfx, bool culling);\n"
|
||||
"void pxl8_3d_set_model(pxl8_gfx* gfx, pxl8_mat4 mat);\n"
|
||||
"void pxl8_3d_set_projection(pxl8_gfx* gfx, pxl8_mat4 mat);\n"
|
||||
"void pxl8_3d_set_view(pxl8_gfx* gfx, pxl8_mat4 mat);\n"
|
||||
"void pxl8_3d_set_wireframe(pxl8_gfx* gfx, bool wireframe);\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_multiply(pxl8_mat4 a, pxl8_mat4 b);\n"
|
||||
"pxl8_mat4 pxl8_mat4_ortho(float left, float right, float bottom, float top, float near, float far);\n"
|
||||
"pxl8_mat4 pxl8_mat4_perspective(float fov, float aspect, float near, float far);\n"
|
||||
"pxl8_mat4 pxl8_mat4_rotate_x(float angle);\n"
|
||||
"pxl8_mat4 pxl8_mat4_rotate_y(float angle);\n"
|
||||
"pxl8_mat4 pxl8_mat4_rotate_z(float angle);\n"
|
||||
"pxl8_mat4 pxl8_mat4_scale(float x, float y, float z);\n"
|
||||
"pxl8_mat4 pxl8_mat4_translate(float x, float y, float z);\n"
|
||||
"\n"
|
||||
"typedef enum pxl8_procgen_type {\n"
|
||||
" PXL8_PROCGEN_ROOMS = 0,\n"
|
||||
" PXL8_PROCGEN_TERRAIN = 1\n"
|
||||
"} pxl8_procgen_type;\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_procgen_params {\n"
|
||||
" pxl8_procgen_type type;\n"
|
||||
" int width;\n"
|
||||
" int height;\n"
|
||||
" int depth;\n"
|
||||
" unsigned int seed;\n"
|
||||
" int min_room_size;\n"
|
||||
" int max_room_size;\n"
|
||||
" int num_rooms;\n"
|
||||
"} pxl8_procgen_params;\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_procgen_tex_params {\n"
|
||||
" char name[16];\n"
|
||||
" unsigned int seed;\n"
|
||||
" int width;\n"
|
||||
" int height;\n"
|
||||
" float scale;\n"
|
||||
" float roughness;\n"
|
||||
" unsigned char base_color;\n"
|
||||
" unsigned char variation;\n"
|
||||
"} pxl8_procgen_tex_params;\n"
|
||||
"\n"
|
||||
"void pxl8_procgen_tex(u8* buffer, const pxl8_procgen_tex_params* params);\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_bsp pxl8_bsp;\n"
|
||||
"typedef struct pxl8_bsp_face pxl8_bsp_face;\n"
|
||||
"typedef bool (*pxl8_texture_rule)(const pxl8_vec3* normal, const pxl8_bsp_face* face, const pxl8_bsp* bsp);\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_world_texture {\n"
|
||||
" char name[16];\n"
|
||||
" unsigned int texture_id;\n"
|
||||
" pxl8_texture_rule rule;\n"
|
||||
"} pxl8_world_texture;\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_world pxl8_world;\n"
|
||||
"pxl8_world* pxl8_world_create(void);\n"
|
||||
"void pxl8_world_destroy(pxl8_world* world);\n"
|
||||
"int pxl8_world_generate(pxl8_world* world, pxl8_gfx* gfx, const pxl8_procgen_params* params);\n"
|
||||
"int pxl8_world_load(pxl8_world* world, const char* path);\n"
|
||||
"void pxl8_world_unload(pxl8_world* world);\n"
|
||||
"int pxl8_world_apply_textures(pxl8_world* world, const pxl8_world_texture* textures, unsigned int count);\n"
|
||||
"bool pxl8_world_check_collision(const pxl8_world* world, pxl8_vec3 pos, float radius);\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"
|
||||
"pxl8_vec3 pxl8_world_resolve_collision(const pxl8_world* world, pxl8_vec3 from, pxl8_vec3 to, float radius);\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"
|
||||
"pxl8_gui_state* pxl8_gui_state_create(void);\n"
|
||||
"void pxl8_gui_state_destroy(pxl8_gui_state* state);\n"
|
||||
"void pxl8_gui_begin_frame(pxl8_gui_state* state);\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_down(pxl8_gui_state* state);\n"
|
||||
"void pxl8_gui_cursor_up(pxl8_gui_state* state);\n"
|
||||
"bool pxl8_gui_button(pxl8_gui_state* state, pxl8_gfx* gfx, u32 id, i32 x, i32 y, i32 w, i32 h, const char* label);\n"
|
||||
"void pxl8_gui_window(pxl8_gfx* gfx, i32 x, i32 y, i32 w, i32 h, const char* title);\n"
|
||||
"void pxl8_gui_label(pxl8_gfx* gfx, i32 x, i32 y, const char* text, u8 color);\n"
|
||||
"bool pxl8_gui_is_hovering(const pxl8_gui_state* state);\n"
|
||||
"void pxl8_gui_get_cursor_pos(const pxl8_gui_state* state, i32* x, i32* y);\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_save pxl8_save;\n"
|
||||
"pxl8_save* pxl8_save_create(const char* game_name, u32 magic, u32 version);\n"
|
||||
"void pxl8_save_destroy(pxl8_save* save);\n"
|
||||
"i32 pxl8_save_write(pxl8_save* save, u8 slot, const u8* data, u32 size);\n"
|
||||
"i32 pxl8_save_read(pxl8_save* save, u8 slot, u8** data_out, u32* size_out);\n"
|
||||
"void pxl8_save_free(u8* data);\n"
|
||||
"bool pxl8_save_exists(pxl8_save* save, u8 slot);\n"
|
||||
"i32 pxl8_save_delete(pxl8_save* save, u8 slot);\n"
|
||||
"const char* pxl8_save_get_directory(pxl8_save* save);\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_sfx_context pxl8_sfx_context;\n"
|
||||
"typedef struct pxl8_sfx_mixer pxl8_sfx_mixer;\n"
|
||||
"typedef struct pxl8_sfx_node pxl8_sfx_node;\n"
|
||||
"typedef enum pxl8_sfx_filter_type { PXL8_SFX_FILTER_BANDPASS = 0, PXL8_SFX_FILTER_HIGHPASS, PXL8_SFX_FILTER_LOWPASS, PXL8_SFX_FILTER_NONE } pxl8_sfx_filter_type;\n"
|
||||
"typedef enum pxl8_sfx_lfo_target { PXL8_SFX_LFO_AMPLITUDE = 0, PXL8_SFX_LFO_FILTER, PXL8_SFX_LFO_PITCH } pxl8_sfx_lfo_target;\n"
|
||||
"typedef enum pxl8_sfx_node_type { PXL8_SFX_NODE_COMPRESSOR, PXL8_SFX_NODE_DELAY, PXL8_SFX_NODE_REVERB } pxl8_sfx_node_type;\n"
|
||||
"typedef enum pxl8_sfx_waveform { PXL8_SFX_WAVE_NOISE = 0, PXL8_SFX_WAVE_PULSE, PXL8_SFX_WAVE_SAW, PXL8_SFX_WAVE_SINE, PXL8_SFX_WAVE_SQUARE, PXL8_SFX_WAVE_TRIANGLE } pxl8_sfx_waveform;\n"
|
||||
"typedef struct pxl8_sfx_adsr { f32 attack; f32 decay; f32 sustain; f32 release; } pxl8_sfx_adsr;\n"
|
||||
"typedef struct pxl8_sfx_compressor_config { f32 attack; f32 ratio; f32 release; f32 threshold; } pxl8_sfx_compressor_config;\n"
|
||||
"typedef struct pxl8_sfx_delay_config { f32 feedback; f32 mix; u32 time_l; u32 time_r; } pxl8_sfx_delay_config;\n"
|
||||
"typedef struct pxl8_sfx_reverb_config { f32 damping; f32 mix; f32 room; } pxl8_sfx_reverb_config;\n"
|
||||
"typedef struct pxl8_sfx_voice_params { pxl8_sfx_adsr amp_env; pxl8_sfx_adsr filter_env; pxl8_sfx_filter_type filter_type; pxl8_sfx_lfo_target lfo_target; pxl8_sfx_waveform lfo_waveform; pxl8_sfx_waveform waveform; f32 filter_cutoff; f32 filter_env_depth; f32 filter_resonance; f32 fx_send; f32 lfo_depth; f32 lfo_rate; f32 pulse_width; } pxl8_sfx_voice_params;\n"
|
||||
"pxl8_sfx_node* pxl8_sfx_compressor_create(pxl8_sfx_compressor_config cfg);\n"
|
||||
"void pxl8_sfx_compressor_set_attack(pxl8_sfx_node* node, f32 attack);\n"
|
||||
"void pxl8_sfx_compressor_set_ratio(pxl8_sfx_node* node, f32 ratio);\n"
|
||||
"void pxl8_sfx_compressor_set_release(pxl8_sfx_node* node, f32 release);\n"
|
||||
"void pxl8_sfx_compressor_set_threshold(pxl8_sfx_node* node, f32 threshold);\n"
|
||||
"void pxl8_sfx_context_append_node(pxl8_sfx_context* ctx, pxl8_sfx_node* node);\n"
|
||||
"pxl8_sfx_context* pxl8_sfx_context_create(void);\n"
|
||||
"void pxl8_sfx_context_destroy(pxl8_sfx_context* ctx);\n"
|
||||
"pxl8_sfx_node* pxl8_sfx_context_get_head(pxl8_sfx_context* ctx);\n"
|
||||
"f32 pxl8_sfx_context_get_volume(const pxl8_sfx_context* ctx);\n"
|
||||
"void pxl8_sfx_context_insert_node(pxl8_sfx_context* ctx, pxl8_sfx_node* after, pxl8_sfx_node* node);\n"
|
||||
"void pxl8_sfx_context_remove_node(pxl8_sfx_context* ctx, pxl8_sfx_node* node);\n"
|
||||
"void pxl8_sfx_context_set_volume(pxl8_sfx_context* ctx, f32 volume);\n"
|
||||
"pxl8_sfx_node* pxl8_sfx_delay_create(pxl8_sfx_delay_config cfg);\n"
|
||||
"void pxl8_sfx_delay_set_feedback(pxl8_sfx_node* node, f32 feedback);\n"
|
||||
"void pxl8_sfx_delay_set_mix(pxl8_sfx_node* node, f32 mix);\n"
|
||||
"void pxl8_sfx_delay_set_time(pxl8_sfx_node* node, u32 time_l, u32 time_r);\n"
|
||||
"void pxl8_sfx_mixer_attach(pxl8_sfx_mixer* mixer, pxl8_sfx_context* ctx);\n"
|
||||
"pxl8_sfx_mixer* pxl8_sfx_mixer_create(void);\n"
|
||||
"void pxl8_sfx_mixer_destroy(pxl8_sfx_mixer* mixer);\n"
|
||||
"void pxl8_sfx_mixer_detach(pxl8_sfx_mixer* mixer, pxl8_sfx_context* ctx);\n"
|
||||
"f32 pxl8_sfx_mixer_get_master_volume(const pxl8_sfx_mixer* mixer);\n"
|
||||
"void pxl8_sfx_mixer_set_master_volume(pxl8_sfx_mixer* mixer, f32 volume);\n"
|
||||
"void pxl8_sfx_node_destroy(pxl8_sfx_node* node);\n"
|
||||
"f32 pxl8_sfx_note_to_freq(u8 note);\n"
|
||||
"u16 pxl8_sfx_play_note(pxl8_sfx_context* ctx, u8 note, const pxl8_sfx_voice_params* params, f32 volume, f32 duration);\n"
|
||||
"void pxl8_sfx_release_voice(pxl8_sfx_context* ctx, u16 voice_id);\n"
|
||||
"pxl8_sfx_node* pxl8_sfx_reverb_create(pxl8_sfx_reverb_config cfg);\n"
|
||||
"void pxl8_sfx_reverb_set_damping(pxl8_sfx_node* node, f32 damping);\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_stop_all(pxl8_sfx_context* ctx);\n"
|
||||
"void pxl8_sfx_stop_voice(pxl8_sfx_context* ctx, u16 voice_id);\n";
|
||||
|
||||
void pxl8_lua_log(int level, const char* file, int line, const char* msg) {
|
||||
if (file && (file[0] == '?' || file[0] == '\0')) file = NULL;
|
||||
switch (level) {
|
||||
389
client/src/script/pxl8_script_ffi.h
Normal file
389
client/src/script/pxl8_script_ffi.h
Normal file
|
|
@ -0,0 +1,389 @@
|
|||
#pragma once
|
||||
|
||||
static const char* pxl8_ffi_cdefs =
|
||||
"typedef uint8_t u8;\n"
|
||||
"typedef uint16_t u16;\n"
|
||||
"typedef uint32_t u32;\n"
|
||||
"typedef uint64_t u64;\n"
|
||||
"typedef int8_t i8;\n"
|
||||
"typedef int16_t i16;\n"
|
||||
"typedef int32_t i32;\n"
|
||||
"typedef int64_t i64;\n"
|
||||
"typedef float f32;\n"
|
||||
"typedef double f64;\n"
|
||||
"typedef struct pxl8 pxl8;\n"
|
||||
"typedef struct pxl8_gfx pxl8_gfx;\n"
|
||||
"typedef struct { int x, y, w, h; } pxl8_bounds;\n"
|
||||
"typedef struct { int x, y; } pxl8_point;\n"
|
||||
"typedef struct pxl8_rng { u32 state; } pxl8_rng;\n"
|
||||
"\n"
|
||||
"void pxl8_rng_seed(pxl8_rng* rng, u32 seed);\n"
|
||||
"u32 pxl8_rng_next(pxl8_rng* rng);\n"
|
||||
"f32 pxl8_rng_f32(pxl8_rng* rng);\n"
|
||||
"i32 pxl8_rng_range(pxl8_rng* rng, i32 min, i32 max);\n"
|
||||
"\n"
|
||||
"f32 pxl8_get_fps(const pxl8* sys);\n"
|
||||
"\n"
|
||||
"u8 pxl8_gfx_find_color(pxl8_gfx* gfx, u32 color);\n"
|
||||
"i32 pxl8_gfx_get_height(pxl8_gfx* ctx);\n"
|
||||
"typedef struct pxl8_palette pxl8_palette;\n"
|
||||
"pxl8_palette* pxl8_gfx_get_palette(pxl8_gfx* gfx);\n"
|
||||
"u32 pxl8_palette_color(const pxl8_palette* pal, u8 idx);\n"
|
||||
"i32 pxl8_palette_index(const pxl8_palette* pal, u32 color);\n"
|
||||
"u8 pxl8_palette_ramp_index(const pxl8_palette* pal, u8 position);\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_fill(pxl8_gfx* ctx, i32 x, i32 y, i32 r, u32 color);\n"
|
||||
"void pxl8_2d_clear(pxl8_gfx* ctx, u32 color);\n"
|
||||
"u32 pxl8_2d_get_pixel(pxl8_gfx* ctx, i32 x, i32 y);\n"
|
||||
"void pxl8_2d_line(pxl8_gfx* ctx, i32 x0, i32 y0, i32 x1, i32 y1, u32 color);\n"
|
||||
"void pxl8_2d_pixel(pxl8_gfx* ctx, i32 x, i32 y, u32 color);\n"
|
||||
"void pxl8_2d_rect(pxl8_gfx* ctx, i32 x, i32 y, i32 w, i32 h, u32 color);\n"
|
||||
"void pxl8_2d_rect_fill(pxl8_gfx* ctx, i32 x, i32 y, i32 w, i32 h, u32 color);\n"
|
||||
"void pxl8_2d_sprite(pxl8_gfx* ctx, u32 id, i32 x, i32 y, i32 w, i32 h, bool flip_x, bool flip_y);\n"
|
||||
"void pxl8_2d_text(pxl8_gfx* ctx, const char* str, i32 x, i32 y, u32 color);\n"
|
||||
"void pxl8_gfx_color_ramp(pxl8_gfx* ctx, u8 start, u8 count, u32 from_color, u32 to_color);\n"
|
||||
"void pxl8_gfx_cycle_palette(pxl8_gfx* ctx, u8 start, u8 count, i32 step);\n"
|
||||
"void pxl8_gfx_fade_palette(pxl8_gfx* ctx, u8 start, u8 count, f32 amount, u32 target_color);\n"
|
||||
"i32 pxl8_gfx_add_palette_cycle(pxl8_gfx* ctx, u8 start_index, u8 end_index, f32 speed);\n"
|
||||
"void pxl8_gfx_remove_palette_cycle(pxl8_gfx* ctx, i32 cycle_id);\n"
|
||||
"void pxl8_gfx_set_palette_cycle_speed(pxl8_gfx* ctx, i32 cycle_id, f32 speed);\n"
|
||||
"void pxl8_gfx_clear_palette_cycles(pxl8_gfx* ctx);\n"
|
||||
"void pxl8_gfx_update(pxl8_gfx* ctx, f32 dt);\n"
|
||||
"i32 pxl8_gfx_load_palette(pxl8_gfx* ctx, const char* filepath);\n"
|
||||
"i32 pxl8_gfx_load_sprite(pxl8_gfx* ctx, const char* filepath, u32* sprite_id);\n"
|
||||
"i32 pxl8_gfx_create_texture(pxl8_gfx* ctx, const u8* pixels, u32 width, u32 height);\n"
|
||||
"bool pxl8_gfx_push_target(pxl8_gfx* ctx);\n"
|
||||
"void pxl8_gfx_pop_target(pxl8_gfx* ctx);\n"
|
||||
"typedef struct pxl8_input_state pxl8_input_state;\n"
|
||||
"bool pxl8_key_down(const pxl8_input_state* input, const char* key_name);\n"
|
||||
"bool pxl8_key_pressed(const pxl8_input_state* input, const char* key_name);\n"
|
||||
"bool pxl8_key_released(const pxl8_input_state* input, const char* key_name);\n"
|
||||
"bool pxl8_mouse_pressed(const pxl8_input_state* input, i32 button);\n"
|
||||
"bool pxl8_mouse_released(const pxl8_input_state* input, i32 button);\n"
|
||||
"int pxl8_mouse_wheel_x(const pxl8_input_state* input);\n"
|
||||
"int pxl8_mouse_wheel_y(const pxl8_input_state* input);\n"
|
||||
"int pxl8_mouse_x(const pxl8_input_state* input);\n"
|
||||
"int pxl8_mouse_y(const pxl8_input_state* input);\n"
|
||||
"int pxl8_mouse_dx(const pxl8_input_state* input);\n"
|
||||
"int pxl8_mouse_dy(const pxl8_input_state* input);\n"
|
||||
"typedef enum { PXL8_CURSOR_ARROW = 0, PXL8_CURSOR_HAND = 1 } pxl8_cursor;\n"
|
||||
"void pxl8_center_cursor(pxl8* sys);\n"
|
||||
"void pxl8_set_cursor(pxl8* sys, pxl8_cursor cursor);\n"
|
||||
"void pxl8_set_relative_mouse_mode(pxl8* sys, bool enabled);\n"
|
||||
"void pxl8_set_running(pxl8* sys, bool running);\n"
|
||||
"void pxl8_lua_log(int level, const char* file, int line, const char* msg);\n"
|
||||
"typedef struct pxl8_cart pxl8_cart;\n"
|
||||
"pxl8_cart* pxl8_get_cart(void);\n"
|
||||
"const char* pxl8_cart_get_title(const pxl8_cart* cart);\n"
|
||||
"typedef u32 pxl8_tile;\n"
|
||||
"typedef struct pxl8_tilemap pxl8_tilemap;\n"
|
||||
"typedef struct pxl8_tilesheet pxl8_tilesheet;\n"
|
||||
"pxl8_tilemap* pxl8_tilemap_create(u32 width, u32 height, u32 tile_size);\n"
|
||||
"void pxl8_tilemap_destroy(pxl8_tilemap* tilemap);\n"
|
||||
"u32 pxl8_tilemap_get_height(const pxl8_tilemap* tilemap);\n"
|
||||
"pxl8_tile pxl8_tilemap_get_tile(const pxl8_tilemap* tilemap, u32 layer, u32 x, u32 y);\n"
|
||||
"u32 pxl8_tilemap_get_tile_size(const pxl8_tilemap* tilemap);\n"
|
||||
"void* pxl8_tilemap_get_tile_user_data(const pxl8_tilemap* tilemap, u16 tile_id);\n"
|
||||
"u32 pxl8_tilemap_get_width(const pxl8_tilemap* tilemap);\n"
|
||||
"void pxl8_tilemap_set_tile_user_data(pxl8_tilemap* tilemap, u16 tile_id, void* user_data);\n"
|
||||
"bool pxl8_tilemap_check_collision(const pxl8_tilemap* tilemap, i32 x, i32 y, i32 w, i32 h);\n"
|
||||
"u16 pxl8_tilemap_get_tile_id(const pxl8_tilemap* tilemap, u32 layer, u32 x, u32 y);\n"
|
||||
"bool pxl8_tilemap_is_solid(const pxl8_tilemap* tilemap, u32 x, u32 y);\n"
|
||||
"void pxl8_tilemap_render(const pxl8_tilemap* tilemap, pxl8_gfx* gfx);\n"
|
||||
"void pxl8_tilemap_render_layer(const pxl8_tilemap* tilemap, pxl8_gfx* gfx, u32 layer);\n"
|
||||
"void pxl8_tilemap_set_camera(pxl8_tilemap* tilemap, i32 x, i32 y);\n"
|
||||
"void pxl8_tilemap_set_tile(pxl8_tilemap* tilemap, u32 layer, u32 x, u32 y, u16 tile_id, u8 flags);\n"
|
||||
"i32 pxl8_tilemap_set_tilesheet(pxl8_tilemap* tilemap, pxl8_tilesheet* tilesheet);\n"
|
||||
"i32 pxl8_tilemap_load_ase(pxl8_tilemap* tilemap, const char* filepath, u32 layer);\n"
|
||||
"pxl8_tilesheet* pxl8_tilesheet_create(u32 tile_size);\n"
|
||||
"void pxl8_tilesheet_destroy(pxl8_tilesheet* tilesheet);\n"
|
||||
"i32 pxl8_tilesheet_load(pxl8_tilesheet* tilesheet, const char* filepath, pxl8_gfx* gfx);\n"
|
||||
"\n"
|
||||
"typedef struct {\n"
|
||||
" float angle;\n"
|
||||
" float ax, ay, az;\n"
|
||||
" unsigned int color;\n"
|
||||
" unsigned int end_color;\n"
|
||||
" unsigned char flags;\n"
|
||||
" float life;\n"
|
||||
" float max_life;\n"
|
||||
" float size;\n"
|
||||
" float spin;\n"
|
||||
" unsigned int start_color;\n"
|
||||
" float vx, vy, vz;\n"
|
||||
" float x, y, z;\n"
|
||||
"} pxl8_particle;\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_particles pxl8_particles;\n"
|
||||
"pxl8_particles* pxl8_particles_create(u32 max_count, pxl8_rng* rng);\n"
|
||||
"void pxl8_particles_destroy(pxl8_particles* ps);\n"
|
||||
"void pxl8_particles_clear(pxl8_particles* ps);\n"
|
||||
"void pxl8_particles_emit(pxl8_particles* ps, u32 count);\n"
|
||||
"void pxl8_particles_render(pxl8_particles* ps, pxl8_gfx* gfx);\n"
|
||||
"void pxl8_particles_update(pxl8_particles* ps, float dt);\n"
|
||||
"u32 pxl8_particles_count(const pxl8_particles* ps);\n"
|
||||
"u32 pxl8_particles_max_count(const pxl8_particles* ps);\n"
|
||||
"pxl8_particle* pxl8_particles_get(pxl8_particles* ps, u32 index);\n"
|
||||
"pxl8_rng* pxl8_particles_rng(pxl8_particles* ps);\n"
|
||||
"f32 pxl8_particles_get_drag(const pxl8_particles* ps);\n"
|
||||
"f32 pxl8_particles_get_gravity_x(const pxl8_particles* ps);\n"
|
||||
"f32 pxl8_particles_get_gravity_y(const pxl8_particles* ps);\n"
|
||||
"f32 pxl8_particles_get_spawn_rate(const pxl8_particles* ps);\n"
|
||||
"f32 pxl8_particles_get_spread_x(const pxl8_particles* ps);\n"
|
||||
"f32 pxl8_particles_get_spread_y(const pxl8_particles* ps);\n"
|
||||
"f32 pxl8_particles_get_turbulence(const pxl8_particles* ps);\n"
|
||||
"f32 pxl8_particles_get_x(const pxl8_particles* ps);\n"
|
||||
"f32 pxl8_particles_get_y(const pxl8_particles* ps);\n"
|
||||
"void pxl8_particles_set_colors(pxl8_particles* ps, u8 color_min, u8 color_max);\n"
|
||||
"void pxl8_particles_set_drag(pxl8_particles* ps, f32 drag);\n"
|
||||
"void pxl8_particles_set_gravity(pxl8_particles* ps, f32 gx, f32 gy);\n"
|
||||
"void pxl8_particles_set_life(pxl8_particles* ps, f32 life_min, f32 life_max);\n"
|
||||
"void pxl8_particles_set_palette(pxl8_particles* ps, pxl8_palette* palette);\n"
|
||||
"void pxl8_particles_set_position(pxl8_particles* ps, f32 x, f32 y);\n"
|
||||
"void pxl8_particles_set_size(pxl8_particles* ps, f32 size_min, f32 size_max);\n"
|
||||
"void pxl8_particles_set_spawn_rate(pxl8_particles* ps, f32 rate);\n"
|
||||
"void pxl8_particles_set_spread(pxl8_particles* ps, f32 spread_x, f32 spread_y);\n"
|
||||
"void pxl8_particles_set_turbulence(pxl8_particles* ps, f32 turbulence);\n"
|
||||
"void pxl8_particles_set_velocity(pxl8_particles* ps, f32 vx_min, f32 vx_max, f32 vy_min, f32 vy_max);\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_transition pxl8_transition;\n"
|
||||
"typedef struct pxl8_anim pxl8_anim;\n"
|
||||
"\n"
|
||||
"pxl8_transition* pxl8_transition_create(i32 type, f32 duration);\n"
|
||||
"void pxl8_transition_destroy(pxl8_transition* transition);\n"
|
||||
"f32 pxl8_transition_get_progress(const pxl8_transition* transition);\n"
|
||||
"bool pxl8_transition_is_active(const pxl8_transition* transition);\n"
|
||||
"bool pxl8_transition_is_complete(const pxl8_transition* transition);\n"
|
||||
"void pxl8_transition_render(const pxl8_transition* transition, pxl8_gfx* gfx);\n"
|
||||
"void pxl8_transition_reset(pxl8_transition* transition);\n"
|
||||
"void pxl8_transition_set_color(pxl8_transition* transition, u32 color);\n"
|
||||
"void pxl8_transition_set_reverse(pxl8_transition* transition, bool reverse);\n"
|
||||
"void pxl8_transition_start(pxl8_transition* transition);\n"
|
||||
"void pxl8_transition_stop(pxl8_transition* transition);\n"
|
||||
"void pxl8_transition_update(pxl8_transition* transition, f32 dt);\n"
|
||||
"\n"
|
||||
"pxl8_anim* pxl8_anim_create(const u32* frame_ids, const u16* frame_durations, u16 frame_count);\n"
|
||||
"pxl8_anim* pxl8_anim_create_from_ase(pxl8_gfx* gfx, const char* path);\n"
|
||||
"void pxl8_anim_destroy(pxl8_anim* anim);\n"
|
||||
"i32 pxl8_anim_add_state(pxl8_anim* anim, const char* name, pxl8_anim* state_anim);\n"
|
||||
"u16 pxl8_anim_get_current_frame(const pxl8_anim* anim);\n"
|
||||
"u32 pxl8_anim_get_current_frame_id(const pxl8_anim* anim);\n"
|
||||
"const char* pxl8_anim_get_state(const pxl8_anim* anim);\n"
|
||||
"bool pxl8_anim_has_state_machine(const pxl8_anim* anim);\n"
|
||||
"bool pxl8_anim_is_complete(const pxl8_anim* anim);\n"
|
||||
"bool pxl8_anim_is_playing(const pxl8_anim* anim);\n"
|
||||
"void pxl8_anim_pause(pxl8_anim* anim);\n"
|
||||
"void pxl8_anim_play(pxl8_anim* anim);\n"
|
||||
"void pxl8_anim_render_sprite(const pxl8_anim* anim, pxl8_gfx* gfx, i32 x, i32 y, i32 w, i32 h, bool flip_x, bool flip_y);\n"
|
||||
"void pxl8_anim_reset(pxl8_anim* anim);\n"
|
||||
"void pxl8_anim_set_frame(pxl8_anim* anim, u16 frame);\n"
|
||||
"void pxl8_anim_set_loop(pxl8_anim* anim, bool loop);\n"
|
||||
"void pxl8_anim_set_reverse(pxl8_anim* anim, bool reverse);\n"
|
||||
"void pxl8_anim_set_speed(pxl8_anim* anim, f32 speed);\n"
|
||||
"i32 pxl8_anim_set_state(pxl8_anim* anim, const char* name);\n"
|
||||
"void pxl8_anim_stop(pxl8_anim* anim);\n"
|
||||
"void pxl8_anim_update(pxl8_anim* anim, f32 dt);\n"
|
||||
"\n"
|
||||
"typedef struct { float x, y, z; } pxl8_vec3;\n"
|
||||
"typedef struct { float x, y, z, w; } pxl8_vec4;\n"
|
||||
"typedef struct { float m[16]; } pxl8_mat4;\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_3d_uniforms {\n"
|
||||
" u8 ambient;\n"
|
||||
" u8 fog_color;\n"
|
||||
" f32 fog_density;\n"
|
||||
" f32 time;\n"
|
||||
"} pxl8_3d_uniforms;\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_3d_camera pxl8_3d_camera;\n"
|
||||
"pxl8_3d_camera* pxl8_3d_camera_create(void);\n"
|
||||
"void pxl8_3d_camera_destroy(pxl8_3d_camera* cam);\n"
|
||||
"void pxl8_3d_camera_set_perspective(pxl8_3d_camera* cam, f32 fov, f32 aspect, f32 near, f32 far);\n"
|
||||
"void pxl8_3d_camera_set_position(pxl8_3d_camera* cam, pxl8_vec3 pos);\n"
|
||||
"void pxl8_3d_camera_set_rotation(pxl8_3d_camera* cam, f32 pitch, f32 yaw, f32 roll);\n"
|
||||
"void pxl8_3d_camera_lookat(pxl8_3d_camera* cam, pxl8_vec3 eye, pxl8_vec3 target, pxl8_vec3 up);\n"
|
||||
"pxl8_vec3 pxl8_3d_camera_get_forward(const pxl8_3d_camera* cam);\n"
|
||||
"pxl8_vec3 pxl8_3d_camera_get_position(const pxl8_3d_camera* cam);\n"
|
||||
"pxl8_vec3 pxl8_3d_camera_get_right(const pxl8_3d_camera* cam);\n"
|
||||
"pxl8_vec3 pxl8_3d_camera_get_up(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"
|
||||
"void pxl8_3d_camera_update(pxl8_3d_camera* cam, f32 dt);\n"
|
||||
"\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_depth(pxl8_gfx* gfx);\n"
|
||||
"void pxl8_3d_draw_line(pxl8_gfx* gfx, pxl8_vec3 p0, pxl8_vec3 p1, u8 color);\n"
|
||||
"void pxl8_3d_end_frame(pxl8_gfx* gfx);\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"
|
||||
"\n"
|
||||
"typedef struct pxl8_material {\n"
|
||||
" u32 texture_id;\n"
|
||||
" u8 alpha;\n"
|
||||
" u8 blend_mode;\n"
|
||||
" bool dither;\n"
|
||||
" bool double_sided;\n"
|
||||
" bool dynamic_lighting;\n"
|
||||
" bool per_pixel;\n"
|
||||
" bool vertex_color_passthrough;\n"
|
||||
" f32 emissive_intensity;\n"
|
||||
"} pxl8_material;\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_vertex {\n"
|
||||
" pxl8_vec3 position;\n"
|
||||
" pxl8_vec3 normal;\n"
|
||||
" f32 u, v;\n"
|
||||
" u8 color;\n"
|
||||
" u8 light;\n"
|
||||
" u8 _pad[2];\n"
|
||||
"} pxl8_vertex;\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_mesh {\n"
|
||||
" pxl8_vertex* vertices;\n"
|
||||
" u16* indices;\n"
|
||||
" u32 vertex_count;\n"
|
||||
" u32 index_count;\n"
|
||||
" u32 vertex_capacity;\n"
|
||||
" u32 index_capacity;\n"
|
||||
"} pxl8_mesh;\n"
|
||||
"\n"
|
||||
"pxl8_mesh* pxl8_mesh_create(u32 vertex_capacity, u32 index_capacity);\n"
|
||||
"void pxl8_mesh_destroy(pxl8_mesh* mesh);\n"
|
||||
"void pxl8_mesh_clear(pxl8_mesh* mesh);\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_3d_draw_mesh(pxl8_gfx* gfx, const pxl8_mesh* mesh, pxl8_mat4 model, pxl8_material material);\n"
|
||||
"\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_mul(pxl8_mat4 a, pxl8_mat4 b);\n"
|
||||
"pxl8_mat4 pxl8_mat4_ortho(float left, float right, float bottom, float top, float near, float far);\n"
|
||||
"pxl8_mat4 pxl8_mat4_perspective(float fov, float aspect, float near, float far);\n"
|
||||
"pxl8_mat4 pxl8_mat4_rotate_x(float angle);\n"
|
||||
"pxl8_mat4 pxl8_mat4_rotate_y(float angle);\n"
|
||||
"pxl8_mat4 pxl8_mat4_rotate_z(float angle);\n"
|
||||
"pxl8_mat4 pxl8_mat4_scale(float x, float y, float z);\n"
|
||||
"pxl8_mat4 pxl8_mat4_translate(float x, float y, float z);\n"
|
||||
"\n"
|
||||
"typedef enum pxl8_procgen_type {\n"
|
||||
" PXL8_PROCGEN_ROOMS = 0,\n"
|
||||
" PXL8_PROCGEN_TERRAIN = 1\n"
|
||||
"} pxl8_procgen_type;\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_procgen_params {\n"
|
||||
" pxl8_procgen_type type;\n"
|
||||
" int width;\n"
|
||||
" int height;\n"
|
||||
" int depth;\n"
|
||||
" unsigned int seed;\n"
|
||||
" int min_room_size;\n"
|
||||
" int max_room_size;\n"
|
||||
" int num_rooms;\n"
|
||||
"} pxl8_procgen_params;\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_procgen_tex_params {\n"
|
||||
" char name[16];\n"
|
||||
" unsigned int seed;\n"
|
||||
" int width;\n"
|
||||
" int height;\n"
|
||||
" float scale;\n"
|
||||
" float roughness;\n"
|
||||
" unsigned char base_color;\n"
|
||||
" unsigned char variation;\n"
|
||||
"} pxl8_procgen_tex_params;\n"
|
||||
"\n"
|
||||
"void pxl8_procgen_tex(u8* buffer, const pxl8_procgen_tex_params* params);\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_bsp pxl8_bsp;\n"
|
||||
"typedef struct pxl8_bsp_face pxl8_bsp_face;\n"
|
||||
"typedef bool (*pxl8_texture_rule)(const pxl8_vec3* normal, const pxl8_bsp_face* face, const pxl8_bsp* bsp);\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_world_texture {\n"
|
||||
" char name[16];\n"
|
||||
" unsigned int texture_id;\n"
|
||||
" pxl8_texture_rule rule;\n"
|
||||
"} pxl8_world_texture;\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_world pxl8_world;\n"
|
||||
"pxl8_world* pxl8_world_create(void);\n"
|
||||
"void pxl8_world_destroy(pxl8_world* world);\n"
|
||||
"int pxl8_world_generate(pxl8_world* world, pxl8_gfx* gfx, const pxl8_procgen_params* params);\n"
|
||||
"int pxl8_world_load(pxl8_world* world, const char* path);\n"
|
||||
"void pxl8_world_unload(pxl8_world* world);\n"
|
||||
"int pxl8_world_apply_textures(pxl8_world* world, const pxl8_world_texture* textures, unsigned int count);\n"
|
||||
"bool pxl8_world_check_collision(const pxl8_world* world, pxl8_vec3 pos, float radius);\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"
|
||||
"pxl8_vec3 pxl8_world_resolve_collision(const pxl8_world* world, pxl8_vec3 from, pxl8_vec3 to, float radius);\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"
|
||||
"pxl8_gui_state* pxl8_gui_state_create(void);\n"
|
||||
"void pxl8_gui_state_destroy(pxl8_gui_state* state);\n"
|
||||
"void pxl8_gui_begin_frame(pxl8_gui_state* state);\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_down(pxl8_gui_state* state);\n"
|
||||
"void pxl8_gui_cursor_up(pxl8_gui_state* state);\n"
|
||||
"bool pxl8_gui_button(pxl8_gui_state* state, pxl8_gfx* gfx, u32 id, i32 x, i32 y, i32 w, i32 h, const char* label);\n"
|
||||
"void pxl8_gui_window(pxl8_gfx* gfx, i32 x, i32 y, i32 w, i32 h, const char* title);\n"
|
||||
"void pxl8_gui_label(pxl8_gfx* gfx, i32 x, i32 y, const char* text, u8 color);\n"
|
||||
"bool pxl8_gui_is_hovering(const pxl8_gui_state* state);\n"
|
||||
"void pxl8_gui_get_cursor_pos(const pxl8_gui_state* state, i32* x, i32* y);\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_save pxl8_save;\n"
|
||||
"pxl8_save* pxl8_save_create(const char* game_name, u32 magic, u32 version);\n"
|
||||
"void pxl8_save_destroy(pxl8_save* save);\n"
|
||||
"i32 pxl8_save_write(pxl8_save* save, u8 slot, const u8* data, u32 size);\n"
|
||||
"i32 pxl8_save_read(pxl8_save* save, u8 slot, u8** data_out, u32* size_out);\n"
|
||||
"void pxl8_save_free(u8* data);\n"
|
||||
"bool pxl8_save_exists(pxl8_save* save, u8 slot);\n"
|
||||
"i32 pxl8_save_delete(pxl8_save* save, u8 slot);\n"
|
||||
"const char* pxl8_save_get_directory(pxl8_save* save);\n"
|
||||
"\n"
|
||||
"typedef struct pxl8_sfx_context pxl8_sfx_context;\n"
|
||||
"typedef struct pxl8_sfx_mixer pxl8_sfx_mixer;\n"
|
||||
"typedef struct pxl8_sfx_node pxl8_sfx_node;\n"
|
||||
"typedef enum pxl8_sfx_filter_type { PXL8_SFX_FILTER_BANDPASS = 0, PXL8_SFX_FILTER_HIGHPASS, PXL8_SFX_FILTER_LOWPASS, PXL8_SFX_FILTER_NONE } pxl8_sfx_filter_type;\n"
|
||||
"typedef enum pxl8_sfx_lfo_target { PXL8_SFX_LFO_AMPLITUDE = 0, PXL8_SFX_LFO_FILTER, PXL8_SFX_LFO_PITCH } pxl8_sfx_lfo_target;\n"
|
||||
"typedef enum pxl8_sfx_node_type { PXL8_SFX_NODE_COMPRESSOR, PXL8_SFX_NODE_DELAY, PXL8_SFX_NODE_REVERB } pxl8_sfx_node_type;\n"
|
||||
"typedef enum pxl8_sfx_waveform { PXL8_SFX_WAVE_NOISE = 0, PXL8_SFX_WAVE_PULSE, PXL8_SFX_WAVE_SAW, PXL8_SFX_WAVE_SINE, PXL8_SFX_WAVE_SQUARE, PXL8_SFX_WAVE_TRIANGLE } pxl8_sfx_waveform;\n"
|
||||
"typedef struct pxl8_sfx_adsr { f32 attack; f32 decay; f32 sustain; f32 release; } pxl8_sfx_adsr;\n"
|
||||
"typedef struct pxl8_sfx_compressor_config { f32 attack; f32 ratio; f32 release; f32 threshold; } pxl8_sfx_compressor_config;\n"
|
||||
"typedef struct pxl8_sfx_delay_config { f32 feedback; f32 mix; u32 time_l; u32 time_r; } pxl8_sfx_delay_config;\n"
|
||||
"typedef struct pxl8_sfx_reverb_config { f32 damping; f32 mix; f32 room; } pxl8_sfx_reverb_config;\n"
|
||||
"typedef struct pxl8_sfx_voice_params { pxl8_sfx_adsr amp_env; pxl8_sfx_adsr filter_env; pxl8_sfx_filter_type filter_type; pxl8_sfx_lfo_target lfo_target; pxl8_sfx_waveform lfo_waveform; pxl8_sfx_waveform waveform; f32 filter_cutoff; f32 filter_env_depth; f32 filter_resonance; f32 fx_send; f32 lfo_depth; f32 lfo_rate; f32 pulse_width; } pxl8_sfx_voice_params;\n"
|
||||
"pxl8_sfx_node* pxl8_sfx_compressor_create(pxl8_sfx_compressor_config cfg);\n"
|
||||
"void pxl8_sfx_compressor_set_attack(pxl8_sfx_node* node, f32 attack);\n"
|
||||
"void pxl8_sfx_compressor_set_ratio(pxl8_sfx_node* node, f32 ratio);\n"
|
||||
"void pxl8_sfx_compressor_set_release(pxl8_sfx_node* node, f32 release);\n"
|
||||
"void pxl8_sfx_compressor_set_threshold(pxl8_sfx_node* node, f32 threshold);\n"
|
||||
"void pxl8_sfx_context_append_node(pxl8_sfx_context* ctx, pxl8_sfx_node* node);\n"
|
||||
"pxl8_sfx_context* pxl8_sfx_context_create(void);\n"
|
||||
"void pxl8_sfx_context_destroy(pxl8_sfx_context* ctx);\n"
|
||||
"pxl8_sfx_node* pxl8_sfx_context_get_head(pxl8_sfx_context* ctx);\n"
|
||||
"f32 pxl8_sfx_context_get_volume(const pxl8_sfx_context* ctx);\n"
|
||||
"void pxl8_sfx_context_insert_node(pxl8_sfx_context* ctx, pxl8_sfx_node* after, pxl8_sfx_node* node);\n"
|
||||
"void pxl8_sfx_context_remove_node(pxl8_sfx_context* ctx, pxl8_sfx_node* node);\n"
|
||||
"void pxl8_sfx_context_set_volume(pxl8_sfx_context* ctx, f32 volume);\n"
|
||||
"pxl8_sfx_node* pxl8_sfx_delay_create(pxl8_sfx_delay_config cfg);\n"
|
||||
"void pxl8_sfx_delay_set_feedback(pxl8_sfx_node* node, f32 feedback);\n"
|
||||
"void pxl8_sfx_delay_set_mix(pxl8_sfx_node* node, f32 mix);\n"
|
||||
"void pxl8_sfx_delay_set_time(pxl8_sfx_node* node, u32 time_l, u32 time_r);\n"
|
||||
"void pxl8_sfx_mixer_attach(pxl8_sfx_mixer* mixer, pxl8_sfx_context* ctx);\n"
|
||||
"pxl8_sfx_mixer* pxl8_sfx_mixer_create(void);\n"
|
||||
"void pxl8_sfx_mixer_destroy(pxl8_sfx_mixer* mixer);\n"
|
||||
"void pxl8_sfx_mixer_detach(pxl8_sfx_mixer* mixer, pxl8_sfx_context* ctx);\n"
|
||||
"f32 pxl8_sfx_mixer_get_master_volume(const pxl8_sfx_mixer* mixer);\n"
|
||||
"void pxl8_sfx_mixer_set_master_volume(pxl8_sfx_mixer* mixer, f32 volume);\n"
|
||||
"void pxl8_sfx_node_destroy(pxl8_sfx_node* node);\n"
|
||||
"f32 pxl8_sfx_note_to_freq(u8 note);\n"
|
||||
"u16 pxl8_sfx_play_note(pxl8_sfx_context* ctx, u8 note, const pxl8_sfx_voice_params* params, f32 volume, f32 duration);\n"
|
||||
"void pxl8_sfx_release_voice(pxl8_sfx_context* ctx, u16 voice_id);\n"
|
||||
"pxl8_sfx_node* pxl8_sfx_reverb_create(pxl8_sfx_reverb_config cfg);\n"
|
||||
"void pxl8_sfx_reverb_set_damping(pxl8_sfx_node* node, f32 damping);\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_stop_all(pxl8_sfx_context* ctx);\n"
|
||||
"void pxl8_sfx_stop_voice(pxl8_sfx_context* ctx, u16 voice_id);\n";
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pxl8_color.h"
|
||||
#include "pxl8_gfx.h"
|
||||
#include "pxl8_io.h"
|
||||
#include "pxl8_log.h"
|
||||
|
|
@ -367,18 +368,177 @@ i32 pxl8_bsp_find_leaf(const pxl8_bsp* bsp, pxl8_vec3 pos) {
|
|||
}
|
||||
|
||||
bool pxl8_bsp_is_leaf_visible(const pxl8_bsp* bsp, i32 leaf_from, i32 leaf_to) {
|
||||
if (!bsp || !bsp->visdata || leaf_from < 0 || leaf_to < 0) return true;
|
||||
if (!bsp || !bsp->visdata || bsp->visdata_size == 0) return true;
|
||||
if (leaf_from < 0 || leaf_to < 0) return true;
|
||||
if ((u32)leaf_from >= bsp->num_leafs || (u32)leaf_to >= bsp->num_leafs) return true;
|
||||
|
||||
i32 visofs = bsp->leafs[leaf_from].visofs;
|
||||
if (visofs < 0) return true;
|
||||
|
||||
i32 byte_idx = leaf_to >> 3;
|
||||
i32 bit_idx = leaf_to & 7;
|
||||
u32 target_byte = leaf_to >> 3;
|
||||
u32 target_bit = leaf_to & 7;
|
||||
u32 pvs_size = (bsp->num_leafs + 7) / 8;
|
||||
|
||||
if ((u32)visofs + byte_idx >= bsp->visdata_size) return true;
|
||||
u32 pos = (u32)visofs;
|
||||
u32 current_byte = 0;
|
||||
|
||||
return (bsp->visdata[visofs + byte_idx] & (1 << bit_idx)) != 0;
|
||||
while (current_byte < pvs_size && pos < bsp->visdata_size) {
|
||||
u8 b = bsp->visdata[pos++];
|
||||
|
||||
if (b != 0) {
|
||||
if (current_byte == target_byte) {
|
||||
return (b & (1 << target_bit)) != 0;
|
||||
}
|
||||
current_byte++;
|
||||
} else {
|
||||
if (pos >= bsp->visdata_size) return false;
|
||||
u32 count = bsp->visdata[pos++];
|
||||
if (target_byte < current_byte + count) {
|
||||
return false;
|
||||
}
|
||||
current_byte += count;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
pxl8_bsp_pvs pxl8_bsp_decompress_pvs(const pxl8_bsp* bsp, i32 leaf) {
|
||||
pxl8_bsp_pvs pvs = {0};
|
||||
|
||||
u32 pvs_size = (bsp->num_leafs + 7) / 8;
|
||||
pvs.data = calloc(pvs_size, 1);
|
||||
pvs.size = pvs_size;
|
||||
|
||||
if (!pvs.data) return pvs;
|
||||
|
||||
if (!bsp || leaf < 0 || (u32)leaf >= bsp->num_leafs) {
|
||||
memset(pvs.data, 0xFF, pvs_size);
|
||||
return pvs;
|
||||
}
|
||||
|
||||
i32 visofs = bsp->leafs[leaf].visofs;
|
||||
if (visofs < 0 || !bsp->visdata || bsp->visdata_size == 0) {
|
||||
memset(pvs.data, 0xFF, pvs_size);
|
||||
return pvs;
|
||||
}
|
||||
|
||||
u32 pos = (u32)visofs;
|
||||
u32 out = 0;
|
||||
|
||||
while (out < pvs_size && pos < bsp->visdata_size) {
|
||||
u8 b = bsp->visdata[pos++];
|
||||
|
||||
if (b != 0) {
|
||||
pvs.data[out++] = b;
|
||||
} else {
|
||||
if (pos >= bsp->visdata_size) break;
|
||||
u32 count = bsp->visdata[pos++];
|
||||
out += count;
|
||||
}
|
||||
}
|
||||
|
||||
return pvs;
|
||||
}
|
||||
|
||||
void pxl8_bsp_pvs_destroy(pxl8_bsp_pvs* pvs) {
|
||||
if (pvs) {
|
||||
free(pvs->data);
|
||||
pvs->data = NULL;
|
||||
pvs->size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool pxl8_bsp_pvs_is_visible(const pxl8_bsp_pvs* pvs, i32 leaf) {
|
||||
if (!pvs || !pvs->data || leaf < 0) return false;
|
||||
u32 byte_idx = leaf >> 3;
|
||||
u32 bit_idx = leaf & 7;
|
||||
if (byte_idx >= pvs->size) return false;
|
||||
return (pvs->data[byte_idx] & (1 << bit_idx)) != 0;
|
||||
}
|
||||
|
||||
pxl8_bsp_lightmap pxl8_bsp_lightmap_uniform(u8 r, u8 g, u8 b) {
|
||||
return (pxl8_bsp_lightmap){
|
||||
.color = {r, g, b},
|
||||
.height = 0,
|
||||
.offset = 0,
|
||||
.width = 0,
|
||||
};
|
||||
}
|
||||
|
||||
pxl8_bsp_lightmap pxl8_bsp_lightmap_mapped(u8 width, u8 height, u32 offset) {
|
||||
return (pxl8_bsp_lightmap){
|
||||
.color = {0, 0, 0},
|
||||
.height = height,
|
||||
.offset = offset,
|
||||
.width = width,
|
||||
};
|
||||
}
|
||||
|
||||
pxl8_bsp_lightmap_sample pxl8_bsp_sample_lightmap(const pxl8_bsp* bsp, u32 face_idx, f32 u, f32 v) {
|
||||
pxl8_bsp_lightmap_sample white = {255, 255, 255};
|
||||
|
||||
if (!bsp || !bsp->lightmaps || face_idx >= bsp->num_lightmaps) {
|
||||
return white;
|
||||
}
|
||||
|
||||
const pxl8_bsp_lightmap* lm = &bsp->lightmaps[face_idx];
|
||||
|
||||
if (lm->width == 0) {
|
||||
return (pxl8_bsp_lightmap_sample){lm->color[2], lm->color[1], lm->color[0]};
|
||||
}
|
||||
|
||||
if (!bsp->lightdata || bsp->lightdata_size == 0) {
|
||||
return white;
|
||||
}
|
||||
|
||||
f32 w = (f32)lm->width;
|
||||
f32 h = (f32)lm->height;
|
||||
f32 fx = u * w;
|
||||
f32 fy = v * h;
|
||||
|
||||
if (fx < 0) fx = 0;
|
||||
if (fx > w - 1.001f) fx = w - 1.001f;
|
||||
if (fy < 0) fy = 0;
|
||||
if (fy > h - 1.001f) fy = h - 1.001f;
|
||||
|
||||
u32 x0 = (u32)fx;
|
||||
u32 y0 = (u32)fy;
|
||||
u32 x1 = x0 + 1;
|
||||
u32 y1 = y0 + 1;
|
||||
if (x1 >= lm->width) x1 = lm->width - 1;
|
||||
if (y1 >= lm->height) y1 = lm->height - 1;
|
||||
|
||||
f32 frac_x = fx - (f32)x0;
|
||||
f32 frac_y = fy - (f32)y0;
|
||||
|
||||
u32 stride = lm->width;
|
||||
u32 base = lm->offset;
|
||||
|
||||
u32 idx00 = base + y0 * stride + x0;
|
||||
u32 idx10 = base + y0 * stride + x1;
|
||||
u32 idx01 = base + y1 * stride + x0;
|
||||
u32 idx11 = base + y1 * stride + x1;
|
||||
|
||||
u8 r00, g00, b00, r10, g10, b10, r01, g01, b01, r11, g11, b11;
|
||||
|
||||
if (idx00 < bsp->lightdata_size) pxl8_rgb332_unpack(bsp->lightdata[idx00], &r00, &g00, &b00);
|
||||
else { r00 = g00 = b00 = 255; }
|
||||
if (idx10 < bsp->lightdata_size) pxl8_rgb332_unpack(bsp->lightdata[idx10], &r10, &g10, &b10);
|
||||
else { r10 = g10 = b10 = 255; }
|
||||
if (idx01 < bsp->lightdata_size) pxl8_rgb332_unpack(bsp->lightdata[idx01], &r01, &g01, &b01);
|
||||
else { r01 = g01 = b01 = 255; }
|
||||
if (idx11 < bsp->lightdata_size) pxl8_rgb332_unpack(bsp->lightdata[idx11], &r11, &g11, &b11);
|
||||
else { r11 = g11 = b11 = 255; }
|
||||
|
||||
f32 inv_x = 1.0f - frac_x;
|
||||
f32 inv_y = 1.0f - frac_y;
|
||||
|
||||
u8 r = (u8)(r00 * inv_x * inv_y + r10 * frac_x * inv_y + r01 * inv_x * frac_y + r11 * frac_x * frac_y);
|
||||
u8 g = (u8)(g00 * inv_x * inv_y + g10 * frac_x * inv_y + g01 * inv_x * frac_y + g11 * frac_x * frac_y);
|
||||
u8 b = (u8)(b00 * inv_x * inv_y + b10 * frac_x * inv_y + b01 * inv_x * frac_y + b11 * frac_x * frac_y);
|
||||
|
||||
return (pxl8_bsp_lightmap_sample){b, g, r};
|
||||
}
|
||||
|
||||
static inline bool face_in_frustum(const pxl8_bsp* bsp, u32 face_id, const pxl8_frustum* frustum) {
|
||||
|
|
@ -386,64 +546,114 @@ 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);
|
||||
}
|
||||
|
||||
void pxl8_bsp_render_face(pxl8_gfx* gfx, const pxl8_bsp* bsp, u32 face_id, u32 texture_id) {
|
||||
if (!gfx || !bsp || face_id >= bsp->num_faces) return;
|
||||
|
||||
static void collect_face_to_mesh(
|
||||
const pxl8_bsp* bsp,
|
||||
u32 face_id,
|
||||
pxl8_mesh* mesh
|
||||
) {
|
||||
const pxl8_bsp_face* face = &bsp->faces[face_id];
|
||||
if (face->num_edges < 3) return;
|
||||
|
||||
pxl8_vec3 verts[64];
|
||||
pxl8_vec3 normal = {0, 1, 0};
|
||||
if (face->plane_id < bsp->num_planes) {
|
||||
normal = bsp->planes[face->plane_id].normal;
|
||||
if (face->side) {
|
||||
normal.x = -normal.x;
|
||||
normal.y = -normal.y;
|
||||
normal.z = -normal.z;
|
||||
}
|
||||
}
|
||||
|
||||
const pxl8_bsp_texinfo* texinfo = NULL;
|
||||
f32 tex_scale = 64.0f;
|
||||
if (face->texinfo_id < bsp->num_texinfo) {
|
||||
texinfo = &bsp->texinfo[face->texinfo_id];
|
||||
}
|
||||
|
||||
u16 base_idx = (u16)mesh->vertex_count;
|
||||
u32 num_verts = 0;
|
||||
|
||||
u32 color = 15;
|
||||
bool use_texture = (face->texinfo_id < bsp->num_texinfo);
|
||||
|
||||
static int face_debug = 0;
|
||||
bool debug_this = (face_debug++ < 3);
|
||||
|
||||
for (u32 i = 0; i < face->num_edges && num_verts < 64; i++) {
|
||||
i32 surfedge_idx = face->first_edge + i;
|
||||
u32 vert_idx;
|
||||
|
||||
if (!pxl8_bsp_get_edge_vertex(bsp, surfedge_idx, &vert_idx)) continue;
|
||||
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;
|
||||
}
|
||||
|
||||
verts[num_verts++] = bsp->vertices[vert_idx].position;
|
||||
pxl8_vec3 pos = bsp->vertices[vert_idx].position;
|
||||
|
||||
f32 u = 0.0f, v = 0.0f;
|
||||
if (texinfo) {
|
||||
u = (pxl8_vec3_dot(pos, texinfo->u_axis) + texinfo->u_offset) / tex_scale;
|
||||
v = (pxl8_vec3_dot(pos, texinfo->v_axis) + texinfo->v_offset) / tex_scale;
|
||||
}
|
||||
|
||||
u8 light = 255;
|
||||
if (bsp->vertex_lights && vert_idx < bsp->num_vertex_lights) {
|
||||
light = (bsp->vertex_lights[vert_idx] >> 24) & 0xFF;
|
||||
}
|
||||
|
||||
pxl8_vertex vtx = {
|
||||
.position = pos,
|
||||
.normal = normal,
|
||||
.u = u,
|
||||
.v = v,
|
||||
.color = 15,
|
||||
.light = light,
|
||||
};
|
||||
pxl8_mesh_push_vertex(mesh, vtx);
|
||||
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 (use_texture && face->texinfo_id < bsp->num_texinfo) {
|
||||
const pxl8_bsp_texinfo* texinfo = &bsp->texinfo[face->texinfo_id];
|
||||
f32 tex_scale = 64.0f;
|
||||
|
||||
for (u32 tri_idx = 1; tri_idx < num_verts - 1; tri_idx++) {
|
||||
pxl8_vec3 v0 = verts[0];
|
||||
pxl8_vec3 v1 = verts[tri_idx];
|
||||
pxl8_vec3 v2 = verts[tri_idx + 1];
|
||||
|
||||
f32 u0 = (pxl8_vec3_dot(v0, texinfo->u_axis) + texinfo->u_offset) / tex_scale;
|
||||
f32 v0_uv = (pxl8_vec3_dot(v0, texinfo->v_axis) + texinfo->v_offset) / tex_scale;
|
||||
f32 u1 = (pxl8_vec3_dot(v1, texinfo->u_axis) + texinfo->u_offset) / tex_scale;
|
||||
f32 v1_uv = (pxl8_vec3_dot(v1, texinfo->v_axis) + texinfo->v_offset) / tex_scale;
|
||||
f32 u2 = (pxl8_vec3_dot(v2, texinfo->u_axis) + texinfo->u_offset) / tex_scale;
|
||||
f32 v2_uv = (pxl8_vec3_dot(v2, texinfo->v_axis) + texinfo->v_offset) / tex_scale;
|
||||
|
||||
pxl8_3d_draw_triangle_textured(gfx, v0, v1, v2, u0, v0_uv, u1, v1_uv, u2, v2_uv, texture_id);
|
||||
}
|
||||
} else{
|
||||
for (u32 i = 1; i < num_verts - 1; i++) {
|
||||
pxl8_3d_draw_triangle_raw(gfx, verts[0], verts[i], verts[i + 1], color);
|
||||
}
|
||||
for (u32 i = 1; i < num_verts - 1; i++) {
|
||||
pxl8_mesh_push_triangle(mesh, base_idx, base_idx + i, base_idx + i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void pxl8_bsp_render_textured(
|
||||
pxl8_gfx* gfx,
|
||||
const pxl8_bsp* bsp,
|
||||
pxl8_vec3 camera_pos
|
||||
) {
|
||||
if (!gfx || !bsp || bsp->num_faces == 0) return;
|
||||
void pxl8_bsp_render_face(pxl8_gfx* gfx, const pxl8_bsp* bsp, u32 face_id, u32 texture_id) {
|
||||
if (!gfx || !bsp || face_id >= bsp->num_faces) return;
|
||||
|
||||
pxl8_mesh* mesh = pxl8_mesh_create(64, 192);
|
||||
if (!mesh) return;
|
||||
|
||||
collect_face_to_mesh(bsp, face_id, mesh);
|
||||
|
||||
if (mesh->index_count > 0) {
|
||||
pxl8_material mat = pxl8_material_new(texture_id);
|
||||
pxl8_3d_draw_mesh(gfx, mesh, pxl8_mat4_identity(), mat);
|
||||
}
|
||||
|
||||
pxl8_mesh_destroy(mesh);
|
||||
}
|
||||
|
||||
void pxl8_bsp_render_textured(pxl8_gfx* gfx, const pxl8_bsp* bsp, pxl8_vec3 camera_pos) {
|
||||
static int call_count = 0;
|
||||
if (!gfx || !bsp || bsp->num_faces == 0) {
|
||||
if (call_count++ < 5) {
|
||||
pxl8_debug("bsp_render_textured: early return - gfx=%p, bsp=%p, num_faces=%u",
|
||||
(void*)gfx, (void*)bsp, bsp ? bsp->num_faces : 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const pxl8_frustum* frustum = pxl8_3d_get_frustum(gfx);
|
||||
if (!frustum) return;
|
||||
if (!frustum) {
|
||||
if (call_count++ < 5) {
|
||||
pxl8_debug("bsp_render_textured: frustum is NULL!");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
i32 camera_leaf = pxl8_bsp_find_leaf(bsp, camera_pos);
|
||||
|
||||
|
|
@ -455,11 +665,18 @@ void pxl8_bsp_render_textured(
|
|||
if (!new_buffer) return;
|
||||
rendered_faces = new_buffer;
|
||||
rendered_faces_capacity = bsp->num_faces;
|
||||
pxl8_debug("Allocated face tracking buffer: %u bytes", bsp->num_faces);
|
||||
}
|
||||
|
||||
memset(rendered_faces, 0, bsp->num_faces);
|
||||
|
||||
pxl8_mesh* mesh = pxl8_mesh_create(8192, 16384);
|
||||
if (!mesh) return;
|
||||
|
||||
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++) {
|
||||
if (camera_leaf >= 0 && !pxl8_bsp_is_leaf_visible(bsp, camera_leaf, leaf_id)) continue;
|
||||
|
||||
|
|
@ -475,39 +692,66 @@ void pxl8_bsp_render_textured(
|
|||
if (rendered_faces[face_id]) continue;
|
||||
rendered_faces[face_id] = 1;
|
||||
|
||||
if (!face_in_frustum(bsp, face_id, frustum)) continue;
|
||||
faces_checked++;
|
||||
if (!face_in_frustum(bsp, face_id, frustum)) {
|
||||
faces_culled++;
|
||||
continue;
|
||||
}
|
||||
faces_passed++;
|
||||
|
||||
const pxl8_bsp_face* face = &bsp->faces[face_id];
|
||||
u32 texture_id = 0;
|
||||
if (face->texinfo_id < bsp->num_texinfo) {
|
||||
texture_id = bsp->texinfo[face->texinfo_id].miptex;
|
||||
} else {
|
||||
static bool warned = false;
|
||||
if (!warned) {
|
||||
pxl8_warn("Face %u has invalid texinfo_id %u (num_texinfo=%u)",
|
||||
face_id, face->texinfo_id, bsp->num_texinfo);
|
||||
warned = true;
|
||||
}
|
||||
}
|
||||
|
||||
pxl8_bsp_render_face(gfx, bsp, face_id, texture_id);
|
||||
if (texture_id != current_texture && mesh->index_count > 0) {
|
||||
pxl8_material mat = pxl8_material_with_double_sided(pxl8_material_new(current_texture));
|
||||
pxl8_3d_draw_mesh(gfx, mesh, pxl8_mat4_identity(), mat);
|
||||
pxl8_mesh_clear(mesh);
|
||||
}
|
||||
|
||||
current_texture = texture_id;
|
||||
u32 before = mesh->index_count;
|
||||
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) {
|
||||
pxl8_material mat = pxl8_material_with_double_sided(pxl8_material_new(current_texture));
|
||||
pxl8_3d_draw_mesh(gfx, mesh, pxl8_mat4_identity(), mat);
|
||||
if (draw_count++ < 5) {
|
||||
pxl8_debug("bsp_render_textured: drew mesh with %u indices, camera_leaf=%d",
|
||||
mesh->index_count, camera_leaf);
|
||||
}
|
||||
} else if (draw_count < 5) {
|
||||
pxl8_debug("bsp_render_textured: mesh is empty, camera_leaf=%d, num_leafs=%u",
|
||||
camera_leaf, bsp->num_leafs);
|
||||
draw_count++;
|
||||
}
|
||||
|
||||
if (debug_cull++ < 5) {
|
||||
pxl8_debug("bsp_render: checked=%u, culled=%u, passed=%u", faces_checked, faces_culled, faces_passed);
|
||||
}
|
||||
|
||||
pxl8_mesh_destroy(mesh);
|
||||
}
|
||||
|
||||
void pxl8_bsp_render_wireframe(
|
||||
pxl8_gfx* gfx,
|
||||
const pxl8_bsp* bsp,
|
||||
pxl8_vec3 camera_pos,
|
||||
u32 color
|
||||
) {
|
||||
void pxl8_bsp_render_wireframe(pxl8_gfx* gfx, const pxl8_bsp* bsp, pxl8_vec3 camera_pos, u32 color) {
|
||||
if (!gfx || !bsp) return;
|
||||
|
||||
const pxl8_frustum* frustum = pxl8_3d_get_frustum(gfx);
|
||||
if (!frustum) return;
|
||||
|
||||
i32 camera_leaf = pxl8_bsp_find_leaf(bsp, camera_pos);
|
||||
u8 line_color = (u8)(color & 0xFF);
|
||||
|
||||
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;
|
||||
|
|
@ -527,19 +771,16 @@ void pxl8_bsp_render_wireframe(
|
|||
|
||||
for (u32 e = 0; e < face->num_edges; e++) {
|
||||
i32 surfedge_idx = face->first_edge + e;
|
||||
i32 next_surfedge_idx = face->first_edge + ((e + 1) % face->num_edges);
|
||||
|
||||
if (surfedge_idx >= (i32)bsp->num_surfedges ||
|
||||
next_surfedge_idx >= (i32)bsp->num_surfedges) continue;
|
||||
if (surfedge_idx >= (i32)bsp->num_surfedges) continue;
|
||||
|
||||
u32 v0_idx, v1_idx;
|
||||
|
||||
if (!pxl8_bsp_get_edge_vertices(bsp, surfedge_idx, &v0_idx, &v1_idx)) continue;
|
||||
|
||||
pxl8_vec3 p0 = bsp->vertices[v0_idx].position;
|
||||
pxl8_vec3 p1 = bsp->vertices[v1_idx].position;
|
||||
|
||||
pxl8_3d_draw_line_3d(gfx, p0, p1, color);
|
||||
pxl8_3d_draw_line(gfx, p0, p1, line_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "pxl8_gfx.h"
|
||||
#include "pxl8_math.h"
|
||||
#include "pxl8_mesh.h"
|
||||
#include "pxl8_types.h"
|
||||
|
||||
typedef struct pxl8_bsp_edge {
|
||||
|
|
@ -77,17 +78,45 @@ typedef struct pxl8_bsp_vertex {
|
|||
pxl8_vec3 position;
|
||||
} pxl8_bsp_vertex;
|
||||
|
||||
typedef struct pxl8_bsp_lightmap {
|
||||
u8 color[3];
|
||||
u8 height;
|
||||
u32 offset;
|
||||
u8 width;
|
||||
} pxl8_bsp_lightmap;
|
||||
|
||||
typedef struct pxl8_bsp_lightmap_sample {
|
||||
u8 b;
|
||||
u8 g;
|
||||
u8 r;
|
||||
} pxl8_bsp_lightmap_sample;
|
||||
|
||||
typedef struct pxl8_bsp_material_batch {
|
||||
u16* face_indices;
|
||||
u32 face_count;
|
||||
u8 material_id;
|
||||
pxl8_mesh* mesh;
|
||||
} pxl8_bsp_material_batch;
|
||||
|
||||
typedef struct pxl8_bsp_pvs {
|
||||
u8* data;
|
||||
u32 size;
|
||||
} pxl8_bsp_pvs;
|
||||
|
||||
typedef struct pxl8_bsp {
|
||||
pxl8_bsp_edge* edges;
|
||||
pxl8_bsp_face* faces;
|
||||
pxl8_bsp_leaf* leafs;
|
||||
u8* lightdata;
|
||||
pxl8_bsp_lightmap* lightmaps;
|
||||
u16* marksurfaces;
|
||||
pxl8_bsp_material_batch* material_batches;
|
||||
pxl8_bsp_model* models;
|
||||
pxl8_bsp_node* nodes;
|
||||
pxl8_bsp_plane* planes;
|
||||
i32* surfedges;
|
||||
pxl8_bsp_texinfo* texinfo;
|
||||
u32* vertex_lights;
|
||||
pxl8_bsp_vertex* vertices;
|
||||
u8* visdata;
|
||||
|
||||
|
|
@ -95,12 +124,15 @@ typedef struct pxl8_bsp {
|
|||
u32 num_edges;
|
||||
u32 num_faces;
|
||||
u32 num_leafs;
|
||||
u32 num_lightmaps;
|
||||
u32 num_marksurfaces;
|
||||
u32 num_material_batches;
|
||||
u32 num_models;
|
||||
u32 num_nodes;
|
||||
u32 num_planes;
|
||||
u32 num_surfedges;
|
||||
u32 num_texinfo;
|
||||
u32 num_vertex_lights;
|
||||
u32 num_vertices;
|
||||
u32 visdata_size;
|
||||
} pxl8_bsp;
|
||||
|
|
@ -115,25 +147,17 @@ void pxl8_bsp_destroy(pxl8_bsp* bsp);
|
|||
i32 pxl8_bsp_find_leaf(const pxl8_bsp* bsp, pxl8_vec3 pos);
|
||||
bool pxl8_bsp_is_leaf_visible(const pxl8_bsp* bsp, i32 leaf_from, i32 leaf_to);
|
||||
|
||||
void pxl8_bsp_render_face(
|
||||
pxl8_gfx* gfx,
|
||||
const pxl8_bsp* bsp,
|
||||
u32 face_id,
|
||||
u32 texture_id
|
||||
);
|
||||
pxl8_bsp_pvs pxl8_bsp_decompress_pvs(const pxl8_bsp* bsp, i32 leaf);
|
||||
void pxl8_bsp_pvs_destroy(pxl8_bsp_pvs* pvs);
|
||||
bool pxl8_bsp_pvs_is_visible(const pxl8_bsp_pvs* pvs, i32 leaf);
|
||||
|
||||
void pxl8_bsp_render_textured(
|
||||
pxl8_gfx* gfx,
|
||||
const pxl8_bsp* bsp,
|
||||
pxl8_vec3 camera_pos
|
||||
);
|
||||
pxl8_bsp_lightmap pxl8_bsp_lightmap_uniform(u8 r, u8 g, u8 b);
|
||||
pxl8_bsp_lightmap pxl8_bsp_lightmap_mapped(u8 width, u8 height, u32 offset);
|
||||
pxl8_bsp_lightmap_sample pxl8_bsp_sample_lightmap(const pxl8_bsp* bsp, u32 face_idx, f32 u, f32 v);
|
||||
|
||||
void pxl8_bsp_render_wireframe(
|
||||
pxl8_gfx* gfx,
|
||||
const pxl8_bsp* bsp,
|
||||
pxl8_vec3 camera_pos,
|
||||
u32 color
|
||||
);
|
||||
void pxl8_bsp_render_face(pxl8_gfx* gfx, const pxl8_bsp* bsp, u32 face_id, u32 texture_id);
|
||||
void pxl8_bsp_render_textured(pxl8_gfx* gfx, const pxl8_bsp* bsp, pxl8_vec3 camera_pos);
|
||||
void pxl8_bsp_render_wireframe(pxl8_gfx* gfx, const pxl8_bsp* bsp, pxl8_vec3 camera_pos, u32 color);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
@ -389,7 +389,20 @@ pxl8_vec3 pxl8_world_resolve_collision(const pxl8_world* world, pxl8_vec3 from,
|
|||
}
|
||||
|
||||
void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos) {
|
||||
if (!world || !gfx || !world->loaded) return;
|
||||
if (!world || !gfx || !world->loaded) {
|
||||
static int count = 0;
|
||||
if (count++ < 10) {
|
||||
pxl8_debug("world_render: early return - world=%p, gfx=%p, loaded=%d",
|
||||
(void*)world, (void*)gfx, world ? world->loaded : -1);
|
||||
}
|
||||
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) {
|
||||
pxl8_bsp_render_wireframe(gfx, &world->bsp, camera_pos, world->wireframe_color);
|
||||
|
|
@ -6,9 +6,11 @@
|
|||
(var time 0)
|
||||
(var active-demo :logo)
|
||||
(var particles nil)
|
||||
(var particles2 nil)
|
||||
(var fire-init? false)
|
||||
(var rain-init? false)
|
||||
(var snow-init? false)
|
||||
(var snow-init2? false)
|
||||
(var use-famicube-palette? false)
|
||||
|
||||
(var logo-x 256)
|
||||
|
|
@ -22,14 +24,15 @@
|
|||
(fn switch-demo [new-demo]
|
||||
(when (and (not= active-demo new-demo) (not transition))
|
||||
(set transition-pending new-demo)
|
||||
(set transition (pxl8.transition_create :pixelate 0.5))
|
||||
(pxl8.transition_set_color transition 0xFF000000)
|
||||
(pxl8.transition_start transition)))
|
||||
(set transition (pxl8.create_transition :pixelate 0.5))
|
||||
(transition:set_color 0xFF000000)
|
||||
(transition:start)))
|
||||
|
||||
(global init (fn []
|
||||
(pxl8.load_palette "res/sprites/pxl8_logo.ase")
|
||||
(set logo-sprite (pxl8.load_sprite "res/sprites/pxl8_logo.ase"))
|
||||
(set particles (pxl8.particles_new 1000))
|
||||
(set particles (pxl8.create_particles 1000))
|
||||
(set particles2 (pxl8.create_particles 500))
|
||||
(music.init)
|
||||
(music.start)
|
||||
(worldgen.init)))
|
||||
|
|
@ -44,8 +47,8 @@
|
|||
(set time (+ time dt))
|
||||
|
||||
(when transition
|
||||
(pxl8.transition_update transition dt)
|
||||
(when (pxl8.transition_is_complete transition)
|
||||
(transition:update dt)
|
||||
(when (transition:is_complete)
|
||||
(when transition-pending
|
||||
(when (and (= active-demo :worldgen) (not= transition-pending :worldgen))
|
||||
(pxl8.set_relative_mouse_mode false))
|
||||
|
|
@ -55,8 +58,8 @@
|
|||
(set transition-pending nil)
|
||||
(when (= active-demo :fire) (set fire-init? false))
|
||||
(when (= active-demo :rain) (set rain-init? false))
|
||||
(when (= active-demo :snow) (set snow-init? false)))
|
||||
(pxl8.transition_destroy transition)
|
||||
(when (= active-demo :snow) (set snow-init? false) (set snow-init2? false)))
|
||||
(transition:destroy)
|
||||
(set transition nil)))
|
||||
|
||||
(when (pxl8.key_pressed "1") (switch-demo :logo))
|
||||
|
|
@ -93,7 +96,9 @@
|
|||
(music.update dt)
|
||||
|
||||
(when particles
|
||||
(pxl8.particles_update particles dt)))
|
||||
(particles:update dt))
|
||||
(when particles2
|
||||
(particles2:update dt)))
|
||||
|
||||
(when (menu.is-paused)
|
||||
(menu.update))))
|
||||
|
|
@ -105,50 +110,75 @@
|
|||
(when logo-sprite
|
||||
(pxl8.sprite logo-sprite logo-x logo-y 128 64 (< logo-dx 0) (< logo-dy 0))))
|
||||
|
||||
:plasma (pxl8.vfx_plasma time 0.10 0.04 1)
|
||||
:plasma (do
|
||||
(pxl8.clear 0)
|
||||
(pxl8.text "Plasma (TODO: Fennel impl)" 200 170 1))
|
||||
|
||||
:tunnel (pxl8.vfx_tunnel time 2.0 0.25)
|
||||
:tunnel (do
|
||||
(pxl8.clear 0)
|
||||
(pxl8.text "Tunnel (TODO: Fennel impl)" 200 170 1))
|
||||
|
||||
:raster (do
|
||||
(pxl8.clear 0)
|
||||
(local bars [{:base_y 60 :amplitude 30 :height 16 :speed 2.0 :phase 0 :color 1 :fade_color 18}
|
||||
{:base_y 180 :amplitude 35 :height 16 :speed 1.8 :phase 2.0 :color 1 :fade_color 27}
|
||||
{:base_y 300 :amplitude 25 :height 16 :speed 2.2 :phase 4.0 :color 1 :fade_color 24}])
|
||||
(pxl8.vfx_raster_bars bars time))
|
||||
(pxl8.text "Raster Bars (TODO: Fennel impl)" 180 170 1))
|
||||
|
||||
:fire (do
|
||||
(pxl8.clear 0)
|
||||
(when particles
|
||||
(when (not fire-init?)
|
||||
(pxl8.particles_clear particles)
|
||||
(pxl8.vfx_fire particles 160 140 100 12)
|
||||
(particles:clear)
|
||||
(particles:set_position 320 360)
|
||||
(particles:set_spread 320 0)
|
||||
(particles:set_gravity 0 -80)
|
||||
(particles:set_drag 0.98)
|
||||
(particles:set_turbulence 60)
|
||||
(particles:set_spawn_rate 200)
|
||||
(particles:set_colors 1 9)
|
||||
(particles:set_life 2.0 5.0)
|
||||
(particles:set_velocity -30 30 -120 -60)
|
||||
(set fire-init? true))
|
||||
(pxl8.particles_render particles)))
|
||||
(particles:render)))
|
||||
|
||||
:rain (do
|
||||
(pxl8.clear 0)
|
||||
(when particles
|
||||
(when (not rain-init?)
|
||||
(pxl8.particles_clear particles)
|
||||
(pxl8.vfx_rain particles 320 10.0)
|
||||
(particles:clear)
|
||||
(particles:set_position 320 -10)
|
||||
(particles:set_spread 340 0)
|
||||
(particles:set_gravity 30 80)
|
||||
(particles:set_drag 1.0)
|
||||
(particles:set_turbulence 5)
|
||||
(particles:set_spawn_rate 500)
|
||||
(particles:set_colors 20 22)
|
||||
(particles:set_life 2.0 3.0)
|
||||
(particles:set_velocity -20 20 400 600)
|
||||
(set rain-init? true))
|
||||
(pxl8.particles_render particles)))
|
||||
(particles:render)))
|
||||
|
||||
:snow (do
|
||||
(pxl8.clear 0)
|
||||
(when particles
|
||||
(when (not snow-init?)
|
||||
(pxl8.particles_clear particles)
|
||||
(pxl8.vfx_snow particles 320 5.0)
|
||||
(particles:clear)
|
||||
(particles:set_position 320 -10)
|
||||
(particles:set_spread 340 0)
|
||||
(particles:set_gravity 0 25)
|
||||
(particles:set_drag 1.0)
|
||||
(particles:set_turbulence 100)
|
||||
(particles:set_spawn_rate 100)
|
||||
(particles:set_life 8.0 12.0)
|
||||
(particles:set_colors 10 15)
|
||||
(particles:set_velocity -50 50 30 50)
|
||||
(set snow-init? true))
|
||||
(pxl8.particles_render particles)))
|
||||
(particles:render)))
|
||||
|
||||
:worldgen (worldgen.frame)
|
||||
|
||||
_ (pxl8.clear 0))
|
||||
|
||||
(when transition
|
||||
(pxl8.transition_render transition))
|
||||
(transition:render))
|
||||
|
||||
(when (menu.is-paused)
|
||||
(menu.draw))))
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
(local pxl8 (require :pxl8))
|
||||
|
||||
(var paused false)
|
||||
(var gui nil)
|
||||
|
||||
(fn init []
|
||||
(set gui (pxl8.create_gui)))
|
||||
|
||||
(fn show []
|
||||
(set paused true)
|
||||
|
|
@ -12,36 +16,39 @@
|
|||
(pxl8.set_relative_mouse_mode true))
|
||||
|
||||
(fn toggle []
|
||||
(when (not gui) (init))
|
||||
(if paused
|
||||
(hide)
|
||||
(show)))
|
||||
|
||||
(fn update []
|
||||
(let [(mx my) (pxl8.get_mouse_pos)]
|
||||
(pxl8.gui_cursor_move mx my))
|
||||
(when gui
|
||||
(let [(mx my) (pxl8.get_mouse_pos)]
|
||||
(gui:cursor_move mx my))
|
||||
|
||||
(when (pxl8.mouse_pressed 1)
|
||||
(pxl8.gui_cursor_down))
|
||||
(when (pxl8.mouse_pressed 1)
|
||||
(gui:cursor_down))
|
||||
|
||||
(when (pxl8.mouse_released 1)
|
||||
(pxl8.gui_cursor_up)))
|
||||
(when (pxl8.mouse_released 1)
|
||||
(gui:cursor_up))))
|
||||
|
||||
(fn draw []
|
||||
(pxl8.gui_begin_frame)
|
||||
(when gui
|
||||
(gui:begin_frame)
|
||||
|
||||
(pxl8.gui_window 200 100 240 140 "pxl8 demo")
|
||||
(pxl8.gui_window 200 100 240 140 "pxl8 demo")
|
||||
|
||||
(when (pxl8.gui_button 1 215 145 210 32 "Resume")
|
||||
(hide))
|
||||
(when (gui:button 1 215 145 210 32 "Resume")
|
||||
(hide))
|
||||
|
||||
(when (pxl8.gui_button 2 215 185 210 32 "Quit")
|
||||
(pxl8.quit))
|
||||
(when (gui:button 2 215 185 210 32 "Quit")
|
||||
(pxl8.quit))
|
||||
|
||||
(if (pxl8.gui_is_hovering)
|
||||
(pxl8.set_cursor :hand)
|
||||
(pxl8.set_cursor :arrow))
|
||||
(if (gui:is_hovering)
|
||||
(pxl8.set_cursor :hand)
|
||||
(pxl8.set_cursor :arrow))
|
||||
|
||||
(pxl8.gui_end_frame))
|
||||
(gui:end_frame)))
|
||||
|
||||
{:is-paused (fn [] paused)
|
||||
:toggle toggle
|
||||
|
|
|
|||
|
|
@ -64,16 +64,16 @@
|
|||
(local step-duration sixteenth)
|
||||
|
||||
(fn init []
|
||||
(set ctx (pxl8.sfx_context_create))
|
||||
(set ctx (pxl8.create_sfx_context))
|
||||
|
||||
(local reverb (pxl8.sfx_reverb_create
|
||||
(local reverb (pxl8.create_reverb
|
||||
{:room 0.5 :damping 0.4 :mix 0.35}))
|
||||
(local compressor (pxl8.sfx_compressor_create
|
||||
(local compressor (pxl8.create_compressor
|
||||
{:threshold -18 :ratio 6 :attack 3 :release 100}))
|
||||
(pxl8.sfx_context_append_node ctx reverb)
|
||||
(pxl8.sfx_context_append_node ctx compressor)
|
||||
(ctx:append_node reverb)
|
||||
(ctx:append_node compressor)
|
||||
|
||||
(pxl8.sfx_mixer_attach ctx)
|
||||
(ctx:attach)
|
||||
|
||||
(set melody-params (pxl8.sfx_voice_params
|
||||
{:waveform pxl8.SFX_WAVE_TRIANGLE
|
||||
|
|
@ -96,7 +96,7 @@
|
|||
|
||||
(fn stop []
|
||||
(set playing false)
|
||||
(pxl8.sfx_stop_all ctx))
|
||||
(ctx:stop_all))
|
||||
|
||||
(fn update [dt]
|
||||
(when playing
|
||||
|
|
@ -109,7 +109,7 @@
|
|||
(local melody-note (. melody-entry 1))
|
||||
(local melody-dur (. melody-entry 2))
|
||||
(when (> melody-note 0)
|
||||
(pxl8.sfx_play_note ctx melody-note melody-params 0.45 melody-dur))
|
||||
(ctx:play_note melody-note melody-params 0.45 melody-dur))
|
||||
|
||||
(local bass-step (math.floor (/ step 2)))
|
||||
(local bass-idx (+ 1 (% bass-step (length bass))))
|
||||
|
|
@ -117,7 +117,7 @@
|
|||
(local bass-note (. bass-entry 1))
|
||||
(local bass-dur (. bass-entry 2))
|
||||
(when (= (% step 2) 0)
|
||||
(pxl8.sfx_play_note ctx bass-note bass-params 0.55 bass-dur))
|
||||
(ctx:play_note bass-note bass-params 0.55 bass-dur))
|
||||
|
||||
(set step (+ step 1)))))
|
||||
|
||||
|
|
|
|||
|
|
@ -1,41 +1,41 @@
|
|||
(local pxl8 (require :pxl8))
|
||||
|
||||
(var world nil)
|
||||
(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)
|
||||
(local cam-smoothing 0.25)
|
||||
|
||||
(local bob-amount 4.0)
|
||||
(local bob-speed 8.0)
|
||||
(var bob-time 0)
|
||||
(local cell-size 64)
|
||||
(local cursor-sensitivity 0.008)
|
||||
(local gravity -800)
|
||||
(local grid-size 64)
|
||||
(var grounded? true)
|
||||
(local ground-y 64)
|
||||
(local jump-force 175)
|
||||
(local land-recovery-speed 20)
|
||||
(local land-squash-amount -4)
|
||||
(var land-squash 0)
|
||||
(local max-pitch 1.5)
|
||||
(local move-speed 200)
|
||||
(local turn-speed 2.0)
|
||||
(var velocity-y 0)
|
||||
(var world nil)
|
||||
|
||||
(fn init []
|
||||
(set world (pxl8.world_new))
|
||||
(let [result (pxl8.world_generate world {
|
||||
(set camera (pxl8.create_camera_3d))
|
||||
(set world (pxl8.create_world))
|
||||
(let [result (world:generate {
|
||||
:type pxl8.PROCGEN_ROOMS
|
||||
:width 64
|
||||
:height 64
|
||||
|
|
@ -61,7 +61,7 @@
|
|||
:height 64
|
||||
:base_color 4})]
|
||||
|
||||
(let [result (pxl8.world_apply_textures world [
|
||||
(let [result (world:apply_textures [
|
||||
{:name "floor"
|
||||
:texture_id floor-tex
|
||||
:rule (fn [normal] (> normal.y 0.7))}
|
||||
|
|
@ -86,7 +86,7 @@
|
|||
(when (and auto-run-cancel-key (not (pxl8.key_down auto-run-cancel-key)))
|
||||
(set auto-run-cancel-key nil))
|
||||
|
||||
(when (pxl8.world_is_loaded world)
|
||||
(when (world:is_loaded)
|
||||
(let [forward-x (- (math.sin cam-yaw))
|
||||
forward-z (- (math.cos cam-yaw))
|
||||
right-x (math.cos cam-yaw)
|
||||
|
|
@ -124,7 +124,7 @@
|
|||
|
||||
(when (and (>= new-x 0) (<= new-x grid-max)
|
||||
(>= new-z 0) (<= new-z grid-max))
|
||||
(let [(resolved-x resolved-y resolved-z) (pxl8.world_resolve_collision world cam-x cam-y cam-z new-x cam-y new-z 5)]
|
||||
(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)))
|
||||
|
||||
|
|
@ -176,31 +176,36 @@
|
|||
(fn frame []
|
||||
(pxl8.clear 0)
|
||||
|
||||
(when (pxl8.world_is_loaded world)
|
||||
(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)]
|
||||
target-z (+ smooth-cam-z forward-z)
|
||||
aspect (/ (pxl8.get_width) (pxl8.get_height))]
|
||||
|
||||
(pxl8.clear_zbuffer)
|
||||
(pxl8.set_backface_culling true)
|
||||
(pxl8.set_wireframe false)
|
||||
(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 [aspect (/ (pxl8.get_width) (pxl8.get_height))
|
||||
fov 1.047]
|
||||
(pxl8.set_projection (pxl8.mat4_perspective fov aspect 1.0 4096.0)))
|
||||
(pxl8.begin_frame_3d camera)
|
||||
(pxl8.clear_depth)
|
||||
|
||||
(pxl8.set_view (pxl8.mat4_lookat
|
||||
[smooth-cam-x eye-y smooth-cam-z]
|
||||
[target-x target-y target-z]
|
||||
[0 1 0]))
|
||||
(world:render [smooth-cam-x eye-y smooth-cam-z])
|
||||
|
||||
(pxl8.set_model (pxl8.mat4_identity))
|
||||
|
||||
(pxl8.world_render world [smooth-cam-x eye-y smooth-cam-z])
|
||||
(pxl8.end_frame_3d)
|
||||
|
||||
(let [cx (/ (pxl8.get_width) 2)
|
||||
cy (/ (pxl8.get_height) 2)
|
||||
|
|
|
|||
68
pxl8.sh
68
pxl8.sh
|
|
@ -320,7 +320,7 @@ case "$COMMAND" in
|
|||
print_info "Compiler cache: ccache enabled"
|
||||
fi
|
||||
|
||||
INCLUDES="-Isrc -Ilib -Ilib/luajit/src -Ilib/linenoise -Ilib/miniz"
|
||||
INCLUDES="-Iclient/src/core -Iclient/src/math -Iclient/src/gfx -Iclient/src/sfx -Iclient/src/script -Iclient/src/hal -Iclient/src/world -Iclient/src/asset -Iclient/src/game -Ilib -Ilib/luajit/src -Ilib/linenoise -Ilib/miniz"
|
||||
COMPILE_FLAGS="$CFLAGS $INCLUDES"
|
||||
DEP_COMPILE_FLAGS="$DEP_CFLAGS $INCLUDES"
|
||||
|
||||
|
|
@ -329,32 +329,38 @@ case "$COMMAND" in
|
|||
LIB_SOURCE_FILES="lib/linenoise/linenoise.c lib/miniz/miniz.c"
|
||||
|
||||
PXL8_SOURCE_FILES="
|
||||
src/pxl8.c
|
||||
src/pxl8_anim.c
|
||||
src/pxl8_ase.c
|
||||
src/pxl8_atlas.c
|
||||
src/pxl8_blit.c
|
||||
src/pxl8_bsp.c
|
||||
src/pxl8_cart.c
|
||||
src/pxl8_font.c
|
||||
src/pxl8_gen.c
|
||||
src/pxl8_gfx.c
|
||||
src/pxl8_gui.c
|
||||
src/pxl8_io.c
|
||||
src/pxl8_log.c
|
||||
src/pxl8_math.c
|
||||
src/pxl8_repl.c
|
||||
src/pxl8_replay.c
|
||||
src/pxl8_rng.c
|
||||
src/pxl8_save.c
|
||||
src/pxl8_script.c
|
||||
src/pxl8_sdl3.c
|
||||
src/pxl8_sfx.c
|
||||
src/pxl8_tilemap.c
|
||||
src/pxl8_tilesheet.c
|
||||
src/pxl8_transition.c
|
||||
src/pxl8_vfx.c
|
||||
src/pxl8_world.c
|
||||
client/src/core/pxl8.c
|
||||
client/src/core/pxl8_io.c
|
||||
client/src/core/pxl8_log.c
|
||||
client/src/core/pxl8_rng.c
|
||||
client/src/math/pxl8_math.c
|
||||
client/src/gfx/pxl8_anim.c
|
||||
client/src/gfx/pxl8_atlas.c
|
||||
client/src/gfx/pxl8_blit.c
|
||||
client/src/gfx/pxl8_3d_camera.c
|
||||
client/src/gfx/pxl8_colormap.c
|
||||
client/src/gfx/pxl8_cpu.c
|
||||
client/src/gfx/pxl8_dither.c
|
||||
client/src/gfx/pxl8_font.c
|
||||
client/src/gfx/pxl8_gfx.c
|
||||
client/src/gfx/pxl8_mesh.c
|
||||
client/src/gfx/pxl8_palette.c
|
||||
client/src/gfx/pxl8_particles.c
|
||||
client/src/gfx/pxl8_tilemap.c
|
||||
client/src/gfx/pxl8_tilesheet.c
|
||||
client/src/gfx/pxl8_transition.c
|
||||
client/src/sfx/pxl8_sfx.c
|
||||
client/src/script/pxl8_repl.c
|
||||
client/src/script/pxl8_script.c
|
||||
client/src/hal/pxl8_sdl3.c
|
||||
client/src/world/pxl8_bsp.c
|
||||
client/src/world/pxl8_gen.c
|
||||
client/src/world/pxl8_world.c
|
||||
client/src/asset/pxl8_ase.c
|
||||
client/src/asset/pxl8_cart.c
|
||||
client/src/asset/pxl8_save.c
|
||||
client/src/game/pxl8_gui.c
|
||||
client/src/game/pxl8_replay.c
|
||||
"
|
||||
|
||||
LUAJIT_LIB="lib/luajit/src/libluajit.a"
|
||||
|
|
@ -384,13 +390,13 @@ case "$COMMAND" in
|
|||
|
||||
NEEDS_REBUILD=false
|
||||
if [[ "$src_file" -nt "$obj_file" ]] || \
|
||||
[[ "src/pxl8_types.h" -nt "$obj_file" ]] || \
|
||||
[[ "src/pxl8_macros.h" -nt "$obj_file" ]]; then
|
||||
[[ "client/src/core/pxl8_types.h" -nt "$obj_file" ]] || \
|
||||
[[ "client/src/core/pxl8_macros.h" -nt "$obj_file" ]]; then
|
||||
NEEDS_REBUILD=true
|
||||
fi
|
||||
|
||||
if [[ "$src_file" == "src/pxl8_script.c" ]]; then
|
||||
for lua_file in src/lua/*.lua src/lua/pxl8/*.lua lib/fennel/fennel.lua; do
|
||||
if [[ "$src_file" == "client/src/script/pxl8_script.c" ]]; then
|
||||
for lua_file in client/src/lua/*.lua client/src/lua/pxl8/*.lua lib/fennel/fennel.lua; do
|
||||
if [[ -f "$lua_file" ]] && [[ "$lua_file" -nt "$obj_file" ]]; then
|
||||
NEEDS_REBUILD=true
|
||||
break
|
||||
|
|
|
|||
248
src/lua/pxl8.lua
248
src/lua/pxl8.lua
|
|
@ -1,248 +0,0 @@
|
|||
local core = require("pxl8.core")
|
||||
local gfx2d = require("pxl8.gfx2d")
|
||||
local input = require("pxl8.input")
|
||||
local vfx = require("pxl8.vfx")
|
||||
local particles = require("pxl8.particles")
|
||||
local tilemap = require("pxl8.tilemap")
|
||||
local gfx3d = require("pxl8.gfx3d")
|
||||
local math3d = require("pxl8.math")
|
||||
local gui = require("pxl8.gui")
|
||||
local world = require("pxl8.world")
|
||||
local transition = require("pxl8.transition")
|
||||
local anim = require("pxl8.anim")
|
||||
local sfx = require("pxl8.sfx")
|
||||
local pxl8 = {}
|
||||
|
||||
core.init(pxl8_gfx, pxl8_input, pxl8_rng, pxl8_sfx, pxl8_sys)
|
||||
|
||||
pxl8.get_fps = core.get_fps
|
||||
pxl8.get_height = core.get_height
|
||||
pxl8.get_title = core.get_title
|
||||
pxl8.get_width = core.get_width
|
||||
pxl8.info = core.info
|
||||
pxl8.warn = core.warn
|
||||
pxl8.error = core.error
|
||||
pxl8.debug = core.debug
|
||||
pxl8.trace = core.trace
|
||||
pxl8.quit = core.quit
|
||||
|
||||
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.clear = gfx2d.clear
|
||||
pxl8.pixel = gfx2d.pixel
|
||||
pxl8.line = gfx2d.line
|
||||
pxl8.rect = gfx2d.rect
|
||||
pxl8.rect_fill = gfx2d.rect_fill
|
||||
pxl8.circle = gfx2d.circle
|
||||
pxl8.circle_fill = gfx2d.circle_fill
|
||||
pxl8.text = gfx2d.text
|
||||
pxl8.sprite = gfx2d.sprite
|
||||
pxl8.load_palette = gfx2d.load_palette
|
||||
pxl8.load_sprite = gfx2d.load_sprite
|
||||
pxl8.create_texture = gfx2d.create_texture
|
||||
pxl8.gfx_color_ramp = gfx2d.color_ramp
|
||||
pxl8.gfx_fade_palette = gfx2d.fade_palette
|
||||
pxl8.gfx_cycle_palette = gfx2d.cycle_palette
|
||||
pxl8.add_palette_cycle = gfx2d.add_palette_cycle
|
||||
pxl8.remove_palette_cycle = gfx2d.remove_palette_cycle
|
||||
pxl8.set_palette_cycle_speed = gfx2d.set_palette_cycle_speed
|
||||
pxl8.clear_palette_cycles = gfx2d.clear_palette_cycles
|
||||
|
||||
pxl8.key_down = input.key_down
|
||||
pxl8.key_pressed = input.key_pressed
|
||||
pxl8.key_released = input.key_released
|
||||
pxl8.mouse_dx = input.mouse_dx
|
||||
pxl8.mouse_dy = input.mouse_dy
|
||||
pxl8.mouse_wheel_x = input.mouse_wheel_x
|
||||
pxl8.mouse_wheel_y = input.mouse_wheel_y
|
||||
pxl8.mouse_x = input.mouse_x
|
||||
pxl8.mouse_y = input.mouse_y
|
||||
pxl8.get_mouse_pos = input.get_mouse_pos
|
||||
pxl8.mouse_pressed = input.mouse_pressed
|
||||
pxl8.mouse_released = input.mouse_released
|
||||
pxl8.center_cursor = input.center_cursor
|
||||
pxl8.set_cursor = input.set_cursor
|
||||
pxl8.set_relative_mouse_mode = input.set_relative_mouse_mode
|
||||
|
||||
pxl8.vfx_raster_bars = vfx.raster_bars
|
||||
pxl8.vfx_plasma = vfx.plasma
|
||||
pxl8.vfx_rotozoom = vfx.rotozoom
|
||||
pxl8.vfx_tunnel = vfx.tunnel
|
||||
pxl8.vfx_explosion = vfx.explosion
|
||||
pxl8.vfx_fire = vfx.fire
|
||||
pxl8.vfx_rain = vfx.rain
|
||||
pxl8.vfx_smoke = vfx.smoke
|
||||
pxl8.vfx_snow = vfx.snow
|
||||
pxl8.vfx_sparks = vfx.sparks
|
||||
pxl8.vfx_starfield = vfx.starfield
|
||||
|
||||
pxl8.particles_new = particles.new
|
||||
pxl8.particles_destroy = particles.destroy
|
||||
pxl8.particles_clear = particles.clear
|
||||
pxl8.particles_emit = particles.emit
|
||||
pxl8.particles_update = particles.update
|
||||
pxl8.particles_render = particles.render
|
||||
|
||||
pxl8.tilesheet_new = tilemap.tilesheet_new
|
||||
pxl8.tilesheet_destroy = tilemap.tilesheet_destroy
|
||||
pxl8.tilesheet_load = tilemap.tilesheet_load
|
||||
pxl8.tilemap_new = tilemap.new
|
||||
pxl8.tilemap_destroy = tilemap.destroy
|
||||
pxl8.tilemap_set_tilesheet = tilemap.set_tilesheet
|
||||
pxl8.tilemap_set_tile = tilemap.set_tile
|
||||
pxl8.tilemap_get_tile_id = tilemap.get_tile_id
|
||||
pxl8.tilemap_set_camera = tilemap.set_camera
|
||||
pxl8.tilemap_render = tilemap.render
|
||||
pxl8.tilemap_render_layer = tilemap.render_layer
|
||||
pxl8.tilemap_is_solid = tilemap.is_solid
|
||||
pxl8.tilemap_check_collision = tilemap.check_collision
|
||||
pxl8.tilemap_get_tile_data = tilemap.get_tile_data
|
||||
pxl8.tilemap_load_ase = tilemap.load_ase
|
||||
pxl8.tilemap_set_tile_data = tilemap.set_tile_data
|
||||
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.clear_zbuffer = gfx3d.clear_zbuffer
|
||||
pxl8.set_model = gfx3d.set_model
|
||||
pxl8.set_view = gfx3d.set_view
|
||||
pxl8.set_projection = gfx3d.set_projection
|
||||
pxl8.set_wireframe = gfx3d.set_wireframe
|
||||
pxl8.set_affine_textures = gfx3d.set_affine_textures
|
||||
pxl8.set_backface_culling = gfx3d.set_backface_culling
|
||||
pxl8.draw_triangle_3d = gfx3d.draw_triangle
|
||||
pxl8.draw_triangle_3d_textured = gfx3d.draw_triangle_textured
|
||||
pxl8.draw_line_3d = gfx3d.draw_line
|
||||
|
||||
pxl8.mat4_identity = math3d.mat4_identity
|
||||
pxl8.mat4_multiply = math3d.mat4_multiply
|
||||
pxl8.mat4_translate = math3d.mat4_translate
|
||||
pxl8.mat4_rotate_x = math3d.mat4_rotate_x
|
||||
pxl8.mat4_rotate_y = math3d.mat4_rotate_y
|
||||
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_state_create = gui.state_create
|
||||
pxl8.gui_state_destroy = gui.state_destroy
|
||||
pxl8.gui_begin_frame = gui.begin_frame
|
||||
pxl8.gui_end_frame = gui.end_frame
|
||||
pxl8.gui_cursor_move = gui.cursor_move
|
||||
pxl8.gui_cursor_down = gui.cursor_down
|
||||
pxl8.gui_cursor_up = gui.cursor_up
|
||||
pxl8.gui_button = gui.button
|
||||
pxl8.gui_window = gui.window
|
||||
pxl8.gui_label = gui.label
|
||||
pxl8.gui_is_hovering = gui.is_hovering
|
||||
pxl8.gui_get_cursor_pos = gui.get_cursor_pos
|
||||
|
||||
pxl8.world_new = world.new
|
||||
pxl8.world_destroy = world.destroy
|
||||
pxl8.world_load = world.load
|
||||
pxl8.world_render = world.render
|
||||
pxl8.world_unload = world.unload
|
||||
pxl8.world_is_loaded = world.is_loaded
|
||||
pxl8.world_generate = world.generate
|
||||
pxl8.world_apply_textures = world.apply_textures
|
||||
pxl8.world_check_collision = world.check_collision
|
||||
pxl8.world_resolve_collision = world.resolve_collision
|
||||
pxl8.procgen_tex = world.procgen_tex
|
||||
pxl8.PROCGEN_ROOMS = world.PROCGEN_ROOMS
|
||||
pxl8.PROCGEN_TERRAIN = world.PROCGEN_TERRAIN
|
||||
|
||||
pxl8.transition_create = transition.create
|
||||
pxl8.transition_destroy = transition.destroy
|
||||
pxl8.transition_get_progress = transition.get_progress
|
||||
pxl8.transition_is_active = transition.is_active
|
||||
pxl8.transition_is_complete = transition.is_complete
|
||||
pxl8.transition_render = transition.render
|
||||
pxl8.transition_reset = transition.reset
|
||||
pxl8.transition_set_color = transition.set_color
|
||||
pxl8.transition_set_reverse = transition.set_reverse
|
||||
pxl8.transition_start = transition.start
|
||||
pxl8.transition_stop = transition.stop
|
||||
pxl8.transition_update = transition.update
|
||||
|
||||
pxl8.anim_create = anim.create
|
||||
pxl8.anim_create_from_ase = anim.create_from_ase
|
||||
pxl8.anim_destroy = anim.destroy
|
||||
pxl8.anim_add_state = anim.add_state
|
||||
pxl8.anim_get_current_frame = anim.get_current_frame
|
||||
pxl8.anim_get_current_frame_id = anim.get_current_frame_id
|
||||
pxl8.anim_get_state = anim.get_state
|
||||
pxl8.anim_has_state_machine = anim.has_state_machine
|
||||
pxl8.anim_is_complete = anim.is_complete
|
||||
pxl8.anim_is_playing = anim.is_playing
|
||||
pxl8.anim_pause = anim.pause
|
||||
pxl8.anim_play = anim.play
|
||||
pxl8.anim_render_sprite = anim.render_sprite
|
||||
pxl8.anim_reset = anim.reset
|
||||
pxl8.anim_set_frame = anim.set_frame
|
||||
pxl8.anim_set_loop = anim.set_loop
|
||||
pxl8.anim_set_reverse = anim.set_reverse
|
||||
pxl8.anim_set_speed = anim.set_speed
|
||||
pxl8.anim_set_state = anim.set_state
|
||||
pxl8.anim_stop = anim.stop
|
||||
pxl8.anim_update = anim.update
|
||||
|
||||
pxl8.sfx_compressor_create = sfx.compressor_create
|
||||
pxl8.sfx_compressor_set_attack = sfx.compressor_set_attack
|
||||
pxl8.sfx_compressor_set_ratio = sfx.compressor_set_ratio
|
||||
pxl8.sfx_compressor_set_release = sfx.compressor_set_release
|
||||
pxl8.sfx_compressor_set_threshold = sfx.compressor_set_threshold
|
||||
pxl8.sfx_context_append_node = sfx.context_append_node
|
||||
pxl8.sfx_context_create = sfx.context_create
|
||||
pxl8.sfx_context_destroy = sfx.context_destroy
|
||||
pxl8.sfx_context_get_head = sfx.context_get_head
|
||||
pxl8.sfx_context_get_volume = sfx.context_get_volume
|
||||
pxl8.sfx_context_insert_node = sfx.context_insert_node
|
||||
pxl8.sfx_context_remove_node = sfx.context_remove_node
|
||||
pxl8.sfx_context_set_volume = sfx.context_set_volume
|
||||
pxl8.sfx_delay_create = sfx.delay_create
|
||||
pxl8.sfx_delay_set_feedback = sfx.delay_set_feedback
|
||||
pxl8.sfx_delay_set_mix = sfx.delay_set_mix
|
||||
pxl8.sfx_delay_set_time = sfx.delay_set_time
|
||||
pxl8.sfx_get_master_volume = sfx.get_master_volume
|
||||
pxl8.sfx_mixer_attach = sfx.mixer_attach
|
||||
pxl8.sfx_mixer_detach = sfx.mixer_detach
|
||||
pxl8.sfx_node_destroy = sfx.node_destroy
|
||||
pxl8.sfx_note_to_freq = sfx.note_to_freq
|
||||
pxl8.sfx_play_note = sfx.play_note
|
||||
pxl8.sfx_release_voice = sfx.release_voice
|
||||
pxl8.sfx_reverb_create = sfx.reverb_create
|
||||
pxl8.sfx_reverb_set_damping = sfx.reverb_set_damping
|
||||
pxl8.sfx_reverb_set_mix = sfx.reverb_set_mix
|
||||
pxl8.sfx_reverb_set_room = sfx.reverb_set_room
|
||||
pxl8.sfx_set_master_volume = sfx.set_master_volume
|
||||
pxl8.sfx_stop_all = sfx.stop_all
|
||||
pxl8.sfx_stop_voice = sfx.stop_voice
|
||||
pxl8.sfx_voice_params = sfx.voice_params
|
||||
|
||||
pxl8.SFX_FILTER_BANDPASS = sfx.FILTER_BANDPASS
|
||||
pxl8.SFX_FILTER_HIGHPASS = sfx.FILTER_HIGHPASS
|
||||
pxl8.SFX_FILTER_LOWPASS = sfx.FILTER_LOWPASS
|
||||
pxl8.SFX_FILTER_NONE = sfx.FILTER_NONE
|
||||
|
||||
pxl8.SFX_LFO_AMPLITUDE = sfx.LFO_AMPLITUDE
|
||||
pxl8.SFX_LFO_FILTER = sfx.LFO_FILTER
|
||||
pxl8.SFX_LFO_PITCH = sfx.LFO_PITCH
|
||||
|
||||
pxl8.SFX_NODE_COMPRESSOR = sfx.NODE_COMPRESSOR
|
||||
pxl8.SFX_NODE_DELAY = sfx.NODE_DELAY
|
||||
pxl8.SFX_NODE_REVERB = sfx.NODE_REVERB
|
||||
|
||||
pxl8.SFX_WAVE_NOISE = sfx.WAVE_NOISE
|
||||
pxl8.SFX_WAVE_PULSE = sfx.WAVE_PULSE
|
||||
pxl8.SFX_WAVE_SAW = sfx.WAVE_SAW
|
||||
pxl8.SFX_WAVE_SINE = sfx.WAVE_SINE
|
||||
pxl8.SFX_WAVE_SQUARE = sfx.WAVE_SQUARE
|
||||
pxl8.SFX_WAVE_TRIANGLE = sfx.WAVE_TRIANGLE
|
||||
|
||||
return pxl8
|
||||
|
|
@ -1,110 +0,0 @@
|
|||
local ffi = require("ffi")
|
||||
local C = ffi.C
|
||||
local core = require("pxl8.core")
|
||||
|
||||
local anim = {}
|
||||
|
||||
function anim.create(frame_ids, frame_durations)
|
||||
local frame_count = #frame_ids
|
||||
local c_frame_ids = ffi.new("u32[?]", frame_count)
|
||||
local c_frame_durations = nil
|
||||
|
||||
for i = 1, frame_count do
|
||||
c_frame_ids[i-1] = frame_ids[i]
|
||||
end
|
||||
|
||||
if frame_durations then
|
||||
c_frame_durations = ffi.new("u16[?]", frame_count)
|
||||
for i = 1, frame_count do
|
||||
c_frame_durations[i-1] = frame_durations[i]
|
||||
end
|
||||
end
|
||||
|
||||
return C.pxl8_anim_create(c_frame_ids, c_frame_durations, frame_count)
|
||||
end
|
||||
|
||||
function anim.create_from_ase(filepath)
|
||||
return C.pxl8_anim_create_from_ase(core.gfx, filepath)
|
||||
end
|
||||
|
||||
function anim.destroy(a)
|
||||
C.pxl8_anim_destroy(a)
|
||||
end
|
||||
|
||||
function anim.add_state(a, name, state_anim)
|
||||
return C.pxl8_anim_add_state(a, name, state_anim)
|
||||
end
|
||||
|
||||
function anim.get_current_frame(a)
|
||||
return C.pxl8_anim_get_current_frame(a)
|
||||
end
|
||||
|
||||
function anim.get_current_frame_id(a)
|
||||
return C.pxl8_anim_get_current_frame_id(a)
|
||||
end
|
||||
|
||||
function anim.get_state(a)
|
||||
local state_name = C.pxl8_anim_get_state(a)
|
||||
if state_name ~= nil then
|
||||
return ffi.string(state_name)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function anim.has_state_machine(a)
|
||||
return C.pxl8_anim_has_state_machine(a)
|
||||
end
|
||||
|
||||
function anim.is_complete(a)
|
||||
return C.pxl8_anim_is_complete(a)
|
||||
end
|
||||
|
||||
function anim.is_playing(a)
|
||||
return C.pxl8_anim_is_playing(a)
|
||||
end
|
||||
|
||||
function anim.pause(a)
|
||||
C.pxl8_anim_pause(a)
|
||||
end
|
||||
|
||||
function anim.play(a)
|
||||
C.pxl8_anim_play(a)
|
||||
end
|
||||
|
||||
function anim.render_sprite(a, x, y, w, h, flip_x, flip_y)
|
||||
C.pxl8_anim_render_sprite(a, core.gfx, x, y, w, h, flip_x or false, flip_y or false)
|
||||
end
|
||||
|
||||
function anim.reset(a)
|
||||
C.pxl8_anim_reset(a)
|
||||
end
|
||||
|
||||
function anim.set_frame(a, frame)
|
||||
C.pxl8_anim_set_frame(a, frame)
|
||||
end
|
||||
|
||||
function anim.set_loop(a, loop)
|
||||
C.pxl8_anim_set_loop(a, loop)
|
||||
end
|
||||
|
||||
function anim.set_reverse(a, reverse)
|
||||
C.pxl8_anim_set_reverse(a, reverse)
|
||||
end
|
||||
|
||||
function anim.set_speed(a, speed)
|
||||
C.pxl8_anim_set_speed(a, speed)
|
||||
end
|
||||
|
||||
function anim.set_state(a, name)
|
||||
return C.pxl8_anim_set_state(a, name)
|
||||
end
|
||||
|
||||
function anim.stop(a)
|
||||
C.pxl8_anim_stop(a)
|
||||
end
|
||||
|
||||
function anim.update(a, dt)
|
||||
C.pxl8_anim_update(a, dt)
|
||||
end
|
||||
|
||||
return anim
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
local ffi = require("ffi")
|
||||
local C = ffi.C
|
||||
local core = require("pxl8.core")
|
||||
|
||||
local gfx3d = {}
|
||||
|
||||
function gfx3d.clear_zbuffer()
|
||||
C.pxl8_3d_clear_zbuffer(core.gfx)
|
||||
end
|
||||
|
||||
function gfx3d.set_model(mat)
|
||||
C.pxl8_3d_set_model(core.gfx, mat)
|
||||
end
|
||||
|
||||
function gfx3d.set_view(mat)
|
||||
C.pxl8_3d_set_view(core.gfx, mat)
|
||||
end
|
||||
|
||||
function gfx3d.set_projection(mat)
|
||||
C.pxl8_3d_set_projection(core.gfx, mat)
|
||||
end
|
||||
|
||||
function gfx3d.set_wireframe(wireframe)
|
||||
C.pxl8_3d_set_wireframe(core.gfx, wireframe)
|
||||
end
|
||||
|
||||
function gfx3d.set_affine_textures(affine)
|
||||
C.pxl8_3d_set_affine_textures(core.gfx, affine)
|
||||
end
|
||||
|
||||
function gfx3d.set_backface_culling(culling)
|
||||
C.pxl8_3d_set_backface_culling(core.gfx, culling)
|
||||
end
|
||||
|
||||
function gfx3d.draw_triangle(v0, v1, v2, color)
|
||||
local vec0 = ffi.new("pxl8_vec3", {x = v0[1], y = v0[2], z = v0[3]})
|
||||
local vec1 = ffi.new("pxl8_vec3", {x = v1[1], y = v1[2], z = v1[3]})
|
||||
local vec2 = ffi.new("pxl8_vec3", {x = v2[1], y = v2[2], z = v2[3]})
|
||||
C.pxl8_3d_draw_triangle_raw(core.gfx, vec0, vec1, vec2, color)
|
||||
end
|
||||
|
||||
function gfx3d.draw_triangle_textured(v0, v1, v2, uv0, uv1, uv2, texture_id)
|
||||
local vec0 = ffi.new("pxl8_vec3", {x = v0[1], y = v0[2], z = v0[3]})
|
||||
local vec1 = ffi.new("pxl8_vec3", {x = v1[1], y = v1[2], z = v1[3]})
|
||||
local vec2 = ffi.new("pxl8_vec3", {x = v2[1], y = v2[2], z = v2[3]})
|
||||
C.pxl8_3d_draw_triangle_textured(core.gfx, vec0, vec1, vec2,
|
||||
uv0[1], uv0[2], uv1[1], uv1[2], uv2[1], uv2[2], texture_id)
|
||||
end
|
||||
|
||||
function gfx3d.draw_line(p0, p1, color)
|
||||
local vec0 = ffi.new("pxl8_vec3", {x = p0[1], y = p0[2], z = p0[3]})
|
||||
local vec1 = ffi.new("pxl8_vec3", {x = p1[1], y = p1[2], z = p1[3]})
|
||||
C.pxl8_3d_draw_line_3d(core.gfx, vec0, vec1, color)
|
||||
end
|
||||
|
||||
return gfx3d
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
local ffi = require("ffi")
|
||||
local C = ffi.C
|
||||
local core = require("pxl8.core")
|
||||
|
||||
local gui = {}
|
||||
|
||||
local state = nil
|
||||
|
||||
local function gui_state()
|
||||
if not state then
|
||||
state = ffi.gc(C.pxl8_gui_state_create(), C.pxl8_gui_state_destroy)
|
||||
end
|
||||
return state
|
||||
end
|
||||
|
||||
function gui.begin_frame()
|
||||
C.pxl8_gui_begin_frame(gui_state())
|
||||
end
|
||||
|
||||
function gui.end_frame()
|
||||
C.pxl8_gui_end_frame(gui_state())
|
||||
end
|
||||
|
||||
function gui.cursor_move(x, y)
|
||||
C.pxl8_gui_cursor_move(gui_state(), x, y)
|
||||
end
|
||||
|
||||
function gui.cursor_down()
|
||||
C.pxl8_gui_cursor_down(gui_state())
|
||||
end
|
||||
|
||||
function gui.cursor_up()
|
||||
C.pxl8_gui_cursor_up(gui_state())
|
||||
end
|
||||
|
||||
function gui.button(id, x, y, w, h, label)
|
||||
return C.pxl8_gui_button(gui_state(), core.gfx, id, x, y, w, h, label)
|
||||
end
|
||||
|
||||
function gui.window(x, y, w, h, title)
|
||||
C.pxl8_gui_window(core.gfx, x, y, w, h, title)
|
||||
end
|
||||
|
||||
function gui.label(x, y, text, color)
|
||||
C.pxl8_gui_label(core.gfx, x, y, text, color)
|
||||
end
|
||||
|
||||
function gui.is_hovering()
|
||||
return C.pxl8_gui_is_hovering(gui_state())
|
||||
end
|
||||
|
||||
function gui.get_cursor_pos()
|
||||
local x = ffi.new("i32[1]")
|
||||
local y = ffi.new("i32[1]")
|
||||
C.pxl8_gui_get_cursor_pos(gui_state(), x, y)
|
||||
return x[0], y[0]
|
||||
end
|
||||
|
||||
return gui
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
local ffi = require("ffi")
|
||||
local C = ffi.C
|
||||
local core = require("pxl8.core")
|
||||
|
||||
local particles = {}
|
||||
|
||||
function particles.new(max_count)
|
||||
return C.pxl8_particles_create(max_count or 1000, core.rng)
|
||||
end
|
||||
|
||||
function particles.destroy(ps)
|
||||
C.pxl8_particles_destroy(ps)
|
||||
end
|
||||
|
||||
function particles.clear(ps)
|
||||
C.pxl8_particles_clear(ps)
|
||||
end
|
||||
|
||||
function particles.emit(ps, count)
|
||||
C.pxl8_particles_emit(ps, count or 1)
|
||||
end
|
||||
|
||||
function particles.update(ps, dt)
|
||||
C.pxl8_particles_update(ps, dt)
|
||||
end
|
||||
|
||||
function particles.render(ps)
|
||||
C.pxl8_particles_render(ps, core.gfx)
|
||||
end
|
||||
|
||||
return particles
|
||||
|
|
@ -1,198 +0,0 @@
|
|||
local ffi = require("ffi")
|
||||
local C = ffi.C
|
||||
local core = require("pxl8.core")
|
||||
|
||||
local sfx = {}
|
||||
|
||||
sfx.FILTER_BANDPASS = C.PXL8_SFX_FILTER_BANDPASS
|
||||
sfx.FILTER_HIGHPASS = C.PXL8_SFX_FILTER_HIGHPASS
|
||||
sfx.FILTER_LOWPASS = C.PXL8_SFX_FILTER_LOWPASS
|
||||
sfx.FILTER_NONE = C.PXL8_SFX_FILTER_NONE
|
||||
|
||||
sfx.LFO_AMPLITUDE = C.PXL8_SFX_LFO_AMPLITUDE
|
||||
sfx.LFO_FILTER = C.PXL8_SFX_LFO_FILTER
|
||||
sfx.LFO_PITCH = C.PXL8_SFX_LFO_PITCH
|
||||
|
||||
sfx.NODE_COMPRESSOR = C.PXL8_SFX_NODE_COMPRESSOR
|
||||
sfx.NODE_DELAY = C.PXL8_SFX_NODE_DELAY
|
||||
sfx.NODE_REVERB = C.PXL8_SFX_NODE_REVERB
|
||||
|
||||
sfx.WAVE_NOISE = C.PXL8_SFX_WAVE_NOISE
|
||||
sfx.WAVE_PULSE = C.PXL8_SFX_WAVE_PULSE
|
||||
sfx.WAVE_SAW = C.PXL8_SFX_WAVE_SAW
|
||||
sfx.WAVE_SINE = C.PXL8_SFX_WAVE_SINE
|
||||
sfx.WAVE_SQUARE = C.PXL8_SFX_WAVE_SQUARE
|
||||
sfx.WAVE_TRIANGLE = C.PXL8_SFX_WAVE_TRIANGLE
|
||||
|
||||
function sfx.compressor_create(opts)
|
||||
opts = opts or {}
|
||||
local cfg = ffi.new("pxl8_sfx_compressor_config")
|
||||
cfg.attack = opts.attack or 10
|
||||
cfg.ratio = opts.ratio or 4
|
||||
cfg.release = opts.release or 100
|
||||
cfg.threshold = opts.threshold or -12
|
||||
return C.pxl8_sfx_compressor_create(cfg)
|
||||
end
|
||||
|
||||
function sfx.compressor_set_attack(node, attack)
|
||||
C.pxl8_sfx_compressor_set_attack(node, attack)
|
||||
end
|
||||
|
||||
function sfx.compressor_set_ratio(node, ratio)
|
||||
C.pxl8_sfx_compressor_set_ratio(node, ratio)
|
||||
end
|
||||
|
||||
function sfx.compressor_set_release(node, release)
|
||||
C.pxl8_sfx_compressor_set_release(node, release)
|
||||
end
|
||||
|
||||
function sfx.compressor_set_threshold(node, threshold)
|
||||
C.pxl8_sfx_compressor_set_threshold(node, threshold)
|
||||
end
|
||||
|
||||
function sfx.context_append_node(ctx, node)
|
||||
C.pxl8_sfx_context_append_node(ctx, node)
|
||||
end
|
||||
|
||||
function sfx.context_create()
|
||||
return C.pxl8_sfx_context_create()
|
||||
end
|
||||
|
||||
function sfx.context_destroy(ctx)
|
||||
C.pxl8_sfx_context_destroy(ctx)
|
||||
end
|
||||
|
||||
function sfx.context_get_head(ctx)
|
||||
return C.pxl8_sfx_context_get_head(ctx)
|
||||
end
|
||||
|
||||
function sfx.context_get_volume(ctx)
|
||||
return C.pxl8_sfx_context_get_volume(ctx)
|
||||
end
|
||||
|
||||
function sfx.context_insert_node(ctx, after, node)
|
||||
C.pxl8_sfx_context_insert_node(ctx, after, node)
|
||||
end
|
||||
|
||||
function sfx.context_remove_node(ctx, node)
|
||||
C.pxl8_sfx_context_remove_node(ctx, node)
|
||||
end
|
||||
|
||||
function sfx.context_set_volume(ctx, volume)
|
||||
C.pxl8_sfx_context_set_volume(ctx, volume)
|
||||
end
|
||||
|
||||
function sfx.delay_create(opts)
|
||||
opts = opts or {}
|
||||
local cfg = ffi.new("pxl8_sfx_delay_config")
|
||||
cfg.feedback = opts.feedback or 0.4
|
||||
cfg.mix = opts.mix or 0.25
|
||||
cfg.time_l = opts.time_l or 350
|
||||
cfg.time_r = opts.time_r or 500
|
||||
return C.pxl8_sfx_delay_create(cfg)
|
||||
end
|
||||
|
||||
function sfx.delay_set_feedback(node, feedback)
|
||||
C.pxl8_sfx_delay_set_feedback(node, feedback)
|
||||
end
|
||||
|
||||
function sfx.delay_set_mix(node, mix)
|
||||
C.pxl8_sfx_delay_set_mix(node, mix)
|
||||
end
|
||||
|
||||
function sfx.delay_set_time(node, time_l, time_r)
|
||||
C.pxl8_sfx_delay_set_time(node, time_l, time_r)
|
||||
end
|
||||
|
||||
function sfx.get_master_volume()
|
||||
return C.pxl8_sfx_mixer_get_master_volume(core.sfx)
|
||||
end
|
||||
|
||||
function sfx.mixer_attach(ctx)
|
||||
C.pxl8_sfx_mixer_attach(core.sfx, ctx)
|
||||
end
|
||||
|
||||
function sfx.mixer_detach(ctx)
|
||||
C.pxl8_sfx_mixer_detach(core.sfx, ctx)
|
||||
end
|
||||
|
||||
function sfx.node_destroy(node)
|
||||
C.pxl8_sfx_node_destroy(node)
|
||||
end
|
||||
|
||||
function sfx.note_to_freq(note)
|
||||
return C.pxl8_sfx_note_to_freq(note)
|
||||
end
|
||||
|
||||
function sfx.play_note(ctx, note, params, volume, duration)
|
||||
return C.pxl8_sfx_play_note(ctx, note, params, volume or 0.8, duration or 0)
|
||||
end
|
||||
|
||||
function sfx.release_voice(ctx, voice_id)
|
||||
C.pxl8_sfx_release_voice(ctx, voice_id)
|
||||
end
|
||||
|
||||
function sfx.reverb_create(opts)
|
||||
opts = opts or {}
|
||||
local cfg = ffi.new("pxl8_sfx_reverb_config")
|
||||
cfg.damping = opts.damping or 0.5
|
||||
cfg.mix = opts.mix or 0.3
|
||||
cfg.room = opts.room or 0.5
|
||||
return C.pxl8_sfx_reverb_create(cfg)
|
||||
end
|
||||
|
||||
function sfx.reverb_set_damping(node, damping)
|
||||
C.pxl8_sfx_reverb_set_damping(node, damping)
|
||||
end
|
||||
|
||||
function sfx.reverb_set_mix(node, mix)
|
||||
C.pxl8_sfx_reverb_set_mix(node, mix)
|
||||
end
|
||||
|
||||
function sfx.reverb_set_room(node, room)
|
||||
C.pxl8_sfx_reverb_set_room(node, room)
|
||||
end
|
||||
|
||||
function sfx.set_master_volume(volume)
|
||||
C.pxl8_sfx_mixer_set_master_volume(core.sfx, volume)
|
||||
end
|
||||
|
||||
function sfx.stop_all(ctx)
|
||||
C.pxl8_sfx_stop_all(ctx)
|
||||
end
|
||||
|
||||
function sfx.stop_voice(ctx, voice_id)
|
||||
C.pxl8_sfx_stop_voice(ctx, voice_id)
|
||||
end
|
||||
|
||||
function sfx.voice_params(opts)
|
||||
opts = opts or {}
|
||||
local params = ffi.new("pxl8_sfx_voice_params")
|
||||
|
||||
params.amp_env.attack = opts.attack or 0.01
|
||||
params.amp_env.decay = opts.decay or 0.1
|
||||
params.amp_env.sustain = opts.sustain or 0.5
|
||||
params.amp_env.release = opts.release or 0.2
|
||||
|
||||
params.filter_env.attack = opts.filter_attack or 0.01
|
||||
params.filter_env.decay = opts.filter_decay or 0.1
|
||||
params.filter_env.sustain = opts.filter_sustain or 0.3
|
||||
params.filter_env.release = opts.filter_release or 0.1
|
||||
|
||||
params.filter_type = opts.filter_type or C.PXL8_SFX_FILTER_NONE
|
||||
params.lfo_target = opts.lfo_target or C.PXL8_SFX_LFO_PITCH
|
||||
params.lfo_waveform = opts.lfo_waveform or C.PXL8_SFX_WAVE_SINE
|
||||
params.waveform = opts.waveform or C.PXL8_SFX_WAVE_SINE
|
||||
|
||||
params.filter_cutoff = opts.filter_cutoff or 4000
|
||||
params.filter_env_depth = opts.filter_env_depth or 0
|
||||
params.filter_resonance = opts.filter_resonance or 0
|
||||
params.fx_send = opts.fx_send or 0
|
||||
params.lfo_depth = opts.lfo_depth or 0
|
||||
params.lfo_rate = opts.lfo_rate or 0
|
||||
params.pulse_width = opts.pulse_width or 0.5
|
||||
|
||||
return params
|
||||
end
|
||||
|
||||
return sfx
|
||||
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