Add font batch renderer
This commit is contained in:
@@ -9,13 +9,14 @@
|
||||
#include "inferno/input.h"
|
||||
#include "inferno/inputcodes.h"
|
||||
#include "inferno/log.h"
|
||||
#include "inferno/settings.h"
|
||||
#include "inferno/render/buffer.h"
|
||||
#include "inferno/render/camera.h"
|
||||
#include "inferno/render/context.h"
|
||||
#include "inferno/render/font.h"
|
||||
#include "inferno/render/renderer.h"
|
||||
#include "inferno/render/shader.h"
|
||||
#include "inferno/render/texture.h"
|
||||
#include "inferno/settings.h"
|
||||
#include "inferno/time.h"
|
||||
#include "inferno/window.h"
|
||||
|
||||
@@ -49,14 +50,24 @@ namespace Inferno {
|
||||
Renderer2D* renderer2D = new Renderer2D();
|
||||
renderer2D->initialize();
|
||||
|
||||
RendererCharacter* rendererCharacter = new RendererCharacter();
|
||||
rendererCharacter->initialize();
|
||||
|
||||
FontManager* fontManager = new FontManager();
|
||||
fontManager->initialize();
|
||||
|
||||
// Load assets
|
||||
|
||||
m_texture = TextureManager::the().load("assets/gfx/test.png");
|
||||
m_texture2 = TextureManager::the().load("assets/gfx/test-inverted.png");
|
||||
|
||||
m_font = FontManager::the().load("assets/fnt/dejavu-sans");
|
||||
}
|
||||
|
||||
Application::~Application()
|
||||
{
|
||||
FontManager::the().destroy();
|
||||
RendererCharacter::the().destroy();
|
||||
Renderer2D::the().destroy();
|
||||
TextureManager::the().destroy();
|
||||
ShaderManager::the().destroy();
|
||||
@@ -84,6 +95,63 @@ namespace Inferno {
|
||||
Transform cube3({2.2f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f});
|
||||
cube3.update();
|
||||
|
||||
Transform cube4({0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f});
|
||||
cube4.update();
|
||||
|
||||
std::array<CharacterVertex, Renderer::vertexPerQuad> character;
|
||||
|
||||
// character.at(0).quad.textureCoordinates = { 0.0f, 0.0f }; // bottom left
|
||||
// character.at(1).quad.textureCoordinates = { 1.0f, 0.0f };
|
||||
// character.at(2).quad.textureCoordinates = { 1.0f, 1.0f }; // top right
|
||||
// character.at(3).quad.textureCoordinates = { 0.0f, 1.0f };
|
||||
|
||||
auto f = FontManager::the().get("assets/fnt/dejavu-sans");
|
||||
auto c = f->get('5');
|
||||
dbg() << c->position << " " << c->size;
|
||||
|
||||
uint32_t textureWidth = f->texture()->width();
|
||||
uint32_t textureHeight = f->texture()->height();
|
||||
ASSERT(textureWidth == textureHeight, "Invalid font texture!");
|
||||
|
||||
float quadWidth = (c->size.x / (float)textureWidth) - 0.04; // @Todo something wrong with the width
|
||||
float quadHeight = c->size.y / (float)textureHeight;
|
||||
|
||||
character.at(0).quad.position = { -quadWidth, -quadHeight, 0.0f }; // bottom left
|
||||
character.at(1).quad.position = { quadWidth, -quadHeight, 0.0f }; // bottom right
|
||||
character.at(2).quad.position = { quadWidth, quadHeight, 0.0f }; // top right
|
||||
character.at(3).quad.position = { -quadWidth, quadHeight, 0.0f }; // top left
|
||||
|
||||
glm::vec2 x {
|
||||
1 - (textureWidth - c->position.x) / (float)textureWidth,
|
||||
1 - (textureWidth - c->position.x - c->size.x) / (float)textureWidth
|
||||
};
|
||||
glm::vec2 y {
|
||||
(textureHeight - c->position.y - c->size.y) / (float)textureHeight,
|
||||
(textureHeight - c->position.y) / (float)textureHeight
|
||||
};
|
||||
|
||||
dbg() << y;
|
||||
|
||||
character.at(0).quad.textureCoordinates = { x.x, y.x };
|
||||
character.at(1).quad.textureCoordinates = { x.y, y.x };
|
||||
character.at(2).quad.textureCoordinates = { x.y, y.y };
|
||||
character.at(3).quad.textureCoordinates = { x.x, y.y };
|
||||
|
||||
character.at(0).quad.textureIndex = 1.0f;
|
||||
character.at(1).quad.textureIndex = 1.0f;
|
||||
character.at(2).quad.textureIndex = 1.0f;
|
||||
character.at(3).quad.textureIndex = 1.0f;
|
||||
|
||||
// pos
|
||||
// texcoords
|
||||
//
|
||||
// width
|
||||
// edge
|
||||
// borderwidth
|
||||
// borderedge
|
||||
// bordercolor
|
||||
// offse
|
||||
|
||||
while(!m_window->shouldClose()) {
|
||||
|
||||
float time = Time::time();
|
||||
@@ -104,12 +172,16 @@ namespace Inferno {
|
||||
RenderCommand::clear();
|
||||
|
||||
Renderer2D::the().beginScene(m_cameraP); // camera, lights, environment
|
||||
RendererCharacter::the().beginScene();
|
||||
|
||||
Renderer2D::the().drawQuad(std::make_shared<Transform>(cube), colors);
|
||||
Renderer2D::the().drawQuad(std::make_shared<Transform>(cube2), { 0.5f, 0.6f, 0.8f, 1.0f }, m_texture);
|
||||
Renderer2D::the().drawQuad(std::make_shared<Transform>(cube3), { 1.0f, 1.0f, 1.0f, 1.0f }, m_texture2);
|
||||
|
||||
RendererCharacter::the().drawCharacter(character, {1,1,1,1}, f->texture());
|
||||
|
||||
Renderer2D::the().endScene();
|
||||
RendererCharacter::the().endScene();
|
||||
|
||||
m_window->render();
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
namespace Inferno {
|
||||
|
||||
class Event;
|
||||
class Font;
|
||||
class KeyPressEvent;
|
||||
class MousePositionEvent;
|
||||
class OrthographicCamera;
|
||||
@@ -49,6 +50,7 @@ namespace Inferno {
|
||||
//
|
||||
std::shared_ptr<Texture> m_texture;
|
||||
std::shared_ptr<Texture> m_texture2;
|
||||
std::shared_ptr<Font> m_font;
|
||||
//
|
||||
|
||||
static Application* s_instance;
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
#include <bits/stdint-uintn.h>
|
||||
#include <string> // std::getline
|
||||
|
||||
#include "inferno/assertions.h"
|
||||
#include "inferno/file.h"
|
||||
#include "inferno/render/font.h"
|
||||
#include "inferno/render/texture.h"
|
||||
|
||||
namespace Inferno {
|
||||
|
||||
Font::Font(const std::string& name)
|
||||
{
|
||||
std::string path = name + ".fnt";
|
||||
std::string image = name + ".png";
|
||||
|
||||
std::string font = File::read(path);
|
||||
parseFont(font);
|
||||
|
||||
m_texture = std::make_shared<Texture>(image);
|
||||
}
|
||||
|
||||
void Font::parseFont(const std::string& font)
|
||||
{
|
||||
std::istringstream iss(font);
|
||||
for (std::string line; std::getline(iss, line);) {
|
||||
|
||||
if (findAction(line).compare("char") != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::vector<std::string> data = findColumns(line);
|
||||
|
||||
unsigned char id = std::stoi(findValue("id", data));
|
||||
Character character = {
|
||||
{ std::stoi(findValue("x", data)), std::stoi(findValue("y", data)) },
|
||||
{ std::stoi(findValue("width", data)), std::stoi(findValue("height", data)) },
|
||||
{ std::stoi(findValue("xoffset", data)), std::stoi(findValue("yoffset", data)) },
|
||||
std::stoi(findValue("xadvance", data))
|
||||
};
|
||||
m_characterList.emplace(id, std::make_shared<Character>(character));
|
||||
}
|
||||
}
|
||||
|
||||
const std::string Font::findAction(const std::string& line)
|
||||
{
|
||||
return line.substr(0, line.find(" "));
|
||||
}
|
||||
|
||||
const std::vector<std::string> Font::findColumns(const std::string& line)
|
||||
{
|
||||
std::vector<std::string> elements;
|
||||
|
||||
size_t index = 0;
|
||||
size_t find = 0;
|
||||
size_t findFirstNotOf = 0;
|
||||
// Loop over line characters
|
||||
while (find != std::string::npos) {
|
||||
find = line.find(" ", index);
|
||||
findFirstNotOf = line.find_first_not_of(" ", index);
|
||||
|
||||
// Add element
|
||||
if (find > findFirstNotOf) {
|
||||
elements.push_back(line.substr(index, find - findFirstNotOf));
|
||||
index += 1 + find - findFirstNotOf;
|
||||
}
|
||||
// Skip double spaces
|
||||
else {
|
||||
index = findFirstNotOf;
|
||||
}
|
||||
}
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
const std::string Font::findValue(const std::string& key, const std::vector<std::string>& columns)
|
||||
{
|
||||
size_t find = 0;
|
||||
// Loop over columns
|
||||
for (auto it = columns.begin(); it != columns.end(); it++) {
|
||||
find = it->find(key + "=");
|
||||
if (find != std::string::npos) {
|
||||
return it->substr(key.length() + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
FontManager* FontManager::s_instance = nullptr;
|
||||
|
||||
void FontManager::initialize()
|
||||
{
|
||||
ASSERT(!s_instance, "FontManager already exists!");
|
||||
s_instance = this;
|
||||
|
||||
dbg(Log::Info) << "FontManager initialized";
|
||||
}
|
||||
|
||||
void FontManager::destroy()
|
||||
{
|
||||
delete s_instance;
|
||||
s_instance = nullptr;
|
||||
}
|
||||
|
||||
void FontManager::add(const std::string& name, const std::shared_ptr<Font>& font)
|
||||
{
|
||||
// Construct (key, value) pair and insert it into the unordered_map
|
||||
m_fontList.emplace(name, font);
|
||||
}
|
||||
|
||||
std::shared_ptr<Font> FontManager::load(const std::string& name)
|
||||
{
|
||||
if (exists(name)) {
|
||||
return get(name);
|
||||
}
|
||||
|
||||
std::shared_ptr<Font> font = std::make_shared<Font>(name);
|
||||
add(name, font);
|
||||
return get(name);
|
||||
}
|
||||
|
||||
std::shared_ptr<Font> FontManager::get(const std::string& name)
|
||||
{
|
||||
return exists(name) ? m_fontList.at(name) : nullptr;
|
||||
}
|
||||
|
||||
bool FontManager::exists(const std::string& name)
|
||||
{
|
||||
return m_fontList.find(name) != m_fontList.end();
|
||||
}
|
||||
|
||||
void FontManager::remove(const std::string& name)
|
||||
{
|
||||
if (exists(name)) {
|
||||
m_fontList.erase(name);
|
||||
}
|
||||
}
|
||||
|
||||
void FontManager::remove(const std::shared_ptr<Font>& font)
|
||||
{
|
||||
if (exists(font->name())) {
|
||||
m_fontList.erase(font->name());
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
const LogStream& operator<<(const LogStream& stream, const glm::ivec2& value)
|
||||
{
|
||||
return stream << "{ " << value.x << ", " << value.y << " }";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
#ifndef FONT_H
|
||||
#define FONT_H
|
||||
|
||||
#include <cstdint> // int32_t
|
||||
#include <memory> // std::shared_ptr
|
||||
#include <string> // std::string
|
||||
#include <unordered_map> // std::unordered_map
|
||||
#include <vector> // std::vector
|
||||
|
||||
#include <glm/ext/vector_int2.hpp> // glm::ivec2
|
||||
|
||||
#include "inferno/log.h"
|
||||
|
||||
namespace Inferno {
|
||||
|
||||
class Texture;
|
||||
|
||||
struct Character {
|
||||
glm::ivec2 position; // Position
|
||||
glm::ivec2 size; // Width/height
|
||||
glm::ivec2 offset; // Offset from baseline to left / top of glyph
|
||||
int32_t advance; // Amount to advance to next glyph
|
||||
};
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
class Font {
|
||||
public:
|
||||
Font(const std::string& name);
|
||||
virtual ~Font() {}
|
||||
|
||||
inline const std::shared_ptr<Character>& get(unsigned char c) const { return m_characterList.at(c); }
|
||||
inline const std::shared_ptr<Character>& operator[](unsigned char c) const { return m_characterList.at(c); }
|
||||
|
||||
inline std::string name() const { return m_name; }
|
||||
inline const std::shared_ptr<Texture>& texture() const { return m_texture; }
|
||||
|
||||
private:
|
||||
void parseFont(const std::string& font);
|
||||
const std::string findAction(const std::string& line);
|
||||
const std::vector<std::string> findColumns(const std::string& line);
|
||||
const std::string findValue(const std::string& key, const std::vector<std::string>& columns);
|
||||
|
||||
std::string m_name;
|
||||
std::shared_ptr<Texture> m_texture;
|
||||
std::unordered_map<unsigned char, std::shared_ptr<Character>> m_characterList;
|
||||
};
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
class FontManager {
|
||||
public:
|
||||
void initialize();
|
||||
void destroy();
|
||||
|
||||
void add(const std::string& name, const std::shared_ptr<Font>& font);
|
||||
std::shared_ptr<Font> load(const std::string& name);
|
||||
std::shared_ptr<Font> get(const std::string& name);
|
||||
bool exists(const std::string& name);
|
||||
|
||||
void remove(const std::string& name);
|
||||
void remove(const std::shared_ptr<Font>& shader);
|
||||
|
||||
static inline FontManager& the() { return *s_instance; }
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, std::shared_ptr<Font>> m_fontList;
|
||||
|
||||
static FontManager* s_instance;
|
||||
};
|
||||
|
||||
|
||||
// ----------------------------------------
|
||||
|
||||
const LogStream& operator<<(const LogStream& stream, const glm::ivec2& value);
|
||||
|
||||
}
|
||||
|
||||
#endif // FONT_H
|
||||
|
||||
// FontManager fm;
|
||||
// Font f = fm.load("path/to/font");
|
||||
// Font f2("path/to/font");
|
||||
// Character c = f['a'];
|
||||
|
||||
// Look into using signed distance fields for texture map generation ? anti-aliasing for text
|
||||
// https://youtu.be/d8cfgcJR9Tk
|
||||
@@ -57,10 +57,13 @@ namespace Inferno {
|
||||
}
|
||||
|
||||
// Create shader
|
||||
m_shader = ShaderManager::the().load("assets/glsl/batch-quad");
|
||||
loadShader();
|
||||
m_shader->bind();
|
||||
m_shader->setInt("u_textures", samplers, textureUnitPerBatch);
|
||||
m_shader->unbind();
|
||||
|
||||
// Create vertex array
|
||||
m_vertexArray = std::make_shared<VertexArray>();
|
||||
}
|
||||
|
||||
void Renderer::destroy()
|
||||
@@ -121,13 +124,13 @@ namespace Inferno {
|
||||
|
||||
void Renderer2D::initialize()
|
||||
{
|
||||
ASSERT(!s_instance, "ShaderManager already exists!");
|
||||
ASSERT(!s_instance, "RendererCharacter already exists!");
|
||||
s_instance = this;
|
||||
|
||||
Renderer::initialize();
|
||||
|
||||
// CPU
|
||||
// -----------------------------------------
|
||||
// CPU
|
||||
// ---------------------------------
|
||||
|
||||
// Create array for storing quads vertices
|
||||
m_vertexBufferBase = std::unique_ptr<QuadVertex[]>(new QuadVertex[vertexCount]);
|
||||
@@ -139,22 +142,6 @@ namespace Inferno {
|
||||
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 array
|
||||
m_vertexArray = std::make_shared<VertexArray>();
|
||||
|
||||
// Create vertex buffer
|
||||
auto vertexBuffer = std::make_shared<VertexBuffer>(sizeof(QuadVertex) * vertexCount);
|
||||
vertexBuffer->setLayout({
|
||||
{ BufferElementType::Vec3, "a_position" },
|
||||
{ BufferElementType::Vec4, "a_color" },
|
||||
{ BufferElementType::Vec2, "a_textureCoordinates" },
|
||||
{ BufferElementType::Float, "a_textureIndex" },
|
||||
});
|
||||
m_vertexArray->addVertexBuffer(std::move(vertexBuffer));
|
||||
|
||||
// Generate indices
|
||||
|
||||
uint32_t* indices = new uint32_t[indexCount];
|
||||
@@ -171,6 +158,19 @@ namespace Inferno {
|
||||
offset += vertexPerQuad;
|
||||
}
|
||||
|
||||
// GPU
|
||||
// ---------------------------------
|
||||
|
||||
// Create vertex buffer
|
||||
auto vertexBuffer = std::make_shared<VertexBuffer>(sizeof(QuadVertex) * vertexCount);
|
||||
vertexBuffer->setLayout({
|
||||
{ BufferElementType::Vec3, "a_position" },
|
||||
{ BufferElementType::Vec4, "a_color" },
|
||||
{ BufferElementType::Vec2, "a_textureCoordinates" },
|
||||
{ BufferElementType::Float, "a_textureIndex" },
|
||||
});
|
||||
m_vertexArray->addVertexBuffer(std::move(vertexBuffer));
|
||||
|
||||
// Create index buffer
|
||||
auto indexBuffer = std::make_shared<IndexBuffer>(indices, sizeof(uint32_t) * indexCount);
|
||||
m_vertexArray->setIndexBuffer(indexBuffer);
|
||||
@@ -235,6 +235,11 @@ namespace Inferno {
|
||||
m_quadIndex++;
|
||||
}
|
||||
|
||||
void Renderer2D::loadShader()
|
||||
{
|
||||
m_shader = ShaderManager::the().load("assets/glsl/batch-quad");
|
||||
}
|
||||
|
||||
void Renderer2D::flush()
|
||||
{
|
||||
if (m_quadIndex == 0) {
|
||||
@@ -244,13 +249,13 @@ namespace Inferno {
|
||||
// Upload vertex data to GPU
|
||||
m_vertexArray->getVertexBuffers().at(0)->uploadData(
|
||||
m_vertexBufferBase.get(),
|
||||
quadCount * vertexPerQuad * sizeof(QuadVertex));
|
||||
m_quadIndex * vertexPerQuad * sizeof(QuadVertex));
|
||||
|
||||
bind();
|
||||
|
||||
// Render
|
||||
m_shader->setFloat("u_projectionView", s_camera->projection() * s_camera->transform()->transform());
|
||||
RenderCommand::drawIndexed(m_vertexArray, quadCount * indexPerQuad);
|
||||
RenderCommand::drawIndexed(m_vertexArray, m_quadIndex * indexPerQuad);
|
||||
|
||||
unbind();
|
||||
}
|
||||
@@ -269,4 +274,152 @@ namespace Inferno {
|
||||
startBatch();
|
||||
}
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
RendererCharacter* RendererCharacter::s_instance = nullptr;
|
||||
|
||||
void RendererCharacter::initialize()
|
||||
{
|
||||
ASSERT(!s_instance, "RendererCharacter already exists!");
|
||||
s_instance = this;
|
||||
|
||||
Renderer::initialize();
|
||||
|
||||
// CPU
|
||||
// ---------------------------------
|
||||
|
||||
// Create array for storing quads vertices
|
||||
m_vertexBufferBase = std::unique_ptr<CharacterVertex[]>(new CharacterVertex[vertexCount]);
|
||||
m_vertexBufferPtr = m_vertexBufferBase.get();
|
||||
|
||||
// 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
|
||||
// ---------------------------------
|
||||
|
||||
// Create vertex buffer
|
||||
auto vertexBuffer = std::make_shared<VertexBuffer>(sizeof(CharacterVertex) * vertexCount);
|
||||
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(std::move(vertexBuffer));
|
||||
|
||||
// Create index buffer
|
||||
auto indexBuffer = std::make_shared<IndexBuffer>(indices, sizeof(uint32_t) * indexCount);
|
||||
m_vertexArray->setIndexBuffer(indexBuffer);
|
||||
delete[] indices;
|
||||
|
||||
dbg() << "RendererCharacter initialized";
|
||||
}
|
||||
|
||||
void RendererCharacter::destroy()
|
||||
{
|
||||
delete s_instance;
|
||||
s_instance = nullptr;
|
||||
}
|
||||
|
||||
void RendererCharacter::beginScene()
|
||||
{
|
||||
}
|
||||
|
||||
void RendererCharacter::endScene()
|
||||
{
|
||||
nextBatch();
|
||||
}
|
||||
|
||||
void RendererCharacter::drawCharacter(std::array<CharacterVertex, vertexPerQuad>& characterQuad, glm::vec4 color, std::shared_ptr<Texture> texture)
|
||||
{
|
||||
drawCharacter(characterQuad, glm::mat4 { color, color, color, color }, texture);
|
||||
}
|
||||
|
||||
void RendererCharacter::drawCharacter(std::array<CharacterVertex, vertexPerQuad>& characterQuad, glm::mat4 color, std::shared_ptr<Texture> texture)
|
||||
{
|
||||
// Create a new batch if the quad limit has been reached
|
||||
if (m_quadIndex >= quadCount) {
|
||||
nextBatch();
|
||||
}
|
||||
|
||||
uint32_t textureUnitIndex = addTextureUnit(texture);
|
||||
|
||||
// Add the quads 4 vertices
|
||||
for (uint32_t i = 0; i < vertexPerQuad; i++) {
|
||||
m_vertexBufferPtr->quad.position = characterQuad[i].quad.position;
|
||||
m_vertexBufferPtr->quad.color = characterQuad[i].quad.color;
|
||||
m_vertexBufferPtr->quad.textureCoordinates = characterQuad[i].quad.textureCoordinates;
|
||||
m_vertexBufferPtr->quad.textureIndex = (float)textureUnitIndex;
|
||||
|
||||
m_vertexBufferPtr->width = characterQuad[i].width;
|
||||
m_vertexBufferPtr->edge = characterQuad[i].edge;
|
||||
m_vertexBufferPtr->borderWidth = characterQuad[i].borderWidth;
|
||||
m_vertexBufferPtr->borderEdge = characterQuad[i].borderEdge;
|
||||
m_vertexBufferPtr->borderColor = characterQuad[i].borderColor;
|
||||
m_vertexBufferPtr->offset = characterQuad[i].offset;
|
||||
|
||||
m_vertexBufferPtr++;
|
||||
}
|
||||
|
||||
m_quadIndex++;
|
||||
}
|
||||
|
||||
void RendererCharacter::loadShader()
|
||||
{
|
||||
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
|
||||
RenderCommand::drawIndexed(m_vertexArray, m_quadIndex * indexPerQuad);
|
||||
|
||||
unbind();
|
||||
}
|
||||
|
||||
void RendererCharacter::startBatch()
|
||||
{
|
||||
m_quadIndex = 0;
|
||||
m_vertexBufferPtr = m_vertexBufferBase.get();
|
||||
|
||||
m_textureUnitIndex = 1;
|
||||
}
|
||||
|
||||
void RendererCharacter::nextBatch()
|
||||
{
|
||||
flush();
|
||||
startBatch();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,6 +24,18 @@ namespace Inferno {
|
||||
float textureIndex = 0; // @Todo get int to pass to fragment correctly
|
||||
};
|
||||
|
||||
struct CharacterVertex {
|
||||
QuadVertex quad;
|
||||
|
||||
float width = 0.44f;
|
||||
float edge = 0.15f;
|
||||
|
||||
float borderWidth = 0.7f;
|
||||
float borderEdge = 0.1f;
|
||||
glm::vec4 borderColor { 1.0f, 1.0f, 1.0f, 1.0f };
|
||||
float offset = 0.0f;
|
||||
};
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
class RenderCommand {
|
||||
@@ -52,6 +64,7 @@ namespace Inferno {
|
||||
|
||||
void bind();
|
||||
void unbind();
|
||||
virtual void loadShader() = 0;
|
||||
virtual void flush() = 0;
|
||||
virtual void startBatch() = 0;
|
||||
virtual void nextBatch() = 0;
|
||||
@@ -90,6 +103,7 @@ namespace Inferno {
|
||||
static inline Renderer2D& the() { return *s_instance; }
|
||||
|
||||
private:
|
||||
void loadShader() override;
|
||||
void flush() override;
|
||||
void startBatch() override;
|
||||
void nextBatch() override;
|
||||
@@ -106,6 +120,38 @@ namespace Inferno {
|
||||
static Renderer2D* s_instance;
|
||||
};
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
class RendererCharacter final : public Renderer {
|
||||
public:
|
||||
static const uint32_t quadCount = 1000;
|
||||
static const uint32_t vertexCount = quadCount * vertexPerQuad;
|
||||
static const uint32_t indexCount = quadCount * indexPerQuad;
|
||||
|
||||
void initialize() override;
|
||||
void destroy() override;
|
||||
|
||||
void beginScene();
|
||||
void endScene();
|
||||
|
||||
void drawCharacter(std::array<CharacterVertex, vertexPerQuad>& characterQuad, glm::vec4 color, std::shared_ptr<Texture> texture);
|
||||
void drawCharacter(std::array<CharacterVertex, vertexPerQuad>& characterQuad, glm::mat4 color, std::shared_ptr<Texture> texture);
|
||||
|
||||
static inline RendererCharacter& the() { return *s_instance; }
|
||||
|
||||
private:
|
||||
void loadShader() override;
|
||||
void flush() override;
|
||||
void startBatch() override;
|
||||
void nextBatch() override;
|
||||
|
||||
// CPU quad vertices
|
||||
std::unique_ptr<CharacterVertex[]> m_vertexBufferBase = nullptr;
|
||||
CharacterVertex* m_vertexBufferPtr = nullptr;
|
||||
|
||||
static RendererCharacter* s_instance;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // RENDERER_H
|
||||
|
||||
Reference in New Issue
Block a user