using System.Collections.Generic; using UnityEngine; using System; namespace TMPro { public class MaterialReferenceManager { private static MaterialReferenceManager s_Instance; // Dictionaries used to track Asset references. private Dictionary m_FontMaterialReferenceLookup = new Dictionary(); private Dictionary m_FontAssetReferenceLookup = new Dictionary(); private Dictionary m_SpriteAssetReferenceLookup = new Dictionary(); private Dictionary m_ColorGradientReferenceLookup = new Dictionary(); /// /// Get a singleton instance of the registry /// public static MaterialReferenceManager instance { get { if (MaterialReferenceManager.s_Instance == null) MaterialReferenceManager.s_Instance = new MaterialReferenceManager(); return MaterialReferenceManager.s_Instance; } } #if UNITY_EDITOR [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)] static void ResetStaticsOnLoad() { s_Instance = default; } #endif /// /// Add new font asset reference to dictionary. /// /// /// Registers the font asset and its material with the singleton so TextMesh Pro /// can reuse them across text objects. Duplicate hash codes are ignored. Call /// when loading or creating font assets at runtime. /// /// The font asset to register with the manager. /// /// Register a font asset after loading it so it is tracked for material /// and mesh updates. Typically called from font asset initialization. /// ("MyFont"); /// MaterialReferenceManager.AddFontAsset(myFont); /// ]]> /// public static void AddFontAsset(TMP_FontAsset fontAsset) { MaterialReferenceManager.instance.AddFontAssetInternal(fontAsset); } /// /// Add new Font Asset reference to dictionary. /// /// private void AddFontAssetInternal(TMP_FontAsset fontAsset) { if (m_FontAssetReferenceLookup.ContainsKey(fontAsset.hashCode)) return; // Add reference to the font asset. m_FontAssetReferenceLookup.Add(fontAsset.hashCode, fontAsset); // Add reference to the font material. m_FontMaterialReferenceLookup.Add(fontAsset.materialHashCode, fontAsset.material); } /// /// Add new Sprite Asset to dictionary. /// /// /// Registers the sprite asset and its material with the singleton so inline /// sprites in text can resolve references. Duplicate hash codes are ignored. /// Call when loading or creating sprite assets at runtime. /// /// The sprite asset to register with the manager. /// /// Register a sprite asset so it can be used in rich text (e.g. sprite /// tags). Typically called when the asset is loaded or created. /// ("EmojiSprites"); /// MaterialReferenceManager.AddSpriteAsset(emojiAsset); /// ]]> /// public static void AddSpriteAsset(TMP_SpriteAsset spriteAsset) { MaterialReferenceManager.instance.AddSpriteAssetInternal(spriteAsset); } /// /// Internal method to add a new sprite asset to the dictionary. /// /// /// private void AddSpriteAssetInternal(TMP_SpriteAsset spriteAsset) { if (m_SpriteAssetReferenceLookup.ContainsKey(spriteAsset.hashCode)) return; // Add reference to sprite asset. m_SpriteAssetReferenceLookup.Add(spriteAsset.hashCode, spriteAsset); // Adding reference to the sprite asset material as well m_FontMaterialReferenceLookup.Add(spriteAsset.hashCode, spriteAsset.material); } /// /// Add new Sprite Asset to dictionary. /// /// /// Registers the sprite asset under the given hash code so it can be looked up /// by that code. Use when the asset's default hashCode is not suitable (e.g. /// custom atlases). Duplicate hash codes overwrite the previous entry. /// /// The hash code to use when looking up the sprite asset. /// The sprite asset to register with the manager. /// /// Register a sprite asset with a specific hash for custom lookup. Use /// the same hashCode when resolving sprites in rich text. /// /// public static void AddSpriteAsset(int hashCode, TMP_SpriteAsset spriteAsset) { MaterialReferenceManager.instance.AddSpriteAssetInternal(hashCode, spriteAsset); } /// /// Internal method to add a new sprite asset to the dictionary. /// /// /// private void AddSpriteAssetInternal(int hashCode, TMP_SpriteAsset spriteAsset) { if (m_SpriteAssetReferenceLookup.ContainsKey(hashCode)) return; // Add reference to Sprite Asset. m_SpriteAssetReferenceLookup.Add(hashCode, spriteAsset); // Add reference to Sprite Asset using the asset hashcode. m_FontMaterialReferenceLookup.Add(hashCode, spriteAsset.material); // Compatibility check if (spriteAsset.hashCode == 0) spriteAsset.hashCode = hashCode; } /// /// Add new Material reference to dictionary. /// /// /// Registers a font material by hash code so TextMesh Pro can look it up when /// building meshes. Use the same hashCode as the font asset's material for /// correct reference counting. Duplicate hash codes overwrite the previous entry. /// /// The hash code used to look up the material. /// The material instance to register with the manager. /// /// Register a material by hash when you need to track it separately from /// a font asset. Ensure the hashCode matches the material instance. /// /// public static void AddFontMaterial(int hashCode, Material material) { MaterialReferenceManager.instance.AddFontMaterialInternal(hashCode, material); } /// /// Add new material reference to dictionary. /// /// /// private void AddFontMaterialInternal(int hashCode, Material material) { // Since this function is called after checking if the material is // contained in the dictionary, there is no need to check again. m_FontMaterialReferenceLookup.Add(hashCode, material); } /// /// Add new Color Gradient Preset to dictionary. /// /// /// Registers a color gradient preset by hash code so it can be referenced in /// rich text (e.g. <gradient> tags). Duplicate hash codes are ignored. /// Call when loading or creating gradient presets at runtime. /// /// The hash code used to look up the gradient preset. /// The color gradient preset to register with the manager. /// /// Register a gradient preset so it can be used in text. Use a stable /// hashCode (e.g. from the preset name or asset) for lookups. /// /// public static void AddColorGradientPreset(int hashCode, TMP_ColorGradient colorGradient) { MaterialReferenceManager.instance.AddColorGradientPreset_Internal(hashCode, colorGradient); } /// /// Internal method to add a new Color Gradient Preset to the dictionary. /// /// /// private void AddColorGradientPreset_Internal(int hashCode, TMP_ColorGradient colorGradient) { if (m_ColorGradientReferenceLookup.ContainsKey(hashCode)) return; // Add reference to Color Gradient Preset Asset. m_ColorGradientReferenceLookup.Add(hashCode, colorGradient); } /// /// Add new material reference and return the index of this new reference in the materialReferences array. /// /// /// /// //public int AddMaterial(Material material, int materialHashCode, TMP_FontAsset fontAsset) //{ // if (!m_MaterialReferenceLookup.ContainsKey(materialHashCode)) // { // int index = m_MaterialReferenceLookup.Count; // materialReferences[index].fontAsset = fontAsset; // materialReferences[index].material = material; // materialReferences[index].isDefaultMaterial = material.GetEntityId() == fontAsset.material.GetEntityId() ? true : false; // materialReferences[index].index = index; // materialReferences[index].referenceCount = 0; // m_MaterialReferenceLookup[materialHashCode] = index; // // Compute Padding value and store it // // TODO // int fontAssetHashCode = fontAsset.hashCode; // if (!m_FontAssetReferenceLookup.ContainsKey(fontAssetHashCode)) // m_FontAssetReferenceLookup.Add(fontAssetHashCode, fontAsset); // m_countInternal += 1; // return index; // } // else // { // return m_MaterialReferenceLookup[materialHashCode]; // } //} /// /// Add new material reference and return the index of this new reference in the materialReferences array. /// /// /// /// /// //public int AddMaterial(Material material, int materialHashCode, TMP_SpriteAsset spriteAsset) //{ // if (!m_MaterialReferenceLookup.ContainsKey(materialHashCode)) // { // int index = m_MaterialReferenceLookup.Count; // materialReferences[index].fontAsset = materialReferences[0].fontAsset; // materialReferences[index].spriteAsset = spriteAsset; // materialReferences[index].material = material; // materialReferences[index].isDefaultMaterial = true; // materialReferences[index].index = index; // materialReferences[index].referenceCount = 0; // m_MaterialReferenceLookup[materialHashCode] = index; // int spriteAssetHashCode = spriteAsset.hashCode; // if (!m_SpriteAssetReferenceLookup.ContainsKey(spriteAssetHashCode)) // m_SpriteAssetReferenceLookup.Add(spriteAssetHashCode, spriteAsset); // m_countInternal += 1; // return index; // } // else // { // return m_MaterialReferenceLookup[materialHashCode]; // } //} /// /// Checks whether the font asset is referenced. /// /// The font asset to check for in the manager. /// True if the font asset is already registered in the manager. public bool Contains(TMP_FontAsset font) { return m_FontAssetReferenceLookup.ContainsKey(font.hashCode); } /// /// Checks whether the sprite asset is referenced. /// /// The sprite asset to check for in the manager. /// True if the sprite asset is already registered in the manager. public bool Contains(TMP_SpriteAsset sprite) { return m_FontAssetReferenceLookup.ContainsKey(sprite.hashCode); } /// /// Gets the font asset for the given hash code, if registered. /// /// The hash code of the font asset to look up. /// The font asset if found, or null otherwise. /// True if the font asset was found; otherwise, false. public static bool TryGetFontAsset(int hashCode, out TMP_FontAsset fontAsset) { return MaterialReferenceManager.instance.TryGetFontAssetInternal(hashCode, out fontAsset); } /// /// Returns the font asset for the given hash code from the internal lookup. /// /// /// /// private bool TryGetFontAssetInternal(int hashCode, out TMP_FontAsset fontAsset) { fontAsset = null; return m_FontAssetReferenceLookup.TryGetValue(hashCode, out fontAsset); } /// /// Gets the sprite asset for the given hash code, if registered. /// /// The hash code of the sprite asset to look up. /// The sprite asset if found, or null otherwise. /// True if the sprite asset was found; otherwise, false. public static bool TryGetSpriteAsset(int hashCode, out TMP_SpriteAsset spriteAsset) { return MaterialReferenceManager.instance.TryGetSpriteAssetInternal(hashCode, out spriteAsset); } /// /// Returns the sprite asset for the given hash code from the internal lookup. /// /// /// /// private bool TryGetSpriteAssetInternal(int hashCode, out TMP_SpriteAsset spriteAsset) { spriteAsset = null; return m_SpriteAssetReferenceLookup.TryGetValue(hashCode, out spriteAsset); } /// /// Gets the color gradient preset for the given hash code, if registered. /// /// The hash code of the gradient preset to look up. /// The gradient preset if found, or null otherwise. /// True if the gradient preset was found; otherwise, false. public static bool TryGetColorGradientPreset(int hashCode, out TMP_ColorGradient gradientPreset) { return MaterialReferenceManager.instance.TryGetColorGradientPresetInternal(hashCode, out gradientPreset); } /// /// Returns the color gradient preset for the given hash code from the internal lookup. /// /// /// /// private bool TryGetColorGradientPresetInternal(int hashCode, out TMP_ColorGradient gradientPreset) { gradientPreset = null; return m_ColorGradientReferenceLookup.TryGetValue(hashCode, out gradientPreset); } /// /// Gets the font material for the given hash code, if registered. /// /// The hash code of the material to look up. /// The material if found, or null otherwise. /// True if the material was found; otherwise, false. public static bool TryGetMaterial(int hashCode, out Material material) { return MaterialReferenceManager.instance.TryGetMaterialInternal(hashCode, out material); } /// /// Returns the font material for the given hash code from the internal lookup. /// /// /// /// private bool TryGetMaterialInternal(int hashCode, out Material material) { material = null; return m_FontMaterialReferenceLookup.TryGetValue(hashCode, out material); } /// /// Function to lookup a material based on hash code and returning the MaterialReference containing this material. /// /// /// /// //public bool TryGetMaterial(int hashCode, out MaterialReference materialReference) //{ // int materialIndex = -1; // if (m_MaterialReferenceLookup.TryGetValue(hashCode, out materialIndex)) // { // materialReference = materialReferences[materialIndex]; // return true; // } // materialReference = new MaterialReference(); // return false; //} /// /// /// /// /// //public int GetMaterialIndex(TMP_FontAsset fontAsset) //{ // if (m_MaterialReferenceLookup.ContainsKey(fontAsset.materialHashCode)) // return m_MaterialReferenceLookup[fontAsset.materialHashCode]; // return -1; //} /// /// /// /// /// //public TMP_FontAsset GetFontAsset(int index) //{ // if (index >= 0 && index < materialReferences.Length) // return materialReferences[index].fontAsset; // return null; //} /// /// /// /// /// /// //public void SetDefaultMaterial(Material material, int materialHashCode, TMP_FontAsset fontAsset) //{ // if (!m_MaterialReferenceLookup.ContainsKey(materialHashCode)) // { // materialReferences[0].fontAsset = fontAsset; // materialReferences[0].material = material; // materialReferences[0].index = 0; // materialReferences[0].isDefaultMaterial = material.GetEntityId() == fontAsset.material.GetEntityId() ? true : false; // materialReferences[0].referenceCount = 0; // m_MaterialReferenceLookup[materialHashCode] = 0; // // Compute Padding value and store it // // TODO // int fontHashCode = fontAsset.hashCode; // if (!m_FontAssetReferenceLookup.ContainsKey(fontHashCode)) // m_FontAssetReferenceLookup.Add(fontHashCode, fontAsset); // } // else // { // materialReferences[0].fontAsset = fontAsset; // materialReferences[0].material = material; // materialReferences[0].index = 0; // materialReferences[0].referenceCount = 0; // m_MaterialReferenceLookup[materialHashCode] = 0; // } // // Compute padding // // TODO // m_countInternal = 1; //} /// /// /// //public void Clear() //{ // //m_currentIndex = 0; // m_MaterialReferenceLookup.Clear(); // m_SpriteAssetReferenceLookup.Clear(); // m_FontAssetReferenceLookup.Clear(); //} /// /// Function to clear the reference count for each of the material references. /// //public void ClearReferenceCount() //{ // m_countInternal = 0; // for (int i = 0; i < materialReferences.Length; i++) // { // if (materialReferences[i].fontAsset == null) // return; // materialReferences[i].referenceCount = 0; // } //} } public struct TMP_MaterialReference { public Material material; public int referenceCount; } public struct MaterialReference { public int index; public TMP_FontAsset fontAsset; public TMP_SpriteAsset spriteAsset; public Material material; public bool isDefaultMaterial; public bool isFallbackMaterial; public Material fallbackMaterial; public float padding; public int referenceCount; /// /// Constructor for new Material Reference. /// /// The index in the material reference array. /// The font asset for this material reference. /// The sprite asset for this material reference. /// The material instance for this reference entry. /// The padding value applied to the material reference. public MaterialReference(int index, TMP_FontAsset fontAsset, TMP_SpriteAsset spriteAsset, Material material, float padding) { this.index = index; this.fontAsset = fontAsset; this.spriteAsset = spriteAsset; this.material = material; this.isDefaultMaterial = material.GetEntityId() == fontAsset.material.GetEntityId(); this.isFallbackMaterial = false; this.fallbackMaterial = null; this.padding = padding; this.referenceCount = 0; } /// /// Checks whether the font asset is contained in the material reference array. /// /// The material reference array to search. /// The font asset to look for. /// True if the font asset is in the array. public static bool Contains(MaterialReference[] materialReferences, TMP_FontAsset fontAsset) { EntityId id = fontAsset.GetEntityId(); for (int i = 0; i < materialReferences.Length && materialReferences[i].fontAsset != null; i++) { if (materialReferences[i].fontAsset.GetEntityId() == id) return true; } return false; } /// /// Adds a new material reference and returns its index in the material reference array. /// /// The material to add. /// The font asset. /// The material reference array. /// The lookup dictionary. /// The index of the added or existing material reference. public static int AddMaterialReference(Material material, TMP_FontAsset fontAsset, ref MaterialReference[] materialReferences, Dictionary materialReferenceIndexLookup) { EntityId materialID = material.GetEntityId(); int index; if (materialReferenceIndexLookup.TryGetValue(materialID, out index)) return index; index = materialReferenceIndexLookup.Count; // Add new reference index materialReferenceIndexLookup[materialID] = index; if (index >= materialReferences.Length) System.Array.Resize(ref materialReferences, Mathf.NextPowerOfTwo(index + 1)); materialReferences[index].index = index; materialReferences[index].fontAsset = fontAsset; materialReferences[index].spriteAsset = null; materialReferences[index].material = material; materialReferences[index].isDefaultMaterial = materialID == fontAsset.material.GetEntityId(); materialReferences[index].referenceCount = 0; return index; } /// /// Adds a new material reference for a sprite asset and returns its index in the array. /// /// The material to add. /// The sprite asset. /// The material reference array. /// The lookup dictionary. /// The index of the added or existing material reference. public static int AddMaterialReference(Material material, TMP_SpriteAsset spriteAsset, ref MaterialReference[] materialReferences, Dictionary materialReferenceIndexLookup) { EntityId materialID = material.GetEntityId(); int index; if (materialReferenceIndexLookup.TryGetValue(materialID, out index)) return index; index = materialReferenceIndexLookup.Count; // Add new reference index materialReferenceIndexLookup[materialID] = index; if (index >= materialReferences.Length) System.Array.Resize(ref materialReferences, Mathf.NextPowerOfTwo(index + 1)); materialReferences[index].index = index; materialReferences[index].fontAsset = materialReferences[0].fontAsset; materialReferences[index].spriteAsset = spriteAsset; materialReferences[index].material = material; materialReferences[index].isDefaultMaterial = true; materialReferences[index].referenceCount = 0; return index; } } }