소스 검색

Bounding boxes for spine-csharp.

NathanSweet 12 년 전
부모
커밋
e1fe518261

+ 2 - 0
spine-csharp/spine-csharp_xna.csproj

@@ -101,11 +101,13 @@
     <Compile Include="src\Attachments\Attachment.cs" />
     <Compile Include="src\Attachments\AttachmentLoader.cs" />
     <Compile Include="src\Attachments\AttachmentType.cs" />
+    <Compile Include="src\Attachments\BoundingBoxAttachment.cs" />
     <Compile Include="src\Attachments\RegionAttachment.cs" />
     <Compile Include="src\Bone.cs" />
     <Compile Include="src\BoneData.cs" />
     <Compile Include="src\Json.cs" />
     <Compile Include="src\Skeleton.cs" />
+    <Compile Include="src\SkeletonBounds.cs" />
     <Compile Include="src\SkeletonData.cs" />
     <Compile Include="src\SkeletonJson.cs" />
     <Compile Include="src\Skin.cs" />

+ 2 - 0
spine-csharp/src/Attachments/AtlasAttachmentLoader.cs

@@ -49,6 +49,8 @@ namespace Spine {
 				attachment.RegionOriginalWidth = region.originalWidth;
 				attachment.RegionOriginalHeight = region.originalHeight;
 				return attachment;
+			case AttachmentType.boundingbox:
+				return new BoundingBoxAttachment(name);
 			}
 			throw new Exception("Unknown attachment type: " + type);
 		}

+ 1 - 1
spine-csharp/src/Attachments/AttachmentType.cs

@@ -25,6 +25,6 @@
 
 namespace Spine {
 	public enum AttachmentType {
-		region, regionSequence
+		region, regionsequence, boundingbox
 	}
 }

+ 54 - 0
spine-csharp/src/Attachments/BoundingBoxAttachment.cs

@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * 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;
+
+namespace Spine {
+	/** Attachment that has a polygon for bounds checking. */
+	public class BoundingBoxAttachment : Attachment {
+		public float[] Vertices { get; set; }
+
+		public BoundingBoxAttachment (string name)
+			: base(name) {
+		}
+
+		/** @param worldVertices Must have at least the same as this attachment's vertices. */
+		public void ComputeWorldVertices (float x, float y, Bone bone, float[] worldVertices) {
+			x += bone.WorldX;
+			y += bone.WorldY;
+			float m00 = bone.M00;
+			float m01 = bone.M01;
+			float m10 = bone.M10;
+			float m11 = bone.M11;
+			float[] vertices = Vertices;
+			for (int i = 0, n = vertices.Length; i < n; i += 2) {
+				float px = vertices[i];
+				float py = vertices[i + 1];
+				worldVertices[i] = px * m00 + py * m01 + x;
+				worldVertices[i + 1] = px * m10 + py * m11 + y;
+			}
+		}
+	}
+}

+ 275 - 0
spine-csharp/src/SkeletonBounds.cs

