From 18b7caedb71a56381fafaa724ad3c9fa2dc8e465 Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Tue, 11 Dec 2012 12:34:57 -0800 Subject: Initial import --- .gitignore | 17 ++ Makefile.am | 30 +++ config.h.in | 61 +++++ configure.ac | 95 ++++++++ data/simple.frag | 57 +++++ data/simple.vert | 33 +++ src/Makefile.am | 27 +++ src/main.cpp | 691 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/shader.cpp | 69 ++++++ src/shader.h | 41 ++++ 10 files changed, 1121 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile.am create mode 100644 config.h.in create mode 100644 configure.ac create mode 100644 data/simple.frag create mode 100644 data/simple.vert create mode 100644 src/Makefile.am create mode 100644 src/main.cpp create mode 100644 src/shader.cpp create mode 100644 src/shader.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7b1eaf7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +Makefile +Makefile.in +aclocal.m4 +autom4te.cache/ +config.h +config.h.in~ +config.log +config.status +configure +depcomp +install-sh +missing +.deps/ +src/bezier_surface +stamp-h1 +*.o +*.in diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..e2a677f --- /dev/null +++ b/Makefile.am @@ -0,0 +1,30 @@ +# Copyright © 2012 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. + +AUTOMAKE_OPTIONS = foreign +SUBDIRS = src + +EXTRA_DIST = \ + data/simple.vert \ + data/simple.frag \ + src/shader.h \ + src/nano_kontrol.h diff --git a/config.h.in b/config.h.in new file mode 100644 index 0000000..952c75a --- /dev/null +++ b/config.h.in @@ -0,0 +1,61 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `GL' library (-lGL). */ +#undef HAVE_LIBGL + +/* Define to 1 if you have the `GLEW' library (-lGLEW). */ +#undef HAVE_LIBGLEW + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Version number of package */ +#undef VERSION diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..c4ba3dc --- /dev/null +++ b/configure.ac @@ -0,0 +1,95 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ(2.61) +AC_INIT(bezier_surface, 2012Q4.1, idr@freedesktop.org, bezier_surface) +AC_CONFIG_SRCDIR([Makefile.am]) +AM_CONFIG_HEADER([config.h]) + +AM_INIT_AUTOMAKE + +# Checks for programs. +AC_PROG_CXX +AC_PROG_CC +AC_PROG_MAKE_SET +AC_PROG_RANLIB +PKG_PROG_PKG_CONFIG + +# Checks for libraries. + +# Checks for header files. + +# Checks for typedefs, structures, and compiler characteristics. + +# Checks for library functions. +AC_HEADER_STDC + + +# SDL is required for this application. Find its header files and +# libraries. Make sure they exist. + +CPPFLAGS="$CPPFLAGS `sdl-config --cflags`" +LIBS="$LIBS `sdl-config --libs`" + +AC_CHECK_HEADER(SDL.h, [], [exit 1]) + +CPPFLAGS="$CPPFLAGS `pkg-config --cflags alsa`" +LIBS="$LIBS `pkg-config --libs alsa`" + +# OpenGL is required for this application. Find its header files and +# libraries. Make sure they exist. + +AC_CHECK_HEADER(GL/gl.h, [], [exit 1]) +AC_CHECK_LIB([GL], [glVertexPointer], [], [exit 1]) + + +# GLEW is required for this application. Find its header files and +# libraries. Make sure they exist. + +AC_CHECK_HEADER(GL/glew.h, [], [exit 1]) +AC_CHECK_LIB([GLEW], [glewInit], [], [exit 1], [-lGL]) + +AC_LANG_CPLUSPLUS + +PKG_CHECK_MODULES([GLU3], [glu3]) +AC_SUBST(GLU3_CFLAGS) +AC_SUBST(GLU3_LIBS) + +AC_ARG_ENABLE([debug], + [AS_HELP_STRING([--enable-debug], + [use debug compiler flags and macros @<:@default=disabled@:>@])], + [enable_debug="$enableval"], + [enable_debug=no] +) +if test "x$enable_debug" = xyes; then + DEFINES="$DEFINES -DDEBUG" + if test "x$GCC" = xyes; then + # Remove any -g flags from the command line + CFLAGS=[`echo $CFLAGS | sed 's/-g[^ \t]*[ \t]*//g'`] + CFLAGS="$CFLAGS -ggdb3 -fstack-protector -D_FORTIFY_SOURCE=2" + fi + if test "x$GXX" = xyes; then + # Remove any -g flags from the command line + CXXFLAGS=[`echo $CXXFLAGS | sed 's/-g[^ \t]*[ \t]*//g'`] + CXXFLAGS="$CXXFLAGS -ggdb3 -fstack-protector -D_FORTIFY_SOURCE=2" + fi +fi + + +if test "x$GCC" = xyes ; then + WARN="-Wall -Wextra -Wunsafe-loop-optimizations -Wstack-protector" +else + WARN="" +fi + +if test "x$GXX" = xyes ; then + WARN="-Wall -Wextra -Wunsafe-loop-optimizations -Wstack-protector" +else + WARN="" +fi + +CFLAGS="$CFLAGS $WARN" +CXXFLAGS="$CXXFLAGS $WARN" + +AC_OUTPUT([Makefile + src/Makefile]) diff --git a/data/simple.frag b/data/simple.frag new file mode 100644 index 0000000..89480f7 --- /dev/null +++ b/data/simple.frag @@ -0,0 +1,57 @@ +#version 130 +#extension GL_ARB_explicit_attrib_location: require + +#define DO_LIGHTING 0 + +/* Debug mode. This will cause the value supplied as 'normal_cs' to be + * written as the output color. The normal will be remapped from the usual + * [-1,1] range to [0,1]. + */ +#define SHOW_NORMALS 0 + +/* Approximate (u,v) at the current fragment. Passed from the vertex shader, + * this is used to determine the base color of the fragment. + */ +in vec2 frag_uv; + +// Direction of the normal in camera-space. +in vec3 normal_cs; + +// Direction to the light in camera-space. +uniform vec3 light_dir_cs = normalize(vec3(0., 1., 0.)); + +layout(location = 0) out vec4 color; + +void main(void) +{ + /* Partition the surface into a checkerboard of 10x10 tiles that + * alternate colors. + */ + ivec2 tile = ivec2(floor(frag_uv * 10.)); + + vec3 base_color = bool((tile.x ^ tile.y) & 1) + ? vec3(0.00784313679, 0.470588207, 0.996078372) + : vec3(0.00784313679, 0.992156804, 0.00784313679); + + /* Renormalize the normal to account for interpolation errors. + */ + vec3 n_cs = normalize(normal_cs); + + /* If the polygon is back-facing, change the color and invert the + * normal. This causes back-facing polygons to have different colors + * and be correctly lit. + */ + if (! gl_FrontFacing) { + n_cs = -n_cs; + base_color = base_color.zxy; + } + +#if DO_LIGHTING + float diff = min(max(.2, dot(n_cs, light_dir_cs)), 1.); + color = vec4(base_color * diff, 1.); +#elif SHOW_NORMALS + color = vec4(normal_cs * .5 + .5, 1.); +#else + color = vec4(base_color, 1.); +#endif +} diff --git a/data/simple.vert b/data/simple.vert new file mode 100644 index 0000000..1850a67 --- /dev/null +++ b/data/simple.vert @@ -0,0 +1,33 @@ +#version 130 +#extension GL_ARB_explicit_attrib_location: require + +uniform mat4 mvp; +uniform mat3 mv_normal; + +layout(location = 0) in vec2 uv; + +out vec4 color; +out vec2 frag_uv; + +// Direction of the normal in camera-space. +out vec3 normal_cs; + +void main(void) +{ + /* Center the region at the origin. Assume that it's 7 units by 7 + * units. + */ + vec2 bias_uv = uv - 0.5; + vec4 position = vec4(bias_uv.x * 7., + 0.0, + bias_uv.y * 7., + 1.0); + gl_Position = mvp * position; + + /* Transform the normal by the inverse-transpose of the model-view + * matrix. + */ + normal_cs = mv_normal * vec3(0., 1., 0.); + + frag_uv = uv; +} diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..8d6d249 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,27 @@ +# Copyright © 2012 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 = bezier_surface +bezier_surface_SOURCES = main.cpp shader.cpp + +AM_CXXFLAGS= $(GLU3_CFLAGS) +bezier_surface_LDADD = $(FRAMEWORK_LIBS) $(GLU3_LIBS) -lEGL diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..34d4959 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,691 @@ +/* + * Copyright © 2009, 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 "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; + +/* Which data set is begin used for the patches? See set_control_points. + */ +static unsigned data_set = 0; + +static Uint32 t0; + +static GLuint bo; + +static GLUmat4 projection_matrix; + +class bezier_patch_program : public shader_program { +public: + bezier_patch_program(GLuint vs, GLuint fs) + : shader_program(vs, fs, 0) + { + /* empty */ + } + + void set_attribute_locations() + { + } + + void get_uniform_locations() + { + this->mvp_uniform = + glGetUniformLocation(this->program, "mvp"); + this->mv_normal_uniform = + glGetUniformLocation(this->program, "mv_normal"); + this->light_dir_cs_uniform = + glGetUniformLocation(this->program, "light_dir_cs"); + } + + GLint mvp_uniform; + GLint mv_normal_uniform; + GLint light_dir_cs_uniform; +}; + +static bezier_patch_program *patch_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->uv = &data[0 * num_verts]; + this->uv_offset = (uintptr_t) ((char *) this->uv + - (char *) _data) + + base_offset; + + this->position = NULL; + this->normal = NULL; + this->tangent = NULL; + this->vertex_count = num_verts; + + this->elt = (GLushort *) &data[1 * 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 uv_offset; + + GLenum mode; +}; + +class patch_producer : public GLUmeshProducer { +public: + patch_producer(unsigned rows, unsigned columns) + : GLUmeshProducer(rows, columns, columns) + { + /* empty */ + } + + virtual unsigned vertex_count(void) const + { + return (this->rows + 1) * this->columns; + } + + virtual void generate(GLUshapeConsumer *consumer) const + { + unsigned idx = 0; + const float u_step = 1. / (this->columns - 1); + const float v_step = 1. / this->rows; + float u; + float v; + + v = 0; + for (unsigned i = 0; i < (this->rows + 1); i++ ) { + u = 0.; + for (unsigned j = 0; j < this->columns; j++) { + consumer->uv[idx] = GLUvec4(u, v, 0., 0.); + idx++; + u += u_step; + } + + v += v_step; + } + + consumer->vertex_batch(NULL, + NULL, + NULL, + consumer->uv, + idx); + + GLUmeshProducer::generate(consumer); + } +}; + +static simple_consumer *mesh_sink; + +/** + * Fill a buffer object with data defining a worm. + */ +static simple_consumer * +fill_in_object_data(GLuint buf) +{ + patch_producer mesh_source(29, 29); + + const unsigned num_verts = mesh_source.vertex_count(); + const unsigned num_elts = mesh_source.element_count(); + + const unsigned size = (4 * sizeof(float) * 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); + mesh_source.generate(cons); + + glUnmapBuffer(GL_ARRAY_BUFFER); + + return cons; +} + +static GLUvec4 +lerp(GLUvec4 const &a, GLUvec4 const &b, float t) +{ + return (b - a) * t + a; +} + +static void +set_control_points(float angle, GLUvec4 *p) +{ + static const GLUvec4 a[16] = { + GLUvec4( 9.0f, 0.0f, 7.0f, 1.0f), + GLUvec4( 4.5f, 0.0f, 7.0f, 1.0f), + GLUvec4(-4.5f, 0.0f, 7.0f, 1.0f), + GLUvec4(-9.0f, 0.0f, 7.0f, 1.0f), + + GLUvec4( 9.0f, 0.0f, 3.5f, 1.0f), + GLUvec4( 4.5f, 0.0f, 3.5f, 1.0f), + GLUvec4(-4.5f, 0.0f, 3.5f, 1.0f), + GLUvec4(-9.0f, 0.0f, 3.5f, 1.0f), + + GLUvec4( 9.0f, 0.0f, -3.5f, 1.0f), + GLUvec4( 4.5f, 0.0f, -3.5f, 1.0f), + GLUvec4(-4.5f, 0.0f, -3.5f, 1.0f), + GLUvec4(-9.0f, 0.0f, -3.5f, 1.0f), + + GLUvec4( 9.0f, 0.0f, -7.0f, 1.0f), + GLUvec4( 4.5f, 0.0f, -7.0f, 1.0f), + GLUvec4(-4.5f, 0.0f, -7.0f, 1.0f), + GLUvec4(-9.0f, 0.0f, -7.0f, 1.0f), + }; + + static const GLUvec4 b[16] = { + GLUvec4( 2.0f, 0.0f, 1.0f, 1.0f), + GLUvec4( 4.5f, 4.0f, 7.0f, 1.0f), + GLUvec4(-4.5f, 4.0f, 7.0f, 1.0f), + GLUvec4(-2.0f, 0.0f, 1.0f, 1.0f), + + GLUvec4( 4.0f, 2.0f, 0.5f, 1.0f), + GLUvec4( 4.5f, 8.0f, 0.5f, 1.0f), + GLUvec4(-4.5f, 8.0f, 0.5f, 1.0f), + GLUvec4(-4.0f, 2.0f, 0.5f, 1.0f), + + GLUvec4( 4.0f, 2.0f, -0.5f, 1.0f), + GLUvec4( 4.5f, 8.0f, -0.5f, 1.0f), + GLUvec4(-4.5f, 8.0f, -0.5f, 1.0f), + GLUvec4(-4.0f, 2.0f, -0.5f, 1.0f), + + GLUvec4( 2.0f, 0.0f, -1.0f, 1.0f), + GLUvec4( 4.5f, 4.0f, -7.0f, 1.0f), + GLUvec4(-4.5f, 4.0f, -7.0f, 1.0f), + GLUvec4(-2.0f, 0.0f, -1.0f, 1.0f), + }; + +#define Z (0.866025404 * 10.) +#define Y (0.5 * 10.) + + static const GLUvec4 c[16] = { + GLUvec4( 9.0f, 0.0f, 0.0f, 1.0f), + GLUvec4( 4.5f, 0.0f, 0.0f, 1.0f), + GLUvec4(-4.5f, 0.0f, 0.0f, 1.0f), + GLUvec4(-9.0f, 0.0f, 0.0f, 1.0f), + + GLUvec4( 9.0f, Y, Z, 1.0f), + GLUvec4( 4.5f, Y, Z, 1.0f), + GLUvec4(-4.5f, Y, Z, 1.0f), + GLUvec4(-9.0f, Y, Z, 1.0f), + + GLUvec4( 9.0f, Y, -Z, 1.0f), + GLUvec4( 4.5f, Y, -Z, 1.0f), + GLUvec4(-4.5f, Y, -Z, 1.0f), + GLUvec4(-9.0f, Y, -Z, 1.0f), + + GLUvec4( 9.0f, 0.0f, 0.0f, 1.0f), + GLUvec4( 4.5f, 0.0f, 0.0f, 1.0f), + GLUvec4(-4.5f, 0.0f, 0.0f, 1.0f), + GLUvec4(-9.0f, 0.0f, 0.0f, 1.0f), + }; + +#undef Z +#undef Y + + const GLUvec4 *const target = (data_set == 0) ? b : c; + const float t = 1. - ((cos(angle) + 1.) / 2.); + + for (unsigned i = 0; i < ARRAY_SIZE(a); i++) + p[i] = lerp(a[i], target[i], t); +} + +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; + + + /* For the current time, determine the location of the control points. + */ + GLUvec4 control_points[16]; + set_control_points(y_angle, control_points); + + const GLUvec4 eye(eye_distance * cos(y_angle), + 5.0f, + eye_distance * sin(y_angle), + 0.0f); + + const GLUmat4 view(gluLookAt(eye, + GLUvec4(0.0f, 3.0f, 0.0f, 0.0f), + GLUvec4(0.0f, 1.0f, 0.0f, 0.0f))); + + const GLUmat4 mv(view); + const GLUmat4 mvp(projection_matrix * mv); + + /* Based on the current time, set the position of the light in + * world-space. + */ + const GLUvec4 light_dir_ws(GLUvec4(0., + fabs(cos(y_angle)), + sin(y_angle), + 0.)); + + /* Transform the light position from world-space to camera-space + * because all lighting calculations in the shaders will happen in + * camera-space. + */ + const GLUvec4 light_dir_cs(view * light_dir_ws); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + + /* Render the worm. + */ + glUseProgram(patch_program->program); + + if (timer_queries[0]) + glBeginQuery(GL_TIME_ELAPSED, timer_queries[0]); + + glUniform3fv(patch_program->light_dir_cs_uniform, 1, + (float *) &light_dir_cs); + glUniformMatrix4fv(patch_program->mvp_uniform, 1, false, + (float *) &mvp); + + /* Since the model-view matrix is an orthonormal basis, we can just + * use the upper 3x3 portion for transforming normals. + */ + const float mv_normal[9] = { + mv.col[0].values[0], mv.col[0].values[1], mv.col[0].values[2], + mv.col[1].values[0], mv.col[1].values[1], mv.col[1].values[2], + mv.col[2].values[0], mv.col[2].values[1], mv.col[2].values[2], + }; + glUniformMatrix3fv(patch_program->mv_normal_uniform, 1, false, + mv_normal); + + glDrawElements(mesh_sink->mode, + mesh_sink->elt_count, + GL_UNSIGNED_SHORT, + BUFFER_OFFSET(mesh_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 bezier_patch_program * +create_program(GLuint vs, GLuint fs, const char *name) +{ + bezier_patch_program *p = new bezier_patch_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 (patch_program) + delete patch_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"); + + patch_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_3_0) { + printf ("Sorry, this demo requires OpenGL 3.0 or later.\n"); + exit(1); + } + + if (!GLEW_ARB_explicit_attrib_location) { + printf ("Sorry, this demo requires " + "GL_ARB_explicit_attrib_location.\n"); + exit(1); + } + + if (GLEW_EXT_timer_query || GLEW_ARB_timer_query) + glGenQueries(ARRAY_SIZE(timer_queries), timer_queries); + + build_all_shaders(); + + glClearColor(.1f, .1f, .1f, 0.f); + glEnable(GL_DEPTH_TEST); + + glGenBuffers(1, & bo); + mesh_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(mesh_sink->uv_offset)); + glEnableVertexAttribArray(0); + + + 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 'd': + /* Toggle between the two data sets. + */ + data_set ^= 1; + 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 +#include +#include +#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 */ -- cgit v1.2.3