/* * Copyright (C) 2020 - Vito Caputo - * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "clear-node.h" #include "glad.h" #include "macros.h" #include "sars.h" #define SARS_DEFAULT_WIDTH 800 #define SARS_DEFAULT_HEIGHT 600 //#define SARS_WINDOW_FLAGS (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL) #define SARS_WINDOW_FLAGS (SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI) #if 0 #define SARS_ASPECT_RATIO .7 #endif void sars_canvas_size(sars_t *sars, int *res_width, int *res_height) { assert(sars); assert(res_width); assert(res_height); SDL_GL_GetDrawableSize(sars->window, res_width, res_height); } void sars_canvas_to_ndc(sars_t *sars, int x, int y, float *res_x, float *res_y) { int w, h; assert(sars); assert(res_x); assert(res_y); sars_canvas_size(sars, &w, &h); *res_x = (float)x / (float)w * 2.f - 1.f; *res_y = (1.f - (float)y / (float)h) * 2.f - 1.f; } void sars_canvas_from_ndc(sars_t *sars, float x, float y, int *res_x, int *res_y) { int w, h; assert(sars); assert(res_x); assert(res_y); sars_canvas_size(sars, &w, &h); *res_x = roundf(x * (float)w * .5f + .5f * (float)w); *res_y = roundf(y * (float)h * .5f + .5f * (float)h); } void sars_viewport_size(sars_t *sars, int *res_width, int *res_height) { assert(sars); assert(res_width); assert(res_height); SDL_GetWindowSize(sars->window, res_width, res_height); } void sars_viewport_to_ndc(sars_t *sars, int x, int y, float *res_x, float *res_y) { int w, h; assert(sars); assert(res_x); assert(res_y); sars_viewport_size(sars, &w, &h); *res_x = (float)x / (float)w * 2.f - 1.f; *res_y = (1.f - (float)y / (float)h) * 2.f - 1.f; } void sars_viewport_from_ndc(sars_t *sars, float x, float y, int *res_x, int *res_y) { int w, h; assert(sars); assert(res_x); assert(res_y); sars_viewport_size(sars, &w, &h); *res_x = roundf(x * (float)w * .5f + .5f * (float)w); *res_y = roundf(y * (float)h * .5f + .5f * (float)h); } uint32_t sars_viewport_id(sars_t *sars) { return SDL_GetWindowID(sars->window); } void sars_toggle_fullscreen(sars_t *sars) { if (sars->windowed) { if (!SDL_SetWindowFullscreen(sars->window, SDL_WINDOW_FULLSCREEN_DESKTOP)) sars->windowed = 0; } else { if (!SDL_SetWindowFullscreen(sars->window, 0)) sars->windowed = 1; } } static void * sars_init(play_t *play, int argc, char *argv[]) { sars_t *sars; sars = calloc(1, sizeof(sars_t)); fatal_if(!sars, "Unable to allocate sars_t"); sars->stage = stage_new(&(stage_conf_t){.name = "sars", .active = 1, .alpha = 1.f}, NULL, NULL); fatal_if(!sars->stage, "Unable to create new stage"); (void) clear_node_new(&(stage_conf_t){.parent = sars->stage, .name = "gl-clear-sars", .active = 1}); sars->window_width = SARS_DEFAULT_WIDTH; sars->window_height = SARS_DEFAULT_HEIGHT; if (argc > 1) { /* for now just support --window [WxH] */ if (!strcasecmp(argv[1], "--window")) { sars->windowed = 1; if (argc > 2) sscanf(argv[2], "%ux%u", &sars->window_width, &sars->window_height); } } fatal_if(SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY) < 0, "Unable to set GL core profile attribute"); fatal_if(SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2) < 0, "Unable to set GL major version attribute"); fatal_if(SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1) < 0, "Unable to set GL minor version attribute"); fatal_if(SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1) < 0, "Unable to set GL doublebuffer attribute"); fatal_if(SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8) < 0, "Unable to set GL red size attribute"); fatal_if(SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8) < 0, "Unable to set GL green size attribute"); fatal_if(SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8) < 0, "Unable to set GL blue size attribute"); fatal_if(SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8) < 0, /* this in particular is required for cache-node.c to work w/alpha */ "Unable to set GL alpha size attribute"); #define MSAA_RENDER_TARGET #ifdef MSAA_RENDER_TARGET fatal_if(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4) < 0, "Unable to et GL multisample samples attribute"); fatal_if(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1) < 0, "Unable to set GL multisample buffers attribute"); #endif /* On older SDL versions, this define is missing at compile-time, * but we can just provide it here and still try the sethint call, * just in case the runtime SDL version knows how to handle it. */ #ifndef SDL_HINT_TOUCH_MOUSE_EVENTS #define SDL_HINT_TOUCH_MOUSE_EVENTS "SDL_TOUCH_MOUSE_EVENTS" #endif warn_if(!SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0"), "Unable to suppress synthetic mouse events on touch"); sars->window = SDL_CreateWindow("Eon", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, sars->window_width, sars->window_height, SARS_WINDOW_FLAGS | (sars->windowed ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP)); if (!sars->window) { fatal_if(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0) < 0, "Unable to clear GL multisample samples attribute"); fatal_if(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0) < 0, "Unable to clear GL multisample buffers attribute"); sars->window = SDL_CreateWindow("Eon", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, sars->window_width, sars->window_height, SARS_WINDOW_FLAGS | (sars->windowed ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP)); fatal_if(!sars->window, "Unable to create SDL window"); } sars->gl = SDL_GL_CreateContext(sars->window); fatal_if(!sars->gl, "Unable to create GL context"); fatal_if(SDL_GL_SetSwapInterval(1) < 0, "Unable to enable vsync"); fatal_if(!gladLoadGL(), "Failed to initialize GLAD OpenGL extension loader"); //This seems unnecessary now that the game grabs the mouse, //and it's undesirable with clickable UI elements outside the //gameplay - otherwise I'd have to draw a pointer. //SDL_ShowCursor(SDL_DISABLE); #ifdef MSAA_RENDER_TARGET glEnable(GL_MULTISAMPLE); #endif return sars; } static void sars_update(play_t *play, void *context) { play_context_enter(play, SARS_CONTEXT_HUNGRYCAT); } /* XXX: note render and dispatch are public and ignore the passed-in context, * so other contexts can use these as-is for convenience */ void sars_render(play_t *play, void *context) { sars_t *sars = play_context(play, SARS_CONTEXT_SARS); if (stage_render(sars->stage, play)) { SDL_GL_SwapWindow(sars->window); } else { SDL_Delay(100); // FIXME: this should be computed } } void sars_dispatch(play_t *play, void *context, SDL_Event *event) { sars_t *sars = play_context(play, SARS_CONTEXT_SARS); if (event->type == SDL_WINDOWEVENT && event->window.event == SDL_WINDOWEVENT_RESIZED) { int w, h; /* on highdpi the window size and pixels are decoupled, so ignore what * the event contains and query the canvas size. */ sars_canvas_size(sars, &w, &h); glViewport(0, 0, w, h); stage_dirty(sars->stage); } } const play_ops_t sars_ops = { .init = sars_init, .update = sars_update, // .shutdown = sars_shutdown, };