refactor: reorganize pxl8 into client/src/ module structure
- core/: main entry, types, logging, I/O, RNG - asset/: ase loader, cart, save, embed - gfx/: graphics, animation, atlas, fonts, tilemap, transitions - sfx/: audio - script/: lua/fennel runtime, REPL - hal/: platform abstraction (SDL3) - world/: BSP, world, procedural gen - math/: math utilities - game/: GUI, replay - lua/: Lua API modules
This commit is contained in:
parent
272e0bc615
commit
39b604b333
106 changed files with 6078 additions and 3715 deletions
23
client/src/hal/pxl8_hal.h
Normal file
23
client/src/hal/pxl8_hal.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_types.h"
|
||||
|
||||
typedef struct pxl8_hal {
|
||||
void* (*create)(i32 render_w, i32 render_h,
|
||||
const char* title, i32 win_w, i32 win_h);
|
||||
void (*destroy)(void* platform_data);
|
||||
u64 (*get_ticks)(void);
|
||||
void (*center_cursor)(void* platform_data);
|
||||
void (*present)(void* platform_data);
|
||||
void (*set_cursor)(void* platform_data, u32 cursor);
|
||||
void (*set_relative_mouse_mode)(void* platform_data, bool enabled);
|
||||
void (*upload_texture)(void* platform_data, const u8* pixels, u32 w, u32 h,
|
||||
const u32* palette, u32 bpp);
|
||||
|
||||
void* (*audio_create)(i32 sample_rate, i32 channels);
|
||||
void (*audio_destroy)(void* audio_handle);
|
||||
void (*audio_start)(void* audio_handle);
|
||||
void (*audio_stop)(void* audio_handle);
|
||||
bool (*upload_audio)(void* audio_handle, const f32* stereo_samples, i32 sample_count);
|
||||
i32 (*audio_queued)(void* audio_handle);
|
||||
} pxl8_hal;
|
||||
405
client/src/hal/pxl8_sdl3.c
Normal file
405
client/src/hal/pxl8_sdl3.c
Normal file
|
|
@ -0,0 +1,405 @@
|
|||
#include "pxl8_sdl3.h"
|
||||
|
||||
#define SDL_MAIN_USE_CALLBACKS
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_main.h>
|
||||
|
||||
#include "pxl8_color.h"
|
||||
#include "pxl8_log.h"
|
||||
#include "pxl8_sys.h"
|
||||
|
||||
typedef struct pxl8_sdl3_context {
|
||||
SDL_Texture* framebuffer;
|
||||
SDL_Renderer* renderer;
|
||||
SDL_Window* window;
|
||||
|
||||
u32* rgba_buffer;
|
||||
size_t rgba_buffer_size;
|
||||
} pxl8_sdl3_context;
|
||||
|
||||
static void* sdl3_create(i32 render_w, i32 render_h,
|
||||
const char* title, i32 win_w, i32 win_h) {
|
||||
pxl8_sdl3_context* ctx = (pxl8_sdl3_context*)SDL_calloc(1, sizeof(pxl8_sdl3_context));
|
||||
if (!ctx) {
|
||||
pxl8_error("Failed to allocate SDL3 context");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ctx->window = SDL_CreateWindow(title, win_w, win_h, SDL_WINDOW_RESIZABLE);
|
||||
if (!ctx->window) {
|
||||
pxl8_error("Failed to create window: %s", SDL_GetError());
|
||||
SDL_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ctx->renderer = SDL_CreateRenderer(ctx->window, NULL);
|
||||
if (!ctx->renderer) {
|
||||
pxl8_error("Failed to create renderer: %s", SDL_GetError());
|
||||
SDL_DestroyWindow(ctx->window);
|
||||
SDL_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!SDL_SetRenderVSync(ctx->renderer, 1)) {
|
||||
pxl8_error("Failed to set vsync: %s", SDL_GetError());
|
||||
}
|
||||
|
||||
ctx->framebuffer = SDL_CreateTexture(ctx->renderer,
|
||||
SDL_PIXELFORMAT_RGBA32,
|
||||
SDL_TEXTUREACCESS_STREAMING,
|
||||
render_w, render_h);
|
||||
if (!ctx->framebuffer) {
|
||||
pxl8_error("Failed to create framebuffer texture: %s", SDL_GetError());
|
||||
SDL_DestroyRenderer(ctx->renderer);
|
||||
SDL_DestroyWindow(ctx->window);
|
||||
SDL_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SDL_SetTextureScaleMode(ctx->framebuffer, SDL_SCALEMODE_NEAREST);
|
||||
|
||||
ctx->rgba_buffer = NULL;
|
||||
ctx->rgba_buffer_size = 0;
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
static void sdl3_destroy(void* platform_data) {
|
||||
if (!platform_data) return;
|
||||
|
||||
pxl8_sdl3_context* ctx = (pxl8_sdl3_context*)platform_data;
|
||||
|
||||
if (ctx->rgba_buffer) SDL_free(ctx->rgba_buffer);
|
||||
if (ctx->framebuffer) SDL_DestroyTexture(ctx->framebuffer);
|
||||
if (ctx->renderer) SDL_DestroyRenderer(ctx->renderer);
|
||||
if (ctx->window) SDL_DestroyWindow(ctx->window);
|
||||
|
||||
SDL_free(ctx);
|
||||
}
|
||||
|
||||
static u64 sdl3_get_ticks(void) {
|
||||
return SDL_GetTicksNS();
|
||||
}
|
||||
|
||||
|
||||
static void sdl3_present(void* platform_data) {
|
||||
if (!platform_data) return;
|
||||
|
||||
pxl8_sdl3_context* ctx = (pxl8_sdl3_context*)platform_data;
|
||||
|
||||
SDL_SetRenderDrawColor(ctx->renderer, 0, 0, 0, 255);
|
||||
SDL_RenderClear(ctx->renderer);
|
||||
|
||||
if (ctx->framebuffer) {
|
||||
SDL_RenderTexture(ctx->renderer, ctx->framebuffer, NULL, NULL);
|
||||
}
|
||||
|
||||
SDL_RenderPresent(ctx->renderer);
|
||||
}
|
||||
|
||||
static void sdl3_upload_texture(void* platform_data, const u8* pixels, u32 w, u32 h,
|
||||
const u32* palette, u32 bpp) {
|
||||
if (!platform_data || !pixels) return;
|
||||
|
||||
pxl8_sdl3_context* ctx = (pxl8_sdl3_context*)platform_data;
|
||||
size_t needed_size = w * h;
|
||||
|
||||
if (ctx->rgba_buffer_size < needed_size) {
|
||||
u32* new_buffer = (u32*)SDL_realloc(ctx->rgba_buffer, needed_size * 4);
|
||||
if (!new_buffer) return;
|
||||
ctx->rgba_buffer = new_buffer;
|
||||
ctx->rgba_buffer_size = needed_size;
|
||||
}
|
||||
|
||||
if (bpp == 2) {
|
||||
const u16* pixels16 = (const u16*)pixels;
|
||||
for (u32 i = 0; i < w * h; i++) {
|
||||
ctx->rgba_buffer[i] = pxl8_rgb565_to_rgba32(pixels16[i]);
|
||||
}
|
||||
} else {
|
||||
for (u32 i = 0; i < w * h; i++) {
|
||||
ctx->rgba_buffer[i] = palette[pixels[i]];
|
||||
}
|
||||
}
|
||||
|
||||
SDL_UpdateTexture(ctx->framebuffer, NULL, ctx->rgba_buffer, w * 4);
|
||||
}
|
||||
|
||||
SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) {
|
||||
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_AUDIO)) {
|
||||
pxl8_error("SDL_Init failed: %s", SDL_GetError());
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
pxl8* sys = pxl8_create(&pxl8_hal_sdl3);
|
||||
if (!sys) {
|
||||
pxl8_error("Failed to create pxl8 system");
|
||||
SDL_Quit();
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
pxl8_result result = pxl8_init(sys, argc, argv);
|
||||
if (result != PXL8_OK) {
|
||||
pxl8_destroy(sys);
|
||||
SDL_Quit();
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
*appstate = sys;
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
|
||||
SDL_AppResult SDL_AppIterate(void* appstate) {
|
||||
pxl8* sys = (pxl8*)appstate;
|
||||
if (!sys) {
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
pxl8_result update_result = pxl8_update(sys);
|
||||
if (update_result != PXL8_OK) {
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
pxl8_result frame_result = pxl8_frame(sys);
|
||||
if (frame_result != PXL8_OK) {
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
return pxl8_is_running(sys) ? SDL_APP_CONTINUE : SDL_APP_SUCCESS;
|
||||
}
|
||||
|
||||
SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) {
|
||||
pxl8* sys = (pxl8*)appstate;
|
||||
if (!sys) {
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
|
||||
pxl8_input_state* input = pxl8_get_input(sys);
|
||||
if (!input) {
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
|
||||
switch (event->type) {
|
||||
case SDL_EVENT_QUIT:
|
||||
pxl8_set_running(sys, false);
|
||||
break;
|
||||
|
||||
case SDL_EVENT_KEY_DOWN: {
|
||||
SDL_Scancode scancode = event->key.scancode;
|
||||
if (scancode < PXL8_MAX_KEYS) {
|
||||
if (!input->keys_down[scancode]) {
|
||||
input->keys_pressed[scancode] = true;
|
||||
}
|
||||
input->keys_down[scancode] = true;
|
||||
input->keys_released[scancode] = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_EVENT_KEY_UP: {
|
||||
SDL_Scancode scancode = event->key.scancode;
|
||||
if (scancode < PXL8_MAX_KEYS) {
|
||||
input->keys_down[scancode] = false;
|
||||
input->keys_pressed[scancode] = false;
|
||||
input->keys_released[scancode] = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_EVENT_MOUSE_BUTTON_DOWN: {
|
||||
u8 button = event->button.button - 1;
|
||||
if (button < 3) {
|
||||
if (!input->mouse_buttons_down[button]) {
|
||||
input->mouse_buttons_pressed[button] = true;
|
||||
}
|
||||
input->mouse_buttons_down[button] = true;
|
||||
input->mouse_buttons_released[button] = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_EVENT_MOUSE_BUTTON_UP: {
|
||||
u8 button = event->button.button - 1;
|
||||
if (button < 3) {
|
||||
input->mouse_buttons_down[button] = false;
|
||||
input->mouse_buttons_pressed[button] = false;
|
||||
input->mouse_buttons_released[button] = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_EVENT_MOUSE_MOTION: {
|
||||
pxl8_gfx* gfx = pxl8_get_gfx(sys);
|
||||
if (!gfx) break;
|
||||
|
||||
input->mouse_dx = (i32)event->motion.xrel;
|
||||
input->mouse_dy = (i32)event->motion.yrel;
|
||||
|
||||
i32 window_mouse_x = (i32)event->motion.x;
|
||||
i32 window_mouse_y = (i32)event->motion.y;
|
||||
|
||||
SDL_Window* window = SDL_GetWindowFromID(event->motion.windowID);
|
||||
if (!window) break;
|
||||
|
||||
i32 window_width, window_height;
|
||||
SDL_GetWindowSize(window, &window_width, &window_height);
|
||||
|
||||
i32 render_w = pxl8_gfx_get_width(gfx);
|
||||
i32 render_h = pxl8_gfx_get_height(gfx);
|
||||
|
||||
pxl8_bounds window_bounds = {0, 0, window_width, window_height};
|
||||
pxl8_viewport vp = pxl8_gfx_viewport(window_bounds, render_w, render_h);
|
||||
|
||||
input->mouse_x = (i32)((window_mouse_x - vp.offset_x) / vp.scale);
|
||||
input->mouse_y = (i32)((window_mouse_y - vp.offset_y) / vp.scale);
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_EVENT_MOUSE_WHEEL: {
|
||||
input->mouse_wheel_x = (i32)event->wheel.x;
|
||||
input->mouse_wheel_y = (i32)event->wheel.y;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
|
||||
void SDL_AppQuit(void* appstate, SDL_AppResult result) {
|
||||
(void)result;
|
||||
|
||||
pxl8* sys = (pxl8*)appstate;
|
||||
if (sys) {
|
||||
pxl8_quit(sys);
|
||||
pxl8_destroy(sys);
|
||||
}
|
||||
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
static void sdl3_set_relative_mouse_mode(void* platform_data, bool enabled) {
|
||||
if (!platform_data) return;
|
||||
|
||||
pxl8_sdl3_context* ctx = (pxl8_sdl3_context*)platform_data;
|
||||
if (!SDL_SetWindowRelativeMouseMode(ctx->window, enabled)) {
|
||||
pxl8_error("Failed to set relative mouse mode: %s", SDL_GetError());
|
||||
}
|
||||
}
|
||||
|
||||
static void sdl3_set_cursor(void* platform_data, u32 cursor) {
|
||||
if (!platform_data) return;
|
||||
|
||||
SDL_SystemCursor sdl_cursor;
|
||||
switch (cursor) {
|
||||
case PXL8_CURSOR_ARROW:
|
||||
sdl_cursor = SDL_SYSTEM_CURSOR_DEFAULT;
|
||||
break;
|
||||
case PXL8_CURSOR_HAND:
|
||||
sdl_cursor = SDL_SYSTEM_CURSOR_POINTER;
|
||||
break;
|
||||
default:
|
||||
sdl_cursor = SDL_SYSTEM_CURSOR_DEFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
SDL_Cursor* cursor_obj = SDL_CreateSystemCursor(sdl_cursor);
|
||||
if (cursor_obj) {
|
||||
SDL_SetCursor(cursor_obj);
|
||||
}
|
||||
}
|
||||
|
||||
static void sdl3_center_cursor(void* platform_data) {
|
||||
if (!platform_data) return;
|
||||
|
||||
pxl8_sdl3_context* ctx = (pxl8_sdl3_context*)platform_data;
|
||||
i32 w, h;
|
||||
if (SDL_GetWindowSize(ctx->window, &w, &h)) {
|
||||
SDL_WarpMouseInWindow(ctx->window, w / 2, h / 2);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct pxl8_sdl3_audio {
|
||||
SDL_AudioStream* stream;
|
||||
i32 sample_rate;
|
||||
i32 channels;
|
||||
} pxl8_sdl3_audio;
|
||||
|
||||
static void* sdl3_audio_create(i32 sample_rate, i32 channels) {
|
||||
pxl8_sdl3_audio* audio = (pxl8_sdl3_audio*)SDL_calloc(1, sizeof(pxl8_sdl3_audio));
|
||||
if (!audio) return NULL;
|
||||
|
||||
SDL_AudioSpec spec = {
|
||||
.freq = sample_rate,
|
||||
.channels = channels,
|
||||
.format = SDL_AUDIO_F32
|
||||
};
|
||||
|
||||
audio->stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, NULL, NULL);
|
||||
if (!audio->stream) {
|
||||
pxl8_error("Failed to open audio device: %s", SDL_GetError());
|
||||
SDL_free(audio);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
audio->sample_rate = sample_rate;
|
||||
audio->channels = channels;
|
||||
|
||||
return audio;
|
||||
}
|
||||
|
||||
static void sdl3_audio_destroy(void* audio_handle) {
|
||||
if (!audio_handle) return;
|
||||
|
||||
pxl8_sdl3_audio* audio = (pxl8_sdl3_audio*)audio_handle;
|
||||
if (audio->stream) {
|
||||
SDL_DestroyAudioStream(audio->stream);
|
||||
}
|
||||
SDL_free(audio);
|
||||
}
|
||||
|
||||
static void sdl3_audio_start(void* audio_handle) {
|
||||
if (!audio_handle) return;
|
||||
|
||||
pxl8_sdl3_audio* audio = (pxl8_sdl3_audio*)audio_handle;
|
||||
SDL_ResumeAudioStreamDevice(audio->stream);
|
||||
}
|
||||
|
||||
static void sdl3_audio_stop(void* audio_handle) {
|
||||
if (!audio_handle) return;
|
||||
|
||||
pxl8_sdl3_audio* audio = (pxl8_sdl3_audio*)audio_handle;
|
||||
SDL_PauseAudioStreamDevice(audio->stream);
|
||||
}
|
||||
|
||||
static bool sdl3_upload_audio(void* audio_handle, const f32* stereo_samples, i32 sample_count) {
|
||||
if (!audio_handle || !stereo_samples) return false;
|
||||
|
||||
pxl8_sdl3_audio* audio = (pxl8_sdl3_audio*)audio_handle;
|
||||
return SDL_PutAudioStreamData(audio->stream, stereo_samples,
|
||||
sample_count * audio->channels * sizeof(f32));
|
||||
}
|
||||
|
||||
static i32 sdl3_audio_queued(void* audio_handle) {
|
||||
if (!audio_handle) return 0;
|
||||
|
||||
pxl8_sdl3_audio* audio = (pxl8_sdl3_audio*)audio_handle;
|
||||
i32 bytes = SDL_GetAudioStreamQueued(audio->stream);
|
||||
return bytes / (audio->channels * sizeof(f32));
|
||||
}
|
||||
|
||||
const pxl8_hal pxl8_hal_sdl3 = {
|
||||
.create = sdl3_create,
|
||||
.destroy = sdl3_destroy,
|
||||
.get_ticks = sdl3_get_ticks,
|
||||
.center_cursor = sdl3_center_cursor,
|
||||
.present = sdl3_present,
|
||||
.set_cursor = sdl3_set_cursor,
|
||||
.set_relative_mouse_mode = sdl3_set_relative_mouse_mode,
|
||||
.upload_texture = sdl3_upload_texture,
|
||||
.audio_create = sdl3_audio_create,
|
||||
.audio_destroy = sdl3_audio_destroy,
|
||||
.audio_start = sdl3_audio_start,
|
||||
.audio_stop = sdl3_audio_stop,
|
||||
.upload_audio = sdl3_upload_audio,
|
||||
.audio_queued = sdl3_audio_queued,
|
||||
};
|
||||
5
client/src/hal/pxl8_sdl3.h
Normal file
5
client/src/hal/pxl8_sdl3.h
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_hal.h"
|
||||
|
||||
extern const pxl8_hal pxl8_hal_sdl3;
|
||||
Loading…
Add table
Add a link
Reference in a new issue