mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-05 02:06:53 +08:00
Update spine-csharp to v3.2 (shearing) (#562)
* spine-csharp updated to 3.2.00. * Some cleanup, catchup and fixes. * Mix transform constraint scale and shear offsets. * Update readmes. * Clean up. * Formatting. * Fixed single bone IK with nonuniform scale. Improved two bone IK.
This commit is contained in:
parent
05d60e5377
commit
f1406e2f85
@ -10,7 +10,7 @@ The Spine Runtimes are developed with the intent to be used with data exported f
|
|||||||
|
|
||||||
## Spine version
|
## Spine version
|
||||||
|
|
||||||
spine-csharp works with data exported from Spine 3.1.08. Updating spine-csharp to [v3.2](https://trello.com/c/k7KtGdPW/76-update-runtimes-to-support-v3-2-shearing) is in progress.
|
spine-csharp works with data exported from the latest version of Spine.
|
||||||
|
|
||||||
spine-csharp supports all Spine features.
|
spine-csharp supports all Spine features.
|
||||||
|
|
||||||
|
|||||||
@ -209,14 +209,14 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public class RotateTimeline : CurveTimeline {
|
public class RotateTimeline : CurveTimeline {
|
||||||
protected const int PREV_FRAME_TIME = -2;
|
internal const int PREV_TIME = -2;
|
||||||
protected const int FRAME_VALUE = 1;
|
internal const int VALUE = 1;
|
||||||
|
|
||||||
internal int boneIndex;
|
internal int boneIndex;
|
||||||
internal float[] frames;
|
internal float[] frames;
|
||||||
|
|
||||||
public int BoneIndex { get { return boneIndex; } set { boneIndex = value; } }
|
public int BoneIndex { get { return boneIndex; } set { boneIndex = value; } }
|
||||||
public float[] Frames { get { return frames; } set { frames = value; } } // time, value, ...
|
public float[] Frames { get { return frames; } set { frames = value; } } // time, angle, ...
|
||||||
|
|
||||||
public RotateTimeline (int frameCount)
|
public RotateTimeline (int frameCount)
|
||||||
: base(frameCount) {
|
: base(frameCount) {
|
||||||
@ -249,13 +249,13 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Interpolate between the previous frame and the current frame.
|
// Interpolate between the previous frame and the current frame.
|
||||||
int frameIndex = Animation.binarySearch(frames, time, 2);
|
int frame = Animation.binarySearch(frames, time, 2);
|
||||||
float prevFrameValue = frames[frameIndex - 1];
|
float prevFrameValue = frames[frame - 1];
|
||||||
float frameTime = frames[frameIndex];
|
float frameTime = frames[frame];
|
||||||
float percent = 1 - (time - frameTime) / (frames[frameIndex + PREV_FRAME_TIME] - frameTime);
|
float percent = 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime);
|
||||||
percent = GetCurvePercent((frameIndex >> 1) - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
|
percent = GetCurvePercent((frame >> 1) - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
|
||||||
|
|
||||||
amount = frames[frameIndex + FRAME_VALUE] - prevFrameValue;
|
amount = frames[frame + VALUE] - prevFrameValue;
|
||||||
while (amount > 180)
|
while (amount > 180)
|
||||||
amount -= 360;
|
amount -= 360;
|
||||||
while (amount < -180)
|
while (amount < -180)
|
||||||
@ -270,9 +270,9 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public class TranslateTimeline : CurveTimeline {
|
public class TranslateTimeline : CurveTimeline {
|
||||||
protected const int PREV_FRAME_TIME = -3;
|
protected const int PREV_TIME = -3;
|
||||||
protected const int FRAME_X = 1;
|
protected const int X = 1;
|
||||||
protected const int FRAME_Y = 2;
|
protected const int Y = 2;
|
||||||
|
|
||||||
internal int boneIndex;
|
internal int boneIndex;
|
||||||
internal float[] frames;
|
internal float[] frames;
|
||||||
@ -306,15 +306,15 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Interpolate between the previous frame and the current frame.
|
// Interpolate between the previous frame and the current frame.
|
||||||
int frameIndex = Animation.binarySearch(frames, time, 3);
|
int frame = Animation.binarySearch(frames, time, 3);
|
||||||
float prevFrameX = frames[frameIndex - 2];
|
float prevFrameX = frames[frame - 2];
|
||||||
float prevFrameY = frames[frameIndex - 1];
|
float prevFrameY = frames[frame - 1];
|
||||||
float frameTime = frames[frameIndex];
|
float frameTime = frames[frame];
|
||||||
float percent = 1 - (time - frameTime) / (frames[frameIndex + PREV_FRAME_TIME] - frameTime);
|
float percent = 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime);
|
||||||
percent = GetCurvePercent(frameIndex / 3 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
|
percent = GetCurvePercent(frame / 3 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
|
||||||
|
|
||||||
bone.x += (bone.data.x + prevFrameX + (frames[frameIndex + FRAME_X] - prevFrameX) * percent - bone.x) * alpha;
|
bone.x += (bone.data.x + prevFrameX + (frames[frame + X] - prevFrameX) * percent - bone.x) * alpha;
|
||||||
bone.y += (bone.data.y + prevFrameY + (frames[frameIndex + FRAME_Y] - prevFrameY) * percent - bone.y) * alpha;
|
bone.y += (bone.data.y + prevFrameY + (frames[frame + Y] - prevFrameY) * percent - bone.y) * alpha;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -335,24 +335,53 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Interpolate between the previous frame and the current frame.
|
// Interpolate between the previous frame and the current frame.
|
||||||
int frameIndex = Animation.binarySearch(frames, time, 3);
|
int frame = Animation.binarySearch(frames, time, 3);
|
||||||
float prevFrameX = frames[frameIndex - 2];
|
float prevFrameX = frames[frame - 2];
|
||||||
float prevFrameY = frames[frameIndex - 1];
|
float prevFrameY = frames[frame - 1];
|
||||||
float frameTime = frames[frameIndex];
|
float frameTime = frames[frame];
|
||||||
float percent = 1 - (time - frameTime) / (frames[frameIndex + PREV_FRAME_TIME] - frameTime);
|
float percent = 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime);
|
||||||
percent = GetCurvePercent(frameIndex / 3 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
|
percent = GetCurvePercent(frame / 3 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
|
||||||
|
|
||||||
bone.scaleX += (bone.data.scaleX * (prevFrameX + (frames[frameIndex + FRAME_X] - prevFrameX) * percent) - bone.scaleX) * alpha;
|
bone.scaleX += (bone.data.scaleX * (prevFrameX + (frames[frame + X] - prevFrameX) * percent) - bone.scaleX) * alpha;
|
||||||
bone.scaleY += (bone.data.scaleY * (prevFrameY + (frames[frameIndex + FRAME_Y] - prevFrameY) * percent) - bone.scaleY) * alpha;
|
bone.scaleY += (bone.data.scaleY * (prevFrameY + (frames[frame + Y] - prevFrameY) * percent) - bone.scaleY) * alpha;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ShearTimeline : TranslateTimeline {
|
||||||
|
public ShearTimeline (int frameCount)
|
||||||
|
: base (frameCount) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha) {
|
||||||
|
float[] frames = this.frames;
|
||||||
|
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;
|
||||||
|
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];
|
||||||
|
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));
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ColorTimeline : CurveTimeline {
|
public class ColorTimeline : CurveTimeline {
|
||||||
protected const int PREV_FRAME_TIME = -5;
|
protected const int PREV_TIME = -5;
|
||||||
protected const int FRAME_R = 1;
|
protected const int R = 1;
|
||||||
protected const int FRAME_G = 2;
|
protected const int G = 2;
|
||||||
protected const int FRAME_B = 3;
|
protected const int B = 3;
|
||||||
protected const int FRAME_A = 4;
|
protected const int A = 4;
|
||||||
|
|
||||||
internal int slotIndex;
|
internal int slotIndex;
|
||||||
internal float[] frames;
|
internal float[] frames;
|
||||||
@ -380,8 +409,7 @@ namespace Spine {
|
|||||||
if (time < frames[0]) return; // Time is before first frame.
|
if (time < frames[0]) return; // Time is before first frame.
|
||||||
|
|
||||||
float r, g, b, a;
|
float r, g, b, a;
|
||||||
if (time >= frames[frames.Length - 5]) {
|
if (time >= frames[frames.Length - 5]) { // Time is after last frame.
|
||||||
// Time is after last frame.
|
|
||||||
int i = frames.Length - 1;
|
int i = frames.Length - 1;
|
||||||
r = frames[i - 3];
|
r = frames[i - 3];
|
||||||
g = frames[i - 2];
|
g = frames[i - 2];
|
||||||
@ -389,19 +417,19 @@ namespace Spine {
|
|||||||
a = frames[i];
|
a = frames[i];
|
||||||
} else {
|
} else {
|
||||||
// Interpolate between the previous frame and the current frame.
|
// Interpolate between the previous frame and the current frame.
|
||||||
int frameIndex = Animation.binarySearch(frames, time, 5);
|
int frame = Animation.binarySearch(frames, time, 5);
|
||||||
float prevFrameR = frames[frameIndex - 4];
|
float frameTime = frames[frame];
|
||||||
float prevFrameG = frames[frameIndex - 3];
|
float percent = 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime);
|
||||||
float prevFrameB = frames[frameIndex - 2];
|
percent = GetCurvePercent(frame / 5 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
|
||||||
float prevFrameA = frames[frameIndex - 1];
|
|
||||||
float frameTime = frames[frameIndex];
|
|
||||||
float percent = 1 - (time - frameTime) / (frames[frameIndex + PREV_FRAME_TIME] - frameTime);
|
|
||||||
percent = GetCurvePercent(frameIndex / 5 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
|
|
||||||
|
|
||||||
r = prevFrameR + (frames[frameIndex + FRAME_R] - prevFrameR) * percent;
|
r = frames[frame - 4];
|
||||||
g = prevFrameG + (frames[frameIndex + FRAME_G] - prevFrameG) * percent;
|
g = frames[frame - 3];
|
||||||
b = prevFrameB + (frames[frameIndex + FRAME_B] - prevFrameB) * percent;
|
b = frames[frame - 2];
|
||||||
a = prevFrameA + (frames[frameIndex + FRAME_A] - prevFrameA) * percent;
|
a = frames[frame - 1];
|
||||||
|
r += (frames[frame + R] - r) * percent;
|
||||||
|
g += (frames[frame + G] - g) * percent;
|
||||||
|
b += (frames[frame + B] - b) * percent;
|
||||||
|
a += (frames[frame + A] - a) * percent;
|
||||||
}
|
}
|
||||||
Slot slot = skeleton.slots.Items[slotIndex];
|
Slot slot = skeleton.slots.Items[slotIndex];
|
||||||
if (alpha < 1) {
|
if (alpha < 1) {
|
||||||
@ -488,19 +516,19 @@ namespace Spine {
|
|||||||
return;
|
return;
|
||||||
if (time < frames[0]) return; // Time is before first frame.
|
if (time < frames[0]) return; // Time is before first frame.
|
||||||
|
|
||||||
int frameIndex;
|
int frame;
|
||||||
if (lastTime < frames[0])
|
if (lastTime < frames[0])
|
||||||
frameIndex = 0;
|
frame = 0;
|
||||||
else {
|
else {
|
||||||
frameIndex = Animation.binarySearch(frames, lastTime);
|
frame = Animation.binarySearch(frames, lastTime);
|
||||||
float frame = frames[frameIndex];
|
float frameTime = frames[frame];
|
||||||
while (frameIndex > 0) { // Fire multiple events with the same frame.
|
while (frame > 0) { // Fire multiple events with the same frame.
|
||||||
if (frames[frameIndex - 1] != frame) break;
|
if (frames[frame - 1] != frameTime) break;
|
||||||
frameIndex--;
|
frame--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (; frameIndex < frameCount && time >= frames[frameIndex]; frameIndex++)
|
for (; frame < frameCount && time >= frames[frame]; frame++)
|
||||||
firedEvents.Add(events[frameIndex]);
|
firedEvents.Add(events[frame]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -528,15 +556,15 @@ namespace Spine {
|
|||||||
float[] frames = this.frames;
|
float[] frames = this.frames;
|
||||||
if (time < frames[0]) return; // Time is before first frame.
|
if (time < frames[0]) return; // Time is before first frame.
|
||||||
|
|
||||||
int frameIndex;
|
int frame;
|
||||||
if (time >= frames[frames.Length - 1]) // Time is after last frame.
|
if (time >= frames[frames.Length - 1]) // Time is after last frame.
|
||||||
frameIndex = frames.Length - 1;
|
frame = frames.Length - 1;
|
||||||
else
|
else
|
||||||
frameIndex = Animation.binarySearch(frames, time) - 1;
|
frame = Animation.binarySearch(frames, time) - 1;
|
||||||
|
|
||||||
ExposedList<Slot> drawOrder = skeleton.drawOrder;
|
ExposedList<Slot> drawOrder = skeleton.drawOrder;
|
||||||
ExposedList<Slot> slots = skeleton.slots;
|
ExposedList<Slot> slots = skeleton.slots;
|
||||||
int[] drawOrderToSetupIndex = drawOrders[frameIndex];
|
int[] drawOrderToSetupIndex = drawOrders[frame];
|
||||||
if (drawOrderToSetupIndex == null) {
|
if (drawOrderToSetupIndex == null) {
|
||||||
drawOrder.Clear();
|
drawOrder.Clear();
|
||||||
for (int i = 0, n = slots.Count; i < n; i++)
|
for (int i = 0, n = slots.Count; i < n; i++)
|
||||||
@ -605,13 +633,13 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Interpolate between the previous frame and the current frame.
|
// Interpolate between the previous frame and the current frame.
|
||||||
int frameIndex = Animation.binarySearch(frames, time);
|
int frame = Animation.binarySearch(frames, time);
|
||||||
float frameTime = frames[frameIndex];
|
float frameTime = frames[frame];
|
||||||
float percent = 1 - (time - frameTime) / (frames[frameIndex - 1] - frameTime);
|
float percent = 1 - (time - frameTime) / (frames[frame - 1] - frameTime);
|
||||||
percent = GetCurvePercent(frameIndex - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
|
percent = GetCurvePercent(frame - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
|
||||||
|
|
||||||
float[] prevVertices = frameVertices[frameIndex - 1];
|
float[] prevVertices = frameVertices[frame - 1];
|
||||||
float[] nextVertices = frameVertices[frameIndex];
|
float[] nextVertices = frameVertices[frame];
|
||||||
|
|
||||||
if (alpha < 1) {
|
if (alpha < 1) {
|
||||||
for (int i = 0; i < vertexCount; i++) {
|
for (int i = 0; i < vertexCount; i++) {
|
||||||
@ -629,10 +657,10 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public class IkConstraintTimeline : CurveTimeline {
|
public class IkConstraintTimeline : CurveTimeline {
|
||||||
private const int PREV_FRAME_TIME = -3;
|
private const int PREV_TIME = -3;
|
||||||
private const int PREV_FRAME_MIX = -2;
|
private const int PREV_MIX = -2;
|
||||||
private const int PREV_FRAME_BEND_DIRECTION = -1;
|
private const int PREV_BEND_DIRECTION = -1;
|
||||||
private const int FRAME_MIX = 1;
|
private const int MIX = 1;
|
||||||
|
|
||||||
internal int ikConstraintIndex;
|
internal int ikConstraintIndex;
|
||||||
internal float[] frames;
|
internal float[] frames;
|
||||||
@ -644,8 +672,8 @@ namespace Spine {
|
|||||||
: base(frameCount) {
|
: base(frameCount) {
|
||||||
frames = new float[frameCount * 3];
|
frames = new float[frameCount * 3];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets the time, mix and bend direction of the specified keyframe. */
|
/// <summary>Sets the time, mix and bend direction of the specified keyframe.</summary>
|
||||||
public void SetFrame (int frameIndex, float time, float mix, int bendDirection) {
|
public void SetFrame (int frameIndex, float time, float mix, int bendDirection) {
|
||||||
frameIndex *= 3;
|
frameIndex *= 3;
|
||||||
frames[frameIndex] = time;
|
frames[frameIndex] = time;
|
||||||
@ -657,24 +685,86 @@ namespace Spine {
|
|||||||
float[] frames = this.frames;
|
float[] frames = this.frames;
|
||||||
if (time < frames[0]) return; // Time is before first frame.
|
if (time < frames[0]) return; // Time is before first frame.
|
||||||
|
|
||||||
IkConstraint ikConstraint = skeleton.ikConstraints.Items[ikConstraintIndex];
|
IkConstraint constraint = skeleton.ikConstraints.Items[ikConstraintIndex];
|
||||||
|
|
||||||
if (time >= frames[frames.Length - 3]) { // Time is after last frame.
|
if (time >= frames[frames.Length - 3]) { // Time is after last frame.
|
||||||
ikConstraint.mix += (frames[frames.Length - 2] - ikConstraint.mix) * alpha;
|
constraint.mix += (frames[frames.Length + PREV_MIX] - constraint.mix) * alpha;
|
||||||
ikConstraint.bendDirection = (int)frames[frames.Length - 1];
|
constraint.bendDirection = (int)frames[frames.Length + PREV_BEND_DIRECTION];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Interpolate between the previous frame and the current frame.
|
// Interpolate between the previous frame and the current frame.
|
||||||
int frameIndex = Animation.binarySearch(frames, time, 3);
|
int frame = Animation.binarySearch(frames, time, 3);
|
||||||
float prevFrameMix = frames[frameIndex + PREV_FRAME_MIX];
|
float frameTime = frames[frame];
|
||||||
float frameTime = frames[frameIndex];
|
float percent = 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime);
|
||||||
float percent = 1 - (time - frameTime) / (frames[frameIndex + PREV_FRAME_TIME] - frameTime);
|
percent = GetCurvePercent(frame / 3 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
|
||||||
percent = GetCurvePercent(frameIndex / 3 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
|
|
||||||
|
|
||||||
float mix = prevFrameMix + (frames[frameIndex + FRAME_MIX] - prevFrameMix) * percent;
|
float mix = frames[frame + PREV_MIX];
|
||||||
ikConstraint.mix += (mix - ikConstraint.mix) * alpha;
|
constraint.mix += (mix + (frames[frame + MIX] - mix) * percent - constraint.mix) * alpha;
|
||||||
ikConstraint.bendDirection = (int)frames[frameIndex + PREV_FRAME_BEND_DIRECTION];
|
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;
|
||||||
|
|
||||||
|
internal int transformConstraintIndex;
|
||||||
|
internal float[] frames;
|
||||||
|
|
||||||
|
public int TransformConstraintIndex { get { return transformConstraintIndex; } set { transformConstraintIndex = value; } }
|
||||||
|
public float[] Frames { get { return frames; } set { frames = value; } } // time, rotate mix, translate mix, scale mix, shear mix, ...
|
||||||
|
|
||||||
|
public TransformConstraintTimeline (int frameCount)
|
||||||
|
: base(frameCount) {
|
||||||
|
frames = new float[frameCount * 5];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetFrame (int frameIndex, float time, float rotateMix, float translateMix, float scaleMix, float shearMix) {
|
||||||
|
frameIndex *= 5;
|
||||||
|
frames[frameIndex] = time;
|
||||||
|
frames[frameIndex + 1] = rotateMix;
|
||||||
|
frames[frameIndex + 2] = translateMix;
|
||||||
|
frames[frameIndex + 3] = scaleMix;
|
||||||
|
frames[frameIndex + 4] = shearMix;
|
||||||
|
}
|
||||||
|
|
||||||
|
override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha) {
|
||||||
|
float[] frames = this.frames;
|
||||||
|
if (time < frames[0]) return; // Time is before first frame.
|
||||||
|
|
||||||
|
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;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interpolate between the previous frame and the current frame.
|
||||||
|
int frame = Animation.binarySearch(frames, time, 5);
|
||||||
|
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 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,7 +40,7 @@ namespace Spine {
|
|||||||
internal Skeleton skeleton;
|
internal Skeleton skeleton;
|
||||||
internal Bone parent;
|
internal Bone parent;
|
||||||
internal ExposedList<Bone> children = new ExposedList<Bone>();
|
internal ExposedList<Bone> children = new ExposedList<Bone>();
|
||||||
internal float x, y, rotation, scaleX, scaleY;
|
internal float x, y, rotation, scaleX, scaleY, shearX, shearY;
|
||||||
internal float appliedRotation, appliedScaleX, appliedScaleY;
|
internal float appliedRotation, appliedScaleX, appliedScaleY;
|
||||||
|
|
||||||
internal float a, b, worldX;
|
internal float a, b, worldX;
|
||||||
@ -62,6 +62,8 @@ namespace Spine {
|
|||||||
public float AppliedScaleY { get { return appliedScaleY; } set { appliedScaleY = value; } }
|
public float AppliedScaleY { get { return appliedScaleY; } set { appliedScaleY = value; } }
|
||||||
public float ScaleX { get { return scaleX; } set { scaleX = value; } }
|
public float ScaleX { get { return scaleX; } set { scaleX = value; } }
|
||||||
public float ScaleY { get { return scaleY; } set { scaleY = value; } }
|
public float ScaleY { get { return scaleY; } set { scaleY = value; } }
|
||||||
|
public float ShearX { get { return shearX; } set { shearX = value; } }
|
||||||
|
public float ShearY { get { return shearY; } set { shearY = value; } }
|
||||||
|
|
||||||
public float A { get { return a; } }
|
public float A { get { return a; } }
|
||||||
public float B { get { return b; } }
|
public float B { get { return b; } }
|
||||||
@ -88,22 +90,24 @@ namespace Spine {
|
|||||||
|
|
||||||
/// <summary>Same as {@link #updateWorldTransform()}. This method exists for Bone to implement {@link Updatable}.</summary>
|
/// <summary>Same as {@link #updateWorldTransform()}. This method exists for Bone to implement {@link Updatable}.</summary>
|
||||||
public void Update () {
|
public void Update () {
|
||||||
UpdateWorldTransform(x, y, rotation, scaleX, scaleY);
|
UpdateWorldTransform(x, y, rotation, scaleX, scaleY, shearX, shearY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Computes the world SRT using the parent bone and this bone's local SRT.</summary>
|
/// <summary>Computes the world SRT using the parent bone and this bone's local SRT.</summary>
|
||||||
public void UpdateWorldTransform () {
|
public void UpdateWorldTransform () {
|
||||||
UpdateWorldTransform(x, y, rotation, scaleX, scaleY);
|
UpdateWorldTransform(x, y, rotation, scaleX, scaleY, shearX, shearY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Computes the world SRT using the parent bone and the specified local SRT.</summary>
|
/// <summary>Computes the world SRT using the parent bone and the specified local SRT.</summary>
|
||||||
public void UpdateWorldTransform (float x, float y, float rotation, float scaleX, float scaleY) {
|
public void UpdateWorldTransform (float x, float y, float rotation, float scaleX, float scaleY, float shearX, float shearY) {
|
||||||
appliedRotation = rotation;
|
appliedRotation = rotation;
|
||||||
appliedScaleX = scaleX;
|
appliedScaleX = scaleX;
|
||||||
appliedScaleY = scaleY;
|
appliedScaleY = scaleY;
|
||||||
|
|
||||||
float cos = MathUtils.CosDeg(rotation), sin = MathUtils.SinDeg(rotation);
|
float rotationY = rotation + 90 + shearY;
|
||||||
float la = cos * scaleX, lb = -sin * scaleY, lc = sin * scaleX, ld = cos * scaleY;
|
float la = MathUtils.CosDeg(rotation + shearX) * scaleX, lb = MathUtils.CosDeg(rotationY) * scaleY;
|
||||||
|
float lc = MathUtils.SinDeg(rotation + shearX) * scaleX, ld = MathUtils.SinDeg(rotationY) * scaleY;
|
||||||
|
|
||||||
Bone parent = this.parent;
|
Bone parent = this.parent;
|
||||||
if (parent == null) { // Root bone.
|
if (parent == null) { // Root bone.
|
||||||
Skeleton skeleton = this.skeleton;
|
Skeleton skeleton = this.skeleton;
|
||||||
@ -146,8 +150,7 @@ namespace Spine {
|
|||||||
pc = 0;
|
pc = 0;
|
||||||
pd = 1;
|
pd = 1;
|
||||||
do {
|
do {
|
||||||
cos = MathUtils.CosDeg(parent.appliedRotation);
|
float cos = MathUtils.CosDeg(parent.appliedRotation), sin = MathUtils.SinDeg(parent.appliedRotation);
|
||||||
sin = MathUtils.SinDeg(parent.appliedRotation);
|
|
||||||
float temp = pa * cos + pb * sin;
|
float temp = pa * cos + pb * sin;
|
||||||
pb = pa * -sin + pb * cos;
|
pb = pa * -sin + pb * cos;
|
||||||
pa = temp;
|
pa = temp;
|
||||||
@ -168,9 +171,7 @@ namespace Spine {
|
|||||||
pc = 0;
|
pc = 0;
|
||||||
pd = 1;
|
pd = 1;
|
||||||
do {
|
do {
|
||||||
float r = parent.rotation;
|
float r = parent.appliedRotation, cos = MathUtils.CosDeg(r), sin = MathUtils.SinDeg(r);
|
||||||
cos = MathUtils.CosDeg(r);
|
|
||||||
sin = MathUtils.SinDeg(r);
|
|
||||||
float psx = parent.appliedScaleX, psy = parent.appliedScaleY;
|
float psx = parent.appliedScaleX, psy = parent.appliedScaleY;
|
||||||
float za = cos * psx, zb = -sin * psy, zc = sin * psx, zd = cos * psy;
|
float za = cos * psx, zb = -sin * psy, zc = sin * psx, zd = cos * psy;
|
||||||
float temp = pa * za + pb * zc;
|
float temp = pa * za + pb * zc;
|
||||||
@ -221,6 +222,8 @@ namespace Spine {
|
|||||||
rotation = data.rotation;
|
rotation = data.rotation;
|
||||||
scaleX = data.scaleX;
|
scaleX = data.scaleX;
|
||||||
scaleY = data.scaleY;
|
scaleY = data.scaleY;
|
||||||
|
shearX = data.shearX;
|
||||||
|
shearY = data.shearY;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WorldToLocal (float worldX, float worldY, out float localX, out float localY) {
|
public void WorldToLocal (float worldX, float worldY, out float localX, out float localY) {
|
||||||
|
|||||||
@ -35,7 +35,7 @@ namespace Spine {
|
|||||||
public class BoneData {
|
public class BoneData {
|
||||||
internal BoneData parent;
|
internal BoneData parent;
|
||||||
internal String name;
|
internal String name;
|
||||||
internal float length, x, y, rotation, scaleX = 1, scaleY = 1;
|
internal float length, x, y, rotation, scaleX = 1, scaleY = 1, shearX, shearY;
|
||||||
internal bool inheritScale = true, inheritRotation = true;
|
internal bool inheritScale = true, inheritRotation = true;
|
||||||
|
|
||||||
/// <summary>May be null.</summary>
|
/// <summary>May be null.</summary>
|
||||||
@ -47,6 +47,8 @@ namespace Spine {
|
|||||||
public float Rotation { get { return rotation; } set { rotation = value; } }
|
public float Rotation { get { return rotation; } set { rotation = value; } }
|
||||||
public float ScaleX { get { return scaleX; } set { scaleX = value; } }
|
public float ScaleX { get { return scaleX; } set { scaleX = value; } }
|
||||||
public float ScaleY { get { return scaleY; } set { scaleY = value; } }
|
public float ScaleY { get { return scaleY; } set { scaleY = value; } }
|
||||||
|
public 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 InheritScale { get { return inheritScale; } set { inheritScale = value; } }
|
||||||
public bool InheritRotation { get { return inheritRotation; } set { inheritRotation = value; } }
|
public bool InheritRotation { get { return inheritRotation; } set { inheritRotation = value; } }
|
||||||
|
|
||||||
|
|||||||
@ -83,14 +83,17 @@ namespace Spine {
|
|||||||
/// <summary>Adjusts the bone rotation so the tip is as close to the target position as possible. The target is specified
|
/// <summary>Adjusts the bone rotation so the tip is as close to the target position as possible. The target is specified
|
||||||
/// in the world coordinate system.</summary>
|
/// in the world coordinate system.</summary>
|
||||||
static public void Apply (Bone bone, float targetX, float targetY, float alpha) {
|
static public void Apply (Bone bone, float targetX, float targetY, float alpha) {
|
||||||
float parentRotation = bone.parent == null ? 0 : bone.parent.WorldRotationX;
|
Bone pp = bone.parent;
|
||||||
float rotation = bone.rotation;
|
float id = 1 / (pp.a * pp.d - pp.b * pp.c);
|
||||||
float rotationIK = MathUtils.Atan2(targetY - bone.worldY, targetX - bone.worldX) * MathUtils.radDeg - parentRotation;
|
float x = targetX - pp.worldX, y = targetY - pp.worldY;
|
||||||
if ((bone.worldSignX != bone.worldSignY) != (bone.skeleton.flipX != (bone.skeleton.flipY != Bone.yDown)))
|
float tx = (x * pp.d - y * pp.b) * id - bone.x, ty = (y * pp.a - x * pp.c) * id - bone.y;
|
||||||
rotationIK = 360 - rotationIK;
|
float rotationIK = MathUtils.Atan2(ty, tx) * MathUtils.radDeg - bone.shearX;
|
||||||
if (rotationIK > 180) rotationIK -= 360;
|
if (bone.scaleX < 0) rotationIK += 180;
|
||||||
|
if (rotationIK > 180)
|
||||||
|
rotationIK -= 360;
|
||||||
else if (rotationIK < -180) rotationIK += 360;
|
else if (rotationIK < -180) rotationIK += 360;
|
||||||
bone.UpdateWorldTransform(bone.x, bone.y, rotation + (rotationIK - rotation) * alpha, bone.appliedScaleX, bone.appliedScaleY);
|
bone.UpdateWorldTransform(bone.x, bone.y, bone.rotation + (rotationIK - bone.rotation) * alpha, bone.appliedScaleX,
|
||||||
|
bone.appliedScaleY, bone.shearX, bone.shearY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Adjusts the parent and child bone rotations so the tip of the child is as close to the target position as
|
/// <summary>Adjusts the parent and child bone rotations so the tip of the child is as close to the target position as
|
||||||
@ -125,22 +128,12 @@ namespace Spine {
|
|||||||
} else
|
} else
|
||||||
os2 = 0;
|
os2 = 0;
|
||||||
Bone pp = parent.parent;
|
Bone pp = parent.parent;
|
||||||
float tx, ty, dx, dy;
|
float ppa = pp.a, ppb = pp.b, ppc = pp.c, ppd = pp.d, id = 1 / (ppa * ppd - ppb * ppc);
|
||||||
if (pp == null) {
|
float x = targetX - pp.worldX, y = targetY - pp.worldY;
|
||||||
tx = targetX - px;
|
float tx = (x * ppd - y * ppb) * id - px, ty = (y * ppa - x * ppc) * id - py;
|
||||||
ty = targetY - py;
|
x = child.worldX - pp.worldX;
|
||||||
dx = child.worldX - px;
|
y = child.worldY - pp.worldY;
|
||||||
dy = child.worldY - py;
|
float dx = (x * ppd - y * ppb) * id - px, dy = (y * ppa - x * ppc) * id - py;
|
||||||
} else {
|
|
||||||
float a = pp.a, b = pp.b, c = pp.c, d = pp.d, invDet = 1 / (a * d - b * c);
|
|
||||||
float wx = pp.worldX, wy = pp.worldY, x = targetX - wx, y = targetY - wy;
|
|
||||||
tx = (x * d - y * b) * invDet - px;
|
|
||||||
ty = (y * a - x * c) * invDet - py;
|
|
||||||
x = child.worldX - wx;
|
|
||||||
y = child.worldY - wy;
|
|
||||||
dx = (x * d - y * b) * invDet - px;
|
|
||||||
dy = (y * a - x * c) * invDet - py;
|
|
||||||
}
|
|
||||||
float l1 = (float)Math.Sqrt(dx * dx + dy * dy), l2 = child.data.length * csx, a1, a2;
|
float l1 = (float)Math.Sqrt(dx * dx + dy * dy), l2 = child.data.length * csx, a1, a2;
|
||||||
if (u) {
|
if (u) {
|
||||||
l2 *= psx;
|
l2 *= psx;
|
||||||
@ -162,40 +155,41 @@ namespace Spine {
|
|||||||
float r0 = q / c2, r1 = c0 / q;
|
float r0 = q / c2, r1 = c0 / q;
|
||||||
float r = Math.Abs(r0) < Math.Abs(r1) ? r0 : r1;
|
float r = Math.Abs(r0) < Math.Abs(r1) ? r0 : r1;
|
||||||
if (r * r <= dd) {
|
if (r * r <= dd) {
|
||||||
float y1 = (float)Math.Sqrt(dd - r * r) * bendDir;
|
y = (float)Math.Sqrt(dd - r * r) * bendDir;
|
||||||
a1 = ta - MathUtils.Atan2(y1, r);
|
a1 = ta - MathUtils.Atan2(y, r);
|
||||||
a2 = MathUtils.Atan2(y1 / psy, (r - l1) / psx);
|
a2 = MathUtils.Atan2(y / psy, (r - l1) / psx);
|
||||||
goto outer;
|
goto outer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
float minAngle = 0, minDist = float.MaxValue, minX = 0, minY = 0;
|
float minAngle = 0, minDist = float.MaxValue, minX = 0, minY = 0;
|
||||||
float maxAngle = 0, maxDist = 0, maxX = 0, maxY = 0;
|
float maxAngle = 0, maxDist = 0, maxX = 0, maxY = 0;
|
||||||
float x = l1 + a, dist = x * x;
|
x = l1 + a;
|
||||||
if (dist > maxDist) {
|
d = x * x;
|
||||||
|
if (d > maxDist) {
|
||||||
maxAngle = 0;
|
maxAngle = 0;
|
||||||
maxDist = dist;
|
maxDist = d;
|
||||||
maxX = x;
|
maxX = x;
|
||||||
}
|
}
|
||||||
x = l1 - a;
|
x = l1 - a;
|
||||||
dist = x * x;
|
d = x * x;
|
||||||
if (dist < minDist) {
|
if (d < minDist) {
|
||||||
minAngle = MathUtils.PI;
|
minAngle = MathUtils.PI;
|
||||||
minDist = dist;
|
minDist = d;
|
||||||
minX = x;
|
minX = x;
|
||||||
}
|
}
|
||||||
float angle = (float)Math.Acos(-a * l1 / (aa - bb));
|
float angle = (float)Math.Acos(-a * l1 / (aa - bb));
|
||||||
x = a * MathUtils.Cos(angle) + l1;
|
x = a * MathUtils.Cos(angle) + l1;
|
||||||
float y = b * MathUtils.Sin(angle);
|
y = b * MathUtils.Sin(angle);
|
||||||
dist = x * x + y * y;
|
d = x * x + y * y;
|
||||||
if (dist < minDist) {
|
if (d < minDist) {
|
||||||
minAngle = angle;
|
minAngle = angle;
|
||||||
minDist = dist;
|
minDist = d;
|
||||||
minX = x;
|
minX = x;
|
||||||
minY = y;
|
minY = y;
|
||||||
}
|
}
|
||||||
if (dist > maxDist) {
|
if (d > maxDist) {
|
||||||
maxAngle = angle;
|
maxAngle = angle;
|
||||||
maxDist = dist;
|
maxDist = d;
|
||||||
maxX = x;
|
maxX = x;
|
||||||
maxY = y;
|
maxY = y;
|
||||||
}
|
}
|
||||||
@ -210,15 +204,15 @@ namespace Spine {
|
|||||||
outer:
|
outer:
|
||||||
float os = MathUtils.Atan2(cy, cx) * s2;
|
float os = MathUtils.Atan2(cy, cx) * s2;
|
||||||
a1 = (a1 - os) * MathUtils.radDeg + os1;
|
a1 = (a1 - os) * MathUtils.radDeg + os1;
|
||||||
a2 = (a2 + os) * MathUtils.radDeg * s2 + os2;
|
a2 = ((a2 + os) * MathUtils.radDeg - child.shearX) * s2 + os2;
|
||||||
if (a1 > 180) a1 -= 360;
|
if (a1 > 180) a1 -= 360;
|
||||||
else if (a1 < -180) a1 += 360;
|
else if (a1 < -180) a1 += 360;
|
||||||
if (a2 > 180) a2 -= 360;
|
if (a2 > 180) a2 -= 360;
|
||||||
else if (a2 < -180) a2 += 360;
|
else if (a2 < -180) a2 += 360;
|
||||||
float rotation = parent.rotation;
|
float rotation = parent.rotation;
|
||||||
parent.UpdateWorldTransform(px, py, rotation + (a1 - rotation) * alpha, parent.appliedScaleX, parent.appliedScaleY);
|
parent.UpdateWorldTransform(px, py, rotation + (a1 - rotation) * alpha, parent.appliedScaleX, parent.appliedScaleY, 0, 0);
|
||||||
rotation = child.rotation;
|
rotation = child.rotation;
|
||||||
child.UpdateWorldTransform(cx, cy, rotation + (a2 - rotation) * alpha, child.appliedScaleX, child.appliedScaleY);
|
child.UpdateWorldTransform(cx, cy, rotation + (a2 - rotation) * alpha, child.appliedScaleX, child.appliedScaleY, child.shearX, child.shearY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,6 +34,7 @@ using System;
|
|||||||
namespace Spine {
|
namespace Spine {
|
||||||
public static class MathUtils {
|
public static class MathUtils {
|
||||||
public const float PI = 3.1415927f;
|
public const float PI = 3.1415927f;
|
||||||
|
public const float PI2 = PI * 2;
|
||||||
public const float radDeg = 180f / PI;
|
public const float radDeg = 180f / PI;
|
||||||
public const float degRad = PI / 180;
|
public const float degRad = PI / 180;
|
||||||
|
|
||||||
|
|||||||
@ -127,8 +127,7 @@ namespace Spine {
|
|||||||
for (int i = 0; i < transformConstraintsCount; i++) {
|
for (int i = 0; i < transformConstraintsCount; i++) {
|
||||||
TransformConstraint transformConstraint = transformConstraints.Items[i];
|
TransformConstraint transformConstraint = transformConstraints.Items[i];
|
||||||
for (int ii = updateCache.Count - 1; i >= 0; ii--) {
|
for (int ii = updateCache.Count - 1; i >= 0; ii--) {
|
||||||
IUpdatable updateable = updateCache.Items[ii];
|
if (updateCache.Items[ii] == transformConstraint.bone) {
|
||||||
if (updateable == transformConstraint.bone || updateable == transformConstraint.target) {
|
|
||||||
updateCache.Insert(ii + 1, transformConstraint);
|
updateCache.Insert(ii + 1, transformConstraint);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -165,9 +164,11 @@ namespace Spine {
|
|||||||
ExposedList<TransformConstraint> transformConstraints = this.transformConstraints;
|
ExposedList<TransformConstraint> transformConstraints = this.transformConstraints;
|
||||||
for (int i = 0, n = transformConstraints.Count; i < n; i++) {
|
for (int i = 0, n = transformConstraints.Count; i < n; i++) {
|
||||||
TransformConstraint constraint = transformConstraints.Items[i];
|
TransformConstraint constraint = transformConstraints.Items[i];
|
||||||
constraint.translateMix = constraint.data.translateMix;
|
TransformConstraintData data = constraint.data;
|
||||||
constraint.x = constraint.data.x;
|
constraint.rotateMix = data.rotateMix;
|
||||||
constraint.y = constraint.data.y;
|
constraint.translateMix = data.translateMix;
|
||||||
|
constraint.scaleMix = data.scaleMix;
|
||||||
|
constraint.shearMix = data.shearMix;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -29,6 +29,10 @@
|
|||||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#if (UNITY_5 || UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7 || UNITY_WSA || UNITY_WP8 || UNITY_WP8_1)
|
||||||
|
#define IS_UNITY
|
||||||
|
#endif
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -40,11 +44,12 @@ using Windows.Storage;
|
|||||||
|
|
||||||
namespace Spine {
|
namespace Spine {
|
||||||
public class SkeletonBinary {
|
public class SkeletonBinary {
|
||||||
public const int TIMELINE_SCALE = 0;
|
public const int TIMELINE_ROTATE = 0;
|
||||||
public const int TIMELINE_ROTATE = 1;
|
public const int TIMELINE_TRANSLATE = 1;
|
||||||
public const int TIMELINE_TRANSLATE = 2;
|
public const int TIMELINE_SCALE = 2;
|
||||||
public const int TIMELINE_ATTACHMENT = 3;
|
public const int TIMELINE_SHEAR = 3;
|
||||||
public const int TIMELINE_COLOR = 4;
|
public const int TIMELINE_ATTACHMENT = 4;
|
||||||
|
public const int TIMELINE_COLOR = 5;
|
||||||
|
|
||||||
public const int CURVE_LINEAR = 0;
|
public const int CURVE_LINEAR = 0;
|
||||||
public const int CURVE_STEPPED = 1;
|
public const int CURVE_STEPPED = 1;
|
||||||
@ -65,10 +70,8 @@ namespace Spine {
|
|||||||
this.attachmentLoader = attachmentLoader;
|
this.attachmentLoader = attachmentLoader;
|
||||||
Scale = 1;
|
Scale = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !(UNITY_5 || UNITY_4 || UNITY_WSA || UNITY_WP8 || UNITY_WP8_1)
|
#if !ISUNITY && WINDOWS_STOREAPP
|
||||||
#if WINDOWS_STOREAPP
|
|
||||||
|
|
||||||
private async Task<SkeletonData> ReadFile(string path) {
|
private async Task<SkeletonData> ReadFile(string path) {
|
||||||
var folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
|
var folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
|
||||||
using (var input = new BufferedStream(await folder.GetFileAsync(path).AsTask().ConfigureAwait(false))) {
|
using (var input = new BufferedStream(await folder.GetFileAsync(path).AsTask().ConfigureAwait(false))) {
|
||||||
@ -95,7 +98,6 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endif // WINDOWS_STOREAPP
|
#endif // WINDOWS_STOREAPP
|
||||||
#endif // !(UNITY)
|
|
||||||
|
|
||||||
public SkeletonData ReadSkeletonData (Stream input) {
|
public SkeletonData ReadSkeletonData (Stream input) {
|
||||||
if (input == null) throw new ArgumentNullException("input");
|
if (input == null) throw new ArgumentNullException("input");
|
||||||
@ -121,14 +123,16 @@ namespace Spine {
|
|||||||
String name = ReadString(input);
|
String name = ReadString(input);
|
||||||
BoneData parent = i == 0 ? null : skeletonData.bones.Items[ReadVarint(input, true)];
|
BoneData parent = i == 0 ? null : skeletonData.bones.Items[ReadVarint(input, true)];
|
||||||
BoneData boneData = new BoneData(name, parent);
|
BoneData boneData = new BoneData(name, parent);
|
||||||
|
boneData.rotation = ReadFloat(input);
|
||||||
boneData.x = ReadFloat(input) * scale;
|
boneData.x = ReadFloat(input) * scale;
|
||||||
boneData.y = ReadFloat(input) * scale;
|
boneData.y = ReadFloat(input) * scale;
|
||||||
boneData.scaleX = ReadFloat(input);
|
boneData.scaleX = ReadFloat(input);
|
||||||
boneData.scaleY = ReadFloat(input);
|
boneData.scaleY = ReadFloat(input);
|
||||||
boneData.rotation = ReadFloat(input);
|
boneData.shearX = ReadFloat(input);
|
||||||
|
boneData.shearY = ReadFloat(input);
|
||||||
boneData.length = ReadFloat(input) * scale;
|
boneData.length = ReadFloat(input) * scale;
|
||||||
boneData.inheritScale = ReadBoolean(input);
|
|
||||||
boneData.inheritRotation = ReadBoolean(input);
|
boneData.inheritRotation = ReadBoolean(input);
|
||||||
|
boneData.inheritScale = ReadBoolean(input);
|
||||||
if (nonessential) ReadInt(input); // Skip bone color.
|
if (nonessential) ReadInt(input); // Skip bone color.
|
||||||
skeletonData.bones.Add(boneData);
|
skeletonData.bones.Add(boneData);
|
||||||
}
|
}
|
||||||
@ -149,9 +153,16 @@ namespace Spine {
|
|||||||
TransformConstraintData transformConstraintData = new TransformConstraintData(ReadString(input));
|
TransformConstraintData transformConstraintData = new TransformConstraintData(ReadString(input));
|
||||||
transformConstraintData.bone = skeletonData.bones.Items[ReadVarint(input, true)];
|
transformConstraintData.bone = skeletonData.bones.Items[ReadVarint(input, true)];
|
||||||
transformConstraintData.target = 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.translateMix = ReadFloat(input);
|
||||||
transformConstraintData.x = ReadFloat(input) * scale;
|
transformConstraintData.scaleMix = ReadFloat(input);
|
||||||
transformConstraintData.y = ReadFloat(input) * scale;
|
transformConstraintData.shearMix = ReadFloat(input);
|
||||||
skeletonData.transformConstraints.Add(transformConstraintData);
|
skeletonData.transformConstraints.Add(transformConstraintData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,11 +258,11 @@ namespace Spine {
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case AttachmentType.region: {
|
case AttachmentType.region: {
|
||||||
String path = ReadString(input);
|
String path = ReadString(input);
|
||||||
|
float rotation = ReadFloat(input);
|
||||||
float x = ReadFloat(input);
|
float x = ReadFloat(input);
|
||||||
float y = ReadFloat(input);
|
float y = ReadFloat(input);
|
||||||
float scaleX = ReadFloat(input);
|
float scaleX = ReadFloat(input);
|
||||||
float scaleY = ReadFloat(input);
|
float scaleY = ReadFloat(input);
|
||||||
float rotation = ReadFloat(input);
|
|
||||||
float width = ReadFloat(input);
|
float width = ReadFloat(input);
|
||||||
float height = ReadFloat(input);
|
float height = ReadFloat(input);
|
||||||
int color = ReadInt(input);
|
int color = ReadInt(input);
|
||||||
@ -508,11 +519,14 @@ namespace Spine {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TIMELINE_TRANSLATE:
|
case TIMELINE_TRANSLATE:
|
||||||
case TIMELINE_SCALE: {
|
case TIMELINE_SCALE:
|
||||||
|
case TIMELINE_SHEAR: {
|
||||||
TranslateTimeline timeline;
|
TranslateTimeline timeline;
|
||||||
float timelineScale = 1;
|
float timelineScale = 1;
|
||||||
if (timelineType == TIMELINE_SCALE)
|
if (timelineType == TIMELINE_SCALE)
|
||||||
timeline = new ScaleTimeline(frameCount);
|
timeline = new ScaleTimeline(frameCount);
|
||||||
|
else if (timelineType == TIMELINE_SHEAR)
|
||||||
|
timeline = new ShearTimeline(frameCount);
|
||||||
else {
|
else {
|
||||||
timeline = new TranslateTimeline(frameCount);
|
timeline = new TranslateTimeline(frameCount);
|
||||||
timelineScale = scale;
|
timelineScale = scale;
|
||||||
@ -533,10 +547,10 @@ namespace Spine {
|
|||||||
|
|
||||||
// IK timelines.
|
// IK timelines.
|
||||||
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
|
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
|
||||||
IkConstraintData ikConstraint = skeletonData.ikConstraints.Items[ReadVarint(input, true)];
|
IkConstraintData constraint = skeletonData.ikConstraints.Items[ReadVarint(input, true)];
|
||||||
int frameCount = ReadVarint(input, true);
|
int frameCount = ReadVarint(input, true);
|
||||||
IkConstraintTimeline timeline = new IkConstraintTimeline(frameCount);
|
IkConstraintTimeline timeline = new IkConstraintTimeline(frameCount);
|
||||||
timeline.ikConstraintIndex = skeletonData.ikConstraints.IndexOf(ikConstraint);
|
timeline.ikConstraintIndex = skeletonData.ikConstraints.IndexOf(constraint);
|
||||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
||||||
timeline.SetFrame(frameIndex, ReadFloat(input), ReadFloat(input), ReadSByte(input));
|
timeline.SetFrame(frameIndex, ReadFloat(input), ReadFloat(input), ReadSByte(input));
|
||||||
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
|
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
|
||||||
@ -545,6 +559,20 @@ namespace Spine {
|
|||||||
duration = Math.Max(duration, timeline.frames[frameCount * 3 - 3]);
|
duration = Math.Max(duration, timeline.frames[frameCount * 3 - 3]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Transform constraint timelines.
|
||||||
|
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
|
||||||
|
TransformConstraintData constraint = skeletonData.transformConstraints.Items[ReadVarint(input, true)];
|
||||||
|
int frameCount = ReadVarint(input, true);
|
||||||
|
TransformConstraintTimeline timeline = new TransformConstraintTimeline(frameCount);
|
||||||
|
timeline.transformConstraintIndex = skeletonData.transformConstraints.IndexOf(constraint);
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
|
||||||
// FFD timelines.
|
// FFD timelines.
|
||||||
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
|
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
|
||||||
Skin skin = skeletonData.skins.Items[ReadVarint(input, true)];
|
Skin skin = skeletonData.skins.Items[ReadVarint(input, true)];
|
||||||
|
|||||||
@ -59,9 +59,7 @@ namespace Spine {
|
|||||||
Scale = 1;
|
Scale = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !(IS_UNITY)
|
#if !(IS_UNITY) && WINDOWS_STOREAPP
|
||||||
#if WINDOWS_STOREAPP
|
|
||||||
|
|
||||||
private async Task<SkeletonData> ReadFile(string path) {
|
private async Task<SkeletonData> ReadFile(string path) {
|
||||||
var folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
|
var folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
|
||||||
var file = await folder.GetFileAsync(path).AsTask().ConfigureAwait(false);
|
var file = await folder.GetFileAsync(path).AsTask().ConfigureAwait(false);
|
||||||
@ -88,9 +86,7 @@ namespace Spine {
|
|||||||
return skeletonData;
|
return skeletonData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // WINDOWS_STOREAPP
|
#endif // WINDOWS_STOREAPP
|
||||||
#endif // !UNITY
|
|
||||||
|
|
||||||
public SkeletonData ReadSkeletonData (TextReader reader) {
|
public SkeletonData ReadSkeletonData (TextReader reader) {
|
||||||
if (reader == null) throw new ArgumentNullException("reader cannot be null.");
|
if (reader == null) throw new ArgumentNullException("reader cannot be null.");
|
||||||
@ -125,6 +121,8 @@ namespace Spine {
|
|||||||
boneData.rotation = GetFloat(boneMap, "rotation", 0);
|
boneData.rotation = GetFloat(boneMap, "rotation", 0);
|
||||||
boneData.scaleX = GetFloat(boneMap, "scaleX", 1);
|
boneData.scaleX = GetFloat(boneMap, "scaleX", 1);
|
||||||
boneData.scaleY = GetFloat(boneMap, "scaleY", 1);
|
boneData.scaleY = GetFloat(boneMap, "scaleY", 1);
|
||||||
|
boneData.shearX = GetFloat(boneMap, "shearX", 1);
|
||||||
|
boneData.shearY = GetFloat(boneMap, "shearY", 1);
|
||||||
boneData.inheritScale = GetBoolean(boneMap, "inheritScale", true);
|
boneData.inheritScale = GetBoolean(boneMap, "inheritScale", true);
|
||||||
boneData.inheritRotation = GetBoolean(boneMap, "inheritRotation", true);
|
boneData.inheritRotation = GetBoolean(boneMap, "inheritRotation", true);
|
||||||
skeletonData.bones.Add(boneData);
|
skeletonData.bones.Add(boneData);
|
||||||
@ -165,9 +163,17 @@ namespace Spine {
|
|||||||
transformConstraintData.target = skeletonData.FindBone(targetName);
|
transformConstraintData.target = skeletonData.FindBone(targetName);
|
||||||
if (transformConstraintData.target == null) throw new Exception("Target bone not found: " + targetName);
|
if (transformConstraintData.target == null) throw new Exception("Target bone not found: " + targetName);
|
||||||
|
|
||||||
|
transformConstraintData.offsetRotation = GetFloat(transformMap, "rotation", 0);
|
||||||
|
transformConstraintData.offsetX = GetFloat(transformMap, "x", 0) * scale;
|
||||||
|
transformConstraintData.offsetY = GetFloat(transformMap, "y", 0) * scale;
|
||||||
|
transformConstraintData.offsetScaleX = GetFloat(transformMap, "scaleX", 0);
|
||||||
|
transformConstraintData.offsetScaleY = GetFloat(transformMap, "scaleY", 0);
|
||||||
|
transformConstraintData.offsetShearY = GetFloat(transformMap, "shearY", 0);
|
||||||
|
|
||||||
|
transformConstraintData.rotateMix = GetFloat(transformMap, "rotateMix", 1);
|
||||||
transformConstraintData.translateMix = GetFloat(transformMap, "translateMix", 1);
|
transformConstraintData.translateMix = GetFloat(transformMap, "translateMix", 1);
|
||||||
transformConstraintData.x = GetFloat(transformMap, "x", 0) * scale;
|
transformConstraintData.scaleMix = GetFloat(transformMap, "scaleMix", 1);
|
||||||
transformConstraintData.y = GetFloat(transformMap, "y", 0) * scale;
|
transformConstraintData.shearMix = GetFloat(transformMap, "shearMix", 1);
|
||||||
|
|
||||||
skeletonData.transformConstraints.Add(transformConstraintData);
|
skeletonData.transformConstraints.Add(transformConstraintData);
|
||||||
}
|
}
|
||||||
@ -520,11 +526,13 @@ namespace Spine {
|
|||||||
timelines.Add(timeline);
|
timelines.Add(timeline);
|
||||||
duration = Math.Max(duration, timeline.frames[timeline.FrameCount * 2 - 2]);
|
duration = Math.Max(duration, timeline.frames[timeline.FrameCount * 2 - 2]);
|
||||||
|
|
||||||
} else if (timelineName == "translate" || timelineName == "scale") {
|
} else if (timelineName == "translate" || timelineName == "scale" || timelineName == "shear") {
|
||||||
TranslateTimeline timeline;
|
TranslateTimeline timeline;
|
||||||
float timelineScale = 1;
|
float timelineScale = 1;
|
||||||
if (timelineName == "scale")
|
if (timelineName == "scale")
|
||||||
timeline = new ScaleTimeline(values.Count);
|
timeline = new ScaleTimeline(values.Count);
|
||||||
|
else if (timelineName == "shear")
|
||||||
|
timeline = new ShearTimeline(values.Count);
|
||||||
else {
|
else {
|
||||||
timeline = new TranslateTimeline(values.Count);
|
timeline = new TranslateTimeline(values.Count);
|
||||||
timelineScale = scale;
|
timelineScale = scale;
|
||||||
@ -534,8 +542,8 @@ namespace Spine {
|
|||||||
int frameIndex = 0;
|
int frameIndex = 0;
|
||||||
foreach (Dictionary<String, Object> valueMap in values) {
|
foreach (Dictionary<String, Object> valueMap in values) {
|
||||||
float time = (float)valueMap["time"];
|
float time = (float)valueMap["time"];
|
||||||
float x = valueMap.ContainsKey("x") ? (float)valueMap["x"] : 0;
|
float x = GetFloat(valueMap, "x", 0);
|
||||||
float y = valueMap.ContainsKey("y") ? (float)valueMap["y"] : 0;
|
float y = GetFloat(valueMap, "y", 0);
|
||||||
timeline.SetFrame(frameIndex, time, (float)x * timelineScale, (float)y * timelineScale);
|
timeline.SetFrame(frameIndex, time, (float)x * timelineScale, (float)y * timelineScale);
|
||||||
ReadCurve(timeline, frameIndex, valueMap);
|
ReadCurve(timeline, frameIndex, valueMap);
|
||||||
frameIndex++;
|
frameIndex++;
|
||||||
@ -549,17 +557,18 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IK timelines.
|
||||||
if (map.ContainsKey("ik")) {
|
if (map.ContainsKey("ik")) {
|
||||||
foreach (KeyValuePair<String, Object> ikMap in (Dictionary<String, Object>)map["ik"]) {
|
foreach (KeyValuePair<String, Object> constraintMap in (Dictionary<String, Object>)map["ik"]) {
|
||||||
IkConstraintData ikConstraint = skeletonData.FindIkConstraint(ikMap.Key);
|
IkConstraintData constraint = skeletonData.FindIkConstraint(constraintMap.Key);
|
||||||
var values = (List<Object>)ikMap.Value;
|
var values = (List<Object>)constraintMap.Value;
|
||||||
var timeline = new IkConstraintTimeline(values.Count);
|
var timeline = new IkConstraintTimeline(values.Count);
|
||||||
timeline.ikConstraintIndex = skeletonData.ikConstraints.IndexOf(ikConstraint);
|
timeline.ikConstraintIndex = skeletonData.ikConstraints.IndexOf(constraint);
|
||||||
int frameIndex = 0;
|
int frameIndex = 0;
|
||||||
foreach (Dictionary<String, Object> valueMap in values) {
|
foreach (Dictionary<String, Object> valueMap in values) {
|
||||||
float time = (float)valueMap["time"];
|
float time = (float)valueMap["time"];
|
||||||
float mix = valueMap.ContainsKey("mix") ? (float)valueMap["mix"] : 1;
|
float mix = GetFloat(valueMap, "mix", 1);
|
||||||
bool bendPositive = valueMap.ContainsKey("bendPositive") ? (bool)valueMap["bendPositive"] : true;
|
bool bendPositive = GetBoolean(valueMap, "bendPositive", true);
|
||||||
timeline.SetFrame(frameIndex, time, mix, bendPositive ? 1 : -1);
|
timeline.SetFrame(frameIndex, time, mix, bendPositive ? 1 : -1);
|
||||||
ReadCurve(timeline, frameIndex, valueMap);
|
ReadCurve(timeline, frameIndex, valueMap);
|
||||||
frameIndex++;
|
frameIndex++;
|
||||||
@ -569,6 +578,30 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Transform constraint timelines.
|
||||||
|
if (map.ContainsKey("transform")) {
|
||||||
|
foreach (KeyValuePair<String, Object> constraintMap in (Dictionary<String, Object>)map["transform"]) {
|
||||||
|
TransformConstraintData constraint = skeletonData.FindTransformConstraint(constraintMap.Key);
|
||||||
|
var values = (List<Object>)constraintMap.Value;
|
||||||
|
var timeline = new TransformConstraintTimeline(values.Count);
|
||||||
|
timeline.transformConstraintIndex = skeletonData.transformConstraints.IndexOf(constraint);
|
||||||
|
int frameIndex = 0;
|
||||||
|
foreach (Dictionary<String, Object> valueMap in values) {
|
||||||
|
float time = (float)valueMap["time"];
|
||||||
|
float rotateMix = GetFloat(valueMap, "rotateMix", 1);
|
||||||
|
float translateMix = GetFloat(valueMap, "translateMix", 1);
|
||||||
|
float scaleMix = GetFloat(valueMap, "scaleMix", 1);
|
||||||
|
float shearMix = GetFloat(valueMap, "shearMix", 1);
|
||||||
|
timeline.SetFrame(frameIndex, time, rotateMix, translateMix, scaleMix, shearMix);
|
||||||
|
ReadCurve(timeline, frameIndex, valueMap);
|
||||||
|
frameIndex++;
|
||||||
|
}
|
||||||
|
timelines.Add(timeline);
|
||||||
|
duration = Math.Max(duration, timeline.frames[timeline.FrameCount * 5 - 5]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FFD timelines.
|
||||||
if (map.ContainsKey("ffd")) {
|
if (map.ContainsKey("ffd")) {
|
||||||
foreach (KeyValuePair<String, Object> ffdMap in (Dictionary<String, Object>)map["ffd"]) {
|
foreach (KeyValuePair<String, Object> ffdMap in (Dictionary<String, Object>)map["ffd"]) {
|
||||||
Skin skin = skeletonData.FindSkin(ffdMap.Key);
|
Skin skin = skeletonData.FindSkin(ffdMap.Key);
|
||||||
|
|||||||
@ -36,38 +36,94 @@ namespace Spine {
|
|||||||
public class TransformConstraint : IUpdatable {
|
public class TransformConstraint : IUpdatable {
|
||||||
internal TransformConstraintData data;
|
internal TransformConstraintData data;
|
||||||
internal Bone bone, target;
|
internal Bone bone, target;
|
||||||
internal float translateMix;
|
internal float rotateMix, translateMix, scaleMix, shearMix;
|
||||||
internal float x, y;
|
internal float offsetRotation, offsetX, offsetY, offsetScaleX, offsetScaleY, offsetShearY;
|
||||||
|
|
||||||
public TransformConstraintData Data { get { return data; } }
|
public TransformConstraintData Data { get { return data; } }
|
||||||
public Bone Bone { get { return bone; } set { bone = value; } }
|
public Bone Bone { get { return bone; } set { bone = value; } }
|
||||||
public Bone Target { get { return target; } set { target = value; } }
|
public Bone Target { get { return target; } set { target = value; } }
|
||||||
|
public float RotateMix { get { return rotateMix; } set { rotateMix = value; } }
|
||||||
public float TranslateMix { get { return translateMix; } set { translateMix = value; } }
|
public float TranslateMix { get { return translateMix; } set { translateMix = value; } }
|
||||||
public float X { get { return x; } set { x = value; } }
|
public float ScaleMix { get { return scaleMix; } set { scaleMix = value; } }
|
||||||
public float Y { get { return y; } set { y = value; } }
|
public float ShearMix { get { return shearMix; } set { shearMix = value; } }
|
||||||
|
|
||||||
|
public float OffsetRotation { get { return offsetRotation; } set { offsetRotation = value; } }
|
||||||
|
public float OffsetX { get { return offsetX; } set { offsetX = value; } }
|
||||||
|
public float OffsetY { get { return offsetY; } set { offsetY = value; } }
|
||||||
|
public float OffsetScaleX { get { return offsetScaleX; } set { offsetScaleX = value; } }
|
||||||
|
public float OffsetScaleY { get { return offsetScaleY; } set { offsetScaleY = value; } }
|
||||||
|
public float OffsetShearY { get { return offsetShearY; } set { offsetShearY = value; } }
|
||||||
|
|
||||||
public TransformConstraint (TransformConstraintData data, Skeleton skeleton) {
|
public TransformConstraint (TransformConstraintData data, Skeleton skeleton) {
|
||||||
if (data == null) throw new ArgumentNullException("data cannot be null.");
|
if (data == null) throw new ArgumentNullException("data cannot be null.");
|
||||||
if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null.");
|
if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null.");
|
||||||
this.data = data;
|
this.data = data;
|
||||||
translateMix = data.translateMix;
|
translateMix = data.translateMix;
|
||||||
x = data.x;
|
rotateMix = data.rotateMix;
|
||||||
y = data.y;
|
scaleMix = data.scaleMix;
|
||||||
|
shearMix = data.shearMix;
|
||||||
|
offsetX = data.offsetX;
|
||||||
|
offsetY = data.offsetY;
|
||||||
|
offsetScaleX = data.offsetScaleX;
|
||||||
|
offsetScaleY = data.offsetScaleY;
|
||||||
|
offsetShearY = data.offsetShearY;
|
||||||
|
|
||||||
bone = skeleton.FindBone(data.bone.name);
|
bone = skeleton.FindBone(data.bone.name);
|
||||||
target = skeleton.FindBone(data.target.name);
|
target = skeleton.FindBone(data.target.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update () {
|
public void Apply () {
|
||||||
Apply();
|
Update();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Apply () {
|
public void Update () {
|
||||||
|
Bone bone = this.bone;
|
||||||
|
Bone target = this.target;
|
||||||
|
|
||||||
|
if (rotateMix > 0) {
|
||||||
|
float a = bone.a, b = bone.b, c = bone.c, d = bone.d;
|
||||||
|
float r = MathUtils.Atan2(target.c, target.a) - MathUtils.Atan2(c, a) + offsetRotation * MathUtils.degRad;
|
||||||
|
if (r > MathUtils.PI)
|
||||||
|
r -= MathUtils.PI2;
|
||||||
|
else if (r < -MathUtils.PI) r += MathUtils.PI2;
|
||||||
|
r *= rotateMix;
|
||||||
|
float 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scaleMix > 0) {
|
||||||
|
float bs = (float)Math.Sqrt(bone.a * bone.a + bone.c * bone.c);
|
||||||
|
float ts = (float)Math.Sqrt(target.a * target.a + target.c * target.c);
|
||||||
|
float s = bs > 0.00001f ? (bs + (ts - bs + offsetScaleX) * scaleMix) / bs : 0;
|
||||||
|
bone.a *= s;
|
||||||
|
bone.c *= s;
|
||||||
|
bs = (float)Math.Sqrt(bone.b * bone.b + bone.d * bone.d);
|
||||||
|
ts = (float)Math.Sqrt(target.b * target.b + target.d * target.d);
|
||||||
|
s = bs > 0.00001f ? (bs + (ts - bs + offsetScaleY) * scaleMix) / bs : 0;
|
||||||
|
bone.b *= s;
|
||||||
|
bone.d *= s;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shearMix > 0) {
|
||||||
|
float b = bone.b, d = bone.d;
|
||||||
|
float by = MathUtils.Atan2(d, b);
|
||||||
|
float r = MathUtils.Atan2(target.d, target.b) - MathUtils.Atan2(target.c, target.a) - (by - MathUtils.Atan2(bone.c, bone.a));
|
||||||
|
if (r > MathUtils.PI)
|
||||||
|
r -= MathUtils.PI2;
|
||||||
|
else if (r < -MathUtils.PI) r += MathUtils.PI2;
|
||||||
|
r = by + (r + offsetShearY * MathUtils.degRad) * shearMix;
|
||||||
|
float s = (float)Math.Sqrt(b * b + d * d);
|
||||||
|
bone.b = MathUtils.Cos(r) * s;
|
||||||
|
bone.d = MathUtils.Sin(r) * s;
|
||||||
|
}
|
||||||
|
|
||||||
float translateMix = this.translateMix;
|
float translateMix = this.translateMix;
|
||||||
if (translateMix > 0) {
|
if (translateMix > 0) {
|
||||||
Bone bone = this.bone;
|
|
||||||
float tx, ty;
|
float tx, ty;
|
||||||
target.LocalToWorld(x, y, out tx, out ty);
|
target.LocalToWorld(offsetX, offsetY, out tx, out ty);
|
||||||
bone.worldX += (tx - bone.worldX) * translateMix;
|
bone.worldX += (tx - bone.worldX) * translateMix;
|
||||||
bone.worldY += (ty - bone.worldY) * translateMix;
|
bone.worldY += (ty - bone.worldY) * translateMix;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,15 +36,23 @@ namespace Spine {
|
|||||||
public class TransformConstraintData {
|
public class TransformConstraintData {
|
||||||
internal String name;
|
internal String name;
|
||||||
internal BoneData bone, target;
|
internal BoneData bone, target;
|
||||||
internal float translateMix;
|
internal float rotateMix, translateMix, scaleMix, shearMix;
|
||||||
internal float x, y;
|
internal float offsetRotation, offsetX, offsetY, offsetScaleX, offsetScaleY, offsetShearY;
|
||||||
|
|
||||||
public String Name { get { return name; } }
|
public String Name { get { return name; } }
|
||||||
public BoneData Bone { get { return bone; } set { bone = value; } }
|
public BoneData Bone { get { return bone; } set { bone = value; } }
|
||||||
public BoneData Target { get { return target; } set { target = value; } }
|
public BoneData Target { get { return target; } set { target = value; } }
|
||||||
|
public float RotateMix { get { return rotateMix; } set { rotateMix = value; } }
|
||||||
public float TranslateMix { get { return translateMix; } set { translateMix = value; } }
|
public float TranslateMix { get { return translateMix; } set { translateMix = value; } }
|
||||||
public float X { get { return x; } set { x = value; } }
|
public float ScaleMix { get { return scaleMix; } set { scaleMix = value; } }
|
||||||
public float Y { get { return y; } set { y = value; } }
|
public float ShearMix { get { return shearMix; } set { shearMix = value; } }
|
||||||
|
|
||||||
|
public float OffsetRotation { get { return offsetRotation; } set { offsetRotation = value; } }
|
||||||
|
public float OffsetX { get { return offsetX; } set { offsetX = value; } }
|
||||||
|
public float OffsetY { get { return offsetY; } set { offsetY = value; } }
|
||||||
|
public float OffsetScaleX { get { return offsetScaleX; } set { offsetScaleX = value; } }
|
||||||
|
public float OffsetScaleY { get { return offsetScaleY; } set { offsetScaleY = value; } }
|
||||||
|
public float OffsetShearY { get { return offsetShearY; } set { offsetShearY = value; } }
|
||||||
|
|
||||||
public TransformConstraintData (String name) {
|
public TransformConstraintData (String name) {
|
||||||
if (name == null) throw new ArgumentNullException("name cannot be null.");
|
if (name == null) throw new ArgumentNullException("name cannot be null.");
|
||||||
|
|||||||
@ -14,7 +14,7 @@ The Spine Runtimes are developed with the intent to be used with data exported f
|
|||||||
|
|
||||||
## Spine version
|
## Spine version
|
||||||
|
|
||||||
spine-unity works with data exported from Spine 3.1.08. Updating spine-unity to [v3.2](https://trello.com/c/k7KtGdPW/76-update-runtimes-to-support-v3-2-shearing) is in progress.
|
spine-unity works with data exported from the latest version of Spine.
|
||||||
|
|
||||||
spine-unity supports all Spine features.
|
spine-unity supports all Spine features.
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user