=== added file 'COPYING.SGI'
--- COPYING.SGI	1970-01-01 00:00:00 +0000
+++ COPYING.SGI	2012-04-30 18:37:22 +0000
@@ -0,0 +1,40 @@
+The data and some of the control logic for the "ideas" scene was adapted from
+the "Ideas in Motion" GLUT demo.  Per the rights granted by the source for that
+demo, the following notice is reproduced here:
+
+/*
+ * (c) Copyright 1993, Silicon Graphics, Inc.
+ * ALL RIGHTS RESERVED 
+ * Permission to use, copy, modify, and distribute this software for 
+ * any purpose and without fee is hereby granted, provided that the above
+ * copyright notice appear in all copies and that both the copyright notice
+ * and this permission notice appear in supporting documentation, and that 
+ * the name of Silicon Graphics, Inc. not be used in advertising
+ * or publicity pertaining to distribution of the software without specific,
+ * written prior permission. 
+ *
+ * THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS"
+ * AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE.  IN NO EVENT SHALL SILICON
+ * GRAPHICS, INC.  BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT,
+ * SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY
+ * KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION,
+ * LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF
+ * THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC.  HAS BEEN
+ * ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
+ * POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE.
+ * 
+ * US Government Users Restricted Rights 
+ * Use, duplication, or disclosure by the Government is subject to
+ * restrictions set forth in FAR 52.227.19(c)(2) or subparagraph
+ * (c)(1)(ii) of the Rights in Technical Data and Computer Software
+ * clause at DFARS 252.227-7013 and/or in similar or successor
+ * clauses in the FAR or the DOD or NASA FAR Supplement.
+ * Unpublished-- rights reserved under the copyright laws of the
+ * United States.  Contractor/manufacturer is Silicon Graphics,
+ * Inc., 2011 N.  Shoreline Blvd., Mountain View, CA 94039-7311.
+ *
+ * OpenGL(TM) is a trademark of Silicon Graphics, Inc.
+ */

