Add font batch renderer
This commit is contained in:
@@ -0,0 +1,65 @@
|
|||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 color;
|
||||||
|
|
||||||
|
in vec4 v_color;
|
||||||
|
in vec2 v_textureCoordinates;
|
||||||
|
in flat float v_textureIndex;
|
||||||
|
in float v_width;
|
||||||
|
in float v_edge;
|
||||||
|
in float v_borderWidth;
|
||||||
|
in float v_borderEdge;
|
||||||
|
in vec4 v_borderColor;
|
||||||
|
in flat float v_offset;
|
||||||
|
|
||||||
|
uniform sampler2D u_textures[32];
|
||||||
|
|
||||||
|
float alpha(float textureAlpha)
|
||||||
|
{
|
||||||
|
float distance = 1.0f - textureAlpha;
|
||||||
|
float alpha = 1.0f - smoothstep(v_width, v_width + v_edge, distance);
|
||||||
|
|
||||||
|
return alpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 textureColor = v_color;
|
||||||
|
switch(int(v_textureIndex)) {
|
||||||
|
case 0: break; // Texture unit 0 is reserved for no texture
|
||||||
|
case 1: textureColor.a = alpha(texture(u_textures[1], v_textureCoordinates).a); break;
|
||||||
|
// case 1: textureColor *= texture(u_textures[1], v_textureCoordinates); break;
|
||||||
|
case 2: textureColor.a = alpha(texture(u_textures[2], v_textureCoordinates).a); break;
|
||||||
|
case 3: textureColor.a = alpha(texture(u_textures[3], v_textureCoordinates).a); break;
|
||||||
|
case 4: textureColor.a = alpha(texture(u_textures[4], v_textureCoordinates).a); break;
|
||||||
|
case 5: textureColor.a = alpha(texture(u_textures[5], v_textureCoordinates).a); break;
|
||||||
|
case 6: textureColor.a = alpha(texture(u_textures[6], v_textureCoordinates).a); break;
|
||||||
|
case 7: textureColor.a = alpha(texture(u_textures[7], v_textureCoordinates).a); break;
|
||||||
|
case 8: textureColor.a = alpha(texture(u_textures[8], v_textureCoordinates).a); break;
|
||||||
|
case 9: textureColor.a = alpha(texture(u_textures[9], v_textureCoordinates).a); break;
|
||||||
|
case 10: textureColor.a = alpha(texture(u_textures[10], v_textureCoordinates).a); break;
|
||||||
|
case 11: textureColor.a = alpha(texture(u_textures[11], v_textureCoordinates).a); break;
|
||||||
|
case 12: textureColor.a = alpha(texture(u_textures[12], v_textureCoordinates).a); break;
|
||||||
|
case 13: textureColor.a = alpha(texture(u_textures[13], v_textureCoordinates).a); break;
|
||||||
|
case 14: textureColor.a = alpha(texture(u_textures[14], v_textureCoordinates).a); break;
|
||||||
|
case 15: textureColor.a = alpha(texture(u_textures[15], v_textureCoordinates).a); break;
|
||||||
|
case 16: textureColor.a = alpha(texture(u_textures[16], v_textureCoordinates).a); break;
|
||||||
|
case 17: textureColor.a = alpha(texture(u_textures[17], v_textureCoordinates).a); break;
|
||||||
|
case 18: textureColor.a = alpha(texture(u_textures[18], v_textureCoordinates).a); break;
|
||||||
|
case 19: textureColor.a = alpha(texture(u_textures[19], v_textureCoordinates).a); break;
|
||||||
|
case 20: textureColor.a = alpha(texture(u_textures[20], v_textureCoordinates).a); break;
|
||||||
|
case 21: textureColor.a = alpha(texture(u_textures[21], v_textureCoordinates).a); break;
|
||||||
|
case 22: textureColor.a = alpha(texture(u_textures[22], v_textureCoordinates).a); break;
|
||||||
|
case 23: textureColor.a = alpha(texture(u_textures[23], v_textureCoordinates).a); break;
|
||||||
|
case 24: textureColor.a = alpha(texture(u_textures[24], v_textureCoordinates).a); break;
|
||||||
|
case 25: textureColor.a = alpha(texture(u_textures[25], v_textureCoordinates).a); break;
|
||||||
|
case 26: textureColor.a = alpha(texture(u_textures[26], v_textureCoordinates).a); break;
|
||||||
|
case 27: textureColor.a = alpha(texture(u_textures[27], v_textureCoordinates).a); break;
|
||||||
|
case 28: textureColor.a = alpha(texture(u_textures[28], v_textureCoordinates).a); break;
|
||||||
|
case 29: textureColor.a = alpha(texture(u_textures[29], v_textureCoordinates).a); break;
|
||||||
|
case 30: textureColor.a = alpha(texture(u_textures[30], v_textureCoordinates).a); break;
|
||||||
|
case 31: textureColor.a = alpha(texture(u_textures[31], v_textureCoordinates).a); break;
|
||||||
|
}
|
||||||
|
// textureColor.a = 1; // tmp
|
||||||
|
color = textureColor;
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 a_position;
|
||||||
|
layout(location = 1) in vec4 a_color;
|
||||||
|
layout(location = 2) in vec2 a_textureCoordinates;
|
||||||
|
layout(location = 3) in float a_textureIndex;
|
||||||
|
layout(location = 4) in float a_width;
|
||||||
|
layout(location = 5) in float a_edge;
|
||||||
|
layout(location = 6) in float a_borderWidth;
|
||||||
|
layout(location = 7) in float a_borderEdge;
|
||||||
|
layout(location = 8) in vec4 a_borderColor;
|
||||||
|
layout(location = 9) in float a_offset;
|
||||||
|
|
||||||
|
out vec4 v_color;
|
||||||
|
out vec2 v_textureCoordinates;
|
||||||
|
out flat float v_textureIndex;
|
||||||
|
out float v_width;
|
||||||
|
out float v_edge;
|
||||||
|
out float v_borderWidth;
|
||||||
|
out float v_borderEdge;
|
||||||
|
out vec4 v_borderColor;
|
||||||
|
out flat float v_offset;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
v_color = a_color;
|
||||||
|
v_textureCoordinates = a_textureCoordinates;
|
||||||
|
v_textureIndex = a_textureIndex;
|
||||||
|
v_width = a_width;
|
||||||
|
v_edge = a_edge;
|
||||||
|
v_borderWidth = a_borderWidth;
|
||||||
|
v_borderEdge = a_borderEdge;
|
||||||
|
v_borderColor = a_borderColor;
|
||||||
|
v_offset = a_offset;
|
||||||
|
gl_Position = vec4(a_position, 1.0f);
|
||||||
|
}
|
||||||
@@ -9,13 +9,14 @@
|
|||||||
#include "inferno/input.h"
|
#include "inferno/input.h"
|
||||||
#include "inferno/inputcodes.h"
|
#include "inferno/inputcodes.h"
|
||||||
#include "inferno/log.h"
|
#include "inferno/log.h"
|
||||||
#include "inferno/settings.h"
|
|
||||||
#include "inferno/render/buffer.h"
|
#include "inferno/render/buffer.h"
|
||||||
#include "inferno/render/camera.h"
|
#include "inferno/render/camera.h"
|
||||||
#include "inferno/render/context.h"
|
#include "inferno/render/context.h"
|
||||||
|
#include "inferno/render/font.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"
|
||||||
|
#include "inferno/settings.h"
|
||||||
#include "inferno/time.h"
|
#include "inferno/time.h"
|
||||||
#include "inferno/window.h"
|
#include "inferno/window.h"
|
||||||
|
|
||||||
@@ -49,14 +50,24 @@ namespace Inferno {
|
|||||||
Renderer2D* renderer2D = new Renderer2D();
|
Renderer2D* renderer2D = new Renderer2D();
|
||||||
renderer2D->initialize();
|
renderer2D->initialize();
|
||||||
|
|
||||||
|
RendererCharacter* rendererCharacter = new RendererCharacter();
|
||||||
|
rendererCharacter->initialize();
|
||||||
|
|
||||||
|
FontManager* fontManager = new FontManager();
|
||||||
|
fontManager->initialize();
|
||||||
|
|
||||||
// Load assets
|
// Load assets
|
||||||
|
|
||||||
m_texture = TextureManager::the().load("assets/gfx/test.png");
|
m_texture = TextureManager::the().load("assets/gfx/test.png");
|
||||||
m_texture2 = TextureManager::the().load("assets/gfx/test-inverted.png");
|
m_texture2 = TextureManager::the().load("assets/gfx/test-inverted.png");
|
||||||
|
|
||||||
|
m_font = FontManager::the().load("assets/fnt/dejavu-sans");
|
||||||
}
|
}
|
||||||
|
|
||||||
Application::~Application()
|
Application::~Application()
|
||||||
{
|
{
|
||||||
|
FontManager::the().destroy();
|
||||||
|
RendererCharacter::the().destroy();
|
||||||
Renderer2D::the().destroy();
|
Renderer2D::the().destroy();
|
||||||
TextureManager::the().destroy();
|
TextureManager::the().destroy();
|
||||||
ShaderManager::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});
|
Transform cube3({2.2f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f});
|
||||||
cube3.update();
|
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()) {
|
while(!m_window->shouldClose()) {
|
||||||
|
|
||||||
float time = Time::time();
|
float time = Time::time();
|
||||||
@@ -104,12 +172,16 @@ namespace Inferno {
|
|||||||
RenderCommand::clear();
|
RenderCommand::clear();
|
||||||
|
|
||||||
Renderer2D::the().beginScene(m_cameraP); // camera, lights, environment
|
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>(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>(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);
|
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();
|
Renderer2D::the().endScene();
|
||||||
|
RendererCharacter::the().endScene();
|
||||||
|
|
||||||
m_window->render();
|
m_window->render();
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
namespace Inferno {
|
namespace Inferno {
|
||||||
|
|
||||||
class Event;
|
class Event;
|
||||||
|
class Font;
|
||||||
class KeyPressEvent;
|
class KeyPressEvent;
|
||||||
class MousePositionEvent;
|
class MousePositionEvent;
|
||||||
class OrthographicCamera;
|
class OrthographicCamera;
|
||||||
@@ -49,6 +50,7 @@ namespace Inferno {
|
|||||||
//
|
//
|
||||||
std::shared_ptr<Texture> m_texture;
|
std::shared_ptr<Texture> m_texture;
|
||||||
std::shared_ptr<Texture> m_texture2;
|
std::shared_ptr<Texture> m_texture2;
|
||||||
|
std::shared_ptr<Font> m_font;
|
||||||
//
|
//
|
||||||
|
|
||||||
static Application* s_instance;
|
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
|
// Create shader
|
||||||
m_shader = ShaderManager::the().load("assets/glsl/batch-quad");
|
loadShader();
|
||||||
m_shader->bind();
|
m_shader->bind();
|
||||||
m_shader->setInt("u_textures", samplers, textureUnitPerBatch);
|
m_shader->setInt("u_textures", samplers, textureUnitPerBatch);
|
||||||
m_shader->unbind();
|
m_shader->unbind();
|
||||||
|
|
||||||
|
// Create vertex array
|
||||||
|
m_vertexArray = std::make_shared<VertexArray>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::destroy()
|
void Renderer::destroy()
|
||||||
@@ -121,13 +124,13 @@ namespace Inferno {
|
|||||||
|
|
||||||
void Renderer2D::initialize()
|
void Renderer2D::initialize()
|
||||||
{
|
{
|
||||||
ASSERT(!s_instance, "ShaderManager already exists!");
|
ASSERT(!s_instance, "RendererCharacter already exists!");
|
||||||
s_instance = this;
|
s_instance = this;
|
||||||
|
|
||||||
Renderer::initialize();
|
Renderer::initialize();
|
||||||
|
|
||||||
// CPU
|
// CPU
|
||||||
// -----------------------------------------
|
// ---------------------------------
|
||||||
|
|
||||||
// Create array for storing quads vertices
|
// Create array for storing quads vertices
|
||||||
m_vertexBufferBase = std::unique_ptr<QuadVertex[]>(new QuadVertex[vertexCount]);
|
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[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 };
|
||||||
|
|
||||||
// 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
|
// Generate indices
|
||||||
|
|
||||||
uint32_t* indices = new uint32_t[indexCount];
|
uint32_t* indices = new uint32_t[indexCount];
|
||||||
@@ -171,6 +158,19 @@ namespace Inferno {
|
|||||||
offset += vertexPerQuad;
|
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
|
// Create index buffer
|
||||||
auto indexBuffer = std::make_shared<IndexBuffer>(indices, sizeof(uint32_t) * indexCount);
|
auto indexBuffer = std::make_shared<IndexBuffer>(indices, sizeof(uint32_t) * indexCount);
|
||||||
m_vertexArray->setIndexBuffer(indexBuffer);
|
m_vertexArray->setIndexBuffer(indexBuffer);
|
||||||
@@ -235,6 +235,11 @@ namespace Inferno {
|
|||||||
m_quadIndex++;
|
m_quadIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Renderer2D::loadShader()
|
||||||
|
{
|
||||||
|
m_shader = ShaderManager::the().load("assets/glsl/batch-quad");
|
||||||
|
}
|
||||||
|
|
||||||
void Renderer2D::flush()
|
void Renderer2D::flush()
|
||||||
{
|
{
|
||||||
if (m_quadIndex == 0) {
|
if (m_quadIndex == 0) {
|
||||||
@@ -244,13 +249,13 @@ namespace Inferno {
|
|||||||
// Upload vertex data to GPU
|
// Upload vertex data to GPU
|
||||||
m_vertexArray->getVertexBuffers().at(0)->uploadData(
|
m_vertexArray->getVertexBuffers().at(0)->uploadData(
|
||||||
m_vertexBufferBase.get(),
|
m_vertexBufferBase.get(),
|
||||||
quadCount * vertexPerQuad * sizeof(QuadVertex));
|
m_quadIndex * vertexPerQuad * sizeof(QuadVertex));
|
||||||
|
|
||||||
bind();
|
bind();
|
||||||
|
|
||||||
// Render
|
// Render
|
||||||
m_shader->setFloat("u_projectionView", s_camera->projection() * s_camera->transform()->transform());
|
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();
|
unbind();
|
||||||
}
|
}
|
||||||
@@ -269,4 +274,152 @@ namespace Inferno {
|
|||||||
startBatch();
|
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
|
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 {
|
class RenderCommand {
|
||||||
@@ -52,6 +64,7 @@ namespace Inferno {
|
|||||||
|
|
||||||
void bind();
|
void bind();
|
||||||
void unbind();
|
void unbind();
|
||||||
|
virtual void loadShader() = 0;
|
||||||
virtual void flush() = 0;
|
virtual void flush() = 0;
|
||||||
virtual void startBatch() = 0;
|
virtual void startBatch() = 0;
|
||||||
virtual void nextBatch() = 0;
|
virtual void nextBatch() = 0;
|
||||||
@@ -90,6 +103,7 @@ namespace Inferno {
|
|||||||
static inline Renderer2D& the() { return *s_instance; }
|
static inline Renderer2D& the() { return *s_instance; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void loadShader() override;
|
||||||
void flush() override;
|
void flush() override;
|
||||||
void startBatch() override;
|
void startBatch() override;
|
||||||
void nextBatch() override;
|
void nextBatch() override;
|
||||||
@@ -106,6 +120,38 @@ namespace Inferno {
|
|||||||
static Renderer2D* s_instance;
|
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
|
#endif // RENDERER_H
|
||||||
|
|||||||
Reference in New Issue
Block a user