Rendering Engine 0.2.9
Modular Graphics Rendering Engine | v0.2.9
rendering_engine::TextBlock2D Class Reference

2D drawable representing a block of rendered text. More...

#include <text_block_2d.hpp>

Inherits rendering_engine::Drawable2D.

Classes

struct  GlyphQuad
 Renderable quad representing a single glyph. More...
 
struct  Mesh
 CPU-side mesh data for glyph quads. More...
 
struct  Properties
 
struct  ShapedGlyph
 Result of text shaping for a single glyph. More...
 

Public Member Functions

 TextBlock2D (Scene &scene, std::shared_ptr< TextRenderer > textRenderer, Properties properties=Properties())
 Constructs a 2D text block. More...
 
void Initialize () override
 Initializes render resource pointers (material, mesh, etc.). Must be called after setting material and mesh names. More...
 
void Update (float deltaTime) override
 Updates logic (animation, movement, etc.) for this drawable. More...
 
void Draw (const Camera2D &camera) override
 Submits this quad to the renderer for drawing. More...
 
virtual void SetText (std::string text)
 Sets the displayed text. More...
 
void SetTextColor (glm::vec4 color)
 Sets the text color. More...
 
void SetOutlineColor (glm::vec4 color)
 Sets the outline color. More...
 
glm::vec2 GetDimensions () const
 Returns text block dimensions. More...
 
template<>
std::unordered_map< std::string, TextBlock2D::MeshPrepareMeshSlots (const std::vector< std::uint32_t > &glyphs)
 
template<>
std::unordered_map< std::string, TextBlock2D::MeshPrepareMeshSlots (const std::vector< ShapedGlyph > &glyphs)
 
- Public Member Functions inherited from rendering_engine::Drawable2D
 Drawable2D (RenderResourceContext renderContext, Scene &scene)
 Constructs the Drawable2D with a resource context. More...
 
void Initialize () override
 Initializes render resources. More...
 
void Update (float deltaTime) override
 Updates model matrix (and any other logic). More...
 
virtual void Draw (const Camera2D &camera)=0
 Submits this quad to the renderer for drawing. More...
 
void SetPosition (const glm::vec2 &position)
 Sets the quad position in 2D space. More...
 
void SetRotation (float angleDegrees)
 Sets the quad rotation. More...
 
void SetScale (const glm::vec2 &scale)
 Sets the quad scale along each axis. More...
 
const glm::vec2 & GetPosition () const
 Gets the quad position. More...
 
float GetRotation () const
 Gets the quad rotation angle (degrees). More...
 
const glm::vec2 & GetScale () const
 Gets the quad scale. More...
 
SceneComponent2DGetTransform ()
 Access to the underlying SceneComponent2D (transform). More...
 
const SceneComponent2DGetTransform () const
 
void Destroy () override
 Requests destruction of this drawable. More...
 
- Public Member Functions inherited from rendering_engine::DrawableComponent
 DrawableComponent (RenderResourceContext renderContext, Scene &scene)
 Constructs the DrawableComponent with a resource context. More...
 
virtual ~DrawableComponent ()=default
 Virtual destructor. More...
 
virtual void Initialize ()
 Initializes render resource pointers (material, mesh, etc.). Must be called after setting material and mesh names. More...
 
virtual void Update (float deltaTime)=0
 Updates logic (animation, movement, etc.) for this drawable. More...
 
virtual void Shutdown ()
 Releases all render resources owned by this drawable. More...
 
virtual void Destroy ()
 Requests destruction of this drawable. More...
 
void UpdateOnTick (bool in)
 
 DrawableComponent (const DrawableComponent &)=delete
 
DrawableComponentoperator= (const DrawableComponent &)=delete
 

Protected Member Functions

std::vector< std::uint32_t > DecodeUtf8 (const std::string &text)
 Decodes a UTF-8 string into Unicode code points. More...
 
std::string CodepointToUtf8 (std::uint32_t codePoint)
 Converts a Unicode code point to UTF-8. More...
 
std::vector< ShapedGlyphShapeText (const std::string &text)
 Shapes text and returns shaped glyphs. More...
 
void ConstructMeshAutoLinebreak (const std::vector< std::uint32_t > &codePoints)
 
void ConstructMesh ()
 Constructs glyph geometry without shaping. More...
 
void ShapeTextAndConstructMesh ()
 Shapes text and constructs glyph geometry. More...
 
GlyphQuad MakeGlyphQuad (GlyphIndex glyphIndext, float penX, float penY)
 Creates a glyph quad at the current pen position. More...
 
void PushQuad (std::string meshName, std::unordered_map< std::string, TextBlock2D::Mesh > &meshes, GlyphQuad glyphQuad, float horizontalShift=0.0f)
 Appends a glyph quad to a mesh. More...
 
void UploadMeshes (const std::unordered_map< std::string, TextBlock2D::Mesh > &meshes)
 Uploads prepared meshes to GPU resources. More...
 
void SetOutlineThickness (float thicknessPx)
 Sets outline thickness for all render batches. More...
 
template<typename T >
std::unordered_map< std::string, TextBlock2D::MeshPrepareMeshSlots (const std::vector< T > &glyphs)
 Prepares mesh slots based on glyph usage. More...
 
std::vector< std::string > SplitString (const std::string &text, std::string separator)
 Splits a string by a separator. More...
 
bool IsTextShapingRequired (std::uint32_t codePoint) const
 Checks whether shaping is required for a code point. More...
 
- Protected Member Functions inherited from rendering_engine::DrawableComponent
void AddRenderBatch (std::string meshName, std::string materialName)
 

Protected Attributes

std::unordered_map< std::string, std::string > mMaterialMesh
 
const std::shared_ptr< TextRenderermTextRenderer
 
