Jelajahi Sumber

Updated spine-csharp to Spine v3.

NathanSweet 9 tahun lalu
induk
melakukan
08b74f5b18

+ 79 - 23
spine-csharp/spine-csharp.csproj

@@ -53,35 +53,91 @@
   </ItemGroup>
   <ItemGroup>
     <Compile Include="Properties\AssemblyInfo.cs" />
-    <Compile Include="src\Animation.cs" />
-    <Compile Include="src\AnimationStateData.cs" />
-    <Compile Include="src\Atlas.cs" />
-    <Compile Include="src\Attachments\AttachmentLoader.cs" />
+    <Compile Include="src\Animation.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="src\AnimationState.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="src\AnimationStateData.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="src\Atlas.cs">
+      <SubType>Code</SubType>
+    </Compile>
     <Compile Include="src\Attachments\AtlasAttachmentLoader.cs" />
     <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\MeshAttachment.cs" />
     <Compile Include="src\Attachments\RegionAttachment.cs" />
-    <Compile Include="src\Attachments\SkinnedMeshAttachment.cs" />
-    <Compile Include="src\BlendMode.cs" />
-    <Compile Include="src\Bone.cs" />
-    <Compile Include="src\BoneData.cs" />
-    <Compile Include="src\AnimationState.cs" />
-    <Compile Include="src\Event.cs" />
-    <Compile Include="src\EventData.cs" />
-    <Compile Include="src\ExposedList.cs" />
-    <Compile Include="src\IkConstraint.cs" />
-    <Compile Include="src\IkConstraintData.cs" />
-    <Compile Include="src\Json.cs" />
-    <Compile Include="src\Skeleton.cs" />
-    <Compile Include="src\SkeletonData.cs" />
-    <Compile Include="src\SkeletonJson.cs" />
-    <Compile Include="src\SkeletonBinary.cs" />
-    <Compile Include="src\Skin.cs" />
-    <Compile Include="src\Slot.cs" />
-    <Compile Include="src\SlotData.cs" />
+    <Compile Include="src\Attachments\WeightedMeshAttachment.cs" />
+    <Compile Include="src\BlendMode.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="src\Bone.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="src\BoneData.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="src\Event.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="src\EventData.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="src\ExposedList.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="src\IkConstraint.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="src\IkConstraintData.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="src\IUpdatable.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="src\Json.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="src\MathUtils.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="src\Skeleton.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="src\SkeletonBinary.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="src\SkeletonBounds.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="src\SkeletonData.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="src\SkeletonJson.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="src\Skin.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="src\Slot.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="src\SlotData.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="src\TransformConstraint.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="src\TransformConstraintData.cs">
+      <SubType>Code</SubType>
+    </Compile>
   </ItemGroup>
+  <ItemGroup />
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <!--
       To modify your build process, add your task inside one of the targets below and uncomment it.
@@ -91,4 +147,4 @@
       <Target Name="AfterBuild">
       </Target>
     -->
-</Project>
+</Project>

+ 0 - 48
spine-csharp/src/Animation.cs

@@ -674,52 +674,4 @@ namespace Spine {
 			ikConstraint.bendDirection = (int)frames[frameIndex + PREV_FRAME_BEND_DIRECTION];
 		}
 	}
-
-	public class FlipXTimeline : Timeline {
-		internal int boneIndex;
-		internal float[] frames;
-
-		public int BoneIndex { get { return boneIndex; } set { boneIndex = value; } }
-		public float[] Frames { get { return frames; } set { frames = value; } } // time, flip, ...
-		public int FrameCount { get { return frames.Length >> 1; } }
-
-		public FlipXTimeline (int frameCount) {
-			frames = new float[frameCount << 1];
-		}
-
-		/// <summary>Sets the time and value of the specified keyframe.</summary>
-		public void SetFrame (int frameIndex, float time, bool flip) {
-			frameIndex *= 2;
-			frames[frameIndex] = time;
-			frames[frameIndex + 1] = flip ? 1 : 0;
-		}
-
-		public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha) {
-			float[] frames = this.frames;
-			if (time < frames[0]) {
-				if (lastTime > time) Apply(skeleton, lastTime, int.MaxValue, null, 0);
-				return;
-			} else if (lastTime > time) //
-				lastTime = -1;
-
-			int frameIndex = (time >= frames[frames.Length - 2] ? frames.Length : Animation.binarySearch(frames, time, 2)) - 2;
-			if (frames[frameIndex] < lastTime) return;
-
-			SetFlip(skeleton.bones.Items[boneIndex], frames[frameIndex + 1] != 0);
-		}
-
-		virtual protected void SetFlip (Bone bone, bool flip) {
-			bone.flipX = flip;
-		}
-	}
-
-	public class FlipYTimeline : FlipXTimeline {
-		public FlipYTimeline (int frameCount)
-			: base(frameCount) {
-		}
-
-		override protected void SetFlip (Bone bone, bool flip) {
-			bone.flipY = flip;
-		}
-	}
 }

+ 3 - 3
spine-csharp/src/Attachments/AtlasAttachmentLoader.cs

@@ -74,10 +74,10 @@ namespace Spine {
 			return attachment;
 		}
 