=== modified file 'android/jni/Android.ndk.mk'
--- android/jni/Android.ndk.mk	2012-03-22 10:37:35 +0000
+++ android/jni/Android.ndk.mk	2012-05-07 22:31:59 +0000
@@ -19,14 +19,27 @@
 
 include $(CLEAR_VARS)
 
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_MODULE := libglmark2-ideas
+LOCAL_CFLAGS := -DGLMARK_DATA_PATH="" -DUSE_GLESv2 -Werror -Wall -Wextra\
+                -Wnon-virtual-dtor
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/src \
+                    $(LOCAL_PATH)/src/libmatrix
+LOCAL_SRC_FILES := $(subst $(LOCAL_PATH)/,,$(wildcard $(LOCAL_PATH)/src/scene-ideas/*.cc))
+
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE := libglmark2-android
-LOCAL_STATIC_LIBRARIES := libglmark2-matrix libglmark2-png
+LOCAL_STATIC_LIBRARIES := libglmark2-matrix libglmark2-png libglmark2-ideas
 LOCAL_CFLAGS := -DGLMARK_DATA_PATH="" -DGLMARK_VERSION="\"2012.03\"" \
                 -DUSE_GLESv2 -Werror -Wall -Wextra -Wnon-virtual-dtor
 LOCAL_LDLIBS := -landroid -llog -lGLESv2 -lEGL -lz
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/src \
                     $(LOCAL_PATH)/src/libmatrix \
+                    $(LOCAL_PATH)/src/scene-ideas \
                     $(LOCAL_PATH)/src/libpng
 LOCAL_SRC_FILES := $(filter-out src/canvas% src/main.cpp, \
                      $(subst $(LOCAL_PATH)/,,$(wildcard $(LOCAL_PATH)/src/*.cpp))) \

=== added file 'data/shaders/ideas-lamp-lit.frag'
--- data/shaders/ideas-lamp-lit.frag	1970-01-01 00:00:00 +0000
+++ data/shaders/ideas-lamp-lit.frag	2012-05-03 18:10:31 +0000
@@ -0,0 +1,68 @@
+struct LightSourceParameters
+{
+    vec4 ambient;
+    vec4 diffuse;
+    vec4 specular;
+    vec4 position;
+};
+LightSourceParameters lightSource[3];
+uniform mat4 modelview;
+uniform vec4 light0Position;
+uniform vec4 light1Position;
+uniform vec4 light2Position;
+varying vec3 vertex_normal;
+varying vec4 vertex_position;
+varying vec3 eye_direction;
+
+vec3 unitvec(vec4 v1, vec4 v2)
+{
+    if (v1.w == 0.0 && v2.w == 0.0)
+        return vec3(v2 - v1);
+    if (v1.w == 0.0)
+        return vec3(-v1);
+    if (v2.w == 0.0)
+        return vec3(v2);
+    return v2.xyz/v2.w - v1.xyz/v1.w;
+}
+
+void main()
+{
+    lightSource[0] = LightSourceParameters(
+        vec4(0.0, 0.0, 0.0, 1.0),
+        vec4(1.0, 1.0, 1.0, 1.0),
+        vec4(1.0, 1.0, 1.0, 1.0),
+        vec4(0.0, 1.0, 0.0, 0.0)
+    );
+    lightSource[1] = LightSourceParameters(
+        vec4(0.0, 0.0, 0.0, 1.0),
+        vec4(0.3, 0.3, 0.5, 1.0),
+        vec4(0.3, 0.3, 0.5, 1.0),
+        vec4(-1.0, 0.0, 0.0, 0.0)
+    );
+    lightSource[2] = LightSourceParameters(
+        vec4(0.2, 0.2, 0.2, 1.0),
+        vec4(0.2, 0.2, 0.2, 1.0),
+        vec4(0.2, 0.2, 0.2, 1.0),
+        vec4(0.0, -1.0, 0.0, 0.0)
+    );
+    vec4 matAmbient = vec4(0.0, 0.0, 0.0, 1.0);
+    vec4 matDiffuse = vec4(1.0, 0.2, 0.2, 1.0);
+    vec4 matSpecular = vec4(0.5, 0.5, 0.5, 1.0);
+    float matShininess = 20.0;
+    vec4 diffuseSum = vec4(0.0, 0.0, 0.0, 0.0);
+    vec4 specularSum = vec4(0.0, 0.0, 0.0, 0.0);
+    vec4 ambientSum = vec4(0.0, 0.0, 0.0, 0.0);
+    vec3 normalized_normal = normalize(vertex_normal);
+    lightSource[0].position = light0Position;
+    lightSource[1].position = light1Position;
+    lightSource[2].position = light2Position;
+    for (int light = 0; light < 3; light++) {
+        vec4 light_position = lightSource[light].position;
+        vec3 light_direction = normalize(unitvec(vertex_position, light_position));
+        vec3 reflection = reflect(-light_direction, normalized_normal);
+        specularSum += pow(max(0.0, dot(reflection, eye_direction)), matShininess) * lightSource[light].specular;
+        diffuseSum += max(0.0, dot(normalized_normal, light_direction)) * lightSource[light].diffuse;
+        ambientSum += lightSource[light].ambient;
+    }
+    gl_FragColor = (matSpecular * specularSum) + (matAmbient * ambientSum) + (matDiffuse * diffuseSum);
+}

=== added file 'data/shaders/ideas-lamp-lit.vert'
--- data/shaders/ideas-lamp-lit.vert	1970-01-01 00:00:00 +0000
+++ data/shaders/ideas-lamp-lit.vert	2012-05-03 18:10:31 +0000
@@ -0,0 +1,28 @@
+uniform mat4 projection;
+uniform mat4 modelview;
+uniform mat3 normalMatrix;
+varying vec3 vertex_normal;
+varying vec4 vertex_position;
+varying vec3 eye_direction;
+attribute vec3 vertex;
+attribute vec3 normal;
+
+vec3 unitvec(vec4 v1, vec4 v2)
+{
+    if (v1.w == 0.0 && v2.w == 0.0)
+        return vec3(v2 - v1);
+    if (v1.w == 0.0)
+        return vec3(-v1);
+    if (v2.w == 0.0)
+        return vec3(v2);
+    return v2.xyz/v2.w - v1.xyz/v1.w;
+}
+
+void main()
+{
+    vec4 curVertex = vec4(vertex.x, vertex.y, vertex.z, 1.0);
+    gl_Position = projection * modelview * curVertex;
+    vertex_normal = normalMatrix * normal;
+    vertex_position = modelview * curVertex;
+    eye_direction = normalize(unitvec(vertex_position, vec4(0.0, 0.0, 0.0, 1.0)));
+}

=== added file 'data/shaders/ideas-lamp-unlit.frag'
--- data/shaders/ideas-lamp-unlit.frag	1970-01-01 00:00:00 +0000
+++ data/shaders/ideas-lamp-unlit.frag	2012-05-03 18:10:31 +0000
@@ -0,0 +1,6 @@
+varying vec4 color;
+
+void main()
+{
+    gl_FragColor = color;
+}

=== added file 'data/shaders/ideas-lamp-unlit.vert'
--- data/shaders/ideas-lamp-unlit.vert	1970-01-01 00:00:00 +0000
+++ data/shaders/ideas-lamp-unlit.vert	2012-05-03 18:10:31 +0000
@@ -0,0 +1,11 @@
+uniform mat4 projection;
+uniform mat4 modelview;
+attribute vec3 vertex;
+varying vec4 color;
+
+void main()
+{
+    vec4 curVertex = vec4(vertex.x, vertex.y, vertex.z, 1.0);
+    gl_Position = projection * modelview * curVertex;
+    color = vec4(1.0, 1.0, 1.0, 1.0);
+}

=== added file 'data/shaders/ideas-logo-flat.frag'
--- data/shaders/ideas-logo-flat.frag	1970-01-01 00:00:00 +0000
+++ data/shaders/ideas-logo-flat.frag	2012-05-03 18:10:31 +0000
@@ -0,0 +1,6 @@
+uniform vec4 logoColor;
+
+void main()
+{
+    gl_FragColor = logoColor;
+}

=== added file 'data/shaders/ideas-logo-flat.vert'
--- data/shaders/ideas-logo-flat.vert	1970-01-01 00:00:00 +0000
+++ data/shaders/ideas-logo-flat.vert	2012-05-03 18:10:31 +0000
@@ -0,0 +1,9 @@
+uniform mat4 projection;
+uniform mat4 modelview;
+attribute vec3 vertex;
+
+void main()
+{
+    vec4 curVertex = vec4(vertex.x, vertex.y, vertex.z, 1.0);
+    gl_Position = projection * modelview * curVertex;
+}

=== added file 'data/shaders/ideas-logo-shadow.frag'
--- data/shaders/ideas-logo-shadow.frag	1970-01-01 00:00:00 +0000
+++ data/shaders/ideas-logo-shadow.frag	2012-05-03 18:10:31 +0000
@@ -0,0 +1,10 @@
+uniform sampler2D tex;
+
+void main()
+{
+    vec2 curPos = gl_FragCoord.xy / 32.0;
+    vec4 color = texture2D(tex, curPos);
+    if (color.w < 0.5)
+        discard;
+    gl_FragColor = color;
+}

=== added file 'data/shaders/ideas-logo-shadow.vert'
--- data/shaders/ideas-logo-shadow.vert	1970-01-01 00:00:00 +0000
+++ data/shaders/ideas-logo-shadow.vert	2012-05-03 18:10:31 +0000
@@ -0,0 +1,9 @@
+uniform mat4 projection;
+uniform mat4 modelview;
+attribute vec3 vertex;
+
+void main()
+{
+    vec4 curVertex = vec4(vertex.x, vertex.y, vertex.z, 1.0);
+    gl_Position = projection * modelview * curVertex;
+}

=== added file 'data/shaders/ideas-logo.frag'
--- data/shaders/ideas-logo.frag	1970-01-01 00:00:00 +0000
+++ data/shaders/ideas-logo.frag	2012-05-03 18:10:31 +0000
@@ -0,0 +1,35 @@
+uniform vec4 light0Position;
+varying vec3 vertex_normal;
+varying vec4 vertex_position;
+varying vec3 eye_direction;
+
+vec3 unitvec(vec4 v1, vec4 v2)
+{
+    if (v1.w == 0.0 && v2.w == 0.0)
+        return vec3(v2 - v1);
+    if (v1.w == 0.0)
+        return vec3(-v1);
+    if (v2.w == 0.0)
+        return vec3(v2);
+    return v2.xyz/v2.w - v1.xyz/v1.w;
+}
+
+void main()
+{
+    vec4 lightAmbient = vec4(0.0, 0.0, 0.0, 1.0);
+    vec4 lightDiffuse = vec4(1.0, 1.0, 1.0, 1.0);
+    vec4 lightSpecular = vec4(1.0, 1.0, 1.0, 1.0);
+    vec4 matAmbient = vec4(0.1, 0.1, 0.1, 1.0);
+    vec4 matDiffuse = vec4(0.5, 0.4, 0.7, 1.0);
+    vec4 matSpecular = vec4(1.0, 1.0, 1.0, 1.0);
+    float matShininess = 30.0;
+    vec3 light_direction = normalize(unitvec(vertex_position, light0Position));
+    vec3 normalized_normal = normalize(vertex_normal);
+    vec3 reflection = reflect(-light_direction, normalized_normal);
+    float specularTerm = pow(max(0.0, dot(reflection, eye_direction)), matShininess);
+    float diffuseTerm = max(0.0, dot(normalized_normal, light_direction));
+    vec4 specular = (lightSpecular * matSpecular);
+    vec4 ambient = (lightAmbient * matAmbient);
+    vec4 diffuse = (lightDiffuse * matDiffuse);
+    gl_FragColor = (specular * specularTerm) + ambient + (diffuse * diffuseTerm);
+}

=== added file 'data/shaders/ideas-logo.vert'
--- data/shaders/ideas-logo.vert	1970-01-01 00:00:00 +0000
+++ data/shaders/ideas-logo.vert	2012-05-03 18:10:31 +0000
@@ -0,0 +1,28 @@
+uniform mat4 projection;
+uniform mat4 modelview;
+uniform mat3 normalMatrix;
+varying vec3 vertex_normal;
+varying vec4 vertex_position;
+varying vec3 eye_direction;
+attribute vec3 vertex;
+attribute vec3 normal;
+
+vec3 unitvec(vec4 v1, vec4 v2)
+{
+    if (v1.w == 0.0 && v2.w == 0.0)
+        return vec3(v2 - v1);
+    if (v1.w == 0.0)
+        return vec3(-v1);
+    if (v2.w == 0.0)
+        return vec3(v2);
+    return v2.xyz/v2.w - v1.xyz/v1.w;
+}
+
+void main()
+{
+    vec4 curVertex = vec4(vertex.x, vertex.y, vertex.z, 1.0);
+    gl_Position = projection * modelview * curVertex;
+    vertex_normal = normalMatrix * normal;
+    vertex_position = modelview * curVertex;
+    eye_direction = normalize(unitvec(vertex_position, vec4(0.0, 0.0, 0.0, 1.0)));
+}

=== added file 'data/shaders/ideas-paper.frag'
--- data/shaders/ideas-paper.frag	1970-01-01 00:00:00 +0000
+++ data/shaders/ideas-paper.frag	2012-05-03 18:10:31 +0000
@@ -0,0 +1,6 @@
+varying vec4 color;
+
+void main()
+{
+    gl_FragColor = color;
+}

=== added file 'data/shaders/ideas-paper.vert'
--- data/shaders/ideas-paper.vert	1970-01-01 00:00:00 +0000
+++ data/shaders/ideas-paper.vert	2012-05-03 18:10:31 +0000
@@ -0,0 +1,22 @@
+uniform mat4 projection;
+uniform mat4 modelview;
+uniform vec3 lightPosition;
+uniform vec3 logoDirection;
+uniform float currentTime;
+attribute vec3 vertex;
+varying vec4 color;
+
+void main()
+{
+    vec4 curVertex = vec4(vertex.x, vertex.y, vertex.z, 1.0);
+    gl_Position = projection * modelview * curVertex;
+    float referenceTime = 15.0;
+    vec3 lightDirection = normalize(lightPosition - vertex);    
+    float c = max(dot(lightDirection, logoDirection), 0.0);
+    c = c * c * c * lightDirection.y;
+    if ((currentTime > referenceTime - 5.0) && (currentTime < referenceTime - 3.0))
+    {
+        c *= 1.0 - (currentTime - (referenceTime - 5.0)) * 0.5;
+    }
+    color = vec4(c, c, (c * 0.78125), 1.0);
+}

=== added file 'data/shaders/ideas-table.frag'
--- data/shaders/ideas-table.frag	1970-01-01 00:00:00 +0000
+++ data/shaders/ideas-table.frag	2012-05-03 18:10:31 +0000
@@ -0,0 +1,6 @@
+varying vec4 color;
+
+void main()
+{
+    gl_FragColor = color;
+}

=== added file 'data/shaders/ideas-table.vert'
--- data/shaders/ideas-table.vert	1970-01-01 00:00:00 +0000
+++ data/shaders/ideas-table.vert	2012-05-03 18:10:31 +0000
@@ -0,0 +1,22 @@
+uniform mat4 projection;
+uniform mat4 modelview;
+uniform vec3 lightPosition;
+uniform vec3 logoDirection;
+uniform float currentTime;
+attribute vec3 vertex;
+varying vec4 color;
+
+void main()
+{
+    vec4 curVertex = vec4(vertex.x, vertex.y, vertex.z, 1.0);
+    gl_Position = projection * modelview * curVertex;
+    float referenceTime = 15.0;
+    vec3 lightDirection = normalize(lightPosition - vertex);    
+    float c = max(dot(lightDirection, logoDirection), 0.0);
+    c = c * c * c * lightDirection.y;
+    if ((currentTime > referenceTime - 5.0) && (currentTime < referenceTime - 3.0))
+    {
+        c *= 1.0 - (currentTime - (referenceTime - 5.0)) * 0.5;
+    }
+    color = vec4(c, c, c, 1.0);
+}

=== added file 'data/shaders/ideas-text.frag'
--- data/shaders/ideas-text.frag	1970-01-01 00:00:00 +0000
+++ data/shaders/ideas-text.frag	2012-05-03 18:10:31 +0000
@@ -0,0 +1,6 @@
+varying vec4 color;
+
+void main()
+{
+    gl_FragColor = color;
+}

=== added file 'data/shaders/ideas-text.vert'
--- data/shaders/ideas-text.vert	1970-01-01 00:00:00 +0000
+++ data/shaders/ideas-text.vert	2012-05-03 18:10:31 +0000
@@ -0,0 +1,18 @@
+uniform mat4 projection;
+uniform mat4 modelview;
+uniform float currentTime;
+attribute vec2 vertex;
+varying vec4 color;
+
+void main()
+{
+    vec4 curVertex = vec4(vertex.x, vertex.y, 0.0, 1.0);
+    gl_Position = projection * modelview * curVertex;
+    float referenceTime = 15.0;
+    float c = 0.0;
+    if (currentTime > referenceTime * 1.0 - 5.0)
+    {
+        c = (currentTime - (referenceTime * 1.0 - 5.0)) / 2.0;
+    }
+    color = vec4(c, c, c, 1.0);
+}

=== added file 'data/shaders/ideas-under-table.frag'
--- data/shaders/ideas-under-table.frag	1970-01-01 00:00:00 +0000
+++ data/shaders/ideas-under-table.frag	2012-05-03 18:10:31 +0000
@@ -0,0 +1,6 @@
+varying vec4 color;
+
+void main()
+{
+    gl_FragColor = color;
+}

=== added file 'data/shaders/ideas-under-table.vert'
--- data/shaders/ideas-under-table.vert	1970-01-01 00:00:00 +0000
+++ data/shaders/ideas-under-table.vert	2012-05-03 18:10:31 +0000
@@ -0,0 +1,11 @@
+uniform mat4 projection;
+uniform mat4 modelview;
+attribute vec3 vertex;
+varying vec4 color;
+
+void main()
+{
+    vec4 curVertex = vec4(vertex.x, vertex.y, vertex.z, 1.0);
+    gl_Position = projection * modelview * curVertex;
+    color = vec4(0.0, 0.0, 0.0, 1.0);
+}

=== modified file 'src/android.cpp'
--- src/android.cpp	2012-02-15 16:00:26 +0000
+++ src/android.cpp	2012-05-08 13:53:58 +0000
@@ -186,6 +186,7 @@
     Benchmark::register_scene(*new ScenePulsar(*g_canvas));
     Benchmark::register_scene(*new SceneDesktop(*g_canvas));
     Benchmark::register_scene(*new SceneBuffer(*g_canvas));
+    Benchmark::register_scene(*new SceneIdeas(*g_canvas));
 
     g_benchmark_collection = new BenchmarkCollection();
     g_benchmark_collection->populate_from_options();

=== modified file 'src/default-benchmarks.h'
--- src/default-benchmarks.h	2011-11-14 12:39:08 +0000
+++ src/default-benchmarks.h	2012-05-09 16:06:33 +0000
@@ -60,6 +60,7 @@
         benchmarks.push_back("buffer:update-fraction=0.5:update-dispersion=0.9:columns=200:update-method=map:interleave=false");
         benchmarks.push_back("buffer:update-fraction=0.5:update-dispersion=0.9:columns=200:update-method=subdata:interleave=false");
         benchmarks.push_back("buffer:update-fraction=0.5:update-dispersion=0.9:columns=200:update-method=map:interleave=true");
+        benchmarks.push_back("ideas:speed=max:duration=12");
         benchmarks.push_back("conditionals:vertex-steps=0:fragment-steps=0");
         benchmarks.push_back("conditionals:vertex-steps=0:fragment-steps=5");
         benchmarks.push_back("conditionals:vertex-steps=5:fragment-steps=0");

=== modified file 'src/libmatrix/program.cc'
--- src/libmatrix/program.cc	2012-01-26 17:00:43 +0000
+++ src/libmatrix/program.cc	2012-04-30 16:31:04 +0000
@@ -19,6 +19,7 @@
 
 using std::string;
 using LibMatrix::mat4;
+using LibMatrix::mat3;
 using LibMatrix::vec2;
 using LibMatrix::vec3;
 using LibMatrix::vec4;
@@ -275,6 +276,17 @@
 }
 
 Program::Symbol&
+Program::Symbol::operator=(const mat3& m)
+{
+    if (type_ == Uniform)
+    {
+        // Our matrix representation is column-major, so transpose is false here.
+        glUniformMatrix3fv(location_, 1, GL_FALSE, m);
+    }
+    return *this;
+}
+
+Program::Symbol&
 Program::Symbol::operator=(const vec2& v)
 {
     if (type_ == Uniform)

=== modified file 'src/libmatrix/program.h'
--- src/libmatrix/program.h	2012-01-26 17:00:43 +0000
+++ src/libmatrix/program.h	2012-04-30 16:31:04 +0000
@@ -126,6 +126,7 @@
         // These members cause data to be bound to program variables, so
         // the program must be bound for use for these to be effective.
         Symbol& operator=(const LibMatrix::mat4& m);
+        Symbol& operator=(const LibMatrix::mat3& m);
         Symbol& operator=(const LibMatrix::vec2& v);
         Symbol& operator=(const LibMatrix::vec3& v);
         Symbol& operator=(const LibMatrix::vec4& v);

=== modified file 'src/main.cpp'
--- src/main.cpp	2012-03-22 10:37:04 +0000
+++ src/main.cpp	2012-04-27 18:00:45 +0000
@@ -60,6 +60,7 @@
     scenes.push_back(new ScenePulsar(canvas));
     scenes.push_back(new SceneDesktop(canvas));
     scenes.push_back(new SceneBuffer(canvas));
+    scenes.push_back(new SceneIdeas(canvas));
 
     for (vector<Scene*>::const_iterator iter = scenes.begin();
          iter != scenes.end();

=== added directory 'src/scene-ideas'
=== added file 'src/scene-ideas.cpp'
--- src/scene-ideas.cpp	1970-01-01 00:00:00 +0000
+++ src/scene-ideas.cpp	2012-05-09 20:38:24 +0000
@@ -0,0 +1,426 @@
+/*
+ * (c) Copyright 1993, Silicon Graphics, Inc.
+ * Copyright Š 2012 Linaro Limited
+ *
+ * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
+ *
+ * glmark2 is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * glmark2.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Jesse Barker
+ */
+#include "scene.h"
+#include "stack.h"
+#include "splines.h"
+#include "table.h"
+#include "logo.h"
+#include "lamp.h"
+#include "util.h"
+#include "log.h"
+#include <sys/time.h>
+
+using LibMatrix::Stack4;
+using LibMatrix::mat4;
+using LibMatrix::vec3;
+using LibMatrix::vec4;
+using LibMatrix::uvec3;
+using std::string;
+
+class SceneIdeasPrivate
+{
+public:
+    SceneIdeasPrivate() :
+        valid_(false),
+        currentSpeed_(SPEED_MAXIMUM),
+        currentTime_(START_TIME_),
+        timeOffset_(START_TIME_)
+    {
+        startTime_.tv_sec = 0;
+        startTime_.tv_usec = 0; 
+    }
+    ~SceneIdeasPrivate()
+    {
+    }
+    void initialize(const string& speed);
+    void reset_time();
+    void update_time();
+    void update_projection(const mat4& proj);
+    void draw();
+
+private:
+    float speed_from_optval(const string& optval);
+    void postIdle();
+    void initLights();
+    bool valid_;
+    Stack4 projection_;
+    Stack4 modelview_;
+    float currentSpeed_;
+    float currentTime_;
+    float timeOffset_;
+    struct timeval startTime_;
+    static const float CYCLE_TIME_;
+    static const float TIME_;
+    static const float START_TIME_;
+    static const float SPEED_SLOW;
+    static const float SPEED_MEDIUM;
+    static const float SPEED_FAST;
+    static const float SPEED_MAXIMUM;
+    // Table
+    Table table_;
+    // Logo
+    SGILogo logo_;
+    // Lamp
+    Lamp lamp_;
+    // Light constants
+    static const vec4 light0_position_;
+    static const vec4 light1_position_;
+    static const vec4 light2_position_;
+    // Object constants
+    ViewFromSpline viewFromSpline_;
+    ViewToSpline viewToSpline_;
+    LightPositionSpline lightPosSpline_;
+    LogoPositionSpline logoPosSpline_;
+    LogoRotationSpline logoRotSpline_;
+    vec3 viewFrom_;
+    vec3 viewTo_;
+    vec3 lightPos_;
+    vec3 logoPos_;
+    vec3 logoRot_;
+    vec4 lightPositions_[3];
+};
+
+const float SceneIdeasPrivate::SPEED_SLOW(0.2);
+const float SceneIdeasPrivate::SPEED_MEDIUM(0.4);
+const float SceneIdeasPrivate::SPEED_FAST(0.7);
+const float SceneIdeasPrivate::SPEED_MAXIMUM(1.0);
+const float SceneIdeasPrivate::TIME_(15.0);
+const float SceneIdeasPrivate::CYCLE_TIME_(TIME_ * 1.0 - 3.0);
+const float SceneIdeasPrivate::START_TIME_(0.6);
+const vec4 SceneIdeasPrivate::light0_position_(0.0, 1.0, 0.0, 0.0);
+const vec4 SceneIdeasPrivate::light1_position_(-1.0, 0.0, 0.0, 0.0);
+const vec4 SceneIdeasPrivate::light2_position_(0.0, -1.0, 0.0, 0.0);
+
+void
+SceneIdeasPrivate::initLights()
+{
+    const mat4& curMV(modelview_.getCurrent());
+    lightPositions_[0] = curMV * light0_position_;
+    lightPositions_[1] = curMV * light1_position_;
+    lightPositions_[2] = curMV * light2_position_;
+}
+
+void
+SceneIdeasPrivate::initialize(const string& speed)
+{
+    // Initialize the positions for the lights we'll use.
+    initLights();
+
+    // Tell the objects in the scene to initialize themselves.
+    table_.init();
+    if (!table_.valid())
+    {
+        Log::debug("SceneIdeas: table object not properly initialized!\n");
+        return;
+    }
+    logo_.init();
+    if (!logo_.valid())
+    {
+        Log::debug("SceneIdeas: logo object not properly initialized!\n");
+        return;
+    }
+    lamp_.init();
+    if (!lamp_.valid())
+    {
+        Log::debug("SceneIdeas: lamp object not properly initialized!\n");
+        return;
+    }
+
+    reset_time();
+
+    currentSpeed_ = speed_from_optval(speed);
+
+    // If we're here, we're okay to run.
+    valid_ = true;
+}
+
+void
+SceneIdeasPrivate::reset_time()
+{
+    timeOffset_ = START_TIME_;
+    gettimeofday(&startTime_, NULL);
+}
+
+void
+SceneIdeasPrivate::update_time()
+{
+    // Compute new time
+    struct timeval current = {0, 0};
+    gettimeofday(&current, NULL);
+    float timediff = (current.tv_sec - startTime_.tv_sec) + 
+        static_cast<double>(current.tv_usec - startTime_.tv_usec) / 1000000.0;
+    float sceneTime = timediff * currentSpeed_ + timeOffset_;
+
+    // Keep the current time in [START_TIME_..CYCLE_TIME_)
+    // Every other cycle starting with 0 start at the beginning and goes
+    // forward in time.  Other cycles start at the end and go backwards.
+    currentTime_ = std::fmod(sceneTime, CYCLE_TIME_);
+    unsigned int cycle = sceneTime/CYCLE_TIME_;
+    if (cycle % 2)
+    {
+        currentTime_ = CYCLE_TIME_ - currentTime_;
+    }
+}
+
+void
+SceneIdeasPrivate::update_projection(const mat4& proj)
+{
+    // Projection hasn't changed since last frame.
+    if (projection_.getCurrent() == proj)
+    {
+        return;
+    }
+
+    projection_.loadIdentity();
+    projection_ *= proj;
+}
+
+float
+SceneIdeasPrivate::speed_from_optval(const string& optval)
+{
+    float retVal(SPEED_MAXIMUM);
+    if (optval == "slow")
+    {
+        retVal = SPEED_SLOW;
+    }
+    else if (optval == "medium")
+    {
+        retVal = SPEED_MEDIUM;
+    }
+    else if (optval == "fast")
+    {
+        retVal = SPEED_FAST;
+    }
+    else if (optval != "max")
+    {
+        Log::error("Unknown speed option '%s', using default.\n", optval.c_str());
+    }
+
+    return retVal;
+}
+
+SceneIdeas::SceneIdeas(Canvas& canvas) :
+    Scene(canvas, "ideas")
+{
+    options_["speed"] = Scene::Option("speed", "max",
+                                      "Rendering speed [slow, medium, fast, max]");
+}
+
+SceneIdeas::~SceneIdeas()
+{
+    delete priv_;
+}
+
+bool
+SceneIdeas::load()
+{
+    running_ = false;
+    return true;
+}
+
+void
+SceneIdeas::unload()
+{
+}
+
+void
+SceneIdeas::setup()
+{
+    Scene::setup();
+    priv_ = new SceneIdeasPrivate();
+    priv_->initialize(options_["speed"].value);
+    priv_->update_projection(canvas_.projection());
+
+    // Core Scene state
+    currentFrame_ = 0;
+    running_ = true;
+    startTime_ = Util::get_timestamp_us() / 1000000.0;
+    lastUpdateTime_ = startTime_;
+}
+
+void
+SceneIdeas::update()
+{
+    Scene::update();
+    priv_->update_time();
+    priv_->update_projection(canvas_.projection());
+}
+
+void
+SceneIdeasPrivate::draw()
+{
+    viewFromSpline_.getCurrentVec(currentTime_, viewFrom_);
+    viewToSpline_.getCurrentVec(currentTime_, viewTo_);
+    lightPosSpline_.getCurrentVec(currentTime_, lightPos_);
+    logoPosSpline_.getCurrentVec(currentTime_, logoPos_);
+    logoRotSpline_.getCurrentVec(currentTime_, logoRot_);
+
+    // Tell the logo its new position
+    logo_.setPosition(logoPos_);
+
+    vec4 lp4(lightPos_.x(), lightPos_.y(), lightPos_.z(), 0.0);
+
+    //
+    // SHADOW
+    //
+    modelview_.loadIdentity();
+    modelview_.lookAt(viewFrom_.x(), viewFrom_.y(), viewFrom_.z(), 
+                      viewTo_.x(), viewTo_.y(), viewTo_.z(),
+                      0.0, 1.0, 0.0);
+
+    float pca(0.0);
+    if (viewFrom_.y() > 0.0)
+    {
+        table_.draw(modelview_, projection_, lightPos_, logoPos_, currentTime_, pca);
+    }
+
+    glEnable(GL_CULL_FACE); 
+    glDisable(GL_DEPTH_TEST); 
+
+    if (logoPos_.y() < 0.0)
+    {
+        // Set the color assuming we're still under the table.
+        uvec3 flatColor(128 / 2,  102 / 2,  179 / 2);
+        if (logoPos_.y() > -0.33)
+        {
+            // We're emerging from the table
+            float c(1.0 - logoPos_.y() / -0.33);
+            pca /= 4.0;
+            flatColor.x(static_cast<unsigned int>(128.0 * (1.0 - c) * 0.5 + 255.0 * pca * c));
+            flatColor.y(static_cast<unsigned int>(102.0 * (1.0 - c) * 0.5 + 255.0 * pca * c));
+            flatColor.z(static_cast<unsigned int>(179.0 * (1.0 - c) * 0.5 + 200.0 * pca * c));
+        }
+
+        modelview_.push();
+        modelview_.scale(0.04, 0.0, 0.04);
+        modelview_.rotate(-90.0, 1.0, 0.0, 0.0);
+        modelview_.rotate(0.1 * static_cast<int>(10.0 * logoRot_.z()), 0.0, 0.0, 1.0);
+        modelview_.rotate(0.1 * static_cast<int>(10.0 * logoRot_.y()), 0.0, 1.0, 0.0);
+        modelview_.rotate(0.1 * static_cast<int>(10.0 * logoRot_.x()), 1.0, 0.0, 0.0);
+        modelview_.rotate(0.1 * 353, 1.0, 0.0, 0.0);
+        modelview_.rotate(0.1 * 450, 0.0, 1.0, 0.0);
+
+        logo_.draw(modelview_, projection_, lightPositions_[0], SGILogo::LOGO_FLAT, flatColor);
+
+        modelview_.pop();
+    }
+    
+    if (logoPos_.y() > 0.0)
+    {
+        modelview_.push();
+        modelview_.translate(lightPos_.x(), lightPos_.y(), lightPos_.z());
+        mat4 tv;
+        tv[3][1] = -1.0;
+        tv[3][3] = 0.0;
+        tv[0][0] = tv[1][1] = tv[2][2] = lightPos_.y();
+        modelview_ *= tv;
+        modelview_.translate(-lightPos_.x() + logoPos_.x(),
+                             -lightPos_.y() + logoPos_.y(),
+                             -lightPos_.z() + logoPos_.z());
+        modelview_.scale(0.04, 0.04, 0.04);
+        modelview_.rotate(-90.0, 1.0, 0.0, 0.0);
+        modelview_.rotate(0.1 * static_cast<int>(10.0 * logoRot_.z()), 0.0, 0.0, 1.0);
+        modelview_.rotate(0.1 * static_cast<int>(10.0 * logoRot_.y()), 0.0, 1.0, 0.0);
+        modelview_.rotate(0.1 * static_cast<int>(10.0 * logoRot_.x()), 1.0, 0.0, 0.0);
+        modelview_.rotate(35.3, 1.0, 0.0, 0.0);
+        modelview_.rotate(45.0, 0.0, 1.0, 0.0);
+
+        logo_.draw(modelview_, projection_, lightPositions_[0], SGILogo::LOGO_SHADOW);
+
+        modelview_.pop();
+    }
+    //
+    // DONE SHADOW 
+    //
+
+    glEnable(GL_DEPTH_TEST);
+    glDisable(GL_CULL_FACE);
+
+    modelview_.loadIdentity();
+    modelview_.lookAt(viewFrom_.x(), viewFrom_.y(), viewFrom_.z(),
+                      viewTo_.x(), viewTo_.y(), viewTo_.z(), 
+                      0.0, 1.0, 0.0);
+    modelview_.push();
+    modelview_.translate(lightPos_.x(), lightPos_.y(), lightPos_.z());
+    modelview_.scale(0.1, 0.1, 0.1);
+    float x(lightPos_.x() - logoPos_.x());
+    float y(lightPos_.y() - logoPos_.y());
+    float z(lightPos_.z() - logoPos_.z());
+    double a3(0.0);
+    if (x != 0.0)
+    {
+        a3 = -atan2(z, x) * 10.0 * 180.0 / M_PI;
+    }
+    double a4(-atan2(sqrt(x * x + z * z), y) * 10.0 * 180.0 / M_PI);
+    modelview_.rotate(0.1 * static_cast<int>(a3), 0.0, 1.0, 0.0);
+    modelview_.rotate(0.1 * static_cast<int>(a4), 0.0, 0.0, 1.0);
+    modelview_.rotate(-90.0, 1.0, 0.0, 0.0);
+
+    lamp_.draw(modelview_, projection_, lightPositions_);
+
+    modelview_.pop();
+    
+    lightPositions_[0] = modelview_.getCurrent() * lp4;
+    
+    if (logoPos_.y() > -0.33)
+    {
+        modelview_.push();
+        modelview_.translate(logoPos_.x(), logoPos_.y(), logoPos_.z());
+        modelview_.scale(0.04, 0.04, 0.04);
+        modelview_.rotate(-90.0, 1.0, 0.0, 0.0);
+        modelview_.rotate(0.1 * static_cast<int>(10.0 * logoRot_.z()), 0.0, 0.0, 1.0);
+        modelview_.rotate(0.1 * static_cast<int>(10.0 * logoRot_.y()), 0.0, 1.0, 0.0);
+        modelview_.rotate(0.1 * static_cast<int>(10.0 * logoRot_.x()), 1.0, 0.0, 0.0);
+        modelview_.rotate(35.3, 1.0, 0.0, 0.0);
+        modelview_.rotate(45.0, 0.0, 1.0, 0.0);
+
+        logo_.draw(modelview_, projection_, lightPositions_[0], SGILogo::LOGO_NORMAL);
+
+        modelview_.pop();
+    }
+    
+    if (viewFrom_.y() < 0.0)
+    {
+        table_.drawUnder(modelview_, projection_);
+    }
+}
+
+void
+SceneIdeas::draw()
+{
+    priv_->draw();
+}
+
+Scene::ValidationResult
+SceneIdeas::validate()
+{
+    return Scene::ValidationSuccess;
+}
+
+void
+SceneIdeas::teardown()
+{
+    delete priv_;
+    priv_ = 0;
+    Scene::teardown();
+}

=== added file 'src/scene-ideas/a.cc'
--- src/scene-ideas/a.cc	1970-01-01 00:00:00 +0000
+++ src/scene-ideas/a.cc	2012-04-30 18:37:22 +0000
@@ -0,0 +1,179 @@
+/*
+ * Vertex position data describing the letter 'a'
+ *
+ * (c) Copyright 1993, Silicon Graphics, Inc.
+ * Copyright Š 2012 Linaro Limited
+ *
+ * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
+ *
+ * glmark2 is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * glmark2.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Jesse Barker
+ */
+#include "characters.h"
+
+using LibMatrix::vec2;
+
+LetterA::LetterA()
+{
+    // Vertex data...
+    vertexData_.push_back(vec2(5.618949, 10.261048));
+    vertexData_.push_back(vec2(5.322348, 9.438848));
+    vertexData_.push_back(vec2(5.124614, 10.030832));
+    vertexData_.push_back(vec2(4.860968, 9.488181));
+    vertexData_.push_back(vec2(4.811534, 9.932169));
+    vertexData_.push_back(vec2(3.938208, 9.438848));
+    vertexData_.push_back(vec2(3.658084, 9.685509));
+    vertexData_.push_back(vec2(2.784758, 8.994862));
+    vertexData_.push_back(vec2(2.801236, 9.175745));
+    vertexData_.push_back(vec2(1.960865, 8.172662));
+    vertexData_.push_back(vec2(1.186406, 7.761562));
+    vertexData_.push_back(vec2(1.252317, 6.561151));
+    vertexData_.push_back(vec2(0.576725, 6.610483));
+    vertexData_.push_back(vec2(0.939238, 5.525180));
+    vertexData_.push_back(vec2(0.164779, 4.883864));
+    vertexData_.push_back(vec2(0.840371, 4.818089));
+    vertexData_.push_back(vec2(0.230690, 3.963001));
+    vertexData_.push_back(vec2(0.939238, 4.242549));
+    vertexData_.push_back(vec2(0.609681, 3.255909));
+    vertexData_.push_back(vec2(1.268795, 3.963001));
+    vertexData_.push_back(vec2(1.021627, 3.075026));
+    vertexData_.push_back(vec2(1.861998, 4.045221));
+    vertexData_.push_back(vec2(1.829042, 3.535457));
+    vertexData_.push_back(vec2(2.817714, 4.818089));
+    vertexData_.push_back(vec2(3.163749, 4.998972));
+    vertexData_.push_back(vec2(3.971164, 6.643371));
+    vertexData_.push_back(vec2(4.267765, 6.725591));
+    vertexData_.push_back(vec2(4.663234, 7.630010));
+    vertexData_.push_back(vec2(5.404737, 9.734840));
+    vertexData_.push_back(vec2(4.646756, 9.669065));
+    vertexData_.push_back(vec2(5.108136, 8.731757));
+    vertexData_.push_back(vec2(4.679712, 8.600205));
+    vertexData_.push_back(vec2(4.926879, 7.564234));
+    vertexData_.push_back(vec2(4.366632, 6.692703));
+    vertexData_.push_back(vec2(4.663234, 5.344296));
+    vertexData_.push_back(vec2(3.888774, 4.850976));
+    vertexData_.push_back(vec2(4.630278, 4.094553));
+    vertexData_.push_back(vec2(3.954686, 3.963001));
+    vertexData_.push_back(vec2(4.828012, 3.798561));
+    vertexData_.push_back(vec2(4.168898, 3.321686));
+    vertexData_.push_back(vec2(5.157569, 3.864337));
+    vertexData_.push_back(vec2(4.514933, 3.091470));
+    vertexData_.push_back(vec2(5.553038, 4.045221));
+    vertexData_.push_back(vec2(5.305870, 3.634121));
+    vertexData_.push_back(vec2(5.932029, 4.176773));
+
+    // Index data...
+    indexData_.push_back(0);
+    indexData_.push_back(1);
+    indexData_.push_back(2);
+    indexData_.push_back(3);
+    indexData_.push_back(4);
+    indexData_.push_back(5);
+    indexData_.push_back(6);
+    indexData_.push_back(7);
+    indexData_.push_back(8);
+    indexData_.push_back(9);
+    indexData_.push_back(10);
+    indexData_.push_back(11);
+    indexData_.push_back(12);
+    indexData_.push_back(13);
+    indexData_.push_back(14);
+    indexData_.push_back(15);
+    indexData_.push_back(16);
+    indexData_.push_back(17);
+    indexData_.push_back(18);
+    indexData_.push_back(19);
+    indexData_.push_back(20);
+    indexData_.push_back(21);
+    indexData_.push_back(22);
+    indexData_.push_back(23);
+    indexData_.push_back(24);
+    indexData_.push_back(25);
+    indexData_.push_back(26);
+    indexData_.push_back(27);
+    indexData_.push_back(28);
+    indexData_.push_back(29);
+    indexData_.push_back(30);
+    indexData_.push_back(31);
+    indexData_.push_back(32);
+    indexData_.push_back(33);
+    indexData_.push_back(34);
+    indexData_.push_back(35);
+    indexData_.push_back(36);
+    indexData_.push_back(37);
+    indexData_.push_back(38);
+    indexData_.push_back(39);
+    indexData_.push_back(40);
+    indexData_.push_back(41);
+    indexData_.push_back(42);
+    indexData_.push_back(43);
+    indexData_.push_back(44);
+    indexData_.push_back(0);
+    indexData_.push_back(2);
+    indexData_.push_back(4);
+    indexData_.push_back(6);
+    indexData_.push_back(8);
+    indexData_.push_back(10);
+    indexData_.push_back(12);
+    indexData_.push_back(14);
+    indexData_.push_back(16);
+    indexData_.push_back(18);
+    indexData_.push_back(20);
+    indexData_.push_back(22);
+    indexData_.push_back(24);
+    indexData_.push_back(26);
+    indexData_.push_back(27);
+    indexData_.push_back(25);
+    indexData_.push_back(23);
+    indexData_.push_back(21);
+    indexData_.push_back(19);
+    indexData_.push_back(17);
+    indexData_.push_back(15);
+    indexData_.push_back(13);
+    indexData_.push_back(11);
+    indexData_.push_back(9);
+    indexData_.push_back(7);
+    indexData_.push_back(5);
+    indexData_.push_back(3);
+    indexData_.push_back(1);
+    indexData_.push_back(28);
+    indexData_.push_back(30);
+    indexData_.push_back(32);
+    indexData_.push_back(34);
+    indexData_.push_back(36);
+    indexData_.push_back(38);
+    indexData_.push_back(40);
+    indexData_.push_back(42);
+    indexData_.push_back(44);
+    indexData_.push_back(43);
+    indexData_.push_back(41);
+    indexData_.push_back(39);
+    indexData_.push_back(37);
+    indexData_.push_back(35);
+    indexData_.push_back(33);
+    indexData_.push_back(31);
+    indexData_.push_back(29);
+
+    // Primitive state so that the draw call can issue the primitives we want.
+    unsigned int curOffset(0);
+    primVec_.push_back(PrimitiveState(GL_TRIANGLE_STRIP, 28, curOffset));
+    curOffset += (28 * sizeof(unsigned int));
+    primVec_.push_back(PrimitiveState(GL_TRIANGLE_STRIP, 17, curOffset));
+    curOffset += (17 * sizeof(unsigned int));
+    primVec_.push_back(PrimitiveState(GL_LINE_STRIP, 28, curOffset));
+    curOffset += (28 * sizeof(unsigned int));
+    primVec_.push_back(PrimitiveState(GL_LINE_STRIP, 17, curOffset));
+}

=== added file 'src/scene-ideas/characters.h'
--- src/scene-ideas/characters.h	1970-01-01 00:00:00 +0000
+++ src/scene-ideas/characters.h	2012-05-08 20:53:58 +0000
@@ -0,0 +1,157 @@
+/*
+ * (c) Copyright 1993, Silicon Graphics, Inc.
+ * Copyright Š 2012 Linaro Limited
+ *
+ * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
+ *
+ * glmark2 is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * glmark2.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Jesse Barker
+ */
+#ifndef CHARACTERS_H_
+#define CHARACTERS_H_
+
+#include <vector>
+#include "vec.h"
+#include "gl-headers.h"
+
+class PrimitiveState
+{
+public:
+    PrimitiveState(unsigned int type, unsigned int count, unsigned int offset) :
+        type_(type),
+        count_(count),
+        bufferOffset_(offset) {}
+    ~PrimitiveState() {}
+    void issue() const
+    {
+        glDrawElements(type_, count_, GL_UNSIGNED_INT, 
+            reinterpret_cast<const GLvoid*>(bufferOffset_));
+    }
+private:
+    PrimitiveState();
+    unsigned int type_;         // Primitive type (e.g. GL_TRIANGLE_STRIP)
+    unsigned int count_;        // Number of primitives
+    unsigned int bufferOffset_; // Offset into the element array buffer
+};
+
+struct Character
+{
+    void draw()
+    {
+        glBindBuffer(GL_ARRAY_BUFFER, bufferObjects_[0]);
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferObjects_[1]);
+        glVertexAttribPointer(vertexIndex_, 2, GL_FLOAT, GL_FALSE, 0, 0);
+        glEnableVertexAttribArray(vertexIndex_);
+        for (std::vector<PrimitiveState>::const_iterator primIt = primVec_.begin();
+             primIt != primVec_.end();
+             primIt++)
+        {
+            primIt->issue();
+        }
+        glBindBuffer(GL_ARRAY_BUFFER, 0);
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+    }
+    void init(int vertexAttribIndex) 
+    {
+        vertexIndex_ = vertexAttribIndex;
+
+        // We need 2 buffers for our work here.  One for the vertex data.
+        // and one for the index data.
+        glGenBuffers(2, &bufferObjects_[0]);
+
+        // First, setup the vertex data by binding the first buffer object, 
+        // allocating its data store, and filling it in with our vertex data.
+        glBindBuffer(GL_ARRAY_BUFFER, bufferObjects_[0]);
+        glBufferData(GL_ARRAY_BUFFER, vertexData_.size() * sizeof(LibMatrix::vec2), 
+            &vertexData_.front(), GL_STATIC_DRAW);
+
+        // Finally, setup the pointer to our vertex data and enable this
+        // attribute array.
+        glVertexAttribPointer(vertexIndex_, 2, GL_FLOAT, GL_FALSE, 0, 0);
+        glEnableVertexAttribArray(vertexIndex_);
+
+        // Now repeat for our index data.
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferObjects_[1]);
+        glBufferData(GL_ELEMENT_ARRAY_BUFFER, 
+            indexData_.size() * sizeof(unsigned int), &indexData_.front(), 
+            GL_STATIC_DRAW);
+
+        // Unbind our vertex buffer objects so that their state isn't affected
+        // by other objects.
+        glBindBuffer(GL_ARRAY_BUFFER, 0);
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+    }
+    ~Character()
+    {
+        glDeleteBuffers(2, &bufferObjects_[0]);
+    }
+    Character() :
+        vertexIndex_(0),
+        vertexArray_(0) {}
+    unsigned int bufferObjects_[2];
+    std::vector<LibMatrix::vec2> vertexData_;
+    std::vector<unsigned int> indexData_;
+    int vertexIndex_;
+    unsigned int vertexArray_;
+    std::vector<PrimitiveState> primVec_;
+};
+
+struct LetterI : Character
+{
+    LetterI();
+};
+
+struct LetterD : Character
+{
+    LetterD();
+};
+
+struct LetterE : Character
+{
+    LetterE();
+};
+
+struct LetterA : Character
+{
+    LetterA();
+};
+
+struct LetterS : Character
+{
+    LetterS();
+};
+
+struct LetterN : Character
+{
+    LetterN();
+};
+
+struct LetterM : Character
+{
+    LetterM();
+};
+
+struct LetterO : Character
+{
+    LetterO();
+};
+
+struct LetterT : Character
+{
+    LetterT();
+};
+
+#endif // CHARACTERS_H_

