diff options
Diffstat (limited to 'src/scene-refract.cpp')
-rw-r--r-- | src/scene-refract.cpp | 469 |
1 files changed, 469 insertions, 0 deletions
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(); +} + |