Răsfoiți Sursa

Refactoring for multi page atlas support.

NathanSweet 12 ani în urmă
părinte
comite
143f99a81f

+ 106 - 109
spine-csharp/src/Atlas.cs

@@ -28,125 +28,111 @@ using System.IO;
 
 namespace Spine {
 	public class Atlas {
-		public Format Format;
-		public TextureFilter MinFilter;
-		public TextureFilter MagFilter;
-		public TextureWrap UWrap;
-		public TextureWrap VWrap;
-		public int TextureWidth;
-		public int TextureHeight;
-		public List<AtlasRegion> Regions;
-		public Object Texture;
-
-		public Atlas (String path, Object texture, int textureWidth, int textureHeight) {
+		List<AtlasPage> pages = new List<AtlasPage>();
+		List<AtlasRegion> regions = new List<AtlasRegion>();
+
+		public Atlas (String path, TextureLoader textureLoader) {
 			using (StreamReader reader = new StreamReader(path)) {
 				try {
-					initialize(reader, texture, textureWidth, textureHeight);
+					Load(reader, Path.GetDirectoryName(path), textureLoader);
 				} catch (Exception ex) {
 					throw new Exception("Error reading atlas file: " + path, ex);
 				}
 			}
 		}
 
-		public Atlas (TextReader reader, Object texture, int textureWidth, int textureHeight) {
-			initialize(reader, texture, textureWidth, textureHeight);
+		public Atlas (TextReader reader, String dir, TextureLoader textureLoader) {
+			Load(reader, dir, textureLoader);
 		}
 
-		private void initialize (TextReader reader, Object texture, int textureWidth, int textureHeight) {
-			TextureWidth = textureWidth;
-			TextureHeight = textureHeight;
-			Texture = texture;
+		private void Load (TextReader reader, String imagesDir, TextureLoader textureLoader) {
+			if (textureLoader == null) throw new ArgumentNullException("textureLoader cannot be null.");
 
-			Regions = new List<AtlasRegion>();
-			float invTexWidth = 1f / textureWidth;
-			float invTexHeight = 1f / textureHeight;
 			String[] tuple = new String[4];
-
-			// Skip past first page name.
-			while (true) {
-				String line = reader.ReadLine();
-				if (line.Trim().Length != 0)
-					break;
-			}
-
-			Format = (Format)Enum.Parse(typeof(Format), readValue(reader), false);
-
-			readTuple(reader, tuple);
-			MinFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), tuple[0]);
-			MagFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), tuple[1]);
-
-			String direction = readValue(reader);
-			UWrap = TextureWrap.ClampToEdge;
-			VWrap = TextureWrap.ClampToEdge;
-			if (direction == "x")
-				UWrap = TextureWrap.Repeat;
-			else if (direction == "y")
-				VWrap = TextureWrap.Repeat;
-			else if (direction == "xy")
-				UWrap = VWrap = TextureWrap.Repeat;
-
+			AtlasPage page = null;
 			while (true) {
 				String line = reader.ReadLine();
-				if (line == null || line.Trim().Length == 0) break;
-
-				AtlasRegion region = new AtlasRegion();
-				region.Atlas = this;
-				region.Name = line;
-
-				region.Rotate = Boolean.Parse(readValue(reader));
-
-				readTuple(reader, tuple);
-				int x = int.Parse(tuple[0]);
-				int y = int.Parse(tuple[1]);
-
-				readTuple(reader, tuple);
-				int width = int.Parse(tuple[0]);
-				int height = int.Parse(tuple[1]);
-
-				region.U = x * invTexWidth;
-				region.V = y * invTexHeight;
-				region.U2 = (x + width) * invTexWidth;
-				region.V2 = (y + height) * invTexHeight;
-				region.Width = Math.Abs(width);
-				region.Height = Math.Abs(height);
-
-				if (readTuple(reader, tuple) == 4) { // split is optional
-					region.Splits = new int[] {int.Parse(tuple[0]), int.Parse(tuple[1]),
+				if (line == null) break;
+				if (line.Trim().Length == 0)
+					page = null;
+				else if (page == null) {
+					page = new AtlasPage();
+					page.name = line;
+
+					page.format = (Format)Enum.Parse(typeof(Format), readValue(reader), false);
+
+					readTuple(reader, tuple);
+					page.minFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), tuple[0]);
+					page.magFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), tuple[1]);
+
+					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));
+
+					pages.Add(page);
+
+				} else {
+					AtlasRegion region = new AtlasRegion();
+					region.name = line;
+					region.page = page;
+
+					region.rotate = Boolean.Parse(readValue(reader));
+
+					readTuple(reader, tuple);
+					int x = int.Parse(tuple[0]);
+					int y = int.Parse(tuple[1]);
+
+					readTuple(reader, tuple);
+					int width = int.Parse(tuple[0]);
+					int height = int.Parse(tuple[1]);
+
+					region.u = x / (float)page.width;
+					region.v = y / (float)page.height;
+					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[] {int.Parse(tuple[0]), int.Parse(tuple[1]),
 								int.Parse(tuple[2]), int.Parse(tuple[3])};
 
-					if (readTuple(reader, tuple) == 4) { // pad is optional, but only present with splits
-						region.Pads = new int[] {int.Parse(tuple[0]), int.Parse(tuple[1]),
+						if (readTuple(reader, tuple) == 4) { // pad is optional, but only present with splits
+							region.pads = new int[] {int.Parse(tuple[0]), int.Parse(tuple[1]),
 									int.Parse(tuple[2]), int.Parse(tuple[3])};
 
-						readTuple(reader, tuple);
+							readTuple(reader, tuple);
+						}
 					}
-				}
 
-				region.OriginalWidth = int.Parse(tuple[0]);
-				region.OriginalHeight = int.Parse(tuple[1]);
+					region.originalWidth = int.Parse(tuple[0]);
+					region.originalHeight = int.Parse(tuple[1]);
 
-				readTuple(reader, tuple);
-				region.OffsetX = int.Parse(tuple[0]);
-				region.OffsetY = int.Parse(tuple[1]);
+					readTuple(reader, tuple);
+					region.offsetX = int.Parse(tuple[0]);
+					region.offsetY = int.Parse(tuple[1]);
 
-				region.Index = int.Parse(readValue(reader));
+					region.index = int.Parse(readValue(reader));
 
-				Regions.Add(region);
-			}
-
-			while (true) {
-				String line = reader.ReadLine();
-				if (line == null)
-					break;
-				if (line.Trim().Length != 0) throw new Exception("An atlas with multiple images is not supported.");
+					regions.Add(region);
+				}
 			}
 		}
 
 		static String readValue (TextReader reader) {
 			String line = reader.ReadLine();
 			int colon = line.IndexOf(':');
-			if (colon == -1)
-				throw new Exception("Invalid line: " + line);
+			if (colon == -1) throw new Exception("Invalid line: " + line);
 			return line.Substring(colon + 1).Trim();
 		}
 