=== added file 'src/scene-ideas/d.cc'
--- src/scene-ideas/d.cc	1970-01-01 00:00:00 +0000
+++ src/scene-ideas/d.cc	2012-04-30 18:37:22 +0000
@@ -0,0 +1,142 @@
+/*
+ * Vertex position data describing the letter 'd'
+ *
+ * (c) Copyright 1993, Silicon Graphics, Inc.
+ * Copyright Š 2012 Linaro Limited
+ *
+ * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
+ *
+ * glmark2 is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * glmark2.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Jesse Barker
+ */
+#include "characters.h"
+
+using LibMatrix::vec2;
+
+LetterD::LetterD()
+{
+    // Vertex data...
+    vertexData_.push_back(vec2(4.714579, 9.987679));
+    vertexData_.push_back(vec2(2.841889, 9.429158));
+    vertexData_.push_back(vec2(2.825462, 9.166325));
+    vertexData_.push_back(vec2(1.856263, 8.722793));
+    vertexData_.push_back(vec2(2.004107, 8.000000));
+    vertexData_.push_back(vec2(0.969199, 7.605750));
+    vertexData_.push_back(vec2(1.494866, 6.636550));
+    vertexData_.push_back(vec2(0.607803, 6.028748));
+    vertexData_.push_back(vec2(1.527721, 4.960986));
+    vertexData_.push_back(vec2(0.772074, 4.254620));
+    vertexData_.push_back(vec2(1.774127, 4.139630));
+    vertexData_.push_back(vec2(1.445585, 3.186858));
+    vertexData_.push_back(vec2(2.266940, 3.843942));
+    vertexData_.push_back(vec2(2.250513, 3.022587));
+    vertexData_.push_back(vec2(2.776181, 3.843942));
+    vertexData_.push_back(vec2(3.137577, 3.383984));
+    vertexData_.push_back(vec2(3.351129, 4.008214));
+    vertexData_.push_back(vec2(3.909651, 4.451746));
+    vertexData_.push_back(vec2(4.090349, 4.960986));
+    vertexData_.push_back(vec2(4.862423, 5.946612));
+    vertexData_.push_back(vec2(4.763860, 6.652977));
+    vertexData_.push_back(vec2(5.388090, 7.572895));
+    vertexData_.push_back(vec2(4.862423, 8.492813));
+    vertexData_.push_back(vec2(5.618070, 9.921971));
+    vertexData_.push_back(vec2(4.698152, 10.940452));
+    vertexData_.push_back(vec2(5.338809, 12.303902));
+    vertexData_.push_back(vec2(4.238193, 12.960985));
+    vertexData_.push_back(vec2(4.451746, 14.554415));
+    vertexData_.push_back(vec2(3.581109, 14.291581));
+    vertexData_.push_back(vec2(3.613963, 15.342916));
+    vertexData_.push_back(vec2(2.677618, 15.145790));
+    vertexData_.push_back(vec2(2.480493, 15.540041));
+    vertexData_.push_back(vec2(2.036961, 15.211499));
+    vertexData_.push_back(vec2(1.281314, 15.112936));
+
+    // Index data...
+    indexData_.push_back(0);
+    indexData_.push_back(1);
+    indexData_.push_back(2);
+    indexData_.push_back(3);
+    indexData_.push_back(4);
+    indexData_.push_back(5);
+    indexData_.push_back(6);
+    indexData_.push_back(7);
+    indexData_.push_back(8);
+    indexData_.push_back(9);
+    indexData_.push_back(10);
+    indexData_.push_back(11);
+    indexData_.push_back(12);
+    indexData_.push_back(13);
+    indexData_.push_back(14);
+    indexData_.push_back(15);
+    indexData_.push_back(16);
+    indexData_.push_back(17);
+    indexData_.push_back(18);
+    indexData_.push_back(19);
+    indexData_.push_back(20);
+    indexData_.push_back(21);
+    indexData_.push_back(22);
+    indexData_.push_back(23);
+    indexData_.push_back(24);
+    indexData_.push_back(25);
+    indexData_.push_back(26);
+    indexData_.push_back(27);
+    indexData_.push_back(28);
+    indexData_.push_back(29);
+    indexData_.push_back(30);
+    indexData_.push_back(31);
+    indexData_.push_back(32);
+    indexData_.push_back(33);
+    indexData_.push_back(0);
+    indexData_.push_back(2);
+    indexData_.push_back(4);
+    indexData_.push_back(6);
+    indexData_.push_back(8);
+    indexData_.push_back(10);
+    indexData_.push_back(12);
+    indexData_.push_back(14);
+    indexData_.push_back(16);
+    indexData_.push_back(18);
+    indexData_.push_back(20);
+    indexData_.push_back(22);
+    indexData_.push_back(24);
+    indexData_.push_back(26);
+    indexData_.push_back(28);
+    indexData_.push_back(30);
+    indexData_.push_back(32);
+    indexData_.push_back(33);
+    indexData_.push_back(31);
+    indexData_.push_back(29);
+    indexData_.push_back(27);
+    indexData_.push_back(25);
+    indexData_.push_back(23);
+    indexData_.push_back(21);
+    indexData_.push_back(19);
+    indexData_.push_back(17);
+    indexData_.push_back(15);
+    indexData_.push_back(13);
+    indexData_.push_back(11);
+    indexData_.push_back(9);
+    indexData_.push_back(7);
+    indexData_.push_back(5);
+    indexData_.push_back(3);
+    indexData_.push_back(1);
+
+    // Primitive state so that the draw call can issue the primitives we want.
+    unsigned int curOffset(0);
+    primVec_.push_back(PrimitiveState(GL_TRIANGLE_STRIP, 34, curOffset));
+    curOffset += (34 * sizeof(unsigned int));
+    primVec_.push_back(PrimitiveState(GL_LINE_STRIP, 34, curOffset));
+}

=== added file 'src/scene-ideas/e.cc'
--- src/scene-ideas/e.cc	1970-01-01 00:00:00 +0000
+++ src/scene-ideas/e.cc	2012-04-30 18:37:22 +0000
@@ -0,0 +1,139 @@
+/*
+ * Vertex position data describing the letter 'e'
+ *
+ * (c) Copyright 1993, Silicon Graphics, Inc.
+ * Copyright Š 2012 Linaro Limited
+ *
+ * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
+ *
+ * glmark2 is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * glmark2.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Jesse Barker
+ */
+#include "characters.h"
+
+using LibMatrix::vec2;
+
+LetterE::LetterE()
+{
+    // Vertex data...
+    vertexData_.push_back(vec2(1.095436, 6.190871));
+    vertexData_.push_back(vec2(2.107884, 6.970954));
+    vertexData_.push_back(vec2(2.556017, 7.020747));
+    vertexData_.push_back(vec2(3.020747, 7.867220));
+    vertexData_.push_back(vec2(3.518672, 8.033195));
+    vertexData_.push_back(vec2(3.269710, 8.531120));
+    vertexData_.push_back(vec2(4.165975, 8.929461));
+    vertexData_.push_back(vec2(3.302905, 9.062241));
+    vertexData_.push_back(vec2(4.331950, 9.626556));
+    vertexData_.push_back(vec2(3.286307, 9.344398));
+    vertexData_.push_back(vec2(4.116183, 9.958507));
+    vertexData_.push_back(vec2(3.004149, 9.510373));
+    vertexData_.push_back(vec2(3.518672, 9.991701));
+    vertexData_.push_back(vec2(2.705394, 9.493776));
+    vertexData_.push_back(vec2(2.091286, 9.311203));
+    vertexData_.push_back(vec2(2.041494, 9.062241));
+    vertexData_.push_back(vec2(1.178423, 8.514523));
+    vertexData_.push_back(vec2(1.443983, 8.165976));
+    vertexData_.push_back(vec2(0.481328, 7.535270));
+    vertexData_.push_back(vec2(1.045643, 6.904564));
+    vertexData_.push_back(vec2(0.149378, 6.091286));
+    vertexData_.push_back(vec2(1.095436, 5.410789));
+    vertexData_.push_back(vec2(0.464730, 4.232365));
+    vertexData_.push_back(vec2(1.377593, 4.497925));
+    vertexData_.push_back(vec2(1.261411, 3.136930));
+    vertexData_.push_back(vec2(1.925311, 3.950207));
+    vertexData_.push_back(vec2(2.240664, 3.037344));
+    vertexData_.push_back(vec2(2.589212, 3.834025));
+    vertexData_.push_back(vec2(3.087137, 3.269710));
+    vertexData_.push_back(vec2(3.236515, 3.867220));
+    vertexData_.push_back(vec2(3.684647, 3.867220));
+    vertexData_.push_back(vec2(3.867220, 4.448133));
+    vertexData_.push_back(vec2(4.398340, 5.128631));
+
+    // Index data...
+    indexData_.push_back(0);
+    indexData_.push_back(1);
+    indexData_.push_back(2);
+    indexData_.push_back(3);
+    indexData_.push_back(4);
+    indexData_.push_back(5);
+    indexData_.push_back(6);
+    indexData_.push_back(7);
+    indexData_.push_back(8);
+    indexData_.push_back(9);
+    indexData_.push_back(10);
+    indexData_.push_back(11);
+    indexData_.push_back(12);
+    indexData_.push_back(13);
+    indexData_.push_back(14);
+    indexData_.push_back(15);
+    indexData_.push_back(16);
+    indexData_.push_back(17);
+    indexData_.push_back(18);
+    indexData_.push_back(19);
+    indexData_.push_back(20);
+    indexData_.push_back(21);
+    indexData_.push_back(22);
+    indexData_.push_back(23);
+    indexData_.push_back(24);
+    indexData_.push_back(25);
+    indexData_.push_back(26);
+    indexData_.push_back(27);
+    indexData_.push_back(28);
+    indexData_.push_back(29);
+    indexData_.push_back(30);
+    indexData_.push_back(31);
+    indexData_.push_back(32);
+    indexData_.push_back(0);
+    indexData_.push_back(2);
+    indexData_.push_back(4);
+    indexData_.push_back(6);
+    indexData_.push_back(8);
+    indexData_.push_back(10);
+    indexData_.push_back(12);
+    indexData_.push_back(14);
+    indexData_.push_back(16);
+    indexData_.push_back(18);
+    indexData_.push_back(20);
+    indexData_.push_back(22);
+    indexData_.push_back(24);
+    indexData_.push_back(26);
+    indexData_.push_back(28);
+    indexData_.push_back(30);
+    indexData_.push_back(32);
+    indexData_.push_back(31);
+    indexData_.push_back(29);
+    indexData_.push_back(27);
+    indexData_.push_back(25);
+    indexData_.push_back(23);
+    indexData_.push_back(21);
+    indexData_.push_back(19);
+    indexData_.push_back(17);
+    indexData_.push_back(15);
+    indexData_.push_back(13);
+    indexData_.push_back(11);
+    indexData_.push_back(9);
+    indexData_.push_back(7);
+    indexData_.push_back(5);
+    indexData_.push_back(3);
+    indexData_.push_back(1);
+
+    // Primitive state so that the draw call can issue the primitives we want.
+    unsigned int curOffset(0);
+    primVec_.push_back(PrimitiveState(GL_TRIANGLE_STRIP, 33, curOffset));
+    curOffset += (33 * sizeof(unsigned int));
+    primVec_.push_back(PrimitiveState(GL_LINE_STRIP, 33, curOffset));
+}

=== added file 'src/scene-ideas/i.cc'
--- src/scene-ideas/i.cc	1970-01-01 00:00:00 +0000
+++ src/scene-ideas/i.cc	2012-04-30 18:37:22 +0000
@@ -0,0 +1,116 @@
+/*
+ * Vertex position data describing the letter 'i'
+ *
+ * (c) Copyright 1993, Silicon Graphics, Inc.
+ * Copyright Š 2012 Linaro Limited
+ *
+ * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
+ *
+ * glmark2 is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * glmark2.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Jesse Barker
+ */
+#include "characters.h"
+
+using LibMatrix::vec2;
+
+LetterI::LetterI()
+{
+    // Vertex data...
+    vertexData_.push_back(vec2(0.548767, 9.414791));
+    vertexData_.push_back(vec2(2.795284, 9.757771));
+    vertexData_.push_back(vec2(1.457663, 9.311897));
+    vertexData_.push_back(vec2(2.503751, 9.157557));
+    vertexData_.push_back(vec2(1.714898, 8.986067));
+    vertexData_.push_back(vec2(2.109325, 7.785638));
+    vertexData_.push_back(vec2(1.286174, 7.013934));
+    vertexData_.push_back(vec2(1.800643, 6.070740));
+    vertexData_.push_back(vec2(0.994641, 5.161843));
+    vertexData_.push_back(vec2(1.783494, 4.767417));
+    vertexData_.push_back(vec2(0.943194, 4.167202));
+    vertexData_.push_back(vec2(1.852090, 4.304394));
+    vertexData_.push_back(vec2(1.063237, 3.549839));
+    vertexData_.push_back(vec2(2.023580, 3.978564));
+    vertexData_.push_back(vec2(1.406217, 3.172562));
+    vertexData_.push_back(vec2(2.315113, 3.875670));
+    vertexData_.push_back(vec2(2.006431, 3.018221));
+    vertexData_.push_back(vec2(2.812433, 3.944266));
+    vertexData_.push_back(vec2(2.726688, 3.429796));
+    vertexData_.push_back(vec2(3.258307, 4.132905));
+    vertexData_.push_back(vec2(1.989282, 10.923902));
+    vertexData_.push_back(vec2(2.778135, 12.295820));
+    vertexData_.push_back(vec2(2.966774, 11.678456));
+    vertexData_.push_back(vec2(3.687031, 12.947481));
+
+    // Index data...
+    indexData_.push_back(0);
+    indexData_.push_back(1);
+    indexData_.push_back(2);
+    indexData_.push_back(3);
+    indexData_.push_back(4);
+    indexData_.push_back(5);
+    indexData_.push_back(6);
+    indexData_.push_back(7);
+    indexData_.push_back(8);
+    indexData_.push_back(9);
+    indexData_.push_back(10);
+    indexData_.push_back(11);
+    indexData_.push_back(12);
+    indexData_.push_back(13);
+    indexData_.push_back(14);
+    indexData_.push_back(15);
+    indexData_.push_back(16);
+    indexData_.push_back(17);
+    indexData_.push_back(18);
+    indexData_.push_back(19);
+    indexData_.push_back(20);
+    indexData_.push_back(21);
+    indexData_.push_back(22);
+    indexData_.push_back(23);
+    indexData_.push_back(0);
+    indexData_.push_back(2);
+    indexData_.push_back(4);
+    indexData_.push_back(6);
+    indexData_.push_back(8);
+    indexData_.push_back(10);
+    indexData_.push_back(12);
+    indexData_.push_back(14);
+    indexData_.push_back(16);
+    indexData_.push_back(18);
+    indexData_.push_back(19);
+    indexData_.push_back(17);
+    indexData_.push_back(15);
+    indexData_.push_back(13);
+    indexData_.push_back(11);
+    indexData_.push_back(9);
+    indexData_.push_back(7);
+    indexData_.push_back(5);
+    indexData_.push_back(3);
+    indexData_.push_back(1);
+    indexData_.push_back(20);
+    indexData_.push_back(22);
+    indexData_.push_back(23);
+    indexData_.push_back(21);
+
+    // Primitive state so that the draw call can issue the primitives we want.
+    unsigned int curOffset(0);
+    primVec_.push_back(PrimitiveState(GL_TRIANGLE_STRIP, 20, curOffset));
+    curOffset += (20 * sizeof(unsigned int));
+    primVec_.push_back(PrimitiveState(GL_TRIANGLE_STRIP, 4, curOffset));
+    curOffset += (4 * sizeof(unsigned int));
+    primVec_.push_back(PrimitiveState(GL_TRIANGLE_STRIP, 20, curOffset));
+    curOffset += (20 * sizeof(unsigned int));
+    primVec_.push_back(PrimitiveState(GL_TRIANGLE_STRIP, 4, curOffset));
+}

