=== modified file 'android/jni/Android.mk'
--- android/jni/Android.mk	2012-06-27 08:13:50 +0000
+++ android/jni/Android.mk	2012-06-29 12:27:16 +0000
@@ -61,11 +61,13 @@
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/src \
                     $(LOCAL_PATH)/src/libmatrix \
                     $(LOCAL_PATH)/src/scene-ideas \
+                    $(LOCAL_PATH)/src/scene-terrain \
                     $(LOCAL_PATH)/src/libjpeg-turbo \
                     $(LOCAL_PATH)/src/libpng \
                     external/zlib
 LOCAL_SRC_FILES := $(filter-out src/canvas% src/main.cpp, \
                      $(subst $(LOCAL_PATH)/,,$(wildcard $(LOCAL_PATH)/src/*.cpp))) \
+                   $(subst $(LOCAL_PATH)/,,$(wildcard $(LOCAL_PATH)/src/scene-terrain/*.cpp)) \
                    src/canvas-android.cpp
 LOCAL_PRELINK_MODULE := false
 

=== modified file 'android/jni/Android.ndk.mk'
--- android/jni/Android.ndk.mk	2012-06-27 08:13:50 +0000
+++ android/jni/Android.ndk.mk	2012-06-29 12:27:16 +0000
@@ -54,10 +54,12 @@
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/src \
                     $(LOCAL_PATH)/src/libmatrix \
                     $(LOCAL_PATH)/src/scene-ideas \
+                    $(LOCAL_PATH)/src/scene-terrain \
                     $(LOCAL_PATH)/src/libjpeg-turbo \
                     $(LOCAL_PATH)/src/libpng
 LOCAL_SRC_FILES := $(filter-out src/canvas% src/main.cpp, \
                      $(subst $(LOCAL_PATH)/,,$(wildcard $(LOCAL_PATH)/src/*.cpp))) \
+                   $(subst $(LOCAL_PATH)/,,$(wildcard $(LOCAL_PATH)/src/scene-terrain/*.cpp)) \
                    src/canvas-android.cpp
 
 include $(BUILD_SHARED_LIBRARY)

=== added file 'data/shaders/terrain-blur.frag'
--- data/shaders/terrain-blur.frag	1970-01-01 00:00:00 +0000
+++ data/shaders/terrain-blur.frag	2012-06-29 12:27:16 +0000
@@ -0,0 +1,14 @@
+uniform sampler2D Texture0;
+
+varying vec2 vUv;
+
+void main(void)
+{
+    vec4 result;
+    vec2 TextureCoord = vUv;
+
+    $CONVOLUTION$
+
+    gl_FragColor = vec4(result.xyz, 1.0);
+}
+

=== added file 'data/shaders/terrain-luminance.frag'
--- data/shaders/terrain-luminance.frag	1970-01-01 00:00:00 +0000
+++ data/shaders/terrain-luminance.frag	2012-06-29 12:27:16 +0000
@@ -0,0 +1,15 @@
+uniform sampler2D tDiffuse;
+
+varying vec2 vUv;
+
+void main()
+{
+    vec4 texel = texture2D( tDiffuse, vUv );
+
+    vec3 luma = vec3( 0.299, 0.587, 0.114 );
+
+    float v = dot( texel.xyz, luma );
+
+    gl_FragColor = vec4( v, v, v, texel.w );
+}
+

=== added file 'data/shaders/terrain-noise.frag'
--- data/shaders/terrain-noise.frag	1970-01-01 00:00:00 +0000
+++ data/shaders/terrain-noise.frag	2012-06-29 12:27:16 +0000
@@ -0,0 +1,124 @@
+//
+// Description : Array and textureless GLSL 3D simplex noise function.
+//      Author : Ian McEwan, Ashima Arts.
+//  Maintainer : ijm
+//     Lastmod : 20110409 (stegu)
+//     License : Copyright (C) 2011 Ashima Arts. All rights reserved.
+//               Distributed under the MIT License. See LICENSE file.
+//
+
+#ifdef GL_ES
+#define MEDIUMP mediump
+#else
+#define MEDIUMP
+#endif
+
+uniform float time;
+uniform MEDIUMP vec2 uvScale;
+varying vec2 vUv;
+
+vec4 permute(vec4 x)
+{
+    return mod(((x * 34.0) + 1.0) * x, 289.0);
+}
+
+vec4 taylorInvSqrt(vec4 r)
+{
+    return 1.79284291400159 - 0.85373472095314 * r;
+}
+
+float snoise(vec3 v)
+{
+    const vec2 C = vec2(1.0 / 6.0, 1.0 / 3.0);
+    const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
+
+    // First corner
+    vec3 i  = floor(v + dot(v, C.yyy));
+    vec3 x0 = v - i + dot(i, C.xxx);
+
+    // Other corners
+    vec3 g = step(x0.yzx, x0.xyz);
+    vec3 l = 1.0 - g;
+    vec3 i1 = min(g.xyz, l.zxy);
+    vec3 i2 = max(g.xyz, l.zxy);
+
+    vec3 x1 = x0 - i1 + 1.0 * C.xxx;
+    vec3 x2 = x0 - i2 + 2.0 * C.xxx;
+    vec3 x3 = x0 - 1. + 3.0 * C.xxx;
+
+    // Permutations
+    i = mod(i, 289.0);
+    vec4 p = permute(permute(permute(
+                    i.z + vec4(0.0, i1.z, i2.z, 1.0))
+                + i.y + vec4(0.0, i1.y, i2.y, 1.0))
+            + i.x + vec4(0.0, i1.x, i2.x, 1.0));
+
+    // Gradients
+    // (N*N points uniformly over a square, mapped onto an octahedron.)
+
+    float n_ = 1.0 / 7.0; // N=7
+
+    vec3 ns = n_ * D.wyz - D.xzx;
+
+    vec4 j = p - 49.0 * floor(p * ns.z *ns.z);  //  mod(p,N*N)
+
+    vec4 x_ = floor(j * ns.z);
+    vec4 y_ = floor(j - 7.0 * x_);    // mod(j,N)
+
+    vec4 x = x_ *ns.x + ns.yyyy;
+    vec4 y = y_ *ns.x + ns.yyyy;
+    vec4 h = 1.0 - abs(x) - abs(y);
+
+    vec4 b0 = vec4(x.xy, y.xy);
+    vec4 b1 = vec4(x.zw, y.zw);
+
+
+    vec4 s0 = floor(b0) * 2.0 + 1.0;
+    vec4 s1 = floor(b1) * 2.0 + 1.0;
+    vec4 sh = -step(h, vec4(0.0));
+
+    vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy;
+    vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww;
+
+    vec3 p0 = vec3(a0.xy, h.x);
+    vec3 p1 = vec3(a0.zw, h.y);
+    vec3 p2 = vec3(a1.xy, h.z);
+    vec3 p3 = vec3(a1.zw, h.w);
+
+    // Normalise gradients
+
+    vec4 norm = taylorInvSqrt(vec4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3)));
+    p0 *= norm.x;
+    p1 *= norm.y;
+    p2 *= norm.z;
+    p3 *= norm.w;
+
+    // Mix final noise value
+
+    vec4 m = max(0.6 - vec4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0);
+    m = m * m;
+    return 42.0 * dot(m*m, vec4(dot(p0, x0), dot(p1, x1),
+                dot(p2, x2), dot(p3, x3)));
+}
+
+float surface3(vec3 coord)
+{
+    float n = 0.0;
+
+    n += 1.0 * abs(snoise(coord));
+    n += 0.5 * abs(snoise(coord * 2.0));
+    n += 0.25 * abs(snoise(coord * 4.0));
+    n += 0.125 * abs(snoise(coord * 8.0));
+
+    return n;
+}
+
+void main(void)
+{
+    vec3 coord = vec3(vUv.x, uvScale.y - vUv.y, -time);
+    float n = surface3(coord);
+
+    gl_FragColor = vec4(vec3(n, n, n), 1.0);
+}
+
+

=== added file 'data/shaders/terrain-normalmap.frag'
--- data/shaders/terrain-normalmap.frag	1970-01-01 00:00:00 +0000
+++ data/shaders/terrain-normalmap.frag	2012-06-29 12:27:16 +0000
@@ -0,0 +1,15 @@
+uniform float height;
+uniform vec2 resolution;
+uniform sampler2D heightMap;
+
+varying vec2 vUv;
+
+void main()
+{
+    float val = texture2D( heightMap, vUv ).x;
+
+    float valU = texture2D( heightMap, vUv + vec2( 1.0 / resolution.x, 0.0 ) ).x;
+    float valV = texture2D( heightMap, vUv + vec2( 0.0, 1.0 / resolution.y ) ).x;
+
+    gl_FragColor = vec4( ( 0.5 * normalize( vec3( val - valU, val - valV, height  ) ) + 0.5 ), 1.0 );
+}

=== added file 'data/shaders/terrain-overlay.frag'
--- data/shaders/terrain-overlay.frag	1970-01-01 00:00:00 +0000
+++ data/shaders/terrain-overlay.frag	2012-06-29 12:27:16 +0000
@@ -0,0 +1,10 @@
+uniform float opacity;
+uniform sampler2D tDiffuse;
+
+varying vec2 vUv;
+
+void main()
+{
+    vec4 texel = texture2D(tDiffuse, vUv);
+    gl_FragColor = opacity * texel;
+}

=== added file 'data/shaders/terrain-texture.vert'
--- data/shaders/terrain-texture.vert	1970-01-01 00:00:00 +0000
+++ data/shaders/terrain-texture.vert	2012-06-29 12:27:16 +0000
@@ -0,0 +1,18 @@
+#ifdef GL_ES
+#define MEDIUMP mediump
+#else
+#define MEDIUMP
+#endif
+
+uniform vec2 uvOffset;
+uniform MEDIUMP vec2 uvScale;
+
+attribute vec3 position;
+
+varying vec2 vUv;
+
+void main()
+{
+    vUv = uvScale * (position.xy * 0.5 + 0.5) + uvOffset;
+    gl_Position = vec4( position, 1.0);
+}

=== added file 'data/shaders/terrain.frag'
--- data/shaders/terrain.frag	1970-01-01 00:00:00 +0000
+++ data/shaders/terrain.frag	2012-06-29 12:27:16 +0000
@@ -0,0 +1,112 @@
+uniform vec3 uAmbientColor;
+uniform vec3 uDiffuseColor;
+uniform vec3 uSpecularColor;
+uniform float uShininess;
+uniform float uOpacity;
+uniform bool enableDiffuse1;
+uniform bool enableDiffuse2;
+uniform bool enableSpecular;
+uniform sampler2D tDiffuse1;
+uniform sampler2D tDiffuse2;
+uniform sampler2D tDetail;
+uniform sampler2D tNormal;
+uniform sampler2D tSpecular;
+uniform sampler2D tDisplacement;
+uniform float uNormalScale;
+uniform vec2 uRepeatOverlay;
+uniform vec2 uOffset;
+varying vec3 vTangent;
+varying vec3 vBinormal;
+varying vec3 vNormal;
+varying vec2 vUv;
+uniform vec3 ambientLightColor;
+uniform mat4 viewMatrix;
+
+#define MAX_POINT_LIGHTS 1
+//#define USE_FOG
+//#define FOG_EXP2
+
+#if MAX_POINT_LIGHTS > 0
+uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];
+uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];
+uniform float pointLightDistance[ MAX_POINT_LIGHTS ];
+#endif
+
+varying vec3 vViewPosition;
+
+// CHUNK: fog_pars_fragment
+#ifdef USE_FOG
+uniform vec3 fogColor;
+#ifdef FOG_EXP2
+uniform float fogDensity;
+#else
+uniform float fogNear;
+uniform float fogFar;
+#endif
+#endif
+
+void main() {
+    gl_FragColor = vec4( vec3( 1.0 ), uOpacity );
+    vec3 specularTex = vec3( 1.0 );
+    vec2 uvOverlay = uRepeatOverlay * vUv + uOffset;
+    vec3 normalTex = texture2D( tDetail, uvOverlay ).xyz * 2.0 - 1.0;
+    normalTex.xy *= uNormalScale;
+    normalTex = normalize( normalTex );
+
+    vec4 colDiffuse1 = texture2D( tDiffuse1, uvOverlay );
+    vec4 colDiffuse2 = texture2D( tDiffuse2, uvOverlay );
+    gl_FragColor = gl_FragColor * mix ( colDiffuse1, colDiffuse2, 1.0 - texture2D( tDisplacement, vUv) );
+
+    specularTex = texture2D( tSpecular, uvOverlay ).xyz;
+
+    mat3 tbn= mat3( vTangent, vBinormal, vNormal );
+    vec3 finalNormal = tbn * normalTex;
+    vec3 normal = normalize( finalNormal );
+    vec3 viewPosition = normalize( vViewPosition );
+
+    // point lights
+#if MAX_POINT_LIGHTS > 0
+    vec3 pointDiffuse = vec3( 0.0 );
+    vec3 pointSpecular = vec3( 0.0 );
+    for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {
+        vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );
+        vec3 lVector = lPosition.xyz + vViewPosition.xyz;
+        float lDistance = 1.0;
+        if ( pointLightDistance[ i ] > 0.0 )
+            lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );
+        lVector = normalize( lVector );
+        vec3 pointHalfVector = normalize( lVector + viewPosition );
+        float pointDistance = lDistance;
+        float pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );
+        float pointDiffuseWeight = max( dot( normal, lVector ), 0.0 );
+        float pointSpecularWeight = specularTex.r * pow( pointDotNormalHalf, uShininess );
+        pointDiffuse += pointDistance * pointLightColor[ i ] * uDiffuseColor * pointDiffuseWeight;
+        pointSpecular += pointDistance * pointLightColor[ i ] * uSpecularColor * pointSpecularWeight * pointDiffuseWeight;
+    }
+#endif
+
+    // all lights contribution summation
+    vec3 totalDiffuse = vec3( 0.0 );
+    vec3 totalSpecular = vec3( 0.0 );
+
+
+#if MAX_POINT_LIGHTS > 0
+    totalDiffuse += pointDiffuse;
+    totalSpecular += pointSpecular;
+#endif
+
+    gl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * uAmbientColor + totalSpecular );
+
+    //CHUNK: fog_fragment
+#ifdef USE_FOG
+    float depth = gl_FragCoord.z / gl_FragCoord.w;
+#ifdef FOG_EXP2
+    const float LOG2 = 1.442695;
+    float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );
+    fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );
+#else
+    float fogFactor = smoothstep( fogNear, fogFar, depth );
+#endif
+    gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );
+#endif
+}

=== added file 'data/shaders/terrain.vert'
--- data/shaders/terrain.vert	1970-01-01 00:00:00 +0000
+++ data/shaders/terrain.vert	2012-06-29 12:27:16 +0000
@@ -0,0 +1,48 @@
+// Transformation matrices
+uniform mat4 modelViewMatrix;
+uniform mat4 normalMatrix;
+uniform mat4 projectionMatrix;
+uniform mat4 viewMatrix;
+
+// Vertex attributes
+attribute vec3 position;
+attribute vec3 normal;
+attribute vec3 tangent;
+attribute vec2 uv;
+
+// Uniforms
+uniform sampler2D tNormal;
+uniform sampler2D tDisplacement;
+uniform float uDisplacementScale;
+uniform float uDisplacementBias;
+
+// Varyings
+varying vec3 vTangent;
+varying vec3 vBinormal;
+varying vec3 vNormal;
+varying vec2 vUv;
+varying vec3 vViewPosition;
+
+void main()
+{
+    vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
+    vViewPosition = -mvPosition.xyz;
+    vNormal = normalize(vec3(normalMatrix * vec4(normal, 1.0)));
+
+    // tangent and binormal vectors
+    vTangent = normalize(vec3(normalMatrix * vec4(tangent, 1.0)));
+    vBinormal = cross( vNormal, vTangent );
+    vBinormal = normalize( vBinormal );
+
+    // texture coordinates
+    vUv = uv;
+
+    // displacement mapping
+    vec3 dv = texture2D( tDisplacement, uv ).xyz;
+    float df = uDisplacementScale * dv.x + uDisplacementBias;
+    vec4 displacedPosition = vec4(vNormal.xyz * df, 0.0 ) + mvPosition;
+    gl_Position = projectionMatrix * displacedPosition;
+
+    vec3 normalTex = texture2D(tNormal, uv).xyz * 2.0 - 1.0;
+    vNormal = vec3(normalMatrix * vec4(normalTex, 1.0));
+}

=== added file 'data/textures/terrain-backgrounddetailed6.jpg'
Binary files data/textures/terrain-backgrounddetailed6.jpg	1970-01-01 00:00:00 +0000 and data/textures/terrain-backgrounddetailed6.jpg	2012-06-29 12:27:16 +0000 differ
=== added file 'data/textures/terrain-grasslight-512-nm.jpg'
Binary files data/textures/terrain-grasslight-512-nm.jpg	1970-01-01 00:00:00 +0000 and data/textures/terrain-grasslight-512-nm.jpg	2012-06-29 12:27:16 +0000 differ
=== added file 'data/textures/terrain-grasslight-512.jpg'
Binary files data/textures/terrain-grasslight-512.jpg	1970-01-01 00:00:00 +0000 and data/textures/terrain-grasslight-512.jpg	2012-06-29 12:27:16 +0000 differ
=== modified file 'src/android.cpp'
--- src/android.cpp	2012-06-15 13:24:32 +0000
+++ src/android.cpp	2012-06-29 12:27:16 +0000
@@ -222,6 +222,7 @@
     Benchmark::register_scene(*new SceneDesktop(*g_canvas));
     Benchmark::register_scene(*new SceneBuffer(*g_canvas));
     Benchmark::register_scene(*new SceneIdeas(*g_canvas));
+    Benchmark::register_scene(*new SceneTerrain(*g_canvas));
 
     g_benchmark_collection = new BenchmarkCollection();
     g_benchmark_collection->populate_from_options();

=== modified file 'src/main.cpp'
--- src/main.cpp	2012-05-15 08:40:46 +0000
+++ src/main.cpp	2012-06-29 12:27:16 +0000
@@ -61,6 +61,7 @@
     scenes.push_back(new SceneDesktop(canvas));
     scenes.push_back(new SceneBuffer(canvas));
     scenes.push_back(new SceneIdeas(canvas));
+    scenes.push_back(new SceneTerrain(canvas));
 
     for (vector<Scene*>::const_iterator iter = scenes.begin();
          iter != scenes.end();

=== added directory 'src/scene-terrain'
=== added file 'src/scene-terrain.cpp'
--- src/scene-terrain.cpp	1970-01-01 00:00:00 +0000
+++ src/scene-terrain.cpp	2012-07-03 09:56:14 +0000
@@ -0,0 +1,355 @@
+/*
+ * 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:
+ *  Alexandros Frantzis
+ */
+#include "scene.h"
+#include "mat.h"
+#include "stack.h"
+#include "vec.h"
+#include "log.h"
+#include "mesh.h"
+#include "util.h"
+#include "texture.h"
+#include "shader-source.h"
+#include "renderer.h"
+
+using LibMatrix::vec2;
+using LibMatrix::vec3;
+using LibMatrix::vec4;
+using LibMatrix::mat4;
+
+class SceneTerrainPrivate
+{
+public:
+    SceneTerrainPrivate(Canvas &canvas, const LibMatrix::vec2 &repeat_overlay,
+                        bool use_bloom, bool use_tilt_shift) :
+        canvas(canvas), repeat_overlay(repeat_overlay),
+        use_bloom(use_bloom), use_tilt_shift(use_tilt_shift),
+        terrain_renderer(0), bloom_v_renderer(0), bloom_h_renderer(0),
+        overlay_renderer(0), tilt_v_renderer(0), tilt_h_renderer(0),
+        copy_renderer(0), height_map_renderer(0), normal_map_renderer(0),
+        specular_map_renderer(0),
+        height_normal_chain(0), bloom_chain(0), tilt_chain(0), terrain_chain(0)
+    {
+        init_renderers();
+    }
+
+    ~SceneTerrainPrivate()
+    {
+        release_renderers();
+    }
+
+    void init_renderers()
+    {
+        /* Create and set up renderers */
+        const vec2 map_res(256.0f, 256.0f);
+        const vec2 screen_res(canvas.width(), canvas.height());
+        const vec2 bloom_res(256.0f, 256.0f);
+        const vec2 grass_res(512.0f, 512.0f);
+
+        height_map_renderer = new SimplexNoiseRenderer(map_res);
+        height_map_renderer->setup(height_map_renderer->size(), false, false);
+
+        normal_map_renderer = new NormalFromHeightRenderer(map_res);
+        normal_map_renderer->setup(normal_map_renderer->size(), false, false);
+
+        specular_map_renderer = new LuminanceRenderer(grass_res);
+        specular_map_renderer->setup_texture(GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR,
+                                             GL_REPEAT, GL_REPEAT);
+        specular_map_renderer->setup(specular_map_renderer->size(), false, false);
+
+        terrain_renderer = new TerrainRenderer(screen_res, repeat_overlay);
+        terrain_renderer->setup(terrain_renderer->size(),
+                                !use_bloom && !use_tilt_shift,
+                                true);
+        terrain_renderer->setup_texture(GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR,
+                                        GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE);
+
+        /* Bloom */
+        if (use_bloom) {
+            bloom_h_renderer = new BlurRenderer(bloom_res, 2, 4.0,
+                                                BlurRenderer::BlurDirectionHorizontal,
+                                                vec2(1.0, 1.0) / screen_res,
+                                                0.0);
+            bloom_h_renderer->setup_texture(GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR,
+                                            GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE);
+            bloom_h_renderer->setup(bloom_h_renderer->size(), false, false);
+
+            bloom_v_renderer = new BlurRenderer(bloom_res, 2, 4.0,
+                                                BlurRenderer::BlurDirectionVertical,
+                                                vec2(1.0, 1.0) / bloom_res,
+                                                0.0);
+            bloom_v_renderer->setup_texture(GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR,
+                                            GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE);
+            bloom_v_renderer->setup(bloom_v_renderer->size(), false, false);
+            overlay_renderer = new OverlayRenderer(*terrain_renderer, 0.6);
+        }
+
+        /* Tilt-shift */
+        if (use_tilt_shift) {
+            tilt_h_renderer = new BlurRenderer(screen_res, 4, 2.7,
+                                               BlurRenderer::BlurDirectionHorizontal,
+                                               vec2(1.0, 1.0) / screen_res,
+                                               0.5);
+            tilt_h_renderer->setup_texture(GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR,
+                                           GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE);
+            tilt_h_renderer->setup(tilt_h_renderer->size(), false, false);
+
+            tilt_v_renderer = new BlurRenderer(screen_res, 4, 2.7,
+                                               BlurRenderer::BlurDirectionVertical,
+                                               vec2(1.0, 1.0) / screen_res,
+                                               0.5);
+        }
+
+        /* Copy renderer */
+        if (use_bloom && !use_tilt_shift)
+            copy_renderer = new CopyRenderer(screen_res);
+
+        /* Height normal chain */
+        height_normal_chain = new RendererChain();
+        height_normal_chain->append(*height_map_renderer);
+        height_normal_chain->append(*normal_map_renderer);
+
+        /* Bloom effect chain */
+        if (use_bloom) {
+            bloom_chain = new RendererChain();
+            bloom_chain->append(*bloom_h_renderer);
+            bloom_chain->append(*bloom_v_renderer);
+            bloom_chain->append(*overlay_renderer);
+        }
+
+        /* Tilt-shift effect chain */
+        if (use_tilt_shift) {
+            tilt_chain = new RendererChain();
+            tilt_chain->append(*tilt_h_renderer);
+            tilt_chain->append(*tilt_v_renderer);
+        }
+
+        /* Terrain chain */
+        terrain_chain = new RendererChain();
+        terrain_chain->append(*terrain_renderer);
+        if (use_bloom)
+            terrain_chain->append(*bloom_chain);
+        if (use_tilt_shift)
+            terrain_chain->append(*tilt_chain);
+
+        /*
+         * If are just using bloom, the terrain is rendered to a texture and
+         * bloom applied on that texture. We need to "copy" that texture's
+         * contents to the screen to make the scene visible.
+         */
+        if (use_bloom && !use_tilt_shift)
+            terrain_chain->append(*copy_renderer);
+
+        /*
+         * Set up renderer textures.
+         */
+        terrain_renderer->height_map_texture(height_map_renderer->texture());
+        terrain_renderer->normal_map_texture(normal_map_renderer->texture());
+        terrain_renderer->specular_map_texture(specular_map_renderer->texture());
+
+        specular_map_renderer->input_texture(terrain_renderer->diffuse1_texture());
+    }
+
+    void release_renderers()
+    {
+        delete terrain_chain;
+        delete bloom_chain;
+        delete tilt_chain;
+        delete height_normal_chain;
+
+        delete height_map_renderer;
+        delete normal_map_renderer;
+        delete specular_map_renderer;
+        delete terrain_renderer;
+        delete bloom_v_renderer;
+        delete bloom_h_renderer;
+        delete overlay_renderer;
+        delete tilt_v_renderer;
+        delete tilt_h_renderer;
+        delete copy_renderer;
+    }
+
+    Canvas &canvas;
+    LibMatrix::vec2 repeat_overlay;
+    bool use_bloom;
+    bool use_tilt_shift;
+
+    /* Renderers */
+    TerrainRenderer *terrain_renderer;
+    BlurRenderer *bloom_v_renderer;
+    BlurRenderer *bloom_h_renderer;
+    OverlayRenderer *overlay_renderer;
+    BlurRenderer *tilt_v_renderer;
+    BlurRenderer *tilt_h_renderer;
+    CopyRenderer *copy_renderer;
+
+    SimplexNoiseRenderer *height_map_renderer;
+    NormalFromHeightRenderer *normal_map_renderer;
+    LuminanceRenderer *specular_map_renderer;
+
+    /* Chains */
+    RendererChain *height_normal_chain;
+    RendererChain *bloom_chain;
+    RendererChain *tilt_chain;
+    RendererChain *terrain_chain;
+};
+
+SceneTerrain::SceneTerrain(Canvas &pCanvas) :
+    Scene(pCanvas, "terrain"), priv_(0)
+{
+    options_["repeat-overlay"] = Scene::Option("repeat-overlay", "6.0",
+            "How many times to repeat the terrain texture on the terrain plane (per side)");
+    options_["bloom"] = Scene::Option("bloom", "true",
+                                      "Use bloom post-processing effect [true,false]");
+    options_["tilt-shift"] = Scene::Option("tilt-shift", "true",
+                                           "Use tilt-shift post-processing effect [true,false]");
+}
+
+SceneTerrain::~SceneTerrain()
+{
+    delete priv_;
+}
+
+bool
+SceneTerrain::load()
+{
+    Scene::load();
+
+    running_ = false;
+
+    return true;
+}
+
+void
+SceneTerrain::unload()
+{
+    Scene::unload();
+}
+
+void
+SceneTerrain::setup()
+{
+    Scene::setup();
+
+    /* Ensure implementation supports vertex texture fetch */
+    GLint vertex_textures;
+    glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &vertex_textures);
+    if (vertex_textures <= 0) {
+        Log::error("SceneTerrain requires Vertex Texture Fetch support, "
+                   "but GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS is %d\n",
+                   vertex_textures);
+        currentFrame_ = 0;
+        startTime_ = Util::get_timestamp_us() / 1000000.0;
+        running_ = false;
+        return;
+    }
+
+    /* Parse options */
+    float repeat = Util::fromString<double>(options_["repeat-overlay"].value);
+    LibMatrix::vec2 repeat_overlay(repeat, repeat);
+    bool use_bloom = options_["bloom"].value == "true";
+    bool use_tilt_shift = options_["tilt-shift"].value == "true";
+
+    priv_ = new SceneTerrainPrivate(canvas_, repeat_overlay,
+                                    use_bloom, use_tilt_shift);
+
+    /* Set up terrain rendering program */
+    LibMatrix::Stack4 model;
+    LibMatrix::Stack4 camera;
+    LibMatrix::mat4 projection = LibMatrix::Mat4::perspective(
+            40.0, canvas_.width() / static_cast<float>(canvas_.height()),
+            2.0, 4000.0);
+
+    /* Place camera */
+    camera.lookAt(-1200.0f, 800.0f, 1200.0f,
+                  0.0, 0.0, 0.0,
+                  0.0, 1.0, 0.0);
+    
+    /* Move and rotate plane */
+    model.translate(0.0f, -125.0f, 0.0f);
+    model.rotate(-90.0, 1.0f, 0.0f, 0.0f);
+
+    LibMatrix::mat4 view_matrix(camera.getCurrent());
+    LibMatrix::mat4 model_matrix(model.getCurrent());
+
+    LibMatrix::mat4 model_view_matrix(view_matrix * model_matrix);
+
+    LibMatrix::mat4 normal_matrix(model_view_matrix);
+    normal_matrix.inverse().transpose();
+
+    /* Set up terrain renderer program */
+    priv_->terrain_renderer->program().start();
+    priv_->terrain_renderer->program()["viewMatrix"] = view_matrix;
+    priv_->terrain_renderer->program()["modelViewMatrix"] = model_view_matrix;
+    priv_->terrain_renderer->program()["normalMatrix"] = normal_matrix;
+    priv_->terrain_renderer->program()["projectionMatrix"] = projection;
+
+    /* Create the specular map */
+    priv_->specular_map_renderer->render();
+
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
+    glViewport(0, 0, canvas_.width(), canvas_.height());
+
+    currentFrame_ = 0;
+    startTime_ = Util::get_timestamp_us() / 1000000.0;
+    running_ = true;
+}
+
+void
+SceneTerrain::teardown()
+{
+    delete priv_;
+    priv_ = 0;
+    Scene::teardown();
+}
+
+void
+SceneTerrain::update()
+{
+    Scene::update();
+
+    double now = Util::get_timestamp_us() / 1000000.0;
+    float diff = now - startTime_;
+    float scale = priv_->terrain_renderer->repeat_overlay().x() /
+                  priv_->height_map_renderer->uv_scale().x();
+
+    /* Update height map */
+    priv_->height_map_renderer->program().start();
+    priv_->height_map_renderer->program()["uvOffset"] = vec2(diff * 0.05f, 0.0f);
+    priv_->terrain_renderer->program().start();
+    priv_->terrain_renderer->program()["uOffset"] = vec2(scale * diff * 0.05f, 0.0f);
+}
+
+void
+SceneTerrain::draw()
+{
+    /* Render the height and normal maps used by the terrain */
+    priv_->height_normal_chain->render();
+
+    /* Render the terrain plus any post-processing effects */
+    priv_->terrain_chain->render();
+}
+
+Scene::ValidationResult
+SceneTerrain::validate()
+{
+    return Scene::ValidationUnknown;
+}

=== added file 'src/scene-terrain/base-renderer.cpp'
--- src/scene-terrain/base-renderer.cpp	1970-01-01 00:00:00 +0000
+++ src/scene-terrain/base-renderer.cpp	2012-07-04 10:14:08 +0000
@@ -0,0 +1,144 @@
+/*
+ * 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:
+ *  Alexandros Frantzis
+ */
+#include "renderer.h"
+
+BaseRenderer::BaseRenderer(const LibMatrix::vec2 &size) :
+    texture_(0), input_texture_(0), fbo_(0), depth_renderbuffer_(0),
+    min_filter_(GL_LINEAR), mag_filter_(GL_LINEAR),
+    wrap_s_(GL_CLAMP_TO_EDGE), wrap_t_(GL_CLAMP_TO_EDGE)
+{
+    setup(size, true, true);
+}
+
+BaseRenderer::~BaseRenderer()
+{
+    glDeleteTextures(1, &texture_);
+    glDeleteRenderbuffers(1, &depth_renderbuffer_);
+    glDeleteFramebuffers(1, &fbo_);
+}
+
+void
+BaseRenderer::setup(const LibMatrix::vec2 &size, bool onscreen, bool has_depth)
+{
+    size_ = size;
+    recreate(onscreen, has_depth);
+}
+
+void
+BaseRenderer::setup_texture(GLint min_filter, GLint mag_filter,
+                            GLint wrap_s, GLint wrap_t)
+{
+    min_filter_ = min_filter;
+    mag_filter_ = mag_filter;
+    wrap_s_ = wrap_s;
+    wrap_t_ = wrap_t;
+    update_texture_parameters();
+}
+
+void
+BaseRenderer::make_current()
+{
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
+    glViewport(0, 0, size_.x(), size_.y());
+    if (!fbo_ || depth_renderbuffer_) {
+        glEnable(GL_DEPTH_TEST);
+        glDepthMask(GL_TRUE);
+    }
+    else {
+        glDisable(GL_DEPTH_TEST);
+        glDepthMask(GL_FALSE);
+    }
+}
+
+void
+BaseRenderer::update_mipmap()
+{
+    if (texture_ && min_filter_ != GL_NEAREST && min_filter_ != GL_LINEAR) {
+        glBindTexture(GL_TEXTURE_2D, texture_);
+        glGenerateMipmap(GL_TEXTURE_2D);
+    }
+}
+
+void
+BaseRenderer::recreate(bool onscreen, bool has_depth)
+{
+    if (texture_) {
+        glDeleteTextures(1, &texture_);
+        texture_ = 0;
+    }
+    if (fbo_) {
+        glDeleteRenderbuffers(1, &depth_renderbuffer_);
+        depth_renderbuffer_ = 0;
+        glDeleteFramebuffers(1, &fbo_);
+        fbo_ = 0;
+    }
+    if (!onscreen) {
+        create_texture();
+        create_fbo(has_depth);
+    }
+}
+
+void
+BaseRenderer::create_texture()
+{
+    glGenTextures(1, &texture_);
+    glBindTexture(GL_TEXTURE_2D, texture_);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size_.x(), size_.y(), 0,
+            GL_RGBA, GL_UNSIGNED_BYTE, 0);
+    update_texture_parameters();
+}
+
+void
+BaseRenderer::update_texture_parameters()
+{
+    if (texture_) {
+        glBindTexture(GL_TEXTURE_2D, texture_);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter_);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter_);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s_);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t_);
+    }
+    update_mipmap();
+}
+
+void
+BaseRenderer::create_fbo(bool has_depth)
+{
+    if (has_depth) {
+        /* Create a renderbuffer for depth storage */
+        glGenRenderbuffers(1, &depth_renderbuffer_);
+        glBindRenderbuffer(GL_RENDERBUFFER, depth_renderbuffer_);
+        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16,
+                size_.x(), size_.y());
+    }
+
+    /* Create the FBO and attach the texture and the renderebuffer */
+    glGenFramebuffers(1, &fbo_);
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+            GL_TEXTURE_2D, texture_, 0);
+    if (has_depth) {
+        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
+                GL_RENDERBUFFER, depth_renderbuffer_);
+    }
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}

