diff --git a/.gitignore b/.gitignore
index 48c4c8354..1d65a51c3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,7 @@ target
*.user
.DS_Store
+
.idea/
build/
@@ -39,7 +40,9 @@ spine-cocos2d-iphone/spine-cocos2d-iphone-ios.xcodeproj/project.xcworkspace/xcsh
spine-csharp/bin
spine-csharp/obj
+spine-csharp/src/*.meta
spine-csharp/src/*.cs.meta
+spine-csharp/src/Attachments/*.cs.meta
spine-monogame/xamarinstudio-ios/src/bin
spine-monogame/xamarinstudio-ios/src/obj
diff --git a/spine-csharp/README.md b/spine-csharp/README.md
index 5176e7000..f5f25a977 100644
--- a/spine-csharp/README.md
+++ b/spine-csharp/README.md
@@ -10,14 +10,14 @@ The Spine Runtimes are developed with the intent to be used with data exported f
## Spine version
-spine-csharp works with data exported from Spine 3.2.01. Updating spine-csharp to [v3.3](https://github.com/EsotericSoftware/spine-runtimes/issues/613) is in progress.
+spine-csharp works with data exported from Spine 3.3.07.
spine-csharp supports all Spine features.
## Setup
1. Download the Spine Runtimes source using [git](https://help.github.com/articles/set-up-git) or by downloading it [as a zip](https://github.com/EsotericSoftware/spine-runtimes/archive/master.zip).
-1. Open the `spine-csharp.sln` Visual C# 2010 Express project file.
+1. Open the `spine-csharp.sln` Visual Studio 2015 Community project file.
Alternatively, the contents of the `spine-csharp/src` directory can be copied into your project.
diff --git a/spine-csharp/spine-csharp.csproj b/spine-csharp/spine-csharp.csproj
index 8491dd148..b4916ee9b 100644
--- a/spine-csharp/spine-csharp.csproj
+++ b/spine-csharp/spine-csharp.csproj
@@ -70,10 +70,10 @@
-
+
-
+
Code
@@ -105,6 +105,8 @@
Code
+
+
Code
diff --git a/spine-csharp/src/Animation.cs b/spine-csharp/src/Animation.cs
index 09a1f72ad..e56e4ae33 100644
--- a/spine-csharp/src/Animation.cs
+++ b/spine-csharp/src/Animation.cs
@@ -43,8 +43,8 @@ namespace Spine {
public float Duration { get { return duration; } set { duration = value; } }
public Animation (String name, ExposedList timelines, float duration) {
- if (name == null) throw new ArgumentNullException("name cannot be null.");
- if (timelines == null) throw new ArgumentNullException("timelines cannot be null.");
+ if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
+ if (timelines == null) throw new ArgumentNullException("timelines", "timelines cannot be null.");
this.name = name;
this.timelines = timelines;
this.duration = duration;
@@ -52,9 +52,9 @@ namespace Spine {
/// Poses the skeleton at the specified time for this animation.
/// The last time the animation was applied.
- /// Any triggered events are added.
+ /// Any triggered events are added. May be null.
public void Apply (Skeleton skeleton, float lastTime, float time, bool loop, ExposedList events) {
- if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null.");
+ if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null.");
if (loop && duration != 0) {
time %= duration;
@@ -68,10 +68,10 @@ namespace Spine {
/// Poses the skeleton at the specified time for this animation mixed with the current pose.
/// The last time the animation was applied.
- /// Any triggered events are added.
+ /// Any triggered events are added. May be null.
/// The amount of this animation that affects the current pose.
public void Mix (Skeleton skeleton, float lastTime, float time, bool loop, ExposedList events, float alpha) {
- if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null.");
+ if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null.");
if (loop && duration != 0) {
time %= duration;
@@ -131,12 +131,13 @@ namespace Spine {
/// Base class for frames that use an interpolation bezier curve.
abstract public class CurveTimeline : Timeline {
protected const float LINEAR = 0, STEPPED = 1, BEZIER = 2;
- protected const int BEZIER_SEGMENTS = 10, BEZIER_SIZE = BEZIER_SEGMENTS * 2 - 1;
+ protected const int BEZIER_SIZE = 10 * 2 - 1;
private float[] curves; // type, x, y, ...
public int FrameCount { get { return curves.Length / BEZIER_SIZE + 1; } }
public CurveTimeline (int frameCount) {
+ if (frameCount <= 0) throw new ArgumentException("frameCount must be > 0: " + frameCount, "frameCount");
curves = new float[(frameCount - 1) * BEZIER_SIZE];
}
@@ -154,12 +155,10 @@ namespace Spine {
/// cx1 and cx2 are from 0 to 1, representing the percent of time between the two keyframes. cy1 and cy2 are the percent of
/// the difference between the keyframe's values.
public void SetCurve (int frameIndex, float cx1, float cy1, float cx2, float cy2) {
- float subdiv1 = 1f / BEZIER_SEGMENTS, subdiv2 = subdiv1 * subdiv1, subdiv3 = subdiv2 * subdiv1;
- float pre1 = 3 * subdiv1, pre2 = 3 * subdiv2, pre4 = 6 * subdiv2, pre5 = 6 * subdiv3;
- float tmp1x = -cx1 * 2 + cx2, tmp1y = -cy1 * 2 + cy2, tmp2x = (cx1 - cx2) * 3 + 1, tmp2y = (cy1 - cy2) * 3 + 1;
- float dfx = cx1 * pre1 + tmp1x * pre2 + tmp2x * subdiv3, dfy = cy1 * pre1 + tmp1y * pre2 + tmp2y * subdiv3;
- float ddfx = tmp1x * pre4 + tmp2x * pre5, ddfy = tmp1y * pre4 + tmp2y * pre5;
- float dddfx = tmp2x * pre5, dddfy = tmp2y * pre5;
+ float tmpx = (-cx1 * 2 + cx2) * 0.03f, tmpy = (-cy1 * 2 + cy2) * 0.03f;
+ float dddfx = ((cx1 - cx2) * 3 + 1) * 0.006f, dddfy = ((cy1 - cy2) * 3 + 1) * 0.006f;
+ float ddfx = tmpx * 2 + dddfx, ddfy = tmpy * 2 + dddfy;
+ float dfx = cx1 * 0.3f + tmpx + dddfx * 0.16666667f, dfy = cy1 * 0.3f + tmpy + dddfy * 0.16666667f;
int i = frameIndex * BEZIER_SIZE;
float[] curves = this.curves;
@@ -179,6 +178,7 @@ namespace Spine {
}
public float GetCurvePercent (int frameIndex, float percent) {
+ percent = MathUtils.Clamp (percent, 0, 1);
float[] curves = this.curves;
int i = frameIndex * BEZIER_SIZE;
float type = curves[i];
@@ -209,8 +209,9 @@ namespace Spine {
}
public class RotateTimeline : CurveTimeline {
- internal const int PREV_TIME = -2;
- internal const int VALUE = 1;
+ public const int ENTRIES = 2;
+ internal const int PREV_TIME = -2, PREV_ROTATION = -1;
+ internal const int ROTATION = 1;
internal int boneIndex;
internal float[] frames;
@@ -224,10 +225,10 @@ namespace Spine {
}
/// Sets the time and value of the specified keyframe.
- public void SetFrame (int frameIndex, float time, float angle) {
- frameIndex *= 2;
+ public void SetFrame (int frameIndex, float time, float degrees) {
+ frameIndex <<= 1;
frames[frameIndex] = time;
- frames[frameIndex + 1] = angle;
+ frames[frameIndex + ROTATION] = degrees;
}
override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha) {
@@ -238,8 +239,8 @@ namespace Spine {
float amount;
- if (time >= frames[frames.Length - 2]) { // Time is after last frame.
- amount = bone.data.rotation + frames[frames.Length - 1] - bone.rotation;
+ if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
+ amount = bone.data.rotation + frames[frames.Length + PREV_ROTATION] - bone.rotation;
while (amount > 180)
amount -= 360;
while (amount < -180)
@@ -249,18 +250,17 @@ namespace Spine {
}
// Interpolate between the previous frame and the current frame.
- int frame = Animation.binarySearch(frames, time, 2);
- float prevFrameValue = frames[frame - 1];
+ int frame = Animation.binarySearch(frames, time, ENTRIES);
+ float prevRotation = frames[frame + PREV_ROTATION];
float frameTime = frames[frame];
- float percent = 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime);
- percent = GetCurvePercent((frame >> 1) - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
+ float percent = GetCurvePercent((frame >> 1) - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
- amount = frames[frame + VALUE] - prevFrameValue;
+ amount = frames[frame + ROTATION] - prevRotation;
while (amount > 180)
amount -= 360;
while (amount < -180)
amount += 360;
- amount = bone.data.rotation + (prevFrameValue + amount * percent) - bone.rotation;
+ amount = bone.data.rotation + (prevRotation + amount * percent) - bone.rotation;
while (amount > 180)
amount -= 360;
while (amount < -180)
@@ -270,9 +270,9 @@ namespace Spine {
}
public class TranslateTimeline : CurveTimeline {
- protected const int PREV_TIME = -3;
- protected const int X = 1;
- protected const int Y = 2;
+ public const int ENTRIES = 3;
+ protected const int PREV_TIME = -3, PREV_X = -2, PREV_Y = -1;
+ protected const int X = 1, Y = 2;
internal int boneIndex;
internal float[] frames;
@@ -282,15 +282,15 @@ namespace Spine {
public TranslateTimeline (int frameCount)
: base(frameCount) {
- frames = new float[frameCount * 3];
+ frames = new float[frameCount * ENTRIES];
}
/// Sets the time and value of the specified keyframe.
public void SetFrame (int frameIndex, float time, float x, float y) {
- frameIndex *= 3;
+ frameIndex *= ENTRIES;
frames[frameIndex] = time;
- frames[frameIndex + 1] = x;
- frames[frameIndex + 2] = y;
+ frames[frameIndex + X] = x;
+ frames[frameIndex + Y] = y;
}
override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha) {
@@ -299,22 +299,21 @@ namespace Spine {
Bone bone = skeleton.bones.Items[boneIndex];
- if (time >= frames[frames.Length - 3]) { // Time is after last frame.
- bone.x += (bone.data.x + frames[frames.Length - 2] - bone.x) * alpha;
- bone.y += (bone.data.y + frames[frames.Length - 1] - bone.y) * alpha;
+ if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
+ bone.x += (bone.data.x + frames[frames.Length + PREV_X] - bone.x) * alpha;
+ bone.y += (bone.data.y + frames[frames.Length + PREV_Y] - bone.y) * alpha;
return;
}
// Interpolate between the previous frame and the current frame.
- int frame = Animation.binarySearch(frames, time, 3);
- float prevFrameX = frames[frame - 2];
- float prevFrameY = frames[frame - 1];
+ int frame = Animation.binarySearch(frames, time, ENTRIES);
+ float prevX = frames[frame + PREV_X];
+ float prevY = frames[frame + PREV_Y];
float frameTime = frames[frame];
- float percent = 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime);
- percent = GetCurvePercent(frame / 3 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
+ float percent = GetCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
- bone.x += (bone.data.x + prevFrameX + (frames[frame + X] - prevFrameX) * percent - bone.x) * alpha;
- bone.y += (bone.data.y + prevFrameY + (frames[frame + Y] - prevFrameY) * percent - bone.y) * alpha;
+ bone.x += (bone.data.x + prevX + (frames[frame + X] - prevX) * percent - bone.x) * alpha;
+ bone.y += (bone.data.y + prevY + (frames[frame + Y] - prevY) * percent - bone.y) * alpha;
}
}
@@ -328,28 +327,27 @@ namespace Spine {
if (time < frames[0]) return; // Time is before first frame.
Bone bone = skeleton.bones.Items[boneIndex];
- if (time >= frames[frames.Length - 3]) { // Time is after last frame.
- bone.scaleX += (bone.data.scaleX * frames[frames.Length - 2] - bone.scaleX) * alpha;
- bone.scaleY += (bone.data.scaleY * frames[frames.Length - 1] - bone.scaleY) * alpha;
+ if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
+ bone.scaleX += (bone.data.scaleX * frames[frames.Length + PREV_X] - bone.scaleX) * alpha;
+ bone.scaleY += (bone.data.scaleY * frames[frames.Length + PREV_Y] - bone.scaleY) * alpha;
return;
}
// Interpolate between the previous frame and the current frame.
- int frame = Animation.binarySearch(frames, time, 3);
- float prevFrameX = frames[frame - 2];
- float prevFrameY = frames[frame - 1];
+ int frame = Animation.binarySearch(frames, time, ENTRIES);
+ float prevX = frames[frame + PREV_X];
+ float prevY = frames[frame + PREV_Y];
float frameTime = frames[frame];
- float percent = 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime);
- percent = GetCurvePercent(frame / 3 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
+ float percent = GetCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
- bone.scaleX += (bone.data.scaleX * (prevFrameX + (frames[frame + X] - prevFrameX) * percent) - bone.scaleX) * alpha;
- bone.scaleY += (bone.data.scaleY * (prevFrameY + (frames[frame + Y] - prevFrameY) * percent) - bone.scaleY) * alpha;
+ bone.scaleX += (bone.data.scaleX * (prevX + (frames[frame + X] - prevX) * percent) - bone.scaleX) * alpha;
+ bone.scaleY += (bone.data.scaleY * (prevY + (frames[frame + Y] - prevY) * percent) - bone.scaleY) * alpha;
}
}
public class ShearTimeline : TranslateTimeline {
public ShearTimeline (int frameCount)
- : base (frameCount) {
+ : base(frameCount) {
}
override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha) {
@@ -357,31 +355,28 @@ namespace Spine {
if (time < frames[0]) return; // Time is before first frame.
Bone bone = skeleton.bones.Items[boneIndex];
- if (time >= frames[frames.Length - 3]) { // Time is after last frame.
- bone.shearX += (bone.data.shearX + frames[frames.Length - 2] - bone.shearX) * alpha;
- bone.shearY += (bone.data.shearY + frames[frames.Length - 1] - bone.shearY) * alpha;
+ if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
+ bone.shearX += (bone.data.shearX + frames[frames.Length + PREV_X] - bone.shearX) * alpha;
+ bone.shearY += (bone.data.shearY + frames[frames.Length + PREV_Y] - bone.shearY) * alpha;
return;
}
// Interpolate between the previous frame and the current frame.
- int frame = Animation.binarySearch(frames, time, 3);
- float prevFrameX = frames[frame - 2];
- float prevFrameY = frames[frame - 1];
+ int frame = Animation.binarySearch(frames, time, ENTRIES);
+ float prevX = frames[frame + PREV_X];
+ float prevY = frames[frame + PREV_Y];
float frameTime = frames[frame];
- float percent = 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime);
- percent = GetCurvePercent(frame / 3 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
+ float percent = GetCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
- bone.shearX += (bone.data.shearX + (prevFrameX + (frames[frame + X] - prevFrameX) * percent) - bone.shearX) * alpha;
- bone.shearY += (bone.data.shearY + (prevFrameY + (frames[frame + Y] - prevFrameY) * percent) - bone.shearY) * alpha;
+ bone.shearX += (bone.data.shearX + (prevX + (frames[frame + X] - prevX) * percent) - bone.shearX) * alpha;
+ bone.shearY += (bone.data.shearY + (prevY + (frames[frame + Y] - prevY) * percent) - bone.shearY) * alpha;
}
}
public class ColorTimeline : CurveTimeline {
- protected const int PREV_TIME = -5;
- protected const int R = 1;
- protected const int G = 2;
- protected const int B = 3;
- protected const int A = 4;
+ public const int ENTRIES = 5;
+ protected const int PREV_TIME = -5, PREV_R = -4, PREV_G = -3, PREV_B = -2, PREV_A = -1;
+ protected const int R = 1, G = 2, B = 3, A = 4;
internal int slotIndex;
internal float[] frames;
@@ -391,17 +386,17 @@ namespace Spine {
public ColorTimeline (int frameCount)
: base(frameCount) {
- frames = new float[frameCount * 5];
+ frames = new float[frameCount * ENTRIES];
}
/// Sets the time and value of the specified keyframe.
public void SetFrame (int frameIndex, float time, float r, float g, float b, float a) {
- frameIndex *= 5;
+ frameIndex *= ENTRIES;
frames[frameIndex] = time;
- frames[frameIndex + 1] = r;
- frames[frameIndex + 2] = g;
- frames[frameIndex + 3] = b;
- frames[frameIndex + 4] = a;
+ frames[frameIndex + R] = r;
+ frames[frameIndex + G] = g;
+ frames[frameIndex + B] = b;
+ frames[frameIndex + A] = a;
}
override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha) {
@@ -409,23 +404,23 @@ namespace Spine {
if (time < frames[0]) return; // Time is before first frame.
float r, g, b, a;
- if (time >= frames[frames.Length - 5]) { // Time is after last frame.
- int i = frames.Length - 1;
- r = frames[i - 3];
- g = frames[i - 2];
- b = frames[i - 1];
- a = frames[i];
+ if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
+ int i = frames.Length;
+ r = frames[i + PREV_R];
+ g = frames[i + PREV_G];
+ b = frames[i + PREV_B];
+ a = frames[i + PREV_A];
} else {
// Interpolate between the previous frame and the current frame.
- int frame = Animation.binarySearch(frames, time, 5);
+ int frame = Animation.binarySearch(frames, time, ENTRIES);
+ r = frames[frame + PREV_R];
+ g = frames[frame + PREV_G];
+ b = frames[frame + PREV_B];
+ a = frames[frame + PREV_A];
float frameTime = frames[frame];
- float percent = 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime);
- percent = GetCurvePercent(frame / 5 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
+ float percent = GetCurvePercent(frame / ENTRIES - 1,
+ 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
- r = frames[frame - 4];
- g = frames[frame - 3];
- b = frames[frame - 2];
- a = frames[frame - 1];
r += (frames[frame + R] - r) * percent;
g += (frames[frame + G] - g) * percent;
b += (frames[frame + B] - b) * percent;
@@ -469,18 +464,17 @@ namespace Spine {
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;
+ if (time < frames[0]) return; // Time is before first frame.
- int frameIndex = (time >= frames[frames.Length - 1] ? frames.Length : Animation.binarySearch(frames, time)) - 1;
- if (frames[frameIndex] < lastTime) return;
+ int frameIndex;
+ if (time >= frames[frames.Length - 1]) // Time is after last frame.
+ frameIndex = frames.Length - 1;
+ else
+ frameIndex = Animation.binarySearch(frames, time, 1) - 1;
String attachmentName = attachmentNames[frameIndex];
- skeleton.slots.Items[slotIndex].Attachment =
- attachmentName == null ? null : skeleton.GetAttachment(slotIndex, attachmentName);
+ skeleton.slots.Items[slotIndex]
+ .Attachment = attachmentName == null ? null : skeleton.GetAttachment(slotIndex, attachmentName);
}
}
@@ -503,7 +497,7 @@ namespace Spine {
events[frameIndex] = e;
}
- /// Fires events for frames > lastTime and <= time.
+ /// Fires events for frames > lastTime and <= time.
public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha) {
if (firedEvents == null) return;
float[] frames = this.frames;
@@ -570,24 +564,26 @@ namespace Spine {
for (int i = 0, n = slots.Count; i < n; i++)
drawOrder.Add(slots.Items[i]);
} else {
+ var drawOrderItems = drawOrder.Items;
+ var slotsItems = slots.Items;
for (int i = 0, n = drawOrderToSetupIndex.Length; i < n; i++)
- drawOrder.Items[i] = slots.Items[drawOrderToSetupIndex[i]];
+ drawOrderItems[i] = slotsItems[drawOrderToSetupIndex[i]];
}
}
}
- public class FfdTimeline : CurveTimeline {
+ public class DeformTimeline : CurveTimeline {
internal int slotIndex;
internal float[] frames;
private float[][] frameVertices;
- internal Attachment attachment;
+ internal VertexAttachment attachment;
public int SlotIndex { get { return slotIndex; } set { slotIndex = value; } }
public float[] Frames { get { return frames; } set { frames = value; } } // time, ...
public float[][] Vertices { get { return frameVertices; } set { frameVertices = value; } }
- public Attachment Attachment { get { return attachment; } set { attachment = value; } }
+ public VertexAttachment Attachment { get { return attachment; } set { attachment = value; } }
- public FfdTimeline (int frameCount)
+ public DeformTimeline (int frameCount)
: base(frameCount) {
frames = new float[frameCount];
frameVertices = new float[frameCount][];
@@ -601,8 +597,8 @@ namespace Spine {
override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha) {
Slot slot = skeleton.slots.Items[slotIndex];
- IFfdAttachment ffdAttachment = slot.attachment as IFfdAttachment;
- if (ffdAttachment == null || !ffdAttachment.ApplyFFD(attachment)) return;
+ VertexAttachment slotAttachment = slot.attachment as VertexAttachment;
+ if (slotAttachment == null || !slotAttachment.ApplyDeform(attachment)) return;
float[] frames = this.frames;
if (time < frames[0]) return; // Time is before first frame.
@@ -610,15 +606,12 @@ namespace Spine {
float[][] frameVertices = this.frameVertices;
int vertexCount = frameVertices[0].Length;
- float[] vertices = slot.attachmentVertices;
- if (slot.attachmentVerticesCount != vertexCount) alpha = 1; // Don't mix from uninitialized slot vertices.
-
- // Ensure capacity
- if (vertices.Length < vertexCount) {
- vertices = new float[vertexCount];
- slot.attachmentVertices = vertices;
- }
- slot.attachmentVerticesCount = vertexCount;
+ var verticesArray = slot.attachmentVertices;
+ if (verticesArray.Count != vertexCount) alpha = 1; // Don't mix from uninitialized slot vertices.
+ // verticesArray.SetSize(vertexCount) // Ensure size and preemptively set count.
+ if (verticesArray.Capacity < vertexCount) verticesArray.Capacity = vertexCount;
+ verticesArray.Count = vertexCount;
+ float[] vertices = verticesArray.Items;
if (time >= frames[frames.Length - 1]) { // Time is after last frame.
float[] lastVertices = frameVertices[frames.Length - 1];
@@ -634,12 +627,10 @@ namespace Spine {
// Interpolate between the previous frame and the current frame.
int frame = Animation.binarySearch(frames, time);
- float frameTime = frames[frame];
- float percent = 1 - (time - frameTime) / (frames[frame - 1] - frameTime);
- percent = GetCurvePercent(frame - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
-
float[] prevVertices = frameVertices[frame - 1];
float[] nextVertices = frameVertices[frame];
+ float frameTime = frames[frame];
+ float percent = GetCurvePercent(frame - 1, 1 - (time - frameTime) / (frames[frame - 1] - frameTime));
if (alpha < 1) {
for (int i = 0; i < vertexCount; i++) {
@@ -657,10 +648,9 @@ namespace Spine {
}
public class IkConstraintTimeline : CurveTimeline {
- private const int PREV_TIME = -3;
- private const int PREV_MIX = -2;
- private const int PREV_BEND_DIRECTION = -1;
- private const int MIX = 1;
+ public const int ENTRIES = 3;
+ private const int PREV_TIME = -3, PREV_MIX = -2, PREV_BEND_DIRECTION = -1;
+ private const int MIX = 1, BEND_DIRECTION = 2;
internal int ikConstraintIndex;
internal float[] frames;
@@ -670,15 +660,15 @@ namespace Spine {
public IkConstraintTimeline (int frameCount)
: base(frameCount) {
- frames = new float[frameCount * 3];
+ frames = new float[frameCount * ENTRIES];
}
/// Sets the time, mix and bend direction of the specified keyframe.
public void SetFrame (int frameIndex, float time, float mix, int bendDirection) {
- frameIndex *= 3;
+ frameIndex *= ENTRIES;
frames[frameIndex] = time;
- frames[frameIndex + 1] = mix;
- frames[frameIndex + 2] = bendDirection;
+ frames[frameIndex + MIX] = mix;
+ frames[frameIndex + BEND_DIRECTION] = bendDirection;
}
override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha) {
@@ -687,34 +677,27 @@ namespace Spine {
IkConstraint constraint = skeleton.ikConstraints.Items[ikConstraintIndex];
- if (time >= frames[frames.Length - 3]) { // Time is after last frame.
+ if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
constraint.mix += (frames[frames.Length + PREV_MIX] - constraint.mix) * alpha;
constraint.bendDirection = (int)frames[frames.Length + PREV_BEND_DIRECTION];
return;
}
// Interpolate between the previous frame and the current frame.
- int frame = Animation.binarySearch(frames, time, 3);
- float frameTime = frames[frame];
- float percent = 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime);
- percent = GetCurvePercent(frame / 3 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
-
+ int frame = Animation.binarySearch(frames, time, ENTRIES);
float mix = frames[frame + PREV_MIX];
+ float frameTime = frames[frame];
+ float percent = GetCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
+
constraint.mix += (mix + (frames[frame + MIX] - mix) * percent - constraint.mix) * alpha;
constraint.bendDirection = (int)frames[frame + PREV_BEND_DIRECTION];
}
}
public class TransformConstraintTimeline : CurveTimeline {
- private const int PREV_TIME = -5;
- private const int PREV_ROTATE_MIX = -4;
- private const int PREV_TRANSLATE_MIX = -3;
- private const int PREV_SCALE_MIX = -2;
- private const int PREV_SHEAR_MIX = -1;
- private const int ROTATE_MIX = 1;
- private const int TRANSLATE_MIX = 2;
- private const int SCALE_MIX = 3;
- private const int SHEAR_MIX = 4;
+ public const int ENTRIES = 5;
+ private const int PREV_TIME = -5, PREV_ROTATE = -4, PREV_TRANSLATE = -3, PREV_SCALE = -2, PREV_SHEAR = -1;
+ private const int ROTATE = 1, TRANSLATE = 2, SCALE = 3, SHEAR = 4;
internal int transformConstraintIndex;
internal float[] frames;
@@ -724,16 +707,16 @@ namespace Spine {
public TransformConstraintTimeline (int frameCount)
: base(frameCount) {
- frames = new float[frameCount * 5];
+ frames = new float[frameCount * ENTRIES];
}
-
+
public void SetFrame (int frameIndex, float time, float rotateMix, float translateMix, float scaleMix, float shearMix) {
- frameIndex *= 5;
+ frameIndex *= ENTRIES;
frames[frameIndex] = time;
- frames[frameIndex + 1] = rotateMix;
- frames[frameIndex + 2] = translateMix;
- frames[frameIndex + 3] = scaleMix;
- frames[frameIndex + 4] = shearMix;
+ frames[frameIndex + ROTATE] = rotateMix;
+ frames[frameIndex + TRANSLATE] = translateMix;
+ frames[frameIndex + SCALE] = scaleMix;
+ frames[frameIndex + SHEAR] = shearMix;
}
override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha) {
@@ -742,29 +725,151 @@ namespace Spine {
TransformConstraint constraint = skeleton.transformConstraints.Items[transformConstraintIndex];
- if (time >= frames[frames.Length - 5]) { // Time is after last frame.
- int i = frames.Length - 1;
- constraint.rotateMix += (frames[i - 3] - constraint.rotateMix) * alpha;
- constraint.translateMix += (frames[i - 2] - constraint.translateMix) * alpha;
- constraint.scaleMix += (frames[i - 1] - constraint.scaleMix) * alpha;
- constraint.shearMix += (frames[i] - constraint.shearMix) * alpha;
+ if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
+ int i = frames.Length;
+ constraint.rotateMix += (frames[i + PREV_ROTATE] - constraint.rotateMix) * alpha;
+ constraint.translateMix += (frames[i + PREV_TRANSLATE] - constraint.translateMix) * alpha;
+ constraint.scaleMix += (frames[i + PREV_SCALE] - constraint.scaleMix) * alpha;
+ constraint.shearMix += (frames[i + PREV_SHEAR] - constraint.shearMix) * alpha;
return;
}
// Interpolate between the previous frame and the current frame.
- int frame = Animation.binarySearch(frames, time, 5);
+ int frame = Animation.binarySearch(frames, time, ENTRIES);
float frameTime = frames[frame];
- float percent = 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime);
- percent = GetCurvePercent(frame / 5 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
+ float percent = GetCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
- float rotate = frames[frame + PREV_ROTATE_MIX];
- float translate = frames[frame + PREV_TRANSLATE_MIX];
- float scale = frames[frame + PREV_SCALE_MIX];
- float shear = frames[frame + PREV_SHEAR_MIX];
- constraint.rotateMix += (rotate + (frames[frame + ROTATE_MIX] - rotate) * percent - constraint.rotateMix) * alpha;
- constraint.translateMix += (translate + (frames[frame + TRANSLATE_MIX] - translate) * percent - constraint.translateMix) * alpha;
- constraint.scaleMix += (scale + (frames[frame + SCALE_MIX] - scale) * percent - constraint.scaleMix) * alpha;
- constraint.shearMix += (shear + (frames[frame + SHEAR_MIX] - shear) * percent - constraint.shearMix) * alpha;
+ float rotate = frames[frame + PREV_ROTATE];
+ float translate = frames[frame + PREV_TRANSLATE];
+ float scale = frames[frame + PREV_SCALE];
+ float shear = frames[frame + PREV_SHEAR];
+ constraint.rotateMix += (rotate + (frames[frame + ROTATE] - rotate) * percent - constraint.rotateMix) * alpha;
+ constraint.translateMix += (translate + (frames[frame + TRANSLATE] - translate) * percent - constraint.translateMix)
+ * alpha;
+ constraint.scaleMix += (scale + (frames[frame + SCALE] - scale) * percent - constraint.scaleMix) * alpha;
+ constraint.shearMix += (shear + (frames[frame + SHEAR] - shear) * percent - constraint.shearMix) * alpha;
+ }
+ }
+
+ public class PathConstraintPositionTimeline : CurveTimeline {
+ public const int ENTRIES = 2;
+ protected const int PREV_TIME = -2, PREV_VALUE = -1;
+ protected const int VALUE = 1;
+
+ internal int pathConstraintIndex;
+ internal float[] frames;
+
+ public PathConstraintPositionTimeline (int frameCount)
+ : base(frameCount) {
+ frames = new float[frameCount * ENTRIES];
+ }
+
+ public int PathConstraintIndex { get { return pathConstraintIndex; } set { pathConstraintIndex = value; } }
+ public float[] Frames { get { return frames; } set { frames = value; } } // time, position, ...
+
+ /// Sets the time and value of the specified keyframe.
+ public void SetFrame (int frameIndex, float time, float value) {
+ frameIndex *= ENTRIES;
+ frames[frameIndex] = time;
+ frames[frameIndex + VALUE] = value;
+ }
+
+ override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList events, float alpha) {
+ float[] frames = this.frames;
+ if (time < frames[0]) return; // Time is before first frame.
+
+ PathConstraint constraint = skeleton.pathConstraints.Items[pathConstraintIndex];
+
+ if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
+ int i = frames.Length;
+ constraint.position += (frames[i + PREV_VALUE] - constraint.position) * alpha;
+ return;
+ }
+
+ // Interpolate between the previous frame and the current frame.
+ int frame = Animation.binarySearch(frames, time, ENTRIES);
+ float position = frames[frame + PREV_VALUE];
+ float frameTime = frames[frame];
+ float percent = GetCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
+
+ constraint.position += (position + (frames[frame + VALUE] - position) * percent - constraint.position) * alpha;
+ }
+ }
+
+ public class PathConstraintSpacingTimeline : PathConstraintPositionTimeline {
+ public PathConstraintSpacingTimeline (int frameCount)
+ : base(frameCount) {
+ }
+
+ override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList events, float alpha) {
+ float[] frames = this.frames;
+ if (time < frames[0]) return; // Time is before first frame.
+
+ PathConstraint constraint = skeleton.pathConstraints.Items[pathConstraintIndex];
+
+ if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
+ int i = frames.Length;
+ constraint.spacing += (frames[i + PREV_VALUE] - constraint.spacing) * alpha;
+ return;
+ }
+
+ // Interpolate between the previous frame and the current frame.
+ int frame = Animation.binarySearch(frames, time, ENTRIES);
+ float spacing = frames[frame + PREV_VALUE];
+ float frameTime = frames[frame];
+ float percent = GetCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
+
+ constraint.spacing += (spacing + (frames[frame + VALUE] - spacing) * percent - constraint.spacing) * alpha;
+ }
+ }
+
+ public class PathConstraintMixTimeline : CurveTimeline {
+ public const int ENTRIES = 3;
+ private const int PREV_TIME = -3, PREV_ROTATE = -2, PREV_TRANSLATE = -1;
+ private const int ROTATE = 1, TRANSLATE = 2;
+
+ internal int pathConstraintIndex;
+ internal float[] frames;
+
+ public int PathConstraintIndex { get { return pathConstraintIndex; } set { pathConstraintIndex = value; } }
+ public float[] Frames { get { return frames; } set { frames = value; } } // time, rotate mix, translate mix, ...
+
+ public PathConstraintMixTimeline (int frameCount)
+ : base(frameCount) {
+ frames = new float[frameCount * ENTRIES];
+ }
+
+ /** Sets the time and mixes of the specified keyframe. */
+ public void SetFrame (int frameIndex, float time, float rotateMix, float translateMix) {
+ frameIndex *= ENTRIES;
+ frames[frameIndex] = time;
+ frames[frameIndex + ROTATE] = rotateMix;
+ frames[frameIndex + TRANSLATE] = translateMix;
+ }
+
+ override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList events, float alpha) {
+ float[] frames = this.frames;
+ if (time < frames[0]) return; // Time is before first frame.
+
+ PathConstraint constraint = skeleton.pathConstraints.Items[pathConstraintIndex];
+
+ if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
+ int i = frames.Length;
+ constraint.rotateMix += (frames[i + PREV_ROTATE] - constraint.rotateMix) * alpha;
+ constraint.translateMix += (frames[i + PREV_TRANSLATE] - constraint.translateMix) * alpha;
+ return;
+ }
+
+ // Interpolate between the previous frame and the current frame.
+ int frame = Animation.binarySearch(frames, time, ENTRIES);
+ float rotate = frames[frame + PREV_ROTATE];
+ float translate = frames[frame + PREV_TRANSLATE];
+ float frameTime = frames[frame];
+ float percent = GetCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
+
+ constraint.rotateMix += (rotate + (frames[frame + ROTATE] - rotate) * percent - constraint.rotateMix) * alpha;
+ constraint.translateMix += (translate + (frames[frame + TRANSLATE] - translate) * percent - constraint.translateMix)
+ * alpha;
}
}
}
diff --git a/spine-csharp/src/AnimationState.cs b/spine-csharp/src/AnimationState.cs
index 00e2c36e6..1d438efde 100644
--- a/spine-csharp/src/AnimationState.cs
+++ b/spine-csharp/src/AnimationState.cs
@@ -41,6 +41,8 @@ namespace Spine {
private float timeScale = 1;
public AnimationStateData Data { get { return data; } }
+ /// A list of tracks that have animations, which may contain nulls.
+ public ExposedList Tracks { get { return tracks; } }
public float TimeScale { get { return timeScale; } set { timeScale = value; } }
public delegate void StartEndDelegate (AnimationState state, int trackIndex);
@@ -54,7 +56,7 @@ namespace Spine {
public event CompleteDelegate Complete;
public AnimationState (AnimationStateData data) {
- if (data == null) throw new ArgumentNullException("data cannot be null.");
+ if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
this.data = data;
}
@@ -187,15 +189,16 @@ namespace Spine {
if (Start != null) Start(this, index);
}
+ ///
public TrackEntry SetAnimation (int trackIndex, String animationName, bool loop) {
Animation animation = data.skeletonData.FindAnimation(animationName);
- if (animation == null) throw new ArgumentException("Animation not found: " + animationName);
+ if (animation == null) throw new ArgumentException("Animation not found: " + animationName, "animationName");
return SetAnimation(trackIndex, animation, loop);
}
/// Set the current animation. Any queued animations are cleared.
public TrackEntry SetAnimation (int trackIndex, Animation animation, bool loop) {
- if (animation == null) throw new ArgumentException("animation cannot be null.");
+ if (animation == null) throw new ArgumentNullException("animation", "animation cannot be null.");
TrackEntry entry = new TrackEntry();
entry.animation = animation;
entry.loop = loop;
@@ -205,16 +208,17 @@ namespace Spine {
return entry;
}
+ ///
public TrackEntry AddAnimation (int trackIndex, String animationName, bool loop, float delay) {
Animation animation = data.skeletonData.FindAnimation(animationName);
- if (animation == null) throw new ArgumentException("Animation not found: " + animationName);
+ if (animation == null) throw new ArgumentException("Animation not found: " + animationName, "animationName");
return AddAnimation(trackIndex, animation, loop, delay);
}
/// Adds an animation to be played delay seconds after the current or last queued animation.
- /// May be <= 0 to use duration of previous animation minus any mix duration plus the negative delay.
+ /// May be <= 0 to use duration of previous animation minus any mix duration plus the negative delay.
public TrackEntry AddAnimation (int trackIndex, Animation animation, bool loop, float delay) {
- if (animation == null) throw new ArgumentException("animation cannot be null.");
+ if (animation == null) throw new ArgumentNullException("animation", "animation cannot be null.");
TrackEntry entry = new TrackEntry();
entry.animation = animation;
entry.loop = loop;
diff --git a/spine-csharp/src/AnimationStateData.cs b/spine-csharp/src/AnimationStateData.cs
index bf36b2c55..408cdfabf 100644
--- a/spine-csharp/src/AnimationStateData.cs
+++ b/spine-csharp/src/AnimationStateData.cs
@@ -42,6 +42,7 @@ namespace Spine {
public float DefaultMix { get { return defaultMix; } set { defaultMix = value; } }
public AnimationStateData (SkeletonData skeletonData) {
+ if (skeletonData == null) throw new ArgumentException ("skeletonData cannot be null.");
this.skeletonData = skeletonData;
}
@@ -54,8 +55,8 @@ namespace Spine {
}
public void SetMix (Animation from, Animation to, float duration) {
- if (from == null) throw new ArgumentNullException("from cannot be null.");
- if (to == null) throw new ArgumentNullException("to cannot be null.");
+ if (from == null) throw new ArgumentNullException("from", "from cannot be null.");
+ if (to == null) throw new ArgumentNullException("to", "to cannot be null.");
AnimationPair key = new AnimationPair(from, to);
animationToMixTime.Remove(key);
animationToMixTime.Add(key, duration);
diff --git a/spine-csharp/src/Attachments/AtlasAttachmentLoader.cs b/spine-csharp/src/Attachments/AtlasAttachmentLoader.cs
index f05de4b67..b3d133c2f 100644
--- a/spine-csharp/src/Attachments/AtlasAttachmentLoader.cs
+++ b/spine-csharp/src/Attachments/AtlasAttachmentLoader.cs
@@ -72,31 +72,16 @@ namespace Spine {
attachment.regionOriginalWidth = region.originalWidth;
attachment.regionOriginalHeight = region.originalHeight;
return attachment;
- }
-
- 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 + " (weighted mesh attachment: " + name + ")");
- WeightedMeshAttachment attachment = new WeightedMeshAttachment(name);
- attachment.RendererObject = region;
- attachment.RegionU = region.u;
- attachment.RegionV = region.v;
- attachment.RegionU2 = region.u2;
- attachment.RegionV2 = region.v2;
- attachment.RegionRotate = region.rotate;
- attachment.regionOffsetX = region.offsetX;
- attachment.regionOffsetY = region.offsetY;
- attachment.regionWidth = region.width;
- attachment.regionHeight = region.height;
- attachment.regionOriginalWidth = region.originalWidth;
- attachment.regionOriginalHeight = region.originalHeight;
- return attachment;
- }
+ }
public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, String name) {
return new BoundingBoxAttachment(name);
}
+ public PathAttachment NewPathAttachment (Skin skin, String name) {
+ return new PathAttachment (name);
+ }
+
public AtlasRegion FindRegion (string name) {
AtlasRegion region;
diff --git a/spine-csharp/src/Attachments/Attachment.cs b/spine-csharp/src/Attachments/Attachment.cs
index 551785b69..44d8bc2a3 100644
--- a/spine-csharp/src/Attachments/Attachment.cs
+++ b/spine-csharp/src/Attachments/Attachment.cs
@@ -36,7 +36,7 @@ namespace Spine {
public String Name { get; private set; }
public Attachment (String name) {
- if (name == null) throw new ArgumentNullException("name cannot be null.");
+ if (name == null) throw new ArgumentNullException("name", "name cannot be null");
Name = name;
}
diff --git a/spine-csharp/src/Attachments/AttachmentLoader.cs b/spine-csharp/src/Attachments/AttachmentLoader.cs
index 5574e3210..801b8a778 100644
--- a/spine-csharp/src/Attachments/AttachmentLoader.cs
+++ b/spine-csharp/src/Attachments/AttachmentLoader.cs
@@ -39,10 +39,10 @@ namespace Spine {
/// May be null to not load any attachment.
MeshAttachment NewMeshAttachment (Skin skin, String name, String path);
- /// May be null to not load any attachment.
- WeightedMeshAttachment NewWeightedMeshAttachment (Skin skin, String name, String path);
-
/// May be null to not load any attachment.
BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, String name);
+
+ /// May be null to not load any attachment
+ PathAttachment NewPathAttachment (Skin skin, String name);
}
}
diff --git a/spine-csharp/src/Attachments/AttachmentType.cs b/spine-csharp/src/Attachments/AttachmentType.cs
index 80543490f..81b55a99a 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, weightedmesh, linkedmesh, weightedlinkedmesh
+ Region, Boundingbox, Mesh, Linkedmesh, Path
}
}
diff --git a/spine-csharp/src/Attachments/BoundingBoxAttachment.cs b/spine-csharp/src/Attachments/BoundingBoxAttachment.cs
index ccf179edd..41b42c236 100644
--- a/spine-csharp/src/Attachments/BoundingBoxAttachment.cs
+++ b/spine-csharp/src/Attachments/BoundingBoxAttachment.cs
@@ -33,29 +33,9 @@ using System;
namespace Spine {
/// Attachment that has a polygon for bounds checking.
- public class BoundingBoxAttachment : Attachment {
- internal float[] vertices;
-
- public float[] Vertices { get { return vertices; } set { vertices = value; } }
-
+ public class BoundingBoxAttachment : VertexAttachment {
public BoundingBoxAttachment (string name)
: base(name) {
}
-
- /// 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.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];
- float py = vertices[i + 1];
- worldVertices[i] = px * m00 + py * m01 + x;
- worldVertices[i + 1] = px * m10 + py * m11 + y;
- }
- }
}
}
diff --git a/spine-csharp/src/Attachments/MeshAttachment.cs b/spine-csharp/src/Attachments/MeshAttachment.cs
index eae83f0cb..042d7e1ed 100644
--- a/spine-csharp/src/Attachments/MeshAttachment.cs
+++ b/spine-csharp/src/Attachments/MeshAttachment.cs
@@ -33,16 +33,16 @@ using System;
namespace Spine {
/// Attachment that displays a texture region using a mesh.
- public class MeshAttachment : Attachment, IFfdAttachment {
- internal float[] vertices, uvs, regionUVs;
- internal int[] triangles;
+ public class MeshAttachment : VertexAttachment {
internal float regionOffsetX, regionOffsetY, regionWidth, regionHeight, regionOriginalWidth, regionOriginalHeight;
+ internal float[] uvs, regionUVs;
+ internal int[] triangles;
internal float r = 1, g = 1, b = 1, a = 1;
+ internal int hulllength;
internal MeshAttachment parentMesh;
- internal bool inheritFFD;
+ internal bool inheritDeform;
- public int HullLength { get; set; }
- public float[] Vertices { get { return vertices; } set { vertices = value; } }
+ public int HullLength { get { return hulllength; } set { hulllength = value; } }
public float[] RegionUVs { get { return regionUVs; } set { regionUVs = value; } }
public float[] UVs { get { return uvs; } set { uvs = value; } }
public int[] Triangles { get { return triangles; } set { triangles = value; } }
@@ -66,7 +66,7 @@ namespace Spine {
public float RegionOriginalWidth { get { return regionOriginalWidth; } set { regionOriginalWidth = value; } }
public float RegionOriginalHeight { get { return regionOriginalHeight; } set { regionOriginalHeight = value; } } // Unrotated, unstripped size.
- public bool InheritFFD { get { return inheritFFD; } set { inheritFFD = value; } }
+ public bool InheritDeform { get { return inheritDeform; } set { inheritDeform = value; } }
public MeshAttachment ParentMesh {
get { return parentMesh; }
@@ -74,6 +74,7 @@ namespace Spine {
parentMesh = value;
if (value != null) {
vertices = value.vertices;
+ worldVerticesLength = value.worldVerticesLength;
regionUVs = value.regionUVs;
triangles = value.triangles;
HullLength = value.HullLength;
@@ -111,23 +112,8 @@ 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.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;
- for (int i = 0; i < verticesCount; i += 2) {
- float vx = vertices[i];
- float vy = vertices[i + 1];
- worldVertices[i] = vx * m00 + vy * m01 + x;
- worldVertices[i + 1] = vx * m10 + vy * m11 + y;
- }
- }
-
- public bool ApplyFFD (Attachment sourceAttachment) {
- return this == sourceAttachment || (inheritFFD && parentMesh == sourceAttachment);
+ override public bool ApplyDeform (VertexAttachment sourceAttachment) {
+ return this == sourceAttachment || (inheritDeform && parentMesh == sourceAttachment);
}
}
}
diff --git a/spine-csharp/src/Attachments/IFfdAttachment.cs b/spine-csharp/src/Attachments/PathAttachment.cs
similarity index 76%
rename from spine-csharp/src/Attachments/IFfdAttachment.cs
rename to spine-csharp/src/Attachments/PathAttachment.cs
index bdc87f7de..fb36a1a67 100644
--- a/spine-csharp/src/Attachments/IFfdAttachment.cs
+++ b/spine-csharp/src/Attachments/PathAttachment.cs
@@ -29,8 +29,21 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
+using System;
+using System.Collections.Generic;
+
namespace Spine {
- public interface IFfdAttachment {
- bool ApplyFFD (Attachment sourceAttachment);
+ public class PathAttachment : VertexAttachment {
+ internal float[] lengths;
+ internal bool closed, constantSpeed;
+
+ /// The length in the setup pose from the start of the path to the end of each curve.
+ public float[] Lengths { get { return lengths; } set { lengths = value; } }
+ public bool Closed { get { return closed; } set { closed = value; } }
+ public bool ConstantSpeed { get { return constantSpeed; } set { constantSpeed = value; } }
+
+ public PathAttachment (String name)
+ : base(name) {
+ }
}
-}
\ No newline at end of file
+}
diff --git a/spine-csharp/src/Attachments/RegionAttachment.cs b/spine-csharp/src/Attachments/RegionAttachment.cs
index dad5e1cc8..99199397a 100644
--- a/spine-csharp/src/Attachments/RegionAttachment.cs
+++ b/spine-csharp/src/Attachments/RegionAttachment.cs
@@ -111,9 +111,9 @@ namespace Spine {
float localY = -height / 2 * scaleY + regionOffsetY * regionScaleY;
float localX2 = localX + regionWidth * regionScaleX;
float localY2 = localY + regionHeight * regionScaleY;
- float radians = rotation * (float)Math.PI / 180;
- float cos = (float)Math.Cos(radians);
- float sin = (float)Math.Sin(radians);
+ float rotation = this.rotation;
+ float cos = MathUtils.CosDeg(rotation);
+ float sin = MathUtils.SinDeg(rotation);
float x = this.x;
float y = this.y;
float localXCos = localX * cos + x;
@@ -136,17 +136,18 @@ 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.a, m01 = bone.b, m10 = bone.c, m11 = bone.d;
+ Skeleton skeleton = bone.skeleton;
+ float x = skeleton.x + bone.worldX, y = skeleton.y + bone.worldY;
+ float a = bone.a, b = bone.b, c = bone.c, d = bone.d;
float[] offset = this.offset;
- worldVertices[X1] = offset[X1] * m00 + offset[Y1] * m01 + x;
- worldVertices[Y1] = offset[X1] * m10 + offset[Y1] * m11 + y;
- worldVertices[X2] = offset[X2] * m00 + offset[Y2] * m01 + x;
- worldVertices[Y2] = offset[X2] * m10 + offset[Y2] * m11 + y;
- worldVertices[X3] = offset[X3] * m00 + offset[Y3] * m01 + x;
- worldVertices[Y3] = offset[X3] * m10 + offset[Y3] * m11 + y;
- worldVertices[X4] = offset[X4] * m00 + offset[Y4] * m01 + x;
- worldVertices[Y4] = offset[X4] * m10 + offset[Y4] * m11 + y;
+ worldVertices[X1] = offset[X1] * a + offset[Y1] * b + x;
+ worldVertices[Y1] = offset[X1] * c + offset[Y1] * d + y;
+ worldVertices[X2] = offset[X2] * a + offset[Y2] * b + x;
+ worldVertices[Y2] = offset[X2] * c + offset[Y2] * d + y;
+ worldVertices[X3] = offset[X3] * a + offset[Y3] * b + x;
+ worldVertices[Y3] = offset[X3] * c + offset[Y3] * d + y;
+ worldVertices[X4] = offset[X4] * a + offset[Y4] * b + x;
+ worldVertices[Y4] = offset[X4] * c + offset[Y4] * d + y;
}
}
}
diff --git a/spine-csharp/src/Attachments/VertexAttachment.cs b/spine-csharp/src/Attachments/VertexAttachment.cs
new file mode 100644
index 000000000..9a58da479
--- /dev/null
+++ b/spine-csharp/src/Attachments/VertexAttachment.cs
@@ -0,0 +1,114 @@
+/******************************************************************************
+ * 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 {
+ /// >An attachment with vertices that are transformed by one or more bones and can be deformed by a slot's vertices.
+ public class VertexAttachment : Attachment {
+ internal int[] bones;
+ internal float[] vertices;
+ internal int worldVerticesLength;
+
+ public int[] Bones { get { return bones; } set { bones = value; } }
+ public float[] Vertices { get { return vertices; } set { vertices = value; } }
+ public int WorldVerticesLength { get { return worldVerticesLength; } set { worldVerticesLength = value; } }
+
+ public VertexAttachment (String name)
+ : base(name) {
+ }
+
+ public void ComputeWorldVertices (Slot slot, float[] worldVertices) {
+ ComputeWorldVertices(slot, 0, worldVerticesLength, worldVertices, 0);
+ }
+
+ public void ComputeWorldVertices (Slot slot, int start, int count, float[] worldVertices, int offset) {
+ count += offset;
+ Skeleton skeleton = slot.Skeleton;
+ float x = skeleton.x, y = skeleton.y;
+ var deformArray = slot.attachmentVertices;
+ float[] vertices = this.vertices;
+ int[] bones = this.bones;
+ if (bones == null) {
+ if (deformArray.Count > 0) vertices = deformArray.Items;
+ Bone bone = slot.bone;
+ x += bone.worldX;
+ y += bone.worldY;
+ float a = bone.a, b = bone.b, c = bone.c, d = bone.d;
+ for (int vv = start, w = offset; w < count; vv += 2, w += 2) {
+ float vx = vertices[vv], vy = vertices[vv + 1];
+ worldVertices[w] = vx * a + vy * b + x;
+ worldVertices[w + 1] = vx * c + vy * d + y;
+ }
+ return;
+ }
+ int v = 0, skip = 0;
+ for (int i = 0; i < start; i += 2) {
+ int n = bones[v];
+ v += n + 1;
+ skip += n;
+ }
+ Bone[] skeletonBones = skeleton.Bones.Items;
+ if (deformArray.Count == 0) {
+ for (int w = offset, b = skip * 3; w < count; w += 2) {
+ float wx = x, wy = y;
+ for (int n = bones[v++] + v; v < n; v++, b += 3) {
+ Bone bone = skeletonBones[bones[v]];
+ float vx = vertices[b], vy = vertices[b + 1], weight = vertices[b + 2];
+ wx += (vx * bone.a + vy * bone.b + bone.worldX) * weight;
+ wy += (vx * bone.c + vy * bone.d + bone.worldY) * weight;
+ }
+ worldVertices[w] = wx;
+ worldVertices[w + 1] = wy;
+ }
+ } else {
+ float[] deform = deformArray.Items;
+ for (int w = offset, b = skip * 3, f = skip << 1; w < count; w += 2) {
+ float wx = x, wy = y;
+ for (int n = bones[v++] + v; v < n; v++, b += 3, f += 2) {
+ Bone bone = skeletonBones[bones[v]];
+ float vx = vertices[b] + deform[f], vy = vertices[b + 1] + deform[f + 1], weight = vertices[b + 2];
+ wx += (vx * bone.a + vy * bone.b + bone.worldX) * weight;
+ wy += (vx * bone.c + vy * bone.d + bone.worldY) * weight;
+ }
+ worldVertices[w] = wx;
+ worldVertices[w + 1] = wy;
+ }
+ }
+ }
+
+ /// Returns true if a deform originally applied to the specified attachment should be applied to this attachment.
+ virtual public bool ApplyDeform (VertexAttachment sourceAttachment) {
+ return this == sourceAttachment;
+ }
+ }
+}
diff --git a/spine-csharp/src/Attachments/WeightedMeshAttachment.cs b/spine-csharp/src/Attachments/WeightedMeshAttachment.cs
deleted file mode 100644
index a4b15b7ca..000000000
--- a/spine-csharp/src/Attachments/WeightedMeshAttachment.cs
+++ /dev/null
@@ -1,158 +0,0 @@
-/******************************************************************************
- * 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 {
- /// Attachment that displays a texture region using a mesh which can be deformed by bones.
- public class WeightedMeshAttachment : Attachment, IFfdAttachment {
- internal int[] bones;
- internal float[] weights, uvs, regionUVs;
- internal int[] triangles;
- internal float regionOffsetX, regionOffsetY, regionWidth, regionHeight, regionOriginalWidth, regionOriginalHeight;
- internal float r = 1, g = 1, b = 1, a = 1;
- internal WeightedMeshAttachment parentMesh;
- internal bool inheritFFD;
-
- public int HullLength { get; set; }
- public int[] Bones { get { return bones; } set { bones = value; } }
- public float[] Weights { get { return weights; } set { weights = value; } }
- public float[] RegionUVs { get { return regionUVs; } set { regionUVs = value; } }
- public float[] UVs { get { return uvs; } set { uvs = value; } }
- public int[] Triangles { get { return triangles; } set { triangles = value; } }
-
- public float R { get { return r; } set { r = value; } }
- public float G { get { return g; } set { g = value; } }
- public float B { get { return b; } set { b = value; } }
- public float A { get { return a; } set { a = value; } }
-
- public String Path { get; set; }
- public Object RendererObject { get; set; }
- public float RegionU { get; set; }
- public float RegionV { get; set; }
- public float RegionU2 { get; set; }
- public float RegionV2 { get; set; }
- public bool RegionRotate { get; set; }
- public float RegionOffsetX { get { return regionOffsetX; } set { regionOffsetX = value; } }
- public float RegionOffsetY { get { return regionOffsetY; } set { regionOffsetY = value; } } // Pixels stripped from the bottom left, unrotated.
- public float RegionWidth { get { return regionWidth; } set { regionWidth = value; } }
- public float RegionHeight { get { return regionHeight; } set { regionHeight = value; } } // Unrotated, stripped size.
- public float RegionOriginalWidth { get { return regionOriginalWidth; } set { regionOriginalWidth = value; } }
- public float RegionOriginalHeight { get { return regionOriginalHeight; } set { regionOriginalHeight = value; } } // Unrotated, unstripped size.
-
- public bool InheritFFD { get { return inheritFFD; } set { inheritFFD = value; } }
-
- public WeightedMeshAttachment ParentMesh {
- get { return parentMesh; }
- set {
- parentMesh = value;
- if (value != null) {
- bones = value.bones;
- weights = value.weights;
- regionUVs = value.regionUVs;
- triangles = value.triangles;
- HullLength = value.HullLength;
- Edges = value.Edges;
- Width = value.Width;
- Height = value.Height;
- }
- }
- }
-
- // Nonessential.
- public int[] Edges { get; set; }
- public float Width { get; set; }
- public float Height { get; set; }
-
- public WeightedMeshAttachment (string name)
- : base(name) {
- }
-
- public void UpdateUVs () {
- float u = RegionU, v = RegionV, width = RegionU2 - RegionU, height = RegionV2 - RegionV;
- float[] regionUVs = this.regionUVs;
- if (this.uvs == null || this.uvs.Length != regionUVs.Length) this.uvs = new float[regionUVs.Length];
- float[] uvs = this.uvs;
- if (RegionRotate) {
- for (int i = 0, n = uvs.Length; i < n; i += 2) {
- uvs[i] = u + regionUVs[i + 1] * width;
- uvs[i + 1] = v + height - regionUVs[i] * height;
- }
- } else {
- for (int i = 0, n = uvs.Length; i < n; i += 2) {
- uvs[i] = u + regionUVs[i] * width;
- uvs[i + 1] = v + regionUVs[i + 1] * height;
- }
- }
- }
-
- public void ComputeWorldVertices (Slot slot, float[] worldVertices) {
- Skeleton skeleton = slot.bone.skeleton;
- ExposedList skeletonBones = skeleton.bones;
- float x = skeleton.x, y = skeleton.y;
- float[] weights = this.weights;
- int[] bones = this.bones;
- if (slot.attachmentVerticesCount == 0) {
- for (int w = 0, v = 0, b = 0, n = bones.Length; v < n; w += 2) {
- float wx = 0, wy = 0;
- int nn = bones[v++] + v;
- 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.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;
- }
- } else {
- float[] ffd = slot.attachmentVertices;
- for (int w = 0, v = 0, b = 0, f = 0, n = bones.Length; v < n; w += 2) {
- float wx = 0, wy = 0;
- int nn = bones[v++] + v;
- 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.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;
- }
- }
- }
-
- public bool ApplyFFD (Attachment sourceAttachment) {
- return this == sourceAttachment || (inheritFFD && parentMesh == sourceAttachment);
- }
- }
-}
diff --git a/spine-csharp/src/Bone.cs b/spine-csharp/src/Bone.cs
index 01f1efbcb..1c28e9cfb 100644
--- a/spine-csharp/src/Bone.cs
+++ b/spine-csharp/src/Bone.cs
@@ -30,7 +30,6 @@
*****************************************************************************/
using System;
-using System.Collections.Generic;
namespace Spine {
public class Bone : IUpdatable {
@@ -41,12 +40,14 @@ namespace Spine {
internal Bone parent;
internal ExposedList children = new ExposedList();
internal float x, y, rotation, scaleX, scaleY, shearX, shearY;
- internal float appliedRotation, appliedScaleX, appliedScaleY;
+ internal float appliedRotation;
internal float a, b, worldX;
internal float c, d, worldY;
internal float worldSignX, worldSignY;
+ internal bool sorted;
+
public BoneData Data { get { return data; } }
public Skeleton Skeleton { get { return skeleton; } }
public Bone Parent { get { return parent; } }
@@ -56,10 +57,6 @@ namespace Spine {
public float Rotation { get { return rotation; } set { rotation = 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 float ShearX { get { return shearX; } set { shearX = value; } }
@@ -80,29 +77,27 @@ namespace Spine {
/// May be null.
public Bone (BoneData data, Skeleton skeleton, Bone parent) {
- if (data == null) throw new ArgumentNullException("data cannot be null.");
- if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null.");
+ if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
+ if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null.");
this.data = data;
this.skeleton = skeleton;
this.parent = parent;
SetToSetupPose();
}
- /// Same as {@link #updateWorldTransform()}. This method exists for Bone to implement {@link Updatable}.
+ /// Same as . This method exists for Bone to implement .
public void Update () {
UpdateWorldTransform(x, y, rotation, scaleX, scaleY, shearX, shearY);
}
- /// Computes the world SRT using the parent bone and this bone's local SRT.
+ /// Computes the world transform using the parent bone and this bone's local transform.
public void UpdateWorldTransform () {
UpdateWorldTransform(x, y, rotation, scaleX, scaleY, shearX, shearY);
}
- /// Computes the world SRT using the parent bone and the specified local SRT.
+ /// Computes the world transform using the parent bone and the specified local transform.
public void UpdateWorldTransform (float x, float y, float rotation, float scaleX, float scaleY, float shearX, float shearY) {
appliedRotation = rotation;
- appliedScaleX = scaleX;
- appliedScaleY = scaleY;
float rotationY = rotation + 90 + shearY;
float la = MathUtils.CosDeg(rotation + shearX) * scaleX, lb = MathUtils.CosDeg(rotationY) * scaleY;
@@ -152,10 +147,10 @@ namespace Spine {
do {
float cos = MathUtils.CosDeg(parent.appliedRotation), sin = MathUtils.SinDeg(parent.appliedRotation);
float temp = pa * cos + pb * sin;
- pb = pa * -sin + pb * cos;
+ pb = pb * cos - pa * sin;
pa = temp;
temp = pc * cos + pd * sin;
- pd = pc * -sin + pd * cos;
+ pd = pd * cos - pc * sin;
pc = temp;
if (!parent.data.inheritRotation) break;
@@ -171,24 +166,22 @@ namespace Spine {
pc = 0;
pd = 1;
do {
- float r = parent.appliedRotation, cos = MathUtils.CosDeg(r), sin = MathUtils.SinDeg(r);
- float psx = parent.appliedScaleX, psy = parent.appliedScaleY;
- float za = cos * psx, zb = -sin * psy, zc = sin * psx, zd = cos * psy;
+ float cos = MathUtils.CosDeg(parent.appliedRotation), sin = MathUtils.SinDeg(parent.appliedRotation);
+ float psx = parent.scaleX, psy = parent.scaleY;
+ float za = cos * psx, zb = sin * psy, zc = sin * psx, zd = cos * psy;
float temp = pa * za + pb * zc;
- pb = pa * zb + pb * zd;
+ pb = pb * zd - pa * zb;
pa = temp;
temp = pc * za + pd * zc;
- pd = pc * zb + pd * zd;
+ pd = pd * zd - pc * zb;
pc = temp;
- if (psx < 0) r = -r;
- cos = MathUtils.CosDeg(-r);
- sin = MathUtils.SinDeg(-r);
+ if (psx >= 0) sin = -sin;
temp = pa * cos + pb * sin;
- pb = pa * -sin + pb * cos;
+ pb = pb * cos - pa * sin;
pa = temp;
temp = pc * cos + pd * sin;
- pd = pc * -sin + pd * cos;
+ pd = pd * cos - pc * sin;
pc = temp;
if (!parent.data.inheritScale) break;
@@ -226,10 +219,86 @@ namespace Spine {
shearY = data.shearY;
}
- public void WorldToLocal (float worldX, float worldY, out float localX, out float localY) {
- float x = worldX - this.worldX, y = worldY - this.worldY;
+ public float WorldToLocalRotationX {
+ get {
+ Bone parent = this.parent;
+ if (parent == null) return rotation;
+ float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d, a = this.a, c = this.c;
+ return MathUtils.Atan2(pa * c - pc * a, pd * a - pb * c) * MathUtils.radDeg;
+ }
+ }
+
+ public float WorldToLocalRotationY {
+ get {
+ Bone parent = this.parent;
+ if (parent == null) return rotation;
+ float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d, b = this.b, d = this.d;
+ return MathUtils.Atan2(pa * d - pc * b, pd * b - pb * d) * MathUtils.radDeg;
+ }
+ }
+
+ public void RotateWorld (float degrees) {
+ float a = this.a, b = this.b, c = this.c, d = this.d;
+ float cos = MathUtils.CosDeg(degrees), sin = MathUtils.SinDeg(degrees);
+ this.a = cos * a - sin * c;
+ this.b = cos * b - sin * d;
+ this.c = sin * a + cos * c;
+ this.d = sin * b + cos * d;
+ }
+
+ ///
+ /// Computes the local transform from the world transform. This can be useful to perform processing on the local transform
+ /// after the world transform has been modified directly (eg, by a constraint).
+ ///
+ /// Some redundant information is lost by the world transform, such as -1,-1 scale versus 180 rotation. The computed local
+ /// transform values may differ from the original values but are functionally the same.
+ ///
+ public void UpdateLocalTransform () {
+ Bone parent = this.parent;
+ if (parent == null) {
+ x = worldX;
+ y = worldY;
+ rotation = MathUtils.Atan2(c, a) * MathUtils.radDeg;
+ scaleX = (float)Math.Sqrt(a * a + c * c);
+ scaleY = (float)Math.Sqrt(b * b + d * d);
+ float det = a * d - b * c;
+ shearX = 0;
+ shearY = MathUtils.Atan2(a * b + c * d, det) * MathUtils.radDeg;
+ return;
+ }
+ float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
+ float pid = 1 / (pa * pd - pb * pc);
+ float dx = worldX - parent.worldX, dy = worldY - parent.worldY;
+ x = (dx * pd * pid - dy * pb * pid);
+ y = (dy * pa * pid - dx * pc * pid);
+ float ia = pid * pd;
+ float id = pid * pa;
+ float ib = pid * pb;
+ float ic = pid * pc;
+ float ra = ia * a - ib * c;
+ float rb = ia * b - ib * d;
+ float rc = id * c - ic * a;
+ float rd = id * d - ic * b;
+ shearX = 0;
+ scaleX = (float)Math.Sqrt(ra * ra + rc * rc);
+ if (scaleX > 0.0001f) {
+ float det = ra * rd - rb * rc;
+ scaleY = det / scaleX;
+ shearY = MathUtils.Atan2(ra * rb + rc * rd, det) * MathUtils.radDeg;
+ rotation = MathUtils.Atan2(rc, ra) * MathUtils.radDeg;
+ } else {
+ scaleX = 0;
+ scaleY = (float)Math.Sqrt(rb * rb + rd * rd);
+ shearY = 0;
+ rotation = 90 - MathUtils.Atan2(rd, rb) * MathUtils.radDeg;
+ }
+ appliedRotation = rotation;
+ }
+
+ public void WorldToLocal (float worldX, float worldY, out float localX, out float localY) {
float a = this.a, b = this.b, c = this.c, d = this.d;
float invDet = 1 / (a * d - b * c);
+ float x = worldX - this.worldX, y = worldY - this.worldY;
localX = (x * d * invDet - y * b * invDet);
localY = (y * a * invDet - x * c * invDet);
}
diff --git a/spine-csharp/src/BoneData.cs b/spine-csharp/src/BoneData.cs
index 21b1ed555..db51b6738 100644
--- a/spine-csharp/src/BoneData.cs
+++ b/spine-csharp/src/BoneData.cs
@@ -33,14 +33,17 @@ using System;
namespace Spine {
public class BoneData {
- internal BoneData parent;
+ internal int index;
internal String name;
- internal float length, x, y, rotation, scaleX = 1, scaleY = 1, shearX, shearY;
- internal bool inheritScale = true, inheritRotation = true;
+ internal BoneData parent;
+ internal float length;
+ internal float x, y, rotation, scaleX = 1, scaleY = 1, shearX, shearY;
+ internal bool inheritRotation = true, inheritScale = true;
/// May be null.
- public BoneData Parent { get { return parent; } }
+ public int Index { get { return index; } }
public String Name { get { return name; } }
+ public BoneData Parent { get { return parent; } }
public float Length { get { return length; } set { length = value; } }
public float X { get { return x; } set { x = value; } }
public float Y { get { return y; } set { y = value; } }
@@ -49,12 +52,14 @@ namespace Spine {
public float ScaleY { get { return scaleY; } set { scaleY = value; } }
public float ShearX { get { return shearX; } set { shearX = value; } }
public float ShearY { get { return shearY; } set { shearY = value; } }
- public bool InheritScale { get { return inheritScale; } set { inheritScale = value; } }
public bool InheritRotation { get { return inheritRotation; } set { inheritRotation = value; } }
+ public bool InheritScale { get { return inheritScale; } set { inheritScale = value; } }
/// May be null.
- public BoneData (String name, BoneData parent) {
- if (name == null) throw new ArgumentNullException("name cannot be null.");
+ public BoneData (int index, String name, BoneData parent) {
+ if (index < 0) throw new ArgumentException("index must be >= 0", "index");
+ if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
+ this.index = index;
this.name = name;
this.parent = parent;
}
diff --git a/spine-csharp/src/Event.cs b/spine-csharp/src/Event.cs
index bbb2ad350..134a98026 100644
--- a/spine-csharp/src/Event.cs
+++ b/spine-csharp/src/Event.cs
@@ -40,6 +40,7 @@ namespace Spine {
public float Time { get; private set; }
public Event (float time, EventData data) {
+ if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
Time = time;
Data = data;
}
diff --git a/spine-csharp/src/EventData.cs b/spine-csharp/src/EventData.cs
index 187bb8219..3dc647bf5 100644
--- a/spine-csharp/src/EventData.cs
+++ b/spine-csharp/src/EventData.cs
@@ -41,7 +41,7 @@ namespace Spine {
public String String { get; set; }
public EventData (String name) {
- if (name == null) throw new ArgumentNullException("name cannot be null.");
+ if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
this.name = name;
}
diff --git a/spine-csharp/src/ExposedList.cs b/spine-csharp/src/ExposedList.cs
index 45287c0d7..a24db4a93 100644
--- a/spine-csharp/src/ExposedList.cs
+++ b/spine-csharp/src/ExposedList.cs
@@ -89,6 +89,12 @@ namespace Spine {
Capacity = Math.Max(Math.Max(Capacity * 2, DefaultCapacity), minimumSize);
}
+ public ExposedList Resize (int newSize) {
+ if (newSize > Items.Length) Array.Resize(ref Items, newSize);
+ Count = newSize;
+ return this;
+ }
+
private void CheckRange (int idx, int count) {
if (idx < 0)
throw new ArgumentOutOfRangeException("index");
@@ -580,4 +586,4 @@ namespace Spine {
}
}
}
-}
\ No newline at end of file
+}
diff --git a/spine-csharp/src/IkConstraint.cs b/spine-csharp/src/IkConstraint.cs
index 3b4530135..c15627f72 100644
--- a/spine-csharp/src/IkConstraint.cs
+++ b/spine-csharp/src/IkConstraint.cs
@@ -30,15 +30,16 @@
*****************************************************************************/
using System;
-using System.Collections.Generic;
namespace Spine {
public class IkConstraint : IUpdatable {
internal IkConstraintData data;
internal ExposedList bones = new ExposedList();
internal Bone target;
- internal int bendDirection;
internal float mix;
+ internal int bendDirection;
+
+ internal int level;
public IkConstraintData Data { get { return data; } }
public ExposedList Bones { get { return bones; } }
@@ -47,8 +48,8 @@ namespace Spine {
public float Mix { get { return mix; } set { mix = value; } }
public IkConstraint (IkConstraintData data, Skeleton skeleton) {
- if (data == null) throw new ArgumentNullException("data cannot be null.");
- if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null.");
+ if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
+ if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null.");
this.data = data;
mix = data.mix;
bendDirection = data.bendDirection;
@@ -87,21 +88,24 @@ namespace Spine {
float id = 1 / (pp.a * pp.d - pp.b * pp.c);
float x = targetX - pp.worldX, y = targetY - pp.worldY;
float tx = (x * pp.d - y * pp.b) * id - bone.x, ty = (y * pp.a - x * pp.c) * id - bone.y;
- float rotationIK = MathUtils.Atan2(ty, tx) * MathUtils.radDeg - bone.shearX;
+ float rotationIK = MathUtils.Atan2(ty, tx) * MathUtils.radDeg - bone.shearX - bone.rotation;
if (bone.scaleX < 0) rotationIK += 180;
if (rotationIK > 180)
rotationIK -= 360;
else if (rotationIK < -180) rotationIK += 360;
- bone.UpdateWorldTransform(bone.x, bone.y, bone.rotation + (rotationIK - bone.rotation) * alpha, bone.appliedScaleX,
- bone.appliedScaleY, bone.shearX, bone.shearY);
+ bone.UpdateWorldTransform(bone.x, bone.y, bone.rotation + rotationIK * alpha, bone.scaleX, bone.scaleY,
+ bone.shearX, bone.shearY);
}
/// 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.
/// 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.appliedScaleX, psy = parent.appliedScaleY;
+ if (alpha == 0) {
+ child.UpdateWorldTransform ();
+ return;
+ }
+ float px = parent.x, py = parent.y, psx = parent.scaleX, psy = parent.scaleY, csx = child.scaleX;
int os1, os2, s2;
if (psx < 0) {
psx = -psx;
@@ -115,44 +119,55 @@ namespace Spine {
psy = -psy;
s2 = -s2;
}
- float cx = child.x, cy = child.y, csx = child.appliedScaleX;
- bool u = Math.Abs(psx - psy) <= 0.0001f;
- if (!u && cy != 0) {
- child.worldX = parent.a * cx + parent.worldX;
- child.worldY = parent.c * cx + parent.worldY;
- cy = 0;
- }
if (csx < 0) {
csx = -csx;
os2 = 180;
} else
os2 = 0;
+ float cx = child.x, cy, cwx, cwy, a = parent.a, b = parent.b, c = parent.c, d = parent.d;
+ bool u = Math.Abs(psx - psy) <= 0.0001f;
+ if (!u) {
+ cy = 0;
+ cwx = a * cx + parent.worldX;
+ cwy = c * cx + parent.worldY;
+ } else {
+ cy = child.y;
+ cwx = a * cx + b * cy + parent.worldX;
+ cwy = c * cx + d * cy + parent.worldY;
+ }
Bone pp = parent.parent;
- float ppa = pp.a, ppb = pp.b, ppc = pp.c, ppd = pp.d, id = 1 / (ppa * ppd - ppb * ppc);
- float x = targetX - pp.worldX, y = targetY - pp.worldY;
- float tx = (x * ppd - y * ppb) * id - px, ty = (y * ppa - x * ppc) * id - py;
- x = child.worldX - pp.worldX;
- y = child.worldY - pp.worldY;
- float dx = (x * ppd - y * ppb) * id - px, dy = (y * ppa - x * ppc) * id - py;
+ a = pp.a;
+ b = pp.b;
+ c = pp.c;
+ d = pp.d;
+ float id = 1 / (a * d - b * c), x = targetX - pp.worldX, y = targetY - pp.worldY;
+ float tx = (x * d - y * b) * id - px, ty = (y * a - x * c) * id - py;
+ x = cwx - pp.worldX;
+ y = cwy - pp.worldY;
+ float dx = (x * d - y * b) * id - px, dy = (y * a - x * c) * id - py;
float l1 = (float)Math.Sqrt(dx * dx + dy * dy), l2 = child.data.length * csx, a1, a2;
if (u) {
l2 *= psx;
float cos = (tx * tx + ty * ty - l1 * l1 - l2 * l2) / (2 * l1 * l2);
- if (cos < -1) cos = -1;
+ 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);
+ a = l1 + l2 * cos;
+ b = l2 * MathUtils.Sin(a2);
+ a1 = MathUtils.Atan2(ty * a - tx * b, tx * a + ty * b);
} else {
- 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;
+ a = psx * l2;
+ b = psy * l2;
+ float aa = a * a, bb = b * b, dd = tx * tx + ty * ty, ta = MathUtils.Atan2(ty, tx);
+ c = bb * l1 * l1 + aa * dd - aa * bb;
+ float c1 = -2 * bb * l1, c2 = bb - aa;
+ d = c1 * c1 - 4 * c2 * c;
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 r0 = q / c2, r1 = c / q;
float r = Math.Abs(r0) < Math.Abs(r1) ? r0 : r1;
if (r * r <= dd) {
y = (float)Math.Sqrt(dd - r * r) * bendDir;
@@ -201,18 +216,20 @@ namespace Spine {
a2 = maxAngle * bendDir;
}
}
- outer:
+ outer:
float os = MathUtils.Atan2(cy, cx) * s2;
- a1 = (a1 - os) * MathUtils.radDeg + os1;
- a2 = ((a2 + os) * MathUtils.radDeg - child.shearX) * s2 + os2;
- 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(px, py, rotation + (a1 - rotation) * alpha, parent.appliedScaleX, parent.appliedScaleY, 0, 0);
+ a1 = (a1 - os) * MathUtils.radDeg + os1 - rotation;
+ if (a1 > 180)
+ a1 -= 360;
+ else if (a1 < -180) a1 += 360;
+ parent.UpdateWorldTransform(px, py, rotation + a1 * alpha, parent.scaleX, parent.scaleY, 0, 0);
rotation = child.rotation;
- child.UpdateWorldTransform(cx, cy, rotation + (a2 - rotation) * alpha, child.appliedScaleX, child.appliedScaleY, child.shearX, child.shearY);
+ a2 = ((a2 + os) * MathUtils.radDeg - child.shearX) * s2 + os2 - rotation;
+ if (a2 > 180)
+ a2 -= 360;
+ else if (a2 < -180) a2 += 360;
+ child.UpdateWorldTransform(cx, cy, rotation + a2 * alpha, child.scaleX, child.scaleY, child.shearX, child.shearY);
}
}
}
diff --git a/spine-csharp/src/IkConstraintData.cs b/spine-csharp/src/IkConstraintData.cs
index 6bb9c752d..2b0723b93 100644
--- a/spine-csharp/src/IkConstraintData.cs
+++ b/spine-csharp/src/IkConstraintData.cs
@@ -47,7 +47,7 @@ namespace Spine {
public float Mix { get { return mix; } set { mix = value; } }
public IkConstraintData (String name) {
- if (name == null) throw new ArgumentNullException("name cannot be null.");
+ if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
this.name = name;
}
diff --git a/spine-csharp/src/MathUtils.cs b/spine-csharp/src/MathUtils.cs
index 665cf8f06..e8a8f86df 100644
--- a/spine-csharp/src/MathUtils.cs
+++ b/spine-csharp/src/MathUtils.cs
@@ -54,22 +54,22 @@ namespace Spine {
sin[(int)(i * degToIndex) & SIN_MASK] = (float)Math.Sin(i * degRad);
}
- /** Returns the sine in radians from a lookup table. */
+ /// 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. */
+ /// 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. */
+
+ /// 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. */
+
+ /// Returns the cosine in radians from a lookup table.
static public float CosDeg (float degrees) {
return sin[(int)((degrees + 90) * degToIndex) & SIN_MASK];
}
@@ -91,5 +91,11 @@ namespace Spine {
atan = PI / 2 - z / (z * z + 0.28f);
return y < 0f ? atan - PI : atan;
}
+
+ static public float Clamp (float value, float min, float max) {
+ if (value < min) return min;
+ if (value > max) return max;
+ return value;
+ }
}
}
diff --git a/spine-csharp/src/PathConstraint.cs b/spine-csharp/src/PathConstraint.cs
new file mode 100644
index 000000000..eddada80d
--- /dev/null
+++ b/spine-csharp/src/PathConstraint.cs
@@ -0,0 +1,400 @@
+/******************************************************************************
+ * 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 PathConstraint : IUpdatable {
+ private const int NONE = -1, BEFORE = -2, AFTER = -3;
+
+ internal PathConstraintData data;
+ internal ExposedList bones;
+ internal Slot target;
+ internal float position, spacing, rotateMix, translateMix;
+
+ internal ExposedList spaces = new ExposedList(), positions = new ExposedList();
+ internal ExposedList world = new ExposedList(), curves = new ExposedList(), lengths = new ExposedList();
+ internal float[] segments = new float[10];
+
+ public float Position { get { return position; } set { position = value; } }
+ public float Spacing { get { return spacing; } set { spacing = value; } }
+ public float RotateMix { get { return rotateMix; } set { rotateMix = value; } }
+ public float TranslateMix { get { return translateMix; } set { translateMix = value; } }
+ public ExposedList Bones { get { return bones; } }
+ public Slot Target { get { return target; } set { target = value; } }
+ public PathConstraintData Data { get { return data; } }
+
+ public PathConstraint (PathConstraintData data, Skeleton skeleton) {
+ if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
+ if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null.");
+ this.data = data;
+ bones = new ExposedList(data.Bones.Count);
+ foreach (BoneData boneData in data.bones)
+ bones.Add(skeleton.FindBone(boneData.name));
+ target = skeleton.FindSlot(data.target.name);
+ position = data.position;
+ spacing = data.spacing;
+ rotateMix = data.rotateMix;
+ translateMix = data.translateMix;
+ }
+
+ public void Apply () {
+ Update();
+ }
+
+ public void Update () {
+ PathAttachment attachment = target.Attachment as PathAttachment;
+ if (attachment == null) return;
+
+ float rotateMix = this.rotateMix, translateMix = this.translateMix;
+ bool translate = translateMix > 0, rotate = rotateMix > 0;
+ if (!translate && !rotate) return;
+
+ PathConstraintData data = this.data;
+ SpacingMode spacingMode = data.spacingMode;
+ bool lengthSpacing = spacingMode == SpacingMode.Length;
+ RotateMode rotateMode = data.rotateMode;
+ bool tangents = rotateMode == RotateMode.Tangent, scale = rotateMode == RotateMode.ChainScale;
+ int boneCount = this.bones.Count, spacesCount = tangents ? boneCount : boneCount + 1;
+ Bone[] bones = this.bones.Items;
+ ExposedList spaces = this.spaces.Resize(spacesCount), lengths = null;
+ float spacing = this.spacing;
+ if (scale || lengthSpacing) {
+ if (scale) lengths = this.lengths.Resize(boneCount);
+ for (int i = 0, n = spacesCount - 1; i < n;) {
+ Bone bone = bones[i];
+ float length = bone.data.length, x = length * bone.a, y = length * bone.c;
+ length = (float)Math.Sqrt(x * x + y * y);
+ if (scale) lengths.Items[i] = length;
+ spaces.Items[++i] = lengthSpacing ? Math.Max(0, length + spacing) : spacing;
+ }
+ } else {
+ for (int i = 1; i < spacesCount; i++)
+ spaces.Items[i] = spacing;
+ }
+
+ float[] positions = ComputeWorldPositions(attachment, spacesCount, tangents,
+ data.positionMode == PositionMode.Percent, spacingMode == SpacingMode.Percent);
+ Skeleton skeleton = target.Skeleton;
+ float skeletonX = skeleton.x, skeletonY = skeleton.y;
+ float boneX = positions[0], boneY = positions[1], offsetRotation = data.offsetRotation;
+ bool tip = rotateMode == RotateMode.Chain && offsetRotation == 0;
+ for (int i = 0, p = 3; i < boneCount; i++, p += 3) {
+ Bone bone = (Bone)bones[i];
+ bone.worldX += (boneX - skeletonX - bone.worldX) * translateMix;
+ bone.worldY += (boneY - skeletonY - bone.worldY) * translateMix;
+ float x = positions[p], y = positions[p + 1], dx = x - boneX, dy = y - boneY;
+ if (scale) {
+ float length = lengths.Items[i];
+ if (length != 0) {
+ float s = ((float)Math.Sqrt(dx * dx + dy * dy) / length - 1) * rotateMix + 1;
+ bone.a *= s;
+ bone.c *= s;
+ }
+ }
+ boneX = x;
+ boneY = y;
+ if (rotate) {
+ float a = bone.a, b = bone.b, c = bone.c, d = bone.d, r, cos, sin;
+ if (tangents)
+ r = positions[p - 1];
+ else if (spaces.Items[i + 1] == 0)
+ r = positions[p + 2];
+ else
+ r = MathUtils.Atan2(dy, dx);
+ r -= MathUtils.Atan2(c, a) - offsetRotation * MathUtils.degRad;
+ if (tip) {
+ cos = MathUtils.Cos(r);
+ sin = MathUtils.Sin(r);
+ float length = bone.data.length;
+ boneX += (length * (cos * a - sin * c) - dx) * rotateMix;
+ boneY += (length * (sin * a + cos * c) - dy) * rotateMix;
+ }
+ if (r > MathUtils.PI)
+ r -= MathUtils.PI2;
+ else if (r < -MathUtils.PI) //
+ r += MathUtils.PI2;
+ r *= rotateMix;
+ cos = MathUtils.Cos(r);
+ sin = MathUtils.Sin(r);
+ bone.a = cos * a - sin * c;
+ bone.b = cos * b - sin * d;
+ bone.c = sin * a + cos * c;
+ bone.d = sin * b + cos * d;
+ }
+ }
+ }
+
+ float[] ComputeWorldPositions (PathAttachment path, int spacesCount, bool tangents, bool percentPosition,
+ bool percentSpacing) {
+
+ Slot target = this.target;
+ float position = this.position;
+ float[] spaces = this.spaces.Items, output = this.positions.Resize(spacesCount * 3 + 2).Items, world;
+ bool closed = path.Closed;
+ int verticesLength = path.WorldVerticesLength, curveCount = verticesLength / 6, prevCurve = NONE;
+
+ float pathLength;
+ if (!path.ConstantSpeed) {
+ float[] lengths = path.Lengths;
+ curveCount -= closed ? 1 : 2;
+ pathLength = lengths[curveCount];
+ if (percentPosition) position *= pathLength;
+ if (percentSpacing) {
+ for (int i = 0; i < spacesCount; i++)
+ spaces[i] *= pathLength;
+ }
+ world = this.world.Resize(8).Items;
+ for (int i = 0, o = 0, curve = 0; i < spacesCount; i++, o += 3) {
+ float space = spaces[i];
+ position += space;
+ float p = position;
+
+ if (closed) {
+ p %= pathLength;
+ if (p < 0) p += pathLength;
+ curve = 0;
+ } else if (p < 0) {
+ if (prevCurve != BEFORE) {
+ prevCurve = BEFORE;
+ path.ComputeWorldVertices(target, 2, 4, world, 0);
+ }
+ AddBeforePosition(p, world, 0, output, o);
+ continue;
+ } else if (p > pathLength) {
+ if (prevCurve != AFTER) {
+ prevCurve = AFTER;
+ path.ComputeWorldVertices(target, verticesLength - 6, 4, world, 0);
+ }
+ AddAfterPosition(p - pathLength, world, 0, output, o);
+ continue;
+ }
+
+ // Determine curve containing position.
+ for (;; curve++) {
+ float length = lengths[curve];
+ if (p > length) continue;
+ if (curve == 0)
+ p /= length;
+ else {
+ float prev = lengths[curve - 1];
+ p = (p - prev) / (length - prev);
+ }
+ break;
+ }
+ if (curve != prevCurve) {
+ prevCurve = curve;
+ if (closed && curve == curveCount) {
+ path.ComputeWorldVertices(target, verticesLength - 4, 4, world, 0);
+ path.ComputeWorldVertices(target, 0, 4, world, 4);
+ } else
+ path.ComputeWorldVertices(target, curve * 6 + 2, 8, world, 0);
+ }
+ AddCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], output, o,
+ tangents || (i > 0 && space == 0));
+ }
+ return output;
+ }
+
+ // World vertices.
+ if (closed) {
+ verticesLength += 2;
+ world = this.world.Resize(verticesLength).Items;
+ path.ComputeWorldVertices(target, 2, verticesLength - 4, world, 0);
+ path.ComputeWorldVertices(target, 0, 2, world, verticesLength - 4);
+ world[verticesLength - 2] = world[0];
+ world[verticesLength - 1] = world[1];
+ } else {
+ curveCount--;
+ verticesLength -= 4;
+ world = this.world.Resize(verticesLength).Items;
+ path.ComputeWorldVertices(target, 2, verticesLength, world, 0);
+ }
+
+ // Curve lengths.
+ float[] curves = this.curves.Resize(curveCount).Items;
+ pathLength = 0;
+ float x1 = world[0], y1 = world[1], cx1 = 0, cy1 = 0, cx2 = 0, cy2 = 0, x2 = 0, y2 = 0;
+ float tmpx, tmpy, dddfx, dddfy, ddfx, ddfy, dfx, dfy;
+ for (int i = 0, w = 2; i < curveCount; i++, w += 6) {
+ cx1 = world[w];
+ cy1 = world[w + 1];
+ cx2 = world[w + 2];
+ cy2 = world[w + 3];
+ x2 = world[w + 4];
+ y2 = world[w + 5];
+ tmpx = (x1 - cx1 * 2 + cx2) * 0.1875f;
+ tmpy = (y1 - cy1 * 2 + cy2) * 0.1875f;
+ dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.09375f;
+ dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.09375f;
+ ddfx = tmpx * 2 + dddfx;
+ ddfy = tmpy * 2 + dddfy;
+ dfx = (cx1 - x1) * 0.75f + tmpx + dddfx * 0.16666667f;
+ dfy = (cy1 - y1) * 0.75f + tmpy + dddfy * 0.16666667f;
+ pathLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
+ dfx += ddfx;
+ dfy += ddfy;
+ ddfx += dddfx;
+ ddfy += dddfy;
+ pathLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
+ dfx += ddfx;
+ dfy += ddfy;
+ pathLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
+ dfx += ddfx + dddfx;
+ dfy += ddfy + dddfy;
+ pathLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
+ curves[i] = pathLength;
+ x1 = x2;
+ y1 = y2;
+ }
+ if (percentPosition) position *= pathLength;
+ if (percentSpacing) {
+ for (int i = 0; i < spacesCount; i++)
+ spaces[i] *= pathLength;
+ }
+
+ float[] segments = this.segments;
+ float curveLength = 0;
+ for (int i = 0, o = 0, curve = 0, segment = 0; i < spacesCount; i++, o += 3) {
+ float space = spaces[i];
+ position += space;
+ float p = position;
+
+ if (closed) {
+ p %= pathLength;
+ if (p < 0) p += pathLength;
+ curve = 0;
+ } else if (p < 0) {
+ AddBeforePosition(p, world, 0, output, o);
+ continue;
+ } else if (p > pathLength) {
+ AddAfterPosition(p - pathLength, world, verticesLength - 4, output, o);
+ continue;
+ }
+
+ // Determine curve containing position.
+ for (;; curve++) {
+ float length = curves[curve];
+ if (p > length) continue;
+ if (curve == 0)
+ p /= length;
+ else {
+ float prev = curves[curve - 1];
+ p = (p - prev) / (length - prev);
+ }
+ break;
+ }
+
+ // Curve segment lengths.
+ if (curve != prevCurve) {
+ prevCurve = curve;
+ int ii = curve * 6;
+ x1 = world[ii];
+ y1 = world[ii + 1];
+ cx1 = world[ii + 2];
+ cy1 = world[ii + 3];
+ cx2 = world[ii + 4];
+ cy2 = world[ii + 5];
+ x2 = world[ii + 6];
+ y2 = world[ii + 7];
+ tmpx = (x1 - cx1 * 2 + cx2) * 0.03f;
+ tmpy = (y1 - cy1 * 2 + cy2) * 0.03f;
+ dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.006f;
+ dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.006f;
+ ddfx = tmpx * 2 + dddfx;
+ ddfy = tmpy * 2 + dddfy;
+ dfx = (cx1 - x1) * 0.3f + tmpx + dddfx * 0.16666667f;
+ dfy = (cy1 - y1) * 0.3f + tmpy + dddfy * 0.16666667f;
+ curveLength = (float)Math.Sqrt(dfx * dfx + dfy * dfy);
+ segments[0] = curveLength;
+ for (ii = 1; ii < 8; ii++) {
+ dfx += ddfx;
+ dfy += ddfy;
+ ddfx += dddfx;
+ ddfy += dddfy;
+ curveLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
+ segments[ii] = curveLength;
+ }
+ dfx += ddfx;
+ dfy += ddfy;
+ curveLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
+ segments[8] = curveLength;
+ dfx += ddfx + dddfx;
+ dfy += ddfy + dddfy;
+ curveLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
+ segments[9] = curveLength;
+ segment = 0;
+ }
+
+ // Weight by segment length.
+ p *= curveLength;
+ for (;; segment++) {
+ float length = segments[segment];
+ if (p > length) continue;
+ if (segment == 0)
+ p /= length;
+ else {
+ float prev = segments[segment - 1];
+ p = segment + (p - prev) / (length - prev);
+ }
+ break;
+ }
+ AddCurvePosition(p * 0.1f, x1, y1, cx1, cy1, cx2, cy2, x2, y2, output, o, tangents || (i > 0 && space == 0));
+ }
+ return output;
+ }
+
+ private void AddBeforePosition (float p, float[] temp, int i, float[] output, int o) {
+ float x1 = temp[i], y1 = temp[i + 1], dx = temp[i + 2] - x1, dy = temp[i + 3] - y1, r = MathUtils.Atan2(dy, dx);
+ output[o] = x1 + p * MathUtils.Cos(r);
+ output[o + 1] = y1 + p * MathUtils.Sin(r);
+ output[o + 2] = r;
+ }
+
+ private void AddAfterPosition (float p, float[] temp, int i, float[] output, int o) {
+ float x1 = temp[i + 2], y1 = temp[i + 3], dx = x1 - temp[i], dy = y1 - temp[i + 1], r = MathUtils.Atan2(dy, dx);
+ output[o] = x1 + p * MathUtils.Cos(r);
+ output[o + 1] = y1 + p * MathUtils.Sin(r);
+ output[o + 2] = r;
+ }
+
+ private void AddCurvePosition (float p, float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2,
+ float[] output, int o, bool tangents) {
+ if (p == 0) p = 0.0001f;
+ float tt = p * p, ttt = tt * p, u = 1 - p, uu = u * u, uuu = uu * u;
+ float ut = u * p, ut3 = ut * 3, uut3 = u * ut3, utt3 = ut3 * p;
+ float x = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt, y = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt;
+ output[o] = x;
+ output[o + 1] = y;
+ if (tangents) output[o + 2] = (float)Math.Atan2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt), x - (x1 * uu + cx1 * ut * 2 + cx2 * tt));
+ }
+ }
+}
diff --git a/spine-csharp/src/PathConstraintData.cs b/spine-csharp/src/PathConstraintData.cs
new file mode 100644
index 000000000..ed2cda3e0
--- /dev/null
+++ b/spine-csharp/src/PathConstraintData.cs
@@ -0,0 +1,74 @@
+/******************************************************************************
+ * 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 PathConstraintData {
+ internal String name;
+ internal ExposedList bones = new ExposedList();
+ internal SlotData target;
+ internal PositionMode positionMode;
+ internal SpacingMode spacingMode;
+ internal RotateMode rotateMode;
+ internal float offsetRotation;
+ internal float position, spacing, rotateMix, translateMix;
+
+ public ExposedList Bones { get { return bones; } }
+ public SlotData Target { get { return target; } set { target = value; } }
+ public PositionMode PositionMode { get { return positionMode; } set { positionMode = value; } }
+ public SpacingMode SpacingMode { get { return spacingMode; } set { spacingMode = value; } }
+ public RotateMode RotateMode { get { return rotateMode; } set { rotateMode = value; } }
+ public float OffsetRotation { get { return offsetRotation; } set { offsetRotation = value; } }
+ public float Position { get { return position; } set { position = value; } }
+ public float Spacing { get { return spacing; } set { spacing = value; } }
+ public float RotateMix { get { return rotateMix; } set { rotateMix = value; } }
+ public float TranslateMix { get { return translateMix; } set { translateMix = value; } }
+ public String Name { get { return name; } }
+
+ public PathConstraintData (String name) {
+ if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
+ this.name = name;
+ }
+ }
+
+ public enum PositionMode {
+ Fixed, Percent
+ }
+
+ public enum SpacingMode {
+ Length, Fixed, Percent
+ }
+
+ public enum RotateMode {
+ Tangent, Chain, ChainScale
+ }
+}
diff --git a/spine-csharp/src/Skeleton.cs b/spine-csharp/src/Skeleton.cs
index ea88ecd76..ca4c04ee3 100644
--- a/spine-csharp/src/Skeleton.cs
+++ b/spine-csharp/src/Skeleton.cs
@@ -38,9 +38,10 @@ namespace Spine {
internal ExposedList bones;
internal ExposedList slots;
internal ExposedList drawOrder;
- internal ExposedList ikConstraints;
+ internal ExposedList ikConstraints, ikConstraintsSorted;
internal ExposedList transformConstraints;
- private ExposedList updateCache = new ExposedList();
+ internal ExposedList pathConstraints;
+ internal ExposedList updateCache = new ExposedList();
internal Skin skin;
internal float r = 1, g = 1, b = 1, a = 1;
internal float time;
@@ -49,9 +50,12 @@ namespace Spine {
public SkeletonData Data { get { return data; } }
public ExposedList Bones { get { return bones; } }
+ public ExposedList UpdateCacheList { get { return updateCache; } }
public ExposedList Slots { get { return slots; } }
public ExposedList DrawOrder { get { return drawOrder; } }
- public ExposedList IkConstraints { get { return ikConstraints; } set { ikConstraints = value; } }
+ public ExposedList IkConstraints { get { return ikConstraints; } }
+ public ExposedList PathConstraints { get { return pathConstraints; } }
+ public ExposedList TransformConstraints { get { return transformConstraints; } }
public Skin Skin { get { return skin; } set { skin = value; } }
public float R { get { return r; } set { r = value; } }
public float G { get { return g; } set { g = value; } }
@@ -64,33 +68,37 @@ namespace Spine {
public bool FlipY { get { return flipY; } set { flipY = value; } }
public Bone RootBone {
- get {
- return bones.Count == 0 ? null : bones.Items[0];
- }
+ get { return bones.Count == 0 ? null : bones.Items[0]; }
}
public Skeleton (SkeletonData data) {
- if (data == null) throw new ArgumentNullException("data cannot be null.");
+ if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
this.data = data;
bones = new ExposedList(data.bones.Count);
foreach (BoneData boneData in data.bones) {
- Bone parent = boneData.parent == null ? null : bones.Items[data.bones.IndexOf(boneData.parent)];
- Bone bone = new Bone(boneData, this, parent);
- if (parent != null) parent.children.Add(bone);
+ Bone bone;
+ if (boneData.parent == null) {
+ bone = new Bone (boneData, this, null);
+ } else {
+ Bone parent = bones.Items[boneData.parent.index];
+ bone = new Bone (boneData, this, parent);
+ parent.children.Add (bone);
+ }
bones.Add(bone);
}
slots = new ExposedList(data.slots.Count);
drawOrder = new ExposedList(data.slots.Count);
foreach (SlotData slotData in data.slots) {
- Bone bone = bones.Items[data.bones.IndexOf(slotData.boneData)];
+ Bone bone = bones.Items[slotData.boneData.index];
Slot slot = new Slot(slotData, bone);
slots.Add(slot);
drawOrder.Add(slot);
}
ikConstraints = new ExposedList(data.ikConstraints.Count);
+ ikConstraintsSorted = new ExposedList(data.ikConstraints.Count);
foreach (IkConstraintData ikConstraintData in data.ikConstraints)
ikConstraints.Add(new IkConstraint(ikConstraintData, this));
@@ -98,46 +106,150 @@ namespace Spine {
foreach (TransformConstraintData transformConstraintData in data.transformConstraints)
transformConstraints.Add(new TransformConstraint(transformConstraintData, this));
+ pathConstraints = new ExposedList (data.pathConstraints.Count);
+ foreach (PathConstraintData pathConstraintData in data.pathConstraints)
+ pathConstraints.Add(new PathConstraint(pathConstraintData, this));
+
UpdateCache();
UpdateWorldTransform();
}
- /// Caches information about bones and constraints. Must be called if bones or constraints are added
+ /// Caches information about bones and constraints. Must be called if bones, constraints or weighted path attachments are added
/// or removed.
public void UpdateCache () {
- ExposedList bones = this.bones;
ExposedList updateCache = this.updateCache;
- ExposedList ikConstraints = this.ikConstraints;
- ExposedList transformConstraints = this.transformConstraints;
- int ikConstraintsCount = ikConstraints.Count;
- int transformConstraintsCount = transformConstraints.Count;
updateCache.Clear();
- for (int i = 0, n = bones.Count; i < n; i++) {
- Bone bone = bones.Items[i];
- updateCache.Add(bone);
- 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;
- }
+
+ ExposedList bones = this.bones;
+ for (int i = 0, n = bones.Count; i < n; i++)
+ bones.Items[i].sorted = false;
+
+ ExposedList ikConstraints = this.ikConstraintsSorted;
+ ikConstraints.Clear();
+ ikConstraints.AddRange(this.ikConstraints);
+ int ikCount = ikConstraints.Count;
+ for (int i = 0, level, n = ikCount; i < n; i++) {
+ IkConstraint ik = ikConstraints.Items[i];
+ Bone bone = ik.bones.Items[0].Parent;
+ for (level = 0; bone != null; level++)
+ bone = bone.Parent;
+ ik.level = level;
+ }
+ for (int i = 1, ii; i < ikCount; i++) {
+ IkConstraint ik = ikConstraints.Items [i];
+ int level = ik.level;
+ for (ii = i - 1; ii >= 0; ii--) {
+ IkConstraint other = ikConstraints.Items[ii];
+ if (other.level < level) break;
+ ikConstraints.Items[ii + 1] = other;
}
+ ikConstraints.Items[ii + 1] = ik;
+ }
+ for (int i = 0, n = ikConstraints.Count; i < n; i++) {
+ IkConstraint constraint = ikConstraints.Items[i];
+ Bone target = constraint.target;
+ SortBone(target);
+
+ ExposedList constrained = constraint.bones;
+ Bone parent = constrained.Items [0];
+ SortBone(parent);
+
+ updateCache.Add(constraint);
+
+ SortReset(parent.children);
+ constrained.Items[constrained.Count - 1].sorted = true;
}
- for (int i = 0; i < transformConstraintsCount; i++) {
- TransformConstraint transformConstraint = transformConstraints.Items[i];
- for (int ii = updateCache.Count - 1; i >= 0; ii--) {
- if (updateCache.Items[ii] == transformConstraint.bone) {
- updateCache.Insert(ii + 1, transformConstraint);
- break;
- }
- }
+ ExposedList pathConstraints = this.pathConstraints;
+ for (int i = 0, n = pathConstraints.Count; i < n; i++) {
+ PathConstraint constraint = pathConstraints.Items[i];
+
+ Slot slot = constraint.Target;
+ int slotIndex = slot.data.index;
+ Bone slotBone = slot.bone;
+ if (skin != null) SortPathConstraintAttachment(skin, slotIndex, slotBone);
+ if (data.defaultSkin != null && data.defaultSkin != skin)
+ SortPathConstraintAttachment(data.defaultSkin, slotIndex, slotBone);
+ for (int ii = 0, nn = data.skins.Count; ii < nn; ii++)
+ SortPathConstraintAttachment(data.skins.Items[ii], slotIndex, slotBone);
+
+ PathAttachment attachment = slot.Attachment as PathAttachment;
+ if (attachment != null) SortPathConstraintAttachment(attachment, slotBone);
+
+ ExposedList constrained = constraint.bones;
+ int boneCount = constrained.Count;
+ for (int ii = 0; ii < boneCount; ii++)
+ SortBone(constrained.Items[ii]);
+
+ updateCache.Add(constraint);
+
+ for (int ii = 0; ii < boneCount; ii++)
+ SortReset(constrained.Items[ii].children);
+ for (int ii = 0; ii < boneCount; ii++)
+ constrained.Items[ii].sorted = true;
+ }
+
+ ExposedList transformConstraints = this.transformConstraints;
+ for (int i = 0, n = transformConstraints.Count; i < n; i++) {
+ TransformConstraint constraint = transformConstraints.Items[i];
+
+ SortBone(constraint.target);
+
+ ExposedList constrained = constraint.bones;
+ int boneCount = constrained.Count;
+ for (int ii = 0; ii < boneCount; ii++)
+ SortBone(constrained.Items[ii]);
+
+ updateCache.Add(constraint);
+
+ for (int ii = 0; ii < boneCount; ii++)
+ SortReset(constrained.Items[ii].children);
+ for (int ii = 0; ii < boneCount; ii++)
+ constrained.Items[ii].sorted = true;
+ }
+
+ for (int i = 0, n = bones.Count; i < n; i++)
+ SortBone(bones.Items[i]);
+ }
+
+ private void SortPathConstraintAttachment (Skin skin, int slotIndex, Bone slotBone) {
+ foreach (var entry in skin.Attachments)
+ if (entry.Key.slotIndex == slotIndex) SortPathConstraintAttachment(entry.Value, slotBone);
+ }
+
+ private void SortPathConstraintAttachment (Attachment attachment, Bone slotBone) {
+ var pathAttachment = attachment as PathAttachment;
+ if (pathAttachment == null) return;
+ int[] pathBones = pathAttachment.bones;
+ if (pathBones == null)
+ SortBone(slotBone);
+ else {
+ var bones = this.bones;
+ for (int i = 0, n = pathBones.Length; i < n; i++)
+ SortBone(bones.Items[pathBones[i]]);
+ }
+ }
+
+ private void SortBone (Bone bone) {
+ if (bone.sorted) return;
+ Bone parent = bone.parent;
+ if (parent != null) SortBone(parent);
+ bone.sorted = true;
+ updateCache.Add(bone);
+ }
+
+ private void SortReset (ExposedList bones) {
+ var bonesItems = bones.Items;
+ for (int i = 0, n = bones.Count; i < n; i++) {
+ Bone bone = bonesItems[i];
+ if (bone.sorted) SortReset(bone.children);
+ bone.sorted = false;
}
}
/// Updates the world transform for each bone and applies constraints.
public void UpdateWorldTransform () {
- ExposedList updateCache = this.updateCache;
+ var updateCache = this.updateCache;
for (int i = 0, n = updateCache.Count; i < n; i++)
updateCache.Items[i].Update();
}
@@ -150,44 +262,56 @@ namespace Spine {
/// Sets the bones and constraints to their setup pose values.
public void SetBonesToSetupPose () {
- ExposedList bones = this.bones;
+ var bonesItems = this.bones.Items;
for (int i = 0, n = bones.Count; i < n; i++)
- bones.Items[i].SetToSetupPose();
+ bonesItems[i].SetToSetupPose();
- ExposedList ikConstraints = this.ikConstraints;
+ var ikConstraintsItems = this.ikConstraints.Items;
for (int i = 0, n = ikConstraints.Count; i < n; i++) {
- IkConstraint constraint = ikConstraints.Items[i];
+ IkConstraint constraint = ikConstraintsItems[i];
constraint.bendDirection = constraint.data.bendDirection;
constraint.mix = constraint.data.mix;
}
- ExposedList transformConstraints = this.transformConstraints;
+ var transformConstraintsItems = this.transformConstraints.Items;
for (int i = 0, n = transformConstraints.Count; i < n; i++) {
- TransformConstraint constraint = transformConstraints.Items[i];
+ TransformConstraint constraint = transformConstraintsItems[i];
TransformConstraintData data = constraint.data;
constraint.rotateMix = data.rotateMix;
constraint.translateMix = data.translateMix;
constraint.scaleMix = data.scaleMix;
constraint.shearMix = data.shearMix;
}
+
+ var pathConstraints = this.pathConstraints;
+ for (int i = 0, n = pathConstraints.Count; i < n; i++) {
+ PathConstraint constraint = pathConstraints.Items[i];
+ PathConstraintData data = constraint.data;
+ constraint.position = data.position;
+ constraint.spacing = data.spacing;
+ constraint.rotateMix = data.rotateMix;
+ constraint.translateMix = data.translateMix;
+ }
}
public void SetSlotsToSetupPose () {
- ExposedList slots = this.slots;
+ var slots = this.slots;
+ var slotsItems = slots.Items;
drawOrder.Clear();
for (int i = 0, n = slots.Count; i < n; i++)
- drawOrder.Add(slots.Items[i]);
+ drawOrder.Add(slotsItems[i]);
for (int i = 0, n = slots.Count; i < n; i++)
- slots.Items[i].SetToSetupPose(i);
+ slotsItems[i].SetToSetupPose();
}
/// May be null.
public Bone FindBone (String boneName) {
- if (boneName == null) throw new ArgumentNullException("boneName cannot be null.");
- ExposedList bones = this.bones;
+ if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null.");
+ var bones = this.bones;
+ var bonesItems = bones.Items;
for (int i = 0, n = bones.Count; i < n; i++) {
- Bone bone = bones.Items[i];
+ Bone bone = bonesItems[i];
if (bone.data.name == boneName) return bone;
}
return null;
@@ -195,19 +319,21 @@ namespace Spine {
/// -1 if the bone was not found.
public int FindBoneIndex (String boneName) {
- if (boneName == null) throw new ArgumentNullException("boneName cannot be null.");
- ExposedList bones = this.bones;
+ if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null.");
+ var bones = this.bones;
+ var bonesItems = bones.Items;
for (int i = 0, n = bones.Count; i < n; i++)
- if (bones.Items[i].data.name == boneName) return i;
+ if (bonesItems[i].data.name == boneName) return i;
return -1;
}
/// May be null.
public Slot FindSlot (String slotName) {
- if (slotName == null) throw new ArgumentNullException("slotName cannot be null.");
- ExposedList slots = this.slots;
+ if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
+ var slots = this.slots;
+ var slotsItems = slots.Items;
for (int i = 0, n = slots.Count; i < n; i++) {
- Slot slot = slots.Items[i];
+ Slot slot = slotsItems[i];
if (slot.data.name == slotName) return slot;
}
return null;
@@ -215,17 +341,18 @@ namespace Spine {
/// -1 if the bone was not found.
public int FindSlotIndex (String slotName) {
- if (slotName == null) throw new ArgumentNullException("slotName cannot be null.");
- ExposedList slots = this.slots;
+ if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
+ var slots = this.slots;
+ var slotsItems = slots.Items;
for (int i = 0, n = slots.Count; i < n; i++)
- if (slots.Items[i].data.name.Equals(slotName)) return i;
+ if (slotsItems[i].data.name.Equals(slotName)) return i;
return -1;
}
/// Sets a skin by name (see SetSkin).
public void SetSkin (String skinName) {
Skin skin = data.FindSkin(skinName);
- if (skin == null) throw new ArgumentException("Skin not found: " + skinName);
+ if (skin == null) throw new ArgumentException("Skin not found: " + skinName, "skinName");
SetSkin(skin);
}
@@ -259,7 +386,7 @@ namespace Spine {
/// May be null.
public Attachment GetAttachment (int slotIndex, String attachmentName) {
- if (attachmentName == null) throw new ArgumentNullException("attachmentName cannot be null.");
+ if (attachmentName == null) throw new ArgumentNullException("attachmentName", "attachmentName cannot be null.");
if (skin != null) {
Attachment attachment = skin.GetAttachment(slotIndex, attachmentName);
if (attachment != null) return attachment;
@@ -270,7 +397,7 @@ namespace Spine {
/// May be null.
public void SetAttachment (String slotName, String attachmentName) {
- if (slotName == null) throw new ArgumentNullException("slotName cannot be null.");
+ if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
ExposedList slots = this.slots;
for (int i = 0, n = slots.Count; i < n; i++) {
Slot slot = slots.Items[i];
@@ -286,10 +413,10 @@ namespace Spine {
}
throw new Exception("Slot not found: " + slotName);
}
-
- /** @return May be null. */
+
+ /// May be null.
public IkConstraint FindIkConstraint (String constraintName) {
- if (constraintName == null) throw new ArgumentNullException("constraintName cannot be null.");
+ if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
ExposedList ikConstraints = this.ikConstraints;
for (int i = 0, n = ikConstraints.Count; i < n; i++) {
IkConstraint ikConstraint = ikConstraints.Items[i];
@@ -298,9 +425,9 @@ namespace Spine {
return null;
}
- /** @return May be null. */
+ /// May be null.
public TransformConstraint FindTransformConstraint (String constraintName) {
- if (constraintName == null) throw new ArgumentNullException("constraintName cannot be null.");
+ if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
ExposedList transformConstraints = this.transformConstraints;
for (int i = 0, n = transformConstraints.Count; i < n; i++) {
TransformConstraint transformConstraint = transformConstraints.Items[i];
@@ -309,6 +436,17 @@ namespace Spine {
return null;
}
+ /// May be null.
+ public PathConstraint FindPathConstraint (String constraintName) {
+ if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
+ ExposedList pathConstraints = this.pathConstraints;
+ for (int i = 0, n = pathConstraints.Count; i < n; i++) {
+ PathConstraint constraint = pathConstraints.Items[i];
+ if (constraint.data.name.Equals(constraintName)) return constraint;
+ }
+ return null;
+ }
+
public void Update (float delta) {
time += delta;
}
diff --git a/spine-csharp/src/SkeletonBinary.cs b/spine-csharp/src/SkeletonBinary.cs
index d5228f43e..e564952ea 100644
--- a/spine-csharp/src/SkeletonBinary.cs
+++ b/spine-csharp/src/SkeletonBinary.cs
@@ -44,12 +44,17 @@ using Windows.Storage;
namespace Spine {
public class SkeletonBinary {
- public const int TIMELINE_ROTATE = 0;
- public const int TIMELINE_TRANSLATE = 1;
- public const int TIMELINE_SCALE = 2;
- public const int TIMELINE_SHEAR = 3;
- public const int TIMELINE_ATTACHMENT = 4;
- public const int TIMELINE_COLOR = 5;
+ public const int BONE_ROTATE = 0;
+ public const int BONE_TRANSLATE = 1;
+ public const int BONE_SCALE = 2;
+ public const int BONE_SHEAR = 3;
+
+ public const int SLOT_ATTACHMENT = 0;
+ public const int SLOT_COLOR = 1;
+
+ public const int PATH_POSITION = 0;
+ public const int PATH_SPACING = 1;
+ public const int PATH_MIX = 2;
public const int CURVE_LINEAR = 0;
public const int CURVE_STEPPED = 1;
@@ -122,55 +127,26 @@ namespace Spine {
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
String name = ReadString(input);
BoneData parent = i == 0 ? null : skeletonData.bones.Items[ReadVarint(input, true)];
- BoneData boneData = new BoneData(name, parent);
- boneData.rotation = ReadFloat(input);
- boneData.x = ReadFloat(input) * scale;
- boneData.y = ReadFloat(input) * scale;
- boneData.scaleX = ReadFloat(input);
- boneData.scaleY = ReadFloat(input);
- boneData.shearX = ReadFloat(input);
- boneData.shearY = ReadFloat(input);
- boneData.length = ReadFloat(input) * scale;
- boneData.inheritRotation = ReadBoolean(input);
- boneData.inheritScale = ReadBoolean(input);
+ BoneData data = new BoneData(i, name, parent);
+ data.rotation = ReadFloat(input);
+ data.x = ReadFloat(input) * scale;
+ data.y = ReadFloat(input) * scale;
+ data.scaleX = ReadFloat(input);
+ data.scaleY = ReadFloat(input);
+ data.shearX = ReadFloat(input);
+ data.shearY = ReadFloat(input);
+ data.length = ReadFloat(input) * scale;
+ data.inheritRotation = ReadBoolean(input);
+ data.inheritScale = ReadBoolean(input);
if (nonessential) ReadInt(input); // Skip bone color.
- skeletonData.bones.Add(boneData);
- }
-
- // IK constraints.
- for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
- IkConstraintData ikConstraintData = new IkConstraintData(ReadString(input));
- for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++)
- ikConstraintData.bones.Add(skeletonData.bones.Items[ReadVarint(input, true)]);
- ikConstraintData.target = skeletonData.bones.Items[ReadVarint(input, true)];
- ikConstraintData.mix = ReadFloat(input);
- ikConstraintData.bendDirection = ReadSByte(input);
- skeletonData.ikConstraints.Add(ikConstraintData);
- }
-
- // Transform constraints.
- for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
- TransformConstraintData transformConstraintData = new TransformConstraintData(ReadString(input));
- transformConstraintData.bone = skeletonData.bones.Items[ReadVarint(input, true)];
- transformConstraintData.target = skeletonData.bones.Items[ReadVarint(input, true)];
- transformConstraintData.offsetRotation = ReadFloat(input);
- transformConstraintData.offsetX = ReadFloat(input) * scale;
- transformConstraintData.offsetY = ReadFloat(input) * scale;
- transformConstraintData.offsetScaleX = ReadFloat(input);
- transformConstraintData.offsetScaleY = ReadFloat(input);
- transformConstraintData.offsetShearY = ReadFloat(input);
- transformConstraintData.rotateMix = ReadFloat(input);
- transformConstraintData.translateMix = ReadFloat(input);
- transformConstraintData.scaleMix = ReadFloat(input);
- transformConstraintData.shearMix = ReadFloat(input);
- skeletonData.transformConstraints.Add(transformConstraintData);
+ skeletonData.bones.Add(data);
}
// Slots.
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
String slotName = ReadString(input);
BoneData boneData = skeletonData.bones.Items[ReadVarint(input, true)];
- SlotData slotData = new SlotData(slotName, boneData);
+ SlotData slotData = new SlotData(i, slotName, boneData);
int color = ReadInt(input);
slotData.r = ((color & 0xff000000) >> 24) / 255f;
slotData.g = ((color & 0x00ff0000) >> 16) / 255f;
@@ -181,6 +157,55 @@ namespace Spine {
skeletonData.slots.Add(slotData);
}
+ // IK constraints.
+ for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
+ IkConstraintData data = new IkConstraintData(ReadString(input));
+ for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++)
+ data.bones.Add(skeletonData.bones.Items[ReadVarint(input, true)]);
+ data.target = skeletonData.bones.Items[ReadVarint(input, true)];
+ data.mix = ReadFloat(input);
+ data.bendDirection = ReadSByte(input);
+ skeletonData.ikConstraints.Add(data);
+ }
+
+ // Transform constraints.
+ for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
+ TransformConstraintData data = new TransformConstraintData(ReadString(input));
+ for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++)
+ data.bones.Add(skeletonData.bones.Items[ReadVarint(input, true)]);
+ data.target = skeletonData.bones.Items[ReadVarint(input, true)];
+ data.offsetRotation = ReadFloat(input);
+ data.offsetX = ReadFloat(input) * scale;
+ data.offsetY = ReadFloat(input) * scale;
+ data.offsetScaleX = ReadFloat(input);
+ data.offsetScaleY = ReadFloat(input);
+ data.offsetShearY = ReadFloat(input);
+ data.rotateMix = ReadFloat(input);
+ data.translateMix = ReadFloat(input);
+ data.scaleMix = ReadFloat(input);
+ data.shearMix = ReadFloat(input);
+ skeletonData.transformConstraints.Add(data);
+ }
+
+ // Path constraints
+ for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
+ PathConstraintData data = new PathConstraintData(ReadString(input));
+ for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++)
+ data.bones.Add(skeletonData.bones.Items[ReadVarint(input, true)]);
+ data.target = skeletonData.slots.Items[ReadVarint(input, true)];
+ data.positionMode = (PositionMode)Enum.GetValues(typeof(PositionMode)).GetValue(ReadVarint(input, true));
+ data.spacingMode = (SpacingMode)Enum.GetValues(typeof(SpacingMode)).GetValue(ReadVarint(input, true));
+ data.rotateMode = (RotateMode)Enum.GetValues(typeof(RotateMode)).GetValue(ReadVarint(input, true));
+ data.offsetRotation = ReadFloat(input);
+ data.position = ReadFloat(input);
+ if (data.positionMode == PositionMode.Fixed) data.position *= scale;
+ data.spacing = ReadFloat(input);
+ if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) data.spacing *= scale;
+ data.rotateMix = ReadFloat(input);
+ data.translateMix = ReadFloat(input);
+ skeletonData.pathConstraints.Add(data);
+ }
+
// Default skin.
Skin defaultSkin = ReadSkin(input, "default", nonessential);
if (defaultSkin != null) {
@@ -199,25 +224,18 @@ namespace Spine {
if (skin == null) throw new Exception("Skin not found: " + linkedMesh.skin);
Attachment parent = skin.GetAttachment(linkedMesh.slotIndex, linkedMesh.parent);
if (parent == null) throw new Exception("Parent mesh not found: " + linkedMesh.parent);
- if (linkedMesh.mesh is MeshAttachment) {
- MeshAttachment mesh = (MeshAttachment)linkedMesh.mesh;
- mesh.ParentMesh = (MeshAttachment)parent;
- mesh.UpdateUVs();
- } else {
- WeightedMeshAttachment mesh = (WeightedMeshAttachment)linkedMesh.mesh;
- mesh.ParentMesh = (WeightedMeshAttachment)parent;
- mesh.UpdateUVs();
- }
+ linkedMesh.mesh.ParentMesh = (MeshAttachment)parent;
+ linkedMesh.mesh.UpdateUVs();
}
linkedMeshes.Clear();
// Events.
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
- EventData eventData = new EventData(ReadString(input));
- eventData.Int = ReadVarint(input, false);
- eventData.Float = ReadFloat(input);
- eventData.String = ReadString(input);
- skeletonData.events.Add(eventData);
+ EventData data = new EventData(ReadString(input));
+ data.Int = ReadVarint(input, false);
+ data.Float = ReadFloat(input);
+ data.String = ReadString(input);
+ skeletonData.events.Add(data);
}
// Animations.
@@ -230,10 +248,12 @@ namespace Spine {
skeletonData.events.TrimExcess();
skeletonData.animations.TrimExcess();
skeletonData.ikConstraints.TrimExcess();
+ skeletonData.pathConstraints.TrimExcess();
return skeletonData;
}
- /** @return May be null. */
+
+ /// May be null.
private Skin ReadSkin (Stream input, String skinName, bool nonessential) {
int slotCount = ReadVarint(input, true);
if (slotCount == 0) return null;
@@ -256,7 +276,7 @@ namespace Spine {
AttachmentType type = (AttachmentType)input.ReadByte();
switch (type) {
- case AttachmentType.region: {
+ case AttachmentType.Region: {
String path = ReadString(input);
float rotation = ReadFloat(input);
float x = ReadFloat(input);
@@ -285,96 +305,25 @@ namespace Spine {
region.UpdateOffset();
return region;
}
- case AttachmentType.boundingbox: {
- float[] vertices = ReadFloatArray(input, ReadVarint(input, true) * 2, scale);
+ case AttachmentType.Boundingbox: {
+ int vertexCount = ReadVarint(input, true);
+ Vertices vertices = ReadVertices(input, vertexCount);
+ if (nonessential) ReadInt(input); //int color = nonessential ? ReadInt(input) : 0; // Avoid unused local warning.
+
BoundingBoxAttachment box = attachmentLoader.NewBoundingBoxAttachment(skin, name);
if (box == null) return null;
- box.vertices = vertices;
+ box.worldVerticesLength = vertexCount << 1;
+ box.vertices = vertices.vertices;
+ box.bones = vertices.bones;
return box;
}
- case AttachmentType.mesh: {
+ case AttachmentType.Mesh: {
String path = ReadString(input);
int color = ReadInt(input);
- int hullLength = 0;
- int verticesLength = ReadVarint(input, true) * 2;
- float[] uvs = ReadFloatArray(input, verticesLength, 1);
+ int vertexCount = ReadVarint(input, true);
+ float[] uvs = ReadFloatArray(input, vertexCount << 1, 1);
int[] triangles = ReadShortArray(input);
- float[] vertices = ReadFloatArray(input, verticesLength, scale);
- hullLength = ReadVarint(input, true);
- int[] edges = null;
- float width = 0, height = 0;
- if (nonessential) {
- edges = ReadShortArray(input);
- width = ReadFloat(input);
- height = ReadFloat(input);
- }
-
- if (path == null) path = name;
- MeshAttachment mesh = attachmentLoader.NewMeshAttachment(skin, name, path);
- if (mesh == null) return null;
- mesh.Path = path;
- mesh.r = ((color & 0xff000000) >> 24) / 255f;
- mesh.g = ((color & 0x00ff0000) >> 16) / 255f;
- mesh.b = ((color & 0x0000ff00) >> 8) / 255f;
- mesh.a = ((color & 0x000000ff)) / 255f;
- mesh.vertices = vertices;
- mesh.triangles = triangles;
- mesh.regionUVs = uvs;
- mesh.UpdateUVs();
- mesh.HullLength = hullLength;
- if (nonessential) {
- mesh.Edges = edges;
- mesh.Width = width * scale;
- mesh.Height = height * scale;
- }
- return mesh;
- }
- case AttachmentType.linkedmesh: {
- String path = ReadString(input);
- int color = ReadInt(input);
- String skinName = ReadString(input);
- String parent = ReadString(input);
- bool inheritFFD = ReadBoolean(input);
- float width = 0, height = 0;
- if (nonessential) {
- width = ReadFloat(input);
- height = ReadFloat(input);
- }
-
- if (path == null) path = name;
- MeshAttachment mesh = attachmentLoader.NewMeshAttachment(skin, name, path);
- if (mesh == null) return null;
- mesh.Path = path;
- mesh.r = ((color & 0xff000000) >> 24) / 255f;
- mesh.g = ((color & 0x00ff0000) >> 16) / 255f;
- mesh.b = ((color & 0x0000ff00) >> 8) / 255f;
- mesh.a = ((color & 0x000000ff)) / 255f;
- mesh.inheritFFD = inheritFFD;
- if (nonessential) {
- mesh.Width = width * scale;
- mesh.Height = height * scale;
- }
- linkedMeshes.Add(new SkeletonJson.LinkedMesh(mesh, skinName, slotIndex, parent));
- return mesh;
- }
- case AttachmentType.weightedmesh: {
- String path = ReadString(input);
- int color = ReadInt(input);
- int vertexCount = ReadVarint(input, true);
- float[] uvs = ReadFloatArray(input, vertexCount * 2, 1);
- int[] triangles = ReadShortArray(input);
- var weights = new List(uvs.Length * 3 * 3);
- var bones = new List(uvs.Length * 3);
- for (int i = 0; i < vertexCount; i++) {
- int boneCount = (int)ReadFloat(input);
- bones.Add(boneCount);
- for (int ii = 0; ii < boneCount; ii++) {
- bones.Add((int)ReadFloat(input));
- weights.Add(ReadFloat(input) * scale);
- weights.Add(ReadFloat(input) * scale);
- weights.Add(ReadFloat(input));
- }
- }
+ Vertices vertices = ReadVertices(input, vertexCount);
int hullLength = ReadVarint(input, true);
int[] edges = null;
float width = 0, height = 0;
@@ -385,33 +334,33 @@ namespace Spine {
}
if (path == null) path = name;
- WeightedMeshAttachment mesh = attachmentLoader.NewWeightedMeshAttachment(skin, name, path);
+ MeshAttachment mesh = attachmentLoader.NewMeshAttachment(skin, name, path);
if (mesh == null) return null;
mesh.Path = path;
mesh.r = ((color & 0xff000000) >> 24) / 255f;
mesh.g = ((color & 0x00ff0000) >> 16) / 255f;
mesh.b = ((color & 0x0000ff00) >> 8) / 255f;
mesh.a = ((color & 0x000000ff)) / 255f;
- mesh.bones = bones.ToArray();
- mesh.weights = weights.ToArray();
+ mesh.bones = vertices.bones;
+ mesh.vertices = vertices.vertices;
+ mesh.WorldVerticesLength = vertexCount << 1;
mesh.triangles = triangles;
mesh.regionUVs = uvs;
mesh.UpdateUVs();
- mesh.HullLength = hullLength * 2;
+ mesh.HullLength = hullLength << 1;
if (nonessential) {
mesh.Edges = edges;
mesh.Width = width * scale;
mesh.Height = height * scale;
}
- //
return mesh;
}
- case AttachmentType.weightedlinkedmesh: {
+ case AttachmentType.Linkedmesh: {
String path = ReadString(input);
int color = ReadInt(input);
String skinName = ReadString(input);
String parent = ReadString(input);
- bool inheritFFD = ReadBoolean(input);
+ bool inheritDeform = ReadBoolean(input);
float width = 0, height = 0;
if (nonessential) {
width = ReadFloat(input);
@@ -419,14 +368,14 @@ namespace Spine {
}
if (path == null) path = name;
- WeightedMeshAttachment mesh = attachmentLoader.NewWeightedMeshAttachment(skin, name, path);
+ MeshAttachment mesh = attachmentLoader.NewMeshAttachment(skin, name, path);
if (mesh == null) return null;
mesh.Path = path;
mesh.r = ((color & 0xff000000) >> 24) / 255f;
mesh.g = ((color & 0x00ff0000) >> 16) / 255f;
mesh.b = ((color & 0x0000ff00) >> 8) / 255f;
mesh.a = ((color & 0x000000ff)) / 255f;
- mesh.inheritFFD = inheritFFD;
+ mesh.inheritDeform = inheritDeform;
if (nonessential) {
mesh.Width = width * scale;
mesh.Height = height * scale;
@@ -434,10 +383,56 @@ namespace Spine {
linkedMeshes.Add(new SkeletonJson.LinkedMesh(mesh, skinName, slotIndex, parent));
return mesh;
}
+ case AttachmentType.Path: {
+ bool closed = ReadBoolean(input);
+ bool constantSpeed = ReadBoolean(input);
+ int vertexCount = ReadVarint(input, true);
+ Vertices vertices = ReadVertices(input, vertexCount);
+ float[] lengths = new float[vertexCount / 3];
+ for (int i = 0, n = lengths.Length; i < n; i++)
+ lengths[i] = ReadFloat(input) * scale;
+ if (nonessential) ReadInt(input); //int color = nonessential ? ReadInt(input) : 0; // Avoid unused local warning.
+
+ PathAttachment path = attachmentLoader.NewPathAttachment(skin, name);
+ if (path == null) return null;
+ path.closed = closed;
+ path.constantSpeed = constantSpeed;
+ path.worldVerticesLength = vertexCount << 1;
+ path.vertices = vertices.vertices;
+ path.bones = vertices.bones;
+ path.lengths = lengths;
+ return path;
+ }
}
return null;
}
+ private Vertices ReadVertices (Stream input, int vertexCount) {
+ float scale = Scale;
+ int verticesLength = vertexCount << 1;
+ Vertices vertices = new Vertices();
+ if(!ReadBoolean(input)) {
+ vertices.vertices = ReadFloatArray(input, verticesLength, scale);
+ return vertices;
+ }
+ var weights = new ExposedList(verticesLength * 3 * 3);
+ var bonesArray = new ExposedList(verticesLength * 3);
+ for (int i = 0; i < vertexCount; i++) {
+ int boneCount = ReadVarint(input, true);
+ bonesArray.Add(boneCount);
+ for (int ii = 0; ii < boneCount; ii++) {
+ bonesArray.Add(ReadVarint(input, true));
+ weights.Add(ReadFloat(input) * scale);
+ weights.Add(ReadFloat(input) * scale);
+ weights.Add(ReadFloat(input));
+ }
+ }
+
+ vertices.vertices = weights.ToArray();
+ vertices.bones = bonesArray.ToArray();
+ return vertices;
+ }
+
private float[] ReadFloatArray (Stream input, int n, float scale) {
float[] array = new float[n];
if (scale == 1) {
@@ -470,7 +465,7 @@ namespace Spine {
int timelineType = input.ReadByte();
int frameCount = ReadVarint(input, true);
switch (timelineType) {
- case TIMELINE_COLOR: {
+ case SLOT_COLOR: {
ColorTimeline timeline = new ColorTimeline(frameCount);
timeline.slotIndex = slotIndex;
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
@@ -484,10 +479,10 @@ namespace Spine {
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
}
timelines.Add(timeline);
- duration = Math.Max(duration, timeline.frames[frameCount * 5 - 5]);
+ duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * ColorTimeline.ENTRIES]);
break;
}
- case TIMELINE_ATTACHMENT: {
+ case SLOT_ATTACHMENT: {
AttachmentTimeline timeline = new AttachmentTimeline(frameCount);
timeline.slotIndex = slotIndex;
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++)
@@ -507,7 +502,7 @@ namespace Spine {
int timelineType = input.ReadByte();
int frameCount = ReadVarint(input, true);
switch (timelineType) {
- case TIMELINE_ROTATE: {
+ case BONE_ROTATE: {
RotateTimeline timeline = new RotateTimeline(frameCount);
timeline.boneIndex = boneIndex;
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
@@ -515,17 +510,17 @@ namespace Spine {
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
}
timelines.Add(timeline);
- duration = Math.Max(duration, timeline.frames[frameCount * 2 - 2]);
+ duration = Math.Max(duration, timeline.frames[(frameCount - 1) * RotateTimeline.ENTRIES]);
break;
}
- case TIMELINE_TRANSLATE:
- case TIMELINE_SCALE:
- case TIMELINE_SHEAR: {
+ case BONE_TRANSLATE:
+ case BONE_SCALE:
+ case BONE_SHEAR: {
TranslateTimeline timeline;
float timelineScale = 1;
- if (timelineType == TIMELINE_SCALE)
+ if (timelineType == BONE_SCALE)
timeline = new ScaleTimeline(frameCount);
- else if (timelineType == TIMELINE_SHEAR)
+ else if (timelineType == BONE_SHEAR)
timeline = new ShearTimeline(frameCount);
else {
timeline = new TranslateTimeline(frameCount);
@@ -538,7 +533,7 @@ namespace Spine {
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
}
timelines.Add(timeline);
- duration = Math.Max(duration, timeline.frames[frameCount * 3 - 3]);
+ duration = Math.Max(duration, timeline.frames[(frameCount - 1) * TranslateTimeline.ENTRIES]);
break;
}
}
@@ -546,81 +541,118 @@ namespace Spine {
}
// IK timelines.
- for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
- IkConstraintData constraint = skeletonData.ikConstraints.Items[ReadVarint(input, true)];
+ for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
+ int index = ReadVarint(input, true);
int frameCount = ReadVarint(input, true);
IkConstraintTimeline timeline = new IkConstraintTimeline(frameCount);
- timeline.ikConstraintIndex = skeletonData.ikConstraints.IndexOf(constraint);
+ timeline.ikConstraintIndex = index;
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
timeline.SetFrame(frameIndex, ReadFloat(input), ReadFloat(input), ReadSByte(input));
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
}
timelines.Add(timeline);
- duration = Math.Max(duration, timeline.frames[frameCount * 3 - 3]);
+ duration = Math.Max(duration, timeline.frames[(frameCount - 1) * IkConstraintTimeline.ENTRIES]);
}
// Transform constraint timelines.
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
- TransformConstraintData constraint = skeletonData.transformConstraints.Items[ReadVarint(input, true)];
+ int index = ReadVarint(input, true);
int frameCount = ReadVarint(input, true);
TransformConstraintTimeline timeline = new TransformConstraintTimeline(frameCount);
- timeline.transformConstraintIndex = skeletonData.transformConstraints.IndexOf(constraint);
+ timeline.transformConstraintIndex = index;
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
timeline.SetFrame(frameIndex, ReadFloat(input), ReadFloat(input), ReadFloat(input), ReadFloat(input), ReadFloat(input));
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
}
timelines.Add(timeline);
- duration = Math.Max(duration, timeline.frames[frameCount * 5 - 5]);
+ duration = Math.Max(duration, timeline.frames[(frameCount - 1) * TransformConstraintTimeline.ENTRIES]);
}
- // FFD timelines.
+ // Path constraint timelines.
+ for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
+ int index = ReadVarint(input, true);
+ PathConstraintData data = skeletonData.pathConstraints.Items[index];
+ for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++) {
+ int timelineType = ReadSByte(input);
+ int frameCount = ReadVarint(input, true);
+ switch(timelineType) {
+ case PATH_POSITION:
+ case PATH_SPACING: {
+ PathConstraintPositionTimeline timeline;
+ float timelineScale = 1;
+ if (timelineType == PATH_SPACING) {
+ timeline = new PathConstraintSpacingTimeline(frameCount);
+ if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) timelineScale = scale;
+ } else {
+ timeline = new PathConstraintPositionTimeline(frameCount);
+ if (data.positionMode == PositionMode.Fixed) timelineScale = scale;
+ }
+ timeline.pathConstraintIndex = index;
+ for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
+ timeline.SetFrame(frameIndex, ReadFloat(input), ReadFloat(input) * timelineScale);
+ if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
+ }
+ timelines.Add(timeline);
+ duration = Math.Max(duration, timeline.frames[(frameCount - 1) * PathConstraintPositionTimeline.ENTRIES]);
+ break;
+ }
+ case PATH_MIX: {
+ PathConstraintMixTimeline timeline = new PathConstraintMixTimeline(frameCount);
+ timeline.pathConstraintIndex = index;
+ for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
+ timeline.SetFrame(frameIndex, ReadFloat(input), ReadFloat(input), ReadFloat(input));
+ if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
+ }
+ timelines.Add(timeline);
+ duration = Math.Max(duration, timeline.frames[(frameCount - 1) * PathConstraintMixTimeline.ENTRIES]);
+ break;
+ }
+ }
+ }
+ }
+
+ // Deform timelines.
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
Skin skin = skeletonData.skins.Items[ReadVarint(input, true)];
for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++) {
int slotIndex = ReadVarint(input, true);
for (int iii = 0, nnn = ReadVarint(input, true); iii < nnn; iii++) {
- Attachment attachment = skin.GetAttachment(slotIndex, ReadString(input));
+ VertexAttachment attachment = (VertexAttachment)skin.GetAttachment(slotIndex, ReadString(input));
+ bool weighted = attachment.bones != null;
+ float[] vertices = attachment.vertices;
+ int deformLength = weighted ? vertices.Length / 3 * 2 : vertices.Length;
+
int frameCount = ReadVarint(input, true);
- FfdTimeline timeline = new FfdTimeline(frameCount);
+ DeformTimeline timeline = new DeformTimeline(frameCount);
timeline.slotIndex = slotIndex;
timeline.attachment = attachment;
+
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
float time = ReadFloat(input);
-
- float[] vertices;
- int vertexCount;
- if (attachment is MeshAttachment)
- vertexCount = ((MeshAttachment)attachment).vertices.Length;
- else
- vertexCount = ((WeightedMeshAttachment)attachment).weights.Length / 3 * 2;
-
+ float[] deform;
int end = ReadVarint(input, true);
- if (end == 0) {
- if (attachment is MeshAttachment)
- vertices = ((MeshAttachment)attachment).vertices;
- else
- vertices = new float[vertexCount];
- } else {
- vertices = new float[vertexCount];
+ if (end == 0)
+ deform = weighted ? new float[deformLength] : vertices;
+ else {
+ deform = new float[deformLength];
int start = ReadVarint(input, true);
end += start;
if (scale == 1) {
for (int v = start; v < end; v++)
- vertices[v] = ReadFloat(input);
+ deform[v] = ReadFloat(input);
} else {
for (int v = start; v < end; v++)
- vertices[v] = ReadFloat(input) * scale;
+ deform[v] = ReadFloat(input) * scale;
}
- if (attachment is MeshAttachment) {
- float[] meshVertices = ((MeshAttachment)attachment).vertices;
- for (int v = 0, vn = vertices.Length; v < vn; v++)
- vertices[v] += meshVertices[v];
+ if (!weighted) {
+ for (int v = 0, vn = deform.Length; v < vn; v++)
+ deform[v] += vertices[v];
}
}
- timeline.SetFrame(frameIndex, time, vertices);
+ timeline.SetFrame(frameIndex, time, deform);
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
- }
+ }
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[frameCount - 1]);
}
@@ -756,5 +788,10 @@ namespace Spine {
length -= count;
}
}
+
+ internal class Vertices {
+ public int[] bones;
+ public float[] vertices;
+ }
}
}
diff --git a/spine-csharp/src/SkeletonBounds.cs b/spine-csharp/src/SkeletonBounds.cs
index 11d875a68..e694e596e 100644
--- a/spine-csharp/src/SkeletonBounds.cs
+++ b/spine-csharp/src/SkeletonBounds.cs
@@ -30,7 +30,6 @@
*****************************************************************************/
using System;
-using System.Collections.Generic;
namespace Spine {
public class SkeletonBounds {
@@ -80,7 +79,7 @@ namespace Spine {
int count = boundingBox.Vertices.Length;
polygon.Count = count;
if (polygon.Vertices.Length < count) polygon.Vertices = new float[count];
- boundingBox.ComputeWorldVertices(slot.bone, polygon.Vertices);
+ boundingBox.ComputeWorldVertices(slot, polygon.Vertices);
}
if (updateAabb) aabbCompute();
diff --git a/spine-csharp/src/SkeletonData.cs b/spine-csharp/src/SkeletonData.cs
index b6ff7edb5..dd3dcdbab 100644
--- a/spine-csharp/src/SkeletonData.cs
+++ b/spine-csharp/src/SkeletonData.cs
@@ -30,7 +30,6 @@
*****************************************************************************/
using System;
-using System.Collections.Generic;
namespace Spine {
public class SkeletonData {
@@ -43,6 +42,7 @@ namespace Spine {
internal ExposedList animations = new ExposedList();
internal ExposedList ikConstraints = new ExposedList();
internal ExposedList transformConstraints = new ExposedList();
+ internal ExposedList pathConstraints = new ExposedList();
internal float width, height;
internal String version, hash, imagesPath;
@@ -55,6 +55,8 @@ namespace Spine {
public ExposedList Events { get { return events; } set { events = value; } }
public ExposedList Animations { get { return animations; } set { animations = value; } }
public ExposedList IkConstraints { get { return ikConstraints; } set { ikConstraints = value; } }
+ public ExposedList TransformConstraints { get { return transformConstraints; } set { transformConstraints = value; } }
+ public ExposedList PathConstraints { get { return pathConstraints; } set { pathConstraints = value; } }
public float Width { get { return width; } set { width = value; } }
public float Height { get { return height; } set { height = value; } }
/// The Spine version used to export this data.
@@ -65,7 +67,7 @@ namespace Spine {
/// May be null.
public BoneData FindBone (String boneName) {
- if (boneName == null) throw new ArgumentNullException("boneName cannot be null.");
+ if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null.");
ExposedList bones = this.bones;
for (int i = 0, n = bones.Count; i < n; i++) {
BoneData bone = bones.Items[i];
@@ -76,7 +78,7 @@ namespace Spine {
/// -1 if the bone was not found.
public int FindBoneIndex (String boneName) {
- if (boneName == null) throw new ArgumentNullException("boneName cannot be null.");
+ if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null.");
ExposedList bones = this.bones;
for (int i = 0, n = bones.Count; i < n; i++)
if (bones.Items[i].name == boneName) return i;
@@ -87,7 +89,7 @@ namespace Spine {
/// May be null.
public SlotData FindSlot (String slotName) {
- if (slotName == null) throw new ArgumentNullException("slotName cannot be null.");
+ if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
ExposedList slots = this.slots;
for (int i = 0, n = slots.Count; i < n; i++) {
SlotData slot = slots.Items[i];
@@ -96,9 +98,9 @@ namespace Spine {
return null;
}
- /// -1 if the bone was not found.
+ /// -1 if the slot was not found.
public int FindSlotIndex (String slotName) {
- if (slotName == null) throw new ArgumentNullException("slotName cannot be null.");
+ if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
ExposedList slots = this.slots;
for (int i = 0, n = slots.Count; i < n; i++)
if (slots.Items[i].name == slotName) return i;
@@ -109,7 +111,7 @@ namespace Spine {
/// May be null.
public Skin FindSkin (String skinName) {
- if (skinName == null) throw new ArgumentNullException("skinName cannot be null.");
+ if (skinName == null) throw new ArgumentNullException("skinName", "skinName cannot be null.");
foreach (Skin skin in skins)
if (skin.name == skinName) return skin;
return null;
@@ -119,7 +121,7 @@ namespace Spine {
/// May be null.
public EventData FindEvent (String eventDataName) {
- if (eventDataName == null) throw new ArgumentNullException("eventDataName cannot be null.");
+ if (eventDataName == null) throw new ArgumentNullException("eventDataName", "eventDataName cannot be null.");
foreach (EventData eventData in events)
if (eventData.name == eventDataName) return eventData;
return null;
@@ -129,7 +131,7 @@ namespace Spine {
/// May be null.
public Animation FindAnimation (String animationName) {
- if (animationName == null) throw new ArgumentNullException("animationName cannot be null.");
+ if (animationName == null) throw new ArgumentNullException("animationName", "animationName cannot be null.");
ExposedList animations = this.animations;
for (int i = 0, n = animations.Count; i < n; i++) {
Animation animation = animations.Items[i];
@@ -142,7 +144,7 @@ namespace Spine {
/// May be null.
public IkConstraintData FindIkConstraint (String constraintName) {
- if (constraintName == null) throw new ArgumentNullException("constraintName cannot be null.");
+ if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
ExposedList ikConstraints = this.ikConstraints;
for (int i = 0, n = ikConstraints.Count; i < n; i++) {
IkConstraintData ikConstraint = ikConstraints.Items[i];
@@ -155,7 +157,7 @@ namespace Spine {
/// May be null.
public TransformConstraintData FindTransformConstraint (String constraintName) {
- if (constraintName == null) throw new ArgumentNullException("constraintName cannot be null.");
+ if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
ExposedList transformConstraints = this.transformConstraints;
for (int i = 0, n = transformConstraints.Count; i < n; i++) {
TransformConstraintData transformConstraint = transformConstraints.Items[i];
@@ -164,6 +166,28 @@ namespace Spine {
return null;
}
+ // --- Path constraints.
+
+ /// May be null.
+ public PathConstraintData FindPathConstraint (String constraintName) {
+ if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
+ ExposedList pathConstraints = this.pathConstraints;
+ for (int i = 0, n = pathConstraints.Count; i < n; i++) {
+ PathConstraintData constraint = pathConstraints.Items[i];
+ if (constraint.name.Equals(constraintName)) return constraint;
+ }
+ return null;
+ }
+
+ /// -1 if the path constraint was not found.
+ public int FindPathConstraintIndex (String pathConstraintName) {
+ if (pathConstraintName == null) throw new ArgumentNullException("pathConstraintName", "pathConstraintName cannot be null.");
+ ExposedList pathConstraints = this.pathConstraints;
+ for (int i = 0, n = pathConstraints.Count; i < n; i++)
+ if (pathConstraints.Items[i].name.Equals(pathConstraintName)) return i;
+ return -1;
+ }
+
// ---
override public String ToString () {
diff --git a/spine-csharp/src/SkeletonJson.cs b/spine-csharp/src/SkeletonJson.cs
index 5ae9f1238..1ad145e72 100644
--- a/spine-csharp/src/SkeletonJson.cs
+++ b/spine-csharp/src/SkeletonJson.cs
@@ -54,7 +54,7 @@ namespace Spine {
}
public SkeletonJson (AttachmentLoader attachmentLoader) {
- if (attachmentLoader == null) throw new ArgumentNullException("attachmentLoader cannot be null.");
+ if (attachmentLoader == null) throw new ArgumentNullException("attachmentLoader", "attachmentLoader cannot be null.");
this.attachmentLoader = attachmentLoader;
Scale = 1;
}
@@ -79,7 +79,7 @@ namespace Spine {
Stream stream = Microsoft.Xna.Framework.TitleContainer.OpenStream(path);
using (StreamReader reader = new StreamReader(stream)) {
#else
- using (StreamReader reader = new StreamReader(path)) {
+ using (var reader = new StreamReader(path)) {
#endif // WINDOWS_PHONE
SkeletonData skeletonData = ReadSkeletonData(reader);
skeletonData.name = Path.GetFileNameWithoutExtension(path);
@@ -89,7 +89,7 @@ namespace Spine {
#endif // WINDOWS_STOREAPP
public SkeletonData ReadSkeletonData (TextReader reader) {
- if (reader == null) throw new ArgumentNullException("reader cannot be null.");
+ if (reader == null) throw new ArgumentNullException("reader", "reader cannot be null.");
var scale = this.Scale;
var skeletonData = new SkeletonData();
@@ -114,69 +114,19 @@ namespace Spine {
if (parent == null)
throw new Exception("Parent bone not found: " + boneMap["parent"]);
}
- var boneData = new BoneData((String)boneMap["name"], parent);
- boneData.length = GetFloat(boneMap, "length", 0) * scale;
- boneData.x = GetFloat(boneMap, "x", 0) * scale;
- boneData.y = GetFloat(boneMap, "y", 0) * scale;
- boneData.rotation = GetFloat(boneMap, "rotation", 0);
- boneData.scaleX = GetFloat(boneMap, "scaleX", 1);
- boneData.scaleY = GetFloat(boneMap, "scaleY", 1);
- boneData.shearX = GetFloat(boneMap, "shearX", 0);
- boneData.shearY = GetFloat(boneMap, "shearY", 0);
- boneData.inheritScale = GetBoolean(boneMap, "inheritScale", true);
- boneData.inheritRotation = GetBoolean(boneMap, "inheritRotation", true);
- skeletonData.bones.Add(boneData);
- }
+ var data = new BoneData(skeletonData.Bones.Count, (String)boneMap["name"], parent);
+ data.length = GetFloat(boneMap, "length", 0) * scale;
+ data.x = GetFloat(boneMap, "x", 0) * scale;
+ data.y = GetFloat(boneMap, "y", 0) * scale;
+ data.rotation = GetFloat(boneMap, "rotation", 0);
+ data.scaleX = GetFloat(boneMap, "scaleX", 1);
+ data.scaleY = GetFloat(boneMap, "scaleY", 1);
+ data.shearX = GetFloat(boneMap, "shearX", 0);
+ data.shearY = GetFloat(boneMap, "shearY", 0);
+ data.inheritRotation = GetBoolean(boneMap, "inheritRotation", true);
+ data.inheritScale = GetBoolean(boneMap, "inheritScale", true);
- // IK constraints.
- if (root.ContainsKey("ik")) {
- foreach (Dictionary ikMap in (List