diff --git a/inferno/src/inferno/render/renderer.cpp b/inferno/src/inferno/render/renderer.cpp index 25851ae..77c8363 100644 --- a/inferno/src/inferno/render/renderer.cpp +++ b/inferno/src/inferno/render/renderer.cpp @@ -1,3 +1,5 @@ +#include // std::min + #include #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) + void RenderCommand::drawIndexed(const std::shared_ptr& 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 Renderer2D::s_camera = nullptr; + QuadBatch* Renderer2D::s_quadBatch; + + void Renderer2D::initialize() + { + s_quadBatch = new QuadBatch(); + +// CPU // ----------------------------------------- - std::shared_ptr Renderer2D::m_camera = nullptr; + // Create array for storing quads vertices + s_quadBatch->vertexBufferBase = std::shared_ptr(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("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(); + + // Create vertex buffer + s_quadBatch->vertexBuffer = std::make_shared(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 = std::make_shared(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) { - m_camera = camera; + s_camera = camera; } void Renderer2D::endScene() { + nextBatch(); + } + + void Renderer2D::drawQuad(std::shared_ptr transform, glm::vec4 color) + { + drawQuad(transform, color, nullptr); + } + + void Renderer2D::drawQuad(std::shared_ptr transform, glm::mat4 color) + { + drawQuad(transform, color, nullptr); + } + void Renderer2D::drawQuad(std::shared_ptr transform, glm::vec4 color, std::shared_ptr texture) + { + drawQuad(transform, glm::mat4(color, color, color, color), texture); } - void Renderer2D::submit(const std::shared_ptr& vertexArray) + void Renderer2D::drawQuad(std::shared_ptr transform, glm::mat4 color, std::shared_ptr texture) { - vertexArray->bind(); - RenderCommand::drawIndexed(vertexArray); - vertexArray->unbind(); + // 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++; } - void Renderer2D::submit(const RenderBundle& bundle) + uint32_t Renderer2D::addTextureUnit(std::shared_ptr texture) { - bundle.shader->bind(); - if (bundle.texture) bundle.texture->bind(); - bundle.vertexArray->bind(); + 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; + } + } - bundle.shader->setFloat("u_projectionView", m_camera->projection() * m_camera->transform()->transform()); - bundle.shader->setFloat("u_model", bundle.transform->transform()); - RenderCommand::drawIndexed(bundle.vertexArray); + // Add texture + uint32_t textureUnitIndex = s_quadBatch->textureUnitIndex; + s_quadBatch->textureUnits[textureUnitIndex] = texture; + s_quadBatch->textureUnitIndex++; - bundle.vertexArray->unbind(); - if (bundle.texture) bundle.texture->unbind(); - bundle.shader->unbind(); + 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(); } } diff --git a/inferno/src/inferno/render/renderer.h b/inferno/src/inferno/render/renderer.h index 8acb3af..b3cdae0 100644 --- a/inferno/src/inferno/render/renderer.h +++ b/inferno/src/inferno/render/renderer.h @@ -1,9 +1,13 @@ #ifndef RENDERER_H #define RENDERER_H +#include // std::uint32_t #include // std::shared_ptr -#include "glm/ext/vector_float4.hpp" // glm::vec4 +#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; - const std::shared_ptr& texture; - const std::shared_ptr& transform; - const std::shared_ptr& 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 vertexBufferBase = nullptr; + QuadVertex* vertexBufferPtr = nullptr; + + glm::vec4 vertexPositions[vertexPerQuad]; + + uint32_t supportedTextureUnitPerBatch = 0; + uint32_t textureUnitIndex = 1; + std::array, textureUnitPerBatch> textureUnits; + + std::shared_ptr shader = nullptr; + std::shared_ptr vertexBuffer = nullptr; + std::shared_ptr vertexArray = nullptr; + }; + +// ----------------------------------------- + class RenderCommand { public: static void clear(); static void clearColor(const glm::vec4& color); - static void drawIndexed(const std::shared_ptr& vertexArray); + static void drawIndexed(const std::shared_ptr& 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); static void endScene(); - static void submit(const std::shared_ptr& vertexArray); - static void submit(const RenderBundle& bundle); + static void drawQuad(std::shared_ptr transform, glm::vec4 color); + static void drawQuad(std::shared_ptr transform, glm::mat4 color); + static void drawQuad(std::shared_ptr transform, glm::vec4 color, std::shared_ptr texture); + static void drawQuad(std::shared_ptr transform, glm::mat4 color, std::shared_ptr texture); private: - static std::shared_ptr m_camera; + static uint32_t addTextureUnit(std::shared_ptr texture); + + static void bind(); + static void unbind(); + static void flush(); + static void startBatch(); + static void nextBatch(); + + static std::shared_ptr s_camera; + static QuadBatch* s_quadBatch; }; }