Browse Source

Updated spine-csharp to Spine v3.

NathanSweet 9 years ago
parent
commit
08b74f5b18

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

@@ -53,35 +53,91 @@
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <Compile Include="Properties\AssemblyInfo.cs" />
     <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\AtlasAttachmentLoader.cs" />
     <Compile Include="src\Attachments\Attachment.cs" />
     <Compile Include="src\Attachments\Attachment.cs" />
+    <Compile Include="src\Attachments\AttachmentLoader.cs" />
     <Compile Include="src\Attachments\AttachmentType.cs" />
     <Compile Include="src\Attachments\AttachmentType.cs" />
     <Compile Include="src\Attachments\BoundingBoxAttachment.cs" />
     <Compile Include="src\Attachments\BoundingBoxAttachment.cs" />
     <Compile Include="src\Attachments\MeshAttachment.cs" />
     <Compile Include="src\Attachments\MeshAttachment.cs" />
     <Compile Include="src\Attachments\RegionAttachment.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>
+  <ItemGroup />
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <!--
   <!--
       To modify your build process, add your task inside one of the targets below and uncomment it.
       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 Name="AfterBuild">
       </Target>
       </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];
 			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;
 			return attachment;
 		}
 		}
 
 
-		public SkinnedMeshAttachment NewSkinnedMeshAttachment (Skin skin, String name, String path) {
+		public WeightedMeshAttachment NewWeightedMeshAttachment (Skin skin, String name, String path) {
 			AtlasRegion region = FindRegion(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.RendererObject = region;
 			attachment.RegionU = region.u;
 			attachment.RegionU = region.u;
 			attachment.RegionV = region.v;
 			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);
 		MeshAttachment NewMeshAttachment (Skin skin, String name, String path);
 
 
 		/// <return>May be null to not load any attachment.</return>
 		/// <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>
 		/// <return>May be null to not load any attachment.</return>
 		BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, String name);
 		BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, String name);

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

