summaryrefslogtreecommitdiff
path: root/src/pig.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pig.c')
-rw-r--r--src/pig.c375
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,
+};
© All Rights Reserved