diff --git a/spine-csharp/spine-csharp.csproj b/spine-csharp/spine-csharp.csproj
index c5fc23107..1c7ab2fd6 100644
--- a/spine-csharp/spine-csharp.csproj
+++ b/spine-csharp/spine-csharp.csproj
@@ -53,35 +53,91 @@
-
-
-
-
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
-
+
\ No newline at end of file
diff --git a/spine-csharp/src/Animation.cs b/spine-csharp/src/Animation.cs
index eb0b695c9..6cc1a025a 100644
--- a/spine-csharp/src/Animation.cs
+++ b/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];
- }
-
- /// Sets the time and value of the specified keyframe.
- 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 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;
- }
- }
}
diff --git a/spine-csharp/src/Attachments/AtlasAttachmentLoader.cs b/spine-csharp/src/Attachments/AtlasAttachmentLoader.cs
index 9fd84ab1f..f05de4b67 100644
--- a/spine-csharp/src/Attachments/AtlasAttachmentLoader.cs
+++ b/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;
diff --git a/spine-csharp/src/Attachments/AttachmentLoader.cs b/spine-csharp/src/Attachments/AttachmentLoader.cs
index 69f462748..5574e3210 100644
--- a/spine-csharp/src/Attachments/AttachmentLoader.cs
+++ b/spine-csharp/src/Attachments/AttachmentLoader.cs
@@ -40,7 +40,7 @@ namespace Spine {
MeshAttachment NewMeshAttachment (Skin skin, String name, String path);
/// May be null to not load any attachment.
- SkinnedMeshAttachment NewSkinnedMeshAttachment (Skin skin, String name, String path);
+ WeightedMeshAttachment NewWeightedMeshAttachment (Skin skin, String name, String path);
/// May be null to not load any attachment.
BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, String name);
diff --git a/spine-csharp/src/Attachments/AttachmentType.cs b/spine-csharp/src/Attachments/AttachmentType.cs
index 2f5d1c8fd..c1187915c 100644
--- a/spine-csharp/src/Attachments/AttachmentType.cs
+++ b/spine-csharp/src/Attachments/AttachmentType.cs
@@ -31,6 +31,6 @@
namespace Spine {
public enum AttachmentType {
- region, boundingbox, mesh, skinnedmesh
+ region, boundingbox, mesh, weightedmesh
}
}
diff --git a/spine-csharp/src/Attachments/BoundingBoxAttachment.cs b/spine-csharp/src/Attachments/BoundingBoxAttachment.cs
index 3a8be7934..ccf179edd 100644
--- a/spine-csharp/src/Attachments/BoundingBoxAttachment.cs
+++ b/spine-csharp/src/Attachments/BoundingBoxAttachment.cs
@@ -45,10 +45,10 @@ namespace Spine {
/// Must have at least the same length as this attachment's vertices.
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];
diff --git a/spine-csharp/src/Attachments/MeshAttachment.cs b/spine-csharp/src/Attachments/MeshAttachment.cs
index 488b76b14..de4d569b8 100644
--- a/spine-csharp/src/Attachments/MeshAttachment.cs
+++ b/spine-csharp/src/Attachments/MeshAttachment.cs
@@ -32,7 +32,7 @@
using System;
namespace Spine {
- /// Attachment that displays a texture region.
+ /// Attachment that displays a texture region using a mesh.
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;
diff --git a/spine-csharp/src/Attachments/RegionAttachment.cs b/spine-csharp/src/Attachments/RegionAttachment.cs
index 72bdb058e..dad5e1cc8 100644
--- a/spine-csharp/src/Attachments/RegionAttachment.cs
+++ b/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;
diff --git a/spine-csharp/src/Attachments/SkinnedMeshAttachment.cs b/spine-csharp/src/Attachments/WeightedMeshAttachment.cs
similarity index 92%
rename from spine-csharp/src/Attachments/SkinnedMeshAttachment.cs
rename to spine-csharp/src/Attachments/WeightedMeshAttachment.cs
index 1e99fab38..61a039362 100644
--- a/spine-csharp/src/Attachments/SkinnedMeshAttachment.cs
+++ b/spine-csharp/src/Attachments/WeightedMeshAttachment.cs
@@ -33,8 +33,8 @@ using System;
using System.Collections.Generic;
namespace Spine {
- /// Attachment that displays a texture region.
- public class SkinnedMeshAttachment : Attachment {
+ /// Attachment that displays a texture region using a mesh which can be deformed by bones.
+ 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;
diff --git a/spine-csharp/src/Bone.cs b/spine-csharp/src/Bone.cs
index 6d92871a8..596130156 100644
--- a/spine-csharp/src/Bone.cs
+++ b/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 children = new ExposedList();
- 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 Children { get { return children; } }
public float X { get { return x; } set { x = value; } }
public float Y { get { return y; } set { y = value; } }
- /// The forward kinetics rotation.
public float Rotation { get { return rotation; } set { rotation = value; } }
- /// The inverse kinetics rotation, as calculated by any IK constraints.
- public float RotationIK { get { return rotationIK; } set { rotationIK = value; } }
+ /// The rotation, as calculated by any constraints.
+ public float AppliedRotation { get { return appliedRotation; } set { appliedRotation = value; } }
+ /// The scale X, as calculated by any constraints.
+ public float AppliedScaleX { get { return appliedScaleX; } set { appliedScaleX = value; } }
+ /// The scale Y, as calculated by any constraints.
+ 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; } }
/// May be null.
public Bone (BoneData data, Skeleton skeleton, Bone parent) {
@@ -83,80 +86,155 @@ namespace Spine {
SetToSetupPose();
}
- /// Computes the world SRT using the parent bone and the local SRT.
+ /// Computes the world SRT using the parent bone and this bone's local SRT.
public void UpdateWorldTransform () {
- 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;
- }
- 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;
- }
+ UpdateWorldTransform(x, y, rotation, scaleX, scaleY);
}
+ /// Computes the world SRT using the parent bone and the specified local SRT.
+ 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;
+ 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 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 () {
diff --git a/spine-csharp/src/BoneData.cs b/spine-csharp/src/BoneData.cs
index cc1ebacec..29214e2f6 100644
--- a/spine-csharp/src/BoneData.cs
+++ b/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;
/// May be null.
@@ -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; } }
diff --git a/spine-csharp/src/Event.cs b/spine-csharp/src/Event.cs
index f60710448..057e4e7aa 100644
--- a/spine-csharp/src/Event.cs
+++ b/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;
}
diff --git a/spine-csharp/src/IUpdatable.cs b/spine-csharp/src/IUpdatable.cs
new file mode 100644
index 000000000..824c9a560
--- /dev/null
+++ b/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 ();
+ }
+}
diff --git a/spine-csharp/src/IkConstraint.cs b/spine-csharp/src/IkConstraint.cs
index e5b7bc9d4..0a4ec644f 100644
--- a/spine-csharp/src/IkConstraint.cs
+++ b/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 bones = new ExposedList();
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 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 {
/// 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.
- 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);
}
/// 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.
- /// Any descendant bone of the parent.
- 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;
+ /// A direct descendant of the parent bone.
+ 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 (child.parent == parent) {
- positionX = child.x;
- positionY = child.y;
+ if (psy < 0) {
+ psy = -psy;
+ sign2 = -sign2;
+ }
+ 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);
}
}
}
diff --git a/spine-csharp/src/MathUtils.cs b/spine-csharp/src/MathUtils.cs
new file mode 100644
index 000000000..58d3606a5
--- /dev/null
+++ b/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];
+ }
+
+ /// 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).
+ 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;
+ }
+ }
+}
diff --git a/spine-csharp/src/Skeleton.cs b/spine-csharp/src/Skeleton.cs
index 0c97e1042..308d5606c 100644
--- a/spine-csharp/src/Skeleton.cs
+++ b/spine-csharp/src/Skeleton.cs
@@ -39,7 +39,8 @@ namespace Spine {
internal ExposedList slots;
internal ExposedList drawOrder;
internal ExposedList ikConstraints;
- private ExposedList> boneCache = new ExposedList>();
+ internal ExposedList transformConstraints;
+ private ExposedList updateCache = new ExposedList();
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(data.ikConstraints.Count);
- foreach (IkConstraintData ikConstraintData in data.ikConstraints)
- ikConstraints.Add(new IkConstraint(ikConstraintData, this));
+ ikConstraints = new ExposedList(data.ikConstraints.Count);
+ foreach (IkConstraintData ikConstraintData in data.ikConstraints)
+ ikConstraints.Add(new IkConstraint(ikConstraintData, this));
+
+ transformConstraints = new ExposedList(data.transformConstraints.Count);
+ foreach (TransformConstraintData transformConstraintData in data.transformConstraints)
+ transformConstraints.Add(new TransformConstraint(transformConstraintData, this));
UpdateCache();
UpdateWorldTransform();
}
- /// Caches information about bones and IK constraints. Must be called if bones or IK constraints are added or
- /// removed.
+ /// Caches information about bones and constraints. Must be called if bones or constraints are added
+ /// or removed.
public void UpdateCache () {
- ExposedList> boneCache = this.boneCache;
+ ExposedList bones = this.bones;
+ ExposedList updateCache = this.updateCache;
ExposedList ikConstraints = this.ikConstraints;
+ ExposedList 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());
-
- ExposedList 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;
+ }
+ }
}
}
/// Updates the world transform for each bone and applies IK constraints.
public void UpdateWorldTransform () {
- ExposedList bones = this.bones;
- for (int ii = 0, nn = bones.Count; ii < nn; ii++) {
- Bone bone = bones.Items[ii];
- bone.rotationIK = bone.rotation;
- }
- ExposedList> boneCache = this.boneCache;
- ExposedList ikConstraints = this.ikConstraints;
- int i = 0, last = boneCache.Count - 1;
- while (true) {
- ExposedList 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 updateCache = this.updateCache;
+ for (int i = 0, n = updateCache.Count; i < n; i++)
+ updateCache.Items[i].Update();
}
- /// Sets the bones and slots to their setup pose values.
+ /// Sets the bones, constraints, and slots to their setup pose values.
public void SetToSetupPose () {
SetBonesToSetupPose();
SetSlotsToSetupPose();
}
+ /// Sets the bones and constraints to their setup pose values.
public void SetBonesToSetupPose () {
ExposedList bones = this.bones;
for (int i = 0, n = bones.Count; i < n; i++)
@@ -171,9 +153,17 @@ namespace Spine {
ExposedList 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 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 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 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;
}
diff --git a/spine-csharp/src/SkeletonBinary.cs b/spine-csharp/src/SkeletonBinary.cs
index f72bb83ae..e7e9c31eb 100644
--- a/spine-csharp/src/SkeletonBinary.cs
+++ b/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;
diff --git a/spine-csharp/src/SkeletonData.cs b/spine-csharp/src/SkeletonData.cs
index f7b41a35a..92f149640 100644
--- a/spine-csharp/src/SkeletonData.cs
+++ b/spine-csharp/src/SkeletonData.cs
@@ -42,6 +42,7 @@ namespace Spine {
internal ExposedList events = new ExposedList();
internal ExposedList animations = new ExposedList();
internal ExposedList ikConstraints = new ExposedList();
+ internal ExposedList transformConstraints = new ExposedList();
internal float width, height;
internal String version, hash, imagesPath;
@@ -140,12 +141,25 @@ namespace Spine {
// --- IK constraints.
/// May be null.
- 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 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.
+
+ /// May be null.
+ public TransformConstraintData FindTransformConstraint (String constraintName) {
+ if (constraintName == null) throw new ArgumentNullException("constraintName cannot be null.");
+ ExposedList 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;
}
diff --git a/spine-csharp/src/SkeletonJson.cs b/spine-csharp/src/SkeletonJson.cs
index 17d2523f9..8388100f8 100644
--- a/spine-csharp/src/SkeletonJson.cs
+++ b/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 transformMap in (List