Riyyi
4 years ago
8 changed files with 639 additions and 23 deletions
@ -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); |
||||||
|
} |
@ -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
|
Loading…
Reference in new issue