설명 없음
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_SpriteAsset.cs 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. using UnityEngine;
  2. using UnityEngine.TextCore;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using UnityEngine.Serialization;
  6. namespace TMPro
  7. {
  8. [HelpURL("https://docs.unity3d.com/Packages/com.unity.ugui@2.0/manual/TextMeshPro/Sprites.html")]
  9. [ExcludeFromPresetAttribute]
  10. public class TMP_SpriteAsset : TMP_Asset
  11. {
  12. internal Dictionary<int, int> m_NameLookup;
  13. internal Dictionary<uint, int> m_GlyphIndexLookup;
  14. // The texture which contains the sprites.
  15. public Texture spriteSheet;
  16. /// <summary>
  17. ///
  18. /// </summary>
  19. public List<TMP_SpriteCharacter> spriteCharacterTable
  20. {
  21. get
  22. {
  23. if (m_GlyphIndexLookup == null)
  24. UpdateLookupTables();
  25. return m_SpriteCharacterTable;
  26. }
  27. internal set { m_SpriteCharacterTable = value; }
  28. }
  29. [SerializeField]
  30. private List<TMP_SpriteCharacter> m_SpriteCharacterTable = new List<TMP_SpriteCharacter>();
  31. /// <summary>
  32. /// Dictionary used to lookup sprite characters by their unicode value.
  33. /// </summary>
  34. public Dictionary<uint, TMP_SpriteCharacter> spriteCharacterLookupTable
  35. {
  36. get
  37. {
  38. if (m_SpriteCharacterLookup == null)
  39. UpdateLookupTables();
  40. return m_SpriteCharacterLookup;
  41. }
  42. internal set { m_SpriteCharacterLookup = value; }
  43. }
  44. internal Dictionary<uint, TMP_SpriteCharacter> m_SpriteCharacterLookup;
  45. public List<TMP_SpriteGlyph> spriteGlyphTable
  46. {
  47. get { return m_GlyphTable; }
  48. internal set { m_GlyphTable = value; }
  49. }
  50. [FormerlySerializedAs("m_SpriteGlyphTable")]
  51. [SerializeField]
  52. private List<TMP_SpriteGlyph> m_GlyphTable = new List<TMP_SpriteGlyph>();
  53. internal Dictionary<uint, TMP_SpriteGlyph> m_SpriteGlyphLookup;
  54. // List which contains the SpriteInfo for the sprites contained in the sprite sheet.
  55. public List<TMP_Sprite> spriteInfoList;
  56. /// <summary>
  57. /// List which contains the Fallback font assets for this font.
  58. /// </summary>
  59. [SerializeField]
  60. public List<TMP_SpriteAsset> fallbackSpriteAssets;
  61. internal bool m_IsSpriteAssetLookupTablesDirty = false;
  62. void Awake()
  63. {
  64. // Check version number of sprite asset to see if it needs to be upgraded.
  65. if (this.material != null && string.IsNullOrEmpty(m_Version))
  66. UpgradeSpriteAsset();
  67. }
  68. /// <summary>
  69. /// Create a material for the sprite asset.
  70. /// </summary>
  71. /// <returns></returns>
  72. Material GetDefaultSpriteMaterial()
  73. {
  74. ShaderUtilities.GetShaderPropertyIDs();
  75. // Add a new material
  76. Shader shader = Shader.Find("TextMeshPro/Sprite");
  77. Material tempMaterial = new Material(shader);
  78. tempMaterial.SetTexture(ShaderUtilities.ID_MainTex, spriteSheet);
  79. #if UNITY_EDITOR
  80. UnityEditor.AssetDatabase.AddObjectToAsset(tempMaterial, this);
  81. UnityEditor.AssetDatabase.ImportAsset(UnityEditor.AssetDatabase.GetAssetPath(this));
  82. #endif
  83. return tempMaterial;
  84. }
  85. /// <summary>
  86. /// Function to update the sprite name and unicode lookup tables.
  87. /// This function should be called when a sprite's name or unicode value changes or when a new sprite is added.
  88. /// </summary>
  89. public void UpdateLookupTables()
  90. {
  91. //Debug.Log("Updating [" + this.name + "] Lookup tables.");
  92. // Check version number of sprite asset to see if it needs to be upgraded.
  93. if (this.material != null && string.IsNullOrEmpty(m_Version))
  94. UpgradeSpriteAsset();
  95. // Initialize / Clear glyph index lookup dictionary.
  96. if (m_GlyphIndexLookup == null)
  97. m_GlyphIndexLookup = new Dictionary<uint, int>();
  98. else
  99. m_GlyphIndexLookup.Clear();
  100. //
  101. if (m_SpriteGlyphLookup == null)
  102. m_SpriteGlyphLookup = new Dictionary<uint, TMP_SpriteGlyph>();
  103. else
  104. m_SpriteGlyphLookup.Clear();
  105. // Initialize SpriteGlyphLookup
  106. for (int i = 0; i < m_GlyphTable.Count; i++)
  107. {
  108. TMP_SpriteGlyph spriteGlyph = m_GlyphTable[i];
  109. uint glyphIndex = spriteGlyph.index;
  110. if (m_GlyphIndexLookup.ContainsKey(glyphIndex) == false)
  111. m_GlyphIndexLookup.Add(glyphIndex, i);
  112. if (m_SpriteGlyphLookup.ContainsKey(glyphIndex) == false)
  113. m_SpriteGlyphLookup.Add(glyphIndex, spriteGlyph);
  114. }
  115. // Initialize name lookup
  116. if (m_NameLookup == null)
  117. m_NameLookup = new Dictionary<int, int>();
  118. else
  119. m_NameLookup.Clear();
  120. // Initialize character lookup
  121. if (m_SpriteCharacterLookup == null)
  122. m_SpriteCharacterLookup = new Dictionary<uint, TMP_SpriteCharacter>();
  123. else
  124. m_SpriteCharacterLookup.Clear();
  125. // Populate Sprite Character lookup tables
  126. for (int i = 0; i < m_SpriteCharacterTable.Count; i++)
  127. {
  128. TMP_SpriteCharacter spriteCharacter = m_SpriteCharacterTable[i];
  129. // Make sure sprite character is valid
  130. if (spriteCharacter == null)
  131. continue;
  132. uint glyphIndex = spriteCharacter.glyphIndex;
  133. // Lookup the glyph for this character
  134. if (m_SpriteGlyphLookup.ContainsKey(glyphIndex) == false)
  135. continue;
  136. // Assign glyph and text asset to this character
  137. spriteCharacter.glyph = m_SpriteGlyphLookup[glyphIndex];
  138. spriteCharacter.textAsset = this;
  139. int nameHashCode = TMP_TextUtilities.GetHashCode(m_SpriteCharacterTable[i].name);
  140. if (m_NameLookup.ContainsKey(nameHashCode) == false)
  141. m_NameLookup.Add(nameHashCode, i);
  142. uint unicode = m_SpriteCharacterTable[i].unicode;
  143. if (unicode != 0xFFFE && m_SpriteCharacterLookup.ContainsKey(unicode) == false)
  144. m_SpriteCharacterLookup.Add(unicode, spriteCharacter);
  145. }
  146. m_IsSpriteAssetLookupTablesDirty = false;
  147. }
  148. /// <summary>
  149. /// Function which returns the sprite index using the hashcode of the name
  150. /// </summary>
  151. /// <param name="hashCode"></param>
  152. /// <returns></returns>
  153. public int GetSpriteIndexFromHashcode(int hashCode)
  154. {
  155. if (m_NameLookup == null)
  156. UpdateLookupTables();
  157. int index;
  158. if (m_NameLookup.TryGetValue(hashCode, out index))
  159. return index;
  160. return -1;
  161. }
  162. /// <summary>
  163. /// Returns the index of the sprite for the given unicode value.
  164. /// </summary>
  165. /// <param name="unicode"></param>
  166. /// <returns></returns>
  167. public int GetSpriteIndexFromUnicode (uint unicode)
  168. {
  169. if (m_SpriteCharacterLookup == null)
  170. UpdateLookupTables();
  171. TMP_SpriteCharacter spriteCharacter;
  172. if (m_SpriteCharacterLookup.TryGetValue(unicode, out spriteCharacter))
  173. return (int)spriteCharacter.glyphIndex;
  174. return -1;
  175. }
  176. /// <summary>
  177. /// Returns the index of the sprite for the given name.
  178. /// </summary>
  179. /// <param name="name"></param>
  180. /// <returns></returns>
  181. public int GetSpriteIndexFromName (string name)
  182. {
  183. if (m_NameLookup == null)
  184. UpdateLookupTables();
  185. int hashCode = TMP_TextUtilities.GetSimpleHashCode(name);
  186. return GetSpriteIndexFromHashcode(hashCode);
  187. }
  188. /// <summary>
  189. /// Used to keep track of which Sprite Assets have been searched.
  190. /// </summary>
  191. private static HashSet<int> k_searchedSpriteAssets;
  192. /// <summary>
  193. /// Search through the given sprite asset and its fallbacks for the specified sprite matching the given unicode character.
  194. /// </summary>
  195. /// <param name="spriteAsset">The font asset to search for the given character.</param>
  196. /// <param name="unicode">The character to find.</param>
  197. /// <param name="glyph">out parameter containing the glyph for the specified character (if found).</param>
  198. /// <returns></returns>
  199. public static TMP_SpriteAsset SearchForSpriteByUnicode(TMP_SpriteAsset spriteAsset, uint unicode, bool includeFallbacks, out int spriteIndex)
  200. {
  201. // Check to make sure sprite asset is not null
  202. if (spriteAsset == null) { spriteIndex = -1; return null; }
  203. // Get sprite index for the given unicode
  204. spriteIndex = spriteAsset.GetSpriteIndexFromUnicode(unicode);
  205. if (spriteIndex != -1)
  206. return spriteAsset;
  207. // Initialize list to track instance of Sprite Assets that have already been searched.
  208. if (k_searchedSpriteAssets == null)
  209. k_searchedSpriteAssets = new HashSet<int>();
  210. else
  211. k_searchedSpriteAssets.Clear();
  212. // Get instance ID of sprite asset and add to list.
  213. int id = spriteAsset.GetInstanceID();
  214. k_searchedSpriteAssets.Add(id);
  215. // Search potential fallback sprite assets if includeFallbacks is true.
  216. if (includeFallbacks && spriteAsset.fallbackSpriteAssets != null && spriteAsset.fallbackSpriteAssets.Count > 0)
  217. return SearchForSpriteByUnicodeInternal(spriteAsset.fallbackSpriteAssets, unicode, true, out spriteIndex);
  218. // Search default sprite asset potentially assigned in the TMP Settings.
  219. if (includeFallbacks && TMP_Settings.defaultSpriteAsset != null)
  220. return SearchForSpriteByUnicodeInternal(TMP_Settings.defaultSpriteAsset, unicode, true, out spriteIndex);
  221. spriteIndex = -1;
  222. return null;
  223. }
  224. /// <summary>
  225. /// Search through the given list of sprite assets and fallbacks for a sprite whose unicode value matches the target unicode.
  226. /// </summary>
  227. /// <param name="spriteAssets"></param>
  228. /// <param name="unicode"></param>
  229. /// <param name="includeFallbacks"></param>
  230. /// <param name="spriteIndex"></param>
  231. /// <returns></returns>
  232. private static TMP_SpriteAsset SearchForSpriteByUnicodeInternal(List<TMP_SpriteAsset> spriteAssets, uint unicode, bool includeFallbacks, out int spriteIndex)
  233. {
  234. for (int i = 0; i < spriteAssets.Count; i++)
  235. {
  236. TMP_SpriteAsset temp = spriteAssets[i];
  237. if (temp == null) continue;
  238. int id = temp.GetInstanceID();
  239. // Skip sprite asset if it has already been searched.
  240. if (k_searchedSpriteAssets.Add(id) == false)
  241. continue;
  242. temp = SearchForSpriteByUnicodeInternal(temp, unicode, includeFallbacks, out spriteIndex);
  243. if (temp != null)
  244. return temp;
  245. }
  246. spriteIndex = -1;
  247. return null;
  248. }
  249. /// <summary>
  250. /// Search the given sprite asset and fallbacks for a sprite whose unicode value matches the target unicode.
  251. /// </summary>
  252. /// <param name="spriteAsset"></param>
  253. /// <param name="unicode"></param>
  254. /// <param name="includeFallbacks"></param>
  255. /// <param name="spriteIndex"></param>
  256. /// <returns></returns>
  257. private static TMP_SpriteAsset SearchForSpriteByUnicodeInternal(TMP_SpriteAsset spriteAsset, uint unicode, bool includeFallbacks, out int spriteIndex)
  258. {
  259. // Get sprite index for the given unicode
  260. spriteIndex = spriteAsset.GetSpriteIndexFromUnicode(unicode);
  261. if (spriteIndex != -1)
  262. return spriteAsset;
  263. if (includeFallbacks && spriteAsset.fallbackSpriteAssets != null && spriteAsset.fallbackSpriteAssets.Count > 0)
  264. return SearchForSpriteByUnicodeInternal(spriteAsset.fallbackSpriteAssets, unicode, true, out spriteIndex);
  265. spriteIndex = -1;
  266. return null;
  267. }
  268. /// <summary>
  269. /// Search the given sprite asset and fallbacks for a sprite whose hash code value of its name matches the target hash code.
  270. /// </summary>
  271. /// <param name="spriteAsset">The Sprite Asset to search for the given sprite whose name matches the hashcode value</param>
  272. /// <param name="hashCode">The hash code value matching the name of the sprite</param>
  273. /// <param name="includeFallbacks">Include fallback sprite assets in the search</param>
  274. /// <param name="spriteIndex">The index of the sprite matching the provided hash code</param>
  275. /// <returns>The Sprite Asset that contains the sprite</returns>
  276. public static TMP_SpriteAsset SearchForSpriteByHashCode(TMP_SpriteAsset spriteAsset, int hashCode, bool includeFallbacks, out int spriteIndex)
  277. {
  278. // Make sure sprite asset is not null
  279. if (spriteAsset == null) { spriteIndex = -1; return null; }
  280. spriteIndex = spriteAsset.GetSpriteIndexFromHashcode(hashCode);
  281. if (spriteIndex != -1)
  282. return spriteAsset;
  283. // Initialize or clear list to Sprite Assets that have already been searched.
  284. if (k_searchedSpriteAssets == null)
  285. k_searchedSpriteAssets = new HashSet<int>();
  286. else
  287. k_searchedSpriteAssets.Clear();
  288. int id = spriteAsset.instanceID;
  289. // Add to list of font assets already searched.
  290. k_searchedSpriteAssets.Add(id);
  291. TMP_SpriteAsset tempSpriteAsset;
  292. // Search potential fallbacks assigned to local sprite asset.
  293. if (includeFallbacks && spriteAsset.fallbackSpriteAssets != null && spriteAsset.fallbackSpriteAssets.Count > 0)
  294. {
  295. tempSpriteAsset = SearchForSpriteByHashCodeInternal(spriteAsset.fallbackSpriteAssets, hashCode, true, out spriteIndex);
  296. if (spriteIndex != -1)
  297. return tempSpriteAsset;
  298. }
  299. // Search default sprite asset potentially assigned in the TMP Settings.
  300. if (includeFallbacks && TMP_Settings.defaultSpriteAsset != null)
  301. {
  302. tempSpriteAsset = SearchForSpriteByHashCodeInternal(TMP_Settings.defaultSpriteAsset, hashCode, true, out spriteIndex);
  303. if (spriteIndex != -1)
  304. return tempSpriteAsset;
  305. }
  306. // Clear search list since we are now looking for the missing sprite character.
  307. k_searchedSpriteAssets.Clear();
  308. uint missingSpriteCharacterUnicode = TMP_Settings.missingCharacterSpriteUnicode;
  309. // Get sprite index for the given unicode
  310. spriteIndex = spriteAsset.GetSpriteIndexFromUnicode(missingSpriteCharacterUnicode);
  311. if (spriteIndex != -1)
  312. return spriteAsset;
  313. // Add current sprite asset to list of assets already searched.
  314. k_searchedSpriteAssets.Add(id);
  315. // Search for the missing sprite character in the local sprite asset and potential fallbacks.
  316. if (includeFallbacks && spriteAsset.fallbackSpriteAssets != null && spriteAsset.fallbackSpriteAssets.Count > 0)
  317. {
  318. tempSpriteAsset = SearchForSpriteByUnicodeInternal(spriteAsset.fallbackSpriteAssets, missingSpriteCharacterUnicode, true, out spriteIndex);
  319. if (spriteIndex != -1)
  320. return tempSpriteAsset;
  321. }
  322. // Search for the missing sprite character in the default sprite asset and potential fallbacks.
  323. if (includeFallbacks && TMP_Settings.defaultSpriteAsset != null)
  324. {
  325. tempSpriteAsset = SearchForSpriteByUnicodeInternal(TMP_Settings.defaultSpriteAsset, missingSpriteCharacterUnicode, true, out spriteIndex);
  326. if (spriteIndex != -1)
  327. return tempSpriteAsset;
  328. }
  329. spriteIndex = -1;
  330. return null;
  331. }
  332. /// <summary>
  333. /// Search through the given list of sprite assets and fallbacks for a sprite whose hash code value of its name matches the target hash code.
  334. /// </summary>
  335. /// <param name="spriteAssets"></param>
  336. /// <param name="hashCode"></param>
  337. /// <param name="searchFallbacks"></param>
  338. /// <param name="spriteIndex"></param>
  339. /// <returns></returns>
  340. private static TMP_SpriteAsset SearchForSpriteByHashCodeInternal(List<TMP_SpriteAsset> spriteAssets, int hashCode, bool searchFallbacks, out int spriteIndex)
  341. {
  342. // Search through the list of sprite assets
  343. for (int i = 0; i < spriteAssets.Count; i++)
  344. {
  345. TMP_SpriteAsset temp = spriteAssets[i];
  346. if (temp == null) continue;
  347. int id = temp.instanceID;
  348. // Skip sprite asset if it has already been searched.
  349. if (k_searchedSpriteAssets.Add(id) == false)
  350. continue;
  351. temp = SearchForSpriteByHashCodeInternal(temp, hashCode, searchFallbacks, out spriteIndex);
  352. if (temp != null)
  353. return temp;
  354. }
  355. spriteIndex = -1;
  356. return null;
  357. }
  358. /// <summary>
  359. /// Search through the given sprite asset and fallbacks for a sprite whose hash code value of its name matches the target hash code.
  360. /// </summary>
  361. /// <param name="spriteAsset"></param>
  362. /// <param name="hashCode"></param>
  363. /// <param name="searchFallbacks"></param>
  364. /// <param name="spriteIndex"></param>
  365. /// <returns></returns>
  366. private static TMP_SpriteAsset SearchForSpriteByHashCodeInternal(TMP_SpriteAsset spriteAsset, int hashCode, bool searchFallbacks, out int spriteIndex)
  367. {
  368. // Get the sprite for the given hash code.
  369. spriteIndex = spriteAsset.GetSpriteIndexFromHashcode(hashCode);
  370. if (spriteIndex != -1)
  371. return spriteAsset;
  372. if (searchFallbacks && spriteAsset.fallbackSpriteAssets != null && spriteAsset.fallbackSpriteAssets.Count > 0)
  373. return SearchForSpriteByHashCodeInternal(spriteAsset.fallbackSpriteAssets, hashCode, true, out spriteIndex);
  374. spriteIndex = -1;
  375. return null;
  376. }
  377. /// <summary>
  378. /// Sort the sprite glyph table by glyph index.
  379. /// </summary>
  380. public void SortGlyphTable()
  381. {
  382. if (m_GlyphTable == null || m_GlyphTable.Count == 0) return;
  383. m_GlyphTable = m_GlyphTable.OrderBy(item => item.index).ToList();
  384. }
  385. /// <summary>
  386. /// Sort the sprite character table by Unicode values.
  387. /// </summary>
  388. internal void SortCharacterTable()
  389. {
  390. if (m_SpriteCharacterTable != null && m_SpriteCharacterTable.Count > 0)
  391. m_SpriteCharacterTable = m_SpriteCharacterTable.OrderBy(c => c.unicode).ToList();
  392. }
  393. /// <summary>
  394. /// Sort both sprite glyph and character tables.
  395. /// </summary>
  396. internal void SortGlyphAndCharacterTables()
  397. {
  398. SortGlyphTable();
  399. SortCharacterTable();
  400. }
  401. /// <summary>
  402. /// Internal method used to upgrade sprite asset.
  403. /// </summary>
  404. private void UpgradeSpriteAsset()
  405. {
  406. m_Version = "1.1.0";
  407. Debug.Log("Upgrading sprite asset [" + this.name + "] to version " + m_Version + ".", this);
  408. // Convert legacy glyph and character tables to new format
  409. m_SpriteCharacterTable.Clear();
  410. m_GlyphTable.Clear();
  411. for (int i = 0; i < spriteInfoList.Count; i++)
  412. {
  413. TMP_Sprite oldSprite = spriteInfoList[i];
  414. TMP_SpriteGlyph spriteGlyph = new TMP_SpriteGlyph();
  415. spriteGlyph.index = (uint)i;
  416. spriteGlyph.sprite = oldSprite.sprite;
  417. spriteGlyph.metrics = new GlyphMetrics(oldSprite.width, oldSprite.height, oldSprite.xOffset, oldSprite.yOffset, oldSprite.xAdvance);
  418. spriteGlyph.glyphRect = new GlyphRect((int)oldSprite.x, (int)oldSprite.y, (int)oldSprite.width, (int)oldSprite.height);
  419. spriteGlyph.scale = 1.0f;
  420. spriteGlyph.atlasIndex = 0;
  421. m_GlyphTable.Add(spriteGlyph);
  422. TMP_SpriteCharacter spriteCharacter = new TMP_SpriteCharacter();
  423. spriteCharacter.glyph = spriteGlyph;
  424. spriteCharacter.unicode = oldSprite.unicode == 0x0 ? 0xFFFE : (uint)oldSprite.unicode;
  425. spriteCharacter.name = oldSprite.name;
  426. spriteCharacter.scale = oldSprite.scale;
  427. m_SpriteCharacterTable.Add(spriteCharacter);
  428. }
  429. // Clear legacy glyph info list.
  430. //spriteInfoList.Clear();
  431. UpdateLookupTables();
  432. #if UNITY_EDITOR
  433. UnityEditor.EditorUtility.SetDirty(this);
  434. UnityEditor.AssetDatabase.SaveAssets();
  435. #endif
  436. }
  437. }
  438. }