@@ -154,14 +140,12 @@ namespace Spine {
 		static int readTuple (TextReader reader, String[] tuple) {
 			String line = reader.ReadLine();
 			int colon = line.IndexOf(':');
-			if (colon == -1)
-				throw new Exception("Invalid line: " + line);
+			if (colon == -1) throw new Exception("Invalid line: " + line);
 			int i = 0, lastMatch = colon + 1;
-			for (i = 0; i < 3; i++) {
+			for (; i < 3; i++) {
 				int comma = line.IndexOf(',', lastMatch);
 				if (comma == -1) {
-					if (i == 0)
-						throw new Exception("Invalid line: " + line);
+					if (i == 0) throw new Exception("Invalid line: " + line);
 					break;
 				}
 				tuple[i] = line.Substring(lastMatch, comma - lastMatch).Trim();
@@ -175,9 +159,8 @@ namespace Spine {
 		 * should be cached rather than calling this method multiple times.
 		 * @return The region, or null. */
 		public AtlasRegion FindRegion (String name) {
-			for (int i = 0, n = Regions.Count; i < n; i++)
-				if (Regions[i].Name == name)
-					return Regions[i];
+			for (int i = 0, n = regions.Count; i < n; i++)
+				if (regions[i].name == name) return regions[i];
 			return null;
 		}
 	}
@@ -208,17 +191,31 @@ namespace Spine {
 		Repeat
 	}
 
+	public class AtlasPage {
+		public String name;
+		public Format format;
+		public TextureFilter minFilter;
+		public TextureFilter magFilter;
+		public TextureWrap uWrap;
+		public TextureWrap vWrap;
+		public Object texture;
+		public int width, height;
+	}
+
 	public class AtlasRegion {
-		public Atlas Atlas;
-		public float U, V;
-		public float U2, V2;
-		public int Width, Height;
-		public int Index;
-		public String Name;
-		public float OffsetX, OffsetY;
-		public int OriginalWidth, OriginalHeight;
-		public bool Rotate;
-		public int[] Splits;
-		public int[] Pads;
+		public AtlasPage page;
+		public String name;
+		public int x, y, width, height;
+		public float u, v, u2, v2;
+		public float offsetX, offsetY;
+		public int originalWidth, originalHeight;
+		public int index;
+		public bool rotate;
+		public int[] splits;
+		public int[] pads;
+	}
+
+	public interface TextureLoader {
+		void Load (AtlasPage page, String path);
 	}
 }

+ 10 - 41
spine-csharp/src/Attachments/RegionAttachment.cs

@@ -47,43 +47,12 @@ namespace Spine {
 
 		public float[] Offset { get; private set; }
 		public float[] Vertices { get; private set; }
-		public float[] UVs { get; private set; }
-
-		private AtlasRegion region;
-		public AtlasRegion Region {
-			get {
-				return region;
-			}
-			set {
-				region = value;
-				float[] uvs = UVs;
-				if (value.Rotate) {
-					uvs[X2] = value.U;
-					uvs[Y2] = value.V2;
-					uvs[X3] = value.U;
-					uvs[Y3] = value.V;
-					uvs[X4] = value.U2;
-					uvs[Y4] = value.V;
-					uvs[X1] = value.U2;
-					uvs[Y1] = value.V2;
-				} else {
-					uvs[X1] = value.U;
-					uvs[Y1] = value.V2;
-					uvs[X2] = value.U;
-					uvs[Y2] = value.V;
-					uvs[X3] = value.U2;
-					uvs[Y3] = value.V;
-					uvs[X4] = value.U2;
-					uvs[Y4] = value.V2;
-				}
-			}
-		}
+		public AtlasRegion Region { get; set; }
 
 		public RegionAttachment (string name)
 			: base(name) {
 			Offset = new float[8];
 			Vertices = new float[8];
-			UVs = new float[8];
 			ScaleX = 1;
 			ScaleY = 1;
 		}
@@ -97,16 +66,16 @@ namespace Spine {
 			float localY = -localY2;
 			AtlasRegion region = Region;
 			if (region != null) {
-				if (region.Rotate) {
-					localX += region.OffsetX / region.OriginalWidth * height;
-					localY += region.OffsetY / region.OriginalHeight * width;
-					localX2 -= (region.OriginalWidth - region.OffsetX - region.Height) / region.OriginalWidth * width;
-					localY2 -= (region.OriginalHeight - region.OffsetY - region.Width) / region.OriginalHeight * height;
+				if (region.rotate) {
+					localX += region.offsetX / region.originalWidth * height;
+					localY += region.offsetY / region.originalHeight * width;
+					localX2 -= (region.originalWidth - region.offsetX - region.height) / region.originalWidth * width;
+					localY2 -= (region.originalHeight - region.offsetY - region.width) / region.originalHeight * height;
 				} else {
-					localX += region.OffsetX / region.OriginalWidth * width;
-					localY += region.OffsetY / region.OriginalHeight * height;
-					localX2 -= (region.OriginalWidth - region.OffsetX - region.Width) / region.OriginalWidth * width;
-					localY2 -= (region.OriginalHeight - region.OffsetY - region.Height) / region.OriginalHeight * height;
+					localX += region.offsetX / region.originalWidth * width;
+					localY += region.offsetY / region.originalHeight * height;
+					localX2 -= (region.originalWidth - region.offsetX - region.width) / region.originalWidth * width;
+					localY2 -= (region.originalHeight - region.offsetY - region.height) / region.originalHeight * height;
 				}
 			}
 			float scaleX = ScaleX;

+ 1 - 0
spine-csharp/src/SkeletonJson.cs

@@ -47,6 +47,7 @@ namespace Spine {
 		}
 
 		public SkeletonJson (AttachmentLoader attachmentLoader) {
+			if (attachmentLoader == null) throw new ArgumentNullException("attachmentLoader cannot be null.");
 			this.attachmentLoader = attachmentLoader;
 			Scale = 1;
 		}

+ 14 - 2
spine-unity/Assets/Plugins/Spine/AtlasAsset.cs

@@ -22,7 +22,6 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  ******************************************************************************/
-
 using System;
 using System.IO;
 using UnityEngine;
@@ -55,7 +54,7 @@ public class AtlasAsset : ScriptableObject {
 			return atlas;
 
 		try {
-			atlas = new Atlas(new StringReader(atlasFile.text), material, material.mainTexture.width, material.mainTexture.height);
+			atlas = new Atlas(new StringReader(atlasFile.text), "", new SingleTextureLoader(material));
 			return atlas;
 		} catch (Exception) {
 			Debug.LogException(new Exception("Error reading atlas file for atlas asset: " + name), this);
@@ -63,3 +62,16 @@ public class AtlasAsset : ScriptableObject {
 		}
 	}
 }
+
+public class SingleTextureLoader : TextureLoader {
+	Material material;
+	
+	public SingleTextureLoader (Material material) {
+		this.material = material;
+	}
+	
+	public void Load (AtlasPage page, String path) {
+		page.width = material.mainTexture.width;
+		page.height = material.mainTexture.height;
+	}
+}

+ 13 - 7
spine-unity/Assets/Plugins/Spine/SkeletonComponent.cs

@@ -22,7 +22,6 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  ******************************************************************************/
-
 using System;
 using System.IO;
 using System.Collections.Generic;
@@ -122,12 +121,19 @@ public class SkeletonComponent : MonoBehaviour {
 				vertices[vertexIndex + 1] = new Vector3(regionVertices[RegionAttachment.X4], regionVertices[RegionAttachment.Y4], 0);
 				vertices[vertexIndex + 2] = new Vector3(regionVertices[RegionAttachment.X2], regionVertices[RegionAttachment.Y2], 0);
 				vertices[vertexIndex + 3] = new Vector3(regionVertices[RegionAttachment.X3], regionVertices[RegionAttachment.Y3], 0);
-
-				float[] regionUVs = regionAttachment.UVs;
-				uvs[vertexIndex] = new Vector2(regionUVs[RegionAttachment.X1], 1 - regionUVs[RegionAttachment.Y1]);
-				uvs[vertexIndex + 1] = new Vector2(regionUVs[RegionAttachment.X4], 1 - regionUVs[RegionAttachment.Y4]);
-				uvs[vertexIndex + 2] = new Vector2(regionUVs[RegionAttachment.X2], 1 - regionUVs[RegionAttachment.Y2]);
-				uvs[vertexIndex + 3] = new Vector2(regionUVs[RegionAttachment.X3], 1 - regionUVs[RegionAttachment.Y3]);
+				
+				AtlasRegion region = regionAttachment.Region;
+				if (region.rotate) {
+					uvs[vertexIndex + 1] = new Vector2(region.u, 1 - region.v2);
+					uvs[vertexIndex + 2] = new Vector2(region.u2, 1 - region.v2);
+					uvs[vertexIndex + 3] = new Vector2(region.u, 1 - region.v);
+					uvs[vertexIndex] = new Vector2(region.u2, 1 - region.v);
+				} else {
+					uvs[vertexIndex] = new Vector2(region.u, 1 - region.v2);
+					uvs[vertexIndex + 1] = new Vector2(region.u2, 1 - region.v2);
+					uvs[vertexIndex + 2] = new Vector2(region.u, 1 - region.v);
+					uvs[vertexIndex + 3] = new Vector2(region.u2, 1 - region.v);
+				}
 
 				int index = quadIndex * 6;
 				triangles[index] = vertexIndex;

BIN
spine-unity/Assets/examples/data/spineboy.mat


BIN
spine-unity/Assets/examples/spineboy/spineboy.unity


+ 1 - 2
spine-xna/example/src/ExampleGame.cs

@@ -59,8 +59,7 @@ namespace Spine {
 		protected override void LoadContent () {
 			skeletonRenderer = new SkeletonRenderer(GraphicsDevice);
 
-			Texture2D texture = Util.LoadTexture(GraphicsDevice, "data/goblins.png");
-			Atlas atlas = new Atlas("data/goblins.atlas", texture, texture.Width, texture.Height);
+			Atlas atlas = new Atlas("data/goblins.atlas", new XnaTextureLoader(GraphicsDevice));
 			SkeletonJson json = new SkeletonJson(atlas);
 			skeleton = new Skeleton(json.ReadSkeletonData("data/goblins.json"));
 			skeleton.SetSkin("goblingirl");

+ 1 - 0
spine-xna/spine-xna.csproj

@@ -109,6 +109,7 @@
   </ItemGroup>
   <ItemGroup>
     <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="src\XnaTextureLoader.cs" />
     <Compile Include="src\Util.cs" />
     <Compile Include="src\SkeletonRenderer.cs" />
     <Compile Include="src\SpriteBatcher.cs" />

+ 25 - 14
spine-xna/src/SkeletonRenderer.cs

@@ -78,12 +78,12 @@ namespace Spine {
 					RegionAttachment regionAttachment = (RegionAttachment)attachment;
 
 					SpriteBatchItem item = batcher.CreateBatchItem();
-					item.Texture = (Texture2D)regionAttachment.Region.Atlas.Texture;
+					item.Texture = (Texture2D)regionAttachment.Region.page.texture;
 
-					byte r = (byte)(slot.R * 255);
-					byte g = (byte)(slot.G * 255);
-					byte b = (byte)(slot.B * 255);
-					byte a = (byte)(slot.A * 255);
+					byte r = (byte)(skeleton.R * slot.R * 255);
+					byte g = (byte)(skeleton.G * slot.G * 255);
+					byte b = (byte)(skeleton.B * slot.B * 255);
+					byte a = (byte)(skeleton.A * slot.A * 255);
 					item.vertexTL.Color.R = r;
 					item.vertexTL.Color.G = g;
 					item.vertexTL.Color.B = b;
@@ -116,15 +116,26 @@ namespace Spine {
 					item.vertexTR.Position.Y = vertices[RegionAttachment.Y4];
 					item.vertexTR.Position.Z = 0;
 
-					float[] uvs = regionAttachment.UVs;
-					item.vertexTL.TextureCoordinate.X = uvs[RegionAttachment.X1];
-					item.vertexTL.TextureCoordinate.Y = uvs[RegionAttachment.Y1];
-					item.vertexBL.TextureCoordinate.X = uvs[RegionAttachment.X2];
-					item.vertexBL.TextureCoordinate.Y = uvs[RegionAttachment.Y2];
-					item.vertexBR.TextureCoordinate.X = uvs[RegionAttachment.X3];
-					item.vertexBR.TextureCoordinate.Y = uvs[RegionAttachment.Y3];
-					item.vertexTR.TextureCoordinate.X = uvs[RegionAttachment.X4];
-					item.vertexTR.TextureCoordinate.Y = uvs[RegionAttachment.Y4];
+					AtlasRegion region = regionAttachment.Region;
+					if (region.rotate) {
+						item.vertexBL.TextureCoordinate.X = region.u;
+						item.vertexBL.TextureCoordinate.Y = region.v2;
+						item.vertexBR.TextureCoordinate.X = region.u;
+						item.vertexBR.TextureCoordinate.Y = region.v;
+						item.vertexTR.TextureCoordinate.X = region.u2;
+						item.vertexTR.TextureCoordinate.Y = region.v;
+						item.vertexTL.TextureCoordinate.X = region.u2;
+						item.vertexTL.TextureCoordinate.Y = region.v2;
+					} else {
+						item.vertexTL.TextureCoordinate.X = region.u;
+						item.vertexTL.TextureCoordinate.Y = region.v2;
+						item.vertexBL.TextureCoordinate.X = region.u;
+						item.vertexBL.TextureCoordinate.Y = region.v;
+						item.vertexBR.TextureCoordinate.X = region.u2;
+						item.vertexBR.TextureCoordinate.Y = region.v;
+						item.vertexTR.TextureCoordinate.X = region.u2;
+						item.vertexTR.TextureCoordinate.Y = region.v2;
+					}
 				}
 			}
 		}

+ 45 - 0
spine-xna/src/XnaTextureLoader.cs

@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ******************************************************************************/
+
+using System;
+using System.IO;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace Spine {
+	public class XnaTextureLoader : TextureLoader {
+		GraphicsDevice device;
+		public XnaTextureLoader (GraphicsDevice device) {
+			this.device = device;
+		}
+
+		public void Load (AtlasPage page, String path) {
+			Texture2D texture = Util.LoadTexture(device, path);
+			page.texture = texture;
+			page.width = texture.Width;
+			page.height = texture.Height;
+		}
+	}
+}