/* ---------------------------------------------------------------------------- * spritebatch.cpp * * Copyright (c) 2017 Yann Herklotz Grave -- MIT License * See file LICENSE for more details * ---------------------------------------------------------------------------- */ #include "spritebatch.hpp" #include #include namespace yage { const int SpriteBatch::NUM_VERTICES; Glyph::Glyph(GLuint texture, float depth, const Vertex &top_left, const Vertex &top_right, const Vertex &bottom_right, const Vertex &bottom_left) : texture_(texture), depth_(depth), top_left_(top_left), top_right_(top_right), bottom_right_(bottom_right), bottom_left_(bottom_left) {} RenderBatch::RenderBatch(GLint offset, GLsizei num_vertices, GLuint texture) : num_vertices_(num_vertices), offset_(offset), texture_(texture) {} SpriteBatch::SpriteBatch() {} SpriteBatch::~SpriteBatch() { if(vao_!=0) glDeleteVertexArrays(1, &vao_); if(vbo_!=0) glDeleteVertexArrays(1, &vbo_); } void SpriteBatch::init() { createVertexArray(); } void SpriteBatch::begin() { glyphs_.clear(); glyph_ptrs_.clear(); render_batches_.clear(); } void SpriteBatch::end() { sortGlyphs(); createRenderBatches(); } void SpriteBatch::draw(const glm::vec4 &destination_rect, const glm::vec4 &uv_rect, GLuint texture, const Color &color, float depth) { Vertex top_left, top_right, bottom_right, bottom_left; top_left.color=color; top_left.setPosition(destination_rect.x, destination_rect.y+destination_rect.w); top_left.setUv(uv_rect.x, uv_rect.y+uv_rect.w); top_right.color=color; top_right.setPosition(destination_rect.x+destination_rect.z, destination_rect.y+destination_rect.w); top_right.setUv(uv_rect.x+uv_rect.z, uv_rect.y+uv_rect.w); bottom_right.color=color; bottom_right.setPosition(destination_rect.x+destination_rect.z, destination_rect.y); bottom_right.setUv(uv_rect.x+uv_rect.z, uv_rect.y); bottom_left.color=color; bottom_left.setPosition(destination_rect.x, destination_rect.y); bottom_left.setUv(uv_rect.x, uv_rect.y); // deal with fragmenting by creating vector of pointers glyphs_.emplace_back(texture, depth, top_left, top_right, bottom_right, bottom_left); glyph_ptrs_.push_back(&glyphs_.back()); } void SpriteBatch::render() { glBindVertexArray(vao_); for(auto &&batch : render_batches_) { glBindTexture(GL_TEXTURE_2D, batch.texture()); glDrawArrays(GL_TRIANGLES, batch.offset(), batch.num_vertices()); } glBindVertexArray(0); } void SpriteBatch::createVertexArray() { if(vao_==0) { glGenVertexArrays(1, &vao_); if(vao_==0) throw std::runtime_error("glGenVertexArrays failed"); } // bind vertex array object glBindVertexArray(vao_); if(vbo_==0) { glGenBuffers(1, &vbo_); if(vbo_==0) throw std::runtime_error("glGenBuffers failed"); } // bind vertex buffer object glBindBuffer(GL_ARRAY_BUFFER, vbo_); // enable vertex attribute arrays glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glEnableVertexAttribArray(2); // set the vertex attribute pointers glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *)offsetof(Vertex, position)); glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), (void *)offsetof(Vertex, color)); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *)offsetof(Vertex, uv)); glDrawArrays(GL_TRIANGLES, 0, 6); // unbind vertex array object glBindVertexArray(0); } void SpriteBatch::createRenderBatches() { std::vector vertices; if(glyph_ptrs_.empty()) return; render_batches_.reserve(glyph_ptrs_.size()*NUM_VERTICES); for(int i=0; i<(int)glyph_ptrs_.size(); ++i) { if(i==0 || (i>0 && (glyph_ptrs_[i]->texture()!=glyph_ptrs_[i-1]->texture()))) render_batches_.emplace_back(i*NUM_VERTICES, NUM_VERTICES, glyph_ptrs_[i]->texture()); else render_batches_.back().num_vertices_+=NUM_VERTICES; vertices.push_back(glyph_ptrs_[i]->bottom_left()); vertices.push_back(glyph_ptrs_[i]->top_left()); vertices.push_back(glyph_ptrs_[i]->top_right()); vertices.push_back(glyph_ptrs_[i]->bottom_left()); vertices.push_back(glyph_ptrs_[i]->bottom_right()); vertices.push_back(glyph_ptrs_[i]->top_right()); // bind vbo glBindBuffer(GL_ARRAY_BUFFER, vbo_); // orphan the buffer glBufferData(GL_ARRAY_BUFFER, vertices.size()*sizeof(Vertex), nullptr, GL_DYNAMIC_DRAW); // upload the data glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.size()*sizeof(Vertex), vertices.data()); // unbind buffer glBindBuffer(GL_ARRAY_BUFFER, 0); } } void SpriteBatch::sortGlyphs() { // sort using introsort or quicksort std::sort(glyph_ptrs_.begin(), glyph_ptrs_.end(), [] (Glyph *a, Glyph *b)->bool { if(a->depth()==b->depth()) return a->texture()texture(); return a->depth()depth(); }); } } // yage