ImageAtlasser.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730
  1. using UnityEngine;
  2. using UnityEditor;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Collections;
  6. using System.Collections.Generic;
  7. public class ImageAtlasser : EditorWindow
  8. {
  9. #region Variables & Properties
  10. // Editor Window Variables
  11. private static Vector2 pos = new Vector2(100, 100);
  12. private Rect windowRectangle = new Rect(210, 0, 0, 0);
  13. private Vector2 scrollPosition1 = Vector2.zero;
  14. private Vector2 scrollPosition2 = Vector2.zero;
  15. private bool atlasPreview = false;
  16. private string atlasWindowName = "Atlas";
  17. private GUIContent calcContent = new GUIContent("Atlas");
  18. // Atlas Settings Variables
  19. private bool autoArrange = true;
  20. private Vector2 atlasSize = Vector2.one;
  21. new private int maxSize = 256;
  22. private string[] maxSizeKeys =
  23. {
  24. "32",
  25. "64",
  26. "128",
  27. "256",
  28. "512",
  29. "1024",
  30. "2048",
  31. "4096"
  32. };
  33. private int[] maxSizeValues =
  34. {
  35. 32,
  36. 64,
  37. 128,
  38. 256,
  39. 512,
  40. 1024,
  41. 2048,
  42. 4096
  43. };
  44. private int packMethod = 0;
  45. private string[] packMethodKeys =
  46. {
  47. "---",
  48. "1",
  49. "2",
  50. "3 (Rotational)",
  51. "4 (Rotational)",
  52. "5 (Rotational)",
  53. "6 (Rotational)",
  54. "7 (Rotational)",
  55. };
  56. private int[] packMethodValues =
  57. {
  58. 0,
  59. 1,
  60. 2,
  61. 3,
  62. 4,
  63. 5,
  64. 6,
  65. 7
  66. };
  67. private int spacing = 1;
  68. // Import Variables
  69. private Texture2D atlasImage;
  70. private TextAsset importCodings;
  71. private Object importDirectory;
  72. private string importDirectoryStr = string.Empty;
  73. private bool subDirectories = true;
  74. private bool replaceImages = true;
  75. private bool readFromText = true;
  76. // Export Variables
  77. private string exportPath = string.Empty;
  78. // Atlas Drawer Variables
  79. private Texture2D[] systemImages;
  80. private Color atlasBackgroundCol = Color.white;
  81. private string imageNameFilter = string.Empty;
  82. private bool isMasked = false;
  83. // Atlas Variables
  84. private List<ImageAtlasBase> parts = new List<ImageAtlasBase>();
  85. #endregion
  86. [MenuItem("RO_Tool/Image Atlasser")]
  87. static void Init()
  88. {
  89. ImageAtlasser window = EditorWindow.GetWindow<ImageAtlasser>("Image Atlasser");
  90. window.position = new Rect(pos.x, pos.y, 1024, 600);
  91. window.Show();
  92. }
  93. void OnEnable()
  94. {
  95. systemImages = new Texture2D[]
  96. {
  97. AssetDatabase.LoadAssetAtPath("Assets/Image Atlasser/Editor/icons/bg.jpg", typeof(Texture2D)) as Texture2D,
  98. AssetDatabase.LoadAssetAtPath("Assets/Image Atlasser/Editor/icons/mask.jpg", typeof(Texture2D)) as Texture2D
  99. };
  100. if (Selection.objects.Length > 0)
  101. {
  102. importDirectory = Selection.objects[0];
  103. string assetPath = AssetDatabase.GetAssetPath(importDirectory);
  104. importDirectoryStr = Directory.Exists(assetPath) ? assetPath : assetPath.Remove(assetPath.LastIndexOf('/') + 1, assetPath.Length - assetPath.LastIndexOf('/') - 1);
  105. }
  106. }
  107. void OnDestroy()
  108. {
  109. parts.Dispose();
  110. pos = new Vector2(position.x, position.y);
  111. }
  112. void OnGUI()
  113. {
  114. GUILayout.BeginVertical(GUILayout.Width(200));
  115. GUI.skin.label.fontSize = 15;
  116. GUI.skin.label.fontStyle = FontStyle.Bold;
  117. GUIX.LabelCentered("Import Settings", 200);
  118. GUI.skin.label.fontStyle = FontStyle.Normal;
  119. GUI.skin.label.fontSize = 11;
  120. ImportSettings();
  121. GUILayout.Space(5);
  122. GUIX.Line();
  123. GUILayout.Space(5);
  124. GUI.skin.label.fontSize = 15;
  125. GUI.skin.label.fontStyle = FontStyle.Bold;
  126. GUIX.LabelCentered("Pack Settings", 200);
  127. GUI.skin.label.fontStyle = FontStyle.Normal;
  128. GUI.skin.label.fontSize = 11;
  129. PackSettings();
  130. GUILayout.Space(5);
  131. GUIX.Line();
  132. GUILayout.Space(5);
  133. GUI.skin.label.fontSize = 15;
  134. GUI.skin.label.fontStyle = FontStyle.Bold;
  135. GUIX.LabelCentered("Extract Buttons", 200);
  136. GUI.skin.label.fontStyle = FontStyle.Normal;
  137. GUI.skin.label.fontSize = 11;
  138. ExportButtons();
  139. GUILayout.EndVertical();
  140. windowRectangle.width = position.width - windowRectangle.x;
  141. windowRectangle.height = position.height;
  142. BeginWindows();
  143. if (atlasPreview)
  144. GUI.Window(0, windowRectangle, DrawAtlas, string.Empty);
  145. else
  146. GUI.Window(0, windowRectangle, DrawParts, string.Empty);
  147. EndWindows();
  148. Vector2 elementPosition = new Vector2(windowRectangle.x + 2, 0);
  149. atlasWindowName = "Atlas [" +
  150. atlasSize.x + "x" +
  151. atlasSize.y + "]";
  152. //atlasWindowName = "Atlas [" +
  153. // atlasSize.x + "x" +
  154. // atlasSize.y + "] - [" +
  155. // string.Format("{0:0.000}", (atlasSize.x + atlasSize.y) * 0.5f) +
  156. // "]";
  157. GUI.skin.label.fontStyle = FontStyle.Bold;
  158. if (atlasPreview)
  159. {
  160. calcContent.text = atlasWindowName;
  161. float strWidth = GUI.skin.label.CalcSize(calcContent).x;
  162. GUI.Label(new Rect(elementPosition.x, elementPosition.y, strWidth, 16), atlasWindowName);
  163. elementPosition.x += strWidth + 10;
  164. }
  165. else
  166. {
  167. GUI.Label(new Rect(elementPosition.x, elementPosition.y, 48, 16), "Parts");
  168. elementPosition.x += 48;
  169. }
  170. GUI.skin.label.fontStyle = FontStyle.Normal;
  171. GUI.Label(new Rect(elementPosition.x, elementPosition.y, 98, 16), "| [Atlas preview ");
  172. elementPosition.x += 98;
  173. atlasPreview = GUI.Toggle(new Rect(elementPosition.x, elementPosition.y, 23, 16), atlasPreview, "]");
  174. elementPosition.x += 25;
  175. GUI.Label(new Rect(elementPosition.x, elementPosition.y, 55, 16), "[Masked ");
  176. elementPosition.x += 55;
  177. isMasked = GUI.Toggle(new Rect(elementPosition.x, elementPosition.y, 23, 16), isMasked, "]");
  178. elementPosition.x += 25;
  179. GUI.Label(new Rect(elementPosition.x, elementPosition.y, 200, 16), "[Toggle Rotate All ");
  180. elementPosition.x += 110;
  181. if (GUI.Button(new Rect(elementPosition.x, elementPosition.y, 60, 16), "Change") && parts.Count > 0)
  182. {
  183. bool rotated = parts[0].attr.canBeRotated;
  184. for (int i = 0; i < parts.Count; i++)
  185. parts[i].attr.canBeRotated = !rotated;
  186. }
  187. elementPosition.x += 60;
  188. GUI.Label(new Rect(elementPosition.x, elementPosition.y, 10, 16), "]");
  189. elementPosition.x = position.width - 210;
  190. GUI.Label(new Rect(elementPosition.x, elementPosition.y, 115, 16), "Search Image Part");
  191. imageNameFilter = GUI.TextField(new Rect(elementPosition.x + 117, elementPosition.y, 90, 15), imageNameFilter);
  192. }
  193. #region Import Settings
  194. private void ImportSettings()
  195. {
  196. // [max width: 200]
  197. // label field: 200
  198. // texture field: 200
  199. // toggle: 182 + 16
  200. // button: 200
  201. GUILayout.Label("Import directory", GUILayout.Width(200));
  202. EditorGUI.indentLevel = 1;
  203. importDirectory = EditorGUILayout.ObjectField(importDirectory, typeof(Object), false, GUILayout.Width(200));
  204. EditorGUI.indentLevel = 0;
  205. GUILayout.BeginHorizontal();
  206. GUILayout.Label("Search sub-directories", GUILayout.Width(182));
  207. subDirectories = EditorGUILayout.Toggle(subDirectories, GUILayout.Width(16));
  208. GUILayout.EndHorizontal();
  209. GUILayout.BeginHorizontal();
  210. GUILayout.Label("Replace imported parts", GUILayout.Width(182));
  211. replaceImages = EditorGUILayout.Toggle(replaceImages, GUILayout.Width(16));
  212. GUILayout.EndHorizontal();
  213. if (GUILayout.Button("Import Parts", GUILayout.Width(200), GUILayout.Height(25)) && importDirectory)
  214. {
  215. string assetPath = AssetDatabase.GetAssetPath(importDirectory);
  216. importDirectoryStr = Directory.Exists(assetPath) ? assetPath : assetPath.Remove(assetPath.LastIndexOf('/') + 1, assetPath.Length - assetPath.LastIndexOf('/') - 1);
  217. if (ImportParts(importDirectoryStr))
  218. Pack();
  219. }
  220. GUILayout.Space(5);
  221. GUIX.LineDotted();
  222. GUILayout.Space(5);
  223. GUILayout.Label("Atlas Image", GUILayout.Width(200));
  224. EditorGUI.indentLevel = 1;
  225. atlasImage = EditorGUILayout.ObjectField((Texture2D)atlasImage, typeof(Texture2D), false, GUILayout.Width(200)) as Texture2D;
  226. EditorGUI.indentLevel = 0;
  227. GUILayout.Label("Atlas Codings", GUILayout.Width(200));
  228. EditorGUI.indentLevel = 1;
  229. importCodings = EditorGUILayout.ObjectField((TextAsset)importCodings, typeof(TextAsset), false, GUILayout.Width(200)) as TextAsset;
  230. EditorGUI.indentLevel = 0;
  231. GUILayout.BeginHorizontal();
  232. GUILayout.Label("Read from Text", GUILayout.Width(182));
  233. readFromText = EditorGUILayout.Toggle(readFromText, GUILayout.Width(16));
  234. GUILayout.EndHorizontal();
  235. if (GUILayout.Button("Import Atlas to Default", GUILayout.Width(200), GUILayout.Height(25)))
  236. ImportAtlas();
  237. }
  238. #endregion
  239. #region Pack Settings
  240. private void PackSettings()
  241. {
  242. // [max width: 200]
  243. // label field: 200
  244. // enam field: 200
  245. // toggle: 182 + 16
  246. // button: 200
  247. GUILayout.BeginHorizontal();
  248. GUILayout.Label("Auto Arrange", GUILayout.Width(182));
  249. autoArrange = EditorGUILayout.Toggle(autoArrange, GUILayout.Width(16));
  250. GUILayout.EndHorizontal();
  251. if (autoArrange)
  252. GUI.enabled = false;
  253. EditorGUI.indentLevel = 1;
  254. GUILayout.BeginHorizontal();
  255. EditorGUILayout.LabelField("Pack Method", GUILayout.Width(105));
  256. int prevPackMethod = packMethod;
  257. packMethod = EditorGUILayout.IntPopup(packMethod, packMethodKeys, packMethodValues, GUILayout.Width(95));
  258. GUILayout.EndHorizontal();
  259. GUILayout.BeginHorizontal();
  260. EditorGUILayout.LabelField("Size", GUILayout.Width(105));
  261. int prevMaxSize = maxSize;
  262. maxSize = EditorGUILayout.IntPopup(maxSize, maxSizeKeys, maxSizeValues, GUILayout.Width(95));
  263. GUILayout.EndHorizontal();
  264. EditorGUI.indentLevel = 0;
  265. GUI.enabled = true;
  266. if (prevPackMethod != packMethod && packMethod > 0 && parts.Count > 0)
  267. Pack();
  268. if (prevMaxSize != maxSize && parts.Count > 0)
  269. Pack();
  270. if (GUILayout.Button("Pack", GUILayout.Height(25)) && parts.Count > 0)
  271. Pack();
  272. }
  273. #endregion
  274. #region Export Buttons
  275. private void ExportButtons()
  276. {
  277. if (GUILayout.Button("Export Atlas", GUILayout.Width(200), GUILayout.Height(25)) && parts.Count > 0)
  278. {
  279. exportPath = EditorUtility.SaveFilePanel("Export Atlas to...", Application.dataPath + "/", "Atlas image", string.Empty);
  280. if (!string.IsNullOrEmpty(exportPath))
  281. ExportAtlas();
  282. }
  283. if (GUILayout.Button("Export Parts", GUILayout.Width(200), GUILayout.Height(25)) && parts.Count > 0)
  284. {
  285. string exportPathFolder = EditorUtility.SaveFolderPanel("Export Parts to...", Application.dataPath, "Atlas parts");
  286. if (!string.IsNullOrEmpty(exportPathFolder))
  287. {
  288. for (int i = 0; i < parts.Count; i++)
  289. File.WriteAllBytes(exportPathFolder + "/" + parts[i].attr.name + ".png", parts[i].image.EncodeToPNG());
  290. AssetDatabase.Refresh();
  291. Debug.Log("OK(!): Image parts have been successfully exported to " + exportPathFolder);
  292. }
  293. }
  294. }
  295. #endregion
  296. #region Draw Parts
  297. private void DrawParts(int winID)
  298. {
  299. int horizontalCount = (int)(windowRectangle.width / 105f);
  300. int activeElementCount = 0;
  301. for (int i = 0; i < parts.Count; i++)
  302. if (string.IsNullOrEmpty(imageNameFilter) || parts[i].attr.name.Contains(imageNameFilter))
  303. activeElementCount++;
  304. if (horizontalCount == 0)
  305. return;
  306. int windowGridHeight = activeElementCount / horizontalCount;
  307. if ((float)windowGridHeight >= (float)activeElementCount / (float)horizontalCount)
  308. windowGridHeight--;
  309. scrollPosition2 = GUI.BeginScrollView(
  310. new Rect(0, 18, windowRectangle.width - 9, windowRectangle.height - 22),
  311. scrollPosition2, new Rect(0, 0, 0, windowGridHeight * 125 + 125));
  312. GUI.skin.label.fontStyle = FontStyle.Bold;
  313. for (int i = 0, activeIndex = 1, xIndex = 0, yIndex = 0; i < parts.Count; i++)
  314. if (string.IsNullOrEmpty(imageNameFilter) || parts[i].attr.name.Contains(imageNameFilter))
  315. {
  316. if (horizontalCount == 0)
  317. horizontalCount = 1;
  318. xIndex = activeIndex % horizontalCount;
  319. if (xIndex == 0)
  320. xIndex = horizontalCount;
  321. xIndex--;
  322. yIndex = activeIndex / horizontalCount;
  323. if ((float)yIndex >= (float)activeIndex / (float)horizontalCount)
  324. yIndex--;
  325. GUILayout.BeginArea(new Rect(5 + 100 * xIndex, 125 * yIndex, 100, 120));
  326. GUILayout.BeginHorizontal();
  327. GUILayout.FlexibleSpace();
  328. GUILayout.Label(": " + parts[i].attr.name + " :");
  329. GUILayout.FlexibleSpace();
  330. GUILayout.EndHorizontal();
  331. GUILayout.BeginHorizontal();
  332. GUILayout.FlexibleSpace();
  333. Rect partRect = GUILayoutUtility.GetRect(95, 50);
  334. GUI.DrawTexture(partRect, parts[i].image);
  335. GUILayout.FlexibleSpace();
  336. GUILayout.EndHorizontal();
  337. GUILayout.BeginHorizontal();
  338. GUILayout.FlexibleSpace();
  339. GUILayout.BeginVertical();
  340. GUILayout.BeginHorizontal();
  341. EditorGUILayout.LabelField("Rotate?", GUILayout.Width(78), GUILayout.Height(14));
  342. parts[i].attr.canBeRotated = EditorGUILayout.Toggle(parts[i].attr.canBeRotated);
  343. GUILayout.EndHorizontal();
  344. GUI.color = Color.red;
  345. if (GUILayout.Button("Remove", GUILayout.Height(25)))
  346. {
  347. ImageAtlasBase image = parts[i];
  348. parts.RemoveAt(i);
  349. image.Dispose();
  350. }
  351. GUI.color = Color.white;
  352. GUILayout.EndVertical();
  353. GUILayout.FlexibleSpace();
  354. GUILayout.EndHorizontal();
  355. GUILayout.EndArea();
  356. activeIndex++;
  357. }
  358. GUI.skin.label.fontStyle = FontStyle.Normal;
  359. GUI.EndScrollView(true);
  360. }
  361. #endregion
  362. #region Draw Atlas
  363. public void DrawAtlas(int winID)
  364. {
  365. if (parts.Count == 0)
  366. return;
  367. scrollPosition1 = GUILayout.BeginScrollView(scrollPosition1, GUILayout.Width(windowRectangle.width - 12), GUILayout.Height(windowRectangle.height - 28));
  368. GUILayout.Box("", GUILayout.Width(atlasSize.x - 0.5f), GUILayout.Height(atlasSize.y - 0.5f));
  369. GUI.color = atlasBackgroundCol;
  370. GUI.DrawTexture(new Rect(5, 5, atlasSize.x - 0.5f, atlasSize.y - 0.5f),
  371. systemImages[0]);
  372. GUI.color = Color.white;
  373. for (int i = 0; i < parts.Count; i++)
  374. if (string.IsNullOrEmpty(imageNameFilter) || parts[i].attr.name.Contains(imageNameFilter))
  375. GUI.DrawTexture(
  376. new Rect(
  377. parts[i].attr.position.x + 5,
  378. parts[i].attr.position.y + 5,
  379. parts[i].attr.position.width,
  380. parts[i].attr.position.height),
  381. !isMasked ? parts[i].image : systemImages[1]);
  382. GUILayout.EndScrollView();
  383. }
  384. #endregion
  385. #region Import Export Pack Methods
  386. private bool ImportParts(string path)
  387. {
  388. DirectoryInfo importDirectory = new DirectoryInfo(path);
  389. List<FileInfo> fileInfos = GetImageFilesFromDirectory(importDirectory);
  390. if (replaceImages)
  391. parts.Dispose();
  392. for (int i = 0; i < fileInfos.Count; i++)
  393. {
  394. string fullName = fileInfos[i].FullName.Replace('\\', '/');
  395. string pathDirectory = fullName.Remove(0, fullName.IndexOf("Assets/"));
  396. Texture2D image = (Texture2D)AssetDatabase.LoadAssetAtPath(pathDirectory, typeof(Texture2D));
  397. if ((AssetImporter.GetAtPath(pathDirectory) as TextureImporter).isReadable)
  398. {
  399. ImageAttributes attr = new ImageAttributes(fileInfos[i].Name.Remove(fileInfos[i].Name.IndexOf('.')), new Rect(), false);
  400. parts.Add(new ImageAtlasBase(image, attr, false));
  401. }
  402. }
  403. if (parts.Count > 0)
  404. Debug.Log("OK(!): Imported [" + parts.Count + "] parts out of [" + fileInfos.Count + "]");
  405. else
  406. {
  407. Debug.LogWarning("Warning: No parts have been imported");
  408. if (fileInfos.Count > 0)
  409. Debug.Log("Tip: Images must have \"Read/Write Enabled\" flag in order to be imported!");
  410. }
  411. return parts.Count > 0;
  412. }
  413. private void ImportAtlas()
  414. {
  415. TextureImporter importer;
  416. if (atlasImage == null)
  417. {
  418. Debug.LogError("Error(!): Atlas image is not yet assigned.");
  419. return;
  420. }
  421. else
  422. {
  423. importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(atlasImage)) as TextureImporter;
  424. if (!importer.isReadable)
  425. {
  426. Debug.LogError("Error(!): Atlas image must have \"Read/Write Enabled\" flag in order to be imported!");
  427. return;
  428. }
  429. if (readFromText && importCodings == null)
  430. {
  431. Debug.LogError("Error(!): \"Read from text\" is enabled but no codings file is yet assigned.");
  432. return;
  433. }
  434. }
  435. if (replaceImages)
  436. parts.Dispose();
  437. parts.AddRange(ImageAtlas.GetParts(atlasImage, importCodings, importer, readFromText));
  438. maxSize = atlasImage.width > atlasImage.height ? atlasImage.width : atlasImage.height;
  439. atlasSize = new Vector2(atlasImage.width, atlasImage.height);
  440. if (parts.Count > 0)
  441. Debug.Log("OK(!): Image parts have been successfully imported!");
  442. else
  443. Debug.LogWarning("Warning(!): Something must have gone wrong. No image parts have been imported.");
  444. }
  445. int GetPotValue(float size)
  446. {
  447. int value = 1;
  448. while(value < size)
  449. {
  450. value *= 2;
  451. }
  452. return value;
  453. }
  454. private void ExportAtlas()
  455. {
  456. if (atlasSize == Vector2.zero || atlasSize == Vector2.one)
  457. {
  458. Debug.LogError("Error(!): Atlas creation failed.");
  459. return;
  460. }
  461. atlasSize.Set(GetPotValue(atlasSize.x), GetPotValue((int)atlasSize.y));
  462. Texture2D atlas = new Texture2D((int)(atlasSize.x), ((int)atlasSize.y), TextureFormat.RGBA32, false);
  463. string codings = string.Empty;
  464. Color clear = Color.clear;
  465. for (int y = 0; y < atlasSize.y; y++)
  466. for (int x = 0; x < atlasSize.x; x++)
  467. atlas.SetPixel(x, y, clear);
  468. atlas.Apply();
  469. for (int i = 0; i < parts.Count; i++)
  470. {
  471. Rect position = new Rect(
  472. parts[i].attr.position.x,
  473. parts[i].attr.position.y,
  474. parts[i].attr.position.width,
  475. parts[i].attr.position.height);
  476. atlas.SetPixels(
  477. (int)position.x,
  478. (int)(atlasSize.y - (position.y + parts[i].image.height)),
  479. (int)parts[i].image.width,
  480. (int)parts[i].image.height,
  481. parts[i].image.GetPixels()
  482. );
  483. codings += ImageAtlas.EncodeLine(parts[i].attr);
  484. if (i + 1 < parts.Count)
  485. codings += '\n';
  486. }
  487. atlas.Apply();
  488. if (codings[codings.Length - 1] == '\n')
  489. codings = codings.Remove(codings.Length - 1);
  490. if (exportPath.Contains(".png"))
  491. exportPath = exportPath.Replace(".png", "");
  492. File.WriteAllBytes(exportPath + ".png", atlas.EncodeToPNG());
  493. File.WriteAllText(exportPath + ".txt", codings);
  494. AssetDatabase.Refresh();
  495. AtlasInfo atlasInfo = new AtlasInfo();
  496. atlasInfo.texture = (Texture2D)AssetDatabase.LoadAssetAtPath(exportPath.Remove(0, exportPath.IndexOf("Assets/")) + ".png", typeof(Texture));
  497. atlasInfo.uvDetails = new AtlasInfo.UVDetail[parts.Count];
  498. SpriteMetaData[] spriteData = new SpriteMetaData[parts.Count];
  499. for (int i = 0; i < spriteData.Length; i++)
  500. {
  501. spriteData[i] = new SpriteMetaData();
  502. spriteData[i].name = parts[i].attr.name;
  503. spriteData[i].alignment = 0;
  504. spriteData[i].rect = new Rect(
  505. parts[i].attr.position.x,
  506. atlas.height - parts[i].attr.position.y - parts[i].attr.position.height,
  507. parts[i].attr.position.width,
  508. parts[i].attr.position.height
  509. );
  510. atlasInfo.uvDetails[i] = new AtlasInfo.UVDetail();
  511. atlasInfo.uvDetails[i].Name = parts[i].attr.name;
  512. atlasInfo.uvDetails[i].rotate = parts[i].attr.isRotated;
  513. if(atlasInfo.uvDetails[i].rotate)
  514. {
  515. atlasInfo.uvDetails[i].uvTL = new Vector2((parts[i].attr.position.x + parts[i].attr.position.width) / atlas.width, (atlas.height - parts[i].attr.position.y) / atlas.height);
  516. atlasInfo.uvDetails[i].uvTR = new Vector2((parts[i].attr.position.x + parts[i].attr.position.width) / atlas.width, (atlas.height - parts[i].attr.position.y - parts[i].attr.position.height) / atlas.height);
  517. atlasInfo.uvDetails[i].uvBL = new Vector2(parts[i].attr.position.x / atlas.width, (atlas.height - parts[i].attr.position.y) / atlas.height);
  518. atlasInfo.uvDetails[i].uvBR = new Vector2(parts[i].attr.position.x / atlas.width, (atlas.height - parts[i].attr.position.y - parts[i].attr.position.height) / atlas.height);
  519. atlasInfo.uvDetails[i].height = parts[i].image.width;
  520. atlasInfo.uvDetails[i].width = parts[i].image.height;
  521. }
  522. else
  523. {
  524. atlasInfo.uvDetails[i].uvTL = new Vector2(parts[i].attr.position.x / atlas.width, (atlas.height - parts[i].attr.position.y) / atlas.height);
  525. atlasInfo.uvDetails[i].uvTR = new Vector2((parts[i].attr.position.x + parts[i].attr.position .width)/ atlas.width, (atlas.height - parts[i].attr.position.y) / atlas.height);
  526. atlasInfo.uvDetails[i].uvBL = new Vector2(parts[i].attr.position.x / atlas.width, (atlas.height - parts[i].attr.position.y - parts[i].attr.position.height) / atlas.height);
  527. atlasInfo.uvDetails[i].uvBR = new Vector2((parts[i].attr.position.x + parts[i].attr.position.width) / atlas.width, (atlas.height - parts[i].attr.position.y - parts[i].attr.position.height) / atlas.height);
  528. atlasInfo.uvDetails[i].width = parts[i].image.width;
  529. atlasInfo.uvDetails[i].height = parts[i].image.height;
  530. }
  531. }
  532. string localAtlasPath = exportPath.Remove(0, exportPath.IndexOf("Assets/")) + ".png";
  533. TextureImporter importer = AssetImporter.GetAtPath(localAtlasPath) as TextureImporter;
  534. importer.textureType = TextureImporterType.Sprite;
  535. importer.npotScale = TextureImporterNPOTScale.None;
  536. importer.spriteImportMode = SpriteImportMode.Multiple;
  537. importer.spritePackingTag = string.Empty;
  538. importer.spritePixelsToUnits = 100;
  539. importer.spritesheet = spriteData;
  540. importer.filterMode = FilterMode.Bilinear;
  541. importer.maxTextureSize = maxSize;
  542. importer.textureFormat = TextureImporterFormat.AutomaticTruecolor;
  543. AssetDatabase.ImportAsset(localAtlasPath);
  544. DestroyImmediate(atlas);
  545. string atlasInfoPath = exportPath.Remove(0, exportPath.IndexOf("Assets/")) + ".asset";
  546. AssetDatabase.CreateAsset(atlasInfo, atlasInfoPath);
  547. AssetDatabase.Refresh();
  548. Debug.Log("OK(!): Atlas image have been successfully exported to " + localAtlasPath);
  549. }
  550. private List<FileInfo> GetImageFilesFromDirectory(DirectoryInfo importDirectory)
  551. {
  552. string[] files = Directory.GetFiles(importDirectory.FullName, "*.*", (subDirectories) ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly)
  553. .Where(s => s.ToLower().EndsWith(".png") || s.ToLower().EndsWith(".tga") || s.ToLower().EndsWith(".jpg") ||
  554. s.ToLower().EndsWith(".psd") || s.ToLower().EndsWith(".dds")).ToArray();
  555. List<FileInfo> fileList = new List<FileInfo>();
  556. foreach (string p in files)
  557. fileList.Add(new FileInfo(p));
  558. ImageAtlas.AlphanumComparatorFast comparison = new ImageAtlas.AlphanumComparatorFast();
  559. fileList.Sort(comparison);
  560. return new List<FileInfo>(fileList);
  561. }
  562. private void Pack()
  563. {
  564. if (autoArrange)
  565. ImageAtlas.PackParts(ref parts, out packMethod, out maxSize, spacing, out atlasSize);
  566. else
  567. ImageAtlas.PackParts(ref parts, packMethod, maxSize, spacing, out atlasSize);
  568. Debug.Log("Last Action: Pack");
  569. }
  570. #endregion
  571. #region GUILayout Overloads
  572. private static class GUIX
  573. {
  574. public static void LabelCentered(string label, int width)
  575. {
  576. EditorGUILayout.BeginHorizontal(GUILayout.Width(width));
  577. GUILayout.FlexibleSpace();
  578. GUILayout.Label(label);
  579. GUILayout.FlexibleSpace();
  580. EditorGUILayout.EndHorizontal();
  581. }
  582. public static void Line()
  583. {
  584. EditorGUILayout.BeginHorizontal();
  585. GUILayout.FlexibleSpace();
  586. GUILayout.Box("", GUILayout.Height(3), GUILayout.Width(100));
  587. GUILayout.FlexibleSpace();
  588. EditorGUILayout.EndHorizontal();
  589. }
  590. public static void LineDotted()
  591. {
  592. EditorGUILayout.BeginHorizontal();
  593. GUILayout.FlexibleSpace();
  594. GUILayout.Box("", GUILayout.Height(3), GUILayout.Width(15));
  595. GUILayout.Space(5);
  596. GUILayout.Box("", GUILayout.Height(3), GUILayout.Width(15));
  597. GUILayout.Space(5);
  598. GUILayout.Box("", GUILayout.Height(3), GUILayout.Width(15));
  599. GUILayout.Space(5);
  600. GUILayout.Box("", GUILayout.Height(3), GUILayout.Width(15));
  601. GUILayout.Space(5);
  602. GUILayout.Box("", GUILayout.Height(3), GUILayout.Width(15));
  603. GUILayout.Space(5);
  604. GUILayout.Box("", GUILayout.Height(3), GUILayout.Width(15));
  605. GUILayout.FlexibleSpace();
  606. EditorGUILayout.EndHorizontal();
  607. }
  608. }
  609. #endregion
  610. }