=== added file 'src/scene-terrain/blur-renderer.cpp'
--- src/scene-terrain/blur-renderer.cpp	1970-01-01 00:00:00 +0000
+++ src/scene-terrain/blur-renderer.cpp	2012-06-29 12:27:16 +0000
@@ -0,0 +1,161 @@
+/*
+ * 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:
+ *  Alexandros Frantzis
+ */
+#include "scene.h"
+#include "renderer.h"
+#include "shader-source.h"
+
+void
+create_blur_shaders(ShaderSource& vtx_source, ShaderSource& frg_source,
+                    unsigned int radius, float sigma, BlurRenderer::BlurDirection direction,
+                    float tilt_shift);
+
+BlurRenderer::BlurRenderer(const LibMatrix::vec2 &size, int radius, float sigma,
+                           BlurDirection dir, const LibMatrix::vec2 &step, float tilt_shift) :
+        TextureRenderer(size, *blur_program(true, radius, sigma, dir, step, tilt_shift))
+{
+    blur_program_ = blur_program(false, radius, sigma, dir, step, tilt_shift);
+}
+
+Program *
+BlurRenderer::blur_program(bool create_new, int radius, float sigma,
+                           BlurDirection dir, const LibMatrix::vec2 &step,
+                           float tilt_shift)
+{
+    static Program *blur_program(0);
+    if (create_new)
+        blur_program = 0;
+
+    if (!blur_program) {
+        blur_program = new Program();
+        ShaderSource blur_vtx_shader;
+        ShaderSource blur_frg_shader;
+        create_blur_shaders(blur_vtx_shader, blur_frg_shader, radius,
+                            sigma, dir, tilt_shift);
+
+        if (dir == BlurDirectionHorizontal || dir == BlurDirectionBoth)
+            blur_frg_shader.add_const("TextureStepX", step.x());
+        if (dir == BlurDirectionVertical || dir == BlurDirectionBoth)
+            blur_frg_shader.add_const("TextureStepY", step.y());
+
+        Scene::load_shaders_from_strings(*blur_program,
+                blur_vtx_shader.str(),
+                blur_frg_shader.str());
+        blur_program->start();
+        (*blur_program)["Texture0"] = 0;
+        (*blur_program)["uvOffset"] = LibMatrix::vec2(0.0f, 0.0f);
+        (*blur_program)["uvScale"] = LibMatrix::vec2(1.0f, 1.0f);
+        blur_program->stop();
+    }
+
+    return blur_program;
+}
+
+void
+create_blur_shaders(ShaderSource& vtx_source, ShaderSource& frg_source,
+                    unsigned int radius, float sigma, BlurRenderer::BlurDirection direction,
+                    float tilt_shift)
+{
+    vtx_source.append_file(GLMARK_DATA_PATH"/shaders/terrain-texture.vert");
+    frg_source.append_file(GLMARK_DATA_PATH"/shaders/terrain-blur.frag");
+
+    /* Don't let the gaussian curve become too narrow */
+    if (sigma < 1.0)
+        sigma = 1.0;
+
+    unsigned int side = 2 * radius + 1;
+    float values[radius];
+    float sum = 0.0;
+
+    for (unsigned int i = 0; i < radius + 1; i++) {
+        float s2 = 2.0 * sigma * sigma;
+        float k = 1.0 / std::sqrt(M_PI * s2) * std::exp( - (static_cast<float>(i) * i) / s2);
+        values[i] = k;
+        sum += k;
+    }
+
+    sum += sum - values[0];
+
+    for (unsigned int i = 0; i < radius + 1; i++) {
+        std::stringstream ss_tmp;
+        ss_tmp << "Kernel" << i;
+        frg_source.add_const(ss_tmp.str(), values[i] / sum);
+    }
+
+    frg_source.add_const("TiltShift", tilt_shift);
+
+    std::stringstream ss;
+
+    if (direction == BlurRenderer::BlurDirectionHorizontal ||
+        direction == BlurRenderer::BlurDirectionBoth)
+    {
+        if (tilt_shift == 1.0)
+            ss << "const float stepX = TextureStepX;" << std::endl;
+        else
+            ss << "float stepX = TextureStepX * abs(TiltShift - TextureCoord.y) / abs(1.0 - TiltShift);" << std::endl;
+    }
+
+    if (direction == BlurRenderer::BlurDirectionVertical ||
+        direction == BlurRenderer::BlurDirectionBoth)
+    {
+        if (tilt_shift == 1.0)
+            ss << "const float stepY = TextureStepY;" << std::endl;
+        else
+            ss << "float stepY = TextureStepY * abs(TiltShift - TextureCoord.y) / abs(1.0 - TiltShift);" << std::endl;
+    }
+
+    ss << "result = " << std::endl;
+
+    if (direction == BlurRenderer::BlurDirectionHorizontal) {
+        for (unsigned int i = 0; i < side; i++) {
+            int offset = static_cast<int>(i - radius);
+            ss << "texture2D(Texture0, TextureCoord + vec2(" <<
+                  offset << ".0 * stepX, 0.0)) * Kernel" <<
+                  std::abs(offset) << " +" << std::endl;
+        }
+        ss << "0.0 ;" << std::endl;
+    }
+    else if (direction == BlurRenderer::BlurDirectionVertical) {
+        for (unsigned int i = 0; i < side; i++) {
+            int offset = static_cast<int>(i - radius);
+            ss << "texture2D(Texture0, TextureCoord + vec2(0.0, " <<
+                  offset << ".0 * stepY)) * Kernel" <<
+                  std::abs(offset) << " +" << std::endl;
+        }
+        ss << "0.0 ;" << std::endl;
+    }
+    else if (direction == BlurRenderer::BlurDirectionBoth) {
+        for (unsigned int i = 0; i < side; i++) {
+            int ioffset = static_cast<int>(i - radius);
+            for (unsigned int j = 0; j < side; j++) {
+                int joffset = static_cast<int>(j - radius);
+                ss << "texture2D(Texture0, TextureCoord + vec2(" <<
+                      ioffset << ".0 * stepX, " <<
+                      joffset << ".0 * stepY))" <<
+                      " * Kernel" << std::abs(ioffset) <<
+                      " * Kernel" << std::abs(joffset) << " +" << std::endl;
+            }
+        }
+        ss << " 0.0;" << std::endl;
+    }
+
+    frg_source.replace("$CONVOLUTION$", ss.str());
+}

