texturepacker fixed and functional. 9p not tested yet.

This commit is contained in:
Harrison Deng 2018-12-09 13:45:47 -06:00
parent 87952248c5
commit c8ec1b01b1
3 changed files with 103 additions and 88 deletions

View File

@ -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();
}
} }
} }
} }

View File

@ -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
{ {

View File

@ -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;
} }