TMP_SpriteAssetMenu.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. using UnityEngine;
  2. using UnityEngine.TextCore;
  3. using UnityEditor;
  4. using System.Linq;
  5. using System.IO;
  6. using System.Collections;
  7. using System.Collections.Generic;
  8. namespace TMPro.EditorUtilities
  9. {
  10. public static class TMP_SpriteAssetMenu
  11. {
  12. // Add a Context Menu to the Sprite Asset Editor Panel to Create and Add a Default Material.
  13. [MenuItem("CONTEXT/TMP_SpriteAsset/Add Default Material", false, 2200)]
  14. static void CopyTexture(MenuCommand command)
  15. {
  16. TMP_SpriteAsset spriteAsset = (TMP_SpriteAsset)command.context;
  17. // Make sure the sprite asset already contains a default material
  18. if (spriteAsset != null && spriteAsset.material == null)
  19. {
  20. // Add new default material for sprite asset.
  21. AddDefaultMaterial(spriteAsset);
  22. }
  23. }
  24. // Add a Context Menu to the Sprite Asset Editor Panel to update existing sprite assets.
  25. [MenuItem("CONTEXT/TMP_SpriteAsset/Update Sprite Asset", false, 2100)]
  26. static void UpdateSpriteAsset(MenuCommand command)
  27. {
  28. TMP_SpriteAsset spriteAsset = (TMP_SpriteAsset)command.context;
  29. if (spriteAsset == null)
  30. return;
  31. // Get a list of all the sprites contained in the texture referenced by the sprite asset.
  32. // This only works if the texture is set to sprite mode.
  33. string filePath = AssetDatabase.GetAssetPath(spriteAsset.spriteSheet);
  34. if (string.IsNullOrEmpty(filePath))
  35. return;
  36. // Get all the Sprites sorted Left to Right / Top to Bottom
  37. Sprite[] sprites = AssetDatabase.LoadAllAssetsAtPath(filePath).Select(x => x as Sprite).Where(x => x != null).OrderByDescending(x => x.rect.y).ThenBy(x => x.rect.x).ToArray();
  38. List<TMP_SpriteGlyph> spriteGlyphTable = spriteAsset.spriteGlyphTable;
  39. // Finding available glyph indexes to insert new glyphs into.
  40. var tempGlyphTable = spriteGlyphTable.OrderBy(glyph => glyph.index).ToList();
  41. List<uint> availableGlyphIndexes = new List<uint>();
  42. int elementIndex = 0;
  43. for (uint i = 0; i < tempGlyphTable[tempGlyphTable.Count - 1].index; i++)
  44. {
  45. uint currentElementIndex = tempGlyphTable[elementIndex].index;
  46. if (i == currentElementIndex)
  47. elementIndex += 1;
  48. else
  49. availableGlyphIndexes.Add(i);
  50. }
  51. // Iterate over each of the sprites in the texture to try to match them to existing sprites in the sprite asset.
  52. for (int i = 0; i < sprites.Length; i++)
  53. {
  54. int id = sprites[i].GetInstanceID();
  55. int glyphIndex = spriteGlyphTable.FindIndex(item => item.sprite.GetInstanceID() == id);
  56. if (glyphIndex == -1)
  57. {
  58. // Add new Sprite Glyph to the table
  59. Sprite sprite = sprites[i];
  60. TMP_SpriteGlyph spriteGlyph = new TMP_SpriteGlyph();
  61. // Get available glyph index
  62. if (availableGlyphIndexes.Count > 0)
  63. {
  64. spriteGlyph.index = availableGlyphIndexes[0];
  65. availableGlyphIndexes.RemoveAt(0);
  66. }
  67. else
  68. spriteGlyph.index = (uint)spriteGlyphTable.Count;
  69. spriteGlyph.metrics = new GlyphMetrics(sprite.rect.width, sprite.rect.height, -sprite.pivot.x, sprite.rect.height - sprite.pivot.y, sprite.rect.width);
  70. spriteGlyph.glyphRect = new GlyphRect(sprite.rect);
  71. spriteGlyph.scale = 1.0f;
  72. spriteGlyph.sprite = sprite;
  73. spriteGlyphTable.Add(spriteGlyph);
  74. TMP_SpriteCharacter spriteCharacter = new TMP_SpriteCharacter(0, spriteGlyph);
  75. spriteCharacter.name = sprite.name;
  76. spriteCharacter.scale = 1.0f;
  77. spriteAsset.spriteCharacterTable.Add(spriteCharacter);
  78. }
  79. else
  80. {
  81. // Look for changes in existing Sprite Glyph
  82. Sprite sprite = sprites[i];
  83. TMP_SpriteGlyph spriteGlyph = spriteGlyphTable[glyphIndex];
  84. // We only update changes to the sprite position / glyph rect.
  85. if (spriteGlyph.glyphRect.x != sprite.rect.x || spriteGlyph.glyphRect.y != sprite.rect.y || spriteGlyph.glyphRect.width != sprite.rect.width || spriteGlyph.glyphRect.height != sprite.rect.height)
  86. spriteGlyph.glyphRect = new GlyphRect(sprite.rect);
  87. }
  88. }
  89. // Sort glyph table by glyph index
  90. spriteAsset.SortGlyphTable();
  91. spriteAsset.UpdateLookupTables();
  92. TMPro_EventManager.ON_SPRITE_ASSET_PROPERTY_CHANGED(true, spriteAsset);
  93. }
  94. [MenuItem("Assets/Create/TextMeshPro/Sprite Asset", false, 110)]
  95. public static void CreateSpriteAsset()
  96. {
  97. Object target = Selection.activeObject;
  98. // Make sure the selection is a texture.
  99. if (target == null || target.GetType() != typeof(Texture2D))
  100. {
  101. Debug.LogWarning("A texture which contains sprites must first be selected in order to create a TextMesh Pro Sprite Asset.");
  102. return;
  103. }
  104. Texture2D sourceTex = target as Texture2D;
  105. // Get the path to the selected texture.
  106. string filePathWithName = AssetDatabase.GetAssetPath(sourceTex);
  107. string fileNameWithExtension = Path.GetFileName(filePathWithName);
  108. string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(filePathWithName);
  109. string filePath = filePathWithName.Replace(fileNameWithExtension, "");
  110. // Check if Sprite Asset already exists
  111. TMP_SpriteAsset spriteAsset = AssetDatabase.LoadAssetAtPath(filePath + fileNameWithoutExtension + ".asset", typeof(TMP_SpriteAsset)) as TMP_SpriteAsset;
  112. bool isNewAsset = spriteAsset == null ? true : false;
  113. if (isNewAsset)
  114. {
  115. // Create new Sprite Asset using this texture
  116. spriteAsset = ScriptableObject.CreateInstance<TMP_SpriteAsset>();
  117. AssetDatabase.CreateAsset(spriteAsset, filePath + fileNameWithoutExtension + ".asset");
  118. spriteAsset.version = "1.1.0";
  119. // Compute the hash code for the sprite asset.
  120. spriteAsset.hashCode = TMP_TextUtilities.GetSimpleHashCode(spriteAsset.name);
  121. // Assign new Sprite Sheet texture to the Sprite Asset.
  122. spriteAsset.spriteSheet = sourceTex;
  123. List<TMP_SpriteGlyph> spriteGlyphTable = new List<TMP_SpriteGlyph>();
  124. List<TMP_SpriteCharacter> spriteCharacterTable = new List<TMP_SpriteCharacter>();
  125. PopulateSpriteTables(sourceTex, ref spriteCharacterTable, ref spriteGlyphTable);
  126. spriteAsset.spriteCharacterTable = spriteCharacterTable;
  127. spriteAsset.spriteGlyphTable = spriteGlyphTable;
  128. // Add new default material for sprite asset.
  129. AddDefaultMaterial(spriteAsset);
  130. }
  131. //else
  132. //{
  133. // spriteAsset.spriteInfoList = UpdateSpriteInfo(spriteAsset);
  134. // // Make sure the sprite asset already contains a default material
  135. // if (spriteAsset.material == null)
  136. // {
  137. // // Add new default material for sprite asset.
  138. // AddDefaultMaterial(spriteAsset);
  139. // }
  140. //}
  141. // Update Lookup tables.
  142. spriteAsset.UpdateLookupTables();
  143. // Get the Sprites contained in the Sprite Sheet
  144. EditorUtility.SetDirty(spriteAsset);
  145. //spriteAsset.sprites = sprites;
  146. // Set source texture back to Not Readable.
  147. //texImporter.isReadable = false;
  148. AssetDatabase.SaveAssets();
  149. AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(spriteAsset)); // Re-import font asset to get the new updated version.
  150. //AssetDatabase.Refresh();
  151. }
  152. private static void PopulateSpriteTables(Texture source, ref List<TMP_SpriteCharacter> spriteCharacterTable, ref List<TMP_SpriteGlyph> spriteGlyphTable)
  153. {
  154. //Debug.Log("Creating new Sprite Asset.");
  155. string filePath = AssetDatabase.GetAssetPath(source);
  156. // Get all the Sprites sorted by Index
  157. Sprite[] sprites = AssetDatabase.LoadAllAssetsAtPath(filePath).Select(x => x as Sprite).Where(x => x != null).OrderByDescending(x => x.rect.y).ThenBy(x => x.rect.x).ToArray();
  158. for (int i = 0; i < sprites.Length; i++)
  159. {
  160. Sprite sprite = sprites[i];
  161. TMP_SpriteGlyph spriteGlyph = new TMP_SpriteGlyph();
  162. spriteGlyph.index = (uint)i;
  163. spriteGlyph.metrics = new GlyphMetrics(sprite.rect.width, sprite.rect.height, -sprite.pivot.x, sprite.rect.height - sprite.pivot.y, sprite.rect.width);
  164. spriteGlyph.glyphRect = new GlyphRect(sprite.rect);
  165. spriteGlyph.scale = 1.0f;
  166. spriteGlyph.sprite = sprite;
  167. spriteGlyphTable.Add(spriteGlyph);
  168. TMP_SpriteCharacter spriteCharacter = new TMP_SpriteCharacter(0, spriteGlyph);
  169. spriteCharacter.name = sprite.name;
  170. spriteCharacter.scale = 1.0f;
  171. spriteCharacterTable.Add(spriteCharacter);
  172. }
  173. }
  174. /// <summary>
  175. /// Create and add new default material to sprite asset.
  176. /// </summary>
  177. /// <param name="spriteAsset"></param>
  178. private static void AddDefaultMaterial(TMP_SpriteAsset spriteAsset)
  179. {
  180. Shader shader = Shader.Find("TextMeshPro/Sprite");
  181. Material material = new Material(shader);
  182. material.SetTexture(ShaderUtilities.ID_MainTex, spriteAsset.spriteSheet);
  183. spriteAsset.material = material;
  184. material.hideFlags = HideFlags.HideInHierarchy;
  185. AssetDatabase.AddObjectToAsset(material, spriteAsset);
  186. }
  187. // Update existing SpriteInfo
  188. private static List<TMP_Sprite> UpdateSpriteInfo(TMP_SpriteAsset spriteAsset)
  189. {
  190. //Debug.Log("Updating Sprite Asset.");
  191. string filePath = AssetDatabase.GetAssetPath(spriteAsset.spriteSheet);
  192. // Get all the Sprites sorted Left to Right / Top to Bottom
  193. Sprite[] sprites = AssetDatabase.LoadAllAssetsAtPath(filePath).Select(x => x as Sprite).Where(x => x != null).OrderByDescending(x => x.rect.y).ThenBy(x => x.rect.x).ToArray();
  194. for (int i = 0; i < sprites.Length; i++)
  195. {
  196. Sprite sprite = sprites[i];
  197. // Check if the sprite is already contained in the SpriteInfoList
  198. int index = -1;
  199. if (spriteAsset.spriteInfoList.Count > i && spriteAsset.spriteInfoList[i].sprite != null)
  200. index = spriteAsset.spriteInfoList.FindIndex(item => item.sprite.GetInstanceID() == sprite.GetInstanceID());
  201. // Use existing SpriteInfo if it already exists
  202. TMP_Sprite spriteInfo = index == -1 ? new TMP_Sprite() : spriteAsset.spriteInfoList[index];
  203. Rect spriteRect = sprite.rect;
  204. spriteInfo.x = spriteRect.x;
  205. spriteInfo.y = spriteRect.y;
  206. spriteInfo.width = spriteRect.width;
  207. spriteInfo.height = spriteRect.height;
  208. // Get Sprite Pivot
  209. Vector2 pivot = new Vector2(0 - (sprite.bounds.min.x) / (sprite.bounds.extents.x * 2), 0 - (sprite.bounds.min.y) / (sprite.bounds.extents.y * 2));
  210. // The position of the pivot influences the Offset position.
  211. spriteInfo.pivot = new Vector2(0 - pivot.x * spriteRect.width, spriteRect.height - pivot.y * spriteRect.height);
  212. if (index == -1)
  213. {
  214. // Find the next available index for this Sprite
  215. int[] ids = spriteAsset.spriteInfoList.Select(item => item.id).ToArray();
  216. int id = 0;
  217. for (int j = 0; j < ids.Length; j++ )
  218. {
  219. if (ids[0] != 0) break;
  220. if (j > 0 && (ids[j] - ids[j - 1]) > 1)
  221. {
  222. id = ids[j - 1] + 1;
  223. break;
  224. }
  225. id = j + 1;
  226. }
  227. spriteInfo.sprite = sprite;
  228. spriteInfo.name = sprite.name;
  229. spriteInfo.hashCode = TMP_TextUtilities.GetSimpleHashCode(spriteInfo.name);
  230. spriteInfo.id = id;
  231. spriteInfo.xAdvance = spriteRect.width;
  232. spriteInfo.scale = 1.0f;
  233. spriteInfo.xOffset = spriteInfo.pivot.x;
  234. spriteInfo.yOffset = spriteInfo.pivot.y;
  235. spriteAsset.spriteInfoList.Add(spriteInfo);
  236. // Sort the Sprites by ID
  237. spriteAsset.spriteInfoList = spriteAsset.spriteInfoList.OrderBy(s => s.id).ToList();
  238. }
  239. else
  240. {
  241. spriteAsset.spriteInfoList[index] = spriteInfo;
  242. }
  243. }
  244. return spriteAsset.spriteInfoList;
  245. }
  246. }
  247. }