Merge of lp:~glmark2-dev/glmark2/canvas-drm-rebranch

Adds CanvasDRM, which uses GBM and KMS APIs to manage surfaces and modesetting,
respectively.  Allows glmark2 to run on a console and take over the framebuffer
directly (no window/display manager).
diff --git a/data/shaders/light-refract.frag b/data/shaders/light-refract.frag
new file mode 100644
index 0000000..d1f1397
--- /dev/null
+++ b/data/shaders/light-refract.frag
@@ -0,0 +1,45 @@
+uniform sampler2D DistanceMap;
+uniform sampler2D NormalMap;
+uniform sampler2D ImageMap;
+
+varying vec3 vertex_normal;
+varying vec4 vertex_position;
+varying vec4 MapCoord;
+
+void main()
+{
+    const vec4 lightSpecular = vec4(0.8, 0.8, 0.8, 1.0);
+    const vec4 matSpecular = vec4(1.0, 1.0, 1.0, 1.0);
+    const float matShininess = 100.0;
+    const vec2 point_five = vec2(0.5);
+    // Need the normalized eye direction and surface normal vectors to
+    // compute the transmitted vector through the "front" surface of the object.
+    vec3 eye_direction = normalize(-vertex_position.xyz);
+    vec3 normalized_normal = normalize(vertex_normal);
+    vec3 front_refraction = refract(eye_direction, normalized_normal, RefractiveIndex);
+    // Find our best distance approximation through the object so we can
+    // project the transmitted vector to the back of the object to find
+    // the exit point.
+    vec3 mc_perspective = (MapCoord.xyz / MapCoord.w) + front_refraction;
+    vec2 dcoord = mc_perspective.st * point_five + point_five;
+    vec4 distance_value = texture2D(DistanceMap, dcoord);
+    vec3 back_position = vertex_position.xyz + front_refraction * distance_value.z;
+    // Use the exit point to index the map of back-side normals, and use the
+    // back-side position and normal to find the transmitted vector out of the
+    // object.
+    vec2 normcoord = back_position.st * point_five + point_five;
+    vec3 back_normal = texture2D(NormalMap, normcoord).xyz;
+    vec3 back_refraction = refract(back_position, back_normal, 1.0);
+    // Use the transmitted vector from the exit point to determine where
+    // the vector would intersect the environment (in this case a background
+    // image.
+    vec2 imagecoord = back_refraction.st * point_five + point_five;
+    vec4 texel = texture2D(ImageMap, imagecoord);
+    // Add in specular reflection, and we have our fragment value.
+    vec3 light_direction = normalize(vertex_position.xyz/vertex_position.w -
+                                     LightSourcePosition.xyz/LightSourcePosition.w);
+    vec3 reflection = reflect(light_direction, normalized_normal);
+    float specularTerm = pow(max(0.0, dot(reflection, eye_direction)), matShininess);
+    vec4 specular = (lightSpecular * matSpecular);
+    gl_FragColor = (specular * specularTerm) + texel;
+}
diff --git a/data/shaders/light-refract.vert b/data/shaders/light-refract.vert
new file mode 100644
index 0000000..9beeb9c
--- /dev/null
+++ b/data/shaders/light-refract.vert
@@ -0,0 +1,28 @@
+attribute vec3 position;
+attribute vec3 normal;
+
+uniform mat4 ModelViewProjectionMatrix;
+uniform mat4 NormalMatrix;
+uniform mat4 ModelViewMatrix;
+uniform mat4 LightMatrix;
+
+varying vec3 vertex_normal;
+varying vec4 vertex_position;
+varying vec4 MapCoord;
+
+void main(void)
+{
+    vec4 current_position = vec4(position, 1.0);
+
+    // Transform the normal to eye coordinates
+    vertex_normal = normalize(vec3(NormalMatrix * vec4(normal, 1.0)));
+
+    // Transform the current position to eye coordinates
+    vertex_position = ModelViewMatrix * current_position;
+
+    // Transform the current position for use as texture coordinates
+    MapCoord = LightMatrix * current_position;
+
+    // Transform the current position to clip coordinates
+    gl_Position = ModelViewProjectionMatrix * current_position;
+}
diff --git a/src/android.cpp b/src/android.cpp
index 53a17f7..d098626 100644
--- a/src/android.cpp
+++ b/src/android.cpp
@@ -276,6 +276,7 @@
     scenes.push_back(new SceneTerrain(canvas));
     scenes.push_back(new SceneJellyfish(canvas));
     scenes.push_back(new SceneShadow(canvas));
