Compare commits

...

3 Commits

  1. 7
      src/inferno/application.cpp
  2. 42
      src/inferno/render/buffer.h
  3. 22
      src/inferno/render/font.cpp
  4. 14
      src/inferno/render/font.h
  5. 73
      src/inferno/render/render-command.cpp
  6. 31
      src/inferno/render/render-command.h
  7. 433
      src/inferno/render/renderer.cpp
  8. 132
      src/inferno/render/renderer.h
  9. 59
      src/inferno/system/textareasystem.cpp
  10. 13
      src/inferno/system/textareasystem.h
  11. 1
      src/inferno/window.cpp

7
src/inferno/application.cpp

@ -23,6 +23,7 @@
#include "inferno/render/context.h" #include "inferno/render/context.h"
#include "inferno/render/font.h" #include "inferno/render/font.h"
// #include "inferno/render/gltf.h" // #include "inferno/render/gltf.h"
#include "inferno/render/render-command.h"
#include "inferno/render/renderer.h" #include "inferno/render/renderer.h"
#include "inferno/render/shader.h" #include "inferno/render/shader.h"
#include "inferno/render/texture.h" #include "inferno/render/texture.h"
@ -78,7 +79,7 @@ Application::~Application()
m_scene->destroy(); m_scene->destroy();
FontManager::destroy(); FontManager::destroy();
RendererCharacter::destroy(); RendererFont::destroy();
Renderer2D::destroy(); Renderer2D::destroy();
RendererCubemap::destroy(); RendererCubemap::destroy();
RenderCommand::destroy(); RenderCommand::destroy();
@ -170,14 +171,14 @@ int Application::run()
std::pair<glm::mat4, glm::mat4> projectionView = m_scene->cameraProjectionView(); std::pair<glm::mat4, glm::mat4> projectionView = m_scene->cameraProjectionView();
RendererCubemap::the().beginScene(projectionView.first, projectionView.second); // camera, lights, environment RendererCubemap::the().beginScene(projectionView.first, projectionView.second); // camera, lights, environment
Renderer2D::the().beginScene(projectionView.first, projectionView.second); // camera, lights, environment Renderer2D::the().beginScene(projectionView.first, projectionView.second); // camera, lights, environment
RendererCharacter::the().beginScene(); RendererFont::the().beginScene(projectionView.first, projectionView.second); // camera, lights, environment
m_scene->render(); m_scene->render();
// RendererCharacter::the().drawCharacter(character, f->texture()); // RendererCharacter::the().drawCharacter(character, f->texture());
RendererCubemap::the().endScene(); RendererCubemap::the().endScene();
Renderer2D::the().endScene(); Renderer2D::the().endScene();
RendererCharacter::the().endScene(); RendererFont::the().endScene();
m_window->render(); m_window->render();
} }

42
src/inferno/render/buffer.h

@ -42,17 +42,17 @@ public:
static uint32_t getTypeCount(const BufferElementType type); static uint32_t getTypeCount(const BufferElementType type);
static uint32_t getTypeGL(const BufferElementType type); static uint32_t getTypeGL(const BufferElementType type);
inline BufferElementType getType() const { return m_type; } BufferElementType getType() const { return m_type; }
inline std::string getName() const { return m_name; } std::string getName() const { return m_name; }
inline uint32_t getSize() const { return m_size; } uint32_t getSize() const { return m_size; }
inline uint32_t getOffset() const { return m_offset; } uint32_t getOffset() const { return m_offset; }
inline bool getNormalized() const { return m_normalized; } bool getNormalized() const { return m_normalized; }
inline void setType(const BufferElementType& type) { m_type = type; } void setType(const BufferElementType& type) { m_type = type; }
inline void setName(const std::string& name) { m_name = name; } void setName(const std::string& name) { m_name = name; }
inline void setSize(const uint32_t& size) { m_size = size; } void setSize(const uint32_t& size) { m_size = size; }
inline void setOffset(const uint32_t& offset) { m_offset = offset; } void setOffset(const uint32_t& offset) { m_offset = offset; }
inline void setNormalized(const bool& normalized) { m_normalized = normalized; } void setNormalized(const bool& normalized) { m_normalized = normalized; }
private: private:
BufferElementType m_type; BufferElementType m_type;
@ -70,14 +70,14 @@ public:
BufferLayout() {} BufferLayout() {}
BufferLayout(const std::initializer_list<BufferElement>& elements); BufferLayout(const std::initializer_list<BufferElement>& elements);
inline const std::vector<BufferElement>& getElements() const { return m_elements; } const std::vector<BufferElement>& getElements() const { return m_elements; }
inline uint32_t getStride() const { return m_stride; } uint32_t getStride() const { return m_stride; }
// Iterators // Iterators
inline std::vector<BufferElement>::iterator begin() { return m_elements.begin(); } std::vector<BufferElement>::iterator begin() { return m_elements.begin(); }
inline std::vector<BufferElement>::iterator end() { return m_elements.end(); } std::vector<BufferElement>::iterator end() { return m_elements.end(); }
inline std::vector<BufferElement>::const_iterator begin() const { return m_elements.begin(); } std::vector<BufferElement>::const_iterator begin() const { return m_elements.begin(); }
inline std::vector<BufferElement>::const_iterator end() const { return m_elements.end(); } std::vector<BufferElement>::const_iterator end() const { return m_elements.end(); }
protected: protected:
void calculateOffsetsAndStride(); void calculateOffsetsAndStride();
@ -101,7 +101,7 @@ public:
void uploadData(const void* data, uint32_t size); void uploadData(const void* data, uint32_t size);
inline const BufferLayout& getLayout() const { return m_layout; } const BufferLayout& getLayout() const { return m_layout; }
inline void setLayout(const BufferLayout& layout) { m_layout = layout; } inline void setLayout(const BufferLayout& layout) { m_layout = layout; }
@ -121,7 +121,7 @@ public:
void bind() const; void bind() const;
void unbind() const; void unbind() const;
inline uint32_t getCount() const { return m_count; } uint32_t getCount() const { return m_count; }
private: private:
uint32_t m_id { 0 }; uint32_t m_id { 0 };
@ -142,8 +142,8 @@ public:
void addVertexBuffer(std::shared_ptr<VertexBuffer> vertexBuffer); void addVertexBuffer(std::shared_ptr<VertexBuffer> vertexBuffer);
void setIndexBuffer(std::shared_ptr<IndexBuffer> indexBuffer); void setIndexBuffer(std::shared_ptr<IndexBuffer> indexBuffer);
inline const std::vector<std::shared_ptr<VertexBuffer>>& getVertexBuffers() const { return m_vertexBuffers; } std::shared_ptr<VertexBuffer> at(size_t i) const { return m_vertexBuffers.at(i); }
inline std::shared_ptr<IndexBuffer> getIndexBuffer() const { return m_indexBuffer; } std::shared_ptr<IndexBuffer> getIndexBuffer() const { return m_indexBuffer; }
private: private:
uint32_t m_id { 0 }; uint32_t m_id { 0 };

