summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore17
-rw-r--r--Makefile.am30
-rw-r--r--config.h.in61
-rw-r--r--configure.ac95
-rw-r--r--data/simple.frag57
-rw-r--r--data/simple.vert33
-rw-r--r--src/Makefile.am27
-rw-r--r--src/main.cpp691
-rw-r--r--src/shader.cpp69
-rw-r--r--src/shader.h41
10 files changed, 1121 insertions, 0 deletions
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 <inttypes.h> 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 <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <unistd.h> 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 <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;
+
+/* 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 <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 */