@@ -0,0 +1,275 @@
+/*******************************************************************************
+ * 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.Collections.Generic;
+
+namespace Spine {
+	public class SkeletonBounds {
+		private bool aabb;
+		private List<Polygon> polygonPool = new List<Polygon>();
+
+		public List<BoundingBoxAttachment> BoundingBoxes { get; private set; }
+		public List<Polygon> Polygons { get; private set; }
+
+		private float minX;
+		public float MinX {
+			get {
+				if (!aabb) aabbCompute();
+				return minX;
+			}
+			private set {
+				minX = value;
+			}
+		}
+
+		private float maxX;
+		public float MaxX {
+			get {
+				if (!aabb) aabbCompute();
+				return maxX;
+			}
+			private set {
+				maxX = value;
+			}
+		}
+
+		private float minY;
+		public float MinY {
+			get {
+				if (!aabb) aabbCompute();
+				return minY;
+			}
+			private set {
+				minY = value;
+			}
+		}
+
+		private float maxY;
+		public float MaxY {
+			get {
+				if (!aabb) aabbCompute();
+				return maxY;
+			}
+			private set {
+				maxY = value;
+			}
+		}
+
+		public float Width {
+			get {
+				if (!aabb) aabbCompute();
+				return maxX - minX;
+			}
+		}
+
+		public float Height {
+			get {
+				if (!aabb) aabbCompute();
+				return maxY - minY;
+			}
+		}
+
+		public SkeletonBounds () {
+			BoundingBoxes = new List<BoundingBoxAttachment>();
+			Polygons = new List<Polygon>();
+		}
+
+		public void Update (Skeleton skeleton) {
+			aabb = false;
+
+			List<BoundingBoxAttachment> boundingBoxes = BoundingBoxes;
+			List<Polygon> polygons = Polygons;
+			List<Slot> slots = skeleton.Slots;
+			int slotCount = slots.Count;
+			float x = skeleton.X, y = skeleton.Y;
+
+			boundingBoxes.Clear();
+			foreach (Polygon polygon in polygons) {
+				polygonPool.Add(polygon);
+			}
+			polygons.Clear();
+
+			for (int i = 0; i < slotCount; i++) {
+				Slot slot = slots[i];
+				BoundingBoxAttachment boundingBox = slot.Attachment as BoundingBoxAttachment;
+				if (boundingBox == null) continue;
+				boundingBoxes.Add(boundingBox);
+
+				Polygon polygon = null;
+				int poolCount = polygonPool.Count;
+				if (poolCount > 0) {
+					polygon = polygonPool[poolCount - 1];
+					polygonPool.RemoveAt(poolCount - 1);
+				} else
+					polygon = new Polygon();
+				polygons.Add(polygon);
+				polygon.Count = boundingBox.Vertices.Length;
+				if (polygon.Vertices == null || polygon.Vertices.Length < polygon.Count) polygon.Vertices = new float[polygon.Count];
+				boundingBox.ComputeWorldVertices(x, y, slot.Bone, polygon.Vertices);
+			}
+		}
+
+		private void aabbCompute () {
+			float minX = int.MaxValue, minY = int.MaxValue, maxX = int.MinValue, maxY = int.MinValue;
+			List<Polygon> polygons = Polygons;
+			for (int i = 0, n = polygons.Count; i < n; i++) {
+				Polygon polygon = polygons[i];
+				float[] vertices = polygon.Vertices;
+				for (int ii = 0, nn = polygon.Count; ii < nn; ii += 2) {
+					float x = vertices[ii];
+					float y = vertices[ii + 1];
+					minX = Math.Min(minX, x);
+					minY = Math.Min(minY, y);
+					maxX = Math.Max(maxX, x);
+					maxY = Math.Max(maxY, y);
+				}
+			}
+			this.minX = minX;
+			this.minY = minY;
+			this.maxX = maxX;
+			this.maxY = maxY;
+			aabb = true;
+		}
+
+
+		/** Returns true if the axis aligned bounding box contains the point. */
+		public bool AabbContainsPoint (float x, float y) {
+			if (!aabb) aabbCompute();
+			return x >= minX && x <= maxX && y >= minY && y <= maxY;
+		}
+
+		/** Returns true if the axis aligned bounding box intersects the line segment. */
+		public bool AabbIntersectsSegment (float x1, float y1, float x2, float y2) {
+			if (!aabb) aabbCompute();
+			float minX = this.minX;
+			float minY = this.minY;
+			float maxX = this.maxX;
+			float maxY = this.maxY;
+			if ((x1 <= minX && x2 <= minX) || (y1 <= minY && y2 <= minY) || (x1 >= maxX && x2 >= maxX) || (y1 >= maxY && y2 >= maxY))
+				return false;
+			float m = (y2 - y1) / (x2 - x1);
+			float y = m * (minX - x1) + y1;
+			if (y > minY && y < maxY) return true;
+			y = m * (maxX - x1) + y1;
+			if (y > minY && y < maxY) return true;
+			float x = (minY - y1) / m + x1;
+			if (x > minX && x < maxX) return true;
+			x = (maxY - y1) / m + x1;
+			if (x > minX && x < maxX) return true;
+			return false;
+		}
+
+		/** Returns true if the axis aligned bounding box intersects the axis aligned bounding box of the specified bounds. */
+		public bool AabbIntersectsSkeleton (SkeletonBounds bounds) {
+			if (!aabb) aabbCompute();
+			if (!bounds.aabb) bounds.aabbCompute();
+			return minX < bounds.maxX && maxX > bounds.minX && minY < bounds.maxY && maxY > bounds.minY;
+		}
+
+		/** Returns true if the bounding box attachment contains the point. */
+		public bool ContainsPoint (int index, float x, float y) {
+			Polygon polygon = Polygons[index];
+			float[] vertices = polygon.Vertices;
+			int nn = polygon.Count;
+
+			int prevIndex = nn - 2;
+			bool inside = false;
+			for (int ii = 0; ii < nn; ii += 2) {
+				float vertexY = vertices[ii + 1];
+				float prevY = vertices[prevIndex + 1];
+				if (vertexY < y && prevY >= y || prevY < y && vertexY >= y) {
+					float vertexX = vertices[ii];
+					if (vertexX + (y - vertexY) / (prevY - vertexY) * (vertices[prevIndex] - vertexX) < x) inside = !inside;
+				}
+				prevIndex = ii;
+			}
+			return inside;
+		}
+
+		/** Returns the first bounding box attachment that contains the point, or null. When doing many checks, it is usually more
+		 * efficient to only call this method if {@link #aabbContainsPoint(float, float)} return true. */
+		public BoundingBoxAttachment ContainsPoint (float x, float y) {
+			List<BoundingBoxAttachment> boundingBoxes = BoundingBoxes;
+			for (int i = 0, n = boundingBoxes.Count; i < n; i++)
+				if (ContainsPoint(i, x, y)) return boundingBoxes[i];
+			return null;
+		}
+
+		/** Returns true if the bounding box attachment contains the point. The bounding box must be in the SkeletonBounds. */
+		public bool containsPoint (BoundingBoxAttachment attachment, float x, float y) {
+			int index = BoundingBoxes.IndexOf(attachment);
+			return index == -1 ? false : ContainsPoint(index, x, y);
+		}
+
+		/** Returns the first bounding box attachment that contains the line segment, or null. When doing many checks, it is usually
+		 * more efficient to only call this method if {@link #aabbIntersectsSegment(float, float, float, float)} return true. */
+		public BoundingBoxAttachment IntersectsSegment (float x1, float y1, float x2, float y2) {
+			List<BoundingBoxAttachment> boundingBoxes = BoundingBoxes;
+			for (int i = 0, n = boundingBoxes.Count; i < n; i++) {
+				BoundingBoxAttachment attachment = boundingBoxes[i];
+				if (IntersectsSegment(attachment, x1, y1, x2, y2)) return attachment;
+			}
+			return null;
+		}
+
+		/** Returns true if the bounding box attachment contains the line segment. */
+		public bool IntersectsSegment (BoundingBoxAttachment attachment, float x1, float y1, float x2, float y2) {
+			int index = BoundingBoxes.IndexOf(attachment);
+			return index == -1 ? false : IntersectsSegment(index, x1, y1, x2, y2);
+		}
+
+		/** Returns true if the bounding box attachment contains the line segment. */
+		public bool IntersectsSegment (int index, float x1, float y1, float x2, float y2) {
+			Polygon polygon = Polygons[index];
+			float[] vertices = polygon.Vertices;
+			int nn = polygon.Count;
+
+			float width12 = x1 - x2, height12 = y1 - y2;
+			float det1 = x1 * y2 - y1 * x2;
+			float x3 = vertices[nn - 2], y3 = vertices[nn - 1];
+			for (int ii = 0; ii < nn; ii += 2) {
+				float x4 = vertices[ii], y4 = vertices[ii + 1];
+				float det2 = x3 * y4 - y3 * x4;
+				float width34 = x3 - x4, height34 = y3 - y4;
+				float det3 = width12 * height34 - height12 * width34;
+				float x = (det1 * width34 - width12 * det2) / det3;
+				if (((x >= x3 && x <= x4) || (x >= x4 && x <= x3)) && ((x >= x1 && x <= x2) || (x >= x2 && x <= x1))) {
+					float y = (det1 * height34 - height12 * det2) / det3;
+					if (((y >= y3 && y <= y4) || (y >= y4 && y <= y3)) && ((y >= y1 && y <= y2) || (y >= y2 && y <= y1))) return true;
+				}
+				x3 = x4;
+				y3 = y4;
+
+			}
+			return false;
+		}
+	}
+}
+
+public class Polygon {
+	public float[] Vertices { get; set; }
+	public int Count { get; set; }
+}

