Add batch rendering functionality to Renderer2D
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
#include <algorithm> // std::min
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "inferno/component/transform.h"
|
||||
@@ -19,45 +21,236 @@ namespace Inferno {
|
||||
glClearColor(color.r, color.g, color.b, color.a);
|
||||
}
|
||||
|
||||
void RenderCommand::drawIndexed(const std::shared_ptr<VertexArray>& vertexArray)
|
||||
void RenderCommand::drawIndexed(const std::shared_ptr<VertexArray>& vertexArray, uint32_t indexCount)
|
||||
{
|
||||
glDrawElements(GL_TRIANGLES, vertexArray->getIndexBuffer()->getCount(), GL_UNSIGNED_INT, nullptr);
|
||||
uint32_t count = indexCount ? indexCount : vertexArray->getIndexBuffer()->getCount();
|
||||
glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, nullptr);
|
||||
}
|
||||
|
||||
int32_t RenderCommand::textureUnitAmount()
|
||||
{
|
||||
int32_t amount = 0;
|
||||
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &amount);
|
||||
return amount;
|
||||
}
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
std::shared_ptr<Camera> Renderer2D::m_camera = nullptr;
|
||||
std::shared_ptr<Camera> Renderer2D::s_camera = nullptr;
|
||||
QuadBatch* Renderer2D::s_quadBatch;
|
||||
|
||||
void Renderer2D::initialize()
|
||||
{
|
||||
s_quadBatch = new QuadBatch();
|
||||
|
||||
// CPU
|
||||
// -----------------------------------------
|
||||
|
||||
// Create array for storing quads vertices
|
||||
s_quadBatch->vertexBufferBase = std::shared_ptr<QuadVertex[]>(new QuadVertex[QuadBatch::vertexCount]);
|
||||
s_quadBatch->vertexBufferPtr = s_quadBatch->vertexBufferBase.get();
|
||||
|
||||
// Set basic quad vertex positions
|
||||
s_quadBatch->vertexPositions[0] = { -0.5f, -0.5f, 0.0f, 1.0f };
|
||||
s_quadBatch->vertexPositions[1] = { 0.5f, -0.5f, 0.0f, 1.0f };
|
||||
s_quadBatch->vertexPositions[2] = { 0.5f, 0.5f, 0.0f, 1.0f };
|
||||
s_quadBatch->vertexPositions[3] = { -0.5f, 0.5f, 0.0f, 1.0f };
|
||||
|
||||
// Create texture unit samplers
|
||||
int32_t samplers[s_quadBatch->textureUnitPerBatch];
|
||||
for (uint32_t i = 0; i < s_quadBatch->textureUnitPerBatch; i++) {
|
||||
samplers[i] = i;
|
||||
}
|
||||
|
||||
// Get amount of texture units supported by the GPU
|
||||
uint32_t constTextureUnitCount = QuadBatch::textureUnitPerBatch;
|
||||
uint32_t gpuTextureUnitCount = RenderCommand::textureUnitAmount();
|
||||
s_quadBatch->supportedTextureUnitPerBatch = std::min(constTextureUnitCount, gpuTextureUnitCount);
|
||||
|
||||
// Texture unit 0 is reserved for no texture
|
||||
s_quadBatch->textureUnits[0] = nullptr;
|
||||
|
||||
// GPU
|
||||
// -----------------------------------------
|
||||
|
||||
// Create shader
|
||||
s_quadBatch->shader = std::make_shared<Shader>("assets/glsl/batch-quad");
|
||||
s_quadBatch->shader->bind();
|
||||
s_quadBatch->shader->setInt("u_textures", samplers, s_quadBatch->textureUnitPerBatch);
|
||||
s_quadBatch->shader->unbind();
|
||||
|
||||
// Create vertex array
|
||||
s_quadBatch->vertexArray = std::make_shared<VertexArray>();
|
||||
|
||||
// Create vertex buffer
|
||||
s_quadBatch->vertexBuffer = std::make_shared<VertexBuffer>(sizeof(QuadVertex) * QuadBatch::vertexCount);
|
||||
s_quadBatch->vertexBuffer->setLayout({
|
||||
{ BufferElementType::Vec3, "a_position" },
|
||||
{ BufferElementType::Vec4, "a_color" },
|
||||
{ BufferElementType::Vec2, "a_textureCoordinates" },
|
||||
{ BufferElementType::Float, "a_textureIndex" },
|
||||
});
|
||||
s_quadBatch->vertexArray->addVertexBuffer(s_quadBatch->vertexBuffer);
|
||||
|
||||
// Generate indices
|
||||
|
||||
uint32_t* indices = new uint32_t[QuadBatch::indexCount];
|
||||
|
||||
uint32_t offset = 0;
|
||||
for (uint32_t i = 0; i < QuadBatch::indexCount; i += QuadBatch::indexPerQuad) {
|
||||
indices[i + 0] = offset + 0;
|
||||
indices[i + 1] = offset + 1;
|
||||
indices[i + 2] = offset + 2;
|
||||
indices[i + 3] = offset + 2;
|
||||
indices[i + 4] = offset + 3;
|
||||
indices[i + 5] = offset + 0;
|
||||
|
||||
offset += QuadBatch::vertexPerQuad;
|
||||
}
|
||||
|
||||
// Create index buffer
|
||||
std::shared_ptr<IndexBuffer> indexBuffer = std::make_shared<IndexBuffer>(indices, sizeof(uint32_t) * QuadBatch::indexCount);
|
||||
s_quadBatch->vertexArray->setIndexBuffer(indexBuffer);
|
||||
delete[] indices;
|
||||
|
||||
dbg(Log::Info) << "Renderer2D initialized";
|
||||
}
|
||||
|
||||
void Renderer2D::destroy()
|
||||
{
|
||||
delete s_quadBatch;
|
||||
}
|
||||
|
||||
void Renderer2D::beginScene(const std::shared_ptr<Camera>& camera)
|
||||
{
|
||||
m_camera = camera;
|
||||
s_camera = camera;
|
||||
}
|
||||
|
||||
void Renderer2D::endScene()
|
||||
{
|
||||
|
||||
nextBatch();
|
||||
}
|
||||
|
||||
void Renderer2D::submit(const std::shared_ptr<VertexArray>& vertexArray)
|
||||
void Renderer2D::drawQuad(std::shared_ptr<Transform> transform, glm::vec4 color)
|
||||
{
|
||||
vertexArray->bind();
|
||||
RenderCommand::drawIndexed(vertexArray);
|
||||
vertexArray->unbind();
|
||||
drawQuad(transform, color, nullptr);
|
||||
}
|
||||
|
||||
void Renderer2D::submit(const RenderBundle& bundle)
|
||||
void Renderer2D::drawQuad(std::shared_ptr<Transform> transform, glm::mat4 color)
|
||||
{
|
||||
bundle.shader->bind();
|
||||
if (bundle.texture) bundle.texture->bind();
|
||||
bundle.vertexArray->bind();
|
||||
drawQuad(transform, color, nullptr);
|
||||
}
|
||||
|
||||
bundle.shader->setFloat("u_projectionView", m_camera->projection() * m_camera->transform()->transform());
|
||||
bundle.shader->setFloat("u_model", bundle.transform->transform());
|
||||
RenderCommand::drawIndexed(bundle.vertexArray);
|
||||
void Renderer2D::drawQuad(std::shared_ptr<Transform> transform, glm::vec4 color, std::shared_ptr<Texture> texture)
|
||||
{
|
||||
drawQuad(transform, glm::mat4(color, color, color, color), texture);
|
||||
}
|
||||
|
||||
bundle.vertexArray->unbind();
|
||||
if (bundle.texture) bundle.texture->unbind();
|
||||
bundle.shader->unbind();
|
||||
void Renderer2D::drawQuad(std::shared_ptr<Transform> transform, glm::mat4 color, std::shared_ptr<Texture> texture)
|
||||
{
|
||||
// Create a new batch if the quad limit has been reached
|
||||
if (s_quadBatch->quadCount >= QuadBatch::quads) {
|
||||
nextBatch();
|
||||
}
|
||||
|
||||
constexpr glm::vec2 textureCoordinates[] = {
|
||||
{ 0.0f, 0.0f }, { 1.0f, 0.0f }, { 1.0f, 1.0f }, { 0.0f, 1.0f }
|
||||
};
|
||||
|
||||
uint32_t textureUnitIndex = addTextureUnit(texture);
|
||||
|
||||
// Add the quads 4 vertices
|
||||
for (uint32_t i = 0; i < QuadBatch::vertexPerQuad; i++) {
|
||||
s_quadBatch->vertexBufferPtr->position = transform->transform() * s_quadBatch->vertexPositions[i];
|
||||
s_quadBatch->vertexBufferPtr->color = color[i];
|
||||
s_quadBatch->vertexBufferPtr->textureCoordinates = textureCoordinates[i];
|
||||
s_quadBatch->vertexBufferPtr->textureIndex = (float)textureUnitIndex;
|
||||
s_quadBatch->vertexBufferPtr++;
|
||||
}
|
||||
|
||||
s_quadBatch->quadCount++;
|
||||
}
|
||||
|
||||
uint32_t Renderer2D::addTextureUnit(std::shared_ptr<Texture> texture)
|
||||
{
|
||||
if (texture == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Create a new batch if the texture unit limit has been reached
|
||||
if (s_quadBatch->textureUnitIndex >= s_quadBatch->supportedTextureUnitPerBatch) {
|
||||
nextBatch();
|
||||
}
|
||||
|
||||
// If texure was already added
|
||||
for (uint32_t i = 1; i < s_quadBatch->textureUnitIndex; i++) {
|
||||
if (s_quadBatch->textureUnits[i] == texture) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
// Add texture
|
||||
uint32_t textureUnitIndex = s_quadBatch->textureUnitIndex;
|
||||
s_quadBatch->textureUnits[textureUnitIndex] = texture;
|
||||
s_quadBatch->textureUnitIndex++;
|
||||
|
||||
return textureUnitIndex;
|
||||
}
|
||||
|
||||
void Renderer2D::bind()
|
||||
{
|
||||
s_quadBatch->shader->bind();
|
||||
|
||||
for (uint32_t i = 1; i < s_quadBatch->textureUnitIndex; i++) {
|
||||
s_quadBatch->textureUnits[i]->bind(i);
|
||||
}
|
||||
|
||||
s_quadBatch->vertexArray->bind();
|
||||
}
|
||||
|
||||
void Renderer2D::unbind()
|
||||
{
|
||||
s_quadBatch->vertexArray->unbind();
|
||||
|
||||
for (uint32_t i = 1; i < s_quadBatch->textureUnitIndex; i++) {
|
||||
s_quadBatch->textureUnits[i]->unbind();
|
||||
}
|
||||
|
||||
s_quadBatch->shader->unbind();
|
||||
}
|
||||
|
||||
void Renderer2D::flush()
|
||||
{
|
||||
if (s_quadBatch->quadCount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Upload vertex data to GPU
|
||||
s_quadBatch->vertexBuffer->uploadData(
|
||||
s_quadBatch->vertexBufferBase.get(),
|
||||
s_quadBatch->quadCount * QuadBatch::vertexPerQuad * sizeof(QuadVertex));
|
||||
|
||||
bind();
|
||||
|
||||
// Render
|
||||
s_quadBatch->shader->setFloat("u_projectionView", s_camera->projection() * s_camera->transform()->transform());
|
||||
RenderCommand::drawIndexed(s_quadBatch->vertexArray, s_quadBatch->quadCount * QuadBatch::indexPerQuad);
|
||||
|
||||
unbind();
|
||||
}
|
||||
|
||||
void Renderer2D::startBatch()
|
||||
{
|
||||
s_quadBatch->quadCount = 0;
|
||||
s_quadBatch->vertexBufferPtr = s_quadBatch->vertexBufferBase.get();
|
||||
|
||||
s_quadBatch->textureUnitIndex = 1;
|
||||
}
|
||||
|
||||
void Renderer2D::nextBatch()
|
||||
{
|
||||
flush();
|
||||
startBatch();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#ifndef RENDERER_H
|
||||
#define RENDERER_H
|
||||
|
||||
#include <cstdint> // std::uint32_t
|
||||
#include <memory> // std::shared_ptr
|
||||
|
||||
#include "glm/ext/matrix_float4x4.hpp" // glm::mat4
|
||||
#include "glm/ext/vector_float2.hpp" // glm::vec2
|
||||
#include "glm/ext/vector_float3.hpp" // glm::vec3
|
||||
#include "glm/ext/vector_float4.hpp" // glm::vec4
|
||||
|
||||
namespace Inferno {
|
||||
@@ -11,35 +15,77 @@ namespace Inferno {
|
||||
class Shader;
|
||||
class Texture;
|
||||
class Transform;
|
||||
class VertexBuffer;
|
||||
class VertexArray;
|
||||
|
||||
struct RenderBundle {
|
||||
const std::shared_ptr<Shader>& shader;
|
||||
const std::shared_ptr<Texture>& texture;
|
||||
const std::shared_ptr<Transform>& transform;
|
||||
const std::shared_ptr<VertexArray>& vertexArray;
|
||||
struct QuadVertex {
|
||||
glm::vec3 position = { 0.0f, 0.0f, 0.0f };
|
||||
glm::vec4 color = { 1.0f, 1.0f, 1.0f, 1.0f };
|
||||
glm::vec2 textureCoordinates = { 0.0f, 0.0f };
|
||||
float textureIndex = 0; // @Todo get int to pass to fragment correctly
|
||||
};
|
||||
|
||||
struct QuadBatch {
|
||||
static const uint32_t vertexPerQuad = 4;
|
||||
static const uint32_t indexPerQuad = 6;
|
||||
static const uint32_t quads = 1000;
|
||||
static const uint32_t vertexCount = quads * vertexPerQuad;
|
||||
static const uint32_t indexCount = quads * indexPerQuad;
|
||||
static const uint32_t textureUnitPerBatch = 32;
|
||||
|
||||
uint32_t quadCount = 0;
|
||||
std::shared_ptr<QuadVertex[]> vertexBufferBase = nullptr;
|
||||
QuadVertex* vertexBufferPtr = nullptr;
|
||||
|
||||
glm::vec4 vertexPositions[vertexPerQuad];
|
||||
|
||||
uint32_t supportedTextureUnitPerBatch = 0;
|
||||
uint32_t textureUnitIndex = 1;
|
||||
std::array<std::shared_ptr<Texture>, textureUnitPerBatch> textureUnits;
|
||||
|
||||
std::shared_ptr<Shader> shader = nullptr;
|
||||
std::shared_ptr<VertexBuffer> vertexBuffer = nullptr;
|
||||
std::shared_ptr<VertexArray> vertexArray = nullptr;
|
||||
};
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
class RenderCommand {
|
||||
public:
|
||||
static void clear();
|
||||
static void clearColor(const glm::vec4& color);
|
||||
|
||||
static void drawIndexed(const std::shared_ptr<VertexArray>& vertexArray);
|
||||
static void drawIndexed(const std::shared_ptr<VertexArray>& vertexArray, uint32_t indexCount = 0);
|
||||
|
||||
static int32_t textureUnitAmount();
|
||||
};
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
class Renderer2D {
|
||||
public:
|
||||
static void initialize();
|
||||
static void destroy();
|
||||
|
||||
static void beginScene(const std::shared_ptr<Camera>& camera);
|
||||
static void endScene();
|
||||
|
||||
static void submit(const std::shared_ptr<VertexArray>& vertexArray);
|
||||
static void submit(const RenderBundle& bundle);
|
||||
static void drawQuad(std::shared_ptr<Transform> transform, glm::vec4 color);
|
||||
static void drawQuad(std::shared_ptr<Transform> transform, glm::mat4 color);
|
||||
static void drawQuad(std::shared_ptr<Transform> transform, glm::vec4 color, std::shared_ptr<Texture> texture);
|
||||
static void drawQuad(std::shared_ptr<Transform> transform, glm::mat4 color, std::shared_ptr<Texture> texture);
|
||||
|
||||
private:
|
||||
static std::shared_ptr<Camera> m_camera;
|
||||
static uint32_t addTextureUnit(std::shared_ptr<Texture> texture);
|
||||
|
||||
static void bind();
|
||||
static void unbind();
|
||||
static void flush();
|
||||
static void startBatch();
|
||||
static void nextBatch();
|
||||
|
||||
static std::shared_ptr<Camera> s_camera;
|
||||
static QuadBatch* s_quadBatch;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user