Rendering Engine 0.2.17
Modular Graphics Rendering Engine | v0.2.17
Loading...
Searching...
No Matches
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.
 
void Initialize () override
 Initializes render resource pointers (material, mesh, etc.). Must be called after setting material and mesh names.
 
void Update (float deltaTime) override
 Updates logic (animation, movement, etc.) for this drawable.
 
void Draw (const Camera2D &camera) override
 Submits this quad to the renderer for drawing.
 
virtual void SetText (std::string text)
 Sets the displayed text.
 
void SetTextColor (glm::vec4 color)
 Sets the text color.
 
void SetOutlineColor (glm::vec4 color)
 Sets the outline color.
 
glm::vec2 GetDimensions () const
 Returns text block dimensions.
 
template<>
std::unordered_map< std::string, TextBlock2D::Mesh > PrepareMeshSlots (const std::vector< std::uint32_t > &glyphs)
 
template<>
std::unordered_map< std::string, TextBlock2D::Mesh > PrepareMeshSlots (const std::vector< ShapedGlyph > &glyphs)
 
template<>
std::unordered_map< std::string, TextBlock2D::Mesh > PrepareMeshSlots (const std::vector< std::uint32_t > &glyphs)
 
template<>
std::unordered_map< std::string, TextBlock2D::Mesh > PrepareMeshSlots (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.
 
void SetPosition (const glm::vec2 &position)
 Sets the quad position in 2D space.
 
void SetRotation (float angleDegrees)
 Sets the quad rotation.
 
void SetScale (const glm::vec2 &scale)
 Sets the quad scale along each axis.
 
const glm::vec2 & GetPosition () const
 Gets the quad position.
 
float GetRotation () const
 Gets the quad rotation angle (degrees).
 
const glm::vec2 & GetScale () const
 Gets the quad scale.
 
SceneComponent2D & GetTransform ()
 Access to the underlying SceneComponent2D (transform).
 
const SceneComponent2D & GetTransform () const
 
void Destroy () override
 Requests destruction of this drawable.
 
- Public Member Functions inherited from rendering_engine::DrawableComponent
 DrawableComponent (RenderResourceContext renderContext, Scene &scene)
 Constructs the DrawableComponent with a resource context.
 
virtual ~DrawableComponent ()=default
 Virtual destructor.
 
virtual void Shutdown ()
 Releases all render resources owned by this drawable.
 
void UpdateOnTick (bool in)
 
 DrawableComponent (const DrawableComponent &)=delete
 
DrawableComponent & operator= (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.
 
std::string CodepointToUtf8 (std::uint32_t codePoint)
 Converts a Unicode code point to UTF-8.
 
std::vector< ShapedGlyph > ShapeText (const std::string &text)
 Shapes text and returns shaped glyphs.
 
void ConstructMeshAutoLinebreak (const std::vector< std::uint32_t > &codePoints)
 
void ConstructMesh ()
 Constructs glyph geometry without shaping.
 
void ShapeTextAndConstructMesh ()
 Shapes text and constructs glyph geometry.
 
GlyphQuad MakeGlyphQuad (GlyphIndex glyphIndext, float penX, float penY)
 Creates a glyph quad at the current pen position.
 
void PushQuad (std::unordered_map< std::string, TextBlock2D::Mesh > &meshes, GlyphQuad glyphQuad, float horizontalShift=0.0f)
 Appends a glyph quad to a mesh.
 
void UploadMeshes (const std::unordered_map< std::string, TextBlock2D::Mesh > &meshes)
 Uploads prepared meshes to GPU resources.
 
void SetOutlineThickness (float thicknessPx)
 Sets outline thickness for all render batches.
 
template<typename T >
std::unordered_map< std::string, TextBlock2D::Mesh > PrepareMeshSlots (const std::vector< T > &glyphs)
 Prepares mesh slots based on glyph usage.
 
std::vector< std::string > SplitString (const std::string &text, std::string separator)
 Splits a string by a separator.
 
bool IsTextShapingRequired (std::uint32_t codePoint) const
 Checks whether shaping is required for a code point.
 
- 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< TextRenderer > mTextRenderer
 
std::shared_ptr< FontResources > mFontResources
 
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
 
Scene & mScene
 
std::vector< RenderBatch > mRenderBatches
 
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 mColor(glm::vec4(1.0f, 1.0f, 1.0f, 1.0f)),
46 mFontName(properties.fontName),
47 mFontSize(properties.fontSize),
48 mLineSpacingScale(properties.lineSpacingScale),
49 mMaxLineLength(properties.maxLineLength),
50 mTextAlign(properties.textAlign),
51 mDimensions(glm::vec2(0.0f, 0.0f)),
52 bIsTextShapeEnabled(properties.textShapeEnabled),
53 mOutlineThicknessPx(properties.outlineThicknessPx > 2.0f ? 2.0f : properties.outlineThicknessPx)
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.
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 644 of file text_block_2d.cpp.

645{
646 std::string out;
647
648 if (codePoint <= 0x7F) {
649 out.push_back(static_cast<char>(codePoint));
650 }
651 else if (codePoint <= 0x7FF) {
652 out.push_back(static_cast<char>(0xC0 | ((codePoint >> 6) & 0x1F)));
653 out.push_back(static_cast<char>(0x80 | (codePoint & 0x3F)));
654 }
655 else if (codePoint <= 0xFFFF) {
656 out.push_back(static_cast<char>(0xE0 | ((codePoint >> 12) & 0x0F)));
657 out.push_back(static_cast<char>(0x80 | ((codePoint >> 6) & 0x3F)));
658 out.push_back(static_cast<char>(0x80 | (codePoint & 0x3F)));
659 }
660 else if (codePoint <= 0x10FFFF) {
661 out.push_back(static_cast<char>(0xF0 | ((codePoint >> 18) & 0x07)));
662 out.push_back(static_cast<char>(0x80 | ((codePoint >> 12) & 0x3F)));
663 out.push_back(static_cast<char>(0x80 | ((codePoint >> 6) & 0x3F)));
664 out.push_back(static_cast<char>(0x80 | (codePoint & 0x3F)));
665 }
666
667 return out;
668}

◆ ConstructMesh()

void rendering_engine::TextBlock2D::ConstructMesh ( )
protected

Constructs glyph geometry without shaping.

Definition at line 324 of file text_block_2d.cpp.

325{
326 std::vector<std::uint32_t> allUsedGlyphs;
327 std::vector<std::vector<std::uint32_t>> linesOfGlyphs;
328
329 auto textRuns = SplitString(mText, "\n");
330
331 for (const auto& textRunString : textRuns)
332 {
333 auto textRunCodePoints = DecodeUtf8(textRunString);
334 linesOfGlyphs.push_back(textRunCodePoints);
335 std::copy(textRunCodePoints.begin(), textRunCodePoints.end(), std::back_inserter(allUsedGlyphs));
336 }
337 // It is better to ensure all required glyphs at once, as those if not ready yet,
338 // will be added to a single font atlas
339 mFontResources->EnsureGlyphs(allUsedGlyphs);
340 auto meshes = PrepareMeshSlots(allUsedGlyphs);
341
342
343 float maximumLineLengh = 0.0f;
344 std::vector<float> lineLengths;
345
346 std::vector<std::vector<GlyphQuad>> linesOfGlyphQuads;
347
348 const FontMetrics& fontMetrics = mFontResources->GetFontMetrics();
349 const float lineHeight = static_cast<float>(fontMetrics.lineHeight);
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 += static_cast<float>(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 += (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 float horizontalShift = 0.0f;
389 {
390 horizontalShift = (mMaxLineLength - lineLengths[curLine]) / 2.0f;
391 }
393 {
394 horizontalShift = (mMaxLineLength - lineLengths[curLine]);
395 }
396 PushQuad(meshes, glyphQuad, horizontalShift);
397 }
398 ++curLine;
399 }
400
401 UploadMeshes(meshes);
402
403 Initialize();
404}
std::vector< std::string > SplitString(const std::string &text, std::string separator)
Splits a string by a separator.
std::vector< std::uint32_t > DecodeUtf8(const std::string &text)
Decodes a UTF-8 string into Unicode code points.
void PushQuad(std::unordered_map< std::string, TextBlock2D::Mesh > &meshes, GlyphQuad glyphQuad, float horizontalShift=0.0f)
Appends a glyph quad to a mesh.
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.
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 const float lineHeight = static_cast<float>(fontMetrics.lineHeight);
214 // Pen position (baseline)
215 float penX = 0.0f;
216 float penY = 0.0f;
217
218 const std::uint32_t space{ 0x20 };
219 const std::uint32_t newLine{ 0x0A };
220
221 const bool autoTextWrappingRequested = mMaxLineLength > 0.0f;
222 if (!autoTextWrappingRequested)
223 {
224 for (std::uint32_t glyph : codePoints)
225 {
226 if (glyph == newLine)
227 {
228 penX = 0.0f;
229 penY += lineHeight;
230 continue;
231 }
232
233 GlyphIndex glyphIndex = mFontResources->GetIndexFromCodePoint(glyph);
234 GlyphQuad glyphQuad = MakeGlyphQuad(glyphIndex, penX, penY);
235 PushQuad(meshes, glyphQuad);
236 penX += static_cast<float>(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 += static_cast<float>(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 += static_cast<float>(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 PushQuad(meshes, quad, horizontalShift);
309 }
310 line.clear();
311
312 penX = 0.0f;
313 penY += lineHeight;
314 nextGlyphIndex = currentIndex;
315 }
316 }
317 }
318
319 UploadMeshes(meshes);
320
321 Initialize();
322}
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 <= 0x7FU)
154 {
155 // 1-byte sequence (ASCII)
156 codePoint = c;
157 i += 1;
158 }
159 else if ((c & 0xE0U) == 0xC0U)
160 {
161 // 2-byte sequence
162 if (i + 1 >= length) break;
163
164 codePoint =
165 ((c & 0x1FU) << 6) |
166 (bytes[i + 1] & 0x3FU);
167
168 i += 2;
169 }
170 else if ((c & 0xF0U) == 0xE0U)
171 {
172 // 3-byte sequence
173 if (i + 2 >= length) break;
174
175 codePoint =
176 ((c & 0x0FU) << 12) |
177 ((bytes[i + 1] & 0x3FU) << 6) |
178 (bytes[i + 2] & 0x3FU);
179
180 i += 3;
181 }
182 else if ((c & 0xF8U) == 0xF0U)
183 {
184 // 4-byte sequence
185 if (i + 3 >= length) break;
186
187 codePoint =
188 ((c & 0x07U) << 18) |
189 ((bytes[i + 1] & 0x3FU) << 12) |
190 ((bytes[i + 2] & 0x3FU) << 6) |
191 (bytes[i + 3] & 0x3FU);
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).
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.

◆ 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 629 of file text_block_2d.cpp.

630{
631 auto scriptsRequiredShaping = mTextRenderer->GetScriptsRequiredShaping();
632 for (const auto& script : scriptsRequiredShaping)
633 {
634 auto range = mTextRenderer->GetScriptRange(script);
635 if (codePoint >= range.first && codePoint <= range.second)
636 {
637 return true;
638 }
639 }
640
641 return false;
642}

◆ 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 492 of file text_block_2d.cpp.

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

◆ PrepareMeshSlots() [1/5]

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

◆ PrepareMeshSlots() [2/5]

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

Definition at line 739 of file text_block_2d.cpp.

740{
741 mMaterialMesh.clear();
742 // Prepare map of used material names with corresponding mesh names.
743 for (const auto& glyph : glyphs)
744 {
745 const std::uint32_t linefeed = 0x000A;
746 const std::uint32_t carriageReturn = 0x000D;
747 const std::uint32_t tab = 0x0009;
748 if (glyph.glyphIndex == linefeed || glyph.glyphIndex == carriageReturn || glyph.glyphIndex == tab)
749 {
750 continue;
751 }
752
753 GlyphIndex glyphIndex;
754 glyphIndex.index = glyph.glyphIndex;
755 auto fontAtlasMaterialName = mFontResources->GetFontAtlasMaterialName(glyphIndex);
756 const auto search = mMaterialMesh.find(fontAtlasMaterialName);
757 if (search == mMaterialMesh.end())
758 {
759 const std::string meshName = mTextBlockID + "_" + std::to_string(mMaterialMesh.size());
760 mMaterialMesh[fontAtlasMaterialName] = meshName;
761 }
762 }
763
764 // Prepare map of used mesh names with mesh structures
765 std::unordered_map<std::string, TextBlock2D::Mesh> meshes;
766 for (const auto& mesh : mMaterialMesh)
767 {
768 meshes[mesh.second] = TextBlock2D::Mesh();
769 }
770
771 return meshes;
772}
std::unordered_map< std::string, std::string > mMaterialMesh

◆ PrepareMeshSlots() [3/5]

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

◆ PrepareMeshSlots() [4/5]

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

Definition at line 708 of file text_block_2d.cpp.

709{
710 mMaterialMesh.clear();
711 // Prepare map of used material names with corresponding mesh names.
712 for (auto codePoint : glyphs)
713 {
714 if (codePoint == 0x000A || codePoint == 0x000D || codePoint == 0x0009)
715 {
716 continue;
717 }
718
719 GlyphIndex glyphIndex = mFontResources->GetIndexFromCodePoint(codePoint);
720 auto fontAtlasMaterialName = mFontResources->GetFontAtlasMaterialName(glyphIndex);
721 const auto search = mMaterialMesh.find(fontAtlasMaterialName);
722 if (search == mMaterialMesh.end())
723 {
724 const std::string meshName = mTextBlockID + "_" + std::to_string(mMaterialMesh.size());
725 mMaterialMesh[fontAtlasMaterialName] = meshName;
726 }
727 }
728
729 // Prepare map of used mesh names with mesh structures
730 std::unordered_map<std::string, TextBlock2D::Mesh> meshes;
731 for (const auto& mesh : mMaterialMesh)
732 {
733 meshes[mesh.second] = TextBlock2D::Mesh();
734 }
735 return meshes;
736}

◆ PrepareMeshSlots() [5/5]

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::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 539 of file text_block_2d.cpp.

540{
541 auto& mesh = meshes.at(mMaterialMesh[glyphQuad.fontAtlasMaterialName]);
542
543 if (mesh.positions2D.size() >
544 static_cast<std::size_t>(std::numeric_limits<std::uint32_t>::max()) - 3U)
545 {
546 throw std::overflow_error("TextBlock2D mesh exceeds 32-bit index range.");
547 }
548
549 const std::uint32_t vertexBase = static_cast<std::uint32_t>(mesh.positions2D.size());
550
551 const glm::vec2 shift(horizontalShift, 0.0f);
552
553 const glm::vec2 vert_0 = glm::vec2(glyphQuad.x0, glyphQuad.y0) + shift;
554 const glm::vec2 vert_1 = glm::vec2(glyphQuad.x1, glyphQuad.y0) + shift;
555 const glm::vec2 vert_2 = glm::vec2(glyphQuad.x1, glyphQuad.y1) + shift;
556 const glm::vec2 vert_3 = glm::vec2(glyphQuad.x0, glyphQuad.y1) + shift;
557
558 mesh.positions2D.push_back(vert_0);
559 mesh.positions2D.push_back(vert_1);
560 mesh.positions2D.push_back(vert_2);
561 mesh.positions2D.push_back(vert_3);
562
563 // UVs
564 mesh.texCoords.push_back(glm::vec2(glyphQuad.u0, glyphQuad.v0));
565 mesh.texCoords.push_back(glm::vec2(glyphQuad.u1, glyphQuad.v0));
566 mesh.texCoords.push_back(glm::vec2(glyphQuad.u1, glyphQuad.v1));
567 mesh.texCoords.push_back(glm::vec2(glyphQuad.u0, glyphQuad.v1));
568
569 // Colors
570 mesh.colors.push_back(glm::vec4(1.0f, 1.0f, 1.0f, 1.0f));
571 mesh.colors.push_back(glm::vec4(1.0f, 1.0f, 1.0f, 1.0f));
572 mesh.colors.push_back(glm::vec4(1.0f, 1.0f, 1.0f, 1.0f));
573 mesh.colors.push_back(glm::vec4(1.0f, 1.0f, 1.0f, 1.0f));
574
575 // Indices
576 mesh.indices.push_back(vertexBase + 0U);
577 mesh.indices.push_back(vertexBase + 1U);
578 mesh.indices.push_back(vertexBase + 2U);
579
580 mesh.indices.push_back(vertexBase + 2U);
581 mesh.indices.push_back(vertexBase + 3U);
582 mesh.indices.push_back(vertexBase + 0U);
583}

◆ 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 670 of file text_block_2d.cpp.

671{
672 std::vector<TextBlock2D::ShapedGlyph> result;
673
674 hb_buffer_t* buf;
675 buf = hb_buffer_create();
676 hb_buffer_add_utf8(buf, text.c_str(), -1, 0, -1);
677
678 hb_buffer_guess_segment_properties(buf);
679
680 auto* fontFace = mFontResources->GetFontFace();
681 hb_font_t* font = hb_ft_font_create_referenced(fontFace);
682
683 hb_shape(font, buf, NULL, 0);
684
685 unsigned int glyph_count;
686 hb_glyph_info_t* glyph_info = hb_buffer_get_glyph_infos(buf, &glyph_count);
687 hb_glyph_position_t* glyph_pos = hb_buffer_get_glyph_positions(buf, &glyph_count);
688
689 for (unsigned int i = 0; i < glyph_count; i++)
690 {
691 ShapedGlyph shapedGlyph;
692 shapedGlyph.glyphIndex = glyph_info[i].codepoint;
693 shapedGlyph.xOffset = static_cast<float>(glyph_pos[i].x_offset) / 64.0f;
694 shapedGlyph.yOffset = static_cast<float>(glyph_pos[i].y_offset) / 64.0f;
695 shapedGlyph.xAdvance =static_cast<float>(glyph_pos[i].x_advance) / 64.0f;
696 shapedGlyph.yAdvance = static_cast<float>(glyph_pos[i].y_advance) / 64.0f;
697 shapedGlyph.cluster = glyph_info[i].cluster;
698 result.push_back(shapedGlyph);
699 }
700
701 hb_font_destroy(font);
702 hb_buffer_destroy(buf);
703
704 return result;
705}

◆ ShapeTextAndConstructMesh()

void rendering_engine::TextBlock2D::ShapeTextAndConstructMesh ( )
protected

Shapes text and constructs glyph geometry.

Definition at line 406 of file text_block_2d.cpp.

407{
408 auto textRuns = SplitString(mText, "\n");
409
410 std::vector<std::vector<ShapedGlyph>> linesOfShapedGlyphs;
411 std::vector<ShapedGlyph> preShapedGlyphs;
412
413 for (const auto& textRun : textRuns)
414 {
415 auto shapedTextRun = ShapeText(textRun);
416 linesOfShapedGlyphs.push_back(shapedTextRun);
417 std::copy(shapedTextRun.begin(), shapedTextRun.end(), std::back_inserter(preShapedGlyphs));
418 }
419 std::vector<GlyphIndex> ensureGlyphs;
420 for (const auto& shapedGlyph : preShapedGlyphs)
421 {
422 GlyphIndex gi;
423 gi.index = shapedGlyph.glyphIndex;
424 ensureGlyphs.push_back(gi);
425 }
426 mFontResources->EnsureGlyphs(ensureGlyphs);
427
428 auto meshes = PrepareMeshSlots(preShapedGlyphs);
429
430 // Pen position (baseline)
431 float penX = 0.0f;
432 float penY = 0.0f;
433
434 float maximumLineLengh = 0.0f;
435 std::vector<float> lineLengths;
436
437 for (auto& line : linesOfShapedGlyphs)
438 {
439 float lineLength = 0.0f;
440 for (const auto& glyph : line)
441 {
442 lineLength += glyph.xAdvance;
443 }
444 lineLengths.push_back(lineLength);
445 if (lineLength > maximumLineLengh)
446 {
447 maximumLineLengh = lineLength;
448 }
449 }
450 mMaxLineLength = (mMaxLineLength > maximumLineLengh ? mMaxLineLength : maximumLineLengh);
451
452 const FontMetrics& fontMetrics = mFontResources->GetFontMetrics();
453 const float lineHeight = static_cast<float>(fontMetrics.lineHeight);
454 std::size_t curLine = 0U;
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
464 float horizontalShift = 0.0f;
466 {
467 horizontalShift = (mMaxLineLength - lineLengths[curLine]) / 2.0f;
468 }
470 {
471 horizontalShift = (mMaxLineLength - lineLengths[curLine]);
472 }
473 PushQuad(meshes, glyphQuad, horizontalShift);
474 penX += glyph.xAdvance;
475 }
476
477 penX = 0.0f;
478 penY += (lineHeight * mLineSpacingScale);
479 ++curLine;
480 }
481
482 const float lineCount =
483 static_cast<float>(linesOfShapedGlyphs.size());
484
485 mDimensions = glm::vec2(mMaxLineLength, lineCount * lineHeight * mLineSpacingScale);
486
487 UploadMeshes(meshes);
488
489 Initialize();
490}
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 612 of file text_block_2d.cpp.