=== added file 'src/scene-terrain/copy-renderer.cpp'
--- src/scene-terrain/copy-renderer.cpp	1970-01-01 00:00:00 +0000
+++ src/scene-terrain/copy-renderer.cpp	2012-06-29 12:27:16 +0000
@@ -0,0 +1,55 @@
+/*
+ * 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:
+ *  Alexandros Frantzis
+ */
+#include "scene.h"
+#include "renderer.h"
+#include "shader-source.h"
+
+CopyRenderer::CopyRenderer(const LibMatrix::vec2 &size) :
+    TextureRenderer(size, *copy_program(true))
+{
+    copy_program_ = copy_program(false);
+}
+
+Program *
+CopyRenderer::copy_program(bool create_new)
+{
+    static Program *copy_program(0);
+    if (create_new)
+        copy_program = 0;
+
+    if (!copy_program) {
+        copy_program = new Program();
+        ShaderSource vtx_shader(GLMARK_DATA_PATH"/shaders/terrain-texture.vert");
+        ShaderSource frg_shader(GLMARK_DATA_PATH"/shaders/terrain-overlay.frag");
+
+        Scene::load_shaders_from_strings(*copy_program, vtx_shader.str(), frg_shader.str());
+
+        copy_program->start();
+        (*copy_program)["tDiffuse"] = 0;
+        (*copy_program)["opacity"] = 1.0f;
+        (*copy_program)["uvOffset"] = LibMatrix::vec2(0.0f, 0.0f);
+        (*copy_program)["uvScale"] = LibMatrix::vec2(1.0f, 1.0f);
+        copy_program->stop();
+    }
+
+    return copy_program;
+}

