diff options
author | Matt Turner <mattst88@gmail.com> | 2012-12-11 11:05:52 -0800 |
---|---|---|
committer | Matt Turner <mattst88@gmail.com> | 2012-12-11 11:05:52 -0800 |
commit | de28b8ba484266a03b8650326b306785d75720b3 (patch) | |
tree | 2113476378d3799659bd3d7ee57335630d0810f5 /src |
Initial import
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 27 | ||||
-rw-r--r-- | src/main.cpp | 647 | ||||
-rw-r--r-- | src/shader.cpp | 69 | ||||
-rw-r--r-- | src/shader.h | 41 |
4 files changed, 784 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..838519b --- /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 = worm +worm_SOURCES = main.cpp shader.cpp + +AM_CXXFLAGS= $(GLU3_CFLAGS) +worm_LDADD = $(FRAMEWORK_LIBS) $(GLU3_LIBS) -lEGL diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..78050f3 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,647 @@ +/* + * 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 <math.h> +#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 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 set_attribute_locations() + { + glBindAttribLocation(this->program, 0, "position"); + glBindAttribLocation(this->program, 1, "vert_color"); + glBindAttribLocation(this->program, 2, "weight"); + } + + void get_uniform_locations() + { + this->vp_uniform = + glGetUniformLocation(this->program, "vp"); + this->m_uniform = + glGetUniformLocation(this->program, "m"); + } + + GLint vp_uniform; + GLint m_uniform; + GLint weight_uniform; +}; + +static complex_points_program *worm_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->uv = &data[2 * num_verts]; + this->uv_offset = (uintptr_t) ((char *) this->uv + - (char *) _data) + + base_offset; + + this->tangent = NULL; + this->vertex_count = num_verts; + + this->elt = (GLushort *) &data[3 * 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; + uintptr_t uv_offset; + + GLenum mode; +}; + +static inline float frac(float x) +{ + return x - floor(x); +} + +class worm_producer : public GLUmeshProducer { +public: + worm_producer(float _length, unsigned _bones) + : GLUmeshProducer(5, 9, 9), length(_length), bones(_bones) + { + /* empty */ + } + + virtual unsigned + vertex_count(void) const + { + return (this->rows + 1) * this->columns; + } + + virtual void + generate(GLUshapeConsumer *consumer) const + { + float data[2 * 8]; + + for (unsigned i = 0; i < 8; i++) { + const float angle = float(i) * ((2.0 * M_PI) / 8.0); + data[(2 * i) + 0] = cos(angle); + data[(2 * i) + 1] = sin(angle); + } + + const float step = this->length / float(this->rows); + unsigned idx = 0; + for (unsigned i = 0; i < (this->rows + 1); i++) { + for (unsigned j = 0; j < 8; j++) { + consumer->position[idx] = + GLUvec4(2.0f * data[(2 * j) + 0], + step * float(i), + 2.0f * data[(2 * j) + 1], + 1.0f); + consumer->normal[idx] = + GLUvec4(data[(2 * j) + 0], + 0.0f, + data[(2 * j) + 1], + 0.0f); + consumer->uv[idx] = encode_weight(i); + + idx++; + } + + consumer->position[idx] = + GLUvec4(2.0f * data[(2 * 0) + 0], + step * float(i), + 2.0f * data[(2 * 0) + 1], + 1.0f); + consumer->normal[idx] = + GLUvec4(data[0], + 0.0f, + data[1], + 0.0f); + consumer->uv[idx] = encode_weight(i); + idx++; + } + + consumer->vertex_batch(consumer->position, + consumer->normal, + consumer->tangent, + consumer->uv, + idx); + + GLUmeshProducer::generate(consumer); + } + +private: + GLUvec4 encode_weight(unsigned i) const + { + const unsigned n = (this->bones - 1) * i; + const unsigned d = (this->rows); + const float w = float(n) / float(d); + const unsigned b = n / d; + + return GLUvec4(float(b), + float(b + 1), + 1.0 - frac(w), + 0.0f); + } + + float length; + unsigned bones; +}; + + +static simple_consumer *worm_sink; + +/** + * Fill a buffer object with data defining a worm. + */ +static simple_consumer * +fill_in_object_data(GLuint buf) +{ + worm_producer worm_source(10.0, 6); + + const unsigned num_verts = worm_source.vertex_count(); + const unsigned num_elts = worm_source.element_count(); + + const unsigned size = (3 * 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); + worm_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 60 degrees per second respectively. All + * of the measurements want angles measured in + * radians. Do the conversion here. + */ + y_angle += degrees_to_radians(60.0 * dt); + } + } + + last_t = t; + + /* Storage for all of the transformation matrices. + */ + GLUmat4 transformations[6]; + + const GLUvec4 eye(eye_distance * cos(0.0), + 5.0f, + eye_distance * sin(0.0), + 0.0f); + + const GLUmat4 vp(projection_matrix + * gluLookAt(eye, + GLUvec4(-5.0f, 3.0f, 0.0f, 0.0f), + GLUvec4(0.0f, 1.0f, 0.0f, 0.0f))); + + /* Each of the bones rotates the segment by an angle such that the + * combined rotation is pi when the full rotation is used. Time the + * animation using y_angle to oscillate between -pi and pi with no + * rotation initially. + */ + const float h = sin(y_angle); + const double angle = (h * M_PI) / float(ARRAY_SIZE(transformations) - 2); + + GLUmat4 model(gluIdentityMatrix); + transformations[0] = model; + + for (unsigned i = 1; i < (ARRAY_SIZE(transformations) - 1); i++) { + /* FINISHME: Generate forward-kinematic transformations for + * FINISHME: each of the bones. + */ + transformations[i] = model; + } + + transformations[5] = transformations[4]; + + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + + /* Render the worm. + */ + glUseProgram(worm_program->program); + + if (timer_queries[0]) + glBeginQuery(GL_TIME_ELAPSED, timer_queries[0]); + + glUniformMatrix4fv(worm_program->vp_uniform, 1, false, (float *) &vp); + + glUniformMatrix4fv(worm_program->m_uniform, ARRAY_SIZE(transformations), + false, (float *) &transformations[0]); + + glDrawElements(worm_sink->mode, + worm_sink->elt_count, + GL_UNSIGNED_SHORT, + BUFFER_OFFSET(worm_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); + + p->set_attribute_locations(); + + 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 (worm_program) + delete worm_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"); + + worm_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); + worm_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(worm_sink->position_offset)); + glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, + BUFFER_OFFSET(worm_sink->normal_offset)); + glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, 0, + BUFFER_OFFSET(worm_sink->uv_offset)); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + + + 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, 100.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) { + bool draw = false; + 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; + + case SDL_USEREVENT: + draw = true; + break; + + default: + break; + } + } while (SDL_PollEvent(&event)); + + + if (draw) { + 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 */ |