blob: b1d8dccb03824686c8a452b0b72c6652c3cee39e [file] [log] [blame]
//
// 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(0.0f, 3.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_["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");
}
SceneRefract::~SceneRefract()
{
delete priv_;
}
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();
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(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
DistanceRenderTarget::teardown()
{
program_.stop();
program_.release();
if (tex_) {
glDeleteTextures(1, &tex_);
tex_ = 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_, 0);
glViewport(0, 0, width_, height_);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glClear(GL_DEPTH_BUFFER_BIT);
}
void DistanceRenderTarget::disable()
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0, 0, canvas_width_, canvas_height_);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
}
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 materialDiffuse(0.0f, 0.0f, 0.0f, 0.0f);
static const vec4 lightColor(1.0, 1.0, 1.0, 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);
frg_source.add_const("MaterialDiffuse", materialDiffuse);
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, 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;
}
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);
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();
// 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();
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture_);
program_["DistanceMap"] = 0;
program_["ModelViewProjectionMatrix"] = mvp;
// 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;
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();
}