613{
614 std::vector<std::string> result;
615 std::string_view sv(text);
616 size_t start = 0, end = 0;
617
618 while (true) {
619 end = sv.find(separator, start);
620 // Extract token (including empty ones)
621 result.emplace_back(sv.substr(start, end - start));
622 if (end == std::string_view::npos) break;
623 start = end + 1; // Move past the delimiter
624 }
625
626 return result;
627}

◆ 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).

◆ 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 585 of file text_block_2d.cpp.

586{
587 // Once meshes are ready, request its upload from ModelCache
588 auto modelCache = mTextRenderer->GetRenderResourceContext().meshCache;
589
590 for (auto& material : mMaterialMesh)
591 {
592 auto meshName = material.second;
593 auto mesh = meshes.at(meshName);
594 modelCache->LoadCustomMesh(meshName,
595 mesh.positions2D,
596 mesh.texCoords,
597 mesh.colors,
598 mesh.indices);
599 }
600
601 // Set mesh name and material name for THIS drawable, so the first font atlas for now only.
602
603 Shutdown();
604 for (const auto& materialMesh : mMaterialMesh)
605 {
606 const std::string meshName = materialMesh.second;
607 const std::string materialName = materialMesh.first;
608 AddRenderBatch(meshName, materialName);
609 }
610}
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 271 of file text_block_2d.hpp.

