Brak opisu
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.

TMP_SpriteAssetMenu.cs 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. using UnityEngine;
  2. using UnityEngine.TextCore;
  3. using UnityEngine.U2D;
  4. using UnityEditor;
  5. using System.Linq;
  6. using System.IO;
  7. using System.Collections;
  8. using System.Collections.Generic;
  9. namespace TMPro.EditorUtilities
  10. {
  11. public static class TMP_SpriteAssetMenu
  12. {
  13. // Add a Context Menu to the Sprite Asset Editor Panel to Create and Add a Default Material.
  14. [MenuItem("CONTEXT/TMP_SpriteAsset/Add Default Material", true, 2200)]
  15. static bool AddDefaultMaterialValidate(MenuCommand command)
  16. {
  17. return AssetDatabase.IsOpenForEdit(command.context);
  18. }
  19. [MenuItem("CONTEXT/TMP_SpriteAsset/Add Default Material", false, 2200)]
  20. static void AddDefaultMaterial(MenuCommand command)
  21. {
  22. TMP_SpriteAsset spriteAsset = (TMP_SpriteAsset)command.context;
  23. // Make sure the sprite asset already contains a default material
  24. if (spriteAsset != null && spriteAsset.material == null)
  25. {
  26. // Add new default material for sprite asset.
  27. AddDefaultMaterial(spriteAsset);
  28. }
  29. }
  30. // Add a Context Menu to the Sprite Asset Editor Panel to update existing sprite assets.
  31. [MenuItem("CONTEXT/TMP_SpriteAsset/Update Sprite Asset", true, 2100)]
  32. static bool UpdateSpriteAssetValidate(MenuCommand command)
  33. {
  34. return AssetDatabase.IsOpenForEdit(command.context);
  35. }
  36. [MenuItem("CONTEXT/TMP_SpriteAsset/Update Sprite Asset", false, 2100)]
  37. static void UpdateSpriteAsset(MenuCommand command)
  38. {
  39. TMP_SpriteAsset spriteAsset = (TMP_SpriteAsset)command.context;
  40. if (spriteAsset == null)
  41. return;
  42. UpdateSpriteAsset(spriteAsset);
  43. }
  44. internal static void UpdateSpriteAsset(TMP_SpriteAsset spriteAsset)
  45. {
  46. // Get a list of all the sprites contained in the texture referenced by the sprite asset.
  47. // This only works if the texture is set to sprite mode.
  48. string filePath = AssetDatabase.GetAssetPath(spriteAsset.spriteSheet);
  49. if (string.IsNullOrEmpty(filePath))
  50. return;
  51. // Get all the sprites defined in the sprite sheet texture referenced by this sprite asset.
  52. Sprite[] sprites = AssetDatabase.LoadAllAssetsAtPath(filePath).Select(x => x as Sprite).Where(x => x != null).ToArray();
  53. // Return if sprite sheet texture does not have any sprites defined in it.
  54. if (sprites.Length == 0)
  55. {
  56. Debug.Log("Sprite Asset <color=#FFFF80>[" + spriteAsset.name + "]</color>'s atlas texture does not appear to have any sprites defined in it. Use the Unity Sprite Editor to define sprites for this texture.", spriteAsset.spriteSheet);
  57. return;
  58. }
  59. List<TMP_SpriteGlyph> spriteGlyphTable = spriteAsset.spriteGlyphTable;
  60. // Find available glpyh indexes
  61. uint[] existingGlyphIndexes = spriteGlyphTable.Select(x => x.index).ToArray();
  62. List<uint> availableGlyphIndexes = new List<uint>();
  63. uint lastGlyphIndex = existingGlyphIndexes.Length > 0 ? existingGlyphIndexes.Last() : 0;
  64. int elementIndex = 0;
  65. for (uint i = 0; i < lastGlyphIndex; i++)
  66. {
  67. uint existingGlyphIndex = existingGlyphIndexes[elementIndex];
  68. if (i == existingGlyphIndex)
  69. elementIndex += 1;
  70. else
  71. availableGlyphIndexes.Add(i);
  72. }
  73. // Iterate over sprites contained in the updated sprite sheet to identify new and / or modified sprites.
  74. for (int i = 0; i < sprites.Length; i++)
  75. {
  76. Sprite sprite = sprites[i];
  77. // Check if current sprites is already contained in the sprite glyph table of the sprite asset.
  78. TMP_SpriteGlyph spriteGlyph = spriteGlyphTable.FirstOrDefault(x => x.sprite == sprite);
  79. if (spriteGlyph != null)
  80. {
  81. // update existing sprite glyph
  82. 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)
  83. spriteGlyph.glyphRect = new GlyphRect(sprite.rect);
  84. }
  85. else
  86. {
  87. TMP_SpriteCharacter spriteCharacter;
  88. // Check if this sprite potentially exists under the same name in the sprite character table.
  89. if (spriteAsset.spriteCharacterTable != null && spriteAsset.spriteCharacterTable.Count > 0)
  90. {
  91. spriteCharacter = spriteAsset.spriteCharacterTable.FirstOrDefault(x => x.name == sprite.name);
  92. spriteGlyph = spriteCharacter != null ? spriteGlyphTable[(int)spriteCharacter.glyphIndex] : null;
  93. if (spriteGlyph != null)
  94. {
  95. // Update sprite reference and data
  96. spriteGlyph.sprite = sprite;
  97. 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)
  98. spriteGlyph.glyphRect = new GlyphRect(sprite.rect);
  99. }
  100. }
  101. // Add new Sprite Glyph to the table
  102. spriteGlyph = new TMP_SpriteGlyph();
  103. // Get available glyph index
  104. if (availableGlyphIndexes.Count > 0)
  105. {
  106. spriteGlyph.index = availableGlyphIndexes[0];
  107. availableGlyphIndexes.RemoveAt(0);
  108. }
  109. else
  110. spriteGlyph.index = (uint)spriteGlyphTable.Count;
  111. spriteGlyph.metrics = new GlyphMetrics(sprite.rect.width, sprite.rect.height, -sprite.pivot.x, sprite.rect.height - sprite.pivot.y, sprite.rect.width);
  112. spriteGlyph.glyphRect = new GlyphRect(sprite.rect);
  113. spriteGlyph.scale = 1.0f;
  114. spriteGlyph.sprite = sprite;
  115. spriteGlyphTable.Add(spriteGlyph);
  116. spriteCharacter = new TMP_SpriteCharacter(0xFFFE, spriteGlyph);
  117. spriteCharacter.name = sprite.name;
  118. spriteCharacter.scale = 1.0f;
  119. spriteAsset.spriteCharacterTable.Add(spriteCharacter);
  120. }
  121. }
  122. // Update Sprite Character Table to replace unicode 0x0 by 0xFFFE
  123. for (int i = 0; i < spriteAsset.spriteCharacterTable.Count; i++)
  124. {
  125. TMP_SpriteCharacter spriteCharacter = spriteAsset.spriteCharacterTable[i];
  126. if (spriteCharacter.unicode == 0)
  127. spriteCharacter.unicode = 0xFFFE;
  128. }
  129. // Sort glyph table by glyph index
  130. spriteAsset.SortGlyphTable();
  131. spriteAsset.UpdateLookupTables();
  132. TMPro_EventManager.ON_SPRITE_ASSET_PROPERTY_CHANGED(true, spriteAsset);
  133. }
  134. [MenuItem("Assets/Create/TextMeshPro/Sprite Asset", false, 110)]
  135. public static void CreateSpriteAsset()
  136. {
  137. Object target = Selection.activeObject;
  138. if (target == null || target.GetType() != typeof(Texture2D)) // && target.GetType() != typeof(SpriteAtlas)))
  139. {
  140. Debug.LogWarning("A texture must first be selected in order to create a TextMesh Pro Sprite Asset.");
  141. return;
  142. }
  143. // Get the path to the selected asset.
  144. string filePathWithName = AssetDatabase.GetAssetPath(target);
  145. string fileNameWithExtension = Path.GetFileName(filePathWithName);
  146. string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(filePathWithName);
  147. string filePath = filePathWithName.Replace(fileNameWithExtension, "");
  148. // Create new Sprite Asset
  149. TMP_SpriteAsset spriteAsset = ScriptableObject.CreateInstance<TMP_SpriteAsset>();
  150. AssetDatabase.CreateAsset(spriteAsset, filePath + fileNameWithoutExtension + ".asset");
  151. spriteAsset.version = "1.1.0";
  152. // Compute the hash code for the sprite asset.
  153. spriteAsset.hashCode = TMP_TextUtilities.GetSimpleHashCode(spriteAsset.name);
  154. List<TMP_SpriteGlyph> spriteGlyphTable = new List<TMP_SpriteGlyph>();
  155. List<TMP_SpriteCharacter> spriteCharacterTable = new List<TMP_SpriteCharacter>();
  156. if (target.GetType() == typeof(Texture2D))
  157. {
  158. Texture2D sourceTex = target as Texture2D;
  159. // Assign new Sprite Sheet texture to the Sprite Asset.
  160. spriteAsset.spriteSheet = sourceTex;
  161. PopulateSpriteTables(sourceTex, ref spriteCharacterTable, ref spriteGlyphTable);
  162. spriteAsset.spriteCharacterTable = spriteCharacterTable;
  163. spriteAsset.spriteGlyphTable = spriteGlyphTable;
  164. // Add new default material for sprite asset.
  165. AddDefaultMaterial(spriteAsset);
  166. }
  167. else if (target.GetType() == typeof(SpriteAtlas))
  168. {
  169. //SpriteAtlas spriteAtlas = target as SpriteAtlas;
  170. //PopulateSpriteTables(spriteAtlas, ref spriteCharacterTable, ref spriteGlyphTable);
  171. //spriteAsset.spriteCharacterTable = spriteCharacterTable;
  172. //spriteAsset.spriteGlyphTable = spriteGlyphTable;
  173. //spriteAsset.spriteSheet = spriteGlyphTable[0].sprite.texture;
  174. //// Add new default material for sprite asset.
  175. //AddDefaultMaterial(spriteAsset);
  176. }
  177. // Update Lookup tables.
  178. spriteAsset.UpdateLookupTables();
  179. // Get the Sprites contained in the Sprite Sheet
  180. EditorUtility.SetDirty(spriteAsset);
  181. //spriteAsset.sprites = sprites;
  182. // Set source texture back to Not Readable.
  183. //texImporter.isReadable = false;
  184. AssetDatabase.SaveAssets();
  185. AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(spriteAsset)); // Re-import font asset to get the new updated version.
  186. //AssetDatabase.Refresh();
  187. }
  188. private static void PopulateSpriteTables(Texture source, ref List<TMP_SpriteCharacter> spriteCharacterTable, ref List<TMP_SpriteGlyph> spriteGlyphTable)
  189. {
  190. //Debug.Log("Creating new Sprite Asset.");
  191. string filePath = AssetDatabase.GetAssetPath(source);
  192. // Get all the Sprites sorted by Index
  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. TMP_SpriteGlyph spriteGlyph = new TMP_SpriteGlyph();
  198. spriteGlyph.index = (uint)i;
  199. spriteGlyph.metrics = new GlyphMetrics(sprite.rect.width, sprite.rect.height, -sprite.pivot.x, sprite.rect.height - sprite.pivot.y, sprite.rect.width);
  200. spriteGlyph.glyphRect = new GlyphRect(sprite.rect);
  201. spriteGlyph.scale = 1.0f;
  202. spriteGlyph.sprite = sprite;
  203. spriteGlyphTable.Add(spriteGlyph);
  204. TMP_SpriteCharacter spriteCharacter = new TMP_SpriteCharacter(0xFFFE, spriteGlyph);
  205. spriteCharacter.name = sprite.name;
  206. spriteCharacter.scale = 1.0f;
  207. spriteCharacterTable.Add(spriteCharacter);
  208. }
  209. }
  210. private static void PopulateSpriteTables(SpriteAtlas spriteAtlas, ref List<TMP_SpriteCharacter> spriteCharacterTable, ref List<TMP_SpriteGlyph> spriteGlyphTable)
  211. {
  212. // Get number of sprites contained in the sprite atlas.
  213. int spriteCount = spriteAtlas.spriteCount;
  214. Sprite[] sprites = new Sprite[spriteCount];
  215. // Get all the sprites
  216. spriteAtlas.GetSprites(sprites);
  217. for (int i = 0; i < sprites.Length; i++)
  218. {
  219. Sprite sprite = sprites[i];
  220. TMP_SpriteGlyph spriteGlyph = new TMP_SpriteGlyph();
  221. spriteGlyph.index = (uint)i;
  222. spriteGlyph.metrics = new GlyphMetrics(sprite.textureRect.width, sprite.textureRect.height, -sprite.pivot.x, sprite.textureRect.height - sprite.pivot.y, sprite.textureRect.width);
  223. spriteGlyph.glyphRect = new GlyphRect(sprite.textureRect);
  224. spriteGlyph.scale = 1.0f;
  225. spriteGlyph.sprite = sprite;
  226. spriteGlyphTable.Add(spriteGlyph);
  227. TMP_SpriteCharacter spriteCharacter = new TMP_SpriteCharacter(0xFFFE, spriteGlyph);
  228. spriteCharacter.name = sprite.name;
  229. spriteCharacter.scale = 1.0f;
  230. spriteCharacterTable.Add(spriteCharacter);
  231. }
  232. }
  233. /// <summary>
  234. /// Create and add new default material to sprite asset.
  235. /// </summary>
  236. /// <param name="spriteAsset"></param>
  237. private static void AddDefaultMaterial(TMP_SpriteAsset spriteAsset)
  238. {
  239. Shader shader = Shader.Find("TextMeshPro/Sprite");
  240. Material material = new Material(shader);
  241. material.SetTexture(ShaderUtilities.ID_MainTex, spriteAsset.spriteSheet);
  242. spriteAsset.material = material;
  243. material.hideFlags = HideFlags.HideInHierarchy;
  244. AssetDatabase.AddObjectToAsset(material, spriteAsset);
  245. }
  246. // Update existing SpriteInfo
  247. private static List<TMP_Sprite> UpdateSpriteInfo(TMP_SpriteAsset spriteAsset)
  248. {
  249. //Debug.Log("Updating Sprite Asset.");
  250. string filePath = AssetDatabase.GetAssetPath(spriteAsset.spriteSheet);
  251. // Get all the Sprites sorted Left to Right / Top to Bottom
  252. 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();
  253. for (int i = 0; i < sprites.Length; i++)
  254. {
  255. Sprite sprite = sprites[i];
  256. // Check if the sprite is already contained in the SpriteInfoList
  257. int index = -1;
  258. if (spriteAsset.spriteInfoList.Count > i && spriteAsset.spriteInfoList[i].sprite != null)
  259. index = spriteAsset.spriteInfoList.FindIndex(item => item.sprite.GetInstanceID() == sprite.GetInstanceID());
  260. // Use existing SpriteInfo if it already exists
  261. TMP_Sprite spriteInfo = index == -1 ? new TMP_Sprite() : spriteAsset.spriteInfoList[index];
  262. Rect spriteRect = sprite.rect;
  263. spriteInfo.x = spriteRect.x;
  264. spriteInfo.y = spriteRect.y;
  265. spriteInfo.width = spriteRect.width;
  266. spriteInfo.height = spriteRect.height;
  267. // Get Sprite Pivot
  268. Vector2 pivot = new Vector2(0 - (sprite.bounds.min.x) / (sprite.bounds.extents.x * 2), 0 - (sprite.bounds.min.y) / (sprite.bounds.extents.y * 2));
  269. // The position of the pivot influences the Offset position.
  270. spriteInfo.pivot = new Vector2(0 - pivot.x * spriteRect.width, spriteRect.height - pivot.y * spriteRect.height);
  271. if (index == -1)
  272. {
  273. // Find the next available index for this Sprite
  274. int[] ids = spriteAsset.spriteInfoList.Select(item => item.id).ToArray();
  275. int id = 0;
  276. for (int j = 0; j < ids.Length; j++ )
  277. {
  278. if (ids[0] != 0) break;
  279. if (j > 0 && (ids[j] - ids[j - 1]) > 1)
  280. {
  281. id = ids[j - 1] + 1;
  282. break;
  283. }
  284. id = j + 1;
  285. }
  286. spriteInfo.sprite = sprite;
  287. spriteInfo.name = sprite.name;
  288. spriteInfo.hashCode = TMP_TextUtilities.GetSimpleHashCode(spriteInfo.name);
  289. spriteInfo.id = id;
  290. spriteInfo.xAdvance = spriteRect.width;
  291. spriteInfo.scale = 1.0f;
  292. spriteInfo.xOffset = spriteInfo.pivot.x;
  293. spriteInfo.yOffset = spriteInfo.pivot.y;
  294. spriteAsset.spriteInfoList.Add(spriteInfo);
  295. // Sort the Sprites by ID
  296. spriteAsset.spriteInfoList = spriteAsset.spriteInfoList.OrderBy(s => s.id).ToList();
  297. }
  298. else
  299. {
  300. spriteAsset.spriteInfoList[index] = spriteInfo;
  301. }
  302. }
  303. return spriteAsset.spriteInfoList;
  304. }
  305. }
  306. }