Compare commits
2 commits
39b604b333
...
71787869e0
| Author | SHA1 | Date | |
|---|---|---|---|
| 71787869e0 | |||
| 415d424057 |
136 changed files with 13441 additions and 1914 deletions
|
|
@ -1,126 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#include "pxl8_types.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
const u8* bytes;
|
|
||||||
u32 offset;
|
|
||||||
u32 size;
|
|
||||||
bool overflow;
|
|
||||||
} pxl8_stream;
|
|
||||||
|
|
||||||
static inline pxl8_stream pxl8_stream_create(const u8* bytes, u32 size) {
|
|
||||||
return (pxl8_stream){
|
|
||||||
.bytes = bytes,
|
|
||||||
.offset = 0,
|
|
||||||
.size = size,
|
|
||||||
.overflow = false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool pxl8_stream_can_read(const pxl8_stream* stream, u32 count) {
|
|
||||||
return !stream->overflow && stream->offset + count <= stream->size;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool pxl8_stream_has_overflow(const pxl8_stream* stream) {
|
|
||||||
return stream->overflow;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void pxl8_stream_seek(pxl8_stream* stream, u32 offset) {
|
|
||||||
stream->offset = offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline u32 pxl8_stream_position(const pxl8_stream* stream) {
|
|
||||||
return stream->offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline u8 pxl8_read_u8(pxl8_stream* stream) {
|
|
||||||
if (stream->offset + 1 > stream->size) { stream->overflow = true; return 0; }
|
|
||||||
return stream->bytes[stream->offset++];
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline u16 pxl8_read_u16(pxl8_stream* stream) {
|
|
||||||
if (stream->offset + 2 > stream->size) { stream->overflow = true; return 0; }
|
|
||||||
u16 val = (u16)stream->bytes[stream->offset] | ((u16)stream->bytes[stream->offset + 1] << 8);
|
|
||||||
stream->offset += 2;
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline u32 pxl8_read_u32(pxl8_stream* stream) {
|
|
||||||
if (stream->offset + 4 > stream->size) { stream->overflow = true; return 0; }
|
|
||||||
u32 val = (u32)stream->bytes[stream->offset] |
|
|
||||||
((u32)stream->bytes[stream->offset + 1] << 8) |
|
|
||||||
((u32)stream->bytes[stream->offset + 2] << 16) |
|
|
||||||
((u32)stream->bytes[stream->offset + 3] << 24);
|
|
||||||
stream->offset += 4;
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline i16 pxl8_read_i16(pxl8_stream* stream) {
|
|
||||||
return (i16)pxl8_read_u16(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline i32 pxl8_read_i32(pxl8_stream* stream) {
|
|
||||||
return (i32)pxl8_read_u32(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline f32 pxl8_read_f32(pxl8_stream* stream) {
|
|
||||||
u32 val = pxl8_read_u32(stream);
|
|
||||||
f32 result;
|
|
||||||
memcpy(&result, &val, sizeof(f32));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void pxl8_read_bytes(pxl8_stream* stream, void* dest, u32 count) {
|
|
||||||
if (stream->offset + count > stream->size) { stream->overflow = true; return; }
|
|
||||||
for (u32 i = 0; i < count; i++) {
|
|
||||||
((u8*)dest)[i] = stream->bytes[stream->offset++];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void pxl8_skip_bytes(pxl8_stream* stream, u32 count) {
|
|
||||||
if (stream->offset + count > stream->size) { stream->overflow = true; return; }
|
|
||||||
stream->offset += count;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline const u8* pxl8_read_ptr(pxl8_stream* stream, u32 count) {
|
|
||||||
if (stream->offset + count > stream->size) { stream->overflow = true; return NULL; }
|
|
||||||
const u8* ptr = &stream->bytes[stream->offset];
|
|
||||||
stream->offset += count;
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
pxl8_result pxl8_io_create_directory(const char* path);
|
|
||||||
bool pxl8_io_file_exists(const char* path);
|
|
||||||
void pxl8_io_free_binary_data(u8* data);
|
|
||||||
void pxl8_io_free_file_content(char* content);
|
|
||||||
f64 pxl8_io_get_file_modified_time(const char* path);
|
|
||||||
pxl8_result pxl8_io_read_binary_file(const char* path, u8** data, size_t* size);
|
|
||||||
pxl8_result pxl8_io_read_file(const char* path, char** content, size_t* size);
|
|
||||||
pxl8_result pxl8_io_write_binary_file(const char* path, const u8* data, size_t size);
|
|
||||||
pxl8_result pxl8_io_write_file(const char* path, const char* content, size_t size);
|
|
||||||
|
|
||||||
bool pxl8_key_down(const pxl8_input_state* input, const char* key_name);
|
|
||||||
bool pxl8_key_pressed(const pxl8_input_state* input, const char* key_name);
|
|
||||||
bool pxl8_key_released(const pxl8_input_state* input, const char* key_name);
|
|
||||||
|
|
||||||
i32 pxl8_mouse_dx(const pxl8_input_state* input);
|
|
||||||
i32 pxl8_mouse_dy(const pxl8_input_state* input);
|
|
||||||
bool pxl8_mouse_pressed(const pxl8_input_state* input, i32 button);
|
|
||||||
bool pxl8_mouse_released(const pxl8_input_state* input, i32 button);
|
|
||||||
i32 pxl8_mouse_wheel_x(const pxl8_input_state* input);
|
|
||||||
i32 pxl8_mouse_wheel_y(const pxl8_input_state* input);
|
|
||||||
i32 pxl8_mouse_x(const pxl8_input_state* input);
|
|
||||||
i32 pxl8_mouse_y(const pxl8_input_state* input);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,79 +0,0 @@
|
||||||
#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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "pxl8_dither.h"
|
|
||||||
#include "pxl8_types.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define PXL8_LIGHT_LEVELS 64
|
|
||||||
#define PXL8_COLORMAP_SIZE (256 * PXL8_LIGHT_LEVELS)
|
|
||||||
|
|
||||||
#define PXL8_FULLBRIGHT_START 240
|
|
||||||
#define PXL8_TRANSPARENT 0
|
|
||||||
#define PXL8_DYNAMIC_RANGE_START 144
|
|
||||||
#define PXL8_DYNAMIC_RANGE_COUNT 16
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
u8 table[PXL8_COLORMAP_SIZE];
|
|
||||||
} pxl8_colormap;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
u8 dark_r, dark_g, dark_b;
|
|
||||||
u8 tint_r, tint_g, tint_b;
|
|
||||||
f32 tint_strength;
|
|
||||||
} pxl8_level_tint;
|
|
||||||
|
|
||||||
void pxl8_colormap_generate(pxl8_colormap* cm, const u32* palette, const pxl8_level_tint* tint);
|
|
||||||
|
|
||||||
static inline u8 pxl8_colormap_lookup(const pxl8_colormap* cm, u8 pal_idx, u8 light) {
|
|
||||||
u32 light_idx = light >> 2;
|
|
||||||
return cm->table[light_idx * 256 + pal_idx];
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline u8 pxl8_colormap_lookup_dithered(const pxl8_colormap* cm, u8 pal_idx, u8 light, u32 x, u32 y) {
|
|
||||||
u8 dithered = pxl8_dither_light(light, x, y);
|
|
||||||
u32 light_idx = dithered >> 2;
|
|
||||||
return cm->table[light_idx * 256 + pal_idx];
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
local ffi = require("ffi")
|
|
||||||
local C = ffi.C
|
|
||||||
local core = require("pxl8.core")
|
|
||||||
|
|
||||||
local vfx = {}
|
|
||||||
|
|
||||||
function vfx.raster_bars(bars, time)
|
|
||||||
local c_bars = ffi.new("pxl8_raster_bar[?]", #bars)
|
|
||||||
for i, bar in ipairs(bars) do
|
|
||||||
c_bars[i-1].base_y = bar.base_y or 0
|
|
||||||
c_bars[i-1].amplitude = bar.amplitude or 10
|
|
||||||
c_bars[i-1].height = bar.height or 5
|
|
||||||
c_bars[i-1].speed = bar.speed or 1.0
|
|
||||||
c_bars[i-1].phase = bar.phase or 0
|
|
||||||
c_bars[i-1].color = bar.color or 15
|
|
||||||
c_bars[i-1].fade_color = bar.fade_color or bar.color or 15
|
|
||||||
end
|
|
||||||
C.pxl8_vfx_raster_bars(core.gfx, c_bars, #bars, time)
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfx.plasma(time, scale1, scale2, palette_offset)
|
|
||||||
C.pxl8_vfx_plasma(core.gfx, time, scale1 or 0.05, scale2 or 0.03, palette_offset or 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfx.rotozoom(angle, zoom, cx, cy)
|
|
||||||
local width = C.pxl8_gfx_get_width(core.gfx)
|
|
||||||
local height = C.pxl8_gfx_get_height(core.gfx)
|
|
||||||
C.pxl8_vfx_rotozoom(core.gfx, angle, zoom, cx or width/2, cy or height/2)
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfx.tunnel(time, speed, twist)
|
|
||||||
C.pxl8_vfx_tunnel(core.gfx, time, speed or 2.0, twist or 0.5)
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfx.explosion(ps, x, y, color, force)
|
|
||||||
C.pxl8_vfx_explosion(ps, x, y, color or 15, force or 200.0)
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfx.fire(ps, x, y, width, palette_start)
|
|
||||||
C.pxl8_vfx_fire(ps, x, y, width or 50, palette_start or 64)
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfx.rain(ps, width, wind)
|
|
||||||
local w = width or C.pxl8_gfx_get_width(core.gfx)
|
|
||||||
C.pxl8_vfx_rain(ps, w, wind or 0.0)
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfx.smoke(ps, x, y, color)
|
|
||||||
C.pxl8_vfx_smoke(ps, x, y, color or 8)
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfx.snow(ps, width, wind)
|
|
||||||
local w = width or C.pxl8_gfx_get_width(core.gfx)
|
|
||||||
C.pxl8_vfx_snow(ps, w, wind or 10.0)
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfx.sparks(ps, x, y, color)
|
|
||||||
C.pxl8_vfx_sparks(ps, x, y, color or 15)
|
|
||||||
end
|
|
||||||
|
|
||||||
function vfx.starfield(ps, speed, spread)
|
|
||||||
C.pxl8_vfx_starfield(ps, speed or 5.0, spread or 500.0)
|
|
||||||
end
|
|
||||||
|
|
||||||
return vfx
|
|
||||||
|
|
@ -1,299 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "pxl8_types.h"
|
|
||||||
|
|
||||||
#if defined(__x86_64__) || defined(_M_X64)
|
|
||||||
#define PXL8_SIMD_SSE2 1
|
|
||||||
#include <emmintrin.h>
|
|
||||||
#elif defined(__aarch64__) || defined(_M_ARM64)
|
|
||||||
#define PXL8_SIMD_NEON 1
|
|
||||||
#include <arm_neon.h>
|
|
||||||
#else
|
|
||||||
#define PXL8_SIMD_SCALAR 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(PXL8_SIMD_SSE2)
|
|
||||||
|
|
||||||
typedef struct { __m128 v; } pxl8_f32x4;
|
|
||||||
typedef struct { __m128i v; } pxl8_i32x4;
|
|
||||||
typedef struct { __m128i v; } pxl8_u16x8;
|
|
||||||
|
|
||||||
static inline pxl8_f32x4 pxl8_f32x4_splat(f32 x) {
|
|
||||||
return (pxl8_f32x4){ _mm_set1_ps(x) };
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_f32x4 pxl8_f32x4_new(f32 a, f32 b, f32 c, f32 d) {
|
|
||||||
return (pxl8_f32x4){ _mm_set_ps(d, c, b, a) };
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_f32x4 pxl8_f32x4_add(pxl8_f32x4 a, pxl8_f32x4 b) {
|
|
||||||
return (pxl8_f32x4){ _mm_add_ps(a.v, b.v) };
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_f32x4 pxl8_f32x4_sub(pxl8_f32x4 a, pxl8_f32x4 b) {
|
|
||||||
return (pxl8_f32x4){ _mm_sub_ps(a.v, b.v) };
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_f32x4 pxl8_f32x4_mul(pxl8_f32x4 a, pxl8_f32x4 b) {
|
|
||||||
return (pxl8_f32x4){ _mm_mul_ps(a.v, b.v) };
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_f32x4 pxl8_f32x4_div(pxl8_f32x4 a, pxl8_f32x4 b) {
|
|
||||||
return (pxl8_f32x4){ _mm_div_ps(a.v, b.v) };
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_f32x4 pxl8_f32x4_min(pxl8_f32x4 a, pxl8_f32x4 b) {
|
|
||||||
return (pxl8_f32x4){ _mm_min_ps(a.v, b.v) };
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_f32x4 pxl8_f32x4_max(pxl8_f32x4 a, pxl8_f32x4 b) {
|
|
||||||
return (pxl8_f32x4){ _mm_max_ps(a.v, b.v) };
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_f32x4 pxl8_f32x4_cmplt(pxl8_f32x4 a, pxl8_f32x4 b) {
|
|
||||||
return (pxl8_f32x4){ _mm_cmplt_ps(a.v, b.v) };
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline i32 pxl8_f32x4_movemask(pxl8_f32x4 a) {
|
|
||||||
return _mm_movemask_ps(a.v);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_i32x4 pxl8_f32x4_to_i32x4(pxl8_f32x4 a) {
|
|
||||||
return (pxl8_i32x4){ _mm_cvttps_epi32(a.v) };
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void pxl8_f32x4_store(pxl8_f32x4 a, f32* out) {
|
|
||||||
_mm_storeu_ps(out, a.v);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_i32x4 pxl8_i32x4_splat(i32 x) {
|
|
||||||
return (pxl8_i32x4){ _mm_set1_epi32(x) };
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_i32x4 pxl8_i32x4_slli(pxl8_i32x4 a, i32 n) {
|
|
||||||
return (pxl8_i32x4){ _mm_slli_epi32(a.v, n) };
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_i32x4 pxl8_i32x4_srai(pxl8_i32x4 a, i32 n) {
|
|
||||||
return (pxl8_i32x4){ _mm_srai_epi32(a.v, n) };
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_i32x4 pxl8_i32x4_and(pxl8_i32x4 a, pxl8_i32x4 b) {
|
|
||||||
return (pxl8_i32x4){ _mm_and_si128(a.v, b.v) };
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_i32x4 pxl8_i32x4_or(pxl8_i32x4 a, pxl8_i32x4 b) {
|
|
||||||
return (pxl8_i32x4){ _mm_or_si128(a.v, b.v) };
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void pxl8_i32x4_store(pxl8_i32x4 a, i32* out) {
|
|
||||||
_mm_storeu_si128((__m128i*)out, a.v);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_u16x8 pxl8_u16x8_cmplt(pxl8_u16x8 a, pxl8_u16x8 b) {
|
|
||||||
return (pxl8_u16x8){ _mm_cmplt_epi16(a.v, b.v) };
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_u16x8 pxl8_u16x8_blend(pxl8_u16x8 a, pxl8_u16x8 b, pxl8_u16x8 mask) {
|
|
||||||
__m128i not_mask = _mm_andnot_si128(mask.v, a.v);
|
|
||||||
__m128i and_mask = _mm_and_si128(mask.v, b.v);
|
|
||||||
return (pxl8_u16x8){ _mm_or_si128(not_mask, and_mask) };
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline i32 pxl8_u16x8_movemask(pxl8_u16x8 a) {
|
|
||||||
return _mm_movemask_epi8(a.v);
|
|
||||||
}
|
|
||||||
|
|
||||||
#elif defined(PXL8_SIMD_NEON)
|
|
||||||
|
|
||||||
typedef struct { float32x4_t v; } pxl8_f32x4;
|
|
||||||
typedef struct { int32x4_t v; } pxl8_i32x4;
|
|
||||||
typedef struct { uint16x8_t v; } pxl8_u16x8;
|
|
||||||
|
|
||||||
static inline pxl8_f32x4 pxl8_f32x4_splat(f32 x) {
|
|
||||||
return (pxl8_f32x4){ vdupq_n_f32(x) };
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_f32x4 pxl8_f32x4_new(f32 a, f32 b, f32 c, f32 d) {
|
|
||||||
f32 arr[4] = {a, b, c, d};
|
|
||||||
return (pxl8_f32x4){ vld1q_f32(arr) };
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_f32x4 pxl8_f32x4_add(pxl8_f32x4 a, pxl8_f32x4 b) {
|
|
||||||
return (pxl8_f32x4){ vaddq_f32(a.v, b.v) };
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_f32x4 pxl8_f32x4_sub(pxl8_f32x4 a, pxl8_f32x4 b) {
|
|
||||||
return (pxl8_f32x4){ vsubq_f32(a.v, b.v) };
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_f32x4 pxl8_f32x4_mul(pxl8_f32x4 a, pxl8_f32x4 b) {
|
|
||||||
return (pxl8_f32x4){ vmulq_f32(a.v, b.v) };
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_f32x4 pxl8_f32x4_div(pxl8_f32x4 a, pxl8_f32x4 b) {
|
|
||||||
return (pxl8_f32x4){ vdivq_f32(a.v, b.v) };
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_f32x4 pxl8_f32x4_min(pxl8_f32x4 a, pxl8_f32x4 b) {
|
|
||||||
return (pxl8_f32x4){ vminq_f32(a.v, b.v) };
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_f32x4 pxl8_f32x4_max(pxl8_f32x4 a, pxl8_f32x4 b) {
|
|
||||||
return (pxl8_f32x4){ vmaxq_f32(a.v, b.v) };
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_f32x4 pxl8_f32x4_cmplt(pxl8_f32x4 a, pxl8_f32x4 b) {
|
|
||||||
uint32x4_t cmp = vcltq_f32(a.v, b.v);
|
|
||||||
return (pxl8_f32x4){ vreinterpretq_f32_u32(cmp) };
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline i32 pxl8_f32x4_movemask(pxl8_f32x4 a) {
|
|
||||||
uint32x4_t input = vreinterpretq_u32_f32(a.v);
|
|
||||||
static const i32 shifts[4] = {0, 1, 2, 3};
|
|
||||||
uint32x4_t shifted = vshrq_n_u32(input, 31);
|
|
||||||
return vgetq_lane_u32(shifted, 0) | (vgetq_lane_u32(shifted, 1) << 1) |
|
|
||||||
(vgetq_lane_u32(shifted, 2) << 2) | (vgetq_lane_u32(shifted, 3) << 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_i32x4 pxl8_f32x4_to_i32x4(pxl8_f32x4 a) {
|
|
||||||
return (pxl8_i32x4){ vcvtq_s32_f32(a.v) };
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void pxl8_f32x4_store(pxl8_f32x4 a, f32* out) {
|
|
||||||
vst1q_f32(out, a.v);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_i32x4 pxl8_i32x4_splat(i32 x) {
|
|
||||||
return (pxl8_i32x4){ vdupq_n_s32(x) };
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_i32x4 pxl8_i32x4_slli(pxl8_i32x4 a, i32 n) {
|
|
||||||
return (pxl8_i32x4){ vshlq_s32(a.v, vdupq_n_s32(n)) };
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_i32x4 pxl8_i32x4_srai(pxl8_i32x4 a, i32 n) {
|
|
||||||
return (pxl8_i32x4){ vshlq_s32(a.v, vdupq_n_s32(-n)) };
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_i32x4 pxl8_i32x4_and(pxl8_i32x4 a, pxl8_i32x4 b) {
|
|
||||||
return (pxl8_i32x4){ vandq_s32(a.v, b.v) };
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_i32x4 pxl8_i32x4_or(pxl8_i32x4 a, pxl8_i32x4 b) {
|
|
||||||
return (pxl8_i32x4){ vorrq_s32(a.v, b.v) };
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void pxl8_i32x4_store(pxl8_i32x4 a, i32* out) {
|
|
||||||
vst1q_s32(out, a.v);
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
typedef struct { f32 v[4]; } pxl8_f32x4;
|
|
||||||
typedef struct { i32 v[4]; } pxl8_i32x4;
|
|
||||||
typedef struct { u16 v[8]; } pxl8_u16x8;
|
|
||||||
|
|
||||||
static inline pxl8_f32x4 pxl8_f32x4_splat(f32 x) {
|
|
||||||
return (pxl8_f32x4){{ x, x, x, x }};
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_f32x4 pxl8_f32x4_new(f32 a, f32 b, f32 c, f32 d) {
|
|
||||||
return (pxl8_f32x4){{ a, b, c, d }};
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_f32x4 pxl8_f32x4_add(pxl8_f32x4 a, pxl8_f32x4 b) {
|
|
||||||
return (pxl8_f32x4){{ a.v[0]+b.v[0], a.v[1]+b.v[1], a.v[2]+b.v[2], a.v[3]+b.v[3] }};
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_f32x4 pxl8_f32x4_sub(pxl8_f32x4 a, pxl8_f32x4 b) {
|
|
||||||
return (pxl8_f32x4){{ a.v[0]-b.v[0], a.v[1]-b.v[1], a.v[2]-b.v[2], a.v[3]-b.v[3] }};
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_f32x4 pxl8_f32x4_mul(pxl8_f32x4 a, pxl8_f32x4 b) {
|
|
||||||
return (pxl8_f32x4){{ a.v[0]*b.v[0], a.v[1]*b.v[1], a.v[2]*b.v[2], a.v[3]*b.v[3] }};
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_f32x4 pxl8_f32x4_div(pxl8_f32x4 a, pxl8_f32x4 b) {
|
|
||||||
return (pxl8_f32x4){{ a.v[0]/b.v[0], a.v[1]/b.v[1], a.v[2]/b.v[2], a.v[3]/b.v[3] }};
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_f32x4 pxl8_f32x4_min(pxl8_f32x4 a, pxl8_f32x4 b) {
|
|
||||||
return (pxl8_f32x4){{
|
|
||||||
a.v[0]<b.v[0]?a.v[0]:b.v[0], a.v[1]<b.v[1]?a.v[1]:b.v[1],
|
|
||||||
a.v[2]<b.v[2]?a.v[2]:b.v[2], a.v[3]<b.v[3]?a.v[3]:b.v[3]
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_f32x4 pxl8_f32x4_max(pxl8_f32x4 a, pxl8_f32x4 b) {
|
|
||||||
return (pxl8_f32x4){{
|
|
||||||
a.v[0]>b.v[0]?a.v[0]:b.v[0], a.v[1]>b.v[1]?a.v[1]:b.v[1],
|
|
||||||
a.v[2]>b.v[2]?a.v[2]:b.v[2], a.v[3]>b.v[3]?a.v[3]:b.v[3]
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_f32x4 pxl8_f32x4_cmplt(pxl8_f32x4 a, pxl8_f32x4 b) {
|
|
||||||
pxl8_f32x4 r;
|
|
||||||
u32* rv = (u32*)r.v;
|
|
||||||
rv[0] = a.v[0] < b.v[0] ? 0xFFFFFFFF : 0;
|
|
||||||
rv[1] = a.v[1] < b.v[1] ? 0xFFFFFFFF : 0;
|
|
||||||
rv[2] = a.v[2] < b.v[2] ? 0xFFFFFFFF : 0;
|
|
||||||
rv[3] = a.v[3] < b.v[3] ? 0xFFFFFFFF : 0;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline i32 pxl8_f32x4_movemask(pxl8_f32x4 a) {
|
|
||||||
u32* av = (u32*)a.v;
|
|
||||||
return ((av[0] >> 31) & 1) | ((av[1] >> 31) & 1) << 1 |
|
|
||||||
((av[2] >> 31) & 1) << 2 | ((av[3] >> 31) & 1) << 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_i32x4 pxl8_f32x4_to_i32x4(pxl8_f32x4 a) {
|
|
||||||
return (pxl8_i32x4){{ (i32)a.v[0], (i32)a.v[1], (i32)a.v[2], (i32)a.v[3] }};
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void pxl8_f32x4_store(pxl8_f32x4 a, f32* out) {
|
|
||||||
out[0] = a.v[0]; out[1] = a.v[1]; out[2] = a.v[2]; out[3] = a.v[3];
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_i32x4 pxl8_i32x4_splat(i32 x) {
|
|
||||||
return (pxl8_i32x4){{ x, x, x, x }};
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_i32x4 pxl8_i32x4_slli(pxl8_i32x4 a, i32 n) {
|
|
||||||
return (pxl8_i32x4){{ a.v[0]<<n, a.v[1]<<n, a.v[2]<<n, a.v[3]<<n }};
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_i32x4 pxl8_i32x4_srai(pxl8_i32x4 a, i32 n) {
|
|
||||||
return (pxl8_i32x4){{ a.v[0]>>n, a.v[1]>>n, a.v[2]>>n, a.v[3]>>n }};
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_i32x4 pxl8_i32x4_and(pxl8_i32x4 a, pxl8_i32x4 b) {
|
|
||||||
return (pxl8_i32x4){{ a.v[0]&b.v[0], a.v[1]&b.v[1], a.v[2]&b.v[2], a.v[3]&b.v[3] }};
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_i32x4 pxl8_i32x4_or(pxl8_i32x4 a, pxl8_i32x4 b) {
|
|
||||||
return (pxl8_i32x4){{ a.v[0]|b.v[0], a.v[1]|b.v[1], a.v[2]|b.v[2], a.v[3]|b.v[3] }};
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void pxl8_i32x4_store(pxl8_i32x4 a, i32* out) {
|
|
||||||
out[0] = a.v[0]; out[1] = a.v[1]; out[2] = a.v[2]; out[3] = a.v[3];
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static inline f32 pxl8_fast_inv_sqrt(f32 x) {
|
|
||||||
f32 half = 0.5f * x;
|
|
||||||
i32 i = *(i32*)&x;
|
|
||||||
i = 0x5f375a86 - (i >> 1);
|
|
||||||
f32 y = *(f32*)&i;
|
|
||||||
return y * (1.5f - half * y * y);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,504 +0,0 @@
|
||||||
#include "pxl8_gen.h"
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "pxl8_log.h"
|
|
||||||
#include "pxl8_rng.h"
|
|
||||||
|
|
||||||
typedef struct room_grid {
|
|
||||||
u8* cells;
|
|
||||||
i32 width;
|
|
||||||
i32 height;
|
|
||||||
} room_grid;
|
|
||||||
|
|
||||||
static bool room_grid_init(room_grid* grid, i32 width, i32 height) {
|
|
||||||
grid->width = width;
|
|
||||||
grid->height = height;
|
|
||||||
grid->cells = calloc(width * height, sizeof(u8));
|
|
||||||
|
|
||||||
return grid->cells != NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static u8 room_grid_get(const room_grid* grid, i32 x, i32 y) {
|
|
||||||
if (x < 0 || x >= grid->width || y < 0 || y >= grid->height) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return grid->cells[y * grid->width + x];
|
|
||||||
}
|
|
||||||
|
|
||||||
static void room_grid_set(room_grid* grid, i32 x, i32 y, u8 value) {
|
|
||||||
if (x < 0 || x >= grid->width || y < 0 || y >= grid->height) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
grid->cells[y * grid->width + x] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void compute_face_aabb(pxl8_bsp_face* face, const pxl8_bsp_vertex* verts, u32 vert_idx) {
|
|
||||||
face->aabb_min = (pxl8_vec3){1e30f, 1e30f, 1e30f};
|
|
||||||
face->aabb_max = (pxl8_vec3){-1e30f, -1e30f, -1e30f};
|
|
||||||
|
|
||||||
for (u32 i = 0; i < 4; i++) {
|
|
||||||
pxl8_vec3 v = verts[vert_idx + i].position;
|
|
||||||
if (v.x < face->aabb_min.x) face->aabb_min.x = v.x;
|
|
||||||
if (v.x > face->aabb_max.x) face->aabb_max.x = v.x;
|
|
||||||
if (v.y < face->aabb_min.y) face->aabb_min.y = v.y;
|
|
||||||
if (v.y > face->aabb_max.y) face->aabb_max.y = v.y;
|
|
||||||
if (v.z < face->aabb_min.z) face->aabb_min.z = v.z;
|
|
||||||
if (v.z > face->aabb_max.z) face->aabb_max.z = v.z;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void room_grid_fill(room_grid* grid, u8 value) {
|
|
||||||
for (i32 y = 0; y < grid->height; y++) {
|
|
||||||
for (i32 x = 0; x < grid->width; x++) {
|
|
||||||
room_grid_set(grid, x, y, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static pxl8_result grid_to_bsp(pxl8_bsp* bsp, const room_grid* grid) {
|
|
||||||
i32 vertex_count = 0;
|
|
||||||
i32 face_count = 0;
|
|
||||||
i32 floor_ceiling_count = 0;
|
|
||||||
|
|
||||||
for (i32 y = 0; y < grid->height; y++) {
|
|
||||||
for (i32 x = 0; x < grid->width; x++) {
|
|
||||||
if (room_grid_get(grid, x, y) == 0) {
|
|
||||||
if (room_grid_get(grid, x - 1, y) == 1) face_count++;
|
|
||||||
if (room_grid_get(grid, x + 1, y) == 1) face_count++;
|
|
||||||
if (room_grid_get(grid, x, y - 1) == 1) face_count++;
|
|
||||||
if (room_grid_get(grid, x, y + 1) == 1) face_count++;
|
|
||||||
floor_ceiling_count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
face_count += floor_ceiling_count * 2;
|
|
||||||
vertex_count = face_count * 4;
|
|
||||||
|
|
||||||
pxl8_debug("Cave generation: %dx%d grid -> %d faces, %d vertices",
|
|
||||||
grid->width, grid->height, face_count, vertex_count);
|
|
||||||
|
|
||||||
bsp->vertices = calloc(vertex_count, sizeof(pxl8_bsp_vertex));
|
|
||||||
bsp->faces = calloc(face_count, sizeof(pxl8_bsp_face));
|
|
||||||
bsp->planes = calloc(face_count, sizeof(pxl8_bsp_plane));
|
|
||||||
bsp->edges = calloc(vertex_count, sizeof(pxl8_bsp_edge));
|
|
||||||
bsp->surfedges = calloc(vertex_count, sizeof(i32));
|
|
||||||
|
|
||||||
if (!bsp->vertices || !bsp->faces || !bsp->planes || !bsp->edges || !bsp->surfedges) {
|
|
||||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
bsp->texinfo = NULL;
|
|
||||||
bsp->num_texinfo = 0;
|
|
||||||
|
|
||||||
i32 vert_idx = 0;
|
|
||||||
i32 face_idx = 0;
|
|
||||||
i32 edge_idx = 0;
|
|
||||||
|
|
||||||
const f32 cell_size = 64.0f;
|
|
||||||
const f32 wall_height = 128.0f;
|
|
||||||
|
|
||||||
for (i32 y = 0; y < grid->height; y++) {
|
|
||||||
for (i32 x = 0; x < grid->width; x++) {
|
|
||||||
if (room_grid_get(grid, x, y) == 0) {
|
|
||||||
f32 fx = (f32)x * cell_size;
|
|
||||||
f32 fy = (f32)y * cell_size;
|
|
||||||
|
|
||||||
if (room_grid_get(grid, x - 1, y) == 1) {
|
|
||||||
bsp->vertices[vert_idx + 0].position = (pxl8_vec3){fx, 0, fy};
|
|
||||||
bsp->vertices[vert_idx + 1].position = (pxl8_vec3){fx, wall_height, fy};
|
|
||||||
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx, wall_height, fy + cell_size};
|
|
||||||
bsp->vertices[vert_idx + 3].position = (pxl8_vec3){fx, 0, fy + cell_size};
|
|
||||||
|
|
||||||
bsp->planes[face_idx].normal = (pxl8_vec3){-1, 0, 0};
|
|
||||||
bsp->planes[face_idx].dist = -fx;
|
|
||||||
|
|
||||||
bsp->faces[face_idx].plane_id = face_idx;
|
|
||||||
bsp->faces[face_idx].num_edges = 4;
|
|
||||||
bsp->faces[face_idx].first_edge = edge_idx;
|
|
||||||
bsp->faces[face_idx].texinfo_id = 0;
|
|
||||||
|
|
||||||
for (i32 i = 0; i < 4; i++) {
|
|
||||||
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
|
||||||
bsp->edges[edge_idx + i].vertex[1] = vert_idx + ((i + 1) % 4);
|
|
||||||
bsp->surfedges[edge_idx + i] = edge_idx + i;
|
|
||||||
}
|
|
||||||
|
|
||||||
compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx);
|
|
||||||
|
|
||||||
vert_idx += 4;
|
|
||||||
edge_idx += 4;
|
|
||||||
face_idx++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (room_grid_get(grid, x + 1, y) == 1) {
|
|
||||||
bsp->vertices[vert_idx + 0].position = (pxl8_vec3){fx + cell_size, 0, fy};
|
|
||||||
bsp->vertices[vert_idx + 1].position = (pxl8_vec3){fx + cell_size, 0, fy + cell_size};
|
|
||||||
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx + cell_size, wall_height, fy + cell_size};
|
|
||||||
bsp->vertices[vert_idx + 3].position = (pxl8_vec3){fx + cell_size, wall_height, fy};
|
|
||||||
|
|
||||||
bsp->planes[face_idx].normal = (pxl8_vec3){1, 0, 0};
|
|
||||||
bsp->planes[face_idx].dist = fx + cell_size;
|
|
||||||
|
|
||||||
bsp->faces[face_idx].plane_id = face_idx;
|
|
||||||
bsp->faces[face_idx].num_edges = 4;
|
|
||||||
bsp->faces[face_idx].first_edge = edge_idx;
|
|
||||||
bsp->faces[face_idx].texinfo_id = 0;
|
|
||||||
|
|
||||||
for (i32 i = 0; i < 4; i++) {
|
|
||||||
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
|
||||||
bsp->edges[edge_idx + i].vertex[1] = vert_idx + ((i + 1) % 4);
|
|
||||||
bsp->surfedges[edge_idx + i] = edge_idx + i;
|
|
||||||
}
|
|
||||||
|
|
||||||
compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx);
|
|
||||||
|
|
||||||
vert_idx += 4;
|
|
||||||
edge_idx += 4;
|
|
||||||
face_idx++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (room_grid_get(grid, x, y - 1) == 1) {
|
|
||||||
bsp->vertices[vert_idx + 0].position = (pxl8_vec3){fx, 0, fy};
|
|
||||||
bsp->vertices[vert_idx + 1].position = (pxl8_vec3){fx + cell_size, 0, fy};
|
|
||||||
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx + cell_size, wall_height, fy};
|
|
||||||
bsp->vertices[vert_idx + 3].position = (pxl8_vec3){fx, wall_height, fy};
|
|
||||||
|
|
||||||
bsp->planes[face_idx].normal = (pxl8_vec3){0, 0, -1};
|
|
||||||
bsp->planes[face_idx].dist = -fy;
|
|
||||||
|
|
||||||
bsp->faces[face_idx].plane_id = face_idx;
|
|
||||||
bsp->faces[face_idx].num_edges = 4;
|
|
||||||
bsp->faces[face_idx].first_edge = edge_idx;
|
|
||||||
bsp->faces[face_idx].texinfo_id = 0;
|
|
||||||
|
|
||||||
for (i32 i = 0; i < 4; i++) {
|
|
||||||
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
|
||||||
bsp->edges[edge_idx + i].vertex[1] = vert_idx + ((i + 1) % 4);
|
|
||||||
bsp->surfedges[edge_idx + i] = edge_idx + i;
|
|
||||||
}
|
|
||||||
|
|
||||||
compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx);
|
|
||||||
|
|
||||||
vert_idx += 4;
|
|
||||||
edge_idx += 4;
|
|
||||||
face_idx++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (room_grid_get(grid, x, y + 1) == 1) {
|
|
||||||
bsp->vertices[vert_idx + 0].position = (pxl8_vec3){fx, 0, fy + cell_size};
|
|
||||||
bsp->vertices[vert_idx + 1].position = (pxl8_vec3){fx, wall_height, fy + cell_size};
|
|
||||||
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx + cell_size, wall_height, fy + cell_size};
|
|
||||||
bsp->vertices[vert_idx + 3].position = (pxl8_vec3){fx + cell_size, 0, fy + cell_size};
|
|
||||||
|
|
||||||
bsp->planes[face_idx].normal = (pxl8_vec3){0, 0, 1};
|
|
||||||
bsp->planes[face_idx].dist = fy + cell_size;
|
|
||||||
|
|
||||||
bsp->faces[face_idx].plane_id = face_idx;
|
|
||||||
bsp->faces[face_idx].num_edges = 4;
|
|
||||||
bsp->faces[face_idx].first_edge = edge_idx;
|
|
||||||
bsp->faces[face_idx].texinfo_id = 0;
|
|
||||||
|
|
||||||
for (i32 i = 0; i < 4; i++) {
|
|
||||||
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
|
||||||
bsp->edges[edge_idx + i].vertex[1] = vert_idx + ((i + 1) % 4);
|
|
||||||
bsp->surfedges[edge_idx + i] = edge_idx + i;
|
|
||||||
}
|
|
||||||
|
|
||||||
compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx);
|
|
||||||
|
|
||||||
vert_idx += 4;
|
|
||||||
edge_idx += 4;
|
|
||||||
face_idx++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i32 y = 0; y < grid->height; y++) {
|
|
||||||
for (i32 x = 0; x < grid->width; x++) {
|
|
||||||
if (room_grid_get(grid, x, y) == 0) {
|
|
||||||
f32 fx = (f32)x * cell_size;
|
|
||||||
f32 fy = (f32)y * cell_size;
|
|
||||||
|
|
||||||
bsp->vertices[vert_idx + 0].position = (pxl8_vec3){fx, 0, fy};
|
|
||||||
bsp->vertices[vert_idx + 1].position = (pxl8_vec3){fx, 0, fy + cell_size};
|
|
||||||
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx + cell_size, 0, fy + cell_size};
|
|
||||||
bsp->vertices[vert_idx + 3].position = (pxl8_vec3){fx + cell_size, 0, fy};
|
|
||||||
|
|
||||||
bsp->planes[face_idx].normal = (pxl8_vec3){0, 1, 0};
|
|
||||||
bsp->planes[face_idx].dist = 0;
|
|
||||||
|
|
||||||
bsp->faces[face_idx].plane_id = face_idx;
|
|
||||||
bsp->faces[face_idx].num_edges = 4;
|
|
||||||
bsp->faces[face_idx].first_edge = edge_idx;
|
|
||||||
bsp->faces[face_idx].texinfo_id = 0;
|
|
||||||
|
|
||||||
for (i32 i = 0; i < 4; i++) {
|
|
||||||
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
|
||||||
bsp->edges[edge_idx + i].vertex[1] = vert_idx + ((i + 1) % 4);
|
|
||||||
bsp->surfedges[edge_idx + i] = edge_idx + i;
|
|
||||||
}
|
|
||||||
|
|
||||||
compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx);
|
|
||||||
|
|
||||||
vert_idx += 4;
|
|
||||||
edge_idx += 4;
|
|
||||||
face_idx++;
|
|
||||||
|
|
||||||
bsp->vertices[vert_idx + 0].position = (pxl8_vec3){fx, wall_height, fy};
|
|
||||||
bsp->vertices[vert_idx + 1].position = (pxl8_vec3){fx + cell_size, wall_height, fy};
|
|
||||||
bsp->vertices[vert_idx + 2].position = (pxl8_vec3){fx + cell_size, wall_height, fy + cell_size};
|
|
||||||
bsp->vertices[vert_idx + 3].position = (pxl8_vec3){fx, wall_height, fy + cell_size};
|
|
||||||
|
|
||||||
bsp->planes[face_idx].normal = (pxl8_vec3){0, -1, 0};
|
|
||||||
bsp->planes[face_idx].dist = -wall_height;
|
|
||||||
|
|
||||||
bsp->faces[face_idx].plane_id = face_idx;
|
|
||||||
bsp->faces[face_idx].num_edges = 4;
|
|
||||||
bsp->faces[face_idx].first_edge = edge_idx;
|
|
||||||
bsp->faces[face_idx].texinfo_id = 0;
|
|
||||||
|
|
||||||
for (i32 i = 0; i < 4; i++) {
|
|
||||||
bsp->edges[edge_idx + i].vertex[0] = vert_idx + i;
|
|
||||||
bsp->edges[edge_idx + i].vertex[1] = vert_idx + ((i + 1) % 4);
|
|
||||||
bsp->surfedges[edge_idx + i] = edge_idx + i;
|
|
||||||
}
|
|
||||||
|
|
||||||
compute_face_aabb(&bsp->faces[face_idx], bsp->vertices, vert_idx);
|
|
||||||
|
|
||||||
vert_idx += 4;
|
|
||||||
edge_idx += 4;
|
|
||||||
face_idx++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bsp->num_vertices = vertex_count;
|
|
||||||
bsp->num_faces = face_count;
|
|
||||||
bsp->num_planes = face_count;
|
|
||||||
bsp->num_edges = vertex_count;
|
|
||||||
bsp->num_surfedges = vertex_count;
|
|
||||||
|
|
||||||
bsp->leafs = calloc(1, sizeof(pxl8_bsp_leaf));
|
|
||||||
bsp->marksurfaces = calloc(face_count, sizeof(u16));
|
|
||||||
|
|
||||||
if (!bsp->leafs || !bsp->marksurfaces) {
|
|
||||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
bsp->num_leafs = 1;
|
|
||||||
bsp->num_marksurfaces = face_count;
|
|
||||||
|
|
||||||
bsp->leafs[0].first_marksurface = 0;
|
|
||||||
bsp->leafs[0].num_marksurfaces = face_count;
|
|
||||||
bsp->leafs[0].contents = -2;
|
|
||||||
|
|
||||||
for (i32 i = 0; i < face_count; i++) {
|
|
||||||
bsp->marksurfaces[i] = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
return PXL8_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool bounds_intersects(const pxl8_bounds* a, const pxl8_bounds* b) {
|
|
||||||
return !(a->x + a->w <= b->x || b->x + b->w <= a->x ||
|
|
||||||
a->y + a->h <= b->y || b->y + b->h <= a->y);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void carve_corridor_h(room_grid* grid, i32 x1, i32 x2, i32 y) {
|
|
||||||
i32 start = (x1 < x2) ? x1 : x2;
|
|
||||||
i32 end = (x1 > x2) ? x1 : x2;
|
|
||||||
for (i32 x = start; x <= end; x++) {
|
|
||||||
room_grid_set(grid, x, y, 0);
|
|
||||||
room_grid_set(grid, x, y - 1, 0);
|
|
||||||
room_grid_set(grid, x, y + 1, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void carve_corridor_v(room_grid* grid, i32 y1, i32 y2, i32 x) {
|
|
||||||
i32 start = (y1 < y2) ? y1 : y2;
|
|
||||||
i32 end = (y1 > y2) ? y1 : y2;
|
|
||||||
for (i32 y = start; y <= end; y++) {
|
|
||||||
room_grid_set(grid, x, y, 0);
|
|
||||||
room_grid_set(grid, x - 1, y, 0);
|
|
||||||
room_grid_set(grid, x + 1, y, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static pxl8_result procgen_rooms(pxl8_bsp* bsp, const pxl8_procgen_params* params) {
|
|
||||||
pxl8_debug("procgen_rooms called: %dx%d, seed=%u, min=%d, max=%d, num=%d",
|
|
||||||
params->width, params->height, params->seed,
|
|
||||||
params->min_room_size, params->max_room_size, params->num_rooms);
|
|
||||||
|
|
||||||
pxl8_rng rng;
|
|
||||||
pxl8_rng_seed(&rng, params->seed);
|
|
||||||
|
|
||||||
room_grid grid;
|
|
||||||
if (!room_grid_init(&grid, params->width, params->height)) {
|
|
||||||
pxl8_error("Failed to allocate room grid");
|
|
||||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
room_grid_fill(&grid, 1);
|
|
||||||
|
|
||||||
pxl8_bounds rooms[256];
|
|
||||||
i32 room_count = 0;
|
|
||||||
i32 max_attempts = params->num_rooms * 10;
|
|
||||||
|
|
||||||
for (i32 attempt = 0; attempt < max_attempts && room_count < params->num_rooms && room_count < 256; attempt++) {
|
|
||||||
i32 w = params->min_room_size + (pxl8_rng_next(&rng) % (params->max_room_size - params->min_room_size + 1));
|
|
||||||
i32 h = params->min_room_size + (pxl8_rng_next(&rng) % (params->max_room_size - params->min_room_size + 1));
|
|
||||||
i32 x = 1 + (pxl8_rng_next(&rng) % (params->width - w - 2));
|
|
||||||
i32 y = 1 + (pxl8_rng_next(&rng) % (params->height - h - 2));
|
|
||||||
|
|
||||||
pxl8_bounds new_room = {x, y, w, h};
|
|
||||||
|
|
||||||
bool overlaps = false;
|
|
||||||
for (i32 i = 0; i < room_count; i++) {
|
|
||||||
if (bounds_intersects(&new_room, &rooms[i])) {
|
|
||||||
overlaps = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!overlaps) {
|
|
||||||
for (i32 ry = y; ry < y + h; ry++) {
|
|
||||||
for (i32 rx = x; rx < x + w; rx++) {
|
|
||||||
room_grid_set(&grid, rx, ry, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (room_count > 0) {
|
|
||||||
i32 new_cx = x + w / 2;
|
|
||||||
i32 new_cy = y + h / 2;
|
|
||||||
i32 prev_cx = rooms[room_count - 1].x + rooms[room_count - 1].w / 2;
|
|
||||||
i32 prev_cy = rooms[room_count - 1].y + rooms[room_count - 1].h / 2;
|
|
||||||
|
|
||||||
if (pxl8_rng_next(&rng) % 2 == 0) {
|
|
||||||
carve_corridor_h(&grid, prev_cx, new_cx, prev_cy);
|
|
||||||
carve_corridor_v(&grid, prev_cy, new_cy, new_cx);
|
|
||||||
} else {
|
|
||||||
carve_corridor_v(&grid, prev_cy, new_cy, prev_cx);
|
|
||||||
carve_corridor_h(&grid, prev_cx, new_cx, new_cy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rooms[room_count++] = new_room;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pxl8_debug("Room generation: %dx%d grid -> %d rooms created",
|
|
||||||
params->width, params->height, room_count);
|
|
||||||
|
|
||||||
pxl8_result result = grid_to_bsp(bsp, &grid);
|
|
||||||
free(grid.cells);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
pxl8_result pxl8_procgen(pxl8_bsp* bsp, const pxl8_procgen_params* params) {
|
|
||||||
if (!bsp || !params) {
|
|
||||||
return PXL8_ERROR_NULL_POINTER;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (params->type) {
|
|
||||||
case PXL8_PROCGEN_ROOMS:
|
|
||||||
return procgen_rooms(bsp, params);
|
|
||||||
|
|
||||||
case PXL8_PROCGEN_TERRAIN:
|
|
||||||
pxl8_error("Terrain generation not yet implemented");
|
|
||||||
return PXL8_ERROR_NOT_INITIALIZED;
|
|
||||||
|
|
||||||
default:
|
|
||||||
pxl8_error("Unknown procgen type: %d", params->type);
|
|
||||||
return PXL8_ERROR_INVALID_ARGUMENT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static u32 hash2d(i32 x, i32 y) {
|
|
||||||
u32 h = ((u32)x * 374761393u) + ((u32)y * 668265263u);
|
|
||||||
h ^= h >> 13;
|
|
||||||
h ^= h << 17;
|
|
||||||
h ^= h >> 5;
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
|
|
||||||
void pxl8_procgen_tex(u8* buffer, const pxl8_procgen_tex_params* params) {
|
|
||||||
if (!buffer || !params) return;
|
|
||||||
|
|
||||||
for (i32 y = 0; y < params->height; y++) {
|
|
||||||
for (i32 x = 0; x < params->width; x++) {
|
|
||||||
f32 u = (f32)x / (f32)params->width;
|
|
||||||
f32 v = (f32)y / (f32)params->height;
|
|
||||||
|
|
||||||
u8 color = params->base_color;
|
|
||||||
|
|
||||||
// Tile-based pattern (floor style)
|
|
||||||
if (params->seed == 11111) {
|
|
||||||
i32 tile_x = (i32)floorf(u * 8.0f);
|
|
||||||
i32 tile_y = (i32)floorf(v * 8.0f);
|
|
||||||
u32 h = hash2d(tile_x, tile_y);
|
|
||||||
|
|
||||||
f32 pattern = (f32)(h & 0xFF) / 255.0f;
|
|
||||||
i32 quantized = (pattern < 0.3f) ? 0 : (pattern < 0.7f) ? 1 : 2;
|
|
||||||
|
|
||||||
color = params->base_color + quantized;
|
|
||||||
|
|
||||||
// Checkerboard dither
|
|
||||||
if (((tile_x + tile_y) & 1) == 0 && (h & 0x100)) {
|
|
||||||
color = (color < 255) ? color + 1 : color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Large tile pattern (ceiling style)
|
|
||||||
else if (params->seed == 22222) {
|
|
||||||
i32 coarse_x = (i32)floorf(u * 2.0f);
|
|
||||||
i32 coarse_y = (i32)floorf(v * 2.0f);
|
|
||||||
u32 coarse_h = hash2d(coarse_x, coarse_y);
|
|
||||||
|
|
||||||
i32 subdivision = (coarse_h >> 8) & 0x3;
|
|
||||||
i32 tile_x, tile_y;
|
|
||||||
|
|
||||||
switch (subdivision) {
|
|
||||||
case 0: tile_x = (i32)floorf(u * 3.0f); tile_y = (i32)floorf(v * 3.0f); break;
|
|
||||||
case 1: tile_x = (i32)floorf(u * 5.0f); tile_y = (i32)floorf(v * 5.0f); break;
|
|
||||||
case 2: tile_x = (i32)floorf(u * 2.0f); tile_y = (i32)floorf(v * 4.0f); break;
|
|
||||||
default: tile_x = (i32)floorf(u * 4.0f); tile_y = (i32)floorf(v * 2.0f); break;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 h = hash2d(tile_x, tile_y);
|
|
||||||
f32 pattern = (f32)(h & 0xFF) / 255.0f;
|
|
||||||
|
|
||||||
if (pattern < 0.25f) color = params->base_color;
|
|
||||||
else if (pattern < 0.50f) color = params->base_color + 1;
|
|
||||||
else if (pattern < 0.75f) color = params->base_color + 2;
|
|
||||||
else color = params->base_color + 3;
|
|
||||||
}
|
|
||||||
// Brick pattern (wall style)
|
|
||||||
else {
|
|
||||||
f32 brick_y = floorf(v * 4.0f);
|
|
||||||
f32 offset = ((i32)brick_y & 1) ? 0.5f : 0.0f;
|
|
||||||
i32 brick_x = (i32)floorf(u * 4.0f + offset);
|
|
||||||
brick_y = (i32)brick_y;
|
|
||||||
|
|
||||||
f32 brick_u = fabsf((u * 4.0f + offset) - floorf(u * 4.0f + offset) - 0.5f);
|
|
||||||
f32 brick_v = fabsf((v * 4.0f) - floorf(v * 4.0f) - 0.5f);
|
|
||||||
|
|
||||||
u32 h = hash2d(brick_x, (i32)brick_y);
|
|
||||||
f32 noise = (f32)(h & 0xFF) / 255.0f;
|
|
||||||
|
|
||||||
// Mortar lines
|
|
||||||
if (brick_u > 0.47f || brick_v > 0.47f) {
|
|
||||||
color = params->base_color - 2;
|
|
||||||
} else {
|
|
||||||
i32 shade = (i32)(noise * 3.0f);
|
|
||||||
color = params->base_color + shade;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer[y * params->width + x] = color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +1,15 @@
|
||||||
(local pxl8 (require :pxl8))
|
(local pxl8 (require :pxl8))
|
||||||
(local menu (require :mod.menu))
|
(local menu (require :mod.menu))
|
||||||
(local music (require :mod.music))
|
(local music (require :mod.music))
|
||||||
(local worldgen (require :mod.worldgen))
|
(local first_person3d (require :mod.first_person3d))
|
||||||
|
|
||||||
(var time 0)
|
(var time 0)
|
||||||
(var active-demo :logo)
|
(var active-demo :logo)
|
||||||
(var particles nil)
|
(var particles nil)
|
||||||
(var particles2 nil)
|
|
||||||
(var fire-init? false)
|
(var fire-init? false)
|
||||||
(var rain-init? false)
|
(var rain-init? false)
|
||||||
(var snow-init? false)
|
(var snow-init? false)
|
||||||
(var snow-init2? false)
|
(var first_person3d-init? false)
|
||||||
(var use-famicube-palette? false)
|
(var use-famicube-palette? false)
|
||||||
|
|
||||||
(var logo-x 256)
|
(var logo-x 256)
|
||||||
|
|
@ -32,15 +31,12 @@
|
||||||
(pxl8.load_palette "res/sprites/pxl8_logo.ase")
|
(pxl8.load_palette "res/sprites/pxl8_logo.ase")
|
||||||
(set logo-sprite (pxl8.load_sprite "res/sprites/pxl8_logo.ase"))
|
(set logo-sprite (pxl8.load_sprite "res/sprites/pxl8_logo.ase"))
|
||||||
(set particles (pxl8.create_particles 1000))
|
(set particles (pxl8.create_particles 1000))
|
||||||
(set particles2 (pxl8.create_particles 500))
|
(music.init)))
|
||||||
(music.init)
|
|
||||||
(music.start)
|
|
||||||
(worldgen.init)))
|
|
||||||
|
|
||||||
(global update (fn [dt]
|
(global update (fn [dt]
|
||||||
(when (pxl8.key_pressed "escape")
|
(when (pxl8.key_pressed "escape")
|
||||||
(menu.toggle)
|
(menu.toggle)
|
||||||
(when (= active-demo :worldgen)
|
(when (= active-demo :first_person3d)
|
||||||
(pxl8.set_relative_mouse_mode (not (menu.is-paused)))))
|
(pxl8.set_relative_mouse_mode (not (menu.is-paused)))))
|
||||||
|
|
||||||
(when (not (menu.is-paused))
|
(when (not (menu.is-paused))
|
||||||
|
|
@ -50,15 +46,16 @@
|
||||||
(transition:update dt)
|
(transition:update dt)
|
||||||
(when (transition:is_complete)
|
(when (transition:is_complete)
|
||||||
(when transition-pending
|
(when transition-pending
|
||||||
(when (and (= active-demo :worldgen) (not= transition-pending :worldgen))
|
(when (and (= active-demo :first_person3d) (not= transition-pending :first_person3d))
|
||||||
(pxl8.set_relative_mouse_mode false))
|
(pxl8.set_relative_mouse_mode false))
|
||||||
(when (and (not= active-demo :worldgen) (= transition-pending :worldgen))
|
(when (and (not= active-demo :first_person3d) (= transition-pending :first_person3d))
|
||||||
(pxl8.set_relative_mouse_mode true))
|
(pxl8.set_relative_mouse_mode true))
|
||||||
(set active-demo transition-pending)
|
(set active-demo transition-pending)
|
||||||
(set transition-pending nil)
|
(set transition-pending nil)
|
||||||
(when (= active-demo :fire) (set fire-init? false))
|
(when (= active-demo :fire) (set fire-init? false))
|
||||||
(when (= active-demo :rain) (set rain-init? false))
|
(when (= active-demo :rain) (set rain-init? false))
|
||||||
(when (= active-demo :snow) (set snow-init? false) (set snow-init2? false)))
|
(when (= active-demo :snow) (set snow-init? false))
|
||||||
|
(when (= active-demo :first_person3d) (set first_person3d-init? false)))
|
||||||
(transition:destroy)
|
(transition:destroy)
|
||||||
(set transition nil)))
|
(set transition nil)))
|
||||||
|
|
||||||
|
|
@ -69,12 +66,14 @@
|
||||||
(when (pxl8.key_pressed "5") (switch-demo :fire))
|
(when (pxl8.key_pressed "5") (switch-demo :fire))
|
||||||
(when (pxl8.key_pressed "6") (switch-demo :rain))
|
(when (pxl8.key_pressed "6") (switch-demo :rain))
|
||||||
(when (pxl8.key_pressed "7") (switch-demo :snow))
|
(when (pxl8.key_pressed "7") (switch-demo :snow))
|
||||||
(when (pxl8.key_pressed "8") (switch-demo :worldgen))
|
(when (pxl8.key_pressed "8") (switch-demo :first_person3d))
|
||||||
(when (pxl8.key_pressed "=")
|
(when (pxl8.key_pressed "=")
|
||||||
(set use-famicube-palette? (not use-famicube-palette?))
|
(set use-famicube-palette? (not use-famicube-palette?))
|
||||||
(local palette-path (if use-famicube-palette? "res/palettes/famicube.ase" "res/sprites/pxl8_logo.ase"))
|
(local palette-path (if use-famicube-palette? "res/palettes/famicube.ase" "res/sprites/pxl8_logo.ase"))
|
||||||
(pxl8.load_palette palette-path))
|
(pxl8.load_palette palette-path))
|
||||||
|
|
||||||
|
(music.update dt)
|
||||||
|
|
||||||
(case active-demo
|
(case active-demo
|
||||||
:logo (do
|
:logo (do
|
||||||
(set logo-x (+ logo-x (* logo-dx dt)))
|
(set logo-x (+ logo-x (* logo-dx dt)))
|
||||||
|
|
@ -91,14 +90,14 @@
|
||||||
(when (> logo-y 296)
|
(when (> logo-y 296)
|
||||||
(set logo-y 296)
|
(set logo-y 296)
|
||||||
(set logo-dy (- (math.abs logo-dy)))))
|
(set logo-dy (- (math.abs logo-dy)))))
|
||||||
:worldgen (worldgen.update dt))
|
:first_person3d (do
|
||||||
|
(when (not first_person3d-init?)
|
||||||
(music.update dt)
|
(first_person3d.init)
|
||||||
|
(set first_person3d-init? true))
|
||||||
|
(first_person3d.update dt)))
|
||||||
|
|
||||||
(when particles
|
(when particles
|
||||||
(particles:update dt))
|
(particles:update dt)))
|
||||||
(when particles2
|
|
||||||
(particles2:update dt)))
|
|
||||||
|
|
||||||
(when (menu.is-paused)
|
(when (menu.is-paused)
|
||||||
(menu.update))))
|
(menu.update))))
|
||||||
|
|
@ -173,7 +172,8 @@
|
||||||
(set snow-init? true))
|
(set snow-init? true))
|
||||||
(particles:render)))
|
(particles:render)))
|
||||||
|
|
||||||
:worldgen (worldgen.frame)
|
:first_person3d (first_person3d.frame)
|
||||||
|
|
||||||
|
|
||||||
_ (pxl8.clear 0))
|
_ (pxl8.clear 0))
|
||||||
|
|
||||||
|
|
|
||||||
4103
demo/mod/blendtable.fnl
Normal file
4103
demo/mod/blendtable.fnl
Normal file
File diff suppressed because it is too large
Load diff
1031
demo/mod/colormap.fnl
Normal file
1031
demo/mod/colormap.fnl
Normal file
File diff suppressed because it is too large
Load diff
516
demo/mod/first_person3d.fnl
Normal file
516
demo/mod/first_person3d.fnl
Normal file
|
|
@ -0,0 +1,516 @@
|
||||||
|
(local pxl8 (require :pxl8))
|
||||||
|
(local effects (require :pxl8.effects))
|
||||||
|
(local net (require :pxl8.net))
|
||||||
|
|
||||||
|
(local colormap (require :mod.colormap))
|
||||||
|
(local menu (require :mod.menu))
|
||||||
|
(local palette (require :mod.palette))
|
||||||
|
(local sky (require :mod.sky))
|
||||||
|
(local textures (require :mod.textures))
|
||||||
|
|
||||||
|
(local bob-amount 4.0)
|
||||||
|
(local bob-speed 8.0)
|
||||||
|
(local cam-smoothing 0.25)
|
||||||
|
(local cell-size 64)
|
||||||
|
(local cursor-sensitivity 0.010)
|
||||||
|
(local gravity -800)
|
||||||
|
(local grid-size 64)
|
||||||
|
(local ground-y 64)
|
||||||
|
(local jump-force 175)
|
||||||
|
(local land-recovery-speed 20)
|
||||||
|
(local land-squash-amount -4)
|
||||||
|
(local max-pitch 1.5)
|
||||||
|
(local move-speed 200)
|
||||||
|
(local turn-speed 4.0)
|
||||||
|
|
||||||
|
(local sim-tick-rate 60)
|
||||||
|
(local sim-dt (/ 1.0 sim-tick-rate))
|
||||||
|
(local history-size 128)
|
||||||
|
(local correction-threshold 1.0)
|
||||||
|
|
||||||
|
(var auto-run? false)
|
||||||
|
(var auto-run-cancel-key nil)
|
||||||
|
(var bob-time 0)
|
||||||
|
(var cam-pitch 0)
|
||||||
|
(var cam-x 1000)
|
||||||
|
(var cam-y 64)
|
||||||
|
(var cam-yaw 0)
|
||||||
|
(var cam-z 1000)
|
||||||
|
(var camera nil)
|
||||||
|
(var grounded? true)
|
||||||
|
(var land-squash 0)
|
||||||
|
(var light-time 0)
|
||||||
|
(var real-time 0)
|
||||||
|
(var network nil)
|
||||||
|
(var smooth-cam-x 1000)
|
||||||
|
(var smooth-cam-z 1000)
|
||||||
|
(var velocity-y 0)
|
||||||
|
(var world nil)
|
||||||
|
(var fps-avg 0)
|
||||||
|
(var fps-sample-count 0)
|
||||||
|
(var fireball-mesh nil)
|
||||||
|
(var last-dt 0.016)
|
||||||
|
|
||||||
|
(local cursor-look? true)
|
||||||
|
(local FIREBALL_COLOR 218)
|
||||||
|
(local STONE_FLOOR_START 37)
|
||||||
|
(local STONE_WALL_START 2)
|
||||||
|
(local MOSS_COLOR 200)
|
||||||
|
|
||||||
|
(local trail-positions [])
|
||||||
|
(local TRAIL_LENGTH 8)
|
||||||
|
|
||||||
|
(fn create-fireball-mesh []
|
||||||
|
(let [verts []
|
||||||
|
indices []
|
||||||
|
radius 5
|
||||||
|
rings 4
|
||||||
|
segments 6
|
||||||
|
core-color (+ FIREBALL_COLOR 6)
|
||||||
|
spike-color (+ FIREBALL_COLOR 2)]
|
||||||
|
|
||||||
|
;; top pole
|
||||||
|
(table.insert verts {:x 0 :y radius :z 0 :nx 0 :ny 1 :nz 0 :color core-color :light 255})
|
||||||
|
|
||||||
|
;; sphere rings
|
||||||
|
(for [ring 1 (- rings 1)]
|
||||||
|
(let [phi (* (/ ring rings) math.pi)
|
||||||
|
sin-phi (math.sin phi)
|
||||||
|
cos-phi (math.cos phi)
|
||||||
|
y (* radius cos-phi)
|
||||||
|
ring-radius (* radius sin-phi)]
|
||||||
|
(for [seg 0 (- segments 1)]
|
||||||
|
(let [theta (* (/ seg segments) math.pi 2)
|
||||||
|
x (* ring-radius (math.cos theta))
|
||||||
|
z (* ring-radius (math.sin theta))
|
||||||
|
nx (* sin-phi (math.cos theta))
|
||||||
|
nz (* sin-phi (math.sin theta))]
|
||||||
|
(table.insert verts {:x x :y y :z z :nx nx :ny cos-phi :nz nz :color core-color :light 255})))))
|
||||||
|
|
||||||
|
;; bottom pole
|
||||||
|
(let [bottom-idx (length verts)]
|
||||||
|
(table.insert verts {:x 0 :y (- radius) :z 0 :nx 0 :ny -1 :nz 0 :color core-color :light 255})
|
||||||
|
|
||||||
|
;; top cap triangles
|
||||||
|
(for [seg 0 (- segments 1)]
|
||||||
|
(let [next-seg (% (+ seg 1) segments)]
|
||||||
|
(table.insert indices 0)
|
||||||
|
(table.insert indices (+ 1 next-seg))
|
||||||
|
(table.insert indices (+ 1 seg))))
|
||||||
|
|
||||||
|
;; middle quads
|
||||||
|
(for [ring 0 (- rings 3)]
|
||||||
|
(for [seg 0 (- segments 1)]
|
||||||
|
(let [next-seg (% (+ seg 1) segments)
|
||||||
|
curr-row (+ 1 (* ring segments))
|
||||||
|
next-row (+ 1 (* (+ ring 1) segments))]
|
||||||
|
(table.insert indices (+ curr-row seg))
|
||||||
|
(table.insert indices (+ curr-row next-seg))
|
||||||
|
(table.insert indices (+ next-row seg))
|
||||||
|
(table.insert indices (+ curr-row next-seg))
|
||||||
|
(table.insert indices (+ next-row next-seg))
|
||||||
|
(table.insert indices (+ next-row seg)))))
|
||||||
|
|
||||||
|
;; bottom cap triangles
|
||||||
|
(let [last-ring-start (+ 1 (* (- rings 2) segments))]
|
||||||
|
(for [seg 0 (- segments 1)]
|
||||||
|
(let [next-seg (% (+ seg 1) segments)]
|
||||||
|
(table.insert indices bottom-idx)
|
||||||
|
(table.insert indices (+ last-ring-start seg))
|
||||||
|
(table.insert indices (+ last-ring-start next-seg))))))
|
||||||
|
|
||||||
|
;; add spikes - evenly distributed using golden ratio
|
||||||
|
(let [num-spikes 12
|
||||||
|
spike-len 8
|
||||||
|
base-size 1.2
|
||||||
|
golden-ratio (/ (+ 1 (math.sqrt 5)) 2)]
|
||||||
|
(for [i 0 (- num-spikes 1)]
|
||||||
|
(let [;; fibonacci sphere distribution
|
||||||
|
y (- 1 (* (/ i (- num-spikes 1)) 2))
|
||||||
|
r-at-y (math.sqrt (- 1 (* y y)))
|
||||||
|
theta (* math.pi 2 i golden-ratio)
|
||||||
|
nx (* r-at-y (math.cos theta))
|
||||||
|
ny y
|
||||||
|
nz (* r-at-y (math.sin theta))
|
||||||
|
;; tangent vectors for base
|
||||||
|
tx (if (> (math.abs ny) 0.9) 1 0)
|
||||||
|
ty (if (> (math.abs ny) 0.9) 0 1)
|
||||||
|
tz 0
|
||||||
|
;; cross product for perpendicular
|
||||||
|
px (- (* ty nz) (* tz ny))
|
||||||
|
py (- (* tz nx) (* tx nz))
|
||||||
|
pz (- (* tx ny) (* ty nx))
|
||||||
|
pl (math.sqrt (+ (* px px) (* py py) (* pz pz)))
|
||||||
|
px (/ px pl) py (/ py pl) pz (/ pz pl)
|
||||||
|
;; second perpendicular
|
||||||
|
qx (- (* ny pz) (* nz py))
|
||||||
|
qy (- (* nz px) (* nx pz))
|
||||||
|
qz (- (* nx py) (* ny px))
|
||||||
|
;; base center inside sphere
|
||||||
|
bx (* radius 0.8 nx)
|
||||||
|
by (* radius 0.8 ny)
|
||||||
|
bz (* radius 0.8 nz)
|
||||||
|
;; spike tip
|
||||||
|
sx (* (+ radius spike-len) nx)
|
||||||
|
sy (* (+ radius spike-len) ny)
|
||||||
|
sz (* (+ radius spike-len) nz)
|
||||||
|
base-idx (length verts)]
|
||||||
|
;; 4 base vertices forming a square
|
||||||
|
(table.insert verts {:x (+ bx (* base-size px) (* base-size qx))
|
||||||
|
:y (+ by (* base-size py) (* base-size qy))
|
||||||
|
:z (+ bz (* base-size pz) (* base-size qz))
|
||||||
|
:nx nx :ny ny :nz nz :color core-color :light 255})
|
||||||
|
(table.insert verts {:x (+ bx (* base-size px) (* (- base-size) qx))
|
||||||
|
:y (+ by (* base-size py) (* (- base-size) qy))
|
||||||
|
:z (+ bz (* base-size pz) (* (- base-size) qz))
|
||||||
|
:nx nx :ny ny :nz nz :color core-color :light 255})
|
||||||
|
(table.insert verts {:x (+ bx (* (- base-size) px) (* (- base-size) qx))
|
||||||
|
:y (+ by (* (- base-size) py) (* (- base-size) qy))
|
||||||
|
:z (+ bz (* (- base-size) pz) (* (- base-size) qz))
|
||||||
|
:nx nx :ny ny :nz nz :color core-color :light 255})
|
||||||
|
(table.insert verts {:x (+ bx (* (- base-size) px) (* base-size qx))
|
||||||
|
:y (+ by (* (- base-size) py) (* base-size qy))
|
||||||
|
:z (+ bz (* (- base-size) pz) (* base-size qz))
|
||||||
|
:nx nx :ny ny :nz nz :color core-color :light 255})
|
||||||
|
;; spike tip
|
||||||
|
(table.insert verts {:x sx :y sy :z sz :nx nx :ny ny :nz nz :color spike-color :light 255})
|
||||||
|
;; 4 triangular faces of pyramid
|
||||||
|
(table.insert indices base-idx)
|
||||||
|
(table.insert indices (+ base-idx 1))
|
||||||
|
(table.insert indices (+ base-idx 4))
|
||||||
|
(table.insert indices (+ base-idx 1))
|
||||||
|
(table.insert indices (+ base-idx 2))
|
||||||
|
(table.insert indices (+ base-idx 4))
|
||||||
|
(table.insert indices (+ base-idx 2))
|
||||||
|
(table.insert indices (+ base-idx 3))
|
||||||
|
(table.insert indices (+ base-idx 4))
|
||||||
|
(table.insert indices (+ base-idx 3))
|
||||||
|
(table.insert indices base-idx)
|
||||||
|
(table.insert indices (+ base-idx 4)))))
|
||||||
|
|
||||||
|
(set fireball-mesh (pxl8.create_mesh verts indices))))
|
||||||
|
|
||||||
|
(var client-tick 0)
|
||||||
|
(var last-processed-tick 0)
|
||||||
|
(var time-accumulator 0)
|
||||||
|
(var position-history {})
|
||||||
|
(var pending-inputs {})
|
||||||
|
|
||||||
|
(fn history-idx [tick]
|
||||||
|
(+ 1 (% tick history-size)))
|
||||||
|
|
||||||
|
(fn store-position [tick x z yaw]
|
||||||
|
(tset position-history (history-idx tick) {:tick tick :x x :z z :yaw yaw}))
|
||||||
|
|
||||||
|
(fn get-position [tick]
|
||||||
|
(let [entry (. position-history (history-idx tick))]
|
||||||
|
(when (and entry (= entry.tick tick))
|
||||||
|
entry)))
|
||||||
|
|
||||||
|
(fn store-pending-input [tick input]
|
||||||
|
(tset pending-inputs (history-idx tick) {:tick tick :input input}))
|
||||||
|
|
||||||
|
(fn get-pending-input [tick]
|
||||||
|
(let [entry (. pending-inputs (history-idx tick))]
|
||||||
|
(when (and entry (= entry.tick tick))
|
||||||
|
entry.input)))
|
||||||
|
|
||||||
|
(fn apply-movement [x z yaw input]
|
||||||
|
(var new-x x)
|
||||||
|
(var new-z z)
|
||||||
|
|
||||||
|
(let [move-forward (or input.move_y 0)
|
||||||
|
move-right (or input.move_x 0)]
|
||||||
|
(when (or (not= move-forward 0) (not= move-right 0))
|
||||||
|
(let [forward-x (- (math.sin yaw))
|
||||||
|
forward-z (- (math.cos yaw))
|
||||||
|
right-x (math.cos yaw)
|
||||||
|
right-z (- (math.sin yaw))
|
||||||
|
len (math.sqrt (+ (* move-forward move-forward) (* move-right move-right)))
|
||||||
|
norm-forward (/ move-forward len)
|
||||||
|
norm-right (/ move-right len)
|
||||||
|
move-delta (* move-speed sim-dt)]
|
||||||
|
(set new-x (+ new-x (* move-delta (+ (* forward-x norm-forward) (* right-x norm-right)))))
|
||||||
|
(set new-z (+ new-z (* move-delta (+ (* forward-z norm-forward) (* right-z norm-right))))))))
|
||||||
|
|
||||||
|
(values new-x new-z))
|
||||||
|
(fn init []
|
||||||
|
(pxl8.set_relative_mouse_mode true)
|
||||||
|
(pxl8.set_palette palette 256)
|
||||||
|
(pxl8.set_colormap colormap 16384)
|
||||||
|
(for [i 0 7]
|
||||||
|
(let [t (/ i 7)
|
||||||
|
r 0xFF
|
||||||
|
g (math.floor (+ 0x60 (* t (- 0xE0 0x60))))
|
||||||
|
b (math.floor (+ 0x10 (* t (- 0x80 0x10))))]
|
||||||
|
(pxl8.set_palette_rgb (+ FIREBALL_COLOR i) r g b)))
|
||||||
|
(sky.update-gradient 1 2 6 6 10 18)
|
||||||
|
(pxl8.update_palette_deps)
|
||||||
|
(set camera (pxl8.create_camera_3d))
|
||||||
|
(set world (pxl8.create_world))
|
||||||
|
(sky.generate-stars 12345)
|
||||||
|
(create-fireball-mesh)
|
||||||
|
|
||||||
|
(set network (net.Net.new {:port 7777}))
|
||||||
|
(when network
|
||||||
|
(network:connect)
|
||||||
|
(network:spawn cam-x cam-y cam-z cam-yaw cam-pitch))
|
||||||
|
|
||||||
|
(let [result (world:generate {
|
||||||
|
:type pxl8.PROCGEN_ROOMS
|
||||||
|
:width 64
|
||||||
|
:height 64
|
||||||
|
:seed 42
|
||||||
|
:min_room_size 5
|
||||||
|
:max_room_size 10
|
||||||
|
:num_rooms 20})]
|
||||||
|
(if (< result 0)
|
||||||
|
(pxl8.error (.. "Failed to generate rooms - result: " result))
|
||||||
|
(let [floor-tex (textures.mossy-cobblestone 44444 STONE_FLOOR_START MOSS_COLOR)
|
||||||
|
wall-tex (textures.ashlar-wall 55555 STONE_WALL_START MOSS_COLOR)
|
||||||
|
sky-tex (pxl8.create_texture [0] 1 1)]
|
||||||
|
|
||||||
|
(let [result (world:apply_textures [
|
||||||
|
{:name "floor"
|
||||||
|
:texture_id floor-tex
|
||||||
|
:rule (fn [normal] (> normal.y 0.7))}
|
||||||
|
{:name "ceiling"
|
||||||
|
:texture_id sky-tex
|
||||||
|
:rule (fn [normal] (< normal.y -0.7))}
|
||||||
|
{:name "wall"
|
||||||
|
:texture_id wall-tex
|
||||||
|
:rule (fn [normal] (and (<= normal.y 0.7) (>= normal.y -0.7)))}])]
|
||||||
|
(when (< result 0)
|
||||||
|
(pxl8.error (.. "Failed to apply textures - result: " result))))))))
|
||||||
|
|
||||||
|
(fn sample-input []
|
||||||
|
(var move-forward 0)
|
||||||
|
(var move-right 0)
|
||||||
|
|
||||||
|
(when (pxl8.key_pressed "`")
|
||||||
|
(set auto-run? (not auto-run?))
|
||||||
|
(when (and auto-run? (pxl8.key_down "w"))
|
||||||
|
(set auto-run-cancel-key "w")))
|
||||||
|
(when (and auto-run? (not auto-run-cancel-key) (or (pxl8.key_down "w") (pxl8.key_down "s")))
|
||||||
|
(set auto-run? false)
|
||||||
|
(when (pxl8.key_down "s")
|
||||||
|
(set auto-run-cancel-key "s")))
|
||||||
|
(when (and auto-run-cancel-key (not (pxl8.key_down auto-run-cancel-key)))
|
||||||
|
(set auto-run-cancel-key nil))
|
||||||
|
|
||||||
|
(when (or (pxl8.key_down "w") auto-run?)
|
||||||
|
(set move-forward (+ move-forward 1)))
|
||||||
|
(when (and (pxl8.key_down "s") (not= auto-run-cancel-key "s"))
|
||||||
|
(set move-forward (- move-forward 1)))
|
||||||
|
(when (pxl8.key_down "a")
|
||||||
|
(set move-right (- move-right 1)))
|
||||||
|
(when (pxl8.key_down "d")
|
||||||
|
(set move-right (+ move-right 1)))
|
||||||
|
|
||||||
|
{:move_x move-right
|
||||||
|
:move_y move-forward
|
||||||
|
:look_dx (pxl8.mouse_dx)
|
||||||
|
:look_dy (pxl8.mouse_dy)})
|
||||||
|
|
||||||
|
(fn reconcile [server-tick server-x server-z]
|
||||||
|
(let [predicted (get-position server-tick)]
|
||||||
|
(when predicted
|
||||||
|
(let [dx (- predicted.x server-x)
|
||||||
|
dz (- predicted.z server-z)
|
||||||
|
error (math.sqrt (+ (* dx dx) (* dz dz)))]
|
||||||
|
(when (> error correction-threshold)
|
||||||
|
(set cam-x server-x)
|
||||||
|
(set cam-z server-z)
|
||||||
|
|
||||||
|
(for [t (+ server-tick 1) client-tick]
|
||||||
|
(let [input (get-pending-input t)
|
||||||
|
hist (get-position t)]
|
||||||
|
(when (and input hist)
|
||||||
|
(let [(new-x new-z) (apply-movement cam-x cam-z hist.yaw input)]
|
||||||
|
(set cam-x new-x)
|
||||||
|
(set cam-z new-z)
|
||||||
|
(store-position t cam-x cam-z hist.yaw))))))))))
|
||||||
|
|
||||||
|
(fn update [dt]
|
||||||
|
(set last-dt dt)
|
||||||
|
(let [fps (pxl8.get_fps)]
|
||||||
|
(set fps-sample-count (+ fps-sample-count 1))
|
||||||
|
(set fps-avg (+ (* fps-avg (/ (- fps-sample-count 1) fps-sample-count))
|
||||||
|
(/ fps fps-sample-count)))
|
||||||
|
(when (>= fps-sample-count 120)
|
||||||
|
(set fps-sample-count 0)
|
||||||
|
(set fps-avg 0)))
|
||||||
|
|
||||||
|
(when (world:is_loaded)
|
||||||
|
(let [input (sample-input)
|
||||||
|
grid-max (* grid-size cell-size)
|
||||||
|
movement-yaw cam-yaw]
|
||||||
|
|
||||||
|
(set time-accumulator (+ time-accumulator dt))
|
||||||
|
|
||||||
|
(while (>= time-accumulator sim-dt)
|
||||||
|
(set time-accumulator (- time-accumulator sim-dt))
|
||||||
|
(set client-tick (+ client-tick 1))
|
||||||
|
|
||||||
|
(store-pending-input client-tick input)
|
||||||
|
|
||||||
|
(let [(new-x new-z) (apply-movement cam-x cam-z movement-yaw input)]
|
||||||
|
(when (and (>= new-x 0) (<= new-x grid-max)
|
||||||
|
(>= new-z 0) (<= new-z grid-max))
|
||||||
|
(let [(resolved-x _ resolved-z) (world:resolve_collision cam-x cam-y cam-z new-x cam-y new-z 5)]
|
||||||
|
(set cam-x resolved-x)
|
||||||
|
(set cam-z resolved-z)))
|
||||||
|
|
||||||
|
(store-position client-tick cam-x cam-z movement-yaw)))
|
||||||
|
|
||||||
|
(when cursor-look?
|
||||||
|
(set cam-yaw (- cam-yaw (* input.look_dx cursor-sensitivity)))
|
||||||
|
(set cam-pitch (math.max (- max-pitch)
|
||||||
|
(math.min max-pitch
|
||||||
|
(- cam-pitch (* input.look_dy cursor-sensitivity))))))
|
||||||
|
|
||||||
|
(when (and (not cursor-look?) (pxl8.key_down "up"))
|
||||||
|
(set cam-pitch (math.min max-pitch (+ cam-pitch (* turn-speed dt)))))
|
||||||
|
(when (and (not cursor-look?) (pxl8.key_down "down"))
|
||||||
|
(set cam-pitch (math.max (- max-pitch) (- cam-pitch (* turn-speed dt)))))
|
||||||
|
(when (and (not cursor-look?) (pxl8.key_down "left"))
|
||||||
|
(set cam-yaw (- cam-yaw (* turn-speed dt))))
|
||||||
|
(when (and (not cursor-look?) (pxl8.key_down "right"))
|
||||||
|
(set cam-yaw (+ cam-yaw (* turn-speed dt))))
|
||||||
|
|
||||||
|
(when network
|
||||||
|
(let [(ok err) (pcall (fn []
|
||||||
|
(network:send_input {:move_x input.move_x
|
||||||
|
:move_y input.move_y
|
||||||
|
:look_dx input.look_dx
|
||||||
|
:look_dy input.look_dy
|
||||||
|
:yaw movement-yaw
|
||||||
|
:tick client-tick})
|
||||||
|
(network:update dt)
|
||||||
|
(when (network:poll)
|
||||||
|
(let [snapshot (network:snapshot)]
|
||||||
|
(when (and snapshot (> snapshot.tick last-processed-tick))
|
||||||
|
(set last-processed-tick snapshot.tick)
|
||||||
|
(let [player-id (network:player_id)]
|
||||||
|
(when (> player-id 0)
|
||||||
|
(let [curr (network:entity_userdata player-id)]
|
||||||
|
(when curr
|
||||||
|
(let [srv-x (pxl8.unpack_f32_be curr 0)
|
||||||
|
srv-z (pxl8.unpack_f32_be curr 8)]
|
||||||
|
(reconcile snapshot.tick srv-x srv-z)))))))))))]
|
||||||
|
(when (not ok)
|
||||||
|
(pxl8.error (.. "Network error: " err)))))
|
||||||
|
|
||||||
|
(set smooth-cam-x (+ (* smooth-cam-x (- 1 cam-smoothing)) (* cam-x cam-smoothing)))
|
||||||
|
(set smooth-cam-z (+ (* smooth-cam-z (- 1 cam-smoothing)) (* cam-z cam-smoothing)))
|
||||||
|
|
||||||
|
(when (and (pxl8.key_pressed "space") grounded?)
|
||||||
|
(set velocity-y jump-force)
|
||||||
|
(set grounded? false))
|
||||||
|
|
||||||
|
(set velocity-y (+ velocity-y (* gravity dt)))
|
||||||
|
(set cam-y (+ cam-y (* velocity-y dt)))
|
||||||
|
|
||||||
|
(when (<= cam-y ground-y)
|
||||||
|
(when (not grounded?)
|
||||||
|
(set land-squash land-squash-amount))
|
||||||
|
(set cam-y ground-y)
|
||||||
|
(set velocity-y 0)
|
||||||
|
(set grounded? true))
|
||||||
|
|
||||||
|
(when (< land-squash 0)
|
||||||
|
(set land-squash (math.min 0 (+ land-squash (* land-recovery-speed dt)))))
|
||||||
|
|
||||||
|
(let [moving (or (not= input.move_x 0) (not= input.move_y 0))]
|
||||||
|
(if (and moving grounded?)
|
||||||
|
(set bob-time (+ bob-time (* dt bob-speed)))
|
||||||
|
(let [target-phase (* (math.floor (/ bob-time math.pi)) math.pi)]
|
||||||
|
(set bob-time (+ (* bob-time 0.8) (* target-phase 0.2))))))
|
||||||
|
|
||||||
|
(set light-time (+ light-time (* dt 0.5)))
|
||||||
|
(set real-time (+ real-time dt)))))
|
||||||
|
|
||||||
|
(fn frame []
|
||||||
|
(pxl8.clear 1)
|
||||||
|
|
||||||
|
(when (not camera)
|
||||||
|
(pxl8.error "camera is nil!"))
|
||||||
|
|
||||||
|
(when (not world)
|
||||||
|
(pxl8.error "world is nil!"))
|
||||||
|
|
||||||
|
(when (and world (not (world:is_loaded)))
|
||||||
|
(pxl8.text "World not loaded yet..." 5 30 12))
|
||||||
|
|
||||||
|
(when (and camera world (world:is_loaded))
|
||||||
|
(let [bob-offset (* (math.sin bob-time) bob-amount)
|
||||||
|
eye-y (+ cam-y bob-offset land-squash)
|
||||||
|
forward-x (- (math.sin cam-yaw))
|
||||||
|
forward-z (- (math.cos cam-yaw))
|
||||||
|
target-x (+ smooth-cam-x forward-x)
|
||||||
|
target-y (+ eye-y (math.sin cam-pitch))
|
||||||
|
target-z (+ smooth-cam-z forward-z)
|
||||||
|
aspect (/ (pxl8.get_width) (pxl8.get_height))]
|
||||||
|
|
||||||
|
(camera:lookat [smooth-cam-x eye-y smooth-cam-z]
|
||||||
|
[target-x target-y target-z]
|
||||||
|
[0 1 0])
|
||||||
|
(camera:set_perspective 1.047 aspect 1.0 4096.0)
|
||||||
|
|
||||||
|
(let [light-x (+ 1000 (* 50 (math.cos light-time)))
|
||||||
|
light-z (+ 940 (* 50 (math.sin light-time)))
|
||||||
|
light-y 80
|
||||||
|
phase (+ (* light-x 0.01) 1.7)
|
||||||
|
f1 (* 0.08 (math.sin (+ (* real-time 2.5) phase)))
|
||||||
|
f2 (* 0.05 (math.sin (+ (* real-time 4.1) (* phase 0.7))))
|
||||||
|
f3 (* 0.03 (math.sin (+ (* real-time 7.3) (* phase 1.2))))
|
||||||
|
flicker (+ 0.92 f1 f2 f3)
|
||||||
|
light-intensity (math.floor (math.max 0 (math.min 255 (* 255 flicker))))
|
||||||
|
r1 (* 0.06 (math.sin (+ (* real-time 1.8) (* phase 0.5))))
|
||||||
|
r2 (* 0.04 (math.sin (+ (* real-time 3.2) phase)))
|
||||||
|
light-radius (* 150 (+ 0.95 r1 r2))]
|
||||||
|
(pxl8.begin_frame_3d camera {
|
||||||
|
:ambient 30
|
||||||
|
:fog_density 0.0
|
||||||
|
:celestial_dir [0.5 -0.8 0.3]
|
||||||
|
:celestial_intensity 0.5
|
||||||
|
:lights [{:x light-x :y light-y :z light-z
|
||||||
|
:r 255 :g 200 :b 150
|
||||||
|
:intensity light-intensity
|
||||||
|
:radius light-radius}]})
|
||||||
|
(pxl8.clear_depth)
|
||||||
|
|
||||||
|
(sky.update-gradient 1 2 6 6 10 18)
|
||||||
|
(sky.render smooth-cam-x eye-y smooth-cam-z (menu.is-wireframe))
|
||||||
|
(pxl8.clear_depth)
|
||||||
|
|
||||||
|
(world:set_wireframe (menu.is-wireframe) 15)
|
||||||
|
(world:render [smooth-cam-x eye-y smooth-cam-z])
|
||||||
|
|
||||||
|
(when fireball-mesh
|
||||||
|
(let [wire (menu.is-wireframe)]
|
||||||
|
(pxl8.draw_mesh fireball-mesh {:x light-x :y light-y :z light-z
|
||||||
|
:passthrough true
|
||||||
|
:wireframe wire
|
||||||
|
:emissive 1.0})))
|
||||||
|
|
||||||
|
(pxl8.end_frame_3d))
|
||||||
|
|
||||||
|
(sky.render-stars cam-yaw cam-pitch 1.0 last-dt)
|
||||||
|
|
||||||
|
(let [cx (/ (pxl8.get_width) 2)
|
||||||
|
cy (/ (pxl8.get_height) 2)
|
||||||
|
crosshair-size 4
|
||||||
|
crosshair-color 240
|
||||||
|
text-color 251]
|
||||||
|
(pxl8.line (- cx crosshair-size) cy (+ cx crosshair-size) cy crosshair-color)
|
||||||
|
(pxl8.line cx (- cy crosshair-size) cx (+ cy crosshair-size) crosshair-color)
|
||||||
|
|
||||||
|
(pxl8.text (.. "fps: " (string.format "%.0f" fps-avg)) 5 5 text-color)
|
||||||
|
(pxl8.text (.. "pos: " (string.format "%.0f" cam-x) ","
|
||||||
|
(string.format "%.0f" cam-y) ","
|
||||||
|
(string.format "%.0f" cam-z)) 5 15 text-color)))))
|
||||||
|
|
||||||
|
{:init init
|
||||||
|
:update update
|
||||||
|
:frame frame}
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
(local pxl8 (require :pxl8))
|
(local pxl8 (require :pxl8))
|
||||||
|
(local music (require :mod.music))
|
||||||
|
|
||||||
(var paused false)
|
(var paused false)
|
||||||
|
(var wireframe false)
|
||||||
(var gui nil)
|
(var gui nil)
|
||||||
|
|
||||||
(fn init []
|
(fn init []
|
||||||
|
|
@ -36,12 +38,22 @@
|
||||||
(when gui
|
(when gui
|
||||||
(gui:begin_frame)
|
(gui:begin_frame)
|
||||||
|
|
||||||
(pxl8.gui_window 200 100 240 140 "pxl8 demo")
|
(pxl8.gui_window 200 100 240 200 "pxl8 demo")
|
||||||
|
|
||||||
(when (gui:button 1 215 145 210 32 "Resume")
|
(when (gui:button 1 215 140 210 30 "Resume")
|
||||||
(hide))
|
(hide))
|
||||||
|
|
||||||
(when (gui:button 2 215 185 210 32 "Quit")
|
(let [music-label (if (music.is-playing) "Music: On" "Music: Off")]
|
||||||
|
(when (gui:button 3 215 175 210 30 music-label)
|
||||||
|
(if (music.is-playing)
|
||||||
|
(music.stop)
|
||||||
|
(music.start))))
|
||||||
|
|
||||||
|
(let [wire-label (if wireframe "Wireframe: On" "Wireframe: Off")]
|
||||||
|
(when (gui:button 4 215 210 210 30 wire-label)
|
||||||
|
(set wireframe (not wireframe))))
|
||||||
|
|
||||||
|
(when (gui:button 2 215 245 210 30 "Quit")
|
||||||
(pxl8.quit))
|
(pxl8.quit))
|
||||||
|
|
||||||
(if (gui:is_hovering)
|
(if (gui:is_hovering)
|
||||||
|
|
@ -51,6 +63,7 @@
|
||||||
(gui:end_frame)))
|
(gui:end_frame)))
|
||||||
|
|
||||||
{:is-paused (fn [] paused)
|
{:is-paused (fn [] paused)
|
||||||
|
:is-wireframe (fn [] wireframe)
|
||||||
:toggle toggle
|
:toggle toggle
|
||||||
:show show
|
:show show
|
||||||
:hide hide
|
:hide hide
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@
|
||||||
(fn update [dt]
|
(fn update [dt]
|
||||||
(when playing
|
(when playing
|
||||||
(set time (+ time dt))
|
(set time (+ time dt))
|
||||||
(when (>= time step-duration)
|
(while (>= time step-duration)
|
||||||
(set time (- time step-duration))
|
(set time (- time step-duration))
|
||||||
|
|
||||||
(local melody-idx (+ 1 (% step (length melody))))
|
(local melody-idx (+ 1 (% step (length melody))))
|
||||||
|
|
@ -125,4 +125,5 @@
|
||||||
:start start
|
:start start
|
||||||
:stop stop
|
:stop stop
|
||||||
:update update
|
:update update
|
||||||
:is-playing (fn [] playing)}
|
:is-playing (fn [] playing)
|
||||||
|
:get-step (fn [] step)}
|
||||||
|
|
|
||||||
263
demo/mod/palette.fnl
Normal file
263
demo/mod/palette.fnl
Normal file
|
|
@ -0,0 +1,263 @@
|
||||||
|
(require :pxl8)
|
||||||
|
(local ffi (require :ffi))
|
||||||
|
|
||||||
|
(local data (ffi.new "u32[256]" [
|
||||||
|
0x080602
|
||||||
|
0x14120E
|
||||||
|
0x23211E
|
||||||
|
0x31302C
|
||||||
|
0x403E3B
|
||||||
|
0x4B4946
|
||||||
|
0x595755
|
||||||
|
0x676664
|
||||||
|
0x767573
|
||||||
|
0x858382
|
||||||
|
0x939290
|
||||||
|
0xA2A09F
|
||||||
|
0xB0AFAE
|
||||||
|
0xBEBDBC
|
||||||
|
0xCDCCCC
|
||||||
|
0xDADAD9
|
||||||
|
0x594625
|
||||||
|
0x544023
|
||||||
|
0x4F3C24
|
||||||
|
0x4C3A22
|
||||||
|
0x453821
|
||||||
|
0x40321F
|
||||||
|
0x3E2F20
|
||||||
|
0x382D1D
|
||||||
|
0x33291E
|
||||||
|
0x30271F
|
||||||
|
0x2F251D
|
||||||
|
0x2D231E
|
||||||
|
0x28211C
|
||||||
|
0x251F1D
|
||||||
|
0x23201A
|
||||||
|
0x221F1B
|
||||||
|
0x646269
|
||||||
|
0x5F5C61
|
||||||
|
0x5C545A
|
||||||
|
0x584F55
|
||||||
|
0x5B514F
|
||||||
|
0x554A47
|
||||||
|
0x4B413F
|
||||||
|
0x423C36
|
||||||
|
0x463D31
|
||||||
|
0x3E352A
|
||||||
|
0x362E25
|
||||||
|
0x2D2922
|
||||||
|
0x26221D
|
||||||
|
0x1C1916
|
||||||
|
0x151310
|
||||||
|
0x100F0D
|
||||||
|
0x8C6F52
|
||||||
|
0x7E6045
|
||||||
|
0x73553B
|
||||||
|
0x715134
|
||||||
|
0xBA8346
|
||||||
|
0xA1723B
|
||||||
|
0x815C2E
|
||||||
|
0x745226
|
||||||
|
0xCC8926
|
||||||
|
0xBB7E22
|
||||||
|
0x9F6B1F
|
||||||
|
0x875A1C
|
||||||
|
0x6E4918
|
||||||
|
0x553712
|
||||||
|
0x3B250D
|
||||||
|
0x24180A
|
||||||
|
0xA34331
|
||||||
|
0x9B3728
|
||||||
|
0x923220
|
||||||
|
0x882E18
|
||||||
|
0x842B16
|
||||||
|
0x772312
|
||||||
|
0x69200D
|
||||||
|
0x5A1C06
|
||||||
|
0x541C04
|
||||||
|
0x4C1A03
|
||||||
|
0x411701
|
||||||
|
0x371000
|
||||||
|
0x2E0D00
|
||||||
|
0x250B00
|
||||||
|
0x1B0600
|
||||||
|
0x130500
|
||||||
|
0x7D5741
|
||||||
|
0x76503A
|
||||||
|
0x6E4C37
|
||||||
|
0x684833
|
||||||
|
0x5D3F2F
|
||||||
|
0x553A2C
|
||||||
|
0x4F3628
|
||||||
|
0x483024
|
||||||
|
0x4A3126
|
||||||
|
0x483025
|
||||||
|
0x432D22
|
||||||
|
0x3C2C22
|
||||||
|
0x352922
|
||||||
|
0x2C241F
|
||||||
|
0x221C1B
|
||||||
|
0x1A1916
|
||||||
|
0x6E4626
|
||||||
|
0x5F4025
|
||||||
|
0x523924
|
||||||
|
0x433322
|
||||||
|
0x352B1E
|
||||||
|
0x28231A
|
||||||
|
0x1A1A14
|
||||||
|
0x1C1815
|
||||||
|
0x96544B
|
||||||
|
0xAC7369
|
||||||
|
0xB48C86
|
||||||
|
0xBCA7A4
|
||||||
|
0xB1BCC2
|
||||||
|
0x9DB0B9
|
||||||
|
0x8A9FAA
|
||||||
|
0x77929F
|
||||||
|
0x738995
|
||||||
|
0x5E7C8B
|
||||||
|
0x4A6C7D
|
||||||
|
0x345E72
|
||||||
|
0x1F4C64
|
||||||
|
0x19445C
|
||||||
|
0x143C51
|
||||||
|
0x10384B
|
||||||
|
0x183748
|
||||||
|
0x1A3341
|
||||||
|
0x192F39
|
||||||
|
0x152B34
|
||||||
|
0x13262E
|
||||||
|
0x101E23
|
||||||
|
0x0E1519
|
||||||
|
0x0B0E10
|
||||||
|
0x896463
|
||||||
|
0x815C5B
|
||||||
|
0x785352
|
||||||
|
0x6F4C4D
|
||||||
|
0x664444
|
||||||
|
0x5F3C3D
|
||||||
|
0x573738
|
||||||
|
0x523233
|
||||||
|
0x442929
|
||||||
|
0x392324
|
||||||
|
0x2D1D1D
|
||||||
|
0x241414
|
||||||
|
0x1A0E0E
|
||||||
|
0x100909
|
||||||
|
0x070403
|
||||||
|
0x000000
|
||||||
|
0x98936F
|
||||||
|
0x918B68
|
||||||
|
0x887F60
|
||||||
|
0x807759
|
||||||
|
0x797055
|
||||||
|
0x73684D
|
||||||
|
0x6B6146
|
||||||
|
0x63593F
|
||||||
|
0x5B523A
|
||||||
|
0x504834
|
||||||
|
0x423D2D
|
||||||
|
0x373226
|
||||||
|
0x2E2B1F
|
||||||
|
0x222018
|
||||||
|
0x161511
|
||||||
|
0x0E0F0A
|
||||||
|
0x9A554F
|
||||||
|
0x904D48
|
||||||
|
0x87453F
|
||||||
|
0x7D4037
|
||||||
|
0x743831
|
||||||
|
0x693329
|
||||||
|
0x612C24
|
||||||
|
0x572720
|
||||||
|
0x4F231A
|
||||||
|
0x441E16
|
||||||
|
0x391914
|
||||||
|
0x2D150F
|
||||||
|
0x22110D
|
||||||
|
0x1A0B06
|
||||||
|
0x0D0403
|
||||||
|
0x040202
|
||||||
|
0x7F77C0
|
||||||
|
0x7770B5
|
||||||
|
0x6E68A8
|
||||||
|
0x686099
|
||||||
|
0x60588C
|
||||||
|
0x575381
|
||||||
|
0x4E4C72
|
||||||
|
0x454263
|
||||||
|
0x3D3957
|
||||||
|
0x34324A
|
||||||
|
0x2C2940
|
||||||
|
0x242135
|
||||||
|
0x1E1928
|
||||||
|
0x16121D
|
||||||
|
0x0C0A12
|
||||||
|
0x050306
|
||||||
|
0x88AF7B
|
||||||
|
0x81A473
|
||||||
|
0x7B9A67
|
||||||
|
0x728E5D
|
||||||
|
0x6D8553
|
||||||
|
0x61794A
|
||||||
|
0x5B7144
|
||||||
|
0x61734B
|
||||||
|
0x586A3D
|
||||||
|
0x4D5E2D
|
||||||
|
0x465422
|
||||||
|
0x3F4D17
|
||||||
|
0x36420E
|
||||||
|
0x2F3507
|
||||||
|
0x272804
|
||||||
|
0x211F02
|
||||||
|
0x1EF708
|
||||||
|
0x3CE10D
|
||||||
|
0x51CC1B
|
||||||
|
0x64B621
|
||||||
|
0x6DA12C
|
||||||
|
0x69882B
|
||||||
|
0x727F3B
|
||||||
|
0xE4DDCE
|
||||||
|
0xEEE6BA
|
||||||
|
0xEAE290
|
||||||
|
0xE9E26D
|
||||||
|
0xE5DE43
|
||||||
|
0xE3DB20
|
||||||
|
0xE1CC18
|
||||||
|
0xDFB911
|
||||||
|
0xDCA60B
|
||||||
|
0xE8A306
|
||||||
|
0xDF9312
|
||||||
|
0xE17B05
|
||||||
|
0xC86815
|
||||||
|
0xBC5908
|
||||||
|
0xB14805
|
||||||
|
0xA63D07
|
||||||
|
0xB6431E
|
||||||
|
0xAA381A
|
||||||
|
0x9A2E12
|
||||||
|
0x8C270F
|
||||||
|
0x892B17
|
||||||
|
0x762311
|
||||||
|
0x5F1F0D
|
||||||
|
0x491B09
|
||||||
|
0x3B1809
|
||||||
|
0xE50F19
|
||||||
|
0x6A34C4
|
||||||
|
0xE00B28
|
||||||
|
0x2B08C8
|
||||||
|
0x322A33
|
||||||
|
0x281C0E
|
||||||
|
0x2F1E15
|
||||||
|
0xD48067
|
||||||
|
0xC26B4C
|
||||||
|
0x974928
|
||||||
|
0x814123
|
||||||
|
0xD5B3A9
|
||||||
|
0xBE9D93
|
||||||
|
0x9A7B6C
|
||||||
|
0x7F5F51
|
||||||
|
0x8E504C
|
||||||
|
]))
|
||||||
|
|
||||||
|
data
|
||||||
271
demo/mod/sky.fnl
Normal file
271
demo/mod/sky.fnl
Normal file
|
|
@ -0,0 +1,271 @@
|
||||||
|
(local ffi (require :ffi))
|
||||||
|
(local pxl8 (require :pxl8))
|
||||||
|
(local effects (require :pxl8.effects))
|
||||||
|
|
||||||
|
(local SKY_GRADIENT_START 144)
|
||||||
|
(local SKY_GRADIENT_COUNT 16)
|
||||||
|
(local sky-radius 900)
|
||||||
|
(local sky-segments 16)
|
||||||
|
(local sky-rings 16)
|
||||||
|
|
||||||
|
(local NUM_RANDOM_STARS 300)
|
||||||
|
(local NUM_TINY_STARS 7000)
|
||||||
|
(local STAR_SEED 0xDEADBEEF)
|
||||||
|
(local STAR_CYCLE_PERIOD 7200)
|
||||||
|
|
||||||
|
;; Use existing bright palette colors
|
||||||
|
;; Silver/white: indices 14-15 (brightest grays)
|
||||||
|
(local IDX_SILVER 14)
|
||||||
|
;; Warm/torch: indices 216-218 (bright creams/yellows)
|
||||||
|
(local IDX_TORCH 216)
|
||||||
|
;; Blue/magic: indices 176-178 (purples - brightest of the range)
|
||||||
|
(local IDX_MAGIC 176)
|
||||||
|
|
||||||
|
(var sky-mesh nil)
|
||||||
|
(var star-time 0)
|
||||||
|
(var last-gradient-key nil)
|
||||||
|
(var random-stars [])
|
||||||
|
(var tiny-stars [])
|
||||||
|
|
||||||
|
(fn generate-sky-gradient [zenith-r zenith-g zenith-b horizon-r horizon-g horizon-b]
|
||||||
|
(for [i 0 (- SKY_GRADIENT_COUNT 1)]
|
||||||
|
(let [t (/ i (- SKY_GRADIENT_COUNT 1))
|
||||||
|
r (math.floor (+ zenith-r (* t (- horizon-r zenith-r))))
|
||||||
|
g (math.floor (+ zenith-g (* t (- horizon-g zenith-g))))
|
||||||
|
b (math.floor (+ zenith-b (* t (- horizon-b zenith-b))))]
|
||||||
|
(pxl8.set_palette_rgb (+ SKY_GRADIENT_START i) r g b))))
|
||||||
|
|
||||||
|
(fn create-sky-dome []
|
||||||
|
(let [verts []
|
||||||
|
indices []]
|
||||||
|
|
||||||
|
(for [i 0 (- sky-rings 1)]
|
||||||
|
(let [theta0 (* (/ i sky-rings) math.pi 0.5)
|
||||||
|
theta1 (* (/ (+ i 1) sky-rings) math.pi 0.5)
|
||||||
|
sin-t0 (math.sin theta0)
|
||||||
|
cos-t0 (math.cos theta0)
|
||||||
|
sin-t1 (math.sin theta1)
|
||||||
|
cos-t1 (math.cos theta1)
|
||||||
|
y0 (* sky-radius cos-t0)
|
||||||
|
y1 (* sky-radius cos-t1)
|
||||||
|
r0 (* sky-radius sin-t0)
|
||||||
|
r1 (* sky-radius sin-t1)
|
||||||
|
t0 (/ i sky-rings)
|
||||||
|
t1 (/ (+ i 1) sky-rings)
|
||||||
|
c0 (math.floor (+ SKY_GRADIENT_START (* t0 (- SKY_GRADIENT_COUNT 1)) 0.5))
|
||||||
|
c1 (math.floor (+ SKY_GRADIENT_START (* t1 (- SKY_GRADIENT_COUNT 1)) 0.5))]
|
||||||
|
|
||||||
|
(for [j 0 (- sky-segments 1)]
|
||||||
|
(let [phi0 (* (/ j sky-segments) math.pi 2)
|
||||||
|
phi1 (* (/ (+ j 1) sky-segments) math.pi 2)
|
||||||
|
cos-p0 (math.cos phi0)
|
||||||
|
sin-p0 (math.sin phi0)
|
||||||
|
cos-p1 (math.cos phi1)
|
||||||
|
sin-p1 (math.sin phi1)
|
||||||
|
x00 (* r0 cos-p0) z00 (* r0 sin-p0)
|
||||||
|
x01 (* r0 cos-p1) z01 (* r0 sin-p1)
|
||||||
|
x10 (* r1 cos-p0) z10 (* r1 sin-p0)
|
||||||
|
x11 (* r1 cos-p1) z11 (* r1 sin-p1)
|
||||||
|
nx00 (- (* sin-t0 cos-p0)) ny00 (- cos-t0) nz00 (- (* sin-t0 sin-p0))
|
||||||
|
nx01 (- (* sin-t0 cos-p1)) ny01 (- cos-t0) nz01 (- (* sin-t0 sin-p1))
|
||||||
|
nx10 (- (* sin-t1 cos-p0)) ny10 (- cos-t1) nz10 (- (* sin-t1 sin-p0))
|
||||||
|
nx11 (- (* sin-t1 cos-p1)) ny11 (- cos-t1) nz11 (- (* sin-t1 sin-p1))
|
||||||
|
base-idx (# verts)]
|
||||||
|
|
||||||
|
(if (= i 0)
|
||||||
|
(do
|
||||||
|
(table.insert verts {:x x00 :y y0 :z z00 :nx nx00 :ny ny00 :nz nz00 :color c0 :light 255})
|
||||||
|
(table.insert verts {:x x11 :y y1 :z z11 :nx nx11 :ny ny11 :nz nz11 :color c1 :light 255})
|
||||||
|
(table.insert verts {:x x10 :y y1 :z z10 :nx nx10 :ny ny10 :nz nz10 :color c1 :light 255})
|
||||||
|
(table.insert indices base-idx)
|
||||||
|
(table.insert indices (+ base-idx 2))
|
||||||
|
(table.insert indices (+ base-idx 1)))
|
||||||
|
(do
|
||||||
|
(table.insert verts {:x x00 :y y0 :z z00 :nx nx00 :ny ny00 :nz nz00 :color c0 :light 255})
|
||||||
|
(table.insert verts {:x x01 :y y0 :z z01 :nx nx01 :ny ny01 :nz nz01 :color c0 :light 255})
|
||||||
|
(table.insert verts {:x x11 :y y1 :z z11 :nx nx11 :ny ny11 :nz nz11 :color c1 :light 255})
|
||||||
|
(table.insert verts {:x x10 :y y1 :z z10 :nx nx10 :ny ny10 :nz nz10 :color c1 :light 255})
|
||||||
|
(table.insert indices base-idx)
|
||||||
|
(table.insert indices (+ base-idx 3))
|
||||||
|
(table.insert indices (+ base-idx 2))
|
||||||
|
(table.insert indices base-idx)
|
||||||
|
(table.insert indices (+ base-idx 2))
|
||||||
|
(table.insert indices (+ base-idx 1))))))))
|
||||||
|
|
||||||
|
(set sky-mesh (pxl8.create_mesh verts indices))))
|
||||||
|
|
||||||
|
(fn update-gradient [zenith-r zenith-g zenith-b horizon-r horizon-g horizon-b]
|
||||||
|
(let [key (.. zenith-r "," zenith-g "," zenith-b "," horizon-r "," horizon-g "," horizon-b)]
|
||||||
|
(when (not= key last-gradient-key)
|
||||||
|
(generate-sky-gradient zenith-r zenith-g zenith-b horizon-r horizon-g horizon-b)
|
||||||
|
(set last-gradient-key key))))
|
||||||
|
|
||||||
|
(fn galactic-band-factor [dx dy dz]
|
||||||
|
(let [band-len (math.sqrt (+ (* 0.6 0.6) (* 0.3 0.3) (* 0.742 0.742)))
|
||||||
|
bx (/ 0.6 band-len)
|
||||||
|
by (/ 0.3 band-len)
|
||||||
|
bz (/ 0.742 band-len)
|
||||||
|
dist-from-band (math.abs (+ (* dx bx) (* dy by) (* dz bz)))
|
||||||
|
in-band (- 1 (math.min (* dist-from-band 3) 1))]
|
||||||
|
(* in-band in-band)))
|
||||||
|
|
||||||
|
(fn generate-stars []
|
||||||
|
(set random-stars [])
|
||||||
|
(set tiny-stars [])
|
||||||
|
|
||||||
|
;; Generate random stars - use full upper hemisphere (dy > -0.1)
|
||||||
|
(for [i 0 (- NUM_RANDOM_STARS 1)]
|
||||||
|
(let [h1 (pxl8.hash32 (+ STAR_SEED (* i 5)))
|
||||||
|
h2 (pxl8.hash32 (+ STAR_SEED (* i 5) 1))
|
||||||
|
h3 (pxl8.hash32 (+ STAR_SEED (* i 5) 2))
|
||||||
|
h4 (pxl8.hash32 (+ STAR_SEED (* i 5) 3))
|
||||||
|
theta (* (/ h1 0xFFFFFFFF) math.pi 2)
|
||||||
|
phi (math.acos (- 1 (* (/ h2 0xFFFFFFFF) 1.0)))
|
||||||
|
sin-phi (math.sin phi)
|
||||||
|
cos-phi (math.cos phi)
|
||||||
|
dx (* sin-phi (math.cos theta))
|
||||||
|
dy cos-phi
|
||||||
|
dz (* sin-phi (math.sin theta))
|
||||||
|
brightness-raw (/ (% h3 256) 255)
|
||||||
|
brightness (math.floor (+ 60 (* brightness-raw brightness-raw 195)))
|
||||||
|
color-type (% h4 100)
|
||||||
|
color (if (< color-type 8) (+ IDX_TORCH (% (bit.rshift h4 8) 2))
|
||||||
|
(< color-type 16) (+ IDX_MAGIC (% (bit.rshift h4 8) 2))
|
||||||
|
(+ IDX_SILVER (% (bit.rshift h4 8) 2)))]
|
||||||
|
|
||||||
|
(when (> dy -0.1)
|
||||||
|
(table.insert random-stars {:dx dx :dy dy :dz dz
|
||||||
|
:brightness brightness
|
||||||
|
:color color}))))
|
||||||
|
|
||||||
|
(let [tiny-seed (+ STAR_SEED 0xCAFEBABE)]
|
||||||
|
(for [i 0 (- NUM_TINY_STARS 1)]
|
||||||
|
(let [h1 (pxl8.hash32 (+ tiny-seed (* i 4)))
|
||||||
|
h2 (pxl8.hash32 (+ tiny-seed (* i 4) 1))
|
||||||
|
h3 (pxl8.hash32 (+ tiny-seed (* i 4) 2))
|
||||||
|
h4 (pxl8.hash32 (+ tiny-seed (* i 4) 3))
|
||||||
|
theta (* (/ h1 0xFFFFFFFF) math.pi 2)
|
||||||
|
phi (math.acos (- 1 (* (/ h2 0xFFFFFFFF) 1.0)))
|
||||||
|
sin-phi (math.sin phi)
|
||||||
|
cos-phi (math.cos phi)
|
||||||
|
dx (* sin-phi (math.cos theta))
|
||||||
|
dy cos-phi
|
||||||
|
dz (* sin-phi (math.sin theta))
|
||||||
|
band-boost (galactic-band-factor dx dy dz)
|
||||||
|
base-bright (+ 40 (% h3 50))
|
||||||
|
brightness (+ base-bright (math.floor (* band-boost 40)))
|
||||||
|
color-shift (% h4 100)
|
||||||
|
color (if (< color-shift 3) (+ IDX_TORCH (% (bit.rshift h4 8) 2))
|
||||||
|
(< color-shift 12) (+ IDX_MAGIC (% (bit.rshift h4 8) 2))
|
||||||
|
(+ IDX_SILVER (% (bit.rshift h4 8) 2)))]
|
||||||
|
(when (> dy -0.1)
|
||||||
|
(table.insert tiny-stars {:dx dx :dy dy :dz dz
|
||||||
|
:brightness brightness
|
||||||
|
:color color}))))))
|
||||||
|
|
||||||
|
(fn project-direction [dir-x dir-y dir-z yaw pitch cos-rot sin-rot width height]
|
||||||
|
(let [rot-x (- (* dir-x cos-rot) (* dir-z sin-rot))
|
||||||
|
rot-z (+ (* dir-x sin-rot) (* dir-z cos-rot))
|
||||||
|
cos-yaw (math.cos yaw)
|
||||||
|
sin-yaw (math.sin yaw)
|
||||||
|
cos-pitch (math.cos pitch)
|
||||||
|
sin-pitch (math.sin pitch)
|
||||||
|
rotated-x (+ (* rot-x cos-yaw) (* rot-z sin-yaw))
|
||||||
|
rotated-z (+ (* (- rot-x) sin-yaw) (* rot-z cos-yaw))
|
||||||
|
rotated-y (- (* dir-y cos-pitch) (* rotated-z sin-pitch))
|
||||||
|
final-z (+ (* dir-y sin-pitch) (* rotated-z cos-pitch))]
|
||||||
|
(when (> final-z 0.01)
|
||||||
|
(let [fov 1.047
|
||||||
|
aspect (/ width height)
|
||||||
|
half-fov-tan (math.tan (* fov 0.5))
|
||||||
|
ndc-x (/ rotated-x (* final-z half-fov-tan aspect))
|
||||||
|
ndc-y (/ rotated-y (* final-z half-fov-tan))]
|
||||||
|
(when (and (>= ndc-x -1) (<= ndc-x 1) (>= ndc-y -1) (<= ndc-y 1))
|
||||||
|
{:x (math.floor (* (+ 1 ndc-x) 0.5 width))
|
||||||
|
:y (math.floor (* (- 1 ndc-y) 0.5 height))})))))
|
||||||
|
|
||||||
|
(fn render-stars [yaw pitch intensity dt]
|
||||||
|
(set star-time (+ star-time (or dt 0)))
|
||||||
|
(when (> intensity 0)
|
||||||
|
(let [width (pxl8.get_width)
|
||||||
|
height (pxl8.get_height)
|
||||||
|
glows []
|
||||||
|
fade-in (* intensity intensity)
|
||||||
|
time-factor (/ star-time 60)
|
||||||
|
star-rotation (/ (* star-time math.pi 2) STAR_CYCLE_PERIOD)
|
||||||
|
cos-rot (math.cos star-rotation)
|
||||||
|
sin-rot (math.sin star-rotation)]
|
||||||
|
|
||||||
|
(each [i star (ipairs tiny-stars)]
|
||||||
|
(let [screen (project-direction star.dx star.dy star.dz yaw pitch cos-rot sin-rot width height)]
|
||||||
|
(when screen
|
||||||
|
(let [int (math.floor (* star.brightness fade-in))]
|
||||||
|
(when (> int 8)
|
||||||
|
(table.insert glows {:x screen.x :y screen.y
|
||||||
|
:radius 1
|
||||||
|
:intensity int
|
||||||
|
:color star.color
|
||||||
|
:shape effects.GLOW_CIRCLE}))))))
|
||||||
|
|
||||||
|
(each [i star (ipairs random-stars)]
|
||||||
|
(let [screen (project-direction star.dx star.dy star.dz yaw pitch cos-rot sin-rot width height)]
|
||||||
|
(when screen
|
||||||
|
(let [phase (+ (* i 2.137) (* time-factor 3.0))
|
||||||
|
twinkle (+ 0.75 (* 0.25 (math.sin (* phase 6.28))))
|
||||||
|
int (math.floor (* star.brightness fade-in twinkle))]
|
||||||
|
(if (> star.brightness 220)
|
||||||
|
(do
|
||||||
|
(table.insert glows {:x screen.x :y screen.y
|
||||||
|
:radius 3
|
||||||
|
:intensity (math.floor (* int 1.5))
|
||||||
|
:color star.color
|
||||||
|
:shape effects.GLOW_DIAMOND})
|
||||||
|
(table.insert glows {:x screen.x :y screen.y
|
||||||
|
:radius 5
|
||||||
|
:intensity (math.floor (/ int 2))
|
||||||
|
:color star.color
|
||||||
|
:shape effects.GLOW_CIRCLE}))
|
||||||
|
(> star.brightness 180)
|
||||||
|
(do
|
||||||
|
(table.insert glows {:x screen.x :y screen.y
|
||||||
|
:radius 2
|
||||||
|
:intensity int
|
||||||
|
:color star.color
|
||||||
|
:shape effects.GLOW_DIAMOND})
|
||||||
|
(table.insert glows {:x screen.x :y screen.y
|
||||||
|
:radius 4
|
||||||
|
:intensity (math.floor (/ int 3))
|
||||||
|
:color star.color
|
||||||
|
:shape effects.GLOW_CIRCLE}))
|
||||||
|
(> star.brightness 120)
|
||||||
|
(do
|
||||||
|
(table.insert glows {:x screen.x :y screen.y
|
||||||
|
:radius 2
|
||||||
|
:intensity (math.floor (* int 0.67))
|
||||||
|
:color star.color
|
||||||
|
:shape effects.GLOW_DIAMOND})
|
||||||
|
(table.insert glows {:x screen.x :y screen.y
|
||||||
|
:radius 3
|
||||||
|
:intensity (math.floor (/ int 4))
|
||||||
|
:color star.color
|
||||||
|
:shape effects.GLOW_CIRCLE}))
|
||||||
|
(table.insert glows {:x screen.x :y screen.y
|
||||||
|
:radius 2
|
||||||
|
:intensity (math.floor (* int 0.5))
|
||||||
|
:color star.color
|
||||||
|
:shape effects.GLOW_CIRCLE}))))))
|
||||||
|
|
||||||
|
(when (> (length glows) 0)
|
||||||
|
(effects.glows glows)))))
|
||||||
|
|
||||||
|
(fn render [cam-x cam-y cam-z wireframe]
|
||||||
|
(when (not sky-mesh) (create-sky-dome))
|
||||||
|
(when sky-mesh
|
||||||
|
(pxl8.draw_mesh sky-mesh {:x cam-x :y cam-y :z cam-z :passthrough true :wireframe wireframe})))
|
||||||
|
|
||||||
|
{:render render
|
||||||
|
:render-stars render-stars
|
||||||
|
:generate-stars generate-stars
|
||||||
|
:update-gradient update-gradient
|
||||||
|
:SKY_GRADIENT_START SKY_GRADIENT_START
|
||||||
|
:SKY_GRADIENT_COUNT SKY_GRADIENT_COUNT}
|
||||||
168
demo/mod/textures.fnl
Normal file
168
demo/mod/textures.fnl
Normal file
|
|
@ -0,0 +1,168 @@
|
||||||
|
(local pxl8 (require :pxl8))
|
||||||
|
(local procgen (require :pxl8.procgen))
|
||||||
|
|
||||||
|
(local textures {})
|
||||||
|
|
||||||
|
(fn build-graph [seed builder]
|
||||||
|
(let [g (pxl8.create_graph 128)
|
||||||
|
x (g:add_node procgen.OP_INPUT_X 0 0 0 0 0)
|
||||||
|
y (g:add_node procgen.OP_INPUT_Y 0 0 0 0 0)
|
||||||
|
ctx {:graph g :x x :y y}]
|
||||||
|
(g:set_seed seed)
|
||||||
|
(let [output (builder ctx)]
|
||||||
|
(g:set_output output)
|
||||||
|
g)))
|
||||||
|
|
||||||
|
(fn const [ctx val]
|
||||||
|
(ctx.graph:add_node procgen.OP_CONST 0 0 0 0 val))
|
||||||
|
|
||||||
|
(fn add [ctx a b]
|
||||||
|
(ctx.graph:add_node procgen.OP_ADD a b 0 0 0))
|
||||||
|
|
||||||
|
(fn sub [ctx a b]
|
||||||
|
(ctx.graph:add_node procgen.OP_SUB a b 0 0 0))
|
||||||
|
|
||||||
|
(fn mul [ctx a b]
|
||||||
|
(ctx.graph:add_node procgen.OP_MUL a b 0 0 0))
|
||||||
|
|
||||||
|
(fn div [ctx a b]
|
||||||
|
(ctx.graph:add_node procgen.OP_DIV a b 0 0 0))
|
||||||
|
|
||||||
|
(fn min-op [ctx a b]
|
||||||
|
(ctx.graph:add_node procgen.OP_MIN a b 0 0 0))
|
||||||
|
|
||||||
|
(fn max-op [ctx a b]
|
||||||
|
(ctx.graph:add_node procgen.OP_MAX a b 0 0 0))
|
||||||
|
|
||||||
|
(fn abs [ctx a]
|
||||||
|
(ctx.graph:add_node procgen.OP_ABS a 0 0 0 0))
|
||||||
|
|
||||||
|
(fn floor [ctx a]
|
||||||
|
(ctx.graph:add_node procgen.OP_FLOOR a 0 0 0 0))
|
||||||
|
|
||||||
|
(fn fract [ctx a]
|
||||||
|
(ctx.graph:add_node procgen.OP_FRACT a 0 0 0 0))
|
||||||
|
|
||||||
|
(fn lerp [ctx a b t]
|
||||||
|
(ctx.graph:add_node procgen.OP_LERP a b t 0 0))
|
||||||
|
|
||||||
|
(fn clamp [ctx val lo hi]
|
||||||
|
(ctx.graph:add_node procgen.OP_CLAMP val lo hi 0 0))
|
||||||
|
|
||||||
|
(fn select [ctx cond a b]
|
||||||
|
(ctx.graph:add_node procgen.OP_SELECT a b cond 0 0))
|
||||||
|
|
||||||
|
(fn smoothstep [ctx edge0 edge1 x]
|
||||||
|
(ctx.graph:add_node procgen.OP_SMOOTHSTEP edge0 edge1 x 0 0))
|
||||||
|
|
||||||
|
(fn noise-value [ctx scale]
|
||||||
|
(let [s (const ctx scale)]
|
||||||
|
(ctx.graph:add_node procgen.OP_NOISE_VALUE ctx.x ctx.y s 0 0)))
|
||||||
|
|
||||||
|
(fn noise-perlin [ctx scale]
|
||||||
|
(let [s (const ctx scale)]
|
||||||
|
(ctx.graph:add_node procgen.OP_NOISE_PERLIN ctx.x ctx.y s 0 0)))
|
||||||
|
|
||||||
|
(fn noise-fbm [ctx octaves scale persistence]
|
||||||
|
(let [s (const ctx scale)
|
||||||
|
p (const ctx persistence)]
|
||||||
|
(ctx.graph:add_node procgen.OP_NOISE_FBM ctx.x ctx.y s p octaves)))
|
||||||
|
|
||||||
|
(fn noise-ridged [ctx octaves scale persistence]
|
||||||
|
(let [s (const ctx scale)
|
||||||
|
p (const ctx persistence)]
|
||||||
|
(ctx.graph:add_node procgen.OP_NOISE_RIDGED ctx.x ctx.y s p octaves)))
|
||||||
|
|
||||||
|
(fn noise-turbulence [ctx octaves scale persistence]
|
||||||
|
(let [s (const ctx scale)
|
||||||
|
p (const ctx persistence)]
|
||||||
|
(ctx.graph:add_node procgen.OP_NOISE_TURBULENCE ctx.x ctx.y s p octaves)))
|
||||||
|
|
||||||
|
(fn voronoi-cell [ctx scale]
|
||||||
|
(let [s (const ctx scale)]
|
||||||
|
(ctx.graph:add_node procgen.OP_VORONOI_CELL ctx.x ctx.y s 0 0)))
|
||||||
|
|
||||||
|
(fn voronoi-edge [ctx scale]
|
||||||
|
(let [s (const ctx scale)]
|
||||||
|
(ctx.graph:add_node procgen.OP_VORONOI_EDGE ctx.x ctx.y s 0 0)))
|
||||||
|
|
||||||
|
(fn voronoi-id [ctx scale]
|
||||||
|
(let [s (const ctx scale)]
|
||||||
|
(ctx.graph:add_node procgen.OP_VORONOI_ID ctx.x ctx.y s 0 0)))
|
||||||
|
|
||||||
|
(fn gradient-linear [ctx angle]
|
||||||
|
(let [a (const ctx angle)]
|
||||||
|
(ctx.graph:add_node procgen.OP_GRADIENT_LINEAR ctx.x ctx.y a 0 0)))
|
||||||
|
|
||||||
|
(fn gradient-radial [ctx cx cy]
|
||||||
|
(let [center-x (const ctx cx)
|
||||||
|
center-y (const ctx cy)]
|
||||||
|
(ctx.graph:add_node procgen.OP_GRADIENT_RADIAL ctx.x ctx.y center-x center-y 0)))
|
||||||
|
|
||||||
|
(fn quantize [ctx val base range]
|
||||||
|
(let [r (const ctx range)]
|
||||||
|
(ctx.graph:add_node procgen.OP_QUANTIZE val r 0 0 base)))
|
||||||
|
|
||||||
|
(fn textures.mossy-cobblestone [seed base-color moss-color]
|
||||||
|
(let [g (build-graph seed
|
||||||
|
(fn [ctx]
|
||||||
|
(let [cell (voronoi-cell ctx 6)
|
||||||
|
edge (voronoi-edge ctx 6)
|
||||||
|
mortar-threshold (const ctx 0.05)
|
||||||
|
is-mortar (sub ctx mortar-threshold edge)
|
||||||
|
mortar-color (const ctx (- base-color 2))
|
||||||
|
stone-detail (noise-value ctx 48)
|
||||||
|
stone-base (mul ctx cell (const ctx 0.6))
|
||||||
|
stone-combined (add ctx stone-base (mul ctx stone-detail (const ctx 0.4)))
|
||||||
|
stone-quant (quantize ctx stone-combined base-color 8)
|
||||||
|
moss-pattern (noise-fbm ctx 4 10 0.5)
|
||||||
|
moss-detail (noise-value ctx 64)
|
||||||
|
moss-var (add ctx (mul ctx moss-pattern (const ctx 0.7))
|
||||||
|
(mul ctx moss-detail (const ctx 0.3)))
|
||||||
|
moss-threshold (const ctx 0.55)
|
||||||
|
has-moss (sub ctx moss-pattern moss-threshold)
|
||||||
|
moss-quant (quantize ctx moss-var moss-color 6)
|
||||||
|
stone-or-moss (select ctx has-moss moss-quant stone-quant)]
|
||||||
|
(select ctx is-mortar mortar-color stone-or-moss))))]
|
||||||
|
(let [tex-id (g:eval_texture 64 64)]
|
||||||
|
(g:destroy)
|
||||||
|
tex-id)))
|
||||||
|
|
||||||
|
(fn textures.ashlar-wall [seed base-color moss-color]
|
||||||
|
(let [g (build-graph seed
|
||||||
|
(fn [ctx]
|
||||||
|
(let [cell (voronoi-cell ctx 5)
|
||||||
|
edge (voronoi-edge ctx 5)
|
||||||
|
cell-id (voronoi-id ctx 5)
|
||||||
|
mortar-threshold (const ctx 0.12)
|
||||||
|
is-mortar (sub ctx mortar-threshold edge)
|
||||||
|
stone-detail (noise-fbm ctx 3 32 0.5)
|
||||||
|
stone-tint (mul ctx cell-id (const ctx 0.4))
|
||||||
|
stone-shade (mul ctx cell (const ctx 0.3))
|
||||||
|
stone-combined (add ctx stone-detail (add ctx stone-tint stone-shade))
|
||||||
|
stone-quant (quantize ctx stone-combined base-color 10)
|
||||||
|
crack-moss (noise-fbm ctx 3 16 0.5)
|
||||||
|
moss-in-crack (mul ctx crack-moss (sub ctx (const ctx 0.2) edge))
|
||||||
|
moss-threshold (const ctx 0.06)
|
||||||
|
has-moss (sub ctx moss-in-crack moss-threshold)
|
||||||
|
moss-quant (quantize ctx crack-moss moss-color 4)
|
||||||
|
mortar-color (const ctx (+ base-color 1))
|
||||||
|
stone-or-moss (select ctx has-moss moss-quant stone-quant)]
|
||||||
|
(select ctx is-mortar mortar-color stone-or-moss))))]
|
||||||
|
(let [tex-id (g:eval_texture 64 64)]
|
||||||
|
(g:destroy)
|
||||||
|
tex-id)))
|
||||||
|
|
||||||
|
(fn textures.gradient-sky [seed zenith-color horizon-color]
|
||||||
|
(let [g (build-graph seed
|
||||||
|
(fn [ctx]
|
||||||
|
(let [grad (gradient-linear ctx (* math.pi 0.5))
|
||||||
|
zenith (const ctx zenith-color)
|
||||||
|
horizon (const ctx horizon-color)
|
||||||
|
range (const ctx (- horizon-color zenith-color))]
|
||||||
|
(quantize ctx grad zenith-color (- horizon-color zenith-color)))))]
|
||||||
|
(let [tex-id (g:eval_texture 64 64)]
|
||||||
|
(g:destroy)
|
||||||
|
tex-id)))
|
||||||
|
|
||||||
|
textures
|
||||||
75
demo/mod/vfx.fnl
Normal file
75
demo/mod/vfx.fnl
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
(local vfx {})
|
||||||
|
|
||||||
|
(fn vfx.explosion [ps x y ?opts]
|
||||||
|
(let [opts (or ?opts {})
|
||||||
|
color (or opts.color 208)
|
||||||
|
force (or opts.force 200)]
|
||||||
|
(ps:set_position x y)
|
||||||
|
(ps:set_colors color (+ color 15))
|
||||||
|
(ps:set_velocity (- force) force (- force) force)
|
||||||
|
(ps:set_gravity 0 100)
|
||||||
|
(ps:set_life 0.3 0.8)
|
||||||
|
(ps:set_size 1 3)
|
||||||
|
(ps:set_drag 0.98)
|
||||||
|
(ps:set_spawn_rate 0)
|
||||||
|
(ps:emit (or opts.count 50))))
|
||||||
|
|
||||||
|
(fn vfx.fire [ps x y ?opts]
|
||||||
|
(let [opts (or ?opts {})
|
||||||
|
width (or opts.width 50)
|
||||||
|
color (or opts.color 208)]
|
||||||
|
(ps:set_position x y)
|
||||||
|
(ps:set_spread width 5)
|
||||||
|
(ps:set_colors color (+ color 15))
|
||||||
|
(ps:set_velocity -20 20 -80 -40)
|
||||||
|
(ps:set_gravity 0 -30)
|
||||||
|
(ps:set_life 0.5 1.5)
|
||||||
|
(ps:set_size 1 2)
|
||||||
|
(ps:set_turbulence 30)
|
||||||
|
(ps:set_drag 0.95)
|
||||||
|
(ps:set_spawn_rate (or opts.rate 50))))
|
||||||
|
|
||||||
|
(fn vfx.rain [ps width ?opts]
|
||||||
|
(let [opts (or ?opts {})
|
||||||
|
wind (or opts.wind 0)
|
||||||
|
color (or opts.color 153)]
|
||||||
|
(ps:set_position (/ width 2) -10)
|
||||||
|
(ps:set_spread width 0)
|
||||||
|
(ps:set_colors color (+ color 3))
|
||||||
|
(ps:set_velocity (- wind 10) (+ wind 10) 300 400)
|
||||||
|
(ps:set_gravity 0 200)
|
||||||
|
(ps:set_life 1 2)
|
||||||
|
(ps:set_size 1 1)
|
||||||
|
(ps:set_drag 1)
|
||||||
|
(ps:set_spawn_rate (or opts.rate 100))))
|
||||||
|
|
||||||
|
(fn vfx.smoke [ps x y ?opts]
|
||||||
|
(let [opts (or ?opts {})
|
||||||
|
color (or opts.color 248)]
|
||||||
|
(ps:set_position x y)
|
||||||
|
(ps:set_spread 10 5)
|
||||||
|
(ps:set_colors color (+ color 7))
|
||||||
|
(ps:set_velocity -15 15 -30 -10)
|
||||||
|
(ps:set_gravity 0 -20)
|
||||||
|
(ps:set_life 1 3)
|
||||||
|
(ps:set_size 2 4)
|
||||||
|
(ps:set_turbulence 20)
|
||||||
|
(ps:set_drag 0.98)
|
||||||
|
(ps:set_spawn_rate (or opts.rate 20))))
|
||||||
|
|
||||||
|
(fn vfx.snow [ps width ?opts]
|
||||||
|
(let [opts (or ?opts {})
|
||||||
|
wind (or opts.wind 10)
|
||||||
|
color (or opts.color 15)]
|
||||||
|
(ps:set_position (/ width 2) -10)
|
||||||
|
(ps:set_spread width 0)
|
||||||
|
(ps:set_colors color color)
|
||||||
|
(ps:set_velocity (- wind 20) (+ wind 20) 30 60)
|
||||||
|
(ps:set_gravity 0 10)
|
||||||
|
(ps:set_life 3 6)
|
||||||
|
(ps:set_size 1 2)
|
||||||
|
(ps:set_turbulence 15)
|
||||||
|
(ps:set_drag 0.99)
|
||||||
|
(ps:set_spawn_rate (or opts.rate 30))))
|
||||||
|
|
||||||
|
vfx
|
||||||
|
|
@ -1,224 +0,0 @@
|
||||||
(local pxl8 (require :pxl8))
|
|
||||||
|
|
||||||
(local bob-amount 4.0)
|
|
||||||
(local bob-speed 8.0)
|
|
||||||
(local cam-smoothing 0.25)
|
|
||||||
(local cell-size 64)
|
|
||||||
(local cursor-sensitivity 0.008)
|
|
||||||
(local gravity -800)
|
|
||||||
(local grid-size 64)
|
|
||||||
(local ground-y 64)
|
|
||||||
(local jump-force 175)
|
|
||||||
(local land-recovery-speed 20)
|
|
||||||
(local land-squash-amount -4)
|
|
||||||
(local max-pitch 1.5)
|
|
||||||
(local move-speed 200)
|
|
||||||
(local turn-speed 2.0)
|
|
||||||
|
|
||||||
(var auto-run? false)
|
|
||||||
(var auto-run-cancel-key nil)
|
|
||||||
(var bob-time 0)
|
|
||||||
(var cam-pitch 0)
|
|
||||||
(var cam-x 1000)
|
|
||||||
(var cam-y 64)
|
|
||||||
(var cam-yaw 0)
|
|
||||||
(var cam-z 1000)
|
|
||||||
(var camera nil)
|
|
||||||
(var cursor-look? true)
|
|
||||||
(var grounded? true)
|
|
||||||
(var land-squash 0)
|
|
||||||
(var smooth-cam-x 1000)
|
|
||||||
(var smooth-cam-z 1000)
|
|
||||||
(var velocity-y 0)
|
|
||||||
(var world nil)
|
|
||||||
|
|
||||||
(fn init []
|
|
||||||
(set camera (pxl8.create_camera_3d))
|
|
||||||
(set world (pxl8.create_world))
|
|
||||||
(let [result (world:generate {
|
|
||||||
:type pxl8.PROCGEN_ROOMS
|
|
||||||
:width 64
|
|
||||||
:height 64
|
|
||||||
:seed 42
|
|
||||||
:min_room_size 5
|
|
||||||
:max_room_size 10
|
|
||||||
:num_rooms 20})]
|
|
||||||
(if (< result 0)
|
|
||||||
(pxl8.error (.. "Failed to generate rooms - result: " result))
|
|
||||||
(let [floor-tex (pxl8.procgen_tex {:name "floor"
|
|
||||||
:seed 11111
|
|
||||||
:width 64
|
|
||||||
:height 64
|
|
||||||
:base_color 19})
|
|
||||||
ceiling-tex (pxl8.procgen_tex {:name "ceiling"
|
|
||||||
:seed 22222
|
|
||||||
:width 64
|
|
||||||
:height 64
|
|
||||||
:base_color 1})
|
|
||||||
wall-tex (pxl8.procgen_tex {:name "wall"
|
|
||||||
:seed 12345
|
|
||||||
:width 64
|
|
||||||
:height 64
|
|
||||||
:base_color 4})]
|
|
||||||
|
|
||||||
(let [result (world:apply_textures [
|
|
||||||
{:name "floor"
|
|
||||||
:texture_id floor-tex
|
|
||||||
:rule (fn [normal] (> normal.y 0.7))}
|
|
||||||
{:name "ceiling"
|
|
||||||
:texture_id ceiling-tex
|
|
||||||
:rule (fn [normal] (< normal.y -0.7))}
|
|
||||||
{:name "wall"
|
|
||||||
:texture_id wall-tex
|
|
||||||
:rule (fn [normal] (and (<= normal.y 0.7) (>= normal.y -0.7)))}])]
|
|
||||||
(when (< result 0)
|
|
||||||
(pxl8.error (.. "Failed to apply textures - result: " result))))))))
|
|
||||||
|
|
||||||
(fn update [dt]
|
|
||||||
(when (pxl8.key_pressed "`")
|
|
||||||
(set auto-run? (not auto-run?))
|
|
||||||
(when (and auto-run? (pxl8.key_down "w"))
|
|
||||||
(set auto-run-cancel-key "w")))
|
|
||||||
(when (and auto-run? (not auto-run-cancel-key) (or (pxl8.key_down "w") (pxl8.key_down "s")))
|
|
||||||
(set auto-run? false)
|
|
||||||
(when (pxl8.key_down "s")
|
|
||||||
(set auto-run-cancel-key "s")))
|
|
||||||
(when (and auto-run-cancel-key (not (pxl8.key_down auto-run-cancel-key)))
|
|
||||||
(set auto-run-cancel-key nil))
|
|
||||||
|
|
||||||
(when (world:is_loaded)
|
|
||||||
(let [forward-x (- (math.sin cam-yaw))
|
|
||||||
forward-z (- (math.cos cam-yaw))
|
|
||||||
right-x (math.cos cam-yaw)
|
|
||||||
right-z (- (math.sin cam-yaw))
|
|
||||||
grid-max (* grid-size cell-size)
|
|
||||||
move-delta (* move-speed dt)]
|
|
||||||
|
|
||||||
(var moving false)
|
|
||||||
(var move-forward 0)
|
|
||||||
(var move-right 0)
|
|
||||||
|
|
||||||
(when (or (pxl8.key_down "w") auto-run?)
|
|
||||||
(set move-forward (+ move-forward 1)))
|
|
||||||
|
|
||||||
(when (and (pxl8.key_down "s") (not= auto-run-cancel-key "s"))
|
|
||||||
(set move-forward (- move-forward 1)))
|
|
||||||
|
|
||||||
(when (pxl8.key_down "a")
|
|
||||||
(set move-right (- move-right 1)))
|
|
||||||
|
|
||||||
(when (pxl8.key_down "d")
|
|
||||||
(set move-right (+ move-right 1)))
|
|
||||||
|
|
||||||
(set moving (or (not= move-forward 0) (not= move-right 0)))
|
|
||||||
|
|
||||||
(var new-x cam-x)
|
|
||||||
(var new-z cam-z)
|
|
||||||
|
|
||||||
(when moving
|
|
||||||
(let [len (math.sqrt (+ (* move-forward move-forward) (* move-right move-right)))
|
|
||||||
norm-forward (/ move-forward len)
|
|
||||||
norm-right (/ move-right len)]
|
|
||||||
(set new-x (+ new-x (* move-delta (+ (* forward-x norm-forward) (* right-x norm-right)))))
|
|
||||||
(set new-z (+ new-z (* move-delta (+ (* forward-z norm-forward) (* right-z norm-right)))))))
|
|
||||||
|
|
||||||
(when (and (>= new-x 0) (<= new-x grid-max)
|
|
||||||
(>= new-z 0) (<= new-z grid-max))
|
|
||||||
(let [(resolved-x resolved-y resolved-z) (world:resolve_collision cam-x cam-y cam-z new-x cam-y new-z 5)]
|
|
||||||
(set cam-x resolved-x)
|
|
||||||
(set cam-z resolved-z)))
|
|
||||||
|
|
||||||
(set smooth-cam-x (+ (* smooth-cam-x (- 1 cam-smoothing)) (* cam-x cam-smoothing)))
|
|
||||||
(set smooth-cam-z (+ (* smooth-cam-z (- 1 cam-smoothing)) (* cam-z cam-smoothing)))
|
|
||||||
|
|
||||||
(when cursor-look?
|
|
||||||
(let [dx (pxl8.mouse_dx)
|
|
||||||
dy (pxl8.mouse_dy)]
|
|
||||||
(set cam-yaw (- cam-yaw (* dx cursor-sensitivity)))
|
|
||||||
(set cam-pitch (math.max (- max-pitch)
|
|
||||||
(math.min max-pitch
|
|
||||||
(- cam-pitch (* dy cursor-sensitivity)))))))
|
|
||||||
|
|
||||||
(when (and (not cursor-look?) (pxl8.key_down "left"))
|
|
||||||
(set cam-yaw (+ cam-yaw (* turn-speed dt))))
|
|
||||||
|
|
||||||
(when (and (not cursor-look?) (pxl8.key_down "right"))
|
|
||||||
(set cam-yaw (- cam-yaw (* turn-speed dt))))
|
|
||||||
|
|
||||||
(when (and (not cursor-look?) (pxl8.key_down "up"))
|
|
||||||
(set cam-pitch (math.min max-pitch (+ cam-pitch (* turn-speed dt)))))
|
|
||||||
|
|
||||||
(when (and (not cursor-look?) (pxl8.key_down "down"))
|
|
||||||
(set cam-pitch (math.max (- max-pitch) (- cam-pitch (* turn-speed dt)))))
|
|
||||||
|
|
||||||
(when (and (pxl8.key_pressed "space") grounded?)
|
|
||||||
(set velocity-y jump-force)
|
|
||||||
(set grounded? false))
|
|
||||||
|
|
||||||
(set velocity-y (+ velocity-y (* gravity dt)))
|
|
||||||
(set cam-y (+ cam-y (* velocity-y dt)))
|
|
||||||
|
|
||||||
(when (<= cam-y ground-y)
|
|
||||||
(when (not grounded?)
|
|
||||||
(set land-squash land-squash-amount))
|
|
||||||
(set cam-y ground-y)
|
|
||||||
(set velocity-y 0)
|
|
||||||
(set grounded? true))
|
|
||||||
|
|
||||||
(when (< land-squash 0)
|
|
||||||
(set land-squash (math.min 0 (+ land-squash (* land-recovery-speed dt)))))
|
|
||||||
|
|
||||||
(if (and moving grounded?)
|
|
||||||
(set bob-time (+ bob-time (* dt bob-speed)))
|
|
||||||
(let [target-phase (* (math.floor (/ bob-time math.pi)) math.pi)]
|
|
||||||
(set bob-time (+ (* bob-time 0.8) (* target-phase 0.2))))))))
|
|
||||||
|
|
||||||
(fn frame []
|
|
||||||
(pxl8.clear 0)
|
|
||||||
|
|
||||||
(when (not camera)
|
|
||||||
(pxl8.error "camera is nil!"))
|
|
||||||
|
|
||||||
(when (not world)
|
|
||||||
(pxl8.error "world is nil!"))
|
|
||||||
|
|
||||||
(when (and world (not (world:is_loaded)))
|
|
||||||
(pxl8.text "World not loaded yet..." 5 30 12))
|
|
||||||
|
|
||||||
(when (and camera world (world:is_loaded))
|
|
||||||
(let [bob-offset (* (math.sin bob-time) bob-amount)
|
|
||||||
eye-y (+ cam-y bob-offset land-squash)
|
|
||||||
forward-x (- (math.sin cam-yaw))
|
|
||||||
forward-z (- (math.cos cam-yaw))
|
|
||||||
target-x (+ smooth-cam-x forward-x)
|
|
||||||
target-y (+ eye-y (math.sin cam-pitch))
|
|
||||||
target-z (+ smooth-cam-z forward-z)
|
|
||||||
aspect (/ (pxl8.get_width) (pxl8.get_height))]
|
|
||||||
|
|
||||||
(camera:lookat [smooth-cam-x eye-y smooth-cam-z]
|
|
||||||
[target-x target-y target-z]
|
|
||||||
[0 1 0])
|
|
||||||
(camera:set_perspective 1.047 aspect 1.0 4096.0)
|
|
||||||
|
|
||||||
(pxl8.begin_frame_3d camera)
|
|
||||||
(pxl8.clear_depth)
|
|
||||||
|
|
||||||
(world:render [smooth-cam-x eye-y smooth-cam-z])
|
|
||||||
|
|
||||||
(pxl8.end_frame_3d)
|
|
||||||
|
|
||||||
(let [cx (/ (pxl8.get_width) 2)
|
|
||||||
cy (/ (pxl8.get_height) 2)
|
|
||||||
crosshair-size 4
|
|
||||||
red-color 18]
|
|
||||||
(pxl8.line (- cx crosshair-size) cy (+ cx crosshair-size) cy red-color)
|
|
||||||
(pxl8.line cx (- cy crosshair-size) cx (+ cy crosshair-size) red-color))
|
|
||||||
|
|
||||||
(pxl8.text (.. "fps: " (string.format "%.1f" (pxl8.get_fps))) 5 5 12)
|
|
||||||
(pxl8.text (.. "pos: " (string.format "%.0f" cam-x) ","
|
|
||||||
(string.format "%.0f" cam-y) ","
|
|
||||||
(string.format "%.0f" cam-z)) 5 15 12))))
|
|
||||||
|
|
||||||
{:init init
|
|
||||||
:update update
|
|
||||||
:frame frame}
|
|
||||||
BIN
demo/res/tiles/adventurer.png
Normal file
BIN
demo/res/tiles/adventurer.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.9 MiB |
BIN
demo/res/tiles/castle-wall.jpg
Normal file
BIN
demo/res/tiles/castle-wall.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 185 KiB |
147
pxl8.sh
147
pxl8.sh
|
|
@ -56,6 +56,55 @@ build_luajit() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
build_server() {
|
||||||
|
local mode="$1"
|
||||||
|
if [[ -d "server" ]]; then
|
||||||
|
print_info "Building server ($mode mode)"
|
||||||
|
cd server
|
||||||
|
if [[ "$mode" == "release" ]]; then
|
||||||
|
cargo build --release
|
||||||
|
else
|
||||||
|
cargo build
|
||||||
|
fi
|
||||||
|
local status=$?
|
||||||
|
cd - > /dev/null
|
||||||
|
if [[ $status -eq 0 ]]; then
|
||||||
|
print_info "Built server"
|
||||||
|
else
|
||||||
|
print_error "Server build failed"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
start_server() {
|
||||||
|
local mode="$1"
|
||||||
|
local server_bin
|
||||||
|
if [[ "$mode" == "release" ]]; then
|
||||||
|
server_bin="server/target/release/pxl8-server"
|
||||||
|
else
|
||||||
|
server_bin="server/target/debug/pxl8-server"
|
||||||
|
fi
|
||||||
|
print_info "Server mode: $mode, binary: $server_bin"
|
||||||
|
if [[ -f "$server_bin" ]]; then
|
||||||
|
print_info "Starting server..."
|
||||||
|
./$server_bin &
|
||||||
|
SERVER_PID=$!
|
||||||
|
print_info "Server started with PID $SERVER_PID"
|
||||||
|
sleep 0.5
|
||||||
|
else
|
||||||
|
print_error "Server binary not found: $server_bin"
|
||||||
|
print_error "Build the server first with: cd server && cargo build"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
stop_server() {
|
||||||
|
if [[ -n "$SERVER_PID" ]]; then
|
||||||
|
print_info "Stopping server"
|
||||||
|
kill $SERVER_PID 2>/dev/null || true
|
||||||
|
wait $SERVER_PID 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
build_sdl() {
|
build_sdl() {
|
||||||
if [[ ! -f "lib/SDL/build/libSDL3.so" ]] && [[ ! -f "lib/SDL/build/libSDL3.a" ]] && [[ ! -f "lib/SDL/build/libSDL3.dylib" ]]; then
|
if [[ ! -f "lib/SDL/build/libSDL3.so" ]] && [[ ! -f "lib/SDL/build/libSDL3.a" ]] && [[ ! -f "lib/SDL/build/libSDL3.dylib" ]]; then
|
||||||
print_info "Building SDL3"
|
print_info "Building SDL3"
|
||||||
|
|
@ -113,6 +162,7 @@ print_usage() {
|
||||||
echo " --all Clean both build artifacts and dependencies"
|
echo " --all Clean both build artifacts and dependencies"
|
||||||
echo " --deps Clean only dependencies"
|
echo " --deps Clean only dependencies"
|
||||||
echo " --release Build/run/clean in release mode (default: debug)"
|
echo " --release Build/run/clean in release mode (default: debug)"
|
||||||
|
echo " --server Start game server before running (for networked games)"
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_sdl3() {
|
setup_sdl3() {
|
||||||
|
|
@ -173,7 +223,7 @@ timestamp() {
|
||||||
update_fennel() {
|
update_fennel() {
|
||||||
print_info "Fetching Fennel"
|
print_info "Fetching Fennel"
|
||||||
|
|
||||||
local version="1.6.0"
|
local version="1.6.1"
|
||||||
|
|
||||||
if curl -sL --max-time 5 -o lib/fennel/fennel.lua "https://fennel-lang.org/downloads/fennel-${version}.lua" 2>/dev/null; then
|
if curl -sL --max-time 5 -o lib/fennel/fennel.lua "https://fennel-lang.org/downloads/fennel-${version}.lua" 2>/dev/null; then
|
||||||
if [[ -f "lib/fennel/fennel.lua" ]] && [[ -s "lib/fennel/fennel.lua" ]]; then
|
if [[ -f "lib/fennel/fennel.lua" ]] && [[ -s "lib/fennel/fennel.lua" ]]; then
|
||||||
|
|
@ -264,7 +314,8 @@ for arg in "$@"; do
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ "$MODE" = "release" ]; then
|
if [ "$MODE" = "release" ]; then
|
||||||
CFLAGS="$CFLAGS -O3 -ffast-math -funroll-loops -fno-unwind-tables -fno-asynchronous-unwind-tables"
|
CFLAGS="$CFLAGS -O3 -flto -ffast-math -funroll-loops -fno-unwind-tables -fno-asynchronous-unwind-tables"
|
||||||
|
LINKER_FLAGS="$LINKER_FLAGS -flto"
|
||||||
BUILDDIR="$BUILDDIR/release"
|
BUILDDIR="$BUILDDIR/release"
|
||||||
BINDIR="$BINDIR/release"
|
BINDIR="$BINDIR/release"
|
||||||
else
|
else
|
||||||
|
|
@ -273,7 +324,7 @@ else
|
||||||
BINDIR="$BINDIR/debug"
|
BINDIR="$BINDIR/debug"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
DEP_CFLAGS="-O3 -ffast-math -funroll-loops"
|
DEP_CFLAGS="-O3 -funroll-loops"
|
||||||
|
|
||||||
case "$COMMAND" in
|
case "$COMMAND" in
|
||||||
build)
|
build)
|
||||||
|
|
@ -320,7 +371,7 @@ case "$COMMAND" in
|
||||||
print_info "Compiler cache: ccache enabled"
|
print_info "Compiler cache: ccache enabled"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
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"
|
INCLUDES="-Isrc/asset -Isrc/core -Isrc/gfx -Isrc/gui -Isrc/hal -Isrc/math -Isrc/net -Isrc/procgen -Isrc/script -Isrc/sfx -Isrc/world -Ilib/linenoise -Ilib/luajit/src -Ilib/miniz"
|
||||||
COMPILE_FLAGS="$CFLAGS $INCLUDES"
|
COMPILE_FLAGS="$CFLAGS $INCLUDES"
|
||||||
DEP_COMPILE_FLAGS="$DEP_CFLAGS $INCLUDES"
|
DEP_COMPILE_FLAGS="$DEP_CFLAGS $INCLUDES"
|
||||||
|
|
||||||
|
|
@ -329,38 +380,43 @@ case "$COMMAND" in
|
||||||
LIB_SOURCE_FILES="lib/linenoise/linenoise.c lib/miniz/miniz.c"
|
LIB_SOURCE_FILES="lib/linenoise/linenoise.c lib/miniz/miniz.c"
|
||||||
|
|
||||||
PXL8_SOURCE_FILES="
|
PXL8_SOURCE_FILES="
|
||||||
client/src/core/pxl8.c
|
src/core/pxl8.c
|
||||||
client/src/core/pxl8_io.c
|
src/core/pxl8_bytes.c
|
||||||
client/src/core/pxl8_log.c
|
src/core/pxl8_io.c
|
||||||
client/src/core/pxl8_rng.c
|
src/core/pxl8_log.c
|
||||||
client/src/math/pxl8_math.c
|
src/core/pxl8_rng.c
|
||||||
client/src/gfx/pxl8_anim.c
|
src/math/pxl8_math.c
|
||||||
client/src/gfx/pxl8_atlas.c
|
src/gfx/pxl8_anim.c
|
||||||
client/src/gfx/pxl8_blit.c
|
src/gfx/pxl8_atlas.c
|
||||||
client/src/gfx/pxl8_3d_camera.c
|
src/gfx/pxl8_blit.c
|
||||||
client/src/gfx/pxl8_colormap.c
|
src/gfx/pxl8_3d_camera.c
|
||||||
client/src/gfx/pxl8_cpu.c
|
src/gfx/pxl8_colormap.c
|
||||||
client/src/gfx/pxl8_dither.c
|
src/gfx/pxl8_cpu.c
|
||||||
client/src/gfx/pxl8_font.c
|
src/gfx/pxl8_dither.c
|
||||||
client/src/gfx/pxl8_gfx.c
|
src/gfx/pxl8_font.c
|
||||||
client/src/gfx/pxl8_mesh.c
|
src/gfx/pxl8_gfx.c
|
||||||
client/src/gfx/pxl8_palette.c
|
src/gfx/pxl8_lightmap.c
|
||||||
client/src/gfx/pxl8_particles.c
|
src/gfx/pxl8_mesh.c
|
||||||
client/src/gfx/pxl8_tilemap.c
|
src/gfx/pxl8_palette.c
|
||||||
client/src/gfx/pxl8_tilesheet.c
|
src/gfx/pxl8_particles.c
|
||||||
client/src/gfx/pxl8_transition.c
|
src/gfx/pxl8_tilemap.c
|
||||||
client/src/sfx/pxl8_sfx.c
|
src/gfx/pxl8_tilesheet.c
|
||||||
client/src/script/pxl8_repl.c
|
src/gfx/pxl8_transition.c
|
||||||
client/src/script/pxl8_script.c
|
src/sfx/pxl8_sfx.c
|
||||||
client/src/hal/pxl8_sdl3.c
|
src/script/pxl8_repl.c
|
||||||
client/src/world/pxl8_bsp.c
|
src/script/pxl8_script.c
|
||||||
client/src/world/pxl8_gen.c
|
src/hal/pxl8_sdl3.c
|
||||||
client/src/world/pxl8_world.c
|
src/world/pxl8_bsp.c
|
||||||
client/src/asset/pxl8_ase.c
|
src/world/pxl8_gen.c
|
||||||
client/src/asset/pxl8_cart.c
|
src/world/pxl8_world.c
|
||||||
client/src/asset/pxl8_save.c
|
src/procgen/pxl8_graph.c
|
||||||
client/src/game/pxl8_gui.c
|
src/asset/pxl8_ase.c
|
||||||
client/src/game/pxl8_replay.c
|
src/asset/pxl8_cart.c
|
||||||
|
src/asset/pxl8_save.c
|
||||||
|
src/gui/pxl8_gui.c
|
||||||
|
src/core/pxl8_replay.c
|
||||||
|
src/net/pxl8_net.c
|
||||||
|
src/net/pxl8_protocol.c
|
||||||
"
|
"
|
||||||
|
|
||||||
LUAJIT_LIB="lib/luajit/src/libluajit.a"
|
LUAJIT_LIB="lib/luajit/src/libluajit.a"
|
||||||
|
|
@ -390,13 +446,13 @@ case "$COMMAND" in
|
||||||
|
|
||||||
NEEDS_REBUILD=false
|
NEEDS_REBUILD=false
|
||||||
if [[ "$src_file" -nt "$obj_file" ]] || \
|
if [[ "$src_file" -nt "$obj_file" ]] || \
|
||||||
[[ "client/src/core/pxl8_types.h" -nt "$obj_file" ]] || \
|
[[ "src/core/pxl8_types.h" -nt "$obj_file" ]] || \
|
||||||
[[ "client/src/core/pxl8_macros.h" -nt "$obj_file" ]]; then
|
[[ "src/core/pxl8_macros.h" -nt "$obj_file" ]]; then
|
||||||
NEEDS_REBUILD=true
|
NEEDS_REBUILD=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$src_file" == "client/src/script/pxl8_script.c" ]]; then
|
if [[ "$src_file" == "src/script/pxl8_script.c" ]]; then
|
||||||
for lua_file in client/src/lua/*.lua client/src/lua/pxl8/*.lua lib/fennel/fennel.lua; do
|
for lua_file in src/lua/*.lua src/lua/pxl8/*.lua lib/fennel/fennel.lua; do
|
||||||
if [[ -f "$lua_file" ]] && [[ "$lua_file" -nt "$obj_file" ]]; then
|
if [[ -f "$lua_file" ]] && [[ "$lua_file" -nt "$obj_file" ]]; then
|
||||||
NEEDS_REBUILD=true
|
NEEDS_REBUILD=true
|
||||||
break
|
break
|
||||||
|
|
@ -430,16 +486,25 @@ case "$COMMAND" in
|
||||||
|
|
||||||
CART=""
|
CART=""
|
||||||
EXTRA_ARGS=""
|
EXTRA_ARGS=""
|
||||||
|
RUN_SERVER=false
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
if [[ "$arg" == "--release" ]]; then
|
if [[ "$arg" == "--release" ]]; then
|
||||||
continue
|
MODE="release"
|
||||||
elif [[ "$arg" == "--repl" ]]; then
|
elif [[ "$arg" == "--repl" ]]; then
|
||||||
EXTRA_ARGS="$EXTRA_ARGS --repl"
|
EXTRA_ARGS="$EXTRA_ARGS --repl"
|
||||||
|
elif [[ "$arg" == "--server" ]]; then
|
||||||
|
RUN_SERVER=true
|
||||||
elif [[ -z "$CART" ]]; then
|
elif [[ -z "$CART" ]]; then
|
||||||
CART="$arg"
|
CART="$arg"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
if [[ "$RUN_SERVER" == true ]]; then
|
||||||
|
build_server "$MODE"
|
||||||
|
start_server "$MODE"
|
||||||
|
trap stop_server EXIT
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ -z "$CART" ]]; then
|
if [[ -z "$CART" ]]; then
|
||||||
"$BINDIR/pxl8" $EXTRA_ARGS
|
"$BINDIR/pxl8" $EXTRA_ARGS
|
||||||
else
|
else
|
||||||
|
|
|
||||||
2
server/.cargo/config.toml
Normal file
2
server/.cargo/config.toml
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
[unstable]
|
||||||
|
build-std = ["core", "alloc"]
|
||||||
1
server/.gitignore
vendored
Normal file
1
server/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
target/
|
||||||
1090
server/Cargo.lock
generated
Normal file
1090
server/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
34
server/Cargo.toml
Normal file
34
server/Cargo.toml
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
[package]
|
||||||
|
name = "pxl8-server"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
bindgen = "0.72"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bevy_ecs = { version = "0.18", default-features = false }
|
||||||
|
libc = { version = "0.2", default-features = false }
|
||||||
|
libm = { version = "0.2", default-features = false }
|
||||||
|
|
||||||
|
[target.'cfg(windows)'.dependencies]
|
||||||
|
windows-sys = { version = "0.61", default-features = false, features = [
|
||||||
|
"Win32_System_Memory",
|
||||||
|
"Win32_System_Performance",
|
||||||
|
"Win32_System_Threading",
|
||||||
|
"Win32_Networking_WinSock",
|
||||||
|
] }
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "pxl8-server"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
panic = "abort"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
panic = "abort"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
std = ["bevy_ecs/std"]
|
||||||
23
server/build.rs
Normal file
23
server/build.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
use std::env;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||||
|
let pxl8_src = PathBuf::from(&manifest_dir).join("../src");
|
||||||
|
|
||||||
|
println!("cargo:rerun-if-changed=../src/net/pxl8_protocol.h");
|
||||||
|
println!("cargo:rerun-if-changed=../src/core/pxl8_types.h");
|
||||||
|
|
||||||
|
let bindings = bindgen::Builder::default()
|
||||||
|
.header(pxl8_src.join("net/pxl8_protocol.h").to_str().unwrap())
|
||||||
|
.clang_arg(format!("-I{}", pxl8_src.join("core").display()))
|
||||||
|
.use_core()
|
||||||
|
.rustified_enum(".*")
|
||||||
|
.generate()
|
||||||
|
.expect("Unable to generate bindings");
|
||||||
|
|
||||||
|
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||||
|
bindings
|
||||||
|
.write_to_file(out_path.join("protocol.rs"))
|
||||||
|
.expect("Couldn't write bindings");
|
||||||
|
}
|
||||||
2
server/rust-toolchain.toml
Normal file
2
server/rust-toolchain.toml
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
[toolchain]
|
||||||
|
channel = "nightly"
|
||||||
36
server/src/allocator.rs
Normal file
36
server/src/allocator.rs
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
use core::alloc::{GlobalAlloc, Layout};
|
||||||
|
|
||||||
|
pub struct Allocator;
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
unsafe impl GlobalAlloc for Allocator {
|
||||||
|
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||||
|
unsafe { libc::memalign(layout.align(), layout.size()) as *mut u8 }
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
|
||||||
|
unsafe { libc::free(ptr as *mut libc::c_void) }
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn realloc(&self, ptr: *mut u8, _layout: Layout, new_size: usize) -> *mut u8 {
|
||||||
|
unsafe { libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
unsafe impl GlobalAlloc for Allocator {
|
||||||
|
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||||
|
use windows_sys::Win32::System::Memory::*;
|
||||||
|
unsafe { HeapAlloc(GetProcessHeap(), 0, layout.size()) as *mut u8 }
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
|
||||||
|
use windows_sys::Win32::System::Memory::*;
|
||||||
|
unsafe { HeapFree(GetProcessHeap(), 0, ptr as *mut core::ffi::c_void) };
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn realloc(&self, ptr: *mut u8, _layout: Layout, new_size: usize) -> *mut u8 {
|
||||||
|
use windows_sys::Win32::System::Memory::*;
|
||||||
|
unsafe { HeapReAlloc(GetProcessHeap(), 0, ptr as *mut core::ffi::c_void, new_size) as *mut u8 }
|
||||||
|
}
|
||||||
|
}
|
||||||
45
server/src/components.rs
Normal file
45
server/src/components.rs
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
use bevy_ecs::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Component, Clone, Copy, Default)]
|
||||||
|
pub struct Position {
|
||||||
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
|
pub z: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Clone, Copy, Default)]
|
||||||
|
pub struct Rotation {
|
||||||
|
pub pitch: f32,
|
||||||
|
pub yaw: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Clone, Copy, Default)]
|
||||||
|
pub struct Velocity {
|
||||||
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
|
pub z: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Clone, Copy)]
|
||||||
|
pub struct Health {
|
||||||
|
pub current: f32,
|
||||||
|
pub max: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Health {
|
||||||
|
pub fn new(max: f32) -> Self {
|
||||||
|
Self { current: max, max }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Health {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(100.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Clone, Copy, Default)]
|
||||||
|
pub struct Player;
|
||||||
|
|
||||||
|
#[derive(Component, Clone, Copy)]
|
||||||
|
pub struct TypeId(pub u16);
|
||||||
18
server/src/lib.rs
Normal file
18
server/src/lib.rs
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
pub mod components;
|
||||||
|
mod simulation;
|
||||||
|
pub mod transport;
|
||||||
|
|
||||||
|
#[allow(dead_code, non_camel_case_types, non_snake_case, non_upper_case_globals)]
|
||||||
|
pub mod protocol {
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/protocol.rs"));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use bevy_ecs::prelude::*;
|
||||||
|
pub use components::*;
|
||||||
|
pub use protocol::*;
|
||||||
|
pub use simulation::*;
|
||||||
|
pub use transport::*;
|
||||||
142
server/src/main.rs
Normal file
142
server/src/main.rs
Normal file
|
|
@ -0,0 +1,142 @@
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
mod allocator;
|
||||||
|
|
||||||
|
use core::panic::PanicInfo;
|
||||||
|
use pxl8_server::*;
|
||||||
|
|
||||||
|
#[global_allocator]
|
||||||
|
static ALLOCATOR: allocator::Allocator = allocator::Allocator;
|
||||||
|
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(_info: &PanicInfo) -> ! {
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const TICK_RATE: u64 = 30;
|
||||||
|
const TICK_NS: u64 = 1_000_000_000 / TICK_RATE;
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn get_time_ns() -> u64 {
|
||||||
|
let mut ts = libc::timespec { tv_sec: 0, tv_nsec: 0 };
|
||||||
|
unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut ts) };
|
||||||
|
(ts.tv_sec as u64) * 1_000_000_000 + (ts.tv_nsec as u64)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn get_time_ns() -> u64 {
|
||||||
|
use windows_sys::Win32::System::Performance::*;
|
||||||
|
static mut FREQ: i64 = 0;
|
||||||
|
unsafe {
|
||||||
|
if FREQ == 0 {
|
||||||
|
QueryPerformanceFrequency(&mut FREQ);
|
||||||
|
}
|
||||||
|
let mut count: i64 = 0;
|
||||||
|
QueryPerformanceCounter(&mut count);
|
||||||
|
((count as u128 * 1_000_000_000) / FREQ as u128) as u64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn sleep_ms(ms: u64) {
|
||||||
|
let ts = libc::timespec {
|
||||||
|
tv_sec: (ms / 1000) as i64,
|
||||||
|
tv_nsec: ((ms % 1000) * 1_000_000) as i64,
|
||||||
|
};
|
||||||
|
unsafe { libc::nanosleep(&ts, core::ptr::null_mut()) };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn sleep_ms(ms: u64) {
|
||||||
|
use windows_sys::Win32::System::Threading::Sleep;
|
||||||
|
unsafe { Sleep(ms as u32) };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_spawn_position(payload: &[u8]) -> (f32, f32, f32, f32, f32) {
|
||||||
|
let x = f32::from_be_bytes([payload[0], payload[1], payload[2], payload[3]]);
|
||||||
|
let y = f32::from_be_bytes([payload[4], payload[5], payload[6], payload[7]]);
|
||||||
|
let z = f32::from_be_bytes([payload[8], payload[9], payload[10], payload[11]]);
|
||||||
|
let yaw = f32::from_be_bytes([payload[12], payload[13], payload[14], payload[15]]);
|
||||||
|
let pitch = f32::from_be_bytes([payload[16], payload[17], payload[18], payload[19]]);
|
||||||
|
(x, y, z, yaw, pitch)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 {
|
||||||
|
let mut transport = match transport::Transport::bind(transport::DEFAULT_PORT) {
|
||||||
|
Some(t) => t,
|
||||||
|
None => return 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut sim = Simulation::new();
|
||||||
|
let mut player_id: u64 = 0;
|
||||||
|
let mut last_client_tick: u64 = 0;
|
||||||
|
|
||||||
|
let mut sequence: u32 = 0;
|
||||||
|
let mut last_tick = get_time_ns();
|
||||||
|
let mut entities_buf = [protocol::pxl8_entity_state {
|
||||||
|
entity_id: 0,
|
||||||
|
userdata: [0u8; 56],
|
||||||
|
}; 64];
|
||||||
|
let mut inputs_buf: [protocol::pxl8_input_msg; 16] = unsafe { core::mem::zeroed() };
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let now = get_time_ns();
|
||||||
|
let elapsed = now.saturating_sub(last_tick);
|
||||||
|
|
||||||
|
if elapsed >= TICK_NS {
|
||||||
|
last_tick = now;
|
||||||
|
let dt = (elapsed as f32) / 1_000_000_000.0;
|
||||||
|
|
||||||
|
let mut latest_input: Option<protocol::pxl8_input_msg> = None;
|
||||||
|
while let Some(msg_type) = transport.recv() {
|
||||||
|
match msg_type {
|
||||||
|
x if x == protocol::pxl8_msg_type::PXL8_MSG_INPUT as u8 => {
|
||||||
|
latest_input = Some(transport.get_input());
|
||||||
|
}
|
||||||
|
x if x == protocol::pxl8_msg_type::PXL8_MSG_COMMAND as u8 => {
|
||||||
|
let cmd = transport.get_command();
|
||||||
|
if cmd.cmd_type == protocol::pxl8_cmd_type::PXL8_CMD_SPAWN_ENTITY as u16 {
|
||||||
|
let (x, y, z, _yaw, _pitch) = extract_spawn_position(&cmd.payload);
|
||||||
|
let player = sim.spawn_player(x, y, z);
|
||||||
|
player_id = player.to_bits();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(input) = latest_input {
|
||||||
|
last_client_tick = input.tick;
|
||||||
|
inputs_buf[0] = input;
|
||||||
|
sim.step(&inputs_buf[..1], dt);
|
||||||
|
} else {
|
||||||
|
sim.step(&[], dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
sim.generate_snapshot(player_id, |state| {
|
||||||
|
if count < entities_buf.len() {
|
||||||
|
entities_buf[count] = *state;
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let header = protocol::pxl8_snapshot_header {
|
||||||
|
entity_count: count as u16,
|
||||||
|
event_count: 0,
|
||||||
|
player_id,
|
||||||
|
tick: last_client_tick,
|
||||||
|
time: sim.time,
|
||||||
|
};
|
||||||
|
|
||||||
|
transport.send_snapshot(&header, &entities_buf[..count], sequence);
|
||||||
|
sequence = sequence.wrapping_add(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep_ms(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
133
server/src/simulation.rs
Normal file
133
server/src/simulation.rs
Normal file
|
|
@ -0,0 +1,133 @@
|
||||||
|
use bevy_ecs::prelude::*;
|
||||||
|
use libm::{cosf, sinf};
|
||||||
|
|
||||||
|
use crate::components::*;
|
||||||
|
use crate::protocol::*;
|
||||||
|
|
||||||
|
#[derive(Resource, Default)]
|
||||||
|
pub struct SimTime {
|
||||||
|
pub dt: f32,
|
||||||
|
pub time: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Simulation {
|
||||||
|
pub player: Option<Entity>,
|
||||||
|
pub tick: u64,
|
||||||
|
pub time: f32,
|
||||||
|
pub world: World,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Simulation {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut world = World::new();
|
||||||
|
world.insert_resource(SimTime::default());
|
||||||
|
Self {
|
||||||
|
player: None,
|
||||||
|
tick: 0,
|
||||||
|
time: 0.0,
|
||||||
|
world,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn step(&mut self, inputs: &[pxl8_input_msg], dt: f32) {
|
||||||
|
self.tick += 1;
|
||||||
|
self.time += dt;
|
||||||
|
|
||||||
|
if let Some(mut sim_time) = self.world.get_resource_mut::<SimTime>() {
|
||||||
|
sim_time.dt = dt;
|
||||||
|
sim_time.time = self.time;
|
||||||
|
}
|
||||||
|
|
||||||
|
for input in inputs {
|
||||||
|
self.apply_input(input, dt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_input(&mut self, input: &pxl8_input_msg, dt: f32) {
|
||||||
|
let Some(player) = self.player else { return };
|
||||||
|
let speed = 200.0;
|
||||||
|
|
||||||
|
let yaw = input.yaw;
|
||||||
|
let sin_yaw = sinf(yaw);
|
||||||
|
let cos_yaw = cosf(yaw);
|
||||||
|
|
||||||
|
let move_x = input.move_x;
|
||||||
|
let move_y = input.move_y;
|
||||||
|
let len_sq = move_x * move_x + move_y * move_y;
|
||||||
|
|
||||||
|
if len_sq > 0.0 {
|
||||||
|
let len = libm::sqrtf(len_sq);
|
||||||
|
let norm_x = move_x / len;
|
||||||
|
let norm_y = move_y / len;
|
||||||
|
|
||||||
|
let forward_x = -sin_yaw * norm_y * speed * dt;
|
||||||
|
let forward_z = -cos_yaw * norm_y * speed * dt;
|
||||||
|
let strafe_x = cos_yaw * norm_x * speed * dt;
|
||||||
|
let strafe_z = -sin_yaw * norm_x * speed * dt;
|
||||||
|
|
||||||
|
if let Some(mut pos) = self.world.get_mut::<Position>(player) {
|
||||||
|
pos.x += forward_x + strafe_x;
|
||||||
|
pos.z += forward_z + strafe_z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(mut rot) = self.world.get_mut::<Rotation>(player) {
|
||||||
|
rot.yaw = yaw;
|
||||||
|
rot.pitch = (rot.pitch - input.look_dy * 0.008).clamp(-1.5, 1.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spawn_entity(&mut self) -> Entity {
|
||||||
|
self.world.spawn((
|
||||||
|
Position::default(),
|
||||||
|
Rotation::default(),
|
||||||
|
Velocity::default(),
|
||||||
|
)).id()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spawn_player(&mut self, x: f32, y: f32, z: f32) -> Entity {
|
||||||
|
let entity = self.world.spawn((
|
||||||
|
Player,
|
||||||
|
Position { x, y, z },
|
||||||
|
Rotation::default(),
|
||||||
|
Velocity::default(),
|
||||||
|
Health::default(),
|
||||||
|
)).id();
|
||||||
|
self.player = Some(entity);
|
||||||
|
entity
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_snapshot<F>(&mut self, player_id: u64, mut writer: F)
|
||||||
|
where
|
||||||
|
F: FnMut(&pxl8_entity_state),
|
||||||
|
{
|
||||||
|
let mut query = self.world.query::<(Entity, &Position, &Rotation)>();
|
||||||
|
for (entity, pos, rot) in query.iter(&self.world) {
|
||||||
|
let mut state = pxl8_entity_state {
|
||||||
|
entity_id: entity.to_bits(),
|
||||||
|
userdata: [0u8; 56],
|
||||||
|
};
|
||||||
|
|
||||||
|
let bytes = &mut state.userdata;
|
||||||
|
bytes[0..4].copy_from_slice(&pos.x.to_be_bytes());
|
||||||
|
bytes[4..8].copy_from_slice(&pos.y.to_be_bytes());
|
||||||
|
bytes[8..12].copy_from_slice(&pos.z.to_be_bytes());
|
||||||
|
bytes[12..16].copy_from_slice(&rot.yaw.to_be_bytes());
|
||||||
|
bytes[16..20].copy_from_slice(&rot.pitch.to_be_bytes());
|
||||||
|
|
||||||
|
writer(&state);
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = player_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn entity_count(&self) -> usize {
|
||||||
|
self.world.entities().len() as usize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Simulation {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
281
server/src/transport.rs
Normal file
281
server/src/transport.rs
Normal file
|
|
@ -0,0 +1,281 @@
|
||||||
|
use crate::protocol::*;
|
||||||
|
|
||||||
|
pub const DEFAULT_PORT: u16 = 7777;
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
mod sys {
|
||||||
|
use libc::{c_int, c_void, sockaddr, sockaddr_in, socklen_t};
|
||||||
|
|
||||||
|
pub type RawSocket = c_int;
|
||||||
|
pub const INVALID_SOCKET: RawSocket = -1;
|
||||||
|
|
||||||
|
pub fn socket() -> RawSocket {
|
||||||
|
unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bind(sock: RawSocket, port: u16) -> c_int {
|
||||||
|
let addr = sockaddr_in {
|
||||||
|
sin_family: libc::AF_INET as u16,
|
||||||
|
sin_port: port.to_be(),
|
||||||
|
sin_addr: libc::in_addr { s_addr: u32::from_be_bytes([127, 0, 0, 1]).to_be() },
|
||||||
|
sin_zero: [0; 8],
|
||||||
|
};
|
||||||
|
unsafe { libc::bind(sock, &addr as *const _ as *const sockaddr, core::mem::size_of::<sockaddr_in>() as socklen_t) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_nonblocking(sock: RawSocket) -> c_int {
|
||||||
|
unsafe {
|
||||||
|
let flags = libc::fcntl(sock, libc::F_GETFL, 0);
|
||||||
|
libc::fcntl(sock, libc::F_SETFL, flags | libc::O_NONBLOCK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn recvfrom(sock: RawSocket, buf: &mut [u8], addr: &mut sockaddr_in) -> isize {
|
||||||
|
let mut addr_len = core::mem::size_of::<sockaddr_in>() as socklen_t;
|
||||||
|
unsafe {
|
||||||
|
libc::recvfrom(
|
||||||
|
sock,
|
||||||
|
buf.as_mut_ptr() as *mut c_void,
|
||||||
|
buf.len(),
|
||||||
|
0,
|
||||||
|
addr as *mut _ as *mut sockaddr,
|
||||||
|
&mut addr_len,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sendto(sock: RawSocket, buf: &[u8], addr: &sockaddr_in) -> isize {
|
||||||
|
unsafe {
|
||||||
|
libc::sendto(
|
||||||
|
sock,
|
||||||
|
buf.as_ptr() as *const c_void,
|
||||||
|
buf.len(),
|
||||||
|
0,
|
||||||
|
addr as *const _ as *const sockaddr,
|
||||||
|
core::mem::size_of::<sockaddr_in>() as socklen_t,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn close(sock: RawSocket) {
|
||||||
|
unsafe { libc::close(sock) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
mod sys {
|
||||||
|
use windows_sys::Win32::Networking::WinSock::*;
|
||||||
|
|
||||||
|
pub type RawSocket = SOCKET;
|
||||||
|
pub const INVALID_SOCKET_VAL: RawSocket = INVALID_SOCKET;
|
||||||
|
|
||||||
|
pub fn socket() -> RawSocket {
|
||||||
|
unsafe { socket(AF_INET as i32, SOCK_DGRAM as i32, 0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bind(sock: RawSocket, port: u16) -> i32 {
|
||||||
|
let addr = SOCKADDR_IN {
|
||||||
|
sin_family: AF_INET,
|
||||||
|
sin_port: port.to_be(),
|
||||||
|
sin_addr: IN_ADDR { S_un: IN_ADDR_0 { S_addr: u32::from_be_bytes([127, 0, 0, 1]).to_be() } },
|
||||||
|
sin_zero: [0; 8],
|
||||||
|
};
|
||||||
|
unsafe { bind(sock, &addr as *const _ as *const SOCKADDR, core::mem::size_of::<SOCKADDR_IN>() as i32) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_nonblocking(sock: RawSocket) -> i32 {
|
||||||
|
let mut nonblocking: u32 = 1;
|
||||||
|
unsafe { ioctlsocket(sock, FIONBIO as i32, &mut nonblocking) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn recvfrom(sock: RawSocket, buf: &mut [u8], addr: &mut SOCKADDR_IN) -> i32 {
|
||||||
|
let mut addr_len = core::mem::size_of::<SOCKADDR_IN>() as i32;
|
||||||
|
unsafe {
|
||||||
|
recvfrom(
|
||||||
|
sock,
|
||||||
|
buf.as_mut_ptr(),
|
||||||
|
buf.len() as i32,
|
||||||
|
0,
|
||||||
|
addr as *mut _ as *mut SOCKADDR,
|
||||||
|
&mut addr_len,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sendto(sock: RawSocket, buf: &[u8], addr: &SOCKADDR_IN) -> i32 {
|
||||||
|
unsafe {
|
||||||
|
sendto(
|
||||||
|
sock,
|
||||||
|
buf.as_ptr(),
|
||||||
|
buf.len() as i32,
|
||||||
|
0,
|
||||||
|
addr as *const _ as *const SOCKADDR,
|
||||||
|
core::mem::size_of::<SOCKADDR_IN>() as i32,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn close(sock: RawSocket) {
|
||||||
|
unsafe { closesocket(sock) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
type SockAddr = libc::sockaddr_in;
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
type SockAddr = windows_sys::Win32::Networking::WinSock::SOCKADDR_IN;
|
||||||
|
|
||||||
|
pub struct Transport {
|
||||||
|
client_addr: SockAddr,
|
||||||
|
has_client: bool,
|
||||||
|
recv_buf: [u8; 4096],
|
||||||
|
send_buf: [u8; 4096],
|
||||||
|
socket: sys::RawSocket,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Transport {
|
||||||
|
pub fn bind(port: u16) -> Option<Self> {
|
||||||
|
let sock = sys::socket();
|
||||||
|
if sock == sys::INVALID_SOCKET {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if sys::bind(sock, port) < 0 {
|
||||||
|
sys::close(sock);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if sys::set_nonblocking(sock) < 0 {
|
||||||
|
sys::close(sock);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(Self {
|
||||||
|
client_addr: unsafe { core::mem::zeroed() },
|
||||||
|
has_client: false,
|
||||||
|
recv_buf: [0u8; 4096],
|
||||||
|
send_buf: [0u8; 4096],
|
||||||
|
socket: sock,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn recv(&mut self) -> Option<u8> {
|
||||||
|
let mut addr: SockAddr = unsafe { core::mem::zeroed() };
|
||||||
|
let len = sys::recvfrom(self.socket, &mut self.recv_buf, &mut addr);
|
||||||
|
|
||||||
|
if len <= 0 || (len as usize) < size_of::<pxl8_msg_header>() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.client_addr = addr;
|
||||||
|
self.has_client = true;
|
||||||
|
|
||||||
|
let header = self.deserialize_header();
|
||||||
|
Some(header.type_)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_input(&self) -> pxl8_input_msg {
|
||||||
|
self.deserialize_input()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_command(&self) -> pxl8_command_msg {
|
||||||
|
self.deserialize_command()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_snapshot(
|
||||||
|
&mut self,
|
||||||
|
header: &pxl8_snapshot_header,
|
||||||
|
entities: &[pxl8_entity_state],
|
||||||
|
sequence: u32,
|
||||||
|
) {
|
||||||
|
if !self.has_client {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut offset = 0;
|
||||||
|
|
||||||
|
let msg_header = pxl8_msg_header {
|
||||||
|
sequence,
|
||||||
|
size: 0,
|
||||||
|
type_: pxl8_msg_type::PXL8_MSG_SNAPSHOT as u8,
|
||||||
|
version: PXL8_PROTOCOL_VERSION as u8,
|
||||||
|
};
|
||||||
|
offset += self.serialize_header(&msg_header, offset);
|
||||||
|
offset += self.serialize_snapshot_header(header, offset);
|
||||||
|
|
||||||
|
for entity in entities {
|
||||||
|
offset += self.serialize_entity_state(entity, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
sys::sendto(self.socket, &self.send_buf[..offset], &self.client_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_header(&mut self, h: &pxl8_msg_header, offset: usize) -> usize {
|
||||||
|
let buf = &mut self.send_buf[offset..];
|
||||||
|
buf[0..4].copy_from_slice(&h.sequence.to_be_bytes());
|
||||||
|
buf[4..6].copy_from_slice(&h.size.to_be_bytes());
|
||||||
|
buf[6] = h.type_;
|
||||||
|
buf[7] = h.version;
|
||||||
|
8
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_snapshot_header(&mut self, h: &pxl8_snapshot_header, offset: usize) -> usize {
|
||||||
|
let buf = &mut self.send_buf[offset..];
|
||||||
|
buf[0..2].copy_from_slice(&h.entity_count.to_be_bytes());
|
||||||
|
buf[2..4].copy_from_slice(&h.event_count.to_be_bytes());
|
||||||
|
buf[4..12].copy_from_slice(&h.player_id.to_be_bytes());
|
||||||
|
buf[12..20].copy_from_slice(&h.tick.to_be_bytes());
|
||||||
|
buf[20..24].copy_from_slice(&h.time.to_be_bytes());
|
||||||
|
24
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_entity_state(&mut self, e: &pxl8_entity_state, offset: usize) -> usize {
|
||||||
|
let buf = &mut self.send_buf[offset..];
|
||||||
|
buf[0..8].copy_from_slice(&e.entity_id.to_be_bytes());
|
||||||
|
buf[8..64].copy_from_slice(&e.userdata);
|
||||||
|
64
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_header(&self) -> pxl8_msg_header {
|
||||||
|
let buf = &self.recv_buf;
|
||||||
|
pxl8_msg_header {
|
||||||
|
sequence: u32::from_be_bytes([buf[0], buf[1], buf[2], buf[3]]),
|
||||||
|
size: u16::from_be_bytes([buf[4], buf[5]]),
|
||||||
|
type_: buf[6],
|
||||||
|
version: buf[7],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_input(&self) -> pxl8_input_msg {
|
||||||
|
let buf = &self.recv_buf[8..];
|
||||||
|
pxl8_input_msg {
|
||||||
|
buttons: u32::from_be_bytes([buf[0], buf[1], buf[2], buf[3]]),
|
||||||
|
look_dx: f32::from_be_bytes([buf[4], buf[5], buf[6], buf[7]]),
|
||||||
|
look_dy: f32::from_be_bytes([buf[8], buf[9], buf[10], buf[11]]),
|
||||||
|
move_x: f32::from_be_bytes([buf[12], buf[13], buf[14], buf[15]]),
|
||||||
|
move_y: f32::from_be_bytes([buf[16], buf[17], buf[18], buf[19]]),
|
||||||
|
yaw: f32::from_be_bytes([buf[20], buf[21], buf[22], buf[23]]),
|
||||||
|
tick: u64::from_be_bytes([buf[24], buf[25], buf[26], buf[27], buf[28], buf[29], buf[30], buf[31]]),
|
||||||
|
timestamp: u64::from_be_bytes([buf[32], buf[33], buf[34], buf[35], buf[36], buf[37], buf[38], buf[39]]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_command(&self) -> pxl8_command_msg {
|
||||||
|
let buf = &self.recv_buf[8..];
|
||||||
|
let mut cmd = pxl8_command_msg {
|
||||||
|
cmd_type: u16::from_be_bytes([buf[0], buf[1]]),
|
||||||
|
payload: [0u8; 64],
|
||||||
|
payload_size: u16::from_be_bytes([buf[66], buf[67]]),
|
||||||
|
tick: u64::from_be_bytes([buf[68], buf[69], buf[70], buf[71], buf[72], buf[73], buf[74], buf[75]]),
|
||||||
|
};
|
||||||
|
cmd.payload.copy_from_slice(&buf[2..66]);
|
||||||
|
cmd
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Transport {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
sys::close(self.socket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,55 +8,67 @@ static const char embed_fennel[] = {
|
||||||
, 0 };
|
, 0 };
|
||||||
|
|
||||||
static const char embed_pxl8[] = {
|
static const char embed_pxl8[] = {
|
||||||
#embed "client/src/lua/pxl8.lua"
|
#embed "src/lua/pxl8.lua"
|
||||||
, 0 };
|
, 0 };
|
||||||
|
|
||||||
static const char embed_pxl8_anim[] = {
|
static const char embed_pxl8_anim[] = {
|
||||||
#embed "client/src/lua/pxl8/anim.lua"
|
#embed "src/lua/pxl8/anim.lua"
|
||||||
|
, 0 };
|
||||||
|
|
||||||
|
static const char embed_pxl8_bytes[] = {
|
||||||
|
#embed "src/lua/pxl8/bytes.lua"
|
||||||
, 0 };
|
, 0 };
|
||||||
|
|
||||||
static const char embed_pxl8_core[] = {
|
static const char embed_pxl8_core[] = {
|
||||||
#embed "client/src/lua/pxl8/core.lua"
|
#embed "src/lua/pxl8/core.lua"
|
||||||
|
, 0 };
|
||||||
|
|
||||||
|
static const char embed_pxl8_effects[] = {
|
||||||
|
#embed "src/lua/pxl8/effects.lua"
|
||||||
, 0 };
|
, 0 };
|
||||||
|
|
||||||
static const char embed_pxl8_gfx2d[] = {
|
static const char embed_pxl8_gfx2d[] = {
|
||||||
#embed "client/src/lua/pxl8/gfx2d.lua"
|
#embed "src/lua/pxl8/gfx2d.lua"
|
||||||
, 0 };
|
, 0 };
|
||||||
|
|
||||||
static const char embed_pxl8_gfx3d[] = {
|
static const char embed_pxl8_gfx3d[] = {
|
||||||
#embed "client/src/lua/pxl8/gfx3d.lua"
|
#embed "src/lua/pxl8/gfx3d.lua"
|
||||||
, 0 };
|
, 0 };
|
||||||
|
|
||||||
static const char embed_pxl8_gui[] = {
|
static const char embed_pxl8_gui[] = {
|
||||||
#embed "client/src/lua/pxl8/gui.lua"
|
#embed "src/lua/pxl8/gui.lua"
|
||||||
, 0 };
|
, 0 };
|
||||||
|
|
||||||
static const char embed_pxl8_input[] = {
|
static const char embed_pxl8_input[] = {
|
||||||
#embed "client/src/lua/pxl8/input.lua"
|
#embed "src/lua/pxl8/input.lua"
|
||||||
, 0 };
|
, 0 };
|
||||||
|
|
||||||
static const char embed_pxl8_math[] = {
|
static const char embed_pxl8_math[] = {
|
||||||
#embed "client/src/lua/pxl8/math.lua"
|
#embed "src/lua/pxl8/math.lua"
|
||||||
|
, 0 };
|
||||||
|
|
||||||
|
static const char embed_pxl8_net[] = {
|
||||||
|
#embed "src/lua/pxl8/net.lua"
|
||||||
, 0 };
|
, 0 };
|
||||||
|
|
||||||
static const char embed_pxl8_particles[] = {
|
static const char embed_pxl8_particles[] = {
|
||||||
#embed "client/src/lua/pxl8/particles.lua"
|
#embed "src/lua/pxl8/particles.lua"
|
||||||
, 0 };
|
, 0 };
|
||||||
|
|
||||||
static const char embed_pxl8_sfx[] = {
|
static const char embed_pxl8_sfx[] = {
|
||||||
#embed "client/src/lua/pxl8/sfx.lua"
|
#embed "src/lua/pxl8/sfx.lua"
|
||||||
, 0 };
|
, 0 };
|
||||||
|
|
||||||
static const char embed_pxl8_tilemap[] = {
|
static const char embed_pxl8_tilemap[] = {
|
||||||
#embed "client/src/lua/pxl8/tilemap.lua"
|
#embed "src/lua/pxl8/tilemap.lua"
|
||||||
, 0 };
|
, 0 };
|
||||||
|
|
||||||
static const char embed_pxl8_transition[] = {
|
static const char embed_pxl8_transition[] = {
|
||||||
#embed "client/src/lua/pxl8/transition.lua"
|
#embed "src/lua/pxl8/transition.lua"
|
||||||
, 0 };
|
, 0 };
|
||||||
|
|
||||||
static const char embed_pxl8_world[] = {
|
static const char embed_pxl8_world[] = {
|
||||||
#embed "client/src/lua/pxl8/world.lua"
|
#embed "src/lua/pxl8/world.lua"
|
||||||
, 0 };
|
, 0 };
|
||||||
|
|
||||||
#define PXL8_EMBED_ENTRY(var, name) {name, var, sizeof(var) - 1}
|
#define PXL8_EMBED_ENTRY(var, name) {name, var, sizeof(var) - 1}
|
||||||
|
|
@ -67,12 +79,15 @@ static const pxl8_embed pxl8_embeds[] = {
|
||||||
PXL8_EMBED_ENTRY(embed_fennel, "fennel"),
|
PXL8_EMBED_ENTRY(embed_fennel, "fennel"),
|
||||||
PXL8_EMBED_ENTRY(embed_pxl8, "pxl8"),
|
PXL8_EMBED_ENTRY(embed_pxl8, "pxl8"),
|
||||||
PXL8_EMBED_ENTRY(embed_pxl8_anim, "pxl8.anim"),
|
PXL8_EMBED_ENTRY(embed_pxl8_anim, "pxl8.anim"),
|
||||||
|
PXL8_EMBED_ENTRY(embed_pxl8_bytes, "pxl8.bytes"),
|
||||||
PXL8_EMBED_ENTRY(embed_pxl8_core, "pxl8.core"),
|
PXL8_EMBED_ENTRY(embed_pxl8_core, "pxl8.core"),
|
||||||
|
PXL8_EMBED_ENTRY(embed_pxl8_effects, "pxl8.effects"),
|
||||||
PXL8_EMBED_ENTRY(embed_pxl8_gfx2d, "pxl8.gfx2d"),
|
PXL8_EMBED_ENTRY(embed_pxl8_gfx2d, "pxl8.gfx2d"),
|
||||||
PXL8_EMBED_ENTRY(embed_pxl8_gfx3d, "pxl8.gfx3d"),
|
PXL8_EMBED_ENTRY(embed_pxl8_gfx3d, "pxl8.gfx3d"),
|
||||||
PXL8_EMBED_ENTRY(embed_pxl8_gui, "pxl8.gui"),
|
PXL8_EMBED_ENTRY(embed_pxl8_gui, "pxl8.gui"),
|
||||||
PXL8_EMBED_ENTRY(embed_pxl8_input, "pxl8.input"),
|
PXL8_EMBED_ENTRY(embed_pxl8_input, "pxl8.input"),
|
||||||
PXL8_EMBED_ENTRY(embed_pxl8_math, "pxl8.math"),
|
PXL8_EMBED_ENTRY(embed_pxl8_math, "pxl8.math"),
|
||||||
|
PXL8_EMBED_ENTRY(embed_pxl8_net, "pxl8.net"),
|
||||||
PXL8_EMBED_ENTRY(embed_pxl8_particles, "pxl8.particles"),
|
PXL8_EMBED_ENTRY(embed_pxl8_particles, "pxl8.particles"),
|
||||||
PXL8_EMBED_ENTRY(embed_pxl8_sfx, "pxl8.sfx"),
|
PXL8_EMBED_ENTRY(embed_pxl8_sfx, "pxl8.sfx"),
|
||||||
PXL8_EMBED_ENTRY(embed_pxl8_tilemap, "pxl8.tilemap"),
|
PXL8_EMBED_ENTRY(embed_pxl8_tilemap, "pxl8.tilemap"),
|
||||||
|
|
@ -6,7 +6,6 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
|
@ -310,7 +309,7 @@ pxl8_result pxl8_update(pxl8* sys) {
|
||||||
|
|
||||||
if (pxl8_script_load_module(game->script, "pxl8") != PXL8_OK) {
|
if (pxl8_script_load_module(game->script, "pxl8") != PXL8_OK) {
|
||||||
const char* err_msg = pxl8_script_get_last_error(game->script);
|
const char* err_msg = pxl8_script_get_last_error(game->script);
|
||||||
pxl8_error("failed to setup pxl8 global: %s", err_msg);
|
pxl8_error("failed to load pxl8 global: %s", err_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
sys->repl = pxl8_repl_create();
|
sys->repl = pxl8_repl_create();
|
||||||
234
src/core/pxl8_bytes.c
Normal file
234
src/core/pxl8_bytes.c
Normal file
|
|
@ -0,0 +1,234 @@
|
||||||
|
#include "pxl8_bytes.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
void pxl8_pack_u8(u8* buf, size_t offset, u8 val) {
|
||||||
|
buf[offset] = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_pack_u16_le(u8* buf, size_t offset, u16 val) {
|
||||||
|
buf[offset] = (u8)(val);
|
||||||
|
buf[offset + 1] = (u8)(val >> 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_pack_u16_be(u8* buf, size_t offset, u16 val) {
|
||||||
|
buf[offset] = (u8)(val >> 8);
|
||||||
|
buf[offset + 1] = (u8)(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_pack_u32_le(u8* buf, size_t offset, u32 val) {
|
||||||
|
buf[offset] = (u8)(val);
|
||||||
|
buf[offset + 1] = (u8)(val >> 8);
|
||||||
|
buf[offset + 2] = (u8)(val >> 16);
|
||||||
|
buf[offset + 3] = (u8)(val >> 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_pack_u32_be(u8* buf, size_t offset, u32 val) {
|
||||||
|
buf[offset] = (u8)(val >> 24);
|
||||||
|
buf[offset + 1] = (u8)(val >> 16);
|
||||||
|
buf[offset + 2] = (u8)(val >> 8);
|
||||||
|
buf[offset + 3] = (u8)(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_pack_u64_le(u8* buf, size_t offset, u64 val) {
|
||||||
|
buf[offset] = (u8)(val);
|
||||||
|
buf[offset + 1] = (u8)(val >> 8);
|
||||||
|
buf[offset + 2] = (u8)(val >> 16);
|
||||||
|
buf[offset + 3] = (u8)(val >> 24);
|
||||||
|
buf[offset + 4] = (u8)(val >> 32);
|
||||||
|
buf[offset + 5] = (u8)(val >> 40);
|
||||||
|
buf[offset + 6] = (u8)(val >> 48);
|
||||||
|
buf[offset + 7] = (u8)(val >> 56);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_pack_u64_be(u8* buf, size_t offset, u64 val) {
|
||||||
|
buf[offset] = (u8)(val >> 56);
|
||||||
|
buf[offset + 1] = (u8)(val >> 48);
|
||||||
|
buf[offset + 2] = (u8)(val >> 40);
|
||||||
|
buf[offset + 3] = (u8)(val >> 32);
|
||||||
|
buf[offset + 4] = (u8)(val >> 24);
|
||||||
|
buf[offset + 5] = (u8)(val >> 16);
|
||||||
|
buf[offset + 6] = (u8)(val >> 8);
|
||||||
|
buf[offset + 7] = (u8)(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_pack_i8(u8* buf, size_t offset, i8 val) {
|
||||||
|
buf[offset] = (u8)val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_pack_i16_le(u8* buf, size_t offset, i16 val) {
|
||||||
|
pxl8_pack_u16_le(buf, offset, (u16)val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_pack_i16_be(u8* buf, size_t offset, i16 val) {
|
||||||
|
pxl8_pack_u16_be(buf, offset, (u16)val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_pack_i32_le(u8* buf, size_t offset, i32 val) {
|
||||||
|
pxl8_pack_u32_le(buf, offset, (u32)val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_pack_i32_be(u8* buf, size_t offset, i32 val) {
|
||||||
|
pxl8_pack_u32_be(buf, offset, (u32)val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_pack_i64_le(u8* buf, size_t offset, i64 val) {
|
||||||
|
pxl8_pack_u64_le(buf, offset, (u64)val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_pack_i64_be(u8* buf, size_t offset, i64 val) {
|
||||||
|
pxl8_pack_u64_be(buf, offset, (u64)val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_pack_f32_le(u8* buf, size_t offset, f32 val) {
|
||||||
|
u32 bits;
|
||||||
|
memcpy(&bits, &val, sizeof(bits));
|
||||||
|
pxl8_pack_u32_le(buf, offset, bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_pack_f32_be(u8* buf, size_t offset, f32 val) {
|
||||||
|
u32 bits;
|
||||||
|
memcpy(&bits, &val, sizeof(bits));
|
||||||
|
pxl8_pack_u32_be(buf, offset, bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_pack_f64_le(u8* buf, size_t offset, f64 val) {
|
||||||
|
u64 bits;
|
||||||
|
memcpy(&bits, &val, sizeof(bits));
|
||||||
|
pxl8_pack_u64_le(buf, offset, bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_pack_f64_be(u8* buf, size_t offset, f64 val) {
|
||||||
|
u64 bits;
|
||||||
|
memcpy(&bits, &val, sizeof(bits));
|
||||||
|
pxl8_pack_u64_be(buf, offset, bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 pxl8_unpack_u8(const u8* buf, size_t offset) {
|
||||||
|
return buf[offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
u16 pxl8_unpack_u16_le(const u8* buf, size_t offset) {
|
||||||
|
return (u16)buf[offset] | ((u16)buf[offset + 1] << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
u16 pxl8_unpack_u16_be(const u8* buf, size_t offset) {
|
||||||
|
return ((u16)buf[offset] << 8) | (u16)buf[offset + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 pxl8_unpack_u32_le(const u8* buf, size_t offset) {
|
||||||
|
return (u32)buf[offset] |
|
||||||
|
((u32)buf[offset + 1] << 8) |
|
||||||
|
((u32)buf[offset + 2] << 16) |
|
||||||
|
((u32)buf[offset + 3] << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 pxl8_unpack_u32_be(const u8* buf, size_t offset) {
|
||||||
|
return ((u32)buf[offset] << 24) |
|
||||||
|
((u32)buf[offset + 1] << 16) |
|
||||||
|
((u32)buf[offset + 2] << 8) |
|
||||||
|
(u32)buf[offset + 3];
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 pxl8_unpack_u64_le(const u8* buf, size_t offset) {
|
||||||
|
return (u64)buf[offset] |
|
||||||
|
((u64)buf[offset + 1] << 8) |
|
||||||
|
((u64)buf[offset + 2] << 16) |
|
||||||
|
((u64)buf[offset + 3] << 24) |
|
||||||
|
((u64)buf[offset + 4] << 32) |
|
||||||
|
((u64)buf[offset + 5] << 40) |
|
||||||
|
((u64)buf[offset + 6] << 48) |
|
||||||
|
((u64)buf[offset + 7] << 56);
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 pxl8_unpack_u64_be(const u8* buf, size_t offset) {
|
||||||
|
return ((u64)buf[offset] << 56) |
|
||||||
|
((u64)buf[offset + 1] << 48) |
|
||||||
|
((u64)buf[offset + 2] << 40) |
|
||||||
|
((u64)buf[offset + 3] << 32) |
|
||||||
|
((u64)buf[offset + 4] << 24) |
|
||||||
|
((u64)buf[offset + 5] << 16) |
|
||||||
|
((u64)buf[offset + 6] << 8) |
|
||||||
|
(u64)buf[offset + 7];
|
||||||
|
}
|
||||||
|
|
||||||
|
i8 pxl8_unpack_i8(const u8* buf, size_t offset) {
|
||||||
|
return (i8)buf[offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
i16 pxl8_unpack_i16_le(const u8* buf, size_t offset) {
|
||||||
|
return (i16)pxl8_unpack_u16_le(buf, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
i16 pxl8_unpack_i16_be(const u8* buf, size_t offset) {
|
||||||
|
return (i16)pxl8_unpack_u16_be(buf, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
i32 pxl8_unpack_i32_le(const u8* buf, size_t offset) {
|
||||||
|
return (i32)pxl8_unpack_u32_le(buf, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
i32 pxl8_unpack_i32_be(const u8* buf, size_t offset) {
|
||||||
|
return (i32)pxl8_unpack_u32_be(buf, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
i64 pxl8_unpack_i64_le(const u8* buf, size_t offset) {
|
||||||
|
return (i64)pxl8_unpack_u64_le(buf, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
i64 pxl8_unpack_i64_be(const u8* buf, size_t offset) {
|
||||||
|
return (i64)pxl8_unpack_u64_be(buf, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
f32 pxl8_unpack_f32_le(const u8* buf, size_t offset) {
|
||||||
|
u32 bits = pxl8_unpack_u32_le(buf, offset);
|
||||||
|
f32 result;
|
||||||
|
memcpy(&result, &bits, sizeof(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
f32 pxl8_unpack_f32_be(const u8* buf, size_t offset) {
|
||||||
|
u32 bits = pxl8_unpack_u32_be(buf, offset);
|
||||||
|
f32 result;
|
||||||
|
memcpy(&result, &bits, sizeof(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
f64 pxl8_unpack_f64_le(const u8* buf, size_t offset) {
|
||||||
|
u64 bits = pxl8_unpack_u64_le(buf, offset);
|
||||||
|
f64 result;
|
||||||
|
memcpy(&result, &bits, sizeof(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
f64 pxl8_unpack_f64_be(const u8* buf, size_t offset) {
|
||||||
|
u64 bits = pxl8_unpack_u64_be(buf, offset);
|
||||||
|
f64 result;
|
||||||
|
memcpy(&result, &bits, sizeof(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_bit_set(u32* val, u8 bit) {
|
||||||
|
*val |= (1u << bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_bit_clear(u32* val, u8 bit) {
|
||||||
|
*val &= ~(1u << bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pxl8_bit_test(u32 val, u8 bit) {
|
||||||
|
return (val & (1u << bit)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 pxl8_bit_count(u32 val) {
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
return (u32)__builtin_popcount(val);
|
||||||
|
#else
|
||||||
|
val = val - ((val >> 1) & 0x55555555);
|
||||||
|
val = (val & 0x33333333) + ((val >> 2) & 0x33333333);
|
||||||
|
return (((val + (val >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_bit_toggle(u32* val, u8 bit) {
|
||||||
|
*val ^= (1u << bit);
|
||||||
|
}
|
||||||
251
src/core/pxl8_bytes.h
Normal file
251
src/core/pxl8_bytes.h
Normal file
|
|
@ -0,0 +1,251 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "pxl8_types.h"
|
||||||
|
|
||||||
|
void pxl8_bit_clear(u32* val, u8 bit);
|
||||||
|
u32 pxl8_bit_count(u32 val);
|
||||||
|
void pxl8_bit_set(u32* val, u8 bit);
|
||||||
|
bool pxl8_bit_test(u32 val, u8 bit);
|
||||||
|
void pxl8_bit_toggle(u32* val, u8 bit);
|
||||||
|
|
||||||
|
void pxl8_pack_u8(u8* buf, size_t offset, u8 val);
|
||||||
|
void pxl8_pack_u16_be(u8* buf, size_t offset, u16 val);
|
||||||
|
void pxl8_pack_u16_le(u8* buf, size_t offset, u16 val);
|
||||||
|
void pxl8_pack_u32_be(u8* buf, size_t offset, u32 val);
|
||||||
|
void pxl8_pack_u32_le(u8* buf, size_t offset, u32 val);
|
||||||
|
void pxl8_pack_u64_be(u8* buf, size_t offset, u64 val);
|
||||||
|
void pxl8_pack_u64_le(u8* buf, size_t offset, u64 val);
|
||||||
|
void pxl8_pack_i8(u8* buf, size_t offset, i8 val);
|
||||||
|
void pxl8_pack_i16_be(u8* buf, size_t offset, i16 val);
|
||||||
|
void pxl8_pack_i16_le(u8* buf, size_t offset, i16 val);
|
||||||
|
void pxl8_pack_i32_be(u8* buf, size_t offset, i32 val);
|
||||||
|
void pxl8_pack_i32_le(u8* buf, size_t offset, i32 val);
|
||||||
|
void pxl8_pack_i64_be(u8* buf, size_t offset, i64 val);
|
||||||
|
void pxl8_pack_i64_le(u8* buf, size_t offset, i64 val);
|
||||||
|
void pxl8_pack_f32_be(u8* buf, size_t offset, f32 val);
|
||||||
|
void pxl8_pack_f32_le(u8* buf, size_t offset, f32 val);
|
||||||
|
void pxl8_pack_f64_be(u8* buf, size_t offset, f64 val);
|
||||||
|
void pxl8_pack_f64_le(u8* buf, size_t offset, f64 val);
|
||||||
|
|
||||||
|
u8 pxl8_unpack_u8(const u8* buf, size_t offset);
|
||||||
|
u16 pxl8_unpack_u16_be(const u8* buf, size_t offset);
|
||||||
|
u16 pxl8_unpack_u16_le(const u8* buf, size_t offset);
|
||||||
|
u32 pxl8_unpack_u32_be(const u8* buf, size_t offset);
|
||||||
|
u32 pxl8_unpack_u32_le(const u8* buf, size_t offset);
|
||||||
|
u64 pxl8_unpack_u64_be(const u8* buf, size_t offset);
|
||||||
|
u64 pxl8_unpack_u64_le(const u8* buf, size_t offset);
|
||||||
|
i8 pxl8_unpack_i8(const u8* buf, size_t offset);
|
||||||
|
i16 pxl8_unpack_i16_be(const u8* buf, size_t offset);
|
||||||
|
i16 pxl8_unpack_i16_le(const u8* buf, size_t offset);
|
||||||
|
i32 pxl8_unpack_i32_be(const u8* buf, size_t offset);
|
||||||
|
i32 pxl8_unpack_i32_le(const u8* buf, size_t offset);
|
||||||
|
i64 pxl8_unpack_i64_be(const u8* buf, size_t offset);
|
||||||
|
i64 pxl8_unpack_i64_le(const u8* buf, size_t offset);
|
||||||
|
f32 pxl8_unpack_f32_be(const u8* buf, size_t offset);
|
||||||
|
f32 pxl8_unpack_f32_le(const u8* buf, size_t offset);
|
||||||
|
f64 pxl8_unpack_f64_be(const u8* buf, size_t offset);
|
||||||
|
f64 pxl8_unpack_f64_le(const u8* buf, size_t offset);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const u8* bytes;
|
||||||
|
u32 offset;
|
||||||
|
u32 size;
|
||||||
|
bool overflow;
|
||||||
|
} pxl8_stream;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u8* bytes;
|
||||||
|
u32 capacity;
|
||||||
|
u32 offset;
|
||||||
|
bool overflow;
|
||||||
|
} pxl8_write_stream;
|
||||||
|
|
||||||
|
static inline pxl8_stream pxl8_stream_create(const u8* bytes, u32 size) {
|
||||||
|
return (pxl8_stream){
|
||||||
|
.bytes = bytes,
|
||||||
|
.offset = 0,
|
||||||
|
.size = size,
|
||||||
|
.overflow = false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline pxl8_write_stream pxl8_write_stream_create(u8* bytes, u32 capacity) {
|
||||||
|
return (pxl8_write_stream){
|
||||||
|
.bytes = bytes,
|
||||||
|
.capacity = capacity,
|
||||||
|
.offset = 0,
|
||||||
|
.overflow = false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool pxl8_stream_can_read(const pxl8_stream* s, u32 count) {
|
||||||
|
return !s->overflow && s->offset + count <= s->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool pxl8_stream_has_overflow(const pxl8_stream* s) {
|
||||||
|
return s->overflow;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool pxl8_write_stream_has_overflow(const pxl8_write_stream* s) {
|
||||||
|
return s->overflow;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 pxl8_stream_position(const pxl8_stream* s) {
|
||||||
|
return s->offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 pxl8_write_stream_position(const pxl8_write_stream* s) {
|
||||||
|
return s->offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void pxl8_stream_seek(pxl8_stream* s, u32 offset) {
|
||||||
|
s->offset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u8 pxl8_read_u8(pxl8_stream* s) {
|
||||||
|
if (s->offset + 1 > s->size) { s->overflow = true; return 0; }
|
||||||
|
return pxl8_unpack_u8(s->bytes, s->offset++);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u16 pxl8_read_u16(pxl8_stream* s) {
|
||||||
|
if (s->offset + 2 > s->size) { s->overflow = true; return 0; }
|
||||||
|
u16 val = pxl8_unpack_u16_le(s->bytes, s->offset);
|
||||||
|
s->offset += 2;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u16 pxl8_read_u16_be(pxl8_stream* s) {
|
||||||
|
if (s->offset + 2 > s->size) { s->overflow = true; return 0; }
|
||||||
|
u16 val = pxl8_unpack_u16_be(s->bytes, s->offset);
|
||||||
|
s->offset += 2;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 pxl8_read_u32(pxl8_stream* s) {
|
||||||
|
if (s->offset + 4 > s->size) { s->overflow = true; return 0; }
|
||||||
|
u32 val = pxl8_unpack_u32_le(s->bytes, s->offset);
|
||||||
|
s->offset += 4;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 pxl8_read_u32_be(pxl8_stream* s) {
|
||||||
|
if (s->offset + 4 > s->size) { s->overflow = true; return 0; }
|
||||||
|
u32 val = pxl8_unpack_u32_be(s->bytes, s->offset);
|
||||||
|
s->offset += 4;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u64 pxl8_read_u64(pxl8_stream* s) {
|
||||||
|
if (s->offset + 8 > s->size) { s->overflow = true; return 0; }
|
||||||
|
u64 val = pxl8_unpack_u64_le(s->bytes, s->offset);
|
||||||
|
s->offset += 8;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u64 pxl8_read_u64_be(pxl8_stream* s) {
|
||||||
|
if (s->offset + 8 > s->size) { s->overflow = true; return 0; }
|
||||||
|
u64 val = pxl8_unpack_u64_be(s->bytes, s->offset);
|
||||||
|
s->offset += 8;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline i16 pxl8_read_i16(pxl8_stream* s) {
|
||||||
|
return (i16)pxl8_read_u16(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline i32 pxl8_read_i32(pxl8_stream* s) {
|
||||||
|
return (i32)pxl8_read_u32(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline f32 pxl8_read_f32(pxl8_stream* s) {
|
||||||
|
if (s->offset + 4 > s->size) { s->overflow = true; return 0; }
|
||||||
|
f32 val = pxl8_unpack_f32_le(s->bytes, s->offset);
|
||||||
|
s->offset += 4;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline f32 pxl8_read_f32_be(pxl8_stream* s) {
|
||||||
|
if (s->offset + 4 > s->size) { s->overflow = true; return 0; }
|
||||||
|
f32 val = pxl8_unpack_f32_be(s->bytes, s->offset);
|
||||||
|
s->offset += 4;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void pxl8_read_bytes(pxl8_stream* s, void* dest, u32 count) {
|
||||||
|
if (s->offset + count > s->size) { s->overflow = true; return; }
|
||||||
|
memcpy(dest, &s->bytes[s->offset], count);
|
||||||
|
s->offset += count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void pxl8_skip_bytes(pxl8_stream* s, u32 count) {
|
||||||
|
if (s->offset + count > s->size) { s->overflow = true; return; }
|
||||||
|
s->offset += count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const u8* pxl8_read_ptr(pxl8_stream* s, u32 count) {
|
||||||
|
if (s->offset + count > s->size) { s->overflow = true; return NULL; }
|
||||||
|
const u8* ptr = &s->bytes[s->offset];
|
||||||
|
s->offset += count;
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void pxl8_write_u8(pxl8_write_stream* s, u8 val) {
|
||||||
|
if (s->offset + 1 > s->capacity) { s->overflow = true; return; }
|
||||||
|
pxl8_pack_u8(s->bytes, s->offset++, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void pxl8_write_u16(pxl8_write_stream* s, u16 val) {
|
||||||
|
if (s->offset + 2 > s->capacity) { s->overflow = true; return; }
|
||||||
|
pxl8_pack_u16_le(s->bytes, s->offset, val);
|
||||||
|
s->offset += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void pxl8_write_u16_be(pxl8_write_stream* s, u16 val) {
|
||||||
|
if (s->offset + 2 > s->capacity) { s->overflow = true; return; }
|
||||||
|
pxl8_pack_u16_be(s->bytes, s->offset, val);
|
||||||
|
s->offset += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void pxl8_write_u32(pxl8_write_stream* s, u32 val) {
|
||||||
|
if (s->offset + 4 > s->capacity) { s->overflow = true; return; }
|
||||||
|
pxl8_pack_u32_le(s->bytes, s->offset, val);
|
||||||
|
s->offset += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void pxl8_write_u32_be(pxl8_write_stream* s, u32 val) {
|
||||||
|
if (s->offset + 4 > s->capacity) { s->overflow = true; return; }
|
||||||
|
pxl8_pack_u32_be(s->bytes, s->offset, val);
|
||||||
|
s->offset += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void pxl8_write_u64(pxl8_write_stream* s, u64 val) {
|
||||||
|
if (s->offset + 8 > s->capacity) { s->overflow = true; return; }
|
||||||
|
pxl8_pack_u64_le(s->bytes, s->offset, val);
|
||||||
|
s->offset += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void pxl8_write_u64_be(pxl8_write_stream* s, u64 val) {
|
||||||
|
if (s->offset + 8 > s->capacity) { s->overflow = true; return; }
|
||||||
|
pxl8_pack_u64_be(s->bytes, s->offset, val);
|
||||||
|
s->offset += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void pxl8_write_f32(pxl8_write_stream* s, f32 val) {
|
||||||
|
if (s->offset + 4 > s->capacity) { s->overflow = true; return; }
|
||||||
|
pxl8_pack_f32_le(s->bytes, s->offset, val);
|
||||||
|
s->offset += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void pxl8_write_f32_be(pxl8_write_stream* s, f32 val) {
|
||||||
|
if (s->offset + 4 > s->capacity) { s->overflow = true; return; }
|
||||||
|
pxl8_pack_f32_be(s->bytes, s->offset, val);
|
||||||
|
s->offset += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void pxl8_write_bytes(pxl8_write_stream* s, const void* src, u32 count) {
|
||||||
|
if (s->offset + count > s->capacity) { s->overflow = true; return; }
|
||||||
|
memcpy(&s->bytes[s->offset], src, count);
|
||||||
|
s->offset += count;
|
||||||
|
}
|
||||||
38
src/core/pxl8_io.h
Normal file
38
src/core/pxl8_io.h
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include "pxl8_bytes.h"
|
||||||
|
#include "pxl8_types.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
pxl8_result pxl8_io_create_directory(const char* path);
|
||||||
|
bool pxl8_io_file_exists(const char* path);
|
||||||
|
void pxl8_io_free_binary_data(u8* data);
|
||||||
|
void pxl8_io_free_file_content(char* content);
|
||||||
|
f64 pxl8_io_get_file_modified_time(const char* path);
|
||||||
|
pxl8_result pxl8_io_read_binary_file(const char* path, u8** data, size_t* size);
|
||||||
|
pxl8_result pxl8_io_read_file(const char* path, char** content, size_t* size);
|
||||||
|
pxl8_result pxl8_io_write_binary_file(const char* path, const u8* data, size_t size);
|
||||||
|
pxl8_result pxl8_io_write_file(const char* path, const char* content, size_t size);
|
||||||
|
|
||||||
|
bool pxl8_key_down(const pxl8_input_state* input, const char* key_name);
|
||||||
|
bool pxl8_key_pressed(const pxl8_input_state* input, const char* key_name);
|
||||||
|
bool pxl8_key_released(const pxl8_input_state* input, const char* key_name);
|
||||||
|
|
||||||
|
i32 pxl8_mouse_dx(const pxl8_input_state* input);
|
||||||
|
i32 pxl8_mouse_dy(const pxl8_input_state* input);
|
||||||
|
bool pxl8_mouse_pressed(const pxl8_input_state* input, i32 button);
|
||||||
|
bool pxl8_mouse_released(const pxl8_input_state* input, i32 button);
|
||||||
|
i32 pxl8_mouse_wheel_x(const pxl8_input_state* input);
|
||||||
|
i32 pxl8_mouse_wheel_y(const pxl8_input_state* input);
|
||||||
|
i32 pxl8_mouse_x(const pxl8_input_state* input);
|
||||||
|
i32 pxl8_mouse_y(const pxl8_input_state* input);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
@ -29,8 +29,9 @@ typedef __uint128_t u128;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef enum pxl8_pixel_mode {
|
typedef enum pxl8_pixel_mode {
|
||||||
PXL8_PIXEL_INDEXED,
|
PXL8_PIXEL_INDEXED = 1,
|
||||||
PXL8_PIXEL_HICOLOR
|
PXL8_PIXEL_HICOLOR = 2,
|
||||||
|
PXL8_PIXEL_RGBA = 4,
|
||||||
} pxl8_pixel_mode;
|
} pxl8_pixel_mode;
|
||||||
|
|
||||||
typedef enum pxl8_cursor {
|
typedef enum pxl8_cursor {
|
||||||
|
|
@ -56,8 +56,8 @@ void pxl8_3d_camera_lookat(pxl8_3d_camera* cam, pxl8_vec3 eye, pxl8_vec3 target,
|
||||||
|
|
||||||
pxl8_vec3 forward = pxl8_vec3_normalize(pxl8_vec3_sub(target, eye));
|
pxl8_vec3 forward = pxl8_vec3_normalize(pxl8_vec3_sub(target, eye));
|
||||||
|
|
||||||
cam->pitch = asinf(-forward.y);
|
cam->pitch = asinf(forward.y);
|
||||||
cam->yaw = atan2f(forward.x, forward.z);
|
cam->yaw = atan2f(-forward.x, -forward.z);
|
||||||
cam->roll = 0;
|
cam->roll = 0;
|
||||||
|
|
||||||
(void)up;
|
(void)up;
|
||||||
|
|
@ -104,9 +104,9 @@ pxl8_vec3 pxl8_3d_camera_get_forward(const pxl8_3d_camera* cam) {
|
||||||
f32 sy = sinf(cam->yaw);
|
f32 sy = sinf(cam->yaw);
|
||||||
|
|
||||||
return (pxl8_vec3){
|
return (pxl8_vec3){
|
||||||
cp * sy,
|
-sy * cp,
|
||||||
-sp,
|
sp,
|
||||||
cp * cy
|
-cy * cp
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -183,8 +183,8 @@ void pxl8_3d_camera_follow(pxl8_3d_camera* cam, pxl8_vec3 target, pxl8_vec3 offs
|
||||||
cam->position = pxl8_vec3_lerp(cam->position, desired, t);
|
cam->position = pxl8_vec3_lerp(cam->position, desired, t);
|
||||||
|
|
||||||
pxl8_vec3 forward = pxl8_vec3_normalize(pxl8_vec3_sub(target, cam->position));
|
pxl8_vec3 forward = pxl8_vec3_normalize(pxl8_vec3_sub(target, cam->position));
|
||||||
cam->pitch = asinf(-forward.y);
|
cam->pitch = asinf(forward.y);
|
||||||
cam->yaw = atan2f(forward.x, forward.z);
|
cam->yaw = atan2f(-forward.x, -forward.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_3d_camera_shake(pxl8_3d_camera* cam, f32 intensity, f32 duration) {
|
void pxl8_3d_camera_shake(pxl8_3d_camera* cam, f32 intensity, f32 duration) {
|
||||||
|
|
@ -212,3 +212,30 @@ void pxl8_3d_camera_update(pxl8_3d_camera* cam, f32 dt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pxl8_projected_point pxl8_3d_camera_world_to_screen(const pxl8_3d_camera* cam, pxl8_vec3 world_pos, u32 screen_width, u32 screen_height) {
|
||||||
|
pxl8_projected_point result = {0, 0, 0.0f, false};
|
||||||
|
if (!cam) return result;
|
||||||
|
|
||||||
|
pxl8_mat4 view = pxl8_3d_camera_get_view(cam);
|
||||||
|
pxl8_mat4 proj = pxl8_3d_camera_get_projection(cam);
|
||||||
|
pxl8_mat4 vp = pxl8_mat4_mul(proj, view);
|
||||||
|
|
||||||
|
pxl8_vec4 clip = pxl8_mat4_mul_vec4(vp, (pxl8_vec4){world_pos.x, world_pos.y, world_pos.z, 1.0f});
|
||||||
|
|
||||||
|
if (clip.w <= 0.0f) return result;
|
||||||
|
|
||||||
|
f32 inv_w = 1.0f / clip.w;
|
||||||
|
f32 ndc_x = clip.x * inv_w;
|
||||||
|
f32 ndc_y = clip.y * inv_w;
|
||||||
|
f32 ndc_z = clip.z * inv_w;
|
||||||
|
|
||||||
|
if (ndc_x < -1.0f || ndc_x > 1.0f || ndc_y < -1.0f || ndc_y > 1.0f) return result;
|
||||||
|
|
||||||
|
result.x = (i32)((ndc_x + 1.0f) * 0.5f * (f32)screen_width);
|
||||||
|
result.y = (i32)((1.0f - ndc_y) * 0.5f * (f32)screen_height);
|
||||||
|
result.depth = ndc_z;
|
||||||
|
result.visible = true;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
@ -32,6 +32,7 @@ pxl8_mat4 pxl8_3d_camera_get_view(const pxl8_3d_camera* cam);
|
||||||
|
|
||||||
void pxl8_3d_camera_blend(pxl8_3d_camera* dest, const pxl8_3d_camera* a, const pxl8_3d_camera* b, f32 t);
|
void pxl8_3d_camera_blend(pxl8_3d_camera* dest, const pxl8_3d_camera* a, const pxl8_3d_camera* b, f32 t);
|
||||||
void pxl8_3d_camera_follow(pxl8_3d_camera* cam, pxl8_vec3 target, pxl8_vec3 offset, f32 smoothing, f32 dt);
|
void pxl8_3d_camera_follow(pxl8_3d_camera* cam, pxl8_vec3 target, pxl8_vec3 offset, f32 smoothing, f32 dt);
|
||||||
|
pxl8_projected_point pxl8_3d_camera_world_to_screen(const pxl8_3d_camera* cam, pxl8_vec3 world_pos, u32 screen_width, u32 screen_height);
|
||||||
void pxl8_3d_camera_shake(pxl8_3d_camera* cam, f32 intensity, f32 duration);
|
void pxl8_3d_camera_shake(pxl8_3d_camera* cam, f32 intensity, f32 duration);
|
||||||
void pxl8_3d_camera_update(pxl8_3d_camera* cam, f32 dt);
|
void pxl8_3d_camera_update(pxl8_3d_camera* cam, f32 dt);
|
||||||
|
|
||||||
|
|
@ -27,6 +27,8 @@ typedef struct pxl8_skyline {
|
||||||
struct pxl8_atlas {
|
struct pxl8_atlas {
|
||||||
u32 height, width;
|
u32 height, width;
|
||||||
u8* pixels;
|
u8* pixels;
|
||||||
|
u8* pixels_tiled;
|
||||||
|
u32 tiled_capacity, tiled_size;
|
||||||
|
|
||||||
bool dirty;
|
bool dirty;
|
||||||
|
|
||||||
|
|
@ -195,6 +197,7 @@ void pxl8_atlas_destroy(pxl8_atlas* atlas) {
|
||||||
free(atlas->entries);
|
free(atlas->entries);
|
||||||
free(atlas->free_list);
|
free(atlas->free_list);
|
||||||
free(atlas->pixels);
|
free(atlas->pixels);
|
||||||
|
free(atlas->pixels_tiled);
|
||||||
free(atlas->skyline.nodes);
|
free(atlas->skyline.nodes);
|
||||||
free(atlas);
|
free(atlas);
|
||||||
}
|
}
|
||||||
|
|
@ -209,6 +212,13 @@ void pxl8_atlas_clear(pxl8_atlas* atlas, u32 preserve_count) {
|
||||||
atlas->entry_count = preserve_count;
|
atlas->entry_count = preserve_count;
|
||||||
atlas->free_count = 0;
|
atlas->free_count = 0;
|
||||||
|
|
||||||
|
if (preserve_count == 0) {
|
||||||
|
atlas->tiled_size = 0;
|
||||||
|
} else {
|
||||||
|
pxl8_atlas_entry* last = &atlas->entries[preserve_count - 1];
|
||||||
|
atlas->tiled_size = last->tiled_base + (u32)(last->w * last->h);
|
||||||
|
}
|
||||||
|
|
||||||
atlas->skyline.nodes[0] = (pxl8_skyline_node){0, 0, (i32)atlas->width};
|
atlas->skyline.nodes[0] = (pxl8_skyline_node){0, 0, (i32)atlas->width};
|
||||||
atlas->skyline.count = 1;
|
atlas->skyline.count = 1;
|
||||||
|
|
||||||
|
|
@ -334,6 +344,7 @@ u32 pxl8_atlas_add_texture(
|
||||||
entry->y = fit.pos.y;
|
entry->y = fit.pos.y;
|
||||||
entry->w = w;
|
entry->w = w;
|
||||||
entry->h = h;
|
entry->h = h;
|
||||||
|
entry->log2_w = pxl8_log2(w);
|
||||||
|
|
||||||
i32 bytes_per_pixel = pxl8_bytes_per_pixel(pixel_mode);
|
i32 bytes_per_pixel = pxl8_bytes_per_pixel(pixel_mode);
|
||||||
for (u32 y = 0; y < h; y++) {
|
for (u32 y = 0; y < h; y++) {
|
||||||
|
|
@ -349,6 +360,30 @@ u32 pxl8_atlas_add_texture(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 tiled_tex_size = w * h;
|
||||||
|
u32 new_tiled_size = atlas->tiled_size + tiled_tex_size;
|
||||||
|
if (new_tiled_size > atlas->tiled_capacity) {
|
||||||
|
u32 new_cap = atlas->tiled_capacity ? atlas->tiled_capacity * 2 : 4096;
|
||||||
|
while (new_cap < new_tiled_size) new_cap *= 2;
|
||||||
|
u8* new_tiled = (u8*)realloc(atlas->pixels_tiled, new_cap);
|
||||||
|
if (!new_tiled) {
|
||||||
|
entry->active = false;
|
||||||
|
return UINT32_MAX;
|
||||||
|
}
|
||||||
|
atlas->pixels_tiled = new_tiled;
|
||||||
|
atlas->tiled_capacity = new_cap;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->tiled_base = atlas->tiled_size;
|
||||||
|
u8* tiled_dst = atlas->pixels_tiled + entry->tiled_base;
|
||||||
|
for (u32 ty = 0; ty < h; ty++) {
|
||||||
|
for (u32 tx = 0; tx < w; tx++) {
|
||||||
|
u32 tiled_offset = pxl8_tile_addr(tx, ty, entry->log2_w);
|
||||||
|
tiled_dst[tiled_offset] = pixels[ty * w + tx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
atlas->tiled_size = new_tiled_size;
|
||||||
|
|
||||||
if (!pxl8_skyline_add_rect(&atlas->skyline, fit.pos, w, h)) {
|
if (!pxl8_skyline_add_rect(&atlas->skyline, fit.pos, w, h)) {
|
||||||
entry->active = false;
|
entry->active = false;
|
||||||
return UINT32_MAX;
|
return UINT32_MAX;
|
||||||
|
|
@ -377,6 +412,10 @@ const u8* pxl8_atlas_get_pixels(const pxl8_atlas* atlas) {
|
||||||
return atlas ? atlas->pixels : NULL;
|
return atlas ? atlas->pixels : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const u8* pxl8_atlas_get_pixels_tiled(const pxl8_atlas* atlas) {
|
||||||
|
return atlas ? atlas->pixels_tiled : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
u32 pxl8_atlas_get_width(const pxl8_atlas* atlas) {
|
u32 pxl8_atlas_get_width(const pxl8_atlas* atlas) {
|
||||||
return atlas ? atlas->width : 0;
|
return atlas ? atlas->width : 0;
|
||||||
}
|
}
|
||||||
|
|
@ -8,28 +8,42 @@ typedef struct pxl8_atlas_entry {
|
||||||
bool active;
|
bool active;
|
||||||
u32 texture_id;
|
u32 texture_id;
|
||||||
i32 x, y, w, h;
|
i32 x, y, w, h;
|
||||||
|
u32 tiled_base;
|
||||||
|
u8 log2_w;
|
||||||
} pxl8_atlas_entry;
|
} pxl8_atlas_entry;
|
||||||
|
|
||||||
|
static inline u32 pxl8_tile_addr(u32 u, u32 v, u8 log2_w) {
|
||||||
|
u32 tile_y = v >> 3;
|
||||||
|
u32 tile_x = u >> 3;
|
||||||
|
u32 local_y = v & 7;
|
||||||
|
u32 local_x = u & 7;
|
||||||
|
return (tile_y << (log2_w + 3)) | (tile_x << 6) | (local_y << 3) | local_x;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u8 pxl8_log2(u32 v) {
|
||||||
|
u8 r = 0;
|
||||||
|
while (v >>= 1) r++;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
u32 pxl8_atlas_add_texture(pxl8_atlas* atlas, const u8* pixels, u32 w, u32 h, pxl8_pixel_mode pixel_mode);
|
||||||
|
void pxl8_atlas_clear(pxl8_atlas* atlas, u32 preserve_count);
|
||||||
pxl8_atlas* pxl8_atlas_create(u32 width, u32 height, pxl8_pixel_mode pixel_mode);
|
pxl8_atlas* pxl8_atlas_create(u32 width, u32 height, pxl8_pixel_mode pixel_mode);
|
||||||
void pxl8_atlas_destroy(pxl8_atlas* atlas);
|
void pxl8_atlas_destroy(pxl8_atlas* atlas);
|
||||||
|
bool pxl8_atlas_expand(pxl8_atlas* atlas, pxl8_pixel_mode pixel_mode);
|
||||||
const pxl8_atlas_entry* pxl8_atlas_get_entry(const pxl8_atlas* atlas, u32 id);
|
const pxl8_atlas_entry* pxl8_atlas_get_entry(const pxl8_atlas* atlas, u32 id);
|
||||||
u32 pxl8_atlas_get_entry_count(const pxl8_atlas* atlas);
|
u32 pxl8_atlas_get_entry_count(const pxl8_atlas* atlas);
|
||||||
u32 pxl8_atlas_get_height(const pxl8_atlas* atlas);
|
u32 pxl8_atlas_get_height(const pxl8_atlas* atlas);
|
||||||
const u8* pxl8_atlas_get_pixels(const pxl8_atlas* atlas);
|
const u8* pxl8_atlas_get_pixels(const pxl8_atlas* atlas);
|
||||||
|
const u8* pxl8_atlas_get_pixels_tiled(const pxl8_atlas* atlas);
|
||||||
u32 pxl8_atlas_get_width(const pxl8_atlas* atlas);
|
u32 pxl8_atlas_get_width(const pxl8_atlas* atlas);
|
||||||
bool pxl8_atlas_is_dirty(const pxl8_atlas* atlas);
|
bool pxl8_atlas_is_dirty(const pxl8_atlas* atlas);
|
||||||
|
|
||||||
void pxl8_atlas_mark_clean(pxl8_atlas* atlas);
|
void pxl8_atlas_mark_clean(pxl8_atlas* atlas);
|
||||||
|
|
||||||
u32 pxl8_atlas_add_texture(pxl8_atlas* atlas, const u8* pixels, u32 w, u32 h, pxl8_pixel_mode pixel_mode);
|
|
||||||
void pxl8_atlas_clear(pxl8_atlas* atlas, u32 preserve_count);
|
|
||||||
bool pxl8_atlas_expand(pxl8_atlas* atlas, pxl8_pixel_mode pixel_mode);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
#include "pxl8_types.h"
|
#include "pxl8_types.h"
|
||||||
|
|
||||||
static inline i32 pxl8_bytes_per_pixel(pxl8_pixel_mode mode) {
|
static inline i32 pxl8_bytes_per_pixel(pxl8_pixel_mode mode) {
|
||||||
return (mode == PXL8_PIXEL_HICOLOR) ? 2 : 1;
|
return (i32)mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u16 pxl8_rgb565_pack(u8 r, u8 g, u8 b) {
|
static inline u16 pxl8_rgb565_pack(u8 r, u8 g, u8 b) {
|
||||||
123
src/gfx/pxl8_colormap.c
Normal file
123
src/gfx/pxl8_colormap.c
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
#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 & 0xFF;
|
||||||
|
u8 pg = (c >> 8) & 0xFF;
|
||||||
|
u8 pb = (c >> 16) & 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_set_colormap(pxl8_colormap* cm, const u8* data, u32 size) {
|
||||||
|
if (!cm || !data || size == 0) return;
|
||||||
|
u32 copy_size = size > PXL8_COLORMAP_SIZE ? PXL8_COLORMAP_SIZE : size;
|
||||||
|
memcpy(cm->table, data, copy_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void generate_light_table(pxl8_colormap* cm, const u32* palette, pxl8_light_color light_color) {
|
||||||
|
pxl8_rgb light = pxl8_light_colors[light_color];
|
||||||
|
u32 base_row = (u32)light_color * PXL8_LIGHT_LEVELS;
|
||||||
|
|
||||||
|
for (u32 level = 0; level < PXL8_LIGHT_LEVELS; level++) {
|
||||||
|
f32 brightness = (f32)level / (f32)(PXL8_LIGHT_LEVELS - 1);
|
||||||
|
u32 row = base_row + level;
|
||||||
|
|
||||||
|
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 & 0xFF;
|
||||||
|
u8 g = (c >> 8) & 0xFF;
|
||||||
|
u8 b = (c >> 16) & 0xFF;
|
||||||
|
|
||||||
|
f32 lr = (f32)light.r / 255.0f;
|
||||||
|
f32 lg = (f32)light.g / 255.0f;
|
||||||
|
f32 lb = (f32)light.b / 255.0f;
|
||||||
|
|
||||||
|
u8 target_r = (u8)(r * brightness * lr);
|
||||||
|
u8 target_g = (u8)(g * brightness * lg);
|
||||||
|
u8 target_b = (u8)(b * brightness * lb);
|
||||||
|
|
||||||
|
result_idx = find_closest_color(palette, target_r, target_g, target_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
cm->table[row * 256 + pal_idx] = result_idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void generate_blend_table(pxl8_colormap* cm, const u32* palette) {
|
||||||
|
for (u32 src = 0; src < 256; src++) {
|
||||||
|
u32 row = PXL8_LIGHT_ROWS + src;
|
||||||
|
|
||||||
|
u8 sr, sg, sb;
|
||||||
|
if (src == PXL8_TRANSPARENT) {
|
||||||
|
sr = sg = sb = 0;
|
||||||
|
} else {
|
||||||
|
u32 sc = palette[src];
|
||||||
|
sr = sc & 0xFF;
|
||||||
|
sg = (sc >> 8) & 0xFF;
|
||||||
|
sb = (sc >> 16) & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (u32 dst = 0; dst < 256; dst++) {
|
||||||
|
u8 result_idx;
|
||||||
|
|
||||||
|
if (src == PXL8_TRANSPARENT) {
|
||||||
|
result_idx = (u8)dst;
|
||||||
|
} else {
|
||||||
|
u32 dc = palette[dst];
|
||||||
|
u8 dr = dc & 0xFF;
|
||||||
|
u8 dg = (dc >> 8) & 0xFF;
|
||||||
|
u8 db = (dc >> 16) & 0xFF;
|
||||||
|
|
||||||
|
u8 blend_r = (u8)((sr + dr) / 2);
|
||||||
|
u8 blend_g = (u8)((sg + dg) / 2);
|
||||||
|
u8 blend_b = (u8)((sb + db) / 2);
|
||||||
|
|
||||||
|
result_idx = find_closest_color(palette, blend_r, blend_g, blend_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
cm->table[row * 256 + dst] = result_idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_colormap_generate(pxl8_colormap* cm, const u32* palette) {
|
||||||
|
if (!cm || !palette) return;
|
||||||
|
|
||||||
|
for (u32 light = 0; light < PXL8_LIGHT_COLORS; light++) {
|
||||||
|
generate_light_table(cm, palette, (pxl8_light_color)light);
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_blend_table(cm, palette);
|
||||||
|
}
|
||||||
73
src/gfx/pxl8_colormap.h
Normal file
73
src/gfx/pxl8_colormap.h
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "pxl8_dither.h"
|
||||||
|
#include "pxl8_types.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define PXL8_LIGHT_COLORS 8
|
||||||
|
#define PXL8_LIGHT_LEVELS 8
|
||||||
|
#define PXL8_LIGHT_ROWS (PXL8_LIGHT_COLORS * PXL8_LIGHT_LEVELS)
|
||||||
|
#define PXL8_BLEND_ROWS 256
|
||||||
|
#define PXL8_COLORMAP_ROWS (PXL8_LIGHT_ROWS + PXL8_BLEND_ROWS)
|
||||||
|
#define PXL8_COLORMAP_SIZE (256 * PXL8_COLORMAP_ROWS)
|
||||||
|
|
||||||
|
#define PXL8_FULLBRIGHT_START 240
|
||||||
|
#define PXL8_TRANSPARENT 0
|
||||||
|
#define PXL8_DYNAMIC_RANGE_START 144
|
||||||
|
#define PXL8_DYNAMIC_RANGE_COUNT 16
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
PXL8_LIGHT_WHITE = 0,
|
||||||
|
PXL8_LIGHT_RED = 1,
|
||||||
|
PXL8_LIGHT_ORANGE = 2,
|
||||||
|
PXL8_LIGHT_YELLOW = 3,
|
||||||
|
PXL8_LIGHT_GREEN = 4,
|
||||||
|
PXL8_LIGHT_CYAN = 5,
|
||||||
|
PXL8_LIGHT_BLUE = 6,
|
||||||
|
PXL8_LIGHT_PURPLE = 7,
|
||||||
|
} pxl8_light_color;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u8 r, g, b;
|
||||||
|
} pxl8_rgb;
|
||||||
|
|
||||||
|
static const pxl8_rgb pxl8_light_colors[PXL8_LIGHT_COLORS] = {
|
||||||
|
{255, 255, 255},
|
||||||
|
{255, 64, 64},
|
||||||
|
{255, 160, 64},
|
||||||
|
{255, 255, 64},
|
||||||
|
{64, 255, 64},
|
||||||
|
{64, 255, 255},
|
||||||
|
{64, 64, 255},
|
||||||
|
{255, 64, 255},
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u8 table[PXL8_COLORMAP_SIZE];
|
||||||
|
} pxl8_colormap;
|
||||||
|
|
||||||
|
void pxl8_colormap_generate(pxl8_colormap* cm, const u32* palette);
|
||||||
|
void pxl8_set_colormap(pxl8_colormap* cm, const u8* data, u32 size);
|
||||||
|
|
||||||
|
static inline u8 pxl8_colormap_lookup(const pxl8_colormap* cm, u8 pal_idx, pxl8_light_color light_color, u8 intensity) {
|
||||||
|
u32 light_row = ((u32)light_color << 3) + (intensity >> 5);
|
||||||
|
return cm->table[(light_row << 8) + pal_idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u8 pxl8_colormap_lookup_dithered(const pxl8_colormap* cm, u8 pal_idx, pxl8_light_color light_color, u8 intensity, u32 x, u32 y) {
|
||||||
|
u8 dithered = pxl8_dither_light(intensity, x, y);
|
||||||
|
u32 light_row = ((u32)light_color << 3) + (dithered >> 5);
|
||||||
|
return cm->table[(light_row << 8) + pal_idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u8 pxl8_colormap_blend(const pxl8_colormap* cm, u8 src, u8 dst) {
|
||||||
|
u32 blend_row = PXL8_LIGHT_ROWS + src;
|
||||||
|
return cm->table[(blend_row << 8) + dst];
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
@ -1,18 +1,116 @@
|
||||||
#include "pxl8_cpu.h"
|
#include "pxl8_cpu.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "pxl8_simd.h"
|
|
||||||
|
|
||||||
struct pxl8_cpu_render_target {
|
struct pxl8_cpu_render_target {
|
||||||
u8* framebuffer;
|
u8* framebuffer;
|
||||||
u32 height;
|
u32 height;
|
||||||
u32 width;
|
u32 width;
|
||||||
f32* zbuffer;
|
u16* zbuffer;
|
||||||
u32* light_accum;
|
u32* light_accum;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static inline u16 depth_to_u16(f32 z) {
|
||||||
|
f32 d = (z + 1.0f) * 32767.5f;
|
||||||
|
if (d < 0.0f) d = 0.0f;
|
||||||
|
if (d > 65535.0f) d = 65535.0f;
|
||||||
|
return (u16)d;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline f32 calc_light_intensity(const pxl8_light* light, pxl8_vec3 world_pos, pxl8_vec3 normal) {
|
||||||
|
pxl8_vec3 to_light = pxl8_vec3_sub(light->position, world_pos);
|
||||||
|
f32 dist_sq = pxl8_vec3_dot(to_light, to_light);
|
||||||
|
|
||||||
|
if (dist_sq >= light->radius_sq) {
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
f32 intensity_norm = light->intensity * (1.0f / 255.0f);
|
||||||
|
|
||||||
|
if (dist_sq < 0.001f) {
|
||||||
|
return intensity_norm;
|
||||||
|
}
|
||||||
|
|
||||||
|
f32 inv_dist = pxl8_fast_inv_sqrt(dist_sq);
|
||||||
|
pxl8_vec3 light_dir = pxl8_vec3_scale(to_light, inv_dist);
|
||||||
|
f32 n_dot_l = pxl8_vec3_dot(normal, light_dir);
|
||||||
|
|
||||||
|
if (n_dot_l <= 0.0f) {
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
f32 falloff = 1.0f - dist_sq * light->inv_radius_sq;
|
||||||
|
return n_dot_l * falloff * intensity_norm;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct pxl8_light_result {
|
||||||
|
u8 light;
|
||||||
|
u32 light_color;
|
||||||
|
} pxl8_light_result;
|
||||||
|
|
||||||
|
static pxl8_light_result calc_vertex_light(
|
||||||
|
pxl8_vec3 world_pos,
|
||||||
|
pxl8_vec3 normal,
|
||||||
|
const pxl8_3d_frame* frame
|
||||||
|
) {
|
||||||
|
f32 intensity = 0.25f + frame->uniforms.ambient * (1.0f / 255.0f);
|
||||||
|
|
||||||
|
f32 accum_r = 0.0f;
|
||||||
|
f32 accum_g = 0.0f;
|
||||||
|
f32 accum_b = 0.0f;
|
||||||
|
f32 total_dynamic = 0.0f;
|
||||||
|
|
||||||
|
f32 celestial_dot = -pxl8_vec3_dot(normal, frame->uniforms.celestial_dir);
|
||||||
|
if (celestial_dot > 0.0f) {
|
||||||
|
intensity += celestial_dot * frame->uniforms.celestial_intensity;
|
||||||
|
}
|
||||||
|
|
||||||
|
f32 sky_factor = normal.y * 0.5f + 0.5f;
|
||||||
|
if (sky_factor < 0.0f) sky_factor = 0.0f;
|
||||||
|
intensity += sky_factor * frame->uniforms.celestial_intensity * 0.3f;
|
||||||
|
|
||||||
|
for (u32 i = 0; i < frame->uniforms.num_lights; i++) {
|
||||||
|
const pxl8_light* light = &frame->uniforms.lights[i];
|
||||||
|
f32 contrib = calc_light_intensity(light, world_pos, normal);
|
||||||
|
if (contrib > 0.0f) {
|
||||||
|
intensity += contrib;
|
||||||
|
total_dynamic += contrib;
|
||||||
|
|
||||||
|
accum_r += light->r * contrib;
|
||||||
|
accum_g += light->g * contrib;
|
||||||
|
accum_b += light->b * contrib;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 light_color = 0;
|
||||||
|
if (total_dynamic > 0.001f) {
|
||||||
|
f32 inv_total = 1.0f / total_dynamic;
|
||||||
|
f32 tint_strength = total_dynamic * 1.5f;
|
||||||
|
if (tint_strength > 1.0f) tint_strength = 1.0f;
|
||||||
|
|
||||||
|
u32 r = (u32)(accum_r * inv_total);
|
||||||
|
u32 g = (u32)(accum_g * inv_total);
|
||||||
|
u32 b = (u32)(accum_b * inv_total);
|
||||||
|
u32 a = (u32)(tint_strength * 255.0f);
|
||||||
|
|
||||||
|
if (r > 255) r = 255;
|
||||||
|
if (g > 255) g = 255;
|
||||||
|
if (b > 255) b = 255;
|
||||||
|
|
||||||
|
light_color = r | (g << 8) | (b << 16) | (a << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (intensity < 0.0f) intensity = 0.0f;
|
||||||
|
if (intensity > 1.0f) intensity = 1.0f;
|
||||||
|
|
||||||
|
return (pxl8_light_result){
|
||||||
|
.light = (u8)(intensity * 255.0f),
|
||||||
|
.light_color = light_color,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#define PXL8_MAX_TARGET_STACK 8
|
#define PXL8_MAX_TARGET_STACK 8
|
||||||
|
|
||||||
struct pxl8_cpu_backend {
|
struct pxl8_cpu_backend {
|
||||||
|
|
@ -20,9 +118,12 @@ struct pxl8_cpu_backend {
|
||||||
pxl8_cpu_render_target* target_stack[PXL8_MAX_TARGET_STACK];
|
pxl8_cpu_render_target* target_stack[PXL8_MAX_TARGET_STACK];
|
||||||
u32 target_stack_depth;
|
u32 target_stack_depth;
|
||||||
const pxl8_colormap* colormap;
|
const pxl8_colormap* colormap;
|
||||||
|
const pxl8_palette_cube* palette_cube;
|
||||||
const u32* palette;
|
const u32* palette;
|
||||||
pxl8_3d_frame frame;
|
pxl8_3d_frame frame;
|
||||||
pxl8_mat4 mvp;
|
pxl8_mat4 mvp;
|
||||||
|
u32* output;
|
||||||
|
u32 output_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void clip_line_2d(i32* x0, i32* y0, i32* x1, i32* y1, i32 w, i32 h, bool* visible) {
|
static inline void clip_line_2d(i32* x0, i32* y0, i32* x1, i32* y1, i32 w, i32 h, bool* visible) {
|
||||||
|
|
@ -90,6 +191,15 @@ pxl8_cpu_backend* pxl8_cpu_create(u32 width, u32 height) {
|
||||||
cpu->target_stack[0] = base_target;
|
cpu->target_stack[0] = base_target;
|
||||||
cpu->target_stack_depth = 1;
|
cpu->target_stack_depth = 1;
|
||||||
cpu->current_target = base_target;
|
cpu->current_target = base_target;
|
||||||
|
|
||||||
|
cpu->output_size = width * height;
|
||||||
|
cpu->output = calloc(cpu->output_size, sizeof(u32));
|
||||||
|
if (!cpu->output) {
|
||||||
|
pxl8_cpu_destroy_render_target(base_target);
|
||||||
|
free(cpu);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return cpu;
|
return cpu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -98,6 +208,7 @@ void pxl8_cpu_destroy(pxl8_cpu_backend* cpu) {
|
||||||
for (u32 i = 0; i < cpu->target_stack_depth; i++) {
|
for (u32 i = 0; i < cpu->target_stack_depth; i++) {
|
||||||
pxl8_cpu_destroy_render_target(cpu->target_stack[i]);
|
pxl8_cpu_destroy_render_target(cpu->target_stack[i]);
|
||||||
}
|
}
|
||||||
|
free(cpu->output);
|
||||||
free(cpu);
|
free(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -120,11 +231,12 @@ void pxl8_cpu_clear(pxl8_cpu_backend* cpu, u8 color) {
|
||||||
void pxl8_cpu_clear_depth(pxl8_cpu_backend* cpu) {
|
void pxl8_cpu_clear_depth(pxl8_cpu_backend* cpu) {
|
||||||
if (!cpu || !cpu->current_target) return;
|
if (!cpu || !cpu->current_target) return;
|
||||||
pxl8_cpu_render_target* render_target = cpu->current_target;
|
pxl8_cpu_render_target* render_target = cpu->current_target;
|
||||||
|
u32 count = render_target->width * render_target->height;
|
||||||
if (render_target->zbuffer) {
|
if (render_target->zbuffer) {
|
||||||
memset(render_target->zbuffer, 0x7F, render_target->width * render_target->height * sizeof(f32));
|
memset(render_target->zbuffer, 0xFF, count * sizeof(u16));
|
||||||
}
|
}
|
||||||
if (render_target->light_accum) {
|
if (render_target->light_accum) {
|
||||||
memset(render_target->light_accum, 0, render_target->width * render_target->height * sizeof(u32));
|
memset(render_target->light_accum, 0, count * sizeof(u32));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -132,6 +244,10 @@ void pxl8_cpu_set_colormap(pxl8_cpu_backend* cpu, const pxl8_colormap* cm) {
|
||||||
if (cpu) cpu->colormap = cm;
|
if (cpu) cpu->colormap = cm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pxl8_cpu_set_palette_cube(pxl8_cpu_backend* cpu, const pxl8_palette_cube* cube) {
|
||||||
|
if (cpu) cpu->palette_cube = cube;
|
||||||
|
}
|
||||||
|
|
||||||
void pxl8_cpu_set_palette(pxl8_cpu_backend* cpu, const u32* palette) {
|
void pxl8_cpu_set_palette(pxl8_cpu_backend* cpu, const u32* palette) {
|
||||||
if (cpu) cpu->palette = palette;
|
if (cpu) cpu->palette = palette;
|
||||||
}
|
}
|
||||||
|
|
@ -275,8 +391,44 @@ typedef struct {
|
||||||
f32 u, v;
|
f32 u, v;
|
||||||
u8 color;
|
u8 color;
|
||||||
u8 light;
|
u8 light;
|
||||||
|
u32 light_color;
|
||||||
} vertex_output;
|
} vertex_output;
|
||||||
|
|
||||||
|
static inline u32 blend_tri_light_color(u32 lc0, u32 lc1, u32 lc2) {
|
||||||
|
u32 a0 = (lc0 >> 24) & 0xFF;
|
||||||
|
u32 a1 = (lc1 >> 24) & 0xFF;
|
||||||
|
u32 a2 = (lc2 >> 24) & 0xFF;
|
||||||
|
u32 total_a = a0 + a1 + a2;
|
||||||
|
if (total_a == 0) return 0;
|
||||||
|
|
||||||
|
u32 r = ((lc0 & 0xFF) * a0 + (lc1 & 0xFF) * a1 + (lc2 & 0xFF) * a2) / total_a;
|
||||||
|
u32 g = (((lc0 >> 8) & 0xFF) * a0 + ((lc1 >> 8) & 0xFF) * a1 + ((lc2 >> 8) & 0xFF) * a2) / total_a;
|
||||||
|
u32 b = (((lc0 >> 16) & 0xFF) * a0 + ((lc1 >> 16) & 0xFF) * a1 + ((lc2 >> 16) & 0xFF) * a2) / total_a;
|
||||||
|
u32 a = total_a / 3;
|
||||||
|
if (a > 255) a = 255;
|
||||||
|
|
||||||
|
return r | (g << 8) | (b << 16) | (a << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 lerp_light_color(u32 a, u32 b, f32 t) {
|
||||||
|
u32 ar = a & 0xFF;
|
||||||
|
u32 ag = (a >> 8) & 0xFF;
|
||||||
|
u32 ab = (a >> 16) & 0xFF;
|
||||||
|
u32 aa = (a >> 24) & 0xFF;
|
||||||
|
|
||||||
|
u32 br = b & 0xFF;
|
||||||
|
u32 bg = (b >> 8) & 0xFF;
|
||||||
|
u32 bb = (b >> 16) & 0xFF;
|
||||||
|
u32 ba = (b >> 24) & 0xFF;
|
||||||
|
|
||||||
|
u32 or = ar + (u32)((i32)(br - ar) * t);
|
||||||
|
u32 og = ag + (u32)((i32)(bg - ag) * t);
|
||||||
|
u32 ob = ab + (u32)((i32)(bb - ab) * t);
|
||||||
|
u32 oa = aa + (u32)((i32)(ba - aa) * t);
|
||||||
|
|
||||||
|
return or | (og << 8) | (ob << 16) | (oa << 24);
|
||||||
|
}
|
||||||
|
|
||||||
static vertex_output lerp_vertex(const vertex_output* a, const vertex_output* b, f32 t) {
|
static vertex_output lerp_vertex(const vertex_output* a, const vertex_output* b, f32 t) {
|
||||||
vertex_output out;
|
vertex_output out;
|
||||||
out.clip_pos.x = a->clip_pos.x + (b->clip_pos.x - a->clip_pos.x) * t;
|
out.clip_pos.x = a->clip_pos.x + (b->clip_pos.x - a->clip_pos.x) * t;
|
||||||
|
|
@ -293,6 +445,7 @@ static vertex_output lerp_vertex(const vertex_output* a, const vertex_output* b,
|
||||||
out.v = a->v + (b->v - a->v) * t;
|
out.v = a->v + (b->v - a->v) * t;
|
||||||
out.color = t < 0.5f ? a->color : b->color;
|
out.color = t < 0.5f ? a->color : b->color;
|
||||||
out.light = (u8)(a->light + (b->light - a->light) * t);
|
out.light = (u8)(a->light + (b->light - a->light) * t);
|
||||||
|
out.light_color = lerp_light_color(a->light_color, b->light_color, t);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -348,6 +501,8 @@ typedef struct {
|
||||||
f32 w0_recip, w1_recip, w2_recip;
|
f32 w0_recip, w1_recip, w2_recip;
|
||||||
f32 u0_w, v0_w, u1_w, v1_w, u2_w, v2_w;
|
f32 u0_w, v0_w, u1_w, v1_w, u2_w, v2_w;
|
||||||
f32 l0_w, l1_w, l2_w;
|
f32 l0_w, l1_w, l2_w;
|
||||||
|
u32 lc0, lc1, lc2;
|
||||||
|
f32 c0_w, c1_w, c2_w;
|
||||||
i32 y_start, y_end, total_height;
|
i32 y_start, y_end, total_height;
|
||||||
f32 inv_total;
|
f32 inv_total;
|
||||||
u32 target_width, target_height;
|
u32 target_width, target_height;
|
||||||
|
|
@ -419,6 +574,14 @@ static bool setup_triangle(
|
||||||
setup->l1_w = sorted[1]->light * setup->w1_recip;
|
setup->l1_w = sorted[1]->light * setup->w1_recip;
|
||||||
setup->l2_w = sorted[2]->light * setup->w2_recip;
|
setup->l2_w = sorted[2]->light * setup->w2_recip;
|
||||||
|
|
||||||
|
setup->lc0 = sorted[0]->light_color;
|
||||||
|
setup->lc1 = sorted[1]->light_color;
|
||||||
|
setup->lc2 = sorted[2]->light_color;
|
||||||
|
|
||||||
|
setup->c0_w = (f32)sorted[0]->color * setup->w0_recip;
|
||||||
|
setup->c1_w = (f32)sorted[1]->color * setup->w1_recip;
|
||||||
|
setup->c2_w = (f32)sorted[2]->color * setup->w2_recip;
|
||||||
|
|
||||||
setup->inv_total = 1.0f / (f32)setup->total_height;
|
setup->inv_total = 1.0f / (f32)setup->total_height;
|
||||||
setup->target_width = width;
|
setup->target_width = width;
|
||||||
setup->target_height = height;
|
setup->target_height = height;
|
||||||
|
|
@ -432,6 +595,8 @@ static void rasterize_triangle_opaque(
|
||||||
const pxl8_atlas* textures, u32 texture_id, bool dither
|
const pxl8_atlas* textures, u32 texture_id, bool dither
|
||||||
) {
|
) {
|
||||||
pxl8_cpu_render_target* render_target = cpu->current_target;
|
pxl8_cpu_render_target* render_target = cpu->current_target;
|
||||||
|
u32* light_accum = render_target->light_accum;
|
||||||
|
u32 tri_light_color = light_accum ? blend_tri_light_color(setup->lc0, setup->lc1, setup->lc2) : 0;
|
||||||
const pxl8_atlas_entry* tex_entry = textures ? pxl8_atlas_get_entry(textures, texture_id) : NULL;
|
const pxl8_atlas_entry* tex_entry = textures ? pxl8_atlas_get_entry(textures, texture_id) : NULL;
|
||||||
u32 atlas_width = textures ? pxl8_atlas_get_width(textures) : 1;
|
u32 atlas_width = textures ? pxl8_atlas_get_width(textures) : 1;
|
||||||
f32 tex_w = tex_entry ? (f32)tex_entry->w : 1.0f;
|
f32 tex_w = tex_entry ? (f32)tex_entry->w : 1.0f;
|
||||||
|
|
@ -516,7 +681,7 @@ static void rasterize_triangle_opaque(
|
||||||
|
|
||||||
u32 row_start = (u32)y * render_target->width;
|
u32 row_start = (u32)y * render_target->width;
|
||||||
u8* prow = render_target->framebuffer + row_start;
|
u8* prow = render_target->framebuffer + row_start;
|
||||||
f32* zrow = render_target->zbuffer + row_start;
|
u16* zrow = render_target->zbuffer + row_start;
|
||||||
|
|
||||||
const i32 SUBDIV = 32;
|
const i32 SUBDIV = 32;
|
||||||
i32 x = x_start;
|
i32 x = x_start;
|
||||||
|
|
@ -536,6 +701,22 @@ static void rasterize_triangle_opaque(
|
||||||
f32 v_end = (vw + dvw * steps) * pw_end;
|
f32 v_end = (vw + dvw * steps) * pw_end;
|
||||||
f32 l_start = lw * pw_start;
|
f32 l_start = lw * pw_start;
|
||||||
f32 l_end = (lw + d_lw * steps) * pw_end;
|
f32 l_end = (lw + d_lw * steps) * pw_end;
|
||||||
|
|
||||||
|
f32 fog_density = cpu->frame.uniforms.fog_density;
|
||||||
|
if (fog_density > 0.0f) {
|
||||||
|
f32 z_end_fog = z + dz * steps;
|
||||||
|
f32 t_start = (z + 1.0f) * 0.5f;
|
||||||
|
f32 t_end = (z_end_fog + 1.0f) * 0.5f;
|
||||||
|
if (t_start < 0.0f) t_start = 0.0f;
|
||||||
|
if (t_end < 0.0f) t_end = 0.0f;
|
||||||
|
f32 fog_start = t_start * t_start * fog_density;
|
||||||
|
f32 fog_end = t_end * t_end * fog_density;
|
||||||
|
if (fog_start > 1.0f) fog_start = 1.0f;
|
||||||
|
if (fog_end > 1.0f) fog_end = 1.0f;
|
||||||
|
l_start *= (1.0f - fog_start);
|
||||||
|
l_end *= (1.0f - fog_end);
|
||||||
|
}
|
||||||
|
|
||||||
if (l_start > 255.0f) l_start = 255.0f;
|
if (l_start > 255.0f) l_start = 255.0f;
|
||||||
if (l_end > 255.0f) l_end = 255.0f;
|
if (l_end > 255.0f) l_end = 255.0f;
|
||||||
|
|
||||||
|
|
@ -553,7 +734,8 @@ static void rasterize_triangle_opaque(
|
||||||
f32 z_a = z;
|
f32 z_a = z;
|
||||||
|
|
||||||
for (i32 px = x; px <= span_end; px++) {
|
for (i32 px = x; px <= span_end; px++) {
|
||||||
if (z_a <= zrow[px]) {
|
u16 z16 = depth_to_u16(z_a);
|
||||||
|
if (z16 < zrow[px]) {
|
||||||
i32 tx = (u_fixed >> 16) & tex_mask_w;
|
i32 tx = (u_fixed >> 16) & tex_mask_w;
|
||||||
i32 ty = (v_fixed >> 16) & tex_mask_h;
|
i32 ty = (v_fixed >> 16) & tex_mask_h;
|
||||||
u8 tex_idx = tex_pixels ? tex_pixels[tex_base + (u32)ty * (u32)tex_stride + (u32)tx] : 1;
|
u8 tex_idx = tex_pixels ? tex_pixels[tex_base + (u32)ty * (u32)tex_stride + (u32)tx] : 1;
|
||||||
|
|
@ -563,9 +745,12 @@ static void rasterize_triangle_opaque(
|
||||||
if (dither) {
|
if (dither) {
|
||||||
light = pxl8_dither_light(light, (u32)px, (u32)y);
|
light = pxl8_dither_light(light, (u32)px, (u32)y);
|
||||||
}
|
}
|
||||||
u8 pal_idx = cpu->colormap ? pxl8_colormap_lookup(cpu->colormap, tex_idx, light) : tex_idx;
|
u8 pal_idx = cpu->colormap ? pxl8_colormap_lookup(cpu->colormap, tex_idx, PXL8_LIGHT_WHITE, light) : tex_idx;
|
||||||
prow[px] = pal_idx;
|
prow[px] = pal_idx;
|
||||||
zrow[px] = z_a;
|
zrow[px] = z16;
|
||||||
|
if (light_accum) {
|
||||||
|
light_accum[row_start + (u32)px] = tri_light_color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -587,11 +772,9 @@ static void rasterize_triangle_opaque(
|
||||||
|
|
||||||
static void rasterize_triangle_passthrough(
|
static void rasterize_triangle_passthrough(
|
||||||
pxl8_cpu_backend* cpu,
|
pxl8_cpu_backend* cpu,
|
||||||
const tri_setup* setup,
|
const tri_setup* setup
|
||||||
const vertex_output* vo0
|
|
||||||
) {
|
) {
|
||||||
pxl8_cpu_render_target* render_target = cpu->current_target;
|
pxl8_cpu_render_target* render_target = cpu->current_target;
|
||||||
u8 color = vo0->color;
|
|
||||||
|
|
||||||
for (i32 y = setup->y_start; y <= setup->y_end; y++) {
|
for (i32 y = setup->y_start; y <= setup->y_end; y++) {
|
||||||
bool second_half = y > setup->y1 || setup->y1 == setup->y0;
|
bool second_half = y > setup->y1 || setup->y1 == setup->y0;
|
||||||
|
|
@ -603,20 +786,33 @@ static void rasterize_triangle_passthrough(
|
||||||
|
|
||||||
f32 ax = (f32)setup->x0 + (f32)(setup->x2 - setup->x0) * alpha;
|
f32 ax = (f32)setup->x0 + (f32)(setup->x2 - setup->x0) * alpha;
|
||||||
f32 az = setup->z0 + (setup->z2 - setup->z0) * alpha;
|
f32 az = setup->z0 + (setup->z2 - setup->z0) * alpha;
|
||||||
f32 bx, bz;
|
f32 a_wr = setup->w0_recip + (setup->w2_recip - setup->w0_recip) * alpha;
|
||||||
|
f32 a_cw = setup->c0_w + (setup->c2_w - setup->c0_w) * alpha;
|
||||||
|
|
||||||
|
f32 bx, bz, b_wr, b_cw;
|
||||||
if (second_half) {
|
if (second_half) {
|
||||||
bx = (f32)setup->x1 + (f32)(setup->x2 - setup->x1) * beta;
|
bx = (f32)setup->x1 + (f32)(setup->x2 - setup->x1) * beta;
|
||||||
bz = setup->z1 + (setup->z2 - setup->z1) * beta;
|
bz = setup->z1 + (setup->z2 - setup->z1) * beta;
|
||||||
|
b_wr = setup->w1_recip + (setup->w2_recip - setup->w1_recip) * beta;
|
||||||
|
b_cw = setup->c1_w + (setup->c2_w - setup->c1_w) * beta;
|
||||||
} else {
|
} else {
|
||||||
bx = (f32)setup->x0 + (f32)(setup->x1 - setup->x0) * beta;
|
bx = (f32)setup->x0 + (f32)(setup->x1 - setup->x0) * beta;
|
||||||
bz = setup->z0 + (setup->z1 - setup->z0) * beta;
|
bz = setup->z0 + (setup->z1 - setup->z0) * beta;
|
||||||
|
b_wr = setup->w0_recip + (setup->w1_recip - setup->w0_recip) * beta;
|
||||||
|
b_cw = setup->c0_w + (setup->c1_w - setup->c0_w) * beta;
|
||||||
}
|
}
|
||||||
|
|
||||||
f32 x_start_f, x_end_f, z_start, z_end;
|
f32 x_start_f, x_end_f, z_start, z_end, wr_start, wr_end, cw_start, cw_end;
|
||||||
if (ax <= bx) {
|
if (ax <= bx) {
|
||||||
x_start_f = ax; x_end_f = bx; z_start = az; z_end = bz;
|
x_start_f = ax; x_end_f = bx;
|
||||||
|
z_start = az; z_end = bz;
|
||||||
|
wr_start = a_wr; wr_end = b_wr;
|
||||||
|
cw_start = a_cw; cw_end = b_cw;
|
||||||
} else {
|
} else {
|
||||||
x_start_f = bx; x_end_f = ax; z_start = bz; z_end = az;
|
x_start_f = bx; x_end_f = ax;
|
||||||
|
z_start = bz; z_end = az;
|
||||||
|
wr_start = b_wr; wr_end = a_wr;
|
||||||
|
cw_start = b_cw; cw_end = a_cw;
|
||||||
}
|
}
|
||||||
|
|
||||||
i32 x_start_orig = (i32)(x_start_f + 0.5f);
|
i32 x_start_orig = (i32)(x_start_f + 0.5f);
|
||||||
|
|
@ -630,19 +826,30 @@ static void rasterize_triangle_passthrough(
|
||||||
f32 inv_width = 1.0f / (f32)width_orig;
|
f32 inv_width = 1.0f / (f32)width_orig;
|
||||||
|
|
||||||
f32 dz = (z_end - z_start) * inv_width;
|
f32 dz = (z_end - z_start) * inv_width;
|
||||||
|
f32 dwr = (wr_end - wr_start) * inv_width;
|
||||||
|
f32 dcw = (cw_end - cw_start) * inv_width;
|
||||||
|
|
||||||
f32 skip = (f32)(x_start - x_start_orig);
|
f32 skip = (f32)(x_start - x_start_orig);
|
||||||
f32 z = z_start + dz * skip;
|
f32 z = z_start + dz * skip;
|
||||||
|
f32 wr = wr_start + dwr * skip;
|
||||||
|
f32 cw = cw_start + dcw * skip;
|
||||||
|
|
||||||
u32 row_start = (u32)y * render_target->width;
|
u32 row_start = (u32)y * render_target->width;
|
||||||
u8* prow = render_target->framebuffer + row_start;
|
u8* prow = render_target->framebuffer + row_start;
|
||||||
f32* zrow = render_target->zbuffer + row_start;
|
u16* zrow = render_target->zbuffer + row_start;
|
||||||
|
|
||||||
for (i32 px = x_start; px <= x_end; px++) {
|
for (i32 px = x_start; px <= x_end; px++) {
|
||||||
if (z <= zrow[px]) {
|
u16 z16 = depth_to_u16(z);
|
||||||
|
if (z16 < zrow[px]) {
|
||||||
|
f32 w = 1.0f / wr;
|
||||||
|
f32 color_f = cw * w;
|
||||||
|
u8 color = pxl8_dither_float(color_f, (u32)px, (u32)y);
|
||||||
prow[px] = color;
|
prow[px] = color;
|
||||||
zrow[px] = z;
|
zrow[px] = z16;
|
||||||
}
|
}
|
||||||
z += dz;
|
z += dz;
|
||||||
|
wr += dwr;
|
||||||
|
cw += dcw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -653,6 +860,8 @@ static void rasterize_triangle_alpha(
|
||||||
const pxl8_atlas* textures, u32 texture_id, u8 mat_alpha, bool dither
|
const pxl8_atlas* textures, u32 texture_id, u8 mat_alpha, bool dither
|
||||||
) {
|
) {
|
||||||
pxl8_cpu_render_target* render_target = cpu->current_target;
|
pxl8_cpu_render_target* render_target = cpu->current_target;
|
||||||
|
u32* light_accum = render_target->light_accum;
|
||||||
|
u32 tri_light_color = light_accum ? blend_tri_light_color(setup->lc0, setup->lc1, setup->lc2) : 0;
|
||||||
const pxl8_atlas_entry* tex_entry = textures ? pxl8_atlas_get_entry(textures, texture_id) : NULL;
|
const pxl8_atlas_entry* tex_entry = textures ? pxl8_atlas_get_entry(textures, texture_id) : NULL;
|
||||||
u32 atlas_width = textures ? pxl8_atlas_get_width(textures) : 1;
|
u32 atlas_width = textures ? pxl8_atlas_get_width(textures) : 1;
|
||||||
f32 tex_w = tex_entry ? (f32)tex_entry->w : 1.0f;
|
f32 tex_w = tex_entry ? (f32)tex_entry->w : 1.0f;
|
||||||
|
|
@ -737,7 +946,7 @@ static void rasterize_triangle_alpha(
|
||||||
|
|
||||||
u32 row_start = (u32)y * render_target->width;
|
u32 row_start = (u32)y * render_target->width;
|
||||||
u8* prow = render_target->framebuffer + row_start;
|
u8* prow = render_target->framebuffer + row_start;
|
||||||
f32* zrow = render_target->zbuffer + row_start;
|
u16* zrow = render_target->zbuffer + row_start;
|
||||||
|
|
||||||
const i32 SUBDIV = 32;
|
const i32 SUBDIV = 32;
|
||||||
i32 x = x_start;
|
i32 x = x_start;
|
||||||
|
|
@ -757,6 +966,22 @@ static void rasterize_triangle_alpha(
|
||||||
f32 v_end = (vw + dvw * steps) * pw_end;
|
f32 v_end = (vw + dvw * steps) * pw_end;
|
||||||
f32 l_start = lw * pw_start;
|
f32 l_start = lw * pw_start;
|
||||||
f32 l_end = (lw + d_lw * steps) * pw_end;
|
f32 l_end = (lw + d_lw * steps) * pw_end;
|
||||||
|
|
||||||
|
f32 fog_density = cpu->frame.uniforms.fog_density;
|
||||||
|
if (fog_density > 0.0f) {
|
||||||
|
f32 z_end_fog = z + dz * steps;
|
||||||
|
f32 t_start = (z + 1.0f) * 0.5f;
|
||||||
|
f32 t_end = (z_end_fog + 1.0f) * 0.5f;
|
||||||
|
if (t_start < 0.0f) t_start = 0.0f;
|
||||||
|
if (t_end < 0.0f) t_end = 0.0f;
|
||||||
|
f32 fog_start = t_start * t_start * fog_density;
|
||||||
|
f32 fog_end = t_end * t_end * fog_density;
|
||||||
|
if (fog_start > 1.0f) fog_start = 1.0f;
|
||||||
|
if (fog_end > 1.0f) fog_end = 1.0f;
|
||||||
|
l_start *= (1.0f - fog_start);
|
||||||
|
l_end *= (1.0f - fog_end);
|
||||||
|
}
|
||||||
|
|
||||||
if (l_start > 255.0f) l_start = 255.0f;
|
if (l_start > 255.0f) l_start = 255.0f;
|
||||||
if (l_end > 255.0f) l_end = 255.0f;
|
if (l_end > 255.0f) l_end = 255.0f;
|
||||||
|
|
||||||
|
|
@ -783,11 +1008,15 @@ static void rasterize_triangle_alpha(
|
||||||
if (dither) {
|
if (dither) {
|
||||||
light = pxl8_dither_light(light, (u32)px, (u32)y);
|
light = pxl8_dither_light(light, (u32)px, (u32)y);
|
||||||
}
|
}
|
||||||
u8 src_idx = cpu->colormap ? pxl8_colormap_lookup(cpu->colormap, tex_idx, light) : tex_idx;
|
u8 src_idx = cpu->colormap ? pxl8_colormap_lookup(cpu->colormap, tex_idx, PXL8_LIGHT_WHITE, light) : tex_idx;
|
||||||
|
|
||||||
if (mat_alpha >= 128) {
|
if (mat_alpha >= 128) {
|
||||||
prow[px] = src_idx;
|
prow[px] = src_idx;
|
||||||
if (z_a <= zrow[px]) zrow[px] = z_a;
|
u16 z16 = depth_to_u16(z_a);
|
||||||
|
if (z16 < zrow[px]) zrow[px] = z16;
|
||||||
|
if (light_accum) {
|
||||||
|
light_accum[row_start + (u32)px] = tri_light_color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -807,11 +1036,43 @@ static void rasterize_triangle_alpha(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void rasterize_triangle_wireframe(
|
||||||
|
pxl8_cpu_backend* cpu,
|
||||||
|
const vertex_output* vo0, const vertex_output* vo1, const vertex_output* vo2,
|
||||||
|
u8 color, bool double_sided
|
||||||
|
) {
|
||||||
|
pxl8_cpu_render_target* render_target = cpu->current_target;
|
||||||
|
f32 hw = (f32)render_target->width * 0.5f;
|
||||||
|
f32 hh = (f32)render_target->height * 0.5f;
|
||||||
|
|
||||||
|
i32 x0 = (i32)(hw + vo0->clip_pos.x / vo0->clip_pos.w * hw);
|
||||||
|
i32 y0 = (i32)(hh - vo0->clip_pos.y / vo0->clip_pos.w * hh);
|
||||||
|
i32 x1 = (i32)(hw + vo1->clip_pos.x / vo1->clip_pos.w * hw);
|
||||||
|
i32 y1 = (i32)(hh - vo1->clip_pos.y / vo1->clip_pos.w * hh);
|
||||||
|
i32 x2 = (i32)(hw + vo2->clip_pos.x / vo2->clip_pos.w * hw);
|
||||||
|
i32 y2 = (i32)(hh - vo2->clip_pos.y / vo2->clip_pos.w * hh);
|
||||||
|
|
||||||
|
if (!double_sided) {
|
||||||
|
i32 cross = (x1 - x0) * (y2 - y0) - (y1 - y0) * (x2 - x0);
|
||||||
|
if (cross >= 0) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pxl8_cpu_draw_line_2d(cpu, x0, y0, x1, y1, color);
|
||||||
|
pxl8_cpu_draw_line_2d(cpu, x1, y1, x2, y2, color);
|
||||||
|
pxl8_cpu_draw_line_2d(cpu, x2, y2, x0, y0, color);
|
||||||
|
}
|
||||||
|
|
||||||
static void dispatch_triangle(
|
static void dispatch_triangle(
|
||||||
pxl8_cpu_backend* cpu,
|
pxl8_cpu_backend* cpu,
|
||||||
const vertex_output* vo0, const vertex_output* vo1, const vertex_output* vo2,
|
const vertex_output* vo0, const vertex_output* vo1, const vertex_output* vo2,
|
||||||
const pxl8_atlas* textures, const pxl8_material* material
|
const pxl8_atlas* textures, const pxl8_gfx_material* material
|
||||||
) {
|
) {
|
||||||
|
if (material->wireframe) {
|
||||||
|
u8 color = (material->texture_id > 0) ? (u8)material->texture_id : 15;
|
||||||
|
rasterize_triangle_wireframe(cpu, vo0, vo1, vo2, color, material->double_sided);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
pxl8_cpu_render_target* render_target = cpu->current_target;
|
pxl8_cpu_render_target* render_target = cpu->current_target;
|
||||||
tri_setup setup;
|
tri_setup setup;
|
||||||
if (!setup_triangle(&setup, vo0, vo1, vo2, render_target->width, render_target->height, material->double_sided)) {
|
if (!setup_triangle(&setup, vo0, vo1, vo2, render_target->width, render_target->height, material->double_sided)) {
|
||||||
|
|
@ -824,7 +1085,7 @@ static void dispatch_triangle(
|
||||||
if (alpha_blend) {
|
if (alpha_blend) {
|
||||||
rasterize_triangle_alpha(cpu, &setup, textures, material->texture_id, material->alpha, material->dither);
|
rasterize_triangle_alpha(cpu, &setup, textures, material->texture_id, material->alpha, material->dither);
|
||||||
} else if (passthrough) {
|
} else if (passthrough) {
|
||||||
rasterize_triangle_passthrough(cpu, &setup, vo0);
|
rasterize_triangle_passthrough(cpu, &setup);
|
||||||
} else {
|
} else {
|
||||||
rasterize_triangle_opaque(cpu, &setup, textures, material->texture_id, material->dither);
|
rasterize_triangle_opaque(cpu, &setup, textures, material->texture_id, material->dither);
|
||||||
}
|
}
|
||||||
|
|
@ -833,13 +1094,13 @@ static void dispatch_triangle(
|
||||||
void pxl8_cpu_draw_mesh(
|
void pxl8_cpu_draw_mesh(
|
||||||
pxl8_cpu_backend* cpu,
|
pxl8_cpu_backend* cpu,
|
||||||
const pxl8_mesh* mesh,
|
const pxl8_mesh* mesh,
|
||||||
pxl8_mat4 model,
|
const pxl8_mat4* model,
|
||||||
pxl8_material material,
|
const pxl8_gfx_material* material,
|
||||||
const pxl8_atlas* textures
|
const pxl8_atlas* textures
|
||||||
) {
|
) {
|
||||||
if (!cpu || !mesh || mesh->index_count < 3 || !cpu->current_target) return;
|
if (!cpu || !mesh || !model || !material || mesh->index_count < 3 || !cpu->current_target) return;
|
||||||
|
|
||||||
pxl8_mat4 mv = pxl8_mat4_mul(cpu->frame.view, model);
|
pxl8_mat4 mv = pxl8_mat4_mul(cpu->frame.view, *model);
|
||||||
pxl8_mat4 mvp = pxl8_mat4_mul(cpu->frame.projection, mv);
|
pxl8_mat4 mvp = pxl8_mat4_mul(cpu->frame.projection, mv);
|
||||||
|
|
||||||
f32 near = cpu->frame.near_clip > 0.0f ? cpu->frame.near_clip : 0.1f;
|
f32 near = cpu->frame.near_clip > 0.0f ? cpu->frame.near_clip : 0.1f;
|
||||||
|
|
@ -863,9 +1124,9 @@ void pxl8_cpu_draw_mesh(
|
||||||
vo1.clip_pos = pxl8_mat4_mul_vec4(mvp, p1);
|
vo1.clip_pos = pxl8_mat4_mul_vec4(mvp, p1);
|
||||||
vo2.clip_pos = pxl8_mat4_mul_vec4(mvp, p2);
|
vo2.clip_pos = pxl8_mat4_mul_vec4(mvp, p2);
|
||||||
|
|
||||||
pxl8_vec4 w0 = pxl8_mat4_mul_vec4(model, p0);
|
pxl8_vec4 w0 = pxl8_mat4_mul_vec4(*model, p0);
|
||||||
pxl8_vec4 w1 = pxl8_mat4_mul_vec4(model, p1);
|
pxl8_vec4 w1 = pxl8_mat4_mul_vec4(*model, p1);
|
||||||
pxl8_vec4 w2 = pxl8_mat4_mul_vec4(model, p2);
|
pxl8_vec4 w2 = pxl8_mat4_mul_vec4(*model, p2);
|
||||||
vo0.world_pos = (pxl8_vec3){w0.x, w0.y, w0.z};
|
vo0.world_pos = (pxl8_vec3){w0.x, w0.y, w0.z};
|
||||||
vo1.world_pos = (pxl8_vec3){w1.x, w1.y, w1.z};
|
vo1.world_pos = (pxl8_vec3){w1.x, w1.y, w1.z};
|
||||||
vo2.world_pos = (pxl8_vec3){w2.x, w2.y, w2.z};
|
vo2.world_pos = (pxl8_vec3){w2.x, w2.y, w2.z};
|
||||||
|
|
@ -882,15 +1143,36 @@ void pxl8_cpu_draw_mesh(
|
||||||
vo1.color = v1->color;
|
vo1.color = v1->color;
|
||||||
vo2.color = v2->color;
|
vo2.color = v2->color;
|
||||||
|
|
||||||
vo0.light = material.dynamic_lighting ? v0->light : 255;
|
if (material->dynamic_lighting) {
|
||||||
vo1.light = material.dynamic_lighting ? v1->light : 255;
|
pxl8_vec3 n0 = pxl8_vec3_normalize(pxl8_mat4_mul_vec3(*model, v0->normal));
|
||||||
vo2.light = material.dynamic_lighting ? v2->light : 255;
|
pxl8_vec3 n1 = pxl8_vec3_normalize(pxl8_mat4_mul_vec3(*model, v1->normal));
|
||||||
|
pxl8_vec3 n2 = pxl8_vec3_normalize(pxl8_mat4_mul_vec3(*model, v2->normal));
|
||||||
|
|
||||||
|
pxl8_light_result lr0 = calc_vertex_light(vo0.world_pos, n0, &cpu->frame);
|
||||||
|
pxl8_light_result lr1 = calc_vertex_light(vo1.world_pos, n1, &cpu->frame);
|
||||||
|
pxl8_light_result lr2 = calc_vertex_light(vo2.world_pos, n2, &cpu->frame);
|
||||||
|
|
||||||
|
vo0.light = lr0.light;
|
||||||
|
vo1.light = lr1.light;
|
||||||
|
vo2.light = lr2.light;
|
||||||
|
|
||||||
|
vo0.light_color = lr0.light_color;
|
||||||
|
vo1.light_color = lr1.light_color;
|
||||||
|
vo2.light_color = lr2.light_color;
|
||||||
|
} else {
|
||||||
|
vo0.light = 255;
|
||||||
|
vo1.light = 255;
|
||||||
|
vo2.light = 255;
|
||||||
|
vo0.light_color = 0;
|
||||||
|
vo1.light_color = 0;
|
||||||
|
vo2.light_color = 0;
|
||||||
|
}
|
||||||
|
|
||||||
vertex_output clipped[6];
|
vertex_output clipped[6];
|
||||||
i32 clipped_count = clip_triangle_near(&vo0, &vo1, &vo2, near, clipped);
|
i32 clipped_count = clip_triangle_near(&vo0, &vo1, &vo2, near, clipped);
|
||||||
|
|
||||||
for (i32 t = 0; t < clipped_count; t += 3) {
|
for (i32 t = 0; t < clipped_count; t += 3) {
|
||||||
dispatch_triangle(cpu, &clipped[t], &clipped[t+1], &clipped[t+2], textures, &material);
|
dispatch_triangle(cpu, &clipped[t], &clipped[t+1], &clipped[t+2], textures, material);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -964,7 +1246,7 @@ pxl8_cpu_render_target* pxl8_cpu_create_render_target(const pxl8_cpu_render_targ
|
||||||
}
|
}
|
||||||
|
|
||||||
if (desc->with_depth) {
|
if (desc->with_depth) {
|
||||||
target->zbuffer = calloc(size, sizeof(f32));
|
target->zbuffer = calloc(size, sizeof(u16));
|
||||||
if (!target->zbuffer) {
|
if (!target->zbuffer) {
|
||||||
free(target->framebuffer);
|
free(target->framebuffer);
|
||||||
free(target);
|
free(target);
|
||||||
|
|
@ -1021,10 +1303,12 @@ void pxl8_cpu_blit(pxl8_cpu_backend* cpu, pxl8_cpu_render_target* src, i32 x, i3
|
||||||
for (i32 row = 0; row < copy_h; row++) {
|
for (i32 row = 0; row < copy_h; row++) {
|
||||||
u8* src_row = src->framebuffer + (src_y0 + row) * src->width + src_x0;
|
u8* src_row = src->framebuffer + (src_y0 + row) * src->width + src_x0;
|
||||||
u8* dst_row = dst->framebuffer + (dst_y0 + row) * dst->width + dst_x0;
|
u8* dst_row = dst->framebuffer + (dst_y0 + row) * dst->width + dst_x0;
|
||||||
|
u32* light_row = dst->light_accum ? dst->light_accum + (dst_y0 + row) * dst->width + dst_x0 : NULL;
|
||||||
for (i32 col = 0; col < copy_w; col++) {
|
for (i32 col = 0; col < copy_w; col++) {
|
||||||
u8 pixel = src_row[col];
|
u8 pixel = src_row[col];
|
||||||
if (pixel != transparent_idx) {
|
if (pixel != transparent_idx) {
|
||||||
dst_row[col] = pixel;
|
dst_row[col] = pixel;
|
||||||
|
if (light_row) light_row[col] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1073,3 +1357,194 @@ u32 pxl8_cpu_render_target_get_height(const pxl8_cpu_render_target* target) {
|
||||||
u32 pxl8_cpu_render_target_get_width(const pxl8_cpu_render_target* target) {
|
u32 pxl8_cpu_render_target_get_width(const pxl8_cpu_render_target* target) {
|
||||||
return target ? target->width : 0;
|
return target ? target->width : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32* pxl8_cpu_render_target_get_light_accum(pxl8_cpu_render_target* target) {
|
||||||
|
return target ? target->light_accum : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32* pxl8_cpu_get_output(pxl8_cpu_backend* cpu) {
|
||||||
|
return cpu ? cpu->output : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_cpu_render_glows(
|
||||||
|
pxl8_cpu_backend* cpu,
|
||||||
|
const pxl8_glow_source* glows,
|
||||||
|
u32 glow_count
|
||||||
|
) {
|
||||||
|
if (!cpu || cpu->target_stack_depth == 0) return;
|
||||||
|
|
||||||
|
pxl8_cpu_render_target* target = cpu->target_stack[cpu->target_stack_depth - 1];
|
||||||
|
if (!target || !target->framebuffer) return;
|
||||||
|
|
||||||
|
u32 width = target->width;
|
||||||
|
u32 height = target->height;
|
||||||
|
u8* pixels = target->framebuffer;
|
||||||
|
u16* zbuffer = target->zbuffer;
|
||||||
|
u32* light_accum = target->light_accum;
|
||||||
|
|
||||||
|
for (u32 gi = 0; gi < glow_count; gi++) {
|
||||||
|
const pxl8_glow_source* glow = &glows[gi];
|
||||||
|
i32 cx = glow->x;
|
||||||
|
i32 cy = glow->y;
|
||||||
|
i32 radius = glow->radius;
|
||||||
|
|
||||||
|
if (radius <= 0 || glow->intensity == 0) continue;
|
||||||
|
|
||||||
|
i32 x0 = cx - radius;
|
||||||
|
i32 y0 = cy - radius;
|
||||||
|
i32 x1 = cx + radius;
|
||||||
|
i32 y1 = cy + radius;
|
||||||
|
|
||||||
|
if (x0 < 0) x0 = 0;
|
||||||
|
if (y0 < 0) y0 = 0;
|
||||||
|
if (x1 >= (i32)width) x1 = (i32)width - 1;
|
||||||
|
if (y1 >= (i32)height) y1 = (i32)height - 1;
|
||||||
|
|
||||||
|
if (x0 >= x1 || y0 >= y1) continue;
|
||||||
|
|
||||||
|
u16 glow_depth = glow->depth;
|
||||||
|
u8 base_color = glow->color;
|
||||||
|
f32 base_intensity = glow->intensity / 255.0f;
|
||||||
|
f32 radius_f = (f32)radius;
|
||||||
|
f32 radius_sq = radius_f * radius_f;
|
||||||
|
f32 inv_radius_sq = 1.0f / radius_sq;
|
||||||
|
f32 inv_radius = 1.0f / radius_f;
|
||||||
|
|
||||||
|
for (i32 y = y0; y <= y1; y++) {
|
||||||
|
u32 row = (u32)y * width;
|
||||||
|
f32 dy = (f32)(y - cy);
|
||||||
|
|
||||||
|
for (i32 x = x0; x <= x1; x++) {
|
||||||
|
u32 idx = row + (u32)x;
|
||||||
|
|
||||||
|
if (zbuffer && glow_depth > zbuffer[idx]) continue;
|
||||||
|
|
||||||
|
f32 dx = (f32)(x - cx);
|
||||||
|
f32 intensity = 0.0f;
|
||||||
|
|
||||||
|
switch (glow->shape) {
|
||||||
|
case PXL8_GLOW_CIRCLE: {
|
||||||
|
f32 dist_sq = dx * dx + dy * dy;
|
||||||
|
if (dist_sq >= radius_sq) continue;
|
||||||
|
f32 falloff = 1.0f - dist_sq * inv_radius_sq;
|
||||||
|
intensity = base_intensity * falloff * falloff;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PXL8_GLOW_DIAMOND: {
|
||||||
|
f32 abs_dx = dx < 0 ? -dx : dx;
|
||||||
|
f32 abs_dy = dy < 0 ? -dy : dy;
|
||||||
|
f32 manhattan = abs_dx + abs_dy;
|
||||||
|
if (manhattan >= radius_f) continue;
|
||||||
|
f32 falloff = 1.0f - manhattan * inv_radius;
|
||||||
|
intensity = base_intensity * falloff * falloff;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PXL8_GLOW_SHAFT: {
|
||||||
|
f32 abs_dx = dx < 0 ? -dx : dx;
|
||||||
|
f32 abs_dy = dy < 0 ? -dy : dy;
|
||||||
|
f32 height_f = glow->height > 0 ? (f32)glow->height : radius_f;
|
||||||
|
if (abs_dx >= radius_f || abs_dy >= height_f) continue;
|
||||||
|
f32 falloff_x = 1.0f - abs_dx * inv_radius;
|
||||||
|
f32 falloff_y = 1.0f - abs_dy / height_f;
|
||||||
|
intensity = base_intensity * falloff_x * falloff_x * falloff_y;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (intensity < 0.02f) continue;
|
||||||
|
|
||||||
|
u8 int_val = intensity < 1.0f ? (u8)(intensity * 255.0f) : 255;
|
||||||
|
u8 light_level = pxl8_ordered_dither(int_val, (u32)x, (u32)y);
|
||||||
|
|
||||||
|
if (light_level < 8) continue;
|
||||||
|
|
||||||
|
u8 shaded = pxl8_colormap_lookup(cpu->colormap, base_color, PXL8_LIGHT_WHITE, light_level);
|
||||||
|
|
||||||
|
if (cpu->palette_cube) {
|
||||||
|
u8 sr, sg, sb, dr, dg, db;
|
||||||
|
pxl8_palette_cube_get_rgb(cpu->palette_cube, shaded, &sr, &sg, &sb);
|
||||||
|
pxl8_palette_cube_get_rgb(cpu->palette_cube, pixels[idx], &dr, &dg, &db);
|
||||||
|
u32 r = (u32)sr + (u32)dr;
|
||||||
|
u32 g = (u32)sg + (u32)dg;
|
||||||
|
u32 b = (u32)sb + (u32)db;
|
||||||
|
if (r > 255) r = 255;
|
||||||
|
if (g > 255) g = 255;
|
||||||
|
if (b > 255) b = 255;
|
||||||
|
pixels[idx] = pxl8_palette_cube_lookup(cpu->palette_cube, (u8)r, (u8)g, (u8)b);
|
||||||
|
} else {
|
||||||
|
pixels[idx] = shaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (light_accum) {
|
||||||
|
light_accum[idx] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_cpu_resolve(pxl8_cpu_backend* cpu) {
|
||||||
|
if (!cpu || !cpu->palette || cpu->target_stack_depth == 0) return;
|
||||||
|
|
||||||
|
pxl8_cpu_render_target* target = cpu->target_stack[0];
|
||||||
|
if (!target || !target->framebuffer) return;
|
||||||
|
|
||||||
|
u32 pixel_count = target->width * target->height;
|
||||||
|
const u8* pixels = target->framebuffer;
|
||||||
|
const u32* light_accum = target->light_accum;
|
||||||
|
const u32* palette = cpu->palette;
|
||||||
|
u32* output = cpu->output;
|
||||||
|
|
||||||
|
for (u32 i = 0; i < pixel_count; i++) {
|
||||||
|
u32 base = palette[pixels[i]];
|
||||||
|
|
||||||
|
if (light_accum && light_accum[i] != 0) {
|
||||||
|
u32 light_color = light_accum[i];
|
||||||
|
u32 tint_alpha = (light_color >> 24) & 0xFF;
|
||||||
|
|
||||||
|
if (tint_alpha > 0) {
|
||||||
|
u32 lr = light_color & 0xFF;
|
||||||
|
u32 lg = (light_color >> 8) & 0xFF;
|
||||||
|
u32 lb = (light_color >> 16) & 0xFF;
|
||||||
|
|
||||||
|
u32 br = base & 0xFF;
|
||||||
|
u32 bg = (base >> 8) & 0xFF;
|
||||||
|
u32 bb = (base >> 16) & 0xFF;
|
||||||
|
u32 ba = (base >> 24) & 0xFF;
|
||||||
|
|
||||||
|
u32 t = (tint_alpha * tint_alpha) / 255;
|
||||||
|
u32 inv_t = 255 - t;
|
||||||
|
|
||||||
|
u32 lr_norm = (lr * 255) / 240;
|
||||||
|
u32 lg_norm = (lg * 255) / 240;
|
||||||
|
u32 lb_norm = (lb * 255) / 220;
|
||||||
|
|
||||||
|
u32 or = ((br * inv_t + (br * lr_norm / 255) * t) / 255);
|
||||||
|
u32 og = ((bg * inv_t + (bg * lg_norm / 255) * t) / 255);
|
||||||
|
u32 ob = ((bb * inv_t + (bb * lb_norm / 255) * t) / 255);
|
||||||
|
|
||||||
|
if (or > 255) or = 255;
|
||||||
|
if (og > 255) og = 255;
|
||||||
|
if (ob > 255) ob = 255;
|
||||||
|
|
||||||
|
output[i] = or | (og << 8) | (ob << 16) | (ba << 24);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output[i] = base;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (u32 t = 1; t < cpu->target_stack_depth; t++) {
|
||||||
|
pxl8_cpu_render_target* overlay = cpu->target_stack[t];
|
||||||
|
if (!overlay || !overlay->framebuffer) continue;
|
||||||
|
|
||||||
|
const u8* overlay_pixels = overlay->framebuffer;
|
||||||
|
for (u32 i = 0; i < pixel_count; i++) {
|
||||||
|
u8 idx = overlay_pixels[i];
|
||||||
|
if (idx != 0) {
|
||||||
|
output[i] = palette[idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,8 +2,12 @@
|
||||||
|
|
||||||
#include "pxl8_atlas.h"
|
#include "pxl8_atlas.h"
|
||||||
#include "pxl8_colormap.h"
|
#include "pxl8_colormap.h"
|
||||||
|
#include "pxl8_gfx.h"
|
||||||
|
#include "pxl8_gfx3d.h"
|
||||||
|
#include "pxl8_lightmap.h"
|
||||||
#include "pxl8_math.h"
|
#include "pxl8_math.h"
|
||||||
#include "pxl8_mesh.h"
|
#include "pxl8_mesh.h"
|
||||||
|
#include "pxl8_palette.h"
|
||||||
#include "pxl8_types.h"
|
#include "pxl8_types.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
@ -20,19 +24,6 @@ typedef struct pxl8_cpu_render_target_desc {
|
||||||
bool with_lighting;
|
bool with_lighting;
|
||||||
} pxl8_cpu_render_target_desc;
|
} pxl8_cpu_render_target_desc;
|
||||||
|
|
||||||
typedef struct pxl8_3d_frame {
|
|
||||||
u8 ambient_light;
|
|
||||||
pxl8_vec3 camera_dir;
|
|
||||||
pxl8_vec3 camera_pos;
|
|
||||||
f32 far_clip;
|
|
||||||
u8 fog_color;
|
|
||||||
f32 fog_density;
|
|
||||||
f32 near_clip;
|
|
||||||
pxl8_mat4 projection;
|
|
||||||
f32 time;
|
|
||||||
pxl8_mat4 view;
|
|
||||||
} pxl8_3d_frame;
|
|
||||||
|
|
||||||
pxl8_cpu_backend* pxl8_cpu_create(u32 width, u32 height);
|
pxl8_cpu_backend* pxl8_cpu_create(u32 width, u32 height);
|
||||||
void pxl8_cpu_destroy(pxl8_cpu_backend* cpu);
|
void pxl8_cpu_destroy(pxl8_cpu_backend* cpu);
|
||||||
|
|
||||||
|
|
@ -44,6 +35,10 @@ void pxl8_cpu_clear_depth(pxl8_cpu_backend* cpu);
|
||||||
|
|
||||||
void pxl8_cpu_set_colormap(pxl8_cpu_backend* cpu, const pxl8_colormap* cm);
|
void pxl8_cpu_set_colormap(pxl8_cpu_backend* cpu, const pxl8_colormap* cm);
|
||||||
void pxl8_cpu_set_palette(pxl8_cpu_backend* cpu, const u32* palette);
|
void pxl8_cpu_set_palette(pxl8_cpu_backend* cpu, const u32* palette);
|
||||||
|
void pxl8_cpu_set_palette_cube(pxl8_cpu_backend* cpu, const pxl8_palette_cube* cube);
|
||||||
|
|
||||||
|
u32 pxl8_cpu_add_lightmap(pxl8_cpu_backend* cpu, pxl8_lightmap* lm);
|
||||||
|
pxl8_lightmap* pxl8_cpu_get_lightmap(pxl8_cpu_backend* cpu, u32 id);
|
||||||
|
|
||||||
void pxl8_cpu_draw_pixel(pxl8_cpu_backend* cpu, i32 x, i32 y, u8 color);
|
void pxl8_cpu_draw_pixel(pxl8_cpu_backend* cpu, i32 x, i32 y, u8 color);
|
||||||
u8 pxl8_cpu_get_pixel(pxl8_cpu_backend* cpu, i32 x, i32 y);
|
u8 pxl8_cpu_get_pixel(pxl8_cpu_backend* cpu, i32 x, i32 y);
|
||||||
|
|
@ -57,8 +52,8 @@ void pxl8_cpu_draw_line_3d(pxl8_cpu_backend* cpu, pxl8_vec3 v0, pxl8_vec3 v1, u8
|
||||||
void pxl8_cpu_draw_mesh(
|
void pxl8_cpu_draw_mesh(
|
||||||
pxl8_cpu_backend* cpu,
|
pxl8_cpu_backend* cpu,
|
||||||
const pxl8_mesh* mesh,
|
const pxl8_mesh* mesh,
|
||||||
pxl8_mat4 model,
|
const pxl8_mat4* model,
|
||||||
pxl8_material material,
|
const pxl8_gfx_material* material,
|
||||||
const pxl8_atlas* textures
|
const pxl8_atlas* textures
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -70,9 +65,18 @@ void pxl8_cpu_draw_mesh_wireframe(
|
||||||
);
|
);
|
||||||
|
|
||||||
u8* pxl8_cpu_get_framebuffer(pxl8_cpu_backend* cpu);
|
u8* pxl8_cpu_get_framebuffer(pxl8_cpu_backend* cpu);
|
||||||
|
u32* pxl8_cpu_get_output(pxl8_cpu_backend* cpu);
|
||||||
u32 pxl8_cpu_get_height(const pxl8_cpu_backend* cpu);
|
u32 pxl8_cpu_get_height(const pxl8_cpu_backend* cpu);
|
||||||
u32 pxl8_cpu_get_width(const pxl8_cpu_backend* cpu);
|
u32 pxl8_cpu_get_width(const pxl8_cpu_backend* cpu);
|
||||||
|
|
||||||
|
void pxl8_cpu_render_glows(
|
||||||
|
pxl8_cpu_backend* cpu,
|
||||||
|
const pxl8_glow_source* glows,
|
||||||
|
u32 glow_count
|
||||||
|
);
|
||||||
|
|
||||||
|
void pxl8_cpu_resolve(pxl8_cpu_backend* cpu);
|
||||||
|
|
||||||
pxl8_cpu_render_target* pxl8_cpu_create_render_target(const pxl8_cpu_render_target_desc* desc);
|
pxl8_cpu_render_target* pxl8_cpu_create_render_target(const pxl8_cpu_render_target_desc* desc);
|
||||||
void pxl8_cpu_destroy_render_target(pxl8_cpu_render_target* target);
|
void pxl8_cpu_destroy_render_target(pxl8_cpu_render_target* target);
|
||||||
|
|
||||||
|
|
@ -88,6 +92,7 @@ u32 pxl8_cpu_get_target_depth(const pxl8_cpu_backend* cpu);
|
||||||
u8* pxl8_cpu_render_target_get_framebuffer(pxl8_cpu_render_target* target);
|
u8* pxl8_cpu_render_target_get_framebuffer(pxl8_cpu_render_target* target);
|
||||||
u32 pxl8_cpu_render_target_get_height(const pxl8_cpu_render_target* target);
|
u32 pxl8_cpu_render_target_get_height(const pxl8_cpu_render_target* target);
|
||||||
u32 pxl8_cpu_render_target_get_width(const pxl8_cpu_render_target* target);
|
u32 pxl8_cpu_render_target_get_width(const pxl8_cpu_render_target* target);
|
||||||
|
u32* pxl8_cpu_render_target_get_light_accum(pxl8_cpu_render_target* target);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
@ -34,6 +34,7 @@ struct pxl8_gfx {
|
||||||
const pxl8_hal* hal;
|
const pxl8_hal* hal;
|
||||||
bool initialized;
|
bool initialized;
|
||||||
pxl8_palette* palette;
|
pxl8_palette* palette;
|
||||||
|
pxl8_palette_cube* palette_cube;
|
||||||
pxl8_pixel_mode pixel_mode;
|
pxl8_pixel_mode pixel_mode;
|
||||||
void* platform_data;
|
void* platform_data;
|
||||||
pxl8_sprite_cache_entry* sprite_cache;
|
pxl8_sprite_cache_entry* sprite_cache;
|
||||||
|
|
@ -74,10 +75,14 @@ i32 pxl8_gfx_get_width(const pxl8_gfx* gfx) {
|
||||||
return gfx ? gfx->framebuffer_width : 0;
|
return gfx ? gfx->framebuffer_width : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_palette* pxl8_gfx_get_palette(pxl8_gfx* gfx) {
|
pxl8_palette* pxl8_gfx_palette(pxl8_gfx* gfx) {
|
||||||
return gfx ? gfx->palette : NULL;
|
return gfx ? gfx->palette : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pxl8_colormap* pxl8_gfx_colormap(pxl8_gfx* gfx) {
|
||||||
|
return gfx ? gfx->colormap : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
u8 pxl8_gfx_find_color(pxl8_gfx* gfx, u32 color) {
|
u8 pxl8_gfx_find_color(pxl8_gfx* gfx, u32 color) {
|
||||||
if (!gfx || !gfx->palette) return 0;
|
if (!gfx || !gfx->palette) return 0;
|
||||||
if (color <= 0xFFFFFF) color = (color << 8) | 0xFF;
|
if (color <= 0xFFFFFF) color = (color << 8) | 0xFF;
|
||||||
|
|
@ -87,37 +92,24 @@ u8 pxl8_gfx_find_color(pxl8_gfx* gfx, u32 color) {
|
||||||
return pxl8_palette_find_closest(gfx->palette, r, g, b);
|
return pxl8_palette_find_closest(gfx->palette, r, g, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_gfx_set_palette(pxl8_gfx* gfx, pxl8_palette* pal) {
|
void pxl8_gfx_set_palette_colors(pxl8_gfx* gfx, const u32* colors, u16 count) {
|
||||||
if (!gfx) return;
|
if (!gfx || !gfx->palette || !colors) return;
|
||||||
if (gfx->palette) {
|
pxl8_set_palette(gfx->palette, colors, count);
|
||||||
pxl8_palette_destroy(gfx->palette);
|
if (gfx->pixel_mode != PXL8_PIXEL_HICOLOR && gfx->backend.type == PXL8_GFX_BACKEND_CPU) {
|
||||||
}
|
pxl8_cpu_set_palette(gfx->backend.cpu, pxl8_palette_colors(gfx->palette));
|
||||||
gfx->palette = pal;
|
|
||||||
|
|
||||||
if (gfx->palette && gfx->pixel_mode != PXL8_PIXEL_HICOLOR) {
|
|
||||||
u32* colors = pxl8_palette_colors(gfx->palette);
|
|
||||||
if (gfx->colormap) {
|
|
||||||
pxl8_colormap_generate(gfx->colormap, colors, NULL);
|
|
||||||
}
|
|
||||||
if (gfx->backend.type == PXL8_GFX_BACKEND_CPU) {
|
|
||||||
pxl8_cpu_set_palette(gfx->backend.cpu, colors);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
i32 pxl8_gfx_load_palette(pxl8_gfx* gfx, const char* filepath) {
|
i32 pxl8_gfx_load_palette(pxl8_gfx* gfx, const char* filepath) {
|
||||||
if (!gfx || !filepath) return -1;
|
if (!gfx || !gfx->palette || !filepath) return -1;
|
||||||
pxl8_palette* pal = gfx->palette;
|
pxl8_result result = pxl8_palette_load_ase(gfx->palette, filepath);
|
||||||
if (!pal) return -1;
|
|
||||||
pxl8_result result = pxl8_palette_load_ase(pal, filepath);
|
|
||||||
if (result != PXL8_OK) return (i32)result;
|
if (result != PXL8_OK) return (i32)result;
|
||||||
if (gfx->pixel_mode != PXL8_PIXEL_HICOLOR) {
|
if (gfx->pixel_mode != PXL8_PIXEL_HICOLOR) {
|
||||||
u32* colors = pxl8_palette_colors(pal);
|
|
||||||
if (gfx->colormap) {
|
if (gfx->colormap) {
|
||||||
pxl8_colormap_generate(gfx->colormap, colors, NULL);
|
pxl8_colormap_generate(gfx->colormap, pxl8_palette_colors(gfx->palette));
|
||||||
}
|
}
|
||||||
if (gfx->backend.type == PXL8_GFX_BACKEND_CPU) {
|
if (gfx->backend.type == PXL8_GFX_BACKEND_CPU) {
|
||||||
pxl8_cpu_set_palette(gfx->backend.cpu, colors);
|
pxl8_cpu_set_palette(gfx->backend.cpu, pxl8_palette_colors(gfx->palette));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -188,6 +180,7 @@ void pxl8_gfx_destroy(pxl8_gfx* gfx) {
|
||||||
pxl8_cpu_destroy(gfx->backend.cpu);
|
pxl8_cpu_destroy(gfx->backend.cpu);
|
||||||
}
|
}
|
||||||
free(gfx->colormap);
|
free(gfx->colormap);
|
||||||
|
pxl8_palette_cube_destroy(gfx->palette_cube);
|
||||||
pxl8_palette_destroy(gfx->palette);
|
pxl8_palette_destroy(gfx->palette);
|
||||||
free(gfx->sprite_cache);
|
free(gfx->sprite_cache);
|
||||||
|
|
||||||
|
|
@ -309,15 +302,30 @@ pxl8_result pxl8_gfx_load_font_atlas(pxl8_gfx* gfx) {
|
||||||
void pxl8_gfx_upload_framebuffer(pxl8_gfx* gfx) {
|
void pxl8_gfx_upload_framebuffer(pxl8_gfx* gfx) {
|
||||||
if (!gfx || !gfx->initialized || !gfx->hal) return;
|
if (!gfx || !gfx->initialized || !gfx->hal) return;
|
||||||
|
|
||||||
u32 bpp = (gfx->pixel_mode == PXL8_PIXEL_HICOLOR) ? 2 : 1;
|
if (gfx->backend.type == PXL8_GFX_BACKEND_CPU && gfx->pixel_mode == PXL8_PIXEL_INDEXED) {
|
||||||
|
pxl8_cpu_resolve(gfx->backend.cpu);
|
||||||
|
u32* output = pxl8_cpu_get_output(gfx->backend.cpu);
|
||||||
|
if (output) {
|
||||||
|
gfx->hal->upload_texture(
|
||||||
|
gfx->platform_data,
|
||||||
|
output,
|
||||||
|
gfx->framebuffer_width,
|
||||||
|
gfx->framebuffer_height,
|
||||||
|
PXL8_PIXEL_RGBA,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
u32* colors = gfx->palette ? pxl8_palette_colors(gfx->palette) : NULL;
|
u32* colors = gfx->palette ? pxl8_palette_colors(gfx->palette) : NULL;
|
||||||
gfx->hal->upload_texture(
|
gfx->hal->upload_texture(
|
||||||
gfx->platform_data,
|
gfx->platform_data,
|
||||||
gfx->framebuffer,
|
gfx->framebuffer,
|
||||||
gfx->framebuffer_width,
|
gfx->framebuffer_width,
|
||||||
gfx->framebuffer_height,
|
gfx->framebuffer_height,
|
||||||
colors,
|
gfx->pixel_mode,
|
||||||
bpp
|
colors
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -438,6 +446,7 @@ void pxl8_2d_text(pxl8_gfx* gfx, const char* text, i32 x, i32 y, u32 color) {
|
||||||
if (!gfx || !text) return;
|
if (!gfx || !text) return;
|
||||||
|
|
||||||
u8* framebuffer = NULL;
|
u8* framebuffer = NULL;
|
||||||
|
u32* light_accum = NULL;
|
||||||
i32 fb_width = 0;
|
i32 fb_width = 0;
|
||||||
i32 fb_height = 0;
|
i32 fb_height = 0;
|
||||||
|
|
||||||
|
|
@ -446,6 +455,7 @@ void pxl8_2d_text(pxl8_gfx* gfx, const char* text, i32 x, i32 y, u32 color) {
|
||||||
pxl8_cpu_render_target* render_target = pxl8_cpu_get_target(gfx->backend.cpu);
|
pxl8_cpu_render_target* render_target = pxl8_cpu_get_target(gfx->backend.cpu);
|
||||||
if (!render_target) return;
|
if (!render_target) return;
|
||||||
framebuffer = pxl8_cpu_render_target_get_framebuffer(render_target);
|
framebuffer = pxl8_cpu_render_target_get_framebuffer(render_target);
|
||||||
|
light_accum = pxl8_cpu_render_target_get_light_accum(render_target);
|
||||||
fb_width = (i32)pxl8_cpu_render_target_get_width(render_target);
|
fb_width = (i32)pxl8_cpu_render_target_get_width(render_target);
|
||||||
fb_height = (i32)pxl8_cpu_render_target_get_height(render_target);
|
fb_height = (i32)pxl8_cpu_render_target_get_height(render_target);
|
||||||
break;
|
break;
|
||||||
|
|
@ -483,7 +493,9 @@ void pxl8_2d_text(pxl8_gfx* gfx, const char* text, i32 x, i32 y, u32 color) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pixel_bit) {
|
if (pixel_bit) {
|
||||||
framebuffer[py * fb_width + px] = (u8)color;
|
i32 idx = py * fb_width + px;
|
||||||
|
framebuffer[idx] = (u8)color;
|
||||||
|
if (light_accum) light_accum[idx] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -497,6 +509,7 @@ void pxl8_2d_sprite(pxl8_gfx* gfx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h, bo
|
||||||
if (!gfx || !gfx->atlas) return;
|
if (!gfx || !gfx->atlas) return;
|
||||||
|
|
||||||
u8* framebuffer = NULL;
|
u8* framebuffer = NULL;
|
||||||
|
u32* light_accum = NULL;
|
||||||
i32 fb_width = 0;
|
i32 fb_width = 0;
|
||||||
i32 fb_height = 0;
|
i32 fb_height = 0;
|
||||||
|
|
||||||
|
|
@ -505,6 +518,7 @@ void pxl8_2d_sprite(pxl8_gfx* gfx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h, bo
|
||||||
pxl8_cpu_render_target* render_target = pxl8_cpu_get_target(gfx->backend.cpu);
|
pxl8_cpu_render_target* render_target = pxl8_cpu_get_target(gfx->backend.cpu);
|
||||||
if (!render_target) return;
|
if (!render_target) return;
|
||||||
framebuffer = pxl8_cpu_render_target_get_framebuffer(render_target);
|
framebuffer = pxl8_cpu_render_target_get_framebuffer(render_target);
|
||||||
|
light_accum = pxl8_cpu_render_target_get_light_accum(render_target);
|
||||||
fb_width = (i32)pxl8_cpu_render_target_get_width(render_target);
|
fb_width = (i32)pxl8_cpu_render_target_get_width(render_target);
|
||||||
fb_height = (i32)pxl8_cpu_render_target_get_height(render_target);
|
fb_height = (i32)pxl8_cpu_render_target_get_height(render_target);
|
||||||
break;
|
break;
|
||||||
|
|
@ -541,6 +555,11 @@ void pxl8_2d_sprite(pxl8_gfx* gfx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h, bo
|
||||||
if (is_1to1_scale && is_unclipped && !is_flipped) {
|
if (is_1to1_scale && is_unclipped && !is_flipped) {
|
||||||
const u8* sprite_data = atlas_pixels + entry->y * atlas_width + entry->x;
|
const u8* sprite_data = atlas_pixels + entry->y * atlas_width + entry->x;
|
||||||
pxl8_blit_indexed(framebuffer, fb_width, sprite_data, atlas_width, x, y, w, h);
|
pxl8_blit_indexed(framebuffer, fb_width, sprite_data, atlas_width, x, y, w, h);
|
||||||
|
if (light_accum) {
|
||||||
|
for (i32 py = 0; py < h; py++) {
|
||||||
|
memset(light_accum + (y + py) * fb_width + x, 0, w * sizeof(u32));
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
for (i32 py = 0; py < draw_height; py++) {
|
for (i32 py = 0; py < draw_height; py++) {
|
||||||
for (i32 px = 0; px < draw_width; px++) {
|
for (i32 px = 0; px < draw_width; px++) {
|
||||||
|
|
@ -554,6 +573,7 @@ void pxl8_2d_sprite(pxl8_gfx* gfx, u32 sprite_id, i32 x, i32 y, i32 w, i32 h, bo
|
||||||
i32 dest_idx = (dest_y + py) * fb_width + (dest_x + px);
|
i32 dest_idx = (dest_y + py) * fb_width + (dest_x + px);
|
||||||
|
|
||||||
framebuffer[dest_idx] = pxl8_blend_indexed(atlas_pixels[src_idx], framebuffer[dest_idx]);
|
framebuffer[dest_idx] = pxl8_blend_indexed(atlas_pixels[src_idx], framebuffer[dest_idx]);
|
||||||
|
if (light_accum) light_accum[dest_idx] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -568,30 +588,32 @@ void pxl8_gfx_update(pxl8_gfx* gfx, f32 dt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pxl8_3d_frame pxl8_3d_frame_from_camera(const pxl8_3d_camera* camera, const pxl8_3d_uniforms* uniforms) {
|
||||||
|
pxl8_3d_frame frame = {0};
|
||||||
|
if (!camera) return frame;
|
||||||
|
|
||||||
|
frame.view = pxl8_3d_camera_get_view(camera);
|
||||||
|
frame.projection = pxl8_3d_camera_get_projection(camera);
|
||||||
|
frame.camera_pos = pxl8_3d_camera_get_position(camera);
|
||||||
|
frame.camera_dir = pxl8_3d_camera_get_forward(camera);
|
||||||
|
frame.near_clip = 1.0f;
|
||||||
|
frame.far_clip = 4096.0f;
|
||||||
|
|
||||||
|
if (uniforms) {
|
||||||
|
frame.uniforms = *uniforms;
|
||||||
|
}
|
||||||
|
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
void pxl8_3d_begin_frame(pxl8_gfx* gfx, const pxl8_3d_camera* camera, const pxl8_3d_uniforms* uniforms) {
|
void pxl8_3d_begin_frame(pxl8_gfx* gfx, const pxl8_3d_camera* camera, const pxl8_3d_uniforms* uniforms) {
|
||||||
if (!gfx || !camera) return;
|
if (!gfx || !camera) return;
|
||||||
|
|
||||||
pxl8_mat4 view = pxl8_3d_camera_get_view(camera);
|
pxl8_3d_frame frame = pxl8_3d_frame_from_camera(camera, uniforms);
|
||||||
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);
|
pxl8_mat4 vp = pxl8_mat4_mul(frame.projection, frame.view);
|
||||||
gfx->frustum = pxl8_frustum_from_matrix(vp);
|
gfx->frustum = pxl8_frustum_from_matrix(vp);
|
||||||
|
|
||||||
pxl8_3d_frame frame = {
|
|
||||||
.ambient_light = uniforms ? uniforms->ambient : 0,
|
|
||||||
.camera_dir = forward,
|
|
||||||
.camera_pos = position,
|
|
||||||
.far_clip = 4096.0f,
|
|
||||||
.fog_color = uniforms ? uniforms->fog_color : 0,
|
|
||||||
.fog_density = uniforms ? uniforms->fog_density : 0.0f,
|
|
||||||
.near_clip = 1.0f,
|
|
||||||
.projection = projection,
|
|
||||||
.time = uniforms ? uniforms->time : 0.0f,
|
|
||||||
.view = view,
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (gfx->backend.type) {
|
switch (gfx->backend.type) {
|
||||||
case PXL8_GFX_BACKEND_CPU:
|
case PXL8_GFX_BACKEND_CPU:
|
||||||
pxl8_cpu_begin_frame(gfx->backend.cpu, &frame);
|
pxl8_cpu_begin_frame(gfx->backend.cpu, &frame);
|
||||||
|
|
@ -639,8 +661,8 @@ void pxl8_3d_draw_line(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, u8 color) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_3d_draw_mesh(pxl8_gfx* gfx, const pxl8_mesh* mesh, pxl8_mat4 model, pxl8_material material) {
|
void pxl8_3d_draw_mesh(pxl8_gfx* gfx, const pxl8_mesh* mesh, const pxl8_mat4* model, const pxl8_gfx_material* material) {
|
||||||
if (!gfx || !mesh) return;
|
if (!gfx || !mesh || !model || !material) return;
|
||||||
switch (gfx->backend.type) {
|
switch (gfx->backend.type) {
|
||||||
case PXL8_GFX_BACKEND_CPU:
|
case PXL8_GFX_BACKEND_CPU:
|
||||||
pxl8_cpu_draw_mesh(gfx->backend.cpu, mesh, model, material, gfx->atlas);
|
pxl8_cpu_draw_mesh(gfx->backend.cpu, mesh, model, material, gfx->atlas);
|
||||||
|
|
@ -704,3 +726,67 @@ void pxl8_gfx_pop_target(pxl8_gfx* gfx) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pxl8_gfx_ensure_blend_tables(pxl8_gfx* gfx) {
|
||||||
|
if (!gfx || !gfx->palette) return;
|
||||||
|
|
||||||
|
if (!gfx->palette_cube) {
|
||||||
|
gfx->palette_cube = pxl8_palette_cube_create(gfx->palette);
|
||||||
|
if (gfx->backend.cpu) {
|
||||||
|
pxl8_cpu_set_palette_cube(gfx->backend.cpu, gfx->palette_cube);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_gfx_blend_tables_update(pxl8_gfx* gfx) {
|
||||||
|
if (!gfx || !gfx->palette) return;
|
||||||
|
|
||||||
|
pxl8_gfx_ensure_blend_tables(gfx);
|
||||||
|
|
||||||
|
if (gfx->palette_cube) {
|
||||||
|
pxl8_palette_cube_rebuild(gfx->palette_cube, gfx->palette);
|
||||||
|
if (gfx->backend.cpu) {
|
||||||
|
pxl8_cpu_set_palette_cube(gfx->backend.cpu, gfx->palette_cube);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_gfx_colormap_update(pxl8_gfx* gfx) {
|
||||||
|
if (!gfx || !gfx->palette || !gfx->colormap) return;
|
||||||
|
u32* colors = pxl8_palette_colors(gfx->palette);
|
||||||
|
if (colors) {
|
||||||
|
pxl8_colormap_generate(gfx->colormap, colors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 pxl8_gfx_find_closest_color(pxl8_gfx* gfx, u8 r, u8 g, u8 b) {
|
||||||
|
if (!gfx || !gfx->palette) return 0;
|
||||||
|
return pxl8_palette_find_closest(gfx->palette, r, g, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 pxl8_gfx_ui_color(pxl8_gfx* gfx, u8 index) {
|
||||||
|
if (!gfx || !gfx->palette || index >= PXL8_UI_PALETTE_SIZE) return 0;
|
||||||
|
u32 abgr = pxl8_ui_palette[index];
|
||||||
|
u8 r = (abgr >> 0) & 0xFF;
|
||||||
|
u8 g = (abgr >> 8) & 0xFF;
|
||||||
|
u8 b = (abgr >> 16) & 0xFF;
|
||||||
|
return pxl8_palette_find_closest(gfx->palette, r, g, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_gfx_apply_effect(pxl8_gfx* gfx, pxl8_gfx_effect effect, const void* params, u32 count) {
|
||||||
|
if (!gfx || !params || count == 0) return;
|
||||||
|
|
||||||
|
switch (effect) {
|
||||||
|
case PXL8_GFX_EFFECT_GLOWS: {
|
||||||
|
const pxl8_glow_source* glows = (const pxl8_glow_source*)params;
|
||||||
|
switch (gfx->backend.type) {
|
||||||
|
case PXL8_GFX_BACKEND_CPU:
|
||||||
|
pxl8_cpu_render_glows(gfx->backend.cpu, glows, count);
|
||||||
|
break;
|
||||||
|
case PXL8_GFX_BACKEND_GPU:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
110
src/gfx/pxl8_gfx.h
Normal file
110
src/gfx/pxl8_gfx.h
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "pxl8_gfx2d.h"
|
||||||
|
#include "pxl8_gfx3d.h"
|
||||||
|
#include "pxl8_hal.h"
|
||||||
|
#include "pxl8_colormap.h"
|
||||||
|
#include "pxl8_palette.h"
|
||||||
|
#include "pxl8_types.h"
|
||||||
|
#include "pxl8_gui_palette.h"
|
||||||
|
|
||||||
|
typedef struct pxl8_gfx pxl8_gfx;
|
||||||
|
|
||||||
|
typedef enum pxl8_gfx_effect {
|
||||||
|
PXL8_GFX_EFFECT_GLOWS = 0,
|
||||||
|
} pxl8_gfx_effect;
|
||||||
|
|
||||||
|
#define PXL8_MAX_GLOWS 256
|
||||||
|
|
||||||
|
typedef enum pxl8_glow_shape {
|
||||||
|
PXL8_GLOW_CIRCLE = 0,
|
||||||
|
PXL8_GLOW_DIAMOND = 1,
|
||||||
|
PXL8_GLOW_SHAFT = 2,
|
||||||
|
} pxl8_glow_shape;
|
||||||
|
|
||||||
|
typedef struct pxl8_glow_source {
|
||||||
|
u8 color;
|
||||||
|
u16 depth;
|
||||||
|
u8 height;
|
||||||
|
u16 intensity;
|
||||||
|
u8 radius;
|
||||||
|
pxl8_glow_shape shape;
|
||||||
|
i16 x;
|
||||||
|
i16 y;
|
||||||
|
} pxl8_glow_source;
|
||||||
|
|
||||||
|
static inline pxl8_glow_source pxl8_glow_create(i32 x, i32 y, u8 radius, u16 intensity, u8 color) {
|
||||||
|
return (pxl8_glow_source){
|
||||||
|
.color = color,
|
||||||
|
.depth = 0xFFFF,
|
||||||
|
.height = 0,
|
||||||
|
.intensity = intensity,
|
||||||
|
.radius = radius,
|
||||||
|
.shape = PXL8_GLOW_CIRCLE,
|
||||||
|
.x = (i16)x,
|
||||||
|
.y = (i16)y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline pxl8_glow_source pxl8_glow_with_depth(pxl8_glow_source g, u16 depth) {
|
||||||
|
g.depth = depth;
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline pxl8_glow_source pxl8_glow_with_shape(pxl8_glow_source g, pxl8_glow_shape shape) {
|
||||||
|
g.shape = shape;
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline pxl8_glow_source pxl8_glow_with_height(pxl8_glow_source g, u8 height) {
|
||||||
|
g.height = height;
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
#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_palette(pxl8_gfx* gfx);
|
||||||
|
pxl8_colormap* pxl8_gfx_colormap(pxl8_gfx* gfx);
|
||||||
|
pxl8_pixel_mode pxl8_gfx_get_pixel_mode(pxl8_gfx* gfx);
|
||||||
|
i32 pxl8_gfx_get_width(const pxl8_gfx* gfx);
|
||||||
|
|
||||||
|
i32 pxl8_gfx_load_palette(pxl8_gfx* gfx, const char* filepath);
|
||||||
|
void pxl8_gfx_project(pxl8_gfx* gfx, f32 left, f32 right, f32 top, f32 bottom);
|
||||||
|
void pxl8_gfx_set_palette_colors(pxl8_gfx* gfx, const u32* colors, u16 count);
|
||||||
|
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);
|
||||||
|
|
||||||
|
void pxl8_gfx_apply_effect(pxl8_gfx* gfx, pxl8_gfx_effect effect, const void* params, u32 count);
|
||||||
|
void pxl8_gfx_blend_tables_update(pxl8_gfx* gfx);
|
||||||
|
void pxl8_gfx_colormap_update(pxl8_gfx* gfx);
|
||||||
|
void pxl8_gfx_ensure_blend_tables(pxl8_gfx* gfx);
|
||||||
|
|
||||||
|
u8 pxl8_gfx_find_closest_color(pxl8_gfx* gfx, u8 r, u8 g, u8 b);
|
||||||
|
u8 pxl8_gfx_ui_color(pxl8_gfx* gfx, u8 index);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
72
src/gfx/pxl8_gfx3d.h
Normal file
72
src/gfx/pxl8_gfx3d.h
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
#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;
|
||||||
|
|
||||||
|
#define PXL8_MAX_LIGHTS 16
|
||||||
|
|
||||||
|
typedef struct pxl8_light {
|
||||||
|
pxl8_vec3 position;
|
||||||
|
u8 r, g, b;
|
||||||
|
u8 intensity;
|
||||||
|
f32 radius;
|
||||||
|
f32 radius_sq;
|
||||||
|
f32 inv_radius_sq;
|
||||||
|
} pxl8_light;
|
||||||
|
|
||||||
|
static inline pxl8_light pxl8_light_create(pxl8_vec3 pos, u8 r, u8 g, u8 b, u8 intensity, f32 radius) {
|
||||||
|
f32 radius_sq = radius * radius;
|
||||||
|
return (pxl8_light){
|
||||||
|
.position = pos,
|
||||||
|
.r = r, .g = g, .b = b,
|
||||||
|
.intensity = intensity,
|
||||||
|
.radius = radius,
|
||||||
|
.radius_sq = radius_sq,
|
||||||
|
.inv_radius_sq = radius_sq > 0.0f ? 1.0f / radius_sq : 0.0f,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct pxl8_3d_uniforms {
|
||||||
|
u8 ambient;
|
||||||
|
pxl8_vec3 celestial_dir;
|
||||||
|
f32 celestial_intensity;
|
||||||
|
u8 fog_color;
|
||||||
|
f32 fog_density;
|
||||||
|
pxl8_light lights[PXL8_MAX_LIGHTS];
|
||||||
|
u32 num_lights;
|
||||||
|
f32 time;
|
||||||
|
} pxl8_3d_uniforms;
|
||||||
|
|
||||||
|
typedef struct pxl8_3d_frame {
|
||||||
|
pxl8_3d_uniforms uniforms;
|
||||||
|
pxl8_vec3 camera_dir;
|
||||||
|
pxl8_vec3 camera_pos;
|
||||||
|
f32 far_clip;
|
||||||
|
f32 near_clip;
|
||||||
|
pxl8_mat4 projection;
|
||||||
|
pxl8_mat4 view;
|
||||||
|
} pxl8_3d_frame;
|
||||||
|
|
||||||
|
#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, const pxl8_mat4* model, const pxl8_gfx_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);
|
||||||
|
|
||||||
|
pxl8_3d_frame pxl8_3d_frame_from_camera(const pxl8_3d_camera* camera, const pxl8_3d_uniforms* uniforms);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
113
src/gfx/pxl8_lightmap.c
Normal file
113
src/gfx/pxl8_lightmap.c
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
#include "pxl8_lightmap.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
pxl8_lightmap* pxl8_lightmap_create(u32 width, u32 height, u32 scale) {
|
||||||
|
pxl8_lightmap* lm = calloc(1, sizeof(pxl8_lightmap));
|
||||||
|
if (!lm) return NULL;
|
||||||
|
|
||||||
|
lm->width = width;
|
||||||
|
lm->height = height;
|
||||||
|
lm->scale = scale;
|
||||||
|
lm->data = calloc(width * height * 3, sizeof(u8));
|
||||||
|
if (!lm->data) {
|
||||||
|
free(lm);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pxl8_lightmap_clear(lm, PXL8_LIGHTMAP_NEUTRAL, PXL8_LIGHTMAP_NEUTRAL, PXL8_LIGHTMAP_NEUTRAL);
|
||||||
|
return lm;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_lightmap_destroy(pxl8_lightmap* lm) {
|
||||||
|
if (!lm) return;
|
||||||
|
free(lm->data);
|
||||||
|
free(lm);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_lightmap_clear(pxl8_lightmap* lm, u8 r, u8 g, u8 b) {
|
||||||
|
if (!lm || !lm->data) return;
|
||||||
|
u32 count = lm->width * lm->height;
|
||||||
|
for (u32 i = 0; i < count; i++) {
|
||||||
|
lm->data[i * 3 + 0] = r;
|
||||||
|
lm->data[i * 3 + 1] = g;
|
||||||
|
lm->data[i * 3 + 2] = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_lightmap_set(pxl8_lightmap* lm, u32 x, u32 y, u8 r, u8 g, u8 b) {
|
||||||
|
if (!lm || !lm->data || x >= lm->width || y >= lm->height) return;
|
||||||
|
u32 idx = (y * lm->width + x) * 3;
|
||||||
|
lm->data[idx + 0] = r;
|
||||||
|
lm->data[idx + 1] = g;
|
||||||
|
lm->data[idx + 2] = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_lightmap_get(const pxl8_lightmap* lm, u32 x, u32 y, u8* r, u8* g, u8* b) {
|
||||||
|
if (!lm || !lm->data || x >= lm->width || y >= lm->height) {
|
||||||
|
*r = *g = *b = PXL8_LIGHTMAP_NEUTRAL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
u32 idx = (y * lm->width + x) * 3;
|
||||||
|
*r = lm->data[idx + 0];
|
||||||
|
*g = lm->data[idx + 1];
|
||||||
|
*b = lm->data[idx + 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_lightmap_add_point(
|
||||||
|
pxl8_lightmap* lm,
|
||||||
|
f32 lx, f32 ly,
|
||||||
|
u8 r, u8 g, u8 b,
|
||||||
|
f32 radius,
|
||||||
|
f32 intensity
|
||||||
|
) {
|
||||||
|
if (!lm || !lm->data || radius <= 0.0f) return;
|
||||||
|
|
||||||
|
f32 radius_sq = radius * radius;
|
||||||
|
f32 inv_radius_sq = 1.0f / radius_sq;
|
||||||
|
|
||||||
|
i32 cx = (i32)(lx * (f32)lm->width);
|
||||||
|
i32 cy = (i32)(ly * (f32)lm->height);
|
||||||
|
i32 rad_pixels = (i32)(radius * (f32)lm->width) + 1;
|
||||||
|
|
||||||
|
i32 x0 = cx - rad_pixels;
|
||||||
|
i32 y0 = cy - rad_pixels;
|
||||||
|
i32 x1 = cx + rad_pixels;
|
||||||
|
i32 y1 = cy + rad_pixels;
|
||||||
|
|
||||||
|
if (x0 < 0) x0 = 0;
|
||||||
|
if (y0 < 0) y0 = 0;
|
||||||
|
if (x1 >= (i32)lm->width) x1 = (i32)lm->width - 1;
|
||||||
|
if (y1 >= (i32)lm->height) y1 = (i32)lm->height - 1;
|
||||||
|
|
||||||
|
f32 scale_x = 1.0f / (f32)lm->width;
|
||||||
|
f32 scale_y = 1.0f / (f32)lm->height;
|
||||||
|
|
||||||
|
for (i32 y = y0; y <= y1; y++) {
|
||||||
|
f32 dy = ((f32)y * scale_y) - ly;
|
||||||
|
for (i32 x = x0; x <= x1; x++) {
|
||||||
|
f32 dx = ((f32)x * scale_x) - lx;
|
||||||
|
f32 dist_sq = dx * dx + dy * dy;
|
||||||
|
|
||||||
|
if (dist_sq >= radius_sq) continue;
|
||||||
|
|
||||||
|
f32 falloff = 1.0f - dist_sq * inv_radius_sq;
|
||||||
|
f32 contrib = falloff * falloff * intensity;
|
||||||
|
|
||||||
|
u32 idx = ((u32)y * lm->width + (u32)x) * 3;
|
||||||
|
|
||||||
|
i32 nr = (i32)lm->data[idx + 0] + (i32)((f32)(r - 128) * contrib);
|
||||||
|
i32 ng = (i32)lm->data[idx + 1] + (i32)((f32)(g - 128) * contrib);
|
||||||
|
i32 nb = (i32)lm->data[idx + 2] + (i32)((f32)(b - 128) * contrib);
|
||||||
|
|
||||||
|
if (nr < 0) nr = 0; if (nr > 255) nr = 255;
|
||||||
|
if (ng < 0) ng = 0; if (ng > 255) ng = 255;
|
||||||
|
if (nb < 0) nb = 0; if (nb > 255) nb = 255;
|
||||||
|
|
||||||
|
lm->data[idx + 0] = (u8)nr;
|
||||||
|
lm->data[idx + 1] = (u8)ng;
|
||||||
|
lm->data[idx + 2] = (u8)nb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
48
src/gfx/pxl8_lightmap.h
Normal file
48
src/gfx/pxl8_lightmap.h
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "pxl8_types.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define PXL8_LIGHTMAP_MAX 16
|
||||||
|
#define PXL8_LIGHTMAP_NEUTRAL 128
|
||||||
|
|
||||||
|
typedef struct pxl8_lightmap {
|
||||||
|
u8* data;
|
||||||
|
u32 width;
|
||||||
|
u32 height;
|
||||||
|
u32 scale;
|
||||||
|
} pxl8_lightmap;
|
||||||
|
|
||||||
|
pxl8_lightmap* pxl8_lightmap_create(u32 width, u32 height, u32 scale);
|
||||||
|
void pxl8_lightmap_destroy(pxl8_lightmap* lm);
|
||||||
|
void pxl8_lightmap_clear(pxl8_lightmap* lm, u8 r, u8 g, u8 b);
|
||||||
|
void pxl8_lightmap_set(pxl8_lightmap* lm, u32 x, u32 y, u8 r, u8 g, u8 b);
|
||||||
|
void pxl8_lightmap_get(const pxl8_lightmap* lm, u32 x, u32 y, u8* r, u8* g, u8* b);
|
||||||
|
|
||||||
|
void pxl8_lightmap_add_point(
|
||||||
|
pxl8_lightmap* lm,
|
||||||
|
f32 lx, f32 ly,
|
||||||
|
u8 r, u8 g, u8 b,
|
||||||
|
f32 radius,
|
||||||
|
f32 intensity
|
||||||
|
);
|
||||||
|
|
||||||
|
static inline void pxl8_lightmap_sample(
|
||||||
|
const pxl8_lightmap* lm,
|
||||||
|
f32 u, f32 v,
|
||||||
|
u8* r, u8* g, u8* b
|
||||||
|
) {
|
||||||
|
i32 x = (i32)(u * (f32)lm->width) & (i32)(lm->width - 1);
|
||||||
|
i32 y = (i32)(v * (f32)lm->height) & (i32)(lm->height - 1);
|
||||||
|
u32 idx = ((u32)y * lm->width + (u32)x) * 3;
|
||||||
|
*r = lm->data[idx + 0];
|
||||||
|
*g = lm->data[idx + 1];
|
||||||
|
*b = lm->data[idx + 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
@ -17,8 +17,9 @@ typedef enum pxl8_blend_mode {
|
||||||
PXL8_BLEND_ADDITIVE,
|
PXL8_BLEND_ADDITIVE,
|
||||||
} pxl8_blend_mode;
|
} pxl8_blend_mode;
|
||||||
|
|
||||||
typedef struct pxl8_material {
|
typedef struct pxl8_gfx_material {
|
||||||
u32 texture_id;
|
u32 texture_id;
|
||||||
|
u32 lightmap_id;
|
||||||
u8 alpha;
|
u8 alpha;
|
||||||
u8 blend_mode;
|
u8 blend_mode;
|
||||||
bool dither;
|
bool dither;
|
||||||
|
|
@ -26,13 +27,15 @@ typedef struct pxl8_material {
|
||||||
bool dynamic_lighting;
|
bool dynamic_lighting;
|
||||||
bool per_pixel;
|
bool per_pixel;
|
||||||
bool vertex_color_passthrough;
|
bool vertex_color_passthrough;
|
||||||
|
bool wireframe;
|
||||||
f32 emissive_intensity;
|
f32 emissive_intensity;
|
||||||
} pxl8_material;
|
} pxl8_gfx_material;
|
||||||
|
|
||||||
typedef struct pxl8_vertex {
|
typedef struct pxl8_vertex {
|
||||||
pxl8_vec3 position;
|
pxl8_vec3 position;
|
||||||
pxl8_vec3 normal;
|
pxl8_vec3 normal;
|
||||||
f32 u, v;
|
f32 u, v;
|
||||||
|
f32 lu, lv;
|
||||||
u8 color;
|
u8 color;
|
||||||
u8 light;
|
u8 light;
|
||||||
u8 _pad[2];
|
u8 _pad[2];
|
||||||
|
|
@ -62,62 +65,6 @@ static inline u32 pxl8_mesh_triangle_count(const pxl8_mesh* mesh) {
|
||||||
return mesh->index_count / 3;
|
return mesh->index_count / 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline pxl8_material pxl8_material_new(u32 texture_id) {
|
|
||||||
return (pxl8_material){
|
|
||||||
.texture_id = texture_id,
|
|
||||||
.alpha = 255,
|
|
||||||
.blend_mode = PXL8_BLEND_OPAQUE,
|
|
||||||
.dither = true,
|
|
||||||
.double_sided = false,
|
|
||||||
.dynamic_lighting = false,
|
|
||||||
.per_pixel = false,
|
|
||||||
.vertex_color_passthrough = false,
|
|
||||||
.emissive_intensity = 0.0f,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_material pxl8_material_with_alpha(pxl8_material m, u8 alpha) {
|
|
||||||
m.alpha = alpha;
|
|
||||||
if (alpha < 255 && m.blend_mode == PXL8_BLEND_OPAQUE) {
|
|
||||||
m.blend_mode = PXL8_BLEND_ALPHA;
|
|
||||||
}
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_material pxl8_material_with_blend(pxl8_material m, pxl8_blend_mode mode) {
|
|
||||||
m.blend_mode = mode;
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_material pxl8_material_with_double_sided(pxl8_material m) {
|
|
||||||
m.double_sided = true;
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_material pxl8_material_with_emissive(pxl8_material m, f32 intensity) {
|
|
||||||
m.emissive_intensity = intensity;
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_material pxl8_material_with_lighting(pxl8_material m) {
|
|
||||||
m.dynamic_lighting = true;
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_material pxl8_material_with_no_dither(pxl8_material m) {
|
|
||||||
m.dither = false;
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_material pxl8_material_with_passthrough(pxl8_material m) {
|
|
||||||
m.vertex_color_passthrough = true;
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline pxl8_material pxl8_material_with_per_pixel(pxl8_material m) {
|
|
||||||
m.per_pixel = true;
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
@ -5,10 +5,17 @@
|
||||||
|
|
||||||
#include "pxl8_ase.h"
|
#include "pxl8_ase.h"
|
||||||
#include "pxl8_color.h"
|
#include "pxl8_color.h"
|
||||||
|
#include "pxl8_colormap.h"
|
||||||
#include "pxl8_log.h"
|
#include "pxl8_log.h"
|
||||||
|
|
||||||
#define PXL8_PALETTE_HASH_SIZE 512
|
#define PXL8_PALETTE_HASH_SIZE 512
|
||||||
|
|
||||||
|
struct pxl8_palette_cube {
|
||||||
|
u8 colors[PXL8_PALETTE_SIZE * 3];
|
||||||
|
u8 table[PXL8_CUBE_ENTRIES];
|
||||||
|
u8 stable[PXL8_CUBE_ENTRIES];
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
u32 color;
|
u32 color;
|
||||||
i16 index;
|
i16 index;
|
||||||
|
|
@ -332,10 +339,6 @@ void pxl8_palette_get_rgba(const pxl8_palette* pal, u8 idx, u8* r, u8* g, u8* b,
|
||||||
unpack_rgba(pal->colors[idx], r, g, b, a);
|
unpack_rgba(pal->colors[idx], r, g, b, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_palette_set(pxl8_palette* pal, u8 idx, u32 color) {
|
|
||||||
if (pal) pal->colors[idx] = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
void pxl8_palette_set_rgb(pxl8_palette* pal, u8 idx, u8 r, u8 g, u8 b) {
|
void pxl8_palette_set_rgb(pxl8_palette* pal, u8 idx, u8 r, u8 g, u8 b) {
|
||||||
if (pal) pal->colors[idx] = pack_rgb(r, g, b);
|
if (pal) pal->colors[idx] = pack_rgb(r, g, b);
|
||||||
}
|
}
|
||||||
|
|
@ -344,6 +347,17 @@ void pxl8_palette_set_rgba(pxl8_palette* pal, u8 idx, u8 r, u8 g, u8 b, u8 a) {
|
||||||
if (pal) pal->colors[idx] = pack_rgba(r, g, b, a);
|
if (pal) pal->colors[idx] = pack_rgba(r, g, b, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pxl8_set_palette(pxl8_palette* pal, const u32* colors, u16 count) {
|
||||||
|
if (!pal || !colors) return;
|
||||||
|
for (u16 i = 0; i < count; i++) {
|
||||||
|
u32 rgb = colors[i];
|
||||||
|
u8 r = (rgb >> 16) & 0xFF;
|
||||||
|
u8 g = (rgb >> 8) & 0xFF;
|
||||||
|
u8 b = rgb & 0xFF;
|
||||||
|
pal->colors[i] = pack_rgb(r, g, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void pxl8_palette_fill_gradient(pxl8_palette* pal, u8 start, u8 count, u32 from, u32 to) {
|
void pxl8_palette_fill_gradient(pxl8_palette* pal, u8 start, u8 count, u32 from, u32 to) {
|
||||||
if (!pal || count == 0) return;
|
if (!pal || count == 0) return;
|
||||||
|
|
||||||
|
|
@ -449,7 +463,7 @@ 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_create(u8 start, u8 len, u16 period) {
|
||||||
pxl8_cycle_range range = {
|
pxl8_cycle_range range = {
|
||||||
.easing = PXL8_EASE_LINEAR,
|
.easing = PXL8_EASE_LINEAR,
|
||||||
.interpolate = true,
|
.interpolate = true,
|
||||||
|
|
@ -472,3 +486,91 @@ pxl8_cycle_range pxl8_cycle_range_disabled(void) {
|
||||||
};
|
};
|
||||||
return range;
|
return range;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u8 find_closest_stable(const pxl8_palette* pal, u8 r, u8 g, u8 b) {
|
||||||
|
u8 best_idx = 1;
|
||||||
|
u32 best_dist = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
u8 dynamic_end = PXL8_DYNAMIC_RANGE_START + PXL8_DYNAMIC_RANGE_COUNT;
|
||||||
|
|
||||||
|
for (u32 i = 1; i < PXL8_FULLBRIGHT_START; i++) {
|
||||||
|
if (i >= PXL8_DYNAMIC_RANGE_START && i < dynamic_end) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 pr, pg, pb;
|
||||||
|
pxl8_palette_get_rgb(pal, (u8)i, &pr, &pg, &pb);
|
||||||
|
|
||||||
|
i32 dr = (i32)r - (i32)pr;
|
||||||
|
i32 dg = (i32)g - (i32)pg;
|
||||||
|
i32 db = (i32)b - (i32)pb;
|
||||||
|
u32 dist = (u32)(dr * dr + dg * dg + db * db);
|
||||||
|
|
||||||
|
if (dist < best_dist) {
|
||||||
|
best_dist = dist;
|
||||||
|
best_idx = (u8)i;
|
||||||
|
if (dist == 0) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return best_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
pxl8_palette_cube* pxl8_palette_cube_create(const pxl8_palette* pal) {
|
||||||
|
pxl8_palette_cube* cube = calloc(1, sizeof(pxl8_palette_cube));
|
||||||
|
if (!cube) return NULL;
|
||||||
|
pxl8_palette_cube_rebuild(cube, pal);
|
||||||
|
return cube;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_palette_cube_destroy(pxl8_palette_cube* cube) {
|
||||||
|
free(cube);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_palette_cube_rebuild(pxl8_palette_cube* cube, const pxl8_palette* pal) {
|
||||||
|
if (!cube || !pal) return;
|
||||||
|
|
||||||
|
for (u32 i = 0; i < PXL8_PALETTE_SIZE; i++) {
|
||||||
|
u8 r, g, b;
|
||||||
|
pxl8_palette_get_rgb(pal, (u8)i, &r, &g, &b);
|
||||||
|
cube->colors[i * 3 + 0] = r;
|
||||||
|
cube->colors[i * 3 + 1] = g;
|
||||||
|
cube->colors[i * 3 + 2] = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (u32 bi = 0; bi < PXL8_CUBE_SIZE; bi++) {
|
||||||
|
for (u32 gi = 0; gi < PXL8_CUBE_SIZE; gi++) {
|
||||||
|
for (u32 ri = 0; ri < PXL8_CUBE_SIZE; ri++) {
|
||||||
|
u32 idx = (bi * PXL8_CUBE_SIZE * PXL8_CUBE_SIZE) + (gi * PXL8_CUBE_SIZE) + ri;
|
||||||
|
u8 r8 = (u8)((ri * 255) / (PXL8_CUBE_SIZE - 1));
|
||||||
|
u8 g8 = (u8)((gi * 255) / (PXL8_CUBE_SIZE - 1));
|
||||||
|
u8 b8 = (u8)((bi * 255) / (PXL8_CUBE_SIZE - 1));
|
||||||
|
|
||||||
|
cube->table[idx] = pxl8_palette_find_closest(pal, r8, g8, b8);
|
||||||
|
cube->stable[idx] = find_closest_stable(pal, r8, g8, b8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 pxl8_palette_cube_lookup(const pxl8_palette_cube* cube, u8 r, u8 g, u8 b) {
|
||||||
|
u32 ri = (r * (PXL8_CUBE_SIZE - 1)) / 255;
|
||||||
|
u32 gi = (g * (PXL8_CUBE_SIZE - 1)) / 255;
|
||||||
|
u32 bi = (b * (PXL8_CUBE_SIZE - 1)) / 255;
|
||||||
|
u32 idx = (bi * PXL8_CUBE_SIZE * PXL8_CUBE_SIZE) + (gi * PXL8_CUBE_SIZE) + ri;
|
||||||
|
return cube->table[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 pxl8_palette_cube_lookup_stable(const pxl8_palette_cube* cube, u8 r, u8 g, u8 b) {
|
||||||
|
u32 ri = (r * (PXL8_CUBE_SIZE - 1)) / 255;
|
||||||
|
u32 gi = (g * (PXL8_CUBE_SIZE - 1)) / 255;
|
||||||
|
u32 bi = (b * (PXL8_CUBE_SIZE - 1)) / 255;
|
||||||
|
u32 idx = (bi * PXL8_CUBE_SIZE * PXL8_CUBE_SIZE) + (gi * PXL8_CUBE_SIZE) + ri;
|
||||||
|
return cube->stable[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_palette_cube_get_rgb(const pxl8_palette_cube* cube, u8 idx, u8* r, u8* g, u8* b) {
|
||||||
|
*r = cube->colors[idx * 3 + 0];
|
||||||
|
*g = cube->colors[idx * 3 + 1];
|
||||||
|
*b = cube->colors[idx * 3 + 2];
|
||||||
|
}
|
||||||
|
|
@ -5,8 +5,11 @@
|
||||||
#define PXL8_PALETTE_SIZE 256
|
#define PXL8_PALETTE_SIZE 256
|
||||||
#define PXL8_MAX_CYCLES 8
|
#define PXL8_MAX_CYCLES 8
|
||||||
#define PXL8_MAX_CYCLE_LEN 16
|
#define PXL8_MAX_CYCLE_LEN 16
|
||||||
|
#define PXL8_CUBE_SIZE 32
|
||||||
|
#define PXL8_CUBE_ENTRIES (PXL8_CUBE_SIZE * PXL8_CUBE_SIZE * PXL8_CUBE_SIZE)
|
||||||
|
|
||||||
typedef struct pxl8_palette pxl8_palette;
|
typedef struct pxl8_palette pxl8_palette;
|
||||||
|
typedef struct pxl8_palette_cube pxl8_palette_cube;
|
||||||
|
|
||||||
typedef enum pxl8_cycle_mode {
|
typedef enum pxl8_cycle_mode {
|
||||||
PXL8_CYCLE_LOOP,
|
PXL8_CYCLE_LOOP,
|
||||||
|
|
@ -49,9 +52,9 @@ u32 pxl8_palette_color(const pxl8_palette* pal, u8 idx);
|
||||||
i32 pxl8_palette_index(const pxl8_palette* pal, u32 color);
|
i32 pxl8_palette_index(const pxl8_palette* pal, u32 color);
|
||||||
void pxl8_palette_get_rgb(const pxl8_palette* pal, u8 idx, u8* r, u8* g, u8* b);
|
void pxl8_palette_get_rgb(const pxl8_palette* pal, u8 idx, u8* r, u8* g, u8* b);
|
||||||
void pxl8_palette_get_rgba(const pxl8_palette* pal, u8 idx, u8* r, u8* g, u8* b, u8* a);
|
void pxl8_palette_get_rgba(const pxl8_palette* pal, u8 idx, u8* r, u8* g, u8* b, u8* a);
|
||||||
void pxl8_palette_set(pxl8_palette* pal, u8 idx, u32 color);
|
|
||||||
void pxl8_palette_set_rgb(pxl8_palette* pal, u8 idx, u8 r, u8 g, u8 b);
|
void pxl8_palette_set_rgb(pxl8_palette* pal, u8 idx, u8 r, u8 g, u8 b);
|
||||||
void pxl8_palette_set_rgba(pxl8_palette* pal, u8 idx, u8 r, u8 g, u8 b, u8 a);
|
void pxl8_palette_set_rgba(pxl8_palette* pal, u8 idx, u8 r, u8 g, u8 b, u8 a);
|
||||||
|
void pxl8_set_palette(pxl8_palette* pal, const u32* colors, u16 count);
|
||||||
|
|
||||||
void pxl8_palette_fill_gradient(pxl8_palette* pal, u8 start, u8 count, u32 from, u32 to);
|
void pxl8_palette_fill_gradient(pxl8_palette* pal, u8 start, u8 count, u32 from, u32 to);
|
||||||
void pxl8_palette_fill_gradient_rgb(pxl8_palette* pal, u8 start, u8 count, u8 r0, u8 g0, u8 b0, u8 r1, u8 g1, u8 b1);
|
void pxl8_palette_fill_gradient_rgb(pxl8_palette* pal, u8 start, u8 count, u8 r0, u8 g0, u8 b0, u8 r1, u8 g1, u8 b1);
|
||||||
|
|
@ -62,9 +65,16 @@ void pxl8_palette_set_cycle_colors(pxl8_palette* pal, u8 slot, const u32* colors
|
||||||
void pxl8_palette_set_cycle_phase(pxl8_palette* pal, u8 slot, f32 phase);
|
void pxl8_palette_set_cycle_phase(pxl8_palette* pal, u8 slot, f32 phase);
|
||||||
void pxl8_palette_tick(pxl8_palette* pal, u16 delta_ticks);
|
void pxl8_palette_tick(pxl8_palette* pal, u16 delta_ticks);
|
||||||
|
|
||||||
pxl8_cycle_range pxl8_cycle_range_new(u8 start, u8 len, u16 period);
|
pxl8_cycle_range pxl8_cycle_range_create(u8 start, u8 len, u16 period);
|
||||||
pxl8_cycle_range pxl8_cycle_range_disabled(void);
|
pxl8_cycle_range pxl8_cycle_range_disabled(void);
|
||||||
|
|
||||||
|
pxl8_palette_cube* pxl8_palette_cube_create(const pxl8_palette* pal);
|
||||||
|
void pxl8_palette_cube_destroy(pxl8_palette_cube* cube);
|
||||||
|
void pxl8_palette_cube_rebuild(pxl8_palette_cube* cube, const pxl8_palette* pal);
|
||||||
|
u8 pxl8_palette_cube_lookup(const pxl8_palette_cube* cube, u8 r, u8 g, u8 b);
|
||||||
|
u8 pxl8_palette_cube_lookup_stable(const pxl8_palette_cube* cube, u8 r, u8 g, u8 b);
|
||||||
|
void pxl8_palette_cube_get_rgb(const pxl8_palette_cube* cube, u8 idx, u8* r, u8* g, u8* b);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -18,18 +18,20 @@ void pxl8_gui_state_destroy(pxl8_gui_state* state) {
|
||||||
free(state);
|
free(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_gui_begin_frame(pxl8_gui_state* state) {
|
void pxl8_gui_begin_frame(pxl8_gui_state* state, pxl8_gfx* gfx) {
|
||||||
if (!state) return;
|
if (!state) return;
|
||||||
state->hot_id = 0;
|
state->hot_id = 0;
|
||||||
|
if (gfx) pxl8_gfx_push_target(gfx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_gui_end_frame(pxl8_gui_state* state) {
|
void pxl8_gui_end_frame(pxl8_gui_state* state, pxl8_gfx* gfx) {
|
||||||
if (!state) return;
|
if (!state) return;
|
||||||
|
|
||||||
if (!state->cursor_down) {
|
if (!state->cursor_down) {
|
||||||
state->active_id = 0;
|
state->active_id = 0;
|
||||||
}
|
}
|
||||||
state->cursor_clicked = false;
|
state->cursor_clicked = false;
|
||||||
|
if (gfx) pxl8_gfx_pop_target(gfx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_gui_cursor_move(pxl8_gui_state* state, i32 x, i32 y) {
|
void pxl8_gui_cursor_move(pxl8_gui_state* state, i32 x, i32 y) {
|
||||||
|
|
@ -80,16 +82,16 @@ bool pxl8_gui_button(pxl8_gui_state* state, pxl8_gfx* gfx, u32 id, i32 x, i32 y,
|
||||||
i32 offset_y = 0;
|
i32 offset_y = 0;
|
||||||
|
|
||||||
if (is_active) {
|
if (is_active) {
|
||||||
bg_color = 4;
|
bg_color = pxl8_gfx_ui_color(gfx, PXL8_UI_BG3);
|
||||||
border_color = 3;
|
border_color = pxl8_gfx_ui_color(gfx, PXL8_UI_BG2);
|
||||||
offset_x = 1;
|
offset_x = 1;
|
||||||
offset_y = 1;
|
offset_y = 1;
|
||||||
} else if (is_hot || cursor_over) {
|
} else if (is_hot || cursor_over) {
|
||||||
bg_color = 4;
|
bg_color = pxl8_gfx_ui_color(gfx, PXL8_UI_BG3);
|
||||||
border_color = 8;
|
border_color = pxl8_gfx_ui_color(gfx, PXL8_UI_FG0);
|
||||||
} else {
|
} else {
|
||||||
bg_color = 3;
|
bg_color = pxl8_gfx_ui_color(gfx, PXL8_UI_BG2);
|
||||||
border_color = 4;
|
border_color = pxl8_gfx_ui_color(gfx, PXL8_UI_BG3);
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_2d_rect_fill(gfx, x, y, w, h, bg_color);
|
pxl8_2d_rect_fill(gfx, x, y, w, h, bg_color);
|
||||||
|
|
@ -98,7 +100,7 @@ bool pxl8_gui_button(pxl8_gui_state* state, pxl8_gfx* gfx, u32 id, i32 x, i32 y,
|
||||||
i32 text_len = (i32)strlen(label);
|
i32 text_len = (i32)strlen(label);
|
||||||
i32 text_x = x + (w / 2) - ((text_len * 8) / 2) + offset_x;
|
i32 text_x = x + (w / 2) - ((text_len * 8) / 2) + offset_x;
|
||||||
i32 text_y = y + (h / 2) - 5 + offset_y;
|
i32 text_y = y + (h / 2) - 5 + offset_y;
|
||||||
pxl8_2d_text(gfx, label, text_x, text_y, 6);
|
pxl8_2d_text(gfx, label, text_x, text_y, pxl8_gfx_ui_color(gfx, PXL8_UI_FG1));
|
||||||
|
|
||||||
return clicked;
|
return clicked;
|
||||||
}
|
}
|
||||||
|
|
@ -106,14 +108,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) {
|
void pxl8_gui_window(pxl8_gfx* gfx, i32 x, i32 y, i32 w, i32 h, const char* title) {
|
||||||
if (!gfx || !title) return;
|
if (!gfx || !title) return;
|
||||||
|
|
||||||
pxl8_2d_rect_fill(gfx, x, y, w, 28, 1);
|
u8 title_bg = pxl8_gfx_ui_color(gfx, PXL8_UI_BG1);
|
||||||
pxl8_2d_rect_fill(gfx, x, y + 28, w, h - 28, 2);
|
u8 body_bg = pxl8_gfx_ui_color(gfx, PXL8_UI_BG2);
|
||||||
pxl8_2d_rect(gfx, x, y, w, h, 4);
|
u8 border = pxl8_gfx_ui_color(gfx, PXL8_UI_BG3);
|
||||||
pxl8_2d_rect_fill(gfx, x, y + 28, w, 1, 4);
|
u8 title_fg = pxl8_gfx_ui_color(gfx, PXL8_UI_FG0);
|
||||||
|
|
||||||
|
pxl8_2d_rect_fill(gfx, x, y, w, 28, title_bg);
|
||||||
|
pxl8_2d_rect_fill(gfx, x, y + 28, w, h - 28, body_bg);
|
||||||
|
pxl8_2d_rect(gfx, x, y, w, h, border);
|
||||||
|
pxl8_2d_rect_fill(gfx, x, y + 28, w, 1, border);
|
||||||
|
|
||||||
i32 title_x = x + 10;
|
i32 title_x = x + 10;
|
||||||
i32 title_y = y + (28 / 2) - 5;
|
i32 title_y = y + (28 / 2) - 5;
|
||||||
pxl8_2d_text(gfx, title, title_x, title_y, 8);
|
pxl8_2d_text(gfx, title, title_x, title_y, title_fg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pxl8_gui_label(pxl8_gfx* gfx, i32 x, i32 y, const char* text, u8 color) {
|
void pxl8_gui_label(pxl8_gfx* gfx, i32 x, i32 y, const char* text, u8 color) {
|
||||||
|
|
@ -22,8 +22,8 @@ void pxl8_gui_state_destroy(pxl8_gui_state* state);
|
||||||
void pxl8_gui_get_cursor_pos(const pxl8_gui_state* state, i32* x, i32* y);
|
void pxl8_gui_get_cursor_pos(const pxl8_gui_state* state, i32* x, i32* y);
|
||||||
bool pxl8_gui_is_hovering(const pxl8_gui_state* state);
|
bool pxl8_gui_is_hovering(const pxl8_gui_state* state);
|
||||||
|
|
||||||
void pxl8_gui_begin_frame(pxl8_gui_state* state);
|
void pxl8_gui_begin_frame(pxl8_gui_state* state, pxl8_gfx* gfx);
|
||||||
void pxl8_gui_end_frame(pxl8_gui_state* state);
|
void pxl8_gui_end_frame(pxl8_gui_state* state, pxl8_gfx* gfx);
|
||||||
|
|
||||||
void pxl8_gui_cursor_down(pxl8_gui_state* state);
|
void pxl8_gui_cursor_down(pxl8_gui_state* state);
|
||||||
void pxl8_gui_cursor_move(pxl8_gui_state* state, i32 x, i32 y);
|
void pxl8_gui_cursor_move(pxl8_gui_state* state, i32 x, i32 y);
|
||||||
41
src/gui/pxl8_gui_palette.h
Normal file
41
src/gui/pxl8_gui_palette.h
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "pxl8_types.h"
|
||||||
|
|
||||||
|
#define PXL8_UI_PALETTE_SIZE 16
|
||||||
|
|
||||||
|
#define PXL8_UI_BG0 0
|
||||||
|
#define PXL8_UI_BG1 1
|
||||||
|
#define PXL8_UI_BG2 2
|
||||||
|
#define PXL8_UI_BG3 3
|
||||||
|
#define PXL8_UI_FG0 4
|
||||||
|
#define PXL8_UI_FG1 5
|
||||||
|
#define PXL8_UI_FG2 6
|
||||||
|
#define PXL8_UI_FG3 7
|
||||||
|
#define PXL8_UI_RED 8
|
||||||
|
#define PXL8_UI_GREEN 9
|
||||||
|
#define PXL8_UI_YELLOW 10
|
||||||
|
#define PXL8_UI_BLUE 11
|
||||||
|
#define PXL8_UI_PURPLE 12
|
||||||
|
#define PXL8_UI_AQUA 13
|
||||||
|
#define PXL8_UI_ORANGE 14
|
||||||
|
#define PXL8_UI_GRAY 15
|
||||||
|
|
||||||
|
static const u32 pxl8_ui_palette[PXL8_UI_PALETTE_SIZE] = {
|
||||||
|
0xFF282828,
|
||||||
|
0xFF3c3836,
|
||||||
|
0xFF504945,
|
||||||
|
0xFF665c54,
|
||||||
|
0xFFc7f1fb,
|
||||||
|
0xFFb2dbeb,
|
||||||
|
0xFFa1c4d5,
|
||||||
|
0xFF93aebd,
|
||||||
|
0xFF3449fb,
|
||||||
|
0xFF26bbb8,
|
||||||
|
0xFF2fbdfa,
|
||||||
|
0xFF98a583,
|
||||||
|
0xFF9b86d3,
|
||||||
|
0xFF7cc08e,
|
||||||
|
0xFF1980fe,
|
||||||
|
0xFF928374,
|
||||||
|
};
|
||||||
|
|
@ -11,8 +11,8 @@ typedef struct pxl8_hal {
|
||||||
void (*present)(void* platform_data);
|
void (*present)(void* platform_data);
|
||||||
void (*set_cursor)(void* platform_data, u32 cursor);
|
void (*set_cursor)(void* platform_data, u32 cursor);
|
||||||
void (*set_relative_mouse_mode)(void* platform_data, bool enabled);
|
void (*set_relative_mouse_mode)(void* platform_data, bool enabled);
|
||||||
void (*upload_texture)(void* platform_data, const u8* pixels, u32 w, u32 h,
|
void (*upload_texture)(void* platform_data, const void* pixels, u32 w, u32 h,
|
||||||
const u32* palette, u32 bpp);
|
u32 bpp, const u32* palette);
|
||||||
|
|
||||||
void* (*audio_create)(i32 sample_rate, i32 channels);
|
void* (*audio_create)(i32 sample_rate, i32 channels);
|
||||||
void (*audio_destroy)(void* audio_handle);
|
void (*audio_destroy)(void* audio_handle);
|
||||||
|
|
@ -81,7 +81,6 @@ static u64 sdl3_get_ticks(void) {
|
||||||
return SDL_GetTicksNS();
|
return SDL_GetTicksNS();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void sdl3_present(void* platform_data) {
|
static void sdl3_present(void* platform_data) {
|
||||||
if (!platform_data) return;
|
if (!platform_data) return;
|
||||||
|
|
||||||
|
|
@ -97,28 +96,34 @@ static void sdl3_present(void* platform_data) {
|
||||||
SDL_RenderPresent(ctx->renderer);
|
SDL_RenderPresent(ctx->renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sdl3_upload_texture(void* platform_data, const u8* pixels, u32 w, u32 h,
|
static void sdl3_upload_texture(void* platform_data, const void* pixels, u32 w, u32 h,
|
||||||
const u32* palette, u32 bpp) {
|
u32 bpp, const u32* palette) {
|
||||||
if (!platform_data || !pixels) return;
|
if (!platform_data || !pixels) return;
|
||||||
|
|
||||||
pxl8_sdl3_context* ctx = (pxl8_sdl3_context*)platform_data;
|
pxl8_sdl3_context* ctx = (pxl8_sdl3_context*)platform_data;
|
||||||
size_t needed_size = w * h;
|
size_t pixel_count = w * h;
|
||||||
|
|
||||||
if (ctx->rgba_buffer_size < needed_size) {
|
if (bpp == 4) {
|
||||||
u32* new_buffer = (u32*)SDL_realloc(ctx->rgba_buffer, needed_size * 4);
|
SDL_UpdateTexture(ctx->framebuffer, NULL, pixels, w * 4);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->rgba_buffer_size < pixel_count) {
|
||||||
|
u32* new_buffer = (u32*)SDL_realloc(ctx->rgba_buffer, pixel_count * 4);
|
||||||
if (!new_buffer) return;
|
if (!new_buffer) return;
|
||||||
ctx->rgba_buffer = new_buffer;
|
ctx->rgba_buffer = new_buffer;
|
||||||
ctx->rgba_buffer_size = needed_size;
|
ctx->rgba_buffer_size = pixel_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bpp == 2) {
|
if (bpp == 2) {
|
||||||
const u16* pixels16 = (const u16*)pixels;
|
const u16* pixels16 = (const u16*)pixels;
|
||||||
for (u32 i = 0; i < w * h; i++) {
|
for (u32 i = 0; i < pixel_count; i++) {
|
||||||
ctx->rgba_buffer[i] = pxl8_rgb565_to_rgba32(pixels16[i]);
|
ctx->rgba_buffer[i] = pxl8_rgb565_to_rgba32(pixels16[i]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (u32 i = 0; i < w * h; i++) {
|
const u8* pixels8 = (const u8*)pixels;
|
||||||
ctx->rgba_buffer[i] = palette[pixels[i]];
|
for (u32 i = 0; i < pixel_count; i++) {
|
||||||
|
ctx->rgba_buffer[i] = palette[pixels8[i]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
local anim = require("pxl8.anim")
|
local anim = require("pxl8.anim")
|
||||||
|
local bytes = require("pxl8.bytes")
|
||||||
local core = require("pxl8.core")
|
local core = require("pxl8.core")
|
||||||
local gfx2d = require("pxl8.gfx2d")
|
local gfx2d = require("pxl8.gfx2d")
|
||||||
local gfx3d = require("pxl8.gfx3d")
|
local gfx3d = require("pxl8.gfx3d")
|
||||||
local gui = require("pxl8.gui")
|
local gui = require("pxl8.gui")
|
||||||
local input = require("pxl8.input")
|
local input = require("pxl8.input")
|
||||||
local math3d = require("pxl8.math")
|
local math = require("pxl8.math")
|
||||||
|
local net = require("pxl8.net")
|
||||||
local particles = require("pxl8.particles")
|
local particles = require("pxl8.particles")
|
||||||
|
local procgen = require("pxl8.procgen")
|
||||||
local sfx = require("pxl8.sfx")
|
local sfx = require("pxl8.sfx")
|
||||||
local tilemap = require("pxl8.tilemap")
|
local tilemap = require("pxl8.tilemap")
|
||||||
local transition = require("pxl8.transition")
|
local transition = require("pxl8.transition")
|
||||||
|
|
@ -25,15 +28,20 @@ pxl8.debug = core.debug
|
||||||
pxl8.trace = core.trace
|
pxl8.trace = core.trace
|
||||||
pxl8.quit = core.quit
|
pxl8.quit = core.quit
|
||||||
|
|
||||||
pxl8.rng_seed = core.rng_seed
|
pxl8.hash32 = math.hash32
|
||||||
pxl8.rng_next = core.rng_next
|
|
||||||
pxl8.rng_f32 = core.rng_f32
|
pxl8.rng_f32 = core.rng_f32
|
||||||
|
pxl8.rng_next = core.rng_next
|
||||||
pxl8.rng_range = core.rng_range
|
pxl8.rng_range = core.rng_range
|
||||||
|
pxl8.rng_seed = core.rng_seed
|
||||||
|
|
||||||
pxl8.find_color = core.find_color
|
pxl8.find_color = core.find_color
|
||||||
pxl8.palette_color = core.palette_color
|
pxl8.palette_color = core.palette_color
|
||||||
pxl8.palette_index = core.palette_index
|
pxl8.palette_index = core.palette_index
|
||||||
pxl8.ramp_index = core.ramp_index
|
pxl8.ramp_index = core.ramp_index
|
||||||
|
pxl8.set_colormap = core.set_colormap
|
||||||
|
pxl8.set_palette = core.set_palette
|
||||||
|
pxl8.set_palette_rgb = core.set_palette_rgb
|
||||||
|
pxl8.update_palette_deps = core.update_palette_deps
|
||||||
|
|
||||||
pxl8.clear = gfx2d.clear
|
pxl8.clear = gfx2d.clear
|
||||||
pxl8.pixel = gfx2d.pixel
|
pxl8.pixel = gfx2d.pixel
|
||||||
|
|
@ -71,87 +79,96 @@ pxl8.center_cursor = input.center_cursor
|
||||||
pxl8.set_cursor = input.set_cursor
|
pxl8.set_cursor = input.set_cursor
|
||||||
pxl8.set_relative_mouse_mode = input.set_relative_mouse_mode
|
pxl8.set_relative_mouse_mode = input.set_relative_mouse_mode
|
||||||
|
|
||||||
pxl8.Particles = particles.Particles
|
pxl8.Anim = anim.Anim
|
||||||
pxl8.create_particles = function(max_count) return particles.Particles.new(max_count) end
|
pxl8.create_anim = anim.Anim.new
|
||||||
|
pxl8.create_anim_from_ase = anim.Anim.from_ase
|
||||||
|
|
||||||
pxl8.Tilesheet = tilemap.Tilesheet
|
pxl8.bounds = math.bounds
|
||||||
pxl8.Tilemap = tilemap.Tilemap
|
|
||||||
pxl8.create_tilesheet = function(tile_size) return tilemap.Tilesheet.new(tile_size) end
|
|
||||||
pxl8.create_tilemap = function(w, h, tile_size) return tilemap.Tilemap.new(w, h, tile_size) end
|
|
||||||
pxl8.TILE_FLIP_X = tilemap.TILE_FLIP_X
|
|
||||||
pxl8.TILE_FLIP_Y = tilemap.TILE_FLIP_Y
|
|
||||||
pxl8.TILE_SOLID = tilemap.TILE_SOLID
|
|
||||||
pxl8.TILE_TRIGGER = tilemap.TILE_TRIGGER
|
|
||||||
|
|
||||||
pxl8.Camera3D = gfx3d.Camera3D
|
pxl8.Camera3D = gfx3d.Camera3D
|
||||||
pxl8.create_camera_3d = function() return gfx3d.Camera3D.new() end
|
pxl8.create_camera_3d = gfx3d.Camera3D.new
|
||||||
pxl8.begin_frame_3d = gfx3d.begin_frame
|
pxl8.begin_frame_3d = gfx3d.begin_frame
|
||||||
pxl8.end_frame_3d = gfx3d.end_frame
|
|
||||||
pxl8.clear_3d = gfx3d.clear
|
pxl8.clear_3d = gfx3d.clear
|
||||||
pxl8.clear_depth = gfx3d.clear_depth
|
pxl8.clear_depth = gfx3d.clear_depth
|
||||||
pxl8.draw_line_3d = gfx3d.draw_line
|
pxl8.draw_line_3d = gfx3d.draw_line
|
||||||
pxl8.Mesh = gfx3d.Mesh
|
|
||||||
pxl8.create_mesh = function(vertices, indices) return gfx3d.Mesh.new(vertices, indices) end
|
|
||||||
pxl8.draw_mesh = gfx3d.draw_mesh
|
pxl8.draw_mesh = gfx3d.draw_mesh
|
||||||
|
pxl8.end_frame_3d = gfx3d.end_frame
|
||||||
|
pxl8.Mesh = gfx3d.Mesh
|
||||||
|
pxl8.create_mesh = gfx3d.Mesh.new
|
||||||
|
|
||||||
pxl8.mat4_identity = math3d.mat4_identity
|
pxl8.Compressor = sfx.Compressor
|
||||||
pxl8.mat4_multiply = math3d.mat4_multiply
|
pxl8.create_compressor = sfx.Compressor.new
|
||||||
pxl8.mat4_translate = math3d.mat4_translate
|
pxl8.Delay = sfx.Delay
|
||||||
pxl8.mat4_rotate_x = math3d.mat4_rotate_x
|
pxl8.create_delay = sfx.Delay.new
|
||||||
pxl8.mat4_rotate_y = math3d.mat4_rotate_y
|
pxl8.Reverb = sfx.Reverb
|
||||||
pxl8.mat4_rotate_z = math3d.mat4_rotate_z
|
pxl8.create_reverb = sfx.Reverb.new
|
||||||
pxl8.mat4_scale = math3d.mat4_scale
|
|
||||||
pxl8.mat4_ortho = math3d.mat4_ortho
|
|
||||||
pxl8.mat4_perspective = math3d.mat4_perspective
|
|
||||||
pxl8.mat4_lookat = math3d.mat4_lookat
|
|
||||||
pxl8.bounds = math3d.bounds
|
|
||||||
|
|
||||||
pxl8.Gui = gui.Gui
|
pxl8.Gui = gui.Gui
|
||||||
pxl8.create_gui = function() return gui.Gui.new() end
|
pxl8.create_gui = gui.Gui.new
|
||||||
pxl8.gui_label = gui.label
|
pxl8.gui_label = gui.label
|
||||||
pxl8.gui_window = gui.window
|
pxl8.gui_window = gui.window
|
||||||
|
|
||||||
pxl8.World = world.World
|
pxl8.mat4_identity = math.mat4_identity
|
||||||
pxl8.create_world = function() return world.World.new() end
|
pxl8.mat4_lookat = math.mat4_lookat
|
||||||
pxl8.procgen_tex = world.procgen_tex
|
pxl8.mat4_multiply = math.mat4_multiply
|
||||||
|
pxl8.mat4_ortho = math.mat4_ortho
|
||||||
|
pxl8.mat4_perspective = math.mat4_perspective
|
||||||
|
pxl8.mat4_rotate_x = math.mat4_rotate_x
|
||||||
|
pxl8.mat4_rotate_y = math.mat4_rotate_y
|
||||||
|
pxl8.mat4_rotate_z = math.mat4_rotate_z
|
||||||
|
pxl8.mat4_scale = math.mat4_scale
|
||||||
|
pxl8.mat4_translate = math.mat4_translate
|
||||||
|
|
||||||
|
pxl8.Net = net.Net
|
||||||
|
pxl8.create_net = net.Net.new
|
||||||
|
pxl8.NET_MODE_LOCAL = net.MODE_LOCAL
|
||||||
|
pxl8.NET_MODE_REMOTE = net.MODE_REMOTE
|
||||||
|
|
||||||
|
pxl8.pack_f32_be = bytes.pack_f32_be
|
||||||
|
pxl8.pack_f32_le = bytes.pack_f32_le
|
||||||
|
pxl8.pack_f64_be = bytes.pack_f64_be
|
||||||
|
pxl8.pack_f64_le = bytes.pack_f64_le
|
||||||
|
pxl8.pack_i8 = bytes.pack_i8
|
||||||
|
pxl8.pack_i16_be = bytes.pack_i16_be
|
||||||
|
pxl8.pack_i16_le = bytes.pack_i16_le
|
||||||
|
pxl8.pack_i32_be = bytes.pack_i32_be
|
||||||
|
pxl8.pack_i32_le = bytes.pack_i32_le
|
||||||
|
pxl8.pack_i64_be = bytes.pack_i64_be
|
||||||
|
pxl8.pack_i64_le = bytes.pack_i64_le
|
||||||
|
pxl8.pack_u8 = bytes.pack_u8
|
||||||
|
pxl8.pack_u16_be = bytes.pack_u16_be
|
||||||
|
pxl8.pack_u16_le = bytes.pack_u16_le
|
||||||
|
pxl8.pack_u32_be = bytes.pack_u32_be
|
||||||
|
pxl8.pack_u32_le = bytes.pack_u32_le
|
||||||
|
pxl8.pack_u64_be = bytes.pack_u64_be
|
||||||
|
pxl8.pack_u64_le = bytes.pack_u64_le
|
||||||
|
|
||||||
|
pxl8.Particles = particles.Particles
|
||||||
|
pxl8.create_particles = particles.Particles.new
|
||||||
|
|
||||||
|
pxl8.Graph = procgen.Graph
|
||||||
|
pxl8.create_graph = procgen.create_graph
|
||||||
pxl8.PROCGEN_ROOMS = world.PROCGEN_ROOMS
|
pxl8.PROCGEN_ROOMS = world.PROCGEN_ROOMS
|
||||||
pxl8.PROCGEN_TERRAIN = world.PROCGEN_TERRAIN
|
pxl8.PROCGEN_TERRAIN = world.PROCGEN_TERRAIN
|
||||||
|
|
||||||
pxl8.Transition = transition.Transition
|
|
||||||
pxl8.create_transition = function(type_name, duration) return transition.Transition.new(type_name, duration) end
|
|
||||||
pxl8.TRANSITION_TYPES = transition.TYPES
|
|
||||||
|
|
||||||
pxl8.Anim = anim.Anim
|
|
||||||
pxl8.create_anim = function(frame_ids, frame_durations) return anim.Anim.new(frame_ids, frame_durations) end
|
|
||||||
pxl8.create_anim_from_ase = function(filepath) return anim.Anim.from_ase(filepath) end
|
|
||||||
|
|
||||||
pxl8.SfxContext = sfx.SfxContext
|
pxl8.SfxContext = sfx.SfxContext
|
||||||
pxl8.SfxNode = sfx.SfxNode
|
pxl8.SfxNode = sfx.SfxNode
|
||||||
pxl8.Compressor = sfx.Compressor
|
pxl8.create_sfx_context = sfx.SfxContext.new
|
||||||
pxl8.Delay = sfx.Delay
|
|
||||||
pxl8.Reverb = sfx.Reverb
|
|
||||||
pxl8.create_sfx_context = function() return sfx.SfxContext.new() end
|
|
||||||
pxl8.create_compressor = function(opts) return sfx.Compressor.new(opts) end
|
|
||||||
pxl8.create_delay = function(opts) return sfx.Delay.new(opts) end
|
|
||||||
pxl8.create_reverb = function(opts) return sfx.Reverb.new(opts) end
|
|
||||||
pxl8.sfx_get_master_volume = sfx.get_master_volume
|
pxl8.sfx_get_master_volume = sfx.get_master_volume
|
||||||
pxl8.sfx_set_master_volume = sfx.set_master_volume
|
|
||||||
pxl8.sfx_note_to_freq = sfx.note_to_freq
|
pxl8.sfx_note_to_freq = sfx.note_to_freq
|
||||||
|
pxl8.sfx_set_master_volume = sfx.set_master_volume
|
||||||
pxl8.sfx_voice_params = sfx.voice_params
|
pxl8.sfx_voice_params = sfx.voice_params
|
||||||
|
|
||||||
pxl8.SFX_FILTER_BANDPASS = sfx.FILTER_BANDPASS
|
pxl8.SFX_FILTER_BANDPASS = sfx.FILTER_BANDPASS
|
||||||
pxl8.SFX_FILTER_HIGHPASS = sfx.FILTER_HIGHPASS
|
pxl8.SFX_FILTER_HIGHPASS = sfx.FILTER_HIGHPASS
|
||||||
pxl8.SFX_FILTER_LOWPASS = sfx.FILTER_LOWPASS
|
pxl8.SFX_FILTER_LOWPASS = sfx.FILTER_LOWPASS
|
||||||
pxl8.SFX_FILTER_NONE = sfx.FILTER_NONE
|
pxl8.SFX_FILTER_NONE = sfx.FILTER_NONE
|
||||||
|
|
||||||
pxl8.SFX_LFO_AMPLITUDE = sfx.LFO_AMPLITUDE
|
pxl8.SFX_LFO_AMPLITUDE = sfx.LFO_AMPLITUDE
|
||||||
pxl8.SFX_LFO_FILTER = sfx.LFO_FILTER
|
pxl8.SFX_LFO_FILTER = sfx.LFO_FILTER
|
||||||
pxl8.SFX_LFO_PITCH = sfx.LFO_PITCH
|
pxl8.SFX_LFO_PITCH = sfx.LFO_PITCH
|
||||||
|
|
||||||
pxl8.SFX_NODE_COMPRESSOR = sfx.NODE_COMPRESSOR
|
pxl8.SFX_NODE_COMPRESSOR = sfx.NODE_COMPRESSOR
|
||||||
pxl8.SFX_NODE_DELAY = sfx.NODE_DELAY
|
pxl8.SFX_NODE_DELAY = sfx.NODE_DELAY
|
||||||
pxl8.SFX_NODE_REVERB = sfx.NODE_REVERB
|
pxl8.SFX_NODE_REVERB = sfx.NODE_REVERB
|
||||||
|
|
||||||
pxl8.SFX_WAVE_NOISE = sfx.WAVE_NOISE
|
pxl8.SFX_WAVE_NOISE = sfx.WAVE_NOISE
|
||||||
pxl8.SFX_WAVE_PULSE = sfx.WAVE_PULSE
|
pxl8.SFX_WAVE_PULSE = sfx.WAVE_PULSE
|
||||||
pxl8.SFX_WAVE_SAW = sfx.WAVE_SAW
|
pxl8.SFX_WAVE_SAW = sfx.WAVE_SAW
|
||||||
|
|
@ -159,4 +176,39 @@ pxl8.SFX_WAVE_SINE = sfx.WAVE_SINE
|
||||||
pxl8.SFX_WAVE_SQUARE = sfx.WAVE_SQUARE
|
pxl8.SFX_WAVE_SQUARE = sfx.WAVE_SQUARE
|
||||||
pxl8.SFX_WAVE_TRIANGLE = sfx.WAVE_TRIANGLE
|
pxl8.SFX_WAVE_TRIANGLE = sfx.WAVE_TRIANGLE
|
||||||
|
|
||||||
|
pxl8.TILE_FLIP_X = tilemap.TILE_FLIP_X
|
||||||
|
pxl8.TILE_FLIP_Y = tilemap.TILE_FLIP_Y
|
||||||
|
pxl8.TILE_SOLID = tilemap.TILE_SOLID
|
||||||
|
pxl8.TILE_TRIGGER = tilemap.TILE_TRIGGER
|
||||||
|
pxl8.Tilemap = tilemap.Tilemap
|
||||||
|
pxl8.create_tilemap = tilemap.Tilemap.new
|
||||||
|
pxl8.Tilesheet = tilemap.Tilesheet
|
||||||
|
pxl8.create_tilesheet = tilemap.Tilesheet.new
|
||||||
|
|
||||||
|
pxl8.Transition = transition.Transition
|
||||||
|
pxl8.create_transition = transition.Transition.new
|
||||||
|
pxl8.TRANSITION_TYPES = transition.TYPES
|
||||||
|
|
||||||
|
pxl8.unpack_f32_be = bytes.unpack_f32_be
|
||||||
|
pxl8.unpack_f32_le = bytes.unpack_f32_le
|
||||||
|
pxl8.unpack_f64_be = bytes.unpack_f64_be
|
||||||
|
pxl8.unpack_f64_le = bytes.unpack_f64_le
|
||||||
|
pxl8.unpack_i8 = bytes.unpack_i8
|
||||||
|
pxl8.unpack_i16_be = bytes.unpack_i16_be
|
||||||
|
pxl8.unpack_i16_le = bytes.unpack_i16_le
|
||||||
|
pxl8.unpack_i32_be = bytes.unpack_i32_be
|
||||||
|
pxl8.unpack_i32_le = bytes.unpack_i32_le
|
||||||
|
pxl8.unpack_i64_be = bytes.unpack_i64_be
|
||||||
|
pxl8.unpack_i64_le = bytes.unpack_i64_le
|
||||||
|
pxl8.unpack_u8 = bytes.unpack_u8
|
||||||
|
pxl8.unpack_u16_be = bytes.unpack_u16_be
|
||||||
|
pxl8.unpack_u16_le = bytes.unpack_u16_le
|
||||||
|
pxl8.unpack_u32_be = bytes.unpack_u32_be
|
||||||
|
pxl8.unpack_u32_le = bytes.unpack_u32_le
|
||||||
|
pxl8.unpack_u64_be = bytes.unpack_u64_be
|
||||||
|
pxl8.unpack_u64_le = bytes.unpack_u64_le
|
||||||
|
|
||||||
|
pxl8.World = world.World
|
||||||
|
pxl8.create_world = world.World.new
|
||||||
|
|
||||||
return pxl8
|
return pxl8
|
||||||
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