std::shared_ptr< FontResourcesmFontResources
 
glm::vec4 mColor
 
const std::string mFontName
 
const unsigned int mFontSize
 
float mLineSpacingScale = 1.0f
 
std::string mText
 
float mMaxLineLength = 0.0f
 
const TextAlign mTextAlign
 
glm::vec2 mDimensions
 
const bool bIsTextShapeEnabled
 
const float mOutlineThicknessPx
 
- Protected Attributes inherited from rendering_engine::Drawable2D
SceneComponent2D mSceneComponent
 
- Protected Attributes inherited from rendering_engine::DrawableComponent
RenderResourceContext mRenderContext
 
ScenemScene
 
std::vector< RenderBatchmRenderBatches
 
bool bUpdateOnTick
 

Static Protected Attributes

static std::string sDefaultFontName = "RobotoMono-Regular"
 

Detailed Description

2D drawable representing a block of rendered text.

Converts UTF-8 text into glyph quads, performs layout and alignment, and submits render batches using font atlas materials.

A TextBlock2D instance is bound to a single font and font size.

Definition at line 52 of file text_block_2d.hpp.

Constructor & Destructor Documentation

◆ TextBlock2D()

rendering_engine::TextBlock2D::TextBlock2D ( Scene scene,
std::shared_ptr< TextRenderer textRenderer,
Properties  properties = Properties() 
)

Constructs a 2D text block.

Parameters
textRendererOwning text renderer.
propertiesText block configuration.

Definition at line 41 of file text_block_2d.cpp.

42 :
43 Drawable2D(textRenderer->GetRenderResourceContext(), scene),
44 mTextRenderer(textRenderer),
45 bIsTextShapeEnabled(properties.textShapeEnabled),
46 mFontName(properties.fontName),
47 mFontSize(properties.fontSize),
48 mLineSpacingScale(properties.lineSpacingScale),
49 mTextAlign(properties.textAlign),
50 mMaxLineLength(properties.maxLineLength),
51 mOutlineThicknessPx(properties.outlineThicknessPx > 2 ? 2 : properties.outlineThicknessPx),
52 mColor(glm::vec4(1.0f, 1.0f, 1.0f, 1.0f)),
53 mDimensions(glm::vec2(0.0f, 0.0f))
54{
55 mFontResources = mTextRenderer->GetFontResources(mFontName, mFontSize);
56 if (!mFontResources)
57 {
58 throw std::runtime_error("FontResources is not initialized for this TextBlock2D.");
59 }
60
61 ++sNumOfTextBlocks;
62 mTextBlockID = "TextBlock_" + std::to_string(sNumOfTextBlocks);
63}
Drawable2D(RenderResourceContext renderContext, Scene &scene)
Constructs the Drawable2D with a resource context.
Definition: drawable_2d.cpp:7
std::shared_ptr< FontResources > mFontResources
const std::shared_ptr< TextRenderer > mTextRenderer

Member Function Documentation

◆ CodepointToUtf8()

std::string rendering_engine::TextBlock2D::CodepointToUtf8 ( std::uint32_t  codePoint)
protected

Converts a Unicode code point to UTF-8.

Parameters
codePointUnicode code point.
Returns
UTF-8 encoded string.

Definition at line 629 of file text_block_2d.cpp.

630{
631 std::string out;
632
633 if (codePoint <= 0x7F) {
634 out.push_back(static_cast<char>(codePoint));
635 }
636 else if (codePoint <= 0x7FF) {
637 out.push_back(static_cast<char>(0xC0 | ((codePoint >> 6) & 0x1F)));
638 out.push_back(static_cast<char>(0x80 | (codePoint & 0x3F)));
639 }
640 else if (codePoint <= 0xFFFF) {
641 out.push_back(static_cast<char>(0xE0 | ((codePoint >> 12) & 0x0F)));
642 out.push_back(static_cast<char>(0x80 | ((codePoint >> 6) & 0x3F)));
643 out.push_back(static_cast<char>(0x80 | (codePoint & 0x3F)));
644 }
645 else if (codePoint <= 0x10FFFF) {
646 out.push_back(static_cast<char>(0xF0 | ((codePoint >> 18) & 0x07)));
647 out.push_back(static_cast<char>(0x80 | ((codePoint >> 12) & 0x3F)));
648 out.push_back(static_cast<char>(0x80 | ((codePoint >> 6) & 0x3F)));
649 out.push_back(static_cast<char>(0x80 | (codePoint & 0x3F)));
650 }
651
652 return out;
653}

◆ ConstructMesh()

void rendering_engine::TextBlock2D::ConstructMesh ( )
protected

Constructs glyph geometry without shaping.

Definition at line 325 of file text_block_2d.cpp.

