See More

#include "SpriteBatch.h" #include namespace Bengine { Glyph::Glyph(const glm::vec4& destRect, const glm::vec4& uvRect, GLuint Texture, float Depth, const ColorRGBA8& color) : texture(Texture), depth(Depth) { topLeft.color = color; topLeft.setPosition(destRect.x, destRect.y + destRect.w); topLeft.setUV(uvRect.x, uvRect.y + uvRect.w); bottomLeft.color = color; bottomLeft.setPosition(destRect.x, destRect.y); bottomLeft.setUV(uvRect.x, uvRect.y); bottomRight.color = color; bottomRight.setPosition(destRect.x + destRect.z, destRect.y); bottomRight.setUV(uvRect.x + uvRect.z, uvRect.y); topRight.color = color; topRight.setPosition(destRect.x + destRect.z, destRect.y + destRect.w); topRight.setUV(uvRect.x + uvRect.z, uvRect.y + uvRect.w); } Glyph::Glyph(const glm::vec4& destRect, const glm::vec4& uvRect, GLuint Texture, float Depth, const ColorRGBA8& color, float angle) : texture(Texture), depth(Depth) { glm::vec2 halfDims(destRect.z / 2.0f, destRect.w / 2.0f); // Get points centered at origin glm::vec2 tl(-halfDims.x, halfDims.y); glm::vec2 bl(-halfDims.x, -halfDims.y); glm::vec2 br(halfDims.x, -halfDims.y); glm::vec2 tr(halfDims.x, halfDims.y); // Rotate the points tl = rotatePoint(tl, angle) + halfDims; bl = rotatePoint(bl, angle) + halfDims; br = rotatePoint(br, angle) + halfDims; tr = rotatePoint(tr, angle) + halfDims; topLeft.color = color; topLeft.setPosition(destRect.x + tl.x, destRect.y + tl.y); topLeft.setUV(uvRect.x, uvRect.y + uvRect.w); bottomLeft.color = color; bottomLeft.setPosition(destRect.x + bl.x, destRect.y + bl.y); bottomLeft.setUV(uvRect.x, uvRect.y); bottomRight.color = color; bottomRight.setPosition(destRect.x + br.x, destRect.y + br.y); bottomRight.setUV(uvRect.x + uvRect.z, uvRect.y); topRight.color = color; topRight.setPosition(destRect.x + tr.x, destRect.y + tr.y); topRight.setUV(uvRect.x + uvRect.z, uvRect.y + uvRect.w); } glm::vec2 Glyph::rotatePoint(const glm::vec2& pos, float angle) { glm::vec2 newv; newv.x = pos.x * cos(angle) - pos.y * sin(angle); newv.y = pos.x * sin(angle) + pos.y * cos(angle); return newv; } SpriteBatch::SpriteBatch() : _vbo(0), _vao(0) { } SpriteBatch::~SpriteBatch() { } void SpriteBatch::init() { createVertexArray(); } void SpriteBatch::begin(GlyphSortType sortType /* GlyphSortType::TEXTURE */) { _sortType = sortType; _renderBatches.clear(); // Makes _glpyhs.size() == 0, however it does not free internal memory. // So when we later call emplace_back it doesn't need to internally call new. _glyphs.clear(); } void SpriteBatch::end() { // Set up all pointers for fast sorting _glyphPointers.resize(_glyphs.size()); for (size_t i = 0; i < _glyphs.size(); i++) { _glyphPointers[i] = &_glyphs[i]; } sortGlyphs(); createRenderBatches(); } void SpriteBatch::draw(const glm::vec4& destRect, const glm::vec4& uvRect, GLuint texture, float depth, const ColorRGBA8& color) { _glyphs.emplace_back(destRect, uvRect, texture, depth, color); } void SpriteBatch::draw(const glm::vec4& destRect, const glm::vec4& uvRect, GLuint texture, float depth, const ColorRGBA8& color, float angle) { _glyphs.emplace_back(destRect, uvRect, texture, depth, color, angle); } void SpriteBatch::draw(const glm::vec4& destRect, const glm::vec4& uvRect, GLuint texture, float depth, const ColorRGBA8& color, const glm::vec2& dir) { const glm::vec2 right(1.0f, 0.0f); float angle = acos(glm::dot(right, dir)); if (dir.y < 0.0f) angle = -angle; _glyphs.emplace_back(destRect, uvRect, texture, depth, color, angle); } void SpriteBatch::renderBatch() { // Bind our VAO. This sets up the opengl state we need, including the // vertex attribute pointers and it binds the VBO glBindVertexArray(_vao); for (size_t i = 0; i < _renderBatches.size(); i++) { glBindTexture(GL_TEXTURE_2D, _renderBatches[i].texture); glDrawArrays(GL_TRIANGLES, _renderBatches[i].offset, _renderBatches[i].numVertices); } glBindVertexArray(0); } void SpriteBatch::createRenderBatches() { // This will store all the vertices that we need to upload std::vector vertices; // Resize the buffer to the exact size we need so we can treat // it like an array vertices.resize(_glyphPointers.size() * 6); if (_glyphPointers.empty()) { return; } int offset = 0; // current offset int cv = 0; // current vertex //Add the first batch _renderBatches.emplace_back(offset, 6, _glyphPointers[0]->texture); vertices[cv++] = _glyphPointers[0]->topLeft; vertices[cv++] = _glyphPointers[0]->bottomLeft; vertices[cv++] = _glyphPointers[0]->bottomRight; vertices[cv++] = _glyphPointers[0]->bottomRight; vertices[cv++] = _glyphPointers[0]->topRight; vertices[cv++] = _glyphPointers[0]->topLeft; offset += 6; //Add all the rest of the glyphs for (size_t cg = 1; cg < _glyphPointers.size(); cg++) { // Check if this glyph can be part of the current batch if (_glyphPointers[cg]->texture != _glyphPointers[cg - 1]->texture) { // Make a new batch _renderBatches.emplace_back(offset, 6, _glyphPointers[cg]->texture); } else { // If its part of the current batch, just increase numVertices _renderBatches.back().numVertices += 6; } vertices[cv++] = _glyphPointers[cg]->topLeft; vertices[cv++] = _glyphPointers[cg]->bottomLeft; vertices[cv++] = _glyphPointers[cg]->bottomRight; vertices[cv++] = _glyphPointers[cg]->bottomRight; vertices[cv++] = _glyphPointers[cg]->topRight; vertices[cv++] = _glyphPointers[cg]->topLeft; offset += 6; } // Bind our VBO glBindBuffer(GL_ARRAY_BUFFER, _vbo); // Orphan the buffer (for speed) 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 the VBO glBindBuffer(GL_ARRAY_BUFFER, 0); } void SpriteBatch::createVertexArray() { // Generate the VAO if it isn't already generated if (_vao == 0) { glGenVertexArrays(1, &_vao); } // Bind the VAO. All subsequent opengl calls will modify it's state. glBindVertexArray(_vao); //G enerate the VBO if it isn't already generated if (_vbo == 0) { glGenBuffers(1, &_vbo); } glBindBuffer(GL_ARRAY_BUFFER, _vbo); //Tell opengl what attribute arrays we need glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glEnableVertexAttribArray(2); //This is the position attribute pointer glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, position)); //This is the color attribute pointer glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), (void*)offsetof(Vertex, color)); //This is the UV attribute pointer glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, uv)); glBindVertexArray(0); } void SpriteBatch::sortGlyphs() { switch (_sortType) { case GlyphSortType::BACK_TO_FRONT: std::stable_sort(_glyphPointers.begin(), _glyphPointers.end(), compareBackToFront); break; case GlyphSortType::FRONT_TO_BACK: std::stable_sort(_glyphPointers.begin(), _glyphPointers.end(), compareFrontToBack); break; case GlyphSortType::TEXTURE: std::stable_sort(_glyphPointers.begin(), _glyphPointers.end(), compareTexture); break; } } bool SpriteBatch::compareFrontToBack(Glyph* a, Glyph* b) { return (a->depth < b->depth); } bool SpriteBatch::compareBackToFront(Glyph* a, Glyph* b) { return (a->depth > b->depth); } bool SpriteBatch::compareTexture(Glyph* a, Glyph* b) { return (a->texture < b->texture); } }