22
src/inferno/render/font.cpp

@ -73,14 +73,14 @@ void Font::parseFont(const std::string& font)
continue; continue;
} }
// Character // Symbol
// --------------------------------- // ---------------------------------
if (action.compare("char") == 0) { if (action.compare("char") == 0) {
unsigned char id = convert<unsigned char>(findValue("id", columns)); auto id = convert<char>(findValue("id", columns));
uint32_t width = convert<uint32_t>(findValue("width", columns)); auto width = convert<uint32_t>(findValue("width", columns));
uint32_t height = convert<uint32_t>(findValue("height", columns)); auto height = convert<uint32_t>(findValue("height", columns));
Character character = { Symbol symbol = {
.id = id, .id = id,
.position = { .position = {
convert<uint32_t>(findValue("x", columns)) + m_padding[Padding::Left], convert<uint32_t>(findValue("x", columns)) + m_padding[Padding::Left],
@ -97,7 +97,7 @@ void Font::parseFont(const std::string& font)
.advance = convert<uint32_t>(findValue("xadvance", columns)) - m_padding[Padding::Left] - m_padding[Padding::Right] .advance = convert<uint32_t>(findValue("xadvance", columns)) - m_padding[Padding::Left] - m_padding[Padding::Right]
}; };
m_characterList.emplace(id, std::make_shared<Character>(character)); m_symbolList.emplace(id, std::make_shared<Symbol>(symbol));
continue; continue;
} }
@ -109,10 +109,10 @@ void Font::parseFont(const std::string& font)
unsigned char second = convert<unsigned char>(findValue("second", columns)); unsigned char second = convert<unsigned char>(findValue("second", columns));
char amount = convert<char>(findValue("amount", columns)); char amount = convert<char>(findValue("amount", columns));
// Add the kerning of the previous character to this character // Add the kerning of the previous symbol to this symbol
if (m_characterList.find(second) != m_characterList.end()) { if (m_symbolList.find(second) != m_symbolList.end()) {
auto character = m_characterList.at(second); auto symbol = m_symbolList.at(second);
character->kernings.emplace(first, amount); symbol->kernings.emplace(first, amount);
} }
continue; continue;
@ -132,7 +132,7 @@ std::vector<std::string> Font::findColumns(const std::string& line) const
size_t index = 0; size_t index = 0;
size_t find = 0; size_t find = 0;
size_t findFirstNotOf = 0; size_t findFirstNotOf = 0;
// Loop over line characters // Loop over line symbols
while (find != std::string::npos) { while (find != std::string::npos) {
find = line.find(" ", index); find = line.find(" ", index);
findFirstNotOf = line.find_first_not_of(" ", index); findFirstNotOf = line.find_first_not_of(" ", index);

14
src/inferno/render/font.h

@ -24,13 +24,13 @@ namespace Inferno {
class Texture; class Texture;
struct Character { struct Symbol {
char id; // Character char id; // Symbol
glm::uvec2 position; // Position glm::uvec2 position; // Position
glm::uvec2 size; // Width/height glm::uvec2 size; // Width/height
glm::ivec2 offset; // Offset from baseline to left / top of glyph glm::ivec2 offset; // Offset from baseline to left / top of glyph
uint32_t advance; // Amount to advance to next glyph uint32_t advance; // Amount to advance to next glyph
std::unordered_map<unsigned char, char> kernings; // Kernings for characters that come before this one std::unordered_map<unsigned char, char> kernings; // Kernings for symbols that come before this one
}; };
// ------------------------------------- // -------------------------------------
@ -52,8 +52,8 @@ public:
inline uint32_t lineSpacing() const { return m_lineSpacing; } inline uint32_t lineSpacing() const { return m_lineSpacing; }
inline std::shared_ptr<Texture> texture() const { return m_texture; } inline std::shared_ptr<Texture> texture() const { return m_texture; }
inline std::shared_ptr<Character> get(unsigned char c) const { return m_characterList.at(c); } inline std::shared_ptr<Symbol> get(unsigned char c) const { return m_symbolList.at(c); }
inline std::shared_ptr<Character> operator[](unsigned char c) const { return m_characterList.at(c); } inline std::shared_ptr<Symbol> operator[](unsigned char c) const { return m_symbolList.at(c); }
private: private:
void parseFont(const std::string& font); void parseFont(const std::string& font);
@ -66,7 +66,7 @@ private:
uint32_t m_lineSpacing = { 0 }; uint32_t m_lineSpacing = { 0 };
std::array<uint32_t, 4> m_padding = { 0 }; std::array<uint32_t, 4> m_padding = { 0 };
std::shared_ptr<Texture> m_texture; std::shared_ptr<Texture> m_texture;
std::unordered_map<unsigned char, std::shared_ptr<Character>> m_characterList; std::unordered_map<unsigned char, std::shared_ptr<Symbol>> m_symbolList;
}; };
// ------------------------------------- // -------------------------------------
@ -98,7 +98,7 @@ struct ruc::format::Formatter<glm::ivec2> : Formatter<std::vector<int32_t>> {
// FontManager fm; // FontManager fm;
// Font f = fm.load("path/to/font"); // Font f = fm.load("path/to/font");
// Font f2("path/to/font"); // Font f2("path/to/font");
// Character c = f['a']; // Symbol c = f['a'];
// Look into using signed distance fields for texture map generation ? anti-aliasing for text // Look into using signed distance fields for texture map generation ? anti-aliasing for text
// https://youtu.be/d8cfgcJR9Tk // https://youtu.be/d8cfgcJR9Tk

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

@ -0,0 +1,73 @@
/*
* Copyright (C) 2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include "glad/glad.h"
#include "ruc/format/log.h"
#include "inferno/render/buffer.h"
#include "inferno/render/render-command.h"
namespace Inferno {
void RenderCommand::initialize()
{
setDepthTest(true);
// Enable transparency
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
ruc::info("RenderCommand initialized");
}
void RenderCommand::destroy()
{
}
void RenderCommand::clear()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
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)
{
uint32_t count = indexCount ? indexCount : vertexArray.getIndexBuffer()->getCount();
glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, nullptr);
}
void RenderCommand::setViewport(int32_t x, int32_t y, uint32_t width, uint32_t height)
{
glViewport(x, y, width, height);
}
void RenderCommand::setDepthTest(bool enabled)
{
// Set z-buffer / depth buffer
enabled ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST);
}
bool RenderCommand::depthTest()
{
unsigned char depthTest = GL_FALSE;
glGetBooleanv(GL_DEPTH_TEST, &depthTest);
return depthTest == GL_TRUE;
}
int32_t RenderCommand::textureUnitAmount()
{
int32_t amount = 0;
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &amount);
return amount;
}
} // namespace Inferno

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

@ -0,0 +1,31 @@
/*
* Copyright (C) 2024 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include "glm/ext/matrix_float4x4.hpp" // glm::mat4
namespace Inferno {
class VertexArray;
class RenderCommand {
public:
static void initialize();
static void destroy();
static void clear();
static void clearColor(const glm::vec4& color);
static void drawIndexed(const 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);
static bool depthTest();
static int32_t textureUnitAmount();
};
} // namespace Inferno

433
src/inferno/render/renderer.cpp

@ -10,151 +10,180 @@
#include "glad/glad.h" #include "glad/glad.h"
#include "ruc/format/log.h" #include "ruc/format/log.h"
#include "inferno/component/transformcomponent.h"
#include "inferno/render/buffer.h" #include "inferno/render/buffer.h"
#include "inferno/render/render-command.h"
#include "inferno/render/renderer.h" #include "inferno/render/renderer.h"
#include "inferno/render/shader.h" #include "inferno/render/shader.h"
#include "inferno/render/texture.h" #include "inferno/render/texture.h"
namespace Inferno { namespace Inferno {
void RenderCommand::initialize() template<typename T>
void Renderer<T>::beginScene(glm::mat4, glm::mat4)
{ {
setDepthTest(true);
// Enable transparency
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
ruc::info("RenderCommand initialized");
}
void RenderCommand::destroy()
{
}
void RenderCommand::clear()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
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)
{
uint32_t count = indexCount ? indexCount : vertexArray.getIndexBuffer()->getCount();
glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, nullptr);
}
void RenderCommand::setViewport(int32_t x, int32_t y, uint32_t width, uint32_t height)
{
glViewport(x, y, width, height);
}
void RenderCommand::setDepthTest(bool enabled)
{
// Set z-buffer / depth buffer
enabled ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST);
}
bool RenderCommand::depthTest()
{
unsigned char depthTest = GL_FALSE;
glGetBooleanv(GL_DEPTH_TEST, &depthTest);
return depthTest == GL_TRUE;
} }
int32_t RenderCommand::textureUnitAmount() template<typename T>
void Renderer<T>::endScene()
{ {
int32_t amount = 0; nextBatch();
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &amount);
return amount;
} }
// ----------------------------------------- // -----------------------------------------
uint32_t Renderer::m_supportedTextureUnitPerBatch = 0; template<typename T>
uint32_t Renderer<T>::m_maxSupportedTextureSlots = 0;
void Renderer::initialize() template<typename T>
void Renderer<T>::initialize()
{ {
// Get amount of texture units supported by the GPU // Get amount of texture units supported by the GPU
uint32_t constTextureUnitCount = textureUnitPerBatch;
uint32_t gpuTextureUnitCount = RenderCommand::textureUnitAmount(); uint32_t gpuTextureUnitCount = RenderCommand::textureUnitAmount();
m_supportedTextureUnitPerBatch = std::min(constTextureUnitCount, gpuTextureUnitCount); m_maxSupportedTextureSlots = std::min(maxTextureSlots, gpuTextureUnitCount);
// Texture unit 0 is reserved for no texture // Texture unit 0 is reserved for no texture
m_textureUnits[0] = nullptr; m_textureSlots[0] = nullptr;
// Create texture unit samplers // Create texture unit samplers
int32_t samplers[textureUnitPerBatch]; int32_t samplers[maxTextureSlots];
for (uint32_t i = 0; i < textureUnitPerBatch; i++) { for (uint32_t i = 0; i < maxTextureSlots; i++) {
samplers[i] = i; samplers[i] = i;
} }
// Create shader // Create shader
loadShader(); loadShader();
m_shader->bind(); m_shader->bind();
m_shader->setInt("u_textures", samplers, textureUnitPerBatch); m_shader->setInt("u_textures", samplers, maxTextureSlots);
m_shader->unbind(); m_shader->unbind();
// Create vertex array // Create vertex array
m_vertexArray = std::make_shared<VertexArray>(); 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;
} }
void Renderer::destroy() template<typename T>
void Renderer<T>::destroy()
{ {
delete[] m_vertexBufferBase;
} }
uint32_t Renderer::addTextureUnit(std::shared_ptr<Texture> texture) template<typename T>
uint32_t Renderer<T>::addTextureUnit(std::shared_ptr<Texture> texture)
{ {
if (texture == nullptr) { if (texture == nullptr) {
return 0; return 0;
} }
// Create a new batch if the texture unit limit has been reached // Create a new batch if the texture unit limit has been reached
if (m_textureUnitIndex >= m_supportedTextureUnitPerBatch) { if (m_textureSlotIndex >= m_maxSupportedTextureSlots) {
nextBatch(); nextBatch();
} }
// If texure was already added // If texure was already added
for (uint32_t i = 1; i < m_textureUnitIndex; i++) { for (uint32_t i = 1; i < m_textureSlotIndex; i++) {
if (m_textureUnits[i] == texture) { if (m_textureSlots[i] == texture) {
return i; return i;
} }
} }
// Add texture // Add texture
uint32_t textureUnitIndex = m_textureUnitIndex; uint32_t textureSlotIndex = m_textureSlotIndex;
m_textureUnits[textureUnitIndex] = texture; m_textureSlots[textureSlotIndex] = texture;
m_textureUnitIndex++; m_textureSlotIndex++;
return textureUnitIndex; return textureSlotIndex;
} }
void Renderer::bind() template<typename T>
void Renderer<T>::bind()
{ {
m_shader->bind(); m_shader->bind();
for (uint32_t i = 1; i < m_textureUnitIndex; i++) { for (uint32_t i = 1; i < m_textureSlotIndex; i++) {
m_textureUnits[i]->bind(i); m_textureSlots[i]->bind(i);
} }
m_vertexArray->bind(); m_vertexArray->bind();
} }
void Renderer::unbind() template<typename T>
void Renderer<T>::unbind()
{ {
m_vertexArray->unbind(); m_vertexArray->unbind();
for (uint32_t i = 1; i < m_textureUnitIndex; i++) { for (uint32_t i = 1; i < m_textureSlotIndex; i++) {
m_textureUnits[i]->unbind(); m_textureSlots[i]->unbind();
} }
m_shader->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) Renderer2D::Renderer2D(s)
@ -165,8 +194,8 @@ Renderer2D::Renderer2D(s)
// --------------------------------- // ---------------------------------
// Create array for storing quads vertices // Create array for storing quads vertices
m_vertexBufferBase = std::make_unique<QuadVertex[]>(vertexCount); m_vertexBufferBase = new QuadVertex[maxVertices];
m_vertexBufferPtr = m_vertexBufferBase.get(); m_vertexBufferPtr = m_vertexBufferBase;
// Set default quad vertex positions // Set default quad vertex positions
m_vertexPositions[0] = { -0.5f, -0.5f, 0.0f, 1.0f }; m_vertexPositions[0] = { -0.5f, -0.5f, 0.0f, 1.0f };
@ -174,27 +203,11 @@ Renderer2D::Renderer2D(s)
m_vertexPositions[2] = { 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 }; m_vertexPositions[3] = { -0.5f, 0.5f, 0.0f, 1.0f };
// Generate indices
uint32_t* indices = new uint32_t[indexCount];
uint32_t offset = 0;
for (uint32_t i = 0; i < indexCount; 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 // GPU
// --------------------------------- // ---------------------------------
// Create vertex buffer // Create vertex buffer
auto vertexBuffer = std::make_shared<VertexBuffer>(sizeof(QuadVertex) * vertexCount); auto vertexBuffer = std::make_shared<VertexBuffer>(sizeof(QuadVertex) * maxVertices);
vertexBuffer->setLayout({ vertexBuffer->setLayout({
{ BufferElementType::Vec3, "a_position" }, { BufferElementType::Vec3, "a_position" },
{ BufferElementType::Vec4, "a_color" }, { BufferElementType::Vec4, "a_color" },
@ -203,19 +216,9 @@ Renderer2D::Renderer2D(s)
}); });
m_vertexArray->addVertexBuffer(vertexBuffer); m_vertexArray->addVertexBuffer(vertexBuffer);
// Create index buffer
auto indexBuffer = std::make_shared<IndexBuffer>(indices, sizeof(uint32_t) * indexCount);
m_vertexArray->setIndexBuffer(indexBuffer);
delete[] indices;
ruc::info("Renderer2D initialized"); ruc::info("Renderer2D initialized");
} }
Renderer2D::~Renderer2D()
{
Renderer::destroy();
}
void Renderer2D::beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView) void Renderer2D::beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView)
{ {
m_shader->bind(); m_shader->bind();
@ -223,11 +226,6 @@ void Renderer2D::beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView)
m_shader->unbind(); m_shader->unbind();
} }
void Renderer2D::endScene()
{
nextBatch();
}
void Renderer2D::drawQuad(const TransformComponent& transform, glm::vec4 color) void Renderer2D::drawQuad(const TransformComponent& transform, glm::vec4 color)
{ {
drawQuad(transform, color, nullptr); drawQuad(transform, color, nullptr);
@ -246,7 +244,7 @@ void Renderer2D::drawQuad(const TransformComponent& transform, glm::vec4 color,
void Renderer2D::drawQuad(const TransformComponent& transform, glm::mat4 color, std::shared_ptr<Texture> 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 // Create a new batch if the quad limit has been reached
if (m_quadIndex >= quadCount) { if (m_quadIndex >= maxQuads) {
nextBatch(); nextBatch();
} }
@ -276,39 +274,6 @@ void Renderer2D::loadShader()
m_shader = ShaderManager::the().load("assets/glsl/batch-quad"); m_shader = ShaderManager::the().load("assets/glsl/batch-quad");
} }
void Renderer2D::flush()
{
if (m_quadIndex == 0) {
return;
}
// Upload vertex data to GPU
m_vertexArray->getVertexBuffers().at(0)->uploadData(
m_vertexBufferBase.get(),
m_quadIndex * vertexPerQuad * sizeof(QuadVertex));
bind();
// Render
RenderCommand::drawIndexed(*m_vertexArray, m_quadIndex * indexPerQuad);
unbind();
}
void Renderer2D::startBatch()
{
m_quadIndex = 0;
m_vertexBufferPtr = m_vertexBufferBase.get();
m_textureUnitIndex = 1;
}
void Renderer2D::nextBatch()
{
flush();
startBatch();
}
// ----------------------------------------- // -----------------------------------------
RendererCubemap::RendererCubemap(s) RendererCubemap::RendererCubemap(s)
@ -319,8 +284,8 @@ RendererCubemap::RendererCubemap(s)
// --------------------------------- // ---------------------------------
// Create array for storing quads vertices // Create array for storing quads vertices
m_vertexBufferBase = std::make_unique<CubemapVertex[]>(vertexCount); m_vertexBufferBase = new CubemapVertex[maxVertices];
m_vertexBufferPtr = m_vertexBufferBase.get(); m_vertexBufferPtr = m_vertexBufferBase;
// Set default cubemap vertex positions // Set default cubemap vertex positions
@ -360,27 +325,13 @@ RendererCubemap::RendererCubemap(s)
m_vertexPositions[22] = { 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 }; m_vertexPositions[23] = { 0.5f, -0.5f, -0.5f, 1.0f };
// Generate indices
uint32_t* indices = new uint32_t[indexCount];
uint32_t offset = 0;
for (uint32_t i = 0; i < indexCount; 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 // GPU
// --------------------------------- // ---------------------------------
m_enableDepthBuffer = false;
// Create vertex buffer // Create vertex buffer
auto vertexBuffer = std::make_shared<VertexBuffer>(sizeof(CubemapVertex) * vertexCount); auto vertexBuffer = std::make_shared<VertexBuffer>(sizeof(CubemapVertex) * maxVertices);
vertexBuffer->setLayout({ vertexBuffer->setLayout({
{ BufferElementType::Vec3, "a_position" }, { BufferElementType::Vec3, "a_position" },
{ BufferElementType::Vec4, "a_color" }, { BufferElementType::Vec4, "a_color" },
@ -388,19 +339,9 @@ RendererCubemap::RendererCubemap(s)
}); });
m_vertexArray->addVertexBuffer(vertexBuffer); m_vertexArray->addVertexBuffer(vertexBuffer);
// Create index buffer
auto indexBuffer = std::make_shared<IndexBuffer>(indices, sizeof(uint32_t) * indexCount);
m_vertexArray->setIndexBuffer(indexBuffer);
delete[] indices;
ruc::info("RendererCubemap initialized"); ruc::info("RendererCubemap initialized");
} }
RendererCubemap::~RendererCubemap()
{
Renderer::destroy();
}
void RendererCubemap::beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView) void RendererCubemap::beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView)
{ {
// We want the skybox fixed in position, so only retain the rotation and scale. // We want the skybox fixed in position, so only retain the rotation and scale.
@ -416,11 +357,6 @@ void RendererCubemap::beginScene(glm::mat4 cameraProjection, glm::mat4 cameraVie
m_shader->unbind(); m_shader->unbind();
} }
void RendererCubemap::endScene()
{
nextBatch();
}
void RendererCubemap::drawCubemap(const TransformComponent& transform, glm::vec4 color, std::shared_ptr<Texture> texture) void RendererCubemap::drawCubemap(const TransformComponent& transform, glm::vec4 color, std::shared_ptr<Texture> texture)
{ {
drawCubemap(transform, glm::mat4(color, color, color, color), texture); drawCubemap(transform, glm::mat4(color, color, color, color), texture);
@ -429,13 +365,13 @@ void RendererCubemap::drawCubemap(const TransformComponent& transform, glm::vec4
void RendererCubemap::drawCubemap(const TransformComponent& transform, glm::mat4 color, std::shared_ptr<Texture> 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 // Create a new batch if the quad limit has been reached
if (m_quadIndex >= quadCount) { if (m_quadIndex >= maxQuads) {
nextBatch(); nextBatch();
} }
uint32_t textureUnitIndex = addTextureUnit(texture); uint32_t textureUnitIndex = addTextureUnit(texture);
// Add the quads 4 vertices // Add the quads 4 vertices, 6 times, once per cube side
for (uint32_t i = 0; i < vertexPerQuad * quadPerCube; i++) { for (uint32_t i = 0; i < vertexPerQuad * quadPerCube; i++) {
m_vertexBufferPtr->position = transform.transform * m_vertexPositions[i]; m_vertexBufferPtr->position = transform.transform * m_vertexPositions[i];
m_vertexBufferPtr->color = color[i % 4]; m_vertexBufferPtr->color = color[i % 4];
@ -451,45 +387,9 @@ void RendererCubemap::loadShader()
m_shader = ShaderManager::the().load("assets/glsl/batch-cubemap"); m_shader = ShaderManager::the().load("assets/glsl/batch-cubemap");
} }
void RendererCubemap::flush()
{
if (m_quadIndex == 0) {
return;
}
// Upload vertex data to GPU
m_vertexArray->getVertexBuffers().at(0)->uploadData(
m_vertexBufferBase.get(),
m_quadIndex * vertexPerQuad * sizeof(CubemapVertex));
bind();
// Render
bool depthTest = RenderCommand::depthTest();
RenderCommand::setDepthTest(false);
RenderCommand::drawIndexed(*m_vertexArray, m_quadIndex * indexPerQuad);
RenderCommand::setDepthTest(depthTest);
unbind();
}
void RendererCubemap::startBatch()
{
m_quadIndex = 0;
m_vertexBufferPtr = m_vertexBufferBase.get();
m_textureUnitIndex = 1;
}
void RendererCubemap::nextBatch()
{
flush();
startBatch();
}
// ----------------------------------------- // -----------------------------------------
RendererCharacter::RendererCharacter(s) RendererFont::RendererFont(s)
{ {
Renderer::initialize(); Renderer::initialize();
@ -497,30 +397,16 @@ RendererCharacter::RendererCharacter(s)
// --------------------------------- // ---------------------------------
// Create array for storing quads vertices // Create array for storing quads vertices
m_vertexBufferBase = std::make_unique<CharacterVertex[]>(vertexCount); m_vertexBufferBase = new SymbolVertex[maxVertices];
m_vertexBufferPtr = m_vertexBufferBase.get(); m_vertexBufferPtr = m_vertexBufferBase;
// Generate indices
uint32_t* indices = new uint32_t[indexCount];
uint32_t offset = 0;
for (uint32_t i = 0; i < indexCount; 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 // GPU
// --------------------------------- // ---------------------------------
m_enableDepthBuffer = false;
// Create vertex buffer // Create vertex buffer
auto vertexBuffer = std::make_shared<VertexBuffer>(sizeof(CharacterVertex) * vertexCount); auto vertexBuffer = std::make_shared<VertexBuffer>(sizeof(SymbolVertex) * maxVertices);
vertexBuffer->setLayout({ vertexBuffer->setLayout({
{ BufferElementType::Vec3, "a_position" }, { BufferElementType::Vec3, "a_position" },
{ BufferElementType::Vec4, "a_color" }, { BufferElementType::Vec4, "a_color" },
@ -535,32 +421,13 @@ RendererCharacter::RendererCharacter(s)
}); });
m_vertexArray->addVertexBuffer(vertexBuffer); m_vertexArray->addVertexBuffer(vertexBuffer);
// Create index buffer ruc::info("RendererFont initialized");
auto indexBuffer = std::make_shared<IndexBuffer>(indices, sizeof(uint32_t) * indexCount);
m_vertexArray->setIndexBuffer(indexBuffer);
delete[] indices;
ruc::info("RendererCharacter initialized");
}
RendererCharacter::~RendererCharacter()
{
Renderer::destroy();
}
void RendererCharacter::beginScene()
{
}
void RendererCharacter::endScene()
{
nextBatch();
} }
void RendererCharacter::drawCharacter(std::array<CharacterVertex, vertexPerQuad>& characterQuad, std::shared_ptr<Texture> texture) void RendererFont::drawSymbol(std::array<SymbolVertex, vertexPerQuad>& symbolQuad, std::shared_ptr<Texture> texture)
{ {
// Create a new batch if the quad limit has been reached // Create a new batch if the quad limit has been reached
if (m_quadIndex >= quadCount) { if (m_quadIndex >= maxQuads) {
nextBatch(); nextBatch();
} }
@ -568,17 +435,17 @@ void RendererCharacter::drawCharacter(std::array<CharacterVertex, vertexPerQuad>
// Add the quads 4 vertices // Add the quads 4 vertices
for (uint32_t i = 0; i < vertexPerQuad; i++) { for (uint32_t i = 0; i < vertexPerQuad; i++) {
m_vertexBufferPtr->quad.position = characterQuad[i].quad.position; m_vertexBufferPtr->quad.position = symbolQuad[i].quad.position;
m_vertexBufferPtr->quad.color = characterQuad[i].quad.color; m_vertexBufferPtr->quad.color = symbolQuad[i].quad.color;
m_vertexBufferPtr->quad.textureCoordinates = characterQuad[i].quad.textureCoordinates; m_vertexBufferPtr->quad.textureCoordinates = symbolQuad[i].quad.textureCoordinates;
m_vertexBufferPtr->quad.textureIndex = (float)textureUnitIndex; m_vertexBufferPtr->quad.textureIndex = (float)textureUnitIndex;
m_vertexBufferPtr->width = characterQuad[i].width; m_vertexBufferPtr->width = symbolQuad[i].width;
m_vertexBufferPtr->edge = characterQuad[i].edge; m_vertexBufferPtr->edge = symbolQuad[i].edge;
m_vertexBufferPtr->borderWidth = characterQuad[i].borderWidth; m_vertexBufferPtr->borderWidth = symbolQuad[i].borderWidth;
m_vertexBufferPtr->borderEdge = characterQuad[i].borderEdge; m_vertexBufferPtr->borderEdge = symbolQuad[i].borderEdge;
m_vertexBufferPtr->borderColor = characterQuad[i].borderColor; m_vertexBufferPtr->borderColor = symbolQuad[i].borderColor;
m_vertexBufferPtr->offset = characterQuad[i].offset; m_vertexBufferPtr->offset = symbolQuad[i].offset;
m_vertexBufferPtr++; m_vertexBufferPtr++;
} }
@ -586,45 +453,9 @@ void RendererCharacter::drawCharacter(std::array<CharacterVertex, vertexPerQuad>
m_quadIndex++; m_quadIndex++;
} }
void RendererCharacter::loadShader() void RendererFont::loadShader()
{ {
m_shader = ShaderManager::the().load("assets/glsl/batch-font"); m_shader = ShaderManager::the().load("assets/glsl/batch-font");
} }
void RendererCharacter::flush()
{
if (m_quadIndex == 0) {
return;
}
// Upload vertex data to GPU
m_vertexArray->getVertexBuffers().at(0)->uploadData(
m_vertexBufferBase.get(),
m_quadIndex * vertexPerQuad * sizeof(CharacterVertex));
bind();
// Render
bool depthTest = RenderCommand::depthTest();
RenderCommand::setDepthTest(false);
RenderCommand::drawIndexed(*m_vertexArray, m_quadIndex * indexPerQuad);
RenderCommand::setDepthTest(depthTest);
unbind();
}
void RendererCharacter::startBatch()
{
m_quadIndex = 0;
m_vertexBufferPtr = m_vertexBufferBase.get();
m_textureUnitIndex = 1;
}
void RendererCharacter::nextBatch()
{
flush();
startBatch();
}
} // namespace Inferno } // namespace Inferno

132
src/inferno/render/renderer.h

@ -15,28 +15,27 @@
#include "glm/ext/vector_float4.hpp" // glm::vec4 #include "glm/ext/vector_float4.hpp" // glm::vec4
#include "ruc/singleton.h" #include "ruc/singleton.h"
#include "inferno/component/transformcomponent.h"
namespace Inferno { namespace Inferno {
class Shader; class Shader;
class Texture; class Texture;
class TransformComponent;
class VertexArray; class VertexArray;
struct QuadVertex { struct QuadVertex {
glm::vec3 position { 0.0f, 0.0f, 0.0f }; glm::vec3 position { 0.0f, 0.0f, 0.0f };
glm::vec4 color { 1.0f, 1.0f, 1.0f, 1.0f }; glm::vec4 color { 1.0f, 1.0f, 1.0f, 1.0f };
glm::vec2 textureCoordinates { 0.0f, 0.0f }; glm::vec2 textureCoordinates { 0.0f, 0.0f };
float textureIndex = 0; // TODO: get int to pass to fragment correctly float textureIndex = 0;
}; };
struct CubemapVertex { struct CubemapVertex {
glm::vec3 position { 0.0f, 0.0f, 0.0f }; glm::vec3 position { 0.0f, 0.0f, 0.0f };
glm::vec4 color { 1.0f, 1.0f, 1.0f, 1.0f }; glm::vec4 color { 1.0f, 1.0f, 1.0f, 1.0f };
float textureIndex = 0; // TODO: get int to pass to fragment correctly float textureIndex = 0;
}; };
struct CharacterVertex { struct SymbolVertex {
QuadVertex quad; QuadVertex quad;
// Font // Font
@ -52,33 +51,25 @@ struct CharacterVertex {
// ------------------------------------- // -------------------------------------
class RenderCommand { template<typename T>
public:
static void initialize();
static void destroy();
static void clear();
static void clearColor(const glm::vec4& color);
static void drawIndexed(const 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);
static bool depthTest();
static int32_t textureUnitAmount();
};
// -------------------------------------
class Renderer { class Renderer {
public: public:
static constexpr const uint32_t vertexPerQuad = 4; static constexpr const uint32_t vertexPerQuad = 4;
static constexpr const uint32_t indexPerQuad = 6; static constexpr const uint32_t indexPerQuad = 6;
static constexpr const uint32_t quadPerCube = 6; static constexpr const uint32_t quadPerCube = 6;
static constexpr const uint32_t textureUnitPerBatch = 32;
// 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 maxTextureSlots = 32;
virtual void beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView);
virtual void endScene();
protected: protected:
Renderer() {} Renderer() {}
virtual ~Renderer() { destroy(); };
void initialize(); void initialize();
void destroy(); void destroy();
@ -87,41 +78,47 @@ protected:
void bind(); void bind();
void unbind(); void unbind();
virtual void loadShader() = 0; virtual void loadShader() = 0;
virtual void flush() = 0; virtual void flush();
virtual void startBatch() = 0; virtual void startBatch();
virtual void nextBatch() = 0; virtual void nextBatch();
uint32_t m_quadIndex = 0; protected:
// CPU quad vertices
uint32_t m_quadIndex { 0 };
T* m_vertexBufferBase { nullptr };
T* m_vertexBufferPtr { nullptr };
// Texture units // Texture units
static uint32_t m_supportedTextureUnitPerBatch; static uint32_t m_maxSupportedTextureSlots;
uint32_t m_textureUnitIndex = 1; uint32_t m_textureSlotIndex { 1 };
std::array<std::shared_ptr<Texture>, textureUnitPerBatch> m_textureUnits; std::array<std::shared_ptr<Texture>, maxTextureSlots> m_textureSlots;
// GPU objects // GPU objects
bool m_enableDepthBuffer { true };
std::shared_ptr<Shader> m_shader; std::shared_ptr<Shader> m_shader;
std::shared_ptr<VertexArray> m_vertexArray; std::shared_ptr<VertexArray> m_vertexArray;
}; };
// TOOD:
// - 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
// ------------------------------------- // -------------------------------------
class Renderer2D final class Renderer2D final
: public Renderer : public Renderer<QuadVertex>
, public ruc::Singleton<Renderer2D> { , public ruc::Singleton<Renderer2D> {
public: public:
Renderer2D(s); Renderer2D(s);
virtual ~Renderer2D(); virtual ~Renderer2D() {};
using Singleton<Renderer2D>::destroy; using Singleton<Renderer2D>::destroy;
// When to start a new batch virtual void beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView) override;
static constexpr const uint32_t quadCount = 1000;
static constexpr const uint32_t vertexCount = quadCount * vertexPerQuad;
static constexpr const uint32_t indexCount = quadCount * indexPerQuad;
void beginScene(glm::mat4 cameraProjectionView, glm::mat4 cameraView);
void endScene();
void drawQuad(const TransformComponent& transform, glm::vec4 color); void drawQuad(const TransformComponent& transform, glm::vec4 color);
void drawQuad(const TransformComponent& transform, glm::mat4 color); void drawQuad(const TransformComponent& transform, glm::mat4 color);
@ -130,13 +127,6 @@ public:
private: private:
void loadShader() override; void loadShader() override;
void flush() override;
void startBatch() override;
void nextBatch() override;
// CPU quad vertices
std::unique_ptr<QuadVertex[]> m_vertexBufferBase;
QuadVertex* m_vertexBufferPtr { nullptr };
// Default quad vertex positions // Default quad vertex positions
glm::vec4 m_vertexPositions[vertexPerQuad]; glm::vec4 m_vertexPositions[vertexPerQuad];
@ -144,35 +134,21 @@ private:
// ------------------------------------- // -------------------------------------
class RendererCubemap final class RendererCubemap final
: public Renderer : public Renderer<CubemapVertex>
, public ruc::Singleton<RendererCubemap> { , public ruc::Singleton<RendererCubemap> {
public: public:
RendererCubemap(s); RendererCubemap(s);
virtual ~RendererCubemap(); virtual ~RendererCubemap() {};
using Singleton<RendererCubemap>::destroy; using Singleton<RendererCubemap>::destroy;
// When to start a new batch virtual void beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView) override;
static constexpr const uint32_t cubemapCount = 166;
static constexpr const uint32_t quadCount = cubemapCount * quadPerCube;
static constexpr const uint32_t vertexCount = quadCount * vertexPerQuad;
static constexpr const uint32_t indexCount = quadCount * indexPerQuad;
void beginScene(glm::mat4 cameraProjectionView, glm::mat4 cameraView);
void endScene();
void drawCubemap(const TransformComponent& transform, glm::vec4 color, std::shared_ptr<Texture> texture); void drawCubemap(const TransformComponent& transform, glm::vec4 color, std::shared_ptr<Texture> texture);
void drawCubemap(const TransformComponent& transform, glm::mat4 color, std::shared_ptr<Texture> texture); void drawCubemap(const TransformComponent& transform, glm::mat4 color, std::shared_ptr<Texture> texture);
private: private:
void loadShader() override; void loadShader() override;
void flush() override;
void startBatch() override;
void nextBatch() override;
// CPU quad vertices
std::unique_ptr<CubemapVertex[]> m_vertexBufferBase;
CubemapVertex* m_vertexBufferPtr { nullptr };
// Default cubemap vertex positions // Default cubemap vertex positions
glm::vec4 m_vertexPositions[vertexPerQuad * quadPerCube]; glm::vec4 m_vertexPositions[vertexPerQuad * quadPerCube];
@ -180,33 +156,19 @@ private:
// ------------------------------------- // -------------------------------------
class RendererCharacter final class RendererFont final
: public Renderer : public Renderer<SymbolVertex>
, public ruc::Singleton<RendererCharacter> { , public ruc::Singleton<RendererFont> {
public: public:
RendererCharacter(s); RendererFont(s);
virtual ~RendererCharacter(); virtual ~RendererFont() {};
using Singleton<RendererCharacter>::destroy;
static const uint32_t quadCount = 1000; using Singleton<RendererFont>::destroy;
static const uint32_t vertexCount = quadCount * vertexPerQuad;
static const uint32_t indexCount = quadCount * indexPerQuad;
void beginScene(); void drawSymbol(std::array<SymbolVertex, vertexPerQuad>& symbolQuad, std::shared_ptr<Texture> texture);
void endScene();
void drawCharacter(std::array<CharacterVertex, vertexPerQuad>& characterQuad, std::shared_ptr<Texture> texture);
private: private:
void loadShader() override; void loadShader() override;
void flush() override;
void startBatch() override;
void nextBatch() override;
// CPU quad vertices
std::unique_ptr<CharacterVertex[]> m_vertexBufferBase;
CharacterVertex* m_vertexBufferPtr { nullptr };
}; };
} // namespace Inferno } // namespace Inferno

59
src/inferno/system/textareasystem.cpp

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022 Riyyi * Copyright (C) 2022-2024 Riyyi
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
@ -11,6 +11,7 @@
#include "inferno/application.h" #include "inferno/application.h"
#include "inferno/component/textareacomponent.h" #include "inferno/component/textareacomponent.h"
#include "inferno/component/transformcomponent.h"
#include "inferno/render/font.h" #include "inferno/render/font.h"
#include "inferno/render/renderer.h" #include "inferno/render/renderer.h"
#include "inferno/render/texture.h" #include "inferno/render/texture.h"
@ -42,19 +43,19 @@ void TextAreaSystem::render()
// Loop through textareas content // Loop through textareas content
// Linebreak if width reached // Linebreak if width reached
// Break if lines AND width reached // Break if lines AND width reached
// Calculate character quad // Calculate symbol quad
// Submit character quad for rendering // Submit symbol quad for rendering
std::shared_ptr<Font> font = FontManager::the().load(textarea.font); std::shared_ptr<Font> font = FontManager::the().load(textarea.font);
// glm::mat4 translate = transform.translate; // glm::mat4 translate = transform.translate;
m_characters.clear(); m_symbols.clear();
createLines(font, textarea); createLines(font, textarea);
createQuads(font, textarea); createQuads(font, textarea);
} }
} }
using Characters = std::vector<std::shared_ptr<Character>>; using Symbols = std::vector<std::shared_ptr<Symbol>>;
void TextAreaSystem::createLines(std::shared_ptr<Font> font, const TextAreaComponent& textarea) void TextAreaSystem::createLines(std::shared_ptr<Font> font, const TextAreaComponent& textarea)
{ {
@ -73,9 +74,9 @@ void TextAreaSystem::createLines(std::shared_ptr<Font> font, const TextAreaCompo
size_t spaceIndex = 0; size_t spaceIndex = 0;
float lineWidth = 0.0f; float lineWidth = 0.0f;
float lineWidthSinceLastSpace = 0.0f; float lineWidthSinceLastSpace = 0.0f;
for (char character : textarea.content) { for (char symbol : textarea.content) {
auto c = font->get(character); auto c = font->get(symbol);
m_characters.push_back(c); m_symbols.push_back(c);
// Kerning // Kerning
char kerning = 0; char kerning = 0;
@ -86,18 +87,18 @@ void TextAreaSystem::createLines(std::shared_ptr<Font> font, const TextAreaCompo
lineWidth += (c->advance + kerning) * fontScale; lineWidth += (c->advance + kerning) * fontScale;
lineWidthSinceLastSpace += (c->advance + kerning) * fontScale; lineWidthSinceLastSpace += (c->advance + kerning) * fontScale;
if (character == ' ') { if (symbol == ' ') {
spaceIndex = m_characters.size() - 1; spaceIndex = m_symbols.size() - 1;
lineWidthSinceLastSpace = 0; lineWidthSinceLastSpace = 0;
} }
if (lineWidth > textureWidth) { if (lineWidth > textureWidth) {
m_characters[spaceIndex] = nullptr; m_symbols[spaceIndex] = nullptr;
lineWidth = 0; lineWidth = 0;
lineWidth = lineWidthSinceLastSpace; lineWidth = lineWidthSinceLastSpace;
} }
previous = character; previous = symbol;
} }
} }
@ -108,26 +109,26 @@ void TextAreaSystem::createQuads(std::shared_ptr<Font> font, const TextAreaCompo
char previous = 0; char previous = 0;
float advanceX = 0.0f; float advanceX = 0.0f;
float advanceY = 0.0f; float advanceY = 0.0f;
for (const auto& character : m_characters) { for (const auto& symbol : m_symbols) {
// Go to the next line on "\n" // Go to the next line on "\n"
if (character == nullptr) { if (symbol == nullptr) {
advanceX = 0; advanceX = 0;
advanceY -= (font->lineSpacing() * textarea.lineSpacing) * fontScale; advanceY -= (font->lineSpacing() * textarea.lineSpacing) * fontScale;
continue; continue;
} }
std::optional<CharacterQuad> quad = calculateCharacterQuad(character, previous, font, fontScale, advanceX, advanceY); std::optional<SymbolQuad> quad = calculateSymbolQuad(symbol, previous, font, fontScale, advanceX, advanceY);
if (quad) { if (quad) {
RendererCharacter::the().drawCharacter(quad.value(), font->texture()); RendererFont::the().drawSymbol(quad.value(), font->texture());
} }
previous = character->id; previous = symbol->id;
} }
} }
std::optional<CharacterQuad> TextAreaSystem::calculateCharacterQuad(std::shared_ptr<Character> c, char previous, std::shared_ptr<Font> font, float fontScale, float& advanceX, float& advanceY) std::optional<SymbolQuad> TextAreaSystem::calculateSymbolQuad(std::shared_ptr<Symbol> c, char previous, std::shared_ptr<Font> font, float fontScale, float& advanceX, float& advanceY)
{ {
CharacterQuad characterQuad; SymbolQuad symbolQuad;
// Texture // Texture
// ------------------------------------- // -------------------------------------
@ -136,7 +137,7 @@ std::optional<CharacterQuad> TextAreaSystem::calculateCharacterQuad(std::shared_
float textureHeight = static_cast<float>(font->texture()->height()); float textureHeight = static_cast<float>(font->texture()->height());
VERIFY(textureWidth == textureHeight, "TextAreaSystem read invalid font texture"); VERIFY(textureWidth == textureHeight, "TextAreaSystem read invalid font texture");
// Skip empty characters (like space) // Skip empty symbols (like space)
if (c->size.x == 0 || c->size.y == 0) { if (c->size.x == 0 || c->size.y == 0) {
// Jump to the next glyph // Jump to the next glyph
advanceX += c->advance * fontScale; advanceX += c->advance * fontScale;
@ -167,10 +168,10 @@ std::optional<CharacterQuad> TextAreaSystem::calculateCharacterQuad(std::shared_
(cursorMax.y / textureHeight * 2) + 1, (cursorMax.y / textureHeight * 2) + 1,
}; };
characterQuad.at(0).quad.position = { cursorScreen.x, cursorScreenMax.y, 0.0f }; // bottom left symbolQuad.at(0).quad.position = { cursorScreen.x, cursorScreenMax.y, 0.0f }; // bottom left
characterQuad.at(1).quad.position = { cursorScreenMax.x, cursorScreenMax.y, 0.0f }; // bottom right symbolQuad.at(1).quad.position = { cursorScreenMax.x, cursorScreenMax.y, 0.0f }; // bottom right
characterQuad.at(2).quad.position = { cursorScreenMax.x, cursorScreen.y, 0.0f }; // top right symbolQuad.at(2).quad.position = { cursorScreenMax.x, cursorScreen.y, 0.0f }; // top right
characterQuad.at(3).quad.position = { cursorScreen.x, cursorScreen.y, 0.0f }; // top left symbolQuad.at(3).quad.position = { cursorScreen.x, cursorScreen.y, 0.0f }; // top left
// Jump to the next glyph // Jump to the next glyph
advanceX += (c->advance + kerning) * fontScale; advanceX += (c->advance + kerning) * fontScale;
@ -187,12 +188,12 @@ std::optional<CharacterQuad> TextAreaSystem::calculateCharacterQuad(std::shared_
(textureHeight - c->position.y) / textureHeight (textureHeight - c->position.y) / textureHeight
}; };
characterQuad.at(0).quad.textureCoordinates = { x.x, y.x }; symbolQuad.at(0).quad.textureCoordinates = { x.x, y.x };
characterQuad.at(1).quad.textureCoordinates = { x.y, y.x }; symbolQuad.at(1).quad.textureCoordinates = { x.y, y.x };
characterQuad.at(2).quad.textureCoordinates = { x.y, y.y }; symbolQuad.at(2).quad.textureCoordinates = { x.y, y.y };
characterQuad.at(3).quad.textureCoordinates = { x.x, y.y }; symbolQuad.at(3).quad.textureCoordinates = { x.x, y.y };
return characterQuad; return symbolQuad;
} }
} // namespace Inferno } // namespace Inferno

13
src/inferno/system/textareasystem.h

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022 Riyyi * Copyright (C) 2022-2024 Riyyi
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
@ -14,17 +14,16 @@
#include "glm/ext/vector_float3.hpp" // glm::vec3 #include "glm/ext/vector_float3.hpp" // glm::vec3
#include "ruc/singleton.h" #include "ruc/singleton.h"
#include "inferno/component/textareacomponent.h"
#include "inferno/render/font.h" #include "inferno/render/font.h"
#include "inferno/render/renderer.h" #include "inferno/render/renderer.h"
namespace Inferno { namespace Inferno {
using Characters = std::vector<std::shared_ptr<Character>>; using Symbols = std::vector<std::shared_ptr<Symbol>>;
using CharacterQuad = std::array<CharacterVertex, Renderer::vertexPerQuad>; using SymbolQuad = std::array<SymbolVertex, Renderer<void>::vertexPerQuad>;
class Font;
class Scene; class Scene;
class TextAreaComponent;
class TextAreaSystem final : public ruc::Singleton<TextAreaSystem> { class TextAreaSystem final : public ruc::Singleton<TextAreaSystem> {
public: public:
@ -39,9 +38,9 @@ private:
void createLines(std::shared_ptr<Font> font, const TextAreaComponent& textarea); void createLines(std::shared_ptr<Font> font, const TextAreaComponent& textarea);
void createQuads(std::shared_ptr<Font> font, const TextAreaComponent& textarea); void createQuads(std::shared_ptr<Font> font, const TextAreaComponent& textarea);
std::optional<CharacterQuad> calculateCharacterQuad(std::shared_ptr<Character> c, char previous, std::shared_ptr<Font> font, float fontSize, float& advanceX, float& advanceY); std::optional<SymbolQuad> calculateSymbolQuad(std::shared_ptr<Symbol> c, char previous, std::shared_ptr<Font> font, float fontSize, float& advanceX, float& advanceY);
Characters m_characters; Symbols m_symbols;
Scene* m_scene { nullptr }; Scene* m_scene { nullptr };
}; };

1
src/inferno/window.cpp

@ -18,6 +18,7 @@
#include "inferno/io/input.h" #include "inferno/io/input.h"
#include "inferno/keycodes.h" #include "inferno/keycodes.h"
#include "inferno/render/context.h" #include "inferno/render/context.h"
#include "inferno/render/render-command.h"
#include "inferno/render/renderer.h" #include "inferno/render/renderer.h"
#include "inferno/settings.h" #include "inferno/settings.h"
#include "inferno/window.h" #include "inferno/window.h"

Loading…
Cancel
Save