326{
327 std::vector<std::uint32_t> allUsedGlyphs;
328 std::vector<std::vector<std::uint32_t>> linesOfGlyphs;
329
330 auto textRuns = SplitString(mText, "\n");
331
332 for (const auto& textRunString : textRuns)
333 {
334 auto textRunCodePoints = DecodeUtf8(textRunString);
335 linesOfGlyphs.push_back(textRunCodePoints);
336 std::copy(textRunCodePoints.begin(), textRunCodePoints.end(), std::back_inserter(allUsedGlyphs));
337 }
338 // It is better to ensure all required glyphs at once, as those if not ready yet,
339 // will be added to a single font atlas
340 mFontResources->EnsureGlyphs(allUsedGlyphs);
341 auto meshes = PrepareMeshSlots(allUsedGlyphs);
342
343
344 float maximumLineLengh = 0.0f;
345 std::vector<float> lineLengths;
346
347 std::vector<std::vector<GlyphQuad>> linesOfGlyphQuads;
348
349 const FontMetrics& fontMetrics = mFontResources->GetFontMetrics();
350
351 float penX = 0.0f;
352 float penY = 0.0f;
353
354 for (auto line : linesOfGlyphs)
355 {
356 penX = 0.0f;
357 std::vector<GlyphQuad> lineOfGlyphQuads;
358 for (const auto& glyph : line)
359 {
360 GlyphIndex glyphIndex = mFontResources->GetIndexFromCodePoint(glyph);
361 GlyphQuad glyphQuad = MakeGlyphQuad(glyphIndex, penX, penY);
362
363 penX += glyphQuad.advanceX;
364 lineOfGlyphQuads.push_back(glyphQuad);
365 }
366
367 lineLengths.push_back(penX);
368 linesOfGlyphQuads.push_back(lineOfGlyphQuads);
369 const float lineLength = penX;
370
371 penY += (fontMetrics.lineHeight * mLineSpacingScale);
372
373 if (lineLength > maximumLineLengh)
374 {
375 maximumLineLengh = lineLength;
376 }
377 }
378
379 mMaxLineLength = (mMaxLineLength > maximumLineLengh ? mMaxLineLength : maximumLineLengh);
380 mDimensions = glm::vec2(mMaxLineLength, penY);
381
382 size_t curLine = 0;
383 for (const auto& line : linesOfGlyphQuads)
384 {
385 for (const auto& glyphQuad : line)
386 {
387 const std::string meshName = mMaterialMesh[glyphQuad.fontAtlasMaterialName];
388 float horizontalShift = 0.0f;
390 {
391 horizontalShift = (mMaxLineLength - lineLengths[curLine]) / 2.0f;
392 }
394 {
395 horizontalShift = (mMaxLineLength - lineLengths[curLine]);
396 }
397 PushQuad(meshName, meshes, glyphQuad, horizontalShift);
398 }
399 ++curLine;
400 }
401
402 UploadMeshes(meshes);
403
404 Initialize();
405}
std::vector< std::string > SplitString(const std::string &text, std::string separator)
Splits a string by a separator.
std::unordered_map< std::string, std::string > mMaterialMesh
std::vector< std::uint32_t > DecodeUtf8(const std::string &text)
Decodes a UTF-8 string into Unicode code points.
void UploadMeshes(const std::unordered_map< std::string, TextBlock2D::Mesh > &meshes)
Uploads prepared meshes to GPU resources.
void Initialize() override
Initializes render resource pointers (material, mesh, etc.). Must be called after setting material an...
GlyphQuad MakeGlyphQuad(GlyphIndex glyphIndext, float penX, float penY)
Creates a glyph quad at the current pen position.
void PushQuad(std::string meshName, std::unordered_map< std::string, TextBlock2D::Mesh > &meshes, GlyphQuad glyphQuad, float horizontalShift=0.0f)
Appends a glyph quad to a mesh.
std::unordered_map< std::string, TextBlock2D::Mesh > PrepareMeshSlots(const std::vector< T > &glyphs)
Prepares mesh slots based on glyph usage.

◆ ConstructMeshAutoLinebreak()

void rendering_engine::TextBlock2D::ConstructMeshAutoLinebreak ( const std::vector< std::uint32_t > &  codePoints)
protected

Definition at line 208 of file text_block_2d.cpp.

209{
210 auto meshes = PrepareMeshSlots(codePoints);
211
212 const FontMetrics& fontMetrics = mFontResources->GetFontMetrics();
213 // Pen position (baseline)
214 float penX = 0.0f;
215 float penY = 0.0f;
216
217 const std::uint32_t space{ 0x20 };
218 const std::uint32_t newLine{ 0x0A };
219
220 const bool autoTextWrappingRequested = mMaxLineLength > 0.0f;
221 if (!autoTextWrappingRequested)
222 {
223 for (std::uint32_t glyph : codePoints)
224 {
225 if (glyph == newLine)
226 {
227 penX = 0.0f;
228 penY += fontMetrics.lineHeight;
229 continue;
230 }
231
232 GlyphIndex glyphIndex = mFontResources->GetIndexFromCodePoint(glyph);
233 GlyphQuad glyphQuad = MakeGlyphQuad(glyphIndex, penX, penY);
234 const std::string meshName = mMaterialMesh[glyphQuad.fontAtlasMaterialName];
235 PushQuad(meshName, meshes, glyphQuad);
236 penX += glyphQuad.advanceX;
237 }
238 }
239 else
240 {
241 bool isStringComplete = false;
242
243 // This variable describe the index we stay until new word is added.
244 std::uint32_t currentIndex = 0;
245 bool isLastGlyphProcessed = false;
246 float lineLength = 0.0f;
247 std::uint32_t nextGlyphIndex = 0;
248
249 std::vector<GlyphQuad> line;
250 std::vector<GlyphQuad> nextWord;
251
252 while (!isStringComplete)
253 {
254 std::string stringProcessed;
255
256 while (nextGlyphIndex < codePoints.size())
257 {
258 if (codePoints[nextGlyphIndex] == space)
259 break;
260
261 const std::string curGlyph = CodepointToUtf8(codePoints[nextGlyphIndex]);
262 stringProcessed.append(curGlyph);
263 GlyphIndex glyphIndex = mFontResources->GetIndexFromCodePoint(codePoints[nextGlyphIndex]);
264 GlyphQuad glyphQuad = MakeGlyphQuad(glyphIndex, penX, penY);
265 penX += glyphQuad.advanceX;
266 nextWord.push_back(glyphQuad);
267 ++nextGlyphIndex;
268 isLastGlyphProcessed = nextGlyphIndex >= codePoints.size();
269 }
270
271 const bool isNewWordFitLine = penX <= mMaxLineLength;
272
273 if (line.empty() || isNewWordFitLine)
274 {
275 if (!line.empty())
276 {
277 //Insert SPACE
278 GlyphIndex glyphIndex = mFontResources->GetIndexFromCodePoint(space);
279 GlyphQuad glyphQuad = MakeGlyphQuad(glyphIndex, penX, penY);
280 stringProcessed.append(" ");
281 penX += glyphQuad.advanceX;
282 nextWord.push_back(glyphQuad);
283 ++nextGlyphIndex;
284 }
285 // Add new word to the line
286 std::copy(nextWord.begin(), nextWord.end(), std::back_inserter(line));
287 nextWord.clear();
288 lineLength = penX;
289 currentIndex = nextGlyphIndex;
290 isStringComplete = isLastGlyphProcessed;
291 }
292
293 if (!isNewWordFitLine || isLastGlyphProcessed)
294 {
295 nextWord.clear();
296 // Finalize current line and switch to next. Push quads for all line, setting horizontal alignment
297 for (auto& quad : line)
298 {
299 float horizontalShift = 0.0f;
301 {
302 horizontalShift = (mMaxLineLength - lineLength) / 2.0f;
303 }
305 {
306 horizontalShift = (mMaxLineLength - lineLength);
307 }
308 const std::string meshName = mMaterialMesh[quad.fontAtlasMaterialName];
309 PushQuad(meshName, meshes, quad, horizontalShift);
310 }
311 line.clear();
312
313 penX = 0.0f;
314 penY += fontMetrics.lineHeight;
315 nextGlyphIndex = currentIndex;
316 }
317 }
318 }
319
320 UploadMeshes(meshes);
321
322 Initialize();
323}
std::string CodepointToUtf8(std::uint32_t codePoint)
Converts a Unicode code point to UTF-8.

