259 lines
No EOL
6.6 KiB
C++
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, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (void*) 8);
|
|
|
|
glEnableVertexAttribArray(VertexAttribTexCoord0);
|
|
glVertexAttribPointer(VertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (void*) 20);
|
|
|
|
// 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);
|
|
}
|
|
} |