A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3using UnityEngine;
4using UnityEngine.UI;
5using UnityEngine.TextCore;
6using UnityEngine.TextCore.LowLevel;
7using Unity.Profiling;
8using Object = UnityEngine.Object;
9
10#pragma warning disable 0414 // Disabled a few warnings related to serialized variables not used in this script but used in the editor.
11
12
13namespace TMPro
14{
15
16 [DisallowMultipleComponent]
17 [RequireComponent(typeof(MeshRenderer))]
18 [AddComponentMenu("Mesh/TextMeshPro - Text")]
19 [ExecuteAlways]
20 #if UNITY_2023_2_OR_NEWER
21 [HelpURL("https://docs.unity3d.com/Packages/com.unity.ugui@2.0/manual/TextMeshPro/index.html")]
22 #else
23 [HelpURL("https://docs.unity3d.com/Packages/com.unity.textmeshpro@3.2")]
24 #endif
25 public class TextMeshPro : TMP_Text, ILayoutElement
26 {
27 // Public Properties and Serializable Properties
28
29 [SerializeField] internal int _SortingLayer;
30
31 /// <summary>
32 /// Sets the Renderer's sorting Layer ID
33 /// </summary>
34 public int sortingLayerID
35 {
36 get
37 {
38 if (renderer == null)
39 return 0;
40
41 return m_renderer.sortingLayerID;
42 }
43 set
44 {
45 if (renderer == null)
46 return;
47
48 m_renderer.sortingLayerID = value;
49 _SortingLayerID = value;
50
51 // Make sure sorting layer ID change is also reflected on sub text objects.
52 UpdateSubMeshSortingLayerID(value);
53 }
54 }
55 [SerializeField]
56 internal int _SortingLayerID;
57
58 /// <summary>
59 /// Sets the Renderer's sorting order within the assigned layer.
60 /// </summary>
61 public int sortingOrder
62 {
63 get
64 {
65 if (renderer == null)
66 return 0;
67
68 return m_renderer.sortingOrder;
69 }
70 set
71 {
72 if (renderer == null)
73 return;
74
75 m_renderer.sortingOrder = value;
76 _SortingOrder = value;
77
78 // Make sure sorting order change is also reflected on sub text objects.
79 UpdateSubMeshSortingOrder(value);
80 }
81 }
82 [SerializeField]
83 internal int _SortingOrder;
84
85 /// <summary>
86 /// Determines if the size of the text container will be adjusted to fit the text object when it is first created.
87 /// </summary>
88 public override bool autoSizeTextContainer
89 {
90 get { return m_autoSizeTextContainer; }
91
92 set { if (m_autoSizeTextContainer == value) return; m_autoSizeTextContainer = value; if (m_autoSizeTextContainer) { TMP_UpdateManager.RegisterTextElementForLayoutRebuild(this); SetLayoutDirty(); } }
93 }
94
95
96 /// <summary>
97 /// Returns a reference to the Text Container
98 /// </summary>
99 [Obsolete("The TextContainer is now obsolete. Use the RectTransform instead.")]
100 public TextContainer textContainer
101 {
102 get
103 {
104 return null;
105 }
106 }
107
108
109 /// <summary>
110 /// Returns a reference to the Transform
111 /// </summary>
112 public new Transform transform
113 {
114 get
115 {
116 if (m_transform == null)
117 m_transform = GetComponent<Transform>();
118
119 return m_transform;
120 }
121 }
122
123
124 #pragma warning disable 0108
125 /// <summary>
126 /// Returns the rendered assigned to the text object.
127 /// </summary>
128 public Renderer renderer
129 {
130 get
131 {
132 if (m_renderer == null)
133 m_renderer = GetComponent<Renderer>();
134
135 return m_renderer;
136 }
137 }
138
139
140 /// <summary>
141 /// Returns the mesh assigned to the text object.
142 /// </summary>
143 public override Mesh mesh
144 {
145 get
146 {
147 if (m_mesh == null)
148 {
149 m_mesh = new Mesh();
150 m_mesh.hideFlags = HideFlags.HideAndDontSave;
151 }
152
153 return m_mesh;
154 }
155 }
156
157 /// <summary>
158 /// Returns the Mesh Filter of the text object.
159 /// </summary>
160 public MeshFilter meshFilter
161 {
162 get
163 {
164 if (m_meshFilter == null)
165 {
166 m_meshFilter = GetComponent<MeshFilter>();
167
168 if (m_meshFilter == null)
169 {
170 m_meshFilter = gameObject.AddComponent<MeshFilter>();
171 m_meshFilter.hideFlags = HideFlags.HideInInspector | HideFlags.HideAndDontSave;
172 }
173 }
174
175 return m_meshFilter;
176 }
177 }
178
179 // MASKING RELATED PROPERTIES
180 /// <summary>
181 /// Sets the mask type
182 /// </summary>
183 public MaskingTypes maskType
184 {
185 get { return m_maskType; }
186 set { m_maskType = value; SetMask(m_maskType); }
187 }
188
189
190 /// <summary>
191 /// Function used to set the mask type and coordinates in World Space
192 /// </summary>
193 /// <param name="type"></param>
194 /// <param name="maskCoords"></param>
195 public void SetMask(MaskingTypes type, Vector4 maskCoords)
196 {
197 SetMask(type);
198
199 SetMaskCoordinates(maskCoords);
200 }
201
202 /// <summary>
203 /// Function used to set the mask type, coordinates and softness
204 /// </summary>
205 /// <param name="type"></param>
206 /// <param name="maskCoords"></param>
207 /// <param name="softnessX"></param>
208 /// <param name="softnessY"></param>
209 public void SetMask(MaskingTypes type, Vector4 maskCoords, float softnessX, float softnessY)
210 {
211 SetMask(type);
212
213 SetMaskCoordinates(maskCoords, softnessX, softnessY);
214 }
215
216
217 /// <summary>
218 /// Schedule rebuilding of the text geometry.
219 /// </summary>
220 public override void SetVerticesDirty()
221 {
222 //Debug.Log("***** SetVerticesDirty() called on object [" + this.name + "] at frame [" + Time.frameCount + "] *****");
223
224 if (this == null || !this.IsActive())
225 return;
226
227 TMP_UpdateManager.RegisterTextElementForGraphicRebuild(this);
228 }
229
230
231 /// <summary>
232 ///
233 /// </summary>
234 public override void SetLayoutDirty()
235 {
236 m_isPreferredWidthDirty = true;
237 m_isPreferredHeightDirty = true;
238
239 if (this == null || !this.IsActive())
240 return;
241
242 LayoutRebuilder.MarkLayoutForRebuild(this.rectTransform);
243
244 m_isLayoutDirty = true;
245 }
246
247
248 /// <summary>
249 /// Schedule updating of the material used by the text object.
250 /// </summary>
251 public override void SetMaterialDirty()
252 {
253 //Debug.Log("SetMaterialDirty()");
254
255 //if (!this.IsActive())
256 // return;
257
258 //m_isMaterialDirty = true;
259 UpdateMaterial();
260 //TMP_UpdateManager.RegisterTextElementForGraphicRebuild(this);
261 }
262
263
264 /// <summary>
265 ///
266 /// </summary>
267 public override void SetAllDirty()
268 {
269 SetLayoutDirty();
270 SetVerticesDirty();
271 SetMaterialDirty();
272 }
273
274
275 /// <summary>
276 ///
277 /// </summary>
278 /// <param name="update"></param>
279 public override void Rebuild(CanvasUpdate update)
280 {
281 if (this == null) return;
282
283 if (update == CanvasUpdate.Prelayout)
284 {
285 if (m_autoSizeTextContainer)
286 {
287 m_rectTransform.sizeDelta = GetPreferredValues(Mathf.Infinity, Mathf.Infinity);
288 }
289 }
290 else if (update == CanvasUpdate.PreRender)
291 {
292 this.OnPreRenderObject();
293
294 if (!m_isMaterialDirty) return;
295
296 UpdateMaterial();
297 m_isMaterialDirty = false;
298 }
299 }
300
301
302 /// <summary>
303 ///
304 /// </summary>
305 protected override void UpdateMaterial()
306 {
307 //Debug.Log("***** UpdateMaterial() called on object ID " + GetInstanceID() + ". *****");
308
309 //if (!this.IsActive())
310 // return;
311
312 if (renderer == null || m_sharedMaterial == null)
313 return;
314
315 // Only update the material if it has changed.
316 if (m_renderer.sharedMaterial == null || m_renderer.sharedMaterial.GetInstanceID() != m_sharedMaterial.GetInstanceID())
317 m_renderer.sharedMaterial = m_sharedMaterial;
318 }
319
320
321 /// <summary>
322 /// Function to be used to force recomputing of character padding when Shader / Material properties have been changed via script.
323 /// </summary>
324 public override void UpdateMeshPadding()
325 {
326 m_padding = ShaderUtilities.GetPadding(m_sharedMaterial, m_enableExtraPadding, m_isUsingBold);
327 m_isMaskingEnabled = ShaderUtilities.IsMaskingEnabled(m_sharedMaterial);
328 m_havePropertiesChanged = true;
329 checkPaddingRequired = false;
330
331 // Return if text object is not awake yet.
332 if (m_textInfo == null) return;
333
334 // Update sub text objects
335 for (int i = 1; i < m_textInfo.materialCount; i++)
336 m_subTextObjects[i].UpdateMeshPadding(m_enableExtraPadding, m_isUsingBold);
337 }
338
339
340 /// <summary>
341 /// Function to force regeneration of the text object before its normal process time. This is useful when changes to the text object properties need to be applied immediately.
342 /// </summary>
343 /// <param name="ignoreActiveState">Ignore Active State of text objects. Inactive objects are ignored by default.</param>
344 /// <param name="forceTextReparsing">Force re-parsing of the text.</param>
345 public override void ForceMeshUpdate(bool ignoreActiveState = false, bool forceTextReparsing = false)
346 {
347 m_havePropertiesChanged = true;
348 m_ignoreActiveState = ignoreActiveState;
349 OnPreRenderObject();
350 }
351
352
353 /// <summary>
354 /// Function used to evaluate the length of a text string.
355 /// </summary>
356 /// <param name="text"></param>
357 /// <returns></returns>
358 public override TMP_TextInfo GetTextInfo(string text)
359 {
360 SetText(text);
361 SetArraySizes(m_TextProcessingArray);
362
363 m_renderMode = TextRenderFlags.DontRender;
364
365 ComputeMarginSize();
366
367 GenerateTextMesh();
368
369 m_renderMode = TextRenderFlags.Render;
370
371 return this.textInfo;
372 }
373
374
375 /// <summary>
376 /// Function to clear the geometry of the Primary and Sub Text objects.
377 /// </summary>
378 public override void ClearMesh(bool updateMesh)
379 {
380 if (m_textInfo.meshInfo[0].mesh == null) m_textInfo.meshInfo[0].mesh = m_mesh;
381
382 m_textInfo.ClearMeshInfo(updateMesh);
383 }
384
385
386 /// <summary>
387 /// Event to allow users to modify the content of the text info before the text is rendered.
388 /// </summary>
389 public override event Action<TMP_TextInfo> OnPreRenderText;
390
391
392 /// <summary>
393 /// Function to update the geometry of the main and sub text objects.
394 /// </summary>
395 /// <param name="mesh"></param>
396 /// <param name="index"></param>
397 public override void UpdateGeometry(Mesh mesh, int index)
398 {
399 mesh.RecalculateBounds();
400 }
401
402
403 /// <summary>
404 /// Function to upload the updated vertex data and renderer.
405 /// </summary>
406 public override void UpdateVertexData(TMP_VertexDataUpdateFlags flags)
407 {
408 int materialCount = m_textInfo.materialCount;
409
410 for (int i = 0; i < materialCount; i++)
411 {
412 Mesh mesh;
413
414 if (i == 0)
415 mesh = m_mesh;
416 else
417 {
418 // Clear unused vertices
419 // TODO: Causes issues when sorting geometry as last vertex data attribute get wiped out.
420 //m_textInfo.meshInfo[i].ClearUnusedVertices();
421
422 mesh = m_subTextObjects[i].mesh;
423 }
424
425 //mesh.MarkDynamic();
426
427 if ((flags & TMP_VertexDataUpdateFlags.Vertices) == TMP_VertexDataUpdateFlags.Vertices)
428 mesh.vertices = m_textInfo.meshInfo[i].vertices;
429
430 if ((flags & TMP_VertexDataUpdateFlags.Uv0) == TMP_VertexDataUpdateFlags.Uv0)
431 mesh.SetUVs(0, m_textInfo.meshInfo[i].uvs0);
432
433 if ((flags & TMP_VertexDataUpdateFlags.Uv2) == TMP_VertexDataUpdateFlags.Uv2)
434 mesh.uv2 = m_textInfo.meshInfo[i].uvs2;
435
436 //if ((flags & TMP_VertexDataUpdateFlags.Uv4) == TMP_VertexDataUpdateFlags.Uv4)
437 // mesh.uv4 = m_textInfo.meshInfo[i].uvs4;
438
439 if ((flags & TMP_VertexDataUpdateFlags.Colors32) == TMP_VertexDataUpdateFlags.Colors32)
440 mesh.colors32 = m_textInfo.meshInfo[i].colors32;
441
442 mesh.RecalculateBounds();
443 }
444 }
445
446
447 /// <summary>
448 /// Function to upload the updated vertex data and renderer.
449 /// </summary>
450 public override void UpdateVertexData()
451 {
452 int materialCount = m_textInfo.materialCount;
453
454 for (int i = 0; i < materialCount; i++)
455 {
456 Mesh mesh;
457
458 if (i == 0)
459 mesh = m_mesh;
460 else
461 {
462 // Clear unused vertices
463 m_textInfo.meshInfo[i].ClearUnusedVertices();
464
465 mesh = m_subTextObjects[i].mesh;
466 }
467
468
469 //mesh.MarkDynamic();
470 mesh.vertices = m_textInfo.meshInfo[i].vertices;
471 mesh.SetUVs(0, m_textInfo.meshInfo[i].uvs0);
472 mesh.uv2 = m_textInfo.meshInfo[i].uvs2;
473 //mesh.uv4 = m_textInfo.meshInfo[i].uvs4;
474 mesh.colors32 = m_textInfo.meshInfo[i].colors32;
475
476 mesh.RecalculateBounds();
477 }
478 }
479
480 public void UpdateFontAsset()
481 {
482 LoadFontAsset();
483 }
484
485 public void CalculateLayoutInputHorizontal() { }
486
487 public void CalculateLayoutInputVertical() { }
488
489 #region TMPro_Private
490
491 [SerializeField]
492 private bool m_hasFontAssetChanged = false; // Used to track when font properties have changed.
493
494 float m_previousLossyScaleY = -1; // Used for Tracking lossy scale changes in the transform;
495
496 [SerializeField]
497 private Renderer m_renderer;
498 private MeshFilter m_meshFilter;
499
500 private bool m_isFirstAllocation; // Flag to determine if this is the first allocation of the buffers.
501 private int m_max_characters = 8; // Determines the initial allocation and size of the character array / buffer.
502 private int m_max_numberOfLines = 4; // Determines the initial allocation and maximum number of lines of text.
503
504 private TMP_SubMesh[] m_subTextObjects = new TMP_SubMesh[8];
505
506 // MASKING RELATED PROPERTIES
507
508 [SerializeField]
509 private MaskingTypes m_maskType;
510
511 // Matrix used to animated Env Map
512 private Matrix4x4 m_EnvMapMatrix = new Matrix4x4();
513
514 // Text Container / RectTransform Component
515 private Vector3[] m_RectTransformCorners = new Vector3[4];
516
517 [NonSerialized]
518 private bool m_isRegisteredForEvents;
519
520 // Profiler Marker declarations
521 private static ProfilerMarker k_GenerateTextMarker = new ProfilerMarker("TMP Layout Text");
522 private static ProfilerMarker k_SetArraySizesMarker = new ProfilerMarker("TMP.SetArraySizes");
523 private static ProfilerMarker k_GenerateTextPhaseIMarker = new ProfilerMarker("TMP GenerateText - Phase I");
524 private static ProfilerMarker k_ParseMarkupTextMarker = new ProfilerMarker("TMP Parse Markup Text");
525 private static ProfilerMarker k_CharacterLookupMarker = new ProfilerMarker("TMP Lookup Character & Glyph Data");
526 private static ProfilerMarker k_HandleGPOSFeaturesMarker = new ProfilerMarker("TMP Handle GPOS Features");
527 private static ProfilerMarker k_CalculateVerticesPositionMarker = new ProfilerMarker("TMP Calculate Vertices Position");
528 private static ProfilerMarker k_ComputeTextMetricsMarker = new ProfilerMarker("TMP Compute Text Metrics");
529 private static ProfilerMarker k_HandleVisibleCharacterMarker = new ProfilerMarker("TMP Handle Visible Character");
530 private static ProfilerMarker k_HandleWhiteSpacesMarker = new ProfilerMarker("TMP Handle White Space & Control Character");
531 private static ProfilerMarker k_HandleHorizontalLineBreakingMarker = new ProfilerMarker("TMP Handle Horizontal Line Breaking");
532 private static ProfilerMarker k_HandleVerticalLineBreakingMarker = new ProfilerMarker("TMP Handle Vertical Line Breaking");
533 private static ProfilerMarker k_SaveGlyphVertexDataMarker = new ProfilerMarker("TMP Save Glyph Vertex Data");
534 private static ProfilerMarker k_ComputeCharacterAdvanceMarker = new ProfilerMarker("TMP Compute Character Advance");
535 private static ProfilerMarker k_HandleCarriageReturnMarker = new ProfilerMarker("TMP Handle Carriage Return");
536 private static ProfilerMarker k_HandleLineTerminationMarker = new ProfilerMarker("TMP Handle Line Termination");
537 private static ProfilerMarker k_SavePageInfoMarker = new ProfilerMarker("TMP Save Page Info");
538 private static ProfilerMarker k_SaveTextExtentMarker = new ProfilerMarker("TMP Save Text Extent");
539 private static ProfilerMarker k_SaveProcessingStatesMarker = new ProfilerMarker("TMP Save Processing States");
540 private static ProfilerMarker k_GenerateTextPhaseIIMarker = new ProfilerMarker("TMP GenerateText - Phase II");
541 private static ProfilerMarker k_GenerateTextPhaseIIIMarker = new ProfilerMarker("TMP GenerateText - Phase III");
542
543
544 protected override void Awake()
545 {
546 //Debug.Log("***** Awake() called on object ID " + GetInstanceID() + ". *****");
547
548 #if UNITY_EDITOR
549 // Special handling for TMP Settings and importing Essential Resources
550 if (TMP_Settings.instance == null)
551 {
552 if (m_isWaitingOnResourceLoad == false)
553 TMPro_EventManager.RESOURCE_LOAD_EVENT.Add(ON_RESOURCES_LOADED);
554
555 m_isWaitingOnResourceLoad = true;
556 return;
557 }
558 #endif
559
560 // Cache Reference to the Mesh Renderer.
561 m_renderer = GetComponent<Renderer>();
562 if (m_renderer == null)
563 m_renderer = gameObject.AddComponent<Renderer>();
564
565 // Cache Reference to RectTransform
566 m_rectTransform = this.rectTransform;
567
568 // Cache Reference to the transform;
569 m_transform = this.transform;
570
571 // Cache a reference to the Mesh Filter.
572 m_meshFilter = GetComponent<MeshFilter>();
573 if (m_meshFilter == null)
574 m_meshFilter = gameObject.AddComponent<MeshFilter>();
575
576 // Create new Mesh if necessary and cache reference to it.
577 if (m_mesh == null)
578 {
579 m_mesh = new Mesh();
580 m_mesh.hideFlags = HideFlags.HideAndDontSave;
581 #if DEVELOPMENT_BUILD || UNITY_EDITOR
582 m_mesh.name = "TextMeshPro Mesh";
583 #endif
584 m_meshFilter.sharedMesh = m_mesh;
585
586 // Create new TextInfo for the text object.
587 m_textInfo = new TMP_TextInfo(this);
588 }
589 m_meshFilter.hideFlags = HideFlags.HideInInspector | HideFlags.HideAndDontSave;
590
591 #if UNITY_EDITOR
592 // Special handling for the CanvasRenderer which used to be automatically added by the Graphic class.
593 CanvasRenderer canvasRendererComponent = GetComponent<CanvasRenderer>();
594 if (canvasRendererComponent != null)
595 {
596 Debug.LogWarning("Please remove the CanvasRenderer component from the [" + this.name + "] GameObject as this component is no longer necessary.", this);
597 canvasRendererComponent.hideFlags = HideFlags.None;
598 }
599 #endif
600
601 // Load TMP Settings for new text object instances.
602 LoadDefaultSettings();
603
604 // Load the font asset and assign material to renderer.
605 LoadFontAsset();
606
607 // Allocate our initial buffers.
608 if (m_TextProcessingArray == null)
609 m_TextProcessingArray = new TextProcessingElement[m_max_characters];
610
611 m_cached_TextElement = new TMP_Character();
612 m_isFirstAllocation = true;
613
614 // Set flags to ensure our text is parsed and redrawn.
615 m_havePropertiesChanged = true;
616
617 m_isAwake = true;
618 }
619
620
621 protected override void OnEnable()
622 {
623 //Debug.Log("***** OnEnable() called on object ID " + GetInstanceID() + ". *****");
624
625 // Return if Awake() has not been called on the text object.
626 if (m_isAwake == false)
627 return;
628
629 // Register Callbacks for various events.
630 if (!m_isRegisteredForEvents)
631 {
632 #if UNITY_EDITOR
633 TMPro_EventManager.MATERIAL_PROPERTY_EVENT.Add(ON_MATERIAL_PROPERTY_CHANGED);
634 TMPro_EventManager.FONT_PROPERTY_EVENT.Add(ON_FONT_PROPERTY_CHANGED);
635 TMPro_EventManager.TEXTMESHPRO_PROPERTY_EVENT.Add(ON_TEXTMESHPRO_PROPERTY_CHANGED);
636 TMPro_EventManager.DRAG_AND_DROP_MATERIAL_EVENT.Add(ON_DRAG_AND_DROP_MATERIAL);
637 TMPro_EventManager.TEXT_STYLE_PROPERTY_EVENT.Add(ON_TEXT_STYLE_CHANGED);
638 TMPro_EventManager.COLOR_GRADIENT_PROPERTY_EVENT.Add(ON_COLOR_GRADIENT_CHANGED);
639 TMPro_EventManager.TMP_SETTINGS_PROPERTY_EVENT.Add(ON_TMP_SETTINGS_CHANGED);
640
641 UnityEditor.PrefabUtility.prefabInstanceUpdated += OnPrefabInstanceUpdate;
642 #endif
643 m_isRegisteredForEvents = true;
644 }
645
646 // Register text object for internal updates
647 if (m_IsTextObjectScaleStatic == false)
648 TMP_UpdateManager.RegisterTextObjectForUpdate(this);
649
650 meshFilter.sharedMesh = mesh;
651 SetActiveSubMeshes(true);
652
653 // Schedule potential text object update (if any of the properties have changed.
654 ComputeMarginSize();
655
656 SetAllDirty();
657
658 //m_havePropertiesChanged = true;
659 }
660
661
662 protected override void OnDisable()
663 {
664 //Debug.Log("***** OnDisable() called on object ID " + GetInstanceID() + ". *****");
665
666 // Return if Awake() has not been called on the text object.
667 if (m_isAwake == false)
668 return;
669
670 TMP_UpdateManager.UnRegisterTextElementForRebuild(this);
671 TMP_UpdateManager.UnRegisterTextObjectForUpdate(this);
672
673 meshFilter.sharedMesh = null;
674 SetActiveSubMeshes(false);
675 }
676
677
678 protected override void OnDestroy()
679 {
680 //Debug.Log("***** OnDestroy() called on object ID " + GetInstanceID() + ". *****");
681
682 // Destroy the mesh if we have one.
683 if (m_mesh != null)
684 DestroyImmediate(m_mesh);
685
686 // Unregister the event this object was listening to
687 #if UNITY_EDITOR
688 TMPro_EventManager.MATERIAL_PROPERTY_EVENT.Remove(ON_MATERIAL_PROPERTY_CHANGED);
689 TMPro_EventManager.FONT_PROPERTY_EVENT.Remove(ON_FONT_PROPERTY_CHANGED);
690 TMPro_EventManager.TEXTMESHPRO_PROPERTY_EVENT.Remove(ON_TEXTMESHPRO_PROPERTY_CHANGED);
691 TMPro_EventManager.DRAG_AND_DROP_MATERIAL_EVENT.Remove(ON_DRAG_AND_DROP_MATERIAL);
692 TMPro_EventManager.TEXT_STYLE_PROPERTY_EVENT.Remove(ON_TEXT_STYLE_CHANGED);
693 TMPro_EventManager.COLOR_GRADIENT_PROPERTY_EVENT.Remove(ON_COLOR_GRADIENT_CHANGED);
694 TMPro_EventManager.TMP_SETTINGS_PROPERTY_EVENT.Remove(ON_TMP_SETTINGS_CHANGED);
695 TMPro_EventManager.RESOURCE_LOAD_EVENT.Remove(ON_RESOURCES_LOADED);
696
697 UnityEditor.PrefabUtility.prefabInstanceUpdated -= OnPrefabInstanceUpdate;
698 #endif
699
700 m_isRegisteredForEvents = false;
701 TMP_UpdateManager.UnRegisterTextElementForRebuild(this);
702 TMP_UpdateManager.UnRegisterTextObjectForUpdate(this);
703 }
704
705
706 #if UNITY_EDITOR
707 protected override void Reset()
708 {
709 //Debug.Log("***** Reset() called on object ID " + GetInstanceID() + ". *****");
710
711 // Return if Awake() has not been called on the text object.
712 if (m_isAwake == false)
713 return;
714
715 if (m_mesh != null)
716 DestroyImmediate(m_mesh);
717
718 Awake();
719 }
720
721
722 protected override void OnValidate()
723 {
724 //Debug.Log("***** OnValidate() called on object ID " + GetInstanceID() + ". *****", this);
725
726 if (m_isAwake == false)
727 return;
728
729 if (meshFilter != null && m_meshFilter.hideFlags != (HideFlags.HideInInspector | HideFlags.HideAndDontSave))
730 m_meshFilter.hideFlags = HideFlags.HideInInspector | HideFlags.HideAndDontSave;
731
732 // Handle Font Asset changes in the inspector
733 if (m_fontAsset == null || m_hasFontAssetChanged)
734 {
735 LoadFontAsset();
736 m_hasFontAssetChanged = false;
737 }
738
739 m_padding = GetPaddingForMaterial();
740 ComputeMarginSize();
741
742 m_inputSource = TextInputSources.TextInputBox;
743 m_havePropertiesChanged = true;
744 m_isPreferredWidthDirty = true;
745 m_isPreferredHeightDirty = true;
746
747 SetAllDirty();
748 }
749
750 private void OnBecameVisible()
751 {
752 // Keep the parent text object's renderer in sync with child sub objects' renderers.
753 SetActiveSubTextObjectRenderers(true);
754 }
755
756 private void OnBecameInvisible()
757 {
758 // Keep the parent text object's renderer in sync with child sub objects' renderers.
759 SetActiveSubTextObjectRenderers(false);
760 }
761
762
763 /// <summary>
764 /// Callback received when Prefabs are updated.
765 /// </summary>
766 /// <param name="go">The affected GameObject</param>
767 void OnPrefabInstanceUpdate(GameObject go)
768 {
769 // Remove Callback if this prefab has been deleted.
770 if (this == null)
771 {
772 UnityEditor.PrefabUtility.prefabInstanceUpdated -= OnPrefabInstanceUpdate;
773 return;
774 }
775
776 if (go == this.gameObject)
777 {
778 TMP_SubMesh[] subTextObjects = GetComponentsInChildren<TMP_SubMesh>();
779 if (subTextObjects.Length > 0)
780 {
781 for (int i = 0; i < subTextObjects.Length; i++)
782 m_subTextObjects[i + 1] = subTextObjects[i];
783 }
784 }
785 }
786
787
788 // Event received when TMP resources have been loaded.
789 void ON_RESOURCES_LOADED()
790 {
791 TMPro_EventManager.RESOURCE_LOAD_EVENT.Remove(ON_RESOURCES_LOADED);
792
793 if (this == null)
794 return;
795
796 m_isWaitingOnResourceLoad = false;
797
798 Awake();
799 OnEnable();
800 }
801
802
803 // Event received when custom material editor properties are changed.
804 void ON_MATERIAL_PROPERTY_CHANGED(bool isChanged, Material mat)
805 {
806 //Debug.Log("ON_MATERIAL_PROPERTY_CHANGED event received. Targeted Material is: " + mat.name + " m_sharedMaterial: " + m_sharedMaterial.name + " m_renderer.sharedMaterial: " + m_renderer.sharedMaterial);
807
808 if (m_renderer.sharedMaterial == null)
809 {
810 if (m_fontAsset != null)
811 {
812 m_renderer.sharedMaterial = m_fontAsset.material;
813 Debug.LogWarning("No Material was assigned to " + name + ". " + m_fontAsset.material.name + " was assigned.", this);
814 }
815 else
816 {
817 Debug.LogWarning("No Font Asset assigned to " + name + ". Please assign a Font Asset.", this);
818 return;
819 }
820 }
821
822 // if (m_fontAsset.atlasTexture != null && m_fontAsset.atlasTexture.GetInstanceID() != m_renderer.sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID())
823 // {
824 // m_renderer.sharedMaterial = m_sharedMaterial;
825 // //m_renderer.sharedMaterial = m_fontAsset.material;
826 // Debug.LogWarning("Font Asset Atlas doesn't match the Atlas in the newly assigned material. Select a matching material or a different font asset.", this);
827 // }
828
829 if (m_renderer.sharedMaterial != m_sharedMaterial)
830 {
831 //Debug.Log("ON_MATERIAL_PROPERTY_CHANGED Called on Target ID: " + GetInstanceID() + ". Previous Material:" + m_sharedMaterial + " New Material:" + m_renderer.sharedMaterial); // on Object ID:" + GetInstanceID() + ". m_sharedMaterial: " + m_sharedMaterial.name + " m_renderer.sharedMaterial: " + m_renderer.sharedMaterial.name);
832 m_sharedMaterial = m_renderer.sharedMaterial;
833 }
834
835 m_padding = GetPaddingForMaterial();
836 //m_sharedMaterialHashCode = TMP_TextUtilities.GetSimpleHashCode(m_sharedMaterial.name);
837
838 UpdateMask();
839 ValidateEnvMapProperty();
840 m_havePropertiesChanged = true;
841
842 SetVerticesDirty();
843 }
844
845
846 // Event received when font asset properties are changed in Font Inspector
847 void ON_FONT_PROPERTY_CHANGED(bool isChanged, Object fontAsset)
848 {
849 //Debug.Log("ON_FONT_PROPERTY_CHANGED event received. Target is [" + font.name + "]");
850
851 // TODO: Optimize so we don't update all text objects when font asset properties are changed.
852 //if (MaterialReference.Contains(m_materialReferences, (TMP_FontAsset)fontAsset))
853 {
854 m_havePropertiesChanged = true;
855
856 UpdateMeshPadding();
857
858 SetAllDirty();
859 }
860 }
861
862
863 // Event received when UNDO / REDO Event alters the properties of the object.
864 void ON_TEXTMESHPRO_PROPERTY_CHANGED(bool isChanged, Object textComponent)
865 {
866 if (textComponent == this)
867 {
868 //Debug.Log("Undo / Redo Event Received by Object ID:" + GetInstanceID());
869 m_havePropertiesChanged = true;
870
871 m_padding = GetPaddingForMaterial();
872 ComputeMarginSize(); // Verify this change
873
874 SetVerticesDirty();
875 }
876 }
877
878
879 // Event to Track Material Changed resulting from Drag-n-drop.
880 void ON_DRAG_AND_DROP_MATERIAL(GameObject obj, Material currentMaterial, Material newMaterial)
881 {
882 //Debug.Log("Drag-n-Drop Event - Receiving Object ID " + GetInstanceID()); // + ". Target Object ID " + obj.GetInstanceID() + ". New Material is " + mat.name + " with ID " + mat.GetInstanceID() + ". Base Material is " + m_baseMaterial.name + " with ID " + m_baseMaterial.GetInstanceID());
883
884 // Check if event applies to this current object
885 if (obj == gameObject || UnityEditor.PrefabUtility.GetCorrespondingObjectFromSource(gameObject) == obj)
886 {
887 UnityEditor.Undo.RecordObject(this, "Material Assignment");
888 UnityEditor.Undo.RecordObject(m_renderer, "Material Assignment");
889
890 m_sharedMaterial = newMaterial;
891
892 m_padding = GetPaddingForMaterial();
893 m_havePropertiesChanged = true;
894
895 SetVerticesDirty();
896 SetMaterialDirty();
897 }
898 }
899
900
901 // Event received when Text Styles are changed.
902 void ON_TEXT_STYLE_CHANGED(bool isChanged)
903 {
904 m_havePropertiesChanged = true;
905
906 SetVerticesDirty();
907 }
908
909
910 /// <summary>
911 /// Event received when a Color Gradient Preset is modified.
912 /// </summary>
913 /// <param name="textObject"></param>
914 void ON_COLOR_GRADIENT_CHANGED(Object gradient)
915 {
916 m_havePropertiesChanged = true;
917
918 SetVerticesDirty();
919 }
920
921
922 /// <summary>
923 /// Event received when the TMP Settings are changed.
924 /// </summary>
925 void ON_TMP_SETTINGS_CHANGED()
926 {
927 m_defaultSpriteAsset = null;
928 m_havePropertiesChanged = true;
929
930 SetAllDirty();
931 }
932#endif
933
934
935 // Function which loads either the default font or a newly assigned font asset. This function also assigned the appropriate material to the renderer.
936 protected override void LoadFontAsset()
937 {
938 //Debug.Log("TextMeshPro LoadFontAsset() has been called."); // Current Font Asset is " + (font != null ? font.name: "Null") );
939
940 ShaderUtilities.GetShaderPropertyIDs(); // Initialize & Get shader property IDs.
941
942 if (m_fontAsset == null)
943 {
944 if (TMP_Settings.defaultFontAsset != null)
945 m_fontAsset = TMP_Settings.defaultFontAsset;
946
947 if (m_fontAsset == null)
948 {
949 Debug.LogWarning("The LiberationSans SDF Font Asset was not found. There is no Font Asset assigned to " + gameObject.name + ".", this);
950 return;
951 }
952
953 if (m_fontAsset.characterLookupTable == null)
954 {
955 Debug.Log("Dictionary is Null!");
956 }
957
958 m_sharedMaterial = m_fontAsset.material;
959 m_sharedMaterial.SetFloat("_CullMode", 0);
960
961 m_renderer.receiveShadows = false;
962 m_renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
963 }
964 else
965 {
966 if (m_fontAsset.characterLookupTable == null)
967 m_fontAsset.ReadFontAssetDefinition();
968
969 // If font atlas texture doesn't match the assigned material font atlas, switch back to default material specified in the Font Asset.
970 if (m_sharedMaterial == null || m_sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex) == null || m_fontAsset.atlasTexture.GetInstanceID() != m_sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID())
971 {
972 if (m_fontAsset.material == null)
973 Debug.LogWarning("The Font Atlas Texture of the Font Asset " + m_fontAsset.name + " assigned to " + gameObject.name + " is missing.", this);
974 else
975 m_sharedMaterial = m_fontAsset.material;
976 }
977 }
978
979 // Cache environment map property validation.
980 ValidateEnvMapProperty();
981
982 m_padding = GetPaddingForMaterial();
983 m_isMaskingEnabled = ShaderUtilities.IsMaskingEnabled(m_sharedMaterial);
984
985 // Find and cache Underline & Ellipsis characters.
986 GetSpecialCharacters(m_fontAsset);
987
988 SetMaterialDirty();
989 }
990
991 /// <summary>
992 /// Method to check if the environment map property is valid.
993 /// </summary>
994 void ValidateEnvMapProperty()
995 {
996 if (m_sharedMaterial != null)
997 m_hasEnvMapProperty = m_sharedMaterial.HasProperty(ShaderUtilities.ID_EnvMap) && m_sharedMaterial.GetTexture(ShaderUtilities.ID_EnvMap) != null;
998 else
999 m_hasEnvMapProperty = false;
1000 }
1001
1002 void UpdateEnvMapMatrix()
1003 {
1004 if (!m_hasEnvMapProperty)
1005 return;
1006
1007 //Debug.Log("Updating Env Matrix...");
1008 Vector3 rotation = m_sharedMaterial.GetVector(ShaderUtilities.ID_EnvMatrixRotation);
1009 #if !UNITY_EDITOR
1010 // The matrix property is reverted on editor save because m_sharedMaterial will be replaced with a new material instance.
1011 // Disable rotation change check if editor to handle this material change.
1012 if (m_currentEnvMapRotation == rotation)
1013 return;
1014 #endif
1015
1016 m_currentEnvMapRotation = rotation;
1017 m_EnvMapMatrix = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(rotation), Vector3.one);
1018
1019 m_sharedMaterial.SetMatrix(ShaderUtilities.ID_EnvMatrix, m_EnvMapMatrix);
1020 }
1021
1022
1023 //
1024 void SetMask(MaskingTypes maskType)
1025 {
1026 switch(maskType)
1027 {
1028 case MaskingTypes.MaskOff:
1029 m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_SOFT);
1030 m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_HARD);
1031 m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_TEX);
1032 break;
1033 case MaskingTypes.MaskSoft:
1034 m_sharedMaterial.EnableKeyword(ShaderUtilities.Keyword_MASK_SOFT);
1035 m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_HARD);
1036 m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_TEX);
1037 break;
1038 case MaskingTypes.MaskHard:
1039 m_sharedMaterial.EnableKeyword(ShaderUtilities.Keyword_MASK_HARD);
1040 m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_SOFT);
1041 m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_TEX);
1042 break;
1043 //case MaskingTypes.MaskTex:
1044 // m_sharedMaterial.EnableKeyword(ShaderUtilities.Keyword_MASK_TEX);
1045 // m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_HARD);
1046 // m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_SOFT);
1047 // break;
1048 }
1049 }
1050
1051
1052 // Method used to set the masking coordinates
1053 void SetMaskCoordinates(Vector4 coords)
1054 {
1055 m_sharedMaterial.SetVector(ShaderUtilities.ID_ClipRect, coords);
1056 }
1057
1058 // Method used to set the masking coordinates
1059 void SetMaskCoordinates(Vector4 coords, float softX, float softY)
1060 {
1061 m_sharedMaterial.SetVector(ShaderUtilities.ID_ClipRect, coords);
1062 m_sharedMaterial.SetFloat(ShaderUtilities.ID_MaskSoftnessX, softX);
1063 m_sharedMaterial.SetFloat(ShaderUtilities.ID_MaskSoftnessY, softY);
1064 }
1065
1066
1067
1068 // Enable Masking in the Shader
1069 void EnableMasking()
1070 {
1071 if (m_sharedMaterial.HasProperty(ShaderUtilities.ID_ClipRect))
1072 {
1073 m_sharedMaterial.EnableKeyword(ShaderUtilities.Keyword_MASK_SOFT);
1074 m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_HARD);
1075 m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_TEX);
1076
1077 m_isMaskingEnabled = true;
1078 UpdateMask();
1079 }
1080 }
1081
1082
1083 // Enable Masking in the Shader
1084 void DisableMasking()
1085 {
1086 if (m_sharedMaterial.HasProperty(ShaderUtilities.ID_ClipRect))
1087 {
1088 m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_SOFT);
1089 m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_HARD);
1090 m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_TEX);
1091
1092 m_isMaskingEnabled = false;
1093 UpdateMask();
1094 }
1095 }
1096
1097
1098 void UpdateMask()
1099 {
1100 //Debug.Log("UpdateMask() called.");
1101
1102 if (!m_isMaskingEnabled)
1103 {
1104 // Release Masking Material
1105
1106 // Re-assign Base Material
1107
1108 return;
1109 }
1110
1111 if (m_isMaskingEnabled && m_fontMaterial == null)
1112 {
1113 CreateMaterialInstance();
1114 }
1115
1116
1117 /*
1118 if (!m_isMaskingEnabled)
1119 {
1120 //Debug.Log("Masking is not enabled.");
1121 if (m_maskingPropertyBlock != null)
1122 {
1123 m_renderer.SetPropertyBlock(null);
1124 //havePropertiesChanged = true;
1125 }
1126 return;
1127 }
1128 //else
1129 // Debug.Log("Updating Masking...");
1130 */
1131
1132 // Compute Masking Coordinates & Softness
1133 //float softnessX = Mathf.Min(Mathf.Min(m_textContainer.margins.x, m_textContainer.margins.z), m_sharedMaterial.GetFloat(ShaderUtilities.ID_MaskSoftnessX));
1134 //float softnessY = Mathf.Min(Mathf.Min(m_textContainer.margins.y, m_textContainer.margins.w), m_sharedMaterial.GetFloat(ShaderUtilities.ID_MaskSoftnessY));
1135
1136 //softnessX = softnessX > 0 ? softnessX : 0;
1137 //softnessY = softnessY > 0 ? softnessY : 0;
1138
1139 //float width = (m_textContainer.width - Mathf.Max(m_textContainer.margins.x, 0) - Mathf.Max(m_textContainer.margins.z, 0)) / 2 + softnessX;
1140 //float height = (m_textContainer.height - Mathf.Max(m_textContainer.margins.y, 0) - Mathf.Max(m_textContainer.margins.w, 0)) / 2 + softnessY;
1141
1142 //Vector2 center = new Vector2((0.5f - m_textContainer.pivot.x) * m_textContainer.width + (Mathf.Max(m_textContainer.margins.x, 0) - Mathf.Max(m_textContainer.margins.z, 0)) / 2, (0.5f - m_textContainer.pivot.y) * m_textContainer.height + (- Mathf.Max(m_textContainer.margins.y, 0) + Mathf.Max(m_textContainer.margins.w, 0)) / 2);
1143 //Vector4 mask = new Vector4(center.x, center.y, width, height);
1144
1145
1146 //m_fontMaterial.SetVector(ShaderUtilities.ID_ClipRect, mask);
1147 //m_fontMaterial.SetFloat(ShaderUtilities.ID_MaskSoftnessX, softnessX);
1148 //m_fontMaterial.SetFloat(ShaderUtilities.ID_MaskSoftnessY, softnessY);
1149
1150 /*
1151 if(m_maskingPropertyBlock == null)
1152 {
1153 m_maskingPropertyBlock = new MaterialPropertyBlock();
1154
1155 //m_maskingPropertyBlock.AddFloat(ShaderUtilities.ID_VertexOffsetX, m_sharedMaterial.GetFloat(ShaderUtilities.ID_VertexOffsetX));
1156 //m_maskingPropertyBlock.AddFloat(ShaderUtilities.ID_VertexOffsetY, m_sharedMaterial.GetFloat(ShaderUtilities.ID_VertexOffsetY));
1157 //Debug.Log("Creating new MaterialPropertyBlock.");
1158 }
1159
1160 //Debug.Log("Updating Material Property Block.");
1161 //m_maskingPropertyBlock.Clear();
1162 m_maskingPropertyBlock.AddFloat(ShaderUtilities.ID_MaskID, m_renderer.GetInstanceID());
1163 m_maskingPropertyBlock.AddVector(ShaderUtilities.ID_MaskCoord, mask);
1164 m_maskingPropertyBlock.AddFloat(ShaderUtilities.ID_MaskSoftnessX, softnessX);
1165 m_maskingPropertyBlock.AddFloat(ShaderUtilities.ID_MaskSoftnessY, softnessY);
1166
1167 m_renderer.SetPropertyBlock(m_maskingPropertyBlock);
1168 */
1169 }
1170
1171
1172 // Function called internally when a new material is assigned via the fontMaterial property.
1173 protected override Material GetMaterial(Material mat)
1174 {
1175 // Check in case Object is disabled. If so, we don't have a valid reference to the Renderer.
1176 // This can occur when the Duplicate Material Context menu is used on an inactive object.
1177 //if (m_renderer == null)
1178 // m_renderer = GetComponent<Renderer>();
1179
1180 // Create Instance Material only if the new material is not the same instance previously used.
1181 if (m_fontMaterial == null || m_fontMaterial.GetInstanceID() != mat.GetInstanceID())
1182 m_fontMaterial = CreateMaterialInstance(mat);
1183
1184 m_sharedMaterial = m_fontMaterial;
1185
1186 m_padding = GetPaddingForMaterial();
1187
1188 SetVerticesDirty();
1189 SetMaterialDirty();
1190
1191 return m_sharedMaterial;
1192 }
1193
1194
1195 /// <summary>
1196 /// Method returning instances of the materials used by the text object.
1197 /// </summary>
1198 /// <returns></returns>
1199 protected override Material[] GetMaterials(Material[] mats)
1200 {
1201 int materialCount = m_textInfo.materialCount;
1202
1203 if (m_fontMaterials == null)
1204 m_fontMaterials = new Material[materialCount];
1205 else if (m_fontMaterials.Length != materialCount)
1206 TMP_TextInfo.Resize(ref m_fontMaterials, materialCount, false);
1207
1208 // Get instances of the materials
1209 for (int i = 0; i < materialCount; i++)
1210 {
1211 if (i == 0)
1212 m_fontMaterials[i] = fontMaterial;
1213 else
1214 m_fontMaterials[i] = m_subTextObjects[i].material;
1215 }
1216
1217 m_fontSharedMaterials = m_fontMaterials;
1218
1219 return m_fontMaterials;
1220 }
1221
1222
1223 // Function called internally when a new shared material is assigned via the fontSharedMaterial property.
1224 protected override void SetSharedMaterial(Material mat)
1225 {
1226 // Check in case Object is disabled. If so, we don't have a valid reference to the Renderer.
1227 // This can occur when the Duplicate Material Context menu is used on an inactive object.
1228 //if (m_renderer == null)
1229 // m_renderer = GetComponent<Renderer>();
1230
1231 m_sharedMaterial = mat;
1232
1233 m_padding = GetPaddingForMaterial();
1234
1235 SetMaterialDirty();
1236 }
1237
1238
1239 /// <summary>
1240 /// Method returning an array containing the materials used by the text object.
1241 /// </summary>
1242 /// <returns></returns>
1243 protected override Material[] GetSharedMaterials()
1244 {
1245 int materialCount = m_textInfo.materialCount;
1246
1247 if (m_fontSharedMaterials == null)
1248 m_fontSharedMaterials = new Material[materialCount];
1249 else if (m_fontSharedMaterials.Length != materialCount)
1250 TMP_TextInfo.Resize(ref m_fontSharedMaterials, materialCount, false);
1251
1252 for (int i = 0; i < materialCount; i++)
1253 {
1254 if (i == 0)
1255 m_fontSharedMaterials[i] = m_sharedMaterial;
1256 else
1257 m_fontSharedMaterials[i] = m_subTextObjects[i].sharedMaterial;
1258 }
1259
1260 return m_fontSharedMaterials;
1261 }
1262
1263
1264 /// <summary>
1265 /// Method used to assign new materials to the text and sub text objects.
1266 /// </summary>
1267 protected override void SetSharedMaterials(Material[] materials)
1268 {
1269 int materialCount = m_textInfo.materialCount;
1270
1271 // Check allocation of the fontSharedMaterials array.
1272 if (m_fontSharedMaterials == null)
1273 m_fontSharedMaterials = new Material[materialCount];
1274 else if (m_fontSharedMaterials.Length != materialCount)
1275 TMP_TextInfo.Resize(ref m_fontSharedMaterials, materialCount, false);
1276
1277 // Only assign as many materials as the text object contains.
1278 for (int i = 0; i < materialCount; i++)
1279 {
1280 Texture mat_MainTex = materials[i].GetTexture(ShaderUtilities.ID_MainTex);
1281
1282 if (i == 0)
1283 {
1284 // Only assign new material if the font atlas textures match.
1285 if ( mat_MainTex == null || mat_MainTex.GetInstanceID() != m_sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID())
1286 continue;
1287
1288 m_sharedMaterial = m_fontSharedMaterials[i] = materials[i];
1289 m_padding = GetPaddingForMaterial(m_sharedMaterial);
1290 }
1291 else
1292 {
1293 // Only assign new material if the font atlas textures match.
1294 if (mat_MainTex == null || mat_MainTex.GetInstanceID() != m_subTextObjects[i].sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID())
1295 continue;
1296
1297 // Only assign a new material if none were specified in the text input.
1298 if (m_subTextObjects[i].isDefaultMaterial)
1299 m_subTextObjects[i].sharedMaterial = m_fontSharedMaterials[i] = materials[i];
1300 }
1301 }
1302 }
1303
1304
1305 // This function will create an instance of the Font Material.
1306 protected override void SetOutlineThickness(float thickness)
1307 {
1308 thickness = Mathf.Clamp01(thickness);
1309 m_renderer.material.SetFloat(ShaderUtilities.ID_OutlineWidth, thickness);
1310
1311 if (m_fontMaterial == null)
1312 m_fontMaterial = m_renderer.material;
1313
1314 m_fontMaterial = m_renderer.material;
1315 m_sharedMaterial = m_fontMaterial;
1316 m_padding = GetPaddingForMaterial();
1317 }
1318
1319
1320 // This function will create an instance of the Font Material.
1321 protected override void SetFaceColor(Color32 color)
1322 {
1323 m_renderer.material.SetColor(ShaderUtilities.ID_FaceColor, color);
1324
1325 if (m_fontMaterial == null)
1326 m_fontMaterial = m_renderer.material;
1327
1328 m_sharedMaterial = m_fontMaterial;
1329 }
1330
1331
1332 // This function will create an instance of the Font Material.
1333 protected override void SetOutlineColor(Color32 color)
1334 {
1335 m_renderer.material.SetColor(ShaderUtilities.ID_OutlineColor, color);
1336
1337 if (m_fontMaterial == null)
1338 m_fontMaterial = m_renderer.material;
1339
1340 //Debug.Log("Material ID:" + m_fontMaterial.GetInstanceID());
1341 m_sharedMaterial = m_fontMaterial;
1342 }
1343
1344
1345 // Function used to create an instance of the material
1346 void CreateMaterialInstance()
1347 {
1348 Material mat = new Material(m_sharedMaterial);
1349 mat.shaderKeywords = m_sharedMaterial.shaderKeywords;
1350
1351 //mat.hideFlags = HideFlags.DontSave;
1352 mat.name += " Instance";
1353
1354 m_fontMaterial = mat;
1355 }
1356
1357
1358 // Sets the Render Queue and Ztest mode
1359 protected override void SetShaderDepth()
1360 {
1361 if (m_isOverlay)
1362 {
1363 // Changing these properties results in an instance of the material
1364 //m_sharedMaterial.SetFloat(ShaderUtilities.ShaderTag_ZTestMode, 0);
1365
1366 Material mat = m_renderer.material;
1367 //mat.renderQueue = 4000;
1368 m_sharedMaterial = mat;
1369 }
1370 else
1371 {
1372 // Should this use an instanced material?
1373 //m_sharedMaterial.SetFloat(ShaderUtilities.ShaderTag_ZTestMode, 4);
1374
1375 Material mat = m_renderer.material;
1376 //mat.renderQueue = -1;
1377 m_sharedMaterial = mat;
1378 }
1379 }
1380
1381
1382 // Sets the Culling mode of the material
1383 protected override void SetCulling()
1384 {
1385 if (m_isCullingEnabled)
1386 {
1387 m_renderer.material.SetFloat("_CullMode", 2);
1388
1389 for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
1390 {
1391 Renderer renderer = m_subTextObjects[i].renderer;
1392
1393 if (renderer != null)
1394 {
1395 renderer.material.SetFloat(ShaderUtilities.ShaderTag_CullMode, 2);
1396 }
1397 }
1398 }
1399 else
1400 {
1401 m_renderer.material.SetFloat("_CullMode", 0);
1402
1403 for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
1404 {
1405 Renderer renderer = m_subTextObjects[i].renderer;
1406
1407 if (renderer != null)
1408 {
1409 renderer.material.SetFloat(ShaderUtilities.ShaderTag_CullMode, 0);
1410 }
1411 }
1412 }
1413 }
1414
1415
1416 // Set Perspective Correction Mode based on whether Camera is Orthographic or Perspective
1417 void SetPerspectiveCorrection()
1418 {
1419 if (m_isOrthographic)
1420 m_sharedMaterial.SetFloat(ShaderUtilities.ID_PerspectiveFilter, 0.0f);
1421 else
1422 m_sharedMaterial.SetFloat(ShaderUtilities.ID_PerspectiveFilter, 0.875f);
1423 }
1424
1425
1426 // This function parses through the Char[] to determine how many characters will be visible. It then makes sure the arrays are large enough for all those characters.
1427 internal override int SetArraySizes(TextProcessingElement[] textProcessingArray)
1428 {
1429 k_SetArraySizesMarker.Begin();
1430
1431 int spriteCount = 0;
1432
1433 m_totalCharacterCount = 0;
1434 m_isUsingBold = false;
1435 m_isTextLayoutPhase = false;
1436 tag_NoParsing = false;
1437 m_FontStyleInternal = m_fontStyle;
1438 m_fontStyleStack.Clear();
1439
1440 m_FontWeightInternal = (m_FontStyleInternal & FontStyles.Bold) == FontStyles.Bold ? FontWeight.Bold : m_fontWeight;
1441 m_FontWeightStack.SetDefault(m_FontWeightInternal);
1442
1443 m_currentFontAsset = m_fontAsset;
1444 m_currentMaterial = m_sharedMaterial;
1445 m_currentMaterialIndex = 0;
1446
1447 m_materialReferenceStack.SetDefault(new MaterialReference(m_currentMaterialIndex, m_currentFontAsset, null, m_currentMaterial, m_padding));
1448
1449 m_materialReferenceIndexLookup.Clear();
1450 MaterialReference.AddMaterialReference(m_currentMaterial, m_currentFontAsset, ref m_materialReferences, m_materialReferenceIndexLookup);
1451
1452 // Set allocations for the text object's TextInfo
1453 if (m_textInfo == null)
1454 m_textInfo = new TMP_TextInfo(m_InternalTextProcessingArraySize);
1455 else if (m_textInfo.characterInfo.Length < m_InternalTextProcessingArraySize)
1456 TMP_TextInfo.Resize(ref m_textInfo.characterInfo, m_InternalTextProcessingArraySize, false);
1457
1458 m_textElementType = TMP_TextElementType.Character;
1459
1460 // Handling for Underline special character
1461 #region Setup Underline Special Character
1462 /*
1463 GetUnderlineSpecialCharacter(m_currentFontAsset);
1464 if (m_Underline.character != null)
1465 {
1466 if (m_Underline.fontAsset.GetInstanceID() != m_currentFontAsset.GetInstanceID())
1467 {
1468 if (TMP_Settings.matchMaterialPreset && m_currentMaterial.GetInstanceID() != m_Underline.fontAsset.material.GetInstanceID())
1469 m_Underline.material = TMP_MaterialManager.GetFallbackMaterial(m_currentMaterial, m_Underline.fontAsset.material);
1470 else
1471 m_Underline.material = m_Underline.fontAsset.material;
1472
1473 m_Underline.materialIndex = MaterialReference.AddMaterialReference(m_Underline.material, m_Underline.fontAsset, m_materialReferences, m_materialReferenceIndexLookup);
1474 m_materialReferences[m_Underline.materialIndex].referenceCount = 0;
1475 }
1476 }
1477 */
1478 #endregion
1479
1480
1481 // Handling for Ellipsis special character
1482 #region Setup Ellipsis Special Character
1483 if (m_overflowMode == TextOverflowModes.Ellipsis)
1484 {
1485 GetEllipsisSpecialCharacter(m_currentFontAsset);
1486
1487 if (m_Ellipsis.character != null)
1488 {
1489 if (m_Ellipsis.fontAsset.GetInstanceID() != m_currentFontAsset.GetInstanceID())
1490 {
1491 if (TMP_Settings.matchMaterialPreset && m_currentMaterial.GetInstanceID() != m_Ellipsis.fontAsset.material.GetInstanceID())
1492 m_Ellipsis.material = TMP_MaterialManager.GetFallbackMaterial(m_currentMaterial, m_Ellipsis.fontAsset.material);
1493 else
1494 m_Ellipsis.material = m_Ellipsis.fontAsset.material;
1495
1496 m_Ellipsis.materialIndex = MaterialReference.AddMaterialReference(m_Ellipsis.material, m_Ellipsis.fontAsset, ref m_materialReferences, m_materialReferenceIndexLookup);
1497 m_materialReferences[m_Ellipsis.materialIndex].referenceCount = 0;
1498 }
1499 }
1500 else
1501 {
1502 m_overflowMode = TextOverflowModes.Truncate;
1503
1504 if (!TMP_Settings.warningsDisabled)
1505 Debug.LogWarning("The character used for Ellipsis is not available in font asset [" + m_currentFontAsset.name + "] or any potential fallbacks. Switching Text Overflow mode to Truncate.", this);
1506 }
1507 }
1508 #endregion
1509
1510 // Check if we should process Ligatures
1511 bool ligature = m_ActiveFontFeatures.Contains(OTL_FeatureTag.liga);
1512
1513 // Clear Linked Text object if we have one.
1514 if (m_overflowMode == TextOverflowModes.Linked && m_linkedTextComponent != null && !m_isCalculatingPreferredValues)
1515 m_linkedTextComponent.text = string.Empty;
1516
1517 // Parsing XML tags in the text
1518 for (int i = 0; i < textProcessingArray.Length && textProcessingArray[i].unicode != 0; i++)
1519 {
1520 //Make sure the characterInfo array can hold the next text element.
1521 if (m_textInfo.characterInfo == null || m_totalCharacterCount >= m_textInfo.characterInfo.Length)
1522 TMP_TextInfo.Resize(ref m_textInfo.characterInfo, m_totalCharacterCount + 1, true);
1523
1524 uint unicode = textProcessingArray[i].unicode;
1525
1526 // PARSE XML TAGS
1527 #region PARSE XML TAGS
1528 if (m_isRichText && unicode == 60) // if Char '<'
1529 {
1530 int prev_MaterialIndex = m_currentMaterialIndex;
1531 int endTagIndex;
1532
1533 // Check if Tag is Valid
1534 if (ValidateHtmlTag(textProcessingArray, i + 1, out endTagIndex))
1535 {
1536 int tagStartIndex = textProcessingArray[i].stringIndex;
1537 i = endTagIndex;
1538
1539 if ((m_FontStyleInternal & FontStyles.Bold) == FontStyles.Bold)
1540 m_isUsingBold = true;
1541
1542 if (m_textElementType == TMP_TextElementType.Sprite)
1543 {
1544 m_materialReferences[m_currentMaterialIndex].referenceCount += 1;
1545
1546 m_textInfo.characterInfo[m_totalCharacterCount].character = (char)(57344 + m_spriteIndex);
1547 m_textInfo.characterInfo[m_totalCharacterCount].fontAsset = m_currentFontAsset;
1548 m_textInfo.characterInfo[m_totalCharacterCount].materialReferenceIndex = m_currentMaterialIndex;
1549 m_textInfo.characterInfo[m_totalCharacterCount].textElement = m_currentSpriteAsset.spriteCharacterTable[m_spriteIndex];
1550 m_textInfo.characterInfo[m_totalCharacterCount].elementType = m_textElementType;
1551 m_textInfo.characterInfo[m_totalCharacterCount].index = tagStartIndex;
1552 m_textInfo.characterInfo[m_totalCharacterCount].stringLength = textProcessingArray[i].stringIndex - tagStartIndex + 1;
1553
1554 // Restore element type and material index to previous values.
1555 m_textElementType = TMP_TextElementType.Character;
1556 m_currentMaterialIndex = prev_MaterialIndex;
1557
1558 spriteCount += 1;
1559 m_totalCharacterCount += 1;
1560 }
1561
1562 continue;
1563 }
1564 }
1565 #endregion
1566
1567 bool isUsingAlternativeTypeface = false;
1568 bool isUsingFallbackOrAlternativeTypeface = false;
1569
1570 TMP_FontAsset prev_fontAsset = m_currentFontAsset;
1571 Material prev_material = m_currentMaterial;
1572 int prev_materialIndex = m_currentMaterialIndex;
1573
1574 // Handle Font Styles like LowerCase, UpperCase and SmallCaps.
1575 #region Handling of LowerCase, UpperCase and SmallCaps Font Styles
1576 if (m_textElementType == TMP_TextElementType.Character)
1577 {
1578 if ((m_FontStyleInternal & FontStyles.UpperCase) == FontStyles.UpperCase)
1579 {
1580 // If this character is lowercase, switch to uppercase.
1581 if (char.IsLower((char)unicode))
1582 unicode = char.ToUpper((char)unicode);
1583
1584 }
1585 else if ((m_FontStyleInternal & FontStyles.LowerCase) == FontStyles.LowerCase)
1586 {
1587 // If this character is uppercase, switch to lowercase.
1588 if (char.IsUpper((char)unicode))
1589 unicode = char.ToLower((char)unicode);
1590 }
1591 else if ((m_FontStyleInternal & FontStyles.SmallCaps) == FontStyles.SmallCaps)
1592 {
1593 // Only convert lowercase characters to uppercase.
1594 if (char.IsLower((char)unicode))
1595 unicode = char.ToUpper((char)unicode);
1596 }
1597 }
1598 #endregion
1599
1600 // Lookup the Glyph data for each character and cache it.
1601 #region LOOKUP GLYPH
1602 TMP_TextElement character = null;
1603
1604 uint nextCharacter = i + 1 < textProcessingArray.Length ? textProcessingArray[i + 1].unicode : 0;
1605
1606 // Check Emoji Fallback first in the event the requested unicode code point is an Emoji
1607 if (emojiFallbackSupport && ((TMP_TextParsingUtilities.IsEmojiPresentationForm(unicode) && nextCharacter != 0xFE0E) || (TMP_TextParsingUtilities.IsEmoji(unicode) && nextCharacter == 0xFE0F)))
1608 {
1609 if (TMP_Settings.emojiFallbackTextAssets != null && TMP_Settings.emojiFallbackTextAssets.Count > 0)
1610 {
1611 character = TMP_FontAssetUtilities.GetTextElementFromTextAssets(unicode, m_currentFontAsset, TMP_Settings.emojiFallbackTextAssets, true, fontStyle, fontWeight, out isUsingAlternativeTypeface);
1612
1613 if (character != null)
1614 {
1615 // Add character to font asset lookup cache
1616 //fontAsset.AddCharacterToLookupCache(unicode, character);
1617 }
1618 }
1619 }
1620
1621 if (character == null)
1622 character = GetTextElement(unicode, m_currentFontAsset, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface);
1623
1624 // Check if Lowercase or Uppercase variant of the character is available.
1625 /* Not sure this is necessary anyone as it is very unlikely with recursive search through fallback fonts.
1626 if (glyph == null)
1627 {
1628 if (char.IsLower((char)c))
1629 {
1630 if (m_currentFontAsset.characterDictionary.TryGetValue(char.ToUpper((char)c), out glyph))
1631 c = chars[i] = char.ToUpper((char)c);
1632 }
1633 else if (char.IsUpper((char)c))
1634 {
1635 if (m_currentFontAsset.characterDictionary.TryGetValue(char.ToLower((char)c), out glyph))
1636 c = chars[i] = char.ToLower((char)c);
1637 }
1638 }*/
1639
1640 #region MISSING CHARACTER HANDLING
1641 // Replace missing glyph by the Square (9633) glyph or possibly the Space (32) glyph.
1642 if (character == null)
1643 {
1644 DoMissingGlyphCallback((int)unicode, textProcessingArray[i].stringIndex, m_currentFontAsset);
1645
1646 // Save the original unicode character
1647 uint srcGlyph = unicode;
1648
1649 // Try replacing the missing glyph character by TMP Settings Missing Glyph or Square (9633) character.
1650 unicode = textProcessingArray[i].unicode = (uint)TMP_Settings.missingGlyphCharacter == 0 ? 9633 : (uint)TMP_Settings.missingGlyphCharacter;
1651
1652 // Check for the missing glyph character in the currently assigned font asset and its fallbacks
1653 character = TMP_FontAssetUtilities.GetCharacterFromFontAsset((uint)unicode, m_currentFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface);
1654
1655 if (character == null)
1656 {
1657 // Search for the missing glyph character in the TMP Settings Fallback list.
1658 if (TMP_Settings.fallbackFontAssets != null && TMP_Settings.fallbackFontAssets.Count > 0)
1659 character = TMP_FontAssetUtilities.GetCharacterFromFontAssets(unicode, m_currentFontAsset, TMP_Settings.fallbackFontAssets, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface);
1660 }
1661
1662 if (character == null)
1663 {
1664 // Search for the missing glyph in the TMP Settings Default Font Asset.
1665 if (TMP_Settings.defaultFontAsset != null)
1666 character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(unicode, TMP_Settings.defaultFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface);
1667 }
1668
1669 if (character == null)
1670 {
1671 // Use Space (32) Glyph from the currently assigned font asset.
1672 unicode = textProcessingArray[i].unicode = 32;
1673 character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(unicode, m_currentFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface);
1674 }
1675
1676 if (character == null)
1677 {
1678 // Use End of Text (0x03) Glyph from the currently assigned font asset.
1679 unicode = textProcessingArray[i].unicode = 0x03;
1680 character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(unicode, m_currentFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface);
1681 }
1682
1683 if (!TMP_Settings.warningsDisabled)
1684 {
1685 string formattedWarning = srcGlyph > 0xFFFF
1686 ? string.Format("The character with Unicode value \\U{0:X8} was not found in the [{1}] font asset or any potential fallbacks. It was replaced by Unicode character \\u{2:X4} in text object [{3}].", srcGlyph, m_fontAsset.name, character.unicode, this.name)
1687 : string.Format("The character with Unicode value \\u{0:X4} was not found in the [{1}] font asset or any potential fallbacks. It was replaced by Unicode character \\u{2:X4} in text object [{3}].", srcGlyph, m_fontAsset.name, character.unicode, this.name);
1688
1689 Debug.LogWarning(formattedWarning, this);
1690 }
1691 }
1692 #endregion
1693
1694 m_textInfo.characterInfo[m_totalCharacterCount].alternativeGlyph = null;
1695
1696 if (character.elementType == TextElementType.Character)
1697 {
1698 if (character.textAsset.instanceID != m_currentFontAsset.instanceID)
1699 {
1700 isUsingFallbackOrAlternativeTypeface = true;
1701 m_currentFontAsset = character.textAsset as TMP_FontAsset;
1702 }
1703
1704 #region VARIATION SELECTOR
1705 if (nextCharacter >= 0xFE00 && nextCharacter <= 0xFE0F || nextCharacter >= 0xE0100 && nextCharacter <= 0xE01EF)
1706 {
1707 // Get potential variant glyph index
1708 uint variantGlyphIndex = m_currentFontAsset.GetGlyphVariantIndex((uint)unicode, nextCharacter);
1709
1710 if (variantGlyphIndex != 0)
1711 {
1712 if (m_currentFontAsset.TryAddGlyphInternal(variantGlyphIndex, out Glyph glyph))
1713 {
1714 m_textInfo.characterInfo[m_totalCharacterCount].alternativeGlyph = glyph;
1715 }
1716 }
1717
1718 textProcessingArray[i + 1].unicode = 0x1A;
1719 i += 1;
1720 }
1721 #endregion
1722
1723 #region LIGATURES
1724 if (ligature && m_currentFontAsset.fontFeatureTable.m_LigatureSubstitutionRecordLookup.TryGetValue(character.glyphIndex, out List<LigatureSubstitutionRecord> records))
1725 {
1726 if (records == null)
1727 break;
1728
1729 for (int j = 0; j < records.Count; j++)
1730 {
1731 LigatureSubstitutionRecord record = records[j];
1732
1733 int componentCount = record.componentGlyphIDs.Length;
1734 uint ligatureGlyphID = record.ligatureGlyphID;
1735
1736 //
1737 for (int k = 1; k < componentCount; k++)
1738 {
1739 uint componentUnicode = (uint)textProcessingArray[i + k].unicode;
1740
1741 // Special Handling for Zero Width Joiner (ZWJ)
1742 //if (componentUnicode == 0x200D)
1743 // continue;
1744
1745 uint glyphIndex = m_currentFontAsset.GetGlyphIndex(componentUnicode);
1746
1747 if (glyphIndex == record.componentGlyphIDs[k])
1748 continue;
1749
1750 ligatureGlyphID = 0;
1751 break;
1752 }
1753
1754 if (ligatureGlyphID != 0)
1755 {
1756 if (m_currentFontAsset.TryAddGlyphInternal(ligatureGlyphID, out Glyph glyph))
1757 {
1758 m_textInfo.characterInfo[m_totalCharacterCount].alternativeGlyph = glyph;
1759
1760 // Update text processing array
1761 for (int c = 0; c < componentCount; c++)
1762 {
1763 if (c == 0)
1764 {
1765 textProcessingArray[i + c].length = componentCount;
1766 continue;
1767 }
1768
1769 textProcessingArray[i + c].unicode = 0x1A;
1770 }
1771
1772 i += componentCount - 1;
1773 break;
1774 }
1775 }
1776 }
1777 }
1778 #endregion
1779 }
1780 #endregion
1781
1782 // Save text element data
1783 m_textInfo.characterInfo[m_totalCharacterCount].elementType = TMP_TextElementType.Character;
1784 m_textInfo.characterInfo[m_totalCharacterCount].textElement = character;
1785 m_textInfo.characterInfo[m_totalCharacterCount].isUsingAlternateTypeface = isUsingAlternativeTypeface;
1786 m_textInfo.characterInfo[m_totalCharacterCount].character = (char)unicode;
1787 m_textInfo.characterInfo[m_totalCharacterCount].index = textProcessingArray[i].stringIndex;
1788 m_textInfo.characterInfo[m_totalCharacterCount].stringLength = textProcessingArray[i].length;
1789 m_textInfo.characterInfo[m_totalCharacterCount].fontAsset = m_currentFontAsset;
1790
1791 // Special handling if the character is a sprite.
1792 if (character.elementType == TextElementType.Sprite)
1793 {
1794 TMP_SpriteAsset spriteAssetRef = character.textAsset as TMP_SpriteAsset;
1795 m_currentMaterialIndex = MaterialReference.AddMaterialReference(spriteAssetRef.material, spriteAssetRef, ref m_materialReferences, m_materialReferenceIndexLookup);
1796 m_materialReferences[m_currentMaterialIndex].referenceCount += 1;
1797
1798 m_textInfo.characterInfo[m_totalCharacterCount].elementType = TMP_TextElementType.Sprite;
1799 m_textInfo.characterInfo[m_totalCharacterCount].materialReferenceIndex = m_currentMaterialIndex;
1800
1801 // Restore element type and material index to previous values.
1802 m_textElementType = TMP_TextElementType.Character;
1803 m_currentMaterialIndex = prev_materialIndex;
1804
1805 spriteCount += 1;
1806 m_totalCharacterCount += 1;
1807
1808 continue;
1809 }
1810
1811 if (isUsingFallbackOrAlternativeTypeface && m_currentFontAsset.instanceID != m_fontAsset.instanceID)
1812 {
1813 // Create Fallback material instance matching current material preset if necessary
1814 if (TMP_Settings.matchMaterialPreset)
1815 m_currentMaterial = TMP_MaterialManager.GetFallbackMaterial(m_currentMaterial, m_currentFontAsset.material);
1816 else
1817 m_currentMaterial = m_currentFontAsset.material;
1818
1819 m_currentMaterialIndex = MaterialReference.AddMaterialReference(m_currentMaterial, m_currentFontAsset, ref m_materialReferences, m_materialReferenceIndexLookup);
1820 }
1821
1822 // Handle Multi Atlas Texture support
1823 if (character != null && character.glyph.atlasIndex > 0)
1824 {
1825 m_currentMaterial = TMP_MaterialManager.GetFallbackMaterial(m_currentFontAsset, m_currentMaterial, character.glyph.atlasIndex);
1826
1827 m_currentMaterialIndex = MaterialReference.AddMaterialReference(m_currentMaterial, m_currentFontAsset, ref m_materialReferences, m_materialReferenceIndexLookup);
1828
1829 isUsingFallbackOrAlternativeTypeface = true;
1830 }
1831
1832 if (!char.IsWhiteSpace((char)unicode) && unicode != 0x200B)
1833 {
1834 // Limit the mesh of the main text object to 65535 vertices and use sub objects for the overflow.
1835 if (m_materialReferences[m_currentMaterialIndex].referenceCount < 16383)
1836 m_materialReferences[m_currentMaterialIndex].referenceCount += 1;
1837 else
1838 {
1839 m_currentMaterialIndex = MaterialReference.AddMaterialReference(new Material(m_currentMaterial), m_currentFontAsset, ref m_materialReferences, m_materialReferenceIndexLookup);
1840 m_materialReferences[m_currentMaterialIndex].referenceCount += 1;
1841 }
1842 }
1843
1844 m_textInfo.characterInfo[m_totalCharacterCount].material = m_currentMaterial;
1845 m_textInfo.characterInfo[m_totalCharacterCount].materialReferenceIndex = m_currentMaterialIndex;
1846 m_materialReferences[m_currentMaterialIndex].isFallbackMaterial = isUsingFallbackOrAlternativeTypeface;
1847
1848 // Restore previous font asset and material if fallback font was used.
1849 if (isUsingFallbackOrAlternativeTypeface)
1850 {
1851 m_materialReferences[m_currentMaterialIndex].fallbackMaterial = prev_material;
1852 m_currentFontAsset = prev_fontAsset;
1853 m_currentMaterial = prev_material;
1854 m_currentMaterialIndex = prev_materialIndex;
1855 }
1856
1857 m_totalCharacterCount += 1;
1858 }
1859
1860 // Early return if we are calculating the preferred values.
1861 if (m_isCalculatingPreferredValues)
1862 {
1863 m_isCalculatingPreferredValues = false;
1864
1865 k_SetArraySizesMarker.End();
1866 return m_totalCharacterCount;
1867 }
1868
1869 // Save material and sprite count.
1870 m_textInfo.spriteCount = spriteCount;
1871 int materialCount = m_textInfo.materialCount = m_materialReferenceIndexLookup.Count;
1872
1873 // Check if we need to resize the MeshInfo array for handling different materials.
1874 if (materialCount > m_textInfo.meshInfo.Length)
1875 TMP_TextInfo.Resize(ref m_textInfo.meshInfo, materialCount, false);
1876
1877 // Resize SubTextObject array if necessary
1878 if (materialCount > m_subTextObjects.Length)
1879 TMP_TextInfo.Resize(ref m_subTextObjects, Mathf.NextPowerOfTwo(materialCount + 1));
1880
1881 // Resize CharacterInfo[] if allocations are excessive
1882 if (m_VertexBufferAutoSizeReduction && m_textInfo.characterInfo.Length - m_totalCharacterCount > 256)
1883 TMP_TextInfo.Resize(ref m_textInfo.characterInfo, Mathf.Max(m_totalCharacterCount + 1, 256), true);
1884
1885
1886 // Iterate through the material references to set the mesh buffer allocations
1887 for (int i = 0; i < materialCount; i++)
1888 {
1889 // Add new sub text object for each material reference
1890 if (i > 0)
1891 {
1892 if (m_subTextObjects[i] == null)
1893 {
1894 m_subTextObjects[i] = TMP_SubMesh.AddSubTextObject(this, m_materialReferences[i]);
1895
1896 // Not sure this is necessary
1897 m_textInfo.meshInfo[i].vertices = null;
1898 }
1899 //else if (m_subTextObjects[i].gameObject.activeInHierarchy == false)
1900 // m_subTextObjects[i].gameObject.SetActive(true);
1901
1902 // Check if the material has changed.
1903 if (m_subTextObjects[i].sharedMaterial == null || m_subTextObjects[i].sharedMaterial.GetInstanceID() != m_materialReferences[i].material.GetInstanceID())
1904 {
1905 m_subTextObjects[i].sharedMaterial = m_materialReferences[i].material;
1906 m_subTextObjects[i].fontAsset = m_materialReferences[i].fontAsset;
1907 m_subTextObjects[i].spriteAsset = m_materialReferences[i].spriteAsset;
1908 }
1909
1910 // Check if we need to use a Fallback Material
1911 if (m_materialReferences[i].isFallbackMaterial)
1912 {
1913 m_subTextObjects[i].fallbackMaterial = m_materialReferences[i].material;
1914 m_subTextObjects[i].fallbackSourceMaterial = m_materialReferences[i].fallbackMaterial;
1915 }
1916 }
1917
1918 int referenceCount = m_materialReferences[i].referenceCount;
1919
1920 // Check to make sure our buffers allocations can accommodate the required text elements.
1921 if (m_textInfo.meshInfo[i].vertices == null || m_textInfo.meshInfo[i].vertices.Length < referenceCount * 4)
1922 {
1923 if (m_textInfo.meshInfo[i].vertices == null)
1924 {
1925 if (i == 0)
1926 m_textInfo.meshInfo[i] = new TMP_MeshInfo(m_mesh, referenceCount + 1);
1927 else
1928 m_textInfo.meshInfo[i] = new TMP_MeshInfo(m_subTextObjects[i].mesh, referenceCount + 1);
1929 }
1930 else
1931 m_textInfo.meshInfo[i].ResizeMeshInfo(referenceCount > 1024 ? referenceCount + 256 : Mathf.NextPowerOfTwo(referenceCount + 1));
1932 }
1933 else if (m_VertexBufferAutoSizeReduction && referenceCount > 0 && m_textInfo.meshInfo[i].vertices.Length / 4 - referenceCount > 256)
1934 {
1935 // Resize vertex buffers if allocations are excessive.
1936 //Debug.Log("Reducing the size of the vertex buffers.");
1937 m_textInfo.meshInfo[i].ResizeMeshInfo(referenceCount > 1024 ? referenceCount + 256 : Mathf.NextPowerOfTwo(referenceCount + 1));
1938 }
1939
1940 // Assign material reference
1941 m_textInfo.meshInfo[i].material = m_materialReferences[i].material;
1942 }
1943
1944 //TMP_MaterialManager.CleanupFallbackMaterials();
1945
1946 // Clean up unused SubMeshes
1947 for (int i = materialCount; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
1948 {
1949 if (i < m_textInfo.meshInfo.Length)
1950 m_textInfo.meshInfo[i].ClearUnusedVertices(0, true);
1951
1952 //m_subTextObjects[i].gameObject.SetActive(false);
1953 }
1954
1955 k_SetArraySizesMarker.End();
1956 return m_totalCharacterCount;
1957 }
1958
1959
1960 // Added to sort handle the potential issue with OnWillRenderObject() not getting called when objects are not visible by camera.
1961 //void OnBecameInvisible()
1962 //{
1963 // if (m_mesh != null)
1964 // m_mesh.bounds = new Bounds(transform.position, new Vector3(1000, 1000, 0));
1965 //}
1966
1967
1968 /// <summary>
1969 /// Update the margin width and height
1970 /// </summary>
1971 public override void ComputeMarginSize()
1972 {
1973 if (this.rectTransform != null)
1974 {
1975 //Debug.Log("*** ComputeMarginSize() *** Current RectTransform's Width is " + m_rectTransform.rect.width + " and Height is " + m_rectTransform.rect.height); // + " and size delta is " + m_rectTransform.sizeDelta);
1976 Rect rect = m_rectTransform.rect;
1977
1978 m_marginWidth = rect.width - m_margin.x - m_margin.z;
1979 m_marginHeight = rect.height - m_margin.y - m_margin.w;
1980
1981 // Cache current RectTransform width and pivot referenced in OnRectTransformDimensionsChange() to get around potential rounding error in the reported width of the RectTransform.
1982 m_PreviousRectTransformSize = rect.size;
1983 m_PreviousPivotPosition = m_rectTransform.pivot;
1984
1985 // Update the corners of the RectTransform
1986 m_RectTransformCorners = GetTextContainerLocalCorners();
1987 }
1988 }
1989
1990
1991 /// <summary>
1992 ///
1993 /// </summary>
1994 protected override void OnDidApplyAnimationProperties()
1995 {
1996 m_havePropertiesChanged = true;
1997 isMaskUpdateRequired = true;
1998
1999 SetVerticesDirty();
2000 }
2001
2002
2003 protected override void OnTransformParentChanged()
2004 {
2005 //Debug.Log("*** OnTransformParentChanged() ***");
2006 //ComputeMarginSize();
2007
2008 SetVerticesDirty();
2009 SetLayoutDirty();
2010 }
2011
2012
2013 protected override void OnRectTransformDimensionsChange()
2014 {
2015 //Debug.Log("*** OnRectTransformDimensionsChange() ***");
2016
2017 // Ignore changes to RectTransform SizeDelta that are very small and typically the result of rounding errors when using RectTransform in Anchor Stretch mode.
2018 if (rectTransform != null &&
2019 Mathf.Abs(m_rectTransform.rect.width - m_PreviousRectTransformSize.x) < 0.0001f && Mathf.Abs(m_rectTransform.rect.height - m_PreviousRectTransformSize.y) < 0.0001f &&
2020 Mathf.Abs(m_rectTransform.pivot.x - m_PreviousPivotPosition.x) < 0.0001f && Mathf.Abs(m_rectTransform.pivot.y - m_PreviousPivotPosition.y) < 0.0001f)
2021 {
2022 return;
2023 }
2024
2025 ComputeMarginSize();
2026
2027 SetVerticesDirty();
2028 SetLayoutDirty();
2029 }
2030
2031
2032 /// <summary>
2033 /// Function used as a replacement for LateUpdate to check if the transform or scale of the text object has changed.
2034 /// </summary>
2035 internal override void InternalUpdate()
2036 {
2037 // We need to update the SDF scale or possibly regenerate the text object if lossy scale has changed.
2038 if (m_havePropertiesChanged == false)
2039 {
2040 float lossyScaleY = m_rectTransform.lossyScale.y;
2041
2042 if (lossyScaleY != m_previousLossyScaleY && m_TextProcessingArray[0].unicode != 0)
2043 {
2044 float scaleDelta = lossyScaleY / m_previousLossyScaleY;
2045
2046 // Only update SDF Scale when lossy scale has changed by more than 20%
2047 if (scaleDelta < 0.8f || scaleDelta > 1.25f)
2048 {
2049 UpdateSDFScale(scaleDelta);
2050 m_previousLossyScaleY = lossyScaleY;
2051 }
2052 }
2053 }
2054
2055 // Added to handle legacy animation mode.
2056 if (m_isUsingLegacyAnimationComponent)
2057 {
2058 m_havePropertiesChanged = true;
2059 OnPreRenderObject();
2060 }
2061
2062 // Update Environment Matrix property to support changing the rotation via a script.
2063 UpdateEnvMapMatrix();
2064 }
2065
2066
2067 /// <summary>
2068 /// Function called when the text needs to be updated.
2069 /// </summary>
2070 void OnPreRenderObject()
2071 {
2072 //Debug.Log("*** OnPreRenderObject() called on object [" + this.name + "] ***");
2073
2074 // Make sure object is active.
2075 if (!m_isAwake || (this.IsActive() == false && m_ignoreActiveState == false))
2076 return;
2077
2078 // Check if we have a font asset assigned. Return if we don't because no one likes to see purple squares on screen.
2079 if (m_fontAsset == null)
2080 {
2081 Debug.LogWarning("Please assign a Font Asset to this " + transform.name + " gameobject.", this);
2082 return;
2083 }
2084
2085 if (m_havePropertiesChanged || m_isLayoutDirty)
2086 {
2087 //Debug.Log("Properties have changed!"); // Assigned Material is:" + m_sharedMaterial); // New Text is: " + m_text + ".");
2088
2089 if (isMaskUpdateRequired)
2090 {
2091 UpdateMask();
2092 isMaskUpdateRequired = false;
2093 }
2094
2095 // Update mesh padding if necessary.
2096 if (checkPaddingRequired)
2097 UpdateMeshPadding();
2098
2099 // Reparse the text as input may have changed or been truncated.
2100 ParseInputText();
2101 TMP_FontAsset.UpdateFontAssetsInUpdateQueue();
2102
2103 // Reset Font min / max used with Auto-sizing
2104 if (m_enableAutoSizing)
2105 m_fontSize = Mathf.Clamp(m_fontSizeBase, m_fontSizeMin, m_fontSizeMax);
2106
2107 m_maxFontSize = m_fontSizeMax;
2108 m_minFontSize = m_fontSizeMin;
2109 m_lineSpacingDelta = 0;
2110 m_charWidthAdjDelta = 0;
2111
2112 m_isTextTruncated = false;
2113
2114 m_havePropertiesChanged = false;
2115 m_isLayoutDirty = false;
2116 m_ignoreActiveState = false;
2117
2118 // Reset Text Auto Size iteration tracking.
2119 m_IsAutoSizePointSizeSet = false;
2120 m_AutoSizeIterationCount = 0;
2121
2122 // Make sure state of MeshRenderer is mirrored on potential sub text objects.
2123 SetActiveSubTextObjectRenderers(m_renderer.enabled);
2124
2125 // The GenerateTextMesh function is potentially called repeatedly when text auto size is enabled.
2126 // This is a revised implementation to remove the use of recursion which could potentially result in stack overflow issues.
2127 while (m_IsAutoSizePointSizeSet == false)
2128 {
2129 GenerateTextMesh();
2130 m_AutoSizeIterationCount += 1;
2131 }
2132 }
2133 }
2134
2135
2136 /// <summary>
2137 /// This is the main function that is responsible for creating / displaying the text.
2138 /// </summary>
2139 protected virtual void GenerateTextMesh()
2140 {
2141 k_GenerateTextMarker.Begin();
2142
2143 // Early exit if no font asset was assigned. This should not be needed since LiberationSans SDF will be assigned by default.
2144 if (m_fontAsset == null || m_fontAsset.characterLookupTable == null)
2145 {
2146 Debug.LogWarning("Can't Generate Mesh! No Font Asset has been assigned to Object ID: " + this.GetInstanceID());
2147 m_IsAutoSizePointSizeSet = true;
2148 k_GenerateTextMarker.End();
2149 return;
2150 }
2151
2152 // Clear TextInfo
2153 if (m_textInfo != null)
2154 m_textInfo.Clear();
2155
2156 // Early exit if we don't have any Text to generate.
2157 if (m_TextProcessingArray == null || m_TextProcessingArray.Length == 0 || m_TextProcessingArray[0].unicode == 0)
2158 {
2159 // Clear mesh and upload changes to the mesh.
2160 ClearMesh(true);
2161
2162 m_preferredWidth = 0;
2163 m_preferredHeight = 0;
2164
2165 // Event indicating the text has been regenerated.
2166 TMPro_EventManager.ON_TEXT_CHANGED(this);
2167 m_IsAutoSizePointSizeSet = true;
2168 k_GenerateTextMarker.End();
2169 return;
2170 }
2171
2172 m_currentFontAsset = m_fontAsset;
2173 m_currentMaterial = m_sharedMaterial;
2174 m_currentMaterialIndex = 0;
2175 m_materialReferenceStack.SetDefault(new MaterialReference(m_currentMaterialIndex, m_currentFontAsset, null, m_currentMaterial, m_padding));
2176
2177 m_currentSpriteAsset = m_spriteAsset;
2178
2179 // Stop all Sprite Animations
2180 if (m_spriteAnimator != null)
2181 m_spriteAnimator.StopAllAnimations();
2182
2183 // Total character count is computed when the text is parsed.
2184 int totalCharacterCount = m_totalCharacterCount;
2185
2186 // Calculate the scale of the font based on selected font size and sampling point size.
2187 // baseScale is calculated using the font asset assigned to the text object.
2188 float baseScale = (m_fontSize / m_fontAsset.m_FaceInfo.pointSize * m_fontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f));
2189 float currentElementScale = baseScale;
2190 float currentEmScale = m_fontSize * 0.01f * (m_isOrthographic ? 1 : 0.1f);
2191 m_fontScaleMultiplier = 1;
2192
2193 m_currentFontSize = m_fontSize;
2194 m_sizeStack.SetDefault(m_currentFontSize);
2195 float fontSizeDelta = 0;
2196
2197 uint charCode = 0; // Holds the character code of the currently being processed character.
2198
2199 m_FontStyleInternal = m_fontStyle; // Set the default style.
2200 m_FontWeightInternal = (m_FontStyleInternal & FontStyles.Bold) == FontStyles.Bold ? FontWeight.Bold : m_fontWeight;
2201 m_FontWeightStack.SetDefault(m_FontWeightInternal);
2202 m_fontStyleStack.Clear();
2203
2204 m_lineJustification = m_HorizontalAlignment; // m_textAlignment; // Sets the line justification mode to match editor alignment.
2205 m_lineJustificationStack.SetDefault(m_lineJustification);
2206
2207 float padding = 0;
2208
2209 m_baselineOffset = 0; // Used by subscript characters.
2210 m_baselineOffsetStack.Clear();
2211
2212 // Underline
2213 bool beginUnderline = false;
2214 Vector3 underline_start = Vector3.zero; // Used to track where underline starts & ends.
2215 Vector3 underline_end = Vector3.zero;
2216
2217 // Strike-through
2218 bool beginStrikethrough = false;
2219 Vector3 strikethrough_start = Vector3.zero;
2220 Vector3 strikethrough_end = Vector3.zero;
2221
2222 // Text Highlight
2223 bool beginHighlight = false;
2224 Vector3 highlight_start = Vector3.zero;
2225 Vector3 highlight_end = Vector3.zero;
2226
2227 m_fontColor32 = m_fontColor;
2228 m_htmlColor = m_fontColor32;
2229 m_underlineColor = m_htmlColor;
2230 m_strikethroughColor = m_htmlColor;
2231
2232 m_colorStack.SetDefault(m_htmlColor);
2233 m_underlineColorStack.SetDefault(m_htmlColor);
2234 m_strikethroughColorStack.SetDefault(m_htmlColor);
2235 m_HighlightStateStack.SetDefault(new HighlightState(m_htmlColor, TMP_Offset.zero));
2236
2237 m_colorGradientPreset = null;
2238 m_colorGradientStack.SetDefault(null);
2239
2240 m_ItalicAngle = m_currentFontAsset.italicStyle;
2241 m_ItalicAngleStack.SetDefault(m_ItalicAngle);
2242
2243 // Clear the Style stack.
2244 //m_styleStack.Clear();
2245
2246 // Clear the Action stack.
2247 m_actionStack.Clear();
2248
2249 m_FXScale = Vector3.one;
2250 m_FXRotation = Quaternion.identity;
2251
2252 m_lineOffset = 0; // Amount of space between lines (font line spacing + m_linespacing).
2253 m_lineHeight = TMP_Math.FLOAT_UNSET;
2254 float lineGap = m_currentFontAsset.m_FaceInfo.lineHeight - (m_currentFontAsset.m_FaceInfo.ascentLine - m_currentFontAsset.m_FaceInfo.descentLine);
2255
2256 m_cSpacing = 0; // Amount of space added between characters as a result of the use of the <cspace> tag.
2257 m_monoSpacing = 0;
2258 m_xAdvance = 0; // Used to track the position of each character.
2259
2260 tag_LineIndent = 0; // Used for indentation of text.
2261 tag_Indent = 0;
2262 m_indentStack.SetDefault(0);
2263 tag_NoParsing = false;
2264 //m_isIgnoringAlignment = false;
2265
2266 m_characterCount = 0; // Total characters in the char[]
2267
2268 // Tracking of line information
2269 m_firstCharacterOfLine = m_firstVisibleCharacter;
2270 m_lastCharacterOfLine = 0;
2271 m_firstVisibleCharacterOfLine = 0;
2272 m_lastVisibleCharacterOfLine = 0;
2273 m_maxLineAscender = k_LargeNegativeFloat;
2274 m_maxLineDescender = k_LargePositiveFloat;
2275 m_lineNumber = 0;
2276 m_startOfLineAscender = 0;
2277 m_startOfLineDescender = 0;
2278 m_lineVisibleCharacterCount = 0;
2279 m_lineVisibleSpaceCount = 0;
2280 bool isStartOfNewLine = true;
2281 m_IsDrivenLineSpacing = false;
2282 m_firstOverflowCharacterIndex = -1;
2283 m_LastBaseGlyphIndex = int.MinValue;
2284
2285 bool kerning = m_ActiveFontFeatures.Contains(OTL_FeatureTag.kern);
2286 bool markToBase = m_ActiveFontFeatures.Contains(OTL_FeatureTag.mark);
2287 bool markToMark = m_ActiveFontFeatures.Contains(OTL_FeatureTag.mkmk);
2288
2289 m_pageNumber = 0;
2290 int pageToDisplay = Mathf.Clamp(m_pageToDisplay - 1, 0, m_textInfo.pageInfo.Length - 1);
2291 m_textInfo.ClearPageInfo();
2292
2293 Vector4 margins = m_margin;
2294 float marginWidth = m_marginWidth > 0 ? m_marginWidth : 0;
2295 float marginHeight = m_marginHeight > 0 ? m_marginHeight : 0;
2296 m_marginLeft = 0;
2297 m_marginRight = 0;
2298 m_width = -1;
2299 float widthOfTextArea = marginWidth + 0.0001f - m_marginLeft - m_marginRight;
2300
2301 // Need to initialize these Extents structures
2302 m_meshExtents.min = k_LargePositiveVector2;
2303 m_meshExtents.max = k_LargeNegativeVector2;
2304
2305 // Initialize lineInfo
2306 m_textInfo.ClearLineInfo();
2307
2308 // Tracking of the highest Ascender
2309 m_maxCapHeight = 0;
2310 m_maxTextAscender = 0;
2311 m_ElementDescender = 0;
2312 m_PageAscender = 0;
2313 float maxVisibleDescender = 0;
2314 bool isMaxVisibleDescenderSet = false;
2315 m_isNewPage = false;
2316
2317 // Initialize struct to track states of word wrapping
2318 bool isFirstWordOfLine = true;
2319 m_isNonBreakingSpace = false;
2320 bool ignoreNonBreakingSpace = false;
2321 int lastSoftLineBreak = 0;
2322
2323 CharacterSubstitution characterToSubstitute = new CharacterSubstitution(-1, 0);
2324 bool isSoftHyphenIgnored = false;
2325
2326 // Save character and line state before we begin layout.
2327 SaveWordWrappingState(ref m_SavedWordWrapState, -1, -1);
2328 SaveWordWrappingState(ref m_SavedLineState, -1, -1);
2329 SaveWordWrappingState(ref m_SavedEllipsisState, -1, -1);
2330 SaveWordWrappingState(ref m_SavedLastValidState, -1, -1);
2331 SaveWordWrappingState(ref m_SavedSoftLineBreakState, -1, -1);
2332
2333 m_EllipsisInsertionCandidateStack.Clear();
2334
2335 // Safety Tracker
2336 int restoreCount = 0;
2337
2338 k_GenerateTextPhaseIMarker.Begin();
2339
2340 // Parse through Character buffer to read HTML tags and begin creating mesh.
2341 for (int i = 0; i < m_TextProcessingArray.Length && m_TextProcessingArray[i].unicode != 0; i++)
2342 {
2343 charCode = m_TextProcessingArray[i].unicode;
2344
2345 if (restoreCount > 5)
2346 {
2347 Debug.LogError("Line breaking recursion max threshold hit... Character [" + charCode + "] index: " + i);
2348 characterToSubstitute.index = m_characterCount;
2349 characterToSubstitute.unicode = 0x03;
2350 }
2351
2352 // Skip characters that have been substituted.
2353 if (charCode == 0x1A)
2354 continue;
2355
2356 // Parse Rich Text Tag
2357 #region Parse Rich Text Tag
2358 if (m_isRichText && charCode == '<')
2359 {
2360 k_ParseMarkupTextMarker.Begin();
2361
2362 m_isTextLayoutPhase = true;
2363 m_textElementType = TMP_TextElementType.Character;
2364 int endTagIndex;
2365
2366 // Check if Tag is valid. If valid, skip to the end of the validated tag.
2367 if (ValidateHtmlTag(m_TextProcessingArray, i + 1, out endTagIndex))
2368 {
2369 i = endTagIndex;
2370
2371 // Continue to next character or handle the sprite element
2372 if (m_textElementType == TMP_TextElementType.Character)
2373 {
2374 k_ParseMarkupTextMarker.End();
2375 continue;
2376 }
2377 }
2378 k_ParseMarkupTextMarker.End();
2379 }
2380 else
2381 {
2382 m_textElementType = m_textInfo.characterInfo[m_characterCount].elementType;
2383 m_currentMaterialIndex = m_textInfo.characterInfo[m_characterCount].materialReferenceIndex;
2384 m_currentFontAsset = m_textInfo.characterInfo[m_characterCount].fontAsset;
2385 }
2386 #endregion End Parse Rich Text Tag
2387
2388 int previousMaterialIndex = m_currentMaterialIndex;
2389 bool isUsingAltTypeface = m_textInfo.characterInfo[m_characterCount].isUsingAlternateTypeface;
2390
2391 m_isTextLayoutPhase = false;
2392
2393 // Handle potential character substitutions
2394 #region Character Substitutions
2395 bool isInjectedCharacter = false;
2396
2397 if (characterToSubstitute.index == m_characterCount)
2398 {
2399 charCode = characterToSubstitute.unicode;
2400 m_textElementType = TMP_TextElementType.Character;
2401 isInjectedCharacter = true;
2402
2403 switch (charCode)
2404 {
2405 case 0x03:
2406 m_textInfo.characterInfo[m_characterCount].textElement = m_currentFontAsset.characterLookupTable[0x03];
2407 m_isTextTruncated = true;
2408 break;
2409 case 0x2D:
2410 //
2411 break;
2412 case 0x2026:
2413 m_textInfo.characterInfo[m_characterCount].textElement = m_Ellipsis.character;
2414 m_textInfo.characterInfo[m_characterCount].elementType = TMP_TextElementType.Character;
2415 m_textInfo.characterInfo[m_characterCount].fontAsset = m_Ellipsis.fontAsset;
2416 m_textInfo.characterInfo[m_characterCount].material = m_Ellipsis.material;
2417 m_textInfo.characterInfo[m_characterCount].materialReferenceIndex = m_Ellipsis.materialIndex;
2418
2419 // Need to increase reference count in the event the primary mesh has no characters.
2420 m_materialReferences[m_Underline.materialIndex].referenceCount += 1;
2421
2422 // Indicates the source parsing data has been modified.
2423 m_isTextTruncated = true;
2424
2425 // End Of Text
2426 characterToSubstitute.index = m_characterCount + 1;
2427 characterToSubstitute.unicode = 0x03;
2428 break;
2429 }
2430 }
2431 #endregion
2432
2433
2434 // When using Linked text, mark character as ignored and skip to next character.
2435 #region Linked Text
2436 if (m_characterCount < m_firstVisibleCharacter && charCode != 0x03)
2437 {
2438 m_textInfo.characterInfo[m_characterCount].isVisible = false;
2439 m_textInfo.characterInfo[m_characterCount].character = (char)0x200B;
2440 m_textInfo.characterInfo[m_characterCount].lineNumber = 0;
2441 m_characterCount += 1;
2442 continue;
2443 }
2444 #endregion
2445
2446
2447 // Handle Font Styles like LowerCase, UpperCase and SmallCaps.
2448 #region Handling of LowerCase, UpperCase and SmallCaps Font Styles
2449
2450 float smallCapsMultiplier = 1.0f;
2451
2452 if (m_textElementType == TMP_TextElementType.Character)
2453 {
2454 if ((m_FontStyleInternal & FontStyles.UpperCase) == FontStyles.UpperCase)
2455 {
2456 // If this character is lowercase, switch to uppercase.
2457 if (char.IsLower((char)charCode))
2458 charCode = char.ToUpper((char)charCode);
2459
2460 }
2461 else if ((m_FontStyleInternal & FontStyles.LowerCase) == FontStyles.LowerCase)
2462 {
2463 // If this character is uppercase, switch to lowercase.
2464 if (char.IsUpper((char)charCode))
2465 charCode = char.ToLower((char)charCode);
2466 }
2467 else if ((m_FontStyleInternal & FontStyles.SmallCaps) == FontStyles.SmallCaps)
2468 {
2469 if (char.IsLower((char)charCode))
2470 {
2471 smallCapsMultiplier = 0.8f;
2472 charCode = char.ToUpper((char)charCode);
2473 }
2474 }
2475 }
2476 #endregion
2477
2478
2479 // Look up Character Data from Dictionary and cache it.
2480 #region Look up Character Data
2481 k_CharacterLookupMarker.Begin();
2482
2483 float baselineOffset = 0;
2484 float elementAscentLine = 0;
2485 float elementDescentLine = 0;
2486 if (m_textElementType == TMP_TextElementType.Sprite)
2487 {
2488 // If a sprite is used as a fallback then get a reference to it and set the color to white.
2489 TMP_SpriteCharacter sprite = (TMP_SpriteCharacter)textInfo.characterInfo[m_characterCount].textElement;
2490 m_currentSpriteAsset = sprite.textAsset as TMP_SpriteAsset;
2491 m_spriteIndex = (int)sprite.glyphIndex;
2492
2493 if (sprite == null)
2494 {
2495 k_CharacterLookupMarker.End();
2496 continue;
2497 }
2498
2499 // Sprites are assigned in the E000 Private Area + sprite Index
2500 if (charCode == '<')
2501 charCode = 57344 + (uint)m_spriteIndex;
2502 else
2503 m_spriteColor = s_colorWhite;
2504
2505 float fontScale = (m_currentFontSize / m_currentFontAsset.faceInfo.pointSize * m_currentFontAsset.faceInfo.scale * (m_isOrthographic ? 1 : 0.1f));
2506
2507 // The sprite scale calculations are based on the font asset assigned to the text object.
2508 if (m_currentSpriteAsset.m_FaceInfo.pointSize > 0)
2509 {
2510 float spriteScale = m_currentFontSize / m_currentSpriteAsset.m_FaceInfo.pointSize * m_currentSpriteAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f);
2511 currentElementScale = sprite.m_Scale * sprite.m_Glyph.scale * spriteScale;
2512 elementAscentLine = m_currentSpriteAsset.m_FaceInfo.ascentLine;
2513 baselineOffset = m_currentSpriteAsset.m_FaceInfo.baseline * fontScale * m_fontScaleMultiplier * m_currentSpriteAsset.m_FaceInfo.scale;
2514 elementDescentLine = m_currentSpriteAsset.m_FaceInfo.descentLine;
2515 }
2516 else
2517 {
2518 float spriteScale = m_currentFontSize / m_currentFontAsset.m_FaceInfo.pointSize * m_currentFontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f);
2519 currentElementScale = m_currentFontAsset.m_FaceInfo.ascentLine / sprite.m_Glyph.metrics.height * sprite.m_Scale * sprite.m_Glyph.scale * spriteScale;
2520 float scaleDelta = spriteScale / currentElementScale;
2521 elementAscentLine = m_currentFontAsset.m_FaceInfo.ascentLine * scaleDelta;
2522 baselineOffset = m_currentFontAsset.m_FaceInfo.baseline * fontScale * m_fontScaleMultiplier * m_currentFontAsset.m_FaceInfo.scale;
2523 elementDescentLine = m_currentFontAsset.m_FaceInfo.descentLine * scaleDelta;
2524 }
2525
2526 m_cached_TextElement = sprite;
2527
2528 m_textInfo.characterInfo[m_characterCount].elementType = TMP_TextElementType.Sprite;
2529 m_textInfo.characterInfo[m_characterCount].scale = currentElementScale;
2530 m_textInfo.characterInfo[m_characterCount].fontAsset = m_currentFontAsset;
2531 m_textInfo.characterInfo[m_characterCount].materialReferenceIndex = m_currentMaterialIndex;
2532
2533 m_currentMaterialIndex = previousMaterialIndex;
2534
2535 padding = 0;
2536 }
2537 else if (m_textElementType == TMP_TextElementType.Character)
2538 {
2539 m_cached_TextElement = m_textInfo.characterInfo[m_characterCount].textElement;
2540 if (m_cached_TextElement == null)
2541 {
2542 k_CharacterLookupMarker.End();
2543 continue;
2544 }
2545
2546 m_currentFontAsset = m_textInfo.characterInfo[m_characterCount].fontAsset;
2547 m_currentMaterial = m_textInfo.characterInfo[m_characterCount].material;
2548 m_currentMaterialIndex = m_textInfo.characterInfo[m_characterCount].materialReferenceIndex;
2549
2550 // Special handling if replaced character was a line feed where in this case we have to use the scale of the previous character.
2551 float adjustedScale;
2552 if (isInjectedCharacter && m_TextProcessingArray[i].unicode == 0x0A && m_characterCount != m_firstCharacterOfLine)
2553 adjustedScale = m_textInfo.characterInfo[m_characterCount - 1].pointSize * smallCapsMultiplier / m_currentFontAsset.m_FaceInfo.pointSize * m_currentFontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f);
2554 else
2555 adjustedScale = m_currentFontSize * smallCapsMultiplier / m_currentFontAsset.m_FaceInfo.pointSize * m_currentFontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f);
2556
2557 // Special handling for injected Ellipsis
2558 if (isInjectedCharacter && charCode == 0x2026)
2559 {
2560 elementAscentLine = 0;
2561 elementDescentLine = 0;
2562 }
2563 else
2564 {
2565 elementAscentLine = m_currentFontAsset.m_FaceInfo.ascentLine;
2566 elementDescentLine = m_currentFontAsset.m_FaceInfo.descentLine;
2567 }
2568
2569 currentElementScale = adjustedScale * m_fontScaleMultiplier * m_cached_TextElement.m_Scale * m_cached_TextElement.m_Glyph.scale;
2570 baselineOffset = m_currentFontAsset.m_FaceInfo.baseline * adjustedScale * m_fontScaleMultiplier * m_currentFontAsset.m_FaceInfo.scale;
2571
2572 m_textInfo.characterInfo[m_characterCount].elementType = TMP_TextElementType.Character;
2573 m_textInfo.characterInfo[m_characterCount].scale = currentElementScale;
2574
2575 padding = m_currentMaterialIndex == 0 ? m_padding : m_subTextObjects[m_currentMaterialIndex].padding;
2576 }
2577 k_CharacterLookupMarker.End();
2578 #endregion
2579
2580
2581 // Handle Soft Hyphen
2582 #region Handle Soft Hyphen
2583 float currentElementUnmodifiedScale = currentElementScale;
2584 if (charCode == 0xAD || charCode == 0x03)
2585 currentElementScale = 0;
2586 #endregion
2587
2588
2589 // Store some of the text object's information
2590 m_textInfo.characterInfo[m_characterCount].character = (char)charCode;
2591 m_textInfo.characterInfo[m_characterCount].pointSize = m_currentFontSize;
2592 m_textInfo.characterInfo[m_characterCount].color = m_htmlColor;
2593 m_textInfo.characterInfo[m_characterCount].underlineColor = m_underlineColor;
2594 m_textInfo.characterInfo[m_characterCount].strikethroughColor = m_strikethroughColor;
2595 m_textInfo.characterInfo[m_characterCount].highlightState = m_HighlightState;
2596 m_textInfo.characterInfo[m_characterCount].style = m_FontStyleInternal;
2597
2598 // Cache glyph metrics
2599 Glyph altGlyph = m_textInfo.characterInfo[m_characterCount].alternativeGlyph;
2600 GlyphMetrics currentGlyphMetrics = altGlyph == null ? m_cached_TextElement.m_Glyph.metrics : altGlyph.metrics;
2601
2602 // Optimization to avoid calling this more than once per character.
2603 bool isWhiteSpace = charCode <= 0xFFFF && char.IsWhiteSpace((char)charCode);
2604
2605 // Handle Kerning if Enabled.
2606 #region Handle Kerning
2607 GlyphValueRecord glyphAdjustments = new GlyphValueRecord();
2608 float characterSpacingAdjustment = m_characterSpacing;
2609 if (kerning && m_textElementType == TMP_TextElementType.Character)
2610 {
2611 k_HandleGPOSFeaturesMarker.Begin();
2612
2613 GlyphPairAdjustmentRecord adjustmentPair;
2614 uint baseGlyphIndex = m_cached_TextElement.m_GlyphIndex;
2615
2616 if (m_characterCount < totalCharacterCount - 1 && m_textInfo.characterInfo[m_characterCount + 1].elementType == TMP_TextElementType.Character)
2617 {
2618 uint nextGlyphIndex = m_textInfo.characterInfo[m_characterCount + 1].textElement.m_GlyphIndex;
2619 uint key = nextGlyphIndex << 16 | baseGlyphIndex;
2620
2621 if (m_currentFontAsset.m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookup.TryGetValue(key, out adjustmentPair))
2622 {
2623 glyphAdjustments = adjustmentPair.firstAdjustmentRecord.glyphValueRecord;
2624 characterSpacingAdjustment = (adjustmentPair.featureLookupFlags & UnityEngine.TextCore.LowLevel.FontFeatureLookupFlags.IgnoreSpacingAdjustments) == UnityEngine.TextCore.LowLevel.FontFeatureLookupFlags.IgnoreSpacingAdjustments ? 0 : characterSpacingAdjustment;
2625 }
2626 }
2627
2628 if (m_characterCount >= 1)
2629 {
2630 uint previousGlyphIndex = m_textInfo.characterInfo[m_characterCount - 1].textElement.m_GlyphIndex;
2631 uint key = baseGlyphIndex << 16 | previousGlyphIndex;
2632
2633 if (textInfo.characterInfo[m_characterCount - 1].elementType == TMP_TextElementType.Character && m_currentFontAsset.m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookup.TryGetValue(key, out adjustmentPair))
2634 {
2635 glyphAdjustments += adjustmentPair.secondAdjustmentRecord.glyphValueRecord;
2636 characterSpacingAdjustment = (adjustmentPair.featureLookupFlags & UnityEngine.TextCore.LowLevel.FontFeatureLookupFlags.IgnoreSpacingAdjustments) == UnityEngine.TextCore.LowLevel.FontFeatureLookupFlags.IgnoreSpacingAdjustments ? 0 : characterSpacingAdjustment;
2637 }
2638 }
2639
2640 k_HandleGPOSFeaturesMarker.End();
2641 }
2642
2643 m_textInfo.characterInfo[m_characterCount].adjustedHorizontalAdvance = glyphAdjustments.xAdvance;
2644 #endregion
2645
2646
2647 // Handle Diacritical Marks
2648 #region Handle Diacritical Marks
2649 bool isBaseGlyph = TMP_TextParsingUtilities.IsBaseGlyph(charCode);
2650
2651 if (isBaseGlyph)
2652 m_LastBaseGlyphIndex = m_characterCount;
2653
2654 if (m_characterCount > 0 && !isBaseGlyph)
2655 {
2656 // Check for potential Mark-to-Base lookup if previous glyph was a base glyph
2657 if (markToBase && m_LastBaseGlyphIndex != int.MinValue && m_LastBaseGlyphIndex == m_characterCount - 1)
2658 {
2659 Glyph baseGlyph = m_textInfo.characterInfo[m_LastBaseGlyphIndex].textElement.glyph;
2660 uint baseGlyphIndex = baseGlyph.index;
2661 uint markGlyphIndex = m_cached_TextElement.glyphIndex;
2662 uint key = markGlyphIndex << 16 | baseGlyphIndex;
2663
2664 if (m_currentFontAsset.fontFeatureTable.m_MarkToBaseAdjustmentRecordLookup.TryGetValue(key, out MarkToBaseAdjustmentRecord glyphAdjustmentRecord))
2665 {
2666 float advanceOffset = (m_textInfo.characterInfo[m_LastBaseGlyphIndex].origin - m_xAdvance) / currentElementScale;
2667
2668 glyphAdjustments.xPlacement = advanceOffset + glyphAdjustmentRecord.baseGlyphAnchorPoint.xCoordinate - glyphAdjustmentRecord.markPositionAdjustment.xPositionAdjustment;
2669 glyphAdjustments.yPlacement = glyphAdjustmentRecord.baseGlyphAnchorPoint.yCoordinate - glyphAdjustmentRecord.markPositionAdjustment.yPositionAdjustment;
2670
2671 characterSpacingAdjustment = 0;
2672 }
2673 }
2674 else
2675 {
2676 // Iterate from previous glyph to last base glyph checking for any potential Mark-to-Mark lookups to apply. Otherwise check for potential Mark-to-Base lookup between the current glyph and last base glyph
2677 bool wasLookupApplied = false;
2678
2679 // Check for any potential Mark-to-Mark lookups
2680 if (markToMark)
2681 {
2682 for (int characterLookupIndex = m_characterCount - 1; characterLookupIndex >= 0 && characterLookupIndex != m_LastBaseGlyphIndex; characterLookupIndex--)
2683 {
2684 // Handle any potential Mark-to-Mark lookup
2685 Glyph baseMarkGlyph = m_textInfo.characterInfo[characterLookupIndex].textElement.glyph;
2686 uint baseGlyphIndex = baseMarkGlyph.index;
2687 uint combiningMarkGlyphIndex = m_cached_TextElement.glyphIndex;
2688 uint key = combiningMarkGlyphIndex << 16 | baseGlyphIndex;
2689
2690 if (m_currentFontAsset.fontFeatureTable.m_MarkToMarkAdjustmentRecordLookup.TryGetValue(key, out MarkToMarkAdjustmentRecord glyphAdjustmentRecord))
2691 {
2692 float baseMarkOrigin = (m_textInfo.characterInfo[characterLookupIndex].origin - m_xAdvance) / currentElementScale;
2693 float currentBaseline = baselineOffset - m_lineOffset + m_baselineOffset;
2694 float baseMarkBaseline = (m_textInfo.characterInfo[characterLookupIndex].baseLine - currentBaseline) / currentElementScale;
2695
2696 glyphAdjustments.xPlacement = baseMarkOrigin + glyphAdjustmentRecord.baseMarkGlyphAnchorPoint.xCoordinate - glyphAdjustmentRecord.combiningMarkPositionAdjustment.xPositionAdjustment;
2697 glyphAdjustments.yPlacement = baseMarkBaseline + glyphAdjustmentRecord.baseMarkGlyphAnchorPoint.yCoordinate - glyphAdjustmentRecord.combiningMarkPositionAdjustment.yPositionAdjustment;
2698
2699 characterSpacingAdjustment = 0;
2700 wasLookupApplied = true;
2701 break;
2702 }
2703 }
2704 }
2705
2706 // If no Mark-to-Mark lookups were applied, check for potential Mark-to-Base lookup.
2707 if (markToBase && m_LastBaseGlyphIndex != int.MinValue && !wasLookupApplied)
2708 {
2709 // Handle lookup for Mark-to-Base
2710 Glyph baseGlyph = m_textInfo.characterInfo[m_LastBaseGlyphIndex].textElement.glyph;
2711 uint baseGlyphIndex = baseGlyph.index;
2712 uint markGlyphIndex = m_cached_TextElement.glyphIndex;
2713 uint key = markGlyphIndex << 16 | baseGlyphIndex;
2714
2715 if (m_currentFontAsset.fontFeatureTable.m_MarkToBaseAdjustmentRecordLookup.TryGetValue(key, out MarkToBaseAdjustmentRecord glyphAdjustmentRecord))
2716 {
2717 float advanceOffset = (m_textInfo.characterInfo[m_LastBaseGlyphIndex].origin - m_xAdvance) / currentElementScale;
2718
2719 glyphAdjustments.xPlacement = advanceOffset + glyphAdjustmentRecord.baseGlyphAnchorPoint.xCoordinate - glyphAdjustmentRecord.markPositionAdjustment.xPositionAdjustment;
2720 glyphAdjustments.yPlacement = glyphAdjustmentRecord.baseGlyphAnchorPoint.yCoordinate - glyphAdjustmentRecord.markPositionAdjustment.yPositionAdjustment;
2721
2722 characterSpacingAdjustment = 0;
2723 }
2724 }
2725 }
2726 }
2727
2728 // Adjust relevant text metrics
2729 elementAscentLine += glyphAdjustments.yPlacement;
2730 elementDescentLine += glyphAdjustments.yPlacement;
2731 #endregion
2732
2733
2734 // Initial Implementation for RTL support.
2735 #region Handle Right-to-Left
2736 if (m_isRightToLeft)
2737 {
2738 m_xAdvance -= currentGlyphMetrics.horizontalAdvance * (1 - m_charWidthAdjDelta) * currentElementScale;
2739
2740 if (isWhiteSpace || charCode == 0x200B)
2741 m_xAdvance -= m_wordSpacing * currentEmScale;
2742 }
2743 #endregion
2744
2745
2746 // Handle Mono Spacing
2747 #region Handle Mono Spacing
2748 float monoAdvance = 0;
2749 if (m_monoSpacing != 0)
2750 {
2751 if (m_duoSpace && (charCode == '.' || charCode == ':' || charCode == ','))
2752 monoAdvance = (m_monoSpacing / 4 - (currentGlyphMetrics.width / 2 + currentGlyphMetrics.horizontalBearingX) * currentElementScale) * (1 - m_charWidthAdjDelta);
2753 else
2754 monoAdvance = (m_monoSpacing / 2 - (currentGlyphMetrics.width / 2 + currentGlyphMetrics.horizontalBearingX) * currentElementScale) * (1 - m_charWidthAdjDelta);
2755
2756 m_xAdvance += monoAdvance;
2757 }
2758 #endregion
2759
2760
2761 // Set Padding based on selected font style
2762 #region Handle Style Padding
2763 float boldSpacingAdjustment;
2764 float style_padding;
2765 if (m_textElementType == TMP_TextElementType.Character && !isUsingAltTypeface && ((m_FontStyleInternal & FontStyles.Bold) == FontStyles.Bold)) // Checks for any combination of Bold Style.
2766 {
2767 if (m_currentMaterial != null && m_currentMaterial.HasProperty(ShaderUtilities.ID_GradientScale))
2768 {
2769 float gradientScale = m_currentMaterial.GetFloat(ShaderUtilities.ID_GradientScale);
2770 style_padding = m_currentFontAsset.boldStyle / 4.0f * gradientScale * m_currentMaterial.GetFloat(ShaderUtilities.ID_ScaleRatio_A);
2771
2772 // Clamp overall padding to Gradient Scale size.
2773 if (style_padding + padding > gradientScale)
2774 padding = gradientScale - style_padding;
2775 }
2776 else
2777 style_padding = 0;
2778
2779 boldSpacingAdjustment = m_currentFontAsset.boldSpacing;
2780 }
2781 else
2782 {
2783 if (m_currentMaterial != null && m_currentMaterial.HasProperty(ShaderUtilities.ID_GradientScale) && m_currentMaterial.HasProperty(ShaderUtilities.ID_ScaleRatio_A))
2784 {
2785 float gradientScale = m_currentMaterial.GetFloat(ShaderUtilities.ID_GradientScale);
2786 style_padding = m_currentFontAsset.normalStyle / 4.0f * gradientScale * m_currentMaterial.GetFloat(ShaderUtilities.ID_ScaleRatio_A);
2787
2788 // Clamp overall padding to Gradient Scale size.
2789 if (style_padding + padding > gradientScale)
2790 padding = gradientScale - style_padding;
2791 }
2792 else
2793 style_padding = 0;
2794
2795 boldSpacingAdjustment = 0;
2796 }
2797 #endregion Handle Style Padding
2798
2799
2800 // Determine the position of the vertices of the Character or Sprite.
2801 #region Calculate Vertices Position
2802 k_CalculateVerticesPositionMarker.Begin();
2803 Vector3 top_left;
2804 top_left.x = m_xAdvance + ((currentGlyphMetrics.horizontalBearingX * m_FXScale.x - padding - style_padding + glyphAdjustments.xPlacement) * currentElementScale * (1 - m_charWidthAdjDelta));
2805 top_left.y = baselineOffset + (currentGlyphMetrics.horizontalBearingY + padding + glyphAdjustments.yPlacement) * currentElementScale - m_lineOffset + m_baselineOffset;
2806 top_left.z = 0;
2807
2808 Vector3 bottom_left;
2809 bottom_left.x = top_left.x;
2810 bottom_left.y = top_left.y - ((currentGlyphMetrics.height + padding * 2) * currentElementScale);
2811 bottom_left.z = 0;
2812
2813 Vector3 top_right;
2814 top_right.x = bottom_left.x + ((currentGlyphMetrics.width * m_FXScale.x + padding * 2 + style_padding * 2) * currentElementScale * (1 - m_charWidthAdjDelta));
2815 top_right.y = top_left.y;
2816 top_right.z = 0;
2817
2818 Vector3 bottom_right;
2819 bottom_right.x = top_right.x;
2820 bottom_right.y = bottom_left.y;
2821 bottom_right.z = 0;
2822
2823 k_CalculateVerticesPositionMarker.End();
2824 #endregion
2825
2826
2827 // Check if we need to Shear the rectangles for Italic styles
2828 #region Handle Italic & Shearing
2829 if (m_textElementType == TMP_TextElementType.Character && !isUsingAltTypeface && ((m_FontStyleInternal & FontStyles.Italic) == FontStyles.Italic))
2830 {
2831 // Shift Top vertices forward by half (Shear Value * height of character) and Bottom vertices back by same amount.
2832 float shear_value = m_ItalicAngle * 0.01f;
2833 float midPoint = ((m_currentFontAsset.m_FaceInfo.capLine - (m_currentFontAsset.m_FaceInfo.baseline + m_baselineOffset)) / 2) * m_fontScaleMultiplier * m_currentFontAsset.m_FaceInfo.scale;
2834 Vector3 topShear = new Vector3(shear_value * ((currentGlyphMetrics.horizontalBearingY + padding + style_padding - midPoint) * currentElementScale), 0, 0);
2835 Vector3 bottomShear = new Vector3(shear_value * (((currentGlyphMetrics.horizontalBearingY - currentGlyphMetrics.height - padding - style_padding - midPoint)) * currentElementScale), 0, 0);
2836
2837 top_left += topShear;
2838 bottom_left += bottomShear;
2839 top_right += topShear;
2840 bottom_right += bottomShear;
2841 }
2842 #endregion Handle Italics & Shearing
2843
2844
2845 // Handle Character FX Rotation
2846 #region Handle Character FX Rotation
2847 if (m_FXRotation != Quaternion.identity)
2848 {
2849 Matrix4x4 rotationMatrix = Matrix4x4.Rotate(m_FXRotation);
2850 Vector3 positionOffset = (top_right + bottom_left) / 2;
2851
2852 top_left = rotationMatrix.MultiplyPoint3x4(top_left - positionOffset) + positionOffset;
2853 bottom_left = rotationMatrix.MultiplyPoint3x4(bottom_left - positionOffset) + positionOffset;
2854 top_right = rotationMatrix.MultiplyPoint3x4(top_right - positionOffset) + positionOffset;
2855 bottom_right = rotationMatrix.MultiplyPoint3x4(bottom_right - positionOffset) + positionOffset;
2856 }
2857 #endregion
2858
2859
2860 // Store vertex information for the character or sprite.
2861 m_textInfo.characterInfo[m_characterCount].bottomLeft = bottom_left;
2862 m_textInfo.characterInfo[m_characterCount].topLeft = top_left;
2863 m_textInfo.characterInfo[m_characterCount].topRight = top_right;
2864 m_textInfo.characterInfo[m_characterCount].bottomRight = bottom_right;
2865
2866 m_textInfo.characterInfo[m_characterCount].origin = m_xAdvance + glyphAdjustments.xPlacement * currentElementScale;
2867 m_textInfo.characterInfo[m_characterCount].baseLine = (baselineOffset - m_lineOffset + m_baselineOffset) + glyphAdjustments.yPlacement * currentElementScale;
2868 m_textInfo.characterInfo[m_characterCount].aspectRatio = (top_right.x - bottom_left.x) / (top_left.y - bottom_left.y);
2869
2870
2871 // Compute text metrics
2872 #region Compute Ascender & Descender values
2873 k_ComputeTextMetricsMarker.Begin();
2874 // Element Ascender in line space
2875 float elementAscender = m_textElementType == TMP_TextElementType.Character
2876 ? elementAscentLine * currentElementScale / smallCapsMultiplier + m_baselineOffset
2877 : elementAscentLine * currentElementScale + m_baselineOffset;
2878
2879 // Element Descender in line space
2880 float elementDescender = m_textElementType == TMP_TextElementType.Character
2881 ? elementDescentLine * currentElementScale / smallCapsMultiplier + m_baselineOffset
2882 : elementDescentLine * currentElementScale + m_baselineOffset;
2883
2884 float adjustedAscender = elementAscender;
2885 float adjustedDescender = elementDescender;
2886
2887 // Max line ascender and descender in line space
2888 bool isFirstCharacterOfLine = m_characterCount == m_firstCharacterOfLine;
2889 if (isFirstCharacterOfLine || isWhiteSpace == false)
2890 {
2891 // Special handling for Superscript and Subscript where we use the unadjusted line ascender and descender
2892 if (m_baselineOffset != 0)
2893 {
2894 adjustedAscender = Mathf.Max((elementAscender - m_baselineOffset) / m_fontScaleMultiplier, adjustedAscender);
2895 adjustedDescender = Mathf.Min((elementDescender - m_baselineOffset) / m_fontScaleMultiplier, adjustedDescender);
2896 }
2897
2898 m_maxLineAscender = Mathf.Max(adjustedAscender, m_maxLineAscender);
2899 m_maxLineDescender = Mathf.Min(adjustedDescender, m_maxLineDescender);
2900 }
2901
2902 // Element Ascender and Descender in object space
2903 if (isFirstCharacterOfLine || isWhiteSpace == false)
2904 {
2905 m_textInfo.characterInfo[m_characterCount].adjustedAscender = adjustedAscender;
2906 m_textInfo.characterInfo[m_characterCount].adjustedDescender = adjustedDescender;
2907
2908 m_ElementAscender = m_textInfo.characterInfo[m_characterCount].ascender = elementAscender - m_lineOffset;
2909 m_ElementDescender = m_textInfo.characterInfo[m_characterCount].descender = elementDescender - m_lineOffset;
2910 }
2911 else
2912 {
2913 m_textInfo.characterInfo[m_characterCount].adjustedAscender = m_maxLineAscender;
2914 m_textInfo.characterInfo[m_characterCount].adjustedDescender = m_maxLineDescender;
2915
2916 m_ElementAscender = m_textInfo.characterInfo[m_characterCount].ascender = m_maxLineAscender - m_lineOffset;
2917 m_ElementDescender = m_textInfo.characterInfo[m_characterCount].descender = m_maxLineDescender - m_lineOffset;
2918 }
2919
2920 // Max text object ascender and cap height
2921 if (m_lineNumber == 0 || m_isNewPage)
2922 {
2923 if (isFirstCharacterOfLine || isWhiteSpace == false)
2924 {
2925 m_maxTextAscender = m_maxLineAscender;
2926 m_maxCapHeight = Mathf.Max(m_maxCapHeight, m_currentFontAsset.m_FaceInfo.capLine * currentElementScale / smallCapsMultiplier);
2927 }
2928 }
2929
2930 // Page ascender
2931 if (m_lineOffset == 0)
2932 {
2933 if (isFirstCharacterOfLine || isWhiteSpace == false)
2934 m_PageAscender = m_PageAscender > elementAscender ? m_PageAscender : elementAscender;
2935 }
2936 k_ComputeTextMetricsMarker.End();
2937 #endregion
2938
2939
2940 // Set Characters to not visible by default.
2941 m_textInfo.characterInfo[m_characterCount].isVisible = false;
2942
2943 bool isJustifiedOrFlush = (m_lineJustification & HorizontalAlignmentOptions.Flush) == HorizontalAlignmentOptions.Flush || (m_lineJustification & HorizontalAlignmentOptions.Justified) == HorizontalAlignmentOptions.Justified;
2944
2945 // Setup Mesh for visible text elements. ie. not a SPACE / LINEFEED / CARRIAGE RETURN.
2946 #region Handle Visible Characters
2947 if (charCode == 9 || ((m_TextWrappingMode == TextWrappingModes.PreserveWhitespace || m_TextWrappingMode == TextWrappingModes.PreserveWhitespaceNoWrap) && (isWhiteSpace || charCode == 0x200B)) || (isWhiteSpace == false && charCode != 0x200B && charCode != 0xAD && charCode != 0x03) || (charCode == 0xAD && isSoftHyphenIgnored == false) || m_textElementType == TMP_TextElementType.Sprite)
2948 {
2949 k_HandleVisibleCharacterMarker.Begin();
2950
2951 m_textInfo.characterInfo[m_characterCount].isVisible = true;
2952
2953 #region Experimental Margin Shaper
2954 //Vector2 shapedMargins;
2955 //if (marginShaper)
2956 //{
2957 // shapedMargins = m_marginShaper.GetShapedMargins(m_textInfo.characterInfo[m_characterCount].baseLine);
2958 // if (shapedMargins.x < margins.x)
2959 // {
2960 // shapedMargins.x = m_marginLeft;
2961 // }
2962 // else
2963 // {
2964 // shapedMargins.x += m_marginLeft - margins.x;
2965 // }
2966 // if (shapedMargins.y < margins.z)
2967 // {
2968 // shapedMargins.y = m_marginRight;
2969 // }
2970 // else
2971 // {
2972 // shapedMargins.y += m_marginRight - margins.z;
2973 // }
2974 //}
2975 //else
2976 //{
2977 // shapedMargins.x = m_marginLeft;
2978 // shapedMargins.y = m_marginRight;
2979 //}
2980 //width = marginWidth + 0.0001f - shapedMargins.x - shapedMargins.y;
2981 //if (m_width != -1 && m_width < width)
2982 //{
2983 // width = m_width;
2984 //}
2985 //m_textInfo.lineInfo[m_lineNumber].marginLeft = shapedMargins.x;
2986 #endregion
2987
2988 float marginLeft = m_marginLeft;
2989 float marginRight = m_marginRight;
2990
2991 // Injected characters do not override margins
2992 if (isInjectedCharacter)
2993 {
2994 marginLeft = m_textInfo.lineInfo[m_lineNumber].marginLeft;
2995 marginRight = m_textInfo.lineInfo[m_lineNumber].marginRight;
2996 }
2997
2998 widthOfTextArea = m_width != -1 ? Mathf.Min(marginWidth + 0.0001f - marginLeft - marginRight, m_width) : marginWidth + 0.0001f - marginLeft - marginRight;
2999
3000 // Calculate the line breaking width of the text.
3001 float textWidth = Mathf.Abs(m_xAdvance) + (!m_isRightToLeft ? currentGlyphMetrics.horizontalAdvance : 0) * (1 - m_charWidthAdjDelta) * (charCode == 0xAD ? currentElementUnmodifiedScale : currentElementScale);
3002 float textHeight = m_maxTextAscender - (m_maxLineDescender - m_lineOffset) + (m_lineOffset > 0 && m_IsDrivenLineSpacing == false ? m_maxLineAscender - m_startOfLineAscender : 0);
3003
3004 int testedCharacterCount = m_characterCount;
3005
3006 // Handling of current line Vertical Bounds
3007 #region Current Line Vertical Bounds Check
3008 if (textHeight > marginHeight + 0.0001f)
3009 {
3010 k_HandleVerticalLineBreakingMarker.Begin();
3011
3012 // Set isTextOverflowing and firstOverflowCharacterIndex
3013 if (m_firstOverflowCharacterIndex == -1)
3014 m_firstOverflowCharacterIndex = m_characterCount;
3015
3016 // Check if Auto-Size is enabled
3017 if (m_enableAutoSizing)
3018 {
3019 // Handle Line spacing adjustments
3020 #region Line Spacing Adjustments
3021 if (m_lineSpacingDelta > m_lineSpacingMax && m_lineOffset > 0 && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount)
3022 {
3023 float adjustmentDelta = (marginHeight - textHeight) / m_lineNumber;
3024
3025 m_lineSpacingDelta = Mathf.Max(m_lineSpacingDelta + adjustmentDelta / baseScale, m_lineSpacingMax);
3026
3027 //Debug.Log("[" + m_AutoSizeIterationCount + "] Reducing Line Spacing. Delta of [" + m_lineSpacingDelta.ToString("f3") + "].");
3028 k_HandleVerticalLineBreakingMarker.End();
3029 k_HandleVisibleCharacterMarker.End();
3030 k_GenerateTextPhaseIMarker.End();
3031 k_GenerateTextMarker.End();
3032 return;
3033 }
3034 #endregion
3035
3036
3037 // Handle Text Auto-sizing resulting from text exceeding vertical bounds.
3038 #region Text Auto-Sizing (Text greater than vertical bounds)
3039 if (m_fontSize > m_fontSizeMin && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount)
3040 {
3041 m_maxFontSize = m_fontSize;
3042
3043 float sizeDelta = Mathf.Max((m_fontSize - m_minFontSize) / 2, 0.05f);
3044 m_fontSize -= sizeDelta;
3045 m_fontSize = Mathf.Max((int)(m_fontSize * 20 + 0.5f) / 20f, m_fontSizeMin);
3046
3047 //Debug.Log("[" + m_AutoSizeIterationCount + "] Reducing Point Size from [" + m_maxFontSize.ToString("f3") + "] to [" + m_fontSize.ToString("f3") + "] with delta of [" + sizeDelta.ToString("f3") + "].");
3048 k_HandleVerticalLineBreakingMarker.End();
3049 k_HandleVisibleCharacterMarker.End();
3050 k_GenerateTextPhaseIMarker.End();
3051 k_GenerateTextMarker.End();
3052 return;
3053 }
3054 #endregion Text Auto-Sizing
3055 }
3056
3057 // Handle Vertical Overflow on current line
3058 switch (m_overflowMode)
3059 {
3060 case TextOverflowModes.Overflow:
3061 case TextOverflowModes.ScrollRect:
3062 case TextOverflowModes.Masking:
3063 // Nothing happens as vertical bounds are ignored in this mode.
3064 break;
3065
3066 case TextOverflowModes.Truncate:
3067 i = RestoreWordWrappingState(ref m_SavedLastValidState);
3068
3069 characterToSubstitute.index = testedCharacterCount;
3070 characterToSubstitute.unicode = 0x03;
3071 k_HandleVerticalLineBreakingMarker.End();
3072 k_HandleVisibleCharacterMarker.End();
3073 continue;
3074
3075 case TextOverflowModes.Ellipsis:
3076 if (m_EllipsisInsertionCandidateStack.Count == 0)
3077 {
3078 i = -1;
3079 m_characterCount = 0;
3080 characterToSubstitute.index = 0;
3081 characterToSubstitute.unicode = 0x03;
3082 m_firstCharacterOfLine = 0;
3083 k_HandleVerticalLineBreakingMarker.End();
3084 k_HandleVisibleCharacterMarker.End();
3085 continue;
3086 }
3087
3088 var ellipsisState = m_EllipsisInsertionCandidateStack.Pop();
3089 i = RestoreWordWrappingState(ref ellipsisState);
3090
3091 i -= 1;
3092 m_characterCount -= 1;
3093 characterToSubstitute.index = m_characterCount;
3094 characterToSubstitute.unicode = 0x2026;
3095
3096 restoreCount += 1;
3097 k_HandleVerticalLineBreakingMarker.End();
3098 k_HandleVisibleCharacterMarker.End();
3099 continue;
3100
3101 case TextOverflowModes.Linked:
3102 i = RestoreWordWrappingState(ref m_SavedLastValidState);
3103
3104 if (m_linkedTextComponent != null)
3105 {
3106 m_linkedTextComponent.text = text;
3107 m_linkedTextComponent.m_inputSource = m_inputSource;
3108 m_linkedTextComponent.firstVisibleCharacter = m_characterCount;
3109 m_linkedTextComponent.ForceMeshUpdate();
3110
3111 m_isTextTruncated = true;
3112 }
3113
3114 // Truncate remaining text
3115 characterToSubstitute.index = testedCharacterCount;
3116 characterToSubstitute.unicode = 0x03;
3117 k_HandleVerticalLineBreakingMarker.End();
3118 k_HandleVisibleCharacterMarker.End();
3119 continue;
3120
3121 case TextOverflowModes.Page:
3122 // End layout of text if first character / page doesn't fit.
3123 if (i < 0 || testedCharacterCount == 0)
3124 {
3125 i = -1;
3126 m_characterCount = 0;
3127 characterToSubstitute.index = 0;
3128 characterToSubstitute.unicode = 0x03;
3129 k_HandleVerticalLineBreakingMarker.End();
3130 k_HandleVisibleCharacterMarker.End();
3131 continue;
3132 }
3133 else if (m_maxLineAscender - m_maxLineDescender > marginHeight + 0.0001f)
3134 {
3135 // Current line exceeds the height of the text container
3136 // as such we stop on the previous line.
3137 i = RestoreWordWrappingState(ref m_SavedLineState);
3138
3139 characterToSubstitute.index = testedCharacterCount;
3140 characterToSubstitute.unicode = 0x03;
3141 k_HandleVerticalLineBreakingMarker.End();
3142 k_HandleVisibleCharacterMarker.End();
3143 continue;
3144 }
3145
3146 // Go back to previous line and re-layout
3147 i = RestoreWordWrappingState(ref m_SavedLineState);
3148
3149 m_isNewPage = true;
3150 m_firstCharacterOfLine = m_characterCount;
3151 m_maxLineAscender = k_LargeNegativeFloat;
3152 m_maxLineDescender = k_LargePositiveFloat;
3153 m_startOfLineAscender = 0;
3154
3155 m_xAdvance = 0 + tag_Indent;
3156 m_lineOffset = 0;
3157 m_maxTextAscender = 0;
3158 m_PageAscender = 0;
3159 m_lineNumber += 1;
3160 m_pageNumber += 1;
3161
3162 // Should consider saving page data here
3163 k_HandleVerticalLineBreakingMarker.End();
3164 k_HandleVisibleCharacterMarker.End();
3165 continue;
3166 }
3167
3168 k_HandleVerticalLineBreakingMarker.End();
3169 }
3170 #endregion
3171
3172
3173 // Handling of Horizontal Bounds
3174 #region Current Line Horizontal Bounds Check
3175 if (isBaseGlyph && textWidth > widthOfTextArea * (isJustifiedOrFlush ? 1.05f : 1.0f))
3176 {
3177 k_HandleHorizontalLineBreakingMarker.Begin();
3178
3179 // Handle Line Breaking (if still possible)
3180 if (m_TextWrappingMode != TextWrappingModes.NoWrap && m_TextWrappingMode != TextWrappingModes.PreserveWhitespaceNoWrap && m_characterCount != m_firstCharacterOfLine)
3181 {
3182 // Restore state to previous safe line breaking
3183 i = RestoreWordWrappingState(ref m_SavedWordWrapState);
3184
3185 // Compute potential new line offset in the event a line break is needed.
3186 float lineOffsetDelta = 0;
3187 if (m_lineHeight == TMP_Math.FLOAT_UNSET)
3188 {
3189 float ascender = m_textInfo.characterInfo[m_characterCount].adjustedAscender;
3190 lineOffsetDelta = (m_lineOffset > 0 && m_IsDrivenLineSpacing == false ? m_maxLineAscender - m_startOfLineAscender : 0) - m_maxLineDescender + ascender + (lineGap + m_lineSpacingDelta) * baseScale + m_lineSpacing * currentEmScale;
3191 }
3192 else
3193 {
3194 lineOffsetDelta = m_lineHeight + m_lineSpacing * currentEmScale;
3195 m_IsDrivenLineSpacing = true;
3196 }
3197
3198 // Calculate new text height
3199 float newTextHeight = m_maxTextAscender + lineOffsetDelta + m_lineOffset - m_textInfo.characterInfo[m_characterCount].adjustedDescender;
3200
3201 // Replace Soft Hyphen by Hyphen Minus 0x2D
3202 #region Handle Soft Hyphenation
3203 if (m_textInfo.characterInfo[m_characterCount - 1].character == 0xAD && isSoftHyphenIgnored == false)
3204 {
3205 // Only inject Hyphen Minus if new line is possible
3206 if (m_overflowMode == TextOverflowModes.Overflow || newTextHeight < marginHeight + 0.0001f)
3207 {
3208 characterToSubstitute.index = m_characterCount - 1;
3209 characterToSubstitute.unicode = 0x2D;
3210
3211 i -= 1;
3212 m_characterCount -= 1;
3213 k_HandleHorizontalLineBreakingMarker.End();
3214 k_HandleVisibleCharacterMarker.End();
3215 continue;
3216 }
3217 }
3218
3219 isSoftHyphenIgnored = false;
3220
3221 // Ignore Soft Hyphen to prevent it from wrapping
3222 if (m_textInfo.characterInfo[m_characterCount].character == 0xAD)
3223 {
3224 isSoftHyphenIgnored = true;
3225 k_HandleHorizontalLineBreakingMarker.End();
3226 k_HandleVisibleCharacterMarker.End();
3227 continue;
3228 }
3229 #endregion
3230
3231 // Adjust character spacing before breaking up word if auto size is enabled
3232 if (m_enableAutoSizing && isFirstWordOfLine)
3233 {
3234 // Handle Character Width Adjustments
3235 #region Character Width Adjustments
3236 if (m_charWidthAdjDelta < m_charWidthMaxAdj / 100 && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount)
3237 {
3238 float adjustedTextWidth = textWidth;
3239
3240 // Determine full width of the text
3241 if (m_charWidthAdjDelta > 0)
3242 adjustedTextWidth /= 1f - m_charWidthAdjDelta;
3243
3244 float adjustmentDelta = textWidth - (widthOfTextArea - 0.0001f) * (isJustifiedOrFlush ? 1.05f : 1.0f);
3245 m_charWidthAdjDelta += adjustmentDelta / adjustedTextWidth;
3246 m_charWidthAdjDelta = Mathf.Min(m_charWidthAdjDelta, m_charWidthMaxAdj / 100);
3247
3248 //Debug.Log("[" + m_AutoSizeIterationCount + "] Reducing Character Width by " + (m_charWidthAdjDelta * 100) + "%");
3249 k_HandleHorizontalLineBreakingMarker.End();
3250 k_HandleVisibleCharacterMarker.End();
3251 k_GenerateTextPhaseIMarker.End();
3252 k_GenerateTextMarker.End();
3253 return;
3254 }
3255 #endregion
3256
3257 // Handle Text Auto-sizing resulting from text exceeding vertical bounds.
3258 #region Text Auto-Sizing (Text greater than vertical bounds)
3259 if (m_fontSize > m_fontSizeMin && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount)
3260 {
3261 m_maxFontSize = m_fontSize;
3262
3263 float sizeDelta = Mathf.Max((m_fontSize - m_minFontSize) / 2, 0.05f);
3264 m_fontSize -= sizeDelta;
3265 m_fontSize = Mathf.Max((int)(m_fontSize * 20 + 0.5f) / 20f, m_fontSizeMin);
3266
3267 //Debug.Log("[" + m_AutoSizeIterationCount + "] Reducing Point Size from [" + m_maxFontSize.ToString("f3") + "] to [" + m_fontSize.ToString("f3") + "] with delta of [" + sizeDelta.ToString("f3") + "].");
3268 k_HandleHorizontalLineBreakingMarker.End();
3269 k_HandleVisibleCharacterMarker.End();
3270 k_GenerateTextPhaseIMarker.End();
3271 k_GenerateTextMarker.End();
3272 return;
3273 }
3274 #endregion Text Auto-Sizing
3275 }
3276
3277
3278 // Special handling if first word of line and non breaking space
3279 int savedSoftLineBreakingSpace = m_SavedSoftLineBreakState.previous_WordBreak;
3280 if (isFirstWordOfLine && savedSoftLineBreakingSpace != -1)
3281 {
3282 if (savedSoftLineBreakingSpace != lastSoftLineBreak)
3283 {
3284 i = RestoreWordWrappingState(ref m_SavedSoftLineBreakState);
3285 lastSoftLineBreak = savedSoftLineBreakingSpace;
3286
3287 // check if soft hyphen
3288 if (m_textInfo.characterInfo[m_characterCount - 1].character == 0xAD)
3289 {
3290 characterToSubstitute.index = m_characterCount - 1;
3291 characterToSubstitute.unicode = 0x2D;
3292
3293 i -= 1;
3294 m_characterCount -= 1;
3295 k_HandleHorizontalLineBreakingMarker.End();
3296 k_HandleVisibleCharacterMarker.End();
3297 continue;
3298 }
3299 }
3300 }
3301
3302 // Determine if new line of text would exceed the vertical bounds of text container
3303 if (newTextHeight > marginHeight + 0.0001f)
3304 {
3305 k_HandleVerticalLineBreakingMarker.Begin();
3306
3307 // Set isTextOverflowing and firstOverflowCharacterIndex
3308 if (m_firstOverflowCharacterIndex == -1)
3309 m_firstOverflowCharacterIndex = m_characterCount;
3310
3311 // Check if Auto-Size is enabled
3312 if (m_enableAutoSizing)
3313 {
3314 // Handle Line spacing adjustments
3315 #region Line Spacing Adjustments
3316 if (m_lineSpacingDelta > m_lineSpacingMax && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount)
3317 {
3318 float adjustmentDelta = (marginHeight - newTextHeight) / (m_lineNumber + 1);
3319
3320 m_lineSpacingDelta = Mathf.Max(m_lineSpacingDelta + adjustmentDelta / baseScale, m_lineSpacingMax);
3321
3322 //Debug.Log("[" + m_AutoSizeIterationCount + "] Reducing Line Spacing. Delta of [" + m_lineSpacingDelta.ToString("f3") + "].");
3323 k_HandleVerticalLineBreakingMarker.End();
3324 k_HandleHorizontalLineBreakingMarker.End();
3325 k_HandleVisibleCharacterMarker.End();
3326 k_GenerateTextPhaseIMarker.End();
3327 k_GenerateTextMarker.End();
3328 return;
3329 }
3330 #endregion
3331
3332 // Handle Character Width Adjustments
3333 #region Character Width Adjustments
3334 if (m_charWidthAdjDelta < m_charWidthMaxAdj / 100 && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount)
3335 {
3336 float adjustedTextWidth = textWidth;
3337
3338 // Determine full width of the text
3339 if (m_charWidthAdjDelta > 0)
3340 adjustedTextWidth /= 1f - m_charWidthAdjDelta;
3341
3342 float adjustmentDelta = textWidth - (widthOfTextArea - 0.0001f) * (isJustifiedOrFlush ? 1.05f : 1.0f);
3343 m_charWidthAdjDelta += adjustmentDelta / adjustedTextWidth;
3344 m_charWidthAdjDelta = Mathf.Min(m_charWidthAdjDelta, m_charWidthMaxAdj / 100);
3345
3346 //Debug.Log("[" + m_AutoSizeIterationCount + "] Reducing Character Width by " + (m_charWidthAdjDelta * 100) + "%");
3347 k_HandleVerticalLineBreakingMarker.End();
3348 k_HandleHorizontalLineBreakingMarker.End();
3349 k_HandleVisibleCharacterMarker.End();
3350 k_GenerateTextPhaseIMarker.End();
3351 k_GenerateTextMarker.End();
3352 return;
3353 }
3354 #endregion
3355
3356 // Handle Text Auto-sizing resulting from text exceeding vertical bounds.
3357 #region Text Auto-Sizing (Text greater than vertical bounds)
3358 if (m_fontSize > m_fontSizeMin && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount)
3359 {
3360 m_maxFontSize = m_fontSize;
3361
3362 float sizeDelta = Mathf.Max((m_fontSize - m_minFontSize) / 2, 0.05f);
3363 m_fontSize -= sizeDelta;
3364 m_fontSize = Mathf.Max((int)(m_fontSize * 20 + 0.5f) / 20f, m_fontSizeMin);
3365
3366 //Debug.Log("[" + m_AutoSizeIterationCount + "] Reducing Point Size from [" + m_maxFontSize.ToString("f3") + "] to [" + m_fontSize.ToString("f3") + "] with delta of [" + sizeDelta.ToString("f3") + "].");
3367 k_HandleVerticalLineBreakingMarker.End();
3368 k_HandleHorizontalLineBreakingMarker.End();
3369 k_HandleVisibleCharacterMarker.End();
3370 k_GenerateTextPhaseIMarker.End();
3371 k_GenerateTextMarker.End();
3372 return;
3373 }
3374 #endregion Text Auto-Sizing
3375 }
3376
3377 // Check Text Overflow Modes
3378 switch (m_overflowMode)
3379 {
3380 case TextOverflowModes.Overflow:
3381 case TextOverflowModes.ScrollRect:
3382 case TextOverflowModes.Masking:
3383 InsertNewLine(i, baseScale, currentElementScale, currentEmScale, boldSpacingAdjustment, characterSpacingAdjustment, widthOfTextArea, lineGap, ref isMaxVisibleDescenderSet, ref maxVisibleDescender);
3384 isStartOfNewLine = true;
3385 isFirstWordOfLine = true;
3386 k_HandleVerticalLineBreakingMarker.End();
3387 k_HandleHorizontalLineBreakingMarker.End();
3388 k_HandleVisibleCharacterMarker.End();
3389 continue;
3390
3391 case TextOverflowModes.Truncate:
3392 i = RestoreWordWrappingState(ref m_SavedLastValidState);
3393
3394 characterToSubstitute.index = testedCharacterCount;
3395 characterToSubstitute.unicode = 0x03;
3396 k_HandleVerticalLineBreakingMarker.End();
3397 k_HandleHorizontalLineBreakingMarker.End();
3398 k_HandleVisibleCharacterMarker.End();
3399 continue;
3400
3401 case TextOverflowModes.Ellipsis:
3402 if (m_EllipsisInsertionCandidateStack.Count == 0)
3403 {
3404 i = -1;
3405 m_characterCount = 0;
3406 characterToSubstitute.index = 0;
3407 characterToSubstitute.unicode = 0x03;
3408 m_firstCharacterOfLine = 0;
3409 k_HandleVerticalLineBreakingMarker.End();
3410 k_HandleHorizontalLineBreakingMarker.End();
3411 k_HandleVisibleCharacterMarker.End();
3412 continue;
3413 }
3414
3415 var ellipsisState = m_EllipsisInsertionCandidateStack.Pop();
3416 i = RestoreWordWrappingState(ref ellipsisState);
3417
3418 i -= 1;
3419 m_characterCount -= 1;
3420 characterToSubstitute.index = m_characterCount;
3421 characterToSubstitute.unicode = 0x2026;
3422
3423 restoreCount += 1;
3424 k_HandleVerticalLineBreakingMarker.End();
3425 k_HandleHorizontalLineBreakingMarker.End();
3426 k_HandleVisibleCharacterMarker.End();
3427 continue;
3428
3429 case TextOverflowModes.Linked:
3430 if (m_linkedTextComponent != null)
3431 {
3432 m_linkedTextComponent.text = text;
3433 m_linkedTextComponent.m_inputSource = m_inputSource;
3434 m_linkedTextComponent.firstVisibleCharacter = m_characterCount;
3435 m_linkedTextComponent.ForceMeshUpdate();
3436
3437 m_isTextTruncated = true;
3438 }
3439
3440 // Truncate remaining text
3441 characterToSubstitute.index = m_characterCount;
3442 characterToSubstitute.unicode = 0x03;
3443 k_HandleVerticalLineBreakingMarker.End();
3444 k_HandleHorizontalLineBreakingMarker.End();
3445 k_HandleVisibleCharacterMarker.End();
3446 continue;
3447
3448 case TextOverflowModes.Page:
3449 // Add new page
3450 m_isNewPage = true;
3451
3452 InsertNewLine(i, baseScale, currentElementScale, currentEmScale, boldSpacingAdjustment, characterSpacingAdjustment, widthOfTextArea, lineGap, ref isMaxVisibleDescenderSet, ref maxVisibleDescender);
3453
3454 m_startOfLineAscender = 0;
3455 m_lineOffset = 0;
3456 m_maxTextAscender = 0;
3457 m_PageAscender = 0;
3458 m_pageNumber += 1;
3459
3460 isStartOfNewLine = true;
3461 isFirstWordOfLine = true;
3462 k_HandleVerticalLineBreakingMarker.End();
3463 k_HandleHorizontalLineBreakingMarker.End();
3464 k_HandleVisibleCharacterMarker.End();
3465 continue;
3466 }
3467 }
3468 else
3469 {
3470 //if (m_enableAutoSizing && isFirstWordOfLine)
3471 //{
3472 // // Handle Character Width Adjustments
3473 // #region Character Width Adjustments
3474 // if (m_charWidthAdjDelta < m_charWidthMaxAdj / 100 && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount)
3475 // {
3476 // //m_AutoSizeIterationCount = 0;
3477 // float adjustedTextWidth = textWidth;
3478
3479 // // Determine full width of the text
3480 // if (m_charWidthAdjDelta > 0)
3481 // adjustedTextWidth /= 1f - m_charWidthAdjDelta;
3482
3483 // float adjustmentDelta = textWidth - (widthOfTextArea - 0.0001f) * (isJustifiedOrFlush ? 1.05f : 1.0f);
3484 // m_charWidthAdjDelta += adjustmentDelta / adjustedTextWidth;
3485 // m_charWidthAdjDelta = Mathf.Min(m_charWidthAdjDelta, m_charWidthMaxAdj / 100);
3486
3487 // //Debug.Log("[" + m_AutoSizeIterationCount + "] Reducing Character Width by " + (m_charWidthAdjDelta * 100) + "%");
3488
3489 // GenerateTextMesh();
3490 // return;
3491 // }
3492 // #endregion
3493 //}
3494
3495 // New line of text does not exceed vertical bounds of text container
3496 InsertNewLine(i, baseScale, currentElementScale, currentEmScale, boldSpacingAdjustment, characterSpacingAdjustment, widthOfTextArea, lineGap, ref isMaxVisibleDescenderSet, ref maxVisibleDescender);
3497 isStartOfNewLine = true;
3498 isFirstWordOfLine = true;
3499 k_HandleHorizontalLineBreakingMarker.End();
3500 k_HandleVisibleCharacterMarker.End();
3501 continue;
3502 }
3503 }
3504 else
3505 {
3506 if (m_enableAutoSizing && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount)
3507 {
3508 // Handle Character Width Adjustments
3509 #region Character Width Adjustments
3510 if (m_charWidthAdjDelta < m_charWidthMaxAdj / 100)
3511 {
3512 float adjustedTextWidth = textWidth;
3513
3514 // Determine full width of the text
3515 if (m_charWidthAdjDelta > 0)
3516 adjustedTextWidth /= 1f - m_charWidthAdjDelta;
3517
3518 float adjustmentDelta = textWidth - (widthOfTextArea - 0.0001f) * (isJustifiedOrFlush ? 1.05f : 1.0f);
3519 m_charWidthAdjDelta += adjustmentDelta / adjustedTextWidth;
3520 m_charWidthAdjDelta = Mathf.Min(m_charWidthAdjDelta, m_charWidthMaxAdj / 100);
3521
3522 //Debug.Log("[" + m_AutoSizeIterationCount + "] Reducing Character Width by " + (m_charWidthAdjDelta * 100) + "%");
3523 k_HandleHorizontalLineBreakingMarker.End();
3524 k_HandleVisibleCharacterMarker.End();
3525 k_GenerateTextPhaseIMarker.End();
3526 k_GenerateTextMarker.End();
3527 return;
3528 }
3529 #endregion
3530
3531 // Handle Text Auto-sizing resulting from text exceeding horizontal bounds.
3532 #region Text Exceeds Horizontal Bounds - Reducing Point Size
3533 if (m_fontSize > m_fontSizeMin)
3534 {
3535 // Reset character width adjustment delta
3536 //m_charWidthAdjDelta = 0;
3537
3538 // Adjust Point Size
3539 m_maxFontSize = m_fontSize;
3540
3541 float sizeDelta = Mathf.Max((m_fontSize - m_minFontSize) / 2, 0.05f);
3542 m_fontSize -= sizeDelta;
3543 m_fontSize = Mathf.Max((int)(m_fontSize * 20 + 0.5f) / 20f, m_fontSizeMin);
3544
3545 //Debug.Log("[" + m_AutoSizeIterationCount + "] Reducing Point Size from [" + m_maxFontSize.ToString("f3") + "] to [" + m_fontSize.ToString("f3") + "] with delta of [" + sizeDelta.ToString("f3") + "].");
3546 k_HandleHorizontalLineBreakingMarker.End();
3547 k_HandleVisibleCharacterMarker.End();
3548 k_GenerateTextPhaseIMarker.End();
3549 k_GenerateTextMarker.End();
3550 return;
3551 }
3552 #endregion
3553
3554 }
3555
3556 // Check Text Overflow Modes
3557 switch (m_overflowMode)
3558 {
3559 case TextOverflowModes.Overflow:
3560 case TextOverflowModes.ScrollRect:
3561 case TextOverflowModes.Masking:
3562 // Nothing happens as horizontal bounds are ignored in this mode.
3563 break;
3564
3565 case TextOverflowModes.Truncate:
3566 i = RestoreWordWrappingState(ref m_SavedWordWrapState);
3567
3568 characterToSubstitute.index = testedCharacterCount;
3569 characterToSubstitute.unicode = 0x03;
3570 k_HandleHorizontalLineBreakingMarker.End();
3571 k_HandleVisibleCharacterMarker.End();
3572 continue;
3573
3574 case TextOverflowModes.Ellipsis:
3575 if (m_EllipsisInsertionCandidateStack.Count == 0)
3576 {
3577 i = -1;
3578 m_characterCount = 0;
3579 characterToSubstitute.index = 0;
3580 characterToSubstitute.unicode = 0x03;
3581 m_firstCharacterOfLine = 0;
3582 k_HandleHorizontalLineBreakingMarker.End();
3583 k_HandleVisibleCharacterMarker.End();
3584 continue;
3585 }
3586
3587 var ellipsisState = m_EllipsisInsertionCandidateStack.Pop();
3588 i = RestoreWordWrappingState(ref ellipsisState);
3589
3590 i -= 1;
3591 m_characterCount -= 1;
3592 characterToSubstitute.index = m_characterCount;
3593 characterToSubstitute.unicode = 0x2026;
3594
3595 restoreCount += 1;
3596 k_HandleHorizontalLineBreakingMarker.End();
3597 k_HandleVisibleCharacterMarker.End();
3598 continue;
3599
3600 case TextOverflowModes.Linked:
3601 i = RestoreWordWrappingState(ref m_SavedWordWrapState);
3602
3603 if (m_linkedTextComponent != null)
3604 {
3605 m_linkedTextComponent.text = text;
3606 m_linkedTextComponent.m_inputSource = m_inputSource;
3607 m_linkedTextComponent.firstVisibleCharacter = m_characterCount;
3608 m_linkedTextComponent.ForceMeshUpdate();
3609
3610 m_isTextTruncated = true;
3611 }
3612
3613 // Truncate text the overflows the vertical bounds
3614 characterToSubstitute.index = m_characterCount;
3615 characterToSubstitute.unicode = 0x03;
3616 k_HandleHorizontalLineBreakingMarker.End();
3617 k_HandleVisibleCharacterMarker.End();
3618 continue;
3619 }
3620
3621 }
3622
3623 k_HandleHorizontalLineBreakingMarker.End();
3624 }
3625 #endregion
3626
3627
3628 // Special handling of characters that are not ignored at the end of a line.
3629 if (isWhiteSpace)
3630 {
3631 m_textInfo.characterInfo[m_characterCount].isVisible = false;
3632 m_lastVisibleCharacterOfLine = m_characterCount;
3633 m_lineVisibleSpaceCount = m_textInfo.lineInfo[m_lineNumber].spaceCount += 1;
3634 m_textInfo.lineInfo[m_lineNumber].marginLeft = marginLeft;
3635 m_textInfo.lineInfo[m_lineNumber].marginRight = marginRight;
3636 m_textInfo.spaceCount += 1;
3637
3638 if (charCode == 0xA0)
3639 m_textInfo.lineInfo[m_lineNumber].controlCharacterCount += 1;
3640 }
3641 else if (charCode == 0xAD)
3642 {
3643 m_textInfo.characterInfo[m_characterCount].isVisible = false;
3644 }
3645 else
3646 {
3647 // Determine Vertex Color
3648 Color32 vertexColor;
3649 if (m_overrideHtmlColors)
3650 vertexColor = m_fontColor32;
3651 else
3652 vertexColor = m_htmlColor;
3653
3654 k_SaveGlyphVertexDataMarker.Begin();
3655 // Store Character & Sprite Vertex Information
3656 if (m_textElementType == TMP_TextElementType.Character)
3657 {
3658 // Save Character Vertex Data
3659 SaveGlyphVertexInfo(padding, style_padding, vertexColor);
3660 }
3661 else if (m_textElementType == TMP_TextElementType.Sprite)
3662 {
3663 SaveSpriteVertexInfo(vertexColor);
3664 }
3665 k_SaveGlyphVertexDataMarker.End();
3666
3667 if (isStartOfNewLine)
3668 {
3669 isStartOfNewLine = false;
3670 m_firstVisibleCharacterOfLine = m_characterCount;
3671 }
3672
3673 m_lineVisibleCharacterCount += 1;
3674 m_lastVisibleCharacterOfLine = m_characterCount;
3675 m_textInfo.lineInfo[m_lineNumber].marginLeft = marginLeft;
3676 m_textInfo.lineInfo[m_lineNumber].marginRight = marginRight;
3677 }
3678
3679 k_HandleVisibleCharacterMarker.End();
3680 }
3681 else
3682 {
3683 k_HandleWhiteSpacesMarker.Begin();
3684
3685 // Special handling for text overflow linked mode
3686 #region Check Vertical Bounds
3687 if (m_overflowMode == TextOverflowModes.Linked && (charCode == 10 || charCode == 11))
3688 {
3689 float textHeight = m_maxTextAscender - (m_maxLineDescender - m_lineOffset) + (m_lineOffset > 0 && m_IsDrivenLineSpacing == false ? m_maxLineAscender - m_startOfLineAscender : 0);
3690
3691 int testedCharacterCount = m_characterCount;
3692
3693 if (textHeight > marginHeight + 0.0001f)
3694 {
3695 // Set isTextOverflowing and firstOverflowCharacterIndex
3696 if (m_firstOverflowCharacterIndex == -1)
3697 m_firstOverflowCharacterIndex = m_characterCount;
3698
3699 i = RestoreWordWrappingState(ref m_SavedLastValidState);
3700
3701 if (m_linkedTextComponent != null)
3702 {
3703 m_linkedTextComponent.text = text;
3704 m_linkedTextComponent.m_inputSource = m_inputSource;
3705 m_linkedTextComponent.firstVisibleCharacter = m_characterCount;
3706 m_linkedTextComponent.ForceMeshUpdate();
3707
3708 m_isTextTruncated = true;
3709 }
3710
3711 // Truncate remaining text
3712 characterToSubstitute.index = testedCharacterCount;
3713 characterToSubstitute.unicode = 0x03;
3714 k_HandleWhiteSpacesMarker.End();
3715 continue;
3716 }
3717 }
3718 #endregion
3719
3720 // Track # of spaces per line which is used for line justification.
3721 if ((charCode == 10 || charCode == 11 || charCode == 0xA0 || charCode == 0x2007 || charCode == 0x2028 || charCode == 0x2029 || char.IsSeparator((char)charCode)) && charCode != 0xAD && charCode != 0x200B && charCode != 0x2060)
3722 {
3723 m_textInfo.lineInfo[m_lineNumber].spaceCount += 1;
3724 m_textInfo.spaceCount += 1;
3725 }
3726
3727 // Special handling for control characters like <NBSP>
3728 if (charCode == 0xA0)
3729 m_textInfo.lineInfo[m_lineNumber].controlCharacterCount += 1;
3730
3731 k_HandleWhiteSpacesMarker.End();
3732 }
3733 #endregion Handle Visible Characters
3734
3735
3736 // Tracking of potential insertion positions for Ellipsis character
3737 #region Track Potential Insertion Location for Ellipsis
3738 if (m_overflowMode == TextOverflowModes.Ellipsis && (isInjectedCharacter == false || charCode == 0x2D))
3739 {
3740 float fontScale = m_currentFontSize / m_Ellipsis.fontAsset.m_FaceInfo.pointSize * m_Ellipsis.fontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f);
3741 float scale = fontScale * m_fontScaleMultiplier * m_Ellipsis.character.m_Scale * m_Ellipsis.character.m_Glyph.scale;
3742 float marginLeft = m_marginLeft;
3743 float marginRight = m_marginRight;
3744
3745 // Use the scale and margins of the previous character if Line Feed (LF) is not the first character of a line.
3746 if (charCode == 0x0A && m_characterCount != m_firstCharacterOfLine)
3747 {
3748 fontScale = m_textInfo.characterInfo[m_characterCount - 1].pointSize / m_Ellipsis.fontAsset.m_FaceInfo.pointSize * m_Ellipsis.fontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f);
3749 scale = fontScale * m_fontScaleMultiplier * m_Ellipsis.character.m_Scale * m_Ellipsis.character.m_Glyph.scale;
3750 marginLeft = m_textInfo.lineInfo[m_lineNumber].marginLeft;
3751 marginRight = m_textInfo.lineInfo[m_lineNumber].marginRight;
3752 }
3753
3754 float textHeight = m_maxTextAscender - (m_maxLineDescender - m_lineOffset) + (m_lineOffset > 0 && m_IsDrivenLineSpacing == false ? m_maxLineAscender - m_startOfLineAscender : 0);
3755 float textWidth = Mathf.Abs(m_xAdvance) + (!m_isRightToLeft ? m_Ellipsis.character.m_Glyph.metrics.horizontalAdvance : 0) * (1 - m_charWidthAdjDelta) * scale;
3756 float widthOfTextAreaForEllipsis = m_width != -1 ? Mathf.Min(marginWidth + 0.0001f - marginLeft - marginRight, m_width) : marginWidth + 0.0001f - marginLeft - marginRight;
3757
3758 if (textWidth < widthOfTextAreaForEllipsis * (isJustifiedOrFlush ? 1.05f : 1.0f) && textHeight < marginHeight + 0.0001f)
3759 {
3760 SaveWordWrappingState(ref m_SavedEllipsisState, i, m_characterCount);
3761 m_EllipsisInsertionCandidateStack.Push(m_SavedEllipsisState);
3762 }
3763 }
3764 #endregion
3765
3766
3767 // Store Rectangle positions for each Character.
3768 #region Store Character Data
3769 m_textInfo.characterInfo[m_characterCount].lineNumber = m_lineNumber;
3770 m_textInfo.characterInfo[m_characterCount].pageNumber = m_pageNumber;
3771
3772 if (charCode != 10 && charCode != 11 && charCode != 13 && isInjectedCharacter == false /* && charCode != 8230 */ || m_textInfo.lineInfo[m_lineNumber].characterCount == 1)
3773 m_textInfo.lineInfo[m_lineNumber].alignment = m_lineJustification;
3774 #endregion Store Character Data
3775
3776
3777 // Handle xAdvance & Tabulation Stops. Tab stops at every 25% of Font Size.
3778 #region XAdvance, Tabulation & Stops
3779 k_ComputeCharacterAdvanceMarker.Begin();
3780 if (charCode == 9)
3781 {
3782 float tabSize = m_currentFontAsset.m_FaceInfo.tabWidth * m_currentFontAsset.tabSize * currentElementScale;
3783 // Adjust horizontal tab depending on RTL
3784 if (m_isRightToLeft)
3785 {
3786 float tabs = Mathf.Floor(m_xAdvance / tabSize) * tabSize;
3787 m_xAdvance = tabs < m_xAdvance ? tabs : m_xAdvance - tabSize;
3788 }
3789 else
3790 {
3791 float tabs = Mathf.Ceil(m_xAdvance / tabSize) * tabSize;
3792 m_xAdvance = tabs > m_xAdvance ? tabs : m_xAdvance + tabSize;
3793 }
3794 }
3795 else if (m_monoSpacing != 0)
3796 {
3797 float monoAdjustment;
3798 if (m_duoSpace && (charCode == '.' || charCode == ':' || charCode == ','))
3799 monoAdjustment = m_monoSpacing / 2 - monoAdvance;
3800 else
3801 monoAdjustment = m_monoSpacing - monoAdvance;
3802
3803 m_xAdvance += (monoAdjustment + ((m_currentFontAsset.normalSpacingOffset + characterSpacingAdjustment) * currentEmScale) + m_cSpacing) * (1 - m_charWidthAdjDelta);
3804
3805 if (isWhiteSpace || charCode == 0x200B)
3806 m_xAdvance += m_wordSpacing * currentEmScale;
3807 }
3808 else if (m_isRightToLeft)
3809 {
3810 m_xAdvance -= ((glyphAdjustments.xAdvance * currentElementScale + (m_currentFontAsset.normalSpacingOffset + characterSpacingAdjustment + boldSpacingAdjustment) * currentEmScale + m_cSpacing) * (1 - m_charWidthAdjDelta));
3811
3812 if (isWhiteSpace || charCode == 0x200B)
3813 m_xAdvance -= m_wordSpacing * currentEmScale;
3814 }
3815 else
3816 {
3817 m_xAdvance += ((currentGlyphMetrics.horizontalAdvance * m_FXScale.x + glyphAdjustments.xAdvance) * currentElementScale + (m_currentFontAsset.normalSpacingOffset + characterSpacingAdjustment + boldSpacingAdjustment) * currentEmScale + m_cSpacing) * (1 - m_charWidthAdjDelta);
3818
3819 if (isWhiteSpace || charCode == 0x200B)
3820 m_xAdvance += m_wordSpacing * currentEmScale;
3821 }
3822
3823 // Store xAdvance information
3824 m_textInfo.characterInfo[m_characterCount].xAdvance = m_xAdvance;
3825 k_ComputeCharacterAdvanceMarker.End();
3826 #endregion Tabulation & Stops
3827
3828
3829 // Handle Carriage Return
3830 #region Carriage Return
3831 if (charCode == 13)
3832 {
3833 k_HandleCarriageReturnMarker.Begin();
3834 m_xAdvance = 0 + tag_Indent;
3835 k_HandleCarriageReturnMarker.End();
3836 }
3837 #endregion Carriage Return
3838
3839
3840 // Tracking of text overflow page mode
3841 #region Save PageInfo
3842 k_SavePageInfoMarker.Begin();
3843 if (m_overflowMode == TextOverflowModes.Page && charCode != 10 && charCode != 11 && charCode != 13 && charCode != 0x2028 && charCode != 0x2029)
3844 {
3845 // Check if we need to increase allocations for the pageInfo array.
3846 if (m_pageNumber + 1 > m_textInfo.pageInfo.Length)
3847 TMP_TextInfo.Resize(ref m_textInfo.pageInfo, m_pageNumber + 1, true);
3848
3849 m_textInfo.pageInfo[m_pageNumber].ascender = m_PageAscender;
3850 m_textInfo.pageInfo[m_pageNumber].descender = m_ElementDescender < m_textInfo.pageInfo[m_pageNumber].descender
3851 ? m_ElementDescender
3852 : m_textInfo.pageInfo[m_pageNumber].descender;
3853
3854 if (m_isNewPage)
3855 {
3856 m_isNewPage = false;
3857 m_textInfo.pageInfo[m_pageNumber].firstCharacterIndex = m_characterCount;
3858 }
3859
3860 // Last index
3861 m_textInfo.pageInfo[m_pageNumber].lastCharacterIndex = m_characterCount;
3862 }
3863 k_SavePageInfoMarker.End();
3864 #endregion Save PageInfo
3865
3866
3867 // Handle Line Spacing Adjustments + Word Wrapping & special case for last line.
3868 #region Check for Line Feed and Last Character
3869 if (charCode == 10 || charCode == 11 || charCode == 0x03 || charCode == 0x2028 || charCode == 0x2029 || (charCode == 0x2D && isInjectedCharacter) || m_characterCount == totalCharacterCount - 1)
3870 {
3871 k_HandleLineTerminationMarker.Begin();
3872
3873 // Adjust current line spacing (if necessary) before inserting new line
3874 float baselineAdjustmentDelta = m_maxLineAscender - m_startOfLineAscender;
3875 if (m_lineOffset > 0 && Math.Abs(baselineAdjustmentDelta) > 0.01f && m_IsDrivenLineSpacing == false && !m_isNewPage)
3876 {
3877 //Debug.Log("Line Feed - Adjusting Line Spacing on line #" + m_lineNumber);
3878 AdjustLineOffset(m_firstCharacterOfLine, m_characterCount, baselineAdjustmentDelta);
3879 m_ElementDescender -= baselineAdjustmentDelta;
3880 m_lineOffset += baselineAdjustmentDelta;
3881
3882 // Adjust saved ellipsis state only if we are adjusting the same line number
3883 if (m_SavedEllipsisState.lineNumber == m_lineNumber)
3884 {
3885 m_SavedEllipsisState = m_EllipsisInsertionCandidateStack.Pop();
3886 m_SavedEllipsisState.startOfLineAscender += baselineAdjustmentDelta;
3887 m_SavedEllipsisState.lineOffset += baselineAdjustmentDelta;
3888 m_EllipsisInsertionCandidateStack.Push(m_SavedEllipsisState);
3889 }
3890 }
3891 m_isNewPage = false;
3892
3893 // Calculate lineAscender & make sure if last character is superscript or subscript that we check that as well.
3894 float lineAscender = m_maxLineAscender - m_lineOffset;
3895 float lineDescender = m_maxLineDescender - m_lineOffset;
3896
3897 // Update maxDescender and maxVisibleDescender
3898 m_ElementDescender = m_ElementDescender < lineDescender ? m_ElementDescender : lineDescender;
3899 if (!isMaxVisibleDescenderSet)
3900 maxVisibleDescender = m_ElementDescender;
3901
3902 if (m_useMaxVisibleDescender && (m_characterCount >= m_maxVisibleCharacters || m_lineNumber >= m_maxVisibleLines))
3903 isMaxVisibleDescenderSet = true;
3904
3905 // Save Line Information
3906 m_textInfo.lineInfo[m_lineNumber].firstCharacterIndex = m_firstCharacterOfLine;
3907 m_textInfo.lineInfo[m_lineNumber].firstVisibleCharacterIndex = m_firstVisibleCharacterOfLine = m_firstCharacterOfLine > m_firstVisibleCharacterOfLine ? m_firstCharacterOfLine : m_firstVisibleCharacterOfLine;
3908 m_textInfo.lineInfo[m_lineNumber].lastCharacterIndex = m_lastCharacterOfLine = m_characterCount;
3909 m_textInfo.lineInfo[m_lineNumber].lastVisibleCharacterIndex = m_lastVisibleCharacterOfLine = m_lastVisibleCharacterOfLine < m_firstVisibleCharacterOfLine ? m_firstVisibleCharacterOfLine : m_lastVisibleCharacterOfLine;
3910
3911 m_textInfo.lineInfo[m_lineNumber].characterCount = m_textInfo.lineInfo[m_lineNumber].lastCharacterIndex - m_textInfo.lineInfo[m_lineNumber].firstCharacterIndex + 1;
3912 m_textInfo.lineInfo[m_lineNumber].visibleCharacterCount = m_lineVisibleCharacterCount;
3913 m_textInfo.lineInfo[m_lineNumber].visibleSpaceCount = (m_textInfo.lineInfo[m_lineNumber].lastVisibleCharacterIndex + 1) - m_lineVisibleCharacterCount;
3914 m_textInfo.lineInfo[m_lineNumber].lineExtents.min = new Vector2(m_textInfo.characterInfo[m_firstVisibleCharacterOfLine].bottomLeft.x, lineDescender);
3915 m_textInfo.lineInfo[m_lineNumber].lineExtents.max = new Vector2(m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].topRight.x, lineAscender);
3916 m_textInfo.lineInfo[m_lineNumber].length = m_textInfo.lineInfo[m_lineNumber].lineExtents.max.x - (padding * currentElementScale);
3917 m_textInfo.lineInfo[m_lineNumber].width = widthOfTextArea;
3918
3919 if (m_textInfo.lineInfo[m_lineNumber].characterCount == 1)
3920 m_textInfo.lineInfo[m_lineNumber].alignment = m_lineJustification;
3921
3922 float maxAdvanceOffset = ((m_currentFontAsset.normalSpacingOffset + characterSpacingAdjustment + boldSpacingAdjustment) * currentEmScale + m_cSpacing) * (1 - m_charWidthAdjDelta);
3923 if (m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].isVisible)
3924 m_textInfo.lineInfo[m_lineNumber].maxAdvance = m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].xAdvance + (m_isRightToLeft ? maxAdvanceOffset : - maxAdvanceOffset);
3925 else
3926 m_textInfo.lineInfo[m_lineNumber].maxAdvance = m_textInfo.characterInfo[m_lastCharacterOfLine].xAdvance + (m_isRightToLeft ? maxAdvanceOffset : - maxAdvanceOffset);
3927
3928 m_textInfo.lineInfo[m_lineNumber].baseline = 0 - m_lineOffset;
3929 m_textInfo.lineInfo[m_lineNumber].ascender = lineAscender;
3930 m_textInfo.lineInfo[m_lineNumber].descender = lineDescender;
3931 m_textInfo.lineInfo[m_lineNumber].lineHeight = lineAscender - lineDescender + lineGap * baseScale;
3932
3933 // Add new line if not last line or character.
3934 if (charCode == 10 || charCode == 11 || (charCode == 0x2D && isInjectedCharacter) || charCode == 0x2028 || charCode == 0x2029)
3935 {
3936 // Store the state of the line before starting on the new line.
3937 SaveWordWrappingState(ref m_SavedLineState, i, m_characterCount);
3938
3939 m_lineNumber += 1;
3940 isStartOfNewLine = true;
3941 ignoreNonBreakingSpace = false;
3942 isFirstWordOfLine = true;
3943
3944 m_firstCharacterOfLine = m_characterCount + 1;
3945 m_lineVisibleCharacterCount = 0;
3946 m_lineVisibleSpaceCount = 0;
3947
3948 // Check to make sure Array is large enough to hold a new line.
3949 if (m_lineNumber >= m_textInfo.lineInfo.Length)
3950 ResizeLineExtents(m_lineNumber);
3951
3952 float lastVisibleAscender = m_textInfo.characterInfo[m_characterCount].adjustedAscender;
3953
3954 // Apply Line Spacing with special handling for VT char(11)
3955 if (m_lineHeight == TMP_Math.FLOAT_UNSET)
3956 {
3957 float lineOffsetDelta = 0 - m_maxLineDescender + lastVisibleAscender + (lineGap + m_lineSpacingDelta) * baseScale + (m_lineSpacing + (charCode == 10 || charCode == 0x2029 ? m_paragraphSpacing : 0)) * currentEmScale;
3958 m_lineOffset += lineOffsetDelta;
3959 m_IsDrivenLineSpacing = false;
3960 }
3961 else
3962 {
3963 m_lineOffset += m_lineHeight + (m_lineSpacing + (charCode == 10 || charCode == 0x2029 ? m_paragraphSpacing : 0)) * currentEmScale;
3964 m_IsDrivenLineSpacing = true;
3965 }
3966
3967 m_maxLineAscender = k_LargeNegativeFloat;
3968 m_maxLineDescender = k_LargePositiveFloat;
3969 m_startOfLineAscender = lastVisibleAscender;
3970
3971 m_xAdvance = 0 + tag_LineIndent + tag_Indent;
3972
3973 SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount);
3974 SaveWordWrappingState(ref m_SavedLastValidState, i, m_characterCount);
3975
3976 m_characterCount += 1;
3977
3978 k_HandleLineTerminationMarker.End();
3979
3980 continue;
3981 }
3982
3983 // If End of Text
3984 if (charCode == 0x03)
3985 i = m_TextProcessingArray.Length;
3986
3987 k_HandleLineTerminationMarker.End();
3988 }
3989 #endregion Check for Linefeed or Last Character
3990
3991
3992 // Track extents of the text
3993 #region Track Text Extents
3994 k_SaveTextExtentMarker.Begin();
3995 // Determine the bounds of the Mesh.
3996 if (m_textInfo.characterInfo[m_characterCount].isVisible)
3997 {
3998 m_meshExtents.min.x = Mathf.Min(m_meshExtents.min.x, m_textInfo.characterInfo[m_characterCount].bottomLeft.x);
3999 m_meshExtents.min.y = Mathf.Min(m_meshExtents.min.y, m_textInfo.characterInfo[m_characterCount].bottomLeft.y);
4000
4001 m_meshExtents.max.x = Mathf.Max(m_meshExtents.max.x, m_textInfo.characterInfo[m_characterCount].topRight.x);
4002 m_meshExtents.max.y = Mathf.Max(m_meshExtents.max.y, m_textInfo.characterInfo[m_characterCount].topRight.y);
4003
4004 //m_meshExtents.min = new Vector2(Mathf.Min(m_meshExtents.min.x, m_textInfo.characterInfo[m_characterCount].bottomLeft.x), Mathf.Min(m_meshExtents.min.y, m_textInfo.characterInfo[m_characterCount].bottomLeft.y));
4005 //m_meshExtents.max = new Vector2(Mathf.Max(m_meshExtents.max.x, m_textInfo.characterInfo[m_characterCount].topRight.x), Mathf.Max(m_meshExtents.max.y, m_textInfo.characterInfo[m_characterCount].topRight.y));
4006 }
4007 k_SaveTextExtentMarker.End();
4008 #endregion Track Text Extents
4009
4010
4011 // Save State of Mesh Creation for handling of Word Wrapping
4012 #region Save Word Wrapping State
4013 if ((m_TextWrappingMode != TextWrappingModes.NoWrap && m_TextWrappingMode != TextWrappingModes.PreserveWhitespaceNoWrap) || m_overflowMode == TextOverflowModes.Truncate || m_overflowMode == TextOverflowModes.Ellipsis || m_overflowMode == TextOverflowModes.Linked)
4014 {
4015 k_SaveProcessingStatesMarker.Begin();
4016
4017 bool shouldSaveHardLineBreak = false;
4018 bool shouldSaveSoftLineBreak = false;
4019
4020 if ((isWhiteSpace || charCode == 0x200B || charCode == 0x2D || charCode == 0xAD) && (!m_isNonBreakingSpace || ignoreNonBreakingSpace) && charCode != 0xA0 && charCode != 0x2007 && charCode != 0x2011 && charCode != 0x202F && charCode != 0x2060)
4021 {
4022 // Ignore Hyphen (0x2D) when preceded by a whitespace
4023 if ((charCode == 0x2D && m_characterCount > 0 && char.IsWhiteSpace(m_textInfo.characterInfo[m_characterCount - 1].character)) == false)
4024 {
4025 isFirstWordOfLine = false;
4026 shouldSaveHardLineBreak = true;
4027
4028 // Reset soft line breaking point since we now have a valid hard break point.
4029 m_SavedSoftLineBreakState.previous_WordBreak = -1;
4030 }
4031 }
4032 // Handling for East Asian scripts
4033 else if (m_isNonBreakingSpace == false && (TMP_TextParsingUtilities.IsHangul(charCode) && TMP_Settings.useModernHangulLineBreakingRules == false || TMP_TextParsingUtilities.IsCJK(charCode)))
4034 {
4035 bool isCurrentLeadingCharacter = TMP_Settings.linebreakingRules.leadingCharacters.Contains(charCode);
4036 bool isNextFollowingCharacter = m_characterCount < totalCharacterCount - 1 && TMP_Settings.linebreakingRules.followingCharacters.Contains(m_textInfo.characterInfo[m_characterCount + 1].character);
4037
4038 if (isCurrentLeadingCharacter == false)
4039 {
4040 if (isNextFollowingCharacter == false)
4041 {
4042 isFirstWordOfLine = false;
4043 shouldSaveHardLineBreak = true;
4044 }
4045
4046 if (isFirstWordOfLine)
4047 {
4048 // Special handling for non-breaking space and soft line breaks
4049 if (isWhiteSpace)
4050 shouldSaveSoftLineBreak = true;
4051
4052 shouldSaveHardLineBreak = true;
4053 }
4054 }
4055 else
4056 {
4057 if (isFirstWordOfLine && isFirstCharacterOfLine)
4058 {
4059 // Special handling for non-breaking space and soft line breaks
4060 if (isWhiteSpace)
4061 shouldSaveSoftLineBreak = true;
4062
4063 shouldSaveHardLineBreak = true;
4064 }
4065 }
4066 }
4067 // Special handling for Latin characters followed by a CJK character.
4068 else if (m_isNonBreakingSpace == false && m_characterCount + 1 < totalCharacterCount && TMP_TextParsingUtilities.IsCJK(m_textInfo.characterInfo[m_characterCount + 1].character))
4069 {
4070 shouldSaveHardLineBreak = true;
4071 }
4072 else if (isFirstWordOfLine)
4073 {
4074 // Special handling for non-breaking space and soft line breaks
4075 if (isWhiteSpace && charCode != 0xA0 || (charCode == 0xAD && isSoftHyphenIgnored == false))
4076 shouldSaveSoftLineBreak = true;
4077
4078 shouldSaveHardLineBreak = true;
4079 }
4080
4081 // Save potential Hard lines break
4082 if (shouldSaveHardLineBreak)
4083 SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount);
4084
4085 // Save potential Soft line break
4086 if (shouldSaveSoftLineBreak)
4087 SaveWordWrappingState(ref m_SavedSoftLineBreakState, i, m_characterCount);
4088
4089 k_SaveProcessingStatesMarker.End();
4090 }
4091 #endregion Save Word Wrapping State
4092
4093 // Consider only saving state on base glyphs
4094 SaveWordWrappingState(ref m_SavedLastValidState, i, m_characterCount);
4095
4096 m_characterCount += 1;
4097 }
4098
4099 // Check Auto Sizing and increase font size to fill text container.
4100 #region Check Auto-Sizing (Upper Font Size Bounds)
4101 fontSizeDelta = m_maxFontSize - m_minFontSize;
4102 if (/* !m_isCharacterWrappingEnabled && */ m_enableAutoSizing && fontSizeDelta > 0.051f && m_fontSize < m_fontSizeMax && m_AutoSizeIterationCount < m_AutoSizeMaxIterationCount)
4103 {
4104 // Reset character width adjustment delta
4105 if (m_charWidthAdjDelta < m_charWidthMaxAdj / 100)
4106 m_charWidthAdjDelta = 0;
4107
4108 m_minFontSize = m_fontSize;
4109
4110 float sizeDelta = Mathf.Max((m_maxFontSize - m_fontSize) / 2, 0.05f);
4111 m_fontSize += sizeDelta;
4112 m_fontSize = Mathf.Min((int)(m_fontSize * 20 + 0.5f) / 20f, m_fontSizeMax);
4113
4114 //Debug.Log("[" + m_AutoSizeIterationCount + "] Increasing Point Size from [" + m_minFontSize.ToString("f3") + "] to [" + m_fontSize.ToString("f3") + "] with delta of [" + sizeDelta.ToString("f3") + "].");
4115 k_GenerateTextPhaseIMarker.End();
4116 k_GenerateTextMarker.End();
4117 return;
4118 }
4119 #endregion End Auto-sizing Check
4120
4121 m_IsAutoSizePointSizeSet = true;
4122
4123 if (m_AutoSizeIterationCount >= m_AutoSizeMaxIterationCount)
4124 Debug.Log("Auto Size Iteration Count: " + m_AutoSizeIterationCount + ". Final Point Size: " + m_fontSize);
4125
4126 // If there are no visible characters or only character is End of Text (0x03)... no need to continue
4127 if (m_characterCount == 0 || (m_characterCount == 1 && charCode == 0x03))
4128 {
4129 ClearMesh(true);
4130
4131 // Event indicating the text has been regenerated.
4132 TMPro_EventManager.ON_TEXT_CHANGED(this);
4133 k_GenerateTextPhaseIMarker.End();
4134 k_GenerateTextMarker.End();
4135 return;
4136 }
4137
4138 // End Sampling of Phase I
4139 k_GenerateTextPhaseIMarker.End();
4140
4141 // *** PHASE II of Text Generation ***
4142 k_GenerateTextPhaseIIMarker.Begin();
4143 int last_vert_index = m_materialReferences[m_Underline.materialIndex].referenceCount * 4;
4144
4145 // Partial clear of the vertices array to mark unused vertices as degenerate.
4146 m_textInfo.meshInfo[0].Clear(false);
4147
4148 // Handle Text Alignment
4149 #region Text Vertical Alignment
4150 Vector3 anchorOffset = Vector3.zero;
4151 Vector3[] corners = m_RectTransformCorners; // GetTextContainerLocalCorners();
4152
4153 // Handle Vertical Text Alignment
4154 switch (m_VerticalAlignment)
4155 {
4156 // Top Vertically
4157 case VerticalAlignmentOptions.Top:
4158 if (m_overflowMode != TextOverflowModes.Page)
4159 anchorOffset = corners[1] + new Vector3(0 + margins.x, 0 - m_maxTextAscender - margins.y, 0);
4160 else
4161 anchorOffset = corners[1] + new Vector3(0 + margins.x, 0 - m_textInfo.pageInfo[pageToDisplay].ascender - margins.y, 0);
4162 break;
4163
4164 // Middle Vertically
4165 case VerticalAlignmentOptions.Middle:
4166 if (m_overflowMode != TextOverflowModes.Page)
4167 anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0 - (m_maxTextAscender + margins.y + maxVisibleDescender - margins.w) / 2, 0);
4168 else
4169 anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0 - (m_textInfo.pageInfo[pageToDisplay].ascender + margins.y + m_textInfo.pageInfo[pageToDisplay].descender - margins.w) / 2, 0);
4170 break;
4171
4172 // Bottom Vertically
4173 case VerticalAlignmentOptions.Bottom:
4174 if (m_overflowMode != TextOverflowModes.Page)
4175 anchorOffset = corners[0] + new Vector3(0 + margins.x, 0 - maxVisibleDescender + margins.w, 0);
4176 else
4177 anchorOffset = corners[0] + new Vector3(0 + margins.x, 0 - m_textInfo.pageInfo[pageToDisplay].descender + margins.w, 0);
4178 break;
4179
4180 // Baseline Vertically
4181 case VerticalAlignmentOptions.Baseline:
4182 anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0, 0);
4183 break;
4184
4185 // Midline Vertically
4186 case VerticalAlignmentOptions.Geometry:
4187 anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0 - (m_meshExtents.max.y + margins.y + m_meshExtents.min.y - margins.w) / 2, 0);
4188 break;
4189
4190 // Capline Vertically
4191 case VerticalAlignmentOptions.Capline:
4192 anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0 - (m_maxCapHeight - margins.y - margins.w) / 2, 0);
4193 break;
4194 }
4195 #endregion
4196
4197 // Initialization for Second Pass
4198 Vector3 justificationOffset = Vector3.zero;
4199 Vector3 offset = Vector3.zero;
4200 // int vert_index_X4 = 0;
4201 // int sprite_index_X4 = 0;
4202
4203 int wordCount = 0;
4204 int lineCount = 0;
4205 int lastLine = 0;
4206 bool isFirstSeperator = false;
4207
4208 bool isStartOfWord = false;
4209 int wordFirstChar = 0;
4210 int wordLastChar = 0;
4211
4212 // Second Pass : Line Justification, UV Mapping, Character & Line Visibility & more.
4213 float lossyScale = m_previousLossyScaleY = this.transform.lossyScale.y;
4214
4215 Color32 underlineColor = Color.white;
4216 Color32 strikethroughColor = Color.white;
4217 HighlightState highlightState = new HighlightState(new Color32(255, 255, 0, 64), TMP_Offset.zero);
4218 float xScale = 0;
4219 float xScaleMax = 0;
4220 float underlineStartScale = 0;
4221 float underlineEndScale = 0;
4222 float underlineMaxScale = 0;
4223 float underlineBaseLine = k_LargePositiveFloat;
4224 int lastPage = 0;
4225
4226 float strikethroughPointSize = 0;
4227 float strikethroughScale = 0;
4228 float strikethroughBaseline = 0;
4229
4230 TMP_CharacterInfo[] characterInfos = m_textInfo.characterInfo;
4231 #region Handle Line Justification & UV Mapping & Character Visibility & More
4232 for (int i = 0; i < m_characterCount; i++)
4233 {
4234 TMP_FontAsset currentFontAsset = characterInfos[i].fontAsset;
4235
4236 char unicode = characterInfos[i].character;
4237 bool isWhiteSpace = char.IsWhiteSpace(unicode);
4238
4239 int currentLine = characterInfos[i].lineNumber;
4240 TMP_LineInfo lineInfo = m_textInfo.lineInfo[currentLine];
4241 lineCount = currentLine + 1;
4242
4243 HorizontalAlignmentOptions lineAlignment = lineInfo.alignment;
4244
4245 // Process Line Justification
4246 #region Handle Line Justification
4247 switch (lineAlignment)
4248 {
4249 case HorizontalAlignmentOptions.Left:
4250 if (!m_isRightToLeft)
4251 justificationOffset = new Vector3(0 + lineInfo.marginLeft, 0, 0);
4252 else
4253 justificationOffset = new Vector3(0 - lineInfo.maxAdvance, 0, 0);
4254 break;
4255
4256 case HorizontalAlignmentOptions.Center:
4257 justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width / 2 - lineInfo.maxAdvance / 2, 0, 0);
4258 break;
4259
4260 case HorizontalAlignmentOptions.Geometry:
4261 justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width / 2 - (lineInfo.lineExtents.min.x + lineInfo.lineExtents.max.x) / 2, 0, 0);
4262 break;
4263
4264 case HorizontalAlignmentOptions.Right:
4265 if (!m_isRightToLeft)
4266 justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width - lineInfo.maxAdvance, 0, 0);
4267 else
4268 justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width, 0, 0);
4269 break;
4270
4271 case HorizontalAlignmentOptions.Justified:
4272 case HorizontalAlignmentOptions.Flush:
4273 // Skip Zero Width Characters and spaces outside of the margins.
4274 if (i > lineInfo.lastVisibleCharacterIndex || unicode == 0x0A || unicode == 0xAD || unicode == 0x200B || unicode == 0x2060 || unicode == 0x03) break;
4275
4276 char lastCharOfCurrentLine = characterInfos[lineInfo.lastCharacterIndex].character;
4277
4278 bool isFlush = (lineAlignment & HorizontalAlignmentOptions.Flush) == HorizontalAlignmentOptions.Flush;
4279
4280 // In Justified mode, all lines are justified except the last one.
4281 // In Flush mode, all lines are justified.
4282 if (char.IsControl(lastCharOfCurrentLine) == false && currentLine < m_lineNumber || isFlush || lineInfo.maxAdvance > lineInfo.width)
4283 {
4284 // First character of each line.
4285 if (currentLine != lastLine || i == 0 || i == m_firstVisibleCharacter)
4286 {
4287 if (!m_isRightToLeft)
4288 justificationOffset = new Vector3(lineInfo.marginLeft, 0, 0);
4289 else
4290 justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width, 0, 0);
4291
4292 if (char.IsSeparator(unicode))
4293 isFirstSeperator = true;
4294 else
4295 isFirstSeperator = false;
4296 }
4297 else
4298 {
4299 float gap = !m_isRightToLeft ? lineInfo.width - lineInfo.maxAdvance : lineInfo.width + lineInfo.maxAdvance;
4300 int visibleCount = lineInfo.visibleCharacterCount - 1 + lineInfo.controlCharacterCount;
4301 int spaces = lineInfo.spaceCount - lineInfo.controlCharacterCount;
4302
4303 if (isFirstSeperator) { spaces -= 1; visibleCount += 1; }
4304
4305 float ratio = spaces > 0 ? m_wordWrappingRatios : 1;
4306
4307 if (spaces < 1) spaces = 1;
4308
4309 if (unicode != 0xA0 && (unicode == 9 || char.IsSeparator(unicode)))
4310 {
4311 if (!m_isRightToLeft)
4312 justificationOffset += new Vector3(gap * (1 - ratio) / spaces, 0, 0);
4313 else
4314 justificationOffset -= new Vector3(gap * (1 - ratio) / spaces, 0, 0);
4315 }
4316 else
4317 {
4318 if (!m_isRightToLeft)
4319 justificationOffset += new Vector3(gap * ratio / visibleCount, 0, 0);
4320 else
4321 justificationOffset -= new Vector3(gap * ratio / visibleCount, 0, 0);
4322 }
4323 }
4324 }
4325 else
4326 {
4327 if (!m_isRightToLeft)
4328 justificationOffset = new Vector3(lineInfo.marginLeft, 0, 0); // Keep last line left justified.
4329 else
4330 justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width, 0, 0); // Keep last line right justified.
4331 }
4332 //Debug.Log("Char [" + (char)charCode + "] Code:" + charCode + " Line # " + currentLine + " Offset:" + justificationOffset + " # Spaces:" + lineInfo.spaceCount + " # Characters:" + lineInfo.characterCount);
4333 break;
4334 }
4335 #endregion End Text Justification
4336
4337 offset = anchorOffset + justificationOffset;
4338
4339 // Handle UV2 mapping options and packing of scale information into UV2.
4340 #region Handling of UV2 mapping & Scale packing
4341 bool isCharacterVisible = characterInfos[i].isVisible;
4342 if (isCharacterVisible)
4343 {
4344 TMP_TextElementType elementType = characterInfos[i].elementType;
4345 switch (elementType)
4346 {
4347 // CHARACTERS
4348 case TMP_TextElementType.Character:
4349 Extents lineExtents = lineInfo.lineExtents;
4350 float uvOffset = (m_uvLineOffset * currentLine) % 1; // + m_uvOffset.x;
4351
4352 // Setup UV2 based on Character Mapping Options Selected
4353 #region Handle UV Mapping Options
4354 switch (m_horizontalMapping)
4355 {
4356 case TextureMappingOptions.Character:
4357 characterInfos[i].vertex_BL.uv2.x = 0; //+ m_uvOffset.x;
4358 characterInfos[i].vertex_TL.uv2.x = 0; //+ m_uvOffset.x;
4359 characterInfos[i].vertex_TR.uv2.x = 1; //+ m_uvOffset.x;
4360 characterInfos[i].vertex_BR.uv2.x = 1; //+ m_uvOffset.x;
4361 break;
4362
4363 case TextureMappingOptions.Line:
4364 if (m_textAlignment != TextAlignmentOptions.Justified)
4365 {
4366 characterInfos[i].vertex_BL.uv2.x = (characterInfos[i].vertex_BL.position.x - lineExtents.min.x) / (lineExtents.max.x - lineExtents.min.x) + uvOffset;
4367 characterInfos[i].vertex_TL.uv2.x = (characterInfos[i].vertex_TL.position.x - lineExtents.min.x) / (lineExtents.max.x - lineExtents.min.x) + uvOffset;
4368 characterInfos[i].vertex_TR.uv2.x = (characterInfos[i].vertex_TR.position.x - lineExtents.min.x) / (lineExtents.max.x - lineExtents.min.x) + uvOffset;
4369 characterInfos[i].vertex_BR.uv2.x = (characterInfos[i].vertex_BR.position.x - lineExtents.min.x) / (lineExtents.max.x - lineExtents.min.x) + uvOffset;
4370 break;
4371 }
4372 else // Special Case if Justified is used in Line Mode.
4373 {
4374 characterInfos[i].vertex_BL.uv2.x = (characterInfos[i].vertex_BL.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset;
4375 characterInfos[i].vertex_TL.uv2.x = (characterInfos[i].vertex_TL.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset;
4376 characterInfos[i].vertex_TR.uv2.x = (characterInfos[i].vertex_TR.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset;
4377 characterInfos[i].vertex_BR.uv2.x = (characterInfos[i].vertex_BR.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset;
4378 break;
4379 }
4380
4381 case TextureMappingOptions.Paragraph:
4382 characterInfos[i].vertex_BL.uv2.x = (characterInfos[i].vertex_BL.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset;
4383 characterInfos[i].vertex_TL.uv2.x = (characterInfos[i].vertex_TL.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset;
4384 characterInfos[i].vertex_TR.uv2.x = (characterInfos[i].vertex_TR.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset;
4385 characterInfos[i].vertex_BR.uv2.x = (characterInfos[i].vertex_BR.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset;
4386 break;
4387
4388 case TextureMappingOptions.MatchAspect:
4389
4390 switch (m_verticalMapping)
4391 {
4392 case TextureMappingOptions.Character:
4393 characterInfos[i].vertex_BL.uv2.y = 0; // + m_uvOffset.y;
4394 characterInfos[i].vertex_TL.uv2.y = 1; // + m_uvOffset.y;
4395 characterInfos[i].vertex_TR.uv2.y = 0; // + m_uvOffset.y;
4396 characterInfos[i].vertex_BR.uv2.y = 1; // + m_uvOffset.y;
4397 break;
4398
4399 case TextureMappingOptions.Line:
4400 characterInfos[i].vertex_BL.uv2.y = (characterInfos[i].vertex_BL.position.y - lineExtents.min.y) / (lineExtents.max.y - lineExtents.min.y) + uvOffset;
4401 characterInfos[i].vertex_TL.uv2.y = (characterInfos[i].vertex_TL.position.y - lineExtents.min.y) / (lineExtents.max.y - lineExtents.min.y) + uvOffset;
4402 characterInfos[i].vertex_TR.uv2.y = characterInfos[i].vertex_BL.uv2.y;
4403 characterInfos[i].vertex_BR.uv2.y = characterInfos[i].vertex_TL.uv2.y;
4404 break;
4405
4406 case TextureMappingOptions.Paragraph:
4407 characterInfos[i].vertex_BL.uv2.y = (characterInfos[i].vertex_BL.position.y - m_meshExtents.min.y) / (m_meshExtents.max.y - m_meshExtents.min.y) + uvOffset;
4408 characterInfos[i].vertex_TL.uv2.y = (characterInfos[i].vertex_TL.position.y - m_meshExtents.min.y) / (m_meshExtents.max.y - m_meshExtents.min.y) + uvOffset;
4409 characterInfos[i].vertex_TR.uv2.y = characterInfos[i].vertex_BL.uv2.y;
4410 characterInfos[i].vertex_BR.uv2.y = characterInfos[i].vertex_TL.uv2.y;
4411 break;
4412
4413 case TextureMappingOptions.MatchAspect:
4414 Debug.Log("ERROR: Cannot Match both Vertical & Horizontal.");
4415 break;
4416 }
4417
4418 //float xDelta = 1 - (_uv2s[vert_index + 0].y * textMeshCharacterInfo[i].AspectRatio); // Left aligned
4419 float xDelta = (1 - ((characterInfos[i].vertex_BL.uv2.y + characterInfos[i].vertex_TL.uv2.y) * characterInfos[i].aspectRatio)) / 2; // Center of Rectangle
4420
4421 characterInfos[i].vertex_BL.uv2.x = (characterInfos[i].vertex_BL.uv2.y * characterInfos[i].aspectRatio) + xDelta + uvOffset;
4422 characterInfos[i].vertex_TL.uv2.x = characterInfos[i].vertex_BL.uv2.x;
4423 characterInfos[i].vertex_TR.uv2.x = (characterInfos[i].vertex_TL.uv2.y * characterInfos[i].aspectRatio) + xDelta + uvOffset;
4424 characterInfos[i].vertex_BR.uv2.x = characterInfos[i].vertex_TR.uv2.x;
4425 break;
4426 }
4427
4428 switch (m_verticalMapping)
4429 {
4430 case TextureMappingOptions.Character:
4431 characterInfos[i].vertex_BL.uv2.y = 0; // + m_uvOffset.y;
4432 characterInfos[i].vertex_TL.uv2.y = 1; // + m_uvOffset.y;
4433 characterInfos[i].vertex_TR.uv2.y = 1; // + m_uvOffset.y;
4434 characterInfos[i].vertex_BR.uv2.y = 0; // + m_uvOffset.y;
4435 break;
4436
4437 case TextureMappingOptions.Line:
4438 characterInfos[i].vertex_BL.uv2.y = (characterInfos[i].vertex_BL.position.y - lineInfo.descender) / (lineInfo.ascender - lineInfo.descender); // + m_uvOffset.y;
4439 characterInfos[i].vertex_TL.uv2.y = (characterInfos[i].vertex_TL.position.y - lineInfo.descender) / (lineInfo.ascender - lineInfo.descender); // + m_uvOffset.y;
4440 characterInfos[i].vertex_TR.uv2.y = characterInfos[i].vertex_TL.uv2.y;
4441 characterInfos[i].vertex_BR.uv2.y = characterInfos[i].vertex_BL.uv2.y;
4442 break;
4443
4444 case TextureMappingOptions.Paragraph:
4445 characterInfos[i].vertex_BL.uv2.y = (characterInfos[i].vertex_BL.position.y - m_meshExtents.min.y) / (m_meshExtents.max.y - m_meshExtents.min.y); // + m_uvOffset.y;
4446 characterInfos[i].vertex_TL.uv2.y = (characterInfos[i].vertex_TL.position.y - m_meshExtents.min.y) / (m_meshExtents.max.y - m_meshExtents.min.y); // + m_uvOffset.y;
4447 characterInfos[i].vertex_TR.uv2.y = characterInfos[i].vertex_TL.uv2.y;
4448 characterInfos[i].vertex_BR.uv2.y = characterInfos[i].vertex_BL.uv2.y;
4449 break;
4450
4451 case TextureMappingOptions.MatchAspect:
4452 float yDelta = (1 - ((characterInfos[i].vertex_BL.uv2.x + characterInfos[i].vertex_TR.uv2.x) / characterInfos[i].aspectRatio)) / 2; // Center of Rectangle
4453
4454 characterInfos[i].vertex_BL.uv2.y = yDelta + (characterInfos[i].vertex_BL.uv2.x / characterInfos[i].aspectRatio); // + m_uvOffset.y;
4455 characterInfos[i].vertex_TL.uv2.y = yDelta + (characterInfos[i].vertex_TR.uv2.x / characterInfos[i].aspectRatio); // + m_uvOffset.y;
4456 characterInfos[i].vertex_BR.uv2.y = characterInfos[i].vertex_BL.uv2.y;
4457 characterInfos[i].vertex_TR.uv2.y = characterInfos[i].vertex_TL.uv2.y;
4458 break;
4459 }
4460 #endregion
4461
4462 // Pack UV's so that we can pass Xscale needed for Shader to maintain 1:1 ratio.
4463 #region Pack Scale into UV2
4464 xScale = characterInfos[i].scale * Mathf.Abs(lossyScale) * (1 - m_charWidthAdjDelta);
4465 if (!characterInfos[i].isUsingAlternateTypeface && (characterInfos[i].style & FontStyles.Bold) == FontStyles.Bold) xScale *= -1;
4466
4467 // Set SDF Scale
4468 characterInfos[i].vertex_BL.uv.w = xScale;
4469 characterInfos[i].vertex_TL.uv.w = xScale;
4470 characterInfos[i].vertex_TR.uv.w = xScale;
4471 characterInfos[i].vertex_BR.uv.w = xScale;
4472 #endregion
4473 break;
4474
4475 // SPRITES
4476 case TMP_TextElementType.Sprite:
4477 // Nothing right now
4478 break;
4479 }
4480
4481 // Handle maxVisibleCharacters, maxVisibleLines and Overflow Page Mode.
4482 #region Handle maxVisibleCharacters / maxVisibleLines / Page Mode
4483 if (i < m_maxVisibleCharacters && wordCount < m_maxVisibleWords && currentLine < m_maxVisibleLines && m_overflowMode != TextOverflowModes.Page)
4484 {
4485 characterInfos[i].vertex_BL.position += offset;
4486 characterInfos[i].vertex_TL.position += offset;
4487 characterInfos[i].vertex_TR.position += offset;
4488 characterInfos[i].vertex_BR.position += offset;
4489 }
4490 else if (i < m_maxVisibleCharacters && wordCount < m_maxVisibleWords && currentLine < m_maxVisibleLines && m_overflowMode == TextOverflowModes.Page && characterInfos[i].pageNumber == pageToDisplay)
4491 {
4492 characterInfos[i].vertex_BL.position += offset;
4493 characterInfos[i].vertex_TL.position += offset;
4494 characterInfos[i].vertex_TR.position += offset;
4495 characterInfos[i].vertex_BR.position += offset;
4496 }
4497 else
4498 {
4499 characterInfos[i].vertex_BL.position = Vector3.zero;
4500 characterInfos[i].vertex_TL.position = Vector3.zero;
4501 characterInfos[i].vertex_TR.position = Vector3.zero;
4502 characterInfos[i].vertex_BR.position = Vector3.zero;
4503 characterInfos[i].isVisible = false;
4504 }
4505 #endregion
4506
4507 if (QualitySettings.activeColorSpace == ColorSpace.Linear)
4508 m_ConvertToLinearSpace = true;
4509 else
4510 m_ConvertToLinearSpace = false;
4511
4512 // Fill Vertex Buffers for the various types of element
4513 if (elementType == TMP_TextElementType.Character)
4514 {
4515 FillCharacterVertexBuffers(i);
4516 }
4517 else if (elementType == TMP_TextElementType.Sprite)
4518 {
4519 FillSpriteVertexBuffers(i);
4520 }
4521 }
4522 #endregion
4523
4524 // Apply Alignment and Justification Offset
4525 m_textInfo.characterInfo[i].bottomLeft += offset;
4526 m_textInfo.characterInfo[i].topLeft += offset;
4527 m_textInfo.characterInfo[i].topRight += offset;
4528 m_textInfo.characterInfo[i].bottomRight += offset;
4529
4530 m_textInfo.characterInfo[i].origin += offset.x;
4531 m_textInfo.characterInfo[i].xAdvance += offset.x;
4532
4533 m_textInfo.characterInfo[i].ascender += offset.y;
4534 m_textInfo.characterInfo[i].descender += offset.y;
4535 m_textInfo.characterInfo[i].baseLine += offset.y;
4536
4537 // Update MeshExtents
4538 if (isCharacterVisible)
4539 {
4540 //m_meshExtents.min = new Vector2(Mathf.Min(m_meshExtents.min.x, m_textInfo.characterInfo[i].bottomLeft.x), Mathf.Min(m_meshExtents.min.y, m_textInfo.characterInfo[i].bottomLeft.y));
4541 //m_meshExtents.max = new Vector2(Mathf.Max(m_meshExtents.max.x, m_textInfo.characterInfo[i].topRight.x), Mathf.Max(m_meshExtents.max.y, m_textInfo.characterInfo[i].topLeft.y));
4542 }
4543
4544 // Need to recompute lineExtent to account for the offset from justification.
4545 #region Adjust lineExtents resulting from alignment offset
4546 if (currentLine != lastLine || i == m_characterCount - 1)
4547 {
4548 // Update the previous line's extents
4549 if (currentLine != lastLine)
4550 {
4551 m_textInfo.lineInfo[lastLine].baseline += offset.y;
4552 m_textInfo.lineInfo[lastLine].ascender += offset.y;
4553 m_textInfo.lineInfo[lastLine].descender += offset.y;
4554
4555 m_textInfo.lineInfo[lastLine].maxAdvance += offset.x;
4556
4557 m_textInfo.lineInfo[lastLine].lineExtents.min = new Vector2(m_textInfo.characterInfo[m_textInfo.lineInfo[lastLine].firstCharacterIndex].bottomLeft.x, m_textInfo.lineInfo[lastLine].descender);
4558 m_textInfo.lineInfo[lastLine].lineExtents.max = new Vector2(m_textInfo.characterInfo[m_textInfo.lineInfo[lastLine].lastVisibleCharacterIndex].topRight.x, m_textInfo.lineInfo[lastLine].ascender);
4559 }
4560
4561 // Update the current line's extents
4562 if (i == m_characterCount - 1)
4563 {
4564 m_textInfo.lineInfo[currentLine].baseline += offset.y;
4565 m_textInfo.lineInfo[currentLine].ascender += offset.y;
4566 m_textInfo.lineInfo[currentLine].descender += offset.y;
4567
4568 m_textInfo.lineInfo[currentLine].maxAdvance += offset.x;
4569
4570 m_textInfo.lineInfo[currentLine].lineExtents.min = new Vector2(m_textInfo.characterInfo[m_textInfo.lineInfo[currentLine].firstCharacterIndex].bottomLeft.x, m_textInfo.lineInfo[currentLine].descender);
4571 m_textInfo.lineInfo[currentLine].lineExtents.max = new Vector2(m_textInfo.characterInfo[m_textInfo.lineInfo[currentLine].lastVisibleCharacterIndex].topRight.x, m_textInfo.lineInfo[currentLine].ascender);
4572 }
4573 }
4574 #endregion
4575
4576
4577 // Track Word Count per line and for the object
4578 #region Track Word Count
4579 if (char.IsLetterOrDigit(unicode) || unicode == 0x2D || unicode == 0xAD || unicode == 0x2010 || unicode == 0x2011)
4580 {
4581 if (isStartOfWord == false)
4582 {
4583 isStartOfWord = true;
4584 wordFirstChar = i;
4585 }
4586
4587 // If last character is a word
4588 if (isStartOfWord && i == m_characterCount - 1)
4589 {
4590 int size = m_textInfo.wordInfo.Length;
4591 int index = m_textInfo.wordCount;
4592
4593 if (m_textInfo.wordCount + 1 > size)
4594 TMP_TextInfo.Resize(ref m_textInfo.wordInfo, size + 1);
4595
4596 wordLastChar = i;
4597
4598 m_textInfo.wordInfo[index].firstCharacterIndex = wordFirstChar;
4599 m_textInfo.wordInfo[index].lastCharacterIndex = wordLastChar;
4600 m_textInfo.wordInfo[index].characterCount = wordLastChar - wordFirstChar + 1;
4601 m_textInfo.wordInfo[index].textComponent = this;
4602
4603 wordCount += 1;
4604 m_textInfo.wordCount += 1;
4605 m_textInfo.lineInfo[currentLine].wordCount += 1;
4606 }
4607 }
4608 else if (isStartOfWord || i == 0 && (!char.IsPunctuation(unicode) || isWhiteSpace || unicode == 0x200B || i == m_characterCount - 1))
4609 {
4610 if (i > 0 && i < characterInfos.Length - 1 && i < m_characterCount && (unicode == 39 || unicode == 8217) && char.IsLetterOrDigit(characterInfos[i - 1].character) && char.IsLetterOrDigit(characterInfos[i + 1].character))
4611 {
4612
4613 }
4614 else
4615 {
4616 wordLastChar = i == m_characterCount - 1 && char.IsLetterOrDigit(unicode) ? i : i - 1;
4617 isStartOfWord = false;
4618
4619 int size = m_textInfo.wordInfo.Length;
4620 int index = m_textInfo.wordCount;
4621
4622 if (m_textInfo.wordCount + 1 > size)
4623 TMP_TextInfo.Resize(ref m_textInfo.wordInfo, size + 1);
4624
4625 m_textInfo.wordInfo[index].firstCharacterIndex = wordFirstChar;
4626 m_textInfo.wordInfo[index].lastCharacterIndex = wordLastChar;
4627 m_textInfo.wordInfo[index].characterCount = wordLastChar - wordFirstChar + 1;
4628 m_textInfo.wordInfo[index].textComponent = this;
4629
4630 wordCount += 1;
4631 m_textInfo.wordCount += 1;
4632 m_textInfo.lineInfo[currentLine].wordCount += 1;
4633 }
4634 }
4635 #endregion
4636
4637
4638 // Setup & Handle Underline
4639 #region Underline
4640 // NOTE: Need to figure out how underline will be handled with multiple fonts and which font will be used for the underline.
4641 bool isUnderline = (m_textInfo.characterInfo[i].style & FontStyles.Underline) == FontStyles.Underline;
4642 if (isUnderline)
4643 {
4644 bool isUnderlineVisible = true;
4645 int currentPage = m_textInfo.characterInfo[i].pageNumber;
4646 m_textInfo.characterInfo[i].underlineVertexIndex = last_vert_index;
4647
4648 if (i > m_maxVisibleCharacters || currentLine > m_maxVisibleLines || (m_overflowMode == TextOverflowModes.Page && currentPage + 1 != m_pageToDisplay))
4649 isUnderlineVisible = false;
4650
4651 // We only use the scale of visible characters.
4652 if (!isWhiteSpace && unicode != 0x200B)
4653 {
4654 underlineMaxScale = Mathf.Max(underlineMaxScale, m_textInfo.characterInfo[i].scale);
4655 xScaleMax = Mathf.Max(xScaleMax, Mathf.Abs(xScale));
4656 underlineBaseLine = Mathf.Min(currentPage == lastPage ? underlineBaseLine : k_LargePositiveFloat, m_textInfo.characterInfo[i].baseLine + font.m_FaceInfo.underlineOffset * underlineMaxScale);
4657 lastPage = currentPage; // Need to track pages to ensure we reset baseline for the new pages.
4658 }
4659
4660 if (beginUnderline == false && isUnderlineVisible == true && i <= lineInfo.lastVisibleCharacterIndex && unicode != 10 && unicode != 11 && unicode != 13)
4661 {
4662 if (i == lineInfo.lastVisibleCharacterIndex && char.IsSeparator(unicode))
4663 { }
4664 else
4665 {
4666 beginUnderline = true;
4667 underlineStartScale = m_textInfo.characterInfo[i].scale;
4668 if (underlineMaxScale == 0)
4669 {
4670 underlineMaxScale = underlineStartScale;
4671 xScaleMax = xScale;
4672 }
4673 underline_start = new Vector3(m_textInfo.characterInfo[i].bottomLeft.x, underlineBaseLine, 0);
4674 underlineColor = m_textInfo.characterInfo[i].underlineColor;
4675 }
4676 }
4677
4678 // End Underline if text only contains one character.
4679 if (beginUnderline && m_characterCount == 1)
4680 {
4681 beginUnderline = false;
4682 underline_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, underlineBaseLine, 0);
4683 underlineEndScale = m_textInfo.characterInfo[i].scale;
4684
4685 DrawUnderlineMesh(underline_start, underline_end, ref last_vert_index, underlineStartScale, underlineEndScale, underlineMaxScale, xScaleMax, underlineColor);
4686 underlineMaxScale = 0;
4687 xScaleMax = 0;
4688 underlineBaseLine = k_LargePositiveFloat;
4689 }
4690 else if (beginUnderline && (i == lineInfo.lastCharacterIndex || i >= lineInfo.lastVisibleCharacterIndex))
4691 {
4692 // Terminate underline at previous visible character if space or carriage return.
4693 if (isWhiteSpace || unicode == 0x200B)
4694 {
4695 int lastVisibleCharacterIndex = lineInfo.lastVisibleCharacterIndex;
4696 underline_end = new Vector3(m_textInfo.characterInfo[lastVisibleCharacterIndex].topRight.x, underlineBaseLine, 0);
4697 underlineEndScale = m_textInfo.characterInfo[lastVisibleCharacterIndex].scale;
4698 }
4699 else
4700 { // End underline if last character of the line.
4701 underline_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, underlineBaseLine, 0);
4702 underlineEndScale = m_textInfo.characterInfo[i].scale;
4703 }
4704
4705 beginUnderline = false;
4706 DrawUnderlineMesh(underline_start, underline_end, ref last_vert_index, underlineStartScale, underlineEndScale, underlineMaxScale, xScaleMax, underlineColor);
4707 underlineMaxScale = 0;
4708 xScaleMax = 0;
4709 underlineBaseLine = k_LargePositiveFloat;
4710 }
4711 else if (beginUnderline && !isUnderlineVisible)
4712 {
4713 beginUnderline = false;
4714 underline_end = new Vector3(m_textInfo.characterInfo[i - 1].topRight.x, underlineBaseLine, 0);
4715 underlineEndScale = m_textInfo.characterInfo[i - 1].scale;
4716
4717 DrawUnderlineMesh(underline_start, underline_end, ref last_vert_index, underlineStartScale, underlineEndScale, underlineMaxScale, xScaleMax, underlineColor);
4718 underlineMaxScale = 0;
4719 xScaleMax = 0;
4720 underlineBaseLine = k_LargePositiveFloat;
4721 }
4722 else if (beginUnderline && i < m_characterCount - 1 && !underlineColor.Compare(m_textInfo.characterInfo[i + 1].underlineColor))
4723 {
4724 // End underline if underline color has changed.
4725 beginUnderline = false;
4726 underline_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, underlineBaseLine, 0);
4727 underlineEndScale = m_textInfo.characterInfo[i].scale;
4728
4729 DrawUnderlineMesh(underline_start, underline_end, ref last_vert_index, underlineStartScale, underlineEndScale, underlineMaxScale, xScaleMax, underlineColor);
4730 underlineMaxScale = 0;
4731 xScaleMax = 0;
4732 underlineBaseLine = k_LargePositiveFloat;
4733 }
4734 }
4735 else
4736 {
4737 // End Underline
4738 if (beginUnderline == true)
4739 {
4740 beginUnderline = false;
4741 underline_end = new Vector3(m_textInfo.characterInfo[i - 1].topRight.x, underlineBaseLine, 0);
4742 underlineEndScale = m_textInfo.characterInfo[i - 1].scale;
4743
4744 DrawUnderlineMesh(underline_start, underline_end, ref last_vert_index, underlineStartScale, underlineEndScale, underlineMaxScale, xScaleMax, underlineColor);
4745 underlineMaxScale = 0;
4746 xScaleMax = 0;
4747 underlineBaseLine = k_LargePositiveFloat;
4748 }
4749 }
4750 #endregion
4751
4752
4753 // Setup & Handle Strikethrough
4754 #region Strikethrough
4755 // NOTE: Need to figure out how underline will be handled with multiple fonts and which font will be used for the underline.
4756 bool isStrikethrough = (m_textInfo.characterInfo[i].style & FontStyles.Strikethrough) == FontStyles.Strikethrough;
4757 float strikethroughOffset = currentFontAsset.m_FaceInfo.strikethroughOffset;
4758
4759 if (isStrikethrough)
4760 {
4761 bool isStrikeThroughVisible = true;
4762 m_textInfo.characterInfo[i].strikethroughVertexIndex = last_vert_index;
4763
4764 if (i > m_maxVisibleCharacters || currentLine > m_maxVisibleLines || (m_overflowMode == TextOverflowModes.Page && m_textInfo.characterInfo[i].pageNumber + 1 != m_pageToDisplay))
4765 isStrikeThroughVisible = false;
4766
4767 if (beginStrikethrough == false && isStrikeThroughVisible && i <= lineInfo.lastVisibleCharacterIndex && unicode != 10 && unicode != 11 && unicode != 13)
4768 {
4769 if (i == lineInfo.lastVisibleCharacterIndex && char.IsSeparator(unicode))
4770 { }
4771 else
4772 {
4773 beginStrikethrough = true;
4774 strikethroughPointSize = m_textInfo.characterInfo[i].pointSize;
4775 strikethroughScale = m_textInfo.characterInfo[i].scale;
4776 strikethrough_start = new Vector3(m_textInfo.characterInfo[i].bottomLeft.x, m_textInfo.characterInfo[i].baseLine + strikethroughOffset * strikethroughScale, 0);
4777 strikethroughColor = m_textInfo.characterInfo[i].strikethroughColor;
4778 strikethroughBaseline = m_textInfo.characterInfo[i].baseLine;
4779 //Debug.Log("Char [" + currentCharacter + "] Start Strikethrough POS: " + strikethrough_start);
4780 }
4781 }
4782
4783 // End Strikethrough if text only contains one character.
4784 if (beginStrikethrough && m_characterCount == 1)
4785 {
4786 beginStrikethrough = false;
4787 strikethrough_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, m_textInfo.characterInfo[i].baseLine + strikethroughOffset * strikethroughScale, 0);
4788
4789 DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor);
4790 }
4791 else if (beginStrikethrough && i == lineInfo.lastCharacterIndex)
4792 {
4793 // Terminate Strikethrough at previous visible character if space or carriage return.
4794 if (isWhiteSpace || unicode == 0x200B)
4795 {
4796 int lastVisibleCharacterIndex = lineInfo.lastVisibleCharacterIndex;
4797 strikethrough_end = new Vector3(m_textInfo.characterInfo[lastVisibleCharacterIndex].topRight.x, m_textInfo.characterInfo[lastVisibleCharacterIndex].baseLine + strikethroughOffset * strikethroughScale, 0);
4798 }
4799 else
4800 {
4801 // Terminate Strikethrough at last character of line.
4802 strikethrough_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, m_textInfo.characterInfo[i].baseLine + strikethroughOffset * strikethroughScale, 0);
4803 }
4804
4805 beginStrikethrough = false;
4806 DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor);
4807 }
4808 else if (beginStrikethrough && i < m_characterCount && (m_textInfo.characterInfo[i + 1].pointSize != strikethroughPointSize || !TMP_Math.Approximately(m_textInfo.characterInfo[i + 1].baseLine + offset.y, strikethroughBaseline)))
4809 {
4810 // Terminate Strikethrough if scale changes.
4811 beginStrikethrough = false;
4812
4813 int lastVisibleCharacterIndex = lineInfo.lastVisibleCharacterIndex;
4814 if (i > lastVisibleCharacterIndex)
4815 strikethrough_end = new Vector3(m_textInfo.characterInfo[lastVisibleCharacterIndex].topRight.x, m_textInfo.characterInfo[lastVisibleCharacterIndex].baseLine + strikethroughOffset * strikethroughScale, 0);
4816 else
4817 strikethrough_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, m_textInfo.characterInfo[i].baseLine + strikethroughOffset * strikethroughScale, 0);
4818
4819 DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor);
4820 //Debug.Log("Char [" + currentCharacter + "] at Index: " + i + " End Strikethrough POS: " + strikethrough_end + " Baseline: " + m_textInfo.characterInfo[i].baseLine.ToString("f3"));
4821 }
4822 else if (beginStrikethrough && i < m_characterCount && currentFontAsset.GetInstanceID() != characterInfos[i + 1].fontAsset.GetInstanceID())
4823 {
4824 // Terminate Strikethrough if font asset changes.
4825 beginStrikethrough = false;
4826 strikethrough_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, m_textInfo.characterInfo[i].baseLine + strikethroughOffset * strikethroughScale, 0);
4827
4828 DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor);
4829 }
4830 else if (beginStrikethrough && !isStrikeThroughVisible)
4831 {
4832 // Terminate Strikethrough if character is not visible.
4833 beginStrikethrough = false;
4834 strikethrough_end = new Vector3(m_textInfo.characterInfo[i - 1].topRight.x, m_textInfo.characterInfo[i - 1].baseLine + strikethroughOffset * strikethroughScale, 0);
4835
4836 DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor);
4837 }
4838 }
4839 else
4840 {
4841 // End Strikethrough
4842 if (beginStrikethrough == true)
4843 {
4844 beginStrikethrough = false;
4845 strikethrough_end = new Vector3(m_textInfo.characterInfo[i - 1].topRight.x, m_textInfo.characterInfo[i - 1].baseLine + strikethroughOffset * strikethroughScale, 0);
4846
4847 DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor);
4848 }
4849 }
4850 #endregion
4851
4852
4853 // HANDLE TEXT HIGHLIGHTING
4854 #region Text Highlighting
4855 bool isHighlight = (m_textInfo.characterInfo[i].style & FontStyles.Highlight) == FontStyles.Highlight;
4856 if (isHighlight)
4857 {
4858 bool isHighlightVisible = true;
4859 int currentPage = m_textInfo.characterInfo[i].pageNumber;
4860
4861 if (i > m_maxVisibleCharacters || currentLine > m_maxVisibleLines || (m_overflowMode == TextOverflowModes.Page && currentPage + 1 != m_pageToDisplay))
4862 isHighlightVisible = false;
4863
4864 if (beginHighlight == false && isHighlightVisible == true && i <= lineInfo.lastVisibleCharacterIndex && unicode != 10 && unicode != 11 && unicode != 13)
4865 {
4866 if (i == lineInfo.lastVisibleCharacterIndex && char.IsSeparator(unicode))
4867 { }
4868 else
4869 {
4870 beginHighlight = true;
4871 highlight_start = k_LargePositiveVector2;
4872 highlight_end = k_LargeNegativeVector2;
4873 highlightState = m_textInfo.characterInfo[i].highlightState;
4874 }
4875 }
4876
4877 if (beginHighlight)
4878 {
4879 TMP_CharacterInfo currentCharacter = m_textInfo.characterInfo[i];
4880 HighlightState currentState = currentCharacter.highlightState;
4881
4882 bool isColorTransition = false;
4883
4884 // Handle Highlight color changes
4885 if (highlightState != currentState)
4886 {
4887 // Adjust previous highlight section to prevent a gaps between sections.
4888 if (isWhiteSpace)
4889 highlight_end.x = (highlight_end.x - highlightState.padding.right + currentCharacter.origin) / 2;
4890 else
4891 highlight_end.x = (highlight_end.x - highlightState.padding.right + currentCharacter.bottomLeft.x) / 2;
4892
4893 highlight_start.y = Mathf.Min(highlight_start.y, currentCharacter.descender);
4894 highlight_end.y = Mathf.Max(highlight_end.y, currentCharacter.ascender);
4895
4896 DrawTextHighlight(highlight_start, highlight_end, ref last_vert_index, highlightState.color);
4897
4898 beginHighlight = true;
4899 highlight_start = new Vector2(highlight_end.x, currentCharacter.descender - currentState.padding.bottom);
4900
4901 if (isWhiteSpace)
4902 highlight_end = new Vector2(currentCharacter.xAdvance + currentState.padding.right, currentCharacter.ascender + currentState.padding.top);
4903 else
4904 highlight_end = new Vector2(currentCharacter.topRight.x + currentState.padding.right, currentCharacter.ascender + currentState.padding.top);
4905
4906 highlightState = currentState;
4907
4908 isColorTransition = true;
4909 }
4910
4911 if (!isColorTransition)
4912 {
4913 if (isWhiteSpace)
4914 {
4915 // Use the Min / Max of glyph metrics if white space.
4916 highlight_start.x = Mathf.Min(highlight_start.x, currentCharacter.origin - highlightState.padding.left);
4917 highlight_end.x = Mathf.Max(highlight_end.x, currentCharacter.xAdvance + highlightState.padding.right);
4918 }
4919 else
4920 {
4921 // Use the Min / Max of character bounds
4922 highlight_start.x = Mathf.Min(highlight_start.x, currentCharacter.bottomLeft.x - highlightState.padding.left);
4923 highlight_end.x = Mathf.Max(highlight_end.x, currentCharacter.topRight.x + highlightState.padding.right);
4924 }
4925
4926 highlight_start.y = Mathf.Min(highlight_start.y, currentCharacter.descender - highlightState.padding.bottom);
4927 highlight_end.y = Mathf.Max(highlight_end.y, currentCharacter.ascender + highlightState.padding.top);
4928 }
4929 }
4930
4931 // End Highlight if text only contains one character.
4932 if (beginHighlight && m_characterCount == 1)
4933 {
4934 beginHighlight = false;
4935
4936 DrawTextHighlight(highlight_start, highlight_end, ref last_vert_index, highlightState.color);
4937 }
4938 else if (beginHighlight && (i == lineInfo.lastCharacterIndex || i >= lineInfo.lastVisibleCharacterIndex))
4939 {
4940 beginHighlight = false;
4941 DrawTextHighlight(highlight_start, highlight_end, ref last_vert_index, highlightState.color);
4942 }
4943 else if (beginHighlight && !isHighlightVisible)
4944 {
4945 beginHighlight = false;
4946 DrawTextHighlight(highlight_start, highlight_end, ref last_vert_index, highlightState.color);
4947 }
4948 }
4949 else
4950 {
4951 // End Highlight
4952 if (beginHighlight == true)
4953 {
4954 beginHighlight = false;
4955 DrawTextHighlight(highlight_start, highlight_end, ref last_vert_index, highlightState.color);
4956 }
4957 }
4958 #endregion
4959
4960 lastLine = currentLine;
4961 }
4962 #endregion
4963
4964 // Set vertex count for Underline geometry
4965 m_textInfo.meshInfo[m_Underline.materialIndex].vertexCount = last_vert_index;
4966
4967 // METRICS ABOUT THE TEXT OBJECT
4968 m_textInfo.characterCount = m_characterCount;
4969 m_textInfo.spriteCount = m_spriteCount;
4970 m_textInfo.lineCount = lineCount;
4971 m_textInfo.wordCount = wordCount != 0 && m_characterCount > 0 ? wordCount : 1;
4972 m_textInfo.pageCount = m_pageNumber + 1;
4973
4974 // End Sampling of Phase II
4975 k_GenerateTextPhaseIIMarker.End();
4976
4977 // Phase III - Update Mesh Vertex Data
4978 k_GenerateTextPhaseIIIMarker.Begin();
4979
4980 if (m_renderMode == TextRenderFlags.Render && IsActive())
4981 {
4982 // Event to allow users to modify the content of the text info before the text is rendered.
4983 OnPreRenderText?.Invoke(m_textInfo);
4984
4985 // Sort the geometry of the text object if needed.
4986 if (m_geometrySortingOrder != VertexSortingOrder.Normal)
4987 m_textInfo.meshInfo[0].SortGeometry(VertexSortingOrder.Reverse);
4988
4989 // Upload Mesh Data
4990 m_mesh.MarkDynamic();
4991 m_mesh.vertices = m_textInfo.meshInfo[0].vertices;
4992 m_mesh.SetUVs(0, m_textInfo.meshInfo[0].uvs0);
4993 m_mesh.uv2 = m_textInfo.meshInfo[0].uvs2;
4994 //m_mesh.uv4 = m_textInfo.meshInfo[0].uvs4;
4995 m_mesh.colors32 = m_textInfo.meshInfo[0].colors32;
4996
4997 // Compute Bounds for the mesh. Manual computation is more efficient then using Mesh.RecalculteBounds.
4998 m_mesh.RecalculateBounds();
4999 //m_mesh.bounds = new Bounds(new Vector3((m_meshExtents.max.x + m_meshExtents.min.x) / 2, (m_meshExtents.max.y + m_meshExtents.min.y) / 2, 0) + offset, new Vector3(m_meshExtents.max.x - m_meshExtents.min.x, m_meshExtents.max.y - m_meshExtents.min.y, 0));
5000
5001 for (int i = 1; i < m_textInfo.materialCount; i++)
5002 {
5003 // Clear unused vertices
5004 m_textInfo.meshInfo[i].ClearUnusedVertices();
5005
5006 if (m_subTextObjects[i] == null) continue;
5007
5008 // Sort the geometry of the sub-text objects if needed.
5009 if (m_geometrySortingOrder != VertexSortingOrder.Normal)
5010 m_textInfo.meshInfo[i].SortGeometry(VertexSortingOrder.Reverse);
5011
5012 m_subTextObjects[i].mesh.vertices = m_textInfo.meshInfo[i].vertices;
5013 m_subTextObjects[i].mesh.SetUVs(0, m_textInfo.meshInfo[i].uvs0);
5014 m_subTextObjects[i].mesh.uv2 = m_textInfo.meshInfo[i].uvs2;
5015 //m_subTextObjects[i].mesh.uv4 = m_textInfo.meshInfo[i].uvs4;
5016 m_subTextObjects[i].mesh.colors32 = m_textInfo.meshInfo[i].colors32;
5017
5018 m_subTextObjects[i].mesh.RecalculateBounds();
5019
5020 // Update the collider on the sub text object
5021 //m_subTextObjects[i].UpdateColliders(m_textInfo.meshInfo[i].vertexCount);
5022 }
5023 }
5024
5025 // Event indicating the text has been regenerated.
5026 TMPro_EventManager.ON_TEXT_CHANGED(this);
5027
5028 //Debug.Log("***** Done rendering text object ID " + GetInstanceID() + ". *****");
5029
5030 // Clear allocations no longer necessary given the text object is static
5031 // if (true)
5032 // {
5033 // m_isInputParsingRequired = true;
5034 // m_textInfo.ClearAllData();
5035 // }
5036
5037 // End Sampling
5038 k_GenerateTextPhaseIIIMarker.End();
5039 k_GenerateTextMarker.End();
5040 }
5041
5042
5043 /// <summary>
5044 /// Method to return the local corners of the Text Container or RectTransform.
5045 /// </summary>
5046 /// <returns></returns>
5047 protected override Vector3[] GetTextContainerLocalCorners()
5048 {
5049 if (m_rectTransform == null) m_rectTransform = this.rectTransform;
5050
5051 m_rectTransform.GetLocalCorners(m_RectTransformCorners);
5052
5053 return m_RectTransformCorners;
5054 }
5055
5056
5057 /// <summary>
5058 /// Method to disable the renderers.
5059 /// </summary>
5060 void SetMeshFilters(bool state)
5061 {
5062 // Parent text object
5063 if (m_meshFilter != null)
5064 {
5065 if (state)
5066 m_meshFilter.sharedMesh = m_mesh;
5067 else
5068 m_meshFilter.sharedMesh = null;
5069 }
5070
5071 for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
5072 {
5073 if (m_subTextObjects[i].meshFilter != null)
5074 {
5075 if (state)
5076 m_subTextObjects[i].meshFilter.sharedMesh = m_subTextObjects[i].mesh;
5077 else
5078 m_subTextObjects[i].meshFilter.sharedMesh = null;
5079 }
5080 }
5081 }
5082
5083
5084 /// <summary>
5085 /// Method to Enable or Disable child SubMesh objects.
5086 /// </summary>
5087 /// <param name="state"></param>
5088 protected override void SetActiveSubMeshes(bool state)
5089 {
5090 for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
5091 {
5092 if (m_subTextObjects[i].enabled != state)
5093 m_subTextObjects[i].enabled = state;
5094 }
5095 }
5096
5097 protected void SetActiveSubTextObjectRenderers(bool state)
5098 {
5099 for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
5100 {
5101 Renderer subMeshRenderer = m_subTextObjects[i].renderer;
5102
5103 if (subMeshRenderer != null && subMeshRenderer.enabled != state)
5104 subMeshRenderer.enabled = state;
5105 }
5106 }
5107
5108
5109 /// <summary>
5110 /// Destroy Sub Mesh Objects
5111 /// </summary>
5112 protected override void DestroySubMeshObjects()
5113 {
5114 for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
5115 DestroyImmediate(m_subTextObjects[i]);
5116 }
5117
5118 /// <summary>
5119 ///
5120 /// </summary>
5121 internal void UpdateSubMeshSortingLayerID(int id)
5122 {
5123 for (int i = 1; i < m_subTextObjects.Length; i++)
5124 {
5125 TMP_SubMesh subMesh = m_subTextObjects[i];
5126
5127 if (subMesh != null && subMesh.renderer != null)
5128 {
5129 subMesh.renderer.sortingLayerID = id;
5130 }
5131 }
5132 }
5133
5134 /// <summary>
5135 ///
5136 /// </summary>
5137 internal void UpdateSubMeshSortingOrder(int order)
5138 {
5139 for (int i = 1; i < m_subTextObjects.Length; i++)
5140 {
5141 TMP_SubMesh subMesh = m_subTextObjects[i];
5142
5143 if (subMesh != null && subMesh.renderer != null)
5144 {
5145 subMesh.renderer.sortingOrder = order;
5146 }
5147 }
5148 }
5149
5150 /// <summary>
5151 /// Method returning the compound bounds of the text object and child sub objects.
5152 /// </summary>
5153 /// <returns></returns>
5154 protected override Bounds GetCompoundBounds()
5155 {
5156 Bounds mainBounds = m_mesh.bounds;
5157 Vector3 min = mainBounds.min;
5158 Vector3 max = mainBounds.max;
5159
5160 for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
5161 {
5162 Bounds subBounds = m_subTextObjects[i].mesh.bounds;
5163 min.x = min.x < subBounds.min.x ? min.x : subBounds.min.x;
5164 min.y = min.y < subBounds.min.y ? min.y : subBounds.min.y;
5165
5166 max.x = max.x > subBounds.max.x ? max.x : subBounds.max.x;
5167 max.y = max.y > subBounds.max.y ? max.y : subBounds.max.y;
5168 }
5169
5170 Vector3 center = (min + max) / 2;
5171 Vector2 size = max - min;
5172 return new Bounds(center, size);
5173 }
5174
5175
5176 /// <summary>
5177 /// Method to Update Scale in UV2
5178 /// </summary>
5179 //void UpdateSDFScale(float lossyScale)
5180 //{
5181 // // TODO: Resolve - Underline / Strikethrough segments not getting their SDF Scale adjusted.
5182
5183 // //Debug.Log("*** UpdateSDFScale() ***");
5184
5185 // // Iterate through each of the characters.
5186 // for (int i = 0; i < m_textInfo.characterCount; i++)
5187 // {
5188 // // Only update scale for visible characters.
5189 // if (m_textInfo.characterInfo[i].isVisible && m_textInfo.characterInfo[i].elementType == TMP_TextElementType.Character)
5190 // {
5191 // float scale = lossyScale * m_textInfo.characterInfo[i].scale * (1 - m_charWidthAdjDelta);
5192 // if (!m_textInfo.characterInfo[i].isUsingAlternateTypeface && (m_textInfo.characterInfo[i].style & FontStyles.Bold) == FontStyles.Bold) scale *= -1;
5193
5194 // int index = m_textInfo.characterInfo[i].materialReferenceIndex;
5195 // int vertexIndex = m_textInfo.characterInfo[i].vertexIndex;
5196
5197 // m_textInfo.meshInfo[index].uvs2[vertexIndex + 0].y = scale;
5198 // m_textInfo.meshInfo[index].uvs2[vertexIndex + 1].y = scale;
5199 // m_textInfo.meshInfo[index].uvs2[vertexIndex + 2].y = scale;
5200 // m_textInfo.meshInfo[index].uvs2[vertexIndex + 3].y = scale;
5201 // }
5202 // }
5203
5204 // // Push the updated uv2 scale information to the meshes.
5205 // for (int i = 0; i < m_textInfo.meshInfo.Length; i++)
5206 // {
5207 // if (i == 0)
5208 // m_mesh.uv2 = m_textInfo.meshInfo[0].uvs2;
5209 // else
5210 // m_subTextObjects[i].mesh.uv2 = m_textInfo.meshInfo[i].uvs2;
5211 // }
5212 //}
5213
5214 /// <summary>
5215 /// Method to update the SDF Scale in UV2.
5216 /// </summary>
5217 /// <param name="scaleDelta"></param>
5218 void UpdateSDFScale(float scaleDelta)
5219 {
5220 if (scaleDelta == 0 || scaleDelta == float.PositiveInfinity || scaleDelta == float.NegativeInfinity)
5221 {
5222 m_havePropertiesChanged = true;
5223 OnPreRenderObject();
5224 return;
5225 }
5226
5227 for (int materialIndex = 0; materialIndex < m_textInfo.materialCount; materialIndex++)
5228 {
5229 TMP_MeshInfo meshInfo = m_textInfo.meshInfo[materialIndex];
5230
5231 for (int i = 0; i < meshInfo.uvs0.Length; i++)
5232 {
5233 meshInfo.uvs0[i].w *= Mathf.Abs(scaleDelta);
5234 }
5235 }
5236
5237 // Push the updated uv0 scale information to the meshes.
5238 for (int i = 0; i < m_textInfo.meshInfo.Length; i++)
5239 {
5240 if (i == 0)
5241 m_mesh.SetUVs(0, m_textInfo.meshInfo[0].uvs0);
5242 else
5243 m_subTextObjects[i].mesh.SetUVs(0, m_textInfo.meshInfo[i].uvs0);
5244 }
5245 }
5246 #endregion
5247 }
5248}