No Description
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.

ImportUtilities.cs 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Security.Cryptography;
  4. using System.Text;
  5. using Unity.Collections;
  6. using Unity.Collections.LowLevel.Unsafe;
  7. using UnityEditor.Animations;
  8. using UnityEngine;
  9. namespace UnityEditor.U2D.Aseprite
  10. {
  11. internal class UniqueNameGenerator
  12. {
  13. readonly Dictionary<int, HashSet<int>> m_NameHashes = new();
  14. public string GetUniqueName(string name, int parentIndex = -1, bool logNewNameGenerated = false, UnityEngine.Object context = null)
  15. {
  16. if (!m_NameHashes.ContainsKey(parentIndex))
  17. m_NameHashes.Add(parentIndex, new HashSet<int>());
  18. var nameHashes = m_NameHashes[parentIndex];
  19. return GetUniqueName(name, nameHashes, logNewNameGenerated, context);
  20. }
  21. static string GetUniqueName(string name, HashSet<int> stringHash, bool logNewNameGenerated = false, UnityEngine.Object context = null)
  22. {
  23. var sanitizedName = string.Copy(SanitizeName(name));
  24. string uniqueName = sanitizedName;
  25. int index = 1;
  26. while (true)
  27. {
  28. var hash = GetStringHash(uniqueName);
  29. if (!stringHash.Contains(hash))
  30. {
  31. stringHash.Add(hash);
  32. if (logNewNameGenerated && sanitizedName != uniqueName)
  33. Debug.Log($"Asset name {name} is changed to {uniqueName} to ensure uniqueness", context);
  34. return uniqueName;
  35. }
  36. uniqueName = $"{sanitizedName}_{index}";
  37. ++index;
  38. }
  39. }
  40. static string SanitizeName(string name)
  41. {
  42. name = name.Replace('\0', ' ');
  43. string newName = null;
  44. // We can't create asset name with these name.
  45. if ((name.Length == 2 && name[0] == '.' && name[1] == '.')
  46. || (name.Length == 1 && name[0] == '.')
  47. || (name.Length == 1 && name[0] == '/'))
  48. newName += name + "_";
  49. if (!string.IsNullOrEmpty(newName))
  50. {
  51. Debug.LogWarning($"File contains layer with invalid name for generating asset. {name} is renamed to {newName}");
  52. return newName;
  53. }
  54. return name;
  55. }
  56. static int GetStringHash(string str)
  57. {
  58. var md5Hasher = MD5.Create();
  59. var hashed = md5Hasher.ComputeHash(Encoding.UTF8.GetBytes(str));
  60. return BitConverter.ToInt32(hashed, 0);
  61. }
  62. }
  63. internal static class ImportUtilities
  64. {
  65. public static void SaveAllPalettesToDisk(AsepriteFile file)
  66. {
  67. for (var i = 0; i < file.frameData.Count; ++i)
  68. {
  69. var frame = file.frameData[i];
  70. for (var m = 0; m < frame.chunkCount; ++m)
  71. {
  72. var chunk = frame.chunks[m];
  73. if (chunk.chunkType == ChunkTypes.Palette)
  74. PaletteToDisk(chunk as PaletteChunk);
  75. }
  76. }
  77. }
  78. static void PaletteToDisk(PaletteChunk palette)
  79. {
  80. var noOfEntries = palette.noOfEntries;
  81. const int cellSize = 32;
  82. const int columns = 3;
  83. var rows = Mathf.CeilToInt(noOfEntries / (float)3);
  84. const int width = columns * cellSize;
  85. var height = rows * cellSize;
  86. var buffer = new Color32[width * height];
  87. for (var i = 0; i < buffer.Length; ++i)
  88. {
  89. var x = i % width;
  90. var y = i / width;
  91. var imgColumn = x / 32;
  92. var imgRow = y / 32;
  93. var paletteEntry = imgColumn + (imgRow * columns);
  94. if (paletteEntry < palette.noOfEntries)
  95. buffer[i] = palette.entries[paletteEntry].color;
  96. }
  97. SaveToPng(buffer, width, height);
  98. }
  99. public static string SaveToPng(NativeArray<Color32> buffer, int width, int height)
  100. {
  101. return SaveToPng(buffer.ToArray(), width, height);
  102. }
  103. static string SaveToPng(Color32[] buffer, int width, int height)
  104. {
  105. if (width == 0 || height == 0)
  106. return "No .png generated.";
  107. var texture2D = new Texture2D(width, height);
  108. texture2D.SetPixels32(buffer);
  109. var png = texture2D.EncodeToPNG();
  110. var path = Application.dataPath + $"/tex_{System.Guid.NewGuid().ToString()}.png";
  111. var fileStream = System.IO.File.Create(path);
  112. fileStream.Write(png);
  113. fileStream.Close();
  114. UnityEngine.Object.DestroyImmediate(texture2D);
  115. return path;
  116. }
  117. public static void ExportAnimationAssets(AsepriteImporter[] importers, bool exportClips, bool exportController)
  118. {
  119. var savePath = EditorUtility.SaveFolderPanel(
  120. "Export Animation Assets",
  121. Application.dataPath, "");
  122. ExportAnimationAssets(savePath, importers, exportClips, exportController);
  123. }
  124. public static void ExportAnimationAssets(string savePath, AsepriteImporter[] importers, bool exportClips, bool exportController)
  125. {
  126. if (string.IsNullOrEmpty(savePath))
  127. return;
  128. for (var i = 0; i < importers.Length; ++i)
  129. {
  130. var importedObjectPath = importers[i].assetPath;
  131. AnimationClip[] clips;
  132. if (exportClips)
  133. clips = ExportAnimationClips(importedObjectPath, savePath);
  134. else
  135. clips = GetAllClipsFromController(importedObjectPath);
  136. if (exportController)
  137. ExportAnimatorController(importers[i], clips, savePath);
  138. }
  139. }
  140. static AnimationClip[] ExportAnimationClips(string importedObjectPath, string path)
  141. {
  142. var relativePath = FileUtil.GetProjectRelativePath(path);
  143. var animationClips = GetAllClipsFromController(importedObjectPath);
  144. var clips = new List<AnimationClip>();
  145. for (var i = 0; i < animationClips.Length; ++i)
  146. {
  147. var clip = animationClips[i];
  148. var clipPath = $"{relativePath}/{clip.name}.anim";
  149. var result = AssetDatabase.ExtractAsset(clip, clipPath);
  150. if (!string.IsNullOrEmpty(result))
  151. Debug.LogWarning(result);
  152. var newClip = AssetDatabase.LoadAssetAtPath<AnimationClip>(clipPath);
  153. clips.Add(newClip);
  154. }
  155. return clips.ToArray();
  156. }
  157. static AnimationClip[] GetAllClipsFromController(string assetPath)
  158. {
  159. var controller = AssetDatabase.LoadAssetAtPath<AnimatorController>(assetPath);
  160. return controller.animationClips;
  161. }
  162. static void ExportAnimatorController(AsepriteImporter importer, AnimationClip[] clips, string path)
  163. {
  164. var relativePath = FileUtil.GetProjectRelativePath(path);
  165. var importedObjectPath = importer.assetPath;
  166. var fileName = System.IO.Path.GetFileNameWithoutExtension(importedObjectPath);
  167. var controllerPath = $"{relativePath}/{fileName}.controller";
  168. var controller = AnimatorController.CreateAnimatorControllerAtPath(controllerPath);
  169. for (var i = 0; i < clips.Length; ++i)
  170. controller.AddMotion(clips[i]);
  171. }
  172. public static Vector2 CalculateCellPivot(RectInt cellRect, uint spritePadding, Vector2Int canvasSize, SpriteAlignment alignment, Vector2 customPivot)
  173. {
  174. if (cellRect.width == 0 || cellRect.height == 0)
  175. return Vector2.zero;
  176. var scaleX = canvasSize.x / (float)cellRect.width;
  177. var scaleY = canvasSize.y / (float)cellRect.height;
  178. var halfSpritePadding = spritePadding / 2f;
  179. var pivot = new Vector2((cellRect.x - halfSpritePadding) / (float)canvasSize.x, (cellRect.y - halfSpritePadding) / (float)canvasSize.y);
  180. pivot *= -1f;
  181. Vector2 alignmentPos;
  182. if (alignment == SpriteAlignment.Custom)
  183. alignmentPos = customPivot;
  184. else
  185. alignmentPos = PivotAlignmentToVector(alignment);
  186. pivot.x += alignmentPos.x;
  187. pivot.y += alignmentPos.y;
  188. pivot.x *= scaleX;
  189. pivot.y *= scaleY;
  190. return pivot;
  191. }
  192. public static Vector2 PivotAlignmentToVector(SpriteAlignment alignment)
  193. {
  194. switch (alignment)
  195. {
  196. case SpriteAlignment.Center:
  197. return new Vector2(0.5f, 0.5f);
  198. case SpriteAlignment.TopLeft:
  199. return new Vector2(0f, 1f);
  200. case SpriteAlignment.TopCenter:
  201. return new Vector2(0.5f, 1f);
  202. case SpriteAlignment.TopRight:
  203. return new Vector2(1f, 1f);
  204. case SpriteAlignment.LeftCenter:
  205. return new Vector2(0f, 0.5f);
  206. case SpriteAlignment.RightCenter:
  207. return new Vector2(1f, 0.5f);
  208. case SpriteAlignment.BottomLeft:
  209. return new Vector2(0f, 0f);
  210. case SpriteAlignment.BottomCenter:
  211. return new Vector2(0.5f, 0f);
  212. case SpriteAlignment.BottomRight:
  213. return new Vector2(1f, 0f);
  214. default:
  215. return new Vector2(0f, 0f);
  216. }
  217. }
  218. public static string GetCellName(string baseName, int frameIndex, int noOfFrames)
  219. {
  220. if (noOfFrames == 1)
  221. return baseName;
  222. return $"{baseName}_Frame_{frameIndex}";
  223. }
  224. public static void DisposeIfCreated<T>(this NativeArray<T> arr) where T : struct
  225. {
  226. if (arr == default || !arr.IsCreated)
  227. return;
  228. var handle = NativeArrayUnsafeUtility.GetAtomicSafetyHandle(arr);
  229. if (!AtomicSafetyHandle.IsHandleValid(handle))
  230. return;
  231. arr.Dispose();
  232. }
  233. public static bool IsLayerVisible(int layerIndex, in List<Layer> layers)
  234. {
  235. var layer = layers[layerIndex];
  236. var isVisible = (layer.layerFlags & LayerFlags.Visible) != 0;
  237. if (!isVisible)
  238. return false;
  239. if (layer.parentIndex != -1)
  240. isVisible = IsLayerVisible(layer.parentIndex, in layers);
  241. return isVisible;
  242. }
  243. #if !UNITY_2023_1_OR_NEWER
  244. public static bool IsEqual(this RectInt rectA, RectInt rectB)
  245. {
  246. return rectA.x == rectB.x &&
  247. rectA.y == rectB.y &&
  248. rectA.width == rectB.width &&
  249. rectA.height == rectB.height;
  250. }
  251. #endif
  252. }
  253. }