=== added file 'src/scene-ideas/lamp.cc'
--- src/scene-ideas/lamp.cc	1970-01-01 00:00:00 +0000
+++ src/scene-ideas/lamp.cc	2012-05-08 20:53:58 +0000
@@ -0,0 +1,258 @@
+/*
+ * Vertex position data describing the lamp
+ *
+ * (c) Copyright 1993, Silicon Graphics, Inc.
+ * Copyright Š 2012 Linaro Limited
+ *
+ * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
+ *
+ * glmark2 is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * glmark2.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Jesse Barker
+ */
+#include "lamp.h"
+#include "shader-source.h"
+#include "log.h"
+#include "scene.h"
+
+using std::string;
+using LibMatrix::vec3;
+using LibMatrix::vec4;
+using LibMatrix::Stack4;
+
+const string Lamp::modelviewName_("modelview");
+const string Lamp::projectionName_("projection");
+const string Lamp::light0PositionName_("light0Position");
+const string Lamp::light1PositionName_("light1Position");
+const string Lamp::light2PositionName_("light2Position");
+const string Lamp::vertexAttribName_("vertex");
+const string Lamp::normalAttribName_("normal");
+const string Lamp::normalMatrixName_("normalMatrix");
+
+Lamp::Lamp() :
+    valid_(false)
+{
+    vertexData_.push_back(vec3(0.000000, 0.000000, 1.000000));
+    vertexData_.push_back(vec3(0.000000, 0.000000, 1.000000));
+    vertexData_.push_back(vec3(0.000000, 0.000000, 1.000000));
+    vertexData_.push_back(vec3(0.000000, 0.000000, 1.000000));
+    vertexData_.push_back(vec3(0.000000, 0.000000, 1.000000));
+    vertexData_.push_back(vec3(0.000000, 0.000000, 1.000000));
+    vertexData_.push_back(vec3(0.000000, 0.000000, 1.000000));
+    vertexData_.push_back(vec3(0.000000, 0.000000, 1.000000));
+    vertexData_.push_back(vec3(0.000000, 0.000000, 1.000000));
+    vertexData_.push_back(vec3(0.000000, 0.000000, 1.000000));
+    vertexData_.push_back(vec3(0.000000, 0.000000, 1.000000));
+    vertexData_.push_back(vec3(0.000000, 0.000000, 1.000000));
+    vertexData_.push_back(vec3(0.000000, 0.000000, 1.000000));
+    vertexData_.push_back(vec3(0.438371, 0.000000, 0.898794));
+    vertexData_.push_back(vec3(0.379641, 0.219186, 0.898794));
+    vertexData_.push_back(vec3(0.219186, 0.379641, 0.898794));
+    vertexData_.push_back(vec3(0.000000, 0.438371, 0.898794));
+    vertexData_.push_back(vec3(-0.219186, 0.379641, 0.898794));
+    vertexData_.push_back(vec3(-0.379641, 0.219186, 0.898794));
+    vertexData_.push_back(vec3(-0.438371, 0.000000, 0.898794));
+    vertexData_.push_back(vec3(-0.379641, -0.219186, 0.898794));
+    vertexData_.push_back(vec3(-0.219186, -0.379641, 0.898794));
+    vertexData_.push_back(vec3(0.000000, -0.438371, 0.898794));
+    vertexData_.push_back(vec3(0.219186, -0.379641, 0.898794));
+    vertexData_.push_back(vec3(0.379641, -0.219186, 0.898794));
+    vertexData_.push_back(vec3(0.438371, 0.000000, 0.898794));
+    vertexData_.push_back(vec3(0.788011, 0.000000, 0.615662));
+    vertexData_.push_back(vec3(0.682437, 0.394005, 0.615662));
+    vertexData_.push_back(vec3(0.394005, 0.682437, 0.615662));
+    vertexData_.push_back(vec3(0.000000, 0.788011, 0.615662));
+    vertexData_.push_back(vec3(-0.394005, 0.682437, 0.615662));
+    vertexData_.push_back(vec3(-0.682437, 0.394005, 0.615662));
+    vertexData_.push_back(vec3(-0.788011, 0.000000, 0.615662));
+    vertexData_.push_back(vec3(-0.682437, -0.394005, 0.615662));
+    vertexData_.push_back(vec3(-0.394005, -0.682437, 0.615662));
+    vertexData_.push_back(vec3(0.000000, -0.788011, 0.615662));
+    vertexData_.push_back(vec3(0.394005, -0.682437, 0.615662));
+    vertexData_.push_back(vec3(0.682437, -0.394005, 0.615662));
+    vertexData_.push_back(vec3(0.788011, 0.000000, 0.615662));
+    vertexData_.push_back(vec3(0.978148, 0.000000, 0.207912));
+    vertexData_.push_back(vec3(0.847101, 0.489074, 0.207912));
+    vertexData_.push_back(vec3(0.489074, 0.847101, 0.207912));
+    vertexData_.push_back(vec3(0.000000, 0.978148, 0.207912));
+    vertexData_.push_back(vec3(-0.489074, 0.847101, 0.207912));
+    vertexData_.push_back(vec3(-0.847101, 0.489074, 0.207912));
+    vertexData_.push_back(vec3(-0.978148, 0.000000, 0.207912));
+    vertexData_.push_back(vec3(-0.847101, -0.489074, 0.207912));
+    vertexData_.push_back(vec3(-0.489074, -0.847101, 0.207912));
+    vertexData_.push_back(vec3(0.000000, -0.978148, 0.207912));
+    vertexData_.push_back(vec3(0.489074, -0.847101, 0.207912));
+    vertexData_.push_back(vec3(0.847101, -0.489074, 0.207912));
+    vertexData_.push_back(vec3(0.978148, 0.000000, 0.207912));
+    vertexData_.push_back(vec3(0.970296, 0.000000, -0.241922));
+    vertexData_.push_back(vec3(0.840301, 0.485148, -0.241922));
+    vertexData_.push_back(vec3(0.485148, 0.840301, -0.241922));
+    vertexData_.push_back(vec3(0.000000, 0.970296, -0.241922));
+    vertexData_.push_back(vec3(-0.485148, 0.840301, -0.241922));
+    vertexData_.push_back(vec3(-0.840301, 0.485148, -0.241922));
+    vertexData_.push_back(vec3(-0.970296, 0.000000, -0.241922));
+    vertexData_.push_back(vec3(-0.840301, -0.485148, -0.241922));
+    vertexData_.push_back(vec3(-0.485148, -0.840301, -0.241922));
+    vertexData_.push_back(vec3(0.000000, -0.970296, -0.241922));
+    vertexData_.push_back(vec3(0.485148, -0.840301, -0.241922));
+    vertexData_.push_back(vec3(0.840301, -0.485148, -0.241922));
+    vertexData_.push_back(vec3(0.970296, 0.000000, -0.241922));
+    vertexData_.push_back(vec3(0.766044, 0.000000, -0.642788));
+    vertexData_.push_back(vec3(0.663414, 0.383022, -0.642788));
+    vertexData_.push_back(vec3(0.383022, 0.663414, -0.642788));
+    vertexData_.push_back(vec3(0.000000, 0.766044, -0.642788));
+    vertexData_.push_back(vec3(-0.383022, 0.663414, -0.642788));
+    vertexData_.push_back(vec3(-0.663414, 0.383022, -0.642788));
+    vertexData_.push_back(vec3(-0.766044, 0.000000, -0.642788));
+    vertexData_.push_back(vec3(-0.663414, -0.383022, -0.642788));
+    vertexData_.push_back(vec3(-0.383022, -0.663414, -0.642788));
+    vertexData_.push_back(vec3(0.000000, -0.766044, -0.642788));
+    vertexData_.push_back(vec3(0.383022, -0.663414, -0.642788));
+    vertexData_.push_back(vec3(0.663414, -0.383022, -0.642788));
+    vertexData_.push_back(vec3(0.766044, 0.000000, -0.642788));
+
+    //
+    // The original implementation of both the logo and the lamp represented
+    // the vertex and normal data in a triply-dimensioned array of floats and
+    // all of the calls referenced double-indexed arrays of vector data.
+    // To my mind, this made the code look clunky and overly verbose.
+    // Representing the data as a STL vector of vec3 (itself a 3-float vector
+    // quantity) provides both an efficient container and allows for more
+    // concise looking code.  The slightly goofy loops (using the original 2
+    // dimensional indices to compute a single offset into the STL vector) are 
+    // a compromise to avoid rearranging the original data.
+    //
+    // - jesse 2010/10/04
+    //
+    for (unsigned int i = 0; i < 5; i++)
+    {
+        for (unsigned int j = 0; j < 13; j++)
+        {
+            indexData_.push_back(i * 13 + j);
+            indexData_.push_back((i + 1) * 13 + j);
+        }
+    }
+    unsigned int curIndex(5 * 13);
+    for (unsigned int i = 0; i < 12; i++)
+    {
+        indexData_.push_back(curIndex + i);
+    }
+}
+
+Lamp::~Lamp()
+{
+    if (valid_)
+    {
+        glDeleteBuffers(2, &bufferObjects_[0]);
+    }
+}
+
+void
+Lamp::init()
+{
+    // Make sure we don't re-initialize...
+    if (valid_)
+    {
+        return;
+    }
+
+    // Initialize shader sources from input files and create programs from them
+    // The program for handling lighting...
+    string lit_vtx_filename(GLMARK_DATA_PATH"/shaders/ideas-lamp-lit.vert");
+    string lit_frg_filename(GLMARK_DATA_PATH"/shaders/ideas-lamp-lit.frag");
+    ShaderSource lit_vtx_source(lit_vtx_filename);
+    ShaderSource lit_frg_source(lit_frg_filename);
+    if (!Scene::load_shaders_from_strings(litProgram_, lit_vtx_source.str(),
+                                          lit_frg_source.str()))
+    {
+        Log::error("No valid program for lit lamp rendering\n");
+        return;
+    }
+
+    // The simple program with no lighting...
+    string unlit_vtx_filename(GLMARK_DATA_PATH"/shaders/ideas-lamp-unlit.vert");
+    string unlit_frg_filename(GLMARK_DATA_PATH"/shaders/ideas-lamp-unlit.frag");
+    ShaderSource unlit_vtx_source(unlit_vtx_filename);
+    ShaderSource unlit_frg_source(unlit_frg_filename);
+    if (!Scene::load_shaders_from_strings(unlitProgram_, unlit_vtx_source.str(),
+                                          unlit_frg_source.str()))
+    {
+        Log::error("No valid program for unlit lamp rendering.\n");
+        return;
+    }
+
+    // We need 2 buffers for our work here.  One for the vertex data.
+    // and one for the index data.
+    glGenBuffers(2, &bufferObjects_[0]);
+
+    // First, setup the vertex data by binding the first buffer object, 
+    // allocating its data store, and filling it in with our vertex data.
+    glBindBuffer(GL_ARRAY_BUFFER, bufferObjects_[0]);
+    glBufferData(GL_ARRAY_BUFFER, vertexData_.size() * sizeof(vec3), &vertexData_.front(), GL_STATIC_DRAW);
+
+    // Now repeat for our index data.
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferObjects_[1]);
+    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexData_.size() * sizeof(unsigned int), &indexData_.front(), GL_STATIC_DRAW);
+
+    // We're ready to go.
+    valid_ = true;
+}
+
+void
+Lamp::draw(Stack4& modelview, Stack4& projection, const vec4* lightPositions)
+{
+    glBindBuffer(GL_ARRAY_BUFFER, bufferObjects_[0]);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferObjects_[1]);
+
+    litProgram_.start();
+    int vertexIndex(litProgram_[vertexAttribName_].location());
+    int normalIndex(litProgram_[normalAttribName_].location());
+    glVertexAttribPointer(vertexIndex, 3, GL_FLOAT, GL_FALSE, 0, 0);
+    glVertexAttribPointer(normalIndex, 3, GL_FLOAT, GL_FALSE, 0, 0);
+    glEnableVertexAttribArray(vertexIndex);
+    glEnableVertexAttribArray(normalIndex);
+    const LibMatrix::mat4& mv = modelview.getCurrent();
+    LibMatrix::mat3 normalMatrix(mv[0][0], mv[1][0], mv[2][0],
+                                 mv[0][1], mv[1][1], mv[2][1],
+                                 mv[0][2], mv[1][2], mv[2][2]);
+    normalMatrix.transpose().inverse();
+    litProgram_[normalMatrixName_] = normalMatrix;
+    litProgram_[modelviewName_] = mv;
+    litProgram_[projectionName_] = projection.getCurrent();
+    litProgram_[light0PositionName_] = lightPositions[0];
+    litProgram_[light1PositionName_] = lightPositions[1];
+    litProgram_[light2PositionName_] = lightPositions[2];
+    static const unsigned int sui(sizeof(unsigned int));
+    for (unsigned int i = 0; i < 5; i++)
+    {
+        glDrawElements(GL_TRIANGLE_STRIP, 26, GL_UNSIGNED_INT, reinterpret_cast<const GLvoid*>(i * 26 * sui));
+    }
+    glDisableVertexAttribArray(normalIndex);
+    glDisableVertexAttribArray(vertexIndex);
+    litProgram_.stop();
+
+    unlitProgram_.start();
+    vertexIndex = unlitProgram_[vertexAttribName_].location();
+    glVertexAttribPointer(vertexIndex, 3, GL_FLOAT, GL_FALSE, 0, 0);
+    glEnableVertexAttribArray(vertexIndex);
+    unlitProgram_[modelviewName_] = mv;
+    unlitProgram_[projectionName_] = projection.getCurrent();
+    glDrawElements(GL_TRIANGLE_FAN, 12, GL_UNSIGNED_INT, reinterpret_cast<const GLvoid*>(5 * 26 * sui));
+    glDisableVertexAttribArray(vertexIndex);
+    unlitProgram_.stop();
+
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+}

=== added file 'src/scene-ideas/lamp.h'
--- src/scene-ideas/lamp.h	1970-01-01 00:00:00 +0000
+++ src/scene-ideas/lamp.h	2012-04-30 18:37:22 +0000
@@ -0,0 +1,64 @@
+/*
+ * (c) Copyright 1993, Silicon Graphics, Inc.
+ * Copyright Š 2012 Linaro Limited
+ *
+ * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
+ *
+ * glmark2 is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * glmark2.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Jesse Barker
+ */
+#ifndef LAMP_H_
+#define LAMP_H_
+
+#include <string>
+#include <vector>
+#include "vec.h"
+#include "stack.h"
+#include "gl-headers.h"
+#include "program.h"
+
+class Lamp
+{
+public:
+    Lamp();
+    ~Lamp();
+
+    void init();
+    bool valid() const { return valid_; }
+    void draw(LibMatrix::Stack4& modelview, LibMatrix::Stack4& projection,
+              const LibMatrix::vec4* lightPositions);
+private:    
+    Program litProgram_;
+    Program unlitProgram_;
+    std::string litVertexShader_;
+    std::string litFragmentShader_;
+    std::string unlitVertexShader_;
+    std::string unlitFragmentShader_;
+    static const std::string modelviewName_;
+    static const std::string projectionName_;
+    static const std::string light0PositionName_;
+    static const std::string light1PositionName_;
+    static const std::string light2PositionName_;
+    static const std::string vertexAttribName_;
+    static const std::string normalAttribName_;
+    static const std::string normalMatrixName_;
+    std::vector<LibMatrix::vec3> vertexData_;
+    std::vector<unsigned int> indexData_;
+    unsigned int bufferObjects_[2];
+    bool valid_;
+};
+
+#endif // LAMP_H_

