texturepacker fixed and functional. 9p not tested yet.
This commit is contained in:
parent
87952248c5
commit
c8ec1b01b1
@ -22,9 +22,12 @@ namespace RecrownedAthenaeum.Tools.TextureAtlas
|
|||||||
|
|
||||||
int powLimit;
|
int powLimit;
|
||||||
Node masterNode;
|
Node masterNode;
|
||||||
Dictionary<string, ImageHandler> imageHandlers;
|
Dictionary<string, ImageHandler> imageHandlersDictionary;
|
||||||
int textureLength;
|
int textureLength;
|
||||||
|
int tpl;
|
||||||
|
int TexturePowerLength { get { return tpl; } set { textureLength = (int)Math.Pow(2, value); tpl = value; } }
|
||||||
|
public int TextureLength { get { return textureLength; } }
|
||||||
|
public int TexturesFound { get { return imageHandlersDictionary.Count; } }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Machine to pack multiple textures into one large texture.
|
/// Machine to pack multiple textures into one large texture.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -35,22 +38,28 @@ namespace RecrownedAthenaeum.Tools.TextureAtlas
|
|||||||
{
|
{
|
||||||
this.powLimit = powLimit;
|
this.powLimit = powLimit;
|
||||||
string[] paths = Directory.GetFiles(rootDirectoryPath);
|
string[] paths = Directory.GetFiles(rootDirectoryPath);
|
||||||
textureLength = startingPower;
|
TexturePowerLength = startingPower;
|
||||||
List<ImageHandler> imageHandlers = new List<ImageHandler>();
|
List<ImageHandler> imageHandlers = new List<ImageHandler>();
|
||||||
|
int minAreaRequired = 0;
|
||||||
for (int pathID = 0; pathID < paths.Length; pathID++)
|
for (int pathID = 0; pathID < paths.Length; pathID++)
|
||||||
{
|
{
|
||||||
SupportedExtensions extension;
|
SupportedExtensions extension;
|
||||||
if (Enum.TryParse(Path.GetExtension(paths[pathID]), out extension))
|
if (Enum.TryParse<SupportedExtensions>(Path.GetExtension(paths[pathID]).ToLower().Substring(1), out extension))
|
||||||
{
|
{
|
||||||
ImageHandler image = new ImageHandler(paths[pathID]);
|
ImageHandler image = new ImageHandler(paths[pathID]);
|
||||||
imageHandlers.Add(image);
|
imageHandlers.Add(image);
|
||||||
|
minAreaRequired += image.Area;
|
||||||
|
while (minAreaRequired > textureLength*textureLength)
|
||||||
|
{
|
||||||
|
TexturePowerLength++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
imageHandlers.Sort();
|
imageHandlers.Sort();
|
||||||
this.imageHandlers = new Dictionary<string, ImageHandler>();
|
this.imageHandlersDictionary = new Dictionary<string, ImageHandler>();
|
||||||
foreach (ImageHandler imageHandler in imageHandlers)
|
foreach (ImageHandler imageHandler in imageHandlers)
|
||||||
{
|
{
|
||||||
this.imageHandlers.Add(imageHandler.Name, imageHandler);
|
this.imageHandlersDictionary.Add(imageHandler.Name, imageHandler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,23 +72,23 @@ namespace RecrownedAthenaeum.Tools.TextureAtlas
|
|||||||
masterNode = new Node();
|
masterNode = new Node();
|
||||||
masterNode.region.Width = textureLength;
|
masterNode.region.Width = textureLength;
|
||||||
masterNode.region.Height = textureLength;
|
masterNode.region.Height = textureLength;
|
||||||
Queue<ImageHandler> imageHandlerQueue = new Queue<ImageHandler>(imageHandlers.Values);
|
Queue<ImageHandler> imageHandlerQueue = new Queue<ImageHandler>(imageHandlersDictionary.Values);
|
||||||
ImageHandler imageHandler;
|
ImageHandler imageHandler;
|
||||||
while (imageHandlerQueue.TryDequeue(out imageHandler))
|
while (imageHandlerQueue.TryDequeue(out imageHandler))
|
||||||
{
|
{
|
||||||
Node activeNode = null;
|
Node activeNode = null;
|
||||||
do
|
activeNode = masterNode.InsertImageHandler(imageHandler);
|
||||||
|
if (activeNode == null)
|
||||||
{
|
{
|
||||||
activeNode = masterNode.InsertImageHandler(imageHandler);
|
if (!AutoCorrectAtlasSize || TexturePowerLength + 1 > powLimit)
|
||||||
if (activeNode == null)
|
|
||||||
{
|
{
|
||||||
if (!AutoCorrectAtlasSize || (textureLength *= 2) > Math.Pow(2, powLimit))
|
throw new InvalidOperationException("Texture not large enough. Current size: " + textureLength + "x" + textureLength + ".");
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Dimensions of texture goes past limit amount of " + powLimit + " which is " + Math.Pow(2, powLimit) + ". New texture size would be " + textureLength + "x" + textureLength + ".");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
TexturePowerLength += 1;
|
||||||
|
imageHandlerQueue.Clear();
|
||||||
|
Build(AutoCorrectAtlasSize);
|
||||||
}
|
}
|
||||||
while (activeNode == null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,28 +101,33 @@ namespace RecrownedAthenaeum.Tools.TextureAtlas
|
|||||||
{
|
{
|
||||||
GraphicsOptions gOptions = new GraphicsOptions();
|
GraphicsOptions gOptions = new GraphicsOptions();
|
||||||
|
|
||||||
TextureAtlasData.TextureAtlasRegion[] regions = new TextureAtlasData.TextureAtlasRegion[imageHandlers.Count];
|
TextureAtlasData.TextureAtlasRegion[] regions = new TextureAtlasData.TextureAtlasRegion[imageHandlersDictionary.Count];
|
||||||
|
|
||||||
using (Image<Rgba32> atlasTexture = new Image<Rgba32>(textureLength, textureLength))
|
using (Image<Rgba32> atlasTexture = new Image<Rgba32>(textureLength, textureLength))
|
||||||
{
|
{
|
||||||
ImageHandler[] imageHandlers = this.imageHandlers.Values.ToArray();
|
ImageHandler[] imageHandlers = this.imageHandlersDictionary.Values.ToArray();
|
||||||
|
|
||||||
for (int i = 0; i < imageHandlers.Length; i++)
|
for (int i = 0; i < imageHandlers.Length; i++)
|
||||||
{
|
{
|
||||||
regions[i] = new TextureAtlasData.TextureAtlasRegion();
|
regions[i] = new TextureAtlasData.TextureAtlasRegion();
|
||||||
ImageHandler imageH = imageHandlers[i];
|
ImageHandler ih = imageHandlers[i];
|
||||||
regions[i].SetBounds(imageH.x, imageH.y, imageH.Width, imageH.Height);
|
regions[i].SetBounds(ih.x, ih.y, ih.Width, ih.Height);
|
||||||
regions[i].ninePatchData = imageH.ninePatchData;
|
regions[i].ninePatchData = ih.ninePatchData;
|
||||||
atlasTexture.Mutate(img => img.DrawImage(gOptions, imageH.image, new Point(imageH.x, imageH.y)));
|
regions[i].name = ih.Name;
|
||||||
|
using (Image<Rgba32> image = Image.Load<Rgba32>(ih.path))
|
||||||
|
{
|
||||||
|
atlasTexture.Mutate(img => img.DrawImage(gOptions, image, new Point(ih.x, ih.y)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
using (FileStream stream = new FileStream(output + atlasName + ".png", FileMode.Create))
|
Directory.CreateDirectory(output);
|
||||||
|
using (FileStream stream = new FileStream(output + "/" + atlasName + ".png", FileMode.Create))
|
||||||
{
|
{
|
||||||
atlasTexture.SaveAsPng(stream);
|
atlasTexture.SaveAsPng(stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
string serialized = JsonConvert.SerializeObject(new TextureAtlasData(atlasName + ".png", regions));
|
string serialized = JsonConvert.SerializeObject(new TextureAtlasData(atlasName + ".png", regions), Formatting.Indented);
|
||||||
|
|
||||||
using (StreamWriter stream = new StreamWriter(output + atlasName + ".tatlas"))
|
using (StreamWriter stream = new StreamWriter(output + "/" + atlasName + ".tatlas"))
|
||||||
{
|
{
|
||||||
stream.WriteLine(serialized);
|
stream.WriteLine(serialized);
|
||||||
}
|
}
|
||||||
@ -121,24 +135,20 @@ namespace RecrownedAthenaeum.Tools.TextureAtlas
|
|||||||
|
|
||||||
public void SetNinePatch(string fileName, int a, int b, int c, int d)
|
public void SetNinePatch(string fileName, int a, int b, int c, int d)
|
||||||
{
|
{
|
||||||
ImageHandler imageHandler = imageHandlers[fileName];
|
ImageHandler imageHandler = imageHandlersDictionary[fileName];
|
||||||
NinePatchData ninePatchData = new NinePatchData(fileName, a, b, c, d);
|
NinePatchData ninePatchData = new NinePatchData(fileName, a, b, c, d);
|
||||||
imageHandler.ninePatchData = ninePatchData;
|
imageHandler.ninePatchData = ninePatchData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveNinePatch(string fileName)
|
public void RemoveNinePatch(string fileName)
|
||||||
{
|
{
|
||||||
imageHandlers[fileName].ninePatchData = null;
|
imageHandlersDictionary[fileName].ninePatchData = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
ImageHandler[] imageHandlers = this.imageHandlers.Values.ToArray();
|
ImageHandler[] imageHandlers = this.imageHandlersDictionary.Values.ToArray();
|
||||||
this.imageHandlers.Clear();
|
this.imageHandlersDictionary.Clear();
|
||||||
foreach (ImageHandler imageHandler in imageHandlers)
|
|
||||||
{
|
|
||||||
imageHandler.Dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Node
|
private class Node
|
||||||
@ -148,13 +158,12 @@ namespace RecrownedAthenaeum.Tools.TextureAtlas
|
|||||||
public Node childA { get { if (a == null) a = new Node(this); return a; } set { value.parent = this; a = value; } }
|
public Node childA { get { if (a == null) a = new Node(this); return a; } set { value.parent = this; a = value; } }
|
||||||
public Node childB { get { if (b == null) { b = new Node(this); } return b; } set { value.parent = this; b = value; } }
|
public Node childB { get { if (b == null) { b = new Node(this); } return b; } set { value.parent = this; b = value; } }
|
||||||
public Rectangle region;
|
public Rectangle region;
|
||||||
public bool containsImage = false;
|
public bool ContainsImage = false;
|
||||||
public bool Filled { get { return containsImage || (a != null && b != null && a.Filled && b.Filled); } }
|
public bool CanPlaceImage { get { return (a == null && b == null && !ContainsImage); } }
|
||||||
|
|
||||||
public Node(Node parent = null)
|
public Node(Node parent = null)
|
||||||
{
|
{
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
region = parent.region;
|
if (parent != null) region = parent.region;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -171,17 +180,21 @@ namespace RecrownedAthenaeum.Tools.TextureAtlas
|
|||||||
if (a == null)
|
if (a == null)
|
||||||
{
|
{
|
||||||
childA.region.Width = imageHandler.Width;
|
childA.region.Width = imageHandler.Width;
|
||||||
childB.region.Width = region.Width - childA.region.Width;
|
|
||||||
}
|
}
|
||||||
if (!childA.Filled && imageHandler.Width <= childA.region.Width)
|
Node attemptedNode = null;
|
||||||
|
if (!childA.ContainsImage && imageHandler.Width <= childA.region.Width)
|
||||||
{
|
{
|
||||||
return childA.InsertImageHandler(imageHandler);
|
attemptedNode = childA.InsertImageHandler(imageHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!childB.Filled)
|
if (attemptedNode == null && !childB.ContainsImage)
|
||||||
{
|
{
|
||||||
return childB.InsertImageHandler(imageHandler);
|
childB.region.Width = region.Width - childA.region.Width;
|
||||||
|
childB.region.X = childA.region.X + childA.region.Width;
|
||||||
|
attemptedNode = childB.InsertImageHandler(imageHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return attemptedNode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (imageHandler.Height != region.Height)
|
else if (imageHandler.Height != region.Height)
|
||||||
@ -191,35 +204,37 @@ namespace RecrownedAthenaeum.Tools.TextureAtlas
|
|||||||
if (a == null)
|
if (a == null)
|
||||||
{
|
{
|
||||||
childA.region.Height = imageHandler.Height;
|
childA.region.Height = imageHandler.Height;
|
||||||
|
}
|
||||||
|
Node attemptedNode = null;
|
||||||
|
if (!childA.ContainsImage && imageHandler.Height <= childA.region.Height)
|
||||||
|
{
|
||||||
|
attemptedNode = childA.InsertImageHandler(imageHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attemptedNode == null && !childB.ContainsImage)
|
||||||
|
{
|
||||||
childB.region.Height = region.Height - childA.region.Height;
|
childB.region.Height = region.Height - childA.region.Height;
|
||||||
|
childB.region.Y = childA.region.Y + childA.region.Height;
|
||||||
|
attemptedNode = childB.InsertImageHandler(imageHandler);
|
||||||
}
|
}
|
||||||
|
return attemptedNode;
|
||||||
if (!childA.Filled && imageHandler.Width <= childA.region.Width)
|
|
||||||
{
|
|
||||||
return childA.InsertImageHandler(imageHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!childB.Filled)
|
|
||||||
{
|
|
||||||
return childB.InsertImageHandler(imageHandler);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else if (CanPlaceImage)
|
||||||
{
|
{
|
||||||
imageHandler.x = region.X;
|
imageHandler.x = region.X;
|
||||||
imageHandler.y = region.Y;
|
imageHandler.y = region.Y;
|
||||||
containsImage = true;
|
ContainsImage = true;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ImageHandler : IComparable<ImageHandler>, IDisposable
|
private class ImageHandler : IComparable<ImageHandler>
|
||||||
{
|
{
|
||||||
public readonly string path;
|
public readonly string path;
|
||||||
public readonly Image<Rgba32> image;
|
public readonly IImageInfo image;
|
||||||
public int Area { get { return image.Width * image.Height; } }
|
public int Area { get { return image.Width * image.Height; } }
|
||||||
public string Name { get { return Path.GetFileName(path); } }
|
public string Name { get { return Path.GetFileName(path); } }
|
||||||
public int Width { get { return image.Width; } }
|
public int Width { get { return image.Width; } }
|
||||||
@ -232,7 +247,7 @@ namespace RecrownedAthenaeum.Tools.TextureAtlas
|
|||||||
this.path = path;
|
this.path = path;
|
||||||
using (FileStream stream = new FileStream(path, FileMode.Open))
|
using (FileStream stream = new FileStream(path, FileMode.Open))
|
||||||
{
|
{
|
||||||
image = Image.Load(stream);
|
image = Image.Identify(stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,11 +255,6 @@ namespace RecrownedAthenaeum.Tools.TextureAtlas
|
|||||||
{
|
{
|
||||||
return Area - tImage.Area;
|
return Area - tImage.Area;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
image.Dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ namespace RecrownedAthenaeum.Tools.TextureAtlasTools
|
|||||||
{
|
{
|
||||||
class TexturePackerCommand : EngineCommand
|
class TexturePackerCommand : EngineCommand
|
||||||
{
|
{
|
||||||
TexturePacker texturePacker;
|
|
||||||
|
|
||||||
public TexturePackerCommand() : base("texturepacker") { }
|
public TexturePackerCommand() : base("texturepacker") { }
|
||||||
|
|
||||||
@ -38,9 +37,13 @@ namespace RecrownedAthenaeum.Tools.TextureAtlasTools
|
|||||||
|
|
||||||
public override void Run(string[] arguments)
|
public override void Run(string[] arguments)
|
||||||
{
|
{
|
||||||
|
TexturePacker texturePacker = null;
|
||||||
string path = null;
|
string path = null;
|
||||||
int mp = 8;
|
int mp = 8;
|
||||||
int sp = 8;
|
int sp = 8;
|
||||||
|
bool dau = false;
|
||||||
|
string output = null;
|
||||||
|
|
||||||
if (arguments == null) throw new ArgumentException("Requires arguments. Type \"help texturepacker\" for more info.");
|
if (arguments == null) throw new ArgumentException("Requires arguments. Type \"help texturepacker\" for more info.");
|
||||||
|
|
||||||
for (int i = 0; i < arguments.Length; i++)
|
for (int i = 0; i < arguments.Length; i++)
|
||||||
@ -58,19 +61,28 @@ namespace RecrownedAthenaeum.Tools.TextureAtlasTools
|
|||||||
{
|
{
|
||||||
if (i + 1 >= arguments.Length || !int.TryParse(arguments[i + 1], out sp)) throw new ArgumentException("sp is not followed by maximum power.");
|
if (i + 1 >= arguments.Length || !int.TryParse(arguments[i + 1], out sp)) throw new ArgumentException("sp is not followed by maximum power.");
|
||||||
}
|
}
|
||||||
|
if (arguments[i] == "-o")
|
||||||
|
{
|
||||||
|
if (i + 1 >= arguments.Length) throw new ArgumentException("-o is not followed by path for output files. (eg. path/to/file where file is the name for the atlas.)");
|
||||||
|
output = arguments[i + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == arguments.Length - 1 && output == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("no -o argument found to specify output.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arguments[i] == "-dau") dau = true;
|
||||||
}
|
}
|
||||||
texturePacker = new TexturePacker(path, mp, sp);
|
texturePacker = new TexturePacker(path, mp, sp);
|
||||||
|
ConsoleUtilities.WriteWrappedLine("Calculated minimum texture size: " + texturePacker.TextureLength + "x" + texturePacker.TextureLength + " with a total of " + texturePacker.TexturesFound + " textures.");
|
||||||
if (texturePacker != null)
|
if (texturePacker != null)
|
||||||
{
|
{
|
||||||
bool dau = false;
|
|
||||||
for (int i = 0; i < arguments.Length; i++)
|
|
||||||
{
|
|
||||||
if (arguments[i] == "-dau") dau = true;
|
|
||||||
}
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
texturePacker.Build(!dau);
|
texturePacker.Build(!dau);
|
||||||
} catch (InvalidOperationException e)
|
}
|
||||||
|
catch (InvalidOperationException e)
|
||||||
{
|
{
|
||||||
throw new ArgumentException(e.Message);
|
throw new ArgumentException(e.Message);
|
||||||
}
|
}
|
||||||
@ -91,21 +103,8 @@ namespace RecrownedAthenaeum.Tools.TextureAtlasTools
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
texturePacker.Save(Path.GetDirectoryName(output), Path.GetFileName(output));
|
||||||
for (int i = 0; i < arguments.Length; i++)
|
Console.WriteLine("Complete. Final texture size: " + texturePacker.TextureLength + "x" + texturePacker.TextureLength + ".");
|
||||||
{
|
|
||||||
if (arguments[i] == "-o")
|
|
||||||
{
|
|
||||||
if (i + 1 >= arguments.Length) throw new ArgumentException("-o is not followed by path for output files. (eg. path/to/file where file is the name for the atlas.)");
|
|
||||||
texturePacker.Save(Path.GetDirectoryName(arguments[i + 1]), Path.GetFileName(arguments[i + 1]));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == arguments.Length - 1)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("no -o argument found to specify output.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -58,12 +58,18 @@ namespace RecrownedAthenaeum.Persistence
|
|||||||
private bool LoadSpecific(Type preference)
|
private bool LoadSpecific(Type preference)
|
||||||
{
|
{
|
||||||
string path = savePath + "/" + preference.Name + ".xml";
|
string path = savePath + "/" + preference.Name + ".xml";
|
||||||
if (File.Exists(path))
|
try
|
||||||
{
|
{
|
||||||
Stream stream = new FileStream(path, FileMode.Open);
|
if (File.Exists(path))
|
||||||
preferenceList[preference] = xmlSerializer.Deserialize(stream);
|
{
|
||||||
stream.Close();
|
Stream stream = new FileStream(path, FileMode.Open);
|
||||||
return true;
|
preferenceList[preference] = xmlSerializer.Deserialize(stream);
|
||||||
|
stream.Close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (InvalidOperationException)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user