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