| /* |
| * Copyright © 2010-2011 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 (glmark2) |
| * Jesse Barker |
| */ |
| #include "scene.h" |
| #include "log.h" |
| #include "mat.h" |
| #include "stack.h" |
| #include "shader-source.h" |
| #include "util.h" |
| #include "gl-headers.h" |
| #include <cmath> |
| |
| /*********************** |
| * Wave implementation * |
| ***********************/ |
| |
| /** |
| * A callback used to set up the grid by the Wave class. |
| * It is called for each "quad" of the grid. |
| */ |
| static void |
| wave_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) |
| { |
| // These parameters are unused in this instance of a virtual callback |
| // function. |
| static_cast<void>(x); |
| static_cast<void>(y); |
| static_cast<void>(n_x); |
| static_cast<void>(n_y); |
| |
| /* |
| * Order matters here, so that Wave::vertex_length_index() can work. |
| * Vertices of the triangles at index i that belong to length index i |
| * are even, those that belong to i + 1 are odd. |
| */ |
| const LibMatrix::vec3* t[] = { |
| &ll, &ur, &ul, &ur, &ll, &lr |
| }; |
| |
| for (int i = 0; i < 6; i++) { |
| mesh.next_vertex(); |
| /* |
| * Set the vertex position and the three vertex positions |
| * of the triangle this vertex belongs to. |
| */ |
| mesh.set_attrib(0, *t[i]); |
| mesh.set_attrib(1, *t[3 * (i / 3)]); |
| mesh.set_attrib(2, *t[3 * (i / 3) + 1]); |
| mesh.set_attrib(3, *t[3 * (i / 3) + 2]); |
| } |
| } |
| |
| /** |
| * Renders a grid mesh modulated by a sine wave |
| */ |
| class WaveMesh |
| { |
| public: |
| /** |
| * Creates a wave mesh. |
| * |
| * @param length the total length of the grid (in model coordinates) |
| * @param width the total width of the grid (in model coordinates) |
| * @param nlength the number of length-wise grid subdivisions |
| * @param nwidth the number of width-wise grid subdivisions |
| * @param wavelength the wave length as a proportion of the length |
| * @param duty_cycle the duty cycle () |
| */ |
| WaveMesh(double length, double width, size_t nlength, size_t nwidth, |
| double wavelength, double duty_cycle) : |
| length_(length), width_(width), nlength_(nlength), nwidth_(nwidth), |
| wave_k_(2 * M_PI / (wavelength * length)), |
| wave_period_(2.0 * M_PI / wave_k_), |
| wave_full_period_(wave_period_ / duty_cycle), |
| wave_velocity_(0.1 * length), displacement_(nlength + 1) |
| { |
| create_program(); |
| create_mesh(); |
| } |
| |
| |
| ~WaveMesh() { reset(); } |
| |
| /** |
| * Updates the state of a wave mesh. |
| * |
| * @param elapsed the time elapsed since the beginning of the rendering |
| */ |
| void update(double elapsed) |
| { |
| std::vector<std::vector<float> >& vertices(mesh_.vertices()); |
| |
| /* Figure out which length index ranges need update */ |
| std::vector<std::pair<size_t, size_t> > ranges; |
| |
| for (size_t n = 0; n <= nlength_; n++) { |
| double d(displacement(n, elapsed)); |
| |
| if (d != displacement_[n]) { |
| if (ranges.size() > 0 && ranges.back().second == n - 1) { |
| ranges.back().second = n; |
| } |
| else { |
| ranges.push_back( |
| std::pair<size_t, size_t>(n > 0 ? n - 1 : 0, n) |
| ); |
| } |
| } |
| |
| displacement_[n] = d; |
| } |
| |
| /* Update the vertex data of the changed ranges */ |
| for (std::vector<std::pair<size_t, size_t> >::iterator iter = ranges.begin(); |
| iter != ranges.end(); |
| iter++) |
| { |
| /* First vertex of length index range */ |
| size_t vstart(iter->first * nwidth_ * 6 + (iter->first % 2)); |
| /* |
| * First vertex not included in the range. We should also update all |
| * vertices of triangles touching index i. |
| */ |
| size_t vend((iter->second + (iter->second < nlength_)) * nwidth_ * 6); |
| |
| for (size_t v = vstart; v < vend; v++) { |
| size_t vt = 3 * (v / 3); |
| vertices[v][0 * 3 + 2] = displacement_[vertex_length_index(v)]; |
| vertices[v][1 * 3 + 2] = displacement_[vertex_length_index(vt)]; |
| vertices[v][2 * 3 + 2] = displacement_[vertex_length_index(vt + 1)]; |
| vertices[v][3 * 3 + 2] = displacement_[vertex_length_index(vt + 2)]; |
| } |
| |
| /* Update pair with actual vertex range */ |
| iter->first = vstart; |
| iter->second = vend - 1; |
| } |
| |
| mesh_.update_vbo(ranges); |
| } |
| |
| Mesh& mesh() { return mesh_; } |
| Program& program() { return program_; } |
| |
| void reset() |
| { |
| program_.stop(); |
| program_.release(); |
| mesh_.reset(); |
| } |
| |
| private: |
| Mesh mesh_; |
| Program program_; |
| double length_; |
| double width_; |
| size_t nlength_; |
| size_t nwidth_; |
| /* Wave parameters */ |
| double wave_k_; |
| double wave_period_; |
| double wave_full_period_; |
| double wave_fill_; |
| double wave_velocity_; |
| |
| std::vector<double> displacement_; |
| |
| /** |
| * Calculates the length index of a vertex. |
| */ |
| size_t vertex_length_index(size_t v) |
| { |
| return v / (6 * nwidth_) + (v % 2); |
| } |
| |
| /** |
| * The sine wave function with duty-cycle. |
| * |
| * @param x the space coordinate |
| * |
| * @return the operation error code |
| */ |
| double wave_func(double x) |
| { |
| double r(fmod(x, wave_full_period_)); |
| if (r < 0) |
| r += wave_full_period_; |
| |
| /* |
| * Return either the sine value or 0.0, depending on the |
| * wave duty cycle. |
| */ |
| if (r > wave_period_) |
| { |
| return 0; |
| } |
| else |
| { |
| return 0.2 * std::sin(wave_k_ * r); |
| } |
| } |
| |
| /** |
| * Calculates the displacement of the wave. |
| * |
| * @param n the length index |
| * @param elapsed the time elapsed since the beginning of the rendering |
| * |
| * @return the displacement at point n at time elapsed |
| */ |
| double displacement(size_t n, double elapsed) |
| { |
| double x(n * length_ / nlength_); |
| |
| return wave_func(x - wave_velocity_ * elapsed); |
| } |
| |
| /** |
| * Creates the GL shader program. |
| */ |
| void create_program() |
| { |
| /* Set up shaders */ |
| static const std::string vtx_shader_filename( |
| GLMARK_DATA_PATH"/shaders/buffer-wireframe.vert"); |
| static const std::string frg_shader_filename( |
| GLMARK_DATA_PATH"/shaders/buffer-wireframe.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; |
| } |
| } |
| |
| /** |
| * Creates the grid mesh. |
| */ |
| void create_mesh() |
| { |
| /* |
| * We need to pass the positions of all vertex of the triangle |
| * in order to draw the wireframe. |
| */ |
| std::vector<int> vertex_format; |
| vertex_format.push_back(3); // Position of vertex |
| vertex_format.push_back(3); // Position of triangle vertex 0 |
| vertex_format.push_back(3); // Position of triangle vertex 1 |
| vertex_format.push_back(3); // Position of triangle vertex 2 |
| mesh_.set_vertex_format(vertex_format); |
| |
| std::vector<GLint> attrib_locations; |
| attrib_locations.push_back(program_["position"].location()); |
| attrib_locations.push_back(program_["tvertex0"].location()); |
| attrib_locations.push_back(program_["tvertex1"].location()); |
| attrib_locations.push_back(program_["tvertex2"].location()); |
| mesh_.set_attrib_locations(attrib_locations); |
| |
| mesh_.make_grid(nlength_, nwidth_, length_, width_, |
| 0.0, wave_grid_conf); |
| } |
| |
| }; |
| |
| /****************************** |
| * SceneBuffer implementation * |
| ******************************/ |
| |
| struct SceneBufferPrivate { |
| WaveMesh *wave; |
| SceneBufferPrivate() : wave(0) {} |
| ~SceneBufferPrivate() { delete wave; } |
| }; |
| |
| SceneBuffer::SceneBuffer(Canvas &pCanvas) : |
| Scene(pCanvas, "buffer") |
| { |
| priv_ = new SceneBufferPrivate(); |
| options_["interleave"] = Scene::Option("interleave", "false", |
| "Whether to interleave vertex attribute data", |
| "false,true"); |
| options_["update-method"] = Scene::Option("update-method", "map", |
| "Which method to use to update vertex data", |
| "map,subdata"); |
| options_["update-fraction"] = Scene::Option("update-fraction", "1.0", |
| "The fraction of the mesh length that is updated at every iteration (0.0-1.0)"); |
| options_["update-dispersion"] = Scene::Option("update-dispersion", "0.0", |
| "How dispersed the updates are [0.0 - 1.0]"); |
| options_["columns"] = Scene::Option("columns", "100", |
| "The number of mesh subdivisions length-wise"); |
| options_["rows"] = Scene::Option("rows", "20", |
| "The number of mesh subdisivisions width-wise"); |
| options_["buffer-usage"] = Scene::Option("buffer-usage", "static", |
| "How the buffer will be used", |
| "static,stream,dynamic"); |
| } |
| |
| SceneBuffer::~SceneBuffer() |
| { |
| delete priv_; |
| } |
| |
| bool |
| SceneBuffer::supported(bool show_errors) |
| { |
| if (options_["update-method"].value != "subdata" && |
| (GLExtensions::MapBuffer == 0 || GLExtensions::UnmapBuffer == 0)) |
| { |
| if (show_errors) { |
| Log::error("Requested MapBuffer VBO update method but GL_OES_mapbuffer" |
| " is not supported!\n"); |
| } |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool |
| SceneBuffer::load() |
| { |
| running_ = false; |
| |
| return true; |
| } |
| |
| void |
| SceneBuffer::unload() |
| { |
| } |
| |
| bool |
| SceneBuffer::setup() |
| { |
| using LibMatrix::vec3; |
| |
| if (!Scene::setup()) |
| return false; |
| |
| bool interleave = (options_["interleave"].value == "true"); |
| Mesh::VBOUpdateMethod update_method; |
| Mesh::VBOUsage usage; |
| double update_fraction; |
| double update_dispersion; |
| size_t nlength; |
| size_t nwidth; |
| |
| if (options_["update-method"].value == "map") |
| update_method = Mesh::VBOUpdateMethodMap; |
| else if (options_["update-method"].value == "subdata") |
| update_method = Mesh::VBOUpdateMethodSubData; |
| else |
| update_method = Mesh::VBOUpdateMethodMap; |
| |
| if (options_["buffer-usage"].value == "static") |
| usage = Mesh::VBOUsageStatic; |
| else if (options_["buffer-usage"].value == "stream") |
| usage = Mesh::VBOUsageStream; |
| else |
| usage = Mesh::VBOUsageDynamic; |
| |
| update_fraction = Util::fromString<double>(options_["update-fraction"].value); |
| update_dispersion = Util::fromString<double>(options_["update-dispersion"].value); |
| nlength = Util::fromString<size_t>(options_["columns"].value); |
| nwidth = Util::fromString<size_t>(options_["rows"].value); |
| |
| |
| priv_->wave = new WaveMesh(5.0, 2.0, nlength, nwidth, |
| update_fraction * (1.0 - update_dispersion + 0.0001), |
| update_fraction); |
| |
| priv_->wave->mesh().interleave(interleave); |
| priv_->wave->mesh().vbo_update_method(update_method); |
| priv_->wave->mesh().vbo_usage(usage); |
| priv_->wave->mesh().build_vbo(); |
| |
| priv_->wave->program().start(); |
| priv_->wave->program()["Viewport"] = LibMatrix::vec2(canvas_.width(), canvas_.height()); |
| |
| glDisable(GL_CULL_FACE); |
| |
| currentFrame_ = 0; |
| running_ = true; |
| startTime_ = Util::get_timestamp_us() / 1000000.0; |
| lastUpdateTime_ = startTime_; |
| |
| return true; |
| } |
| |
| void |
| SceneBuffer::teardown() |
| { |
| delete priv_->wave; |
| priv_->wave = 0; |
| |
| glEnable(GL_CULL_FACE); |
| |
| Scene::teardown(); |
| } |
| |
| void |
| SceneBuffer::update() |
| { |
| Scene::update(); |
| |
| double elapsed_time = lastUpdateTime_ - startTime_; |
| |
| priv_->wave->update(elapsed_time); |
| } |
| |
| void |
| SceneBuffer::draw() |
| { |
| LibMatrix::Stack4 model_view; |
| |
| // Load the ModelViewProjectionMatrix uniform in the shader |
| LibMatrix::mat4 model_view_proj(canvas_.projection()); |
| model_view.translate(0.0, 0.0, -4.0); |
| model_view.rotate(45.0, -1.0, 0.0, 0.0); |
| model_view_proj *= model_view.getCurrent(); |
| |
| priv_->wave->program()["ModelViewProjectionMatrix"] = model_view_proj; |
| |
| priv_->wave->mesh().render_vbo(); |
| } |
| |
| Scene::ValidationResult |
| SceneBuffer::validate() |
| { |
| static const double radius_3d(std::sqrt(3.0 * 2.0 * 2.0)); |
| |
| Canvas::Pixel ref(0x34, 0x99, 0xd7, 0xff); |
| Canvas::Pixel pixel = canvas_.read_pixel(402, 189); |
| |
| double dist = pixel.distance_rgb(ref); |
| if (dist < radius_3d + 0.01) { |
| return Scene::ValidationSuccess; |
| } |
| else { |
| Log::debug("Validation failed! Expected: 0x%x Actual: 0x%x Distance: %f\n", |
| ref.to_le32(), pixel.to_le32(), dist); |
| return Scene::ValidationFailure; |
| } |
| |
| return Scene::ValidationUnknown; |
| } |