#include #include #include #include #include #include #include #include #include static bool compare_renderable(const Renderable2D* a, const Renderable2D* b) { if (a->getZOrder() != b->getZOrder()) { return a->getZOrder() < b->getZOrder(); } if (a->getRenderType() != b->getRenderType()) { return a->getRenderType() < b->getRenderType(); } if (a->getTexture() != b->getTexture()) { return a->getTexture() < b->getTexture(); } return false; } BatchRenderer2D::BatchRenderer2D() { // Generate buffer objects. glGenBuffers(1, &m_IBO); glGenBuffers(1, &m_VBO); setBatchSize(10000); m_textShader.create(); if (!m_textShader.loadFromFile("assets/shaders/text.shader.glsl")) { std::cout << "Failed to load shader: " << m_textShader.getLastError() << std::endl; } if (!m_textShader.link()) { std::cout << "Failed to link shader: " << m_textShader.getLastError() << std::endl; } m_spriteShader.create(); if (!m_spriteShader.loadFromFile("assets/shaders/standard.shader.glsl")) { std::cout << "Failed to load shader: " << m_spriteShader.getLastError() << std::endl; } if (!m_spriteShader.link()) { std::cout << "Failed to link shader: " << m_spriteShader.getLastError() << std::endl; } } BatchRenderer2D::~BatchRenderer2D() { glDeleteBuffers(1, &m_VBO); glDeleteBuffers(1, &m_IBO); } void BatchRenderer2D::setBatchSize(unsigned short size) { std::vector indices; int vtx = 0; if (size > 10000) { size = 10000; } m_size = size; indices.resize(size * 6); for(int i = 0; i < m_size * 6; i += 6) { indices[i + 0] = vtx + 0; indices[i + 1] = vtx + 1; indices[i + 2] = vtx + 2; indices[i + 3] = vtx + 2; indices[i + 4] = vtx + 3; indices[i + 5] = vtx + 0; vtx += 4; } // Upload to GPU glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned short), &indices[0], GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); // Allocate memory for vertex buffer. glBindBuffer(GL_ARRAY_BUFFER, m_VBO); glBufferData(GL_ARRAY_BUFFER, m_size * sizeof(Vertex2D), NULL, GL_DYNAMIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); // Log log("BatchRenderer - BatchSize\n"); log(" num sprites: %i\n", m_size); log(" num indices: %i\n", m_size * 6); log(" num vertices: %i\n", m_size); log("------\n"); } void BatchRenderer2D::begin() { } void BatchRenderer2D::end() { } void BatchRenderer2D::submit(const Renderable2D& renderable) { renderable.render(*this); } void BatchRenderer2D::draw(const Renderable2D* renderable) { m_queue.push_back(renderable); } void BatchRenderer2D::render() { Matrix4f viewMatrix = m_camera ? m_camera->getViewMatrix() : Matrix4f::Identity; Matrix4f projMatrix = m_camera ? m_camera->getProjectionMatrix() : Matrix4f::Identity; Matrix4f guiMatrix = math::orthoProjection(0.0f, 800.0f, 600.0f, 0.0f); Matrix4f MVP = projMatrix * viewMatrix; // Nothing to render. if (m_queue.size() < 1) { return; } // Bind buffers. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IBO); glBindBuffer(GL_ARRAY_BUFFER, m_VBO); // Setup position. glEnableVertexAttribArray(VertexAttribPosition); glVertexAttribPointer(VertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (void*) 0); // Setup color glEnableVertexAttribArray(VertexAttribColor0); glVertexAttribPointer(VertexAttribColor0, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (void*) 8); glEnableVertexAttribArray(VertexAttribTexCoord0); glVertexAttribPointer(VertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (void*) 24); // Set shader uniforms. m_textShader.enable(); m_textShader.setUniform("u_MVP", MVP); m_textShader.disable(); m_spriteShader.enable(); m_spriteShader.setUniform("u_MVP", MVP); m_spriteShader.disable(); prepareQueue(); glDisableVertexAttribArray(VertexAttribPosition); glDisableVertexAttribArray(VertexAttribColor0); glDisableVertexAttribArray(VertexAttribTexCoord0); // Unbind all buffers. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0); m_queue.clear(); m_state.cleanup(); } void BatchRenderer2D::prepareQueue() { Vertex2D *buffer = static_cast(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY)); Batch current; std::sort(m_queue.begin(), m_queue.end(), compare_renderable); current.type = m_queue[0]->getRenderType(); current.texture = m_queue[0]->getTexture(); current.count = 0; current.offset = 0; for(int i = 0; i < m_queue.size(); i++) { const Texture *tex = m_queue[i]->getTexture(); RenderType type = m_queue[i]->getRenderType(); unsigned int num_vert = m_queue[i]->getVertices().size(); // TODO: Only upload and draw if buffer is full :) if (current.type != type || current.texture != tex || current.count >= m_size) { glUnmapBuffer(GL_ARRAY_BUFFER); drawBatch(current); buffer = static_cast(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY)); current.type = m_queue[i]->getRenderType(); current.count = 0; current.offset = 0; current.texture = tex; } buffer += addRenderable(buffer, m_queue[i]); current.count += num_vert; } glUnmapBuffer(GL_ARRAY_BUFFER); if (current.count > 0) { drawBatch(current); } } void BatchRenderer2D::uploadBatch(const Batch& batch) { Vertex2D *buffer = static_cast(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY)); unsigned int count = 0; for(int i = 0; i < batch.count; i++) { buffer += addRenderable(buffer, m_queue[batch.offset + i]); } // Upload gpu data and draw. glUnmapBuffer(GL_ARRAY_BUFFER); } unsigned int BatchRenderer2D::addRenderable(Vertex2D* buffer, const Renderable2D* renderable) { const Transform& transform = renderable->getTransform(); const std::vector& vertices = renderable->getVertices(); // Pretransform vertex positions to skip setting a uniform model // matrix in the shader for each renderable reducing the number of draw calls. for(int i = 0; i < vertices.size(); i++) { buffer[i].position = transform * vertices[i].position; buffer[i].color = vertices[i].color; buffer[i].uv = vertices[i].uv; } return vertices.size(); } void BatchRenderer2D::drawBatch(Batch& batch) { const ShaderProgram* shader; if (batch.type == RenderType_UI) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); shader = &m_textShader; } else { shader = &m_spriteShader; } // enable shader. m_state.enableShader(shader); // enable batch texture m_state.enableTexture(batch.texture); glDrawElements(GL_TRIANGLES, (batch.count / 4) * 6, GL_UNSIGNED_SHORT, 0); if (batch.type == RenderType_UI) { glDisable(GL_BLEND); } }