diff options
Diffstat (limited to 'src/pig.c')
-rw-r--r-- | src/pig.c | 375 |
1 files changed, 375 insertions, 0 deletions
diff --git a/src/pig.c b/src/pig.c new file mode 100644 index 0000000..fa6c706 --- /dev/null +++ b/src/pig.c @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2020 - Vito Caputo - <vcaputo@pengaru.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <SDL.h> +#include <stdlib.h> // for rand() + +#include <play.h> +#include <stage.h> + +#include "checker-node.h" +#include "clear-node.h" +#include "glad.h" +#include "m4f.h" +#include "macros.h" +#include "pig.h" +#include "shader-node.h" +#include "shader.h" + +#define PIG_DEFAULT_WIDTH 640 +#define PIG_DEFAULT_HEIGHT 480 +//#define PIG_WINDOW_FLAGS (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL) +#define PIG_WINDOW_FLAGS (SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI) + +#if 0 +#define PIG_ASPECT_RATIO .7 +#endif + +static inline float randf(void) +{ + return rand() * (1.f / (float)RAND_MAX); +} + + +void pig_canvas_size(pig_t *pig, int *res_width, int *res_height) +{ + assert(pig); + assert(res_width); + assert(res_height); + + SDL_GL_GetDrawableSize(pig->window, res_width, res_height); +} + + +void pig_canvas_to_ndc(pig_t *pig, int x, int y, float *res_x, float *res_y) +{ + int w, h; + + assert(pig); + assert(res_x); + assert(res_y); + + pig_canvas_size(pig, &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 pig_canvas_from_ndc(pig_t *pig, float x, float y, int *res_x, int *res_y) +{ + int w, h; + + assert(pig); + assert(res_x); + assert(res_y); + + pig_canvas_size(pig, &w, &h); + + *res_x = roundf(x * (float)w * .5f + .5f * (float)w); + *res_y = roundf(y * (float)h * .5f + .5f * (float)h); +} + + +void pig_viewport_size(pig_t *pig, int *res_width, int *res_height) +{ + assert(pig); + assert(res_width); + assert(res_height); + + SDL_GetWindowSize(pig->window, res_width, res_height); +} + + +void pig_viewport_to_ndc(pig_t *pig, int x, int y, float *res_x, float *res_y) +{ + int w, h; + + assert(pig); + assert(res_x); + assert(res_y); + + pig_viewport_size(pig, &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 pig_viewport_from_ndc(pig_t *pig, float x, float y, int *res_x, int *res_y) +{ + int w, h; + + assert(pig); + assert(res_x); + assert(res_y); + + pig_viewport_size(pig, &w, &h); + + *res_x = roundf(x * (float)w * .5f + .5f * (float)w); + *res_y = roundf(y * (float)h * .5f + .5f * (float)h); +} + + +uint32_t pig_viewport_id(pig_t *pig) +{ + return SDL_GetWindowID(pig->window); +} + + +void pig_toggle_fullscreen(pig_t *pig) +{ + if (pig->windowed) { + if (!SDL_SetWindowFullscreen(pig->window, SDL_WINDOW_FULLSCREEN_DESKTOP)) + pig->windowed = 0; + } else { + if (!SDL_SetWindowFullscreen(pig->window, 0)) + pig->windowed = 1; + } +} + + +static void randomize_color(v3f_t *color) +{ + color->x = randf(); + color->y = randf(); + color->z = randf(); + + if (v3f_length(color) < .8f) + *color = v3f_normalize(color); +} + + +static void pig_uniforms_func(shader_t *shader, void *uniforms_ctxt, void *render_ctxt, unsigned n_uniforms, const int *uniforms, const m4f_t *model_x, float alpha) +{ + play_t *play = render_ctxt; + pig_t *pig = uniforms_ctxt; + unsigned t0, t1; + float r = randf(); + + if (play_ticks_elapsed(play, PLAY_TICKS_TIMER2, 100)) + shader_reload_files(shader); + + /* this one just keeps increasing */ + t0 = play_ticks(play, PLAY_TICKS_TIMER0); + + /* this one resets every second and gets constrained to 0.f-1.f */ + t1 = play_ticks(play, PLAY_TICKS_TIMER1); + if (t1 >= 1000) { + randomize_color(&pig->color); + + pig->seed = randf(); + play_ticks_reset(play, PLAY_TICKS_TIMER1); + } + + glUniform1f(uniforms[0], alpha); + glUniformMatrix4fv(uniforms[1], 1, GL_FALSE, &model_x->m[0][0]); // TODO: make transform manipulatable + glUniform3f(uniforms[2], pig->color.x, pig->color.y, pig->color.z); // TODO: make color configurable + glUniform1f(uniforms[3], (float)t0 * .001f); + glUniform1f(uniforms[4], (float)t1 * .001f); + glUniform1f(uniforms[5], pig->seed); + glUniform1f(uniforms[6], r); +} + +static const char *pig_uniforms[] = { + "alpha", + "model_x", + "color", + "time", + "T", + "seed", + "rand" +}; + + + +static void * pig_init(play_t *play, int argc, char *argv[], unsigned flags) +{ + pig_t *pig; + + fatal_if(argc != 3, "Usage: %s vertex.shader fragment.shader", argv[0]); + + pig = calloc(1, sizeof(pig_t)); + fatal_if(!pig, "Unable to allocate pig_t"); + + pig->window_width = PIG_DEFAULT_WIDTH; + pig->window_height = PIG_DEFAULT_HEIGHT; + pig->windowed = 1; + + 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"); + + pig->window = SDL_CreateWindow("pig", + SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, + pig->window_width, pig->window_height, + PIG_WINDOW_FLAGS | (pig->windowed ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP)); + + if (!pig->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"); + + pig->window = SDL_CreateWindow("pig", + SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, + pig->window_width, pig->window_height, + PIG_WINDOW_FLAGS | (pig->windowed ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP)); + + fatal_if(!pig->window, + "Unable to create SDL window"); + } + + pig->gl = SDL_GL_CreateContext(pig->window); + fatal_if(!pig->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"); + + //SDL_ShowCursor(SDL_DISABLE); + +#ifdef MSAA_RENDER_TARGET + glEnable(GL_MULTISAMPLE); +#endif + + pig->transform = m4f_identity(); + pig->seed = randf(); + randomize_color(&pig->color); + + pig->stage = stage_new(&(stage_conf_t){.name = "pig", .active = 1, .alpha = 1.f}, NULL, NULL); + fatal_if(!pig->stage, "Unable to create new stage"); + + (void) clear_node_new(&(stage_conf_t){ + .parent = pig->stage, + .name = "gl-clear-pig", + .active = 1 + } + ); + + (void) checker_node_new(&(stage_conf_t){ + .parent = pig->stage, + .name = "checkers", + .layer = 1, + .active = 1, + .alpha = 1.f, + }, + &pig->transform, + &pig->color + ); + + (void) shader_node_new_files(&(stage_conf_t){ + .parent = pig->stage, + .name = "shader", + .layer = 2, + .active = 1, + .alpha = 1.f, + }, + argv[1], argv[2], + &pig->transform, + pig_uniforms_func, + pig, + NELEMS(pig_uniforms), + pig_uniforms + ); + + return pig; +} + + +static void pig_update(play_t *play, void *context) +{ + pig_t *pig = context; + + stage_dirty(pig->stage); +} + + +static void pig_render(play_t *play, void *context) +{ + pig_t *pig = context; + + if (stage_render(pig->stage, play)) { + SDL_GL_SwapWindow(pig->window); + } else { + SDL_Delay(100); // FIXME: this should be computed + } +} + + +static void pig_dispatch(play_t *play, void *context, SDL_Event *event) +{ + pig_t *pig = context; + + 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. + */ + pig_canvas_size(pig, &w, &h); + glViewport(0, 0, w, h); + stage_dirty(pig->stage); + } +} + + +const play_ops_t pig_ops = { + .init = pig_init, + .update = pig_update, + .render = pig_render, + .dispatch = pig_dispatch, +// .shutdown = pig_shutdown, +}; |