summaryrefslogtreecommitdiff
path: root/src/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.cpp')
-rw-r--r--src/main.cpp499
1 files changed, 499 insertions, 0 deletions
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..083427c
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,499 @@
+/*
+ * Copyright © 2012 Ian D. Romanick
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#include <math.h>
+#include <GL/glew.h>
+#include <glu3.h>
+#include <SDL.h>
+#include <assert.h>
+#include "shader.h"
+
+#define NUM_POINTS 57
+
+static SDL_Surface *my_surf = NULL;
+
+static bool anim = true;
+static bool done = false;
+static int shift_status = 0;
+
+static float angle_offset = 0.0f;
+
+static GLuint buf = 0;
+
+class complex_points_program : public shader_program {
+public:
+ complex_points_program(GLuint vs, GLuint fs)
+ : shader_program(vs, fs, 0)
+ {
+ /* empty */
+ }
+
+ void get_uniform_locations()
+ {
+ this->angle_uniform =
+ glGetUniformLocation(this->program, "rotation_angle");
+ this->matrix_uniform =
+ glGetUniformLocation(this->program, "m");
+ }
+
+ GLint angle_uniform;
+ GLint matrix_uniform;
+};
+
+static complex_points_program *explicit_rotation = 0;
+static complex_points_program *angle_addition = 0;
+static complex_points_program *matrix_rotation = 0;
+
+static GLuint timer_queries[3] = { 0, 0, 0 };
+
+#define BUFFER_OFFSET(i) ((char *)0 + (i))
+
+#define CHECK_GL_ERROR(e) \
+ do { \
+ e = glGetError(); \
+ if (e != 0) { \
+ printf("%s:%u: GL error = 0x%04x\n", __func__, \
+ __LINE__, e); \
+ } \
+ } while (0)
+
+
+static void
+Redisplay(void)
+{
+ const float m[4] = {
+ 1.0, 0.0,
+ 0.0, 1.0
+ };
+
+
+ /* Set the viewport to the whole window and clear the screen.
+ */
+ glViewport(0, 0, my_surf->w, my_surf->h);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+
+ /* Reset the viewport ot the lower left quadrant and draw using the
+ * "explicit rotation" shader. This shader uses the vertex data
+ * stored as (real, imaginary) pairs.
+ */
+ glViewport(0, 0, my_surf->w / 2, my_surf->h / 2);
+
+ glUseProgram(explicit_rotation->program);
+ glUniform1f(explicit_rotation->angle_uniform, angle_offset);
+ glUniformMatrix2fv(explicit_rotation->matrix_uniform, 1, false, m);
+
+ if (timer_queries[0])
+ glBeginQuery(GL_TIME_ELAPSED, timer_queries[0]);
+
+ glDrawArrays(GL_POINTS, 0, NUM_POINTS);
+
+ if (timer_queries[0])
+ glEndQuery(GL_TIME_ELAPSED);
+
+
+ /* Reset the viewport to the upper left quadrant and draw using the
+ * "angle addition" shader. This shader uses the vertex data stored
+ * as (modulus, angle) pairs.
+ */
+ glViewport(0, my_surf->h / 2, my_surf->w / 2, my_surf->h / 2);
+
+ glUseProgram(angle_addition->program);
+ glUniform1f(angle_addition->angle_uniform, angle_offset);
+ glUniformMatrix2fv(angle_addition->matrix_uniform, 1, false, m);
+
+ if (timer_queries[1])
+ glBeginQuery(GL_TIME_ELAPSED, timer_queries[1]);
+
+ glDrawArrays(GL_POINTS, NUM_POINTS, NUM_POINTS);
+
+ if (timer_queries[1])
+ glEndQuery(GL_TIME_ELAPSED);
+
+ /* Reset the viewport to the upper right quadrant and draw using the
+ * "matrix rotation" shader. This shader uses the vertex data stored
+ * as (real, imaginary) pairs.
+ */
+ glViewport(my_surf->w / 2, my_surf->h / 2,
+ my_surf->w / 2, my_surf->h / 2);
+
+ glUseProgram(matrix_rotation->program);
+ glUniform1f(matrix_rotation->angle_uniform, angle_offset);
+ glUniformMatrix2fv(matrix_rotation->matrix_uniform, 1, false, m);
+
+ if (timer_queries[2])
+ glBeginQuery(GL_TIME_ELAPSED, timer_queries[2]);
+
+ glDrawArrays(GL_POINTS, 0, NUM_POINTS);
+
+ if (timer_queries[2])
+ glEndQuery(GL_TIME_ELAPSED);
+
+ SDL_GL_SwapBuffers();
+
+ assert((timer_queries[0] == 0) == (timer_queries[1] == 0));
+ if (timer_queries[0]) {
+ static GLuint64 totals[3] = { 0, 0, 0 };
+ static unsigned count = 0;
+ GLuint64 result[3] = { 0, 0, 0 };
+
+ for (unsigned i = 0; i < 3; i++) {
+ glGetQueryObjectui64vEXT(timer_queries[i],
+ GL_QUERY_RESULT,
+ &result[i]);
+ totals[i] += result[i];
+ }
+
+ count++;
+ if (count > 100) {
+ printf("Timings: %lu, %lu, %lu\n",
+ totals[0] / 100,
+ totals[1] / 100,
+ totals[2] / 100);
+
+ memset(totals, 0, sizeof(totals));
+ count = 0;
+ }
+ }
+}
+
+
+static void Idle(void)
+{
+ static Uint32 last_t = ~0;
+ Uint32 t = SDL_GetTicks();
+
+
+ if (last_t != (Uint32)~0) {
+ float dt = (float) (t - last_t) / 1000.0f;
+
+ if (anim) {
+ angle_offset += M_PI * dt;
+ }
+ }
+
+ last_t = t;
+}
+
+
+
+
+static GLuint
+compile_shader_from_file(GLenum target, const char *filename)
+{
+ static const char *const paths_to_try[] = {
+ "",
+ "data/",
+ "../data/",
+ 0
+ };
+
+ GLuint shader;
+ char *log = NULL;
+ const char *code;
+
+ const size_t name_len = strlen(filename);
+ for (unsigned i = 0; paths_to_try[i] != 0; i++) {
+ const size_t path_len = strlen(paths_to_try[i]);
+ char *name_with_path = new char [path_len + name_len + 1];
+
+ memcpy(name_with_path, paths_to_try[i], path_len);
+ memcpy(&name_with_path[path_len], filename, name_len);
+ name_with_path[path_len + name_len] = '\0';
+
+ code = gluLoadTextFile(name_with_path);
+ delete [] name_with_path;
+
+ if (code != NULL)
+ break;
+ }
+
+ if (code == NULL) {
+ fprintf(stderr, "Unable to load shader code for %s.\n",
+ filename);
+ exit(1);
+ }
+
+ shader = gluCompileShader(target, code, &log);
+ if (log != NULL) {
+ printf("Shader compile log for %s:\n%s\n", filename, log);
+ gluReleaseInfoLog(log);
+ log = NULL;
+ }
+
+ if (shader == 0) {
+ fprintf(stderr, "Failed to compile %s.\n", filename);
+ exit(1);
+ }
+
+ gluUnloadTextFile(code);
+
+ return shader;
+}
+
+
+static complex_points_program *
+create_program(GLuint vs, GLuint fs, const char *name)
+{
+ complex_points_program *p = new complex_points_program(vs, fs);
+
+ const bool linked = p->link();
+ if (p->log != NULL)
+ printf("%s link log:\n%s\n", name, p->log);
+
+ if (!linked) {
+ fprintf(stderr, "Failed to link %s.\n", name);
+ exit(1);
+ }
+
+ p->get_uniform_locations();
+ return p;
+}
+
+/* Load, compile, link, etc. the vertex and fragment programs.
+ */
+static void
+build_all_shaders(void)
+{
+ GLuint vs[3];
+ GLuint fs;
+
+ /* If there are dangling instances of these program hanging around,
+ * clean them up.
+ */
+ if (explicit_rotation)
+ delete explicit_rotation;
+
+ if (angle_addition)
+ delete angle_addition;
+
+ if (matrix_rotation)
+ delete matrix_rotation;
+
+
+ /* Compile all of the shaders
+ */
+ vs[0] = compile_shader_from_file(GL_VERTEX_SHADER,
+ "explicit_rotation.vert");
+ vs[1] = compile_shader_from_file(GL_VERTEX_SHADER,
+ "angle_addition.vert");
+ vs[2] = compile_shader_from_file(GL_VERTEX_SHADER,
+ "matrix_rotation.vert");
+
+ fs = compile_shader_from_file(GL_FRAGMENT_SHADER,
+ "ellipse.frag");
+
+ /* Create all of the programs that use the previously created shaders.
+ */
+ explicit_rotation = create_program(vs[0], fs, "explicit rotation");
+ angle_addition = create_program(vs[1], fs, "angle addition");
+ matrix_rotation = create_program(vs[2], fs, "matrix rotation");
+
+ /* The individual shaders have been attached to the programs, so they
+ * can safely be deleted.
+ */
+ glDeleteShader(vs[0]);
+ glDeleteShader(vs[1]);
+ glDeleteShader(vs[2]);
+ glDeleteShader(fs);
+}
+
+
+static void
+Init(void)
+{
+ glewInit();
+
+ if (!GLEW_VERSION_2_0) {
+ printf ("Sorry, this demo requires OpenGL 2.0 or later.\n");
+ exit(1);
+ }
+
+ if (GLEW_EXT_timer_query || GLEW_ARB_timer_query)
+ glGenQueries(3, timer_queries);
+
+ gluInitializeCompiler();
+
+ explicit_rotation = 0;
+ angle_addition = 0;
+ matrix_rotation = 0;
+
+ build_all_shaders();
+
+ float data[2 * (2 * NUM_POINTS)];
+
+ for (unsigned i = 0; i < NUM_POINTS; i++) {
+ const float angle = float(i) * M_PI * 2.0f / float(NUM_POINTS);
+ const float r = cos(5.0f * angle) * 0.4f + 0.5f;
+
+ data[(i + 0) * 2 + 0] = r * cos(angle);
+ data[(i + 0) * 2 + 1] = r * sin(angle);
+ data[(i + NUM_POINTS) * 2 + 0] = r;
+ data[(i + NUM_POINTS) * 2 + 1] = angle;
+ }
+
+ glGenBuffers(1, &buf);
+ glBindBuffer(GL_ARRAY_BUFFER, buf);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
+
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat),
+ BUFFER_OFFSET(0));
+ glEnableVertexAttribArray(0);
+
+ glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
+
+
+ printf("GL_RENDERER = %s\n", (const char *) glGetString(GL_RENDERER));
+ printf("Keyboard input:\n"
+ " a: Toggle animation of object.\n"
+ " c: Re-load and compile shader program code.\n"
+ " ESC: Exit program.\n");
+}
+
+
+static void
+Reshape(int w, int h)
+{
+ my_surf = SDL_SetVideoMode(w, h, 0, SDL_OPENGL | SDL_RESIZABLE);
+ if (my_surf == NULL) {
+ exit(1);
+ }
+
+ glViewport(0, 0, my_surf->w, my_surf->h);
+ Init();
+}
+
+
+static void
+Key(SDLKey sym)
+{
+ switch (sym) {
+ case SDLK_ESCAPE:
+ done = true;
+ break;
+
+ case ' ':
+ case 'a':
+ anim = !anim;
+ break;
+
+ case 'c':
+ printf("Reloading and compiling program code...\n");
+ build_all_shaders();
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+/**
+ * Push an event each time the timer callback fires.
+ */
+Uint32
+timer_callback(Uint32 interval, void *not_used)
+{
+ SDL_Event e;
+
+ (void) not_used;
+
+ e.type = SDL_USEREVENT;
+ e.user.code = 0;
+ e.user.data1 = NULL;
+ e.user.data2 = NULL;
+ SDL_PushEvent(& e);
+
+ return interval;
+}
+
+
+int
+main(int argc, char **argv)
+{
+ (void) argc;
+ (void) argv;
+
+ if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0) {
+ exit(1);
+ }
+
+ atexit(SDL_Quit);
+
+ Reshape(800, 600);
+
+ SDL_TimerID timer_id = SDL_AddTimer(10, timer_callback, NULL);
+
+ while (!done) {
+ SDL_Event event;
+
+ SDL_WaitEvent(&event);
+ do {
+ switch (event.type) {
+ case SDL_VIDEORESIZE:
+ Reshape(event.resize.w, event.resize.h);
+ break;
+
+ case SDL_QUIT:
+ done = true;
+ break;
+
+ case SDL_KEYDOWN:
+ if ((event.key.keysym.sym == SDLK_RSHIFT)
+ || (event.key.keysym.sym == SDLK_LSHIFT)) {
+ shift_status++;
+ } else {
+ Key(event.key.keysym.sym);
+ }
+ break;
+
+ case SDL_KEYUP:
+ if ((event.key.keysym.sym == SDLK_RSHIFT)
+ || (event.key.keysym.sym == SDLK_LSHIFT)) {
+ shift_status--;
+ }
+
+ /* By clamping the shift status value to 0 we
+ * prevent some bugs when the program is
+ * started with one or both of the shift keys
+ * held down.
+ */
+ if (shift_status < 0) {
+ shift_status = 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+ } while (SDL_PollEvent(&event));
+
+
+ Idle();
+ Redisplay();
+ }
+
+ SDL_RemoveTimer(timer_id);
+
+ SDL_Quit();
+ return 0;
+}