=== added file 'src/scene-ideas/logo.cc'
--- src/scene-ideas/logo.cc	1970-01-01 00:00:00 +0000
+++ src/scene-ideas/logo.cc	2012-05-08 20:53:58 +0000
@@ -0,0 +1,789 @@
+/*
+ * Vertex position data describing the old Silicon Graphics logo
+ *
+ * (c) Copyright 1993, Silicon Graphics, Inc.
+ * Copyright Š 2012 Linaro Limited
+ *
+ * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
+ *
+ * glmark2 is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * glmark2.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Jesse Barker
+ */
+#include "logo.h"
+#include "scene.h"
+#include "shader-source.h"
+#include "log.h"
+
+using std::string;
+using LibMatrix::vec3;
+using LibMatrix::uvec3;
+using LibMatrix::vec4;
+using LibMatrix::Stack4;
+using LibMatrix::mat4;
+
+const unsigned int SGILogo::textureResolution_(32);
+const string SGILogo::modelviewName_("modelview");
+const string SGILogo::projectionName_("projection");
+const string SGILogo::lightPositionName_("light0Position");
+const string SGILogo::logoColorName_("logoColor");
+const string SGILogo::vertexAttribName_("vertex");
+const string SGILogo::normalAttribName_("normal");
+const string SGILogo::normalMatrixName_("normalMatrix");
+
+SGILogo::SGILogo(void) :
+    normalNormalIndex_(0),
+    normalVertexIndex_(0),
+    flatVertexIndex_(0),
+    shadowVertexIndex_(0),
+    vertexIndex_(0),
+    valid_(false),
+    drawStyle_(LOGO_NORMAL)
+{
+    // Single cylinder data...
+    singleCylinderVertices_.push_back(vec3(1.000000, 0.000000, 0.000000));
+    singleCylinderVertices_.push_back(vec3(1.000000, 0.000000, 5.000000));
+    singleCylinderVertices_.push_back(vec3(0.707107, 0.707107, 0.000000));
+    singleCylinderVertices_.push_back(vec3(0.707107, 0.707107, 5.000000));
+    singleCylinderVertices_.push_back(vec3(0.000000, 1.000000, 0.000000));
+    singleCylinderVertices_.push_back(vec3(0.000000, 1.000000, 5.000000));
+    singleCylinderVertices_.push_back(vec3(-0.707107, 0.707107, 0.000000));
+    singleCylinderVertices_.push_back(vec3(-0.707107, 0.707107, 5.000000));
+    singleCylinderVertices_.push_back(vec3(-1.000000, 0.000000, 0.000000));
+    singleCylinderVertices_.push_back(vec3(-1.000000, 0.000000, 5.000000));
+    singleCylinderVertices_.push_back(vec3(-0.707107, -0.707107, 0.000000));
+    singleCylinderVertices_.push_back(vec3(-0.707107, -0.707107, 5.000000));
+    singleCylinderVertices_.push_back(vec3(0.000000, -1.000000, 0.000000));
+    singleCylinderVertices_.push_back(vec3(0.000000, -1.000000, 5.000000));
+    singleCylinderVertices_.push_back(vec3(0.707107, -0.707107, 0.000000));
+    singleCylinderVertices_.push_back(vec3(0.707107, -0.707107, 5.000000));
+    singleCylinderVertices_.push_back(vec3(1.000000, 0.000000, 0.000000));
+    singleCylinderVertices_.push_back(vec3(1.000000, 0.000000, 5.000000));
+
+    singleCylinderNormals_.push_back(vec3(1.000000, 0.000000, 0.000000));
+    singleCylinderNormals_.push_back(vec3(1.000000, 0.000000, 0.000000));
+    singleCylinderNormals_.push_back(vec3(0.707107, 0.707107, 0.000000));
+    singleCylinderNormals_.push_back(vec3(0.707107, 0.707107, 0.000000));
+    singleCylinderNormals_.push_back(vec3(0.000000, 1.000000, 0.000000));
+    singleCylinderNormals_.push_back(vec3(0.000000, 1.000000, 0.000000));
+    singleCylinderNormals_.push_back(vec3(-0.707107, 0.707107, 0.000000));
+    singleCylinderNormals_.push_back(vec3(-0.707107, 0.707107, 0.000000));
+    singleCylinderNormals_.push_back(vec3(-1.000000, 0.000000, 0.000000));
+    singleCylinderNormals_.push_back(vec3(-1.000000, 0.000000, 0.000000));
+    singleCylinderNormals_.push_back(vec3(-0.707107, -0.707107, 0.000000));
+    singleCylinderNormals_.push_back(vec3(-0.707107, -0.707107, 0.000000));
+    singleCylinderNormals_.push_back(vec3(0.000000, -1.000000, 0.000000));
+    singleCylinderNormals_.push_back(vec3(0.000000, -1.000000, 0.000000));
+    singleCylinderNormals_.push_back(vec3(0.707107, -0.707107, 0.000000));
+    singleCylinderNormals_.push_back(vec3(0.707107, -0.707107, 0.000000));
+    singleCylinderNormals_.push_back(vec3(1.000000, 0.000000, 0.000000));
+    singleCylinderNormals_.push_back(vec3(1.000000, 0.000000, 0.000000));
+
+    // Double cylinder data...
+    doubleCylinderVertices_.push_back(vec3(1.000000, 0.000000, 0.000000));
+    doubleCylinderVertices_.push_back(vec3(1.000000, 0.000000, 7.000000));
+    doubleCylinderVertices_.push_back(vec3(0.707107, 0.707107, 0.000000));
+    doubleCylinderVertices_.push_back(vec3(0.707107, 0.707107, 7.000000));
+    doubleCylinderVertices_.push_back(vec3(0.000000, 1.000000, 0.000000));
+    doubleCylinderVertices_.push_back(vec3(0.000000, 1.000000, 7.000000));
+    doubleCylinderVertices_.push_back(vec3(-0.707107, 0.707107, 0.000000));
+    doubleCylinderVertices_.push_back(vec3(-0.707107, 0.707107, 7.000000));
+    doubleCylinderVertices_.push_back(vec3(-1.000000, 0.000000, 0.000000));
+    doubleCylinderVertices_.push_back(vec3(-1.000000, 0.000000, 7.000000));
+    doubleCylinderVertices_.push_back(vec3(-0.707107, -0.707107, 0.000000));
+    doubleCylinderVertices_.push_back(vec3(-0.707107, -0.707107, 7.000000));
+    doubleCylinderVertices_.push_back(vec3(0.000000, -1.000000, 0.000000));
+    doubleCylinderVertices_.push_back(vec3(0.000000, -1.000000, 7.000000));
+    doubleCylinderVertices_.push_back(vec3(0.707107, -0.707107, 0.000000));
+    doubleCylinderVertices_.push_back(vec3(0.707107, -0.707107, 7.000000));
+    doubleCylinderVertices_.push_back(vec3(1.000000, 0.000000, 0.000000));
+    doubleCylinderVertices_.push_back(vec3(1.000000, 0.000000, 7.000000));
+
+    doubleCylinderNormals_.push_back(vec3(1.000000, 0.000000, 0.000000));
+    doubleCylinderNormals_.push_back(vec3(1.000000, 0.000000, 0.000000));
+    doubleCylinderNormals_.push_back(vec3(0.707107, 0.707107, 0.000000));
+    doubleCylinderNormals_.push_back(vec3(0.707107, 0.707107, 0.000000));
+    doubleCylinderNormals_.push_back(vec3(0.000000, 1.000000, 0.000000));
+    doubleCylinderNormals_.push_back(vec3(0.000000, 1.000000, 0.000000));
+    doubleCylinderNormals_.push_back(vec3(-0.707107, 0.707107, 0.000000));
+    doubleCylinderNormals_.push_back(vec3(-0.707107, 0.707107, 0.000000));
+    doubleCylinderNormals_.push_back(vec3(-1.000000, 0.000000, 0.000000));
+    doubleCylinderNormals_.push_back(vec3(-1.000000, 0.000000, 0.000000));
+    doubleCylinderNormals_.push_back(vec3(-0.707107, -0.707107, 0.000000));
+    doubleCylinderNormals_.push_back(vec3(-0.707107, -0.707107, 0.000000));
+    doubleCylinderNormals_.push_back(vec3(0.000000, -1.000000, 0.000000));
+    doubleCylinderNormals_.push_back(vec3(0.000000, -1.000000, 0.000000));
+    doubleCylinderNormals_.push_back(vec3(0.707107, -0.707107, 0.000000));
+    doubleCylinderNormals_.push_back(vec3(0.707107, -0.707107, 0.000000));
+    doubleCylinderNormals_.push_back(vec3(1.000000, 0.000000, 0.000000));
+    doubleCylinderNormals_.push_back(vec3(1.000000, 0.000000, 0.000000));
+
+    // Elbow data...
+    elbowVertices_.push_back(vec3(1.000000, 0.000000, 0.000000));
+    elbowVertices_.push_back(vec3(0.707107, 0.707107, 0.000000));
+    elbowVertices_.push_back(vec3(0.000000, 1.000000, 0.000000));
+    elbowVertices_.push_back(vec3(-0.707107, 0.707107, 0.000000));
+    elbowVertices_.push_back(vec3(-1.000000, 0.000000, 0.000000));
+    elbowVertices_.push_back(vec3(-0.707107, -0.707107, 0.000000));
+    elbowVertices_.push_back(vec3(0.000000, -1.000000, 0.000000));
+    elbowVertices_.push_back(vec3(0.707107, -0.707107, 0.000000));
+    elbowVertices_.push_back(vec3(1.000000, 0.000000, 0.000000));
+    elbowVertices_.push_back(vec3(1.000000, 0.034074, 0.258819));
+    elbowVertices_.push_back(vec3(0.707107, 0.717087, 0.075806));
+    elbowVertices_.push_back(vec3(0.000000, 1.000000, 0.000000));
+    elbowVertices_.push_back(vec3(-0.707107, 0.717087, 0.075806));
+    elbowVertices_.push_back(vec3(-1.000000, 0.034074, 0.258819));
+    elbowVertices_.push_back(vec3(-0.707107, -0.648939, 0.441832));
+    elbowVertices_.push_back(vec3(0.000000, -0.931852, 0.517638));
+    elbowVertices_.push_back(vec3(0.707107, -0.648939, 0.441832));
+    elbowVertices_.push_back(vec3(1.000000, 0.034074, 0.258819));
+    elbowVertices_.push_back(vec3(1.000000, 0.133975, 0.500000));
+    elbowVertices_.push_back(vec3(0.707107, 0.746347, 0.146447));
+    elbowVertices_.push_back(vec3(0.000000, 1.000000, 0.000000));
+    elbowVertices_.push_back(vec3(-0.707107, 0.746347, 0.146447));
+    elbowVertices_.push_back(vec3(-1.000000, 0.133975, 0.500000));
+    elbowVertices_.push_back(vec3(-0.707107, -0.478398, 0.853553));
+    elbowVertices_.push_back(vec3(0.000000, -0.732051, 1.000000));
+    elbowVertices_.push_back(vec3(0.707107, -0.478398, 0.853553));
+    elbowVertices_.push_back(vec3(1.000000, 0.133975, 0.500000));
+    elbowVertices_.push_back(vec3(1.000000, 0.292893, 0.707107));
+    elbowVertices_.push_back(vec3(0.707107, 0.792893, 0.207107));
+    elbowVertices_.push_back(vec3(0.000000, 1.000000, 0.000000));
+    elbowVertices_.push_back(vec3(-0.707107, 0.792893, 0.207107));
+    elbowVertices_.push_back(vec3(-1.000000, 0.292893, 0.707107));
+    elbowVertices_.push_back(vec3(-0.707107, -0.207107, 1.207107));
+    elbowVertices_.push_back(vec3(0.000000, -0.414214, 1.414214));
+    elbowVertices_.push_back(vec3(0.707107, -0.207107, 1.207107));
+    elbowVertices_.push_back(vec3(1.000000, 0.292893, 0.707107));
+    elbowVertices_.push_back(vec3(1.000000, 0.500000, 0.866025));
+    elbowVertices_.push_back(vec3(0.707107, 0.853553, 0.253653));
+    elbowVertices_.push_back(vec3(0.000000, 1.000000, 0.000000));
+    elbowVertices_.push_back(vec3(-0.707107, 0.853553, 0.253653));
+    elbowVertices_.push_back(vec3(-1.000000, 0.500000, 0.866025));
+    elbowVertices_.push_back(vec3(-0.707107, 0.146447, 1.478398));
+    elbowVertices_.push_back(vec3(0.000000, 0.000000, 1.732051));
+    elbowVertices_.push_back(vec3(0.707107, 0.146447, 1.478398));
+    elbowVertices_.push_back(vec3(1.000000, 0.500000, 0.866025));
+    elbowVertices_.push_back(vec3(1.000000, 0.741181, 0.965926));
+    elbowVertices_.push_back(vec3(0.707107, 0.924194, 0.282913));
+    elbowVertices_.push_back(vec3(0.000000, 1.000000, 0.000000));
+    elbowVertices_.push_back(vec3(-0.707107, 0.924194, 0.282913));
+    elbowVertices_.push_back(vec3(-1.000000, 0.741181, 0.965926));
+    elbowVertices_.push_back(vec3(-0.707107, 0.558168, 1.648939));
+    elbowVertices_.push_back(vec3(0.000000, 0.482362, 1.931852));
+    elbowVertices_.push_back(vec3(0.707107, 0.558168, 1.648939));
+    elbowVertices_.push_back(vec3(1.000000, 0.741181, 0.965926));
+    elbowVertices_.push_back(vec3(1.000000, 1.000000, 1.000000));
+    elbowVertices_.push_back(vec3(0.707107, 1.000000, 0.292893));
+    elbowVertices_.push_back(vec3(0.000000, 1.000000, 0.000000));
+    elbowVertices_.push_back(vec3(-0.707107, 1.000000, 0.292893));
+    elbowVertices_.push_back(vec3(-1.000000, 1.000000, 1.000000));
+    elbowVertices_.push_back(vec3(-0.707107, 1.000000, 1.707107));
+    elbowVertices_.push_back(vec3(0.000000, 1.000000, 2.000000));
+    elbowVertices_.push_back(vec3(0.707107, 1.000000, 1.707107));
+    elbowVertices_.push_back(vec3(1.000000, 1.000000, 1.000000));
+
+    elbowNormals_.push_back(vec3(1.000000, 0.000000, 0.000000));
+    elbowNormals_.push_back(vec3(0.707107, 0.707107, 0.000000));
+    elbowNormals_.push_back(vec3(0.000000, 1.000000, 0.000000));
+    elbowNormals_.push_back(vec3(-0.707107, 0.707107, 0.000000));
+    elbowNormals_.push_back(vec3(-1.000000, 0.000000, 0.000000));
+    elbowNormals_.push_back(vec3(-0.707107, -0.707107, 0.000000));
+    elbowNormals_.push_back(vec3(0.000000, -1.000000, 0.000000));
+    elbowNormals_.push_back(vec3(0.707107, -0.707107, 0.000000));
+    elbowNormals_.push_back(vec3(1.000000, 0.000000, 0.000000));
+    elbowNormals_.push_back(vec3(1.000000, 0.000000, 0.000000));
+    elbowNormals_.push_back(vec3(0.707107, 0.683013, -0.183013));
+    elbowNormals_.push_back(vec3(0.000000, 0.965926, -0.258819));
+    elbowNormals_.push_back(vec3(-0.707107, 0.683013, -0.183013));
+    elbowNormals_.push_back(vec3(-1.000000, 0.000000, 0.000000));
+    elbowNormals_.push_back(vec3(-0.707107, -0.683013, 0.183013));
+    elbowNormals_.push_back(vec3(0.000000, -0.965926, 0.258819));
+    elbowNormals_.push_back(vec3(0.707107, -0.683013, 0.183013));
+    elbowNormals_.push_back(vec3(1.000000, 0.000000, 0.000000));
+    elbowNormals_.push_back(vec3(1.000000, 0.000000, 0.000000));
+    elbowNormals_.push_back(vec3(0.707107, 0.612372, -0.353553));
+    elbowNormals_.push_back(vec3(0.000000, 0.866025, -0.500000));
+    elbowNormals_.push_back(vec3(-0.707107, 0.612372, -0.353553));
+    elbowNormals_.push_back(vec3(-1.000000, 0.000000, 0.000000));
+    elbowNormals_.push_back(vec3(-0.707107, -0.612372, 0.353553));
+    elbowNormals_.push_back(vec3(0.000000, -0.866025, 0.500000));
+    elbowNormals_.push_back(vec3(0.707107, -0.612372, 0.353553));
+    elbowNormals_.push_back(vec3(1.000000, 0.000000, 0.000000));
+    elbowNormals_.push_back(vec3(1.000000, 0.000000, 0.000000));
+    elbowNormals_.push_back(vec3(0.707107, 0.500000, -0.500000));
+    elbowNormals_.push_back(vec3(0.000000, 0.707107, -0.707107));
+    elbowNormals_.push_back(vec3(-0.707107, 0.500000, -0.500000));
+    elbowNormals_.push_back(vec3(-1.000000, 0.000000, 0.000000));
+    elbowNormals_.push_back(vec3(-0.707107, -0.500000, 0.500000));
+    elbowNormals_.push_back(vec3(0.000000, -0.707107, 0.707107));
+    elbowNormals_.push_back(vec3(0.707107, -0.500000, 0.500000));
+    elbowNormals_.push_back(vec3(1.000000, 0.000000, 0.000000));
+    elbowNormals_.push_back(vec3(1.000000, 0.000000, 0.000000));
+    elbowNormals_.push_back(vec3(0.707107, 0.353553, -0.612372));
+    elbowNormals_.push_back(vec3(0.000000, 0.500000, -0.866025));
+    elbowNormals_.push_back(vec3(-0.707107, 0.353553, -0.612372));
+    elbowNormals_.push_back(vec3(-1.000000, 0.000000, 0.000000));
+    elbowNormals_.push_back(vec3(-0.707107, -0.353553, 0.612372));
+    elbowNormals_.push_back(vec3(0.000000, -0.500000, 0.866025));
+    elbowNormals_.push_back(vec3(0.707107, -0.353553, 0.612372));
+    elbowNormals_.push_back(vec3(1.000000, 0.000000, 0.000000));
+    elbowNormals_.push_back(vec3(1.000000, 0.000000, 0.000000));
+    elbowNormals_.push_back(vec3(0.707107, 0.183013, -0.683013));
+    elbowNormals_.push_back(vec3(0.000000, 0.258819, -0.965926));
+    elbowNormals_.push_back(vec3(-0.707107, 0.183013, -0.683013));
+    elbowNormals_.push_back(vec3(-1.000000, 0.000000, 0.000000));
+    elbowNormals_.push_back(vec3(-0.707107, -0.183013, 0.683013));
+    elbowNormals_.push_back(vec3(0.000000, -0.258819, 0.965926));
+    elbowNormals_.push_back(vec3(0.707107, -0.183013, 0.683013));
+    elbowNormals_.push_back(vec3(1.000000, 0.000000, 0.000000));
+    elbowNormals_.push_back(vec3(1.000000, 0.000000, 0.000000));
+    elbowNormals_.push_back(vec3(0.707107, 0.000000, -0.707107));
+    elbowNormals_.push_back(vec3(0.000000, 0.000000, -1.000000));
+    elbowNormals_.push_back(vec3(-0.707107, 0.000000, -0.707107));
+    elbowNormals_.push_back(vec3(-1.000000, 0.000000, 0.000000));
+    elbowNormals_.push_back(vec3(-0.707107, 0.000000, 0.707107));
+    elbowNormals_.push_back(vec3(0.000000, 0.000000, 1.000000));
+    elbowNormals_.push_back(vec3(0.707107, 0.000000, 0.707107));
+    elbowNormals_.push_back(vec3(1.000000, 0.000000, 0.000000));
+
+    elbowShadowVertices_.push_back(vec3(1.000000, 0.000000, 0.000000));
+    elbowShadowVertices_.push_back(vec3(0.707107, 0.707107, 0.000000));
+    elbowShadowVertices_.push_back(vec3(0.000000, 1.000000, 0.000000));
+    elbowShadowVertices_.push_back(vec3(-0.707107, 0.707107, 0.000000));
+    elbowShadowVertices_.push_back(vec3(-1.000000, 0.000000, 0.000000));
+    elbowShadowVertices_.push_back(vec3(-0.707107, -0.707107, 0.000000));
+    elbowShadowVertices_.push_back(vec3(0.000000, -1.000000, 0.000000));
+    elbowShadowVertices_.push_back(vec3(0.707107, -0.707107, 0.000000));
+    elbowShadowVertices_.push_back(vec3(1.000000, 0.000000, 0.000000));
+    elbowShadowVertices_.push_back(vec3(1.000000, 0.019215, 0.195090));
+    elbowShadowVertices_.push_back(vec3(0.707107, 0.712735, 0.057141));
+    elbowShadowVertices_.push_back(vec3(0.000000, 1.000000, 0.000000));
+    elbowShadowVertices_.push_back(vec3(-0.707107, 0.712735, 0.057141));
+    elbowShadowVertices_.push_back(vec3(-1.000000, 0.019215, 0.195090));
+    elbowShadowVertices_.push_back(vec3(-0.707107, -0.674305, 0.333040));
+    elbowShadowVertices_.push_back(vec3(0.000000, -0.961571, 0.390181));
+    elbowShadowVertices_.push_back(vec3(0.707107, -0.674305, 0.333040));
+    elbowShadowVertices_.push_back(vec3(1.000000, 0.019215, 0.195090));
+    elbowShadowVertices_.push_back(vec3(1.000000, 0.076120, 0.382683));
+    elbowShadowVertices_.push_back(vec3(0.707107, 0.729402, 0.112085));
+    elbowShadowVertices_.push_back(vec3(0.000000, 1.000000, 0.000000));
+    elbowShadowVertices_.push_back(vec3(-0.707107, 0.729402, 0.112085));
+    elbowShadowVertices_.push_back(vec3(-1.000000, 0.076120, 0.382683));
+    elbowShadowVertices_.push_back(vec3(-0.707107, -0.577161, 0.653282));
+    elbowShadowVertices_.push_back(vec3(0.000000, -0.847759, 0.765367));
+    elbowShadowVertices_.push_back(vec3(0.707107, -0.577161, 0.653282));
+    elbowShadowVertices_.push_back(vec3(1.000000, 0.076120, 0.382683));
+    elbowShadowVertices_.push_back(vec3(1.000000, 0.168530, 0.555570));
+    elbowShadowVertices_.push_back(vec3(0.707107, 0.756468, 0.162723));
+    elbowShadowVertices_.push_back(vec3(0.000000, 1.000000, 0.000000));
+    elbowShadowVertices_.push_back(vec3(-0.707107, 0.756468, 0.162723));
+    elbowShadowVertices_.push_back(vec3(-1.000000, 0.168530, 0.555570));
+    elbowShadowVertices_.push_back(vec3(-0.707107, -0.419407, 0.948418));
+    elbowShadowVertices_.push_back(vec3(0.000000, -0.662939, 1.111140));
+    elbowShadowVertices_.push_back(vec3(0.707107, -0.419407, 0.948418));
+    elbowShadowVertices_.push_back(vec3(1.000000, 0.168530, 0.555570));
+    elbowShadowVertices_.push_back(vec3(1.000000, 0.292893, 0.707107));
+    elbowShadowVertices_.push_back(vec3(0.707107, 0.792893, 0.207107));
+    elbowShadowVertices_.push_back(vec3(0.000000, 1.000000, 0.000000));
+    elbowShadowVertices_.push_back(vec3(-0.707107, 0.792893, 0.207107));
+    elbowShadowVertices_.push_back(vec3(-1.000000, 0.292893, 0.707107));
+    elbowShadowVertices_.push_back(vec3(-0.707107, -0.207107, 1.207107));
+    elbowShadowVertices_.push_back(vec3(0.000000, -0.414214, 1.414214));
+    elbowShadowVertices_.push_back(vec3(0.707107, -0.207107, 1.207107));
+    elbowShadowVertices_.push_back(vec3(1.000000, 0.292893, 0.707107));
+    elbowShadowVertices_.push_back(vec3(1.000000, 0.444430, 0.831470));
+    elbowShadowVertices_.push_back(vec3(0.707107, 0.837277, 0.243532));
+    elbowShadowVertices_.push_back(vec3(0.000000, 1.000000, 0.000000));
+    elbowShadowVertices_.push_back(vec3(-0.707107, 0.837277, 0.243532));
+    elbowShadowVertices_.push_back(vec3(-1.000000, 0.444430, 0.831470));
+    elbowShadowVertices_.push_back(vec3(-0.707107, 0.051582, 1.419407));
+    elbowShadowVertices_.push_back(vec3(0.000000, -0.111140, 1.662939));
+    elbowShadowVertices_.push_back(vec3(0.707107, 0.051582, 1.419407));
+    elbowShadowVertices_.push_back(vec3(1.000000, 0.444430, 0.831470));
+    elbowShadowVertices_.push_back(vec3(1.000000, 0.617317, 0.923880));
+    elbowShadowVertices_.push_back(vec3(0.707107, 0.887915, 0.270598));
+    elbowShadowVertices_.push_back(vec3(0.000000, 1.000000, 0.000000));
+    elbowShadowVertices_.push_back(vec3(-0.707107, 0.887915, 0.270598));
+    elbowShadowVertices_.push_back(vec3(-1.000000, 0.617317, 0.923880));
+    elbowShadowVertices_.push_back(vec3(-0.707107, 0.346719, 1.577161));
+    elbowShadowVertices_.push_back(vec3(0.000000, 0.234633, 1.847759));
+    elbowShadowVertices_.push_back(vec3(0.707107, 0.346719, 1.577161));
+    elbowShadowVertices_.push_back(vec3(1.000000, 0.617317, 0.923880));
+    elbowShadowVertices_.push_back(vec3(1.000000, 0.804910, 0.980785));
+    elbowShadowVertices_.push_back(vec3(0.707107, 0.942859, 0.287265));
+    elbowShadowVertices_.push_back(vec3(0.000000, 1.000000, 0.000000));
+    elbowShadowVertices_.push_back(vec3(-0.707107, 0.942859, 0.287265));
+    elbowShadowVertices_.push_back(vec3(-1.000000, 0.804910, 0.980785));
+    elbowShadowVertices_.push_back(vec3(-0.707107, 0.666960, 1.674305));
+    elbowShadowVertices_.push_back(vec3(0.000000, 0.609819, 1.961571));
+    elbowShadowVertices_.push_back(vec3(0.707107, 0.666960, 1.674305));
+    elbowShadowVertices_.push_back(vec3(1.000000, 0.804910, 0.980785));
+    elbowShadowVertices_.push_back(vec3(1.000000, 1.000000, 1.000000));
+    elbowShadowVertices_.push_back(vec3(0.707107, 1.000000, 0.292893));
+    elbowShadowVertices_.push_back(vec3(0.000000, 1.000000, 0.000000));
+    elbowShadowVertices_.push_back(vec3(-0.707107, 1.000000, 0.292893));
+    elbowShadowVertices_.push_back(vec3(-1.000000, 1.000000, 1.000000));
+    elbowShadowVertices_.push_back(vec3(-0.707107, 1.000000, 1.707107));
+    elbowShadowVertices_.push_back(vec3(0.000000, 1.000000, 2.000000));
+    elbowShadowVertices_.push_back(vec3(0.707107, 1.000000, 1.707107));
+    elbowShadowVertices_.push_back(vec3(1.000000, 1.000000, 1.000000));
+
+    // Now that we've setup the vertex data, we can setup the map of how
+    // that data will be laid out in the buffer object.
+    static const unsigned int sv3(sizeof(vec3));
+    dataMap_.scvOffset = 0;
+    dataMap_.scvSize = singleCylinderVertices_.size() * sv3;
+    dataMap_.totalSize = dataMap_.scvSize;
+    dataMap_.scnOffset = dataMap_.scvOffset + dataMap_.scvSize;
+    dataMap_.scnSize = singleCylinderNormals_.size() * sv3;
+    dataMap_.totalSize += dataMap_.scnSize;
+    dataMap_.dcvOffset = dataMap_.scnOffset + dataMap_.scnSize;
+    dataMap_.dcvSize = doubleCylinderVertices_.size() * sv3;
+    dataMap_.totalSize += dataMap_.dcvSize;
+    dataMap_.dcnOffset = dataMap_.dcvOffset + dataMap_.dcvSize;
+    dataMap_.dcnSize = doubleCylinderNormals_.size() * sv3;
+    dataMap_.totalSize += dataMap_.dcnSize;
+    dataMap_.evOffset = dataMap_.dcnOffset + dataMap_.dcnSize;
+    dataMap_.evSize = elbowVertices_.size() * sv3;
+    dataMap_.totalSize += dataMap_.evSize;
+    dataMap_.enOffset = dataMap_.evOffset + dataMap_.evSize;
+    dataMap_.enSize = elbowNormals_.size() * sv3;
+    dataMap_.totalSize += dataMap_.enSize;
+    dataMap_.esvOffset = dataMap_.enOffset + dataMap_.enSize;
+    dataMap_.esvSize = elbowShadowVertices_.size() * sv3;
+    dataMap_.totalSize += dataMap_.esvSize;
+
+    //
+    // The original implementation of both the logo and the lamp represented
+    // the vertex and normal data in a triply-dimensioned array of floats and
+    // all of the calls referenced double-indexed arrays of vector data.
+    // To my mind, this made the code look clunky and overly verbose.
+    // Representing the data as a STL vector of vec3 (itself a 3-float vector
+    // quantity) provides both an efficient container and allows for more
+    // concise looking code.  The slightly goofy loops (using the original 2
+    // dimensional indices to compute a single offset into the STL vector) are 
+    // a compromise to avoid rearranging the original data.
+    //
+    // - jesse 2010/10/04
+    //
+    for (unsigned int i = 0; i < 8; i++)
+    {
+        for (unsigned int j = 0; j < 9; j++)
+        {
+            unsigned int index1(i * 9 + j);
+            unsigned int index2((i + 1) * 9 + j);
+            indexData_.push_back(index1);
+            indexData_.push_back(index2);
+        }
+    }
+
+    // Initialize the stipple pattern
+    static const unsigned int patterns[] = { 0xaaaaaaaa, 0x55555555 };
+    for (unsigned int i = 0; i < textureResolution_; i++)
+    {
+        for (unsigned int j = 0; j < textureResolution_; j++)
+        {
+            // Alternate the pattern every other line.
+            unsigned int curMask(1 << j);
+            unsigned int curPattern(patterns[i % 2]);
+            textureImage_[i][j] = ((curPattern & curMask) >> j) * 255;
+        }
+    }
+}
+
+SGILogo::~SGILogo()
+{
+    if (valid_)
+    {
+        glDeleteBuffers(2, &bufferObjects_[0]);
+    }
+}
+
+void
+SGILogo::init()
+{
+    // Make sure we don't re-initialize...
+    if (valid_)
+    {
+        return;
+    }
+
+    // Initialize shader sources from input files and create programs from them
+    // The program for handling the main object with lighting...
+    string logo_vtx_filename(GLMARK_DATA_PATH"/shaders/ideas-logo.vert");
+    string logo_frg_filename(GLMARK_DATA_PATH"/shaders/ideas-logo.frag");
+    ShaderSource logo_vtx_source(logo_vtx_filename);
+    ShaderSource logo_frg_source(logo_frg_filename);
+    if (!Scene::load_shaders_from_strings(normalProgram_, logo_vtx_source.str(),
+                                          logo_frg_source.str()))
+    {
+        Log::error("No valid program for normal logo rendering\n");
+        return;
+    }
+    normalVertexIndex_ = normalProgram_[vertexAttribName_].location();
+    normalNormalIndex_ = normalProgram_[normalAttribName_].location();
+
+    // The program for handling the flat object...
+    string logo_flat_vtx_filename(GLMARK_DATA_PATH"/shaders/ideas-logo-flat.vert");
+    string logo_flat_frg_filename(GLMARK_DATA_PATH"/shaders/ideas-logo-flat.frag");
+    ShaderSource logo_flat_vtx_source(logo_flat_vtx_filename);
+    ShaderSource logo_flat_frg_source(logo_flat_frg_filename);
+    if (!Scene::load_shaders_from_strings(flatProgram_, logo_flat_vtx_source.str(),
+                                          logo_flat_frg_source.str()))
+    {
+        Log::error("No valid program for flat logo rendering\n");
+        return;
+    }
+    flatVertexIndex_ = flatProgram_[vertexAttribName_].location();
+
+    // The program for handling the shadow object with texturing...
+    string logo_shadow_vtx_filename(GLMARK_DATA_PATH"/shaders/ideas-logo-shadow.vert");
+    string logo_shadow_frg_filename(GLMARK_DATA_PATH"/shaders/ideas-logo-shadow.frag");
+    ShaderSource logo_shadow_vtx_source(logo_shadow_vtx_filename);
+    ShaderSource logo_shadow_frg_source(logo_shadow_frg_filename);
+    if (!Scene::load_shaders_from_strings(shadowProgram_, logo_shadow_vtx_source.str(),
+                                          logo_shadow_frg_source.str()))
+    {
+        Log::error("No valid program for shadow logo rendering\n");
+        return;
+    }
+    shadowVertexIndex_ = shadowProgram_[vertexAttribName_].location();
+
+    // We need 2 buffers for our work here.  One for the vertex data.
+    // and one for the index data.
+    glGenBuffers(2, &bufferObjects_[0]);
+
+    // First, setup the vertex data by binding the first buffer object, 
+    // allocating its data store, and filling it in with our vertex data.
+    glBindBuffer(GL_ARRAY_BUFFER, bufferObjects_[0]);
+    glBufferData(GL_ARRAY_BUFFER, dataMap_.totalSize, 0, GL_STATIC_DRAW);
+    glBufferSubData(GL_ARRAY_BUFFER, dataMap_.scvOffset, dataMap_.scvSize,
+                    &singleCylinderVertices_.front());
+    glBufferSubData(GL_ARRAY_BUFFER, dataMap_.scnOffset, dataMap_.scnSize,
+                    &singleCylinderNormals_.front());
+    glBufferSubData(GL_ARRAY_BUFFER, dataMap_.dcvOffset, dataMap_.dcvSize,
+                    &doubleCylinderVertices_.front());
+    glBufferSubData(GL_ARRAY_BUFFER, dataMap_.dcnOffset, dataMap_.dcnSize,
+                    &doubleCylinderNormals_.front());
+    glBufferSubData(GL_ARRAY_BUFFER, dataMap_.evOffset, dataMap_.evSize,
+                    &elbowVertices_.front());
+    glBufferSubData(GL_ARRAY_BUFFER, dataMap_.enOffset, dataMap_.enSize, 
+                    &elbowNormals_.front());
+    glBufferSubData(GL_ARRAY_BUFFER, dataMap_.esvOffset, dataMap_.esvSize, 
+                    &elbowShadowVertices_.front());
+
+    // Now repeat for our index data.
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferObjects_[1]);
+    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexData_.size() * sizeof(unsigned int),
+                 &indexData_.front(), GL_STATIC_DRAW);
+
+    // Setup our the texture that the shadow program will use...
+    glGenTextures(1, &textureName_);
+    glBindTexture(GL_TEXTURE_2D, textureName_);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA,
+                 textureResolution_, textureResolution_,
+                 0, GL_ALPHA, GL_UNSIGNED_BYTE, textureImage_);
+
+    // We're ready to go.
+    valid_ = true;
+}
+
+void
+SGILogo::bendForward(Stack4& ms)
+{
+    ms.translate(0.0, 1.0, 0.0);
+    ms.rotate(90.0, 1.0, 0.0, 0.0);
+    ms.translate(0.0, -1.0, 0.0);
+}
+
+void
+SGILogo::bendLeft(Stack4& ms)
+{
+    ms.rotate(-90.0, 0.0, 0.0, 1.0);
+    ms.translate(0.0, 1.0, 0.0);
+    ms.rotate(90.0, 1.0, 0.0, 0.0);
+    ms.translate(0.0, -1.0, 0.0);
+}
+
+void
+SGILogo::bendRight(Stack4& ms) 
+{
+    ms.rotate(90.0, 0.0, 0.0, 1.0);
+    ms.translate(0.0, 1.0, 0.0);
+    ms.rotate(90.0, 1.0, 0.0, 0.0);
+    ms.translate(0.0, -1.0, 0.0);
+}
+
+void
+SGILogo::drawDoubleCylinder(void)
+{
+    glVertexAttribPointer(vertexIndex_, 3, GL_FLOAT, GL_FALSE, 0, 
+        reinterpret_cast<const GLvoid*>(dataMap_.dcvOffset));
+    if (drawStyle_ == LOGO_NORMAL)
+    {
+        glVertexAttribPointer(normalNormalIndex_, 3, GL_FLOAT, GL_FALSE, 0, 
+            reinterpret_cast<const GLvoid*>(dataMap_.dcnOffset));
+    }
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, 18);
+}
+
+void
+SGILogo::drawSingleCylinder(void)
+{
+    glVertexAttribPointer(vertexIndex_, 3, GL_FLOAT, GL_FALSE, 0,
+        reinterpret_cast<const GLvoid*>(dataMap_.scvOffset));
+    if (drawStyle_ == LOGO_NORMAL)
+    {
+        glVertexAttribPointer(normalNormalIndex_, 3, GL_FLOAT, GL_FALSE, 0, 
+            reinterpret_cast<const GLvoid*>(dataMap_.scnOffset));
+    }
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, 18);
+}
+
+void
+SGILogo::drawElbow(void)
+{
+    static const unsigned int sui(sizeof(unsigned int));
+    unsigned int startIdx(0);
+    unsigned int endIdx(6);
+    if (drawStyle_ == LOGO_NORMAL)
+    {
+        glVertexAttribPointer(vertexIndex_, 3, GL_FLOAT, GL_FALSE, 0,
+            reinterpret_cast<const GLvoid*>(dataMap_.evOffset));
+        glVertexAttribPointer(normalNormalIndex_, 3, GL_FLOAT, GL_FALSE, 0,
+            reinterpret_cast<const GLvoid*>(dataMap_.enOffset));
+    }
+    else
+    {
+        endIdx = 8;
+        glVertexAttribPointer(vertexIndex_, 3, GL_FLOAT, GL_FALSE, 0,
+            reinterpret_cast<const GLvoid*>(dataMap_.esvOffset));
+    }
+
+    for (unsigned int i = startIdx; i < endIdx; i++)
+    {
+        unsigned int curOffset(i * 18 * sui);
+        glDrawElements(GL_TRIANGLE_STRIP, 18, GL_UNSIGNED_INT, 
+             reinterpret_cast<const GLvoid*>(curOffset));
+    }
+}
+
+// Generate a normal matrix from a modelview matrix
+//
+// Since we can't universally handle the normal matrix inside the
+// vertex shader (inverse() and transpose() built-ins not supported by
+// GLSL ES, for example), we'll generate it here, and load it as a
+// uniform.
+void
+SGILogo::updateXform(const mat4& mv, Program& program)
+{
+    if (drawStyle_ == LOGO_NORMAL)
+    {
+        LibMatrix::mat3 normalMatrix(mv[0][0], mv[1][0], mv[2][0],
+                                     mv[0][1], mv[1][1], mv[2][1],
+                                     mv[0][2], mv[1][2], mv[2][2]);
+        normalMatrix.transpose().inverse();
+        program[normalMatrixName_] = normalMatrix;
+    }
+    program[modelviewName_] = mv;
+}
+
+Program&
+SGILogo::getProgram()
+{
+    switch (drawStyle_)
+    {
+        case LOGO_NORMAL:
+            return normalProgram_;
+            break;
+        case LOGO_FLAT:
+            return flatProgram_;
+            break;
+        case LOGO_SHADOW:
+            return shadowProgram_;
+            break;            
+    }
+
+    return normalProgram_;
+}
+
+void
+SGILogo::draw(Stack4& modelview, 
+    Stack4& projection, 
+    const vec4& lightPosition,
+    DrawStyle style,
+    const uvec3& currentColor)
+{
+    if (!valid_)
+    {
+        return;
+    }
+
+    glBindBuffer(GL_ARRAY_BUFFER, bufferObjects_[0]);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferObjects_[1]);
+
+    // Setup the program to use based upon draw style and set it running.
+    drawStyle_ = style;
+    vec4 logoColor(currentColor.x() / 255.0, currentColor.y() / 255.0, currentColor.z() / 255.0, 1.0);
+    Program& curProgram = getProgram();
+    curProgram.start();
+    switch (drawStyle_)
+    {
+        case LOGO_NORMAL:
+            curProgram[lightPositionName_] = lightPosition;
+            vertexIndex_ = normalVertexIndex_;
+            glEnableVertexAttribArray(normalNormalIndex_);
+            break;
+        case LOGO_FLAT:
+            curProgram[logoColorName_] = logoColor;
+            vertexIndex_ = flatVertexIndex_;
+            break;
+        case LOGO_SHADOW:
+            vertexIndex_ = shadowVertexIndex_;
+            break;            
+    }
+
+    glEnableVertexAttribArray(vertexIndex_);
+    curProgram[projectionName_] = projection.getCurrent();
+    modelview.translate(5.500000, -3.500000, 4.500000);
+    modelview.translate(0.0,  0.0,  -7.000000);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawDoubleCylinder();
+    bendForward(modelview);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawElbow();
+    modelview.translate(0.0, 0.0, -7.000000);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawDoubleCylinder();
+    bendForward(modelview);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawElbow();
+    modelview.translate(0.0, 0.0, -5.000000);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawSingleCylinder();
+    bendRight(modelview);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawElbow();
+    modelview.translate(0.0, 0.0, -7.000000);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawDoubleCylinder();
+    bendForward(modelview);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawElbow();
+    modelview.translate(0.0, 0.0, -7.000000);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawDoubleCylinder();
+    bendForward(modelview);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawElbow();
+    modelview.translate(0.0, 0.0, -5.000000);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawSingleCylinder();
+    bendLeft(modelview);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawElbow();
+    modelview.translate(0.0, 0.0, -7.000000);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawDoubleCylinder();
+    bendForward(modelview);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawElbow();
+    modelview.translate(0.0, 0.0, -7.000000);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawDoubleCylinder();
+    bendForward(modelview);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawElbow();
+    modelview.translate(0.0, 0.0, -5.000000);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawSingleCylinder();
+    bendRight(modelview);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawElbow();
+    modelview.translate(0.0, 0.0, -7.000000);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawDoubleCylinder();
+    bendForward(modelview);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawElbow();
+    modelview.translate(0.0, 0.0, -7.000000);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawDoubleCylinder();
+    bendForward(modelview);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawElbow();
+    modelview.translate(0.0, 0.0, -5.000000);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawSingleCylinder();
+    bendLeft(modelview);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawElbow();
+    modelview.translate(0.0, 0.0, -7.000000);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawDoubleCylinder();
+    bendForward(modelview);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawElbow();
+    modelview.translate(0.0, 0.0, -7.000000);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawDoubleCylinder();
+    bendForward(modelview);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawElbow();
+    modelview.translate(0.0, 0.0, -5.000000);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawSingleCylinder();
+    bendRight(modelview);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawElbow();
+    modelview.translate(0.0, 0.0, -7.000000);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawDoubleCylinder();
+    bendForward(modelview);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawElbow();
+    modelview.translate(0.0, 0.0, -7.000000);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawDoubleCylinder();
+    bendForward(modelview);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawElbow();
+    modelview.translate(0.0, 0.0, -5.000000);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawSingleCylinder();
+    bendLeft(modelview);
+    updateXform(modelview.getCurrent(), curProgram);
+    drawElbow();
+    glDisableVertexAttribArray(vertexIndex_);
+    switch (drawStyle_)
+    {
+        case LOGO_NORMAL:
+            glDisableVertexAttribArray(normalNormalIndex_);
+            break;
+        case LOGO_FLAT:
+            break;
+        case LOGO_SHADOW:
+            break;            
+    }
+    curProgram.stop();
+
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+}