◆ DecodeUtf8()

std::vector< std::uint32_t > rendering_engine::TextBlock2D::DecodeUtf8 ( const std::string &  text)
protected

Decodes a UTF-8 string into Unicode code points.

Parameters
textUTF-8 encoded string.
Returns
List of Unicode code points.

Definition at line 138 of file text_block_2d.cpp.

139{
140 std::vector<std::uint32_t> result;
141 result.reserve(text.size()); // worst case: ASCII
142
143 const unsigned char* bytes =
144 reinterpret_cast<const unsigned char*>(text.data());
145 const size_t length = text.size();
146
147 size_t i = 0;
148 while (i < length)
149 {
150 std::uint32_t codePoint = 0;
151 unsigned char c = bytes[i];
152
153 if (c <= 0x7F)
154 {
155 // 1-byte sequence (ASCII)
156 codePoint = c;
157 i += 1;
158 }
159 else if ((c & 0xE0) == 0xC0)
160 {
161 // 2-byte sequence
162 if (i + 1 >= length) break;
163
164 codePoint =
165 ((c & 0x1F) << 6) |
166 (bytes[i + 1] & 0x3F);
167
168 i += 2;
169 }
170 else if ((c & 0xF0) == 0xE0)
171 {
172 // 3-byte sequence
173 if (i + 2 >= length) break;
174
175 codePoint =
176 ((c & 0x0F) << 12) |
177 ((bytes[i + 1] & 0x3F) << 6) |
178 (bytes[i + 2] & 0x3F);
179
180 i += 3;
181 }
182 else if ((c & 0xF8) == 0xF0)
183 {
184 // 4-byte sequence
185 if (i + 3 >= length) break;
186
187 codePoint =
188 ((c & 0x07) << 18) |
189 ((bytes[i + 1] & 0x3F) << 12) |
190 ((bytes[i + 2] & 0x3F) << 6) |
191 (bytes[i + 3] & 0x3F);
192
193 i += 4;
194 }
195 else
196 {
197 // Invalid UTF-8 start byte
198 ++i;
199 continue;
200 }
201
202 result.push_back(codePoint);
203 }
204
205 return result;
206}

◆ Draw()

void rendering_engine::TextBlock2D::Draw ( const Camera2D camera)
overridevirtual

Submits this quad to the renderer for drawing.

Implements rendering_engine::Drawable2D.

Definition at line 75 of file text_block_2d.cpp.

76{
77 Transformations2D transformations;
78 transformations.model = GetTransform().GetWorldMatrix();
79 transformations.view = camera.GetWorldView();
80 transformations.proj = camera.GetProjectionMatrix();
81
82 for (auto& renderBatch : mRenderBatches)
83 {
84 renderBatch.renderResources->SubmitResources(transformations, renderBatch.materialParameters);
85 }
86}
SceneComponent2D & GetTransform()
Access to the underlying SceneComponent2D (transform).
Definition: drawable_2d.cpp:55
std::vector< RenderBatch > mRenderBatches
const glm::mat4 & GetWorldMatrix()
Returns the world transformation matrix.

◆ GetDimensions()

glm::vec2 rendering_engine::TextBlock2D::GetDimensions ( ) const

Returns text block dimensions.

Returns
Width and height in pixels.

Definition at line 125 of file text_block_2d.cpp.

126{
127 return mDimensions;
128}

◆ Initialize()

void rendering_engine::TextBlock2D::Initialize ( )
overridevirtual

Initializes render resource pointers (material, mesh, etc.). Must be called after setting material and mesh names.

Reimplemented from rendering_engine::Drawable2D.

Definition at line 65 of file text_block_2d.cpp.

66{
68}
void Initialize() override
Initializes render resources.
Definition: drawable_2d.cpp:12

◆ IsTextShapingRequired()

bool rendering_engine::TextBlock2D::IsTextShapingRequired ( std::uint32_t  codePoint) const
protected

Checks whether shaping is required for a code point.

Parameters
codePointUnicode code point.
Returns
True if shaping is required.

