Engine: Allow configuration of font size and line spacing
This commit is contained in:
@@ -42,9 +42,9 @@
|
||||
"name": "Text",
|
||||
"content": "Hello World!",
|
||||
"font": "assets/fnt/open-sans",
|
||||
"font-size": 10,
|
||||
"width": 150,
|
||||
"lines": 3
|
||||
"font-size": 24,
|
||||
"line-spacing": 1.0,
|
||||
"width": 150
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -23,12 +23,12 @@ void fromJson(const ruc::Json& json, TextAreaComponent& value)
|
||||
if (json.exists("font-size") && json.at("font-size").type() == ruc::Json::Type::Number) {
|
||||
json.at("font-size").getTo(value.fontSize);
|
||||
}
|
||||
if (json.exists("line-spacing") && json.at("line-spacing").type() == ruc::Json::Type::Number) {
|
||||
json.at("line-spacing").getTo(value.lineSpacing);
|
||||
}
|
||||
if (json.exists("width") && json.at("width").type() == ruc::Json::Type::Number) {
|
||||
json.at("width").getTo(value.width);
|
||||
}
|
||||
if (json.exists("lines") && json.at("lines").type() == ruc::Json::Type::Number) {
|
||||
json.at("lines").getTo(value.lines);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Inferno
|
||||
|
||||
@@ -18,9 +18,9 @@ namespace Inferno {
|
||||
struct TextAreaComponent {
|
||||
std::string content;
|
||||
std::string font;
|
||||
uint32_t fontSize { 0 };
|
||||
unsigned char fontSize { 0 };
|
||||
float lineSpacing { 1.0f };
|
||||
uint32_t width { 0 };
|
||||
uint32_t lines { 0 };
|
||||
|
||||
#if 0
|
||||
TextAreaComponent() {}
|
||||
|
||||
@@ -55,7 +55,7 @@ void Font::parseFont(const std::string& font)
|
||||
// ---------------------------------
|
||||
|
||||
if (action.compare("info") == 0) {
|
||||
m_size = convert<uint32_t>(findValue("size", columns));
|
||||
m_size = convert<unsigned char>(findValue("size", columns));
|
||||
auto paddings = findValue("padding", columns) | std::views::split(',');
|
||||
size_t i = 0;
|
||||
for (const auto& padding : paddings) {
|
||||
@@ -81,6 +81,7 @@ void Font::parseFont(const std::string& font)
|
||||
uint32_t width = convert<uint32_t>(findValue("width", columns));
|
||||
uint32_t height = convert<uint32_t>(findValue("height", columns));
|
||||
Character character = {
|
||||
.id = id,
|
||||
.position = {
|
||||
convert<uint32_t>(findValue("x", columns)) + m_padding[Padding::Left],
|
||||
convert<uint32_t>(findValue("y", columns)) + m_padding[Padding::Top],
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace Inferno {
|
||||
class Texture;
|
||||
|
||||
struct Character {
|
||||
char id; // Character
|
||||
glm::uvec2 position; // Position
|
||||
glm::uvec2 size; // Width/height
|
||||
glm::ivec2 offset; // Offset from baseline to left / top of glyph
|
||||
@@ -61,7 +62,7 @@ private:
|
||||
std::string findValue(const std::string& key, const std::vector<std::string>& columns) const;
|
||||
|
||||
std::string m_name;
|
||||
uint32_t m_size = { 0 };
|
||||
unsigned char m_size = { 0 };
|
||||
uint32_t m_lineSpacing = { 0 };
|
||||
std::array<uint32_t, 4> m_padding = { 0 };
|
||||
std::shared_ptr<Texture> m_texture;
|
||||
|
||||
@@ -48,26 +48,87 @@ void TextAreaSystem::render()
|
||||
std::shared_ptr<Font> font = FontManager::the().load(textarea.font);
|
||||
// glm::mat4 translate = transform.translate;
|
||||
|
||||
unsigned char previous = 0;
|
||||
m_characters.clear();
|
||||
createLines(font, textarea);
|
||||
createQuads(font, textarea);
|
||||
}
|
||||
}
|
||||
|
||||
using Characters = std::vector<std::shared_ptr<Character>>;
|
||||
|
||||
void TextAreaSystem::createLines(std::shared_ptr<Font> font, const TextAreaComponent& textarea)
|
||||
{
|
||||
float fontScale = textarea.fontSize / (float)font->size();
|
||||
|
||||
// Texture
|
||||
// -------------------------------------
|
||||
|
||||
float textureWidth = static_cast<float>(font->texture()->width());
|
||||
float textureHeight = static_cast<float>(font->texture()->height());
|
||||
VERIFY(textureWidth == textureHeight, "TextAreaSystem read invalid font texture");
|
||||
|
||||
// -------------------------------------
|
||||
|
||||
char previous = 0;
|
||||
size_t spaceIndex = 0;
|
||||
float lineWidth = 0.0f;
|
||||
float lineWidthSinceLastSpace = 0.0f;
|
||||
for (char character : textarea.content) {
|
||||
auto c = font->get(character);
|
||||
m_characters.push_back(c);
|
||||
|
||||
// Kerning
|
||||
char kerning = 0;
|
||||
if (c->kernings.find(previous) != c->kernings.end()) {
|
||||
kerning = c->kernings.at(previous);
|
||||
}
|
||||
|
||||
lineWidth += (c->advance + c->offset.x + kerning) * fontScale;
|
||||
lineWidthSinceLastSpace += (c->advance + c->offset.x + kerning) * fontScale;
|
||||
|
||||
if (character == ' ') {
|
||||
spaceIndex = m_characters.size() - 1;
|
||||
lineWidthSinceLastSpace = 0;
|
||||
}
|
||||
|
||||
if (lineWidth > textureWidth) {
|
||||
m_characters[spaceIndex] = nullptr;
|
||||
lineWidth = 0;
|
||||
lineWidth = lineWidthSinceLastSpace;
|
||||
}
|
||||
|
||||
previous = character;
|
||||
}
|
||||
}
|
||||
|
||||
void TextAreaSystem::createQuads(std::shared_ptr<Font> font, const TextAreaComponent& textarea)
|
||||
{
|
||||
float fontScale = textarea.fontSize / (float)font->size();
|
||||
|
||||
char previous = 0;
|
||||
float advanceX = 0.0f;
|
||||
float advanceY = 0.0f;
|
||||
for (auto character : textarea.content) {
|
||||
std::optional<CharacterQuad> quad = calculateCharacterQuad(character, previous, font, advanceX, advanceY);
|
||||
previous = character;
|
||||
for (const auto& character : m_characters) {
|
||||
// Go to the next line on "\n"
|
||||
if (character == nullptr) {
|
||||
advanceX = 0;
|
||||
advanceY -= (font->lineSpacing() * textarea.lineSpacing) * fontScale;
|
||||
continue;
|
||||
}
|
||||
|
||||
std::optional<CharacterQuad> quad = calculateCharacterQuad(character, previous, font, fontScale, advanceX, advanceY);
|
||||
if (quad) {
|
||||
RendererCharacter::the().drawCharacter(quad.value(), font->texture());
|
||||
}
|
||||
}
|
||||
|
||||
previous = character->id;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<CharacterQuad> TextAreaSystem::calculateCharacterQuad(unsigned char character, unsigned char previous, std::shared_ptr<Font> font, float& advanceX, float& advanceY)
|
||||
std::optional<CharacterQuad> TextAreaSystem::calculateCharacterQuad(std::shared_ptr<Character> c, char previous, std::shared_ptr<Font> font, float fontScale, float& advanceX, float& advanceY)
|
||||
{
|
||||
CharacterQuad characterQuad;
|
||||
|
||||
auto c = font->get(character);
|
||||
|
||||
// Texture
|
||||
// -------------------------------------
|
||||
|
||||
@@ -78,7 +139,7 @@ std::optional<CharacterQuad> TextAreaSystem::calculateCharacterQuad(unsigned cha
|
||||
// Skip empty characters (like space)
|
||||
if (c->size.x == 0 || c->size.y == 0) {
|
||||
// Jump to the next glyph
|
||||
advanceX += c->advance;
|
||||
advanceX += c->advance * fontScale;
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -87,19 +148,13 @@ std::optional<CharacterQuad> TextAreaSystem::calculateCharacterQuad(unsigned cha
|
||||
|
||||
// Kerning
|
||||
if (c->kernings.find(previous) != c->kernings.end()) {
|
||||
advanceX += c->kernings.at(previous);
|
||||
advanceX += c->kernings.at(previous) * fontScale;
|
||||
}
|
||||
|
||||
// Line wrapping
|
||||
if (advanceX + c->offset.x + c->size.x > textureWidth) {
|
||||
advanceX = 0;
|
||||
advanceY -= font->lineSpacing();
|
||||
}
|
||||
|
||||
glm::vec2 cursor = { std::max(advanceX + c->offset.x, 0.0f),
|
||||
advanceY - c->offset.y };
|
||||
glm::vec2 cursorMax = { cursor.x + c->size.x,
|
||||
cursor.y - c->size.y };
|
||||
glm::vec2 cursor = { advanceX + (c->offset.x * fontScale),
|
||||
advanceY - (c->offset.y * fontScale) };
|
||||
glm::vec2 cursorMax = { cursor.x + (c->size.x * fontScale),
|
||||
cursor.y - (c->size.y * fontScale) };
|
||||
|
||||
// Scale the values from 0:512 (texture size) to -1:1 (screen space)
|
||||
glm::vec2 cursorScreen = {
|
||||
@@ -117,7 +172,7 @@ std::optional<CharacterQuad> TextAreaSystem::calculateCharacterQuad(unsigned cha
|
||||
characterQuad.at(3).quad.position = { cursorScreen.x, cursorScreen.y, 0.0f }; // top left
|
||||
|
||||
// Jump to the next glyph
|
||||
advanceX += c->advance;
|
||||
advanceX += c->advance * fontScale;
|
||||
|
||||
// Texture coordinates
|
||||
// -------------------------------------
|
||||
|
||||
@@ -14,10 +14,13 @@
|
||||
#include "glm/ext/vector_float3.hpp" // glm::vec3
|
||||
#include "ruc/singleton.h"
|
||||
|
||||
#include "inferno/component/textareacomponent.h"
|
||||
#include "inferno/render/font.h"
|
||||
#include "inferno/render/renderer.h"
|
||||
|
||||
namespace Inferno {
|
||||
|
||||
using Characters = std::vector<std::shared_ptr<Character>>;
|
||||
using CharacterQuad = std::array<CharacterVertex, Renderer::vertexPerQuad>;
|
||||
|
||||
class Font;
|
||||
@@ -33,8 +36,12 @@ public:
|
||||
void setScene(Scene* scene) { m_scene = scene; }
|
||||
|
||||
private:
|
||||
std::optional<CharacterQuad> calculateCharacterQuad(unsigned char character, unsigned char previous, std::shared_ptr<Font> font, float& advanceX, float& advanceY);
|
||||
void createLines(std::shared_ptr<Font> font, const TextAreaComponent& textarea);
|
||||
void createQuads(std::shared_ptr<Font> font, const TextAreaComponent& textarea);
|
||||
|
||||
std::optional<CharacterQuad> calculateCharacterQuad(std::shared_ptr<Character> c, char previous, std::shared_ptr<Font> font, float fontSize, float& advanceX, float& advanceY);
|
||||
|
||||
Characters m_characters;
|
||||
Scene* m_scene { nullptr };
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user