=== added file 'src/scene-terrain/luminance-renderer.cpp'
--- src/scene-terrain/luminance-renderer.cpp	1970-01-01 00:00:00 +0000
+++ src/scene-terrain/luminance-renderer.cpp	2012-06-29 12:27:16 +0000
@@ -0,0 +1,57 @@
+/*
+ * 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:
+ *  Alexandros Frantzis
+ */
+#include "scene.h"
+#include "renderer.h"
+#include "shader-source.h"
+
+LuminanceRenderer::LuminanceRenderer(const LibMatrix::vec2 &size) :
+    TextureRenderer(size, *luminance_program(true))
+{
+    luminance_program_ = luminance_program(false);
+}
+
+Program *
+LuminanceRenderer::luminance_program(bool create_new)
+{
+    static Program *luminance_program(0);
+    if (create_new)
+        luminance_program = 0;
+
+    if (!luminance_program) {
+        luminance_program = new Program();
+        ShaderSource vtx_shader(GLMARK_DATA_PATH"/shaders/terrain-texture.vert");
+        ShaderSource frg_shader(GLMARK_DATA_PATH"/shaders/terrain-luminance.frag");
+
+        Scene::load_shaders_from_strings(*luminance_program,
+                                         vtx_shader.str(), frg_shader.str());
+
+        luminance_program->start();
+        (*luminance_program)["tDiffuse"] = 0;
+        (*luminance_program)["uvOffset"] = LibMatrix::vec2(0.0f, 0.0f);
+        (*luminance_program)["uvScale"] = LibMatrix::vec2(1.0f, 1.0f);
+        luminance_program->stop();
+    }
+
+    return luminance_program;
+}
+
+