◆ mColor

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

Definition at line 261 of file text_block_2d.hpp.

◆ mDimensions

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

Definition at line 269 of file text_block_2d.hpp.

◆ mFontName

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

Definition at line 263 of file text_block_2d.hpp.

◆ mFontResources

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

Definition at line 260 of file text_block_2d.hpp.

◆ mFontSize

const unsigned int rendering_engine::TextBlock2D::mFontSize
protected

Definition at line 264 of file text_block_2d.hpp.

◆ mLineSpacingScale

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

Definition at line 265 of file text_block_2d.hpp.

◆ mMaterialMesh

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

Definition at line 257 of file text_block_2d.hpp.

◆ mMaxLineLength

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

Definition at line 267 of file text_block_2d.hpp.

◆ mOutlineThicknessPx

const float rendering_engine::TextBlock2D::mOutlineThicknessPx
protected

Definition at line 273 of file text_block_2d.hpp.

◆ mText

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

Definition at line 266 of file text_block_2d.hpp.

◆ mTextAlign

const TextAlign rendering_engine::TextBlock2D::mTextAlign
protected

Definition at line 268 of file text_block_2d.hpp.

◆ mTextRenderer

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

Definition at line 259 of file text_block_2d.hpp.

◆ sDefaultFontName

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

Definition at line 262 of file text_block_2d.hpp.


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