using System; using System.Collections; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.UI; #pragma warning disable 0414 // Disabled a few warnings related to serialized variables not used in this script but used in the editor. namespace TMPro { [DisallowMultipleComponent] [RequireComponent(typeof(RectTransform))] [RequireComponent(typeof(CanvasRenderer))] [AddComponentMenu("UI/TextMeshPro - Text (UI)", 11)] [ExecuteAlways] [HelpURL("https://docs.unity3d.com/Packages/com.unity.textmeshpro@3.0")] public partial class TextMeshProUGUI : TMP_Text, ILayoutElement { /// /// Get the material that will be used for rendering. /// public override Material materialForRendering { get { return TMP_MaterialManager.GetMaterialForRendering(this, m_sharedMaterial); } } /// /// Determines if the size of the text container will be adjusted to fit the text object when it is first created. /// public override bool autoSizeTextContainer { get { return m_autoSizeTextContainer; } set { if (m_autoSizeTextContainer == value) return; m_autoSizeTextContainer = value; if (m_autoSizeTextContainer) { CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this); SetLayoutDirty(); } } } /// /// Reference to the Mesh used by the text object. /// public override Mesh mesh { get { return m_mesh; } } /// /// Reference to the CanvasRenderer used by the text object. /// public new CanvasRenderer canvasRenderer { get { if (m_canvasRenderer == null) m_canvasRenderer = GetComponent(); return m_canvasRenderer; } } /// /// Anchor dampening prevents the anchor position from being adjusted unless the positional change exceeds about 40% of the width of the underline character. This essentially stabilizes the anchor position. /// //public bool anchorDampening //{ // get { return m_anchorDampening; } // set { if (m_anchorDampening != value) { havePropertiesChanged = true; m_anchorDampening = value; /* ScheduleUpdate(); */ } } //} #if !UNITY_2019_3_OR_NEWER [SerializeField] private bool m_Maskable = true; #endif private bool m_isRebuildingLayout = false; private Coroutine m_DelayedGraphicRebuild; private Coroutine m_DelayedMaterialRebuild; /// /// Function called by Unity when the horizontal layout needs to be recalculated. /// public void CalculateLayoutInputHorizontal() { //Debug.Log("*** CalculateLayoutHorizontal() on Object ID: " + GetInstanceID() + " at frame: " + Time.frameCount + "***"); } /// /// Function called by Unity when the vertical layout needs to be recalculated. /// public void CalculateLayoutInputVertical() { //Debug.Log("*** CalculateLayoutInputVertical() on Object ID: " + GetInstanceID() + " at frame: " + Time.frameCount + "***"); } public override void SetVerticesDirty() { if (this == null || !this.IsActive()) return; if (CanvasUpdateRegistry.IsRebuildingGraphics()) return; CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this); if (m_OnDirtyVertsCallback != null) m_OnDirtyVertsCallback(); } /// /// /// public override void SetLayoutDirty() { m_isPreferredWidthDirty = true; m_isPreferredHeightDirty = true; if (this == null || !this.IsActive()) return; LayoutRebuilder.MarkLayoutForRebuild(this.rectTransform); m_isLayoutDirty = true; if (m_OnDirtyLayoutCallback != null) m_OnDirtyLayoutCallback(); } /// /// /// public override void SetMaterialDirty() { if (this == null || !this.IsActive()) return; if (CanvasUpdateRegistry.IsRebuildingGraphics()) return; m_isMaterialDirty = true; CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this); if (m_OnDirtyMaterialCallback != null) m_OnDirtyMaterialCallback(); } /// /// /// public override void SetAllDirty() { SetLayoutDirty(); SetVerticesDirty(); SetMaterialDirty(); } /// /// Delay registration of text object for graphic rebuild by one frame. /// /// IEnumerator DelayedGraphicRebuild() { yield return null; CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this); if (m_OnDirtyVertsCallback != null) m_OnDirtyVertsCallback(); m_DelayedGraphicRebuild = null; } /// /// Delay registration of text object for graphic rebuild by one frame. /// /// IEnumerator DelayedMaterialRebuild() { yield return null; m_isMaterialDirty = true; CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this); if (m_OnDirtyMaterialCallback != null) m_OnDirtyMaterialCallback(); m_DelayedMaterialRebuild = null; } /// /// /// /// public override void Rebuild(CanvasUpdate update) { if (this == null) return; if (update == CanvasUpdate.Prelayout) { if (m_autoSizeTextContainer) { m_rectTransform.sizeDelta = GetPreferredValues(Mathf.Infinity, Mathf.Infinity); } } else if (update == CanvasUpdate.PreRender) { OnPreRenderCanvas(); if (!m_isMaterialDirty) return; UpdateMaterial(); m_isMaterialDirty = false; } } /// /// Method to keep the pivot of the sub text objects in sync with the parent pivot. /// private void UpdateSubObjectPivot() { if (m_textInfo == null) return; for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++) { m_subTextObjects[i].SetPivotDirty(); } //m_isPivotDirty = false; } /// /// /// /// /// public override Material GetModifiedMaterial(Material baseMaterial) { Material mat = baseMaterial; if (m_ShouldRecalculateStencil) { var rootCanvas = MaskUtilities.FindRootSortOverrideCanvas(transform); m_StencilValue = maskable ? MaskUtilities.GetStencilDepth(transform, rootCanvas) : 0; m_ShouldRecalculateStencil = false; } if (m_StencilValue > 0) { var maskMat = StencilMaterial.Add(mat, (1 << m_StencilValue) - 1, StencilOp.Keep, CompareFunction.Equal, ColorWriteMask.All, (1 << m_StencilValue) - 1, 0); StencilMaterial.Remove(m_MaskMaterial); m_MaskMaterial = maskMat; mat = m_MaskMaterial; } return mat; } /// /// /// protected override void UpdateMaterial() { //Debug.Log("*** UpdateMaterial() ***"); //if (!this.IsActive()) // return; if (m_sharedMaterial == null || canvasRenderer == null) return; m_canvasRenderer.materialCount = 1; m_canvasRenderer.SetMaterial(materialForRendering, 0); //m_canvasRenderer.SetTexture(materialForRendering.mainTexture); } //public override void OnRebuildRequested() //{ // //Debug.Log("OnRebuildRequested"); // base.OnRebuildRequested(); //} //public override bool Raycast(Vector2 sp, Camera eventCamera) //{ // //Debug.Log("Raycast Event. ScreenPoint: " + sp); // return base.Raycast(sp, eventCamera); //} // MASKING RELATED PROPERTIES /// /// Sets the masking offset from the bounds of the object /// public Vector4 maskOffset { get { return m_maskOffset; } set { m_maskOffset = value; UpdateMask(); m_havePropertiesChanged = true; } } //public override Material defaultMaterial //{ // get { Debug.Log("Default Material called."); return m_sharedMaterial; } //} //protected override void OnCanvasHierarchyChanged() //{ // //Debug.Log("OnCanvasHierarchyChanged..."); //} // IClippable implementation /// /// Method called when the state of a parent changes. /// public override void RecalculateClipping() { //Debug.Log("***** RecalculateClipping() *****"); base.RecalculateClipping(); } // IMaskable Implementation /// /// Method called when Stencil Mask needs to be updated on this element and parents. /// // public override void RecalculateMasking() // { // //Debug.Log("***** RecalculateMasking() *****"); // // this.m_ShouldRecalculateStencil = true; // SetMaterialDirty(); // } //public override void SetClipRect(Rect clipRect, bool validRect) //{ // //Debug.Log("***** SetClipRect (" + clipRect + ", " + validRect + ") *****"); // base.SetClipRect(clipRect, validRect); //} /// /// Override of the Cull function to provide for the ability to override the culling of the text object. /// /// /// public override void Cull(Rect clipRect, bool validRect) { // Delay culling check in the event the text layout is dirty and geometry has to be updated. if (m_isLayoutDirty) { TMP_UpdateManager.RegisterTextElementForCullingUpdate(this); m_ClipRect = clipRect; m_ValidRect = validRect; return; } // Get compound rect for the text object and sub text objects in local canvas space. Rect rect = GetCanvasSpaceClippingRect(); // No point culling if geometry bounds have no width or height. if (rect.width == 0 || rect.height == 0) return; var cull = !validRect || !clipRect.Overlaps(rect, true); if (m_canvasRenderer.cull != cull) { m_canvasRenderer.cull = cull; onCullStateChanged.Invoke(cull); OnCullingChanged(); // Update any potential sub mesh objects for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++) { m_subTextObjects[i].canvasRenderer.cull = cull; } } } private Rect m_ClipRect; private bool m_ValidRect; /// /// Internal function to allow delay of culling until the text geometry has been updated. /// internal override void UpdateCulling() { // Get compound rect for the text object and sub text objects in local canvas space. Rect rect = GetCanvasSpaceClippingRect(); // No point culling if geometry bounds have no width or height. if (rect.width == 0 || rect.height == 0) return; var cull = !m_ValidRect || !m_ClipRect.Overlaps(rect, true); if (m_canvasRenderer.cull != cull) { m_canvasRenderer.cull = cull; onCullStateChanged.Invoke(cull); OnCullingChanged(); // Update any potential sub mesh objects for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++) { m_subTextObjects[i].canvasRenderer.cull = cull; } } } /* /// /// Sets the mask type /// public MaskingTypes mask { get { return m_mask; } set { m_mask = value; havePropertiesChanged = true; isMaskUpdateRequired = true; } } /// /// Set the masking offset mode (as percentage or pixels) /// public MaskingOffsetMode maskOffsetMode { get { return m_maskOffsetMode; } set { m_maskOffsetMode = value; havePropertiesChanged = true; isMaskUpdateRequired = true; } } */ /* /// /// Sets the softness of the mask /// public Vector2 maskSoftness { get { return m_maskSoftness; } set { m_maskSoftness = value; havePropertiesChanged = true; isMaskUpdateRequired = true; } } /// /// Allows to move / offset the mesh vertices by a set amount /// public Vector2 vertexOffset { get { return m_vertexOffset; } set { m_vertexOffset = value; havePropertiesChanged = true; isMaskUpdateRequired = true; } } */ /// /// Function to be used to force recomputing of character padding when Shader / Material properties have been changed via script. /// public override void UpdateMeshPadding() { m_padding = ShaderUtilities.GetPadding(m_sharedMaterial, m_enableExtraPadding, m_isUsingBold); m_isMaskingEnabled = ShaderUtilities.IsMaskingEnabled(m_sharedMaterial); m_havePropertiesChanged = true; checkPaddingRequired = false; // Return if text object is not awake yet. if (m_textInfo == null) return; // Update sub text objects for (int i = 1; i < m_textInfo.materialCount; i++) m_subTextObjects[i].UpdateMeshPadding(m_enableExtraPadding, m_isUsingBold); } /// /// Tweens the CanvasRenderer color associated with this Graphic. /// /// Target color. /// Tween duration. /// Should ignore Time.scale? /// Should also Tween the alpha channel? protected override void InternalCrossFadeColor(Color targetColor, float duration, bool ignoreTimeScale, bool useAlpha) { if (m_textInfo == null) return; int materialCount = m_textInfo.materialCount; for (int i = 1; i < materialCount; i++) { m_subTextObjects[i].CrossFadeColor(targetColor, duration, ignoreTimeScale, useAlpha); } } /// /// Tweens the alpha of the CanvasRenderer color associated with this Graphic. /// /// Target alpha. /// Duration of the tween in seconds. /// Should ignore Time.scale? protected override void InternalCrossFadeAlpha(float alpha, float duration, bool ignoreTimeScale) { if (m_textInfo == null) return; int materialCount = m_textInfo.materialCount; for (int i = 1; i < materialCount; i++) { m_subTextObjects[i].CrossFadeAlpha(alpha, duration, ignoreTimeScale); } } /// /// Function to force regeneration of the text object before its normal process time. This is useful when changes to the text object properties need to be applied immediately. /// /// Ignore Active State of text objects. Inactive objects are ignored by default. /// Force re-parsing of the text. public override void ForceMeshUpdate(bool ignoreActiveState = false, bool forceTextReparsing = false) { m_havePropertiesChanged = true; m_ignoreActiveState = ignoreActiveState; // Special handling in the event the Canvas is only disabled if (m_canvas == null) m_canvas = GetComponentInParent(); OnPreRenderCanvas(); } /// /// Function used to evaluate the length of a text string. /// /// /// public override TMP_TextInfo GetTextInfo(string text) { SetText(text); SetArraySizes(m_TextProcessingArray); m_renderMode = TextRenderFlags.DontRender; ComputeMarginSize(); // Need to make sure we have a valid reference to a Canvas. if (m_canvas == null) m_canvas = this.canvas; GenerateTextMesh(); m_renderMode = TextRenderFlags.Render; return this.textInfo; } /// /// Function to clear the geometry of the Primary and Sub Text objects. /// public override void ClearMesh() { m_canvasRenderer.SetMesh(null); for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++) m_subTextObjects[i].canvasRenderer.SetMesh(null); } /// /// Event to allow users to modify the content of the text info before the text is rendered. /// public override event Action OnPreRenderText; /// /// Function to update the geometry of the main and sub text objects. /// /// /// public override void UpdateGeometry(Mesh mesh, int index) { mesh.RecalculateBounds(); if (index == 0) { m_canvasRenderer.SetMesh(mesh); } else { m_subTextObjects[index].canvasRenderer.SetMesh(mesh); } } /// /// Function to upload the updated vertex data and renderer. /// public override void UpdateVertexData(TMP_VertexDataUpdateFlags flags) { int materialCount = m_textInfo.materialCount; for (int i = 0; i < materialCount; i++) { Mesh mesh; if (i == 0) mesh = m_mesh; else { // Clear unused vertices // TODO: Causes issues when sorting geometry as last vertex data attribute get wiped out. //m_textInfo.meshInfo[i].ClearUnusedVertices(); mesh = m_subTextObjects[i].mesh; } if ((flags & TMP_VertexDataUpdateFlags.Vertices) == TMP_VertexDataUpdateFlags.Vertices) mesh.vertices = m_textInfo.meshInfo[i].vertices; if ((flags & TMP_VertexDataUpdateFlags.Uv0) == TMP_VertexDataUpdateFlags.Uv0) mesh.uv = m_textInfo.meshInfo[i].uvs0; if ((flags & TMP_VertexDataUpdateFlags.Uv2) == TMP_VertexDataUpdateFlags.Uv2) mesh.uv2 = m_textInfo.meshInfo[i].uvs2; //if ((flags & TMP_VertexDataUpdateFlags.Uv4) == TMP_VertexDataUpdateFlags.Uv4) // mesh.uv4 = m_textInfo.meshInfo[i].uvs4; if ((flags & TMP_VertexDataUpdateFlags.Colors32) == TMP_VertexDataUpdateFlags.Colors32) mesh.colors32 = m_textInfo.meshInfo[i].colors32; mesh.RecalculateBounds(); if (i == 0) m_canvasRenderer.SetMesh(mesh); else m_subTextObjects[i].canvasRenderer.SetMesh(mesh); } } /// /// Function to upload the updated vertex data and renderer. /// public override void UpdateVertexData() { int materialCount = m_textInfo.materialCount; for (int i = 0; i < materialCount; i++) { Mesh mesh; if (i == 0) mesh = m_mesh; else { // Clear unused vertices m_textInfo.meshInfo[i].ClearUnusedVertices(); mesh = m_subTextObjects[i].mesh; } //mesh.MarkDynamic(); mesh.vertices = m_textInfo.meshInfo[i].vertices; mesh.uv = m_textInfo.meshInfo[i].uvs0; mesh.uv2 = m_textInfo.meshInfo[i].uvs2; //mesh.uv4 = m_textInfo.meshInfo[i].uvs4; mesh.colors32 = m_textInfo.meshInfo[i].colors32; mesh.RecalculateBounds(); if (i == 0) m_canvasRenderer.SetMesh(mesh); else m_subTextObjects[i].canvasRenderer.SetMesh(mesh); } } public void UpdateFontAsset() { LoadFontAsset(); } } }