=== added file 'src/scene-terrain/normal-from-height-renderer.cpp'
--- src/scene-terrain/normal-from-height-renderer.cpp	1970-01-01 00:00:00 +0000
+++ src/scene-terrain/normal-from-height-renderer.cpp	2012-06-29 12:27:16 +0000
@@ -0,0 +1,59 @@
+/*
+ * 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:
+ *  Alexandros Frantzis
+ */
+#include "scene.h"
+#include "renderer.h"
+#include "shader-source.h"
+
+NormalFromHeightRenderer::NormalFromHeightRenderer(const LibMatrix::vec2 &size) :
+    TextureRenderer(size, *normal_from_height_program(size, true))
+{
+    normal_from_height_program_ = normal_from_height_program(size, false);
+}
+
+Program *
+NormalFromHeightRenderer::normal_from_height_program(const LibMatrix::vec2 &size,
+                                                     bool create_new)
+{
+    static Program *normal_from_height_program(0);
+    if (create_new)
+        normal_from_height_program = 0;
+
+    if (!normal_from_height_program) {
+        normal_from_height_program = new Program();
+        ShaderSource vtx_shader(GLMARK_DATA_PATH"/shaders/terrain-texture.vert");
+        ShaderSource frg_shader(GLMARK_DATA_PATH"/shaders/terrain-normalmap.frag");
+
+        Scene::load_shaders_from_strings(*normal_from_height_program,
+                                         vtx_shader.str(), frg_shader.str());
+
+        normal_from_height_program->start();
+        (*normal_from_height_program)["heightMap"] = 0;
+        (*normal_from_height_program)["resolution"] = size;
+        (*normal_from_height_program)["height"] = 0.05f;
+        (*normal_from_height_program)["uvOffset"] = LibMatrix::vec2(0.0f, 0.0f);
+        (*normal_from_height_program)["uvScale"] = LibMatrix::vec2(1.0f, 1.0f);
+        normal_from_height_program->stop();
+    }
+
+    return normal_from_height_program;
+}
+