Definition at line 614 of file text_block_2d.cpp.

615{
616 auto scriptsRequiredShaping = mTextRenderer->GetScriptsRequiredShaping();
617 for (const auto& script : scriptsRequiredShaping)
618 {
619 auto range = mTextRenderer->GetScriptRange(script);
620 if (codePoint >= range.first && codePoint <= range.second)
621 {
622 return true;
623 }
624 }
625
626 return false;
627}

◆ MakeGlyphQuad()

TextBlock2D::GlyphQuad rendering_engine::TextBlock2D::MakeGlyphQuad ( GlyphIndex  glyphIndext,
float  penX,
float  penY 
)
protected

Creates a glyph quad at the current pen position.

Parameters
glyphIndexGlyph identifier.
penXHorizontal pen position.
penYVertical pen position.
Returns
Glyph quad.

Definition at line 490 of file text_block_2d.cpp.

491{
492 GlyphQuad result;
493
494 const GlyphMetrics& glyphMetrics = mFontResources->GetGlyphMetrics(glyphIndex);
495 auto fontAtlasMaterialName = mFontResources->GetFontAtlasMaterialName(glyphIndex);
496 const auto fontAtlasTextureName = mFontResources->GetFontAtlasTextureName(glyphIndex);
497 auto textureCache = mTextRenderer->GetRenderResourceContext().textureCache;
498 const auto& fontAtlas = textureCache->GetTextureResources(fontAtlasTextureName);
499
500 result.fontAtlasMaterialName = fontAtlasMaterialName;
501
502 // Positions
503 const float x0 = penX + glyphMetrics.bearingX;
504 const float y0 = penY - glyphMetrics.bearingY; // y0 - top
505 const float y1 = y0 + glyphMetrics.height; // y1 - bottom
506 const float x1 = x0 + glyphMetrics.width;
507
508 result.x0 = x0 - mOutlineThicknessPx * 2;
509 result.y0 = y0 - mOutlineThicknessPx * 2;
510 result.x1 = x1 + mOutlineThicknessPx * 2;
511 result.y1 = y1 + mOutlineThicknessPx * 2;
512
513 // UVs
514 const auto atlasWidth = static_cast<float>(fontAtlas->GetCpuImageData().GetWidth());
515 const auto atlasHeight = static_cast<float>(fontAtlas->GetCpuImageData().GetHeight());
516
517 const float u0 = static_cast<float>(glyphMetrics.atlasX - mOutlineThicknessPx * 2) / atlasWidth;
518 const float v0 = static_cast<float>(glyphMetrics.atlasY - mOutlineThicknessPx * 2) / atlasHeight;
519 const float u1 = static_cast<float>(glyphMetrics.atlasX + glyphMetrics.width + mOutlineThicknessPx * 2) / atlasWidth;
520 const float v1 = static_cast<float>(glyphMetrics.atlasY + glyphMetrics.height + mOutlineThicknessPx * 2) / atlasHeight;
521
522 result.u0 = u0;
523 result.v0 = v0;
524 result.u1 = u1;
525 result.v1 = v1;
526
527 result.advanceX = glyphMetrics.advanceX;
528
529 return result;
530}

◆ PrepareMeshSlots() [1/3]

template<>
std::unordered_map< std::string, TextBlock2D::Mesh > rendering_engine::TextBlock2D::PrepareMeshSlots ( const std::vector< ShapedGlyph > &  glyphs)

Definition at line 724 of file text_block_2d.cpp.

725{
726 mMaterialMesh.clear();
727 // Prepare map of used material names with corresponding mesh names.
728 for (const auto& glyph : glyphs)
729 {
730 const std::uint32_t linefeed = 0x000A;
731 const std::uint32_t carriageReturn = 0x000D;
732 const std::uint32_t tab = 0x0009;
733 if (glyph.glyphIndex == linefeed || glyph.glyphIndex == carriageReturn || glyph.glyphIndex == tab)
734 {
735 continue;
736 }
737
738 GlyphIndex glyphIndex;
739 glyphIndex.index = glyph.glyphIndex;
740 auto fontAtlasMaterialName = mFontResources->GetFontAtlasMaterialName(glyphIndex);
741 const auto search = mMaterialMesh.find(fontAtlasMaterialName);
742 if (search == mMaterialMesh.end())
743 {
744 const std::string meshName = mTextBlockID + "_" + std::to_string(mMaterialMesh.size());
745 mMaterialMesh[fontAtlasMaterialName] = meshName;
746 }
747 }
748
749 // Prepare map of used mesh names with mesh structures
750 std::unordered_map<std::string, TextBlock2D::Mesh> meshes;
751 for (const auto& mesh : mMaterialMesh)
752 {
753 meshes[mesh.second] = TextBlock2D::Mesh();
754 }
755
756 return meshes;
757}

◆ PrepareMeshSlots() [2/3]

template<>
std::unordered_map< std::string, TextBlock2D::Mesh > rendering_engine::TextBlock2D::PrepareMeshSlots ( const std::vector< std::uint32_t > &  glyphs)

Definition at line 693 of file text_block_2d.cpp.

694{
695 mMaterialMesh.clear();
696 // Prepare map of used material names with corresponding mesh names.
697 for (auto codePoint : glyphs)
698 {
699 if (codePoint == 0x000A || codePoint == 0x000D || codePoint == 0x0009)
700 {
701 continue;
702 }
703
704 GlyphIndex glyphIndex = mFontResources->GetIndexFromCodePoint(codePoint);
705 auto fontAtlasMaterialName = mFontResources->GetFontAtlasMaterialName(glyphIndex);
706 const auto search = mMaterialMesh.find(fontAtlasMaterialName);
707 if (search == mMaterialMesh.end())
708 {
709 const std::string meshName = mTextBlockID + "_" + std::to_string(mMaterialMesh.size());
710 mMaterialMesh[fontAtlasMaterialName] = meshName;
711 }
712 }
713
714 // Prepare map of used mesh names with mesh structures
715 std::unordered_map<std::string, TextBlock2D::Mesh> meshes;
716 for (const auto& mesh : mMaterialMesh)
717 {
718 meshes[mesh.second] = TextBlock2D::Mesh();
719 }
720 return meshes;
721}

