From patchwork Thu Nov 15 22:35:16 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Jesse Barker X-Patchwork-Id: 12879 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id B7D4123FC1 for ; Thu, 15 Nov 2012 22:35:23 +0000 (UTC) Received: from mail-ie0-f180.google.com (mail-ie0-f180.google.com [209.85.223.180]) by fiordland.canonical.com (Postfix) with ESMTP id 2A261A185BA for ; Thu, 15 Nov 2012 22:35:23 +0000 (UTC) Received: by mail-ie0-f180.google.com with SMTP id e10so2648497iej.11 for ; Thu, 15 Nov 2012 14:35:22 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-forwarded-to:x-forwarded-for:delivered-to:received-spf :content-type:mime-version:x-launchpad-project:x-launchpad-branch :x-launchpad-message-rationale:x-launchpad-branch-revision-number :x-launchpad-notification-type:to:from:subject:message-id:date :reply-to:sender:errors-to:precedence:x-generated-by :x-launchpad-hash:x-gm-message-state; bh=WHsfvuJpwKPoIv7upPxwKY04cCy1d0oXNClaf1PPNjs=; b=TZi/1KoOxPSkBtfEg+ymb/8HmpqtDGiaasV2ZU/EDwipkN/0Wxq/3WnJrQMxaPU0Vu zsaDg1CIfoJimUUWuZjz9WbAfwE6VOQwaeP+s9VBxH0U8KGtO8lBEK2mNtxH9iIqhtNX ySc+ZrG/go6eWR1bPQulq0zIEEsvl6Vd/QsRBeODXuq9LC8qNJ8zunT18KHU6u02mEEz 12mEwVLMNDvH6bEcqq2q53Ac1PHPVVdNMvAmUU0meGMqBCrAT9ohSN9MRNgBZIGrm28D k5wAaE2BkmX3egmywsQeou72TgAouZf2GHuDY+tDX//REpCS9Me6AYleOnvIz+5ZYD8r H5eQ== Received: by 10.50.213.34 with SMTP id np2mr1357813igc.57.1353018922516; Thu, 15 Nov 2012 14:35:22 -0800 (PST) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.50.67.148 with SMTP id n20csp864815igt; Thu, 15 Nov 2012 14:35:21 -0800 (PST) Received: by 10.204.13.28 with SMTP id z28mr1079318bkz.113.1353018920689; Thu, 15 Nov 2012 14:35:20 -0800 (PST) Received: from indium.canonical.com (indium.canonical.com. [91.189.90.7]) by mx.google.com with ESMTPS id hu5si22522971bkc.0.2012.11.15.14.35.16 (version=TLSv1/SSLv3 cipher=OTHER); Thu, 15 Nov 2012 14:35:20 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of bounces@canonical.com designates 91.189.90.7 as permitted sender) client-ip=91.189.90.7; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of bounces@canonical.com designates 91.189.90.7 as permitted sender) smtp.mail=bounces@canonical.com Received: from ackee.canonical.com ([91.189.89.26]) by indium.canonical.com with esmtp (Exim 4.71 #1 (Debian)) id 1TZ81k-0002SI-Ca for ; Thu, 15 Nov 2012 22:35:16 +0000 Received: from ackee.canonical.com (localhost [127.0.0.1]) by ackee.canonical.com (Postfix) with ESMTP id 3BAA2E00F4 for ; Thu, 15 Nov 2012 22:35:16 +0000 (UTC) MIME-Version: 1.0 X-Launchpad-Project: glmark2 X-Launchpad-Branch: ~glmark2-dev/glmark2/trunk X-Launchpad-Message-Rationale: Subscriber X-Launchpad-Branch-Revision-Number: 250 X-Launchpad-Notification-Type: branch-revision To: Linaro Patch Tracker From: noreply@launchpad.net Subject: [Branch ~glmark2-dev/glmark2/trunk] Rev 250: Merge of ~glmark2-dev/glmark2/shadow Message-Id: <20121115223516.7498.40892.launchpad@ackee.canonical.com> Date: Thu, 15 Nov 2012 22:35:16 -0000 Reply-To: noreply@launchpad.net Sender: bounces@canonical.com Errors-To: bounces@canonical.com Precedence: bulk X-Generated-By: Launchpad (canonical.com); Revision="16272"; Instance="launchpad-lazr.conf" X-Launchpad-Hash: ee844b854dbb99f1a7937716f22b12ad9d8e6406 X-Gm-Message-State: ALoCoQk0giyXOYRWudp4yM5xVEaUVQkLTIfZ+t+Z2ejs2T7bXngvBpJ/qOVYqYgPEh7CXLLqkI3K Merge authors: Jesse Barker (jesse-barker) Related merge proposals: https://code.launchpad.net/~glmark2-dev/glmark2/shadow/+merge/130911 proposed by: Jesse Barker (jesse-barker) review: Approve - Alexandros Frantzis (afrantzis) ------------------------------------------------------------ revno: 250 [merge] committer: Jesse Barker branch nick: trunk timestamp: Thu 2012-11-15 14:32:12 -0800 message: Merge of ~glmark2-dev/glmark2/shadow Adds SceneShadow to exercise shadow mapping. added: data/shaders/depth.frag data/shaders/depth.vert data/shaders/shadow.frag data/shaders/shadow.vert src/scene-shadow.cpp modified: src/android.cpp src/default-benchmarks.h src/main.cpp src/mesh.cpp src/scene.h --- lp:glmark2 https://code.launchpad.net/~glmark2-dev/glmark2/trunk You are subscribed to branch lp:glmark2. To unsubscribe from this branch go to https://code.launchpad.net/~glmark2-dev/glmark2/trunk/+edit-subscription === added file 'data/shaders/depth.frag' --- data/shaders/depth.frag 1970-01-01 00:00:00 +0000 +++ data/shaders/depth.frag 2012-10-22 16:55:55 +0000 @@ -0,0 +1,6 @@ +varying vec3 Normal; + +void main() +{ + gl_FragColor = vec4(Normal, 1.0); +} === added file 'data/shaders/depth.vert' --- data/shaders/depth.vert 1970-01-01 00:00:00 +0000 +++ data/shaders/depth.vert 2012-10-22 16:55:55 +0000 @@ -0,0 +1,12 @@ +attribute vec3 position; +attribute vec3 normal; + +uniform mat4 ModelViewProjectionMatrix; + +varying vec3 Normal; + +void main(void) +{ + Normal = normal; + gl_Position = ModelViewProjectionMatrix * vec4(position, 1.0); +} === added file 'data/shaders/shadow.frag' --- data/shaders/shadow.frag 1970-01-01 00:00:00 +0000 +++ data/shaders/shadow.frag 2012-10-22 21:30:36 +0000 @@ -0,0 +1,17 @@ +uniform sampler2D ShadowMap; + +varying vec4 Color; +varying vec4 ShadowCoord; + +void main() +{ + vec4 sc_perspective = ShadowCoord / ShadowCoord.w; + sc_perspective.z += 0.1505; + vec4 shadow_value = texture2D(ShadowMap, sc_perspective.st); + float light_distance = shadow_value.z; + float shadow = 1.0; + if (ShadowCoord.w > 0.0 && light_distance < sc_perspective.z) { + shadow = 0.5; + } + gl_FragColor = vec4(shadow * Color.rgb, 1.0); +} === added file 'data/shaders/shadow.vert' --- data/shaders/shadow.vert 1970-01-01 00:00:00 +0000 +++ data/shaders/shadow.vert 2012-10-22 21:30:36 +0000 @@ -0,0 +1,16 @@ +attribute vec2 position; + +uniform mat4 LightMatrix; +uniform mat4 ModelViewProjectionMatrix; + +varying vec4 ShadowCoord; +varying vec4 Color; + +void main() +{ + Color = MaterialDiffuse; + + vec4 pos4 = vec4(position, 0.0, 1.0); + ShadowCoord = LightMatrix * pos4; + gl_Position = ModelViewProjectionMatrix * pos4; +} === modified file 'src/android.cpp' --- src/android.cpp 2012-08-16 17:17:07 +0000 +++ src/android.cpp 2012-10-19 13:19:19 +0000 @@ -275,6 +275,7 @@ scenes.push_back(new SceneIdeas(canvas)); scenes.push_back(new SceneTerrain(canvas)); scenes.push_back(new SceneJellyfish(canvas)); + scenes.push_back(new SceneShadow(canvas)); } === modified file 'src/default-benchmarks.h' --- src/default-benchmarks.h 2012-07-19 08:44:26 +0000 +++ src/default-benchmarks.h 2012-10-19 13:19:19 +0000 @@ -63,6 +63,7 @@ benchmarks.push_back("ideas:speed=duration"); benchmarks.push_back("jellyfish"); benchmarks.push_back("terrain"); + benchmarks.push_back("shadow"); benchmarks.push_back("conditionals:vertex-steps=0:fragment-steps=0"); benchmarks.push_back("conditionals:vertex-steps=0:fragment-steps=5"); benchmarks.push_back("conditionals:vertex-steps=5:fragment-steps=0"); === modified file 'src/main.cpp' --- src/main.cpp 2012-07-17 08:47:15 +0000 +++ src/main.cpp 2012-10-19 13:19:19 +0000 @@ -63,6 +63,7 @@ scenes.push_back(new SceneIdeas(canvas)); scenes.push_back(new SceneTerrain(canvas)); scenes.push_back(new SceneJellyfish(canvas)); + scenes.push_back(new SceneShadow(canvas)); for (vector::const_iterator iter = scenes.begin(); iter != scenes.end(); === modified file 'src/mesh.cpp' --- src/mesh.cpp 2011-11-16 10:07:47 +0000 +++ src/mesh.cpp 2012-10-22 17:01:50 +0000 @@ -543,6 +543,8 @@ Mesh::render_array() { for (size_t i = 0; i < vertex_format_.size(); i++) { + if (attrib_locations_[i] < 0) + continue; glEnableVertexAttribArray(attrib_locations_[i]); glVertexAttribPointer(attrib_locations_[i], vertex_format_[i].first, GL_FLOAT, GL_FALSE, vertex_stride_, @@ -552,6 +554,8 @@ glDrawArrays(GL_TRIANGLES, 0, vertices_.size()); for (size_t i = 0; i < vertex_format_.size(); i++) { + if (attrib_locations_[i] < 0) + continue; glDisableVertexAttribArray(attrib_locations_[i]); } } @@ -566,6 +570,8 @@ Mesh::render_vbo() { for (size_t i = 0; i < vertex_format_.size(); i++) { + if (attrib_locations_[i] < 0) + continue; glEnableVertexAttribArray(attrib_locations_[i]); glBindBuffer(GL_ARRAY_BUFFER, vbos_[i]); glVertexAttribPointer(attrib_locations_[i], vertex_format_[i].first, @@ -576,6 +582,8 @@ glDrawArrays(GL_TRIANGLES, 0, vertices_.size()); for (size_t i = 0; i < vertex_format_.size(); i++) { + if (attrib_locations_[i] < 0) + continue; glDisableVertexAttribArray(attrib_locations_[i]); } } === added file 'src/scene-shadow.cpp' --- src/scene-shadow.cpp 1970-01-01 00:00:00 +0000 +++ src/scene-shadow.cpp 2012-10-22 21:43:11 +0000 @@ -0,0 +1,550 @@ +// +// 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 . +// +// Authors: +// Jesse Barker +// +#include "scene.h" +#include "model.h" +#include "util.h" +#include "log.h" +#include "shader-source.h" +#include "stack.h" + +using std::string; +using std::vector; +using std::map; +using LibMatrix::Stack4; +using LibMatrix::mat4; +using LibMatrix::vec4; +using LibMatrix::vec3; +using LibMatrix::vec2; + +static const vec4 lightPosition(0.0f, 3.0f, 2.0f, 1.0f); + +// +// 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 DepthRenderTarget +{ + Program program_; + unsigned int canvas_width_; + unsigned int canvas_height_; + unsigned int width_; + unsigned int height_; + unsigned int tex_; + unsigned int fbo_; +public: + DepthRenderTarget() : + canvas_width_(0), + canvas_height_(0), + width_(0), + height_(0), + tex_(0), + fbo_(0) {} + ~DepthRenderTarget() {} + bool setup(unsigned int width, unsigned int height); + void teardown(); + void enable(const mat4& mvp); + void disable(); + unsigned int texture() { return tex_; } + Program& program() { return program_; } +}; + +bool +DepthRenderTarget::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(1, &tex_); + glBindTexture(GL_TEXTURE_2D, tex_); + 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, 0); + + glGenFramebuffers(1, &fbo_); + glBindFramebuffer(GL_FRAMEBUFFER, fbo_); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, + tex_, 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 +DepthRenderTarget::teardown() +{ + program_.stop(); + program_.release(); + if (tex_) { + glDeleteTextures(1, &tex_); + tex_ = 0; + } + if (fbo_) { + glDeleteFramebuffers(1, &fbo_); + fbo_ = 0; + } +} + +void +DepthRenderTarget::enable(const mat4& mvp) +{ + program_.start(); + program_["ModelViewProjectionMatrix"] = mvp; + glBindFramebuffer(GL_FRAMEBUFFER, fbo_); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, + tex_, 0); + glViewport(0, 0, width_, height_); + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + glClear(GL_DEPTH_BUFFER_BIT); +} + +void DepthRenderTarget::disable() +{ + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glViewport(0, 0, canvas_width_, canvas_height_); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); +} + +// +// The actual shadow pass is really just a quad projected into the scene +// with the horse's shadow cast upon it. In the vertex stage, we compute +// a texture coordinate for the depth texture look-up by transforming the +// current vertex position using a matrix describing the light's view point. +// In the fragment stage, that coordinate is perspective corrected, and +// used to sample the depth texture. If the depth value for that fragment +// (effectively the distance from the light to the object at that point) +// is less than the Z component of that coordinate (effectively the distance +// from the light to the ground at that point) then that location is in shadow. +// +class GroundRenderer +{ + Program program_; + mat4 light_; + Stack4 modelview_; + mat4 projection_; + int positionLocation_; + unsigned int bufferObject_; + unsigned int texture_; + vector vertices_; + vector texcoords_; + +public: + GroundRenderer() : + positionLocation_(0), + bufferObject_(0) {} + ~GroundRenderer() {} + bool setup(const mat4& projection, unsigned int texture); + void teardown(); + void draw(); +}; + +bool +GroundRenderer::setup(const mat4& projection, unsigned int texture) +{ + projection_ = projection; + texture_ = texture; + + // Program set up + static const vec4 materialDiffuse(0.3f, 0.3f, 0.3f, 1.0f); + static const string vtx_shader_filename(GLMARK_DATA_PATH"/shaders/shadow.vert"); + static const string frg_shader_filename(GLMARK_DATA_PATH"/shaders/shadow.frag"); + ShaderSource vtx_source(vtx_shader_filename); + ShaderSource frg_source(frg_shader_filename); + + vtx_source.add_const("MaterialDiffuse", materialDiffuse); + + if (!Scene::load_shaders_from_strings(program_, vtx_source.str(), frg_source.str())) { + return false; + } + positionLocation_ = program_["position"].location(); + + // Set up the position data for our "quad". + vertices_.push_back(vec2(-1.0, -1.0)); + vertices_.push_back(vec2(1.0, -1.0)); + vertices_.push_back(vec2(-1.0, 1.0)); + vertices_.push_back(vec2(1.0, 1.0)); + + // Set up the VBO and stash our position data in it. + glGenBuffers(1, &bufferObject_); + glBindBuffer(GL_ARRAY_BUFFER, bufferObject_); + glBufferData(GL_ARRAY_BUFFER, vertices_.size() * sizeof(vec2), + &vertices_.front(), GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + // 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_; + light_ *= LibMatrix::Mat4::lookAt(lightPosition.x(), lightPosition.y(), lightPosition.z(), + 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0); + + return true; +} + +void +GroundRenderer::teardown() +{ + program_.stop(); + program_.release(); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glDeleteBuffers(1, &bufferObject_); + bufferObject_ = 0; + texture_= 0; + vertices_.clear(); +} + +void +GroundRenderer::draw() +{ + // Need to add uniforms for the shadow texture, and transformation to + // "lay the quad down". + modelview_.push(); + modelview_.translate(projection_[0][3], projection_[1][3] - 0.8, projection_[2][3] - 0.5); + modelview_.rotate(-85.0, 1.0, 0.0, 0.0); + modelview_.scale(2.0, 2.0, 2.0); + mat4 mvp(projection_); + mvp *= modelview_.getCurrent(); + + glBindBuffer(GL_ARRAY_BUFFER, bufferObject_); + program_.start(); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture_); + program_["ShadowMap"] = 0; + program_["LightMatrix"] = light_; + program_["ModelViewProjectionMatrix"] = mvp; + + glEnableVertexAttribArray(positionLocation_); + glVertexAttribPointer(positionLocation_, 2, GL_FLOAT, GL_FALSE, 0, 0); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glDisableVertexAttribArray(positionLocation_); + glBindTexture(GL_TEXTURE_2D, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + modelview_.pop(); +} + +class ShadowPrivate +{ + Canvas& canvas_; + DepthRenderTarget depthTarget_; + GroundRenderer ground_; + Program program_; + Stack4 modelview_; + Stack4 projection_; + Mesh mesh_; + vec3 centerVec_; + float radius_; + float rotation_; + float rotationSpeed_; + bool useVbo_; + +public: + ShadowPrivate(Canvas& canvas) : + canvas_(canvas), + radius_(0.0), + rotation_(0.0), + rotationSpeed_(36.0), + useVbo_(true) {} + ~ShadowPrivate() {} + + bool setup(map& options); + void teardown(); + void update(double elapsedTime); + void draw(); +}; + +bool +ShadowPrivate::setup(map& options) +{ + // Program object setup + static const string vtx_shader_filename(GLMARK_DATA_PATH"/shaders/light-basic.vert"); + static const string frg_shader_filename(GLMARK_DATA_PATH"/shaders/light-basic.frag"); + static const vec4 materialDiffuse(1.0f, 1.0f, 1.0f, 1.0f); + + ShaderSource vtx_source(vtx_shader_filename); + ShaderSource frg_source(frg_shader_filename); + + vtx_source.add_const("LightSourcePosition", lightPosition); + vtx_source.add_const("MaterialDiffuse", materialDiffuse); + + if (!Scene::load_shaders_from_strings(program_, vtx_source.str(), frg_source.str())) { + return false; + } + + // Model setup + Model::find_models(); + Model model; + bool modelLoaded = model.load("horse"); + + if(!modelLoaded) { + return false; + } + + model.calculate_normals(); + + // Mesh setup + vector > attribs; + attribs.push_back(std::pair(Model::AttribTypePosition, 3)); + attribs.push_back(std::pair(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(canvas_.width())/static_cast(canvas_.height())); + projection_.perspective(fovy, aspect, 2.0, 50.0); + + if (!depthTarget_.setup(canvas_.width(), canvas_.height())) { + Log::error("Failed to set up the render target for the depth pass\n"); + return false; + } + + if (!ground_.setup(projection_.getCurrent(), depthTarget_.texture())) { + Log::error("Failed to set up the ground renderer\n"); + return false; + } + + return true; +} + + +void +ShadowPrivate::teardown() +{ + depthTarget_.teardown(); + ground_.teardown(); + program_.stop(); + program_.release(); + mesh_.reset(); +} + +void +ShadowPrivate::update(double elapsedTime) +{ + rotation_ = rotationSpeed_ * elapsedTime; +} + +void +ShadowPrivate::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); + mat4 mvp(projection_.getCurrent()); + mvp *= modelview_.getCurrent(); + modelview_.pop(); + + // Enable the depth render target with our transformation and render. + depthTarget_.enable(mvp); + vector 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(); + + // Ground rendering using the above generated texture... + ground_.draw(); + + // 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); + mvp = projection_.getCurrent(); + mvp *= modelview_.getCurrent(); + + program_.start(); + program_["ModelViewProjectionMatrix"] = mvp; + + // Load the NormalMatrix uniform in the shader. The NormalMatrix is the + // inverse transpose of the model view matrix. + LibMatrix::mat4 normal_matrix(modelview_.getCurrent()); + normal_matrix.inverse().transpose(); + program_["NormalMatrix"] = normal_matrix; + 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(); +} + +SceneShadow::SceneShadow(Canvas& canvas) : + Scene(canvas, "shadow"), + priv_(0) +{ + 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"); +} + +SceneShadow::~SceneShadow() +{ + delete priv_; +} + +bool +SceneShadow::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 +SceneShadow::load() +{ + running_ = false; + return true; +} + +void +SceneShadow::unload() +{ +} + +bool +SceneShadow::setup() +{ + // If the scene isn't supported, don't bother to go through setup. + if (!supported(false) || !Scene::setup()) + { + return false; + } + + priv_ = new ShadowPrivate(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 +SceneShadow::teardown() +{ + // Add scene-specific teardown here + priv_->teardown(); + Scene::teardown(); +} + +void +SceneShadow::update() +{ + Scene::update(); + // Add scene-specific update here + priv_->update(lastUpdateTime_ - startTime_); +} + +void +SceneShadow::draw() +{ + priv_->draw(); +} + +Scene::ValidationResult +SceneShadow::validate() +{ + return Scene::ValidationUnknown; +} === modified file 'src/scene.h' --- src/scene.h 2012-08-15 09:45:06 +0000 +++ src/scene.h 2012-10-19 13:19:19 +0000 @@ -556,4 +556,22 @@ void draw(); ValidationResult validate(); }; + +class ShadowPrivate; +class SceneShadow : public Scene +{ + 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(); +}; + #endif