=== added file 'src/scene-terrain/overlay-renderer.cpp'
--- src/scene-terrain/overlay-renderer.cpp	1970-01-01 00:00:00 +0000
+++ src/scene-terrain/overlay-renderer.cpp	2012-06-29 12:27:16 +0000
@@ -0,0 +1,122 @@
+/*
+ * 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:
+ *  Alexandros Frantzis
+ */
+#include "scene.h"
+#include "renderer.h"
+#include "shader-source.h"
+
+OverlayRenderer::OverlayRenderer(IRenderer &target, GLfloat opacity) :
+    target_renderer_(target), opacity_(opacity)
+
+{
+    create_program();
+    create_mesh();
+}
+
+
+void
+OverlayRenderer::setup(const LibMatrix::vec2 &size, bool onscreen, bool has_depth)
+{
+    static_cast<void>(size);
+    static_cast<void>(onscreen);
+    static_cast<void>(has_depth);
+}
+
+void
+OverlayRenderer::setup_texture(GLint min_filter, GLint mag_filter,
+                               GLint wrap_s, GLint wrap_t)
+{
+    static_cast<void>(min_filter);
+    static_cast<void>(mag_filter);
+    static_cast<void>(wrap_s);
+    static_cast<void>(wrap_t);
+}
+
+void
+OverlayRenderer::make_current()
+{
+    target_renderer_.make_current();
+}
+
+void
+OverlayRenderer::update_mipmap()
+{
+    target_renderer_.update_mipmap();
+}
+
+void
+OverlayRenderer::render()
+{
+    target_renderer_.make_current();
+
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, input_texture_);
+
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+    program_.start();
+
+    mesh_.render_vbo();
+
+    program_.stop();
+
+    glDisable(GL_BLEND);
+
+    target_renderer_.update_mipmap();
+}
+
+void
+OverlayRenderer::create_mesh()
+{
+    // Set vertex format
+    std::vector<int> vertex_format;
+    vertex_format.push_back(3); // Position
+    mesh_.set_vertex_format(vertex_format);
+
+    mesh_.make_grid(1, 1, 2.0, 2.0, 0);
+    mesh_.build_vbo();
+
+    program_.start();
+
+    // Set attribute locations
+    std::vector<GLint> attrib_locations;
+    attrib_locations.push_back(program_["position"].location());
+    mesh_.set_attrib_locations(attrib_locations);
+
+    program_.stop();
+}
+
+void
+OverlayRenderer::create_program()
+{
+    ShaderSource vtx_shader(GLMARK_DATA_PATH"/shaders/terrain-texture.vert");
+    ShaderSource frg_shader(GLMARK_DATA_PATH"/shaders/terrain-overlay.frag");
+
+    if (!Scene::load_shaders_from_strings(program_, vtx_shader.str(), frg_shader.str()))
+        return;
+
+    program_.start();
+    program_["tDiffuse"] = 0;
+    program_["opacity"] = opacity_;
+    program_["uvOffset"] = LibMatrix::vec2(0.0f, 0.0f);
+    program_["uvScale"] = LibMatrix::vec2(1.0f, 1.0f);
+    program_.stop();
+}

=== added file 'src/scene-terrain/renderer-chain.cpp'
--- src/scene-terrain/renderer-chain.cpp	1970-01-01 00:00:00 +0000
+++ src/scene-terrain/renderer-chain.cpp	2012-06-29 12:27:16 +0000
@@ -0,0 +1,115 @@
+/*
+ * 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:
+ *  Alexandros Frantzis
+ */
+#include "renderer.h"
+
+void
+RendererChain::setup(const LibMatrix::vec2 &size, bool onscreen, bool has_depth)
+{
+    static_cast<void>(size);
+    static_cast<void>(onscreen);
+    static_cast<void>(has_depth);
+}
+
+void
+RendererChain::setup_texture(GLint min_filter, GLint mag_filter,
+                             GLint wrap_s, GLint wrap_t)
+{
+    static_cast<void>(min_filter);
+    static_cast<void>(mag_filter);
+    static_cast<void>(wrap_s);
+    static_cast<void>(wrap_t);
+}
+
+void
+RendererChain::input_texture(GLuint t)
+{ 
+    if (!renderers_.empty()) {
+        IRenderer &renderer(*renderers_.front());
+        renderer.input_texture(t);
+    }
+}
+
+GLuint
+RendererChain::texture()
+{
+    GLuint t(0);
+
+    if (!renderers_.empty()) {
+        IRenderer &renderer(*renderers_.back());
+        t = renderer.texture();
+    }
+
+    return t;
+}
+
+LibMatrix::vec2
+RendererChain::size()
+{
+    LibMatrix::vec2 s;
+
+    if (!renderers_.empty()) {
+        IRenderer &renderer(*renderers_.back());
+        s = renderer.size();
+    }
+
+    return s;
+}
+
+void
+RendererChain::make_current()
+{
+    if (!renderers_.empty()) {
+        IRenderer &renderer(*renderers_.back());
+        renderer.make_current();
+    }
+}
+
+void
+RendererChain::update_mipmap()
+{
+    if (!renderers_.empty()) {
+        IRenderer &renderer(*renderers_.back());
+        renderer.update_mipmap();
+    }
+}
+
+void
+RendererChain::render()
+{
+    for(std::vector<IRenderer *>::iterator iter = renderers_.begin();
+        iter != renderers_.end();
+        iter++)
+    {
+        (*iter)->render();
+    }
+}
+
+void
+RendererChain::append(IRenderer &renderer)
+{
+    if (!renderers_.empty()) {
+        IRenderer &prev_renderer(*renderers_.back());
+        renderer.input_texture(prev_renderer.texture());
+    }
+
+    renderers_.push_back(&renderer);
+}