=== added file 'src/scene-ideas/logo.h'
--- src/scene-ideas/logo.h	1970-01-01 00:00:00 +0000
+++ src/scene-ideas/logo.h	2012-04-30 18:37:22 +0000
@@ -0,0 +1,126 @@
+/*
+ * (c) Copyright 1993, Silicon Graphics, Inc.
+ * Copyright Š 2012 Linaro Limited
+ *
+ * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
+ *
+ * glmark2 is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * glmark2.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Jesse Barker
+ */
+#ifndef LOGO_H_
+#define LOGO_H_
+
+#include <string>
+#include <vector>
+#include "vec.h"
+#include "stack.h"
+#include "gl-headers.h"
+#include "program.h"
+
+class SGILogo
+{
+public:
+    SGILogo();
+    ~SGILogo();
+
+    // Initialize the logo
+    void init();
+    bool valid() const { return valid_; }
+    void setPosition(const LibMatrix::vec3& position) { currentPosition_ = position; }
+    // Tell the logo to draw itself. DrawStyle tells it how.
+    // - LOGO_NORMAL renders the logo lit and shaded.
+    // - LOGO_FLAT renders the logo as if flattened onto a surface.
+    // - LOGO_SHADOW renders a stippled-looking shadow of the object.
+    enum DrawStyle
+    {
+        LOGO_NORMAL, 
+        LOGO_FLAT, 
+        LOGO_SHADOW
+    };
+    void draw(LibMatrix::Stack4& modelview, LibMatrix::Stack4& projection, 
+              const LibMatrix::vec4& lightPosition, DrawStyle style, 
+              const LibMatrix::uvec3& color = LibMatrix::uvec3());
+
+private:
+    void drawElbow();
+    void drawSingleCylinder();
+    void drawDoubleCylinder();
+    void bendLeft(LibMatrix::Stack4& ms);
+    void bendRight(LibMatrix::Stack4& ms);
+    void bendForward(LibMatrix::Stack4& ms);
+    void updateXform(const LibMatrix::mat4& mv, Program& program);
+    Program& getProgram();
+    LibMatrix::vec3 currentPosition_;
+    std::vector<LibMatrix::vec3> singleCylinderVertices_;
+    std::vector<LibMatrix::vec3> singleCylinderNormals_;
+    std::vector<LibMatrix::vec3> doubleCylinderVertices_;
+    std::vector<LibMatrix::vec3> doubleCylinderNormals_;
+    std::vector<LibMatrix::vec3> elbowVertices_;
+    std::vector<LibMatrix::vec3> elbowNormals_;
+    std::vector<LibMatrix::vec3> elbowShadowVertices_;
+    std::vector<unsigned int> indexData_;
+    // A simple map so we know where each section of our data starts within
+    // our vertex buffer object.
+    struct VertexDataMap
+    {
+        unsigned int scvOffset;
+        unsigned int scvSize;
+        unsigned int scnOffset;
+        unsigned int scnSize;
+        unsigned int dcvOffset;
+        unsigned int dcvSize;
+        unsigned int dcnOffset;
+        unsigned int dcnSize;
+        unsigned int evOffset;
+        unsigned int evSize;
+        unsigned int enOffset;
+        unsigned int enSize;
+        unsigned int esvOffset;
+        unsigned int esvSize;
+        unsigned int totalSize;
+    } dataMap_;
+    unsigned int bufferObjects_[2];
+    Program normalProgram_;
+    Program flatProgram_;
+    Program shadowProgram_;
+    std::string normalVertexShader_;
+    std::string normalFragmentShader_;
+    std::string flatVertexShader_;
+    std::string flatFragmentShader_;
+    std::string shadowVertexShader_;
+    std::string shadowFragmentShader_;
+    int normalNormalIndex_;
+    int normalVertexIndex_;
+    int flatVertexIndex_;
+    int shadowVertexIndex_;
+    int vertexIndex_;
+    static const std::string modelviewName_;
+    static const std::string projectionName_;
+    static const std::string lightPositionName_;
+    static const std::string logoColorName_;
+    static const std::string vertexAttribName_;
+    static const std::string normalAttribName_;
+    static const std::string normalMatrixName_;
+    // "Shadow" state
+    GLuint textureName_;
+    GLubyte textureImage_[32][32];
+    // This is the size in each direction of our texture image
+    static const unsigned int textureResolution_;
+    bool valid_;
+    DrawStyle drawStyle_;
+};
+
+#endif // LOGO_H_

=== added file 'src/scene-ideas/m.cc'
--- src/scene-ideas/m.cc	1970-01-01 00:00:00 +0000
+++ src/scene-ideas/m.cc	2012-04-30 18:37:22 +0000
@@ -0,0 +1,210 @@
+/*
+ * Vertex position data describing the letter 'm'
+ *
+ * (c) Copyright 1993, Silicon Graphics, Inc.
+ * Copyright Š 2012 Linaro Limited
+ *
+ * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
+ *
+ * glmark2 is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * glmark2.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Jesse Barker
+ */
+#include "characters.h"
+
+using LibMatrix::vec2;
+
+LetterM::LetterM()
+{
+    // Vertex data...
+    vertexData_.push_back(vec2(0.590769, 9.449335));
+    vertexData_.push_back(vec2(2.116923, 9.842375));
+    vertexData_.push_back(vec2(1.362051, 9.383828));
+    vertexData_.push_back(vec2(2.527179, 9.825998));
+    vertexData_.push_back(vec2(1.591795, 9.072672));
+    vertexData_.push_back(vec2(2.789744, 9.514841));
+    vertexData_.push_back(vec2(1.690256, 8.663255));
+    vertexData_.push_back(vec2(2.658462, 8.335722));
+    vertexData_.push_back(vec2(1.575385, 7.222108));
+    vertexData_.push_back(vec2(2.067692, 6.255886));
+    vertexData_.push_back(vec2(0.918974, 4.028659));
+    vertexData_.push_back(vec2(1.050256, 3.013306));
+    vertexData_.push_back(vec2(0.705641, 3.013306));
+    vertexData_.push_back(vec2(2.018461, 6.386899));
+    vertexData_.push_back(vec2(1.788718, 5.617196));
+    vertexData_.push_back(vec2(2.921026, 7.991812));
+    vertexData_.push_back(vec2(3.167180, 8.008188));
+    vertexData_.push_back(vec2(3.544615, 8.827022));
+    vertexData_.push_back(vec2(3.872821, 8.843398));
+    vertexData_.push_back(vec2(4.414359, 9.547595));
+    vertexData_.push_back(vec2(4.447179, 9.056294));
+    vertexData_.push_back(vec2(5.120000, 9.891504));
+    vertexData_.push_back(vec2(4.841026, 8.843398));
+    vertexData_.push_back(vec2(5.825641, 9.809621));
+    vertexData_.push_back(vec2(5.005128, 8.040941));
+    vertexData_.push_back(vec2(5.989744, 8.761515));
+    vertexData_.push_back(vec2(4.906667, 6.714432));
+    vertexData_.push_back(vec2(5.595897, 7.123848));
+    vertexData_.push_back(vec2(3.987692, 2.996929));
+    vertexData_.push_back(vec2(4.348718, 2.996929));
+    vertexData_.push_back(vec2(5.218462, 5.977482));
+    vertexData_.push_back(vec2(5.251282, 6.354146));
+    vertexData_.push_back(vec2(6.449231, 7.893552));
+    vertexData_.push_back(vec2(6.400000, 8.221085));
+    vertexData_.push_back(vec2(7.302564, 8.843398));
+    vertexData_.push_back(vec2(7.351795, 9.334698));
+    vertexData_.push_back(vec2(7.827693, 9.154554));
+    vertexData_.push_back(vec2(8.008205, 9.842375));
+    vertexData_.push_back(vec2(8.139487, 9.121801));
+    vertexData_.push_back(vec2(8.795897, 9.973388));
+    vertexData_.push_back(vec2(8.402051, 8.728762));
+    vertexData_.push_back(vec2(9.337436, 9.531218));
+    vertexData_.push_back(vec2(8.402051, 8.040941));
+    vertexData_.push_back(vec2(9.288205, 8.433982));
+    vertexData_.push_back(vec2(7.745641, 5.813715));
+    vertexData_.push_back(vec2(8.320000, 5.928352));
+    vertexData_.push_back(vec2(7.286154, 4.012282));
+    vertexData_.push_back(vec2(7.991795, 4.126919));
+    vertexData_.push_back(vec2(7.499487, 3.357216));
+    vertexData_.push_back(vec2(8.533334, 3.766633));
+    vertexData_.push_back(vec2(8.123077, 3.062436));
+    vertexData_.push_back(vec2(8.927179, 3.832139));
+    vertexData_.push_back(vec2(8.910769, 3.340839));
+    vertexData_.push_back(vec2(9.550769, 4.126919));
+
+    // Index data...
+    indexData_.push_back(0);
+    indexData_.push_back(2);
+    indexData_.push_back(4);
+    indexData_.push_back(6);
+    indexData_.push_back(8);
+    indexData_.push_back(10);
+    indexData_.push_back(12);
+    indexData_.push_back(11);
+    indexData_.push_back(9);
+    indexData_.push_back(7);
+    indexData_.push_back(5);
+    indexData_.push_back(3);
+    indexData_.push_back(1);
+    indexData_.push_back(14);
+    indexData_.push_back(16);
+    indexData_.push_back(18);
+    indexData_.push_back(20);
+    indexData_.push_back(22);
+    indexData_.push_back(24);
+    indexData_.push_back(26);
+    indexData_.push_back(28);
+    indexData_.push_back(29);
+    indexData_.push_back(27);
+    indexData_.push_back(25);
+    indexData_.push_back(23);
+    indexData_.push_back(21);
+    indexData_.push_back(19);
+    indexData_.push_back(17);
+    indexData_.push_back(15);
+    indexData_.push_back(13);
+    indexData_.push_back(30);
+    indexData_.push_back(32);
+    indexData_.push_back(34);
+    indexData_.push_back(36);
+    indexData_.push_back(38);
+    indexData_.push_back(40);
+    indexData_.push_back(42);
+    indexData_.push_back(44);
+    indexData_.push_back(46);
+    indexData_.push_back(48);
+    indexData_.push_back(50);
+    indexData_.push_back(52);
+    indexData_.push_back(53);
+    indexData_.push_back(51);
+    indexData_.push_back(49);
+    indexData_.push_back(47);
+    indexData_.push_back(45);
+    indexData_.push_back(43);
+    indexData_.push_back(41);
+    indexData_.push_back(39);
+    indexData_.push_back(37);
+    indexData_.push_back(35);
+    indexData_.push_back(33);
+    indexData_.push_back(31);
+    indexData_.push_back(0);
+    indexData_.push_back(1);
+    indexData_.push_back(2);
+    indexData_.push_back(3);
+    indexData_.push_back(4);
+    indexData_.push_back(5);
+    indexData_.push_back(6);
+    indexData_.push_back(7);
+    indexData_.push_back(8);
+    indexData_.push_back(9);
+    indexData_.push_back(10);
+    indexData_.push_back(11);
+    indexData_.push_back(12);
+    indexData_.push_back(13);
+    indexData_.push_back(14);
+    indexData_.push_back(15);
+    indexData_.push_back(16);
+    indexData_.push_back(17);
+    indexData_.push_back(18);
+    indexData_.push_back(19);
+    indexData_.push_back(20);
+    indexData_.push_back(21);
+    indexData_.push_back(22);
+    indexData_.push_back(23);
+    indexData_.push_back(24);
+    indexData_.push_back(25);
+    indexData_.push_back(26);
+    indexData_.push_back(27);
+    indexData_.push_back(28);
+    indexData_.push_back(29);
+    indexData_.push_back(30);
+    indexData_.push_back(31);
+    indexData_.push_back(32);
+    indexData_.push_back(33);
+    indexData_.push_back(34);
+    indexData_.push_back(35);
+    indexData_.push_back(36);
+    indexData_.push_back(37);
+    indexData_.push_back(38);
+    indexData_.push_back(39);
+    indexData_.push_back(40);
+    indexData_.push_back(41);
+    indexData_.push_back(42);
+    indexData_.push_back(43);
+    indexData_.push_back(44);
+    indexData_.push_back(45);
+    indexData_.push_back(46);
+    indexData_.push_back(47);
+    indexData_.push_back(48);
+    indexData_.push_back(49);
+    indexData_.push_back(50);
+    indexData_.push_back(51);
+    indexData_.push_back(52);
+    indexData_.push_back(53);
+
+    // Primitive state so that the draw call can issue the primitives we want.
+    unsigned int curOffset(0);
+    primVec_.push_back(PrimitiveState(GL_LINE_STRIP, 13, curOffset));
+    curOffset += (13 * sizeof(unsigned int));
+    primVec_.push_back(PrimitiveState(GL_LINE_STRIP, 17, curOffset));
+    curOffset += (17 * sizeof(unsigned int));
+    primVec_.push_back(PrimitiveState(GL_LINE_STRIP, 24, curOffset));
+    curOffset += (24 * sizeof(unsigned int));
+    primVec_.push_back(PrimitiveState(GL_TRIANGLE_STRIP, 13, curOffset));
+    curOffset += (13 * sizeof(unsigned int));
+    primVec_.push_back(PrimitiveState(GL_TRIANGLE_STRIP, 17, curOffset));
+    curOffset += (17 * sizeof(unsigned int));
+    primVec_.push_back(PrimitiveState(GL_TRIANGLE_STRIP, 24, curOffset));
+}

=== added file 'src/scene-ideas/n.cc'
--- src/scene-ideas/n.cc	1970-01-01 00:00:00 +0000
+++ src/scene-ideas/n.cc	2012-04-30 18:37:22 +0000
@@ -0,0 +1,146 @@
+/*
+ * Vertex position data describing the letter 'o'
+ *
+ * (c) Copyright 1993, Silicon Graphics, Inc.
+ * Copyright Š 2012 Linaro Limited
+ *
+ * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
+ *
+ * glmark2 is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * glmark2.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Jesse Barker
+ */
+#include "characters.h"
+
+using LibMatrix::vec2;
+
+LetterN::LetterN()
+{
+    // Vertex data...
+    vertexData_.push_back(vec2(1.009307, 9.444788));
+    vertexData_.push_back(vec2(2.548087, 9.742002));
+    vertexData_.push_back(vec2(1.737332, 9.213622));
+    vertexData_.push_back(vec2(2.994829, 9.659443));
+    vertexData_.push_back(vec2(1.985522, 8.751290));
+    vertexData_.push_back(vec2(3.127198, 9.180598));
+    vertexData_.push_back(vec2(1.935884, 7.975232));
+    vertexData_.push_back(vec2(2.481903, 6.571723));
+    vertexData_.push_back(vec2(1.472596, 5.019608));
+    vertexData_.push_back(vec2(1.439504, 2.988648));
+    vertexData_.push_back(vec2(1.025853, 2.988648));
+    vertexData_.push_back(vec2(2.283350, 6.059855));
+    vertexData_.push_back(vec2(2.035160, 5.366357));
+    vertexData_.push_back(vec2(3.292658, 7.711042));
+    vertexData_.push_back(vec2(3.540848, 7.744066));
+    vertexData_.push_back(vec2(4.384695, 9.031992));
+    vertexData_.push_back(vec2(4.699069, 8.916409));
+    vertexData_.push_back(vec2(5.609100, 9.808049));
+    vertexData_.push_back(vec2(5.145812, 8.982456));
+    vertexData_.push_back(vec2(6.155119, 9.791537));
+    vertexData_.push_back(vec2(5.410548, 8.635707));
+    vertexData_.push_back(vec2(6.337125, 9.312694));
+    vertexData_.push_back(vec2(5.360910, 7.991744));
+    vertexData_.push_back(vec2(6.088935, 8.090816));
+    vertexData_.push_back(vec2(4.947259, 5.977296));
+    vertexData_.push_back(vec2(5.261634, 4.804954));
+    vertexData_.push_back(vec2(4.616339, 4.028896));
+    vertexData_.push_back(vec2(5.211996, 3.962848));
+    vertexData_.push_back(vec2(4.732162, 3.318886));
+    vertexData_.push_back(vec2(5.559462, 3.814241));
+    vertexData_.push_back(vec2(5.228542, 3.038184));
+    vertexData_.push_back(vec2(5.940021, 3.814241));
+    vertexData_.push_back(vec2(5.906929, 3.335397));
+    vertexData_.push_back(vec2(6.684591, 4.094943));
+
+    // Index data...
+    indexData_.push_back(0);
+    indexData_.push_back(1);
+    indexData_.push_back(2);
+    indexData_.push_back(3);
+    indexData_.push_back(4);
+    indexData_.push_back(5);
+    indexData_.push_back(6);
+    indexData_.push_back(7);
+    indexData_.push_back(8);
+    indexData_.push_back(9);
+    indexData_.push_back(10);
+    indexData_.push_back(11);
+    indexData_.push_back(12);
+    indexData_.push_back(13);
+    indexData_.push_back(14);
+    indexData_.push_back(15);
+    indexData_.push_back(16);
+    indexData_.push_back(17);
+    indexData_.push_back(18);
+    indexData_.push_back(19);
+    indexData_.push_back(20);
+    indexData_.push_back(21);
+    indexData_.push_back(22);
+    indexData_.push_back(23);
+    indexData_.push_back(24);
+    indexData_.push_back(25);
+    indexData_.push_back(26);
+    indexData_.push_back(27);
+    indexData_.push_back(28);
+    indexData_.push_back(29);
+    indexData_.push_back(30);
+    indexData_.push_back(31);
+    indexData_.push_back(32);
+    indexData_.push_back(33);
+    indexData_.push_back(0);
+    indexData_.push_back(2);
+    indexData_.push_back(4);
+    indexData_.push_back(6);
+    indexData_.push_back(8);
+    indexData_.push_back(10);
+    indexData_.push_back(9);
+    indexData_.push_back(7);
+    indexData_.push_back(5);
+    indexData_.push_back(3);
+    indexData_.push_back(1);
+    indexData_.push_back(12);
+    indexData_.push_back(14);
+    indexData_.push_back(16);
+    indexData_.push_back(18);
+    indexData_.push_back(20);
+    indexData_.push_back(22);
+    indexData_.push_back(24);
+    indexData_.push_back(26);
+    indexData_.push_back(28);
+    indexData_.push_back(30);
+    indexData_.push_back(32);
+    indexData_.push_back(33);
+    indexData_.push_back(31);
+    indexData_.push_back(29);
+    indexData_.push_back(27);
+    indexData_.push_back(25);
+    indexData_.push_back(23);
+    indexData_.push_back(21);
+    indexData_.push_back(19);
+    indexData_.push_back(17);
+    indexData_.push_back(15);
+    indexData_.push_back(13);
+    indexData_.push_back(11);
+
+    // Primitive state so that the draw call can issue the primitives we want.
+    unsigned int curOffset(0);
+    primVec_.push_back(PrimitiveState(GL_TRIANGLE_STRIP, 11, curOffset));
+    curOffset += (11 * sizeof(unsigned int));
+    primVec_.push_back(PrimitiveState(GL_TRIANGLE_STRIP, 23, curOffset));
+    curOffset += (23 * sizeof(unsigned int));
+    primVec_.push_back(PrimitiveState(GL_LINE_STRIP, 11, curOffset));
+    curOffset += (11 * sizeof(unsigned int));
+    primVec_.push_back(PrimitiveState(GL_LINE_STRIP, 23, curOffset));
+}