+    scenes.push_back(new SceneRefract(canvas));
 }
 
 
diff --git a/src/canvas-x11-egl.cpp b/src/canvas-x11-egl.cpp
index 0844b74..f7e548b 100644
--- a/src/canvas-x11-egl.cpp
+++ b/src/canvas-x11-egl.cpp
@@ -34,8 +34,7 @@
 bool
 CanvasX11EGL::init_gl_winsys()
 {
-    egl_.init_display(reinterpret_cast<EGLNativeDisplayType>(xdpy_),
-                      visual_config_);
+    egl_.init_display(xdpy_, visual_config_);
     return true;
 }
 
@@ -68,7 +67,7 @@
 bool
 CanvasX11EGL::make_current()
 {
-    egl_.init_surface(reinterpret_cast<EGLNativeWindowType>(xwin_));
+    egl_.init_surface(xwin_);
     if (!egl_.valid()) {
         Log::error("CanvasX11EGL: Invalid EGL state\n");
         return false;
diff --git a/src/default-benchmarks.h b/src/default-benchmarks.h
index 45f7672..3726a61 100644
--- a/src/default-benchmarks.h
+++ b/src/default-benchmarks.h
@@ -64,6 +64,7 @@
         benchmarks.push_back("jellyfish");
         benchmarks.push_back("terrain");
         benchmarks.push_back("shadow");
+        benchmarks.push_back("refract");
         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");
diff --git a/src/main.cpp b/src/main.cpp
index 8340f1e..4402a64 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -66,6 +66,7 @@
     scenes.push_back(new SceneTerrain(canvas));
     scenes.push_back(new SceneJellyfish(canvas));
     scenes.push_back(new SceneShadow(canvas));
+    scenes.push_back(new SceneRefract(canvas));
 
     for (vector<Scene*>::const_iterator iter = scenes.begin();
          iter != scenes.end();
diff --git a/src/scene-refract.cpp b/src/scene-refract.cpp
new file mode 100644
index 0000000..db0aec8
--- /dev/null
+++ b/src/scene-refract.cpp
@@ -0,0 +1,469 @@
+//
+// 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-refract.h"
+#include "model.h"
+#include "texture.h"
+#include "util.h"
+#include "log.h"
+#include "shader-source.h"
+
+using std::string;
+using std::vector;
+using std::map;
+using LibMatrix::mat4;
+using LibMatrix::vec4;
+using LibMatrix::vec3;
+
+static const vec4 lightPosition(1.0f, 1.0f, 2.0f, 1.0f);
+
+//
+// Public interfaces
+//
+
+SceneRefract::SceneRefract(Canvas& canvas) :
+    Scene(canvas, "refract"),
+    priv_(0)
+{
+    const ModelMap& modelMap = Model::find_models();
+    string optionValues;
+    for (ModelMap::const_iterator modelIt = modelMap.begin();
+         modelIt != modelMap.end();
+         modelIt++)
+    {
+        static bool doSeparator(false);
+        if (doSeparator)
+        {
+            optionValues += ",";
+        }
+        const string& curName = modelIt->first;
+        optionValues += curName;
+        doSeparator = true;
+    }
+    options_["model"] = Scene::Option("model", "bunny", "Which model to use",
+                                      optionValues);
+    optionValues = "";
+    const TextureMap& textureMap = Texture::find_textures();
+    for (TextureMap::const_iterator textureIt = textureMap.begin();
+         textureIt != textureMap.end();
+         textureIt++)
+    {
+        static bool doSeparator(false);
+        if (doSeparator)
+        {
+            optionValues += ",";
+        }
+        const string& curName = textureIt->first;
+        optionValues += curName;
+        doSeparator = true;
+    }
+    options_["texture"] = Scene::Option("texture", "nasa1", "Which texture to use",
+                                        optionValues);
+    options_["index"] = Scene::Option("index", "1.2",
+                                      "Index of refraction of the medium to simulate");
+    options_["use-vbo"] = Scene::Option("use-vbo", "true",
+                                        "Whether to use VBOs for rendering",
+                                        "false,true");
+    options_["interleave"] = Scene::Option("interleave", "false",
+                                           "Whether to interleave vertex attribute data",
+                                           "false,true");
+}
+
+bool
+SceneRefract::supported(bool show_errors)
+{
+    static const string oes_depth_texture("GL_OES_depth_texture");
+    static const string arb_depth_texture("GL_ARB_depth_texture");
+    if (!GLExtensions::support(oes_depth_texture) &&
+        !GLExtensions::support(arb_depth_texture)) {
+        if (show_errors) {
+            Log::error("We do not have the depth texture extension!!!\n");
+        }
+
+        return false;
+    }
+    return true;
+}
+
+bool
+SceneRefract::load()
+{
+    running_ = false;
+    return true;
+}
+
+void
+SceneRefract::unload()
+{
+}
+
+bool
+SceneRefract::setup()
+{
+    // If the scene isn't supported, don't bother to go through setup.
+    if (!supported(false) || !Scene::setup())
+    {
+        return false;
+    }
+
+    priv_ = new RefractPrivate(canvas_);
+    if (!priv_->setup(options_)) {
+        delete priv_;
+        priv_ = 0;
+        return false;
+    }
+
+    // Set core scene timing after actual initialization so we don't measure
+    // set up time.
+    startTime_ = Util::get_timestamp_us() / 1000000.0;
+    lastUpdateTime_ = startTime_;
+    running_ = true;
+
+    return true;
+}
+
+void
+SceneRefract::teardown()
+{
+    // Add scene-specific teardown here
+    priv_->teardown();
+    delete priv_;
+    Scene::teardown();
+}
+
+void
+SceneRefract::update()
+{
+    Scene::update();
+    // Add scene-specific update here
+    priv_->update(lastUpdateTime_ - startTime_);
+}
+
+void
+SceneRefract::draw()
+{
+    priv_->draw();
+}
+
+Scene::ValidationResult
+SceneRefract::validate()
+{
+    return Scene::ValidationUnknown;
+}
+
+//
+// Private interfaces
+//
+
+bool
+DistanceRenderTarget::setup(unsigned int width, unsigned int height)
+{
+    canvas_width_ = width;
+    canvas_height_ = height;
+    width_ = canvas_width_ * 2;
+    height_ = canvas_height_ * 2;
+
+    static const string vtx_shader_filename(GLMARK_DATA_PATH"/shaders/depth.vert");
+    static const string frg_shader_filename(GLMARK_DATA_PATH"/shaders/depth.frag");
+
+    ShaderSource vtx_source(vtx_shader_filename);
+    ShaderSource frg_source(frg_shader_filename);
+
+    if (!Scene::load_shaders_from_strings(program_, vtx_source.str(), frg_source.str())) {
+        return false;
+    }
+
+    glGenTextures(2, &tex_[0]);
+    glBindTexture(GL_TEXTURE_2D, tex_[DEPTH]);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width_, height_, 0,
+                 GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 0);
+    glBindTexture(GL_TEXTURE_2D, tex_[COLOR]);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width_, height_, 0,
+                 GL_RGBA, GL_UNSIGNED_BYTE, 0);
+    glGenerateMipmap(GL_TEXTURE_2D);
+    glBindTexture(GL_TEXTURE_2D, 0);
+
+    glGenFramebuffers(1, &fbo_);
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
+                           tex_[DEPTH], 0);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+                           tex_[COLOR], 0);
+    unsigned int status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+    if (status != GL_FRAMEBUFFER_COMPLETE) {
+        Log::error("DepthRenderState::setup: glCheckFramebufferStatus failed (0x%x)\n", status);
+        return false;
+    }
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+    return true;
+}
+
+void
+DistanceRenderTarget::teardown()
+{
+    program_.stop();
+    program_.release();
+    if (tex_) {
+        glDeleteTextures(2, &tex_[0]);
+        tex_[DEPTH] = tex_[COLOR] = 0;
+    }
+    if (fbo_) {
+        glDeleteFramebuffers(1, &fbo_);
+        fbo_ = 0;
+    }
+}
+
+void
+DistanceRenderTarget::enable(const mat4& mvp)
+{
+    program_.start();
+    program_["ModelViewProjectionMatrix"] = mvp;
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
+                           tex_[DEPTH], 0);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+                           tex_[COLOR], 0);
+    glViewport(0, 0, width_, height_);
+    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+    glCullFace(GL_FRONT);
+}
+
+void DistanceRenderTarget::disable()
+{
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
+    glViewport(0, 0, canvas_width_, canvas_height_);
+    glCullFace(GL_BACK);
+}
+
+bool
+RefractPrivate::setup(map<string, Scene::Option>& options)
+{
+    // Program object setup
+    static const string vtx_shader_filename(GLMARK_DATA_PATH"/shaders/light-refract.vert");
+    static const string frg_shader_filename(GLMARK_DATA_PATH"/shaders/light-refract.frag");
+    static const vec4 lightColor(0.4, 0.4, 0.4, 1.0);
+
+    ShaderSource vtx_source(vtx_shader_filename);
+    ShaderSource frg_source(frg_shader_filename);
+
+    frg_source.add_const("LightColor", lightColor);
+    frg_source.add_const("LightSourcePosition", lightPosition);
+    float refractive_index(Util::fromString<float>(options["index"].value));
+    frg_source.add_const("RefractiveIndex", refractive_index);
+
+    if (!Scene::load_shaders_from_strings(program_, vtx_source.str(), frg_source.str())) {
+        return false;
+    }
+
+    const string& whichTexture(options["texture"].value);
+    if (!Texture::load(whichTexture, &texture_, GL_LINEAR, GL_LINEAR, 0))
+        return false;
+
+    // Model setup
+    Model model;
+    const string& whichModel(options["model"].value);
+    bool modelLoaded = model.load(whichModel);
+
+    if(!modelLoaded)
+        return false;
+
+    // Now that we're successfully loaded, there are a few quirks about
+    // some of the known models that we need to account for.  The draw
+    // logic for the scene wants to rotate the model around the Y axis.
+    // Most of our models are described this way.  Some need adjustment
+    // (an additional rotation that gets the model into the correct
+    // orientation).
+    //
+    // Here's a summary:
+    //
+    // Angel rotates around the Y axis
+    // Armadillo rotates around the Y axis
+    // Buddha rotates around the X axis
+    // Bunny rotates around the Y axis
+    // Dragon rotates around the X axis
+    // Horse rotates around the Y axis
+    if (whichModel == "buddha" || whichModel == "dragon")
+    {
+        orientModel_ = true;
+        orientationAngle_ = -90.0;
+        orientationVec_ = vec3(1.0, 0.0, 0.0);
+    }
+    else if (whichModel == "armadillo")
+    {
+        orientModel_ = true;
+        orientationAngle_ = 180.0; 
+        orientationVec_ = vec3(0.0, 1.0, 0.0);
+    }
+
+    model.calculate_normals();
+
+    // Mesh setup
+    vector<std::pair<Model::AttribType, int> > attribs;
+    attribs.push_back(std::pair<Model::AttribType, int>(Model::AttribTypePosition, 3));
+    attribs.push_back(std::pair<Model::AttribType, int>(Model::AttribTypeNormal, 3));
+    model.convert_to_mesh(mesh_, attribs);
+
+    useVbo_ = (options["use-vbo"].value == "true");
+    bool interleave = (options["interleave"].value == "true");
+    mesh_.vbo_update_method(Mesh::VBOUpdateMethodMap);
+    mesh_.interleave(interleave);
+
+    if (useVbo_) {
+        mesh_.build_vbo();
+    }
+    else {
+        mesh_.build_array();
+    }
+
+    // Calculate a projection matrix that is a good fit for the model
+    vec3 maxVec = model.maxVec();
+    vec3 minVec = model.minVec();
+    vec3 diffVec = maxVec - minVec;
+    centerVec_ = maxVec + minVec;
+    centerVec_ /= 2.0;
+    float diameter = diffVec.length();
+    radius_ = diameter / 2;
+    float fovy = 2.0 * atanf(radius_ / (2.0 + radius_));
+    fovy /= M_PI;
+    fovy *= 180.0;
+    float aspect(static_cast<float>(canvas_.width())/static_cast<float>(canvas_.height()));
+    projection_.perspective(fovy, aspect, 2.0, 2.0 + diameter);
+
+    // Set up the light matrix with a bias that will convert values
+    // in the range of [-1, 1] to [0, 1)], then add in the projection
+    // and the "look at" matrix from the light position.
+    light_ *= LibMatrix::Mat4::translate(0.5, 0.5, 0.5);
+    light_ *= LibMatrix::Mat4::scale(0.5, 0.5, 0.5);
+    light_ *= projection_.getCurrent();
+    light_ *= LibMatrix::Mat4::lookAt(lightPosition.x(), lightPosition.y(), lightPosition.z(),
+                                      0.0, 0.0, 0.0,
+                                      0.0, 1.0, 0.0);
+
+    if (!depthTarget_.setup(canvas_.width(), canvas_.height())) {
+        Log::error("Failed to set up the render target for the depth pass\n");
+        return false;
+    }
+
+    return true;
+}
+void
+RefractPrivate::teardown()
+{
+    depthTarget_.teardown();
+    program_.stop();
+    program_.release();
+    mesh_.reset();
+}
+
+void
+RefractPrivate::update(double elapsedTime)
+{
+    rotation_ = rotationSpeed_ * elapsedTime;
+}
+
+void
+RefractPrivate::draw()
+{
+    // To perform the depth pass, set up the model-view transformation so
+    // that we're looking at the horse from the light position.  That will
+    // give us the appropriate view for the shadow.
+    modelview_.push();
+    modelview_.loadIdentity();
+    modelview_.lookAt(lightPosition.x(), lightPosition.y(), lightPosition.z(),
+                      0.0, 0.0, 0.0,
+                      0.0, 1.0, 0.0);
+    modelview_.rotate(rotation_, 0.0f, 1.0f, 0.0f);
+    if (orientModel_)
+    {
+        modelview_.rotate(orientationAngle_, orientationVec_.x(), orientationVec_.y(), orientationVec_.z());
+    }
+    mat4 mvp(projection_.getCurrent());
+    mvp *= modelview_.getCurrent();
+    modelview_.pop();
+
+    // Enable the depth render target with our transformation and render.
+    depthTarget_.enable(mvp);
+    vector<GLint> attrib_locations;
+    attrib_locations.push_back(depthTarget_.program()["position"].location());
+    attrib_locations.push_back(depthTarget_.program()["normal"].location());
+    mesh_.set_attrib_locations(attrib_locations);
+    if (useVbo_) {
+        mesh_.render_vbo();
+    }
+    else {
+        mesh_.render_array();
+    }
+    depthTarget_.disable();
+
+    // Draw the "normal" view of the horse
+    modelview_.push();
+    modelview_.translate(-centerVec_.x(), -centerVec_.y(), -(centerVec_.z() + 2.0 + radius_));
+    modelview_.rotate(rotation_, 0.0f, 1.0f, 0.0f);
+    if (orientModel_)
+    {
+        modelview_.rotate(orientationAngle_, orientationVec_.x(), orientationVec_.y(), orientationVec_.z());
+    }
+    mvp = projection_.getCurrent();
+    mvp *= modelview_.getCurrent();
+
+    program_.start();
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, depthTarget_.depthTexture());
+    program_["DistanceMap"] = 0;
+    glActiveTexture(GL_TEXTURE1);
+    glBindTexture(GL_TEXTURE_2D, depthTarget_.colorTexture());
+    program_["NormalMap"] = 1;
+    glActiveTexture(GL_TEXTURE2);
+    glBindTexture(GL_TEXTURE_2D, texture_);
+    program_["ImageMap"] = 2;
+    // Load both the modelview*projection as well as the modelview matrix itself
+    program_["ModelViewProjectionMatrix"] = mvp;
+    program_["ModelViewMatrix"] = modelview_.getCurrent();
+    // Load the NormalMatrix uniform in the shader. The NormalMatrix is the
+    // inverse transpose of the model view matrix.
+    mat4 normal_matrix(modelview_.getCurrent());
+    normal_matrix.inverse().transpose();
+    program_["NormalMatrix"] = normal_matrix;
+    program_["LightMatrix"] = light_;
+    attrib_locations.clear();
+    attrib_locations.push_back(program_["position"].location());
+    attrib_locations.push_back(program_["normal"].location());
+    mesh_.set_attrib_locations(attrib_locations);
+    if (useVbo_) {
+        mesh_.render_vbo();
+    }
+    else {
+        mesh_.render_array();
+    }
+
+    // Per-frame cleanup
+    modelview_.pop();
+}
+
diff --git a/src/scene-refract.h b/src/scene-refract.h
new file mode 100644
index 0000000..660678b
--- /dev/null
+++ b/src/scene-refract.h
@@ -0,0 +1,106 @@
+//
+// 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 SCENE_REFRACT_
+#define SCENE_REFRACT_
+
+#include "scene.h"
+#include "stack.h"
+
+//
+// To create a shadow map, we need a framebuffer object set up for a 
+// depth-only pass.  The render target can then be bound as a texture,
+// and the depth values sampled from that texture can be used in the
+// distance-from-light computations when rendering the shadow on the
+// ground below the rendered object.
+//
+class DistanceRenderTarget
+{
+    enum
+    {
+        DEPTH = 0,
+        COLOR
+    };
+    Program program_;
+    unsigned int canvas_width_;
+    unsigned int canvas_height_;
+    unsigned int width_;
+    unsigned int height_;
+    unsigned int tex_[2];
+    unsigned int fbo_;
+public:
+    DistanceRenderTarget() :
+        canvas_width_(0),
+        canvas_height_(0),
+        width_(0),
+        height_(0),
+        fbo_(0)
+    {
+        tex_[DEPTH] = tex_[COLOR] = 0;
+    }
+    ~DistanceRenderTarget() {}
+    bool setup(unsigned int width, unsigned int height);
+    void teardown();
+    void enable(const LibMatrix::mat4& mvp);
+    void disable();
+    unsigned int depthTexture() { return tex_[DEPTH]; }
+    unsigned int colorTexture() { return tex_[COLOR]; }
+    Program& program() { return program_; }
+};
+
+class RefractPrivate
+{
+    Canvas& canvas_;
+    DistanceRenderTarget depthTarget_;
+    Program program_;
+    LibMatrix::Stack4 modelview_;
+    LibMatrix::Stack4 projection_;
+    LibMatrix::mat4 light_;
+    Mesh mesh_;
+    LibMatrix::vec3 centerVec_;
+    bool orientModel_;
+    float orientationAngle_;
+    LibMatrix::vec3 orientationVec_;
+    float radius_;
+    float rotation_;
+    float rotationSpeed_;
+    unsigned int texture_;
+    bool useVbo_;
+    
+public:
+    RefractPrivate(Canvas& canvas) :
+        canvas_(canvas),
+        orientModel_(false),
+        orientationAngle_(0.0),
+        radius_(0.0),
+        rotation_(0.0),
+        rotationSpeed_(36.0),
+        texture_(0),
+        useVbo_(true) {}
+    ~RefractPrivate() {}
+
+    bool setup(std::map<std::string, Scene::Option>& options);
+    void teardown();
+    void update(double elapsedTime);
+    void draw();
+};
+
+#endif // SCENE_REFRACT_
diff --git a/src/scene-shadow.cpp b/src/scene-shadow.cpp
index 4fd27c7..dbad8a4 100644
--- a/src/scene-shadow.cpp
+++ b/src/scene-shadow.cpp
@@ -463,11 +463,6 @@
                                            "false,true");
 }
 
-SceneShadow::~SceneShadow()
-{
-    delete priv_;
-}
-
 bool
 SceneShadow::supported(bool show_errors)
 {
@@ -526,6 +521,7 @@
 {
     // Add scene-specific teardown here
     priv_->teardown();
+    delete priv_;
     Scene::teardown();
 }
 
diff --git a/src/scene.h b/src/scene.h
index dd77ba9..8c4d752 100644
--- a/src/scene.h
+++ b/src/scene.h
@@ -563,7 +563,22 @@
     ShadowPrivate* priv_;
 public:
     SceneShadow(Canvas& canvas);
-    ~SceneShadow();
+    bool supported(bool show_errors);
+    bool load();
+    void unload();
+    bool setup();
+    void teardown();
+    void update();
+    void draw();
+    ValidationResult validate();
+};
+
+class RefractPrivate;
+class SceneRefract : public Scene
+{
+    RefractPrivate* priv_;
+public:
+    SceneRefract(Canvas& canvas);
     bool supported(bool show_errors);
     bool load();
     void unload();