=== added file 'src/scene-terrain/renderer.h'
--- src/scene-terrain/renderer.h	1970-01-01 00:00:00 +0000
+++ src/scene-terrain/renderer.h	2012-06-29 12:46:57 +0000
@@ -0,0 +1,347 @@
+/*
+ * 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:
+ *  Alexandros Frantzis
+ */
+#include <vector>
+
+#include "mesh.h"
+#include "vec.h"
+#include "program.h"
+#include "gl-headers.h"
+
+/** 
+ * Renderer interface.
+ */
+class IRenderer
+{
+public:
+    /** 
+     * Sets up the renderer's target.
+     */
+    virtual void setup(const LibMatrix::vec2 &size, bool onscreen, bool has_depth) = 0;
+    /** 
+     * Sets up the renderer's target texture (if any).
+     */
+    virtual void setup_texture(GLint min_filter, GLint mag_filter,
+                               GLint wrap_s, GLint wrap_t) = 0;
+    /** 
+     * Sets the renderer's input texture.
+     */
+    virtual void input_texture(GLuint t) = 0;
+    /** 
+     * Gets the renderer's target texture (if any).
+     */
+    virtual GLuint texture() = 0;
+    /** 
+     * Gets the size of the renderer's target.
+     */
+    virtual LibMatrix::vec2 size() = 0;
+    /** 
+     * Makes the renderer current i.e. the rendering target.
+     */
+    virtual void make_current() = 0;
+    /** 
+     * Updates the mipmap of the texture backing the renderer (if any).
+     */
+    virtual void update_mipmap() = 0;
+    /** 
+     * Renders to the renderer's target.
+     */
+    virtual void render() = 0;
+
+protected:
+    virtual ~IRenderer() {}
+};
+
+/** 
+ * A chain of renderers, which implements IRenderer
+ */
+class RendererChain : public IRenderer
+{
+public:
+    RendererChain() {}
+    virtual ~RendererChain() {}
+
+    /* IRenderer methods */
+    void setup(const LibMatrix::vec2 &size, bool onscreen, bool has_depth);
+    void setup_texture(GLint min_filter, GLint mag_filter,
+                       GLint wrap_s, GLint wrap_t);
+    void input_texture(GLuint t);
+    GLuint texture();
+    LibMatrix::vec2 size();
+    void make_current();
+    void update_mipmap();
+    void render();
+
+    /** 
+     * Appends a renderer to the chain.
+     * 
+     * @param renderer the renderer to append
+     */
+    void append(IRenderer &renderer);
+
+private:
+    std::vector<IRenderer *> renderers_;
+};
+
+/** 
+ * A base implementation of the IRenderer interface.
+ */
+class BaseRenderer : public IRenderer
+{
+public:
+    BaseRenderer(const LibMatrix::vec2 &size);
+    virtual ~BaseRenderer();
+
+    /* IRenderer methods */
+    virtual void setup(const LibMatrix::vec2 &size, bool onscreen, bool has_depth);
+    virtual void setup_texture(GLint min_filter, GLint mag_filter,
+                               GLint wrap_s, GLint wrap_t);
+    virtual void input_texture(GLuint t) { input_texture_ = t; }
+    virtual GLuint texture() { return texture_; }
+    virtual LibMatrix::vec2 size() { return size_; }
+    virtual void make_current();
+    virtual void update_mipmap();
+    virtual void render() = 0;
+
+protected:
+    void recreate(bool onscreen, bool has_depth);
+    void create_texture();
+    void update_texture_parameters();
+    void create_fbo(bool has_depth);
+
+    LibMatrix::vec2 size_;
+    GLuint texture_;
+    GLuint input_texture_;
+    GLuint fbo_;
+    GLuint depth_renderbuffer_;
+    GLint min_filter_;
+    GLint mag_filter_;
+    GLint wrap_s_;
+    GLint wrap_t_;
+};
+
+/** 
+ * A renderer that renders its input texture to its target,
+ * according to the supplied GL Program.
+ */
+class TextureRenderer : public BaseRenderer
+{
+public:
+    TextureRenderer(const LibMatrix::vec2 &size, Program &program);
+    virtual ~TextureRenderer() { }
+
+    /* IRenderer/BaseRenderer methods */
+    virtual void render();
+
+    /**
+     * Gets the program associated with the renderer.
+     */
+    Program &program() { return program_; }
+
+private:
+    void create_mesh();
+
+    Mesh mesh_;
+    Program &program_;
+};
+
+/** 
+ * A renderer that copies the input texture to its target.
+ */
+class CopyRenderer : public TextureRenderer
+{
+public:
+    CopyRenderer(const LibMatrix::vec2 &size);
+
+    virtual ~CopyRenderer() { delete copy_program_; }
+
+private:
+    static Program *copy_program(bool create_new);
+
+    Program *copy_program_;
+};
+
+/** 
+ * A renderer that renders simplex noise to its target.
+ */
+class SimplexNoiseRenderer : public TextureRenderer
+{
+public:
+    SimplexNoiseRenderer(const LibMatrix::vec2 &size);
+    virtual ~SimplexNoiseRenderer() { delete noise_program_; }
+
+    LibMatrix::vec2 uv_scale() { return uv_scale_; }
+private:
+    static Program *noise_program(bool create_new);
+    static LibMatrix::vec2 uv_scale_;
+
+    Program *noise_program_;
+};
+
+/** 
+ * A renderer that renders a normal map to its target from a
+ * height map in its input texture.
+ */
+class NormalFromHeightRenderer : public TextureRenderer
+{
+public:
+    NormalFromHeightRenderer(const LibMatrix::vec2 &size);
+    virtual ~NormalFromHeightRenderer() { delete normal_from_height_program_; }
+
+private:
+    static Program *normal_from_height_program(const LibMatrix::vec2 &size, 
+                                               bool create_new);
+
+    Program *normal_from_height_program_;
+};
+
+/** 
+ * A renderer that renders the luminance of its input texture to its target.
+ */
+class LuminanceRenderer : public TextureRenderer
+{
+public:
+    LuminanceRenderer(const LibMatrix::vec2 &size);
+    virtual ~LuminanceRenderer() { delete luminance_program_; }
+
+private:
+    static Program *luminance_program(bool create_new);
+
+    Program *luminance_program_;
+};
+
+
+/** 
+ * A renderer that renders a blurred version of the input texture to its target.
+ */
+class BlurRenderer : public TextureRenderer
+{
+public:
+    enum BlurDirection {
+        BlurDirectionHorizontal,
+        BlurDirectionVertical,
+        BlurDirectionBoth
+    };
+
+    BlurRenderer(const LibMatrix::vec2 &size, int radius, float sigma,
+                 BlurDirection dir, const LibMatrix::vec2 &step, float tilt_shift);
+    virtual ~BlurRenderer() { delete blur_program_; }
+
+private:
+    static Program *blur_program(bool create_new, int radius, float sigma,
+                                 BlurDirection dir, const LibMatrix::vec2 &step,
+                                 float tilt_shift);
+
+    Program *blur_program_;
+};
+
+/** 
+ * A renderer that renders with opacity (overlays) it's input texture over
+ * the target of another renderer.
+ */
+class OverlayRenderer : public IRenderer
+{
+public:
+    OverlayRenderer(IRenderer &target, GLfloat opacity);
+    virtual ~OverlayRenderer() { }
+
+    /* IRenderable Methods */
+    void setup(const LibMatrix::vec2 &size, bool onscreen, bool has_depth);
+    void setup_texture(GLint min_filter, GLint mag_filter,
+                       GLint wrap_s, GLint wrap_t);
+    void input_texture(GLuint t) { input_texture_ = t; }
+    virtual GLuint texture() { return target_renderer_.texture(); }
+    virtual LibMatrix::vec2 size() { return target_renderer_.size(); }
+    virtual void render();
+    void make_current();
+    void update_mipmap();
+
+private:
+    void create_mesh();
+    void create_program();
+
+    Mesh mesh_;
+    Program program_;
+    IRenderer &target_renderer_;
+    GLfloat opacity_;
+    GLuint input_texture_;
+};
+
+/** 
+ * A renderer that renders a dynamic terrain as per the WebGL
+ * dynamic terrain demo.
+ */
+class TerrainRenderer : public BaseRenderer
+{
+public:
+    TerrainRenderer(const LibMatrix::vec2 &size, const LibMatrix::vec2 &repeat_overlay);
+    virtual ~TerrainRenderer();
+
+    /* IRenderable Methods */
+    virtual void render();
+
+    /**
+     * Gets the program associated with the renderer.
+     */
+    Program &program() { return program_; }
+    /**
+     * Sets the height map texture to use.
+     */
+    void height_map_texture(GLuint tex) { height_map_tex_ = tex; }
+    /**
+     * Sets the normal map texture to use.
+     */
+    void normal_map_texture(GLuint tex) { normal_map_tex_ = tex; }
+    /**
+     * Sets the specular map texture to use.
+     */
+    void specular_map_texture(GLuint tex) { specular_map_tex_ = tex; }
+    /**
+     * Returns the main diffuse texture.
+     */
+    GLuint diffuse1_texture() { return diffuse1_tex_; }
+    LibMatrix::vec2 repeat_overlay() { return repeat_overlay_; }
+
+private:
+    void create_mesh();
+    void init_textures();
+    void init_program();
+    void bind_textures();
+    void deinit_textures();
+
+    LibMatrix::vec3 color_to_vec3(uint32_t c)
+    {
+        return LibMatrix::vec3(((c >> 0) & 0xff) / 255.0,
+                               ((c >> 8) & 0xff) / 255.0,
+                               ((c >> 16) & 0xff) / 255.0);
+    }
+
+    Mesh mesh_;
+    Program program_;
+
+    GLuint height_map_tex_;
+    GLuint normal_map_tex_;
+    GLuint specular_map_tex_;
+    GLuint diffuse1_tex_;
+    GLuint diffuse2_tex_;
+    GLuint detail_tex_;
+    LibMatrix::vec2 repeat_overlay_;
+};

=== added file 'src/scene-terrain/simplex-noise-renderer.cpp'
--- src/scene-terrain/simplex-noise-renderer.cpp	1970-01-01 00:00:00 +0000
+++ src/scene-terrain/simplex-noise-renderer.cpp	2012-06-29 12:27:16 +0000
@@ -0,0 +1,56 @@
+/*
+ * 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:
+ *  Alexandros Frantzis
+ */
+#include "scene.h"
+#include "renderer.h"
+#include "shader-source.h"
+
+LibMatrix::vec2 SimplexNoiseRenderer::uv_scale_(1.5f, 1.5);
+
+SimplexNoiseRenderer::SimplexNoiseRenderer(const LibMatrix::vec2 &size) :
+    TextureRenderer(size, *noise_program(true))
+{
+    noise_program_ = noise_program(false);
+}
+
+Program *
+SimplexNoiseRenderer::noise_program(bool create_new)
+{
+    static Program *noise_program(0);
+    if (create_new)
+        noise_program = 0;
+
+    if (!noise_program) {
+        noise_program = new Program();
+        ShaderSource vtx_shader(GLMARK_DATA_PATH"/shaders/terrain-texture.vert");
+        ShaderSource frg_shader(GLMARK_DATA_PATH"/shaders/terrain-noise.frag");
+
+        Scene::load_shaders_from_strings(*noise_program, vtx_shader.str(), frg_shader.str());
+
+        noise_program->start();
+        (*noise_program)["time"] = 1.0f;
+        (*noise_program)["uvOffset"] = LibMatrix::vec2(0.0f, 0.0f);
+        (*noise_program)["uvScale"] = uv_scale_;
+        noise_program->stop();
+    }
+
+    return noise_program;
+}