=== added file 'src/scene-ideas/o.cc'
--- src/scene-ideas/o.cc	1970-01-01 00:00:00 +0000
+++ src/scene-ideas/o.cc	2012-04-30 18:37:22 +0000
@@ -0,0 +1,139 @@
+/*
+ * Vertex position data describing the letter 'o'
+ *
+ * (c) Copyright 1993, Silicon Graphics, Inc.
+ * Copyright Š 2012 Linaro Limited
+ *
+ * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
+ *
+ * glmark2 is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * glmark2.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Jesse Barker
+ */
+#include "characters.h"
+
+using LibMatrix::vec2;
+
+LetterO::LetterO()
+{
+    // Vertex data...
+    vertexData_.push_back(vec2(2.975610, 9.603255));
+    vertexData_.push_back(vec2(2.878049, 9.342828));
+    vertexData_.push_back(vec2(2.292683, 9.131231));
+    vertexData_.push_back(vec2(2.048780, 8.691760));
+    vertexData_.push_back(vec2(1.707317, 8.528993));
+    vertexData_.push_back(vec2(1.658537, 7.731434));
+    vertexData_.push_back(vec2(0.878049, 7.047813));
+    vertexData_.push_back(vec2(1.349594, 5.550356));
+    vertexData_.push_back(vec2(0.569106, 5.029501));
+    vertexData_.push_back(vec2(1.528455, 4.443540));
+    vertexData_.push_back(vec2(0.991870, 3.434385));
+    vertexData_.push_back(vec2(1.967480, 3.955239));
+    vertexData_.push_back(vec2(1.772358, 2.994914));
+    vertexData_.push_back(vec2(2.422764, 3.825025));
+    vertexData_.push_back(vec2(2.829268, 3.092574));
+    vertexData_.push_back(vec2(3.154472, 3.971516));
+    vertexData_.push_back(vec2(3.512195, 3.727365));
+    vertexData_.push_back(vec2(3.772358, 4.264496));
+    vertexData_.push_back(vec2(4.130081, 4.524924));
+    vertexData_.push_back(vec2(4.162601, 4.996948));
+    vertexData_.push_back(vec2(4.699187, 5.403866));
+    vertexData_.push_back(vec2(4.471545, 6.461852));
+    vertexData_.push_back(vec2(5.219512, 7.243133));
+    vertexData_.push_back(vec2(4.439024, 8.105799));
+    vertexData_.push_back(vec2(5.235772, 8.756866));
+    vertexData_.push_back(vec2(4.065041, 8.870804));
+    vertexData_.push_back(vec2(4.991870, 9.391658));
+    vertexData_.push_back(vec2(3.853658, 9.228891));
+    vertexData_.push_back(vec2(4.390244, 9.912513));
+    vertexData_.push_back(vec2(3.463415, 9.407935));
+    vertexData_.push_back(vec2(3.674797, 9.912513));
+    vertexData_.push_back(vec2(2.829268, 9.342828));
+    vertexData_.push_back(vec2(2.959350, 9.603255));
+
+    // Index data...
+    indexData_.push_back(0);
+    indexData_.push_back(1);
+    indexData_.push_back(2);
+    indexData_.push_back(3);
+    indexData_.push_back(4);
+    indexData_.push_back(5);
+    indexData_.push_back(6);
+    indexData_.push_back(7);
+    indexData_.push_back(8);
+    indexData_.push_back(9);
+    indexData_.push_back(10);
+    indexData_.push_back(11);
+    indexData_.push_back(12);
+    indexData_.push_back(13);
+    indexData_.push_back(14);
+    indexData_.push_back(15);
+    indexData_.push_back(16);
+    indexData_.push_back(17);
+    indexData_.push_back(18);
+    indexData_.push_back(19);
+    indexData_.push_back(20);
+    indexData_.push_back(21);
+    indexData_.push_back(22);
+    indexData_.push_back(23);
+    indexData_.push_back(24);
+    indexData_.push_back(25);
+    indexData_.push_back(26);
+    indexData_.push_back(27);
+    indexData_.push_back(28);
+    indexData_.push_back(29);
+    indexData_.push_back(30);
+    indexData_.push_back(31);
+    indexData_.push_back(32);
+    indexData_.push_back(0);
+    indexData_.push_back(2);
+    indexData_.push_back(4);
+    indexData_.push_back(6);
+    indexData_.push_back(8);
+    indexData_.push_back(10);
+    indexData_.push_back(12);
+    indexData_.push_back(14);
+    indexData_.push_back(16);
+    indexData_.push_back(18);
+    indexData_.push_back(20);
+    indexData_.push_back(22);
+    indexData_.push_back(24);
+    indexData_.push_back(26);
+    indexData_.push_back(28);
+    indexData_.push_back(30);
+    indexData_.push_back(32);
+    indexData_.push_back(31);
+    indexData_.push_back(29);
+    indexData_.push_back(27);
+    indexData_.push_back(25);
+    indexData_.push_back(23);
+    indexData_.push_back(21);
+    indexData_.push_back(19);
+    indexData_.push_back(17);
+    indexData_.push_back(15);
+    indexData_.push_back(13);
+    indexData_.push_back(11);
+    indexData_.push_back(9);
+    indexData_.push_back(7);
+    indexData_.push_back(5);
+    indexData_.push_back(3);
+    indexData_.push_back(1);
+
+    // Primitive state so that the draw call can issue the primitives we want.
+    unsigned int curOffset(0);
+    primVec_.push_back(PrimitiveState(GL_TRIANGLE_STRIP, 33, curOffset));
+    curOffset += (33 * sizeof(unsigned int));
+    primVec_.push_back(PrimitiveState(GL_LINE_STRIP, 33, curOffset));
+}

=== added file 'src/scene-ideas/s.cc'
--- src/scene-ideas/s.cc	1970-01-01 00:00:00 +0000
+++ src/scene-ideas/s.cc	2012-04-30 18:37:22 +0000
@@ -0,0 +1,130 @@
+/*
+ * Vertex position data describing the letter 's'
+ *
+ * (c) Copyright 1993, Silicon Graphics, Inc.
+ * Copyright Š 2012 Linaro Limited
+ *
+ * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
+ *
+ * glmark2 is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * glmark2.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Jesse Barker
+ */
+#include "characters.h"
+
+using LibMatrix::vec2;
+
+LetterS::LetterS()
+{
+    // Vertex data...
+    vertexData_.push_back(vec2(0.860393, 5.283798));
+    vertexData_.push_back(vec2(0.529473, 3.550052));
+    vertexData_.push_back(vec2(0.992761, 4.491228));
+    vertexData_.push_back(vec2(0.910031, 3.368421));
+    vertexData_.push_back(vec2(1.240951, 3.830753));
+    vertexData_.push_back(vec2(1.456050, 3.104231));
+    vertexData_.push_back(vec2(1.935884, 3.517028));
+    vertexData_.push_back(vec2(2.002068, 2.988648));
+    vertexData_.push_back(vec2(2.763185, 3.533540));
+    vertexData_.push_back(vec2(3.061013, 3.120743));
+    vertexData_.push_back(vec2(3.391934, 3.748194));
+    vertexData_.push_back(vec2(4.053774, 3.632611));
+    vertexData_.push_back(vec2(3.822130, 4.540764));
+    vertexData_.push_back(vec2(4.550155, 4.590299));
+    vertexData_.push_back(vec2(3.656670, 5.465428));
+    vertexData_.push_back(vec2(4.517063, 5.713106));
+    vertexData_.push_back(vec2(3.276112, 5.894737));
+    vertexData_.push_back(vec2(3.921407, 6.538700));
+    vertexData_.push_back(vec2(2.299896, 6.736842));
+    vertexData_.push_back(vec2(3.044467, 7.430341));
+    vertexData_.push_back(vec2(1.886246, 7.496388));
+    vertexData_.push_back(vec2(2.581179, 8.222910));
+    vertexData_.push_back(vec2(1.902792, 8.751290));
+    vertexData_.push_back(vec2(2.680455, 8.883385));
+    vertexData_.push_back(vec2(2.283350, 9.312694));
+    vertexData_.push_back(vec2(3.358842, 9.609907));
+    vertexData_.push_back(vec2(3.507756, 9.907121));
+    vertexData_.push_back(vec2(4.285419, 9.758514));
+    vertexData_.push_back(vec2(5.112720, 9.973168));
+    vertexData_.push_back(vec2(4.748707, 9.593395));
+
+    // Index data...
+    indexData_.push_back(0);
+    indexData_.push_back(1);
+    indexData_.push_back(2);
+    indexData_.push_back(3);
+    indexData_.push_back(4);
+    indexData_.push_back(5);
+    indexData_.push_back(6);
+    indexData_.push_back(7);
+    indexData_.push_back(8);
+    indexData_.push_back(9);
+    indexData_.push_back(10);
+    indexData_.push_back(11);
+    indexData_.push_back(12);
+    indexData_.push_back(13);
+    indexData_.push_back(14);
+    indexData_.push_back(15);
+    indexData_.push_back(16);
+    indexData_.push_back(17);
+    indexData_.push_back(18);
+    indexData_.push_back(19);
+    indexData_.push_back(20);
+    indexData_.push_back(21);
+    indexData_.push_back(22);
+    indexData_.push_back(23);
+    indexData_.push_back(24);
+    indexData_.push_back(25);
+    indexData_.push_back(26);
+    indexData_.push_back(27);
+    indexData_.push_back(28);
+    indexData_.push_back(29);
+    indexData_.push_back(0);
+    indexData_.push_back(2);
+    indexData_.push_back(4);
+    indexData_.push_back(6);
+    indexData_.push_back(8);
+    indexData_.push_back(10);
+    indexData_.push_back(12);
+    indexData_.push_back(14);
+    indexData_.push_back(16);
+    indexData_.push_back(18);
+    indexData_.push_back(20);
+    indexData_.push_back(22);
+    indexData_.push_back(24);
+    indexData_.push_back(26);
+    indexData_.push_back(28);
+    indexData_.push_back(29);
+    indexData_.push_back(27);
+    indexData_.push_back(25);
+    indexData_.push_back(23);
+    indexData_.push_back(21);
+    indexData_.push_back(19);
+    indexData_.push_back(17);
+    indexData_.push_back(15);
+    indexData_.push_back(13);
+    indexData_.push_back(11);
+    indexData_.push_back(9);
+    indexData_.push_back(7);
+    indexData_.push_back(5);
+    indexData_.push_back(3);
+    indexData_.push_back(1);
+
+    // Primitive state so that the draw call can issue the primitives we want.
+    unsigned int curOffset(0);
+    primVec_.push_back(PrimitiveState(GL_TRIANGLE_STRIP, 30, curOffset));
+    curOffset += (30 * sizeof(unsigned int));
+    primVec_.push_back(PrimitiveState(GL_LINE_STRIP, 30, curOffset));
+}

=== added file 'src/scene-ideas/splines.cc'
--- src/scene-ideas/splines.cc	1970-01-01 00:00:00 +0000
+++ src/scene-ideas/splines.cc	2012-04-30 18:37:22 +0000
@@ -0,0 +1,200 @@
+/*
+ * (c) Copyright 1993, Silicon Graphics, Inc.
+ * Copyright Š 2012 Linaro Limited
+ *
+ * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
+ *
+ * glmark2 is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * glmark2.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Jesse Barker
+ */
+#include "splines.h"
+
+using LibMatrix::vec3;
+
+void
+Spline::calcParams()
+{
+    unsigned int numParams(controlData_.size() - 3);
+    paramData_ = new param[numParams];
+    for (unsigned int i = 0; i < numParams; i++)
+    {
+        float x3(controlData_[i + 1].x());
+        float x2(controlData_[i + 2].x() - controlData_[i].x());
+        float x1(2.0 * controlData_[i].x() +
+	        -2.0 * controlData_[i+1].x() +
+	         1.0 * controlData_[i+2].x() +
+	        -1.0 * controlData_[i+3].x());
+        float x0(-1.0 * controlData_[i].x() +
+	          1.0 * controlData_[i+1].x() +
+	         -1.0 * controlData_[i+2].x() +
+	          1.0 * controlData_[i+3].x());
+        float y3(controlData_[i + 1].y());
+        float y2(controlData_[i + 2].y() - controlData_[i].y());
+        float y1(2.0 * controlData_[i].y() +
+	        -2.0 * controlData_[i+1].y() +
+	         1.0 * controlData_[i+2].y() +
+	        -1.0 * controlData_[i+3].y());
+        float y0(-1.0 * controlData_[i].y() +
+	          1.0 * controlData_[i+1].y() +
+	         -1.0 * controlData_[i+2].y() +
+	          1.0 * controlData_[i+3].y());
+        float z3(controlData_[i + 1].z());
+        float z2(controlData_[i + 2].z() - controlData_[i].z());
+        float z1(2.0 * controlData_[i].z() +
+	        -2.0 * controlData_[i+1].z() +
+	         1.0 * controlData_[i+2].z() +
+	        -1.0 * controlData_[i+3].z());
+        float z0(-1.0 * controlData_[i].z() +
+	          1.0 * controlData_[i+1].z() +
+	         -1.0 * controlData_[i+2].z() +
+	          1.0 * controlData_[i+3].z());
+        paramData_[i][3].x(x3);
+        paramData_[i][2].x(x2);
+        paramData_[i][1].x(x1);
+        paramData_[i][0].x(x0);
+        paramData_[i][3].y(y3);
+        paramData_[i][2].y(y2);
+        paramData_[i][1].y(y1);
+        paramData_[i][0].y(y0);
+        paramData_[i][3].z(z3);
+        paramData_[i][2].z(z2);
+        paramData_[i][1].z(z1);
+        paramData_[i][0].z(z0);
+    }
+}
+
+void
+Spline::getCurrentVec(float currentTime, vec3& v) const
+{
+    unsigned int integerTime(static_cast<unsigned int>(currentTime));
+    float t(currentTime - static_cast<float>(integerTime));
+    v.x(paramData_[integerTime][3].x() +
+	paramData_[integerTime][2].x() * t +
+	paramData_[integerTime][1].x() * t * t +
+	paramData_[integerTime][0].x() * t * t * t);
+    v.y(paramData_[integerTime][3].y() +
+	paramData_[integerTime][2].y() * t +
+	paramData_[integerTime][1].y() * t * t +
+	paramData_[integerTime][0].y() * t * t * t);
+    v.z(paramData_[integerTime][3].z() +
+	paramData_[integerTime][2].z() * t +
+	paramData_[integerTime][1].z() * t * t +
+	paramData_[integerTime][0].z() * t * t * t);
+}
+
+ViewFromSpline::ViewFromSpline()
+{
+    addControlPoint(vec3(-1.0, 1.0, -4.0));
+    addControlPoint(vec3(-1.0, -3.0, -4.0));
+    addControlPoint(vec3(-3.0, 1.0, -3.0));
+    addControlPoint(vec3(-1.8, 2.0, 5.4));
+    addControlPoint(vec3(-0.4, 2.0, 1.2));
+    addControlPoint(vec3(-0.2, 1.5, 0.6));
+    addControlPoint(vec3(-0.2, 1.2, 0.6));
+    addControlPoint(vec3(-0.8, 1.0, 2.4));
+    addControlPoint(vec3(-1.0, 2.0, 3.0));
+    addControlPoint(vec3(0.0, 4.0, 3.6));
+    addControlPoint(vec3(-0.8, 4.0, 1.2));
+    addControlPoint(vec3(-0.2, 3.0, 0.6));
+    addControlPoint(vec3(-0.1, 2.0, 0.3));
+    addControlPoint(vec3(-0.1, 2.0, 0.3));
+    addControlPoint(vec3(-0.1, 2.0, 0.3));
+    addControlPoint(vec3(-0.1, 2.0, 0.3));
+    calcParams();
+}
+
+ViewToSpline::ViewToSpline()
+{
+    addControlPoint(vec3(-1.0, 1.0, 0.0));
+    addControlPoint(vec3(-1.0, -3.0, 0.0));
+    addControlPoint(vec3(-1.0, 1.0, 0.0));
+    addControlPoint(vec3(0.1, 0.0, -0.3));
+    addControlPoint(vec3(0.1, 0.0, -0.3));
+    addControlPoint(vec3(0.1, 0.0, -0.3));
+    addControlPoint(vec3(0.0, 0.2, 0.0));
+    addControlPoint(vec3(0.0, 0.6, 0.0));
+    addControlPoint(vec3(0.0, 0.8, 0.0));
+    addControlPoint(vec3(0.0, 0.8, 0.0));
+    addControlPoint(vec3(0.0, 0.8, 0.0));
+    addControlPoint(vec3(0.0, 0.8, 0.0));
+    addControlPoint(vec3(0.0, 0.8, 0.0));
+    addControlPoint(vec3(0.0, 0.8, 0.0));
+    addControlPoint(vec3(0.0, 0.8, 0.0));
+    addControlPoint(vec3(0.0, 0.8, 0.0));
+    calcParams();
+}
+
+LightPositionSpline::LightPositionSpline()
+{
+    addControlPoint(vec3(0.0, 1.8, 0.0));
+    addControlPoint(vec3(0.0, 1.8, 0.0));
+    addControlPoint(vec3(0.0, 1.6, 0.0));
+    addControlPoint(vec3(0.0, 1.6, 0.0));
+    addControlPoint(vec3(0.0, 1.6, 0.0));
+    addControlPoint(vec3(0.0, 1.6, 0.0));
+    addControlPoint(vec3(0.0, 1.4, 0.0));
+    addControlPoint(vec3(0.0, 1.3, 0.0));
+    addControlPoint(vec3(-0.2, 1.5, 2.0));
+    addControlPoint(vec3(0.8, 1.5, -0.4));
+    addControlPoint(vec3(-0.8, 1.5, -0.4));
+    addControlPoint(vec3(0.8, 2.0, 1.0));
+    addControlPoint(vec3(1.8, 5.0, -1.8));
+    addControlPoint(vec3(8.0, 10.0, -4.0));
+    addControlPoint(vec3(8.0, 10.0, -4.0));
+    addControlPoint(vec3(8.0, 10.0, -4.0));
+    calcParams();
+}
+
+LogoPositionSpline::LogoPositionSpline()
+{
+    addControlPoint(vec3(0.0, -0.5, 0.0));
+    addControlPoint(vec3(0.0, -0.5, 0.0));
+    addControlPoint(vec3(0.0, -0.5, 0.0));
+    addControlPoint(vec3(0.0, -0.5, 0.0));
+    addControlPoint(vec3(0.0, -0.5, 0.0));
+    addControlPoint(vec3(0.0, -0.5, 0.0));
+    addControlPoint(vec3(0.0, 0.0, 0.0));
+    addControlPoint(vec3(0.0, 0.6, 0.0));
+    addControlPoint(vec3(0.0, 0.75, 0.0));
+    addControlPoint(vec3(0.0, 0.8, 0.0));
+    addControlPoint(vec3(0.0, 0.8, 0.0));
+    addControlPoint(vec3(0.0, 0.5, 0.0));
+    addControlPoint(vec3(0.0, 0.5, 0.0));
+    addControlPoint(vec3(0.0, 0.5, 0.0));
+    addControlPoint(vec3(0.0, 0.5, 0.0));
+    addControlPoint(vec3(0.0, 0.5, 0.0));
+    calcParams();
+}
+
+LogoRotationSpline::LogoRotationSpline()
+{
+    addControlPoint(vec3(0.0, 0.0, -18.4));
+    addControlPoint(vec3(0.0, 0.0, -18.4));
+    addControlPoint(vec3(0.0, 0.0, -18.4));
+    addControlPoint(vec3(0.0, 0.0, -18.4));
+    addControlPoint(vec3(0.0, 0.0, -18.4));
+    addControlPoint(vec3(0.0, 0.0, -18.4));
+    addControlPoint(vec3(0.0, 0.0, -18.4));
+    addControlPoint(vec3(0.0, 0.0, -18.4));
+    addControlPoint(vec3(240.0, 360.0, 180.0));
+    addControlPoint(vec3(90.0, 180.0, 90.0));
+    addControlPoint(vec3(11.9, 0.0, -18.4));
+    addControlPoint(vec3(11.9, 0.0, -18.4));
+    addControlPoint(vec3(11.9, 0.0, -18.4));
+    addControlPoint(vec3(11.9, 0.0, -18.4));
+    addControlPoint(vec3(11.9, 0.0, -18.4));
+    calcParams();
+}

=== added file 'src/scene-ideas/splines.h'
--- src/scene-ideas/splines.h	1970-01-01 00:00:00 +0000
+++ src/scene-ideas/splines.h	2012-04-30 18:37:22 +0000
@@ -0,0 +1,83 @@
+/*
+ * (c) Copyright 1993, Silicon Graphics, Inc.
+ * Copyright Š 2012 Linaro Limited
+ *
+ * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
+ *
+ * glmark2 is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * glmark2.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Jesse Barker
+ */
+#ifndef SPLINES_H_
+#define SPLINES_H_
+
+#include <vector>
+#include "vec.h"
+
+class Spline
+{
+public:
+    Spline() :
+        paramData_(0) {}
+    ~Spline()
+    {
+        delete [] paramData_;
+    }
+    void addControlPoint(const LibMatrix::vec3& point) { controlData_.push_back(point); }
+    void getCurrentVec(float currentTime, LibMatrix::vec3& v) const;
+    void calcParams();
+
+private:
+    std::vector<LibMatrix::vec3> controlData_;
+    typedef LibMatrix::vec3 param[4];
+    param* paramData_;
+};
+
+class ViewFromSpline : public Spline
+{
+public:
+    ViewFromSpline();
+    ~ViewFromSpline() {}
+};
+
+class ViewToSpline : public Spline
+{
+public:
+    ViewToSpline();
+    ~ViewToSpline() {}
+};
+
+class LightPositionSpline : public Spline
+{
+public:
+    LightPositionSpline();
+    ~LightPositionSpline() {}
+};
+
+class LogoPositionSpline : public Spline
+{
+public:
+    LogoPositionSpline();
+    ~LogoPositionSpline() {}
+};
+
+class LogoRotationSpline : public Spline
+{
+public:
+    LogoRotationSpline();
+    ~LogoRotationSpline() {}
+};
+
+#endif // SPLINES_H_

