diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 27 | ||||
-rw-r--r-- | src/main.cpp | 529 | ||||
-rw-r--r-- | src/shader.cpp | 69 | ||||
-rw-r--r-- | src/shader.h | 41 |
4 files changed, 666 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..322901c --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,27 @@ +# Copyright © 2009 Ian D. Romanick +# All Rights Reserved. +# +# 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 +# on the rights to use, copy, modify, merge, publish, distribute, sub +# license, 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 NON-INFRINGEMENT. IN NO EVENT SHALL +# AUTHORS, COPYRIGHT HOLDERS, AND/OR THEIR SUPPLIERS 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. + +bin_PROGRAMS = rotating_cubes +rotating_cubes_SOURCES = main.cpp shader.cpp + +AM_CXXFLAGS= $(GLU3_CFLAGS) +rotating_cubes_LDADD = $(FRAMEWORK_LIBS) $(GLU3_LIBS) 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; +} diff --git a/src/shader.cpp b/src/shader.cpp new file mode 100644 index 0000000..8456362 --- /dev/null +++ b/src/shader.cpp @@ -0,0 +1,69 @@ +/* + * 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 <stdarg.h> +#include <GL/glew.h> +#include <glu3.h> +#include "shader.h" + +void shader_program::init() +{ + this->program = glCreateProgram(); + this->log = 0; +} + +shader_program::shader_program() +{ + init(); +} + +shader_program::shader_program(GLint shader, ...) +{ + va_list ap; + GLint sh; + + init(); + + glAttachShader(this->program, shader); + + va_start(ap, shader); + while ((sh = va_arg(ap, GLint)) != 0) { + glAttachShader(this->program, sh); + } + va_end(ap); +} + +shader_program::~shader_program() +{ + glDeleteProgram(this->program); + this->program = 0; + + if (this->log) { + gluReleaseInfoLog(this->log); + this->log = 0; + } +} + +bool shader_program::link() +{ + return gluLinkProgram(this->program, &this->log); +} diff --git a/src/shader.h b/src/shader.h new file mode 100644 index 0000000..5e49dc3 --- /dev/null +++ b/src/shader.h @@ -0,0 +1,41 @@ +/* + * 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. + */ +#ifndef SHADER_PROGRAM_H +#define SHADER_PROGRAM_H + +class shader_program { +public: + shader_program(); + shader_program(GLint shader, ...); + ~shader_program(); + + bool link(); + + GLint program; + char *log; + +private: + void init(); +}; + +#endif /* SHADER_PROGRAM_H */ |