Renderer: Implement 3D
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,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);
|
||||
|
||||
+151
-40
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user