=== added file 'src/scene-terrain/terrain-renderer.cpp'
--- src/scene-terrain/terrain-renderer.cpp	1970-01-01 00:00:00 +0000
+++ src/scene-terrain/terrain-renderer.cpp	2012-06-29 12:46:57 +0000
@@ -0,0 +1,246 @@
+/*
+ * 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:
+ *  Alexandros Frantzis
+ */
+#include "scene.h"
+#include "renderer.h"
+#include "texture.h"
+#include "shader-source.h"
+
+TerrainRenderer::TerrainRenderer(const LibMatrix::vec2 &size,
+                                 const LibMatrix::vec2 &repeat_overlay) :
+    BaseRenderer(size), height_map_tex_(0), normal_map_tex_(0),
+    specular_map_tex_(0), repeat_overlay_(repeat_overlay)
+{
+    create_mesh();
+    init_textures();
+    init_program();
+}
+
+TerrainRenderer::~TerrainRenderer()
+{
+    deinit_textures();
+}
+
+void
+TerrainRenderer::render()
+{
+    make_current();
+    glClearColor(0.825f, 0.7425f, 0.61875f, 1.0f);
+    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+
+    program_.start();
+
+    bind_textures();
+    mesh_.render_vbo();
+
+    program_.stop();
+
+    update_mipmap();
+}
+
+void
+TerrainRenderer::init_textures()
+{
+    /* Create textures */
+    Texture::load("terrain-grasslight-512", &diffuse1_tex_,
+            GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, 0);
+    Texture::load("terrain-backgrounddetailed6", &diffuse2_tex_,
+            GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, 0);
+    Texture::load("terrain-grasslight-512-nm", &detail_tex_,
+            GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, 0);
+
+    /* Set REPEAT wrap mode */
+    glBindTexture(GL_TEXTURE_2D, diffuse1_tex_);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+    glBindTexture(GL_TEXTURE_2D, diffuse2_tex_);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+    glBindTexture(GL_TEXTURE_2D, detail_tex_);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+}
+
+
+void
+TerrainRenderer::init_program()
+{
+    ShaderSource vtx_shader(GLMARK_DATA_PATH"/shaders/terrain.vert");
+    ShaderSource frg_shader(GLMARK_DATA_PATH"/shaders/terrain.frag");
+
+    if (!Scene::load_shaders_from_strings(program_, vtx_shader.str(), frg_shader.str()))
+        return;
+
+    program_.start();
+    /* Fog */
+    program_["fogDensity"] = 0.00025f;
+    program_["fogNear"] = 1.0f;
+    program_["fogFar"] = 2000.0f;
+    program_["fogColor"] = LibMatrix::vec3(0.825, 0.7425, 0.61875);
+
+    /* Lights */
+    program_["ambientLightColor"] = color_to_vec3(0xffffff);
+
+    program_["pointLightColor[0]"] = color_to_vec3(0xffffff);
+    program_["pointLightPosition[0]"] = LibMatrix::vec3(0.0, 0.0, 0.0);
+    program_["pointLightDistance[0]"] = 0.0f;
+
+    /* Textures */
+    program_["tDiffuse1"] = 0;
+    program_["tDiffuse2"] = 1;
+    program_["tDetail"] = 2;
+    program_["tNormal"] = 3;
+    program_["tSpecular"] = 4;
+    program_["tDisplacement"] = 5;
+
+    program_["uNormalScale"] = 3.5f;
+
+    program_["uDisplacementBias"] = 0.0f;
+    program_["uDisplacementScale"] = 375.0f;
+
+    program_["uDiffuseColor"] = color_to_vec3(0xffffff);
+    program_["uSpecularColor"] = color_to_vec3(0xffffff);
+    program_["uAmbientColor"] = color_to_vec3(0x888888);
+    program_["uShininess"] = 30.0f;
+    program_["uOpacity"] = 1.0f;
+
+    program_["uRepeatOverlay"] = repeat_overlay_;
+
+    program_["uOffset"] = LibMatrix::vec2(0.0, 0.0);
+
+    std::vector<GLint> attrib_locations;
+    attrib_locations.push_back(program_["position"].location());
+    attrib_locations.push_back(program_["normal"].location());
+    attrib_locations.push_back(program_["tangent"].location());
+    attrib_locations.push_back(program_["uv"].location());
+    mesh_.set_attrib_locations(attrib_locations);
+
+    program_.stop();
+}
+
+void
+TerrainRenderer::bind_textures()
+{
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, diffuse1_tex_);
+
+    glActiveTexture(GL_TEXTURE1);
+    glBindTexture(GL_TEXTURE_2D, diffuse2_tex_);
+
+    glActiveTexture(GL_TEXTURE2);
+    glBindTexture(GL_TEXTURE_2D, detail_tex_);
+
+    glActiveTexture(GL_TEXTURE3);
+    glBindTexture(GL_TEXTURE_2D, normal_map_tex_);
+
+    glActiveTexture(GL_TEXTURE4);
+    glBindTexture(GL_TEXTURE_2D, specular_map_tex_);
+
+    glActiveTexture(GL_TEXTURE5);
+    glBindTexture(GL_TEXTURE_2D, height_map_tex_);
+}
+
+void
+TerrainRenderer::deinit_textures()
+{
+    glDeleteTextures(1, &diffuse1_tex_);
+    glDeleteTextures(1, &diffuse2_tex_);
+    glDeleteTextures(1, &detail_tex_);
+}
+
+static void
+grid_conf(Mesh &mesh, int x, int y, int n_x, int n_y,
+          LibMatrix::vec3 &ul,
+          LibMatrix::vec3 &ll,
+          LibMatrix::vec3 &ur,
+          LibMatrix::vec3 &lr)
+{
+    struct PlaneMeshVertex {
+        LibMatrix::vec3 position;
+        LibMatrix::vec2 texcoord;
+        LibMatrix::vec3 normal;
+        LibMatrix::vec3 tangent;
+    };
+
+    LibMatrix::vec2 uv_ul(static_cast<float>(x) / n_x,
+            1.0 - static_cast<float>(y) / n_y);
+    LibMatrix::vec2 uv_lr(static_cast<float>(x + 1) / n_x,
+            1.0 - static_cast<float>(y + 1) / n_y);
+
+    LibMatrix::vec3 normal(0.0, 0.0, 1.0);
+    LibMatrix::vec3 tangent(1.0, 0.0, 0.0);
+
+    PlaneMeshVertex cell_vertices[] = {
+        {
+            ll,
+            LibMatrix::vec2(uv_ul.x(), uv_lr.y()),
+            normal,
+            tangent
+        },
+        {
+            lr,
+            LibMatrix::vec2(uv_lr.x(), uv_lr.y()),
+            normal,
+            tangent
+        },
+        {
+            ur,
+            LibMatrix::vec2(uv_lr.x(), uv_ul.y()),
+            normal,
+            tangent
+        },
+        {
+            ul,
+            LibMatrix::vec2(uv_ul.x(), uv_ul.y()),
+            normal,
+            tangent
+        }
+    };
+
+    unsigned int vertex_index[] = {0, 1, 2, 0, 2, 3};
+
+    for (size_t i = 0; i < sizeof(vertex_index) / sizeof(*vertex_index); i++) {
+        PlaneMeshVertex& vertex = cell_vertices[vertex_index[i]];
+
+        mesh.next_vertex();
+        mesh.set_attrib(0, vertex.position);
+        mesh.set_attrib(1, vertex.normal);
+        mesh.set_attrib(2, vertex.tangent);
+        mesh.set_attrib(3, vertex.texcoord);
+    }
+}
+
+void
+TerrainRenderer::create_mesh()
+{
+    std::vector<int> vertex_format;
+    vertex_format.push_back(3);
+    vertex_format.push_back(3);
+    vertex_format.push_back(3);
+    vertex_format.push_back(2);
+    mesh_.set_vertex_format(vertex_format);
+
+    mesh_.make_grid(256, 256, 6000, 6000, 0, grid_conf);
+    mesh_.build_vbo();
+}
+

=== added file 'src/scene-terrain/texture-renderer.cpp'
--- src/scene-terrain/texture-renderer.cpp	1970-01-01 00:00:00 +0000
+++ src/scene-terrain/texture-renderer.cpp	2012-06-29 12:27:16 +0000
@@ -0,0 +1,69 @@
+/*
+ * 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:
+ *  Alexandros Frantzis
+ */
+#include "renderer.h"
+
+TextureRenderer::TextureRenderer(const LibMatrix::vec2 &size, Program &program) :
+    BaseRenderer(size), program_(program)
+{
+    /* Create the mesh (quad) used for rendering */
+    create_mesh();
+}
+
+void
+TextureRenderer::create_mesh()
+{
+    // Set vertex format
+    std::vector<int> vertex_format;
+    vertex_format.push_back(3); // Position
+    mesh_.set_vertex_format(vertex_format);
+
+    mesh_.make_grid(1, 1, 2.0, 2.0, 0);
+    mesh_.build_vbo();
+
+    program_.start();
+
+    // Set attribute locations
+    std::vector<GLint> attrib_locations;
+    attrib_locations.push_back(program_["position"].location());
+    mesh_.set_attrib_locations(attrib_locations);
+
+    program_.stop();
+}
+
+void
+TextureRenderer::render()
+{
+    make_current();
+
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, input_texture_);
+
+    program_.start();
+
+    mesh_.render_vbo();
+
+    program_.stop();
+
+    update_mipmap();
+}

=== modified file 'src/scene.h'
--- src/scene.h	2012-05-15 12:46:43 +0000
+++ src/scene.h	2012-06-29 12:27:16 +0000
@@ -504,4 +504,24 @@
     SceneIdeasPrivate* priv_;
 };
 
+class SceneTerrainPrivate;
+
+class SceneTerrain : public Scene
+{
+public:
+    SceneTerrain(Canvas &pCanvas);
+    bool load();
+    void unload();
+    void setup();
+    void teardown();
+    void update();
+    void draw();
+    ValidationResult validate();
+
+    ~SceneTerrain();
+
+private:
+    SceneTerrainPrivate* priv_;
+};
+
 #endif

=== modified file 'src/wscript_build'
--- src/wscript_build	2012-06-25 11:40:03 +0000
+++ src/wscript_build	2012-06-29 12:27:16 +0000
@@ -1,11 +1,11 @@
-all_sources = bld.path.ant_glob('*.cpp')
+all_sources = bld.path.ant_glob('*.cpp scene-ideas/*.cc scene-terrain/*.cpp')
 common_sources = [f for f in all_sources if f.name.find('canvas-') == -1 and
                                             f.name.find('android') == -1 ]
 gl_sources = ['canvas-x11.cpp', 'canvas-x11-glx.cpp']
 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')
+includes = ['.', 'scene-ideas', 'scene-terrain']
 
 if bld.env.USE_GL:
     bld(
@@ -19,11 +19,11 @@
         )
     bld(
         features     = ['cxx', 'cprogram'],
-        source       = ideas_sources + common_sources + gl_sources,
+        source       = common_sources + gl_sources,
         target       = 'glmark2',
         use          = ['x11', 'gl', 'matrix', 'libpng12'],
         lib          = ['m', 'jpeg'],
-        includes     = ['.', 'scene-ideas'],
+        includes     = includes,
         defines      = ['USE_GL', 'USE_EXCEPTIONS']
         )
 
@@ -39,10 +39,10 @@
         )
     bld(
         features     = ['cxx', 'cprogram'],
-        source       = ideas_sources + common_sources + glesv2_sources,
+        source       = common_sources + glesv2_sources,
         target       = 'glmark2-es2',
         use          = ['x11', 'egl', 'glesv2', 'matrix-es2', 'libpng12'],
         lib          = ['m', 'dl', 'jpeg'],
-        includes     = ['.', 'scene-ideas'],
+        includes     = includes,
         defines      = ['USE_GLESv2', 'USE_EXCEPTIONS']
         )

