From 931cfd8280dcedc4e916153e4a6844c55f9a4722 Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Wed, 10 Oct 2012 18:05:38 -0700 Subject: Initial import --- src/main.cpp | 499 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 499 insertions(+) create mode 100644 src/main.cpp (limited to 'src/main.cpp') 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 +#include +#include +#include +#include +#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; +} -- cgit v1.2.3