A game about forced loneliness, made by TACStudios
at master 5248 lines 267 kB view raw
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}