From 90fb30cf3a92690178e5ad39095f604c2bc50afa Mon Sep 17 00:00:00 2001 From: pryazha Date: Fri, 6 Jun 2025 10:30:01 +0500 Subject: trying to add windows support, also a lot of changes, some ui modifications --- src/linux.c | 348 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 348 insertions(+) create mode 100644 src/linux.c (limited to 'src/linux.c') diff --git a/src/linux.c b/src/linux.c new file mode 100644 index 0000000..474d430 --- /dev/null +++ b/src/linux.c @@ -0,0 +1,348 @@ +#include "GL/glew.h" +#include "SDL3/SDL.h" + +#include "prge.h" + +#include "crrn.h" +#include "crrn.c" + +typedef struct { + SDL_AudioSpec spec; + SDL_AudioStream *stream; +} SDLSound; + +typedef struct { + SDLSound sdlsounds[MAX_SOUNDS_PLAYING]; + SDL_AudioDeviceID audio_device; + SDL_Window *window; + SDL_GLContext glctx; +} SDLContext; + +static SDLContext sdlctx; + +i32 init_sdl(prge_window_t *window) +{ + if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)) { + printf("error: sdl init: %s\n", SDL_GetError()); + return 0; + } + + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + if (window->flags & WINDOW_MULTISAMPLE) + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); + u32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE; + sdlctx.window = SDL_CreateWindow(window->name, window->width, window->height, flags); + if (!sdlctx.window) { + printf("error: sdl create window: %s\n", SDL_GetError()); + return 0; + } + SDL_GetWindowSize(sdlctx.window, &window->width, &window->height); + + if (!(sdlctx.glctx = SDL_GL_CreateContext(sdlctx.window))) { + printf("error: sdl create gl context: %s\n", SDL_GetError()); + return 0; + } + if (!SDL_GL_MakeCurrent(sdlctx.window, sdlctx.glctx)) { + printf("error: sdl make context current: %s\n", SDL_GetError()); + return 0; + } + + if (SDL_GL_SetSwapInterval(0)) + printf("info: vsync disabled\n"); + else + printf("warning: failed to disable vsync\n"); + + u32 error = glewInit(); + if (error != GLEW_OK) { + printf("error: glew: %s\n", glewGetErrorString(error)); + return 0; + } + if (window->flags & WINDOW_DEPTH_TEST) + glEnable(GL_DEPTH_TEST); + if (window->flags & WINDOW_MULTISAMPLE) + glEnable(GL_MULTISAMPLE); + + sdlctx.audio_device = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, 0); + if (!sdlctx.audio_device) { + printf("error: sdl: couldn't open audio device\n"); + return 0; + } + + return 1; +} + +void process_mouse_pos(input_t *input, SDL_MouseMotionEvent *event, prge_window_t window) +{ + input->mouse.pos = (v2){(f32)event->x, window.height - (f32)event->y}; + if (input->mouse.first) + input->mouse.first = 0; + v2 offset = {(f32)event->xrel, (f32)event->yrel}; + input->mouse.offset = v2_add(input->mouse.offset, offset); +} + +void process_key(i32 down, prge_key_t *key) +{ + key->state = (down ? key_state_press : key_state_release); +} + +void process_keyboard(input_t *input, SDL_KeyboardEvent *keyev) +{ + switch (keyev->scancode) { + case SDL_SCANCODE_D: + process_key(keyev->down, &input->move_right); + break; + case SDL_SCANCODE_W: + process_key(keyev->down, &input->move_forward); + break; + case SDL_SCANCODE_A: + process_key(keyev->down, &input->move_left); + break; + case SDL_SCANCODE_S: + process_key(keyev->down, &input->move_backward); + break; + case SDL_SCANCODE_E: + process_key(keyev->down, &input->move_up); + break; + case SDL_SCANCODE_Q: + process_key(keyev->down, &input->move_down); + break; + case SDL_SCANCODE_SPACE: + process_key(keyev->down, &input->jump); + break; + case SDL_SCANCODE_RIGHT: + process_key(keyev->down, &input->action_right); + break; + case SDL_SCANCODE_UP: + process_key(keyev->down, &input->action_up); + break; + case SDL_SCANCODE_LEFT: + process_key(keyev->down, &input->action_left); + break; + case SDL_SCANCODE_DOWN: + process_key(keyev->down, &input->action_down); + break; + case SDL_SCANCODE_ESCAPE: + process_key(keyev->down, &input->exit); + break; + default: + break; + } +} + +void process_mouse_buttons(input_t *input, SDL_MouseButtonEvent *buttonev) +{ + if (buttonev->button == SDL_BUTTON_LEFT) + process_key(buttonev->down, &input->mouse.left); + else if (buttonev->button == SDL_BUTTON_RIGHT) + process_key(buttonev->down, &input->mouse.right); +} + +void handle_events(prge_context_t *ctx) +{ + SDL_Event event; + + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_EVENT_KEY_DOWN: + case SDL_EVENT_KEY_UP: + process_keyboard(&ctx->input, &event.key); + break; + case SDL_EVENT_MOUSE_MOTION: + process_mouse_pos(&ctx->input, &event.motion, ctx->window); + break; + case SDL_EVENT_MOUSE_BUTTON_DOWN: + case SDL_EVENT_MOUSE_BUTTON_UP: + process_mouse_buttons(&ctx->input, &event.button); + break; + case SDL_EVENT_WINDOW_RESIZED: + ctx->window.width = event.window.data1; + ctx->window.height = event.window.data2; + break; + case SDL_EVENT_QUIT: + ctx->should_close = 1; + break; + default: + break; + } + } +} + +f32 get_elapsed_seconds(u64 current, u64 last) +{ + f32 elapsed = ((f32)current-(f32)last)/1000.0f; + return elapsed; +} + +f32 lock_framerate(u64 last, f32 target) +{ + f32 elapsed = get_elapsed_seconds(SDL_GetTicks(), last)*1000.0f; + if (elapsed < target) { + f32 sleep = target-elapsed; + if (sleep > 0.0f) + SDL_Delay(sleep); + do { + elapsed = get_elapsed_seconds(SDL_GetTicks(), last)*1000.0f; + } while (elapsed < target); + } + f32 dt = get_elapsed_seconds(SDL_GetTicks(), last); + return dt; +} + +#include "stb_vorbis.c" + +i32 load_vorbis(arena_t *arena, sound_t *sounds, const char *filename) +{ + sound_t sound; + sound.name = filename; + + i32 i = find_sound(sounds, sound.name); + if (i != -1) { + printf("info: \"%s\" already loaded\n", sound.name); + return i; + } + + /* TODO(pryazha): Custom loader with arenas (or provide allocator to stb) */ + i16 *output; + i32 samples = stb_vorbis_decode_filename(filename, &sound.channels, + &sound.freq, &output); + if (samples == -1) { + printf("error: \"%s\" failed to open\n", filename); + return -1; + } + + sound.bps = sizeof(i16); + sound.size = sound.channels*samples*sound.bps; + sound.data = push_arena(arena, sound.size); + prb_memmove(sound.data, output, sound.size); + free(output); + + i = load_sound(sounds, sound); + if (i == -1) { + pop_arena(arena, sound.size); + return i; + } + + printf("info: \"%s\" loaded successfully\n", filename); + + return i; +} + +void play_sound(sound_t *sounds, sound_queue_t *queue, sound_queue_node_t *nodes, i32 id) +{ + if ((id == -1) && (id >= MAX_SOUNDS_LOADED)) { + printf("error: invalid sound id = %d\n", id); + return; + } + + if (!sounds[id].data) { + printf("error: sound with id = %d doesn't exist\n", id); + return; + } + + sound_t *sound = sounds+id; + + if (!enqueue_sound(queue, nodes, sound)) { + printf("error: failed to enqueue the sound \"%s\"\n", sound->name); + return; + } + + printf("info: queue content:\n"); + for (sound_queue_node_t *node = queue->first; node; node = node->next) { + printf("%s%s", node->sound->name, (node->next ? " -> " : "\n")); + } +} + +SDLSound *get_empty_sdl_sound() +{ + for (i32 i = 0; i < MAX_SOUNDS_PLAYING; i++) { + SDLSound *sdlsound = sdlctx.sdlsounds+i; + if (!sdlsound->stream) + return sdlsound; + if (SDL_GetAudioStreamAvailable(sdlsound->stream) == 0) + return sdlsound; + } + return 0; +} + +void update_sdl_sounds(sound_queue_t *queue) +{ + SDLSound *sdlsound; + sound_t *sound; + SDL_AudioSpec spec; + + while ((sdlsound = get_empty_sdl_sound()) && (sound = dequeue_sound(queue))) { + switch (sound->bps) { + case 2: + spec.format = SDL_AUDIO_S16; + break; + default: + printf("error: sound: \"%s\". Unsupported audio format\n", sound->name); + return; + } + spec.channels = sound->channels; + spec.freq = sound->freq; + + if (sdlsound->stream) { + if (!prb_memeq(&sdlsound->spec, &spec, sizeof(SDL_AudioSpec))) { + sdlsound->spec = spec; + SDL_SetAudioStreamFormat(sdlsound->stream, &sdlsound->spec, 0); + } + } else { + sdlsound->spec = spec; + sdlsound->stream = SDL_CreateAudioStream(&sdlsound->spec, 0); + if (!sdlsound->stream) { + printf("error: sailed to create audio stream\n"); + return; + } + if (!SDL_BindAudioStream(sdlctx.audio_device, sdlsound->stream)) { + SDL_DestroyAudioStream(sdlsound->stream); + printf("error: failed to bind audio stream to device\n"); + return; + } + } + printf("info: playing: \"%s\"\n", sound->name); + SDL_PutAudioStreamData(sdlsound->stream, sound->data, sound->size); + } +} + +i32 main(void) +{ + // engine init + prge_context_t prgectx = init_prge(); + u32 flags = WINDOW_DEPTH_TEST | WINDOW_MULTISAMPLE; + prgectx.window = (prge_window_t){800, 600, "prge example", flags}; + + f32 fps = 60.0f; + f32 mspf = 1000.0f/fps; + + // sdl and opengl init + if (!init_sdl(&prgectx.window)) + return 1; + + // game init + game_context_t gctx = {0}; + init(&prgectx, &gctx); + + u64 last = SDL_GetTicks(); + while (!prgectx.should_close) { + SDL_SetWindowRelativeMouseMode(sdlctx.window, prgectx.input.mouse.capture); + + handle_events(&prgectx); + + prgectx.input.dt = lock_framerate(last, mspf); + last = SDL_GetTicks(); + + pop_arena(&prgectx.frame_arena, prgectx.frame_arena.used); + + glViewport(0, 0, prgectx.window.width, prgectx.window.height); + update_and_render(&prgectx, &gctx); + update_sdl_sounds(&prgectx.sound_queue); + update_input(&prgectx.input); + + SDL_GL_SwapWindow(sdlctx.window); + } + + return 0; +} -- cgit v1.2.3-70-g09d2