/* * 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 // for rand() #include #include #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); } 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. */ SDL_GL_GetDrawableSize(pig->window, &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, };