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