| /* |
| * Copyright © 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) |
| */ |
| #include "text-renderer.h" |
| #include "gl-headers.h" |
| #include "scene.h" |
| #include "shader-source.h" |
| #include "vec.h" |
| #include "mat.h" |
| #include "texture.h" |
| |
| using LibMatrix::vec2; |
| using LibMatrix::mat4; |
| |
| /* These are specific to the glyph texture atlas we are using */ |
| static const unsigned int texture_size(512); |
| static const vec2 glyph_size_pixels(29.0, 57.0); |
| static const vec2 glyph_size(glyph_size_pixels/texture_size); |
| |
| /****************** |
| * Public methods * |
| ******************/ |
| |
| /** |
| * TextRenderer default constructor. |
| */ |
| TextRenderer::TextRenderer(Canvas& canvas) : |
| canvas_(canvas), dirty_(false), position_(-1.0, -1.0), |
| texture_(0) |
| { |
| size(0.03); |
| |
| glGenBuffers(2, vbo_); |
| ShaderSource vtx_source(GLMARK_DATA_PATH"/shaders/text-renderer.vert"); |
| ShaderSource frg_source(GLMARK_DATA_PATH"/shaders/text-renderer.frag"); |
| |
| if (!Scene::load_shaders_from_strings(program_, vtx_source.str(), |
| frg_source.str())) |
| { |
| return; |
| } |
| |
| GLint prev_program; |
| glGetIntegerv(GL_CURRENT_PROGRAM, &prev_program); |
| |
| program_.start(); |
| program_["Texture0"] = 0; |
| |
| glUseProgram(prev_program); |
| |
| /* Load the glyph texture atlas */ |
| Texture::find_textures(); |
| Texture::load("glyph-atlas", &texture_, |
| GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR,0); |
| } |
| |
| TextRenderer::~TextRenderer() |
| { |
| glDeleteBuffers(2, vbo_); |
| glDeleteTextures(1, &texture_); |
| } |
| |
| /** |
| * Sets the text string to render. |
| * |
| * @param t the text string |
| */ |
| void |
| TextRenderer::text(const std::string& t) |
| { |
| if (text_ != t) { |
| text_ = t; |
| dirty_ = true; |
| } |
| } |
| |
| /** |
| * Sets the screen position to render at. |
| * |
| * @param t the position |
| */ |
| void |
| TextRenderer::position(const LibMatrix::vec2& p) |
| { |
| if (position_ != p) { |
| position_ = p; |
| dirty_ = true; |
| } |
| } |
| |
| /** |
| * Sets the size of each rendered glyph. |
| * |
| * The size corresponds to the width of each glyph |
| * in normalized screen coordinates. |
| * |
| * @param s the size of each glyph |
| */ |
| void |
| TextRenderer::size(float s) |
| { |
| if (size_.x() != s) { |
| /* Take into account the glyph and canvas aspect ratio */ |
| double canvas_aspect = |
| static_cast<double>(canvas_.width()) / canvas_.height(); |
| double glyph_aspect_rev = glyph_size.y() / glyph_size.x(); |
| size_ = vec2(s, s * canvas_aspect * glyph_aspect_rev); |
| dirty_ = true; |
| } |
| } |
| |
| /** |
| * Renders the text. |
| */ |
| void |
| TextRenderer::render() |
| { |
| /* Save state */ |
| GLint prev_program = 0; |
| GLint prev_array_buffer = 0; |
| GLint prev_elem_array_buffer = 0; |
| GLint prev_blend_src_rgb = 0; |
| GLint prev_blend_dst_rgb = 0; |
| GLint prev_blend_src_alpha = 0; |
| GLint prev_blend_dst_alpha = 0; |
| GLboolean prev_blend = GL_FALSE; |
| GLboolean prev_depth_test = GL_FALSE; |
| glGetIntegerv(GL_CURRENT_PROGRAM, &prev_program); |
| glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &prev_array_buffer); |
| glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &prev_elem_array_buffer); |
| glGetIntegerv(GL_BLEND_SRC_RGB, &prev_blend_src_rgb); |
| glGetIntegerv(GL_BLEND_DST_RGB, &prev_blend_dst_rgb); |
| glGetIntegerv(GL_BLEND_SRC_ALPHA, &prev_blend_src_alpha); |
| glGetIntegerv(GL_BLEND_DST_ALPHA, &prev_blend_dst_alpha); |
| glGetBooleanv(GL_BLEND, &prev_blend); |
| glGetBooleanv(GL_DEPTH_TEST, &prev_depth_test); |
| |
| /* Set new state */ |
| glActiveTexture(GL_TEXTURE0); |
| glBindTexture(GL_TEXTURE_2D, texture_); |
| glBindBuffer(GL_ARRAY_BUFFER, vbo_[0]); |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_[1]); |
| glEnable(GL_BLEND); |
| glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
| glDisable(GL_DEPTH_TEST); |
| |
| if (dirty_) { |
| create_geometry(); |
| dirty_ = false; |
| } |
| |
| program_.start(); |
| GLint position_loc = program_["position"].location(); |
| GLint texcoord_loc = program_["texcoord"].location(); |
| |
| /* Render */ |
| glEnableVertexAttribArray(position_loc); |
| glEnableVertexAttribArray(texcoord_loc); |
| glVertexAttribPointer(position_loc, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0); |
| glVertexAttribPointer(texcoord_loc, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), |
| reinterpret_cast<const GLvoid *>(2 * sizeof(float))); |
| |
| glDrawElements(GL_TRIANGLES, 6 * text_.length(), GL_UNSIGNED_SHORT, 0); |
| |
| glDisableVertexAttribArray(texcoord_loc); |
| glDisableVertexAttribArray(position_loc); |
| |
| /* Restore state */ |
| if (prev_depth_test == GL_TRUE) |
| glEnable(GL_DEPTH_TEST); |
| if (prev_blend == GL_FALSE) |
| glDisable(GL_BLEND); |
| glBlendFuncSeparate(prev_blend_src_rgb, prev_blend_dst_rgb, |
| prev_blend_src_alpha, prev_blend_dst_alpha); |
| glBindBuffer(GL_ARRAY_BUFFER, prev_array_buffer); |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, prev_elem_array_buffer); |
| glUseProgram(prev_program); |
| } |
| |
| /******************* |
| * Private methods * |
| *******************/ |
| |
| /** |
| * Creates the geometry needed to render the text. |
| * |
| * This method assumes that the text VBOs are properly bound. |
| */ |
| void |
| TextRenderer::create_geometry() |
| { |
| std::vector<float> array; |
| std::vector<GLushort> elem_array; |
| vec2 pos(position_); |
| |
| for (size_t i = 0; i < text_.size(); i++) { |
| vec2 texcoord = get_glyph_coords(text_[i]); |
| |
| /* Emit the elements for this glyph quad */ |
| /* Lower left */ |
| array.push_back(pos.x()); |
| array.push_back(pos.y()); |
| array.push_back(texcoord.x()); |
| array.push_back(texcoord.y()); |
| |
| /* Lower right */ |
| pos.x(pos.x() + size_.x()); |
| texcoord.x(texcoord.x() + glyph_size.x()); |
| array.push_back(pos.x()); |
| array.push_back(pos.y()); |
| array.push_back(texcoord.x()); |
| array.push_back(texcoord.y()); |
| |
| /* Upper left */ |
| pos.x(pos.x() - size_.x()); |
| pos.y(pos.y() + size_.y()); |
| texcoord.x(texcoord.x() - glyph_size.x()); |
| texcoord.y(texcoord.y() + glyph_size.y()); |
| array.push_back(pos.x()); |
| array.push_back(pos.y()); |
| array.push_back(texcoord.x()); |
| array.push_back(texcoord.y()); |
| |
| /* Upper right */ |
| pos.x(pos.x() + size_.x()); |
| texcoord.x(texcoord.x() + glyph_size.x()); |
| array.push_back(pos.x()); |
| array.push_back(pos.y()); |
| array.push_back(texcoord.x()); |
| array.push_back(texcoord.y()); |
| |
| /* Prepare for the next glyph */ |
| pos.y(pos.y() - size_.y()); |
| |
| /* Emit the element indices for this glyph quad */ |
| elem_array.push_back(4 * i); |
| elem_array.push_back(4 * i + 1); |
| elem_array.push_back(4 * i + 2); |
| elem_array.push_back(4 * i + 2); |
| elem_array.push_back(4 * i + 1); |
| elem_array.push_back(4 * i + 3); |
| } |
| |
| /* Load the data into the corresponding VBOs */ |
| glBufferData(GL_ARRAY_BUFFER, array.size() * sizeof(float), |
| &array[0], GL_DYNAMIC_DRAW); |
| |
| glBufferData(GL_ELEMENT_ARRAY_BUFFER, elem_array.size() * sizeof(GLushort), |
| &elem_array[0], GL_DYNAMIC_DRAW); |
| } |
| |
| /** |
| * Gets the texcoords of a glyph in the glyph texture atlas. |
| * |
| * @param c the character to get the glyph texcoords of |
| * |
| * @return the texcoords |
| */ |
| vec2 |
| TextRenderer::get_glyph_coords(char c) |
| { |
| static const unsigned int glyphs_per_row(texture_size / glyph_size_pixels.x()); |
| |
| /* We only support the ASCII printable characters */ |
| if (c < 32 || c >= 127) |
| c = 32; |
| |
| int n = c - 32; |
| int row = n / glyphs_per_row; |
| int col = n % glyphs_per_row; |
| |
| return vec2(col * glyph_size.x(), 1.0 - (row + 1) * glyph_size.y()); |
| } |
| |