설명 없음
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 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  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. // Special handling for .notdef sprite name.
  118. string fileNameToLowerInvariant = sprite.name.ToLowerInvariant();
  119. if (fileNameToLowerInvariant == ".notdef" || fileNameToLowerInvariant == "notdef")
  120. {
  121. spriteCharacter.name = fileNameToLowerInvariant;
  122. spriteCharacter.unicode = 0;
  123. }
  124. else
  125. {
  126. spriteCharacter.unicode = 0xFFFE;
  127. if (!string.IsNullOrEmpty(sprite.name) && sprite.name.Length > 2 && sprite.name[0] == '0' && (sprite.name[1] == 'x' || sprite.name[1] == 'X'))
  128. {
  129. spriteCharacter.unicode = (uint)TMP_TextUtilities.StringHexToInt(sprite.name.Remove(0, 2));
  130. }
  131. spriteCharacter.name = sprite.name;
  132. }
  133. spriteCharacter.scale = 1.0f;
  134. spriteAsset.spriteCharacterTable.Add(spriteCharacter);
  135. }
  136. }
  137. // Update Sprite Character Table to replace unicode 0x0 by 0xFFFE
  138. for (int i = 0; i < spriteAsset.spriteCharacterTable.Count; i++)
  139. {
  140. TMP_SpriteCharacter spriteCharacter = spriteAsset.spriteCharacterTable[i];
  141. if (spriteCharacter.unicode == 0)
  142. spriteCharacter.unicode = 0xFFFE;
  143. }
  144. // Sort glyph table by glyph index
  145. spriteAsset.SortGlyphTable();
  146. spriteAsset.UpdateLookupTables();
  147. TMPro_EventManager.ON_SPRITE_ASSET_PROPERTY_CHANGED(true, spriteAsset);
  148. EditorUtility.SetDirty(spriteAsset);
  149. }
  150. [MenuItem("Assets/Create/TextMeshPro/Sprite Asset", false, 200)]
  151. static void CreateSpriteAsset()
  152. {
  153. Object[] targets = Selection.objects;
  154. if (targets == null)
  155. {
  156. Debug.LogWarning("A Sprite Texture must first be selected in order to create a Sprite Asset.");
  157. return;
  158. }
  159. // Make sure TMP Essential Resources have been imported in the user project.
  160. if (TMP_Settings.instance == null)
  161. {
  162. Debug.Log("Unable to create sprite asset. Please import the TMP Essential Resources.");
  163. // Show Window to Import TMP Essential Resources
  164. return;
  165. }
  166. for (int i = 0; i < targets.Length; i++)
  167. {
  168. Object target = targets[i];
  169. // Make sure the selection is a font file
  170. if (target == null || target.GetType() != typeof(Texture2D))
  171. {
  172. Debug.LogWarning("Selected Object [" + target.name + "] is not a Sprite Texture. A Sprite Texture must be selected in order to create a Sprite Asset.", target);
  173. continue;
  174. }
  175. CreateSpriteAssetFromSelectedObject(target);
  176. }
  177. }
  178. static void CreateSpriteAssetFromSelectedObject(Object target)
  179. {
  180. // Get the path to the selected asset.
  181. string filePathWithName = AssetDatabase.GetAssetPath(target);
  182. string fileNameWithExtension = Path.GetFileName(filePathWithName);
  183. string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(filePathWithName);
  184. string filePath = filePathWithName.Replace(fileNameWithExtension, "");
  185. string uniquePath = AssetDatabase.GenerateUniqueAssetPath(filePath + fileNameWithoutExtension + ".asset");
  186. // Create new Sprite Asset
  187. TMP_SpriteAsset spriteAsset = ScriptableObject.CreateInstance<TMP_SpriteAsset>();
  188. AssetDatabase.CreateAsset(spriteAsset, uniquePath);
  189. spriteAsset.version = "1.1.0";
  190. // Compute the hash code for the sprite asset.
  191. spriteAsset.hashCode = TMP_TextUtilities.GetSimpleHashCode(spriteAsset.name);
  192. List<TMP_SpriteGlyph> spriteGlyphTable = new List<TMP_SpriteGlyph>();
  193. List<TMP_SpriteCharacter> spriteCharacterTable = new List<TMP_SpriteCharacter>();
  194. if (target.GetType() == typeof(Texture2D))
  195. {
  196. Texture2D sourceTex = target as Texture2D;
  197. // Assign new Sprite Sheet texture to the Sprite Asset.
  198. spriteAsset.spriteSheet = sourceTex;
  199. PopulateSpriteTables(sourceTex, ref spriteCharacterTable, ref spriteGlyphTable);
  200. spriteAsset.spriteCharacterTable = spriteCharacterTable;
  201. spriteAsset.spriteGlyphTable = spriteGlyphTable;
  202. // Add new default material for sprite asset.
  203. AddDefaultMaterial(spriteAsset);
  204. }
  205. else if (target.GetType() == typeof(SpriteAtlas))
  206. {
  207. //SpriteAtlas spriteAtlas = target as SpriteAtlas;
  208. //PopulateSpriteTables(spriteAtlas, ref spriteCharacterTable, ref spriteGlyphTable);
  209. //spriteAsset.spriteCharacterTable = spriteCharacterTable;
  210. //spriteAsset.spriteGlyphTable = spriteGlyphTable;
  211. //spriteAsset.spriteSheet = spriteGlyphTable[0].sprite.texture;
  212. //// Add new default material for sprite asset.
  213. //AddDefaultMaterial(spriteAsset);
  214. }
  215. // Update Lookup tables.
  216. spriteAsset.UpdateLookupTables();
  217. // Get the Sprites contained in the Sprite Sheet
  218. EditorUtility.SetDirty(spriteAsset);
  219. //spriteAsset.sprites = sprites;
  220. // Set source texture back to Not Readable.
  221. //texImporter.isReadable = false;
  222. AssetDatabase.SaveAssets();
  223. AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(spriteAsset)); // Re-import font asset to get the new updated version.
  224. //AssetDatabase.Refresh();
  225. }
  226. static void PopulateSpriteTables(Texture source, ref List<TMP_SpriteCharacter> spriteCharacterTable, ref List<TMP_SpriteGlyph> spriteGlyphTable)
  227. {
  228. //Debug.Log("Creating new Sprite Asset.");
  229. string filePath = AssetDatabase.GetAssetPath(source);
  230. // Get all the Sprites sorted by Index
  231. 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();
  232. for (int i = 0; i < sprites.Length; i++)
  233. {
  234. Sprite sprite = sprites[i];
  235. TMP_SpriteGlyph spriteGlyph = new TMP_SpriteGlyph();
  236. spriteGlyph.index = (uint)i;
  237. spriteGlyph.metrics = new GlyphMetrics(sprite.rect.width, sprite.rect.height, -sprite.pivot.x, sprite.rect.height - sprite.pivot.y, sprite.rect.width);
  238. spriteGlyph.glyphRect = new GlyphRect(sprite.rect);
  239. spriteGlyph.scale = 1.0f;
  240. spriteGlyph.sprite = sprite;
  241. spriteGlyphTable.Add(spriteGlyph);
  242. TMP_SpriteCharacter spriteCharacter = new TMP_SpriteCharacter(0xFFFE, spriteGlyph);
  243. // Special handling for .notdef sprite name.
  244. string fileNameToLowerInvariant = sprite.name.ToLowerInvariant();
  245. if (fileNameToLowerInvariant == ".notdef" || fileNameToLowerInvariant == "notdef")
  246. {
  247. spriteCharacter.unicode = 0;
  248. spriteCharacter.name = fileNameToLowerInvariant;
  249. }
  250. else
  251. {
  252. if (!string.IsNullOrEmpty(sprite.name) && sprite.name.Length > 2 && sprite.name[0] == '0' && (sprite.name[1] == 'x' || sprite.name[1] == 'X'))
  253. {
  254. spriteCharacter.unicode = (uint)TMP_TextUtilities.StringHexToInt(sprite.name.Remove(0, 2));
  255. }
  256. spriteCharacter.name = sprite.name;
  257. }
  258. spriteCharacter.scale = 1.0f;
  259. spriteCharacterTable.Add(spriteCharacter);
  260. }
  261. }
  262. static void PopulateSpriteTables(SpriteAtlas spriteAtlas, ref List<TMP_SpriteCharacter> spriteCharacterTable, ref List<TMP_SpriteGlyph> spriteGlyphTable)
  263. {
  264. // Get number of sprites contained in the sprite atlas.
  265. int spriteCount = spriteAtlas.spriteCount;
  266. Sprite[] sprites = new Sprite[spriteCount];
  267. // Get all the sprites
  268. spriteAtlas.GetSprites(sprites);
  269. for (int i = 0; i < sprites.Length; i++)
  270. {
  271. Sprite sprite = sprites[i];
  272. TMP_SpriteGlyph spriteGlyph = new TMP_SpriteGlyph();
  273. spriteGlyph.index = (uint)i;
  274. spriteGlyph.metrics = new GlyphMetrics(sprite.textureRect.width, sprite.textureRect.height, -sprite.pivot.x, sprite.textureRect.height - sprite.pivot.y, sprite.textureRect.width);
  275. spriteGlyph.glyphRect = new GlyphRect(sprite.textureRect);
  276. spriteGlyph.scale = 1.0f;
  277. spriteGlyph.sprite = sprite;
  278. spriteGlyphTable.Add(spriteGlyph);
  279. TMP_SpriteCharacter spriteCharacter = new TMP_SpriteCharacter(0xFFFE, spriteGlyph);
  280. spriteCharacter.name = sprite.name;
  281. spriteCharacter.scale = 1.0f;
  282. spriteCharacterTable.Add(spriteCharacter);
  283. }
  284. }
  285. /// <summary>
  286. /// Create and add new default material to sprite asset.
  287. /// </summary>
  288. /// <param name="spriteAsset"></param>
  289. static void AddDefaultMaterial(TMP_SpriteAsset spriteAsset)
  290. {
  291. Shader shader = Shader.Find("TextMeshPro/Sprite");
  292. Material material = new Material(shader);
  293. material.SetTexture(ShaderUtilities.ID_MainTex, spriteAsset.spriteSheet);
  294. spriteAsset.material = material;
  295. material.name = spriteAsset.name + " Material";
  296. AssetDatabase.AddObjectToAsset(material, spriteAsset);
  297. }
  298. // Update existing SpriteInfo
  299. static List<TMP_Sprite> UpdateSpriteInfo(TMP_SpriteAsset spriteAsset)
  300. {
  301. //Debug.Log("Updating Sprite Asset.");
  302. string filePath = AssetDatabase.GetAssetPath(spriteAsset.spriteSheet);
  303. // Get all the Sprites sorted Left to Right / Top to Bottom
  304. 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();
  305. for (int i = 0; i < sprites.Length; i++)
  306. {
  307. Sprite sprite = sprites[i];
  308. // Check if the sprite is already contained in the SpriteInfoList
  309. int index = -1;
  310. if (spriteAsset.spriteInfoList.Count > i && spriteAsset.spriteInfoList[i].sprite != null)
  311. index = spriteAsset.spriteInfoList.FindIndex(item => item.sprite.GetInstanceID() == sprite.GetInstanceID());
  312. // Use existing SpriteInfo if it already exists
  313. TMP_Sprite spriteInfo = index == -1 ? new TMP_Sprite() : spriteAsset.spriteInfoList[index];
  314. Rect spriteRect = sprite.rect;
  315. spriteInfo.x = spriteRect.x;
  316. spriteInfo.y = spriteRect.y;
  317. spriteInfo.width = spriteRect.width;
  318. spriteInfo.height = spriteRect.height;
  319. // Get Sprite Pivot
  320. Vector2 pivot = new Vector2(0 - (sprite.bounds.min.x) / (sprite.bounds.extents.x * 2), 0 - (sprite.bounds.min.y) / (sprite.bounds.extents.y * 2));
  321. // The position of the pivot influences the Offset position.
  322. spriteInfo.pivot = new Vector2(0 - pivot.x * spriteRect.width, spriteRect.height - pivot.y * spriteRect.height);
  323. if (index == -1)
  324. {
  325. // Find the next available index for this Sprite
  326. int[] ids = spriteAsset.spriteInfoList.Select(item => item.id).ToArray();
  327. int id = 0;
  328. for (int j = 0; j < ids.Length; j++ )
  329. {
  330. if (ids[0] != 0) break;
  331. if (j > 0 && (ids[j] - ids[j - 1]) > 1)
  332. {
  333. id = ids[j - 1] + 1;
  334. break;
  335. }
  336. id = j + 1;
  337. }
  338. spriteInfo.sprite = sprite;
  339. spriteInfo.name = sprite.name;
  340. spriteInfo.hashCode = TMP_TextUtilities.GetSimpleHashCode(spriteInfo.name);
  341. spriteInfo.id = id;
  342. spriteInfo.xAdvance = spriteRect.width;
  343. spriteInfo.scale = 1.0f;
  344. spriteInfo.xOffset = spriteInfo.pivot.x;
  345. spriteInfo.yOffset = spriteInfo.pivot.y;
  346. spriteAsset.spriteInfoList.Add(spriteInfo);
  347. // Sort the Sprites by ID
  348. spriteAsset.spriteInfoList = spriteAsset.spriteInfoList.OrderBy(s => s.id).ToList();
  349. }
  350. else
  351. {
  352. spriteAsset.spriteInfoList[index] = spriteInfo;
  353. }
  354. }
  355. return spriteAsset.spriteInfoList;
  356. }
  357. }
  358. }