@@ -31,6 +31,6 @@
 
 
 namespace Spine {
 namespace Spine {
 	public enum AttachmentType {
 	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>
 		/// <param name="worldVertices">Must have at least the same length as this attachment's vertices.</param>
 		public void ComputeWorldVertices (Bone bone, float[] worldVertices) {
 		public void ComputeWorldVertices (Bone bone, float[] worldVertices) {
 			float x = bone.skeleton.x + bone.worldX, y = bone.skeleton.y + bone.worldY;
 			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;
 			float[] vertices = this.vertices;
 			for (int i = 0, n = vertices.Length; i < n; i += 2) {
 			for (int i = 0, n = vertices.Length; i < n; i += 2) {
 				float px = vertices[i];
 				float px = vertices[i];

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

@@ -32,7 +32,7 @@
 using System;
 using System;
 
 
 namespace Spine {
 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 {
 	public class MeshAttachment : Attachment {
 		internal float[] vertices, uvs, regionUVs;
 		internal float[] vertices, uvs, regionUVs;
 		internal int[] triangles;
 		internal int[] triangles;
@@ -94,7 +94,7 @@ namespace Spine {
 		public void ComputeWorldVertices (Slot slot, float[] worldVertices) {
 		public void ComputeWorldVertices (Slot slot, float[] worldVertices) {
 			Bone bone = slot.bone;
 			Bone bone = slot.bone;
 			float x = bone.skeleton.x + bone.worldX, y = bone.skeleton.y + bone.worldY;
 			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;
 			float[] vertices = this.vertices;
 			int verticesCount = vertices.Length;
 			int verticesCount = vertices.Length;
 			if (slot.attachmentVerticesCount == verticesCount) vertices = slot.AttachmentVertices;
 			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) {
 		public void ComputeWorldVertices (Bone bone, float[] worldVertices) {
 			float x = bone.skeleton.x + bone.worldX, y = bone.skeleton.y + bone.worldY;
 			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;
 			float[] offset = this.offset;
 			worldVertices[X1] = offset[X1] * m00 + offset[Y1] * m01 + x;
 			worldVertices[X1] = offset[X1] * m00 + offset[Y1] * m01 + x;
 			worldVertices[Y1] = offset[X1] * m10 + offset[Y1] * m11 + y;
 			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;
 using System.Collections.Generic;
 
 
 namespace Spine {
 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 int[] bones;
 		internal float[] weights, uvs, regionUVs;
 		internal float[] weights, uvs, regionUVs;
 		internal int[] triangles;
 		internal int[] triangles;
@@ -72,7 +72,7 @@ namespace Spine {
 		public float Width { get; set; }
 		public float Width { get; set; }
 		public float Height { get; set; }
 		public float Height { get; set; }
 
 
-		public SkinnedMeshAttachment (string name)
+		public WeightedMeshAttachment (string name)
 			: base(name) {
 			: base(name) {
 		}
 		}
 
 
@@ -107,8 +107,8 @@ namespace Spine {
 					for (; v < nn; v++, b += 3) {
 					for (; v < nn; v++, b += 3) {
 						Bone bone = skeletonBones.Items[bones[v]];
 						Bone bone = skeletonBones.Items[bones[v]];
 						float vx = weights[b], vy = weights[b + 1], weight = weights[b + 2];
 						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] = wx + x;
 					worldVertices[w + 1] = wy + y;
 					worldVertices[w + 1] = wy + y;
@@ -121,8 +121,8 @@ namespace Spine {
 					for (; v < nn; v++, b += 3, f += 2) {
 					for (; v < nn; v++, b += 3, f += 2) {
 						Bone bone = skeletonBones.Items[bones[v]];
 						Bone bone = skeletonBones.Items[bones[v]];
 						float vx = weights[b] + ffd[f], vy = weights[b + 1] + ffd[f + 1], weight = weights[b + 2];
 						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] = wx + x;
 					worldVertices[w + 1] = wy + y;
 					worldVertices[w + 1] = wy + y;

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

@@ -33,18 +33,19 @@ using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 
 
 namespace Spine {
 namespace Spine {
-	public class Bone {
+    public class Bone : IUpdatable {
 		static public bool yDown;
 		static public bool yDown;
 
 
 		internal BoneData data;
 		internal BoneData data;
 		internal Skeleton skeleton;
 		internal Skeleton skeleton;
 		internal Bone parent;
 		internal Bone parent;
 		internal ExposedList<Bone> children = new ExposedList<Bone>();
 		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 BoneData Data { get { return data; } }
 		public Skeleton Skeleton { get { return skeleton; } }
 		public Skeleton Skeleton { get { return skeleton; } }
@@ -52,26 +53,28 @@ namespace Spine {
 		public ExposedList<Bone> Children { get { return children; } }
 		public ExposedList<Bone> Children { get { return children; } }
 		public float X { get { return x; } set { x = value; } }
 		public float X { get { return x; } set { x = value; } }
 		public float Y { get { return y; } set { y = 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; } }
 		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 ScaleX { get { return scaleX; } set { scaleX = value; } }
 		public float ScaleY { get { return scaleY; } set { scaleY = 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 WorldX { get { return worldX; } }
 		public float WorldY { get { return worldY; } }
 		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>
 		/// <param name="parent">May be null.</param>
 		public Bone (BoneData data, Skeleton skeleton, Bone parent) {
 		public Bone (BoneData data, Skeleton skeleton, Bone parent) {
@@ -83,80 +86,155 @@ namespace Spine {
 			SetToSetupPose();
 			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 () {
 		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;
 			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 () {
 		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 () {
 		override public String ToString () {

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

@@ -36,7 +36,6 @@ namespace Spine {
 		internal BoneData parent;
 		internal BoneData parent;
 		internal String name;
 		internal String name;
 		internal float length, x, y, rotation, scaleX = 1, scaleY = 1;
 		internal float length, x, y, rotation, scaleX = 1, scaleY = 1;
-		internal bool flipX, flipY;
 		internal bool inheritScale = true, inheritRotation = true;
 		internal bool inheritScale = true, inheritRotation = true;
 
 
 		/// <summary>May be null.</summary>
 		/// <summary>May be null.</summary>
@@ -48,8 +47,6 @@ namespace Spine {
 		public float Rotation { get { return rotation; } set { rotation = value; } }
 		public float Rotation { get { return rotation; } set { rotation = value; } }
 		public float ScaleX { get { return scaleX; } set { scaleX = value; } }
 		public float ScaleX { get { return scaleX; } set { scaleX = value; } }
 		public float ScaleY { get { return scaleY; } set { scaleY = 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 InheritScale { get { return inheritScale; } set { inheritScale = value; } }
 		public bool InheritRotation { get { return inheritRotation; } set { inheritRotation = 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 int Int { get; set; }
 		public float Float { get; set; }
 		public float Float { get; set; }
 		public String String { 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;
 			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;
 using System.Collections.Generic;
 
 
 namespace Spine {
 namespace Spine {
-	public class IkConstraint {
-		private const float radDeg = 180 / (float)Math.PI;
-
+	public class IkConstraint : IUpdatable {
 		internal IkConstraintData data;
 		internal IkConstraintData data;
 		internal ExposedList<Bone> bones = new ExposedList<Bone>();
 		internal ExposedList<Bone> bones = new ExposedList<Bone>();
 		internal Bone target;
 		internal Bone target;
@@ -61,15 +59,19 @@ namespace Spine {
 			target = skeleton.FindBone(data.target.name);
 			target = skeleton.FindBone(data.target.name);
 		}
 		}
 
 
-		public void apply () {
+		public void Update () {
+			Apply();
+		}
+
+		public void Apply () {
 			Bone target = this.target;
 			Bone target = this.target;
 			ExposedList<Bone> bones = this.bones;
 			ExposedList<Bone> bones = this.bones;
 			switch (bones.Count) {
 			switch (bones.Count) {
 			case 1:
 			case 1:
-				apply(bones.Items[0], target.worldX, target.worldY, mix);
+				Apply(bones.Items[0], target.worldX, target.worldY, mix);
 				break;
 				break;
 			case 2:
 			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;
 				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
 		/// <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>
 		/// 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 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
 		/// <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>
 		/// 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 {
 			} 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 {
 			} 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> slots;
 		internal ExposedList<Slot> drawOrder;
 		internal ExposedList<Slot> drawOrder;
 		internal ExposedList<IkConstraint> ikConstraints;
 		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 Skin skin;
 		internal float r = 1, g = 1, b = 1, a = 1;
 		internal float r = 1, g = 1, b = 1, a = 1;
 		internal float time;
 		internal float time;
@@ -89,81 +90,62 @@ namespace Spine {
 				drawOrder.Add(slot);
 				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();
 			UpdateCache();
 			UpdateWorldTransform();
 			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 () {
 		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<IkConstraint> ikConstraints = this.ikConstraints;
+			ExposedList<TransformConstraint> transformConstraints = this.transformConstraints;
 			int ikConstraintsCount = ikConstraints.Count;
 			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++) {
 			for (int i = 0, n = bones.Count; i < n; i++) {
 				Bone bone = bones.Items[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>
 		/// <summary>Updates the world transform for each bone and applies IK constraints.</summary>
 		public void UpdateWorldTransform () {
 		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 () {
 		public void SetToSetupPose () {
 			SetBonesToSetupPose();
 			SetBonesToSetupPose();
 			SetSlotsToSetupPose();
 			SetSlotsToSetupPose();
 		}
 		}
 
 
+		/// <summary>Sets the bones and constraints to their setup pose values.</summary>
 		public void SetBonesToSetupPose () {
 		public void SetBonesToSetupPose () {
 			ExposedList<Bone> bones = this.bones;
 			ExposedList<Bone> bones = this.bones;
 			for (int i = 0, n = bones.Count; i < n; i++)
 			for (int i = 0, n = bones.Count; i < n; i++)
@@ -171,9 +153,17 @@ namespace Spine {
 
 
 			ExposedList<IkConstraint> ikConstraints = this.ikConstraints;
 			ExposedList<IkConstraint> ikConstraints = this.ikConstraints;
 			for (int i = 0, n = ikConstraints.Count; i < n; i++) {
 			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. */
 		/** @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;
 			ExposedList<IkConstraint> ikConstraints = this.ikConstraints;
 			for (int i = 0, n = ikConstraints.Count; i < n; i++) {
 			for (int i = 0, n = ikConstraints.Count; i < n; i++) {
 				IkConstraint ikConstraint = ikConstraints.Items[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;
 			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_TRANSLATE = 2;
 		public const int TIMELINE_ATTACHMENT = 3;
 		public const int TIMELINE_ATTACHMENT = 3;
 		public const int TIMELINE_COLOR = 4;
 		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_LINEAR = 0;
 		public const int CURVE_STEPPED = 1;
 		public const int CURVE_STEPPED = 1;
@@ -130,8 +128,6 @@ namespace Spine {
 				boneData.scaleY = ReadFloat(input);
 				boneData.scaleY = ReadFloat(input);
 				boneData.rotation = ReadFloat(input);
 				boneData.rotation = ReadFloat(input);
 				boneData.length = ReadFloat(input) * scale;
 				boneData.length = ReadFloat(input) * scale;
-				boneData.flipX = ReadBoolean(input);
-				boneData.flipY = ReadBoolean(input);
 				boneData.inheritScale = ReadBoolean(input);
 				boneData.inheritScale = ReadBoolean(input);
 				boneData.inheritRotation = ReadBoolean(input);
 				boneData.inheritRotation = ReadBoolean(input);
 				if (nonessential) ReadInt(input); // Skip bone color.
 				if (nonessential) ReadInt(input); // Skip bone color.
@@ -149,6 +145,17 @@ namespace Spine {
 				skeletonData.ikConstraints.Add(ikConstraintData);
 				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.
 			// Slots.
 			for (int i = 0, n = ReadInt(input, true); i < n; i++) {
 			for (int i = 0, n = ReadInt(input, true); i < n; i++) {
 				String slotName = ReadString(input);
 				String slotName = ReadString(input);
@@ -269,10 +276,10 @@ namespace Spine {
 					}
 					}
 					return mesh;
 					return mesh;
 				}
 				}
-			case AttachmentType.skinnedmesh: {
+			case AttachmentType.weightedmesh: {
 					String path = ReadString(input);
 					String path = ReadString(input);
 					if (path == null) path = name;
 					if (path == null) path = name;
-					SkinnedMeshAttachment mesh = attachmentLoader.NewSkinnedMeshAttachment(skin, name, path);
+					WeightedMeshAttachment mesh = attachmentLoader.NewWeightedMeshAttachment(skin, name, path);
 					if (mesh == null) return null;
 					if (mesh == null) return null;
 					mesh.Path = path;
 					mesh.Path = path;
 					float[] uvs = ReadFloatArray(input, 1);
 					float[] uvs = ReadFloatArray(input, 1);
@@ -422,17 +429,6 @@ namespace Spine {
 							duration = Math.Max(duration, timeline.frames[frameCount * 3 - 3]);
 							duration = Math.Max(duration, timeline.frames[frameCount * 3 - 3]);
 							break;
 							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)
 							if (attachment is MeshAttachment)
 								vertexCount = ((MeshAttachment)attachment).vertices.Length;
 								vertexCount = ((MeshAttachment)attachment).vertices.Length;
 							else
 							else
-								vertexCount = ((SkinnedMeshAttachment)attachment).weights.Length / 3 * 2;
+								vertexCount = ((WeightedMeshAttachment)attachment).weights.Length / 3 * 2;
 
 
 							int end = ReadInt(input, true);
 							int end = ReadInt(input, true);
 							if (end == 0) {
 							if (end == 0) {
@@ -544,7 +540,7 @@ namespace Spine {
 				for (int i = 0; i < eventCount; i++) {
 				for (int i = 0; i < eventCount; i++) {
 					float time = ReadFloat(input);
 					float time = ReadFloat(input);
 					EventData eventData = skeletonData.events.Items[ReadInt(input, true)];
 					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.Int = ReadInt(input, false);
 					e.Float = ReadFloat(input);
 					e.Float = ReadFloat(input);
 					e.String = ReadBoolean(input) ? ReadString(input) : eventData.String;
 					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<EventData> events = new ExposedList<EventData>();
 		internal ExposedList<Animation> animations = new ExposedList<Animation>();
 		internal ExposedList<Animation> animations = new ExposedList<Animation>();
 		internal ExposedList<IkConstraintData> ikConstraints = new ExposedList<IkConstraintData>();
 		internal ExposedList<IkConstraintData> ikConstraints = new ExposedList<IkConstraintData>();
+        internal ExposedList<TransformConstraintData> transformConstraints = new ExposedList<TransformConstraintData>();
 		internal float width, height;
 		internal float width, height;
 		internal String version, hash, imagesPath;
 		internal String version, hash, imagesPath;
 
 
@@ -140,12 +141,25 @@ namespace Spine {
 		// --- IK constraints.
 		// --- IK constraints.
 
 
 		/// <returns>May be null.</returns>
 		/// <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;
 			ExposedList<IkConstraintData> ikConstraints = this.ikConstraints;
 			for (int i = 0, n = ikConstraints.Count; i < n; i++) {
 			for (int i = 0, n = ikConstraints.Count; i < n; i++) {
 				IkConstraintData ikConstraint = ikConstraints.Items[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;
 			return null;
 		}
 		}

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

@@ -118,8 +118,6 @@ namespace Spine {
 				boneData.rotation = GetFloat(boneMap, "rotation", 0);
 				boneData.rotation = GetFloat(boneMap, "rotation", 0);
 				boneData.scaleX = GetFloat(boneMap, "scaleX", 1);
 				boneData.scaleX = GetFloat(boneMap, "scaleX", 1);
 				boneData.scaleY = GetFloat(boneMap, "scaleY", 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.inheritScale = GetBoolean(boneMap, "inheritScale", true);
 				boneData.inheritRotation = GetBoolean(boneMap, "inheritRotation", true);
 				boneData.inheritRotation = GetBoolean(boneMap, "inheritRotation", true);
 				skeletonData.bones.Add(boneData);
 				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.
 			// Slots.
 			if (root.ContainsKey("slots")) {
 			if (root.ContainsKey("slots")) {
 				foreach (Dictionary<String, Object> slotMap in (List<Object>)root["slots"]) {
 				foreach (Dictionary<String, Object> slotMap in (List<Object>)root["slots"]) {
@@ -226,8 +245,11 @@ namespace Spine {
 				name = (String)map["name"];
 				name = (String)map["name"];
 
 
 			var type = AttachmentType.region;
 			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;
 			String path = name;
 			if (map.ContainsKey("path"))
 			if (map.ContainsKey("path"))
@@ -281,8 +303,8 @@ namespace Spine {
 
 
 					return mesh;
 					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;
 					if (mesh == null) return null;
 
 
 					mesh.Path = path;
 					mesh.Path = path;
@@ -478,21 +500,6 @@ namespace Spine {
 							timelines.Add(timeline);
 							timelines.Add(timeline);
 							duration = Math.Max(duration, timeline.frames[timeline.FrameCount * 3 - 3]);
 							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
 						} else
 							throw new Exception("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")");
 							throw new Exception("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")");
 					}
 					}
@@ -536,7 +543,7 @@ namespace Spine {
 							if (attachment is MeshAttachment)
 							if (attachment is MeshAttachment)
 								vertexCount = ((MeshAttachment)attachment).vertices.Length;
 								vertexCount = ((MeshAttachment)attachment).vertices.Length;
 							else
 							else
-								vertexCount = ((SkinnedMeshAttachment)attachment).Weights.Length / 3 * 2;
+								vertexCount = ((WeightedMeshAttachment)attachment).Weights.Length / 3 * 2;
 
 
 							int frameIndex = 0;
 							int frameIndex = 0;
 							foreach (Dictionary<String, Object> valueMap in values) {
 							foreach (Dictionary<String, Object> valueMap in values) {
@@ -619,11 +626,12 @@ namespace Spine {
 				foreach (Dictionary<String, Object> eventMap in eventsMap) {
 				foreach (Dictionary<String, Object> eventMap in eventsMap) {
 					EventData eventData = skeletonData.FindEvent((String)eventMap["name"]);
 					EventData eventData = skeletonData.FindEvent((String)eventMap["name"]);
 					if (eventData == null) throw new Exception("Event not found: " + 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.Int = GetInt(eventMap, "int", eventData.Int);
 					e.Float = GetFloat(eventMap, "float", eventData.Float);
 					e.Float = GetFloat(eventMap, "float", eventData.Float);
 					e.String = GetString(eventMap, "string", eventData.String);
 					e.String = GetString(eventMap, "string", eventData.String);
-					timeline.SetFrame(frameIndex++, (float)eventMap["time"], e);
+					timeline.SetFrame(frameIndex++, time, e);
 				}
 				}
 				timelines.Add(timeline);
 				timelines.Add(timeline);
 				duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]);
 				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 suppressed because it is too large
+ 204 - 115
spine-xna/example/data/raptor.json


BIN
spine-xna/example/data/raptor.skel


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

@@ -125,9 +125,9 @@
     <Content Include="GameThumbnail.png" />
     <Content Include="GameThumbnail.png" />
   </ItemGroup>
   </ItemGroup>
   <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>
     <ProjectReference Include="..\spine-xna.csproj">
     <ProjectReference Include="..\spine-xna.csproj">
       <Project>{7F8F2327-C016-49C8-BB4D-F3F77971961E}</Project>
       <Project>{7F8F2327-C016-49C8-BB4D-F3F77971961E}</Project>

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

@@ -138,13 +138,13 @@
     </BootstrapperPackage>
     </BootstrapperPackage>
   </ItemGroup>
   </ItemGroup>
   <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>
   <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>
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <Import Project="$(MSBuildExtensionsPath)\Microsoft\XNA Game Studio\Microsoft.Xna.GameStudio.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}"
 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
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "spine-xna-example", "example\spine-xna-example.csproj", "{29CC4385-294A-4885-A3E8-FD4825E0CFDD}"
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "spine-xna-example", "example\spine-xna-example.csproj", "{29CC4385-294A-4885-A3E8-FD4825E0CFDD}"
 EndProject
 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
 EndProject
 Global
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Debug|Mixed Platforms = Debug|Mixed Platforms
 		Debug|x86 = Debug|x86
 		Debug|x86 = Debug|x86
+		Release|Any CPU = Release|Any CPU
+		Release|Mixed Platforms = Release|Mixed Platforms
 		Release|x86 = Release|x86
 		Release|x86 = Release|x86
 	EndGlobalSection
 	EndGlobalSection
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
 	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.ActiveCfg = Debug|x86
 		{7F8F2327-C016-49C8-BB4D-F3F77971961E}.Debug|x86.Build.0 = 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.ActiveCfg = Release|x86
 		{7F8F2327-C016-49C8-BB4D-F3F77971961E}.Release|x86.Build.0 = 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.ActiveCfg = Debug|x86
 		{29CC4385-294A-4885-A3E8-FD4825E0CFDD}.Debug|x86.Build.0 = 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.ActiveCfg = Release|x86
 		{29CC4385-294A-4885-A3E8-FD4825E0CFDD}.Release|x86.Build.0 = 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
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 		HideSolutionNode = FALSE

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

@@ -189,8 +189,8 @@ namespace Spine {
 						itemVertices[ii].TextureCoordinate.X = uvs[v];
 						itemVertices[ii].TextureCoordinate.X = uvs[v];
 						itemVertices[ii].TextureCoordinate.Y = uvs[v + 1];
 						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;
 					int vertexCount = mesh.UVs.Length;
 					if (vertices.Length < vertexCount) vertices = new float[vertexCount];
 					if (vertices.Length < vertexCount) vertices = new float[vertexCount];
 					mesh.ComputeWorldVertices(slot, vertices);
 					mesh.ComputeWorldVertices(slot, vertices);

Some files were not shown because too many files changed in this diff