◆ PrepareMeshSlots() [3/3]

template<typename T >
std::unordered_map< std::string, TextBlock2D::Mesh > rendering_engine::TextBlock2D::PrepareMeshSlots ( const std::vector< T > &  glyphs)
protected

Prepares mesh slots based on glyph usage.

Parameters
glyphsGlyph collection.
Returns
Map of mesh names to mesh data.

◆ PushQuad()

void rendering_engine::TextBlock2D::PushQuad ( std::string  meshName,
std::unordered_map< std::string, TextBlock2D::Mesh > &  meshes,
GlyphQuad  glyphQuad,
float  horizontalShift = 0.0f 
)
protected

Appends a glyph quad to a mesh.

Parameters
meshNameTarget mesh name.
meshesMesh container.
glyphQuadGlyph quad data.
horizontalShiftAlignment offset.

Definition at line 532 of file text_block_2d.cpp.

533{
534 const uint32_t vertexBase = meshes.at(mMaterialMesh[glyphQuad.fontAtlasMaterialName]).positions2D.size();
535
536 const glm::vec2 shift(horizontalShift, 0.0f);
537
538 const glm::vec2 vert_0 = glm::vec2(glyphQuad.x0, glyphQuad.y0) + shift;
539 const glm::vec2 vert_1 = glm::vec2(glyphQuad.x1, glyphQuad.y0) + shift;
540 const glm::vec2 vert_2 = glm::vec2(glyphQuad.x1, glyphQuad.y1) + shift;
541 const glm::vec2 vert_3 = glm::vec2(glyphQuad.x0, glyphQuad.y1) + shift;
542
543 meshes.at(mMaterialMesh[glyphQuad.fontAtlasMaterialName]).positions2D.push_back(vert_0);
544 meshes.at(mMaterialMesh[glyphQuad.fontAtlasMaterialName]).positions2D.push_back(vert_1);
545 meshes.at(mMaterialMesh[glyphQuad.fontAtlasMaterialName]).positions2D.push_back(vert_2);
546 meshes.at(mMaterialMesh[glyphQuad.fontAtlasMaterialName]).positions2D.push_back(vert_3);
547
548 // UVs
549 meshes.at(mMaterialMesh[glyphQuad.fontAtlasMaterialName]).texCoords.push_back(glm::vec2(glyphQuad.u0, glyphQuad.v0));
550 meshes.at(mMaterialMesh[glyphQuad.fontAtlasMaterialName]).texCoords.push_back(glm::vec2(glyphQuad.u1, glyphQuad.v0));
551 meshes.at(mMaterialMesh[glyphQuad.fontAtlasMaterialName]).texCoords.push_back(glm::vec2(glyphQuad.u1, glyphQuad.v1));
552 meshes.at(mMaterialMesh[glyphQuad.fontAtlasMaterialName]).texCoords.push_back(glm::vec2(glyphQuad.u0, glyphQuad.v1));
553
554 // Colors
555 meshes.at(mMaterialMesh[glyphQuad.fontAtlasMaterialName]).colors.push_back(glm::vec4(1.0f, 1.0f, 1.0f, 1.0f));
556 meshes.at(mMaterialMesh[glyphQuad.fontAtlasMaterialName]).colors.push_back(glm::vec4(1.0f, 1.0f, 1.0f, 1.0f));
557 meshes.at(mMaterialMesh[glyphQuad.fontAtlasMaterialName]).colors.push_back(glm::vec4(1.0f, 1.0f, 1.0f, 1.0f));
558 meshes.at(mMaterialMesh[glyphQuad.fontAtlasMaterialName]).colors.push_back(glm::vec4(1.0f, 1.0f, 1.0f, 1.0f));
559
560 // Indices
561 meshes.at(mMaterialMesh[glyphQuad.fontAtlasMaterialName]).indices.push_back(vertexBase + 0);
562 meshes.at(mMaterialMesh[glyphQuad.fontAtlasMaterialName]).indices.push_back(vertexBase + 1);
563 meshes.at(mMaterialMesh[glyphQuad.fontAtlasMaterialName]).indices.push_back(vertexBase + 2);
564
565 meshes.at(mMaterialMesh[glyphQuad.fontAtlasMaterialName]).indices.push_back(vertexBase + 2);
566 meshes.at(mMaterialMesh[glyphQuad.fontAtlasMaterialName]).indices.push_back(vertexBase + 3);
567 meshes.at(mMaterialMesh[glyphQuad.fontAtlasMaterialName]).indices.push_back(vertexBase + 0);
568}

◆ SetOutlineColor()

void rendering_engine::TextBlock2D::SetOutlineColor ( glm::vec4  color)

Sets the outline color.

Parameters
colorRGBA color.

Definition at line 117 of file text_block_2d.cpp.

118{
119 for (auto& renderBatch : mRenderBatches)
120 {
121 renderBatch.materialParameters.SetMaterialVec4("OutlineColor", color);
122 }
123}

◆ SetOutlineThickness()

void rendering_engine::TextBlock2D::SetOutlineThickness ( float  thicknessPx)
protected

Sets outline thickness for all render batches.

Parameters
thicknessPxThickness in pixels.

Definition at line 130 of file text_block_2d.cpp.

