1
0
Fork 0
spectre/source/Graphics/BatchRenderer2D.cpp

259 lines
No EOL
6.6 KiB
C++

#include <Spectre/System/Log.h>
#include <Spectre/Graphics/OpenGL.h>
#include <Spectre/Graphics/Renderable.h>
#include <Spectre/Graphics/RenderState.h>
#include <Spectre/Graphics/ShaderProgram.h>
#include <Spectre/Graphics/Texture.h>
#include <Spectre/Graphics/BatchRenderer2D.h>
#include <Spectre/Math/Math.h>
#include <algorithm>
static bool compare_renderable(const Renderable2D* a, const Renderable2D* b) {
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<unsigned short> 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)
{
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<Vertex2D*>(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<Vertex2D*>(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<Vertex2D*>(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<Vertex2D>& 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);
}
}