Browse Source

Engine: Allow configuration of font size and line spacing

master
Riyyi 11 months ago
parent
commit
15b71f8387
  1. 6
      assets/scene/scene1.json
  2. 6
      src/inferno/component/textareacomponent.cpp
  3. 4
      src/inferno/component/textareacomponent.h
  4. 3
      src/inferno/render/font.cpp
  5. 3
      src/inferno/render/font.h
  6. 107
      src/inferno/system/textareasystem.cpp
  7. 9
      src/inferno/system/textareasystem.h

6
assets/scene/scene1.json

@ -42,9 +42,9 @@
"name": "Text", "name": "Text",
"content": "Hello World!", "content": "Hello World!",
"font": "assets/fnt/open-sans", "font": "assets/fnt/open-sans",
"font-size": 10, "font-size": 24,
"width": 150, "line-spacing": 1.0,
"lines": 3 "width": 150
} }
] ]
} }

6
src/inferno/component/textareacomponent.cpp

@ -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) { if (json.exists("font-size") && json.at("font-size").type() == ruc::Json::Type::Number) {
json.at("font-size").getTo(value.fontSize); 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) { if (json.exists("width") && json.at("width").type() == ruc::Json::Type::Number) {
json.at("width").getTo(value.width); 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 } // namespace Inferno

4
src/inferno/component/textareacomponent.h

@ -18,9 +18,9 @@ namespace Inferno {
struct TextAreaComponent { struct TextAreaComponent {
std::string content; std::string content;
std::string font; std::string font;
uint32_t fontSize { 0 }; unsigned char fontSize { 0 };
float lineSpacing { 1.0f };
uint32_t width { 0 }; uint32_t width { 0 };
uint32_t lines { 0 };
#if 0 #if 0
TextAreaComponent() {} TextAreaComponent() {}

3
src/inferno/render/font.cpp

@ -55,7 +55,7 @@ void Font::parseFont(const std::string& font)
// --------------------------------- // ---------------------------------
if (action.compare("info") == 0) { 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(','); auto paddings = findValue("padding", columns) | std::views::split(',');
size_t i = 0; size_t i = 0;
for (const auto& padding : paddings) { 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 width = convert<uint32_t>(findValue("width", columns));
uint32_t height = convert<uint32_t>(findValue("height", columns)); uint32_t height = convert<uint32_t>(findValue("height", columns));
Character character = { Character character = {
.id = id,
.position = { .position = {
convert<uint32_t>(findValue("x", columns)) + m_padding[Padding::Left], convert<uint32_t>(findValue("x", columns)) + m_padding[Padding::Left],
convert<uint32_t>(findValue("y", columns)) + m_padding[Padding::Top], convert<uint32_t>(findValue("y", columns)) + m_padding[Padding::Top],

3
src/inferno/render/font.h

@ -25,6 +25,7 @@ namespace Inferno {
class Texture; class Texture;
struct Character { struct Character {
char id; // Character
glm::uvec2 position; // Position glm::uvec2 position; // Position
glm::uvec2 size; // Width/height glm::uvec2 size; // Width/height
glm::ivec2 offset; // Offset from baseline to left / top of glyph 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 findValue(const std::string& key, const std::vector<std::string>& columns) const;
std::string m_name; std::string m_name;
uint32_t m_size = { 0 }; unsigned char m_size = { 0 };
uint32_t m_lineSpacing = { 0 }; uint32_t m_lineSpacing = { 0 };
std::array<uint32_t, 4> m_padding = { 0 }; std::array<uint32_t, 4> m_padding = { 0 };
std::shared_ptr<Texture> m_texture; std::shared_ptr<Texture> m_texture;

107
src/inferno/system/textareasystem.cpp

@ -48,25 +48,86 @@ void TextAreaSystem::render()
std::shared_ptr<Font> font = FontManager::the().load(textarea.font); std::shared_ptr<Font> font = FontManager::the().load(textarea.font);
// glm::mat4 translate = transform.translate; // glm::mat4 translate = transform.translate;
unsigned char previous = 0; m_characters.clear();
float advanceX = 0.0f; createLines(font, textarea);
float advanceY = 0.0f; createQuads(font, textarea);
for (auto character : textarea.content) { }
std::optional<CharacterQuad> quad = calculateCharacterQuad(character, previous, font, advanceX, advanceY); }
previous = character;
using Characters = std::vector<std::shared_ptr<Character>>;
if (quad) {
RendererCharacter::the().drawCharacter(quad.value(), font->texture()); 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;
} }
} }
std::optional<CharacterQuad> TextAreaSystem::calculateCharacterQuad(unsigned char character, unsigned char previous, std::shared_ptr<Font> font, float& advanceX, float& advanceY) void TextAreaSystem::createQuads(std::shared_ptr<Font> font, const TextAreaComponent& textarea)
{ {
CharacterQuad characterQuad; float fontScale = textarea.fontSize / (float)font->size();
char previous = 0;
float advanceX = 0.0f;
float advanceY = 0.0f;
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;
}
}
auto c = font->get(character); 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;
// Texture // Texture
// ------------------------------------- // -------------------------------------
@ -78,7 +139,7 @@ std::optional<CharacterQuad> TextAreaSystem::calculateCharacterQuad(unsigned cha
// Skip empty characters (like space) // Skip empty characters (like space)
if (c->size.x == 0 || c->size.y == 0) { if (c->size.x == 0 || c->size.y == 0) {
// Jump to the next glyph // Jump to the next glyph
advanceX += c->advance; advanceX += c->advance * fontScale;
return {}; return {};
} }
@ -87,19 +148,13 @@ std::optional<CharacterQuad> TextAreaSystem::calculateCharacterQuad(unsigned cha
// Kerning // Kerning
if (c->kernings.find(previous) != c->kernings.end()) { 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), glm::vec2 cursor = { advanceX + (c->offset.x * fontScale),
advanceY - c->offset.y }; advanceY - (c->offset.y * fontScale) };
glm::vec2 cursorMax = { cursor.x + c->size.x, glm::vec2 cursorMax = { cursor.x + (c->size.x * fontScale),
cursor.y - c->size.y }; cursor.y - (c->size.y * fontScale) };
// Scale the values from 0:512 (texture size) to -1:1 (screen space) // Scale the values from 0:512 (texture size) to -1:1 (screen space)
glm::vec2 cursorScreen = { 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 characterQuad.at(3).quad.position = { cursorScreen.x, cursorScreen.y, 0.0f }; // top left
// Jump to the next glyph // Jump to the next glyph
advanceX += c->advance; advanceX += c->advance * fontScale;
// Texture coordinates // Texture coordinates
// ------------------------------------- // -------------------------------------

9
src/inferno/system/textareasystem.h

@ -14,10 +14,13 @@
#include "glm/ext/vector_float3.hpp" // glm::vec3 #include "glm/ext/vector_float3.hpp" // glm::vec3
#include "ruc/singleton.h" #include "ruc/singleton.h"
#include "inferno/component/textareacomponent.h"
#include "inferno/render/font.h"
#include "inferno/render/renderer.h" #include "inferno/render/renderer.h"
namespace Inferno { namespace Inferno {
using Characters = std::vector<std::shared_ptr<Character>>;
using CharacterQuad = std::array<CharacterVertex, Renderer::vertexPerQuad>; using CharacterQuad = std::array<CharacterVertex, Renderer::vertexPerQuad>;
class Font; class Font;
@ -33,8 +36,12 @@ public:
void setScene(Scene* scene) { m_scene = scene; } void setScene(Scene* scene) { m_scene = scene; }
private: 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 }; Scene* m_scene { nullptr };
}; };

Loading…
Cancel
Save