131{
132 for (auto& renderBatch : mRenderBatches)
133 {
134 renderBatch.materialParameters.SetMaterialFloat("OutlineThicknessPx", thicknessPx);
135 }
136}

◆ SetText()

void rendering_engine::TextBlock2D::SetText ( std::string  text)
virtual

Sets the displayed text.

Parameters
textUTF-8 encoded text string.

Definition at line 88 of file text_block_2d.cpp.

89{
90 if (mText == text)
91 {
92 //Text has not changes, no new ensure/mesh-construction needed.
93 return;
94 }
95 mText = text;
96
97
99 {
101 }
102 else
103 {
105 }
107}
void SetOutlineThickness(float thicknessPx)
Sets outline thickness for all render batches.
void ShapeTextAndConstructMesh()
Shapes text and constructs glyph geometry.
void ConstructMesh()
Constructs glyph geometry without shaping.

◆ SetTextColor()

void rendering_engine::TextBlock2D::SetTextColor ( glm::vec4  color)

Sets the text color.

Parameters
colorRGBA color.

Definition at line 109 of file text_block_2d.cpp.

110{
111 for (auto& renderBatch : mRenderBatches)
112 {
113 renderBatch.materialParameters.SetMaterialVec4("FontColor", color);
114 }
115}

◆ ShapeText()

std::vector< TextBlock2D::ShapedGlyph > rendering_engine::TextBlock2D::ShapeText ( const std::string &  text)
protected

Shapes text and returns shaped glyphs.

Parameters
textUTF-8 encoded text.
Returns
List of shaped glyphs.

Definition at line 655 of file text_block_2d.cpp.

656{
657 std::vector<TextBlock2D::ShapedGlyph> result;
658
659 hb_buffer_t* buf;
660 buf = hb_buffer_create();
661 hb_buffer_add_utf8(buf, text.c_str(), -1, 0, -1);
662
663 hb_buffer_guess_segment_properties(buf);
664
665 auto* fontFace = mFontResources->GetFontFace();
666 hb_font_t* font = hb_ft_font_create_referenced(fontFace);
667
668 hb_shape(font, buf, NULL, 0);
669
670 unsigned int glyph_count;
671 hb_glyph_info_t* glyph_info = hb_buffer_get_glyph_infos(buf, &glyph_count);
672 hb_glyph_position_t* glyph_pos = hb_buffer_get_glyph_positions(buf, &glyph_count);
673
674 for (unsigned int i = 0; i < glyph_count; i++)
675 {
676 ShapedGlyph shapedGlyph;
677 shapedGlyph.glyphIndex = glyph_info[i].codepoint;
678 shapedGlyph.xOffset = glyph_pos[i].x_offset / 64.0f;
679 shapedGlyph.yOffset = glyph_pos[i].y_offset / 64.0f;
680 shapedGlyph.xAdvance = glyph_pos[i].x_advance / 64.0f;
681 shapedGlyph.yAdvance = glyph_pos[i].y_advance / 64.0f;
682 shapedGlyph.cluster = glyph_info[i].cluster;
683 result.push_back(shapedGlyph);
684 }
685
686 hb_font_destroy(font);
687 hb_buffer_destroy(buf);
688
689 return result;
690}

◆ ShapeTextAndConstructMesh()

void rendering_engine::TextBlock2D::ShapeTextAndConstructMesh ( )
protected

Shapes text and constructs glyph geometry.

Definition at line 407 of file text_block_2d.cpp.

408{
409 auto textRuns = SplitString(mText, "\n");
410
411 std::vector<std::vector<ShapedGlyph>> linesOfShapedGlyphs;
412 std::vector<ShapedGlyph> preShapedGlyphs;
413
414 for (const auto& textRun : textRuns)
415 {
416 auto shapedTextRun = ShapeText(textRun);
417 linesOfShapedGlyphs.push_back(shapedTextRun);
418 std::copy(shapedTextRun.begin(), shapedTextRun.end(), std::back_inserter(preShapedGlyphs));
419 }
420 std::vector<GlyphIndex> ensureGlyphs;
421 for (const auto& shapedGlyph : preShapedGlyphs)
422 {
423 GlyphIndex gi;
424 gi.index = shapedGlyph.glyphIndex;
425 ensureGlyphs.push_back(gi);
426 }
427 mFontResources->EnsureGlyphs(ensureGlyphs);
428
429 auto meshes = PrepareMeshSlots(preShapedGlyphs);
430
431 // Pen position (baseline)
432 float penX = 0.0f;
433 float penY = 0.0f;
434
435 float maximumLineLengh = 0.0f;
436 std::vector<float> lineLengths;
437
438 for (auto& line : linesOfShapedGlyphs)
439 {
440 float lineLength = 0.0f;
441 for (const auto& glyph : line)
442 {
443 lineLength += glyph.xAdvance;
444 }
445 lineLengths.push_back(lineLength);
446 if (lineLength > maximumLineLengh)
447 {
448 maximumLineLengh = lineLength;
449 }
450 }
451 mMaxLineLength = (mMaxLineLength > maximumLineLengh ? mMaxLineLength : maximumLineLengh);
452
453 const FontMetrics& fontMetrics = mFontResources->GetFontMetrics();
454 int curLine = 0;
455 for (auto& line : linesOfShapedGlyphs)
456 {
457 for (const auto& glyph : line)
458 {
459 GlyphIndex glyphIndex;
460 glyphIndex.index = glyph.glyphIndex;
461
462 GlyphQuad glyphQuad = MakeGlyphQuad(glyphIndex, penX + glyph.xOffset, penY + glyph.yOffset);
463 const std::string meshName = mMaterialMesh[glyphQuad.fontAtlasMaterialName];
464
465 float horizontalShift = 0.0f;
467 {
468 horizontalShift = (mMaxLineLength - lineLengths[curLine]) / 2.0f;
469 }
471 {
472 horizontalShift = (mMaxLineLength - lineLengths[curLine]);
473 }
474 PushQuad(meshName, meshes, glyphQuad, horizontalShift);
475 penX += glyph.xAdvance;
476 }
477
478 penX = 0.0f;
479 penY += (fontMetrics.lineHeight * mLineSpacingScale);
480 ++curLine;
481 }
482
483 mDimensions = glm::vec2(mMaxLineLength, linesOfShapedGlyphs.size() * fontMetrics.lineHeight * mLineSpacingScale);
484
485 UploadMeshes(meshes);
486
487 Initialize();
488}
std::vector< ShapedGlyph > ShapeText(const std::string &text)
Shapes text and returns shaped glyphs.

