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