diff --git a/src/inferno/render/buffer.cpp b/src/inferno/render/buffer.cpp index 01904c0..65ef9c1 100644 --- a/src/inferno/render/buffer.cpp +++ b/src/inferno/render/buffer.cpp @@ -321,6 +321,16 @@ void IndexBuffer::unbind() const glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } +void IndexBuffer::uploadData(const void* data, uint32_t size) +{ + bind(); + + // Upload data to the GPU + glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, size, data); + + unbind(); +} + // ----------------------------------------- VertexArray::VertexArray() diff --git a/src/inferno/render/buffer.h b/src/inferno/render/buffer.h index e0ddf67..713478c 100644 --- a/src/inferno/render/buffer.h +++ b/src/inferno/render/buffer.h @@ -90,7 +90,7 @@ private: // ----------------------------------------- // GPU memory which holds raw vertex data -class VertexBuffer { +class VertexBuffer { // Vertex Buffer Object, VBO public: VertexBuffer(size_t size); VertexBuffer(float* vertices, size_t size); @@ -113,7 +113,7 @@ private: // ----------------------------------------- // Vertices order of rendering -class IndexBuffer { +class IndexBuffer { // Element Buffer Object, EBO public: IndexBuffer(uint32_t* indices, size_t size); ~IndexBuffer(); @@ -121,6 +121,8 @@ public: void bind() const; void unbind() const; + void uploadData(const void* data, uint32_t size); + uint32_t getCount() const { return m_count; } private: @@ -131,7 +133,7 @@ private: // ----------------------------------------- // Array that holds the vertex attributes configuration -class VertexArray { +class VertexArray { // Vertex Array Object, VAO public: VertexArray(); ~VertexArray(); diff --git a/src/inferno/render/render-command.cpp b/src/inferno/render/render-command.cpp index 5cc29dd..fdc1d28 100644 --- a/src/inferno/render/render-command.cpp +++ b/src/inferno/render/render-command.cpp @@ -4,7 +4,7 @@ * SPDX-License-Identifier: MIT */ -#pragma once +#include // std::shadred_ptr #include "glad/glad.h" #include "ruc/format/log.h" @@ -39,9 +39,9 @@ void RenderCommand::clearColor(const glm::vec4& color) glClearColor(color.r, color.g, color.b, color.a); } -void RenderCommand::drawIndexed(const VertexArray& vertexArray, uint32_t indexCount) +void RenderCommand::drawIndexed(std::shared_ptr vertexArray, uint32_t indexCount) { - uint32_t count = indexCount ? indexCount : vertexArray.getIndexBuffer()->getCount(); + uint32_t count = indexCount ? indexCount : vertexArray->getIndexBuffer()->getCount(); glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, nullptr); } diff --git a/src/inferno/render/render-command.h b/src/inferno/render/render-command.h index c653095..2233103 100644 --- a/src/inferno/render/render-command.h +++ b/src/inferno/render/render-command.h @@ -6,7 +6,9 @@ #pragma once -#include "glm/ext/matrix_float4x4.hpp" // glm::mat4 +#include // std::shadred_ptr + +#include "glm/ext/vector_float4.hpp" // glm::vec4 namespace Inferno { @@ -19,7 +21,7 @@ public: static void clear(); static void clearColor(const glm::vec4& color); - static void drawIndexed(const VertexArray& vertexArray, uint32_t indexCount = 0); + static void drawIndexed(std::shared_ptr vertexArray, uint32_t indexCount = 0); static void setViewport(int32_t x, int32_t y, uint32_t width, uint32_t height); static void setDepthTest(bool enabled); diff --git a/src/inferno/render/renderer.cpp b/src/inferno/render/renderer.cpp index 991f782..a565e0a 100644 --- a/src/inferno/render/renderer.cpp +++ b/src/inferno/render/renderer.cpp @@ -4,8 +4,8 @@ * SPDX-License-Identifier: MIT */ -#include // std::min -#include // std::move +#include // std::copy, std::min +#include #include "glad/glad.h" #include "ruc/format/log.h" @@ -61,32 +61,7 @@ void Renderer::initialize() // Create vertex array m_vertexArray = std::make_shared(); - // CPU - // --------------------------------- - - // Generate indices - - uint32_t* indices = new uint32_t[maxIndices]; - - uint32_t offset = 0; - for (uint32_t i = 0; i < maxIndices; i += 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 += vertexPerQuad; - } - - // GPU - // --------------------------------- - - // Create index buffer - auto indexBuffer = std::make_shared(indices, sizeof(uint32_t) * maxIndices); - m_vertexArray->setIndexBuffer(indexBuffer); - delete[] indices; + createElementBuffer(); } template @@ -146,24 +121,56 @@ void Renderer::unbind() m_shader->unbind(); } +template +void Renderer::createElementBuffer() +{ + // CPU + // --------------------------------- + + // Generate indices + + uint32_t* indices = new uint32_t[maxElements]; + + uint32_t offset = 0; + for (uint32_t i = 0; i < maxElements; i += elementPerQuad) { + 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 += vertexPerQuad; + } + + // GPU + // --------------------------------- + + // Create index buffer + auto indexBuffer = std::make_shared(indices, sizeof(uint32_t) * maxElements); + m_vertexArray->setIndexBuffer(indexBuffer); + delete[] indices; +} + template void Renderer::flush() { - if (m_quadIndex == 0) { + if (m_vertexIndex == 0 || m_elementIndex == 0) { return; } + // Upload index data to GPU + uploadElementBuffer(); + // Upload vertex data to GPU - m_vertexArray->at(0)->uploadData( - m_vertexBufferBase, - m_quadIndex * vertexPerQuad * sizeof(T)); + m_vertexArray->at(0)->uploadData(m_vertexBufferBase, m_vertexIndex * sizeof(T)); bind(); // Render bool depthTest = RenderCommand::depthTest(); RenderCommand::setDepthTest(m_enableDepthBuffer); - RenderCommand::drawIndexed(*m_vertexArray, m_quadIndex * indexPerQuad); + RenderCommand::drawIndexed(m_vertexArray, m_elementIndex); RenderCommand::setDepthTest(depthTest); unbind(); @@ -172,7 +179,8 @@ void Renderer::flush() template void Renderer::startBatch() { - m_quadIndex = 0; + m_vertexIndex = 0; + m_elementIndex = 0; m_vertexBufferPtr = m_vertexBufferBase; m_textureSlotIndex = 1; @@ -245,7 +253,7 @@ void Renderer2D::drawQuad(const TransformComponent& transform, glm::vec4 color, void Renderer2D::drawQuad(const TransformComponent& transform, glm::mat4 color, std::shared_ptr texture) { // Create a new batch if the quad limit has been reached - if (m_quadIndex >= maxQuads) { + if (m_vertexIndex + vertexPerQuad > maxVertices || m_elementIndex + elementPerQuad > maxElements) { nextBatch(); } @@ -267,7 +275,8 @@ void Renderer2D::drawQuad(const TransformComponent& transform, glm::mat4 color, m_vertexBufferPtr++; } - m_quadIndex++; + m_vertexIndex += vertexPerQuad; + m_elementIndex += elementPerQuad; } void Renderer2D::loadShader() @@ -366,7 +375,8 @@ void RendererCubemap::drawCubemap(const TransformComponent& transform, glm::vec4 void RendererCubemap::drawCubemap(const TransformComponent& transform, glm::mat4 color, std::shared_ptr texture) { // Create a new batch if the quad limit has been reached - if (m_quadIndex >= maxQuads) { + if (m_vertexIndex + (vertexPerQuad * quadPerCube) > maxVertices + || m_elementIndex + (elementPerQuad * quadPerCube) > maxElements) { nextBatch(); } @@ -380,7 +390,8 @@ void RendererCubemap::drawCubemap(const TransformComponent& transform, glm::mat4 m_vertexBufferPtr++; } - m_quadIndex += quadPerCube; + m_vertexIndex += vertexPerQuad * quadPerCube; + m_elementIndex += elementPerQuad * quadPerCube; } void RendererCubemap::loadShader() @@ -428,7 +439,7 @@ RendererFont::RendererFont(s) void RendererFont::drawSymbol(std::array& symbolQuad, std::shared_ptr texture) { // Create a new batch if the quad limit has been reached - if (m_quadIndex >= maxQuads) { + if (m_vertexIndex + vertexPerQuad > maxVertices || m_elementIndex + elementPerQuad > maxElements) { nextBatch(); } @@ -451,7 +462,8 @@ void RendererFont::drawSymbol(std::array& symbolQua m_vertexBufferPtr++; } - m_quadIndex++; + m_vertexIndex += vertexPerQuad; + m_elementIndex += elementPerQuad; } void RendererFont::loadShader() @@ -459,4 +471,103 @@ void RendererFont::loadShader() m_shader = AssetManager::the().load("assets/glsl/batch-font"); } +// ----------------------------------------- + +Renderer3D::Renderer3D(s) +{ + Renderer::initialize(); + + // CPU + // --------------------------------- + + // Create array for storing quads vertices + m_vertexBufferBase = new Vertex[maxVertices]; + m_vertexBufferPtr = m_vertexBufferBase; + + // GPU + // --------------------------------- + + m_enableDepthBuffer = true; + + // Create vertex buffer + auto vertexBuffer = std::make_shared(sizeof(Vertex) * maxVertices); + vertexBuffer->setLayout({ + { BufferElementType::Vec3, "a_position" }, + { BufferElementType::Vec3, "a_normal" }, + { BufferElementType::Vec2, "a_textureCoordinates" }, + { BufferElementType::Float, "a_textureIndex" }, + }); + m_vertexArray->addVertexBuffer(vertexBuffer); + + ruc::info("Renderer3D initialized"); +} + +void Renderer3D::drawModel(std::span vertices, std::span indices, const TransformComponent& transform, std::shared_ptr texture) +{ + VERIFY(vertices.size() <= maxVertices, "model vertices too big for buffer"); + VERIFY(indices.size() <= maxElements, "model elements too big for buffer"); + + // Create a new batch if the quad limit has been reached + if (m_vertexIndex + vertices.size() > maxVertices || m_elementIndex + indices.size() > maxElements) { + nextBatch(); + } + + uint32_t textureUnitIndex = addTextureUnit(texture); + + // Add the vertices + for (auto vertex : vertices) { + m_vertexBufferPtr->position = transform.transform * glm::vec4(vertex.position, 1.0f); + m_vertexBufferPtr->normal = vertex.normal; + m_vertexBufferPtr->textureCoordinates = vertex.textureCoordinates; + m_vertexBufferPtr->textureIndex = (float)textureUnitIndex; + m_vertexBufferPtr++; + } + + std::copy(indices.begin(), indices.end(), m_elementBufferPtr); + m_elementBufferPtr += indices.size(); + + m_vertexIndex += vertices.size(); + m_elementIndex += indices.size(); +} + +void Renderer3D::beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView) +{ + m_shader->bind(); + m_shader->setFloat("u_projectionView", cameraProjection * cameraView); + m_shader->unbind(); +} + +void Renderer3D::createElementBuffer() +{ + // CPU + // --------------------------------- + + // Create array for storing quads vertices + m_elementBufferBase = new uint32_t[maxElements]; + m_elementBufferPtr = m_elementBufferBase; + + // GPU + // --------------------------------- + + // Create index buffer + auto indexBuffer = std::make_shared(m_elementBufferBase, sizeof(uint32_t) * maxElements); + m_vertexArray->setIndexBuffer(indexBuffer); +} + +void Renderer3D::uploadElementBuffer() +{ + m_vertexArray->getIndexBuffer()->uploadData(m_elementBufferBase, m_elementIndex * sizeof(uint32_t)); +} + +void Renderer3D::loadShader() +{ + m_shader = AssetManager::the().load("assets/glsl/batch-3d"); +} + +void Renderer3D::startBatch() +{ + Renderer::startBatch(); + m_elementBufferPtr = m_elementBufferBase; +} + } // namespace Inferno diff --git a/src/inferno/render/renderer.h b/src/inferno/render/renderer.h index 569c3eb..6b9e9b4 100644 --- a/src/inferno/render/renderer.h +++ b/src/inferno/render/renderer.h @@ -8,6 +8,7 @@ #include // int32_t, uint32_t #include // std::shared_ptr, std::unique_ptr, std::make_shared, std::make_unique +#include #include "glm/ext/matrix_float4x4.hpp" // glm::mat4 #include "glm/ext/vector_float2.hpp" // glm::vec2 @@ -49,21 +50,27 @@ struct SymbolVertex { float offset = 0.0f; }; +struct Vertex { + glm::vec3 position { 0.0f, 0.0f, 0.0f }; + glm::vec3 normal { 1.0f, 1.0f, 1.0f }; + glm::vec2 textureCoordinates { 0.0f, 0.0f }; + float textureIndex = 0; +}; + // ------------------------------------- template class Renderer { public: static constexpr const uint32_t vertexPerQuad = 4; - static constexpr const uint32_t indexPerQuad = 6; - static constexpr const uint32_t quadPerCube = 6; + static constexpr const uint32_t elementPerQuad = 6; // When to start a new batch - static constexpr const uint32_t maxQuads = 20000; - static constexpr const uint32_t maxVertices = maxQuads * vertexPerQuad; - static constexpr const uint32_t maxIndices = maxQuads * indexPerQuad; + static constexpr const uint32_t maxVertices = 60000; + static constexpr const uint32_t maxElements = 60000; static constexpr const uint32_t maxTextureSlots = 32; +public: virtual void beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView); virtual void endScene(); @@ -79,6 +86,8 @@ protected: void bind(); void unbind(); + virtual void createElementBuffer(); + virtual void uploadElementBuffer() {} virtual void loadShader() = 0; virtual void flush(); virtual void startBatch(); @@ -86,7 +95,8 @@ protected: protected: // CPU quad vertices - uint32_t m_quadIndex { 0 }; + uint32_t m_vertexIndex { 0 }; + uint32_t m_elementIndex { 0 }; T* m_vertexBufferBase { nullptr }; T* m_vertexBufferPtr { nullptr }; @@ -102,7 +112,7 @@ protected: }; // TOOD: -// - Deduplicate flush() +// v Deduplicate flush() // v Add bool for disabling depth buffer // - Add Size for uploadData (this is prob not needed, we got T already) // - Decide if its worth to remove template from Renderer, just cast vertexBufferPtr before usage @@ -114,7 +124,7 @@ class Renderer2D final , public ruc::Singleton { public: Renderer2D(s); - virtual ~Renderer2D() {}; + virtual ~Renderer2D() = default; using Singleton::destroy; @@ -136,9 +146,12 @@ private: class RendererCubemap final : public Renderer , public ruc::Singleton { +public: + static constexpr const uint32_t quadPerCube = 6; + public: RendererCubemap(s); - virtual ~RendererCubemap() {}; + virtual ~RendererCubemap() = default; using Singleton::destroy; @@ -161,7 +174,7 @@ class RendererFont final , public ruc::Singleton { public: RendererFont(s); - virtual ~RendererFont() {}; + virtual ~RendererFont() = default; using Singleton::destroy; @@ -171,4 +184,31 @@ private: void loadShader() override; }; +// ------------------------------------- + +class Renderer3D final + : public Renderer + , public ruc::Singleton { +public: + Renderer3D(s); + virtual ~Renderer3D() = default; + + using Singleton::destroy; + + virtual void beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView) override; + + void drawModel(std::span vertices, std::span indices, const TransformComponent& transform, std::shared_ptr texture); + +private: + void createElementBuffer() override; + void uploadElementBuffer() override; + void loadShader() override; + void startBatch() override; + +private: + // CPU element vertices + uint32_t* m_elementBufferBase { nullptr }; + uint32_t* m_elementBufferPtr { nullptr }; +}; + } // namespace Inferno