◆ SplitString()

std::vector< std::string > rendering_engine::TextBlock2D::SplitString ( const std::string &  text,
std::string  separator 
)
protected

Splits a string by a separator.

Parameters
textInput string.
separatorSeparator string.
Returns
List of substrings.

Definition at line 597 of file text_block_2d.cpp.

598{
599 std::vector<std::string> result;
600 std::string_view sv(text);
601 size_t start = 0, end = 0;
602
603 while (true) {
604 end = sv.find(separator, start);
605 // Extract token (including empty ones)
606 result.emplace_back(sv.substr(start, end - start));
607 if (end == std::string_view::npos) break;
608 start = end + 1; // Move past the delimiter
609 }
610
611 return result;
612}

◆ Update()

void rendering_engine::TextBlock2D::Update ( float  deltaTime)
overridevirtual

Updates logic (animation, movement, etc.) for this drawable.

Parameters
deltaTimeTime step (seconds).

Reimplemented from rendering_engine::Drawable2D.

Definition at line 70 of file text_block_2d.cpp.

71{
72 Drawable2D::Update(deltaTime);
73}
void Update(float deltaTime) override
Updates model matrix (and any other logic).
Definition: drawable_2d.cpp:17

◆ UploadMeshes()

void rendering_engine::TextBlock2D::UploadMeshes ( const std::unordered_map< std::string, TextBlock2D::Mesh > &  meshes)
protected

Uploads prepared meshes to GPU resources.

Parameters
meshesCPU-side mesh data.

Definition at line 570 of file text_block_2d.cpp.

571{
572 // Once meshes are ready, request its upload from ModelCache
573 auto modelCache = mTextRenderer->GetRenderResourceContext().meshCache;
574
575 for (auto& material : mMaterialMesh)
576 {
577 auto meshName = material.second;
578 auto mesh = meshes.at(meshName);
579 modelCache->LoadCustomMesh(meshName,
580 mesh.positions2D,
581 mesh.texCoords,
582 mesh.colors,
583 mesh.indices);
584 }
585
586 // Set mesh name and material name for THIS drawable, so the first font atlas for now only.
587
588 Shutdown();
589 for (const auto& materialMesh : mMaterialMesh)
590 {
591 const std::string meshName = materialMesh.second;
592 const std::string materialName = materialMesh.first;
593 AddRenderBatch(meshName, materialName);
594 }
595}
virtual void Shutdown()
Releases all render resources owned by this drawable.
void AddRenderBatch(std::string meshName, std::string materialName)

Member Data Documentation

◆ bIsTextShapeEnabled

const bool rendering_engine::TextBlock2D::bIsTextShapeEnabled
protected

Definition at line 272 of file text_block_2d.hpp.

◆ mColor

glm::vec4 rendering_engine::TextBlock2D::mColor
protected

Definition at line 262 of file text_block_2d.hpp.

◆ mDimensions

glm::vec2 rendering_engine::TextBlock2D::mDimensions
protected

Definition at line 270 of file text_block_2d.hpp.

◆ mFontName

const std::string rendering_engine::TextBlock2D::mFontName
protected

Definition at line 264 of file text_block_2d.hpp.

◆ mFontResources

std::shared_ptr<FontResources> rendering_engine::TextBlock2D::mFontResources
protected

Definition at line 261 of file text_block_2d.hpp.

◆ mFontSize

const unsigned int rendering_engine::TextBlock2D::mFontSize
protected

Definition at line 265 of file text_block_2d.hpp.

◆ mLineSpacingScale

float rendering_engine::TextBlock2D::mLineSpacingScale = 1.0f
protected

Definition at line 266 of file text_block_2d.hpp.

◆ mMaterialMesh

std::unordered_map<std::string, std::string> rendering_engine::TextBlock2D::mMaterialMesh
protected

Definition at line 258 of file text_block_2d.hpp.

◆ mMaxLineLength

float rendering_engine::TextBlock2D::mMaxLineLength = 0.0f
protected

Definition at line 268 of file text_block_2d.hpp.

◆ mOutlineThicknessPx

const float rendering_engine::TextBlock2D::mOutlineThicknessPx
protected

Definition at line 274 of file text_block_2d.hpp.

◆ mText

std::string rendering_engine::TextBlock2D::mText
protected

Definition at line 267 of file text_block_2d.hpp.

◆ mTextAlign

const TextAlign rendering_engine::TextBlock2D::mTextAlign
protected

Definition at line 269 of file text_block_2d.hpp.

◆ mTextRenderer

const std::shared_ptr<TextRenderer> rendering_engine::TextBlock2D::mTextRenderer
protected

Definition at line 260 of file text_block_2d.hpp.

◆ sDefaultFontName

std::string rendering_engine::TextBlock2D::sDefaultFontName = "RobotoMono-Regular"
staticprotected

Definition at line 263 of file text_block_2d.hpp.


The documentation for this class was generated from the following files: