Browse Source

Renderer: Implement 3D

master
Riyyi 4 months ago
parent
commit
95a64be4cd
  1. 10
      src/inferno/render/buffer.cpp
  2. 8
      src/inferno/render/buffer.h
  3. 6
      src/inferno/render/render-command.cpp
  4. 6
      src/inferno/render/render-command.h
  5. 191
      src/inferno/render/renderer.cpp
  6. 60
      src/inferno/render/renderer.h

10
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()

8
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();

6
src/inferno/render/render-command.cpp

@ -4,7 +4,7 @@
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <memory> // 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> 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);
}

6
src/inferno/render/render-command.h

@ -6,7 +6,9 @@
#pragma once
#include "glm/ext/matrix_float4x4.hpp" // glm::mat4
#include <memory> // 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> 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);

191
src/inferno/render/renderer.cpp

@ -4,8 +4,8 @@
* SPDX-License-Identifier: MIT
*/
#include <algorithm> // std::min
#include <utility> // std::move
#include <algorithm> // std::copy, std::min
#include <span>
#include "glad/glad.h"
#include "ruc/format/log.h"
@ -61,32 +61,7 @@ void Renderer<T>::initialize()
// Create vertex array
m_vertexArray = std::make_shared<VertexArray>();
// 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<IndexBuffer>(indices, sizeof(uint32_t) * maxIndices);
m_vertexArray->setIndexBuffer(indexBuffer);
delete[] indices;
createElementBuffer();
}
template<typename T>
@ -146,24 +121,56 @@ void Renderer<T>::unbind()
m_shader->unbind();
}
template<typename T>
void Renderer<T>::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<IndexBuffer>(indices, sizeof(uint32_t) * maxElements);
m_vertexArray->setIndexBuffer(indexBuffer);
delete[] indices;
}
template<typename T>
void Renderer<T>::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<T>::flush()
template<typename T>
void Renderer<T>::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> 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> 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<SymbolVertex, vertexPerQuad>& symbolQuad, std::shared_ptr<Texture> 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<SymbolVertex, vertexPerQuad>& 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<Shader>("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<VertexBuffer>(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<const Vertex> vertices, std::span<const uint32_t> indices, const TransformComponent& transform, std::shared_ptr<Texture> 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<IndexBuffer>(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<Shader>("assets/glsl/batch-3d");
}
void Renderer3D::startBatch()
{
Renderer<Vertex>::startBatch();
m_elementBufferPtr = m_elementBufferBase;
}
} // namespace Inferno

60
src/inferno/render/renderer.h

@ -8,6 +8,7 @@
#include <cstdint> // int32_t, uint32_t
#include <memory> // std::shared_ptr, std::unique_ptr, std::make_shared, std::make_unique
#include <span>
#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<typename T>
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<T> from Renderer, just cast vertexBufferPtr before usage
@ -114,7 +124,7 @@ class Renderer2D final
, public ruc::Singleton<Renderer2D> {
public:
Renderer2D(s);
virtual ~Renderer2D() {};
virtual ~Renderer2D() = default;
using Singleton<Renderer2D>::destroy;
@ -136,9 +146,12 @@ private:
class RendererCubemap final
: public Renderer<CubemapVertex>
, public ruc::Singleton<RendererCubemap> {
public:
static constexpr const uint32_t quadPerCube = 6;
public:
RendererCubemap(s);
virtual ~RendererCubemap() {};
virtual ~RendererCubemap() = default;
using Singleton<RendererCubemap>::destroy;
@ -161,7 +174,7 @@ class RendererFont final
, public ruc::Singleton<RendererFont> {
public:
RendererFont(s);
virtual ~RendererFont() {};
virtual ~RendererFont() = default;
using Singleton<RendererFont>::destroy;
@ -171,4 +184,31 @@ private:
void loadShader() override;
};
// -------------------------------------
class Renderer3D final
: public Renderer<Vertex>
, public ruc::Singleton<Renderer3D> {
public:
Renderer3D(s);
virtual ~Renderer3D() = default;
using Singleton<Renderer3D>::destroy;
virtual void beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView) override;
void drawModel(std::span<const Vertex> vertices, std::span<const uint32_t> indices, const TransformComponent& transform, std::shared_ptr<Texture> 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

Loading…
Cancel
Save