-		public SkinnedMeshAttachment NewSkinnedMeshAttachment (Skin skin, String name, String path) {
+		public WeightedMeshAttachment NewWeightedMeshAttachment (Skin skin, String name, String path) {
 			AtlasRegion region = FindRegion(path);
-			if (region == null) throw new Exception("Region not found in atlas: " + path + " (skinned mesh attachment: " + name + ")");
-			SkinnedMeshAttachment attachment = new SkinnedMeshAttachment(name);
+			if (region == null) throw new Exception("Region not found in atlas: " + path + " (weighted mesh attachment: " + name + ")");
+			WeightedMeshAttachment attachment = new WeightedMeshAttachment(name);
 			attachment.RendererObject = region;
 			attachment.RegionU = region.u;
 			attachment.RegionV = region.v;

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

@@ -40,7 +40,7 @@ namespace Spine {
 		MeshAttachment NewMeshAttachment (Skin skin, String name, String path);
 
 		/// <return>May be null to not load any attachment.</return>
-		SkinnedMeshAttachment NewSkinnedMeshAttachment (Skin skin, String name, String path);
+		WeightedMeshAttachment NewWeightedMeshAttachment (Skin skin, String name, String path);
 
 		/// <return>May be null to not load any attachment.</return>
 		BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, String name);

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

@@ -31,6 +31,6 @@
 
 namespace Spine {
 	public enum AttachmentType {
-		region, boundingbox, mesh, skinnedmesh
+		region, boundingbox, mesh, weightedmesh
 	}
 }

+ 4 - 4
spine-csharp/src/Attachments/BoundingBoxAttachment.cs

@@ -45,10 +45,10 @@ namespace Spine {
 		/// <param name="worldVertices">Must have at least the same length as this attachment's vertices.</param>
 		public void ComputeWorldVertices (Bone bone, float[] worldVertices) {
 			float x = bone.skeleton.x + bone.worldX, y = bone.skeleton.y + bone.worldY;
-			float m00 = bone.m00;
-			float m01 = bone.m01;
-			float m10 = bone.m10;
-			float m11 = bone.m11;
+			float m00 = bone.a;
+			float m01 = bone.b;
+			float m10 = bone.c;
+			float m11 = bone.d;
 			float[] vertices = this.vertices;
 			for (int i = 0, n = vertices.Length; i < n; i += 2) {
 				float px = vertices[i];

+ 2 - 2
spine-csharp/src/Attachments/MeshAttachment.cs

@@ -32,7 +32,7 @@
 using System;
 
 namespace Spine {
-	/// <summary>Attachment that displays a texture region.</summary>
+	/// <summary>Attachment that displays a texture region using a mesh.</summary>
 	public class MeshAttachment : Attachment {
 		internal float[] vertices, uvs, regionUVs;
 		internal int[] triangles;
@@ -94,7 +94,7 @@ namespace Spine {
 		public void ComputeWorldVertices (Slot slot, float[] worldVertices) {
 			Bone bone = slot.bone;
 			float x = bone.skeleton.x + bone.worldX, y = bone.skeleton.y + bone.worldY;
-			float m00 = bone.m00, m01 = bone.m01, m10 = bone.m10, m11 = bone.m11;
+			float m00 = bone.a, m01 = bone.b, m10 = bone.c, m11 = bone.d;
 			float[] vertices = this.vertices;
 			int verticesCount = vertices.Length;
 			if (slot.attachmentVerticesCount == verticesCount) vertices = slot.AttachmentVertices;

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

@@ -137,7 +137,7 @@ namespace Spine {
 
 		public void ComputeWorldVertices (Bone bone, float[] worldVertices) {
 			float x = bone.skeleton.x + bone.worldX, y = bone.skeleton.y + bone.worldY;
-			float m00 = bone.m00, m01 = bone.m01, m10 = bone.m10, m11 = bone.m11;
+			float m00 = bone.a, m01 = bone.b, m10 = bone.c, m11 = bone.d;
 			float[] offset = this.offset;
 			worldVertices[X1] = offset[X1] * m00 + offset[Y1] * m01 + x;
 			worldVertices[Y1] = offset[X1] * m10 + offset[Y1] * m11 + y;

+ 7 - 7
spine-csharp/src/Attachments/SkinnedMeshAttachment.cs → spine-csharp/src/Attachments/WeightedMeshAttachment.cs

@@ -33,8 +33,8 @@ using System;
 using System.Collections.Generic;
 
 namespace Spine {
-	/// <summary>Attachment that displays a texture region.</summary>
-	public class SkinnedMeshAttachment : Attachment {
+    /// <summary>Attachment that displays a texture region using a mesh which can be deformed by bones.</summary>
+	public class WeightedMeshAttachment : Attachment {
 		internal int[] bones;
 		internal float[] weights, uvs, regionUVs;
 		internal int[] triangles;
@@ -72,7 +72,7 @@ namespace Spine {
 		public float Width { get; set; }
 		public float Height { get; set; }
 
-		public SkinnedMeshAttachment (string name)
+		public WeightedMeshAttachment (string name)
 			: base(name) {
 		}
 
@@ -107,8 +107,8 @@ namespace Spine {
 					for (; v < nn; v++, b += 3) {
 						Bone bone = skeletonBones.Items[bones[v]];
 						float vx = weights[b], vy = weights[b + 1], weight = weights[b + 2];
-						wx += (vx * bone.m00 + vy * bone.m01 + bone.worldX) * weight;
-						wy += (vx * bone.m10 + vy * bone.m11 + bone.worldY) * weight;
+						wx += (vx * bone.a + vy * bone.b + bone.worldX) * weight;
+						wy += (vx * bone.c + vy * bone.d + bone.worldY) * weight;
 					}
 					worldVertices[w] = wx + x;
 					worldVertices[w + 1] = wy + y;
@@ -121,8 +121,8 @@ namespace Spine {
 					for (; v < nn; v++, b += 3, f += 2) {
 						Bone bone = skeletonBones.Items[bones[v]];
 						float vx = weights[b] + ffd[f], vy = weights[b + 1] + ffd[f + 1], weight = weights[b + 2];
-						wx += (vx * bone.m00 + vy * bone.m01 + bone.worldX) * weight;
-						wy += (vx * bone.m10 + vy * bone.m11 + bone.worldY) * weight;
+						wx += (vx * bone.a + vy * bone.b + bone.worldX) * weight;
+						wy += (vx * bone.c + vy * bone.d + bone.worldY) * weight;
 					}
 					worldVertices[w] = wx + x;
 					worldVertices[w + 1] = wy + y;

+ 163 - 85
spine-csharp/src/Bone.cs

@@ -33,18 +33,19 @@ using System;
 using System.Collections.Generic;
 
 namespace Spine {
-	public class Bone {
+    public class Bone : IUpdatable {
 		static public bool yDown;
 
 		internal BoneData data;
 		internal Skeleton skeleton;
 		internal Bone parent;
 		internal ExposedList<Bone> children = new ExposedList<Bone>();
-		internal float x, y, rotation, rotationIK, scaleX, scaleY;
-		internal bool flipX, flipY;
-		internal float m00, m01, m10, m11;
-		internal float worldX, worldY, worldRotation, worldScaleX, worldScaleY;
-		internal bool worldFlipX, worldFlipY;
+		internal float x, y, rotation, scaleX, scaleY;
+		internal float appliedRotation, appliedScaleX, appliedScaleY;
+
+		internal float a, b, worldX;
+		internal float c, d, worldY;
+		internal float worldSignX, worldSignY;
 
 		public BoneData Data { get { return data; } }
 		public Skeleton Skeleton { get { return skeleton; } }
@@ -52,26 +53,28 @@ namespace Spine {
 		public ExposedList<Bone> Children { get { return children; } }
 		public float X { get { return x; } set { x = value; } }
 		public float Y { get { return y; } set { y = value; } }
-		/// <summary>The forward kinetics rotation.</summary>
 		public float Rotation { get { return rotation; } set { rotation = value; } }
-		/// <summary>The inverse kinetics rotation, as calculated by any IK constraints.</summary>
-		public float RotationIK { get { return rotationIK; } set { rotationIK = value; } }
+		/// <summary>The rotation, as calculated by any constraints.</summary>
+		public float AppliedRotation { get { return appliedRotation; } set { appliedRotation = value; } }
+		/// <summary>The scale X, as calculated by any constraints.</summary>
+		public float AppliedScaleX { get { return appliedScaleX; } set { appliedScaleX = value; } }
+		/// <summary>The scale Y, as calculated by any constraints.</summary>
+		public float AppliedScaleY { get { return appliedScaleY; } set { appliedScaleY = value; } }
 		public float ScaleX { get { return scaleX; } set { scaleX = value; } }
 		public float ScaleY { get { return scaleY; } set { scaleY = value; } }
-		public bool FlipX { get { return flipX; } set { flipX = value; } }
-		public bool FlipY { get { return flipY; } set { flipY = value; } }
 
-		public float M00 { get { return m00; } }
-		public float M01 { get { return m01; } }
-		public float M10 { get { return m10; } }
-		public float M11 { get { return m11; } }
+		public float A { get { return a; } }
+		public float B { get { return b; } }
+		public float C { get { return c; } }
+		public float D { get { return d; } }
 		public float WorldX { get { return worldX; } }
 		public float WorldY { get { return worldY; } }
-		public float WorldRotation { get { return worldRotation; } }
-		public float WorldScaleX { get { return worldScaleX; } }
-		public float WorldScaleY { get { return worldScaleY; } }
-		public bool WorldFlipX { get { return worldFlipX; } set { worldFlipX = value; } }
-		public bool WorldFlipY { get { return worldFlipY; } set { worldFlipY = value; } }
+		public float WorldSignX { get { return worldSignX; } }
+		public float WorldSignY { get { return worldSignY; } }
+		public float WorldRotationX { get { return MathUtils.Atan2(c, a) * MathUtils.radDeg; } }
+		public float WorldRotationY { get { return MathUtils.Atan2(d, b) * MathUtils.radDeg; } }
+		public float WorldScaleX { get { return (float)Math.Sqrt(a * a + b * b) * worldSignX; } }
+		public float WorldScaleY { get { return (float)Math.Sqrt(c * c + d * d) * worldSignY; } }
 
 		/// <param name="parent">May be null.</param>
 		public Bone (BoneData data, Skeleton skeleton, Bone parent) {
@@ -83,80 +86,155 @@ namespace Spine {
 			SetToSetupPose();
 		}
 
-		/// <summary>Computes the world SRT using the parent bone and the local SRT.</summary>
+		/// <summary>Computes the world SRT using the parent bone and this bone's local SRT.</summary>
 		public void UpdateWorldTransform () {
+			UpdateWorldTransform(x, y, rotation, scaleX, scaleY);
+		}
+
+		/// <summary>Computes the world SRT using the parent bone and the specified local SRT.</summary>
+		public void UpdateWorldTransform (float x, float y, float rotation, float scaleX, float scaleY) {
+			appliedRotation = rotation;
+			appliedScaleX = scaleX;
+			appliedScaleY = scaleY;
+
+			float cos = MathUtils.CosDeg(rotation), sin = MathUtils.SinDeg(rotation);
+			float la = cos * scaleX, lb = -sin * scaleY, lc = sin * scaleX, ld = cos * scaleY;
 			Bone parent = this.parent;
-			float x = this.x, y = this.y;
-			if (parent != null) {
-				worldX = x * parent.m00 + y * parent.m01 + parent.worldX;
-				worldY = x * parent.m10 + y * parent.m11 + parent.worldY;
-				if (data.inheritScale) {
-					worldScaleX = parent.worldScaleX * scaleX;
-					worldScaleY = parent.worldScaleY * scaleY;
-				} else {
-					worldScaleX = scaleX;
-					worldScaleY = scaleY;
-				}
-				worldRotation = data.inheritRotation ? parent.worldRotation + rotationIK : rotationIK;
-				worldFlipX = parent.worldFlipX != flipX;
-				worldFlipY = parent.worldFlipY != flipY;
-			} else {
-				Skeleton skeleton = this.skeleton;
-				bool skeletonFlipX = skeleton.flipX, skeletonFlipY = skeleton.flipY;
-				worldX = skeletonFlipX ? -x : x;
-				worldY = skeletonFlipY != yDown ? -y : y;
-				worldScaleX = scaleX;
-				worldScaleY = scaleY;
-				worldRotation = rotationIK;
-				worldFlipX = skeletonFlipX != flipX;
-				worldFlipY = skeletonFlipY != flipY;
+			if (parent == null) { // Root bone.
+					Skeleton skeleton = this.skeleton;
+					if (skeleton.flipX) {
+						la = -la;
+						lc = -lc;
+						scaleX = -scaleX;
+						x = -x;
+					}
+					if (skeleton.flipY != yDown) {
+						lb = -lb;
+						ld = -ld;
+						scaleY = -scaleY;
+						y = -y;
+					}
+					a = la;
+					b = lb;
+					c = lc;
+					d = ld;
+					worldX = x;
+					worldY = y;
+					worldSignX = Math.Sign(scaleX);
+					worldSignY = Math.Sign(scaleY);
+					return;
 			}
-			float radians = worldRotation * (float)Math.PI / 180;
-			float cos = (float)Math.Cos(radians);
-			float sin = (float)Math.Sin(radians);
-			if (worldFlipX) {
-				m00 = -cos * worldScaleX;
-				m01 = sin * worldScaleY;
-			} else {
-				m00 = cos * worldScaleX;
-				m01 = -sin * worldScaleY;
-			}
-			if (worldFlipY != yDown) {
-				m10 = -sin * worldScaleX;
-				m11 = -cos * worldScaleY;
-			} else {
-				m10 = sin * worldScaleX;
-				m11 = cos * worldScaleY;
-			}
-		}
+
+         float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
+         worldX = pa * x + pb * y + parent.worldX;
+         worldY = pc * x + pd * y + parent.worldY;
+         worldSignX = parent.worldSignX * Math.Sign(scaleX);
+         worldSignY = parent.worldSignY * Math.Sign(scaleY);
+
+         if (data.inheritRotation && data.inheritScale) {
+               a = pa * la + pb * lc;
+               b = pa * lb + pb * ld;
+               c = pc * la + pd * lc;
+               d = pc * lb + pd * ld;
+         } else if (data.inheritRotation) { // No scale inheritance.
+               Bone p = parent;
+               pa = 1;
+               pb = 0;
+               pc = 0;
+               pd = 1;
+               while (p != null) {
+                  cos = MathUtils.CosDeg(p.appliedRotation);
+                  sin = MathUtils.SinDeg(p.appliedRotation);
+                  float a = pa * cos + pb * sin;
+                  float b = pa * -sin + pb * cos;
+                  float c = pc * cos + pd * sin;
+                  float d = pc * -sin + pd * cos;
+                  pa = a;
+                  pb = b;
+                  pc = c;
+                  pd = d;
+                  p = p.parent;
+               }
+					if (yDown) {
+						pb = -pb;
+						pd = -pd;
+					}
+               this.a = pa * la + pb * lc;
+               this.b = pa * lb + pb * ld;
+               this.c = pc * la + pd * lc;
+               this.d = pc * lb + pd * ld;
+         } else if (data.inheritScale) { // No rotation inheritance.
+               Bone p = parent;
+               pa = 1;
+               pb = 0;
+               pc = 0;
+               pd = 1;
+               while (p != null) {
+                  float r = p.rotation;
+                  cos = MathUtils.CosDeg(r);
+                  sin = MathUtils.SinDeg(r);
+                  float psx = p.appliedScaleX, psy = p.appliedScaleY;
+                  float za = cos * psx, zb = -sin * psy, zc = sin * psx, zd = cos * psy;
+                  float temp = pa * za + pb * zc;
+                  pb = pa * zb + pb * zd;
+                  pa = temp;
+                  temp = pc * za + pd * zc;
+                  pd = pc * zb + pd * zd;
+                  pc = temp;
+
+                  if (psx < 0) r = -r;
+                  cos = MathUtils.CosDeg(-r);
+                  sin = MathUtils.SinDeg(-r);
+                  temp = pa * cos + pb * sin;
+                  pb = pa * -sin + pb * cos;
+                  pa = temp;
+                  temp = pc * cos + pd * sin;
+                  pd = pc * -sin + pd * cos;
+                  pc = temp;
+
+                  p = p.parent;
+               }
+					if (yDown) {
+						pb = -pb;
+						pd = -pd;
+					}
+               a = pa * la + pb * lc;
+               b = pa * lb + pb * ld;
+               c = pc * la + pd * lc;
+               d = pc * lb + pd * ld;
+         } else {
+               a = la;
+               b = lb;
+               c = lc;
+               d = ld;
+         }
+      }
+
+      public void Update () {
+         UpdateWorldTransform(x, y, rotation, scaleX, scaleY);
+      }
 
 		public void SetToSetupPose () {
-			BoneData data = this.data;
-			x = data.x;
-			y = data.y;
-			rotation = data.rotation;
-			rotationIK = rotation;
-			scaleX = data.scaleX;
-			scaleY = data.scaleY;
-			flipX = data.flipX;
-			flipY = data.flipY;
+            BoneData data = this.data;
+            x = data.x;
+            y = data.y;
+            rotation = data.rotation;
+            scaleX = data.scaleX;
+            scaleY = data.scaleY;
 		}
 
-		public void worldToLocal (float worldX, float worldY, out float localX, out float localY) {
-			float dx = worldX - this.worldX, dy = worldY - this.worldY;
-			float m00 = this.m00, m10 = this.m10, m01 = this.m01, m11 = this.m11;
-			if (worldFlipX != (worldFlipY != yDown)) {
-				m00 = -m00;
-				m11 = -m11;
-			}
-			float invDet = 1 / (m00 * m11 - m01 * m10);
-			localX = (dx * m00 * invDet - dy * m01 * invDet);
-			localY = (dy * m11 * invDet - dx * m10 * invDet);
+		public void WorldToLocal (float worldX, float worldY, out float localX, out float localY) {
+            float x = worldX - this.worldX, y = worldY - this.worldY;
+            float a = this.a, b = this.b, c = this.c, d = this.d;
+            float invDet = 1 / (a * d - b * c);
+            localX = (x * a * invDet - y * b * invDet);
+            localY = (y * d * invDet - x * c * invDet);
 		}
 
-		public void localToWorld (float localX, float localY, out float worldX, out float worldY) {
-			worldX = localX * m00 + localY * m01 + this.worldX;
-			worldY = localX * m10 + localY * m11 + this.worldY;
+		public void LocalToWorld (float localX, float localY, out float worldX, out float worldY) {
+            float x = localX, y = localY;
+            worldX = x * a + y * b + this.worldX;
+            worldY = x * c + y * d + this.worldY;
 		}
 
 		override public String ToString () {

+ 0 - 3
spine-csharp/src/BoneData.cs

@@ -36,7 +36,6 @@ namespace Spine {
 		internal BoneData parent;
 		internal String name;
 		internal float length, x, y, rotation, scaleX = 1, scaleY = 1;
-		internal bool flipX, flipY;
 		internal bool inheritScale = true, inheritRotation = true;
 
 		/// <summary>May be null.</summary>
@@ -48,8 +47,6 @@ namespace Spine {
 		public float Rotation { get { return rotation; } set { rotation = value; } }
 		public float ScaleX { get { return scaleX; } set { scaleX = value; } }
 		public float ScaleY { get { return scaleY; } set { scaleY = value; } }
-		public bool FlipX { get { return flipX; } set { flipX = value; } }
-		public bool FlipY { get { return flipY; } set { flipY = value; } }
 		public bool InheritScale { get { return inheritScale; } set { inheritScale = value; } }
 		public bool InheritRotation { get { return inheritRotation; } set { inheritRotation = value; } }
 

+ 2 - 1
spine-csharp/src/Event.cs

@@ -37,8 +37,9 @@ namespace Spine {
 		public int Int { get; set; }
 		public float Float { get; set; }
 		public String String { get; set; }
+		public float Time { get; private set; }
 
-		public Event (EventData data) {
+		public Event (float time, EventData data) {
 			Data = data;
 		}
 

+ 38 - 0
spine-csharp/src/IUpdatable.cs

@@ -0,0 +1,38 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2.3
+ * 
+ * Copyright (c) 2013-2015, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to use, install, execute and perform the Spine
+ * Runtimes Software (the "Software") and derivative works solely for personal
+ * or internal use. Without the written permission of Esoteric Software (see
+ * Section 2 of the Spine Software License Agreement), you may not (a) modify,
+ * translate, adapt or otherwise create derivative works, improvements of the
+ * Software or develop new applications using the Software or (b) remove,
+ * delete, alter or obscure any trademarks or any copyright, trademark, patent
+ * or other intellectual property or proprietary rights notices on or in the
+ * Software, including any copy thereof. Redistributions in binary or source
+ * form must include this license and terms.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "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 ESOTERIC SOFTWARE 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 {
+	public interface IUpdatable {
+		void Update ();
+	}
+}

+ 129 - 62
spine-csharp/src/IkConstraint.cs

@@ -33,9 +33,7 @@ using System;
 using System.Collections.Generic;
 
 namespace Spine {
-	public class IkConstraint {
-		private const float radDeg = 180 / (float)Math.PI;
-
+	public class IkConstraint : IUpdatable {
 		internal IkConstraintData data;
 		internal ExposedList<Bone> bones = new ExposedList<Bone>();
 		internal Bone target;
@@ -61,15 +59,19 @@ namespace Spine {
 			target = skeleton.FindBone(data.target.name);
 		}
 
-		public void apply () {
+		public void Update () {
+			Apply();
+		}
+
+		public void Apply () {
 			Bone target = this.target;
 			ExposedList<Bone> bones = this.bones;
 			switch (bones.Count) {
 			case 1:
-				apply(bones.Items[0], target.worldX, target.worldY, mix);
+				Apply(bones.Items[0], target.worldX, target.worldY, mix);
 				break;
 			case 2:
-				apply(bones.Items[0], bones.Items[1], target.worldX, target.worldY, bendDirection, mix);
+				Apply(bones.Items[0], bones.Items[1], target.worldX, target.worldY, bendDirection, mix);
 				break;
 			}
 		}
@@ -80,72 +82,137 @@ namespace Spine {
 
 		/// <summary>Adjusts the bone rotation so the tip is as close to the target position as possible. The target is specified
 		/// in the world coordinate system.</summary>
-		static public void apply (Bone bone, float targetX, float targetY, float alpha) {
-			float parentRotation = (!bone.data.inheritRotation || bone.parent == null) ? 0 : bone.parent.worldRotation;
+		static public void Apply (Bone bone, float targetX, float targetY, float alpha) {
+			float parentRotation = bone.parent == null ? 0 : bone.parent.WorldRotationX;
 			float rotation = bone.rotation;
-			float rotationIK = (float)Math.Atan2(targetY - bone.worldY, targetX - bone.worldX) * radDeg;
-			if (bone.worldFlipX != (bone.worldFlipY != Bone.yDown)) rotationIK = -rotationIK;
-			rotationIK -= parentRotation;
-			bone.rotationIK = rotation + (rotationIK - rotation) * alpha;
+			float rotationIK = MathUtils.Atan2(targetY - bone.worldY, targetX - bone.worldX) * MathUtils.radDeg - parentRotation;
+			if (bone.worldSignX != bone.worldSignY) rotationIK = 360 - rotationIK;
+			if (rotationIK > 180) rotationIK -= 360;
+			else if (rotationIK < -180) rotationIK += 360;
+			bone.UpdateWorldTransform(bone.x, bone.y, rotation + (rotationIK - rotation) * alpha, bone.scaleX, bone.scaleY);
 		}
 
 		/// <summary>Adjusts the parent and child bone rotations so the tip of the child is as close to the target position as
 		/// possible. The target is specified in the world coordinate system.</summary>
-		/// <param name="child">Any descendant bone of the parent.</param>
-		static public void apply (Bone parent, Bone child, float targetX, float targetY, int bendDirection, float alpha) {
-			float childRotation = child.rotation, parentRotation = parent.rotation;
-			if (alpha == 0) {
-				child.rotationIK = childRotation;
-				parent.rotationIK = parentRotation;
-				return;
-			}
-			float positionX, positionY;
-			Bone parentParent = parent.parent;
-			if (parentParent != null) {
-				parentParent.worldToLocal(targetX, targetY, out positionX, out positionY);
-				targetX = (positionX - parent.x) * parentParent.worldScaleX;
-				targetY = (positionY - parent.y) * parentParent.worldScaleY;
+		/// <param name="child">A direct descendant of the parent bone.</param>
+		static public void Apply (Bone parent, Bone child, float targetX, float targetY, int bendDir, float alpha) {
+			if (alpha == 0) return;
+			float px = parent.x, py = parent.y, psx = parent.scaleX, psy = parent.scaleY, csx = child.scaleX, cy = child.y;
+			int offset1, offset2, sign2;
+			if (psx < 0) {
+				psx = -psx;
+				offset1 = 180;
+				sign2 = -1;
 			} else {
-				targetX -= parent.x;
-				targetY -= parent.y;
+				offset1 = 0;
+				sign2 = 1;
+			}
+			if (psy < 0) {
+				psy = -psy;
+				sign2 = -sign2;
 			}
-			if (child.parent == parent) {
-				positionX = child.x;
-				positionY = child.y;
+			if (csx < 0) {
+				csx = -csx;
+				offset2 = 180;
+			} else
+				offset2 = 0;
+			Bone pp = parent.parent;
+			float tx, ty, dx, dy;
+			if (pp == null) {
+				tx = targetX - px;
+				ty = targetY - py;
+				dx = child.worldX - px;
+				dy = child.worldY - py;
 			} else {
-				child.parent.localToWorld(child.x, child.y, out positionX, out positionY);
-				parent.worldToLocal(positionX, positionY, out positionX, out positionY);
+				float a = pp.a, b = pp.b, c = pp.c, d = pp.d, invDet = 1 / (a * d - b * c);
+				float wx = pp.worldX, wy = pp.worldY, x = targetX - wx, y = targetY - wy;
+				tx = (x * d - y * b) * invDet - px;
+				ty = (y * a - x * c) * invDet - py;
+				x = child.worldX - wx;
+				y = child.worldY - wy;
+				dx = (x * d - y * b) * invDet - px;
+				dy = (y * a - x * c) * invDet - py;
 			}
-			float childX = positionX * parent.worldScaleX, childY = positionY * parent.worldScaleY;
-			float offset = (float)Math.Atan2(childY, childX);
-			float len1 = (float)Math.Sqrt(childX * childX + childY * childY), len2 = child.data.length * child.worldScaleX;
-			// Based on code by Ryan Juckett with permission: Copyright (c) 2008-2009 Ryan Juckett, http://www.ryanjuckett.com/
-			float cosDenom = 2 * len1 * len2;
-			if (cosDenom < 0.0001f) {
-				child.rotationIK = childRotation + ((float)Math.Atan2(targetY, targetX) * radDeg - parentRotation - childRotation)
-					* alpha;
-				return;
+			float l1 = (float)Math.Sqrt(dx * dx + dy * dy), l2 = child.data.length * csx, a1, a2;
+			if (Math.Abs(psx - psy) <= 0.0001f) {
+				l2 *= psx;
+				float cos = (tx * tx + ty * ty - l1 * l1 - l2 * l2) / (2 * l1 * l2);
+				if (cos < -1)
+					cos = -1;
+				else if (cos > 1) cos = 1;
+				a2 = (float)Math.Acos(cos) * bendDir;
+				float a = l1 + l2 * cos, o = l2 * MathUtils.Sin(a2);
+				a1 = MathUtils.Atan2(ty * a - tx * o, tx * a + ty * o);
+			} else {
+				cy = 0;
+				float a = psx * l2, b = psy * l2, ta = MathUtils.Atan2(ty, tx);
+				float aa = a * a, bb = b * b, ll = l1 * l1, dd = tx * tx + ty * ty;
+				float c0 = bb * ll + aa * dd - aa * bb, c1 = -2 * bb * l1, c2 = bb - aa;
+				float d = c1 * c1 - 4 * c2 * c0;
+				if (d >= 0) {
+					float q = (float)Math.Sqrt(d);
+					if (c1 < 0) q = -q;
+					q = -(c1 + q) / 2;
+					float r0 = q / c2, r1 = c0 / q;
+					float r = Math.Abs(r0) < Math.Abs(r1) ? r0 : r1;
+					if (r * r <= dd) {
+						float y1 = (float)Math.Sqrt(dd - r * r) * bendDir;
+						a1 = ta - MathUtils.Atan2(y1, r);
+						a2 = MathUtils.Atan2(y1 / psy, (r - l1) / psx);
+						goto outer;
+					}
+				}
+				float minAngle = 0, minDist = float.MaxValue, minX = 0, minY = 0;
+				float maxAngle = 0, maxDist = 0, maxX = 0, maxY = 0;
+				float x = l1 + a, dist = x * x;
+				if (dist > maxDist) {
+					maxAngle = 0;
+					maxDist = dist;
+					maxX = x;
+				}
+				x = l1 - a;
+				dist = x * x;
+				if (dist < minDist) {
+					minAngle = MathUtils.PI;
+					minDist = dist;
+					minX = x;
+				}
+				float angle = (float)Math.Acos(-a * l1 / (aa - bb));
+				x = a * MathUtils.Cos(angle) + l1;
+				float y = b * MathUtils.Sin(angle);
+				dist = x * x + y * y;
+				if (dist < minDist) {
+					minAngle = angle;
+					minDist = dist;
+					minX = x;
+					minY = y;
+				}
+				if (dist > maxDist) {
+					maxAngle = angle;
+					maxDist = dist;
+					maxX = x;
+					maxY = y;
+				}
+				if (dd <= (minDist + maxDist) / 2) {
+					a1 = ta - MathUtils.Atan2(minY * bendDir, minX);
+					a2 = minAngle * bendDir;
+				} else {
+					a1 = ta - MathUtils.Atan2(maxY * bendDir, maxX);
+					a2 = maxAngle * bendDir;
+				}
 			}
-			float cos = (targetX * targetX + targetY * targetY - len1 * len1 - len2 * len2) / cosDenom;
-			if (cos < -1)
-				cos = -1;
-			else if (cos > 1)
-				cos = 1;
-			float childAngle = (float)Math.Acos(cos) * bendDirection;
-			float adjacent = len1 + len2 * cos, opposite = len2 * (float)Math.Sin(childAngle);
-			float parentAngle = (float)Math.Atan2(targetY * adjacent - targetX * opposite, targetX * adjacent + targetY * opposite);
-			float rotation = (parentAngle - offset) * radDeg - parentRotation;
-			if (rotation > 180)
-				rotation -= 360;
-			else if (rotation < -180) //
-				rotation += 360;
-			parent.rotationIK = parentRotation + rotation * alpha;
-			rotation = (childAngle + offset) * radDeg - childRotation;
-			if (rotation > 180)
-				rotation -= 360;
-			else if (rotation < -180) //
-				rotation += 360;
-			child.rotationIK = childRotation + (rotation + parent.worldRotation - child.parent.worldRotation) * alpha;
+		outer:
+			float offset = MathUtils.Atan2(cy, child.x) * sign2;
+			a1 = (a1 - offset) * MathUtils.radDeg + offset1;
+			a2 = (a2 + offset) * MathUtils.radDeg * sign2 + offset2;
+			if (a1 > 180) a1 -= 360;
+			else if (a1 < -180) a1 += 360;
+			if (a2 > 180) a2 -= 360;
+			else if (a2 < -180) a2 += 360;
+			float rotation = parent.rotation;
+			parent.UpdateWorldTransform(parent.x, parent.y, rotation + (a1 - rotation) * alpha, parent.scaleX, parent.scaleY);
+			rotation = child.rotation;
+			child.UpdateWorldTransform(child.x, cy, rotation + (a2 - rotation) * alpha, child.scaleX, child.scaleY);
 		}
 	}
 }

+ 94 - 0
spine-csharp/src/MathUtils.cs

@@ -0,0 +1,94 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2.3
+ * 
+ * Copyright (c) 2013-2015, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to use, install, execute and perform the Spine
+ * Runtimes Software (the "Software") and derivative works solely for personal
+ * or internal use. Without the written permission of Esoteric Software (see
+ * Section 2 of the Spine Software License Agreement), you may not (a) modify,
+ * translate, adapt or otherwise create derivative works, improvements of the
+ * Software or develop new applications using the Software or (b) remove,
+ * delete, alter or obscure any trademarks or any copyright, trademark, patent
+ * or other intellectual property or proprietary rights notices on or in the
+ * Software, including any copy thereof. Redistributions in binary or source
+ * form must include this license and terms.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "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 ESOTERIC SOFTWARE 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 {
+	public class MathUtils {
+		static public float PI = 3.1415927f;
+	    static public float radDeg = 180f / PI;
+	    static public float degRad = PI / 180;
+
+	    static private int SIN_BITS = 14; // 16KB. Adjust for accuracy.
+	    static private int SIN_MASK = ~(-1 << SIN_BITS);
+	    static private int SIN_COUNT = SIN_MASK + 1;
+	    static private float radFull = PI * 2;
+	    static private float degFull = 360;
+	    static private float radToIndex = SIN_COUNT / radFull;
+	    static private float degToIndex = SIN_COUNT / degFull;
+        static float[] sin = new float[SIN_COUNT];
+
+        static MathUtils () {
+			for (int i = 0; i < SIN_COUNT; i++)
+                sin[i] = (float)Math.Sin((i + 0.5f) / SIN_COUNT * radFull);
+			for (int i = 0; i < 360; i += 90)
+                sin[(int)(i * degToIndex) & SIN_MASK] = (float)Math.Sin(i * degRad);
+	    }
+
+	    /** Returns the sine in radians from a lookup table. */
+	    static public float Sin (float radians) {
+            return sin[(int)(radians * radToIndex) & SIN_MASK];
+	    }
+
+	    /** Returns the cosine in radians from a lookup table. */
+	    static public float Cos (float radians) {
+            return sin[(int)((radians + PI / 2) * radToIndex) & SIN_MASK];
+	    }
+
+	    /** Returns the sine in radians from a lookup table. */
+	    static public float SinDeg (float degrees) {
+            return sin[(int)(degrees * degToIndex) & SIN_MASK];
+	    }
+
+	    /** Returns the cosine in radians from a lookup table. */
+	    static public float CosDeg (float degrees) {
+            return sin[(int)((degrees + 90) * degToIndex) & SIN_MASK];
+	    }
+
+		/// <summary>Returns atan2 in radians, faster but less accurate than Math.Atan2. Average error of 0.00231 radians (0.1323
+		/// degrees), largest error of 0.00488 radians (0.2796 degrees).</summary>
+		static public float Atan2 (float y, float x) {
+			if (x == 0f) {
+				if (y > 0f) return PI / 2;
+				if (y == 0f) return 0f;
+				return -PI / 2;
+			}
+			float atan, z = y / x;
+			if (Math.Abs(z) < 1f) {
+				atan = z / (1f + 0.28f * z * z);
+				if (x < 0f) return atan + (y < 0f ? -PI : PI);
+				return atan;
+			}
+			atan = PI / 2 - z / (z * z + 0.28f);
+			return y < 0f ? atan - PI : atan;
+		}
+	}
+}

+ 60 - 59
spine-csharp/src/Skeleton.cs

@@ -39,7 +39,8 @@ namespace Spine {
 		internal ExposedList<Slot> slots;
 		internal ExposedList<Slot> drawOrder;
 		internal ExposedList<IkConstraint> ikConstraints;
-		private ExposedList<ExposedList<Bone>> boneCache = new ExposedList<ExposedList<Bone>>();
+        internal ExposedList<TransformConstraint> transformConstraints;
+        private ExposedList<IUpdatable> updateCache = new ExposedList<IUpdatable>();
 		internal Skin skin;
 		internal float r = 1, g = 1, b = 1, a = 1;
 		internal float time;
@@ -89,81 +90,62 @@ namespace Spine {
 				drawOrder.Add(slot);
 			}
 
-			ikConstraints = new ExposedList<IkConstraint>(data.ikConstraints.Count);
-			foreach (IkConstraintData ikConstraintData in data.ikConstraints)
-				ikConstraints.Add(new IkConstraint(ikConstraintData, this));
+         ikConstraints = new ExposedList<IkConstraint>(data.ikConstraints.Count);
+         foreach (IkConstraintData ikConstraintData in data.ikConstraints)
+               ikConstraints.Add(new IkConstraint(ikConstraintData, this));
+
+         transformConstraints = new ExposedList<TransformConstraint>(data.transformConstraints.Count);
+         foreach (TransformConstraintData transformConstraintData in data.transformConstraints)
+               transformConstraints.Add(new TransformConstraint(transformConstraintData, this));
 
 			UpdateCache();
 			UpdateWorldTransform();
 		}
 
-		/// <summary>Caches information about bones and IK constraints. Must be called if bones or IK constraints are added or
-		/// removed.</summary>
+		/// <summary>Caches information about bones and constraints. Must be called if bones or constraints are added
+		/// or removed.</summary>
 		public void UpdateCache () {
-			ExposedList<ExposedList<Bone>> boneCache = this.boneCache;
+			ExposedList<Bone> bones = this.bones;
+			ExposedList<IUpdatable> updateCache = this.updateCache;
 			ExposedList<IkConstraint> ikConstraints = this.ikConstraints;
+			ExposedList<TransformConstraint> transformConstraints = this.transformConstraints;
 			int ikConstraintsCount = ikConstraints.Count;
-
-			int arrayCount = ikConstraintsCount + 1;
-			if (boneCache.Count > arrayCount) boneCache.RemoveRange(arrayCount, boneCache.Count - arrayCount);
-			for (int i = 0, n = boneCache.Count; i < n; i++)
-				boneCache.Items[i].Clear();
-			while (boneCache.Count < arrayCount)
-				boneCache.Add(new ExposedList<Bone>());
-
-			ExposedList<Bone> nonIkBones = boneCache.Items[0];
-
+			int transformConstraintsCount = transformConstraints.Count;
+			updateCache.Clear();
 			for (int i = 0, n = bones.Count; i < n; i++) {
 				Bone bone = bones.Items[i];
-				Bone current = bone;
-				do {
-					for (int ii = 0; ii < ikConstraintsCount; ii++) {
-						IkConstraint ikConstraint = ikConstraints.Items[ii];
-						Bone parent = ikConstraint.bones.Items[0];
-						Bone child = ikConstraint.bones.Items[ikConstraint.bones.Count - 1];
-						while (true) {
-							if (current == child) {
-								boneCache.Items[ii].Add(bone);
-								boneCache.Items[ii + 1].Add(bone);
-								goto outer;
-							}
-							if (child == parent) break;
-							child = child.parent;
-						}
+				updateCache.Add(bone);
+				for (int ii = 0; ii < transformConstraintsCount; ii++) {
+					TransformConstraint transformConstraint = transformConstraints.Items[ii];
+					if (bone == transformConstraint.bone) {
+						updateCache.Add(transformConstraint);
+						break;
 					}
-					current = current.parent;
-				} while (current != null);
-				nonIkBones.Add(bone);
-			outer: { }
+				}
+				for (int ii = 0; ii < ikConstraintsCount; ii++) {
+					IkConstraint ikConstraint = ikConstraints.Items[ii];
+					if (bone == ikConstraint.bones.Items[ikConstraint.bones.Count - 1]) {
+						updateCache.Add(ikConstraint);
+						break;
+					}
+				}
 			}
 		}
 
 		/// <summary>Updates the world transform for each bone and applies IK constraints.</summary>
 		public void UpdateWorldTransform () {
-			ExposedList<Bone> bones = this.bones;
-			for (int ii = 0, nn = bones.Count; ii < nn; ii++) {
-				Bone bone = bones.Items[ii];
-				bone.rotationIK = bone.rotation;
-			}
-			ExposedList<ExposedList<Bone>> boneCache = this.boneCache;
-			ExposedList<IkConstraint> ikConstraints = this.ikConstraints;
-			int i = 0, last = boneCache.Count - 1;
-			while (true) {
-				ExposedList<Bone> updateBones = boneCache.Items[i];
-				for (int ii = 0, nn = updateBones.Count; ii < nn; ii++)
-					updateBones.Items[ii].UpdateWorldTransform();
-				if (i == last) break;
-				ikConstraints.Items[i].apply();
-				i++;
-			}
+			ExposedList<IUpdatable> updateCache = this.updateCache;
+			for (int i = 0, n = updateCache.Count; i < n; i++)
+				updateCache.Items[i].Update();
 		}
 
-		/// <summary>Sets the bones and slots to their setup pose values.</summary>
+		/// <summary>Sets the bones, constraints, and slots to their setup pose values.</summary>
 		public void SetToSetupPose () {
 			SetBonesToSetupPose();
 			SetSlotsToSetupPose();
 		}
 
+		/// <summary>Sets the bones and constraints to their setup pose values.</summary>
 		public void SetBonesToSetupPose () {
 			ExposedList<Bone> bones = this.bones;
 			for (int i = 0, n = bones.Count; i < n; i++)
@@ -171,9 +153,17 @@ namespace Spine {
 
 			ExposedList<IkConstraint> ikConstraints = this.ikConstraints;
 			for (int i = 0, n = ikConstraints.Count; i < n; i++) {
-				IkConstraint ikConstraint = ikConstraints.Items[i];
-				ikConstraint.bendDirection = ikConstraint.data.bendDirection;
-				ikConstraint.mix = ikConstraint.data.mix;
+				IkConstraint constraint = ikConstraints.Items[i];
+				constraint.bendDirection = constraint.data.bendDirection;
+				constraint.mix = constraint.data.mix;
+			}
+
+			ExposedList<TransformConstraint> transformConstraints = this.transformConstraints;
+			for (int i = 0, n = transformConstraints.Count; i < n; i++) {
+				TransformConstraint constraint = transformConstraints.Items[i];
+				constraint.translateMix = constraint.data.translateMix;
+				constraint.x = constraint.data.x;
+				constraint.y = constraint.data.y;
 			}
 		}
 
@@ -293,12 +283,23 @@ namespace Spine {
 		}
 
 		/** @return May be null. */
-		public IkConstraint FindIkConstraint (String ikConstraintName) {
-			if (ikConstraintName == null) throw new ArgumentNullException("ikConstraintName cannot be null.");
+		public IkConstraint FindIkConstraint (String constraintName) {
+			if (constraintName == null) throw new ArgumentNullException("constraintName cannot be null.");
 			ExposedList<IkConstraint> ikConstraints = this.ikConstraints;
 			for (int i = 0, n = ikConstraints.Count; i < n; i++) {
 				IkConstraint ikConstraint = ikConstraints.Items[i];
-				if (ikConstraint.data.name == ikConstraintName) return ikConstraint;
+				if (ikConstraint.data.name == constraintName) return ikConstraint;
+			}
+			return null;
+		}
+
+		/** @return May be null. */
+		public TransformConstraint FindTransformConstraint (String constraintName) {
+			if (constraintName == null) throw new ArgumentNullException("constraintName cannot be null.");
+			ExposedList<TransformConstraint> transformConstraints = this.transformConstraints;
+			for (int i = 0, n = transformConstraints.Count; i < n; i++) {
+				TransformConstraint transformConstraint = transformConstraints.Items[i];
+				if (transformConstraint.data.name == constraintName) return transformConstraint;
 			}
 			return null;
 		}

+ 15 - 19
spine-csharp/src/SkeletonBinary.cs

@@ -45,8 +45,6 @@ namespace Spine {
 		public const int TIMELINE_TRANSLATE = 2;
 		public const int TIMELINE_ATTACHMENT = 3;
 		public const int TIMELINE_COLOR = 4;
-		public const int TIMELINE_FLIPX = 5;
-		public const int TIMELINE_FLIPY = 6;
 
 		public const int CURVE_LINEAR = 0;
 		public const int CURVE_STEPPED = 1;
@@ -130,8 +128,6 @@ namespace Spine {
 				boneData.scaleY = ReadFloat(input);
 				boneData.rotation = ReadFloat(input);
 				boneData.length = ReadFloat(input) * scale;
-				boneData.flipX = ReadBoolean(input);
-				boneData.flipY = ReadBoolean(input);
 				boneData.inheritScale = ReadBoolean(input);
 				boneData.inheritRotation = ReadBoolean(input);
 				if (nonessential) ReadInt(input); // Skip bone color.
@@ -149,6 +145,17 @@ namespace Spine {
 				skeletonData.ikConstraints.Add(ikConstraintData);
 			}
 
+			// Transform constraints.
+			for (int i = 0, n = ReadInt(input, true); i < n; i++) {
+				TransformConstraintData transformConstraintData = new TransformConstraintData(ReadString(input));
+				transformConstraintData.bone = skeletonData.bones.Items[ReadInt(input, true)];
+				transformConstraintData.target = skeletonData.bones.Items[ReadInt(input, true)];
+				transformConstraintData.translateMix = ReadFloat(input);
+				transformConstraintData.x = ReadFloat(input);
+				transformConstraintData.y = ReadFloat(input);
+				skeletonData.transformConstraints.Add(transformConstraintData);
+			}
+
 			// Slots.
 			for (int i = 0, n = ReadInt(input, true); i < n; i++) {
 				String slotName = ReadString(input);
@@ -269,10 +276,10 @@ namespace Spine {
 					}
 					return mesh;
 				}
-			case AttachmentType.skinnedmesh: {
+			case AttachmentType.weightedmesh: {
 					String path = ReadString(input);
 					if (path == null) path = name;
-					SkinnedMeshAttachment mesh = attachmentLoader.NewSkinnedMeshAttachment(skin, name, path);
+					WeightedMeshAttachment mesh = attachmentLoader.NewWeightedMeshAttachment(skin, name, path);
 					if (mesh == null) return null;
 					mesh.Path = path;
 					float[] uvs = ReadFloatArray(input, 1);
@@ -422,17 +429,6 @@ namespace Spine {
 							duration = Math.Max(duration, timeline.frames[frameCount * 3 - 3]);
 							break;
 						}
-					case TIMELINE_FLIPX:
-					case TIMELINE_FLIPY: {
-							FlipXTimeline timeline = timelineType == TIMELINE_FLIPX ? new FlipXTimeline(frameCount) : new FlipYTimeline(
-								frameCount);
-							timeline.boneIndex = boneIndex;
-							for (int frameIndex = 0; frameIndex < frameCount; frameIndex++)
-								timeline.SetFrame(frameIndex, ReadFloat(input), ReadBoolean(input));
-							timelines.Add(timeline);
-							duration = Math.Max(duration, timeline.frames[frameCount * 2 - 2]);
-							break;
-						}
 					}
 				}
 			}
@@ -470,7 +466,7 @@ namespace Spine {
 							if (attachment is MeshAttachment)
 								vertexCount = ((MeshAttachment)attachment).vertices.Length;
 							else
-								vertexCount = ((SkinnedMeshAttachment)attachment).weights.Length / 3 * 2;
+								vertexCount = ((WeightedMeshAttachment)attachment).weights.Length / 3 * 2;
 
 							int end = ReadInt(input, true);
 							if (end == 0) {
@@ -544,7 +540,7 @@ namespace Spine {
 				for (int i = 0; i < eventCount; i++) {
 					float time = ReadFloat(input);
 					EventData eventData = skeletonData.events.Items[ReadInt(input, true)];
-					Event e = new Event(eventData);
+					Event e = new Event(time, eventData);
 					e.Int = ReadInt(input, false);
 					e.Float = ReadFloat(input);
 					e.String = ReadBoolean(input) ? ReadString(input) : eventData.String;

+ 17 - 3
spine-csharp/src/SkeletonData.cs

@@ -42,6 +42,7 @@ namespace Spine {
 		internal ExposedList<EventData> events = new ExposedList<EventData>();
 		internal ExposedList<Animation> animations = new ExposedList<Animation>();
 		internal ExposedList<IkConstraintData> ikConstraints = new ExposedList<IkConstraintData>();
+        internal ExposedList<TransformConstraintData> transformConstraints = new ExposedList<TransformConstraintData>();
 		internal float width, height;
 		internal String version, hash, imagesPath;
 
@@ -140,12 +141,25 @@ namespace Spine {
 		// --- IK constraints.
 
 		/// <returns>May be null.</returns>
-		public IkConstraintData FindIkConstraint (String ikConstraintName) {
-			if (ikConstraintName == null) throw new ArgumentNullException("ikConstraintName cannot be null.");
+		public IkConstraintData FindIkConstraint (String constraintName) {
+			if (constraintName == null) throw new ArgumentNullException("constraintName cannot be null.");
 			ExposedList<IkConstraintData> ikConstraints = this.ikConstraints;
 			for (int i = 0, n = ikConstraints.Count; i < n; i++) {
 				IkConstraintData ikConstraint = ikConstraints.Items[i];
-				if (ikConstraint.name == ikConstraintName) return ikConstraint;
+				if (ikConstraint.name == constraintName) return ikConstraint;
+			}
+			return null;
+		}
+
+		// --- Transform constraints.
+
+		/// <returns>May be null.</returns>
+		public TransformConstraintData FindTransformConstraint (String constraintName) {
+			if (constraintName == null) throw new ArgumentNullException("constraintName cannot be null.");
+			ExposedList<TransformConstraintData> transformConstraints = this.transformConstraints;
+			for (int i = 0, n = transformConstraints.Count; i < n; i++) {
+				TransformConstraintData transformConstraint = transformConstraints.Items[i];
+				if (transformConstraint.name == constraintName) return transformConstraint;
 			}
 			return null;
 		}

+ 32 - 24
spine-csharp/src/SkeletonJson.cs

@@ -118,8 +118,6 @@ namespace Spine {
 				boneData.rotation = GetFloat(boneMap, "rotation", 0);
 				boneData.scaleX = GetFloat(boneMap, "scaleX", 1);
 				boneData.scaleY = GetFloat(boneMap, "scaleY", 1);
-				boneData.flipX = GetBoolean(boneMap, "flipX", false);
-				boneData.flipY = GetBoolean(boneMap, "flipY", false);
 				boneData.inheritScale = GetBoolean(boneMap, "inheritScale", true);
 				boneData.inheritRotation = GetBoolean(boneMap, "inheritRotation", true);
 				skeletonData.bones.Add(boneData);
@@ -147,6 +145,27 @@ namespace Spine {
 				}
 			}
 
+			// Transform constraints.
+			if (root.ContainsKey("transform")) {
+				foreach (Dictionary<String, Object> transformMap in (List<Object>)root["ik"]) {
+					TransformConstraintData transformConstraintData = new TransformConstraintData((String)transformMap["name"]);
+
+					String boneName = (String)transformMap["bone"];
+					transformConstraintData.target = skeletonData.FindBone(boneName);
+					if (transformConstraintData.target == null) throw new Exception("Bone not found: " + boneName);
+
+					String targetName = (String)transformMap["target"];
+					transformConstraintData.target = skeletonData.FindBone(targetName);
+					if (transformConstraintData.target == null) throw new Exception("Target bone not found: " + targetName);
+
+					transformConstraintData.translateMix = GetFloat(transformMap, "mix", 1);
+					transformConstraintData.x = GetFloat(transformMap, "x", 0);
+					transformConstraintData.y = GetFloat(transformMap, "y", 0);
+
+					skeletonData.transformConstraints.Add(transformConstraintData);
+				}
+			}
+
 			// Slots.
 			if (root.ContainsKey("slots")) {
 				foreach (Dictionary<String, Object> slotMap in (List<Object>)root["slots"]) {
@@ -226,8 +245,11 @@ namespace Spine {
 				name = (String)map["name"];
 
 			var type = AttachmentType.region;
-			if (map.ContainsKey("type"))
-				type = (AttachmentType)Enum.Parse(typeof(AttachmentType), (String)map["type"], false);
+			if (map.ContainsKey("type")) {
+				var typeName = (String)map["type"];
+				if (typeName == "skinnedmesh") typeName = "weightedmesh";
+				type = (AttachmentType)Enum.Parse(typeof(AttachmentType), typeName , false);
+			}
 
 			String path = name;
 			if (map.ContainsKey("path"))
@@ -281,8 +303,8 @@ namespace Spine {
 
 					return mesh;
 				}
-			case AttachmentType.skinnedmesh: {
-					SkinnedMeshAttachment mesh = attachmentLoader.NewSkinnedMeshAttachment(skin, name, path);
+			case AttachmentType.weightedmesh: {
+					WeightedMeshAttachment mesh = attachmentLoader.NewWeightedMeshAttachment(skin, name, path);
 					if (mesh == null) return null;
 
 					mesh.Path = path;
@@ -478,21 +500,6 @@ namespace Spine {
 							timelines.Add(timeline);
 							duration = Math.Max(duration, timeline.frames[timeline.FrameCount * 3 - 3]);
 
-						} else if (timelineName == "flipX" || timelineName == "flipY") {
-							bool x = timelineName == "flipX";
-							var timeline = x ? new FlipXTimeline(values.Count) : new FlipYTimeline(values.Count);
-							timeline.boneIndex = boneIndex;
-
-							String field = x ? "x" : "y";
-							int frameIndex = 0;
-							foreach (Dictionary<String, Object> valueMap in values) {
-								float time = (float)valueMap["time"];
-								timeline.SetFrame(frameIndex, time, valueMap.ContainsKey(field) ? (bool)valueMap[field] : false);
-								frameIndex++;
-							}
-							timelines.Add(timeline);
-							duration = Math.Max(duration, timeline.frames[timeline.FrameCount * 2 - 2]);
-
 						} else
 							throw new Exception("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")");
 					}
@@ -536,7 +543,7 @@ namespace Spine {
 							if (attachment is MeshAttachment)
 								vertexCount = ((MeshAttachment)attachment).vertices.Length;
 							else
-								vertexCount = ((SkinnedMeshAttachment)attachment).Weights.Length / 3 * 2;
+								vertexCount = ((WeightedMeshAttachment)attachment).Weights.Length / 3 * 2;
 
 							int frameIndex = 0;
 							foreach (Dictionary<String, Object> valueMap in values) {
@@ -619,11 +626,12 @@ namespace Spine {
 				foreach (Dictionary<String, Object> eventMap in eventsMap) {
 					EventData eventData = skeletonData.FindEvent((String)eventMap["name"]);
 					if (eventData == null) throw new Exception("Event not found: " + eventMap["name"]);
-					var e = new Event(eventData);
+					float time = (float)eventMap["time"];
+					var e = new Event(time, eventData);
 					e.Int = GetInt(eventMap, "int", eventData.Int);
 					e.Float = GetFloat(eventMap, "float", eventData.Float);
 					e.String = GetString(eventMap, "string", eventData.String);
-					timeline.SetFrame(frameIndex++, (float)eventMap["time"], e);
+					timeline.SetFrame(frameIndex++, time, e);
 				}
 				timelines.Add(timeline);
 				duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]);

+ 82 - 0
spine-csharp/src/TransformConstraint.cs

@@ -0,0 +1,82 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2.3
+ * 
+ * Copyright (c) 2013-2015, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to use, install, execute and perform the Spine
+ * Runtimes Software (the "Software") and derivative works solely for personal
+ * or internal use. Without the written permission of Esoteric Software (see
+ * Section 2 of the Spine Software License Agreement), you may not (a) modify,
+ * translate, adapt or otherwise create derivative works, improvements of the
+ * Software or develop new applications using the Software or (b) remove,
+ * delete, alter or obscure any trademarks or any copyright, trademark, patent
+ * or other intellectual property or proprietary rights notices on or in the
+ * Software, including any copy thereof. Redistributions in binary or source
+ * form must include this license and terms.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "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 ESOTERIC SOFTWARE 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 TransformConstraint : IUpdatable {
+		internal TransformConstraintData data;
+		internal Bone bone, target;
+		internal float translateMix;
+		internal float x, y;
+
+		public TransformConstraintData Data { get { return data; } }
+		public Bone Bone { get { return bone; } set { bone = value; } }
+		public Bone Target { get { return target; } set { target = value; } }
+		public float TranslateMix { get { return translateMix; } set { translateMix = value; } }
+		public float X { get { return x; } set { x = value; } }
+		public float Y { get { return y; } set { y = value; } }
+
+		public TransformConstraint (TransformConstraintData data, Skeleton skeleton) {
+			if (data == null) throw new ArgumentNullException("data cannot be null.");
+			if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null.");
+			this.data = data;
+			translateMix = data.translateMix;
+			x = data.x;
+			y = data.y;
+
+			if (skeleton != null) {
+				bone = skeleton.FindBone(data.bone.name);
+				target = skeleton.FindBone(data.target.name);
+			}
+		}
+
+		public void Update () {
+			Apply();
+		}
+
+		public void Apply () {
+			float translateMix = this.translateMix;
+			if (translateMix > 0) {
+				Bone bone = this.bone;
+				float tx, ty;
+				target.LocalToWorld(x, y, out tx, out ty);
+				bone.worldX += (tx - bone.worldX) * translateMix;
+				bone.worldY += (ty - bone.worldY) * translateMix;
+			}
+		}
+
+		override public String ToString () {
+			return data.name;
+		}
+	}
+}

+ 58 - 0
spine-csharp/src/TransformConstraintData.cs

@@ -0,0 +1,58 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2.3
+ * 
+ * Copyright (c) 2013-2015, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to use, install, execute and perform the Spine
+ * Runtimes Software (the "Software") and derivative works solely for personal
+ * or internal use. Without the written permission of Esoteric Software (see
+ * Section 2 of the Spine Software License Agreement), you may not (a) modify,
+ * translate, adapt or otherwise create derivative works, improvements of the
+ * Software or develop new applications using the Software or (b) remove,
+ * delete, alter or obscure any trademarks or any copyright, trademark, patent
+ * or other intellectual property or proprietary rights notices on or in the
+ * Software, including any copy thereof. Redistributions in binary or source
+ * form must include this license and terms.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "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 ESOTERIC SOFTWARE 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 TransformConstraintData {
+		internal String name;
+		internal BoneData bone, target;
+		internal float translateMix;
+		internal float x, y;
+
+		public String Name { get { return name; } }
+		public BoneData Bone { get { return bone; } set { bone = value; } }
+		public BoneData Target { get { return target; } set { target = value; } }
+		public float TranslateMix { get { return translateMix; } set { translateMix = value; } }
+		public float X { get { return x; } set { x = value; } }
+		public float Y { get { return y; } set { y = value; } }
+
+		public TransformConstraintData (String name) {
+			if (name == null) throw new ArgumentNullException("name cannot be null.");
+			this.name = name;
+		}
+
+		override public String ToString () {
+			return name;
+		}
+	}
+}

File diff ditekan karena terlalu besar
+ 204 - 115
spine-xna/example/data/raptor.json


TEMPAT SAMPAH
spine-xna/example/data/raptor.skel


+ 3 - 3
spine-xna/example/spine-xna-example.csproj

@@ -125,9 +125,9 @@
     <Content Include="GameThumbnail.png" />
   </ItemGroup>
   <ItemGroup>
-    <ProjectReference Include="..\..\spine-csharp\spine-csharp_xna.csproj">
-      <Project>{DF7AAFE1-401E-4278-8F9A-3DD2B8CC3D0A}</Project>
-      <Name>spine-csharp_xna</Name>
+    <ProjectReference Include="..\..\spine-csharp\spine-csharp.csproj">
+      <Project>{94144e22-2431-4a8f-ac04-dec22f7edd8f}</Project>
+      <Name>spine-csharp</Name>
     </ProjectReference>
     <ProjectReference Include="..\spine-xna.csproj">
       <Project>{7F8F2327-C016-49C8-BB4D-F3F77971961E}</Project>

+ 5 - 5
spine-xna/spine-xna.csproj

@@ -138,13 +138,13 @@
     </BootstrapperPackage>
   </ItemGroup>
   <ItemGroup>
-    <ProjectReference Include="..\spine-csharp\spine-csharp_xna.csproj">
-      <Project>{DF7AAFE1-401E-4278-8F9A-3DD2B8CC3D0A}</Project>
-      <Name>spine-csharp_xna</Name>
-    </ProjectReference>
+    <Compile Include="src\SkeletonMeshRenderer.cs" />
   </ItemGroup>
   <ItemGroup>
-    <Compile Include="src\SkeletonMeshRenderer.cs" />
+    <ProjectReference Include="..\spine-csharp\spine-csharp.csproj">
+      <Project>{94144e22-2431-4a8f-ac04-dec22f7edd8f}</Project>
+      <Name>spine-csharp</Name>
+    </ProjectReference>
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <Import Project="$(MSBuildExtensionsPath)\Microsoft\XNA Game Studio\Microsoft.Xna.GameStudio.targets" />

+ 32 - 7
spine-xna/spine-xna.sln

@@ -1,30 +1,55 @@
 
-Microsoft Visual Studio Solution File, Format Version 11.00
-# Visual C# Express 2010
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Express 2012 for Windows Desktop
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "spine-xna", "spine-xna.csproj", "{7F8F2327-C016-49C8-BB4D-F3F77971961E}"
+	ProjectSection(ProjectDependencies) = postProject
+		{94144E22-2431-4A8F-AC04-DEC22F7EDD8F} = {94144E22-2431-4A8F-AC04-DEC22F7EDD8F}
+	EndProjectSection
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "spine-xna-example", "example\spine-xna-example.csproj", "{29CC4385-294A-4885-A3E8-FD4825E0CFDD}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "spine-csharp_xna", "..\spine-csharp\spine-csharp_xna.csproj", "{DF7AAFE1-401E-4278-8F9A-3DD2B8CC3D0A}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "spine-csharp", "..\spine-csharp\spine-csharp.csproj", "{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Debug|Mixed Platforms = Debug|Mixed Platforms
 		Debug|x86 = Debug|x86
+		Release|Any CPU = Release|Any CPU
+		Release|Mixed Platforms = Release|Mixed Platforms
 		Release|x86 = Release|x86
 	EndGlobalSection
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{7F8F2327-C016-49C8-BB4D-F3F77971961E}.Debug|Any CPU.ActiveCfg = Debug|x86
+		{7F8F2327-C016-49C8-BB4D-F3F77971961E}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
+		{7F8F2327-C016-49C8-BB4D-F3F77971961E}.Debug|Mixed Platforms.Build.0 = Debug|x86
 		{7F8F2327-C016-49C8-BB4D-F3F77971961E}.Debug|x86.ActiveCfg = Debug|x86
 		{7F8F2327-C016-49C8-BB4D-F3F77971961E}.Debug|x86.Build.0 = Debug|x86
+		{7F8F2327-C016-49C8-BB4D-F3F77971961E}.Release|Any CPU.ActiveCfg = Release|x86
+		{7F8F2327-C016-49C8-BB4D-F3F77971961E}.Release|Mixed Platforms.ActiveCfg = Release|x86
+		{7F8F2327-C016-49C8-BB4D-F3F77971961E}.Release|Mixed Platforms.Build.0 = Release|x86
 		{7F8F2327-C016-49C8-BB4D-F3F77971961E}.Release|x86.ActiveCfg = Release|x86
 		{7F8F2327-C016-49C8-BB4D-F3F77971961E}.Release|x86.Build.0 = Release|x86
+		{29CC4385-294A-4885-A3E8-FD4825E0CFDD}.Debug|Any CPU.ActiveCfg = Debug|x86
+		{29CC4385-294A-4885-A3E8-FD4825E0CFDD}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
+		{29CC4385-294A-4885-A3E8-FD4825E0CFDD}.Debug|Mixed Platforms.Build.0 = Debug|x86
 		{29CC4385-294A-4885-A3E8-FD4825E0CFDD}.Debug|x86.ActiveCfg = Debug|x86
 		{29CC4385-294A-4885-A3E8-FD4825E0CFDD}.Debug|x86.Build.0 = Debug|x86
+		{29CC4385-294A-4885-A3E8-FD4825E0CFDD}.Release|Any CPU.ActiveCfg = Release|x86
+		{29CC4385-294A-4885-A3E8-FD4825E0CFDD}.Release|Mixed Platforms.ActiveCfg = Release|x86
+		{29CC4385-294A-4885-A3E8-FD4825E0CFDD}.Release|Mixed Platforms.Build.0 = Release|x86
 		{29CC4385-294A-4885-A3E8-FD4825E0CFDD}.Release|x86.ActiveCfg = Release|x86
 		{29CC4385-294A-4885-A3E8-FD4825E0CFDD}.Release|x86.Build.0 = Release|x86
-		{DF7AAFE1-401E-4278-8F9A-3DD2B8CC3D0A}.Debug|x86.ActiveCfg = Debug|x86
-		{DF7AAFE1-401E-4278-8F9A-3DD2B8CC3D0A}.Debug|x86.Build.0 = Debug|x86
-		{DF7AAFE1-401E-4278-8F9A-3DD2B8CC3D0A}.Release|x86.ActiveCfg = Release|x86
-		{DF7AAFE1-401E-4278-8F9A-3DD2B8CC3D0A}.Release|x86.Build.0 = Release|x86
+		{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+		{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+		{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Release|Any CPU.Build.0 = Release|Any CPU
+		{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+		{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+		{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Release|x86.ActiveCfg = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 2 - 2
spine-xna/src/SkeletonMeshRenderer.cs

@@ -189,8 +189,8 @@ namespace Spine {
 						itemVertices[ii].TextureCoordinate.X = uvs[v];
 						itemVertices[ii].TextureCoordinate.Y = uvs[v + 1];
 					}
-				} else if (attachment is SkinnedMeshAttachment) {
-					SkinnedMeshAttachment mesh = (SkinnedMeshAttachment)attachment;
+				} else if (attachment is WeightedMeshAttachment) {
+					WeightedMeshAttachment mesh = (WeightedMeshAttachment)attachment;
 					int vertexCount = mesh.UVs.Length;
 					if (vertices.Length < vertexCount) vertices = new float[vertexCount];
 					mesh.ComputeWorldVertices(slot, vertices);

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini