You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
461 lines
12 KiB
461 lines
12 KiB
/* |
|
* Copyright (C) 2022,2024 Riyyi |
|
* |
|
* SPDX-License-Identifier: MIT |
|
*/ |
|
|
|
#include <algorithm> // std::min |
|
#include <utility> // std::move |
|
|
|
#include "glad/glad.h" |
|
#include "ruc/format/log.h" |
|
|
|
#include "inferno/component/transformcomponent.h" |
|
#include "inferno/render/buffer.h" |
|
#include "inferno/render/render-command.h" |
|
#include "inferno/render/renderer.h" |
|
#include "inferno/render/shader.h" |
|
#include "inferno/render/texture.h" |
|
|
|
namespace Inferno { |
|
|
|
template<typename T> |
|
void Renderer<T>::beginScene(glm::mat4, glm::mat4) |
|
{ |
|
} |
|
|
|
template<typename T> |
|
void Renderer<T>::endScene() |
|
{ |
|
nextBatch(); |
|
} |
|
|
|
// ----------------------------------------- |
|
|
|
template<typename T> |
|
uint32_t Renderer<T>::m_maxSupportedTextureSlots = 0; |
|
|
|
template<typename T> |
|
void Renderer<T>::initialize() |
|
{ |
|
// Get amount of texture units supported by the GPU |
|
uint32_t gpuTextureUnitCount = RenderCommand::textureUnitAmount(); |
|
m_maxSupportedTextureSlots = std::min(maxTextureSlots, gpuTextureUnitCount); |
|
|
|
// Texture unit 0 is reserved for no texture |
|
m_textureSlots[0] = nullptr; |
|
|
|
// Create texture unit samplers |
|
int32_t samplers[maxTextureSlots]; |
|
for (uint32_t i = 0; i < maxTextureSlots; i++) { |
|
samplers[i] = i; |
|
} |
|
|
|
// Create shader |
|
loadShader(); |
|
m_shader->bind(); |
|
m_shader->setInt("u_textures", samplers, maxTextureSlots); |
|
m_shader->unbind(); |
|
|
|
// 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; |
|
} |
|
|
|
template<typename T> |
|
void Renderer<T>::destroy() |
|
{ |
|
delete[] m_vertexBufferBase; |
|
} |
|
|
|
template<typename T> |
|
uint32_t Renderer<T>::addTextureUnit(std::shared_ptr<Texture> texture) |
|
{ |
|
if (texture == nullptr) { |
|
return 0; |
|
} |
|
|
|
// Create a new batch if the texture unit limit has been reached |
|
if (m_textureSlotIndex >= m_maxSupportedTextureSlots) { |
|
nextBatch(); |
|
} |
|
|
|
// If texure was already added |
|
for (uint32_t i = 1; i < m_textureSlotIndex; i++) { |
|
if (m_textureSlots[i] == texture) { |
|
return i; |
|
} |
|
} |
|
|
|
// Add texture |
|
uint32_t textureSlotIndex = m_textureSlotIndex; |
|
m_textureSlots[textureSlotIndex] = texture; |
|
m_textureSlotIndex++; |
|
|
|
return textureSlotIndex; |
|
} |
|
|
|
template<typename T> |
|
void Renderer<T>::bind() |
|
{ |
|
m_shader->bind(); |
|
|
|
for (uint32_t i = 1; i < m_textureSlotIndex; i++) { |
|
m_textureSlots[i]->bind(i); |
|
} |
|
|
|
m_vertexArray->bind(); |
|
} |
|
|
|
template<typename T> |
|
void Renderer<T>::unbind() |
|
{ |
|
m_vertexArray->unbind(); |
|
|
|
for (uint32_t i = 1; i < m_textureSlotIndex; i++) { |
|
m_textureSlots[i]->unbind(); |
|
} |
|
|
|
m_shader->unbind(); |
|
} |
|
|
|
template<typename T> |
|
void Renderer<T>::flush() |
|
{ |
|
if (m_quadIndex == 0) { |
|
return; |
|
} |
|
|
|
// Upload vertex data to GPU |
|
m_vertexArray->at(0)->uploadData( |
|
m_vertexBufferBase, |
|
m_quadIndex * vertexPerQuad * sizeof(T)); |
|
|
|
bind(); |
|
|
|
// Render |
|
bool depthTest = RenderCommand::depthTest(); |
|
RenderCommand::setDepthTest(m_enableDepthBuffer); |
|
RenderCommand::drawIndexed(*m_vertexArray, m_quadIndex * indexPerQuad); |
|
RenderCommand::setDepthTest(depthTest); |
|
|
|
unbind(); |
|
} |
|
|
|
template<typename T> |
|
void Renderer<T>::startBatch() |
|
{ |
|
m_quadIndex = 0; |
|
m_vertexBufferPtr = m_vertexBufferBase; |
|
|
|
m_textureSlotIndex = 1; |
|
} |
|
|
|
template<typename T> |
|
void Renderer<T>::nextBatch() |
|
{ |
|
flush(); |
|
startBatch(); |
|
} |
|
|
|
// ----------------------------------------- |
|
|
|
Renderer2D::Renderer2D(s) |
|
{ |
|
Renderer::initialize(); |
|
|
|
// CPU |
|
// --------------------------------- |
|
|
|
// Create array for storing quads vertices |
|
m_vertexBufferBase = new QuadVertex[maxVertices]; |
|
m_vertexBufferPtr = m_vertexBufferBase; |
|
|
|
// Set default quad vertex positions |
|
m_vertexPositions[0] = { -0.5f, -0.5f, 0.0f, 1.0f }; |
|
m_vertexPositions[1] = { 0.5f, -0.5f, 0.0f, 1.0f }; |
|
m_vertexPositions[2] = { 0.5f, 0.5f, 0.0f, 1.0f }; |
|
m_vertexPositions[3] = { -0.5f, 0.5f, 0.0f, 1.0f }; |
|
|
|
// GPU |
|
// --------------------------------- |
|
|
|
// Create vertex buffer |
|
auto vertexBuffer = std::make_shared<VertexBuffer>(sizeof(QuadVertex) * maxVertices); |
|
vertexBuffer->setLayout({ |
|
{ BufferElementType::Vec3, "a_position" }, |
|
{ BufferElementType::Vec4, "a_color" }, |
|
{ BufferElementType::Vec2, "a_textureCoordinates" }, |
|
{ BufferElementType::Float, "a_textureIndex" }, |
|
}); |
|
m_vertexArray->addVertexBuffer(vertexBuffer); |
|
|
|
ruc::info("Renderer2D initialized"); |
|
} |
|
|
|
void Renderer2D::beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView) |
|
{ |
|
m_shader->bind(); |
|
m_shader->setFloat("u_projectionView", cameraProjection * cameraView); |
|
m_shader->unbind(); |
|
} |
|
|
|
void Renderer2D::drawQuad(const TransformComponent& transform, glm::vec4 color) |
|
{ |
|
drawQuad(transform, color, nullptr); |
|
} |
|
|
|
void Renderer2D::drawQuad(const TransformComponent& transform, glm::mat4 color) |
|
{ |
|
drawQuad(transform, color, nullptr); |
|
} |
|
|
|
void Renderer2D::drawQuad(const TransformComponent& transform, glm::vec4 color, std::shared_ptr<Texture> texture) |
|
{ |
|
drawQuad(transform, glm::mat4(color, color, color, color), texture); |
|
} |
|
|
|
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) { |
|
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 < vertexPerQuad; i++) { |
|
m_vertexBufferPtr->position = transform.transform * m_vertexPositions[i]; |
|
m_vertexBufferPtr->color = color[i]; |
|
m_vertexBufferPtr->textureCoordinates = textureCoordinates[i]; |
|
m_vertexBufferPtr->textureIndex = (float)textureUnitIndex; |
|
m_vertexBufferPtr++; |
|
} |
|
|
|
m_quadIndex++; |
|
} |
|
|
|
void Renderer2D::loadShader() |
|
{ |
|
m_shader = ShaderManager::the().load("assets/glsl/batch-quad"); |
|
} |
|
|
|
// ----------------------------------------- |
|
|
|
RendererCubemap::RendererCubemap(s) |
|
{ |
|
Renderer::initialize(); |
|
|
|
// CPU |
|
// --------------------------------- |
|
|
|
// Create array for storing quads vertices |
|
m_vertexBufferBase = new CubemapVertex[maxVertices]; |
|
m_vertexBufferPtr = m_vertexBufferBase; |
|
|
|
// Set default cubemap vertex positions |
|
|
|
// Back face - v |
|
m_vertexPositions[0] = { 0.5f, -0.5f, 0.5f, 1.0f }; |
|
m_vertexPositions[1] = { -0.5f, -0.5f, 0.5f, 1.0f }; |
|
m_vertexPositions[2] = { -0.5f, 0.5f, 0.5f, 1.0f }; |
|
m_vertexPositions[3] = { 0.5f, 0.5f, 0.5f, 1.0f }; |
|
|
|
// Left face - v |
|
m_vertexPositions[7] = { -0.5f, 0.5f, 0.5f, 1.0f }; |
|
m_vertexPositions[6] = { -0.5f, 0.5f, -0.5f, 1.0f }; |
|
m_vertexPositions[5] = { -0.5f, -0.5f, -0.5f, 1.0f }; |
|
m_vertexPositions[4] = { -0.5f, -0.5f, 0.5f, 1.0f }; |
|
|
|
// Right face - v |
|
m_vertexPositions[8] = { 0.5f, -0.5f, -0.5f, 1.0f }; |
|
m_vertexPositions[9] = { 0.5f, -0.5f, 0.5f, 1.0f }; |
|
m_vertexPositions[10] = { 0.5f, 0.5f, 0.5f, 1.0f }; |
|
m_vertexPositions[11] = { 0.5f, 0.5f, -0.5f, 1.0f }; |
|
|
|
// Front face - v |
|
m_vertexPositions[12] = { -0.5f, -0.5f, -0.5f, 1.0f }; |
|
m_vertexPositions[13] = { 0.5f, -0.5f, -0.5f, 1.0f }; |
|
m_vertexPositions[14] = { 0.5f, 0.5f, -0.5f, 1.0f }; |
|
m_vertexPositions[15] = { -0.5f, 0.5f, -0.5f, 1.0f }; |
|
|
|
// Top face |
|
m_vertexPositions[16] = { -0.5f, 0.5f, -0.5f, 1.0f }; |
|
m_vertexPositions[17] = { 0.5f, 0.5f, -0.5f, 1.0f }; |
|
m_vertexPositions[18] = { 0.5f, 0.5f, 0.5f, 1.0f }; |
|
m_vertexPositions[19] = { -0.5f, 0.5f, 0.5f, 1.0f }; |
|
|
|
// Bottom face |
|
m_vertexPositions[20] = { -0.5f, -0.5f, -0.5f, 1.0f }; |
|
m_vertexPositions[21] = { -0.5f, -0.5f, 0.5f, 1.0f }; |
|
m_vertexPositions[22] = { 0.5f, -0.5f, 0.5f, 1.0f }; |
|
m_vertexPositions[23] = { 0.5f, -0.5f, -0.5f, 1.0f }; |
|
|
|
// GPU |
|
// --------------------------------- |
|
|
|
m_enableDepthBuffer = false; |
|
|
|
// Create vertex buffer |
|
auto vertexBuffer = std::make_shared<VertexBuffer>(sizeof(CubemapVertex) * maxVertices); |
|
vertexBuffer->setLayout({ |
|
{ BufferElementType::Vec3, "a_position" }, |
|
{ BufferElementType::Vec4, "a_color" }, |
|
{ BufferElementType::Float, "a_textureIndex" }, |
|
}); |
|
m_vertexArray->addVertexBuffer(vertexBuffer); |
|
|
|
ruc::info("RendererCubemap initialized"); |
|
} |
|
|
|
void RendererCubemap::beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView) |
|
{ |
|
// We want the skybox fixed in position, so only retain the rotation and scale. |
|
// Set the translation of the camera's view matrix to 0, meaning: |
|
// x x x 0 |
|
// x x x 0 |
|
// x x x 0 |
|
// 0 0 0 1 |
|
cameraView = glm::mat4(glm::mat3(cameraView)); |
|
|
|
m_shader->bind(); |
|
m_shader->setFloat("u_projectionView", cameraProjection * cameraView); |
|
m_shader->unbind(); |
|
} |
|
|
|
void RendererCubemap::drawCubemap(const TransformComponent& transform, glm::vec4 color, std::shared_ptr<Texture> texture) |
|
{ |
|
drawCubemap(transform, glm::mat4(color, color, color, color), texture); |
|
} |
|
|
|
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) { |
|
nextBatch(); |
|
} |
|
|
|
uint32_t textureUnitIndex = addTextureUnit(texture); |
|
|
|
// Add the quads 4 vertices, 6 times, once per cube side |
|
for (uint32_t i = 0; i < vertexPerQuad * quadPerCube; i++) { |
|
m_vertexBufferPtr->position = transform.transform * m_vertexPositions[i]; |
|
m_vertexBufferPtr->color = color[i % 4]; |
|
m_vertexBufferPtr->textureIndex = (float)textureUnitIndex; |
|
m_vertexBufferPtr++; |
|
} |
|
|
|
m_quadIndex += quadPerCube; |
|
} |
|
|
|
void RendererCubemap::loadShader() |
|
{ |
|
m_shader = ShaderManager::the().load("assets/glsl/batch-cubemap"); |
|
} |
|
|
|
// ----------------------------------------- |
|
|
|
RendererFont::RendererFont(s) |
|
{ |
|
Renderer::initialize(); |
|
|
|
// CPU |
|
// --------------------------------- |
|
|
|
// Create array for storing quads vertices |
|
m_vertexBufferBase = new SymbolVertex[maxVertices]; |
|
m_vertexBufferPtr = m_vertexBufferBase; |
|
|
|
// GPU |
|
// --------------------------------- |
|
|
|
m_enableDepthBuffer = false; |
|
|
|
// Create vertex buffer |
|
auto vertexBuffer = std::make_shared<VertexBuffer>(sizeof(SymbolVertex) * maxVertices); |
|
vertexBuffer->setLayout({ |
|
{ BufferElementType::Vec3, "a_position" }, |
|
{ BufferElementType::Vec4, "a_color" }, |
|
{ BufferElementType::Vec2, "a_textureCoordinates" }, |
|
{ BufferElementType::Float, "a_textureIndex" }, |
|
{ BufferElementType::Float, "a_width" }, |
|
{ BufferElementType::Float, "a_edge" }, |
|
{ BufferElementType::Float, "a_borderWidth" }, |
|
{ BufferElementType::Float, "a_borderEdge" }, |
|
{ BufferElementType::Vec4, "a_borderColor" }, |
|
{ BufferElementType::Float, "a_offset" }, |
|
}); |
|
m_vertexArray->addVertexBuffer(vertexBuffer); |
|
|
|
ruc::info("RendererFont initialized"); |
|
} |
|
|
|
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) { |
|
nextBatch(); |
|
} |
|
|
|
uint32_t textureUnitIndex = addTextureUnit(texture); |
|
|
|
// Add the quads 4 vertices |
|
for (uint32_t i = 0; i < vertexPerQuad; i++) { |
|
m_vertexBufferPtr->quad.position = symbolQuad[i].quad.position; |
|
m_vertexBufferPtr->quad.color = symbolQuad[i].quad.color; |
|
m_vertexBufferPtr->quad.textureCoordinates = symbolQuad[i].quad.textureCoordinates; |
|
m_vertexBufferPtr->quad.textureIndex = (float)textureUnitIndex; |
|
|
|
m_vertexBufferPtr->width = symbolQuad[i].width; |
|
m_vertexBufferPtr->edge = symbolQuad[i].edge; |
|
m_vertexBufferPtr->borderWidth = symbolQuad[i].borderWidth; |
|
m_vertexBufferPtr->borderEdge = symbolQuad[i].borderEdge; |
|
m_vertexBufferPtr->borderColor = symbolQuad[i].borderColor; |
|
m_vertexBufferPtr->offset = symbolQuad[i].offset; |
|
|
|
m_vertexBufferPtr++; |
|
} |
|
|
|
m_quadIndex++; |
|
} |
|
|
|
void RendererFont::loadShader() |
|
{ |
|
m_shader = ShaderManager::the().load("assets/glsl/batch-font"); |
|
} |
|
|
|
} // namespace Inferno
|
|
|