You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3995 lines
200 KiB

  1. //#define TMP_PROFILE_ON
  2. //#define TMP_PROFILE_PHASES_ON
  3. using UnityEngine;
  4. using System;
  5. using System.Collections;
  6. using System.Collections.Generic;
  7. #pragma warning disable 0414 // Disabled a few warnings related to serialized variables not used in this script but used in the editor.
  8. namespace TMPro
  9. {
  10. public partial class TextMeshPro
  11. {
  12. [SerializeField]
  13. private bool m_hasFontAssetChanged = false; // Used to track when font properties have changed.
  14. float m_previousLossyScaleY = -1; // Used for Tracking lossy scale changes in the transform;
  15. [SerializeField]
  16. private Renderer m_renderer;
  17. private MeshFilter m_meshFilter;
  18. private bool m_isFirstAllocation; // Flag to determine if this is the first allocation of the buffers.
  19. private int m_max_characters = 8; // Determines the initial allocation and size of the character array / buffer.
  20. private int m_max_numberOfLines = 4; // Determines the initial allocation and maximum number of lines of text.
  21. private Bounds m_default_bounds = new Bounds(Vector3.zero, new Vector3(1000, 1000, 0));
  22. [SerializeField]
  23. protected TMP_SubMesh[] m_subTextObjects = new TMP_SubMesh[8];
  24. // MASKING RELATED PROPERTIES
  25. //MaterialPropertyBlock m_maskingPropertyBlock;
  26. //[SerializeField]
  27. private bool m_isMaskingEnabled;
  28. private bool isMaskUpdateRequired;
  29. //private bool m_isMaterialBlockSet;
  30. [SerializeField]
  31. private MaskingTypes m_maskType;
  32. // Matrix used to animated Env Map
  33. private Matrix4x4 m_EnvMapMatrix = new Matrix4x4();
  34. // Text Container / RectTransform Component
  35. private Vector3[] m_RectTransformCorners = new Vector3[4];
  36. [NonSerialized]
  37. private bool m_isRegisteredForEvents;
  38. // DEBUG Variables
  39. //private System.Diagnostics.Stopwatch m_StopWatch;
  40. //private bool isDebugOutputDone;
  41. //private int m_recursiveCount = 0;
  42. private int loopCountA;
  43. //private int loopCountB;
  44. //private int loopCountC;
  45. //private int loopCountD;
  46. //private int loopCountE;
  47. protected override void Awake()
  48. {
  49. //Debug.Log("Awake() called on Object ID " + GetInstanceID());
  50. #if UNITY_EDITOR
  51. // Special handling for TMP Settings and importing Essential Resources
  52. if (TMP_Settings.instance == null)
  53. {
  54. if (m_isWaitingOnResourceLoad == false)
  55. TMPro_EventManager.RESOURCE_LOAD_EVENT.Add(ON_RESOURCES_LOADED);
  56. m_isWaitingOnResourceLoad = true;
  57. return;
  58. }
  59. #endif
  60. // Cache Reference to the Mesh Renderer.
  61. m_renderer = GetComponent<Renderer>();
  62. if (m_renderer == null)
  63. m_renderer = gameObject.AddComponent<Renderer>();
  64. // Make sure we have a CanvasRenderer for compatibility reasons and hide it
  65. if (this.canvasRenderer != null)
  66. this.canvasRenderer.hideFlags = HideFlags.HideInInspector;
  67. else
  68. {
  69. CanvasRenderer canvasRenderer = gameObject.AddComponent<CanvasRenderer>();
  70. canvasRenderer.hideFlags = HideFlags.HideInInspector;
  71. }
  72. // Cache Reference to RectTransform
  73. m_rectTransform = this.rectTransform;
  74. // Cache Reference to the transform;
  75. m_transform = this.transform;
  76. // Cache a reference to the Mesh Filter.
  77. m_meshFilter = GetComponent<MeshFilter>();
  78. if (m_meshFilter == null)
  79. m_meshFilter = gameObject.AddComponent<MeshFilter>();
  80. // Cache a reference to our mesh.
  81. if (m_mesh == null)
  82. {
  83. //Debug.Log("Creating new mesh.");
  84. m_mesh = new Mesh();
  85. m_mesh.hideFlags = HideFlags.HideAndDontSave;
  86. m_meshFilter.mesh = m_mesh;
  87. //m_mesh.bounds = new Bounds(transform.position, new Vector3(1000, 1000, 0));
  88. }
  89. m_meshFilter.hideFlags = HideFlags.HideInInspector;
  90. // Load TMP Settings for new text object instances.
  91. LoadDefaultSettings();
  92. // Load the font asset and assign material to renderer.
  93. LoadFontAsset();
  94. // Load Default TMP StyleSheet
  95. TMP_StyleSheet.LoadDefaultStyleSheet();
  96. // Allocate our initial buffers.
  97. if (m_char_buffer == null)
  98. m_char_buffer = new int[m_max_characters];
  99. m_cached_TextElement = new TMP_Glyph();
  100. m_isFirstAllocation = true;
  101. if (m_textInfo == null)
  102. m_textInfo = new TMP_TextInfo(this);
  103. // Check if we have a font asset assigned. Return if we don't because no one likes to see purple squares on screen.
  104. if (m_fontAsset == null)
  105. {
  106. Debug.LogWarning("Please assign a Font Asset to this " + transform.name + " gameobject.", this);
  107. return;
  108. }
  109. // Check to make sure Sub Text Objects are tracked correctly in the event a Prefab is used.
  110. TMP_SubMesh[] subTextObjects = GetComponentsInChildren<TMP_SubMesh>();
  111. if (subTextObjects.Length > 0)
  112. {
  113. for (int i = 0; i < subTextObjects.Length; i++)
  114. m_subTextObjects[i + 1] = subTextObjects[i];
  115. }
  116. // Set flags to ensure our text is parsed and redrawn.
  117. m_isInputParsingRequired = true;
  118. m_havePropertiesChanged = true;
  119. m_isCalculateSizeRequired = true;
  120. m_isAwake = true;
  121. }
  122. protected override void OnEnable()
  123. {
  124. //Debug.Log("***** OnEnable() called on object ID " + GetInstanceID() + ". *****"); // called. Renderer.MeshFilter ID " + m_renderer.GetComponent<MeshFilter>().sharedMesh.GetInstanceID() + " Mesh ID " + m_mesh.GetInstanceID() + " MeshFilter ID " + m_meshFilter.GetInstanceID()); //has been called. HavePropertiesChanged = " + havePropertiesChanged); // has been called on Object ID:" + gameObject.GetInstanceID());
  125. // Return if Awake() has not been called on the text object.
  126. if (m_isAwake == false)
  127. return;
  128. // Register Callbacks for various events.
  129. if (!m_isRegisteredForEvents)
  130. {
  131. #if UNITY_EDITOR
  132. TMPro_EventManager.MATERIAL_PROPERTY_EVENT.Add(ON_MATERIAL_PROPERTY_CHANGED);
  133. TMPro_EventManager.FONT_PROPERTY_EVENT.Add(ON_FONT_PROPERTY_CHANGED);
  134. TMPro_EventManager.TEXTMESHPRO_PROPERTY_EVENT.Add(ON_TEXTMESHPRO_PROPERTY_CHANGED);
  135. TMPro_EventManager.DRAG_AND_DROP_MATERIAL_EVENT.Add(ON_DRAG_AND_DROP_MATERIAL);
  136. TMPro_EventManager.TEXT_STYLE_PROPERTY_EVENT.Add(ON_TEXT_STYLE_CHANGED);
  137. TMPro_EventManager.COLOR_GRADIENT_PROPERTY_EVENT.Add(ON_COLOR_GRADIENT_CHANGED);
  138. TMPro_EventManager.TMP_SETTINGS_PROPERTY_EVENT.Add(ON_TMP_SETTINGS_CHANGED);
  139. #endif
  140. m_isRegisteredForEvents = true;
  141. }
  142. meshFilter.sharedMesh = mesh;
  143. SetActiveSubMeshes(true);
  144. // Schedule potential text object update (if any of the properties have changed.
  145. ComputeMarginSize();
  146. m_isInputParsingRequired = true;
  147. m_havePropertiesChanged = true;
  148. m_verticesAlreadyDirty = false;
  149. SetVerticesDirty();
  150. }
  151. protected override void OnDisable()
  152. {
  153. //Debug.Log("***** OnDisable() called on object ID " + GetInstanceID() + ". *****"); //+ m_renderer.GetComponent<MeshFilter>().sharedMesh.GetInstanceID() + " Mesh ID " + m_mesh.GetInstanceID() + " MeshFilter ID " + m_meshFilter.GetInstanceID()); //has been called. HavePropertiesChanged = " + havePropertiesChanged); // has been called on Object ID:" + gameObject.GetInstanceID());
  154. // Return if Awake() has not been called on the text object.
  155. if (m_isAwake == false)
  156. return;
  157. TMP_UpdateManager.UnRegisterTextElementForRebuild(this);
  158. m_meshFilter.sharedMesh = null;
  159. SetActiveSubMeshes(false);
  160. }
  161. protected override void OnDestroy()
  162. {
  163. //Debug.Log("***** OnDestroy() called on object ID " + GetInstanceID() + ". *****");
  164. // Destroy the mesh if we have one.
  165. if (m_mesh != null)
  166. {
  167. DestroyImmediate(m_mesh);
  168. }
  169. // Unregister the event this object was listening to
  170. #if UNITY_EDITOR
  171. TMPro_EventManager.MATERIAL_PROPERTY_EVENT.Remove(ON_MATERIAL_PROPERTY_CHANGED);
  172. TMPro_EventManager.FONT_PROPERTY_EVENT.Remove(ON_FONT_PROPERTY_CHANGED);
  173. TMPro_EventManager.TEXTMESHPRO_PROPERTY_EVENT.Remove(ON_TEXTMESHPRO_PROPERTY_CHANGED);
  174. TMPro_EventManager.DRAG_AND_DROP_MATERIAL_EVENT.Remove(ON_DRAG_AND_DROP_MATERIAL);
  175. TMPro_EventManager.TEXT_STYLE_PROPERTY_EVENT.Remove(ON_TEXT_STYLE_CHANGED);
  176. TMPro_EventManager.COLOR_GRADIENT_PROPERTY_EVENT.Remove(ON_COLOR_GRADIENT_CHANGED);
  177. TMPro_EventManager.TMP_SETTINGS_PROPERTY_EVENT.Remove(ON_TMP_SETTINGS_CHANGED);
  178. TMPro_EventManager.RESOURCE_LOAD_EVENT.Remove(ON_RESOURCES_LOADED);
  179. #endif
  180. m_isRegisteredForEvents = false;
  181. TMP_UpdateManager.UnRegisterTextElementForRebuild(this);
  182. }
  183. #if UNITY_EDITOR
  184. protected override void Reset()
  185. {
  186. //Debug.Log("Reset() has been called." + m_subTextObjects);
  187. // Return if Awake() has not been called on the text object.
  188. if (m_isAwake == false)
  189. return;
  190. if (m_mesh != null)
  191. DestroyImmediate(m_mesh);
  192. Awake();
  193. }
  194. protected override void OnValidate()
  195. {
  196. //Debug.Log("*** TextMeshPro OnValidate() has been called on Object ID:" + gameObject.GetInstanceID());
  197. // Return if Awake() has not been called on the text object.
  198. if (m_isAwake == false)
  199. return;
  200. // Additional Properties could be added to sync up Serialized Properties & Properties.
  201. // Handle Font Asset changes in the inspector
  202. if (m_fontAsset == null || m_hasFontAssetChanged)
  203. {
  204. LoadFontAsset();
  205. m_isCalculateSizeRequired = true;
  206. m_hasFontAssetChanged = false;
  207. }
  208. m_padding = GetPaddingForMaterial();
  209. m_isInputParsingRequired = true;
  210. m_inputSource = TextInputSources.Text;
  211. m_havePropertiesChanged = true;
  212. m_isCalculateSizeRequired = true;
  213. m_isPreferredWidthDirty = true;
  214. m_isPreferredHeightDirty = true;
  215. SetAllDirty();
  216. }
  217. // Event received when TMP resources have been loaded.
  218. void ON_RESOURCES_LOADED()
  219. {
  220. TMPro_EventManager.RESOURCE_LOAD_EVENT.Remove(ON_RESOURCES_LOADED);
  221. Awake();
  222. OnEnable();
  223. }
  224. // Event received when custom material editor properties are changed.
  225. void ON_MATERIAL_PROPERTY_CHANGED(bool isChanged, Material mat)
  226. {
  227. //Debug.Log("ON_MATERIAL_PROPERTY_CHANGED event received. Targeted Material is: " + mat.name + " m_sharedMaterial: " + m_sharedMaterial.name + " m_renderer.sharedMaterial: " + m_renderer.sharedMaterial);
  228. if (m_renderer.sharedMaterial == null)
  229. {
  230. if (m_fontAsset != null)
  231. {
  232. m_renderer.sharedMaterial = m_fontAsset.material;
  233. Debug.LogWarning("No Material was assigned to " + name + ". " + m_fontAsset.material.name + " was assigned.", this);
  234. }
  235. else
  236. Debug.LogWarning("No Font Asset assigned to " + name + ". Please assign a Font Asset.", this);
  237. }
  238. if (m_fontAsset.atlas.GetInstanceID() != m_renderer.sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID())
  239. {
  240. m_renderer.sharedMaterial = m_sharedMaterial;
  241. //m_renderer.sharedMaterial = m_fontAsset.material;
  242. 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);
  243. }
  244. if (m_renderer.sharedMaterial != m_sharedMaterial) // || m_renderer.sharedMaterials.Contains(mat))
  245. {
  246. //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);
  247. m_sharedMaterial = m_renderer.sharedMaterial;
  248. }
  249. m_padding = GetPaddingForMaterial();
  250. //m_sharedMaterialHashCode = TMP_TextUtilities.GetSimpleHashCode(m_sharedMaterial.name);
  251. UpdateMask();
  252. UpdateEnvMapMatrix();
  253. m_havePropertiesChanged = true;
  254. SetVerticesDirty();
  255. }
  256. // Event received when font asset properties are changed in Font Inspector
  257. void ON_FONT_PROPERTY_CHANGED(bool isChanged, TMP_FontAsset font)
  258. {
  259. if (MaterialReference.Contains(m_materialReferences, font))
  260. {
  261. //Debug.Log("ON_FONT_PROPERTY_CHANGED event received.");
  262. m_isInputParsingRequired = true;
  263. m_havePropertiesChanged = true;
  264. SetMaterialDirty();
  265. SetVerticesDirty();
  266. }
  267. }
  268. // Event received when UNDO / REDO Event alters the properties of the object.
  269. void ON_TEXTMESHPRO_PROPERTY_CHANGED(bool isChanged, TextMeshPro obj)
  270. {
  271. if (obj == this)
  272. {
  273. //Debug.Log("Undo / Redo Event Received by Object ID:" + GetInstanceID());
  274. m_havePropertiesChanged = true;
  275. m_isInputParsingRequired = true;
  276. m_padding = GetPaddingForMaterial();
  277. ComputeMarginSize();
  278. SetVerticesDirty();
  279. }
  280. }
  281. // Event to Track Material Changed resulting from Drag-n-drop.
  282. void ON_DRAG_AND_DROP_MATERIAL(GameObject obj, Material currentMaterial, Material newMaterial)
  283. {
  284. //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());
  285. // Check if event applies to this current object
  286. #if UNITY_2018_2_OR_NEWER
  287. if (obj == gameObject || UnityEditor.PrefabUtility.GetCorrespondingObjectFromSource(gameObject) == obj)
  288. #else
  289. if (obj == gameObject || UnityEditor.PrefabUtility.GetPrefabParent(gameObject) == obj)
  290. #endif
  291. {
  292. UnityEditor.Undo.RecordObject(this, "Material Assignment");
  293. UnityEditor.Undo.RecordObject(m_renderer, "Material Assignment");
  294. m_sharedMaterial = newMaterial;
  295. m_padding = GetPaddingForMaterial();
  296. m_havePropertiesChanged = true;
  297. SetVerticesDirty();
  298. SetMaterialDirty();
  299. }
  300. }
  301. // Event received when Text Styles are changed.
  302. void ON_TEXT_STYLE_CHANGED(bool isChanged)
  303. {
  304. m_havePropertiesChanged = true;
  305. m_isInputParsingRequired = true;
  306. SetVerticesDirty();
  307. }
  308. /// <summary>
  309. /// Event received when a Color Gradient Preset is modified.
  310. /// </summary>
  311. /// <param name="textObject"></param>
  312. void ON_COLOR_GRADIENT_CHANGED(TMP_ColorGradient gradient)
  313. {
  314. if (m_fontColorGradientPreset != null && gradient.GetInstanceID() == m_fontColorGradientPreset.GetInstanceID())
  315. {
  316. m_havePropertiesChanged = true;
  317. SetVerticesDirty();
  318. }
  319. }
  320. /// <summary>
  321. /// Event received when the TMP Settings are changed.
  322. /// </summary>
  323. void ON_TMP_SETTINGS_CHANGED()
  324. {
  325. m_defaultSpriteAsset = null;
  326. m_havePropertiesChanged = true;
  327. m_isInputParsingRequired = true;
  328. SetAllDirty();
  329. }
  330. #endif
  331. // Function which loads either the default font or a newly assigned font asset. This function also assigned the appropriate material to the renderer.
  332. protected override void LoadFontAsset()
  333. {
  334. //Debug.Log("TextMeshPro LoadFontAsset() has been called."); // Current Font Asset is " + (font != null ? font.name: "Null") );
  335. ShaderUtilities.GetShaderPropertyIDs(); // Initialize & Get shader property IDs.
  336. if (m_fontAsset == null)
  337. {
  338. if (TMP_Settings.defaultFontAsset != null)
  339. m_fontAsset =TMP_Settings.defaultFontAsset;
  340. else
  341. m_fontAsset = Resources.Load<TMP_FontAsset>("Fonts & Materials/LiberationSans SDF");
  342. if (m_fontAsset == null)
  343. {
  344. Debug.LogWarning("The LiberationSans SDF Font Asset was not found. There is no Font Asset assigned to " + gameObject.name + ".", this);
  345. return;
  346. }
  347. if (m_fontAsset.characterDictionary == null)
  348. {
  349. Debug.Log("Dictionary is Null!");
  350. }
  351. m_renderer.sharedMaterial = m_fontAsset.material;
  352. m_sharedMaterial = m_fontAsset.material;
  353. m_sharedMaterial.SetFloat("_CullMode", 0);
  354. m_sharedMaterial.SetFloat(ShaderUtilities.ShaderTag_ZTestMode, 4);
  355. m_renderer.receiveShadows = false;
  356. m_renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; // true;
  357. // Get a Reference to the Shader
  358. }
  359. else
  360. {
  361. if (m_fontAsset.characterDictionary == null)
  362. {
  363. //Debug.Log("Reading Font Definition and Creating Character Dictionary.");
  364. m_fontAsset.ReadFontDefinition();
  365. }
  366. //Debug.Log("Font Asset name:" + font.material.name);
  367. // If font atlas texture doesn't match the assigned material font atlas, switch back to default material specified in the Font Asset.
  368. if (m_renderer.sharedMaterial == null || m_renderer.sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex) == null || m_fontAsset.atlas.GetInstanceID() != m_renderer.sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID())
  369. {
  370. m_renderer.sharedMaterial = m_fontAsset.material;
  371. m_sharedMaterial = m_fontAsset.material;
  372. }
  373. else
  374. {
  375. m_sharedMaterial = m_renderer.sharedMaterial;
  376. }
  377. //m_sharedMaterial.SetFloat("_CullMode", 0);
  378. m_sharedMaterial.SetFloat(ShaderUtilities.ShaderTag_ZTestMode, 4);
  379. // Check if we are using the SDF Surface Shader
  380. if (m_sharedMaterial.passCount == 1)
  381. {
  382. m_renderer.receiveShadows = false;
  383. m_renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
  384. }
  385. }
  386. m_padding = GetPaddingForMaterial();
  387. //m_alignmentPadding = ShaderUtilities.GetFontExtent(m_sharedMaterial);
  388. m_isMaskingEnabled = ShaderUtilities.IsMaskingEnabled(m_sharedMaterial);
  389. // Find and cache Underline & Ellipsis characters.
  390. GetSpecialCharacters(m_fontAsset);
  391. //m_sharedMaterials.Add(m_sharedMaterial);
  392. //m_sharedMaterialHashCode = TMP_TextUtilities.GetSimpleHashCode(m_sharedMaterial.name);
  393. // Hide Material Editor Component
  394. //m_renderer.sharedMaterial.hideFlags = HideFlags.None;
  395. }
  396. void UpdateEnvMapMatrix()
  397. {
  398. if (!m_sharedMaterial.HasProperty(ShaderUtilities.ID_EnvMap) || m_sharedMaterial.GetTexture(ShaderUtilities.ID_EnvMap) == null)
  399. return;
  400. //Debug.Log("Updating Env Matrix...");
  401. Vector3 rotation = m_sharedMaterial.GetVector(ShaderUtilities.ID_EnvMatrixRotation);
  402. m_EnvMapMatrix = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(rotation), Vector3.one);
  403. m_sharedMaterial.SetMatrix(ShaderUtilities.ID_EnvMatrix, m_EnvMapMatrix);
  404. }
  405. //
  406. void SetMask(MaskingTypes maskType)
  407. {
  408. switch(maskType)
  409. {
  410. case MaskingTypes.MaskOff:
  411. m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_SOFT);
  412. m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_HARD);
  413. m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_TEX);
  414. break;
  415. case MaskingTypes.MaskSoft:
  416. m_sharedMaterial.EnableKeyword(ShaderUtilities.Keyword_MASK_SOFT);
  417. m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_HARD);
  418. m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_TEX);
  419. break;
  420. case MaskingTypes.MaskHard:
  421. m_sharedMaterial.EnableKeyword(ShaderUtilities.Keyword_MASK_HARD);
  422. m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_SOFT);
  423. m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_TEX);
  424. break;
  425. //case MaskingTypes.MaskTex:
  426. // m_sharedMaterial.EnableKeyword(ShaderUtilities.Keyword_MASK_TEX);
  427. // m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_HARD);
  428. // m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_SOFT);
  429. // break;
  430. }
  431. }
  432. // Method used to set the masking coordinates
  433. void SetMaskCoordinates(Vector4 coords)
  434. {
  435. m_sharedMaterial.SetVector(ShaderUtilities.ID_ClipRect, coords);
  436. }
  437. // Method used to set the masking coordinates
  438. void SetMaskCoordinates(Vector4 coords, float softX, float softY)
  439. {
  440. m_sharedMaterial.SetVector(ShaderUtilities.ID_ClipRect, coords);
  441. m_sharedMaterial.SetFloat(ShaderUtilities.ID_MaskSoftnessX, softX);
  442. m_sharedMaterial.SetFloat(ShaderUtilities.ID_MaskSoftnessY, softY);
  443. }
  444. // Enable Masking in the Shader
  445. void EnableMasking()
  446. {
  447. if (m_sharedMaterial.HasProperty(ShaderUtilities.ID_ClipRect))
  448. {
  449. m_sharedMaterial.EnableKeyword(ShaderUtilities.Keyword_MASK_SOFT);
  450. m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_HARD);
  451. m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_TEX);
  452. m_isMaskingEnabled = true;
  453. UpdateMask();
  454. }
  455. }
  456. // Enable Masking in the Shader
  457. void DisableMasking()
  458. {
  459. if (m_sharedMaterial.HasProperty(ShaderUtilities.ID_ClipRect))
  460. {
  461. m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_SOFT);
  462. m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_HARD);
  463. m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_TEX);
  464. m_isMaskingEnabled = false;
  465. UpdateMask();
  466. }
  467. }
  468. void UpdateMask()
  469. {
  470. //Debug.Log("UpdateMask() called.");
  471. if (!m_isMaskingEnabled)
  472. {
  473. // Release Masking Material
  474. // Re-assign Base Material
  475. return;
  476. }
  477. if (m_isMaskingEnabled && m_fontMaterial == null)
  478. {
  479. CreateMaterialInstance();
  480. }
  481. /*
  482. if (!m_isMaskingEnabled)
  483. {
  484. //Debug.Log("Masking is not enabled.");
  485. if (m_maskingPropertyBlock != null)
  486. {
  487. m_renderer.SetPropertyBlock(null);
  488. //havePropertiesChanged = true;
  489. }
  490. return;
  491. }
  492. //else
  493. // Debug.Log("Updating Masking...");
  494. */
  495. // Compute Masking Coordinates & Softness
  496. //float softnessX = Mathf.Min(Mathf.Min(m_textContainer.margins.x, m_textContainer.margins.z), m_sharedMaterial.GetFloat(ShaderUtilities.ID_MaskSoftnessX));
  497. //float softnessY = Mathf.Min(Mathf.Min(m_textContainer.margins.y, m_textContainer.margins.w), m_sharedMaterial.GetFloat(ShaderUtilities.ID_MaskSoftnessY));
  498. //softnessX = softnessX > 0 ? softnessX : 0;
  499. //softnessY = softnessY > 0 ? softnessY : 0;
  500. //float width = (m_textContainer.width - Mathf.Max(m_textContainer.margins.x, 0) - Mathf.Max(m_textContainer.margins.z, 0)) / 2 + softnessX;
  501. //float height = (m_textContainer.height - Mathf.Max(m_textContainer.margins.y, 0) - Mathf.Max(m_textContainer.margins.w, 0)) / 2 + softnessY;
  502. //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);
  503. //Vector4 mask = new Vector4(center.x, center.y, width, height);
  504. //m_fontMaterial.SetVector(ShaderUtilities.ID_ClipRect, mask);
  505. //m_fontMaterial.SetFloat(ShaderUtilities.ID_MaskSoftnessX, softnessX);
  506. //m_fontMaterial.SetFloat(ShaderUtilities.ID_MaskSoftnessY, softnessY);
  507. /*
  508. if(m_maskingPropertyBlock == null)
  509. {
  510. m_maskingPropertyBlock = new MaterialPropertyBlock();
  511. //m_maskingPropertyBlock.AddFloat(ShaderUtilities.ID_VertexOffsetX, m_sharedMaterial.GetFloat(ShaderUtilities.ID_VertexOffsetX));
  512. //m_maskingPropertyBlock.AddFloat(ShaderUtilities.ID_VertexOffsetY, m_sharedMaterial.GetFloat(ShaderUtilities.ID_VertexOffsetY));
  513. //Debug.Log("Creating new MaterialPropertyBlock.");
  514. }
  515. //Debug.Log("Updating Material Property Block.");
  516. //m_maskingPropertyBlock.Clear();
  517. m_maskingPropertyBlock.AddFloat(ShaderUtilities.ID_MaskID, m_renderer.GetInstanceID());
  518. m_maskingPropertyBlock.AddVector(ShaderUtilities.ID_MaskCoord, mask);
  519. m_maskingPropertyBlock.AddFloat(ShaderUtilities.ID_MaskSoftnessX, softnessX);
  520. m_maskingPropertyBlock.AddFloat(ShaderUtilities.ID_MaskSoftnessY, softnessY);
  521. m_renderer.SetPropertyBlock(m_maskingPropertyBlock);
  522. */
  523. }
  524. // Function called internally when a new material is assigned via the fontMaterial property.
  525. protected override Material GetMaterial(Material mat)
  526. {
  527. // Check in case Object is disabled. If so, we don't have a valid reference to the Renderer.
  528. // This can occur when the Duplicate Material Context menu is used on an inactive object.
  529. //if (m_renderer == null)
  530. // m_renderer = GetComponent<Renderer>();
  531. // Create Instance Material only if the new material is not the same instance previously used.
  532. if (m_fontMaterial == null || m_fontMaterial.GetInstanceID() != mat.GetInstanceID())
  533. m_fontMaterial = CreateMaterialInstance(mat);
  534. m_sharedMaterial = m_fontMaterial;
  535. m_padding = GetPaddingForMaterial();
  536. SetVerticesDirty();
  537. SetMaterialDirty();
  538. return m_sharedMaterial;
  539. }
  540. /// <summary>
  541. /// Method returning instances of the materials used by the text object.
  542. /// </summary>
  543. /// <returns></returns>
  544. protected override Material[] GetMaterials(Material[] mats)
  545. {
  546. int materialCount = m_textInfo.materialCount;
  547. if (m_fontMaterials == null)
  548. m_fontMaterials = new Material[materialCount];
  549. else if (m_fontMaterials.Length != materialCount)
  550. TMP_TextInfo.Resize(ref m_fontMaterials, materialCount, false);
  551. // Get instances of the materials
  552. for (int i = 0; i < materialCount; i++)
  553. {
  554. if (i == 0)
  555. m_fontMaterials[i] = fontMaterial;
  556. else
  557. m_fontMaterials[i] = m_subTextObjects[i].material;
  558. }
  559. m_fontSharedMaterials = m_fontMaterials;
  560. return m_fontMaterials;
  561. }
  562. // Function called internally when a new shared material is assigned via the fontSharedMaterial property.
  563. protected override void SetSharedMaterial(Material mat)
  564. {
  565. // Check in case Object is disabled. If so, we don't have a valid reference to the Renderer.
  566. // This can occur when the Duplicate Material Context menu is used on an inactive object.
  567. //if (m_renderer == null)
  568. // m_renderer = GetComponent<Renderer>();
  569. m_sharedMaterial = mat;
  570. m_padding = GetPaddingForMaterial();
  571. SetMaterialDirty();
  572. }
  573. /// <summary>
  574. /// Method returning an array containing the materials used by the text object.
  575. /// </summary>
  576. /// <returns></returns>
  577. protected override Material[] GetSharedMaterials()
  578. {
  579. int materialCount = m_textInfo.materialCount;
  580. if (m_fontSharedMaterials == null)
  581. m_fontSharedMaterials = new Material[materialCount];
  582. else if (m_fontSharedMaterials.Length != materialCount)
  583. TMP_TextInfo.Resize(ref m_fontSharedMaterials, materialCount, false);
  584. for (int i = 0; i < materialCount; i++)
  585. {
  586. if (i == 0)
  587. m_fontSharedMaterials[i] = m_sharedMaterial;
  588. else
  589. m_fontSharedMaterials[i] = m_subTextObjects[i].sharedMaterial;
  590. }
  591. return m_fontSharedMaterials;
  592. }
  593. /// <summary>
  594. /// Method used to assign new materials to the text and sub text objects.
  595. /// </summary>
  596. protected override void SetSharedMaterials(Material[] materials)
  597. {
  598. int materialCount = m_textInfo.materialCount;
  599. // Check allocation of the fontSharedMaterials array.
  600. if (m_fontSharedMaterials == null)
  601. m_fontSharedMaterials = new Material[materialCount];
  602. else if (m_fontSharedMaterials.Length != materialCount)
  603. TMP_TextInfo.Resize(ref m_fontSharedMaterials, materialCount, false);
  604. // Only assign as many materials as the text object contains.
  605. for (int i = 0; i < materialCount; i++)
  606. {
  607. Texture mat_MainTex = materials[i].GetTexture(ShaderUtilities.ID_MainTex);
  608. if (i == 0)
  609. {
  610. // Only assign new material if the font atlas textures match.
  611. if ( mat_MainTex == null || mat_MainTex.GetInstanceID() != m_sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID())
  612. continue;
  613. m_sharedMaterial = m_fontSharedMaterials[i] = materials[i];
  614. m_padding = GetPaddingForMaterial(m_sharedMaterial);
  615. }
  616. else
  617. {
  618. // Only assign new material if the font atlas textures match.
  619. if (mat_MainTex == null || mat_MainTex.GetInstanceID() != m_subTextObjects[i].sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID())
  620. continue;
  621. // Only assign a new material if none were specified in the text input.
  622. if (m_subTextObjects[i].isDefaultMaterial)
  623. m_subTextObjects[i].sharedMaterial = m_fontSharedMaterials[i] = materials[i];
  624. }
  625. }
  626. }
  627. // This function will create an instance of the Font Material.
  628. protected override void SetOutlineThickness(float thickness)
  629. {
  630. thickness = Mathf.Clamp01(thickness);
  631. m_renderer.material.SetFloat(ShaderUtilities.ID_OutlineWidth, thickness);
  632. if (m_fontMaterial == null)
  633. m_fontMaterial = m_renderer.material;
  634. m_fontMaterial = m_renderer.material;
  635. m_sharedMaterial = m_fontMaterial;
  636. m_padding = GetPaddingForMaterial();
  637. }
  638. // This function will create an instance of the Font Material.
  639. protected override void SetFaceColor(Color32 color)
  640. {
  641. m_renderer.material.SetColor(ShaderUtilities.ID_FaceColor, color);
  642. if (m_fontMaterial == null)
  643. m_fontMaterial = m_renderer.material;
  644. m_sharedMaterial = m_fontMaterial;
  645. }
  646. // This function will create an instance of the Font Material.
  647. protected override void SetOutlineColor(Color32 color)
  648. {
  649. m_renderer.material.SetColor(ShaderUtilities.ID_OutlineColor, color);
  650. if (m_fontMaterial == null)
  651. m_fontMaterial = m_renderer.material;
  652. //Debug.Log("Material ID:" + m_fontMaterial.GetInstanceID());
  653. m_sharedMaterial = m_fontMaterial;
  654. }
  655. // Function used to create an instance of the material
  656. void CreateMaterialInstance()
  657. {
  658. Material mat = new Material(m_sharedMaterial);
  659. mat.shaderKeywords = m_sharedMaterial.shaderKeywords;
  660. //mat.hideFlags = HideFlags.DontSave;
  661. mat.name += " Instance";
  662. //m_uiRenderer.SetMaterial(mat, null);
  663. m_fontMaterial = mat;
  664. }
  665. // Sets the Render Queue and Ztest mode
  666. protected override void SetShaderDepth()
  667. {
  668. if (m_isOverlay)
  669. {
  670. // Changing these properties results in an instance of the material
  671. m_sharedMaterial.SetFloat(ShaderUtilities.ShaderTag_ZTestMode, 0);
  672. //m_renderer.material.SetFloat("_ZTestMode", 8);
  673. m_renderer.material.renderQueue = 4000;
  674. m_sharedMaterial = m_renderer.material;
  675. //Debug.Log("Text set to Overlay mode.");
  676. }
  677. else
  678. {
  679. // Should this use an instanced material?
  680. m_sharedMaterial.SetFloat(ShaderUtilities.ShaderTag_ZTestMode, 4);
  681. m_renderer.material.renderQueue = -1;
  682. m_sharedMaterial = m_renderer.material;
  683. //Debug.Log("Text set to Normal mode.");
  684. }
  685. }
  686. // Sets the Culling mode of the material
  687. protected override void SetCulling()
  688. {
  689. if (m_isCullingEnabled)
  690. {
  691. m_renderer.material.SetFloat("_CullMode", 2);
  692. for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
  693. {
  694. Renderer renderer = m_subTextObjects[i].renderer;
  695. if (renderer != null)
  696. {
  697. renderer.material.SetFloat(ShaderUtilities.ShaderTag_CullMode, 2);
  698. }
  699. }
  700. }
  701. else
  702. {
  703. m_renderer.material.SetFloat("_CullMode", 0);
  704. for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
  705. {
  706. Renderer renderer = m_subTextObjects[i].renderer;
  707. if (renderer != null)
  708. {
  709. renderer.material.SetFloat(ShaderUtilities.ShaderTag_CullMode, 0);
  710. }
  711. }
  712. }
  713. }
  714. // Set Perspective Correction Mode based on whether Camera is Orthographic or Perspective
  715. void SetPerspectiveCorrection()
  716. {
  717. if (m_isOrthographic)
  718. m_sharedMaterial.SetFloat(ShaderUtilities.ID_PerspectiveFilter, 0.0f);
  719. else
  720. m_sharedMaterial.SetFloat(ShaderUtilities.ID_PerspectiveFilter, 0.875f);
  721. }
  722. /// <summary>
  723. /// Get the padding value for the currently assigned material.
  724. /// </summary>
  725. /// <returns></returns>
  726. protected override float GetPaddingForMaterial(Material mat)
  727. {
  728. m_padding = ShaderUtilities.GetPadding(mat, m_enableExtraPadding, m_isUsingBold);
  729. m_isMaskingEnabled = ShaderUtilities.IsMaskingEnabled(m_sharedMaterial);
  730. m_isSDFShader = mat.HasProperty(ShaderUtilities.ID_WeightNormal);
  731. return m_padding;
  732. }
  733. /// <summary>
  734. /// Get the padding value for the currently assigned material.
  735. /// </summary>
  736. /// <returns></returns>
  737. protected override float GetPaddingForMaterial()
  738. {
  739. ShaderUtilities.GetShaderPropertyIDs();
  740. if (m_sharedMaterial == null) return 0;
  741. m_padding = ShaderUtilities.GetPadding(m_sharedMaterial, m_enableExtraPadding, m_isUsingBold);
  742. m_isMaskingEnabled = ShaderUtilities.IsMaskingEnabled(m_sharedMaterial);
  743. m_isSDFShader = m_sharedMaterial.HasProperty(ShaderUtilities.ID_WeightNormal);
  744. return m_padding;
  745. }
  746. // 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.
  747. protected override int SetArraySizes(int[] chars)
  748. {
  749. //Debug.Log("*** SetArraySizes() ***");
  750. int tagEnd = 0;
  751. int spriteCount = 0;
  752. m_totalCharacterCount = 0;
  753. m_isUsingBold = false;
  754. m_isParsingText = false;
  755. tag_NoParsing = false;
  756. m_style = m_fontStyle;
  757. m_fontWeightInternal = (m_style & FontStyles.Bold) == FontStyles.Bold ? 700 : m_fontWeight;
  758. m_fontWeightStack.SetDefault(m_fontWeightInternal);
  759. m_currentFontAsset = m_fontAsset;
  760. m_currentMaterial = m_sharedMaterial;
  761. m_currentMaterialIndex = 0;
  762. m_materialReferenceStack.SetDefault(new MaterialReference(m_currentMaterialIndex, m_currentFontAsset, null, m_currentMaterial, m_padding));
  763. m_materialReferenceIndexLookup.Clear();
  764. MaterialReference.AddMaterialReference(m_currentMaterial, m_currentFontAsset, m_materialReferences, m_materialReferenceIndexLookup);
  765. if (m_textInfo == null) m_textInfo = new TMP_TextInfo();
  766. m_textElementType = TMP_TextElementType.Character;
  767. // Clear Linked Text object if we have one.
  768. if (m_linkedTextComponent != null)
  769. {
  770. m_linkedTextComponent.text = string.Empty;
  771. m_linkedTextComponent.ForceMeshUpdate();
  772. }
  773. // Parsing XML tags in the text
  774. for (int i = 0; i < chars.Length && chars[i] != 0; i++)
  775. {
  776. //Make sure the characterInfo array can hold the next text element.
  777. if (m_textInfo.characterInfo == null || m_totalCharacterCount >= m_textInfo.characterInfo.Length)
  778. TMP_TextInfo.Resize(ref m_textInfo.characterInfo, m_totalCharacterCount + 1, true);
  779. int c = chars[i];
  780. // PARSE XML TAGS
  781. #region PARSE XML TAGS
  782. if (m_isRichText && c == 60) // if Char '<'
  783. {
  784. int prev_MaterialIndex = m_currentMaterialIndex;
  785. // Check if Tag is Valid
  786. if (ValidateHtmlTag(chars, i + 1, out tagEnd))
  787. {
  788. i = tagEnd;
  789. if ((m_style & FontStyles.Bold) == FontStyles.Bold) m_isUsingBold = true;
  790. if (m_textElementType == TMP_TextElementType.Sprite)
  791. {
  792. m_materialReferences[m_currentMaterialIndex].referenceCount += 1;
  793. m_textInfo.characterInfo[m_totalCharacterCount].character = (char)(57344 + m_spriteIndex);
  794. m_textInfo.characterInfo[m_totalCharacterCount].spriteIndex = m_spriteIndex;
  795. m_textInfo.characterInfo[m_totalCharacterCount].fontAsset = m_currentFontAsset;
  796. m_textInfo.characterInfo[m_totalCharacterCount].spriteAsset = m_currentSpriteAsset;
  797. m_textInfo.characterInfo[m_totalCharacterCount].materialReferenceIndex = m_currentMaterialIndex;
  798. m_textInfo.characterInfo[m_totalCharacterCount].elementType = m_textElementType;
  799. // Restore element type and material index to previous values.
  800. m_textElementType = TMP_TextElementType.Character;
  801. m_currentMaterialIndex = prev_MaterialIndex;
  802. spriteCount += 1;
  803. m_totalCharacterCount += 1;
  804. }
  805. continue;
  806. }
  807. }
  808. #endregion
  809. bool isUsingFallback = false;
  810. bool isUsingAlternativeTypeface = false;
  811. TMP_Glyph glyph;
  812. TMP_FontAsset tempFontAsset;
  813. TMP_FontAsset prev_fontAsset = m_currentFontAsset;
  814. Material prev_material = m_currentMaterial;
  815. int prev_materialIndex = m_currentMaterialIndex;
  816. // Handle Font Styles like LowerCase, UpperCase and SmallCaps.
  817. #region Handling of LowerCase, UpperCase and SmallCaps Font Styles
  818. if (m_textElementType == TMP_TextElementType.Character)
  819. {
  820. if ((m_style & FontStyles.UpperCase) == FontStyles.UpperCase)
  821. {
  822. // If this character is lowercase, switch to uppercase.
  823. if (char.IsLower((char)c))
  824. c = char.ToUpper((char)c);
  825. }
  826. else if ((m_style & FontStyles.LowerCase) == FontStyles.LowerCase)
  827. {
  828. // If this character is uppercase, switch to lowercase.
  829. if (char.IsUpper((char)c))
  830. c = char.ToLower((char)c);
  831. }
  832. else if ((m_fontStyle & FontStyles.SmallCaps) == FontStyles.SmallCaps || (m_style & FontStyles.SmallCaps) == FontStyles.SmallCaps)
  833. {
  834. // Only convert lowercase characters to uppercase.
  835. if (char.IsLower((char)c))
  836. c = char.ToUpper((char)c);
  837. }
  838. }
  839. #endregion
  840. // Handling of font weights.
  841. #region HANDLING OF FONT WEIGHT
  842. tempFontAsset = GetFontAssetForWeight(m_fontWeightInternal);
  843. if (tempFontAsset != null)
  844. {
  845. isUsingFallback = true;
  846. isUsingAlternativeTypeface = true;
  847. m_currentFontAsset = tempFontAsset;
  848. //m_currentMaterialIndex = MaterialReference.AddMaterialReference(m_currentMaterial, tempFontAsset, m_materialReferences, m_materialReferenceIndexLookup);
  849. }
  850. #endregion
  851. // Lookup the Glyph data for each character and cache it.
  852. #region LOOKUP GLYPH
  853. tempFontAsset = TMP_FontUtilities.SearchForGlyph(m_currentFontAsset, c, out glyph);
  854. // Search for the glyph in the Sprite Asset assigned to the text object.
  855. if (glyph == null)
  856. {
  857. TMP_SpriteAsset spriteAsset = this.spriteAsset;
  858. if (spriteAsset != null)
  859. {
  860. int spriteIndex = -1;
  861. // Check Default Sprite Asset and its Fallbacks
  862. spriteAsset = TMP_SpriteAsset.SearchForSpriteByUnicode(spriteAsset, c, true, out spriteIndex);
  863. if (spriteIndex != -1)
  864. {
  865. m_textElementType = TMP_TextElementType.Sprite;
  866. m_textInfo.characterInfo[m_totalCharacterCount].elementType = m_textElementType;
  867. m_currentMaterialIndex = MaterialReference.AddMaterialReference(spriteAsset.material, spriteAsset, m_materialReferences, m_materialReferenceIndexLookup);
  868. m_materialReferences[m_currentMaterialIndex].referenceCount += 1;
  869. m_textInfo.characterInfo[m_totalCharacterCount].character = (char)c;
  870. m_textInfo.characterInfo[m_totalCharacterCount].spriteIndex = spriteIndex;
  871. m_textInfo.characterInfo[m_totalCharacterCount].fontAsset = m_currentFontAsset;
  872. m_textInfo.characterInfo[m_totalCharacterCount].spriteAsset = spriteAsset;
  873. m_textInfo.characterInfo[m_totalCharacterCount].materialReferenceIndex = m_currentMaterialIndex;
  874. // Restore element type and material index to previous values.
  875. m_textElementType = TMP_TextElementType.Character;
  876. m_currentMaterialIndex = prev_materialIndex;
  877. spriteCount += 1;
  878. m_totalCharacterCount += 1;
  879. continue;
  880. }
  881. }
  882. }
  883. // Search for the glyph in the list of fallback assigned in the TMP Settings (General Fallbacks).
  884. if (glyph == null)
  885. {
  886. if (TMP_Settings.fallbackFontAssets != null && TMP_Settings.fallbackFontAssets.Count > 0)
  887. tempFontAsset = TMP_FontUtilities.SearchForGlyph(TMP_Settings.fallbackFontAssets, c, out glyph);
  888. }
  889. // Search for the glyph in the Default Font Asset assigned in the TMP Settings file.
  890. if (glyph == null)
  891. {
  892. if (TMP_Settings.defaultFontAsset != null)
  893. tempFontAsset = TMP_FontUtilities.SearchForGlyph(TMP_Settings.defaultFontAsset, c, out glyph);
  894. }
  895. // TODO: Add support for using Sprite Assets like a special Emoji only Sprite Asset when UTF16 or UTF32 glyphs are requested.
  896. // This would kind of mirror native Emoji support.
  897. if (glyph == null)
  898. {
  899. TMP_SpriteAsset spriteAsset = TMP_Settings.defaultSpriteAsset;
  900. if (spriteAsset != null)
  901. {
  902. int spriteIndex = -1;
  903. // Check Default Sprite Asset and its Fallbacks
  904. spriteAsset = TMP_SpriteAsset.SearchForSpriteByUnicode(spriteAsset, c, true, out spriteIndex);
  905. if (spriteIndex != -1)
  906. {
  907. m_textElementType = TMP_TextElementType.Sprite;
  908. m_textInfo.characterInfo[m_totalCharacterCount].elementType = m_textElementType;
  909. m_currentMaterialIndex = MaterialReference.AddMaterialReference(spriteAsset.material, spriteAsset, m_materialReferences, m_materialReferenceIndexLookup);
  910. m_materialReferences[m_currentMaterialIndex].referenceCount += 1;
  911. m_textInfo.characterInfo[m_totalCharacterCount].character = (char)c;
  912. m_textInfo.characterInfo[m_totalCharacterCount].spriteIndex = spriteIndex;
  913. m_textInfo.characterInfo[m_totalCharacterCount].fontAsset = m_currentFontAsset;
  914. m_textInfo.characterInfo[m_totalCharacterCount].spriteAsset = spriteAsset;
  915. m_textInfo.characterInfo[m_totalCharacterCount].materialReferenceIndex = m_currentMaterialIndex;
  916. // Restore element type and material index to previous values.
  917. m_textElementType = TMP_TextElementType.Character;
  918. m_currentMaterialIndex = prev_materialIndex;
  919. spriteCount += 1;
  920. m_totalCharacterCount += 1;
  921. continue;
  922. }
  923. }
  924. }
  925. //Check if Lowercase or Uppercase variant of the character is available.
  926. // Not sure this is necessary anyone as it is very unlikely with recursive search through fallback fonts.
  927. //if (glyph == null)
  928. //{
  929. // if (char.IsLower((char)c))
  930. // {
  931. // if (m_currentFontAsset.characterDictionary.TryGetValue(char.ToUpper((char)c), out glyph))
  932. // c = chars[i] = char.ToUpper((char)c);
  933. // }
  934. // else if (char.IsUpper((char)c))
  935. // {
  936. // if (m_currentFontAsset.characterDictionary.TryGetValue(char.ToLower((char)c), out glyph))
  937. // c = chars[i] = char.ToLower((char)c);
  938. // }
  939. //}
  940. // Replace missing glyph by the Square (9633) glyph or possibly the Space (32) glyph.
  941. if (glyph == null)
  942. {
  943. // Save the original unicode character
  944. int srcGlyph = c;
  945. // Try replacing the missing glyph character by TMP Settings Missing Glyph or Square (9633) character.
  946. c = chars[i] = TMP_Settings.missingGlyphCharacter == 0 ? 9633 : TMP_Settings.missingGlyphCharacter;
  947. // Check for the missing glyph character in the currently assigned font asset.
  948. tempFontAsset = TMP_FontUtilities.SearchForGlyph(m_currentFontAsset, c, out glyph);
  949. if (glyph == null)
  950. {
  951. // Search for the missing glyph character in the TMP Settings Fallback list.
  952. if (TMP_Settings.fallbackFontAssets != null && TMP_Settings.fallbackFontAssets.Count > 0)
  953. tempFontAsset = TMP_FontUtilities.SearchForGlyph(TMP_Settings.fallbackFontAssets, c, out glyph);
  954. }
  955. if (glyph == null)
  956. {
  957. // Search for the missing glyph in the TMP Settings Default Font Asset.
  958. if (TMP_Settings.defaultFontAsset != null)
  959. tempFontAsset = TMP_FontUtilities.SearchForGlyph(TMP_Settings.defaultFontAsset, c, out glyph);
  960. }
  961. if (glyph == null)
  962. {
  963. // Use Space (32) Glyph from the currently assigned font asset.
  964. c = chars[i] = 32;
  965. tempFontAsset = TMP_FontUtilities.SearchForGlyph(m_currentFontAsset, c, out glyph);
  966. if (!TMP_Settings.warningsDisabled) Debug.LogWarning("Character with ASCII value of " + srcGlyph + " was not found in the Font Asset Glyph Table. It was replaced by a space.", this);
  967. }
  968. }
  969. // Determine if the font asset is still the current font asset or a fallback.
  970. if (tempFontAsset != null)
  971. {
  972. if (tempFontAsset.GetInstanceID() != m_currentFontAsset.GetInstanceID())
  973. {
  974. isUsingFallback = true;
  975. isUsingAlternativeTypeface = false;
  976. m_currentFontAsset = tempFontAsset;
  977. }
  978. }
  979. #endregion
  980. m_textInfo.characterInfo[m_totalCharacterCount].elementType = TMP_TextElementType.Character;
  981. m_textInfo.characterInfo[m_totalCharacterCount].textElement = glyph;
  982. m_textInfo.characterInfo[m_totalCharacterCount].isUsingAlternateTypeface = isUsingAlternativeTypeface;
  983. m_textInfo.characterInfo[m_totalCharacterCount].character = (char)c;
  984. m_textInfo.characterInfo[m_totalCharacterCount].fontAsset = m_currentFontAsset;
  985. if (isUsingFallback)
  986. {
  987. // Create Fallback material instance matching current material preset if necessary
  988. if (TMP_Settings.matchMaterialPreset)
  989. m_currentMaterial = TMP_MaterialManager.GetFallbackMaterial(m_currentMaterial, m_currentFontAsset.material);
  990. else
  991. m_currentMaterial = m_currentFontAsset.material;
  992. m_currentMaterialIndex = MaterialReference.AddMaterialReference(m_currentMaterial, m_currentFontAsset, m_materialReferences, m_materialReferenceIndexLookup);
  993. }
  994. if (!char.IsWhiteSpace((char)c) && c != 0x200B)
  995. {
  996. // Limit the mesh of the main text object to 65535 vertices and use sub objects for the overflow.
  997. if (m_materialReferences[m_currentMaterialIndex].referenceCount < 16383)
  998. m_materialReferences[m_currentMaterialIndex].referenceCount += 1;
  999. else
  1000. {
  1001. m_currentMaterialIndex = MaterialReference.AddMaterialReference(new Material(m_currentMaterial), m_currentFontAsset, m_materialReferences, m_materialReferenceIndexLookup);
  1002. m_materialReferences[m_currentMaterialIndex].referenceCount += 1;
  1003. }
  1004. }
  1005. m_textInfo.characterInfo[m_totalCharacterCount].material = m_currentMaterial;
  1006. m_textInfo.characterInfo[m_totalCharacterCount].materialReferenceIndex = m_currentMaterialIndex;
  1007. m_materialReferences[m_currentMaterialIndex].isFallbackMaterial = isUsingFallback;
  1008. // Restore previous font asset and material if fallback font was used.
  1009. if (isUsingFallback)
  1010. {
  1011. m_materialReferences[m_currentMaterialIndex].fallbackMaterial = prev_material;
  1012. m_currentFontAsset = prev_fontAsset;
  1013. m_currentMaterial = prev_material;
  1014. m_currentMaterialIndex = prev_materialIndex;
  1015. }
  1016. m_totalCharacterCount += 1;
  1017. }
  1018. // Early return if we are calculating the preferred values.
  1019. if (m_isCalculatingPreferredValues)
  1020. {
  1021. m_isCalculatingPreferredValues = false;
  1022. m_isInputParsingRequired = true;
  1023. return m_totalCharacterCount;
  1024. }
  1025. // Save material and sprite count.
  1026. m_textInfo.spriteCount = spriteCount;
  1027. int materialCount = m_textInfo.materialCount = m_materialReferenceIndexLookup.Count;
  1028. // Check if we need to resize the MeshInfo array for handling different materials.
  1029. if (materialCount > m_textInfo.meshInfo.Length)
  1030. TMP_TextInfo.Resize(ref m_textInfo.meshInfo, materialCount, false);
  1031. // Resize SubTextObject array if necessary
  1032. if (materialCount > m_subTextObjects.Length)
  1033. TMP_TextInfo.Resize(ref m_subTextObjects, Mathf.NextPowerOfTwo(materialCount + 1));
  1034. // Resize CharacterInfo[] if allocations are excessive
  1035. if (m_textInfo.characterInfo.Length - m_totalCharacterCount > 256)
  1036. TMP_TextInfo.Resize(ref m_textInfo.characterInfo, Mathf.Max(m_totalCharacterCount + 1, 256), true);
  1037. // Iterate through the material references to set the mesh buffer allocations
  1038. for (int i = 0; i < materialCount; i++)
  1039. {
  1040. // Add new sub text object for each material reference
  1041. if (i > 0)
  1042. {
  1043. if (m_subTextObjects[i] == null)
  1044. {
  1045. m_subTextObjects[i] = TMP_SubMesh.AddSubTextObject(this, m_materialReferences[i]);
  1046. // Not sure this is necessary
  1047. m_textInfo.meshInfo[i].vertices = null;
  1048. }
  1049. //else if (m_subTextObjects[i].gameObject.activeInHierarchy == false)
  1050. // m_subTextObjects[i].gameObject.SetActive(true);
  1051. // Check if the material has changed.
  1052. if (m_subTextObjects[i].sharedMaterial == null || m_subTextObjects[i].sharedMaterial.GetInstanceID() != m_materialReferences[i].material.GetInstanceID())
  1053. {
  1054. bool isDefaultMaterial = m_materialReferences[i].isDefaultMaterial;
  1055. m_subTextObjects[i].isDefaultMaterial = isDefaultMaterial;
  1056. // Assign new material if we are not using the default material or if the font asset has changed.
  1057. if (!isDefaultMaterial || m_subTextObjects[i].sharedMaterial == null || m_subTextObjects[i].sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID() != m_materialReferences[i].material.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID())
  1058. {
  1059. m_subTextObjects[i].sharedMaterial = m_materialReferences[i].material;
  1060. m_subTextObjects[i].fontAsset = m_materialReferences[i].fontAsset;
  1061. m_subTextObjects[i].spriteAsset = m_materialReferences[i].spriteAsset;
  1062. }
  1063. }
  1064. // Check if we need to use a Fallback Material
  1065. if (m_materialReferences[i].isFallbackMaterial)
  1066. {
  1067. m_subTextObjects[i].fallbackMaterial = m_materialReferences[i].material;
  1068. m_subTextObjects[i].fallbackSourceMaterial = m_materialReferences[i].fallbackMaterial;
  1069. }
  1070. }
  1071. int referenceCount = m_materialReferences[i].referenceCount;
  1072. // Check to make sure our buffers allocations can accommodate the required text elements.
  1073. if (m_textInfo.meshInfo[i].vertices == null || m_textInfo.meshInfo[i].vertices.Length < referenceCount * (!m_isVolumetricText ? 4 : 8))
  1074. {
  1075. if (m_textInfo.meshInfo[i].vertices == null)
  1076. {
  1077. if (i == 0)
  1078. m_textInfo.meshInfo[i] = new TMP_MeshInfo(m_mesh, referenceCount + 1, m_isVolumetricText);
  1079. else
  1080. m_textInfo.meshInfo[i] = new TMP_MeshInfo(m_subTextObjects[i].mesh, referenceCount + 1, m_isVolumetricText);
  1081. }
  1082. else
  1083. m_textInfo.meshInfo[i].ResizeMeshInfo(referenceCount > 1024 ? referenceCount + 256 : Mathf.NextPowerOfTwo(referenceCount), m_isVolumetricText);
  1084. }
  1085. else if (m_textInfo.meshInfo[i].vertices.Length - referenceCount * (!m_isVolumetricText ? 4 : 8) > 1024)
  1086. {
  1087. // Resize vertex buffers if allocations are excessive.
  1088. //Debug.Log("Reducing the size of the vertex buffers.");
  1089. m_textInfo.meshInfo[i].ResizeMeshInfo(referenceCount > 1024 ? referenceCount + 256 : Mathf.Max(Mathf.NextPowerOfTwo(referenceCount), 256), m_isVolumetricText);
  1090. }
  1091. }
  1092. //TMP_MaterialManager.CleanupFallbackMaterials();
  1093. // Clean up unused SubMeshes
  1094. for (int i = materialCount; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
  1095. {
  1096. if (i < m_textInfo.meshInfo.Length)
  1097. m_textInfo.meshInfo[i].ClearUnusedVertices(0, true);
  1098. //m_subTextObjects[i].gameObject.SetActive(false);
  1099. }
  1100. return m_totalCharacterCount;
  1101. }
  1102. // Added to sort handle the potential issue with OnWillRenderObject() not getting called when objects are not visible by camera.
  1103. //void OnBecameInvisible()
  1104. //{
  1105. // if (m_mesh != null)
  1106. // m_mesh.bounds = new Bounds(transform.position, new Vector3(1000, 1000, 0));
  1107. //}
  1108. /// <summary>
  1109. /// Update the margin width and height
  1110. /// </summary>
  1111. public override void ComputeMarginSize()
  1112. {
  1113. if (this.rectTransform != null)
  1114. {
  1115. //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);
  1116. m_marginWidth = m_rectTransform.rect.width - m_margin.x - m_margin.z;
  1117. m_marginHeight = m_rectTransform.rect.height - m_margin.y - m_margin.w;
  1118. // Update the corners of the RectTransform
  1119. m_RectTransformCorners = GetTextContainerLocalCorners();
  1120. }
  1121. }
  1122. protected override void OnDidApplyAnimationProperties()
  1123. {
  1124. //Debug.Log("*** OnDidApplyAnimationProperties() ***");
  1125. m_havePropertiesChanged = true;
  1126. isMaskUpdateRequired = true;
  1127. SetVerticesDirty();
  1128. }
  1129. protected override void OnTransformParentChanged()
  1130. {
  1131. //Debug.Log("*** OnTransformParentChanged() ***");
  1132. SetVerticesDirty();
  1133. SetLayoutDirty();
  1134. }
  1135. protected override void OnRectTransformDimensionsChange()
  1136. {
  1137. //Debug.Log("*** OnRectTransformDimensionsChange() ***");
  1138. ComputeMarginSize();
  1139. SetVerticesDirty();
  1140. SetLayoutDirty();
  1141. }
  1142. /// <summary>
  1143. /// Unity standard function used to check if the transform or scale of the text object has changed.
  1144. /// </summary>
  1145. void LateUpdate()
  1146. {
  1147. // TODO : Review this
  1148. if (m_rectTransform.hasChanged)
  1149. {
  1150. // We need to update the SDF scale or possibly regenerate the text object if lossy scale has changed.
  1151. float lossyScaleY = m_rectTransform.lossyScale.y;
  1152. if (!m_havePropertiesChanged && lossyScaleY != m_previousLossyScaleY && m_text != string.Empty && m_text != null)
  1153. {
  1154. UpdateSDFScale(lossyScaleY);
  1155. m_previousLossyScaleY = lossyScaleY;
  1156. }
  1157. }
  1158. // Added to handle legacy animation mode.
  1159. if (m_isUsingLegacyAnimationComponent)
  1160. {
  1161. //if (m_havePropertiesChanged)
  1162. m_havePropertiesChanged = true;
  1163. OnPreRenderObject();
  1164. }
  1165. }
  1166. /// <summary>
  1167. /// Function called when the text needs to be updated.
  1168. /// </summary>
  1169. void OnPreRenderObject()
  1170. {
  1171. //Debug.Log("*** OnPreRenderObject() ***");
  1172. if (!m_isAwake || (this.IsActive() == false && m_ignoreActiveState == false)) return;
  1173. // Debug Variables
  1174. loopCountA = 0;
  1175. //loopCountB = 0;
  1176. //loopCountC = 0;
  1177. //loopCountD = 0;
  1178. //loopCountE = 0;
  1179. if (m_havePropertiesChanged || m_isLayoutDirty)
  1180. {
  1181. //Debug.Log("Properties have changed!"); // Assigned Material is:" + m_sharedMaterial); // New Text is: " + m_text + ".");
  1182. if (isMaskUpdateRequired)
  1183. {
  1184. UpdateMask();
  1185. isMaskUpdateRequired = false;
  1186. }
  1187. // Update mesh padding if necessary.
  1188. if (checkPaddingRequired)
  1189. UpdateMeshPadding();
  1190. // Reparse the text if the input has changed or text was truncated.
  1191. if (m_isInputParsingRequired || m_isTextTruncated)
  1192. ParseInputText();
  1193. // Reset Font min / max used with Auto-sizing
  1194. if (m_enableAutoSizing)
  1195. m_fontSize = Mathf.Clamp(m_fontSizeBase, m_fontSizeMin, m_fontSizeMax);
  1196. m_maxFontSize = m_fontSizeMax;
  1197. m_minFontSize = m_fontSizeMin;
  1198. m_lineSpacingDelta = 0;
  1199. m_charWidthAdjDelta = 0;
  1200. //m_recursiveCount = 0;
  1201. m_isCharacterWrappingEnabled = false;
  1202. m_isTextTruncated = false;
  1203. m_havePropertiesChanged = false;
  1204. m_isLayoutDirty = false;
  1205. m_ignoreActiveState = false;
  1206. GenerateTextMesh();
  1207. }
  1208. }
  1209. /// <summary>
  1210. /// This is the main function that is responsible for creating / displaying the text.
  1211. /// </summary>
  1212. protected override void GenerateTextMesh()
  1213. {
  1214. //Debug.Log("***** GenerateTextMesh() *****"); // ***** Frame: " + Time.frameCount); // + ". Point Size: " + m_fontSize + ". Margins are (W) " + m_marginWidth + " (H) " + m_marginHeight); // ". Iteration Count: " + loopCountA + ". Min: " + m_minFontSize + " Max: " + m_maxFontSize + " Delta: " + (m_maxFontSize - m_minFontSize) + " Font size is " + m_fontSize); //called for Object with ID " + GetInstanceID()); // Assigned Material is " + m_uiRenderer.GetMaterial().name); // IncludeForMasking " + this.m_IncludeForMasking); // and text is " + m_text);
  1215. // Early exit if no font asset was assigned. This should not be needed since LiberationSans SDF will be assigned by default.
  1216. if (m_fontAsset == null || m_fontAsset.characterDictionary == null)
  1217. {
  1218. Debug.LogWarning("Can't Generate Mesh! No Font Asset has been assigned to Object ID: " + this.GetInstanceID());
  1219. return;
  1220. }
  1221. // Clear TextInfo
  1222. if (m_textInfo != null)
  1223. m_textInfo.Clear();
  1224. // Early exit if we don't have any Text to generate.
  1225. if (m_char_buffer == null || m_char_buffer.Length == 0 || m_char_buffer[0] == (char)0)
  1226. {
  1227. // Clear mesh and upload changes to the mesh.
  1228. ClearMesh(true);
  1229. m_preferredWidth = 0;
  1230. m_preferredHeight = 0;
  1231. // Event indicating the text has been regenerated.
  1232. TMPro_EventManager.ON_TEXT_CHANGED(this);
  1233. return;
  1234. }
  1235. m_currentFontAsset = m_fontAsset;
  1236. m_currentMaterial = m_sharedMaterial;
  1237. m_currentMaterialIndex = 0;
  1238. m_materialReferenceStack.SetDefault(new MaterialReference(m_currentMaterialIndex, m_currentFontAsset, null, m_currentMaterial, m_padding));
  1239. m_currentSpriteAsset = m_spriteAsset;
  1240. // Stop all Sprite Animations
  1241. if (m_spriteAnimator != null)
  1242. m_spriteAnimator.StopAllAnimations();
  1243. // Total character count is computed when the text is parsed.
  1244. int totalCharacterCount = m_totalCharacterCount;
  1245. // Calculate the scale of the font based on selected font size and sampling point size.
  1246. // baseScale is calculated using the font asset assigned to the text object.
  1247. float baseScale = m_fontScale = (m_fontSize / m_fontAsset.fontInfo.PointSize * m_fontAsset.fontInfo.Scale * (m_isOrthographic ? 1 : 0.1f));
  1248. float currentElementScale = baseScale;
  1249. m_fontScaleMultiplier = 1;
  1250. m_currentFontSize = m_fontSize;
  1251. m_sizeStack.SetDefault(m_currentFontSize);
  1252. float fontSizeDelta = 0;
  1253. int charCode = 0; // Holds the character code of the currently being processed character.
  1254. m_style = m_fontStyle; // Set the default style.
  1255. m_fontWeightInternal = (m_style & FontStyles.Bold) == FontStyles.Bold ? 700 : m_fontWeight;
  1256. m_fontWeightStack.SetDefault(m_fontWeightInternal);
  1257. m_fontStyleStack.Clear();
  1258. m_lineJustification = m_textAlignment; // Sets the line justification mode to match editor alignment.
  1259. m_lineJustificationStack.SetDefault(m_lineJustification);
  1260. float padding = 0;
  1261. float style_padding = 0; // Extra padding required to accommodate Bold style.
  1262. float bold_xAdvance_multiplier = 1; // Used to increase spacing between character when style is bold.
  1263. m_baselineOffset = 0; // Used by subscript characters.
  1264. m_baselineOffsetStack.Clear();
  1265. // Underline
  1266. bool beginUnderline = false;
  1267. Vector3 underline_start = Vector3.zero; // Used to track where underline starts & ends.
  1268. Vector3 underline_end = Vector3.zero;
  1269. // Strike-through
  1270. bool beginStrikethrough = false;
  1271. Vector3 strikethrough_start = Vector3.zero;
  1272. Vector3 strikethrough_end = Vector3.zero;
  1273. // Text Highlight
  1274. bool beginHighlight = false;
  1275. Vector3 highlight_start = Vector3.zero;
  1276. Vector3 highlight_end = Vector3.zero;
  1277. m_fontColor32 = m_fontColor;
  1278. Color32 vertexColor;
  1279. m_htmlColor = m_fontColor32;
  1280. m_underlineColor = m_htmlColor;
  1281. m_strikethroughColor = m_htmlColor;
  1282. m_colorStack.SetDefault(m_htmlColor);
  1283. m_underlineColorStack.SetDefault(m_htmlColor);
  1284. m_strikethroughColorStack.SetDefault(m_htmlColor);
  1285. m_highlightColorStack.SetDefault(m_htmlColor);
  1286. m_colorGradientPreset = null;
  1287. m_colorGradientStack.SetDefault(null);
  1288. // Clear the Style stack.
  1289. //m_styleStack.Clear();
  1290. // Clear the Action stack.
  1291. m_actionStack.Clear();
  1292. m_isFXMatrixSet = false;
  1293. m_lineOffset = 0; // Amount of space between lines (font line spacing + m_linespacing).
  1294. m_lineHeight = TMP_Math.FLOAT_UNSET;
  1295. float lineGap = m_currentFontAsset.fontInfo.LineHeight - (m_currentFontAsset.fontInfo.Ascender - m_currentFontAsset.fontInfo.Descender);
  1296. m_cSpacing = 0; // Amount of space added between characters as a result of the use of the <cspace> tag.
  1297. m_monoSpacing = 0;
  1298. float lineOffsetDelta = 0;
  1299. m_xAdvance = 0; // Used to track the position of each character.
  1300. tag_LineIndent = 0; // Used for indentation of text.
  1301. tag_Indent = 0;
  1302. m_indentStack.SetDefault(0);
  1303. tag_NoParsing = false;
  1304. //m_isIgnoringAlignment = false;
  1305. m_characterCount = 0; // Total characters in the char[]
  1306. // Tracking of line information
  1307. m_firstCharacterOfLine = 0;
  1308. m_lastCharacterOfLine = 0;
  1309. m_firstVisibleCharacterOfLine = 0;
  1310. m_lastVisibleCharacterOfLine = 0;
  1311. m_maxLineAscender = k_LargeNegativeFloat;
  1312. m_maxLineDescender = k_LargePositiveFloat;
  1313. m_lineNumber = 0;
  1314. m_lineVisibleCharacterCount = 0;
  1315. bool isStartOfNewLine = true;
  1316. m_firstOverflowCharacterIndex = -1;
  1317. m_pageNumber = 0;
  1318. int pageToDisplay = Mathf.Clamp(m_pageToDisplay - 1, 0, m_textInfo.pageInfo.Length - 1);
  1319. int previousPageOverflowChar = 0;
  1320. int ellipsisIndex = 0;
  1321. Vector4 margins = m_margin;
  1322. float marginWidth = m_marginWidth;
  1323. float marginHeight = m_marginHeight;
  1324. m_marginLeft = 0;
  1325. m_marginRight = 0;
  1326. m_width = -1;
  1327. float width = marginWidth + 0.0001f - m_marginLeft - m_marginRight;
  1328. // Need to initialize these Extents structures
  1329. m_meshExtents.min = k_LargePositiveVector2;
  1330. m_meshExtents.max = k_LargeNegativeVector2;
  1331. // Initialize lineInfo
  1332. m_textInfo.ClearLineInfo();
  1333. // Tracking of the highest Ascender
  1334. m_maxCapHeight = 0;
  1335. m_maxAscender = 0;
  1336. m_maxDescender = 0;
  1337. float pageAscender = 0;
  1338. float maxVisibleDescender = 0;
  1339. bool isMaxVisibleDescenderSet = false;
  1340. m_isNewPage = false;
  1341. // Initialize struct to track states of word wrapping
  1342. bool isFirstWord = true;
  1343. m_isNonBreakingSpace = false;
  1344. bool ignoreNonBreakingSpace = false;
  1345. bool isLastBreakingChar = false;
  1346. float linebreakingWidth = 0;
  1347. int wrappingIndex = 0;
  1348. // Save character and line state before we begin layout.
  1349. SaveWordWrappingState(ref m_SavedWordWrapState, -1, -1);
  1350. SaveWordWrappingState(ref m_SavedLineState, -1, -1);
  1351. loopCountA += 1;
  1352. int endTagIndex = 0;
  1353. // Parse through Character buffer to read HTML tags and begin creating mesh.
  1354. for (int i = 0; i < m_char_buffer.Length && m_char_buffer[i] != 0; i++)
  1355. {
  1356. charCode = m_char_buffer[i];
  1357. // Parse Rich Text Tag
  1358. #region Parse Rich Text Tag
  1359. if (m_isRichText && charCode == 60) // '<'
  1360. {
  1361. m_isParsingText = true;
  1362. m_textElementType = TMP_TextElementType.Character;
  1363. // Check if Tag is valid. If valid, skip to the end of the validated tag.
  1364. if (ValidateHtmlTag(m_char_buffer, i + 1, out endTagIndex))
  1365. {
  1366. i = endTagIndex;
  1367. // Continue to next character or handle the sprite element
  1368. if (m_textElementType == TMP_TextElementType.Character)
  1369. continue;
  1370. }
  1371. }
  1372. else
  1373. {
  1374. m_textElementType = m_textInfo.characterInfo[m_characterCount].elementType;
  1375. m_currentMaterialIndex = m_textInfo.characterInfo[m_characterCount].materialReferenceIndex;
  1376. m_currentFontAsset = m_textInfo.characterInfo[m_characterCount].fontAsset;
  1377. }
  1378. #endregion End Parse Rich Text Tag
  1379. int prev_MaterialIndex = m_currentMaterialIndex;
  1380. bool isUsingAltTypeface = m_textInfo.characterInfo[m_characterCount].isUsingAlternateTypeface;
  1381. m_isParsingText = false;
  1382. // When using Linked text, mark character as ignored and skip to next character.
  1383. if (m_characterCount < m_firstVisibleCharacter)
  1384. {
  1385. m_textInfo.characterInfo[m_characterCount].isVisible = false;
  1386. m_textInfo.characterInfo[m_characterCount].character = (char)0x200B;
  1387. m_characterCount += 1;
  1388. continue;
  1389. }
  1390. // Handle Font Styles like LowerCase, UpperCase and SmallCaps.
  1391. #region Handling of LowerCase, UpperCase and SmallCaps Font Styles
  1392. float smallCapsMultiplier = 1.0f;
  1393. if (m_textElementType == TMP_TextElementType.Character)
  1394. {
  1395. if ((m_style & FontStyles.UpperCase) == FontStyles.UpperCase)
  1396. {
  1397. // If this character is lowercase, switch to uppercase.
  1398. if (char.IsLower((char)charCode))
  1399. charCode = char.ToUpper((char)charCode);
  1400. }
  1401. else if ((m_style & FontStyles.LowerCase) == FontStyles.LowerCase)
  1402. {
  1403. // If this character is uppercase, switch to lowercase.
  1404. if (char.IsUpper((char)charCode))
  1405. charCode = char.ToLower((char)charCode);
  1406. }
  1407. else if ((m_fontStyle & FontStyles.SmallCaps) == FontStyles.SmallCaps || (m_style & FontStyles.SmallCaps) == FontStyles.SmallCaps)
  1408. {
  1409. if (char.IsLower((char)charCode))
  1410. {
  1411. smallCapsMultiplier = 0.8f;
  1412. charCode = char.ToUpper((char)charCode);
  1413. }
  1414. }
  1415. }
  1416. #endregion
  1417. // Look up Character Data from Dictionary and cache it.
  1418. #region Look up Character Data
  1419. if (m_textElementType == TMP_TextElementType.Sprite)
  1420. {
  1421. // If a sprite is used as a fallback then get a reference to it and set the color to white.
  1422. // TODO : Finish adding support for the ability to use Sprites as Fallbacks.
  1423. m_currentSpriteAsset = m_textInfo.characterInfo[m_characterCount].spriteAsset;
  1424. m_spriteIndex = m_textInfo.characterInfo[m_characterCount].spriteIndex;
  1425. TMP_Sprite sprite = m_currentSpriteAsset.spriteInfoList[m_spriteIndex];
  1426. if (sprite == null) continue;
  1427. // Sprites are assigned in the E000 Private Area + sprite Index
  1428. if (charCode == 60)
  1429. charCode = 57344 + m_spriteIndex;
  1430. else
  1431. m_spriteColor = s_colorWhite;
  1432. // The sprite scale calculations are based on the font asset assigned to the text object.
  1433. float spriteScale = (m_currentFontSize / m_currentFontAsset.fontInfo.PointSize * m_currentFontAsset.fontInfo.Scale * (m_isOrthographic ? 1 : 0.1f));
  1434. currentElementScale = m_currentFontAsset.fontInfo.Ascender / sprite.height * sprite.scale * spriteScale;
  1435. m_cached_TextElement = sprite;
  1436. m_textInfo.characterInfo[m_characterCount].elementType = TMP_TextElementType.Sprite;
  1437. m_textInfo.characterInfo[m_characterCount].scale = spriteScale;
  1438. m_textInfo.characterInfo[m_characterCount].spriteAsset = m_currentSpriteAsset;
  1439. m_textInfo.characterInfo[m_characterCount].fontAsset = m_currentFontAsset;
  1440. m_textInfo.characterInfo[m_characterCount].materialReferenceIndex = m_currentMaterialIndex;
  1441. m_currentMaterialIndex = prev_MaterialIndex;
  1442. padding = 0;
  1443. }
  1444. else if (m_textElementType == TMP_TextElementType.Character)
  1445. {
  1446. m_cached_TextElement = m_textInfo.characterInfo[m_characterCount].textElement;
  1447. if (m_cached_TextElement == null) continue;
  1448. m_currentFontAsset = m_textInfo.characterInfo[m_characterCount].fontAsset;
  1449. m_currentMaterial = m_textInfo.characterInfo[m_characterCount].material;
  1450. m_currentMaterialIndex = m_textInfo.characterInfo[m_characterCount].materialReferenceIndex;
  1451. // Re-calculate font scale as the font asset may have changed.
  1452. m_fontScale = m_currentFontSize * smallCapsMultiplier / m_currentFontAsset.fontInfo.PointSize * m_currentFontAsset.fontInfo.Scale * (m_isOrthographic ? 1 : 0.1f);
  1453. currentElementScale = m_fontScale * m_fontScaleMultiplier * m_cached_TextElement.scale;
  1454. m_textInfo.characterInfo[m_characterCount].elementType = TMP_TextElementType.Character;
  1455. m_textInfo.characterInfo[m_characterCount].scale = currentElementScale;
  1456. padding = m_currentMaterialIndex == 0 ? m_padding : m_subTextObjects[m_currentMaterialIndex].padding;
  1457. }
  1458. #endregion
  1459. // Handle Soft Hyphen
  1460. #region Handle Soft Hyphen
  1461. float old_scale = currentElementScale;
  1462. if (charCode == 0xAD)
  1463. {
  1464. currentElementScale = 0;
  1465. }
  1466. #endregion
  1467. // Store some of the text object's information
  1468. m_textInfo.characterInfo[m_characterCount].character = (char)charCode;
  1469. m_textInfo.characterInfo[m_characterCount].pointSize = m_currentFontSize;
  1470. m_textInfo.characterInfo[m_characterCount].color = m_htmlColor;
  1471. m_textInfo.characterInfo[m_characterCount].underlineColor = m_underlineColor;
  1472. m_textInfo.characterInfo[m_characterCount].strikethroughColor = m_strikethroughColor;
  1473. m_textInfo.characterInfo[m_characterCount].highlightColor = m_highlightColor;
  1474. m_textInfo.characterInfo[m_characterCount].style = m_style;
  1475. m_textInfo.characterInfo[m_characterCount].index = i;
  1476. //m_textInfo.characterInfo[m_characterCount].isIgnoringAlignment = m_isIgnoringAlignment;
  1477. // Handle Kerning if Enabled.
  1478. #region Handle Kerning
  1479. GlyphValueRecord glyphAdjustments = new GlyphValueRecord();
  1480. if (m_enableKerning)
  1481. {
  1482. KerningPair adjustmentPair = null;
  1483. if (m_characterCount < totalCharacterCount - 1)
  1484. {
  1485. uint nextGlyph = m_textInfo.characterInfo[m_characterCount + 1].character;
  1486. KerningPairKey keyValue = new KerningPairKey((uint)charCode, nextGlyph);
  1487. m_currentFontAsset.kerningDictionary.TryGetValue((int)keyValue.key, out adjustmentPair);
  1488. if (adjustmentPair != null)
  1489. glyphAdjustments = adjustmentPair.firstGlyphAdjustments;
  1490. }
  1491. if (m_characterCount >= 1)
  1492. {
  1493. uint previousGlyph = m_textInfo.characterInfo[m_characterCount - 1].character;
  1494. KerningPairKey keyValue = new KerningPairKey(previousGlyph, (uint)charCode);
  1495. m_currentFontAsset.kerningDictionary.TryGetValue((int)keyValue.key, out adjustmentPair);
  1496. if (adjustmentPair != null)
  1497. glyphAdjustments += adjustmentPair.secondGlyphAdjustments;
  1498. }
  1499. }
  1500. #endregion
  1501. // Initial Implementation for RTL support.
  1502. #region Handle Right-to-Left
  1503. if (m_isRightToLeft)
  1504. {
  1505. m_xAdvance -= ((m_cached_TextElement.xAdvance * bold_xAdvance_multiplier + m_characterSpacing + m_wordSpacing + m_currentFontAsset.normalSpacingOffset) * currentElementScale + m_cSpacing) * (1 - m_charWidthAdjDelta);
  1506. if (char.IsWhiteSpace((char)charCode) || charCode == 0x200B)
  1507. m_xAdvance -= m_wordSpacing * currentElementScale;
  1508. }
  1509. #endregion
  1510. // Handle Mono Spacing
  1511. #region Handle Mono Spacing
  1512. float monoAdvance = 0;
  1513. if (m_monoSpacing != 0)
  1514. {
  1515. monoAdvance = (m_monoSpacing / 2 - (m_cached_TextElement.width / 2 + m_cached_TextElement.xOffset) * currentElementScale) * (1 - m_charWidthAdjDelta);
  1516. m_xAdvance += monoAdvance;
  1517. }
  1518. #endregion
  1519. // Set Padding based on selected font style
  1520. #region Handle Style Padding
  1521. if (m_textElementType == TMP_TextElementType.Character && !isUsingAltTypeface && ((m_style & FontStyles.Bold) == FontStyles.Bold || (m_fontStyle & FontStyles.Bold) == FontStyles.Bold)) // Checks for any combination of Bold Style.
  1522. {
  1523. if (m_currentMaterial.HasProperty(ShaderUtilities.ID_GradientScale))
  1524. {
  1525. float gradientScale = m_currentMaterial.GetFloat(ShaderUtilities.ID_GradientScale);
  1526. style_padding = m_currentFontAsset.boldStyle / 4.0f * gradientScale * m_currentMaterial.GetFloat(ShaderUtilities.ID_ScaleRatio_A);
  1527. // Clamp overall padding to Gradient Scale size.
  1528. if (style_padding + padding > gradientScale)
  1529. padding = gradientScale - style_padding;
  1530. }
  1531. else
  1532. style_padding = 0;
  1533. bold_xAdvance_multiplier = 1 + m_currentFontAsset.boldSpacing * 0.01f;
  1534. }
  1535. else
  1536. {
  1537. if (m_currentMaterial.HasProperty(ShaderUtilities.ID_GradientScale))
  1538. {
  1539. float gradientScale = m_currentMaterial.GetFloat(ShaderUtilities.ID_GradientScale);
  1540. style_padding = m_currentFontAsset.normalStyle / 4.0f * gradientScale * m_currentMaterial.GetFloat(ShaderUtilities.ID_ScaleRatio_A);
  1541. // Clamp overall padding to Gradient Scale size.
  1542. if (style_padding + padding > gradientScale)
  1543. padding = gradientScale - style_padding;
  1544. }
  1545. else
  1546. style_padding = 0;
  1547. bold_xAdvance_multiplier = 1.0f;
  1548. }
  1549. #endregion Handle Style Padding
  1550. // Determine the position of the vertices of the Character or Sprite.
  1551. #region Calculate Vertices Position
  1552. float fontBaseLineOffset = m_currentFontAsset.fontInfo.Baseline * m_fontScale * m_fontScaleMultiplier * m_currentFontAsset.fontInfo.Scale;
  1553. Vector3 top_left;
  1554. top_left.x = m_xAdvance + ((m_cached_TextElement.xOffset - padding - style_padding + glyphAdjustments.xPlacement) * currentElementScale * (1 - m_charWidthAdjDelta));
  1555. top_left.y = fontBaseLineOffset + (m_cached_TextElement.yOffset + padding + glyphAdjustments.yPlacement) * currentElementScale - m_lineOffset + m_baselineOffset;
  1556. top_left.z = 0;
  1557. Vector3 bottom_left;
  1558. bottom_left.x = top_left.x;
  1559. bottom_left.y = top_left.y - ((m_cached_TextElement.height + padding * 2) * currentElementScale);
  1560. bottom_left.z = 0;
  1561. Vector3 top_right;
  1562. top_right.x = bottom_left.x + ((m_cached_TextElement.width + padding * 2 + style_padding * 2) * currentElementScale * (1 - m_charWidthAdjDelta));
  1563. top_right.y = top_left.y;
  1564. top_right.z = 0;
  1565. Vector3 bottom_right;
  1566. bottom_right.x = top_right.x;
  1567. bottom_right.y = bottom_left.y;
  1568. bottom_right.z = 0;
  1569. #endregion
  1570. // Check if we need to Shear the rectangles for Italic styles
  1571. #region Handle Italic & Shearing
  1572. if (m_textElementType == TMP_TextElementType.Character && !isUsingAltTypeface && ((m_style & FontStyles.Italic) == FontStyles.Italic || (m_fontStyle & FontStyles.Italic) == FontStyles.Italic))
  1573. {
  1574. // Shift Top vertices forward by half (Shear Value * height of character) and Bottom vertices back by same amount.
  1575. float shear_value = m_currentFontAsset.italicStyle * 0.01f;
  1576. Vector3 topShear = new Vector3(shear_value * ((m_cached_TextElement.yOffset + padding + style_padding) * currentElementScale), 0, 0);
  1577. Vector3 bottomShear = new Vector3(shear_value * (((m_cached_TextElement.yOffset - m_cached_TextElement.height - padding - style_padding)) * currentElementScale), 0, 0);
  1578. top_left = top_left + topShear;
  1579. bottom_left = bottom_left + bottomShear;
  1580. top_right = top_right + topShear;
  1581. bottom_right = bottom_right + bottomShear;
  1582. }
  1583. #endregion Handle Italics & Shearing
  1584. // Handle Character Rotation
  1585. #region Handle Character Rotation
  1586. if (m_isFXMatrixSet)
  1587. {
  1588. // Apply scale matrix when simulating Condensed text.
  1589. if (m_FXMatrix.m00 != 1)
  1590. {
  1591. //top_left = m_FXMatrix.MultiplyPoint3x4(top_left);
  1592. //bottom_left = m_FXMatrix.MultiplyPoint3x4(bottom_left);
  1593. //top_right = m_FXMatrix.MultiplyPoint3x4(top_right);
  1594. //bottom_right = m_FXMatrix.MultiplyPoint3x4(bottom_right);
  1595. }
  1596. Vector3 positionOffset = (top_right + bottom_left) / 2;
  1597. top_left = m_FXMatrix.MultiplyPoint3x4(top_left - positionOffset) + positionOffset;
  1598. bottom_left = m_FXMatrix.MultiplyPoint3x4(bottom_left - positionOffset) + positionOffset;
  1599. top_right = m_FXMatrix.MultiplyPoint3x4(top_right - positionOffset) + positionOffset;
  1600. bottom_right = m_FXMatrix.MultiplyPoint3x4(bottom_right - positionOffset) + positionOffset;
  1601. }
  1602. #endregion
  1603. // Store vertex information for the character or sprite.
  1604. m_textInfo.characterInfo[m_characterCount].bottomLeft = bottom_left;
  1605. m_textInfo.characterInfo[m_characterCount].topLeft = top_left;
  1606. m_textInfo.characterInfo[m_characterCount].topRight = top_right;
  1607. m_textInfo.characterInfo[m_characterCount].bottomRight = bottom_right;
  1608. m_textInfo.characterInfo[m_characterCount].origin = m_xAdvance;
  1609. m_textInfo.characterInfo[m_characterCount].baseLine = fontBaseLineOffset - m_lineOffset + m_baselineOffset;
  1610. m_textInfo.characterInfo[m_characterCount].aspectRatio = (top_right.x - bottom_left.x) / (top_left.y - bottom_left.y);
  1611. // Compute and save text element Ascender and maximum line Ascender.
  1612. float elementAscender = m_currentFontAsset.fontInfo.Ascender * (m_textElementType == TMP_TextElementType.Character ? currentElementScale / smallCapsMultiplier : m_textInfo.characterInfo[m_characterCount].scale) + m_baselineOffset;
  1613. m_textInfo.characterInfo[m_characterCount].ascender = elementAscender - m_lineOffset;
  1614. m_maxLineAscender = elementAscender > m_maxLineAscender ? elementAscender : m_maxLineAscender;
  1615. // Compute and save text element Descender and maximum line Descender.
  1616. float elementDescender = m_currentFontAsset.fontInfo.Descender * (m_textElementType == TMP_TextElementType.Character ? currentElementScale / smallCapsMultiplier : m_textInfo.characterInfo[m_characterCount].scale) + m_baselineOffset;
  1617. float elementDescenderII = m_textInfo.characterInfo[m_characterCount].descender = elementDescender - m_lineOffset;
  1618. m_maxLineDescender = elementDescender < m_maxLineDescender ? elementDescender : m_maxLineDescender;
  1619. // Adjust maxLineAscender and maxLineDescender if style is superscript or subscript
  1620. if ((m_style & FontStyles.Subscript) == FontStyles.Subscript || (m_style & FontStyles.Superscript) == FontStyles.Superscript)
  1621. {
  1622. float baseAscender = (elementAscender - m_baselineOffset) / m_currentFontAsset.fontInfo.SubSize;
  1623. elementAscender = m_maxLineAscender;
  1624. m_maxLineAscender = baseAscender > m_maxLineAscender ? baseAscender : m_maxLineAscender;
  1625. float baseDescender = (elementDescender - m_baselineOffset) / m_currentFontAsset.fontInfo.SubSize;
  1626. elementDescender = m_maxLineDescender;
  1627. m_maxLineDescender = baseDescender < m_maxLineDescender ? baseDescender : m_maxLineDescender;
  1628. }
  1629. if (m_lineNumber == 0 || m_isNewPage)
  1630. {
  1631. m_maxAscender = m_maxAscender > elementAscender ? m_maxAscender : elementAscender;
  1632. m_maxCapHeight = Mathf.Max(m_maxCapHeight, m_currentFontAsset.fontInfo.CapHeight * currentElementScale / smallCapsMultiplier);
  1633. }
  1634. if (m_lineOffset == 0) pageAscender = pageAscender > elementAscender ? pageAscender : elementAscender;
  1635. // Set Characters to not visible by default.
  1636. m_textInfo.characterInfo[m_characterCount].isVisible = false;
  1637. // Setup Mesh for visible text elements. ie. not a SPACE / LINEFEED / CARRIAGE RETURN.
  1638. #region Handle Visible Characters
  1639. if (charCode == 9 || charCode == 0xA0 || charCode == 0x2007 || (!char.IsWhiteSpace((char)charCode) && charCode != 0x200B) || m_textElementType == TMP_TextElementType.Sprite)
  1640. {
  1641. m_textInfo.characterInfo[m_characterCount].isVisible = true;
  1642. #region Experimental Margin Shaper
  1643. //Vector2 shapedMargins;
  1644. //if (marginShaper)
  1645. //{
  1646. // shapedMargins = m_marginShaper.GetShapedMargins(m_textInfo.characterInfo[m_characterCount].baseLine);
  1647. // if (shapedMargins.x < margins.x)
  1648. // {
  1649. // shapedMargins.x = m_marginLeft;
  1650. // }
  1651. // else
  1652. // {
  1653. // shapedMargins.x += m_marginLeft - margins.x;
  1654. // }
  1655. // if (shapedMargins.y < margins.z)
  1656. // {
  1657. // shapedMargins.y = m_marginRight;
  1658. // }
  1659. // else
  1660. // {
  1661. // shapedMargins.y += m_marginRight - margins.z;
  1662. // }
  1663. //}
  1664. //else
  1665. //{
  1666. // shapedMargins.x = m_marginLeft;
  1667. // shapedMargins.y = m_marginRight;
  1668. //}
  1669. //width = marginWidth + 0.0001f - shapedMargins.x - shapedMargins.y;
  1670. //if (m_width != -1 && m_width < width)
  1671. //{
  1672. // width = m_width;
  1673. //}
  1674. //m_textInfo.lineInfo[m_lineNumber].marginLeft = shapedMargins.x;
  1675. #endregion
  1676. width = m_width != -1 ? Mathf.Min(marginWidth + 0.0001f - m_marginLeft - m_marginRight, m_width) : marginWidth + 0.0001f - m_marginLeft - m_marginRight;
  1677. m_textInfo.lineInfo[m_lineNumber].marginLeft = m_marginLeft;
  1678. bool isJustifiedOrFlush = ((_HorizontalAlignmentOptions)m_lineJustification & _HorizontalAlignmentOptions.Flush) == _HorizontalAlignmentOptions.Flush || ((_HorizontalAlignmentOptions)m_lineJustification & _HorizontalAlignmentOptions.Justified) == _HorizontalAlignmentOptions.Justified;
  1679. // Calculate the line breaking width of the text.
  1680. linebreakingWidth = Mathf.Abs(m_xAdvance) + (!m_isRightToLeft ? m_cached_TextElement.xAdvance : 0) * (1 - m_charWidthAdjDelta) * (charCode != 0xAD ? currentElementScale : old_scale);
  1681. // Check if Character exceeds the width of the Text Container
  1682. #region Handle Line Breaking, Text Auto-Sizing and Horizontal Overflow
  1683. if (linebreakingWidth > width * (isJustifiedOrFlush ? 1.05f : 1.0f))
  1684. {
  1685. ellipsisIndex = m_characterCount - 1; // Last safely rendered character
  1686. // Word Wrapping
  1687. #region Handle Word Wrapping
  1688. if (enableWordWrapping && m_characterCount != m_firstCharacterOfLine)
  1689. {
  1690. // Check if word wrapping is still possible
  1691. #region Line Breaking Check
  1692. if (wrappingIndex == m_SavedWordWrapState.previous_WordBreak || isFirstWord)
  1693. {
  1694. // Word wrapping is no longer possible. Shrink size of text if auto-sizing is enabled.
  1695. if (m_enableAutoSizing && m_fontSize > m_fontSizeMin)
  1696. {
  1697. // Handle Character Width Adjustments
  1698. #region Character Width Adjustments
  1699. if (m_charWidthAdjDelta < m_charWidthMaxAdj / 100)
  1700. {
  1701. loopCountA = 0;
  1702. m_charWidthAdjDelta += 0.01f;
  1703. GenerateTextMesh();
  1704. return;
  1705. }
  1706. #endregion
  1707. // Adjust Point Size
  1708. m_maxFontSize = m_fontSize;
  1709. m_fontSize -= Mathf.Max((m_fontSize - m_minFontSize) / 2, 0.05f);
  1710. m_fontSize = (int)(Mathf.Max(m_fontSize, m_fontSizeMin) * 20 + 0.5f) / 20f;
  1711. if (loopCountA > 20) return; // Added to debug
  1712. GenerateTextMesh();
  1713. return;
  1714. }
  1715. // Word wrapping is no longer possible, now breaking up individual words.
  1716. if (m_isCharacterWrappingEnabled == false)
  1717. {
  1718. if (ignoreNonBreakingSpace == false)
  1719. ignoreNonBreakingSpace = true;
  1720. else
  1721. m_isCharacterWrappingEnabled = true;
  1722. }
  1723. else
  1724. isLastBreakingChar = true;
  1725. //m_recursiveCount += 1;
  1726. //if (m_recursiveCount > 20)
  1727. //{
  1728. // Debug.Log("Recursive count exceeded!");
  1729. // continue;
  1730. //}
  1731. }
  1732. #endregion
  1733. // Restore to previously stored state of last valid (space character or linefeed)
  1734. i = RestoreWordWrappingState(ref m_SavedWordWrapState);
  1735. wrappingIndex = i; // Used to detect when line length can no longer be reduced.
  1736. // Handling for Soft Hyphen
  1737. if (m_char_buffer[i] == 0xAD) // && !m_isCharacterWrappingEnabled) // && ellipsisIndex != i && !m_isCharacterWrappingEnabled)
  1738. {
  1739. m_isTextTruncated = true;
  1740. m_char_buffer[i] = 0x2D;
  1741. GenerateTextMesh();
  1742. return;
  1743. }
  1744. //Debug.Log("Last Visible Character of line # " + m_lineNumber + " is [" + m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].character + " Character Count: " + m_characterCount + " Last visible: " + m_lastVisibleCharacterOfLine);
  1745. // Check if Line Spacing of previous line needs to be adjusted.
  1746. if (m_lineNumber > 0 && !TMP_Math.Approximately(m_maxLineAscender, m_startOfLineAscender) && m_lineHeight == TMP_Math.FLOAT_UNSET && !m_isNewPage)
  1747. {
  1748. //Debug.Log("(Line Break - Adjusting Line Spacing on line #" + m_lineNumber);
  1749. float offsetDelta = m_maxLineAscender - m_startOfLineAscender;
  1750. AdjustLineOffset(m_firstCharacterOfLine, m_characterCount, offsetDelta);
  1751. m_lineOffset += offsetDelta;
  1752. m_SavedWordWrapState.lineOffset = m_lineOffset;
  1753. m_SavedWordWrapState.previousLineAscender = m_maxLineAscender;
  1754. // TODO - Add check for character exceeding vertical bounds
  1755. }
  1756. m_isNewPage = false;
  1757. // Calculate lineAscender & make sure if last character is superscript or subscript that we check that as well.
  1758. float lineAscender = m_maxLineAscender - m_lineOffset;
  1759. float lineDescender = m_maxLineDescender - m_lineOffset;
  1760. // Update maxDescender and maxVisibleDescender
  1761. m_maxDescender = m_maxDescender < lineDescender ? m_maxDescender : lineDescender;
  1762. if (!isMaxVisibleDescenderSet)
  1763. maxVisibleDescender = m_maxDescender;
  1764. if (m_useMaxVisibleDescender && (m_characterCount >= m_maxVisibleCharacters || m_lineNumber >= m_maxVisibleLines))
  1765. isMaxVisibleDescenderSet = true;
  1766. // Track & Store lineInfo for the new line
  1767. m_textInfo.lineInfo[m_lineNumber].firstCharacterIndex = m_firstCharacterOfLine;
  1768. m_textInfo.lineInfo[m_lineNumber].firstVisibleCharacterIndex = m_firstVisibleCharacterOfLine = m_firstCharacterOfLine > m_firstVisibleCharacterOfLine ? m_firstCharacterOfLine : m_firstVisibleCharacterOfLine;
  1769. m_textInfo.lineInfo[m_lineNumber].lastCharacterIndex = m_lastCharacterOfLine = m_characterCount - 1 > 0 ? m_characterCount - 1 : 0;
  1770. m_textInfo.lineInfo[m_lineNumber].lastVisibleCharacterIndex = m_lastVisibleCharacterOfLine = m_lastVisibleCharacterOfLine < m_firstVisibleCharacterOfLine ? m_firstVisibleCharacterOfLine : m_lastVisibleCharacterOfLine;
  1771. m_textInfo.lineInfo[m_lineNumber].characterCount = m_textInfo.lineInfo[m_lineNumber].lastCharacterIndex - m_textInfo.lineInfo[m_lineNumber].firstCharacterIndex + 1;
  1772. m_textInfo.lineInfo[m_lineNumber].visibleCharacterCount = m_lineVisibleCharacterCount;
  1773. m_textInfo.lineInfo[m_lineNumber].lineExtents.min = new Vector2(m_textInfo.characterInfo[m_firstVisibleCharacterOfLine].bottomLeft.x, lineDescender);
  1774. m_textInfo.lineInfo[m_lineNumber].lineExtents.max = new Vector2(m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].topRight.x, lineAscender);
  1775. m_textInfo.lineInfo[m_lineNumber].length = m_textInfo.lineInfo[m_lineNumber].lineExtents.max.x;
  1776. m_textInfo.lineInfo[m_lineNumber].width = width;
  1777. //m_textInfo.lineInfo[m_lineNumber].alignment = m_lineJustification;
  1778. m_textInfo.lineInfo[m_lineNumber].maxAdvance = m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].xAdvance - (m_characterSpacing + m_currentFontAsset.normalSpacingOffset) * currentElementScale - m_cSpacing;
  1779. m_textInfo.lineInfo[m_lineNumber].baseline = 0 - m_lineOffset;
  1780. m_textInfo.lineInfo[m_lineNumber].ascender = lineAscender;
  1781. m_textInfo.lineInfo[m_lineNumber].descender = lineDescender;
  1782. m_textInfo.lineInfo[m_lineNumber].lineHeight = lineAscender - lineDescender + lineGap * baseScale;
  1783. m_firstCharacterOfLine = m_characterCount; // Store first character of the next line.
  1784. m_lineVisibleCharacterCount = 0;
  1785. // Store the state of the line before starting on the new line.
  1786. SaveWordWrappingState(ref m_SavedLineState, i, m_characterCount - 1);
  1787. m_lineNumber += 1;
  1788. isStartOfNewLine = true;
  1789. isFirstWord = true;
  1790. // Check to make sure Array is large enough to hold a new line.
  1791. if (m_lineNumber >= m_textInfo.lineInfo.Length)
  1792. ResizeLineExtents(m_lineNumber);
  1793. // Apply Line Spacing based on scale of the last character of the line.
  1794. if (m_lineHeight == TMP_Math.FLOAT_UNSET)
  1795. {
  1796. float ascender = m_textInfo.characterInfo[m_characterCount].ascender - m_textInfo.characterInfo[m_characterCount].baseLine;
  1797. lineOffsetDelta = 0 - m_maxLineDescender + ascender + (lineGap + m_lineSpacing + m_lineSpacingDelta) * baseScale;
  1798. m_lineOffset += lineOffsetDelta;
  1799. m_startOfLineAscender = ascender;
  1800. }
  1801. else
  1802. m_lineOffset += m_lineHeight + m_lineSpacing * baseScale;
  1803. m_maxLineAscender = k_LargeNegativeFloat;
  1804. m_maxLineDescender = k_LargePositiveFloat;
  1805. m_xAdvance = 0 + tag_Indent;
  1806. continue;
  1807. }
  1808. #endregion End Word Wrapping
  1809. // Text Auto-Sizing (text exceeding Width of container.
  1810. #region Handle Text Auto-Sizing
  1811. if (m_enableAutoSizing && m_fontSize > m_fontSizeMin)
  1812. {
  1813. // Handle Character Width Adjustments
  1814. #region Character Width Adjustments
  1815. if (m_charWidthAdjDelta < m_charWidthMaxAdj / 100)
  1816. {
  1817. loopCountA = 0;
  1818. m_charWidthAdjDelta += 0.01f;
  1819. GenerateTextMesh();
  1820. return;
  1821. }
  1822. #endregion
  1823. // Adjust Point Size
  1824. m_maxFontSize = m_fontSize;
  1825. m_fontSize -= Mathf.Max((m_fontSize - m_minFontSize) / 2, 0.05f);
  1826. m_fontSize = (int)(Mathf.Max(m_fontSize, m_fontSizeMin) * 20 + 0.5f) / 20f;
  1827. //m_recursiveCount = 0;
  1828. if (loopCountA > 20) return; // Added to debug
  1829. GenerateTextMesh();
  1830. return;
  1831. }
  1832. #endregion End Text Auto-Sizing
  1833. // Handle Text Overflow
  1834. #region Handle Text Overflow
  1835. switch (m_overflowMode)
  1836. {
  1837. case TextOverflowModes.Overflow:
  1838. if (m_isMaskingEnabled)
  1839. DisableMasking();
  1840. break;
  1841. case TextOverflowModes.Ellipsis:
  1842. if (m_isMaskingEnabled)
  1843. DisableMasking();
  1844. m_isTextTruncated = true;
  1845. if (m_characterCount < 1)
  1846. {
  1847. m_textInfo.characterInfo[m_characterCount].isVisible = false;
  1848. //m_visibleCharacterCount = 0;
  1849. break;
  1850. }
  1851. m_char_buffer[i - 1] = 8230;
  1852. m_char_buffer[i] = (char)0;
  1853. if (m_cached_Ellipsis_GlyphInfo != null)
  1854. {
  1855. m_textInfo.characterInfo[ellipsisIndex].character = (char)8230;
  1856. m_textInfo.characterInfo[ellipsisIndex].textElement = m_cached_Ellipsis_GlyphInfo;
  1857. m_textInfo.characterInfo[ellipsisIndex].fontAsset = m_materialReferences[0].fontAsset;
  1858. m_textInfo.characterInfo[ellipsisIndex].material = m_materialReferences[0].material;
  1859. m_textInfo.characterInfo[ellipsisIndex].materialReferenceIndex = 0;
  1860. }
  1861. else
  1862. {
  1863. Debug.LogWarning("Unable to use Ellipsis character since it wasn't found in the current Font Asset [" + m_fontAsset.name + "]. Consider regenerating this font asset to include the Ellipsis character (u+2026).\nNote: Warnings can be disabled in the TMP Settings file.", this);
  1864. }
  1865. m_totalCharacterCount = ellipsisIndex + 1;
  1866. GenerateTextMesh();
  1867. return;
  1868. case TextOverflowModes.Masking:
  1869. if (!m_isMaskingEnabled)
  1870. EnableMasking();
  1871. break;
  1872. case TextOverflowModes.ScrollRect:
  1873. if (!m_isMaskingEnabled)
  1874. EnableMasking();
  1875. break;
  1876. case TextOverflowModes.Truncate:
  1877. if (m_isMaskingEnabled)
  1878. DisableMasking();
  1879. m_textInfo.characterInfo[m_characterCount].isVisible = false;
  1880. break;
  1881. case TextOverflowModes.Linked:
  1882. //m_textInfo.characterInfo[m_characterCount].isVisible = false;
  1883. //if (m_linkedTextComponent != null)
  1884. //{
  1885. // m_linkedTextComponent.text = text;
  1886. // m_linkedTextComponent.firstVisibleCharacter = m_characterCount;
  1887. // m_linkedTextComponent.ForceMeshUpdate();
  1888. //}
  1889. break;
  1890. }
  1891. #endregion End Text Overflow
  1892. }
  1893. #endregion End Check for Characters Exceeding Width of Text Container
  1894. // Special handling of characters that are not ignored at the end of a line.
  1895. if (charCode == 9 || charCode == 0xA0 || charCode == 0x2007)
  1896. {
  1897. m_textInfo.characterInfo[m_characterCount].isVisible = false;
  1898. m_lastVisibleCharacterOfLine = m_characterCount;
  1899. m_textInfo.lineInfo[m_lineNumber].spaceCount += 1;
  1900. m_textInfo.spaceCount += 1;
  1901. if (charCode == 0xA0)
  1902. m_textInfo.lineInfo[m_lineNumber].controlCharacterCount += 1;
  1903. }
  1904. else
  1905. {
  1906. // Determine Vertex Color
  1907. if (m_overrideHtmlColors)
  1908. vertexColor = m_fontColor32;
  1909. else
  1910. vertexColor = m_htmlColor;
  1911. // Store Character & Sprite Vertex Information
  1912. if (m_textElementType == TMP_TextElementType.Character)
  1913. {
  1914. // Save Character Vertex Data
  1915. SaveGlyphVertexInfo(padding, style_padding, vertexColor);
  1916. }
  1917. else if (m_textElementType == TMP_TextElementType.Sprite)
  1918. {
  1919. SaveSpriteVertexInfo(vertexColor);
  1920. }
  1921. }
  1922. // Increase visible count for Characters.
  1923. if (m_textInfo.characterInfo[m_characterCount].isVisible && charCode != 0xAD)
  1924. {
  1925. if (isStartOfNewLine) { isStartOfNewLine = false; m_firstVisibleCharacterOfLine = m_characterCount; }
  1926. m_lineVisibleCharacterCount += 1;
  1927. m_lastVisibleCharacterOfLine = m_characterCount;
  1928. }
  1929. }
  1930. else
  1931. { // This is a Space, Tab, LineFeed or Carriage Return
  1932. // Track # of spaces per line which is used for line justification.
  1933. if ((charCode == 10 || char.IsSeparator((char)charCode)) && charCode != 0xAD && charCode != 0x200B && charCode != 0x2060)
  1934. {
  1935. m_textInfo.lineInfo[m_lineNumber].spaceCount += 1;
  1936. m_textInfo.spaceCount += 1;
  1937. }
  1938. }
  1939. #endregion Handle Visible Characters
  1940. // Check if Line Spacing of previous line needs to be adjusted.
  1941. #region Adjust Line Spacing
  1942. if (m_lineNumber > 0 && !TMP_Math.Approximately(m_maxLineAscender, m_startOfLineAscender) && m_lineHeight == TMP_Math.FLOAT_UNSET && !m_isNewPage)
  1943. {
  1944. //Debug.Log("Inline - Adjusting Line Spacing on line #" + m_lineNumber);
  1945. //float gap = 0; // Compute gap.
  1946. float offsetDelta = m_maxLineAscender - m_startOfLineAscender;
  1947. AdjustLineOffset(m_firstCharacterOfLine, m_characterCount, offsetDelta);
  1948. elementDescenderII -= offsetDelta;
  1949. m_lineOffset += offsetDelta;
  1950. m_startOfLineAscender += offsetDelta;
  1951. m_SavedWordWrapState.lineOffset = m_lineOffset;
  1952. m_SavedWordWrapState.previousLineAscender = m_startOfLineAscender;
  1953. }
  1954. #endregion
  1955. // Store Rectangle positions for each Character.
  1956. #region Store Character Data
  1957. m_textInfo.characterInfo[m_characterCount].lineNumber = m_lineNumber;
  1958. m_textInfo.characterInfo[m_characterCount].pageNumber = m_pageNumber;
  1959. if (charCode != 10 && charCode != 13 && charCode != 8230 || m_textInfo.lineInfo[m_lineNumber].characterCount == 1)
  1960. m_textInfo.lineInfo[m_lineNumber].alignment = m_lineJustification;
  1961. #endregion Store Character Data
  1962. // Check if text Exceeds the vertical bounds of the margin area.
  1963. #region Check Vertical Bounds & Auto-Sizing
  1964. if (m_maxAscender - elementDescenderII > marginHeight + 0.0001f)
  1965. {
  1966. // Handle Line spacing adjustments
  1967. #region Line Spacing Adjustments
  1968. if (m_enableAutoSizing && m_lineSpacingDelta > m_lineSpacingMax && m_lineNumber > 0)
  1969. {
  1970. loopCountA = 0;
  1971. m_lineSpacingDelta -= 1;
  1972. GenerateTextMesh();
  1973. return;
  1974. }
  1975. #endregion
  1976. // Handle Text Auto-sizing resulting from text exceeding vertical bounds.
  1977. #region Text Auto-Sizing (Text greater than vertical bounds)
  1978. if (m_enableAutoSizing && m_fontSize > m_fontSizeMin)
  1979. {
  1980. m_maxFontSize = m_fontSize;
  1981. m_fontSize -= Mathf.Max((m_fontSize - m_minFontSize) / 2, 0.05f);
  1982. m_fontSize = (int)(Mathf.Max(m_fontSize, m_fontSizeMin) * 20 + 0.5f) / 20f;
  1983. //m_recursiveCount = 0;
  1984. if (loopCountA > 20) return; // Added to debug
  1985. GenerateTextMesh();
  1986. return;
  1987. }
  1988. #endregion Text Auto-Sizing
  1989. // Set isTextOverflowing and firstOverflowCharacterIndex
  1990. if (m_firstOverflowCharacterIndex == -1)
  1991. m_firstOverflowCharacterIndex = m_characterCount;
  1992. // Handle Text Overflow
  1993. #region Text Overflow
  1994. switch (m_overflowMode)
  1995. {
  1996. case TextOverflowModes.Overflow:
  1997. if (m_isMaskingEnabled)
  1998. DisableMasking();
  1999. break;
  2000. case TextOverflowModes.Ellipsis:
  2001. if (m_isMaskingEnabled)
  2002. DisableMasking();
  2003. if (m_lineNumber > 0)
  2004. {
  2005. m_char_buffer[m_textInfo.characterInfo[ellipsisIndex].index] = 8230;
  2006. m_char_buffer[m_textInfo.characterInfo[ellipsisIndex].index + 1] = (char)0;
  2007. if (m_cached_Ellipsis_GlyphInfo != null)
  2008. {
  2009. m_textInfo.characterInfo[ellipsisIndex].character = (char)8230;
  2010. m_textInfo.characterInfo[ellipsisIndex].textElement = m_cached_Ellipsis_GlyphInfo;
  2011. m_textInfo.characterInfo[ellipsisIndex].fontAsset = m_materialReferences[0].fontAsset;
  2012. m_textInfo.characterInfo[ellipsisIndex].material = m_materialReferences[0].material;
  2013. m_textInfo.characterInfo[ellipsisIndex].materialReferenceIndex = 0;
  2014. }
  2015. else
  2016. {
  2017. Debug.LogWarning("Unable to use Ellipsis character since it wasn't found in the current Font Asset [" + m_fontAsset.name + "]. Consider regenerating this font asset to include the Ellipsis character (u+2026).\nNote: Warnings can be disabled in the TMP Settings file.", this);
  2018. }
  2019. m_totalCharacterCount = ellipsisIndex + 1;
  2020. GenerateTextMesh();
  2021. m_isTextTruncated = true;
  2022. return;
  2023. }
  2024. else
  2025. {
  2026. ClearMesh(false);
  2027. return;
  2028. }
  2029. case TextOverflowModes.Masking:
  2030. if (!m_isMaskingEnabled)
  2031. EnableMasking();
  2032. break;
  2033. case TextOverflowModes.ScrollRect:
  2034. if (!m_isMaskingEnabled)
  2035. EnableMasking();
  2036. break;
  2037. case TextOverflowModes.Truncate:
  2038. if (m_isMaskingEnabled)
  2039. DisableMasking();
  2040. // TODO : Optimize
  2041. if (m_lineNumber > 0)
  2042. {
  2043. m_char_buffer[m_textInfo.characterInfo[ellipsisIndex].index + 1] = (char)0;
  2044. m_totalCharacterCount = ellipsisIndex + 1;
  2045. GenerateTextMesh();
  2046. m_isTextTruncated = true;
  2047. return;
  2048. }
  2049. else
  2050. {
  2051. ClearMesh(false);
  2052. return;
  2053. }
  2054. case TextOverflowModes.Page:
  2055. if (m_isMaskingEnabled)
  2056. DisableMasking();
  2057. // Ignore Page Break, Linefeed or carriage return
  2058. if (charCode == 13 || charCode == 10)
  2059. break;
  2060. // Return if the first character doesn't fit.
  2061. if (i == 0)
  2062. {
  2063. ClearMesh();
  2064. return;
  2065. }
  2066. else if (previousPageOverflowChar == i)
  2067. {
  2068. m_char_buffer[i] = 0;
  2069. m_isTextTruncated = true;
  2070. }
  2071. previousPageOverflowChar = i;
  2072. // Go back to previous line and re-layout
  2073. i = RestoreWordWrappingState(ref m_SavedLineState);
  2074. m_isNewPage = true;
  2075. m_xAdvance = 0 + tag_Indent;
  2076. m_lineOffset = 0;
  2077. m_maxAscender = 0;
  2078. pageAscender = 0;
  2079. m_lineNumber += 1;
  2080. m_pageNumber += 1;
  2081. continue;
  2082. case TextOverflowModes.Linked:
  2083. if (m_linkedTextComponent != null)
  2084. {
  2085. m_linkedTextComponent.text = text;
  2086. m_linkedTextComponent.firstVisibleCharacter = m_characterCount;
  2087. m_linkedTextComponent.ForceMeshUpdate();
  2088. }
  2089. // Truncate remaining text
  2090. if (m_lineNumber > 0)
  2091. {
  2092. m_char_buffer[i] = (char)0;
  2093. m_totalCharacterCount = m_characterCount;
  2094. // TODO : Optimize as we should be able to end the layout phase here without having to do another pass.
  2095. GenerateTextMesh();
  2096. m_isTextTruncated = true;
  2097. return;
  2098. }
  2099. else
  2100. {
  2101. ClearMesh(true);
  2102. return;
  2103. }
  2104. }
  2105. #endregion End Text Overflow
  2106. }
  2107. #endregion Check Vertical Bounds
  2108. // Handle xAdvance & Tabulation Stops. Tab stops at every 25% of Font Size.
  2109. #region XAdvance, Tabulation & Stops
  2110. if (charCode == 9)
  2111. {
  2112. float tabSize = m_currentFontAsset.fontInfo.TabWidth * currentElementScale;
  2113. float tabs = Mathf.Ceil(m_xAdvance / tabSize) * tabSize;
  2114. m_xAdvance = tabs > m_xAdvance ? tabs : m_xAdvance + tabSize;
  2115. }
  2116. else if (m_monoSpacing != 0)
  2117. {
  2118. m_xAdvance += (m_monoSpacing - monoAdvance + ((m_characterSpacing + m_currentFontAsset.normalSpacingOffset) * currentElementScale) + m_cSpacing) * (1 - m_charWidthAdjDelta);
  2119. if (char.IsWhiteSpace((char)charCode) || charCode == 0x200B)
  2120. m_xAdvance += m_wordSpacing * currentElementScale;
  2121. }
  2122. else if (!m_isRightToLeft)
  2123. {
  2124. float scaleFXMultiplier = 1;
  2125. if (m_isFXMatrixSet) scaleFXMultiplier = m_FXMatrix.m00;
  2126. m_xAdvance += ((m_cached_TextElement.xAdvance * scaleFXMultiplier * bold_xAdvance_multiplier + m_characterSpacing + m_currentFontAsset.normalSpacingOffset + glyphAdjustments.xAdvance) * currentElementScale + m_cSpacing) * (1 - m_charWidthAdjDelta);
  2127. if (char.IsWhiteSpace((char)charCode) || charCode == 0x200B)
  2128. m_xAdvance += m_wordSpacing * currentElementScale;
  2129. }
  2130. else
  2131. {
  2132. m_xAdvance -= glyphAdjustments.xAdvance * currentElementScale;
  2133. }
  2134. // Store xAdvance information
  2135. m_textInfo.characterInfo[m_characterCount].xAdvance = m_xAdvance;
  2136. #endregion Tabulation & Stops
  2137. // Handle Carriage Return
  2138. #region Carriage Return
  2139. if (charCode == 13)
  2140. {
  2141. m_xAdvance = 0 + tag_Indent;
  2142. }
  2143. #endregion Carriage Return
  2144. // Handle Line Spacing Adjustments + Word Wrapping & special case for last line.
  2145. #region Check for Line Feed and Last Character
  2146. if (charCode == 10 || m_characterCount == totalCharacterCount - 1)
  2147. {
  2148. // Check if Line Spacing of previous line needs to be adjusted.
  2149. if (m_lineNumber > 0 && !TMP_Math.Approximately(m_maxLineAscender, m_startOfLineAscender) && m_lineHeight == TMP_Math.FLOAT_UNSET && !m_isNewPage)
  2150. {
  2151. //Debug.Log("Line Feed - Adjusting Line Spacing on line #" + m_lineNumber);
  2152. float offsetDelta = m_maxLineAscender - m_startOfLineAscender;
  2153. AdjustLineOffset(m_firstCharacterOfLine, m_characterCount, offsetDelta);
  2154. elementDescenderII -= offsetDelta;
  2155. m_lineOffset += offsetDelta;
  2156. }
  2157. m_isNewPage = false;
  2158. // Calculate lineAscender & make sure if last character is superscript or subscript that we check that as well.
  2159. float lineAscender = m_maxLineAscender - m_lineOffset;
  2160. float lineDescender = m_maxLineDescender - m_lineOffset;
  2161. // Update maxDescender and maxVisibleDescender
  2162. m_maxDescender = m_maxDescender < lineDescender ? m_maxDescender : lineDescender;
  2163. if (!isMaxVisibleDescenderSet)
  2164. maxVisibleDescender = m_maxDescender;
  2165. if (m_useMaxVisibleDescender && (m_characterCount >= m_maxVisibleCharacters || m_lineNumber >= m_maxVisibleLines))
  2166. isMaxVisibleDescenderSet = true;
  2167. // Save Line Information
  2168. m_textInfo.lineInfo[m_lineNumber].firstCharacterIndex = m_firstCharacterOfLine;
  2169. m_textInfo.lineInfo[m_lineNumber].firstVisibleCharacterIndex = m_firstVisibleCharacterOfLine = m_firstCharacterOfLine > m_firstVisibleCharacterOfLine ? m_firstCharacterOfLine : m_firstVisibleCharacterOfLine;
  2170. m_textInfo.lineInfo[m_lineNumber].lastCharacterIndex = m_lastCharacterOfLine = m_characterCount;
  2171. m_textInfo.lineInfo[m_lineNumber].lastVisibleCharacterIndex = m_lastVisibleCharacterOfLine = m_lastVisibleCharacterOfLine < m_firstVisibleCharacterOfLine ? m_firstVisibleCharacterOfLine : m_lastVisibleCharacterOfLine;
  2172. m_textInfo.lineInfo[m_lineNumber].characterCount = m_textInfo.lineInfo[m_lineNumber].lastCharacterIndex - m_textInfo.lineInfo[m_lineNumber].firstCharacterIndex + 1;
  2173. m_textInfo.lineInfo[m_lineNumber].visibleCharacterCount = m_lineVisibleCharacterCount;
  2174. m_textInfo.lineInfo[m_lineNumber].lineExtents.min = new Vector2(m_textInfo.characterInfo[m_firstVisibleCharacterOfLine].bottomLeft.x, lineDescender);
  2175. m_textInfo.lineInfo[m_lineNumber].lineExtents.max = new Vector2(m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].topRight.x, lineAscender);
  2176. m_textInfo.lineInfo[m_lineNumber].length = m_textInfo.lineInfo[m_lineNumber].lineExtents.max.x - (padding * currentElementScale);
  2177. m_textInfo.lineInfo[m_lineNumber].width = width;
  2178. if (m_textInfo.lineInfo[m_lineNumber].characterCount == 1)
  2179. m_textInfo.lineInfo[m_lineNumber].alignment = m_lineJustification;
  2180. if (m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].isVisible)
  2181. m_textInfo.lineInfo[m_lineNumber].maxAdvance = m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].xAdvance - (m_characterSpacing + m_currentFontAsset.normalSpacingOffset) * currentElementScale - m_cSpacing;
  2182. else
  2183. m_textInfo.lineInfo[m_lineNumber].maxAdvance = m_textInfo.characterInfo[m_lastCharacterOfLine].xAdvance - (m_characterSpacing + m_currentFontAsset.normalSpacingOffset) * currentElementScale - m_cSpacing;
  2184. m_textInfo.lineInfo[m_lineNumber].baseline = 0 - m_lineOffset;
  2185. m_textInfo.lineInfo[m_lineNumber].ascender = lineAscender;
  2186. m_textInfo.lineInfo[m_lineNumber].descender = lineDescender;
  2187. m_textInfo.lineInfo[m_lineNumber].lineHeight = lineAscender - lineDescender + lineGap * baseScale;
  2188. m_firstCharacterOfLine = m_characterCount + 1;
  2189. m_lineVisibleCharacterCount = 0;
  2190. // Add new line if not last line or character.
  2191. if (charCode == 10)
  2192. {
  2193. // Store the state of the line before starting on the new line.
  2194. SaveWordWrappingState(ref m_SavedLineState, i, m_characterCount);
  2195. // Store the state of the last Character before the new line.
  2196. SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount);
  2197. m_lineNumber += 1;
  2198. isStartOfNewLine = true;
  2199. ignoreNonBreakingSpace = false;
  2200. isFirstWord = true;
  2201. // Check to make sure Array is large enough to hold a new line.
  2202. if (m_lineNumber >= m_textInfo.lineInfo.Length)
  2203. ResizeLineExtents(m_lineNumber);
  2204. // Apply Line Spacing
  2205. if (m_lineHeight == TMP_Math.FLOAT_UNSET)
  2206. {
  2207. lineOffsetDelta = 0 - m_maxLineDescender + elementAscender + (lineGap + m_lineSpacing + m_paragraphSpacing + m_lineSpacingDelta) * baseScale;
  2208. m_lineOffset += lineOffsetDelta;
  2209. }
  2210. else
  2211. m_lineOffset += m_lineHeight + (m_lineSpacing + m_paragraphSpacing) * baseScale;
  2212. m_maxLineAscender = k_LargeNegativeFloat;
  2213. m_maxLineDescender = k_LargePositiveFloat;
  2214. m_startOfLineAscender = elementAscender;
  2215. m_xAdvance = 0 + tag_LineIndent + tag_Indent;
  2216. ellipsisIndex = m_characterCount - 1;
  2217. m_characterCount += 1;
  2218. continue;
  2219. }
  2220. }
  2221. #endregion Check for Linefeed or Last Character
  2222. // Store Rectangle positions for each Character.
  2223. #region Save CharacterInfo for the current character.
  2224. // Determine the bounds of the Mesh.
  2225. if (m_textInfo.characterInfo[m_characterCount].isVisible)
  2226. {
  2227. m_meshExtents.min.x = Mathf.Min(m_meshExtents.min.x, m_textInfo.characterInfo[m_characterCount].bottomLeft.x);
  2228. m_meshExtents.min.y = Mathf.Min(m_meshExtents.min.y, m_textInfo.characterInfo[m_characterCount].bottomLeft.y);
  2229. m_meshExtents.max.x = Mathf.Max(m_meshExtents.max.x, m_textInfo.characterInfo[m_characterCount].topRight.x);
  2230. m_meshExtents.max.y = Mathf.Max(m_meshExtents.max.y, m_textInfo.characterInfo[m_characterCount].topRight.y);
  2231. //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));
  2232. //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));
  2233. }
  2234. // Save pageInfo Data
  2235. if (m_overflowMode == TextOverflowModes.Page && charCode != 13 && charCode != 10) // && m_pageNumber < 16)
  2236. {
  2237. // Check if we need to increase allocations for the pageInfo array.
  2238. if (m_pageNumber + 1 > m_textInfo.pageInfo.Length)
  2239. TMP_TextInfo.Resize(ref m_textInfo.pageInfo, m_pageNumber + 1, true);
  2240. m_textInfo.pageInfo[m_pageNumber].ascender = pageAscender;
  2241. m_textInfo.pageInfo[m_pageNumber].descender = elementDescender < m_textInfo.pageInfo[m_pageNumber].descender ? elementDescender : m_textInfo.pageInfo[m_pageNumber].descender;
  2242. if (m_pageNumber == 0 && m_characterCount == 0)
  2243. m_textInfo.pageInfo[m_pageNumber].firstCharacterIndex = m_characterCount;
  2244. else if (m_characterCount > 0 && m_pageNumber != m_textInfo.characterInfo[m_characterCount - 1].pageNumber)
  2245. {
  2246. m_textInfo.pageInfo[m_pageNumber - 1].lastCharacterIndex = m_characterCount - 1;
  2247. m_textInfo.pageInfo[m_pageNumber].firstCharacterIndex = m_characterCount;
  2248. }
  2249. else if (m_characterCount == totalCharacterCount - 1)
  2250. m_textInfo.pageInfo[m_pageNumber].lastCharacterIndex = m_characterCount;
  2251. }
  2252. #endregion Saving CharacterInfo
  2253. // Save State of Mesh Creation for handling of Word Wrapping
  2254. #region Save Word Wrapping State
  2255. if (m_enableWordWrapping || m_overflowMode == TextOverflowModes.Truncate || m_overflowMode == TextOverflowModes.Ellipsis)
  2256. {
  2257. if ((char.IsWhiteSpace((char)charCode) || charCode == 0x200B || charCode == 0x2D || charCode == 0xAD) && (!m_isNonBreakingSpace || ignoreNonBreakingSpace) && charCode != 0xA0 && charCode != 0x2007 && charCode != 0x2011 && charCode != 0x202F && charCode != 0x2060)
  2258. {
  2259. // We store the state of numerous variables for the most recent Space, LineFeed or Carriage Return to enable them to be restored
  2260. // for Word Wrapping.
  2261. SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount);
  2262. m_isCharacterWrappingEnabled = false;
  2263. isFirstWord = false;
  2264. }
  2265. // Handling for East Asian languages
  2266. else if (( charCode > 0x1100 && charCode < 0x11ff || /* Hangul Jamo */
  2267. charCode > 0x2E80 && charCode < 0x9FFF || /* CJK */
  2268. charCode > 0xA960 && charCode < 0xA97F || /* Hangul Jame Extended-A */
  2269. charCode > 0xAC00 && charCode < 0xD7FF || /* Hangul Syllables */
  2270. charCode > 0xF900 && charCode < 0xFAFF || /* CJK Compatibility Ideographs */
  2271. charCode > 0xFE30 && charCode < 0xFE4F || /* CJK Compatibility Forms */
  2272. charCode > 0xFF00 && charCode < 0xFFEF) /* CJK Halfwidth */
  2273. && !m_isNonBreakingSpace)
  2274. {
  2275. if (isFirstWord || isLastBreakingChar || TMP_Settings.linebreakingRules.leadingCharacters.ContainsKey(charCode) == false &&
  2276. (m_characterCount < totalCharacterCount - 1 &&
  2277. TMP_Settings.linebreakingRules.followingCharacters.ContainsKey(m_textInfo.characterInfo[m_characterCount + 1].character) == false))
  2278. {
  2279. SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount);
  2280. m_isCharacterWrappingEnabled = false;
  2281. isFirstWord = false;
  2282. }
  2283. }
  2284. else if ((isFirstWord || m_isCharacterWrappingEnabled == true || isLastBreakingChar))
  2285. SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount);
  2286. }
  2287. #endregion Save Word Wrapping State
  2288. m_characterCount += 1;
  2289. }
  2290. // Check Auto Sizing and increase font size to fill text container.
  2291. #region Check Auto-Sizing (Upper Font Size Bounds)
  2292. fontSizeDelta = m_maxFontSize - m_minFontSize;
  2293. if (!m_isCharacterWrappingEnabled && m_enableAutoSizing && fontSizeDelta > 0.051f && m_fontSize < m_fontSizeMax)
  2294. {
  2295. m_minFontSize = m_fontSize;
  2296. m_fontSize += Mathf.Max((m_maxFontSize - m_fontSize) / 2, 0.05f);
  2297. m_fontSize = (int)(Mathf.Min(m_fontSize, m_fontSizeMax) * 20 + 0.5f) / 20f;
  2298. //Debug.Log(m_fontSize);
  2299. if (loopCountA > 20) return; // Added to debug
  2300. GenerateTextMesh();
  2301. return;
  2302. }
  2303. #endregion End Auto-sizing Check
  2304. m_isCharacterWrappingEnabled = false;
  2305. //Debug.Log("Iteration Count: " + loopCountA + ". Final Point Size: " + m_fontSize); // + " B: " + loopCountB + " C: " + loopCountC + " D: " + loopCountD);
  2306. // If there are no visible characters... no need to continue
  2307. if (m_characterCount == 0) // && m_visibleSpriteCount == 0)
  2308. {
  2309. ClearMesh(true);
  2310. // Event indicating the text has been regenerated.
  2311. TMPro_EventManager.ON_TEXT_CHANGED(this);
  2312. return;
  2313. }
  2314. // *** PHASE II of Text Generation ***
  2315. int last_vert_index = m_materialReferences[0].referenceCount * (!m_isVolumetricText ? 4 : 8);
  2316. // Partial clear of the vertices array to mark unused vertices as degenerate.
  2317. m_textInfo.meshInfo[0].Clear(false);
  2318. // Handle Text Alignment
  2319. #region Text Vertical Alignment
  2320. Vector3 anchorOffset = Vector3.zero;
  2321. Vector3[] corners = m_RectTransformCorners; // GetTextContainerLocalCorners();
  2322. // Handle Vertical Text Alignment
  2323. switch (m_textAlignment)
  2324. {
  2325. // Top Vertically
  2326. case TextAlignmentOptions.Top:
  2327. case TextAlignmentOptions.TopLeft:
  2328. case TextAlignmentOptions.TopRight:
  2329. case TextAlignmentOptions.TopJustified:
  2330. case TextAlignmentOptions.TopFlush:
  2331. case TextAlignmentOptions.TopGeoAligned:
  2332. if (m_overflowMode != TextOverflowModes.Page)
  2333. anchorOffset = corners[1] + new Vector3(0 + margins.x, 0 - m_maxAscender - margins.y, 0);
  2334. else
  2335. anchorOffset = corners[1] + new Vector3(0 + margins.x, 0 - m_textInfo.pageInfo[pageToDisplay].ascender - margins.y, 0);
  2336. break;
  2337. // Middle Vertically
  2338. case TextAlignmentOptions.Left:
  2339. case TextAlignmentOptions.Right:
  2340. case TextAlignmentOptions.Center:
  2341. case TextAlignmentOptions.Justified:
  2342. case TextAlignmentOptions.Flush:
  2343. case TextAlignmentOptions.CenterGeoAligned:
  2344. if (m_overflowMode != TextOverflowModes.Page)
  2345. anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0 - (m_maxAscender + margins.y + maxVisibleDescender - margins.w) / 2, 0);
  2346. else
  2347. 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);
  2348. break;
  2349. // Bottom Vertically
  2350. case TextAlignmentOptions.Bottom:
  2351. case TextAlignmentOptions.BottomLeft:
  2352. case TextAlignmentOptions.BottomRight:
  2353. case TextAlignmentOptions.BottomJustified:
  2354. case TextAlignmentOptions.BottomFlush:
  2355. case TextAlignmentOptions.BottomGeoAligned:
  2356. if (m_overflowMode != TextOverflowModes.Page)
  2357. anchorOffset = corners[0] + new Vector3(0 + margins.x, 0 - maxVisibleDescender + margins.w, 0);
  2358. else
  2359. anchorOffset = corners[0] + new Vector3(0 + margins.x, 0 - m_textInfo.pageInfo[pageToDisplay].descender + margins.w, 0);
  2360. break;
  2361. // Baseline Vertically
  2362. case TextAlignmentOptions.Baseline:
  2363. case TextAlignmentOptions.BaselineLeft:
  2364. case TextAlignmentOptions.BaselineRight:
  2365. case TextAlignmentOptions.BaselineJustified:
  2366. case TextAlignmentOptions.BaselineFlush:
  2367. case TextAlignmentOptions.BaselineGeoAligned:
  2368. anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0, 0);
  2369. break;
  2370. // Midline Vertically
  2371. case TextAlignmentOptions.MidlineLeft:
  2372. case TextAlignmentOptions.Midline:
  2373. case TextAlignmentOptions.MidlineRight:
  2374. case TextAlignmentOptions.MidlineJustified:
  2375. case TextAlignmentOptions.MidlineFlush:
  2376. case TextAlignmentOptions.MidlineGeoAligned:
  2377. 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);
  2378. break;
  2379. // Capline Vertically
  2380. case TextAlignmentOptions.CaplineLeft:
  2381. case TextAlignmentOptions.Capline:
  2382. case TextAlignmentOptions.CaplineRight:
  2383. case TextAlignmentOptions.CaplineJustified:
  2384. case TextAlignmentOptions.CaplineFlush:
  2385. case TextAlignmentOptions.CaplineGeoAligned:
  2386. anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0 - (m_maxCapHeight - margins.y - margins.w) / 2, 0);
  2387. break;
  2388. }
  2389. #endregion
  2390. // Initialization for Second Pass
  2391. Vector3 justificationOffset = Vector3.zero;
  2392. Vector3 offset = Vector3.zero;
  2393. int vert_index_X4 = 0;
  2394. int sprite_index_X4 = 0;
  2395. int wordCount = 0;
  2396. int lineCount = 0;
  2397. int lastLine = 0;
  2398. bool isFirstSeperator = false;
  2399. bool isStartOfWord = false;
  2400. int wordFirstChar = 0;
  2401. int wordLastChar = 0;
  2402. // Second Pass : Line Justification, UV Mapping, Character & Line Visibility & more.
  2403. float lossyScale = m_previousLossyScaleY = this.transform.lossyScale.y;
  2404. Color32 underlineColor = Color.white;
  2405. Color32 strikethroughColor = Color.white;
  2406. Color32 highlightColor = new Color32(255, 255, 0, 64);
  2407. float xScale = 0;
  2408. float underlineStartScale = 0;
  2409. float underlineEndScale = 0;
  2410. float underlineMaxScale = 0;
  2411. float underlineBaseLine = k_LargePositiveFloat;
  2412. int lastPage = 0;
  2413. float strikethroughPointSize = 0;
  2414. float strikethroughScale = 0;
  2415. float strikethroughBaseline = 0;
  2416. TMP_CharacterInfo[] characterInfos = m_textInfo.characterInfo;
  2417. #region Handle Line Justification & UV Mapping & Character Visibility & More
  2418. for (int i = 0; i < m_characterCount; i++)
  2419. {
  2420. TMP_FontAsset currentFontAsset = characterInfos[i].fontAsset;
  2421. char currentCharacter = characterInfos[i].character;
  2422. int currentLine = characterInfos[i].lineNumber;
  2423. TMP_LineInfo lineInfo = m_textInfo.lineInfo[currentLine];
  2424. lineCount = currentLine + 1;
  2425. TextAlignmentOptions lineAlignment = lineInfo.alignment;
  2426. // Process Line Justification
  2427. #region Handle Line Justification
  2428. switch (lineAlignment)
  2429. {
  2430. case TextAlignmentOptions.TopLeft:
  2431. case TextAlignmentOptions.Left:
  2432. case TextAlignmentOptions.BottomLeft:
  2433. case TextAlignmentOptions.BaselineLeft:
  2434. case TextAlignmentOptions.MidlineLeft:
  2435. case TextAlignmentOptions.CaplineLeft:
  2436. if (!m_isRightToLeft)
  2437. justificationOffset = new Vector3(0 + lineInfo.marginLeft, 0, 0);
  2438. else
  2439. justificationOffset = new Vector3(0 - lineInfo.maxAdvance, 0, 0);
  2440. break;
  2441. case TextAlignmentOptions.Top:
  2442. case TextAlignmentOptions.Center:
  2443. case TextAlignmentOptions.Bottom:
  2444. case TextAlignmentOptions.Baseline:
  2445. case TextAlignmentOptions.Midline:
  2446. case TextAlignmentOptions.Capline:
  2447. justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width / 2 - lineInfo.maxAdvance / 2, 0, 0);
  2448. break;
  2449. case TextAlignmentOptions.TopGeoAligned:
  2450. case TextAlignmentOptions.CenterGeoAligned:
  2451. case TextAlignmentOptions.BottomGeoAligned:
  2452. case TextAlignmentOptions.BaselineGeoAligned:
  2453. case TextAlignmentOptions.MidlineGeoAligned:
  2454. case TextAlignmentOptions.CaplineGeoAligned:
  2455. justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width / 2 - (lineInfo.lineExtents.min.x + lineInfo.lineExtents.max.x) / 2, 0, 0);
  2456. break;
  2457. case TextAlignmentOptions.TopRight:
  2458. case TextAlignmentOptions.Right:
  2459. case TextAlignmentOptions.BottomRight:
  2460. case TextAlignmentOptions.BaselineRight:
  2461. case TextAlignmentOptions.MidlineRight:
  2462. case TextAlignmentOptions.CaplineRight:
  2463. if (!m_isRightToLeft)
  2464. justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width - lineInfo.maxAdvance, 0, 0);
  2465. else
  2466. justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width, 0, 0);
  2467. break;
  2468. case TextAlignmentOptions.TopJustified:
  2469. case TextAlignmentOptions.Justified:
  2470. case TextAlignmentOptions.BottomJustified:
  2471. case TextAlignmentOptions.BaselineJustified:
  2472. case TextAlignmentOptions.MidlineJustified:
  2473. case TextAlignmentOptions.CaplineJustified:
  2474. case TextAlignmentOptions.TopFlush:
  2475. case TextAlignmentOptions.Flush:
  2476. case TextAlignmentOptions.BottomFlush:
  2477. case TextAlignmentOptions.BaselineFlush:
  2478. case TextAlignmentOptions.MidlineFlush:
  2479. case TextAlignmentOptions.CaplineFlush:
  2480. // Skip Zero Width Characters
  2481. if (currentCharacter == 0xAD || currentCharacter == 0x200B || currentCharacter == 0x2060) break;
  2482. char lastCharOfCurrentLine = characterInfos[lineInfo.lastCharacterIndex].character;
  2483. bool isFlush = ((_HorizontalAlignmentOptions)lineAlignment & _HorizontalAlignmentOptions.Flush) == _HorizontalAlignmentOptions.Flush;
  2484. // In Justified mode, all lines are justified except the last one.
  2485. // In Flush mode, all lines are justified.
  2486. if (char.IsControl(lastCharOfCurrentLine) == false && currentLine < m_lineNumber || isFlush || lineInfo.maxAdvance > lineInfo.width)
  2487. {
  2488. // First character of each line.
  2489. if (currentLine != lastLine || i == 0 || i == m_firstVisibleCharacter)
  2490. {
  2491. if (!m_isRightToLeft)
  2492. justificationOffset = new Vector3(lineInfo.marginLeft, 0, 0);
  2493. else
  2494. justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width, 0, 0);
  2495. if (char.IsSeparator(currentCharacter))
  2496. isFirstSeperator = true;
  2497. else
  2498. isFirstSeperator = false;
  2499. }
  2500. else
  2501. {
  2502. float gap = !m_isRightToLeft ? lineInfo.width - lineInfo.maxAdvance : lineInfo.width + lineInfo.maxAdvance;
  2503. int visibleCount = lineInfo.visibleCharacterCount - 1 + lineInfo.controlCharacterCount;
  2504. // Get the number of spaces for each line ignoring the last character if it is not visible (ie. a space or linefeed).
  2505. int spaces = (characterInfos[lineInfo.lastCharacterIndex].isVisible ? lineInfo.spaceCount : lineInfo.spaceCount - 1) - lineInfo.controlCharacterCount;
  2506. if (isFirstSeperator) { spaces -= 1; visibleCount += 1; }
  2507. float ratio = spaces > 0 ? m_wordWrappingRatios : 1;
  2508. if (spaces < 1) spaces = 1;
  2509. if (currentCharacter != 0xA0 && (currentCharacter == 9 || char.IsSeparator((char)currentCharacter)))
  2510. {
  2511. if (!m_isRightToLeft)
  2512. justificationOffset += new Vector3(gap * (1 - ratio) / spaces, 0, 0);
  2513. else
  2514. justificationOffset -= new Vector3(gap * (1 - ratio) / spaces, 0, 0);
  2515. }
  2516. else
  2517. {
  2518. if (!m_isRightToLeft)
  2519. justificationOffset += new Vector3(gap * ratio / visibleCount, 0, 0);
  2520. else
  2521. justificationOffset -= new Vector3(gap * ratio / visibleCount, 0, 0);
  2522. }
  2523. }
  2524. }
  2525. else
  2526. {
  2527. if (!m_isRightToLeft)
  2528. justificationOffset = new Vector3(lineInfo.marginLeft, 0, 0); // Keep last line left justified.
  2529. else
  2530. justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width, 0, 0); // Keep last line right justified.
  2531. }
  2532. //Debug.Log("Char [" + (char)charCode + "] Code:" + charCode + " Line # " + currentLine + " Offset:" + justificationOffset + " # Spaces:" + lineInfo.spaceCount + " # Characters:" + lineInfo.characterCount);
  2533. break;
  2534. }
  2535. #endregion End Text Justification
  2536. offset = anchorOffset + justificationOffset;
  2537. // Handle UV2 mapping options and packing of scale information into UV2.
  2538. #region Handling of UV2 mapping & Scale packing
  2539. bool isCharacterVisible = characterInfos[i].isVisible;
  2540. if (isCharacterVisible)
  2541. {
  2542. TMP_TextElementType elementType = characterInfos[i].elementType;
  2543. switch (elementType)
  2544. {
  2545. // CHARACTERS
  2546. case TMP_TextElementType.Character:
  2547. Extents lineExtents = lineInfo.lineExtents;
  2548. float uvOffset = (m_uvLineOffset * currentLine) % 1; // + m_uvOffset.x;
  2549. // Setup UV2 based on Character Mapping Options Selected
  2550. #region Handle UV Mapping Options
  2551. switch (m_horizontalMapping)
  2552. {
  2553. case TextureMappingOptions.Character:
  2554. characterInfos[i].vertex_BL.uv2.x = 0; //+ m_uvOffset.x;
  2555. characterInfos[i].vertex_TL.uv2.x = 0; //+ m_uvOffset.x;
  2556. characterInfos[i].vertex_TR.uv2.x = 1; //+ m_uvOffset.x;
  2557. characterInfos[i].vertex_BR.uv2.x = 1; //+ m_uvOffset.x;
  2558. break;
  2559. case TextureMappingOptions.Line:
  2560. if (m_textAlignment != TextAlignmentOptions.Justified)
  2561. {
  2562. characterInfos[i].vertex_BL.uv2.x = (characterInfos[i].vertex_BL.position.x - lineExtents.min.x) / (lineExtents.max.x - lineExtents.min.x) + uvOffset;
  2563. characterInfos[i].vertex_TL.uv2.x = (characterInfos[i].vertex_TL.position.x - lineExtents.min.x) / (lineExtents.max.x - lineExtents.min.x) + uvOffset;
  2564. characterInfos[i].vertex_TR.uv2.x = (characterInfos[i].vertex_TR.position.x - lineExtents.min.x) / (lineExtents.max.x - lineExtents.min.x) + uvOffset;
  2565. characterInfos[i].vertex_BR.uv2.x = (characterInfos[i].vertex_BR.position.x - lineExtents.min.x) / (lineExtents.max.x - lineExtents.min.x) + uvOffset;
  2566. break;
  2567. }
  2568. else // Special Case if Justified is used in Line Mode.
  2569. {
  2570. 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;
  2571. 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;
  2572. 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;
  2573. 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;
  2574. break;
  2575. }
  2576. case TextureMappingOptions.Paragraph:
  2577. 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;
  2578. 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;
  2579. 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;
  2580. 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;
  2581. break;
  2582. case TextureMappingOptions.MatchAspect:
  2583. switch (m_verticalMapping)
  2584. {
  2585. case TextureMappingOptions.Character:
  2586. characterInfos[i].vertex_BL.uv2.y = 0; //+ m_uvOffset.y;
  2587. characterInfos[i].vertex_TL.uv2.y = 1; //+ m_uvOffset.y;
  2588. characterInfos[i].vertex_TR.uv2.y = 0; //+ m_uvOffset.y;
  2589. characterInfos[i].vertex_BR.uv2.y = 1; //+ m_uvOffset.y;
  2590. break;
  2591. case TextureMappingOptions.Line:
  2592. characterInfos[i].vertex_BL.uv2.y = (characterInfos[i].vertex_BL.position.y - lineExtents.min.y) / (lineExtents.max.y - lineExtents.min.y) + uvOffset;
  2593. characterInfos[i].vertex_TL.uv2.y = (characterInfos[i].vertex_TL.position.y - lineExtents.min.y) / (lineExtents.max.y - lineExtents.min.y) + uvOffset;
  2594. characterInfos[i].vertex_TR.uv2.y = characterInfos[i].vertex_BL.uv2.y;
  2595. characterInfos[i].vertex_BR.uv2.y = characterInfos[i].vertex_TL.uv2.y;
  2596. break;
  2597. case TextureMappingOptions.Paragraph:
  2598. 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;
  2599. 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;
  2600. characterInfos[i].vertex_TR.uv2.y = characterInfos[i].vertex_BL.uv2.y;
  2601. characterInfos[i].vertex_BR.uv2.y = characterInfos[i].vertex_TL.uv2.y;
  2602. break;
  2603. case TextureMappingOptions.MatchAspect:
  2604. Debug.Log("ERROR: Cannot Match both Vertical & Horizontal.");
  2605. break;
  2606. }
  2607. //float xDelta = 1 - (_uv2s[vert_index + 0].y * textMeshCharacterInfo[i].AspectRatio); // Left aligned
  2608. float xDelta = (1 - ((characterInfos[i].vertex_BL.uv2.y + characterInfos[i].vertex_TL.uv2.y) * characterInfos[i].aspectRatio)) / 2; // Center of Rectangle
  2609. characterInfos[i].vertex_BL.uv2.x = (characterInfos[i].vertex_BL.uv2.y * characterInfos[i].aspectRatio) + xDelta + uvOffset;
  2610. characterInfos[i].vertex_TL.uv2.x = characterInfos[i].vertex_BL.uv2.x;
  2611. characterInfos[i].vertex_TR.uv2.x = (characterInfos[i].vertex_TL.uv2.y * characterInfos[i].aspectRatio) + xDelta + uvOffset;
  2612. characterInfos[i].vertex_BR.uv2.x = characterInfos[i].vertex_TR.uv2.x;
  2613. break;
  2614. }
  2615. switch (m_verticalMapping)
  2616. {
  2617. case TextureMappingOptions.Character:
  2618. characterInfos[i].vertex_BL.uv2.y = 0; //+ m_uvOffset.y;
  2619. characterInfos[i].vertex_TL.uv2.y = 1; //+ m_uvOffset.y;
  2620. characterInfos[i].vertex_TR.uv2.y = 1; //+ m_uvOffset.y;
  2621. characterInfos[i].vertex_BR.uv2.y = 0; //+ m_uvOffset.y;
  2622. break;
  2623. case TextureMappingOptions.Line:
  2624. characterInfos[i].vertex_BL.uv2.y = (characterInfos[i].vertex_BL.position.y - lineInfo.descender) / (lineInfo.ascender - lineInfo.descender); // + m_uvOffset.y;
  2625. characterInfos[i].vertex_TL.uv2.y = (characterInfos[i].vertex_TL.position.y - lineInfo.descender) / (lineInfo.ascender - lineInfo.descender); // + m_uvOffset.y;
  2626. characterInfos[i].vertex_TR.uv2.y = characterInfos[i].vertex_TL.uv2.y;
  2627. characterInfos[i].vertex_BR.uv2.y = characterInfos[i].vertex_BL.uv2.y;
  2628. break;
  2629. case TextureMappingOptions.Paragraph:
  2630. 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;
  2631. 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;
  2632. characterInfos[i].vertex_TR.uv2.y = characterInfos[i].vertex_TL.uv2.y;
  2633. characterInfos[i].vertex_BR.uv2.y = characterInfos[i].vertex_BL.uv2.y;
  2634. break;
  2635. case TextureMappingOptions.MatchAspect:
  2636. float yDelta = (1 - ((characterInfos[i].vertex_BL.uv2.x + characterInfos[i].vertex_TR.uv2.x) / characterInfos[i].aspectRatio)) / 2; // Center of Rectangle
  2637. characterInfos[i].vertex_BL.uv2.y = yDelta + (characterInfos[i].vertex_BL.uv2.x / characterInfos[i].aspectRatio); // + m_uvOffset.y;
  2638. characterInfos[i].vertex_TL.uv2.y = yDelta + (characterInfos[i].vertex_TR.uv2.x / characterInfos[i].aspectRatio); // + m_uvOffset.y;
  2639. characterInfos[i].vertex_BR.uv2.y = characterInfos[i].vertex_BL.uv2.y;
  2640. characterInfos[i].vertex_TR.uv2.y = characterInfos[i].vertex_TL.uv2.y;
  2641. break;
  2642. }
  2643. #endregion
  2644. // Pack UV's so that we can pass Xscale needed for Shader to maintain 1:1 ratio.
  2645. #region Pack Scale into UV2
  2646. xScale = characterInfos[i].scale * lossyScale * (1 - m_charWidthAdjDelta);
  2647. if (!characterInfos[i].isUsingAlternateTypeface && (characterInfos[i].style & FontStyles.Bold) == FontStyles.Bold) xScale *= -1;
  2648. //int isBold = (m_textInfo.characterInfo[i].style & FontStyles.Bold) == FontStyles.Bold ? 1 : 0;
  2649. //Vector2 vertexData = new Vector2(isBold, xScale);
  2650. //characterInfos[i].vertex_BL.uv4 = vertexData;
  2651. //characterInfos[i].vertex_TL.uv4 = vertexData;
  2652. //characterInfos[i].vertex_TR.uv4 = vertexData;
  2653. //characterInfos[i].vertex_BR.uv4 = vertexData;
  2654. float x0 = characterInfos[i].vertex_BL.uv2.x;
  2655. float y0 = characterInfos[i].vertex_BL.uv2.y;
  2656. float x1 = characterInfos[i].vertex_TR.uv2.x;
  2657. float y1 = characterInfos[i].vertex_TR.uv2.y;
  2658. float dx = (int)x0;
  2659. float dy = (int)y0;
  2660. x0 = x0 - dx;
  2661. x1 = x1 - dx;
  2662. y0 = y0 - dy;
  2663. y1 = y1 - dy;
  2664. // Optimization to avoid having a vector2 returned from the Pack UV function.
  2665. characterInfos[i].vertex_BL.uv2.x = PackUV(x0, y0); characterInfos[i].vertex_BL.uv2.y = xScale;
  2666. characterInfos[i].vertex_TL.uv2.x = PackUV(x0, y1); characterInfos[i].vertex_TL.uv2.y = xScale;
  2667. characterInfos[i].vertex_TR.uv2.x = PackUV(x1, y1); characterInfos[i].vertex_TR.uv2.y = xScale;
  2668. characterInfos[i].vertex_BR.uv2.x = PackUV(x1, y0); characterInfos[i].vertex_BR.uv2.y = xScale;
  2669. #endregion
  2670. break;
  2671. // SPRITES
  2672. case TMP_TextElementType.Sprite:
  2673. // Nothing right now
  2674. break;
  2675. }
  2676. // Handle maxVisibleCharacters, maxVisibleLines and Overflow Page Mode.
  2677. #region Handle maxVisibleCharacters / maxVisibleLines / Page Mode
  2678. if (i < m_maxVisibleCharacters && wordCount < m_maxVisibleWords && currentLine < m_maxVisibleLines && m_overflowMode != TextOverflowModes.Page)
  2679. {
  2680. characterInfos[i].vertex_BL.position += offset;
  2681. characterInfos[i].vertex_TL.position += offset;
  2682. characterInfos[i].vertex_TR.position += offset;
  2683. characterInfos[i].vertex_BR.position += offset;
  2684. }
  2685. else if (i < m_maxVisibleCharacters && wordCount < m_maxVisibleWords && currentLine < m_maxVisibleLines && m_overflowMode == TextOverflowModes.Page && characterInfos[i].pageNumber == pageToDisplay)
  2686. {
  2687. characterInfos[i].vertex_BL.position += offset;
  2688. characterInfos[i].vertex_TL.position += offset;
  2689. characterInfos[i].vertex_TR.position += offset;
  2690. characterInfos[i].vertex_BR.position += offset;
  2691. }
  2692. else
  2693. {
  2694. characterInfos[i].vertex_BL.position = Vector3.zero;
  2695. characterInfos[i].vertex_TL.position = Vector3.zero;
  2696. characterInfos[i].vertex_TR.position = Vector3.zero;
  2697. characterInfos[i].vertex_BR.position = Vector3.zero;
  2698. characterInfos[i].isVisible = false;
  2699. }
  2700. #endregion
  2701. // Fill Vertex Buffers for the various types of element
  2702. if (elementType == TMP_TextElementType.Character)
  2703. {
  2704. FillCharacterVertexBuffers(i, vert_index_X4, m_isVolumetricText);
  2705. }
  2706. else if (elementType == TMP_TextElementType.Sprite)
  2707. {
  2708. FillSpriteVertexBuffers(i, sprite_index_X4);
  2709. }
  2710. }
  2711. #endregion
  2712. // Apply Alignment and Justification Offset
  2713. m_textInfo.characterInfo[i].bottomLeft += offset;
  2714. m_textInfo.characterInfo[i].topLeft += offset;
  2715. m_textInfo.characterInfo[i].topRight += offset;
  2716. m_textInfo.characterInfo[i].bottomRight += offset;
  2717. m_textInfo.characterInfo[i].origin += offset.x;
  2718. m_textInfo.characterInfo[i].xAdvance += offset.x;
  2719. m_textInfo.characterInfo[i].ascender += offset.y;
  2720. m_textInfo.characterInfo[i].descender += offset.y;
  2721. m_textInfo.characterInfo[i].baseLine += offset.y;
  2722. // Update MeshExtents
  2723. if (isCharacterVisible)
  2724. {
  2725. //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));
  2726. //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));
  2727. }
  2728. // Need to recompute lineExtent to account for the offset from justification.
  2729. #region Adjust lineExtents resulting from alignment offset
  2730. if (currentLine != lastLine || i == m_characterCount - 1)
  2731. {
  2732. // Update the previous line's extents
  2733. if (currentLine != lastLine)
  2734. {
  2735. m_textInfo.lineInfo[lastLine].baseline += offset.y;
  2736. m_textInfo.lineInfo[lastLine].ascender += offset.y;
  2737. m_textInfo.lineInfo[lastLine].descender += offset.y;
  2738. m_textInfo.lineInfo[lastLine].lineExtents.min = new Vector2(m_textInfo.characterInfo[m_textInfo.lineInfo[lastLine].firstCharacterIndex].bottomLeft.x, m_textInfo.lineInfo[lastLine].descender);
  2739. m_textInfo.lineInfo[lastLine].lineExtents.max = new Vector2(m_textInfo.characterInfo[m_textInfo.lineInfo[lastLine].lastVisibleCharacterIndex].topRight.x, m_textInfo.lineInfo[lastLine].ascender);
  2740. }
  2741. // Update the current line's extents
  2742. if (i == m_characterCount - 1)
  2743. {
  2744. m_textInfo.lineInfo[currentLine].baseline += offset.y;
  2745. m_textInfo.lineInfo[currentLine].ascender += offset.y;
  2746. m_textInfo.lineInfo[currentLine].descender += offset.y;
  2747. m_textInfo.lineInfo[currentLine].lineExtents.min = new Vector2(m_textInfo.characterInfo[m_textInfo.lineInfo[currentLine].firstCharacterIndex].bottomLeft.x, m_textInfo.lineInfo[currentLine].descender);
  2748. m_textInfo.lineInfo[currentLine].lineExtents.max = new Vector2(m_textInfo.characterInfo[m_textInfo.lineInfo[currentLine].lastVisibleCharacterIndex].topRight.x, m_textInfo.lineInfo[currentLine].ascender);
  2749. }
  2750. }
  2751. #endregion
  2752. // Track Word Count per line and for the object
  2753. #region Track Word Count
  2754. if (char.IsLetterOrDigit(currentCharacter) || currentCharacter == 0x2D || currentCharacter == 0xAD || currentCharacter == 0x2010 || currentCharacter == 0x2011)
  2755. {
  2756. if (isStartOfWord == false)
  2757. {
  2758. isStartOfWord = true;
  2759. wordFirstChar = i;
  2760. }
  2761. // If last character is a word
  2762. if (isStartOfWord && i == m_characterCount - 1)
  2763. {
  2764. int size = m_textInfo.wordInfo.Length;
  2765. int index = m_textInfo.wordCount;
  2766. if (m_textInfo.wordCount + 1 > size)
  2767. TMP_TextInfo.Resize(ref m_textInfo.wordInfo, size + 1);
  2768. wordLastChar = i;
  2769. m_textInfo.wordInfo[index].firstCharacterIndex = wordFirstChar;
  2770. m_textInfo.wordInfo[index].lastCharacterIndex = wordLastChar;
  2771. m_textInfo.wordInfo[index].characterCount = wordLastChar - wordFirstChar + 1;
  2772. m_textInfo.wordInfo[index].textComponent = this;
  2773. wordCount += 1;
  2774. m_textInfo.wordCount += 1;
  2775. m_textInfo.lineInfo[currentLine].wordCount += 1;
  2776. }
  2777. }
  2778. else if (isStartOfWord || i == 0 && (!char.IsPunctuation(currentCharacter) || char.IsWhiteSpace(currentCharacter) || currentCharacter == 0x200B || i == m_characterCount - 1))
  2779. {
  2780. if (i > 0 && i < characterInfos.Length - 1 && i < m_characterCount && (currentCharacter == 39 || currentCharacter == 8217) && char.IsLetterOrDigit(characterInfos[i - 1].character) && char.IsLetterOrDigit(characterInfos[i + 1].character))
  2781. {
  2782. }
  2783. else
  2784. {
  2785. wordLastChar = i == m_characterCount - 1 && char.IsLetterOrDigit(currentCharacter) ? i : i - 1;
  2786. isStartOfWord = false;
  2787. int size = m_textInfo.wordInfo.Length;
  2788. int index = m_textInfo.wordCount;
  2789. if (m_textInfo.wordCount + 1 > size)
  2790. TMP_TextInfo.Resize(ref m_textInfo.wordInfo, size + 1);
  2791. m_textInfo.wordInfo[index].firstCharacterIndex = wordFirstChar;
  2792. m_textInfo.wordInfo[index].lastCharacterIndex = wordLastChar;
  2793. m_textInfo.wordInfo[index].characterCount = wordLastChar - wordFirstChar + 1;
  2794. m_textInfo.wordInfo[index].textComponent = this;
  2795. wordCount += 1;
  2796. m_textInfo.wordCount += 1;
  2797. m_textInfo.lineInfo[currentLine].wordCount += 1;
  2798. }
  2799. }
  2800. #endregion
  2801. // Setup & Handle Underline
  2802. #region Underline
  2803. // NOTE: Need to figure out how underline will be handled with multiple fonts and which font will be used for the underline.
  2804. bool isUnderline = (m_textInfo.characterInfo[i].style & FontStyles.Underline) == FontStyles.Underline;
  2805. if (isUnderline)
  2806. {
  2807. bool isUnderlineVisible = true;
  2808. int currentPage = m_textInfo.characterInfo[i].pageNumber;
  2809. if (i > m_maxVisibleCharacters || currentLine > m_maxVisibleLines || (m_overflowMode == TextOverflowModes.Page && currentPage + 1 != m_pageToDisplay))
  2810. isUnderlineVisible = false;
  2811. // We only use the scale of visible characters.
  2812. if (!char.IsWhiteSpace(currentCharacter) && currentCharacter != 0x200B)
  2813. {
  2814. underlineMaxScale = Mathf.Max(underlineMaxScale, m_textInfo.characterInfo[i].scale);
  2815. underlineBaseLine = Mathf.Min(currentPage == lastPage ? underlineBaseLine : k_LargePositiveFloat, m_textInfo.characterInfo[i].baseLine + font.fontInfo.Underline * underlineMaxScale);
  2816. lastPage = currentPage; // Need to track pages to ensure we reset baseline for the new pages.
  2817. }
  2818. if (beginUnderline == false && isUnderlineVisible == true && i <= lineInfo.lastVisibleCharacterIndex && currentCharacter != 10 && currentCharacter != 13)
  2819. {
  2820. if (i == lineInfo.lastVisibleCharacterIndex && char.IsSeparator(currentCharacter))
  2821. { }
  2822. else
  2823. {
  2824. beginUnderline = true;
  2825. underlineStartScale = m_textInfo.characterInfo[i].scale;
  2826. if (underlineMaxScale == 0) underlineMaxScale = underlineStartScale;
  2827. underline_start = new Vector3(m_textInfo.characterInfo[i].bottomLeft.x, underlineBaseLine, 0);
  2828. underlineColor = m_textInfo.characterInfo[i].underlineColor;
  2829. }
  2830. }
  2831. // End Underline if text only contains one character.
  2832. if (beginUnderline && m_characterCount == 1)
  2833. {
  2834. beginUnderline = false;
  2835. underline_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, underlineBaseLine, 0);
  2836. underlineEndScale = m_textInfo.characterInfo[i].scale;
  2837. DrawUnderlineMesh(underline_start, underline_end, ref last_vert_index, underlineStartScale, underlineEndScale, underlineMaxScale, xScale, underlineColor);
  2838. underlineMaxScale = 0;
  2839. underlineBaseLine = k_LargePositiveFloat;
  2840. }
  2841. else if (beginUnderline && (i == lineInfo.lastCharacterIndex || i >= lineInfo.lastVisibleCharacterIndex))
  2842. {
  2843. // Terminate underline at previous visible character if space or carriage return.
  2844. if (char.IsWhiteSpace(currentCharacter) || currentCharacter == 0x200B)
  2845. {
  2846. int lastVisibleCharacterIndex = lineInfo.lastVisibleCharacterIndex;
  2847. underline_end = new Vector3(m_textInfo.characterInfo[lastVisibleCharacterIndex].topRight.x, underlineBaseLine, 0);
  2848. underlineEndScale = m_textInfo.characterInfo[lastVisibleCharacterIndex].scale;
  2849. }
  2850. else
  2851. { // End underline if last character of the line.
  2852. underline_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, underlineBaseLine, 0);
  2853. underlineEndScale = m_textInfo.characterInfo[i].scale;
  2854. }
  2855. beginUnderline = false;
  2856. DrawUnderlineMesh(underline_start, underline_end, ref last_vert_index, underlineStartScale, underlineEndScale, underlineMaxScale, xScale, underlineColor);
  2857. underlineMaxScale = 0;
  2858. underlineBaseLine = k_LargePositiveFloat;
  2859. }
  2860. else if (beginUnderline && !isUnderlineVisible)
  2861. {
  2862. beginUnderline = false;
  2863. underline_end = new Vector3(m_textInfo.characterInfo[i - 1].topRight.x, underlineBaseLine, 0);
  2864. underlineEndScale = m_textInfo.characterInfo[i - 1].scale;
  2865. DrawUnderlineMesh(underline_start, underline_end, ref last_vert_index, underlineStartScale, underlineEndScale, underlineMaxScale, xScale, underlineColor);
  2866. underlineMaxScale = 0;
  2867. underlineBaseLine = k_LargePositiveFloat;
  2868. }
  2869. else if (beginUnderline && i < m_characterCount - 1 && !underlineColor.Compare(m_textInfo.characterInfo[i + 1].underlineColor))
  2870. {
  2871. // End underline if underline color has changed.
  2872. beginUnderline = false;
  2873. underline_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, underlineBaseLine, 0);
  2874. underlineEndScale = m_textInfo.characterInfo[i].scale;
  2875. DrawUnderlineMesh(underline_start, underline_end, ref last_vert_index, underlineStartScale, underlineEndScale, underlineMaxScale, xScale, underlineColor);
  2876. underlineMaxScale = 0;
  2877. underlineBaseLine = k_LargePositiveFloat;
  2878. }
  2879. }
  2880. else
  2881. {
  2882. // End Underline
  2883. if (beginUnderline == true)
  2884. {
  2885. beginUnderline = false;
  2886. underline_end = new Vector3(m_textInfo.characterInfo[i - 1].topRight.x, underlineBaseLine, 0);
  2887. underlineEndScale = m_textInfo.characterInfo[i - 1].scale;
  2888. DrawUnderlineMesh(underline_start, underline_end, ref last_vert_index, underlineStartScale, underlineEndScale, underlineMaxScale, xScale, underlineColor);
  2889. underlineMaxScale = 0;
  2890. underlineBaseLine = k_LargePositiveFloat;
  2891. }
  2892. }
  2893. #endregion
  2894. // Setup & Handle Strikethrough
  2895. #region Strikethrough
  2896. // NOTE: Need to figure out how underline will be handled with multiple fonts and which font will be used for the underline.
  2897. bool isStrikethrough = (m_textInfo.characterInfo[i].style & FontStyles.Strikethrough) == FontStyles.Strikethrough;
  2898. float strikethroughOffset = currentFontAsset.fontInfo.strikethrough;
  2899. if (isStrikethrough)
  2900. {
  2901. bool isStrikeThroughVisible = true;
  2902. if (i > m_maxVisibleCharacters || currentLine > m_maxVisibleLines || (m_overflowMode == TextOverflowModes.Page && m_textInfo.characterInfo[i].pageNumber + 1 != m_pageToDisplay))
  2903. isStrikeThroughVisible = false;
  2904. if (beginStrikethrough == false && isStrikeThroughVisible && i <= lineInfo.lastVisibleCharacterIndex && currentCharacter != 10 && currentCharacter != 13)
  2905. {
  2906. if (i == lineInfo.lastVisibleCharacterIndex && char.IsSeparator(currentCharacter))
  2907. { }
  2908. else
  2909. {
  2910. beginStrikethrough = true;
  2911. strikethroughPointSize = m_textInfo.characterInfo[i].pointSize;
  2912. strikethroughScale = m_textInfo.characterInfo[i].scale;
  2913. strikethrough_start = new Vector3(m_textInfo.characterInfo[i].bottomLeft.x, m_textInfo.characterInfo[i].baseLine + strikethroughOffset * strikethroughScale, 0);
  2914. strikethroughColor = m_textInfo.characterInfo[i].strikethroughColor;
  2915. strikethroughBaseline = m_textInfo.characterInfo[i].baseLine;
  2916. //Debug.Log("Char [" + currentCharacter + "] Start Strikethrough POS: " + strikethrough_start);
  2917. }
  2918. }
  2919. // End Strikethrough if text only contains one character.
  2920. if (beginStrikethrough && m_characterCount == 1)
  2921. {
  2922. beginStrikethrough = false;
  2923. strikethrough_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, m_textInfo.characterInfo[i].baseLine + strikethroughOffset * strikethroughScale, 0);
  2924. DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor);
  2925. }
  2926. else if (beginStrikethrough && i == lineInfo.lastCharacterIndex)
  2927. {
  2928. // Terminate Strikethrough at previous visible character if space or carriage return.
  2929. if (char.IsWhiteSpace(currentCharacter) || currentCharacter == 0x200B)
  2930. {
  2931. int lastVisibleCharacterIndex = lineInfo.lastVisibleCharacterIndex;
  2932. strikethrough_end = new Vector3(m_textInfo.characterInfo[lastVisibleCharacterIndex].topRight.x, m_textInfo.characterInfo[lastVisibleCharacterIndex].baseLine + strikethroughOffset * strikethroughScale, 0);
  2933. }
  2934. else
  2935. {
  2936. // Terminate Strikethrough at last character of line.
  2937. strikethrough_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, m_textInfo.characterInfo[i].baseLine + strikethroughOffset * strikethroughScale, 0);
  2938. }
  2939. beginStrikethrough = false;
  2940. DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor);
  2941. }
  2942. 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)))
  2943. {
  2944. // Terminate Strikethrough if scale changes.
  2945. beginStrikethrough = false;
  2946. int lastVisibleCharacterIndex = lineInfo.lastVisibleCharacterIndex;
  2947. if (i > lastVisibleCharacterIndex)
  2948. strikethrough_end = new Vector3(m_textInfo.characterInfo[lastVisibleCharacterIndex].topRight.x, m_textInfo.characterInfo[lastVisibleCharacterIndex].baseLine + strikethroughOffset * strikethroughScale, 0);
  2949. else
  2950. strikethrough_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, m_textInfo.characterInfo[i].baseLine + strikethroughOffset * strikethroughScale, 0);
  2951. DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor);
  2952. //Debug.Log("Char [" + currentCharacter + "] at Index: " + i + " End Strikethrough POS: " + strikethrough_end + " Baseline: " + m_textInfo.characterInfo[i].baseLine.ToString("f3"));
  2953. }
  2954. else if (beginStrikethrough && i < m_characterCount && currentFontAsset.GetInstanceID() != characterInfos[i + 1].fontAsset.GetInstanceID())
  2955. {
  2956. // Terminate Strikethrough if font asset changes.
  2957. beginStrikethrough = false;
  2958. strikethrough_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, m_textInfo.characterInfo[i].baseLine + strikethroughOffset * strikethroughScale, 0);
  2959. DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor);
  2960. }
  2961. else if (beginStrikethrough && !isStrikeThroughVisible)
  2962. {
  2963. // Terminate Strikethrough if character is not visible.
  2964. beginStrikethrough = false;
  2965. strikethrough_end = new Vector3(m_textInfo.characterInfo[i - 1].topRight.x, m_textInfo.characterInfo[i - 1].baseLine + strikethroughOffset * strikethroughScale, 0);
  2966. DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor);
  2967. }
  2968. }
  2969. else
  2970. {
  2971. // End Strikethrough
  2972. if (beginStrikethrough == true)
  2973. {
  2974. beginStrikethrough = false;
  2975. strikethrough_end = new Vector3(m_textInfo.characterInfo[i - 1].topRight.x, m_textInfo.characterInfo[i - 1].baseLine + strikethroughOffset * strikethroughScale, 0);
  2976. DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor);
  2977. }
  2978. }
  2979. #endregion
  2980. // HANDLE TEXT HIGHLIGHTING
  2981. #region Text Highlighting
  2982. bool isHighlight = (m_textInfo.characterInfo[i].style & FontStyles.Highlight) == FontStyles.Highlight;
  2983. if (isHighlight)
  2984. {
  2985. bool isHighlightVisible = true;
  2986. int currentPage = m_textInfo.characterInfo[i].pageNumber;
  2987. if (i > m_maxVisibleCharacters || currentLine > m_maxVisibleLines || (m_overflowMode == TextOverflowModes.Page && currentPage + 1 != m_pageToDisplay))
  2988. isHighlightVisible = false;
  2989. if (beginHighlight == false && isHighlightVisible == true && i <= lineInfo.lastVisibleCharacterIndex && currentCharacter != 10 && currentCharacter != 13)
  2990. {
  2991. if (i == lineInfo.lastVisibleCharacterIndex && char.IsSeparator(currentCharacter))
  2992. { }
  2993. else
  2994. {
  2995. beginHighlight = true;
  2996. highlight_start = k_LargePositiveVector2;
  2997. highlight_end = k_LargeNegativeVector2;
  2998. highlightColor = m_textInfo.characterInfo[i].highlightColor;
  2999. }
  3000. }
  3001. if (beginHighlight)
  3002. {
  3003. Color32 currentHighlightColor = m_textInfo.characterInfo[i].highlightColor;
  3004. bool isColorTransition = false;
  3005. // Handle Highlight color changes
  3006. if (!highlightColor.Compare(currentHighlightColor))
  3007. {
  3008. // End drawing at the start of new highlight color to prevent a gap between highlight sections.
  3009. highlight_end.x = (highlight_end.x + m_textInfo.characterInfo[i].bottomLeft.x) / 2;
  3010. highlight_start.y = Mathf.Min(highlight_start.y, m_textInfo.characterInfo[i].descender);
  3011. highlight_end.y = Mathf.Max(highlight_end.y, m_textInfo.characterInfo[i].ascender);
  3012. DrawTextHighlight(highlight_start, highlight_end, ref last_vert_index, highlightColor);
  3013. beginHighlight = true;
  3014. highlight_start = highlight_end;
  3015. highlight_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, m_textInfo.characterInfo[i].descender, 0);
  3016. highlightColor = m_textInfo.characterInfo[i].highlightColor;
  3017. isColorTransition = true;
  3018. }
  3019. if (!isColorTransition)
  3020. {
  3021. // Use the Min / Max Extents of the Highlight area to handle different character sizes and fonts.
  3022. highlight_start.x = Mathf.Min(highlight_start.x, m_textInfo.characterInfo[i].bottomLeft.x); // - (1 * m_textInfo.characterInfo[i].scale));
  3023. highlight_start.y = Mathf.Min(highlight_start.y, m_textInfo.characterInfo[i].descender); // - (1 * m_textInfo.characterInfo[i].scale));
  3024. highlight_end.x = Mathf.Max(highlight_end.x, m_textInfo.characterInfo[i].topRight.x); // + (1 * m_textInfo.characterInfo[i].scale));
  3025. highlight_end.y = Mathf.Max(highlight_end.y, m_textInfo.characterInfo[i].ascender); // + (1 * m_textInfo.characterInfo[i].scale));
  3026. }
  3027. }
  3028. // End Highlight if text only contains one character.
  3029. if (beginHighlight && m_characterCount == 1)
  3030. {
  3031. beginHighlight = false;
  3032. DrawTextHighlight(highlight_start, highlight_end, ref last_vert_index, highlightColor);
  3033. }
  3034. else if (beginHighlight && (i == lineInfo.lastCharacterIndex || i >= lineInfo.lastVisibleCharacterIndex))
  3035. {
  3036. beginHighlight = false;
  3037. DrawTextHighlight(highlight_start, highlight_end, ref last_vert_index, highlightColor);
  3038. }
  3039. else if (beginHighlight && !isHighlightVisible)
  3040. {
  3041. beginHighlight = false;
  3042. DrawTextHighlight(highlight_start, highlight_end, ref last_vert_index, highlightColor);
  3043. }
  3044. }
  3045. else
  3046. {
  3047. // End Highlight
  3048. if (beginHighlight == true)
  3049. {
  3050. beginHighlight = false;
  3051. DrawTextHighlight(highlight_start, highlight_end, ref last_vert_index, highlightColor);
  3052. }
  3053. }
  3054. #endregion
  3055. lastLine = currentLine;
  3056. }
  3057. #endregion
  3058. // METRICS ABOUT THE TEXT OBJECT
  3059. m_textInfo.characterCount = m_characterCount;
  3060. m_textInfo.spriteCount = m_spriteCount;
  3061. m_textInfo.lineCount = lineCount;
  3062. m_textInfo.wordCount = wordCount != 0 && m_characterCount > 0 ? wordCount : 1;
  3063. m_textInfo.pageCount = m_pageNumber + 1;
  3064. ////Profiler.BeginSample("TMP Generate Text - Phase III");
  3065. // Update Mesh Vertex Data
  3066. if (m_renderMode == TextRenderFlags.Render && IsActive())
  3067. {
  3068. // Clear unused vertices
  3069. //m_textInfo.meshInfo[0].ClearUnusedVertices();
  3070. // Sort the geometry of the text object if needed.
  3071. if (m_geometrySortingOrder != VertexSortingOrder.Normal)
  3072. m_textInfo.meshInfo[0].SortGeometry(VertexSortingOrder.Reverse);
  3073. // Upload Mesh Data
  3074. m_mesh.MarkDynamic();
  3075. m_mesh.vertices = m_textInfo.meshInfo[0].vertices;
  3076. m_mesh.uv = m_textInfo.meshInfo[0].uvs0;
  3077. m_mesh.uv2 = m_textInfo.meshInfo[0].uvs2;
  3078. //m_mesh.uv4 = m_textInfo.meshInfo[0].uvs4;
  3079. m_mesh.colors32 = m_textInfo.meshInfo[0].colors32;
  3080. // Compute Bounds for the mesh. Manual computation is more efficient then using Mesh.recalcualteBounds.
  3081. m_mesh.RecalculateBounds();
  3082. //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));
  3083. for (int i = 1; i < m_textInfo.materialCount; i++)
  3084. {
  3085. // Clear unused vertices
  3086. m_textInfo.meshInfo[i].ClearUnusedVertices();
  3087. if (m_subTextObjects[i] == null) continue;
  3088. // Sort the geometry of the sub-text objects if needed.
  3089. if (m_geometrySortingOrder != VertexSortingOrder.Normal)
  3090. m_textInfo.meshInfo[i].SortGeometry(VertexSortingOrder.Reverse);
  3091. m_subTextObjects[i].mesh.vertices = m_textInfo.meshInfo[i].vertices;
  3092. m_subTextObjects[i].mesh.uv = m_textInfo.meshInfo[i].uvs0;
  3093. m_subTextObjects[i].mesh.uv2 = m_textInfo.meshInfo[i].uvs2;
  3094. //m_subTextObjects[i].mesh.uv4 = m_textInfo.meshInfo[i].uvs4;
  3095. m_subTextObjects[i].mesh.colors32 = m_textInfo.meshInfo[i].colors32;
  3096. m_subTextObjects[i].mesh.RecalculateBounds();
  3097. // Update the collider on the sub text object
  3098. //m_subTextObjects[i].UpdateColliders(m_textInfo.meshInfo[i].vertexCount);
  3099. }
  3100. }
  3101. // Event indicating the text has been regenerated.
  3102. TMPro_EventManager.ON_TEXT_CHANGED(this);
  3103. ////Profiler.EndSample();
  3104. //Debug.Log("Done Rendering Text.");
  3105. }
  3106. /// <summary>
  3107. /// Method to return the local corners of the Text Container or RectTransform.
  3108. /// </summary>
  3109. /// <returns></returns>
  3110. protected override Vector3[] GetTextContainerLocalCorners()
  3111. {
  3112. if (m_rectTransform == null) m_rectTransform = this.rectTransform;
  3113. m_rectTransform.GetLocalCorners(m_RectTransformCorners);
  3114. return m_RectTransformCorners;
  3115. }
  3116. /// <summary>
  3117. /// Method to disable the renderers.
  3118. /// </summary>
  3119. void SetMeshFilters(bool state)
  3120. {
  3121. // Parent text object
  3122. if (m_meshFilter != null)
  3123. {
  3124. if (state)
  3125. m_meshFilter.sharedMesh = m_mesh;
  3126. else
  3127. m_meshFilter.sharedMesh = null;
  3128. }
  3129. for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
  3130. {
  3131. if (m_subTextObjects[i].meshFilter != null)
  3132. {
  3133. if (state)
  3134. m_subTextObjects[i].meshFilter.sharedMesh = m_subTextObjects[i].mesh;
  3135. else
  3136. m_subTextObjects[i].meshFilter.sharedMesh = null;
  3137. }
  3138. }
  3139. }
  3140. /// <summary>
  3141. /// Method to Enable or Disable child SubMesh objects.
  3142. /// </summary>
  3143. /// <param name="state"></param>
  3144. protected override void SetActiveSubMeshes(bool state)
  3145. {
  3146. for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
  3147. {
  3148. if (m_subTextObjects[i].enabled != state)
  3149. m_subTextObjects[i].enabled = state;
  3150. }
  3151. }
  3152. /// <summary>
  3153. /// Destroy Sub Mesh Objects
  3154. /// </summary>
  3155. protected override void ClearSubMeshObjects()
  3156. {
  3157. for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
  3158. {
  3159. Debug.Log("Destroying Sub Text object[" + i + "].");
  3160. DestroyImmediate(m_subTextObjects[i]);
  3161. }
  3162. }
  3163. /// <summary>
  3164. /// Method returning the compound bounds of the text object and child sub objects.
  3165. /// </summary>
  3166. /// <returns></returns>
  3167. protected override Bounds GetCompoundBounds()
  3168. {
  3169. Bounds mainBounds = m_mesh.bounds;
  3170. Vector3 min = mainBounds.min;
  3171. Vector3 max = mainBounds.max;
  3172. for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
  3173. {
  3174. Bounds subBounds = m_subTextObjects[i].mesh.bounds;
  3175. min.x = min.x < subBounds.min.x ? min.x : subBounds.min.x;
  3176. min.y = min.y < subBounds.min.y ? min.y : subBounds.min.y;
  3177. max.x = max.x > subBounds.max.x ? max.x : subBounds.max.x;
  3178. max.y = max.y > subBounds.max.y ? max.y : subBounds.max.y;
  3179. }
  3180. Vector3 center = (min + max) / 2;
  3181. Vector2 size = max - min;
  3182. return new Bounds(center, size);
  3183. }
  3184. /// <summary>
  3185. /// Method to Update Scale in UV2
  3186. /// </summary>
  3187. void UpdateSDFScale(float lossyScale)
  3188. {
  3189. //Debug.Log("*** UpdateSDFScale() ***");
  3190. // Iterate through each of the characters.
  3191. for (int i = 0; i < m_textInfo.characterCount; i++)
  3192. {
  3193. // Only update scale for visible characters.
  3194. if (m_textInfo.characterInfo[i].isVisible && m_textInfo.characterInfo[i].elementType == TMP_TextElementType.Character)
  3195. {
  3196. float scale = lossyScale * m_textInfo.characterInfo[i].scale * (1 - m_charWidthAdjDelta);
  3197. if (!m_textInfo.characterInfo[i].isUsingAlternateTypeface && (m_textInfo.characterInfo[i].style & FontStyles.Bold) == FontStyles.Bold) scale *= -1;
  3198. int index = m_textInfo.characterInfo[i].materialReferenceIndex;
  3199. int vertexIndex = m_textInfo.characterInfo[i].vertexIndex;
  3200. m_textInfo.meshInfo[index].uvs2[vertexIndex + 0].y = scale;
  3201. m_textInfo.meshInfo[index].uvs2[vertexIndex + 1].y = scale;
  3202. m_textInfo.meshInfo[index].uvs2[vertexIndex + 2].y = scale;
  3203. m_textInfo.meshInfo[index].uvs2[vertexIndex + 3].y = scale;
  3204. }
  3205. }
  3206. // Push the updated uv2 scale information to the meshes.
  3207. for (int i = 0; i < m_textInfo.meshInfo.Length; i++)
  3208. {
  3209. if (i == 0)
  3210. m_mesh.uv2 = m_textInfo.meshInfo[0].uvs2;
  3211. else
  3212. m_subTextObjects[i].mesh.uv2 = m_textInfo.meshInfo[i].uvs2;
  3213. }
  3214. }
  3215. // Function to offset vertices position to account for line spacing changes.
  3216. protected override void AdjustLineOffset(int startIndex, int endIndex, float offset)
  3217. {
  3218. Vector3 vertexOffset = new Vector3(0, offset, 0);
  3219. for (int i = startIndex; i <= endIndex; i++)
  3220. {
  3221. m_textInfo.characterInfo[i].bottomLeft -= vertexOffset;
  3222. m_textInfo.characterInfo[i].topLeft -= vertexOffset;
  3223. m_textInfo.characterInfo[i].topRight -= vertexOffset;
  3224. m_textInfo.characterInfo[i].bottomRight -= vertexOffset;
  3225. m_textInfo.characterInfo[i].ascender -= vertexOffset.y;
  3226. m_textInfo.characterInfo[i].baseLine -= vertexOffset.y;
  3227. m_textInfo.characterInfo[i].descender -= vertexOffset.y;
  3228. if (m_textInfo.characterInfo[i].isVisible)
  3229. {
  3230. m_textInfo.characterInfo[i].vertex_BL.position -= vertexOffset;
  3231. m_textInfo.characterInfo[i].vertex_TL.position -= vertexOffset;
  3232. m_textInfo.characterInfo[i].vertex_TR.position -= vertexOffset;
  3233. m_textInfo.characterInfo[i].vertex_BR.position -= vertexOffset;
  3234. }
  3235. }
  3236. }
  3237. }
  3238. }