=== added file 'src/scene-ideas/t.cc'
--- src/scene-ideas/t.cc	1970-01-01 00:00:00 +0000
+++ src/scene-ideas/t.cc	2012-04-30 18:37:22 +0000
@@ -0,0 +1,147 @@
+/*
+ * Vertex position data describing the letter 't'
+ *
+ * (c) Copyright 1993, Silicon Graphics, Inc.
+ * Copyright Š 2012 Linaro Limited
+ *
+ * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
+ *
+ * glmark2 is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * glmark2.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Jesse Barker
+ */
+#include "characters.h"
+
+using LibMatrix::vec2;
+
+LetterT::LetterT()
+{
+    // Vertex data...
+    vertexData_.push_back(vec2(2.986667, 14.034801));
+    vertexData_.push_back(vec2(2.445128, 10.088024));
+    vertexData_.push_back(vec2(1.788718, 9.236438));
+    vertexData_.push_back(vec2(2.264615, 7.664279));
+    vertexData_.push_back(vec2(1.165128, 5.666326));
+    vertexData_.push_back(vec2(2.034872, 4.945752));
+    vertexData_.push_back(vec2(1.132308, 3.766633));
+    vertexData_.push_back(vec2(2.182564, 3.570113));
+    vertexData_.push_back(vec2(1.411282, 2.309109));
+    vertexData_.push_back(vec2(2.510769, 2.341863));
+    vertexData_.push_back(vec2(2.149744, 1.048106));
+    vertexData_.push_back(vec2(3.364103, 1.375640));
+    vertexData_.push_back(vec2(3.167180, 0.327533));
+    vertexData_.push_back(vec2(4.381538, 0.736950));
+    vertexData_.push_back(vec2(5.005128, 0.032753));
+    vertexData_.push_back(vec2(5.612308, 0.638690));
+    vertexData_.push_back(vec2(6.235898, 0.540430));
+    vertexData_.push_back(vec2(7.187692, 1.162743));
+    vertexData_.push_back(vec2(1.985641, 9.039918));
+    vertexData_.push_back(vec2(2.133333, 10.186285));
+    vertexData_.push_back(vec2(1.509744, 9.023541));
+    vertexData_.push_back(vec2(1.608205, 9.662231));
+    vertexData_.push_back(vec2(1.050256, 9.023541));
+    vertexData_.push_back(vec2(1.050256, 9.334698));
+    vertexData_.push_back(vec2(0.196923, 9.007165));
+    vertexData_.push_back(vec2(2.363077, 9.711361));
+    vertexData_.push_back(vec2(2.264615, 9.023541));
+    vertexData_.push_back(vec2(3.282051, 9.563972));
+    vertexData_.push_back(vec2(3.446154, 9.023541));
+    vertexData_.push_back(vec2(4.069744, 9.531218));
+    vertexData_.push_back(vec2(4.299487, 9.236438));
+    vertexData_.push_back(vec2(4.644103, 9.613101));
+    vertexData_.push_back(vec2(5.251282, 9.875128));
+
+    // Index data...
+    indexData_.push_back(0);
+    indexData_.push_back(1);
+    indexData_.push_back(2);
+    indexData_.push_back(3);
+    indexData_.push_back(4);
+    indexData_.push_back(5);
+    indexData_.push_back(6);
+    indexData_.push_back(7);
+    indexData_.push_back(8);
+    indexData_.push_back(9);
+    indexData_.push_back(10);
+    indexData_.push_back(11);
+    indexData_.push_back(12);
+    indexData_.push_back(13);
+    indexData_.push_back(14);
+    indexData_.push_back(15);
+    indexData_.push_back(16);
+    indexData_.push_back(17);
+    indexData_.push_back(18);
+    indexData_.push_back(19);
+    indexData_.push_back(20);
+    indexData_.push_back(21);
+    indexData_.push_back(22);
+    indexData_.push_back(23);
+    indexData_.push_back(24);
+    indexData_.push_back(25);
+    indexData_.push_back(26);
+    indexData_.push_back(27);
+    indexData_.push_back(28);
+    indexData_.push_back(29);
+    indexData_.push_back(30);
+    indexData_.push_back(31);
+    indexData_.push_back(32);
+    indexData_.push_back(0);
+    indexData_.push_back(2);
+    indexData_.push_back(4);
+    indexData_.push_back(6);
+    indexData_.push_back(8);
+    indexData_.push_back(10);
+    indexData_.push_back(12);
+    indexData_.push_back(14);
+    indexData_.push_back(16);
+    indexData_.push_back(17);
+    indexData_.push_back(15);
+    indexData_.push_back(13);
+    indexData_.push_back(11);
+    indexData_.push_back(9);
+    indexData_.push_back(7);
+    indexData_.push_back(5);
+    indexData_.push_back(3);
+    indexData_.push_back(1);
+    indexData_.push_back(18);
+    indexData_.push_back(20);
+    indexData_.push_back(22);
+    indexData_.push_back(24);
+    indexData_.push_back(23);
+    indexData_.push_back(21);
+    indexData_.push_back(19);
+    indexData_.push_back(26);
+    indexData_.push_back(28);
+    indexData_.push_back(30);
+    indexData_.push_back(32);
+    indexData_.push_back(31);
+    indexData_.push_back(29);
+    indexData_.push_back(27);
+    indexData_.push_back(25);
+
+    // Primitive state so that the draw call can issue the primitives we want.
+    unsigned int curOffset(0);
+    primVec_.push_back(PrimitiveState(GL_TRIANGLE_STRIP, 18, curOffset));
+    curOffset += (18 * sizeof(unsigned int));
+    primVec_.push_back(PrimitiveState(GL_TRIANGLE_STRIP, 7, curOffset));
+    curOffset += (7 * sizeof(unsigned int));
+    primVec_.push_back(PrimitiveState(GL_TRIANGLE_STRIP, 8, curOffset));
+    curOffset += (8 * sizeof(unsigned int));
+    primVec_.push_back(PrimitiveState(GL_LINE_STRIP, 18, curOffset));
+    curOffset += (18 * sizeof(unsigned int));
+    primVec_.push_back(PrimitiveState(GL_LINE_STRIP, 7, curOffset));
+    curOffset += (7 * sizeof(unsigned int));
+    primVec_.push_back(PrimitiveState(GL_LINE_STRIP, 8, curOffset));
+}

=== added file 'src/scene-ideas/table.cc'
--- src/scene-ideas/table.cc	1970-01-01 00:00:00 +0000
+++ src/scene-ideas/table.cc	2012-05-08 20:53:58 +0000
@@ -0,0 +1,353 @@
+/*
+ * (c) Copyright 1993, Silicon Graphics, Inc.
+ * Copyright Š 2012 Linaro Limited
+ *
+ * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
+ *
+ * glmark2 is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * glmark2.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Jesse Barker
+ */
+#include "table.h"
+#include "scene.h"
+#include "shader-source.h"
+#include "log.h"
+
+using std::string;
+using LibMatrix::vec3;
+using LibMatrix::Stack4;
+
+const string Table::modelviewName_("modelview");
+const string Table::projectionName_("projection");
+const string Table::lightPositionName_("lightPosition");
+const string Table::logoDirectionName_("logoDirection");
+const string Table::curTimeName_("currentTime");
+const string Table::vertexAttribName_("vertex");
+const unsigned int Table::TABLERES_(12);
+const vec3 Table::paperVertices_[4] = {
+    vec3(-0.8, 0.0, 0.4),
+    vec3(-0.2, 0.0, -1.4),
+    vec3(0.4, 0.0, 0.8),
+    vec3(1.0, 0.0, -1.0),
+};
+
+Table::Table() :
+    tableVertexIndex_(0),
+    paperVertexIndex_(0),
+    textVertexIndex_(0),
+    underVertexIndex_(0),
+    valid_(false)
+{
+    tableVertices_.reserve((TABLERES_ + 1) * (TABLERES_ + 1));
+    for (unsigned int i = 0; i <= TABLERES_; i++)
+    {
+        for (unsigned int j = 0; j <= TABLERES_; j++)
+        {
+            float x((static_cast<float>(i) - static_cast<float>(TABLERES_) * 1.0 / 2.0) / 2.0);
+            float z((static_cast<float>(j) - static_cast<float>(TABLERES_) * 1.0 / 2.0) / 2.0);
+            tableVertices_.push_back(vec3(x, 0.0, z));
+        }
+    }
+
+    // Now that we've setup the vertex data, we can setup the map of how
+    // that data will be laid out in the buffer object.
+    dataMap_.tvOffset = 0;
+    dataMap_.tvSize = tableVertices_.size() * sizeof(vec3);
+    dataMap_.totalSize = dataMap_.tvSize;
+    dataMap_.pvOffset = dataMap_.tvOffset + dataMap_.tvSize;
+    dataMap_.pvSize = 4 * sizeof(vec3);
+    dataMap_.totalSize += dataMap_.pvSize;
+
+    for (unsigned int i = 0; i < TABLERES_; i++)
+    {
+        for (unsigned int j = 0; j <= TABLERES_; j++)
+        {
+            unsigned int curIndex1(i * (TABLERES_ + 1) + j);
+            unsigned int curIndex2((i + 1) * (TABLERES_ + 1) + j);
+            indexData_.push_back(curIndex1);
+            indexData_.push_back(curIndex2);
+        }
+    }
+}
+
+Table::~Table(void)
+{
+    if (valid_)
+    {
+        glDeleteBuffers(2, &bufferObjects_[0]);
+    }
+}
+
+void
+Table::init(void)
+{
+    // Make sure we don't re-initialize...
+    if (valid_)
+    {
+        return;
+    }
+
+    // Initialize shader sources from input files and create programs from them
+    // Program to render the table with lighting and a time-based fade...
+    string table_vtx_filename(GLMARK_DATA_PATH"/shaders/ideas-table.vert");
+    string table_frg_filename(GLMARK_DATA_PATH"/shaders/ideas-table.frag");
+    ShaderSource table_vtx_source(table_vtx_filename);
+    ShaderSource table_frg_source(table_frg_filename);
+    if (!Scene::load_shaders_from_strings(tableProgram_, table_vtx_source.str(),
+                                          table_frg_source.str()))
+    {
+        Log::error("No valid program for table rendering.\n");
+        return;
+    }
+    textVertexIndex_ = tableProgram_[vertexAttribName_].location();
+
+    // Program to render the paper with lighting and a time-based fade...
+    string paper_vtx_filename(GLMARK_DATA_PATH"/shaders/ideas-paper.vert");
+    string paper_frg_filename(GLMARK_DATA_PATH"/shaders/ideas-paper.frag");
+    ShaderSource paper_vtx_source(paper_vtx_filename);
+    ShaderSource paper_frg_source(paper_frg_filename);
+    if (!Scene::load_shaders_from_strings(paperProgram_, paper_vtx_source.str(),
+                                          paper_frg_source.str()))
+    {
+        Log::error("No valid program for paper rendering.\n");
+        return;
+    }
+    paperVertexIndex_ = paperProgram_[vertexAttribName_].location();
+
+    // Program to handle the text (time-based color fade)...
+    string text_vtx_filename(GLMARK_DATA_PATH"/shaders/ideas-text.vert");
+    string text_frg_filename(GLMARK_DATA_PATH"/shaders/ideas-text.frag");
+    ShaderSource text_vtx_source(text_vtx_filename);
+    ShaderSource text_frg_source(text_frg_filename);
+    if (!Scene::load_shaders_from_strings(textProgram_, text_vtx_source.str(),
+                                          text_frg_source.str()))
+    {
+        Log::error("No valid program for text rendering.\n");
+        return;
+    }
+    textVertexIndex_ = textProgram_[vertexAttribName_].location();
+
+    // Program for the drawUnder functionality (just paint it black)...
+    string under_table_vtx_filename(GLMARK_DATA_PATH"/shaders/ideas-under-table.vert");
+    string under_table_frg_filename(GLMARK_DATA_PATH"/shaders/ideas-under-table.frag");
+    ShaderSource under_table_vtx_source(under_table_vtx_filename);
+    ShaderSource under_table_frg_source(under_table_frg_filename);
+    if (!Scene::load_shaders_from_strings(underProgram_, under_table_vtx_source.str(),
+                                          under_table_frg_source.str()))
+    {
+        Log::error("No valid program for under table rendering.\n");
+        return;
+    }
+    underVertexIndex_ = underProgram_[vertexAttribName_].location();
+
+    // Tell all of the characters to initialize themselves...
+    i_.init(textVertexIndex_);
+    d_.init(textVertexIndex_);
+    e_.init(textVertexIndex_);
+    a_.init(textVertexIndex_);
+    s_.init(textVertexIndex_);
+    n_.init(textVertexIndex_);
+    m_.init(textVertexIndex_);
+    o_.init(textVertexIndex_);
+    t_.init(textVertexIndex_);
+
+    // We need 2 buffers for our work here.  One for the vertex data.
+    // and one for the index data.
+    glGenBuffers(2, &bufferObjects_[0]);
+
+    // First, setup the vertex data by binding the first buffer object, 
+    // allocating its data store, and filling it in with our vertex data.
+    glBindBuffer(GL_ARRAY_BUFFER, bufferObjects_[0]);
+    glBufferData(GL_ARRAY_BUFFER, dataMap_.totalSize, 0, GL_STATIC_DRAW);
+    glBufferSubData(GL_ARRAY_BUFFER, dataMap_.tvOffset, dataMap_.tvSize, 
+                    &tableVertices_.front());
+    glBufferSubData(GL_ARRAY_BUFFER, dataMap_.pvOffset, dataMap_.pvSize,
+                    &paperVertices_[0]);
+
+    // Now repeat for our index data.
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferObjects_[1]);
+    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexData_.size() * sizeof(unsigned int),
+                 &indexData_.front(), GL_STATIC_DRAW);
+
+    // We're ready to go.
+    valid_ = true;
+}
+
+void
+Table::draw(Stack4& modelview,
+    Stack4& projection,
+    const vec3& lightPos,
+    const vec3& logoPos,
+    const float& currentTime,
+    float& paperAlpha_out)
+{
+    glDisable(GL_DEPTH_TEST);
+
+    // Compute the light direction with respect to the logo...
+    vec3 logoDirection(lightPos.x() - logoPos.x(), lightPos.y() - logoPos.y(), 
+        lightPos.z() - logoPos.z());
+    logoDirection.normalize();
+
+    // Compute the alpha component based upon drawing the paper (all of this will
+    // be done in the shader, but we need to pass this back so that the logo's
+    // shadow will look right).
+    for (unsigned int i = 0; i < 4; i++)
+    {
+        vec3 lightDirection(lightPos.x() - paperVertices_[i].x(),
+                            lightPos.y() - paperVertices_[i].y(),
+                            lightPos.z() - paperVertices_[i].z());
+        lightDirection.normalize();
+        float c = vec3::dot(lightDirection, logoDirection);
+        if (c < 0.0)
+        {
+            c = 0.0;
+        }
+        c = c * c * c * lightDirection.y();
+        if ((currentTime > 10.0) && (currentTime < 12.0))
+        {
+            c *= 1.0 - (currentTime - 10.0) * 0.5;
+        }
+        paperAlpha_out += c;
+    }
+
+    glBindBuffer(GL_ARRAY_BUFFER, bufferObjects_[0]);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferObjects_[1]);
+
+    // Draw the table top
+    tableProgram_.start();
+    tableProgram_[projectionName_] = projection.getCurrent();
+    tableProgram_[modelviewName_] = modelview.getCurrent();
+    tableProgram_[lightPositionName_] = lightPos;
+    tableProgram_[logoDirectionName_] = logoDirection;
+    tableProgram_[curTimeName_] = currentTime;
+    glVertexAttribPointer(tableVertexIndex_, 3, GL_FLOAT, GL_FALSE, 0,
+        reinterpret_cast<const GLvoid*>(dataMap_.tvOffset));
+    glEnableVertexAttribArray(tableVertexIndex_);
+    static const unsigned int twiceRes(2 * (TABLERES_ + 1));
+    for (unsigned int i = 0; i < TABLERES_; i++)
+    {
+        glDrawElements(GL_TRIANGLE_STRIP, twiceRes, GL_UNSIGNED_INT,
+            reinterpret_cast<const GLvoid*>(i * twiceRes * sizeof(unsigned int)));
+    }
+    glDisableVertexAttribArray(tableVertexIndex_);
+    tableProgram_.stop();
+
+    if (logoPos.y() > -0.33 && logoPos.y() < 0.33)
+    {
+        glEnable(GL_DEPTH_TEST);
+    }
+
+    // Draw the paper lying on the table top
+    paperProgram_.start();
+    paperProgram_[projectionName_] = projection.getCurrent();
+    paperProgram_[modelviewName_] = modelview.getCurrent();
+    paperProgram_[lightPositionName_] = lightPos;
+    paperProgram_[logoDirectionName_] = logoDirection;
+    paperProgram_[curTimeName_] = currentTime;
+    glVertexAttribPointer(paperVertexIndex_, 3, GL_FLOAT, GL_FALSE, 0,
+        reinterpret_cast<const GLvoid*>(dataMap_.pvOffset));
+    glEnableVertexAttribArray(paperVertexIndex_);
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+    glDisableVertexAttribArray(paperVertexIndex_);
+    paperProgram_.stop();
+
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+    glDisable(GL_DEPTH_TEST);
+
+    modelview.push();
+    modelview.rotate(-18.4, 0.0, 1.0, 0.0);
+    modelview.translate(-0.3, 0.0, -0.8);
+    modelview.rotate(-90.0, 1.0, 0.0, 0.0);
+    modelview.scale(0.015, 0.015, 0.015);
+
+    // Draw the text on the paper lying on the table top.
+    // Each character has its own array and element buffers, and they have
+    // been initialized with the vertex attrib location for this program.
+    textProgram_.start();
+    textProgram_[projectionName_] = projection.getCurrent();
+    textProgram_[modelviewName_] = modelview.getCurrent();
+    textProgram_[curTimeName_] = currentTime;
+    i_.draw();
+    modelview.translate(3.0, 0.0, 0.0);
+    textProgram_[modelviewName_] = modelview.getCurrent();
+    d_.draw();
+    modelview.translate(6.0, 0.0, 0.0);
+    textProgram_[modelviewName_] = modelview.getCurrent();
+    e_.draw();
+    modelview.translate(5.0, 0.0, 0.0);
+    textProgram_[modelviewName_] = modelview.getCurrent();
+    a_.draw();
+    modelview.translate(6.0, 0.0, 0.0);
+    textProgram_[modelviewName_] = modelview.getCurrent();
+    s_.draw();
+    modelview.translate(10.0, 0.0, 0.0);
+    textProgram_[modelviewName_] = modelview.getCurrent();
+    i_.draw();
+    modelview.translate(3.0, 0.0, 0.0);
+    textProgram_[modelviewName_] = modelview.getCurrent();
+    n_.draw();
+    modelview.translate(-31.0, -13.0, 0.0);
+    textProgram_[modelviewName_] = modelview.getCurrent();
+    m_.draw();
+    modelview.translate(10.0, 0.0, 0.0);
+    textProgram_[modelviewName_] = modelview.getCurrent();
+    o_.draw();
+    modelview.translate(5.0, 0.0, 0.0);
+    textProgram_[modelviewName_] = modelview.getCurrent();
+    t_.draw();
+    modelview.translate(4.0, 0.0, 0.0);
+    textProgram_[modelviewName_] = modelview.getCurrent();
+    i_.draw();
+    modelview.translate(3.5, 0.0, 0.0);
+    textProgram_[modelviewName_] = modelview.getCurrent();
+    o_.draw();
+    modelview.translate(5.0, 0.0, 0.0);
+    textProgram_[modelviewName_] = modelview.getCurrent();
+    n_.draw();
+    textProgram_.stop();
+
+    modelview.pop();
+}
+
+void
+Table::drawUnder(Stack4& modelview, Stack4& projection)
+{
+    glDisable(GL_DEPTH_TEST);
+
+    glBindBuffer(GL_ARRAY_BUFFER, bufferObjects_[0]);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferObjects_[1]);
+
+    underProgram_.start();  
+    underProgram_[modelviewName_] = modelview.getCurrent();
+    underProgram_[projectionName_] = projection.getCurrent();
+    glVertexAttribPointer(underVertexIndex_, 3, GL_FLOAT, GL_FALSE, 0,
+        reinterpret_cast<const GLvoid*>(dataMap_.tvOffset));
+    glEnableVertexAttribArray(underVertexIndex_);
+    static const unsigned int twiceRes(2 * (TABLERES_ + 1));
+    for (unsigned int i = 0; i < TABLERES_; i++)
+    {
+        glDrawElements(GL_TRIANGLE_STRIP, twiceRes, GL_UNSIGNED_INT,
+            reinterpret_cast<const GLvoid*>(i * twiceRes * sizeof(unsigned int)));
+    }
+    glDisableVertexAttribArray(underVertexIndex_);
+    underProgram_.stop();
+
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+    glEnable(GL_DEPTH_TEST); 
+}

=== added file 'src/scene-ideas/table.h'
--- src/scene-ideas/table.h	1970-01-01 00:00:00 +0000
+++ src/scene-ideas/table.h	2012-04-30 18:37:22 +0000
@@ -0,0 +1,94 @@
+/*
+ * (c) Copyright 1993, Silicon Graphics, Inc.
+ * Copyright Š 2012 Linaro Limited
+ *
+ * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
+ *
+ * glmark2 is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * glmark2.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Jesse Barker
+ */
+#ifndef TABLE_H_
+#define TABLE_H_
+
+#include <string>
+#include <vector>
+#include "characters.h"
+#include "program.h"
+#include "stack.h"
+
+class Table
+{
+public:
+    Table();
+    ~Table();
+
+    void init();
+    bool valid() const { return valid_; }
+    void draw(LibMatrix::Stack4& modelview, LibMatrix::Stack4& projection, 
+              const LibMatrix::vec3& lightPosition, const LibMatrix::vec3& logoPosition,
+              const float& currentTime, float& paperAlpha_out);
+    void drawUnder(LibMatrix::Stack4& modelview, LibMatrix::Stack4& projection);
+
+private:
+    // Text
+    LetterI i_;
+    LetterD d_;
+    LetterE e_;
+    LetterA a_;
+    LetterS s_;
+    LetterN n_;
+    LetterM m_;
+    LetterO o_;
+    LetterT t_;
+    Program tableProgram_;
+    Program paperProgram_;
+    Program textProgram_;
+    Program underProgram_;
+    std::string tableVertexShader_;
+    std::string tableFragmentShader_;
+    std::string paperVertexShader_;
+    std::string paperFragmentShader_;
+    std::string textVertexShader_;
+    std::string textFragmentShader_;
+    std::string underVertexShader_;
+    std::string underFragmentShader_;
+    static const std::string modelviewName_;
+    static const std::string projectionName_;
+    static const std::string lightPositionName_;
+    static const std::string logoDirectionName_;
+    static const std::string curTimeName_;
+    static const std::string vertexAttribName_;
+    static const unsigned int TABLERES_;
+    std::vector<LibMatrix::vec3> tableVertices_;
+    static const LibMatrix::vec3 paperVertices_[4];
+    struct VertexDataMap
+    {
+        unsigned int tvOffset;
+        unsigned int tvSize;
+        unsigned int pvOffset;
+        unsigned int pvSize;
+        unsigned int totalSize;
+    } dataMap_;
+    unsigned int bufferObjects_[2];
+    std::vector<unsigned int> indexData_;
+    int tableVertexIndex_;
+    int paperVertexIndex_;
+    int textVertexIndex_;
+    int underVertexIndex_;
+    bool valid_;
+};
+
+#endif // TABLE_H_

=== modified file 'src/scene.h'
--- src/scene.h	2012-02-09 01:28:16 +0000
+++ src/scene.h	2012-04-27 18:00:45 +0000
@@ -477,4 +477,25 @@
 private:
     SceneBufferPrivate *priv_;
 };
+
+class SceneIdeasPrivate;
+
+class SceneIdeas : public Scene
+{
+public:
+    SceneIdeas(Canvas &pCanvas);
+    bool load();
+    void unload();
+    void setup();
+    void teardown();
+    void update();
+    void draw();
+    ValidationResult validate();
+
+    ~SceneIdeas();
+
+private:
+    SceneIdeasPrivate* priv_;
+};
+
 #endif

=== modified file 'src/wscript_build'
--- src/wscript_build	2012-01-26 17:00:43 +0000
+++ src/wscript_build	2012-04-27 18:00:45 +0000
@@ -5,6 +5,7 @@
 glesv2_sources = ['canvas-x11.cpp', 'canvas-x11-egl.cpp']
 libmatrix_sources = [f for f in bld.path.ant_glob('libmatrix/*.cc')
                      if not f.name.endswith('test.cc')]
+ideas_sources = bld.path.ant_glob('scene-ideas/*.cc')
 
 if bld.env.USE_GL:
     bld(
@@ -18,10 +19,11 @@
         )
     bld(
         features     = ['cxx', 'cprogram'],
-        source       = common_sources + gl_sources,
+        source       = ideas_sources + common_sources + gl_sources,
         target       = 'glmark2',
         use          = ['x11', 'gl', 'matrix', 'libpng12'],
         lib          = ['m'],
+        includes     = ['.', 'scene-ideas'],
         defines      = ['USE_GL', 'USE_EXCEPTIONS']
         )
 
@@ -37,9 +39,10 @@
         )
     bld(
         features     = ['cxx', 'cprogram'],
-        source       = common_sources + glesv2_sources,
+        source       = ideas_sources + common_sources + glesv2_sources,
         target       = 'glmark2-es2',
         use          = ['x11', 'egl', 'glesv2', 'matrix-es2', 'libpng12'],
         lib          = ['m', 'dl'],
+        includes     = ['.', 'scene-ideas'],
         defines      = ['USE_GLESv2', 'USE_EXCEPTIONS']
         )