+ 297 - 287
spine-csharp/src/SkeletonJson.cs

@@ -21,39 +21,40 @@
  * 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 System.Collections.Generic;
-
+ ******************************************************************************/
+
+using System;
+using System.IO;
+using System.Collections.Generic;
+
 #if WINDOWS_STOREAPP
 using System.Threading.Tasks;
 using Windows.Storage;
-#endif
-
+#endif
+
 namespace Spine {
-    public class SkeletonJson {
-		static public String TIMELINE_SCALE = "scale";
-		static public String TIMELINE_ROTATE = "rotate";
-		static public String TIMELINE_TRANSLATE = "translate";
-		static public String TIMELINE_ATTACHMENT = "attachment";
-		static public String TIMELINE_COLOR = "color";
-
-		static public String ATTACHMENT_REGION = "region";
-		static public String ATTACHMENT_REGION_SEQUENCE = "regionSequence";
-
-		private AttachmentLoader attachmentLoader;
-		public float Scale { get; set; }
-
-		public SkeletonJson (Atlas atlas) : this(new AtlasAttachmentLoader(atlas)) {
-		}
-
-		public SkeletonJson (AttachmentLoader attachmentLoader) {
-			if (attachmentLoader == null) throw new ArgumentNullException("attachmentLoader cannot be null.");
-			this.attachmentLoader = attachmentLoader;
-			Scale = 1;
-		}
+	public class SkeletonJson {
+		static public String TIMELINE_SCALE = "scale";
+		static public String TIMELINE_ROTATE = "rotate";
+		static public String TIMELINE_TRANSLATE = "translate";
+		static public String TIMELINE_ATTACHMENT = "attachment";
+		static public String TIMELINE_COLOR = "color";
+
+		static public String ATTACHMENT_REGION = "region";
+		static public String ATTACHMENT_REGION_SEQUENCE = "regionSequence";
+
+		private AttachmentLoader attachmentLoader;
+		public float Scale { get; set; }
+
+		public SkeletonJson (Atlas atlas)
+			: this(new AtlasAttachmentLoader(atlas)) {
+		}
+
+		public SkeletonJson (AttachmentLoader attachmentLoader) {
+			if (attachmentLoader == null) throw new ArgumentNullException("attachmentLoader cannot be null.");
+			this.attachmentLoader = attachmentLoader;
+			Scale = 1;
+		}
 
 #if WINDOWS_STOREAPP
         private async Task<SkeletonData> ReadFile(string path) {
@@ -69,248 +70,257 @@ namespace Spine {
 		public SkeletonData ReadSkeletonData (String path) {
 		    return this.ReadFile(path).Result;
 		}
-#else
-		public SkeletonData ReadSkeletonData (String path) {
-			using (StreamReader reader = new StreamReader(path)) {
-				SkeletonData skeletonData = ReadSkeletonData(reader);
-				skeletonData.Name = Path.GetFileNameWithoutExtension(path);
-				return skeletonData;
-			}
-		}
-#endif
-
-		public SkeletonData ReadSkeletonData (TextReader reader) {
-			if (reader == null) throw new ArgumentNullException("reader cannot be null.");
-
-			SkeletonData skeletonData = new SkeletonData();
-
-			var root = Json.Deserialize(reader) as Dictionary<String, Object>;
-			if (root == null) throw new Exception("Invalid JSON.");
-
-			// Bones.
-			foreach (Dictionary<String, Object> boneMap in (List<Object>)root["bones"]) {
-				BoneData parent = null;
-				if (boneMap.ContainsKey("parent")) {
-					parent = skeletonData.FindBone((String)boneMap["parent"]);
-					if (parent == null)
-						throw new Exception("Parent bone not found: " + boneMap["parent"]);
-				}
-				BoneData boneData = new BoneData((String)boneMap["name"], parent);
-				boneData.Length = GetFloat(boneMap, "length", 0) * Scale;
-				boneData.X = GetFloat(boneMap, "x", 0) * Scale;
-				boneData.Y = GetFloat(boneMap, "y", 0) * Scale;
-				boneData.Rotation = GetFloat(boneMap, "rotation", 0);
-				boneData.ScaleX = GetFloat(boneMap, "scaleX", 1);
-				boneData.ScaleY = GetFloat(boneMap, "scaleY", 1);
-				boneData.InheritScale = GetBoolean (boneMap, "inheritScale", true);
-				boneData.InheritRotation = GetBoolean (boneMap, "inheritRotation", true);
-				skeletonData.AddBone(boneData);
-			}
-
-			// Slots.
-			if (root.ContainsKey("slots")) {
-				var slots = (List<Object>)root["slots"];
-				foreach (Dictionary<String, Object> slotMap in slots) {
-					String slotName = (String)slotMap["name"];
-					String boneName = (String)slotMap["bone"];
-					BoneData boneData = skeletonData.FindBone(boneName);
-					if (boneData == null)
-						throw new Exception("Slot bone not found: " + boneName);
-					SlotData slotData = new SlotData(slotName, boneData);
-
-					if (slotMap.ContainsKey("color")) {
-						String color = (String)slotMap["color"];
-						slotData.R = ToColor(color, 0);
-						slotData.G = ToColor(color, 1);
-						slotData.B = ToColor(color, 2);
-						slotData.A = ToColor(color, 3);
-					}
-
-					if (slotMap.ContainsKey("attachment"))
+#else
+		public SkeletonData ReadSkeletonData (String path) {
+			using (StreamReader reader = new StreamReader(path)) {
+				SkeletonData skeletonData = ReadSkeletonData(reader);
+				skeletonData.Name = Path.GetFileNameWithoutExtension(path);
+				return skeletonData;
+			}
+		}
+#endif
+
+		public SkeletonData ReadSkeletonData (TextReader reader) {
+			if (reader == null) throw new ArgumentNullException("reader cannot be null.");
+
+			SkeletonData skeletonData = new SkeletonData();
+
+			var root = Json.Deserialize(reader) as Dictionary<String, Object>;
+			if (root == null) throw new Exception("Invalid JSON.");
+
+			// Bones.
+			foreach (Dictionary<String, Object> boneMap in (List<Object>)root["bones"]) {
+				BoneData parent = null;
+				if (boneMap.ContainsKey("parent")) {
+					parent = skeletonData.FindBone((String)boneMap["parent"]);
+					if (parent == null)
+						throw new Exception("Parent bone not found: " + boneMap["parent"]);
+				}
+				BoneData boneData = new BoneData((String)boneMap["name"], parent);
+				boneData.Length = GetFloat(boneMap, "length", 0) * Scale;
+				boneData.X = GetFloat(boneMap, "x", 0) * Scale;
+				boneData.Y = GetFloat(boneMap, "y", 0) * Scale;
+				boneData.Rotation = GetFloat(boneMap, "rotation", 0);
+				boneData.ScaleX = GetFloat(boneMap, "scaleX", 1);
+				boneData.ScaleY = GetFloat(boneMap, "scaleY", 1);
+				boneData.InheritScale = GetBoolean(boneMap, "inheritScale", true);
+				boneData.InheritRotation = GetBoolean(boneMap, "inheritRotation", true);
+				skeletonData.AddBone(boneData);
+			}
+
+			// Slots.
+			if (root.ContainsKey("slots")) {
+				var slots = (List<Object>)root["slots"];
+				foreach (Dictionary<String, Object> slotMap in slots) {
+					String slotName = (String)slotMap["name"];
+					String boneName = (String)slotMap["bone"];
+					BoneData boneData = skeletonData.FindBone(boneName);
+					if (boneData == null)
+						throw new Exception("Slot bone not found: " + boneName);
+					SlotData slotData = new SlotData(slotName, boneData);
+
+					if (slotMap.ContainsKey("color")) {
+						String color = (String)slotMap["color"];
+						slotData.R = ToColor(color, 0);
+						slotData.G = ToColor(color, 1);
+						slotData.B = ToColor(color, 2);
+						slotData.A = ToColor(color, 3);
+					}
+
+					if (slotMap.ContainsKey("attachment"))
 						slotData.AttachmentName = (String)slotMap["attachment"];
 
 					if (slotMap.ContainsKey("additive"))
-						slotData.AdditiveBlending = (bool)slotMap["additive"];
-
-					skeletonData.AddSlot(slotData);
-				}
-			}
-
-			// Skins.
-			if (root.ContainsKey("skins")) {
-				var skinMap = (Dictionary<String, Object>)root["skins"];
-				foreach (KeyValuePair<String, Object> entry in skinMap) {
-					Skin skin = new Skin(entry.Key);
-					foreach (KeyValuePair<String, Object> slotEntry in (Dictionary<String, Object>)entry.Value) {
-						int slotIndex = skeletonData.FindSlotIndex(slotEntry.Key);
-						foreach (KeyValuePair<String, Object> attachmentEntry in ((Dictionary<String, Object>)slotEntry.Value)) {
-							Attachment attachment = ReadAttachment(skin, attachmentEntry.Key, (Dictionary<String, Object>)attachmentEntry.Value);
-							skin.AddAttachment(slotIndex, attachmentEntry.Key, attachment);
-						}
-					}
-					skeletonData.AddSkin(skin);
-					if (skin.Name == "default")
-						skeletonData.DefaultSkin = skin;
-				}
-			}
-
-
-			// Animations.
-			if (root.ContainsKey("animations")) {
-				var animationMap = (Dictionary<String, Object>)root["animations"];
-				foreach (KeyValuePair<String, Object> entry in animationMap)
-					ReadAnimation(entry.Key, (Dictionary<String, Object>)entry.Value, skeletonData);
-			}
-
-			skeletonData.Bones.TrimExcess();
-			skeletonData.Slots.TrimExcess();
-			skeletonData.Skins.TrimExcess();
-			skeletonData.Animations.TrimExcess();
-			return skeletonData;
-		}
-
-		private Attachment ReadAttachment (Skin skin, String name, Dictionary<String, Object> map) {
-			if (map.ContainsKey("name"))
-				name = (String)map["name"];
-
-			AttachmentType type = AttachmentType.region;
-			if (map.ContainsKey("type"))
-				type = (AttachmentType)Enum.Parse(typeof(AttachmentType), (String)map["type"], false);
-			Attachment attachment = attachmentLoader.NewAttachment(skin, type, name);
-
-			if (attachment is RegionAttachment) {
-				RegionAttachment regionAttachment = (RegionAttachment)attachment;
-				regionAttachment.X = GetFloat(map, "x", 0) * Scale;
-				regionAttachment.Y = GetFloat(map, "y", 0) * Scale;
-				regionAttachment.ScaleX = GetFloat(map, "scaleX", 1);
-				regionAttachment.ScaleY = GetFloat(map, "scaleY", 1);
-				regionAttachment.Rotation = GetFloat(map, "rotation", 0);
-				regionAttachment.Width = GetFloat(map, "width", 32) * Scale;
-				regionAttachment.Height = GetFloat(map, "height", 32) * Scale;
-				regionAttachment.UpdateOffset();
-			}
-
-			return attachment;
-		}
-
-		private float GetFloat (Dictionary<String, Object> map, String name, float defaultValue) {
-			if (!map.ContainsKey(name))
-				return (float)defaultValue;
-			return (float)map[name];
-		}
-		
-		private bool GetBoolean (Dictionary<String, Object> map, String name, bool defaultValue) {
-			if (!map.ContainsKey(name))
-				return (bool)defaultValue;
-			return (bool)map[name];
-		}
-
-		public static float ToColor (String hexString, int colorIndex) {
-			if (hexString.Length != 8)
-				throw new ArgumentException("Color hexidecimal length must be 8, recieved: " + hexString);
-			return Convert.ToInt32(hexString.Substring(colorIndex * 2, 2), 16) / (float)255;
-		}
-
-		private void ReadAnimation (String name, Dictionary<String, Object> map, SkeletonData skeletonData) {
-			var timelines = new List<Timeline>();
-			float duration = 0;
-
-			if (map.ContainsKey("bones")) {
-				var bonesMap = (Dictionary<String, Object>)map["bones"];
-				foreach (KeyValuePair<String, Object> entry in bonesMap) {
-					String boneName = entry.Key;
-					int boneIndex = skeletonData.FindBoneIndex(boneName);
-					if (boneIndex == -1)
-						throw new Exception("Bone not found: " + boneName);
-
-					var timelineMap = (Dictionary<String, Object>)entry.Value;
-					foreach (KeyValuePair<String, Object> timelineEntry in timelineMap) {
-						var values = (List<Object>)timelineEntry.Value;
-						String timelineName = (String)timelineEntry.Key;
-						if (timelineName.Equals(TIMELINE_ROTATE)) {
-							RotateTimeline timeline = new RotateTimeline(values.Count);
-							timeline.BoneIndex = boneIndex;
-
-							int frameIndex = 0;
-							foreach (Dictionary<String, Object> valueMap in values) {
-								float time = (float)valueMap["time"];
-								timeline.SetFrame(frameIndex, time, (float)valueMap["angle"]);
-								ReadCurve(timeline, frameIndex, valueMap);
-								frameIndex++;
-							}
-							timelines.Add(timeline);
-							duration = Math.Max(duration, timeline.Frames[timeline.FrameCount * 2 - 2]);
-
-						} else if (timelineName.Equals(TIMELINE_TRANSLATE) || timelineName.Equals(TIMELINE_SCALE)) {
-							TranslateTimeline timeline;
-							float timelineScale = 1;
-							if (timelineName.Equals(TIMELINE_SCALE))
-								timeline = new ScaleTimeline(values.Count);
-							else {
-								timeline = new TranslateTimeline(values.Count);
-								timelineScale = Scale;
-							}
-							timeline.BoneIndex = boneIndex;
-
-							int frameIndex = 0;
-							foreach (Dictionary<String, Object> valueMap in values) {
-								float time = (float)valueMap["time"];
-								float x = valueMap.ContainsKey("x") ? (float)valueMap["x"] : 0;
-								float y = valueMap.ContainsKey("y") ? (float)valueMap["y"] : 0;
-								timeline.SetFrame(frameIndex, time, (float)x * timelineScale, (float)y * timelineScale);
-								ReadCurve(timeline, frameIndex, valueMap);
-								frameIndex++;
-							}
-							timelines.Add(timeline);
-							duration = Math.Max(duration, timeline.Frames[timeline.FrameCount * 3 - 3]);
-
-						} else
-							throw new Exception("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")");
-					}
-				}
-			}
-
-			if (map.ContainsKey("slots")) {
-				var slotsMap = (Dictionary<String, Object>)map["slots"];
-				foreach (KeyValuePair<String, Object> entry in slotsMap) {
-					String slotName = entry.Key;
-					int slotIndex = skeletonData.FindSlotIndex(slotName);
-					var timelineMap = (Dictionary<String, Object>)entry.Value;
-
-					foreach (KeyValuePair<String, Object> timelineEntry in timelineMap) {
-						var values = (List<Object>)timelineEntry.Value;
-						String timelineName = (String)timelineEntry.Key;
-						if (timelineName.Equals(TIMELINE_COLOR)) {
-							ColorTimeline timeline = new ColorTimeline(values.Count);
-							timeline.SlotIndex = slotIndex;
-
-							int frameIndex = 0;
-							foreach (Dictionary<String, Object> valueMap in values) {
-								float time = (float)valueMap["time"];
-								String c = (String)valueMap["color"];
-								timeline.setFrame(frameIndex, time, ToColor(c, 0), ToColor(c, 1), ToColor(c, 2), ToColor(c, 3));
-								ReadCurve(timeline, frameIndex, valueMap);
-								frameIndex++;
-							}
-							timelines.Add(timeline);
-							duration = Math.Max(duration, timeline.Frames[timeline.FrameCount * 5 - 5]);
-
-						} else if (timelineName.Equals(TIMELINE_ATTACHMENT)) {
-							AttachmentTimeline timeline = new AttachmentTimeline(values.Count);
-							timeline.SlotIndex = slotIndex;
-
-							int frameIndex = 0;
-							foreach (Dictionary<String, Object> valueMap in values) {
-								float time = (float)valueMap["time"];
-								timeline.setFrame(frameIndex++, time, (String)valueMap["name"]);
-							}
-							timelines.Add(timeline);
-							duration = Math.Max(duration, timeline.Frames[timeline.FrameCount - 1]);
-
-						} else
-							throw new Exception("Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")");
-					}
-				}
+						slotData.AdditiveBlending = (bool)slotMap["additive"];
+
+					skeletonData.AddSlot(slotData);
+				}
+			}
+
+			// Skins.
+			if (root.ContainsKey("skins")) {
+				var skinMap = (Dictionary<String, Object>)root["skins"];
+				foreach (KeyValuePair<String, Object> entry in skinMap) {
+					Skin skin = new Skin(entry.Key);
+					foreach (KeyValuePair<String, Object> slotEntry in (Dictionary<String, Object>)entry.Value) {
+						int slotIndex = skeletonData.FindSlotIndex(slotEntry.Key);
+						foreach (KeyValuePair<String, Object> attachmentEntry in ((Dictionary<String, Object>)slotEntry.Value)) {
+							Attachment attachment = ReadAttachment(skin, attachmentEntry.Key, (Dictionary<String, Object>)attachmentEntry.Value);
+							if (attachment != null) skin.AddAttachment(slotIndex, attachmentEntry.Key, attachment);
+						}
+					}
+					skeletonData.AddSkin(skin);
+					if (skin.Name == "default")
+						skeletonData.DefaultSkin = skin;
+				}
+			}
+
+
+			// Animations.
+			if (root.ContainsKey("animations")) {
+				var animationMap = (Dictionary<String, Object>)root["animations"];
+				foreach (KeyValuePair<String, Object> entry in animationMap)
+					ReadAnimation(entry.Key, (Dictionary<String, Object>)entry.Value, skeletonData);
+			}
+
+			skeletonData.Bones.TrimExcess();
+			skeletonData.Slots.TrimExcess();
+			skeletonData.Skins.TrimExcess();
+			skeletonData.Animations.TrimExcess();
+			return skeletonData;
+		}
+
+		private Attachment ReadAttachment (Skin skin, String name, Dictionary<String, Object> map) {
+			if (map.ContainsKey("name"))
+				name = (String)map["name"];
+
+			AttachmentType type = AttachmentType.region;
+			if (map.ContainsKey("type"))
+				type = (AttachmentType)Enum.Parse(typeof(AttachmentType), (String)map["type"], false);
+			Attachment attachment = attachmentLoader.NewAttachment(skin, type, name);
+
+			RegionAttachment regionAttachment = attachment as RegionAttachment;
+			if (regionAttachment != null) {
+				regionAttachment.X = GetFloat(map, "x", 0) * Scale;
+				regionAttachment.Y = GetFloat(map, "y", 0) * Scale;
+				regionAttachment.ScaleX = GetFloat(map, "scaleX", 1);
+				regionAttachment.ScaleY = GetFloat(map, "scaleY", 1);
+				regionAttachment.Rotation = GetFloat(map, "rotation", 0);
+				regionAttachment.Width = GetFloat(map, "width", 32) * Scale;
+				regionAttachment.Height = GetFloat(map, "height", 32) * Scale;
+				regionAttachment.UpdateOffset();
+			}
+
+			BoundingBoxAttachment boundingBox = attachment as BoundingBoxAttachment;
+			if (boundingBox != null) {
+				List<Object> values = (List<Object>)map["vertices"];
+				float[] vertices = new float[values.Count];
+				for (int i = 0, n = values.Count; i < n; i++)
+					vertices[i] = (float)values[i];
+				boundingBox.Vertices = vertices;
+			}
+
+			return attachment;
+		}
+
+		private float GetFloat (Dictionary<String, Object> map, String name, float defaultValue) {
+			if (!map.ContainsKey(name))
+				return (float)defaultValue;
+			return (float)map[name];
+		}
+
+		private bool GetBoolean (Dictionary<String, Object> map, String name, bool defaultValue) {
+			if (!map.ContainsKey(name))
+				return (bool)defaultValue;
+			return (bool)map[name];
+		}
+
+		public static float ToColor (String hexString, int colorIndex) {
+			if (hexString.Length != 8)
+				throw new ArgumentException("Color hexidecimal length must be 8, recieved: " + hexString);
+			return Convert.ToInt32(hexString.Substring(colorIndex * 2, 2), 16) / (float)255;
+		}
+
+		private void ReadAnimation (String name, Dictionary<String, Object> map, SkeletonData skeletonData) {
+			var timelines = new List<Timeline>();
+			float duration = 0;
+
+			if (map.ContainsKey("bones")) {
+				var bonesMap = (Dictionary<String, Object>)map["bones"];
+				foreach (KeyValuePair<String, Object> entry in bonesMap) {
+					String boneName = entry.Key;
+					int boneIndex = skeletonData.FindBoneIndex(boneName);
+					if (boneIndex == -1)
+						throw new Exception("Bone not found: " + boneName);
+
+					var timelineMap = (Dictionary<String, Object>)entry.Value;
+					foreach (KeyValuePair<String, Object> timelineEntry in timelineMap) {
+						var values = (List<Object>)timelineEntry.Value;
+						String timelineName = (String)timelineEntry.Key;
+						if (timelineName.Equals(TIMELINE_ROTATE)) {
+							RotateTimeline timeline = new RotateTimeline(values.Count);
+							timeline.BoneIndex = boneIndex;
+
+							int frameIndex = 0;
+							foreach (Dictionary<String, Object> valueMap in values) {
+								float time = (float)valueMap["time"];
+								timeline.SetFrame(frameIndex, time, (float)valueMap["angle"]);
+								ReadCurve(timeline, frameIndex, valueMap);
+								frameIndex++;
+							}
+							timelines.Add(timeline);
+							duration = Math.Max(duration, timeline.Frames[timeline.FrameCount * 2 - 2]);
+
+						} else if (timelineName.Equals(TIMELINE_TRANSLATE) || timelineName.Equals(TIMELINE_SCALE)) {
+							TranslateTimeline timeline;
+							float timelineScale = 1;
+							if (timelineName.Equals(TIMELINE_SCALE))
+								timeline = new ScaleTimeline(values.Count);
+							else {
+								timeline = new TranslateTimeline(values.Count);
+								timelineScale = Scale;
+							}
+							timeline.BoneIndex = boneIndex;
+
+							int frameIndex = 0;
+							foreach (Dictionary<String, Object> valueMap in values) {
+								float time = (float)valueMap["time"];
+								float x = valueMap.ContainsKey("x") ? (float)valueMap["x"] : 0;
+								float y = valueMap.ContainsKey("y") ? (float)valueMap["y"] : 0;
+								timeline.SetFrame(frameIndex, time, (float)x * timelineScale, (float)y * timelineScale);
+								ReadCurve(timeline, frameIndex, valueMap);
+								frameIndex++;
+							}
+							timelines.Add(timeline);
+							duration = Math.Max(duration, timeline.Frames[timeline.FrameCount * 3 - 3]);
+
+						} else
+							throw new Exception("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")");
+					}
+				}
+			}
+
+			if (map.ContainsKey("slots")) {
+				var slotsMap = (Dictionary<String, Object>)map["slots"];
+				foreach (KeyValuePair<String, Object> entry in slotsMap) {
+					String slotName = entry.Key;
+					int slotIndex = skeletonData.FindSlotIndex(slotName);
+					var timelineMap = (Dictionary<String, Object>)entry.Value;
+
+					foreach (KeyValuePair<String, Object> timelineEntry in timelineMap) {
+						var values = (List<Object>)timelineEntry.Value;
+						String timelineName = (String)timelineEntry.Key;
+						if (timelineName.Equals(TIMELINE_COLOR)) {
+							ColorTimeline timeline = new ColorTimeline(values.Count);
+							timeline.SlotIndex = slotIndex;
+
+							int frameIndex = 0;
+							foreach (Dictionary<String, Object> valueMap in values) {
+								float time = (float)valueMap["time"];
+								String c = (String)valueMap["color"];
+								timeline.setFrame(frameIndex, time, ToColor(c, 0), ToColor(c, 1), ToColor(c, 2), ToColor(c, 3));
+								ReadCurve(timeline, frameIndex, valueMap);
+								frameIndex++;
+							}
+							timelines.Add(timeline);
+							duration = Math.Max(duration, timeline.Frames[timeline.FrameCount * 5 - 5]);
+
+						} else if (timelineName.Equals(TIMELINE_ATTACHMENT)) {
+							AttachmentTimeline timeline = new AttachmentTimeline(values.Count);
+							timeline.SlotIndex = slotIndex;
+
+							int frameIndex = 0;
+							foreach (Dictionary<String, Object> valueMap in values) {
+								float time = (float)valueMap["time"];
+								timeline.setFrame(frameIndex++, time, (String)valueMap["name"]);
+							}
+							timelines.Add(timeline);
+							duration = Math.Max(duration, timeline.Frames[timeline.FrameCount - 1]);
+
+						} else
+							throw new Exception("Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")");
+					}
+				}
 			}
 
 			if (map.ContainsKey("draworder")) {
@@ -344,22 +354,22 @@ namespace Spine {
 				}
 				timelines.Add(timeline);
 				duration = Math.Max(duration, timeline.Frames[timeline.FrameCount - 1]);
-			}
-
-			timelines.TrimExcess();
-			skeletonData.AddAnimation(new Animation(name, timelines, duration));
-		}
-
-		private void ReadCurve (CurveTimeline timeline, int frameIndex, Dictionary<String, Object> valueMap) {
-			if (!valueMap.ContainsKey("curve"))
-				return;
-			Object curveObject = valueMap["curve"];
-			if (curveObject.Equals("stepped"))
-				timeline.SetStepped(frameIndex);
-			else if (curveObject is List<Object>) {
-				List<Object> curve = (List<Object>)curveObject;
-				timeline.SetCurve(frameIndex, (float)curve[0], (float)curve[1], (float)curve[2], (float)curve[3]);
-			}
-		}
-	}
-}
+			}
+
+			timelines.TrimExcess();
+			skeletonData.AddAnimation(new Animation(name, timelines, duration));
+		}
+
+		private void ReadCurve (CurveTimeline timeline, int frameIndex, Dictionary<String, Object> valueMap) {
+			if (!valueMap.ContainsKey("curve"))
+				return;
+			Object curveObject = valueMap["curve"];
+			if (curveObject.Equals("stepped"))
+				timeline.SetStepped(frameIndex);
+			else if (curveObject is List<Object>) {
+				List<Object> curve = (List<Object>)curveObject;
+				timeline.SetCurve(frameIndex, (float)curve[0], (float)curve[1], (float)curve[2], (float)curve[3]);
+			}
+		}
+	}
+}

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
spine-xna/example/data/spineboy.json


+ 26 - 4
spine-xna/example/src/ExampleGame.cs

@@ -40,9 +40,13 @@ namespace Spine {
 		GraphicsDeviceManager graphics;
 		SkeletonRenderer skeletonRenderer;
 		Skeleton skeleton;
+		Slot headSlot;
 		AnimationState state;
+		SkeletonBounds bounds = new SkeletonBounds();
 
 		public Example () {
+			IsMouseVisible = true;
+
 			graphics = new GraphicsDeviceManager(this);
 			graphics.IsFullScreen = false;
 			graphics.PreferredBackBufferWidth = 640;
@@ -74,14 +78,19 @@ namespace Spine {
 			}
 
 			state = new AnimationState(stateData);
-			state.SetAnimation("drawOrder", true);
-			//state.SetAnimation("walk", false);
-			//state.AddAnimation("jump", false);
-			//state.AddAnimation("walk", true);
+			if (true) {
+				state.SetAnimation("drawOrder", true);
+			} else {
+				state.SetAnimation("walk", false);
+				state.AddAnimation("jump", false);
+				state.AddAnimation("walk", true);
+			}
 
 			skeleton.X = 320;
 			skeleton.Y = 440;
 			skeleton.UpdateWorldTransform();
+
+			headSlot = skeleton.FindSlot("head");
 		}
 
 		protected override void UnloadContent () {
@@ -108,6 +117,19 @@ namespace Spine {
 			skeletonRenderer.Draw(skeleton);
 			skeletonRenderer.End();
 
+			bounds.Update(skeleton);
+			MouseState mouse = Mouse.GetState();
+			if (bounds.AabbContainsPoint(mouse.X, mouse.Y)) {
+				BoundingBoxAttachment hit = bounds.ContainsPoint(mouse.X, mouse.Y);
+				if (hit != null) {
+					headSlot.G = 0;
+					headSlot.B = 0;
+				} else {
+					headSlot.G = 1;
+					headSlot.B = 1;
+				}
+			}
+
 			base.Draw(gameTime);
 		}
 	}

+ 0 - 2
spine-xna/src/SkeletonRenderer.cs

@@ -69,8 +69,6 @@ namespace Spine {
 		}
 
 		public void Draw (Skeleton skeleton) {
-			Console.WriteLine();
-
 			List<Slot> drawOrder = skeleton.DrawOrder;
 			for (int i = 0, n = drawOrder.Count; i < n; i++) {
 				Slot slot = drawOrder[i];

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.