[csharp][unity] Updated TextureAtlas parsing for latest format.

This commit is contained in:
Nathan Sweet 2020-12-20 15:24:47 -08:00
parent 84c77bd46a
commit f8d6359b32
10 changed files with 181 additions and 162 deletions

View File

@ -61,8 +61,8 @@ namespace Spine {
public List<AtlasRegion> Regions { get { return regions; } }
public List<AtlasPage> Pages { get { return pages; } }
#if !(IS_UNITY)
#if WINDOWS_STOREAPP
#if !(IS_UNITY)
#if WINDOWS_STOREAPP
private async Task ReadFile(string path, TextureLoader textureLoader) {
var folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
var file = await folder.GetFileAsync(path).AsTask().ConfigureAwait(false);
@ -78,166 +78,193 @@ namespace Spine {
public Atlas(string path, TextureLoader textureLoader) {
this.ReadFile(path, textureLoader).Wait();
}
#else
#else
public Atlas (string path, TextureLoader textureLoader) {
#if WINDOWS_PHONE
#if WINDOWS_PHONE
Stream stream = Microsoft.Xna.Framework.TitleContainer.OpenStream(path);
using (StreamReader reader = new StreamReader(stream)) {
#else
#else
using (StreamReader reader = new StreamReader(path)) {
#endif // WINDOWS_PHONE
#endif // WINDOWS_PHONE
try {
Load(reader, Path.GetDirectoryName(path), textureLoader);
} catch (Exception ex) {
throw new Exception("Error reading atlas file: " + path, ex);
}
}
}
#endif // WINDOWS_STOREAPP
#endif
public Atlas (TextReader reader, string dir, TextureLoader textureLoader) {
Load(reader, dir, textureLoader);
}
#endif // WINDOWS_STOREAPP
#endif
public Atlas (List<AtlasPage> pages, List<AtlasRegion> regions) {
if (pages == null) throw new ArgumentNullException("pages", "pages cannot be null.");
if (regions == null) throw new ArgumentNullException("regions", "regions cannot be null.");
this.pages = pages;
this.regions = regions;
this.textureLoader = null;
}
private void Load (TextReader reader, string imagesDir, TextureLoader textureLoader) {
public Atlas (TextReader reader, string imagesDir, TextureLoader textureLoader) {
if (reader == null) throw new ArgumentNullException("reader", "reader cannot be null.");
if (imagesDir == null) throw new ArgumentNullException("imagesDir", "imagesDir cannot be null.");
if (textureLoader == null) throw new ArgumentNullException("textureLoader", "textureLoader cannot be null.");
this.textureLoader = textureLoader;
string[] tuple = new string[4];
string[] entry = new string[5];
AtlasPage page = null;
AtlasRegion region = null;
var pageFields = new Dictionary<string, Action>(5);
pageFields.Add("size", () => {
page.width = int.Parse(entry[1], CultureInfo.InvariantCulture);
page.height = int.Parse(entry[2], CultureInfo.InvariantCulture);
});
pageFields.Add("format", () => {
page.format = (Format)Enum.Parse(typeof(Format), entry[1], false);
});
pageFields.Add("filter", () => {
page.minFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), entry[1], false);
page.magFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), entry[1], false);
});
pageFields.Add("repeat", () => {
if (entry[1].IndexOf('x') != -1) page.uWrap = TextureWrap.Repeat;
if (entry[1].IndexOf('y') != -1) page.vWrap = TextureWrap.Repeat;
});
pageFields.Add("pma", () => {
page.pma = entry[1] == "true";
});
var regionFields = new Dictionary<string, Action>(4);
regionFields.Add("xy", () => { // Deprecated, use bounds.
region.x = int.Parse(entry[1], CultureInfo.InvariantCulture);
region.y = int.Parse(entry[2], CultureInfo.InvariantCulture);
});
regionFields.Add("size", () => { // Deprecated, use bounds.
region.width = int.Parse(entry[1], CultureInfo.InvariantCulture);
region.height = int.Parse(entry[2], CultureInfo.InvariantCulture);
});
regionFields.Add("bounds", () => {
region.x = int.Parse(entry[1], CultureInfo.InvariantCulture);
region.y = int.Parse(entry[2], CultureInfo.InvariantCulture);
region.width = int.Parse(entry[3], CultureInfo.InvariantCulture);
region.height = int.Parse(entry[4], CultureInfo.InvariantCulture);
});
regionFields.Add("offset", () => { // Deprecated, use offsets.
region.offsetX = int.Parse(entry[1], CultureInfo.InvariantCulture);
region.offsetY = int.Parse(entry[2], CultureInfo.InvariantCulture);
});
regionFields.Add("orig", () => { // Deprecated, use offsets.
region.originalWidth = int.Parse(entry[1], CultureInfo.InvariantCulture);
region.originalHeight = int.Parse(entry[2], CultureInfo.InvariantCulture);
});
regionFields.Add("offsets", () => {
region.offsetX = int.Parse(entry[1], CultureInfo.InvariantCulture);
region.offsetY = int.Parse(entry[2], CultureInfo.InvariantCulture);
region.originalWidth = int.Parse(entry[3], CultureInfo.InvariantCulture);
region.originalHeight = int.Parse(entry[4], CultureInfo.InvariantCulture);
});
regionFields.Add("rotate", () => {
string value = entry[1];
if (value == "true")
region.degrees = 90;
else if (value != "false")
region.degrees = int.Parse(value, CultureInfo.InvariantCulture);
});
regionFields.Add("index", () => {
region.index = int.Parse(entry[1], CultureInfo.InvariantCulture);
});
string line = reader.ReadLine();
// Ignore empty lines before first entry.
while (line != null && line.Trim().Length == 0)
line = reader.ReadLine();
// Header entries.
while (true) {
if (line == null || line.Trim().Length == 0) break;
if (ReadEntry(entry, line) == 0) break; // Silently ignore all header fields.
line = reader.ReadLine();
}
// Page and region entries.
List<string> names = null;
List<int[]> values = null;
while (true) {
string line = reader.ReadLine();
if (line == null) break;
if (line.Trim().Length == 0)
if (line.Trim().Length == 0) {
page = null;
else if (page == null) {
line = reader.ReadLine();
} else if (page == null) {
page = new AtlasPage();
page.name = line;
if (ReadTuple(reader, tuple) == 2) { // size is only optional for an atlas packed with an old TexturePacker.
page.width = int.Parse(tuple[0], CultureInfo.InvariantCulture);
page.height = int.Parse(tuple[1], CultureInfo.InvariantCulture);
ReadTuple(reader, tuple);
page.name = line.Trim();
while (true) {
if (ReadEntry(entry, line = reader.ReadLine()) == 0) break;
Action field;
if (pageFields.TryGetValue(entry[0], out field)) field(); // Silently ignore unknown page fields.
}
page.format = (Format)Enum.Parse(typeof(Format), tuple[0], false);
ReadTuple(reader, tuple);
page.minFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), tuple[0], false);
page.magFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), tuple[1], false);
string direction = ReadValue(reader);
page.uWrap = TextureWrap.ClampToEdge;
page.vWrap = TextureWrap.ClampToEdge;
if (direction == "x")
page.uWrap = TextureWrap.Repeat;
else if (direction == "y")
page.vWrap = TextureWrap.Repeat;
else if (direction == "xy")
page.uWrap = page.vWrap = TextureWrap.Repeat;
textureLoader.Load(page, Path.Combine(imagesDir, line));
textureLoader.Load(page, Path.Combine(imagesDir, page.name));
pages.Add(page);
} else {
AtlasRegion region = new AtlasRegion();
region.name = line;
region = new AtlasRegion();
region.page = page;
string rotateValue = ReadValue(reader);
if (rotateValue == "true")
region.degrees = 90;
else if (rotateValue == "false")
region.degrees = 0;
else
region.degrees = int.Parse(rotateValue);
region.rotate = region.degrees == 90;
ReadTuple(reader, tuple);
int x = int.Parse(tuple[0], CultureInfo.InvariantCulture);
int y = int.Parse(tuple[1], CultureInfo.InvariantCulture);
ReadTuple(reader, tuple);
int width = int.Parse(tuple[0], CultureInfo.InvariantCulture);
int height = int.Parse(tuple[1], CultureInfo.InvariantCulture);
region.u = x / (float)page.width;
region.v = y / (float)page.height;
if (region.rotate) {
region.u2 = (x + height) / (float)page.width;
region.v2 = (y + width) / (float)page.height;
} else {
region.u2 = (x + width) / (float)page.width;
region.v2 = (y + height) / (float)page.height;
}
region.x = x;
region.y = y;
region.width = Math.Abs(width);
region.height = Math.Abs(height);
if (ReadTuple(reader, tuple) == 4) { // split is optional
region.splits = new [] {int.Parse(tuple[0], CultureInfo.InvariantCulture),
int.Parse(tuple[1], CultureInfo.InvariantCulture),
int.Parse(tuple[2], CultureInfo.InvariantCulture),
int.Parse(tuple[3], CultureInfo.InvariantCulture)};
if (ReadTuple(reader, tuple) == 4) { // pad is optional, but only present with splits
region.pads = new [] {int.Parse(tuple[0], CultureInfo.InvariantCulture),
int.Parse(tuple[1], CultureInfo.InvariantCulture),
int.Parse(tuple[2], CultureInfo.InvariantCulture),
int.Parse(tuple[3], CultureInfo.InvariantCulture)};
ReadTuple(reader, tuple);
region.name = line;
while (true) {
int count = ReadEntry(entry, line = reader.ReadLine());
if (count == 0) break;
Action field;
if (regionFields.TryGetValue(entry[0], out field))
field();
else {
if (names == null) {
names = new List<string>(8);
values = new List<int[]>(8);
}
names.Add(entry[0]);
int[] entryValues = new int[count];
for (int i = 0; i < count; i++)
int.TryParse(entry[i + 1], NumberStyles.Any, CultureInfo.InvariantCulture, out entryValues[i]); // Silently ignore non-integer values.
values.Add(entryValues);
}
}
region.originalWidth = int.Parse(tuple[0], CultureInfo.InvariantCulture);
region.originalHeight = int.Parse(tuple[1], CultureInfo.InvariantCulture);
ReadTuple(reader, tuple);
region.offsetX = int.Parse(tuple[0], CultureInfo.InvariantCulture);
region.offsetY = int.Parse(tuple[1], CultureInfo.InvariantCulture);
region.index = int.Parse(ReadValue(reader), CultureInfo.InvariantCulture);
if (region.originalWidth == 0 && region.originalHeight == 0) {
region.originalWidth = region.width;
region.originalHeight = region.height;
}
if (names != null && names.Count > 0) {
region.names = names.ToArray();
region.values = values.ToArray();
names.Clear();
values.Clear();
}
region.u = region.x / (float)page.width;
region.v = region.y / (float)page.height;
if (region.degrees == 90) {
region.u2 = (region.x + region.height) / (float)page.width;
region.v2 = (region.y + region.width) / (float)page.height;
} else {
region.u2 = (region.x + region.width) / (float)page.width;
region.v2 = (region.y + region.height) / (float)page.height;
}
regions.Add(region);
}
}
}
static string ReadValue (TextReader reader) {
string line = reader.ReadLine();
static private int ReadEntry (string[] entry, string line) {
if (line == null) return 0;
line = line.Trim();
if (line.Length == 0) return 0;
int colon = line.IndexOf(':');
if (colon == -1) throw new Exception("Invalid line: " + line);
return line.Substring(colon + 1).Trim();
}
/// <summary>Returns the number of tuple values read (1, 2 or 4).</summary>
static int ReadTuple (TextReader reader, string[] tuple) {
string line = reader.ReadLine();
int colon = line.IndexOf(':');
if (colon == -1) throw new Exception("Invalid line: " + line);
int i = 0, lastMatch = colon + 1;
for (; i < 3; i++) {
if (colon == -1) return 0;
entry[0] = line.Substring(0, colon).Trim();
for (int i = 1, lastMatch = colon + 1;; i++) {
int comma = line.IndexOf(',', lastMatch);
if (comma == -1) break;
tuple[i] = line.Substring(lastMatch, comma - lastMatch).Trim();
if (comma == -1) {
entry[i] = line.Substring(lastMatch).Trim();
return i;
}
entry[i] = line.Substring(lastMatch, comma - lastMatch).Trim();
lastMatch = comma + 1;
if (i == 4) return 4;
}
tuple[i] = line.Substring(lastMatch).Trim();
return i + 1;
}
public void FlipV () {
@ -292,13 +319,14 @@ namespace Spine {
public class AtlasPage {
public string name;
public Format format;
public TextureFilter minFilter;
public TextureFilter magFilter;
public TextureWrap uWrap;
public TextureWrap vWrap;
public object rendererObject;
public int width, height;
public Format format = Format.RGBA8888;
public TextureFilter minFilter = TextureFilter.Nearest;
public TextureFilter magFilter = TextureFilter.Nearest;
public TextureWrap uWrap = TextureWrap.ClampToEdge;
public TextureWrap vWrap = TextureWrap.ClampToEdge;
public bool pma;
public object rendererObject;
public AtlasPage Clone () {
return MemberwiseClone() as AtlasPage;
@ -312,11 +340,11 @@ namespace Spine {
public float u, v, u2, v2;
public float offsetX, offsetY;
public int originalWidth, originalHeight;
public int index;
public bool rotate;
public int degrees;
public int[] splits;
public int[] pads;
public bool rotate;
public int index;
public string[] names;
public int[][] values;
public AtlasRegion Clone () {
return MemberwiseClone() as AtlasRegion;

View File

@ -48,7 +48,7 @@ namespace Spine {
if (region == null) throw new ArgumentException(string.Format("Region not found in atlas: {0} (region attachment: {1})", path, name));
RegionAttachment attachment = new RegionAttachment(name);
attachment.RendererObject = region;
attachment.SetUVs(region.u, region.v, region.u2, region.v2, region.rotate);
attachment.SetUVs(region.u, region.v, region.u2, region.v2, region.degrees);
attachment.regionOffsetX = region.offsetX;
attachment.regionOffsetY = region.offsetY;
attachment.regionWidth = region.width;
@ -67,7 +67,6 @@ namespace Spine {
attachment.RegionV = region.v;
attachment.RegionU2 = region.u2;
attachment.RegionV2 = region.v2;
attachment.RegionRotate = region.rotate;
attachment.RegionDegrees = region.degrees;
attachment.regionOffsetX = region.offsetX;
attachment.regionOffsetY = region.offsetY;

View File

@ -56,7 +56,6 @@ namespace Spine {
public float RegionV { get; set; }
public float RegionU2 { get; set; }
public float RegionV2 { get; set; }
public bool RegionRotate { get; set; }
public int RegionDegrees { get; set; }
public float RegionOffsetX { get { return regionOffsetX; } set { regionOffsetX = value; } }
public float RegionOffsetY { get { return regionOffsetY; } set { regionOffsetY = value; } } // Pixels stripped from the bottom left, unrotated.
@ -160,7 +159,6 @@ namespace Spine {
copy.regionHeight = regionHeight;
copy.regionOriginalWidth = regionOriginalWidth;
copy.regionOriginalHeight = regionOriginalHeight;
copy.RegionRotate = RegionRotate;
copy.RegionDegrees = RegionDegrees;
copy.RegionU = RegionU;
copy.RegionV = RegionV;
@ -203,7 +201,6 @@ namespace Spine {
mesh.regionOriginalWidth = regionOriginalWidth;
mesh.regionOriginalHeight = regionOriginalHeight;
mesh.RegionDegrees = RegionDegrees;
mesh.RegionRotate = RegionRotate;
mesh.RegionU = RegionU;
mesh.RegionV = RegionV;
mesh.RegionU2 = RegionU2;

View File

@ -118,10 +118,10 @@ namespace Spine {
offset[BRY] = localYCos + localX2Sin;
}
public void SetUVs (float u, float v, float u2, float v2, bool rotate) {
public void SetUVs (float u, float v, float u2, float v2, int degrees) {
float[] uvs = this.uvs;
// UV values differ from RegionAttachment.java
if (rotate) {
// UV values differ from spine-libgdx.
if (degrees == 90) {
uvs[URX] = u;
uvs[URY] = v2;
uvs[BRX] = u;

View File

@ -73,7 +73,7 @@ public class RegionAttachment extends Attachment {
AtlasRegion region = (AtlasRegion)this.region;
localX += region.offsetX / region.originalWidth * width;
localY += region.offsetY / region.originalHeight * height;
if (region.rotate) {
if (region.degrees == 90) {
localX2 -= (region.originalWidth - region.offsetX - region.packedHeight) / region.originalWidth * width;
localY2 -= (region.originalHeight - region.offsetY - region.packedWidth) / region.originalHeight * height;
} else {
@ -115,7 +115,7 @@ public class RegionAttachment extends Attachment {
if (region == null) throw new IllegalArgumentException("region cannot be null.");
this.region = region;
float[] uvs = this.uvs;
if (region instanceof AtlasRegion && ((AtlasRegion)region).rotate) {
if (region instanceof AtlasRegion && ((AtlasRegion)region).degrees == 90) {
uvs[URX] = region.getU();
uvs[URY] = region.getV2();
uvs[BRX] = region.getU();

View File

@ -342,7 +342,7 @@ namespace Spine.Unity.Editor {
if (pageMatch) {
Rect spriteRect = new Rect();
if (r.rotate) {
if (r.degrees == 90) {
spriteRect.width = r.height;
spriteRect.height = r.width;
} else {

View File

@ -178,16 +178,16 @@ namespace Spine.Unity {
u2 = region.u2;
v2 = region.v2;
if (!region.rotate) {
uvs[0] = new Vector2(u, v2);
uvs[1] = new Vector2(u, v);
uvs[2] = new Vector2(u2, v);
uvs[3] = new Vector2(u2, v2);
} else {
if (region.degrees == 90) {
uvs[0] = new Vector2(u2, v2);
uvs[1] = new Vector2(u, v2);
uvs[2] = new Vector2(u, v);
uvs[3] = new Vector2(u2, v);
} else {
uvs[0] = new Vector2(u, v2);
uvs[1] = new Vector2(u, v);
uvs[2] = new Vector2(u2, v);
uvs[3] = new Vector2(u2, v2);
}
mesh.triangles = new int[0];

View File

@ -132,7 +132,6 @@ namespace Spine.Unity {
var page = region.page;
region.degrees = savedRegion.packingRotation == SpritePackingRotation.None ? 0 : 90;
region.rotate = region.degrees != 0;
float x = savedRegion.x;
float y = savedRegion.y;
@ -141,11 +140,10 @@ namespace Spine.Unity {
region.u = x / (float)page.width;
region.v = y / (float)page.height;
if (region.rotate) {
if (region.degrees == 90) {
region.u2 = (x + height) / (float)page.width;
region.v2 = (y + width) / (float)page.height;
}
else {
} else {
region.u2 = (x + width) / (float)page.width;
region.v2 = (y + height) / (float)page.height;
}
@ -216,7 +214,6 @@ namespace Spine.Unity {
region.name = sprite.name.Replace("(Clone)", "");
region.page = page;
region.degrees = sprite.packingRotation == SpritePackingRotation.None ? 0 : 90;
region.rotate = region.degrees != 0;
region.u2 = 1;
region.v2 = 1;

View File

@ -74,8 +74,6 @@ namespace Spine.Unity.AttachmentTools {
var region = new AtlasRegion();
region.name = t.name;
region.index = -1;
region.rotate = false;
// World space units
Vector2 boundsMin = Vector2.zero, boundsMax = new Vector2(width, height) * scale;
@ -193,7 +191,7 @@ namespace Spine.Unity.AttachmentTools {
var region = new AtlasRegion();
region.name = s.name;
region.index = -1;
region.rotate = s.packed && s.packingRotation != SpritePackingRotation.None;
region.degrees = s.packed && s.packingRotation != SpritePackingRotation.None ? 90 : 0;
// World space units
Bounds bounds = s.bounds;
@ -648,7 +646,7 @@ namespace Spine.Unity.AttachmentTools {
/// <summary>
/// Returns a Rect of the AtlasRegion according to Spine texture coordinates. (x-right, y-down)</summary>
static Rect GetSpineAtlasRect (this AtlasRegion region, bool includeRotate = true) {
if (includeRotate && region.rotate)
if (includeRotate && region.degrees == 90)
return new Rect(region.x, region.y, region.height, region.width);
else
return new Rect(region.x, region.y, region.width, region.height);
@ -682,7 +680,7 @@ namespace Spine.Unity.AttachmentTools {
int x = (int)rr.x, y = (int)rr.y;
int w, h;
if (referenceRegion.rotate) {
if (referenceRegion.degrees == 90) {
w = (int)rr.height;
h = (int)rr.width;
} else {
@ -715,7 +713,7 @@ namespace Spine.Unity.AttachmentTools {
x = x,
y = y,
rotate = referenceRegion.rotate
degrees = referenceRegion.degrees
};
}

View File

@ -52,7 +52,7 @@ namespace Spine.Unity.AttachmentTools {
// (AtlasAttachmentLoader.cs)
attachment.RendererObject = region;
attachment.SetUVs(region.u, region.v, region.u2, region.v2, region.rotate);
attachment.SetUVs(region.u, region.v, region.u2, region.v2, region.degrees);
attachment.regionOffsetX = region.offsetX;
attachment.regionOffsetY = region.offsetY;
attachment.regionWidth = region.width;
@ -73,7 +73,7 @@ namespace Spine.Unity.AttachmentTools {
attachment.RegionV = region.v;
attachment.RegionU2 = region.u2;
attachment.RegionV2 = region.v2;
attachment.RegionRotate = region.rotate;
attachment.RegionDegrees = region.degrees;
attachment.regionOffsetX = region.offsetX;
attachment.regionOffsetY = region.offsetY;
attachment.regionWidth = region.width;
@ -126,7 +126,7 @@ namespace Spine.Unity.AttachmentTools {
var attachment = new RegionAttachment(attachmentName);
attachment.RendererObject = region;
attachment.SetUVs(region.u, region.v, region.u2, region.v2, region.rotate);
attachment.SetUVs(region.u, region.v, region.u2, region.v2, region.degrees);
attachment.regionOffsetX = region.offsetX;
attachment.regionOffsetY = region.offsetY;
attachment.regionWidth = region.width;