Fix wrong file setting, generate multi sprites
This commit is contained in:
@@ -19,12 +19,6 @@ public class DDSAtlasSlicerWindow : EditorWindow
|
||||
[SerializeField] private float _pixelsPerUnit = 100f;
|
||||
[SerializeField] private bool _exportAsIndividualPngs = false;
|
||||
[SerializeField] private string _exportFolder = string.Empty; // Project relative (starts with Assets/)
|
||||
[SerializeField] private bool _autoFitGridToAtlas = true;
|
||||
[SerializeField] private int _nameStartIndex = 0;
|
||||
[SerializeField] private bool _rowMajor = true; // if false => column-major
|
||||
[SerializeField] private bool _startTop = true; // if false => start bottom
|
||||
[SerializeField] private bool _startLeft = true; // if false => start right
|
||||
[SerializeField] private bool _overrideGridFromAtlas = false; // ignore TXT col/row and derive from atlas size
|
||||
|
||||
private string _status;
|
||||
|
||||
@@ -47,14 +41,6 @@ public class DDSAtlasSlicerWindow : EditorWindow
|
||||
_paddingPixels = EditorGUILayout.IntField("Sprite Padding (px)", _paddingPixels);
|
||||
_pixelsPerUnit = EditorGUILayout.FloatField("Pixels Per Unit", _pixelsPerUnit);
|
||||
|
||||
GUILayout.Space(6);
|
||||
_autoFitGridToAtlas = EditorGUILayout.ToggleLeft("Auto-fit columns/rows to atlas size (prevents OOB)", _autoFitGridToAtlas);
|
||||
_nameStartIndex = EditorGUILayout.IntField("Name Start Index (offset)", _nameStartIndex);
|
||||
_rowMajor = EditorGUILayout.ToggleLeft("Row-Major order (else Column-Major)", _rowMajor);
|
||||
_startTop = EditorGUILayout.ToggleLeft("Names start at Top row (else Bottom)", _startTop);
|
||||
_startLeft = EditorGUILayout.ToggleLeft("Names start at Left col (else Right)", _startLeft);
|
||||
_overrideGridFromAtlas = EditorGUILayout.ToggleLeft("Override grid from atlas size (ignore TXT col/row)", _overrideGridFromAtlas);
|
||||
|
||||
GUILayout.Space(6);
|
||||
EditorGUILayout.LabelField("Output", EditorStyles.boldLabel);
|
||||
_exportAsIndividualPngs = EditorGUILayout.ToggleLeft("Export each sprite as an individual PNG file", _exportAsIndividualPngs);
|
||||
@@ -195,8 +181,8 @@ public class DDSAtlasSlicerWindow : EditorWindow
|
||||
|
||||
int spriteWidth = ReadInt(lines[0]);
|
||||
int spriteHeight = ReadInt(lines[1]);
|
||||
int columns = ReadInt(lines[2]);
|
||||
int rows = ReadInt(lines[3]);
|
||||
int columns = ReadInt(lines[3]);
|
||||
int rows = ReadInt(lines[2]);
|
||||
|
||||
var names = new List<string>();
|
||||
for (int i = 4; i < lines.Length; i++)
|
||||
@@ -207,7 +193,7 @@ public class DDSAtlasSlicerWindow : EditorWindow
|
||||
{
|
||||
if (_padIndexIfMissing)
|
||||
{
|
||||
names.Add((i - 4 + _nameStartIndex).ToString());
|
||||
names.Add((i - 4).ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -259,57 +245,39 @@ public class DDSAtlasSlicerWindow : EditorWindow
|
||||
int atlasWidth = tex.width;
|
||||
int atlasHeight = tex.height;
|
||||
|
||||
if (_overrideGridFromAtlas)
|
||||
{
|
||||
columns = Mathf.Max(1, atlasWidth / spriteWidth);
|
||||
rows = Mathf.Max(1, atlasHeight / spriteHeight);
|
||||
}
|
||||
else if (_autoFitGridToAtlas)
|
||||
{
|
||||
int maxCols = Mathf.Max(1, atlasWidth / spriteWidth);
|
||||
int maxRows = Mathf.Max(1, atlasHeight / spriteHeight);
|
||||
columns = Mathf.Min(columns, maxCols);
|
||||
rows = Mathf.Min(rows, maxRows);
|
||||
}
|
||||
|
||||
if (spriteWidth <= 0 || spriteHeight <= 0)
|
||||
throw new InvalidDataException("Sprite width/height must be > 0.");
|
||||
if (columns <= 0 || rows <= 0)
|
||||
throw new InvalidDataException("Columns/Rows must be > 0.");
|
||||
|
||||
int expected = columns * rows;
|
||||
// Align names count
|
||||
if (names.Count < expected)
|
||||
{
|
||||
for (int i = names.Count; i < expected; i++)
|
||||
{
|
||||
names.Add(_padIndexIfMissing ? i.ToString() : string.Empty);
|
||||
}
|
||||
}
|
||||
else if (names.Count > expected)
|
||||
{
|
||||
names = names.Take(expected).ToList();
|
||||
}
|
||||
|
||||
var metas = new List<SpriteMetaData>(expected);
|
||||
for (int r = 0; r < rows; r++)
|
||||
{
|
||||
for (int c = 0; c < columns; c++)
|
||||
{
|
||||
int rr = _startTop ? r : (rows - 1 - r);
|
||||
int cc = _startLeft ? c : (columns - 1 - c);
|
||||
int index = _rowMajor ? (rr * columns + cc) : (cc * rows + rr);
|
||||
int index = r * columns + c;
|
||||
int x = c * spriteWidth;
|
||||
int yFromBottom = (rows - 1 - r) * spriteHeight; // Unity rect y starts from bottom
|
||||
int yFromTop = r * spriteHeight; // DDS starts from top-left
|
||||
int yFromBottom = atlasHeight - yFromTop - spriteHeight; // Convert to Unity bottom-left origin
|
||||
|
||||
var rect = new Rect(x, yFromBottom, spriteWidth, spriteHeight);
|
||||
if (rect.x < 0 || rect.y < 0 || rect.xMax > atlasWidth || rect.yMax > atlasHeight)
|
||||
{
|
||||
if (_autoFitGridToAtlas)
|
||||
{
|
||||
float nx = Mathf.Clamp(rect.x, 0, Mathf.Max(0, atlasWidth - 1));
|
||||
float ny = Mathf.Clamp(rect.y, 0, Mathf.Max(0, atlasHeight - 1));
|
||||
float nw = Mathf.Clamp(rect.width, 0, atlasWidth - nx);
|
||||
float nh = Mathf.Clamp(rect.height, 0, atlasHeight - ny);
|
||||
rect = new Rect(nx, ny, nw, nh);
|
||||
if (rect.width <= 0 || rect.height <= 0) continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
string rawName = GetNameForIndex(names, index);
|
||||
var meta = new SpriteMetaData
|
||||
{
|
||||
name = SafeSpriteName(rawName, index),
|
||||
name = SafeSpriteName(names[index], index),
|
||||
rect = rect,
|
||||
alignment = (int)SpriteAlignment.Center,
|
||||
border = Vector4.zero,
|
||||
@@ -341,19 +309,6 @@ public class DDSAtlasSlicerWindow : EditorWindow
|
||||
int atlasWidth = tex.width;
|
||||
int atlasHeight = tex.height;
|
||||
|
||||
if (_overrideGridFromAtlas)
|
||||
{
|
||||
columns = Mathf.Max(1, atlasWidth / spriteWidth);
|
||||
rows = Mathf.Max(1, atlasHeight / spriteHeight);
|
||||
}
|
||||
else if (_autoFitGridToAtlas)
|
||||
{
|
||||
int maxCols = Mathf.Max(1, atlasWidth / spriteWidth);
|
||||
int maxRows = Mathf.Max(1, atlasHeight / spriteHeight);
|
||||
columns = Mathf.Min(columns, maxCols);
|
||||
rows = Mathf.Min(rows, maxRows);
|
||||
}
|
||||
|
||||
if (spriteWidth <= 0 || spriteHeight <= 0)
|
||||
throw new InvalidDataException("Sprite width/height must be > 0.");
|
||||
if (columns <= 0 || rows <= 0)
|
||||
@@ -362,6 +317,15 @@ public class DDSAtlasSlicerWindow : EditorWindow
|
||||
Debug.LogWarning($"[DDSAtlasSlicer] Grid exceeds atlas bounds: grid={columns}x{rows} cell={spriteWidth}x{spriteHeight} atlas={atlasWidth}x{atlasHeight}");
|
||||
|
||||
int expected = columns * rows;
|
||||
if (names.Count < expected)
|
||||
{
|
||||
for (int i = names.Count; i < expected; i++)
|
||||
names.Add(_padIndexIfMissing ? i.ToString() : string.Empty);
|
||||
}
|
||||
else if (names.Count > expected)
|
||||
{
|
||||
names = names.Take(expected).ToList();
|
||||
}
|
||||
|
||||
// Clear previous Sprite sub-assets under this texture
|
||||
var all = AssetDatabase.LoadAllAssetsAtPath(atlasAssetPath);
|
||||
@@ -377,29 +341,12 @@ public class DDSAtlasSlicerWindow : EditorWindow
|
||||
{
|
||||
for (int c = 0; c < columns; c++)
|
||||
{
|
||||
int rr = _startTop ? r : (rows - 1 - r);
|
||||
int cc = _startLeft ? c : (columns - 1 - c);
|
||||
int index = _rowMajor ? (rr * columns + cc) : (cc * rows + rr);
|
||||
int index = r * columns + c;
|
||||
int x = c * spriteWidth;
|
||||
int yFromBottom = (rows - 1 - r) * spriteHeight; // bottom-left origin
|
||||
int yFromTop = r * spriteHeight; // DDS starts from top-left
|
||||
int yFromBottom = atlasHeight - yFromTop - spriteHeight; // Convert to Unity bottom-left origin
|
||||
|
||||
var rect = new Rect(x, yFromBottom, spriteWidth, spriteHeight);
|
||||
if (rect.x < 0 || rect.y < 0 || rect.xMax > atlasWidth || rect.yMax > atlasHeight)
|
||||
{
|
||||
if (_autoFitGridToAtlas)
|
||||
{
|
||||
float nx = Mathf.Clamp(rect.x, 0, Mathf.Max(0, atlasWidth - 1));
|
||||
float ny = Mathf.Clamp(rect.y, 0, Mathf.Max(0, atlasHeight - 1));
|
||||
float nw = Mathf.Clamp(rect.width, 0, atlasWidth - nx);
|
||||
float nh = Mathf.Clamp(rect.height, 0, atlasHeight - ny);
|
||||
rect = new Rect(nx, ny, nw, nh);
|
||||
if (rect.width <= 0 || rect.height <= 0) continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (_paddingPixels > 0)
|
||||
{
|
||||
float nx = Mathf.Max(0, rect.x - _paddingPixels);
|
||||
@@ -409,7 +356,7 @@ public class DDSAtlasSlicerWindow : EditorWindow
|
||||
rect = new Rect(nx, ny, nw, nh);
|
||||
}
|
||||
|
||||
string sprName = SafeSpriteName(GetNameForIndex(names, index), index);
|
||||
string sprName = SafeSpriteName(names[index], index);
|
||||
var sprite = Sprite.Create(tex, rect, new Vector2(0.5f, 0.5f), Mathf.Max(1f, _pixelsPerUnit));
|
||||
sprite.name = sprName;
|
||||
AssetDatabase.AddObjectToAsset(sprite, tex);
|
||||
@@ -430,120 +377,108 @@ public class DDSAtlasSlicerWindow : EditorWindow
|
||||
int atlasWidth = atlasTex.width;
|
||||
int atlasHeight = atlasTex.height;
|
||||
|
||||
if (_overrideGridFromAtlas)
|
||||
{
|
||||
columns = Mathf.Max(1, atlasWidth / spriteWidth);
|
||||
rows = Mathf.Max(1, atlasHeight / spriteHeight);
|
||||
}
|
||||
else if (_autoFitGridToAtlas)
|
||||
{
|
||||
int maxCols = Mathf.Max(1, atlasWidth / spriteWidth);
|
||||
int maxRows = Mathf.Max(1, atlasHeight / spriteHeight);
|
||||
columns = Mathf.Min(columns, maxCols);
|
||||
rows = Mathf.Min(rows, maxRows);
|
||||
}
|
||||
|
||||
if (spriteWidth <= 0 || spriteHeight <= 0)
|
||||
throw new InvalidDataException("Sprite width/height must be > 0.");
|
||||
if (columns <= 0 || rows <= 0)
|
||||
throw new InvalidDataException("Columns/Rows must be > 0.");
|
||||
|
||||
int expected = columns * rows;
|
||||
if (names.Count < expected)
|
||||
{
|
||||
for (int i = names.Count; i < expected; i++)
|
||||
names.Add(_padIndexIfMissing ? i.ToString() : string.Empty);
|
||||
}
|
||||
else if (names.Count > expected)
|
||||
{
|
||||
names = names.Take(expected).ToList();
|
||||
}
|
||||
|
||||
// Resolve export folder
|
||||
// Resolve export path
|
||||
string targetFolderProject = _exportFolder;
|
||||
if (string.IsNullOrEmpty(targetFolderProject))
|
||||
{
|
||||
string atlasDir = Path.GetDirectoryName(atlasAssetPath).Replace('\\', '/');
|
||||
string atlasName = Path.GetFileNameWithoutExtension(atlasAssetPath);
|
||||
targetFolderProject = $"{atlasDir}/{atlasName}_slices";
|
||||
targetFolderProject = $"{atlasDir}/{atlasName}_multisprite.png";
|
||||
}
|
||||
else
|
||||
{
|
||||
string atlasName = Path.GetFileNameWithoutExtension(atlasAssetPath);
|
||||
targetFolderProject = $"{targetFolderProject}/{atlasName}_multisprite.png";
|
||||
}
|
||||
EnsureFolder(targetFolderProject);
|
||||
string targetFolderAbs = ProjectPathToAbsolute(targetFolderProject);
|
||||
|
||||
var rt = new RenderTexture(atlasWidth, atlasHeight, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB);
|
||||
var prevActive = RenderTexture.active;
|
||||
string targetFolderAbs = ProjectPathToAbsolute(Path.GetDirectoryName(targetFolderProject));
|
||||
string fileName = Path.GetFileName(targetFolderProject);
|
||||
string fileAbs = Path.Combine(targetFolderAbs, fileName);
|
||||
fileAbs = GetUniqueFilePath(fileAbs);
|
||||
|
||||
// Create a readable copy of the texture
|
||||
Texture2D readableTex = new Texture2D(atlasWidth, atlasHeight, TextureFormat.RGBA32, false);
|
||||
RenderTexture rt = RenderTexture.GetTemporary(atlasWidth, atlasHeight, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB);
|
||||
Graphics.Blit(atlasTex, rt);
|
||||
RenderTexture.active = rt;
|
||||
readableTex.ReadPixels(new Rect(0, 0, atlasWidth, atlasHeight), 0, 0);
|
||||
readableTex.Apply();
|
||||
RenderTexture.active = null;
|
||||
RenderTexture.ReleaseTemporary(rt);
|
||||
|
||||
try
|
||||
// Create PNG from readable copy
|
||||
var png = ImageConversion.EncodeToPNG(readableTex);
|
||||
File.WriteAllBytes(fileAbs, png);
|
||||
|
||||
// Clean up
|
||||
UnityEngine.Object.DestroyImmediate(readableTex);
|
||||
|
||||
string fileProject = AbsoluteToProjectPath(fileAbs).Replace('\\', '/');
|
||||
if (!string.IsNullOrEmpty(fileProject))
|
||||
{
|
||||
for (int r = 0; r < rows; r++)
|
||||
AssetDatabase.ImportAsset(fileProject);
|
||||
var pngImporter = AssetImporter.GetAtPath(fileProject) as TextureImporter;
|
||||
if (pngImporter != null)
|
||||
{
|
||||
for (int c = 0; c < columns; c++)
|
||||
pngImporter.textureType = TextureImporterType.Sprite;
|
||||
pngImporter.spriteImportMode = SpriteImportMode.Multiple;
|
||||
pngImporter.mipmapEnabled = false;
|
||||
pngImporter.alphaIsTransparency = true;
|
||||
pngImporter.spritePixelsPerUnit = Mathf.Max(1f, _pixelsPerUnit);
|
||||
|
||||
// Create sprite metadata
|
||||
var metas = new List<SpriteMetaData>(expected);
|
||||
for (int r = 0; r < rows; r++)
|
||||
{
|
||||
int rr = _startTop ? r : (rows - 1 - r);
|
||||
int cc = _startLeft ? c : (columns - 1 - c);
|
||||
int index = _rowMajor ? (rr * columns + cc) : (cc * rows + rr);
|
||||
int x = c * spriteWidth;
|
||||
int yFromBottom = (rows - 1 - r) * spriteHeight;
|
||||
|
||||
var rect = new Rect(x, yFromBottom, spriteWidth, spriteHeight);
|
||||
if (rect.x < 0 || rect.y < 0 || rect.xMax > atlasWidth || rect.yMax > atlasHeight)
|
||||
{
|
||||
if (_autoFitGridToAtlas)
|
||||
for (int c = 0; c < columns; c++)
|
||||
{
|
||||
float nx = Mathf.Clamp(rect.x, 0, Mathf.Max(0, atlasWidth - 1));
|
||||
float ny = Mathf.Clamp(rect.y, 0, Mathf.Max(0, atlasHeight - 1));
|
||||
float nw = Mathf.Clamp(rect.width, 0, atlasWidth - nx);
|
||||
float nh = Mathf.Clamp(rect.height, 0, atlasHeight - ny);
|
||||
rect = new Rect(nx, ny, nw, nh);
|
||||
if (rect.width <= 0 || rect.height <= 0) continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (_paddingPixels > 0)
|
||||
{
|
||||
float nx = Mathf.Max(0, rect.x - _paddingPixels);
|
||||
float ny = Mathf.Max(0, rect.y - _paddingPixels);
|
||||
float nw = Mathf.Min(atlasWidth - nx, rect.width + 2 * _paddingPixels);
|
||||
float nh = Mathf.Min(atlasHeight - ny, rect.height + 2 * _paddingPixels);
|
||||
rect = new Rect(nx, ny, nw, nh);
|
||||
}
|
||||
int index = r * columns + c;
|
||||
int x = c * spriteWidth;
|
||||
int yFromTop = r * spriteHeight; // DDS starts from top-left
|
||||
int yFromBottom = atlasHeight - yFromTop - spriteHeight; // Convert to Unity bottom-left origin
|
||||
|
||||
int w = Mathf.RoundToInt(rect.width);
|
||||
int h = Mathf.RoundToInt(rect.height);
|
||||
if (w <= 0 || h <= 0) continue;
|
||||
|
||||
var slice = new Texture2D(w, h, TextureFormat.RGBA32, false, false);
|
||||
slice.ReadPixels(rect, 0, 0);
|
||||
slice.Apply(false, false);
|
||||
|
||||
string sprName = SafeSpriteName(GetNameForIndex(names, index), index);
|
||||
if (string.IsNullOrWhiteSpace(sprName)) sprName = $"sprite_{index:0000}";
|
||||
string fileAbs = Path.Combine(targetFolderAbs, sprName + ".png");
|
||||
fileAbs = GetUniqueFilePath(fileAbs);
|
||||
var png = ImageConversion.EncodeToPNG(slice);
|
||||
File.WriteAllBytes(fileAbs, png);
|
||||
UnityEngine.Object.DestroyImmediate(slice);
|
||||
|
||||
string fileProject = AbsoluteToProjectPath(fileAbs).Replace('\\', '/');
|
||||
if (!string.IsNullOrEmpty(fileProject))
|
||||
{
|
||||
AssetDatabase.ImportAsset(fileProject);
|
||||
var pngImporter = AssetImporter.GetAtPath(fileProject) as TextureImporter;
|
||||
if (pngImporter != null)
|
||||
var rect = new Rect(x, yFromBottom, spriteWidth, spriteHeight);
|
||||
if (_paddingPixels > 0)
|
||||
{
|
||||
pngImporter.textureType = TextureImporterType.Sprite;
|
||||
pngImporter.spriteImportMode = SpriteImportMode.Single;
|
||||
pngImporter.mipmapEnabled = false;
|
||||
pngImporter.alphaIsTransparency = true;
|
||||
pngImporter.spritePixelsPerUnit = Mathf.Max(1f, _pixelsPerUnit);
|
||||
pngImporter.SaveAndReimport();
|
||||
float nx = Mathf.Max(0, rect.x - _paddingPixels);
|
||||
float ny = Mathf.Max(0, rect.y - _paddingPixels);
|
||||
float nw = Mathf.Min(atlasWidth - nx, rect.width + 2 * _paddingPixels);
|
||||
float nh = Mathf.Min(atlasHeight - ny, rect.height + 2 * _paddingPixels);
|
||||
rect = new Rect(nx, ny, nw, nh);
|
||||
}
|
||||
|
||||
var meta = new SpriteMetaData
|
||||
{
|
||||
name = SafeSpriteName(names[index], index),
|
||||
rect = rect,
|
||||
alignment = (int)SpriteAlignment.Center,
|
||||
border = Vector4.zero,
|
||||
pivot = new Vector2(0.5f, 0.5f)
|
||||
};
|
||||
metas.Add(meta);
|
||||
}
|
||||
}
|
||||
|
||||
pngImporter.spritesheet = metas.ToArray();
|
||||
pngImporter.SaveAndReimport();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
RenderTexture.active = prevActive;
|
||||
rt.Release();
|
||||
UnityEngine.Object.DestroyImmediate(rt);
|
||||
}
|
||||
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
@@ -637,20 +572,5 @@ public class DDSAtlasSlicerWindow : EditorWindow
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private string GetNameForIndex(List<string> names, int gridIndex)
|
||||
{
|
||||
int idx = gridIndex;
|
||||
if (_nameStartIndex != 0)
|
||||
{
|
||||
idx = gridIndex + _nameStartIndex;
|
||||
}
|
||||
if (idx >= 0 && idx < names.Count)
|
||||
{
|
||||
string n = names[idx];
|
||||
if (!string.IsNullOrWhiteSpace(n)) return n;
|
||||
}
|
||||
return gridIndex.ToString();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user