summaryrefslogtreecommitdiff
path: root/src/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.cpp')
-rw-r--r--src/main.cpp529
1 files changed, 529 insertions, 0 deletions
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..0cc151b
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,529 @@
+/*
+ * Copyright © 2009 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 <cmath>
+#include <GL/glew.h>
+#include <SDL.h>
+
+#include <glu3.h>
+
+#include "shader.h"
+
+#define BUFFER_OFFSET(i) ((char *)NULL + (i))
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+
+static SDL_Surface *my_surf = NULL;
+
+static bool anim = true;
+static bool done = false;
+
+static float y_angle = 0.0f;
+static float hinge_angle = 0.0f;
+static float orbit_angle = 0.0f;
+static float eye_distance = 15.0f;
+static float fov = 45.0f;
+
+static Uint32 t0;
+
+static GLuint bo;
+
+static GLUmat4 projection_matrix;
+
+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->mvp_uniform =
+ glGetUniformLocation(this->program, "mvp");
+ }
+
+ GLint mvp_uniform;
+};
+
+static complex_points_program *cube_program = 0;
+
+static GLuint timer_queries[1] = { 0, };
+
+static inline
+float degrees_to_radians(float f)
+{
+ return (f * M_PI) / 180.0;
+}
+
+
+class simple_consumer : public GLUshapeConsumer {
+public:
+ simple_consumer(void *_data, unsigned num_verts, uintptr_t base_offset)
+ {
+ GLUvec4 *data = (GLUvec4 *) _data;
+
+ this->position = &data[0 * num_verts];
+ this->position_offset = (uintptr_t) ((char *) this->position
+ - (char *) _data)
+ + base_offset;
+
+ this->normal = &data[1 * num_verts];
+
+ this->normal_offset = (uintptr_t) ((char *) this->normal
+ - (char *) _data)
+ + base_offset;
+
+ this->tangent = NULL;
+ this->uv = NULL;
+ this->vertex_count = num_verts;
+
+ this->elt = (GLushort *) &data[2 * num_verts];
+ this->elt_offset = (uintptr_t) ((char *) this->elt
+ - (char *) _data)
+ + base_offset;
+ }
+
+ virtual void begin_primitive(GLenum mode, unsigned count)
+ {
+ this->mode = mode;
+ this->elt_count = count;
+ }
+
+ virtual void index_batch(const unsigned *idx, unsigned count)
+ {
+ for (unsigned i = 0; i < count; i++)
+ this->elt[i] = GLushort(idx[i]);
+
+ this->elt += count;
+ }
+
+ GLushort *elt;
+ uintptr_t elt_offset;
+ unsigned elt_count;
+
+ uintptr_t position_offset;
+ uintptr_t normal_offset;
+
+ GLenum mode;
+};
+
+
+static simple_consumer *cube_sink;
+
+/**
+ * Fill a buffer object with data defining a cube.
+ */
+static simple_consumer *
+fill_in_object_data(GLuint buf)
+{
+ GLUcubeProducer cube_source(1.0);
+
+ const unsigned num_verts = cube_source.vertex_count();
+ const unsigned num_elts = cube_source.element_count();
+
+ const unsigned size = (2 * sizeof(GLUvec4) * num_verts)
+ + (sizeof(GLushort) * num_elts);
+
+ glBindBuffer(GL_ARRAY_BUFFER, buf);
+ glBufferData(GL_ARRAY_BUFFER, size, NULL, GL_STATIC_DRAW);
+ void *ptr = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
+
+ simple_consumer *cons = new simple_consumer(ptr, num_verts, 0);
+ cube_source.generate(cons);
+
+ glUnmapBuffer(GL_ARRAY_BUFFER);
+
+ return cons;
+}
+
+
+static void
+Redisplay(void)
+{
+ static Uint32 last_t = ~0;
+ Uint32 t = SDL_GetTicks();
+
+
+ /* Update animation parameters based on the elaped time.
+ */
+ if (last_t != (Uint32)~0) {
+ float dt = (float) (t - last_t) / 1000.0f;
+
+ if (anim) {
+ /* Rotate at 15 degrees and 60 degrees per second
+ * respectively. All of the measurements want angles
+ * measured in radians. Do the conversion here.
+ */
+ orbit_angle += degrees_to_radians(15.0 * dt);
+ y_angle += degrees_to_radians(60.0 * dt);
+
+
+ /* The "hinge" rotation angle oscillates between 0
+ * and 45 degrees. Implement this using the sine
+ * function. Bias by 1.0 then scale by 0.5 to get
+ * sine in the range [0, 1], the scale by 45 degrees.
+ * Finally, convert the angle to radians.
+ */
+ const float h = (sin(y_angle) + 1.0) / 2.0;
+ hinge_angle = degrees_to_radians(h * 45.0);
+ }
+ }
+
+ last_t = t;
+
+ /* Storage for all of the transformation matrices.
+ */
+ GLUmat4 transformations[5];
+
+ /* Start by generating the viewing matrix. The camera will orbit
+ * the world-space origin at a distance of 15 units.
+ */
+ const GLUmat4 vp(projection_matrix * gluTranslate(0.0, 0.0, -15.0));
+
+ for (unsigned i = 0; i < ARRAY_SIZE(transformations); i++) {
+ transformations[i] = vp;
+ }
+
+
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+
+ /* Render the five cubes in the scene.
+ */
+ glUseProgram(cube_program->program);
+
+ if (timer_queries[0])
+ glBeginQuery(GL_TIME_ELAPSED, timer_queries[0]);
+
+ for (unsigned i = 0; i < ARRAY_SIZE(transformations); i++) {
+ /* Set the modelview-projection matrix for rendering this
+ * object.
+ */
+ glUniformMatrix4fv(cube_program->mvp_uniform, 1, false,
+ (float *) &transformations[i]);
+
+
+ /* Draw the object.
+ */
+ glDrawElements(cube_sink->mode,
+ cube_sink->elt_count,
+ GL_UNSIGNED_SHORT,
+ BUFFER_OFFSET(cube_sink->elt_offset));
+ }
+
+ if (timer_queries[0])
+ glEndQuery(GL_TIME_ELAPSED);
+
+ SDL_GL_SwapBuffers();
+
+ if (timer_queries[0]) {
+ static GLuint64 totals[ARRAY_SIZE(timer_queries)] = { 0, };
+ static unsigned count = 0;
+ GLuint64 result[ARRAY_SIZE(timer_queries)] = { 0, };
+
+ for (unsigned i = 0; i < ARRAY_SIZE(timer_queries); i++) {
+ glGetQueryObjectui64vEXT(timer_queries[i],
+ GL_QUERY_RESULT,
+ &result[i]);
+ totals[i] += result[i];
+ }
+
+ count++;
+ if (count > 100) {
+ printf("Timings: %lu\n",
+ totals[0] / 100);
+
+ memset(totals, 0, sizeof(totals));
+ count = 0;
+ }
+ }
+
+}
+
+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)
+{
+ gluInitializeCompiler();
+
+ /* If there are dangling instances of these program hanging around,
+ * clean them up.
+ */
+ if (cube_program)
+ delete cube_program;
+
+ /* Compile all of the shaders
+ */
+ GLuint vs = compile_shader_from_file(GL_VERTEX_SHADER,
+ "simple.vert");
+ GLuint fs = compile_shader_from_file(GL_FRAGMENT_SHADER,
+ "simple.frag");
+
+ cube_program = create_program(vs, fs, "simple");
+
+ /* The individual shaders have been attached to the programs, so they
+ * can safely be deleted.
+ */
+ glDeleteShader(vs);
+ 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(ARRAY_SIZE(timer_queries), timer_queries);
+
+ build_all_shaders();
+
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_CULL_FACE);
+
+
+ glGenBuffers(1, & bo);
+ cube_sink = fill_in_object_data(bo);
+
+ glBindBuffer(GL_ARRAY_BUFFER, bo);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bo);
+
+ glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0,
+ BUFFER_OFFSET(cube_sink->position_offset));
+ glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0,
+ BUFFER_OFFSET(cube_sink->normal_offset));
+ glEnableVertexAttribArray(0);
+ glEnableVertexAttribArray(1);
+
+
+ printf("GL_RENDERER = %s\n", (const char *) glGetString(GL_RENDERER));
+ printf("Keyboard input:\n"
+ " f: Toggle fullscreen.\n"
+ " a: Toggle animation of object.\n"
+ " c: Re-load and compile shader program code.\n"
+ " ESC: Exit program.\n");
+}
+
+
+static void
+Reshape(int width, int height)
+{
+ my_surf = SDL_SetVideoMode(width, height, 0,
+ SDL_OPENGL | SDL_RESIZABLE);
+ if (my_surf == NULL) {
+ exit(1);
+ }
+
+ Init();
+
+ const float aspect = float(width) / float(height);
+ gluPerspective4f(& projection_matrix, degrees_to_radians(fov),
+ aspect, 2.0f, 25.0f);
+ glViewport(0, 0, width, height);
+}
+
+
+static void
+Key(SDLKey sym, bool key_down)
+{
+ if (!key_down)
+ return;
+
+ switch (sym) {
+ case SDLK_ESCAPE:
+ done = true;
+ break;
+
+ case 'f':
+ SDL_WM_ToggleFullScreen(my_surf);
+ 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);
+ t0 = SDL_GetTicks();
+
+ 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:
+ Key(event.key.keysym.sym, true);
+ break;
+
+ case SDL_KEYUP:
+ Key(event.key.keysym.sym, false);
+ break;
+
+ default:
+ break;
+ }
+ } while (SDL_PollEvent(&event));
+
+
+ Redisplay();
+ }
+
+ SDL_RemoveTimer(timer_id);
+
+ SDL_Quit();
+ return 0;
+}