1
0
Fork 0
spectre/source/Graphics/BatchRenderer2D.cpp
Henrik Hautakoski 84ffd0189e Renderer2D: reverse the relationship between Renderer2D and Renderable.
Pass Renderer2D to Renderable and have each subclass of Renderable decide what the Renderer should do (like drawText(), drawRect() etc).

This makes the Renderable more flexible. right now, each renderable has a texture, vertices and indices. but what if some renderables does not use textures? or more than one? What if some renderables are just a group of other renderables? (Scene Graph's).

If we instead just make Renderables implement render(Renderer2D& r) interface. we can make each renderable pass the data it holds to the renderer without a hard defined interface.
2016-06-18 13:26:08 +02:00

264 lines
No EOL
6.7 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)
{
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<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);
}
}