• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "text_system.h"
17 
18 #include <ComponentTools/component_query.h>
19 #if defined(TEXT3D_ENABLE_EXTRUDING) && (TEXT3D_ENABLE_EXTRUDING)
20 #include <tesselator.h>
21 #endif
22 
23 #include <3d/ecs/components/material_component.h>
24 #include <3d/ecs/components/mesh_component.h>
25 #include <3d/ecs/components/render_handle_component.h>
26 #include <3d/ecs/components/render_mesh_component.h>
27 #include <3d/implementation_uids.h>
28 #include <3d/render/default_material_constants.h>
29 #include <3d/util/intf_mesh_builder.h>
30 #include <base/math/matrix_util.h>
31 #include <base/util/utf8_decode.h>
32 #include <core/ecs/intf_ecs.h>
33 #include <core/intf_engine.h>
34 #include <core/plugin/intf_class_factory.h>
35 #include <core/plugin/intf_class_register.h>
36 #include <core/property_tools/property_api_impl.inl>
37 #include <font/implementation_uids.h>
38 #include <font/intf_font_manager.h>
39 #include <render/device/intf_shader_manager.h>
40 #include <render/implementation_uids.h>
41 #include <render/intf_render_context.h>
42 #include <text_3d/ecs/components/text_component.h>
43 
44 TEXT3D_BEGIN_NAMESPACE()
45 using namespace BASE_NS;
46 using namespace CORE_NS;
47 using namespace RENDER_NS;
48 namespace {
49 struct MeshData {
50     BASE_NS::vector<BASE_NS::Math::Vec3> positions;
51     BASE_NS::vector<BASE_NS::Math::Vec2> uvs;
52     BASE_NS::vector<BASE_NS::Math::Vec3> normals;
53     BASE_NS::vector<uint16_t> indices;
54     RENDER_NS::RenderHandleReference atlas;
55 };
56 
57 struct PathVertex {
58     float x;
59     float y;
60 };
61 
GenerateMeshData(FONT_NS::IFont & font,BASE_NS::string_view text)62 MeshData GenerateMeshData(FONT_NS::IFont& font, BASE_NS::string_view text)
63 {
64     // should really go through all glyphs and group based on atlas texture.
65     // could also go through all the TextComponents and create one mesh for all the glyphs, submesh per atlas.
66     MeshData data;
67 
68     float posX = 0.f;
69     const char* str = text.data();
70     while (const uint32_t codepoint = BASE_NS::GetCharUtf8(&str)) {
71         const uint32_t glyphIndex = font.GetGlyphIndex(codepoint);
72         const auto& metrics = font.GetGlyphMetrics(glyphIndex);
73         const auto width = metrics.right - metrics.left;
74         const auto bl = BASE_NS::Math::Vec3(posX + metrics.left - metrics.leftBearing, metrics.bottom, 0.f);
75         const auto br = BASE_NS::Math::Vec3(bl.x + width, bl.y, 0.f);
76         const auto tr = BASE_NS::Math::Vec3(br.x, metrics.top, 0.f);
77         const auto tl = BASE_NS::Math::Vec3(bl.x, tr.y, 0.f);
78         posX += metrics.advance;
79         const auto i0 = static_cast<uint16_t>(data.positions.size());
80         data.positions.push_back(bl);
81         data.positions.push_back(br);
82         data.positions.push_back(tr);
83         data.positions.push_back(tl);
84 
85         const auto& info = font.GetGlyphInfo(glyphIndex);
86         data.atlas = info.atlas;
87         data.uvs.emplace_back(info.tl.x, info.br.y);
88         data.uvs.push_back(info.br);
89         data.uvs.emplace_back(info.br.x, info.tl.y);
90         data.uvs.push_back(info.tl);
91 
92         data.indices.push_back(i0);
93         data.indices.push_back(i0 + 2U); // 2U : idx
94         data.indices.push_back(i0 + 3U); // 3U : idx
95 
96         data.indices.push_back(i0);
97         data.indices.push_back(i0 + 1U);
98         data.indices.push_back(i0 + 2U); // 2U : idx
99     }
100     // +z facing normal for each vertex
101     data.normals.append(data.positions.size(), { 0.f, 0.f, 1.f });
102     return data;
103 }
104 
105 #if defined(TEXT3D_ENABLE_EXTRUDING) && (TEXT3D_ENABLE_EXTRUDING)
GenerateFrontBackFaces(const size_t vertexCount,const size_t elementCount,const size_t vertexOffset,const float depth,const TESSreal * vertices,const float posX,const Font::GlyphInfo & info,const TESSindex * elements,const Font::GlyphMetrics & metrics,int & elementOffset,MeshData & data)106 void GenerateFrontBackFaces(const size_t vertexCount, const size_t elementCount, const size_t vertexOffset,
107     const float depth, const TESSreal* vertices, const float posX, const Font::GlyphInfo& info,
108     const TESSindex* elements, const Font::GlyphMetrics& metrics, int& elementOffset, MeshData& data)
109 {
110     // front face vertices
111     for (size_t ii = 0; ii < vertexCount; ii++) {
112         const BASE_NS::Math::Vec3 pos(
113             vertices[2 * ii] + (posX + metrics.left - metrics.leftBearing), vertices[2 * ii + 1], 0.f); // 2 ;idx
114 
115         const BASE_NS::Math::Vec2 uv(
116             info.tl.x + (vertices[2 * ii] / (metrics.right - metrics.left)) * (info.br.x - info.tl.x), // 2 : idx
117             info.br.y + (vertices[2 * ii + 1] / (metrics.top - metrics.bottom)) * (info.tl.y - info.br.y)); // 2 : idx
118 
119         data.positions.push_back(pos);
120         data.uvs.push_back(uv);
121         data.normals.push_back({ 0.f, 0.f, 1.f });
122         elementOffset++;
123     }
124 
125     // front face indices
126     for (size_t ii = 0; ii < elementCount * 3; ii += 3) { // 3 : size
127         const uint16_t v0 = static_cast<uint16_t>(elements[ii] + vertexOffset);
128         const uint16_t v1 = static_cast<uint16_t>(elements[ii + 1] + vertexOffset);
129         const uint16_t v2 = static_cast<uint16_t>(elements[ii + 2] + vertexOffset); // 2 : idx
130 
131         data.indices.push_back(v2);
132         data.indices.push_back(v1);
133         data.indices.push_back(v0);
134     }
135 
136     // back face vertices
137     for (size_t ii = 0; ii < vertexCount; ii++) {
138         const BASE_NS::Math::Vec3 pos(
139             vertices[2 * ii] + (posX + metrics.left - metrics.leftBearing), vertices[2 * ii + 1], -depth); // 2 : idx
140 
141         const BASE_NS::Math::Vec2 uv = data.uvs[ii];
142 
143         data.positions.push_back(pos);
144         data.uvs.push_back(uv);
145         data.normals.push_back({ 0.f, 0.f, -1.f });
146         elementOffset++;
147     }
148 
149     // back face indices
150     for (size_t ii = 0; ii < elementCount * 3; ii += 3) { // 3 : idx
151         const uint16_t v0 = static_cast<uint16_t>(elements[ii] + vertexOffset + vertexCount);
152         const uint16_t v1 = static_cast<uint16_t>(elements[ii + 1] + vertexOffset + vertexCount);
153         const uint16_t v2 = static_cast<uint16_t>(elements[ii + 2] + vertexOffset + vertexCount); // 2 : idx
154 
155         data.indices.push_back(v0);
156         data.indices.push_back(v1);
157         data.indices.push_back(v2);
158     }
159 }
160 
GenerateSideFaces(const float depth,const float posX,const Font::GlyphInfo & info,const BASE_NS::vector<Font::GlyphContour> & contours,const Font::GlyphMetrics & metrics,int & elementOffset,MeshData & data)161 void GenerateSideFaces(const float depth, const float posX, const Font::GlyphInfo& info,
162     const BASE_NS::vector<Font::GlyphContour>& contours, const Font::GlyphMetrics& metrics, int& elementOffset,
163     MeshData& data)
164 {
165     // side faces
166     for (const auto& contour : contours) {
167         const size_t contourSize = contour.points.size();
168         for (size_t ptIdx = 0; ptIdx < contourSize; ptIdx++) {
169             const size_t nextPtIdx = (ptIdx + 1) % contourSize;
170             const BASE_NS::Math::Vec2& pt = contour.points[ptIdx];
171             const BASE_NS::Math::Vec2& nextPt = contour.points[nextPtIdx];
172 
173             // side face verticies
174             const BASE_NS::Math::Vec3 frontFacePos1(pt.x + (posX + metrics.left - metrics.leftBearing), pt.y, 0.f);
175             const BASE_NS::Math::Vec3 frontFacePos2(
176                 nextPt.x + (posX + metrics.left - metrics.leftBearing), nextPt.y, 0.f);
177             const BASE_NS::Math::Vec3 backFacePos1(pt.x + (posX + metrics.left - metrics.leftBearing), pt.y, -depth);
178             const BASE_NS::Math::Vec3 backFacePos2(
179                 nextPt.x + (posX + metrics.left - metrics.leftBearing), nextPt.y, -depth);
180 
181             data.positions.push_back(frontFacePos1);
182             data.positions.push_back(backFacePos1);
183             data.positions.push_back(frontFacePos2);
184             data.positions.push_back(backFacePos2);
185 
186             const float u1 = info.tl.x + (pt.x / (metrics.right - metrics.left)) * (info.br.x - info.tl.x);
187             const float u2 = info.tl.x + (nextPt.x / (metrics.right - metrics.left)) * (info.br.x - info.tl.x);
188 
189             data.uvs.emplace_back(u1, info.br.y);
190             data.uvs.emplace_back(u1, info.tl.y);
191             data.uvs.emplace_back(u2, info.br.y);
192             data.uvs.emplace_back(u2, info.tl.y);
193 
194             elementOffset += 4; // 4 : offset
195 
196             const BASE_NS::Math::Vec3 edge1 = backFacePos1 - frontFacePos1;
197             const BASE_NS::Math::Vec3 edge2 = frontFacePos2 - frontFacePos1;
198             const BASE_NS::Math::Vec3 normal = Math::Normalize(Math::Cross(edge1, edge2));
199 
200             data.normals.push_back(-normal);
201             data.normals.push_back(-normal);
202             data.normals.push_back(-normal);
203             data.normals.push_back(-normal);
204 
205             const size_t sideVertOffset = data.positions.size() - 4; // 4:size
206 
207             // side face indices
208             data.indices.push_back(static_cast<uint16_t>(sideVertOffset + 2)); // 2 : offset
209             data.indices.push_back(static_cast<uint16_t>(sideVertOffset + 1));
210             data.indices.push_back(static_cast<uint16_t>(sideVertOffset + 0));
211 
212             data.indices.push_back(static_cast<uint16_t>(sideVertOffset + 3)); // 3 : idx
213             data.indices.push_back(static_cast<uint16_t>(sideVertOffset + 1));
214             data.indices.push_back(static_cast<uint16_t>(sideVertOffset + 2)); // 2 : idx
215         }
216     }
217 }
218 
GenerateMeshData3D(FONT_NS::IFont & font,BASE_NS::string_view text,const float depth)219 MeshData GenerateMeshData3D(FONT_NS::IFont& font, BASE_NS::string_view text, const float depth)
220 {
221     MeshData data;
222     float posX = 0.f;
223     size_t vertexOffset = 0;
224     TESStesselator* tessellator = tessNewTess(nullptr);
225     tessSetOption(tessellator, TESS_CONSTRAINED_DELAUNAY_TRIANGULATION, 1);
226 
227     const char* str = text.data();
228     while (const uint32_t codepoint = BASE_NS::GetCharUtf8(&str)) {
229         const uint32_t glyphIndex = font.GetGlyphIndex(codepoint);
230         const auto& metrics = font.GetGlyphMetrics(glyphIndex);
231         const auto& info = font.GetGlyphInfo(glyphIndex);
232 
233         const auto contours = font.GetGlyphContours(glyphIndex);
234 
235         for (const auto& contour : contours) {
236             tessAddContour(tessellator, 2, contour.points.data(), sizeof(BASE_NS::Math::Vec2), // 2 :size
237                 static_cast<int>(contour.points.size()));
238         }
239 
240         if (tessTesselate(tessellator, TESS_WINDING_ODD, TESS_POLYGONS, 3, 2, nullptr)) { // 3,2 :size
241             const size_t vertexCount = static_cast<size_t>(tessGetVertexCount(tessellator));
242             const TESSreal* vertices = tessGetVertices(tessellator);
243             const size_t elementCount = static_cast<size_t>(tessGetElementCount(tessellator));
244             const TESSindex* elements = tessGetElements(tessellator);
245 
246             int elementOffset = 0;
247 
248             GenerateFrontBackFaces(vertexCount, elementCount, vertexOffset, depth, vertices, posX, info, elements,
249                 metrics, elementOffset, data);
250 
251             GenerateSideFaces(depth, posX, info, contours, metrics, elementOffset, data);
252 
253             vertexOffset += elementOffset;
254         }
255         posX += metrics.advance;
256     }
257 
258     tessDeleteTess(tessellator);
259     return data;
260 }
261 #endif
262 
263 template<typename T>
FillData(array_view<const T> c)264 constexpr inline IMeshBuilder::DataBuffer FillData(array_view<const T> c) noexcept
265 {
266     Format format = BASE_FORMAT_UNDEFINED;
267     if constexpr (is_same_v<T, Math::Vec2>) {
268         format = BASE_FORMAT_R32G32_SFLOAT;
269     } else if constexpr (is_same_v<T, Math::Vec3>) {
270         format = BASE_FORMAT_R32G32B32_SFLOAT;
271     } else if constexpr (is_same_v<T, Math::Vec4>) {
272         format = BASE_FORMAT_R32G32B32A32_SFLOAT;
273     } else if constexpr (is_same_v<T, uint16_t>) {
274         format = BASE_FORMAT_R16_UINT;
275     } else if constexpr (is_same_v<T, uint32_t>) {
276         format = BASE_FORMAT_R32_UINT;
277     }
278     return IMeshBuilder::DataBuffer { format, sizeof(T),
279         { reinterpret_cast<const uint8_t*>(c.data()), c.size() * sizeof(T) } };
280 }
281 
282 template<typename T>
FillData(const vector<T> & c)283 constexpr inline IMeshBuilder::DataBuffer FillData(const vector<T>& c) noexcept
284 {
285     return FillData(array_view<const T> { c });
286 }
287 
GetVertexInputDeclaration(IRenderContext & renderContext)288 VertexInputDeclarationView GetVertexInputDeclaration(IRenderContext& renderContext)
289 {
290     IShaderManager& shaderManager = renderContext.GetDevice().GetShaderManager();
291     return shaderManager.GetVertexInputDeclarationView(shaderManager.GetVertexInputDeclarationHandle(
292         DefaultMaterialShaderConstants::VERTEX_INPUT_DECLARATION_FORWARD));
293 }
294 } // namespace
295 
296 struct TextSystem::TextData {
297     uint32_t generation;
298     BASE_NS::string text;
299     BASE_NS::string fontFamily;
300     BASE_NS::string fontStyle;
301     float fontSize;
302     float font3DThickness { 10.0f }; // 10.0 : param
303     TextComponent::FontMethod fontMethod { TextComponent::FontMethod::RASTER };
304     FONT_NS::IFont::Ptr font;
305     CORE_NS::EntityReference mesh;
306 };
307 
SetActive(bool state)308 void TextSystem::SetActive(bool state)
309 {
310     active_ = (state) && (fontManager_);
311 }
312 
IsActive() const313 bool TextSystem::IsActive() const
314 {
315     return active_;
316 }
317 
TextSystem(IEcs & ecs)318 TextSystem::TextSystem(IEcs& ecs)
319     : ecs_(ecs), meshManager_(GetManager<IMeshComponentManager>(ecs)),
320       renderHandleManager_(GetManager<IRenderHandleComponentManager>(ecs)),
321       renderMeshManager_(GetManager<IRenderMeshComponentManager>(ecs)),
322       textManager_(GetManager<ITextComponentManager>(ecs))
323 {
324     if (auto* engineClassRegister = ecs_.GetClassFactory().GetInterface<IClassRegister>()) {
325         if (auto* renderClassRegister =
326                 CORE_NS::GetInstance<CORE_NS::IClassRegister>(*engineClassRegister, RENDER_NS::UID_RENDER_CONTEXT)) {
327             fontManager_ = CORE_NS::GetInstance<FONT_NS::IFontManager>(*renderClassRegister, FONT_NS::UID_FONT_MANAGER);
328             active_ = static_cast<bool>(fontManager_);
329         }
330     }
331 }
332 
333 TextSystem::~TextSystem() = default;
334 
GetName() const335 string_view TextSystem::GetName() const
336 {
337     return CORE3D_NS::GetName(this);
338 }
339 
GetUid() const340 Uid TextSystem::GetUid() const
341 {
342     return UID;
343 }
344 
GetProperties() const345 const IPropertyHandle* TextSystem::GetProperties() const
346 {
347     return nullptr;
348 }
349 
GetProperties()350 IPropertyHandle* TextSystem::GetProperties()
351 {
352     return nullptr;
353 }
354 
SetProperties(const IPropertyHandle &)355 void TextSystem::SetProperties(const IPropertyHandle&) {}
356 
GetECS() const357 const IEcs& TextSystem::GetECS() const
358 {
359     return ecs_;
360 }
361 
Initialize()362 void TextSystem::Initialize()
363 {
364     if (auto* engineClassRegister = ecs_.GetClassFactory().GetInterface<IClassRegister>()) {
365         if (auto* renderContext =
366                 CORE_NS::GetInstance<RENDER_NS::IRenderContext>(*engineClassRegister, RENDER_NS::UID_RENDER_CONTEXT)) {
367             RENDER_NS::IShaderManager& shaderManager = renderContext->GetDevice().GetShaderManager();
368 
369             const auto renderSlot = DefaultMaterialShaderConstants::RENDER_SLOT_FORWARD_TRANSLUCENT;
370             const uint32_t renderSlotId = shaderManager.GetRenderSlotId(renderSlot);
371             const RENDER_NS::IShaderManager::RenderSlotData rsd = shaderManager.GetRenderSlotData(renderSlotId);
372 
373             rasterShader_.shader = rsd.shader;
374             rasterShader_.graphicsState = rsd.graphicsState;
375             rasterShader_.depthShader =
376                 shaderManager.GetShaderHandle("3dshaders://shader/core3d_dm_depth_alpha.shader");
377 
378             sdfShader_.shader = shaderManager.GetShaderHandle("text3dshaders://shader/plugin_core3d_dm_fw_sdf.shader");
379             sdfShader_.graphicsState = rsd.graphicsState;
380             sdfShader_.depthShader =
381                 shaderManager.GetShaderHandle("text3dshaders://shader/plugin_core3d_dm_depth_alpha_sdf.shader");
382         }
383     }
384 }
385 
Update(bool frameRenderingQueued,uint64_t,uint64_t)386 bool TextSystem::Update(bool frameRenderingQueued, uint64_t, uint64_t)
387 {
388     if (!active_) {
389         return false;
390     }
391 
392     const auto counter = textManager_->GetGenerationCounter();
393     if (textManagerGeneration_ == counter) {
394         return false;
395     }
396     textManagerGeneration_ = counter;
397 
398     const auto components = textManager_->GetComponentCount();
399     for (IComponentManager::ComponentId id = 0U; id < components; ++id) {
400         const auto entity = textManager_->GetEntity(id);
401         auto pos = textCache_.find(entity);
402         if (pos != textCache_.end()) {
403             const auto generation = textManager_->GetComponentGeneration(id);
404             if (pos->second.generation == generation) {
405                 continue;
406             }
407         } else {
408             auto result = textCache_.insert({ entity, {} });
409             pos = result.first;
410         }
411         pos->second.generation = textManager_->GetComponentGeneration(id);
412 
413         auto textHandle = textManager_->Read(id);
414         if ((pos->second.fontFamily != textHandle->fontFamily) || (pos->second.fontStyle != textHandle->fontStyle)) {
415             pos->second.fontFamily = textHandle->fontFamily;
416             pos->second.fontStyle = textHandle->fontStyle;
417             if (auto* typeface = fontManager_->GetTypeFace(textHandle->fontFamily, textHandle->fontStyle)) {
418                 pos->second.font = fontManager_->CreateFont(*typeface);
419             }
420         }
421         if (!pos->second.font) {
422             CORE_LOG_ONCE_E(to_hex(entity.id), "Failed to create font with '%s' '%s'", textHandle->fontFamily.data(),
423                 textHandle->fontStyle.data());
424             continue;
425         }
426         if (pos->second.fontSize != textHandle->fontSize) {
427             pos->second.fontSize = textHandle->fontSize;
428             pos->second.font->SetSize(textHandle->fontSize);
429         }
430         if (pos->second.fontMethod != textHandle->fontMethod) {
431             pos->second.fontMethod = textHandle->fontMethod;
432             pos->second.font->SetMethod((textHandle->fontMethod == TextComponent::FontMethod::SDF)
433                                             ? Font::FontMethod::SDF
434                                             : Font::FontMethod::RASTER);
435         }
436         pos->second.text = textHandle->text;
437         GenerateMesh(*textHandle, pos->first, pos->second);
438     }
439     fontManager_->UploadPending();
440 
441     return true;
442 }
443 
GenerateMesh(const TextComponent & textComponent,CORE_NS::Entity entity,struct TextData & cached)444 void TextSystem::GenerateMesh(const TextComponent& textComponent, CORE_NS::Entity entity, struct TextData& cached)
445 {
446     if (!cached.font) {
447         return;
448     }
449 
450     auto* engineClassRegister = ecs_.GetClassFactory().GetInterface<IClassRegister>();
451     if (!engineClassRegister) {
452         return;
453     }
454     auto* renderContext = GetInstance<IRenderContext>(*engineClassRegister, UID_RENDER_CONTEXT);
455     if (!renderContext) {
456         return;
457     }
458     auto* renderClassFactory = renderContext->GetInterface<IClassFactory>();
459     if (!renderClassFactory) {
460         return;
461     }
462 
463 #if defined(TEXT3D_ENABLE_EXTRUDING) && (TEXT3D_ENABLE_EXTRUDING)
464     const bool extrudedGeometry = (textComponent.fontMethod == TextComponent::FontMethod::TEXT3D);
465     const auto data = extrudedGeometry ? GenerateMeshData3D(*cached.font, textComponent.text, cached.font3DThickness)
466                                        : GenerateMeshData(*cached.font, textComponent.text);
467 #else
468     const bool extrudedGeometry = false;
469     const auto data = GenerateMeshData(*cached.font, textComponent.text);
470 #endif
471     auto builder = CreateInstance<IMeshBuilder>(*renderClassFactory, UID_MESH_BUILDER);
472     builder->Initialize(GetVertexInputDeclaration(*renderContext), 1U);
473     builder->AddSubmesh(IMeshBuilder::Submesh { static_cast<uint32_t>(data.positions.size()),
474         static_cast<uint32_t>(data.indices.size()), 1U, 0U, IndexType::CORE_INDEX_TYPE_UINT16, entity, false, false,
475         false });
476     builder->Allocate();
477 
478     static constexpr auto empty = IMeshBuilder::DataBuffer {};
479     auto positionData = FillData(data.positions);
480     builder->SetVertexData(0U, positionData, FillData(data.normals), FillData(data.uvs), empty, empty, empty);
481     builder->CalculateAABB(0U, positionData);
482     builder->SetIndexData(0U, FillData(data.indices));
483 
484     auto meshEntity = builder->CreateMesh(ecs_);
485     renderMeshManager_->Create(entity);
486     renderMeshManager_->Write(entity)->mesh = meshEntity;
487 
488     cached.mesh = ecs_.GetEntityManager().GetReferenceCounted(meshEntity);
489 
490     // NOTE: ATM the materials created in the system are not available current frame
491     // The create-event happens after the system are processed
492     if (data.atlas || extrudedGeometry) {
493         auto* materialManager = GetManager<IMaterialComponentManager>(ecs_);
494         auto id = materialManager->GetComponentId(entity);
495         if (id == IComponentManager::INVALID_COMPONENT_ID) {
496             materialManager->Create(entity);
497             id = materialManager->GetComponentId(entity);
498         }
499         auto materialHandle = materialManager->Write(id);
500         if (!materialHandle) {
501             return;
502         }
503         materialHandle->textures[0U].image =
504             GetOrCreateEntityReference(ecs_.GetEntityManager(), *renderHandleManager_, data.atlas);
505 
506         if (!extrudedGeometry) {
507             const auto& shaderData =
508                 (textComponent.fontMethod == TextComponent::FontMethod::SDF) ? sdfShader_ : rasterShader_;
509             materialHandle->materialShader.shader =
510                 GetOrCreateEntityReference(ecs_.GetEntityManager(), *renderHandleManager_, shaderData.shader);
511             materialHandle->materialShader.graphicsState =
512                 GetOrCreateEntityReference(ecs_.GetEntityManager(), *renderHandleManager_, shaderData.graphicsState);
513             materialHandle->depthShader.shader =
514                 GetOrCreateEntityReference(ecs_.GetEntityManager(), *renderHandleManager_, shaderData.depthShader);
515         }
516     }
517 }
518 
Uninitialize()519 void TextSystem::Uninitialize() {}
520 
TextSystemInstance(IEcs & ecs)521 ISystem* TextSystemInstance(IEcs& ecs)
522 {
523     return new TextSystem(ecs);
524 }
525 
TextSystemDestroy(ISystem * instance)526 void TextSystemDestroy(ISystem* instance)
527 {
528     delete static_cast<TextSystem*>(instance);
529 }
530 TEXT3D_END_NAMESPACE()
531