diff --git a/spine-as3/README.md b/spine-as3/README.md index 377c0b98c..56af9c374 100644 --- a/spine-as3/README.md +++ b/spine-as3/README.md @@ -10,7 +10,7 @@ The Spine Runtimes are developed with the intent to be used with data exported f ## Spine version -spine-as3 works with data exported from the latest version of Spine. +spine-as3 works with data exported from Spine 3.1.08. Updating spine-as3 to [v3.2](https://trello.com/c/k7KtGdPW/76-update-runtimes-to-support-v3-2-shearing) is in progress. spine-as3 supports all Spine features, including meshes. If using the `spine.flash` classes for rendering, meshes are not supported. diff --git a/spine-c/README.md b/spine-c/README.md index 75f51e952..e27415eee 100644 --- a/spine-c/README.md +++ b/spine-c/README.md @@ -12,7 +12,7 @@ The Spine Runtimes are developed with the intent to be used with data exported f ## Spine version -spine-c works with data exported from the latest version of Spine. +spine-c works with data exported from Spine 3.1.08. Updating spine-c to [v3.2](https://trello.com/c/k7KtGdPW/76-update-runtimes-to-support-v3-2-shearing) is in progress. spine-c supports all Spine features. diff --git a/spine-cocos2d-iphone/2/README.md b/spine-cocos2d-iphone/2/README.md index 20bdb146b..22de93123 100644 --- a/spine-cocos2d-iphone/2/README.md +++ b/spine-cocos2d-iphone/2/README.md @@ -10,7 +10,7 @@ The Spine Runtimes are developed with the intent to be used with data exported f ## Spine version -spine-cocos2d-iphone v2 works with data exported from the latest version of Spine. +spine-cocos2d-iphone v2 works with data exported from Spine 3.1.08. Updating spine-cocos2d-iphone v2 to [v3.2](https://trello.com/c/k7KtGdPW/76-update-runtimes-to-support-v3-2-shearing) is in progress. spine-cocos2d-iphone v2 supports all Spine features. diff --git a/spine-cocos2d-iphone/3/README.md b/spine-cocos2d-iphone/3/README.md index 9adcd6a20..6e759d221 100644 --- a/spine-cocos2d-iphone/3/README.md +++ b/spine-cocos2d-iphone/3/README.md @@ -10,7 +10,7 @@ The Spine Runtimes are developed with the intent to be used with data exported f ## Spine version -spine-cocos2d-iphone v3 works with data exported from the latest version of Spine. +spine-cocos2d-iphone v3 works with data exported from Spine 3.1.08. Updating spine-cocos2d-iphone v3 to [v3.2](https://trello.com/c/k7KtGdPW/76-update-runtimes-to-support-v3-2-shearing) is in progress. spine-cocos2d-iphone v3 supports all Spine features. diff --git a/spine-cocos2dx/2/README.md b/spine-cocos2dx/2/README.md index f756bd2f0..b9fee7534 100644 --- a/spine-cocos2dx/2/README.md +++ b/spine-cocos2dx/2/README.md @@ -10,7 +10,7 @@ The Spine Runtimes are developed with the intent to be used with data exported f ## Spine version -spine-cocos2dx v2 works with data exported from the latest version of Spine. +spine-cocos2dx v2 works with data exported from Spine 3.1.08. Updating spine-cocos2dx v2 to [v3.2](https://trello.com/c/k7KtGdPW/76-update-runtimes-to-support-v3-2-shearing) is in progress. spine-cocos2dx v2 supports all Spine features. diff --git a/spine-cocos2dx/3/README.md b/spine-cocos2dx/3/README.md index 5bb0f2d82..140087fcf 100644 --- a/spine-cocos2dx/3/README.md +++ b/spine-cocos2dx/3/README.md @@ -10,7 +10,7 @@ The Spine Runtimes are developed with the intent to be used with data exported f ## Spine version -spine-cocos2dx v3 works with data exported from the latest version of Spine. +spine-cocos2dx v3 works with data exported from Spine 3.1.08. Updating spine-cocos2dx v3 to [v3.2](https://trello.com/c/k7KtGdPW/76-update-runtimes-to-support-v3-2-shearing) is in progress. spine-cocos2dx v3 supports all Spine features. diff --git a/spine-corona/README.md b/spine-corona/README.md index 8a8d8889d..f1a0f46be 100644 --- a/spine-corona/README.md +++ b/spine-corona/README.md @@ -10,7 +10,7 @@ The Spine Runtimes are developed with the intent to be used with data exported f ## Spine version -spine-corona works with data exported from Spine 2.1.27. Updating spine-corona to [v3.0](https://trello.com/c/tF8UykBM/72-update-runtimes-to-support-v3-0-skewing-scale) and [v3.1](https://trello.com/c/bERJAFEq/73-update-runtimes-to-support-v3-1-linked-meshes) is in progress. +spine-corona works with data exported from Spine 2.1.27. Updating spine-corona to [v3.0](https://trello.com/c/tF8UykBM/72-update-runtimes-to-support-v3-0-skewing-scale), [v3.1](https://trello.com/c/bERJAFEq/73-update-runtimes-to-support-v3-1-linked-meshes), and [v3.2](https://trello.com/c/k7KtGdPW/76-update-runtimes-to-support-v3-2-shearing) is in progress. spine-corona supports all Spine features except for rendering meshes due to Corona having a limited graphics API. diff --git a/spine-csharp/README.md b/spine-csharp/README.md index 4f6e5acdc..3b6f0d1a4 100644 --- a/spine-csharp/README.md +++ b/spine-csharp/README.md @@ -10,7 +10,7 @@ The Spine Runtimes are developed with the intent to be used with data exported f ## Spine version -spine-csharp works with data exported from the latest version of Spine. +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 supports all Spine features. diff --git a/spine-js/README.md b/spine-js/README.md index 06077d3f9..8a8631701 100644 --- a/spine-js/README.md +++ b/spine-js/README.md @@ -14,9 +14,9 @@ The Spine Runtimes are developed with the intent to be used with data exported f ## Spine version -spine-js works with data exported from the latest version of Spine. +spine-js works with data exported from Spine 3.1.08. Updating spine-js to [v3.2](https://trello.com/c/k7KtGdPW/76-update-runtimes-to-support-v3-2-shearing) is in progress. -spine-js supports all Spine features. spine-canvas does not support mesh attachments or nonuniform scaling.~ +spine-js supports all Spine features. spine-canvas does not support mesh attachments or nonuniform scaling. spine-js does not yet support loading the binary format. diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java index eb27c3bcb..a05848644 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java @@ -240,11 +240,11 @@ public class Animation { } static public class RotateTimeline extends CurveTimeline { - static private final int PREV_FRAME_TIME = -2; - static private final int FRAME_VALUE = 1; + static final int PREV_TIME = -2; + static final int VALUE = 1; int boneIndex; - private final float[] frames; // time, angle, ... + final float[] frames; // time, angle, ... public RotateTimeline (int frameCount) { super(frameCount); @@ -287,13 +287,13 @@ public class Animation { } // Interpolate between the previous frame and the current frame. - int frameIndex = binarySearch(frames, time, 2); - float prevFrameValue = frames[frameIndex - 1]; - float frameTime = frames[frameIndex]; - float percent = MathUtils.clamp(1 - (time - frameTime) / (frames[frameIndex + PREV_FRAME_TIME] - frameTime), 0, 1); - percent = getCurvePercent((frameIndex >> 1) - 1, percent); + int frame = binarySearch(frames, time, 2); + float prevFrameValue = frames[frame - 1]; + float frameTime = frames[frame]; + float percent = MathUtils.clamp(1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime), 0, 1); + percent = getCurvePercent((frame >> 1) - 1, percent); - float amount = frames[frameIndex + FRAME_VALUE] - prevFrameValue; + float amount = frames[frame + VALUE] - prevFrameValue; while (amount > 180) amount -= 360; while (amount < -180) @@ -308,9 +308,9 @@ public class Animation { } static public class TranslateTimeline extends CurveTimeline { - static final int PREV_FRAME_TIME = -3; - static final int FRAME_X = 1; - static final int FRAME_Y = 2; + static final int PREV_TIME = -3; + static final int X = 1; + static final int Y = 2; int boneIndex; final float[] frames; // time, x, y, ... @@ -353,15 +353,15 @@ public class Animation { } // Interpolate between the previous frame and the current frame. - int frameIndex = binarySearch(frames, time, 3); - float prevFrameX = frames[frameIndex - 2]; - float prevFrameY = frames[frameIndex - 1]; - float frameTime = frames[frameIndex]; - float percent = MathUtils.clamp(1 - (time - frameTime) / (frames[frameIndex + PREV_FRAME_TIME] - frameTime), 0, 1); - percent = getCurvePercent(frameIndex / 3 - 1, percent); + int frame = binarySearch(frames, time, 3); + float prevFrameX = frames[frame - 2]; + float prevFrameY = frames[frame - 1]; + float frameTime = frames[frame]; + float percent = MathUtils.clamp(1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime), 0, 1); + percent = getCurvePercent(frame / 3 - 1, percent); - bone.x += (bone.data.x + prevFrameX + (frames[frameIndex + FRAME_X] - prevFrameX) * percent - bone.x) * alpha; - bone.y += (bone.data.y + prevFrameY + (frames[frameIndex + FRAME_Y] - prevFrameY) * percent - bone.y) * alpha; + 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; } } @@ -382,26 +382,53 @@ public class Animation { } // Interpolate between the previous frame and the current frame. - int frameIndex = binarySearch(frames, time, 3); - float prevFrameX = frames[frameIndex - 2]; - float prevFrameY = frames[frameIndex - 1]; - float frameTime = frames[frameIndex]; - float percent = MathUtils.clamp(1 - (time - frameTime) / (frames[frameIndex + PREV_FRAME_TIME] - frameTime), 0, 1); - percent = getCurvePercent(frameIndex / 3 - 1, percent); + int frame = binarySearch(frames, time, 3); + float prevFrameX = frames[frame - 2]; + float prevFrameY = frames[frame - 1]; + float frameTime = frames[frame]; + float percent = MathUtils.clamp(1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime), 0, 1); + percent = getCurvePercent(frame / 3 - 1, percent); - bone.scaleX += (bone.data.scaleX * (prevFrameX + (frames[frameIndex + FRAME_X] - prevFrameX) * percent) - bone.scaleX) - * alpha; - bone.scaleY += (bone.data.scaleY * (prevFrameY + (frames[frameIndex + FRAME_Y] - prevFrameY) * percent) - bone.scaleY) - * alpha; + 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; + } + } + + static public class ShearTimeline extends TranslateTimeline { + public ShearTimeline (int frameCount) { + super(frameCount); + } + + public void apply (Skeleton skeleton, float lastTime, float time, Array events, float alpha) { + float[] frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + Bone bone = skeleton.bones.get(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 = binarySearch(frames, time, 3); + float prevFrameX = frames[frame - 2]; + float prevFrameY = frames[frame - 1]; + float frameTime = frames[frame]; + float percent = MathUtils.clamp(1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime), 0, 1); + percent = getCurvePercent(frame / 3 - 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; } } static public class ColorTimeline extends CurveTimeline { - static private final int PREV_FRAME_TIME = -5; - static private final int FRAME_R = 1; - static private final int FRAME_G = 2; - static private final int FRAME_B = 3; - static private final int FRAME_A = 4; + static private final int PREV_TIME = -5; + static private final int R = 1; + static private final int G = 2; + static private final int B = 3; + static private final int A = 4; int slotIndex; private final float[] frames; // time, r, g, b, a, ... @@ -438,8 +465,7 @@ public class Animation { 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. + if (time >= frames[frames.length - 5]) { // Time is after last frame. int i = frames.length - 1; r = frames[i - 3]; g = frames[i - 2]; @@ -447,19 +473,19 @@ public class Animation { a = frames[i]; } else { // Interpolate between the previous frame and the current frame. - int frameIndex = binarySearch(frames, time, 5); - float prevFrameR = frames[frameIndex - 4]; - float prevFrameG = frames[frameIndex - 3]; - float prevFrameB = frames[frameIndex - 2]; - float prevFrameA = frames[frameIndex - 1]; - float frameTime = frames[frameIndex]; - float percent = MathUtils.clamp(1 - (time - frameTime) / (frames[frameIndex + PREV_FRAME_TIME] - frameTime), 0, 1); - percent = getCurvePercent(frameIndex / 5 - 1, percent); + int frame = binarySearch(frames, time, 5); + float frameTime = frames[frame]; + float percent = MathUtils.clamp(1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime), 0, 1); + percent = getCurvePercent(frame / 5 - 1, percent); - r = prevFrameR + (frames[frameIndex + FRAME_R] - prevFrameR) * percent; - g = prevFrameG + (frames[frameIndex + FRAME_G] - prevFrameG) * percent; - b = prevFrameB + (frames[frameIndex + FRAME_B] - prevFrameB) * percent; - a = prevFrameA + (frames[frameIndex + FRAME_A] - prevFrameA) * percent; + 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; + a += (frames[frame + A] - a) * percent; } Color color = skeleton.slots.get(slotIndex).color; if (alpha < 1) @@ -513,10 +539,10 @@ public class Animation { } else if (lastTime > time) // lastTime = -1; - int frameIndex = (time >= frames[frames.length - 1] ? frames.length : binarySearch(frames, time)) - 1; - if (frames[frameIndex] < lastTime) return; + int frame = (time >= frames[frames.length - 1] ? frames.length : binarySearch(frames, time)) - 1; + if (frames[frame] < lastTime) return; - String attachmentName = attachmentNames[frameIndex]; + String attachmentName = attachmentNames[frame]; skeleton.slots.get(slotIndex) .setAttachment(attachmentName == null ? null : skeleton.getAttachment(slotIndex, attachmentName)); } @@ -562,19 +588,19 @@ public class Animation { return; if (time < frames[0]) return; // Time is before first frame. - int frameIndex; + int frame; if (lastTime < frames[0]) - frameIndex = 0; + frame = 0; else { - frameIndex = binarySearch(frames, lastTime); - float frame = frames[frameIndex]; - while (frameIndex > 0) { // Fire multiple events with the same frame. - if (frames[frameIndex - 1] != frame) break; - frameIndex--; + frame = binarySearch(frames, lastTime); + float frameTime = frames[frame]; + while (frame > 0) { // Fire multiple events with the same frame. + if (frames[frame - 1] != frameTime) break; + frame--; } } - for (; frameIndex < frameCount && time >= frames[frameIndex]; frameIndex++) - firedEvents.add(events[frameIndex]); + for (; frame < frameCount && time >= frames[frame]; frame++) + firedEvents.add(events[frame]); } } @@ -610,15 +636,15 @@ public class Animation { float[] frames = this.frames; 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. - frameIndex = frames.length - 1; + frame = frames.length - 1; else - frameIndex = binarySearch(frames, time) - 1; + frame = binarySearch(frames, time) - 1; Array drawOrder = skeleton.drawOrder; Array slots = skeleton.slots; - int[] drawOrderToSetupIndex = drawOrders[frameIndex]; + int[] drawOrderToSetupIndex = drawOrders[frame]; if (drawOrderToSetupIndex == null) System.arraycopy(slots.items, 0, drawOrder.items, 0, slots.size); else { @@ -699,14 +725,13 @@ public class Animation { } // Interpolate between the previous frame and the current frame. - int frameIndex = binarySearch(frames, time); - float frameTime = frames[frameIndex]; - float percent = MathUtils.clamp(1 - (time - frameTime) / (frames[frameIndex - 1] - frameTime), 0, 1); - percent = getCurvePercent(frameIndex - 1, percent); - - float[] prevVertices = frameVertices[frameIndex - 1]; - float[] nextVertices = frameVertices[frameIndex]; + int frame = binarySearch(frames, time); + float frameTime = frames[frame]; + float percent = MathUtils.clamp(1 - (time - frameTime) / (frames[frame - 1] - frameTime), 0, 1); + percent = getCurvePercent(frame - 1, percent); + float[] prevVertices = frameVertices[frame - 1]; + float[] nextVertices = frameVertices[frame]; if (alpha < 1) { for (int i = 0; i < vertexCount; i++) { float prev = prevVertices[i]; @@ -722,10 +747,10 @@ public class Animation { } static public class IkConstraintTimeline extends CurveTimeline { - static private final int PREV_FRAME_TIME = -3; - static private final int PREV_FRAME_MIX = -2; - static private final int PREV_FRAME_BEND_DIRECTION = -1; - static private final int FRAME_MIX = 1; + static private final int PREV_TIME = -3; + static private final int PREV_MIX = -2; + static private final int PREV_BEND_DIRECTION = -1; + static private final int MIX = 1; int ikConstraintIndex; private final float[] frames; // time, mix, bendDirection, ... @@ -759,24 +784,97 @@ public class Animation { float[] frames = this.frames; if (time < frames[0]) return; // Time is before first frame. - IkConstraint ikConstraint = skeleton.ikConstraints.get(ikConstraintIndex); + IkConstraint constraint = skeleton.ikConstraints.get(ikConstraintIndex); if (time >= frames[frames.length - 3]) { // Time is after last frame. - ikConstraint.mix += (frames[frames.length - 2] - ikConstraint.mix) * alpha; - ikConstraint.bendDirection = (int)frames[frames.length - 1]; + 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 frameIndex = binarySearch(frames, time, 3); - float prevFrameMix = frames[frameIndex + PREV_FRAME_MIX]; - float frameTime = frames[frameIndex]; - float percent = MathUtils.clamp(1 - (time - frameTime) / (frames[frameIndex + PREV_FRAME_TIME] - frameTime), 0, 1); - percent = getCurvePercent(frameIndex / 3 - 1, percent); + int frame = binarySearch(frames, time, 3); + float frameTime = frames[frame]; + float percent = MathUtils.clamp(1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime), 0, 1); + percent = getCurvePercent(frame / 3 - 1, percent); - float mix = prevFrameMix + (frames[frameIndex + FRAME_MIX] - prevFrameMix) * percent; - ikConstraint.mix += (mix - ikConstraint.mix) * alpha; - ikConstraint.bendDirection = (int)frames[frameIndex + PREV_FRAME_BEND_DIRECTION]; + float mix = frames[frame + PREV_MIX]; + constraint.mix += (mix + (frames[frame + MIX] - mix) * percent - constraint.mix) * alpha; + constraint.bendDirection = (int)frames[frame + PREV_BEND_DIRECTION]; + } + } + + static public class TransformConstraintTimeline extends CurveTimeline { + static private final int PREV_TIME = -5; + static private final int PREV_ROTATE_MIX = -4; + static private final int PREV_TRANSLATE_MIX = -3; + static private final int PREV_SCALE_MIX = -2; + static private final int PREV_SHEAR_MIX = -1; + static private final int ROTATE_MIX = 1; + static private final int TRANSLATE_MIX = 2; + static private final int SCALE_MIX = 3; + static private final int SHEAR_MIX = 4; + + int transformConstraintIndex; + private final float[] frames; // time, rotate mix, translate mix, scale mix, shear mix, ... + + public TransformConstraintTimeline (int frameCount) { + super(frameCount); + frames = new float[frameCount * 5]; + } + + public void setTransformConstraintIndex (int ikConstraint) { + this.transformConstraintIndex = ikConstraint; + } + + public int getTransformConstraintIndex () { + return transformConstraintIndex; + } + + public float[] getFrames () { + return frames; + } + + /** Sets the time and mixes of the specified keyframe. */ + 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; + } + + public void apply (Skeleton skeleton, float lastTime, float time, Array events, float alpha) { + float[] frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + TransformConstraint constraint = skeleton.transformConstraints.get(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 = binarySearch(frames, time, 5); + float frameTime = frames[frame]; + float percent = MathUtils.clamp(1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime), 0, 1); + percent = getCurvePercent(frame / 5 - 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; } } } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java index 28cbe4309..ef52a0712 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java @@ -31,9 +31,9 @@ package com.esotericsoftware.spine; +import static com.badlogic.gdx.math.MathUtils.*; import static com.badlogic.gdx.math.Matrix3.*; -import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.Matrix3; import com.badlogic.gdx.math.Vector2; @@ -41,7 +41,7 @@ public class Bone implements Updatable { final BoneData data; final Skeleton skeleton; final Bone parent; - float x, y, rotation, scaleX, scaleY; + float x, y, rotation, scaleX, scaleY, shearX, shearY; float appliedRotation, appliedScaleX, appliedScaleY; float a, b, worldX; @@ -76,26 +76,30 @@ public class Bone implements Updatable { rotation = bone.rotation; scaleX = bone.scaleX; scaleY = bone.scaleY; + shearX = bone.shearX; + shearY = bone.shearY; } /** Same as {@link #updateWorldTransform()}. This method exists for Bone to implement {@link Updatable}. */ public void update () { - updateWorldTransform(x, y, rotation, scaleX, scaleY); + updateWorldTransform(x, y, rotation, scaleX, scaleY, shearX, shearY); } /** Computes the world SRT using the parent bone and this bone's local SRT. */ public void updateWorldTransform () { - updateWorldTransform(x, y, rotation, scaleX, scaleY); + updateWorldTransform(x, y, rotation, scaleX, scaleY, shearX, shearY); } /** Computes the world SRT using the parent bone and the specified local SRT. */ - public void updateWorldTransform (float x, float y, float rotation, float scaleX, float scaleY) { + public void updateWorldTransform (float x, float y, float rotation, float scaleX, float scaleY, float shearX, float shearY) { appliedRotation = rotation; appliedScaleX = scaleX; appliedScaleY = scaleY; - float cos = MathUtils.cosDeg(rotation), sin = MathUtils.sinDeg(rotation); - float la = cos * scaleX, lb = -sin * scaleY, lc = sin * scaleX, ld = cos * scaleY; + float rotationY = rotation + 90 + shearY; + float la = cosDeg(rotation + shearX) * scaleX, lb = cosDeg(rotationY) * scaleY; + float lc = sinDeg(rotation + shearX) * scaleX, ld = sinDeg(rotationY) * scaleY; + Bone parent = this.parent; if (parent == null) { // Root bone. Skeleton skeleton = this.skeleton; @@ -138,8 +142,7 @@ public class Bone implements Updatable { pc = 0; pd = 1; do { - cos = MathUtils.cosDeg(parent.appliedRotation); - sin = MathUtils.sinDeg(parent.appliedRotation); + float cos = cosDeg(parent.appliedRotation), sin = sinDeg(parent.appliedRotation); float temp = pa * cos + pb * sin; pb = pa * -sin + pb * cos; pa = temp; @@ -160,9 +163,7 @@ public class Bone implements Updatable { pc = 0; pd = 1; do { - float r = parent.appliedRotation; - cos = MathUtils.cosDeg(r); - sin = MathUtils.sinDeg(r); + float r = parent.appliedRotation, cos = cosDeg(r), sin = sinDeg(r); float psx = parent.appliedScaleX, psy = parent.appliedScaleY; float za = cos * psx, zb = -sin * psy, zc = sin * psx, zd = cos * psy; float temp = pa * za + pb * zc; @@ -173,8 +174,8 @@ public class Bone implements Updatable { pc = temp; if (psx < 0) r = -r; - cos = MathUtils.cosDeg(-r); - sin = MathUtils.sinDeg(-r); + cos = cosDeg(-r); + sin = sinDeg(-r); temp = pa * cos + pb * sin; pb = pa * -sin + pb * cos; pa = temp; @@ -213,6 +214,8 @@ public class Bone implements Updatable { rotation = data.rotation; scaleX = data.scaleX; scaleY = data.scaleY; + shearX = data.shearX; + shearY = data.shearY; } public BoneData getData () { @@ -248,7 +251,6 @@ public class Bone implements Updatable { this.y = y; } - /** Returns the forward kinetics rotation. */ public float getRotation () { return rotation; } @@ -283,6 +285,22 @@ public class Bone implements Updatable { scaleY = scale; } + public float getShearX () { + return shearX; + } + + public void setShearX (float shearX) { + this.shearX = shearX; + } + + public float getShearY () { + return shearY; + } + + public void setShearY (float shearY) { + this.shearY = shearY; + } + public float getA () { return a; } @@ -316,11 +334,11 @@ public class Bone implements Updatable { } public float getWorldRotationX () { - return MathUtils.atan2(c, a) * MathUtils.radDeg; + return atan2(c, a) * radDeg; } public float getWorldRotationY () { - return MathUtils.atan2(d, b) * MathUtils.radDeg; + return atan2(d, b) * radDeg; } public float getWorldScaleX () { diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BoneData.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BoneData.java index 9d3d2f13a..fdc5cff99 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BoneData.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BoneData.java @@ -37,9 +37,7 @@ public class BoneData { final BoneData parent; final String name; float length; - float x, y; - float rotation; - float scaleX = 1, scaleY = 1; + float x, y, rotation, scaleX = 1, scaleY = 1, shearX, shearY; boolean inheritScale = true, inheritRotation = true; // Nonessential. @@ -64,6 +62,8 @@ public class BoneData { rotation = bone.rotation; scaleX = bone.scaleX; scaleY = bone.scaleY; + shearX = bone.shearX; + shearY = bone.shearY; } /** @return May be null. */ @@ -133,6 +133,22 @@ public class BoneData { this.scaleY = scaleY; } + public float getShearX () { + return shearX; + } + + public void setShearX (float shearX) { + this.shearX = shearX; + } + + public float getShearY () { + return shearY; + } + + public void setShearY (float shearY) { + this.shearY = shearY; + } + public boolean getInheritScale () { return inheritScale; } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java index 75d12e431..0ee0c1023 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java @@ -130,7 +130,7 @@ public class IkConstraint implements Updatable { rotationIK -= 360; else if (rotationIK < -180) rotationIK += 360; bone.updateWorldTransform(bone.x, bone.y, rotation + (rotationIK - rotation) * alpha, bone.appliedScaleX, - bone.appliedScaleY); + bone.appliedScaleY, 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 @@ -259,8 +259,10 @@ public class IkConstraint implements Updatable { a2 -= 360; else if (a2 < -180) a2 += 360; 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, + parent.shearX, parent.shearY); 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); } } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java index cedb7b49b..1cf609734 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java @@ -148,8 +148,7 @@ public class Skeleton { for (int i = 0; i < transformConstraintsCount; i++) { TransformConstraint transformConstraint = transformConstraints.get(i); for (int ii = updateCache.size - 1; ii >= 0; ii--) { - Updatable object = updateCache.get(ii); - if (object == transformConstraint.bone || object == transformConstraint.target) { + if (updateCache.get(ii) == transformConstraint.bone) { updateCache.insert(ii + 1, transformConstraint); break; } @@ -186,9 +185,11 @@ public class Skeleton { Array transformConstraints = this.transformConstraints; for (int i = 0, n = transformConstraints.size; i < n; i++) { TransformConstraint constraint = transformConstraints.get(i); - constraint.translateMix = constraint.data.translateMix; - constraint.x = constraint.data.x; - constraint.y = constraint.data.y; + TransformConstraintData data = constraint.data; + constraint.rotateMix = data.rotateMix; + constraint.translateMix = data.translateMix; + constraint.scaleMix = data.scaleMix; + constraint.translateMix = data.translateMix; } } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java index 7451bd900..9fb82b280 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java @@ -50,7 +50,9 @@ import com.esotericsoftware.spine.Animation.FfdTimeline; import com.esotericsoftware.spine.Animation.IkConstraintTimeline; import com.esotericsoftware.spine.Animation.RotateTimeline; import com.esotericsoftware.spine.Animation.ScaleTimeline; +import com.esotericsoftware.spine.Animation.ShearTimeline; import com.esotericsoftware.spine.Animation.Timeline; +import com.esotericsoftware.spine.Animation.TransformConstraintTimeline; import com.esotericsoftware.spine.Animation.TranslateTimeline; import com.esotericsoftware.spine.SkeletonJson.LinkedMesh; import com.esotericsoftware.spine.attachments.AtlasAttachmentLoader; @@ -63,11 +65,12 @@ import com.esotericsoftware.spine.attachments.RegionAttachment; import com.esotericsoftware.spine.attachments.WeightedMeshAttachment; public class SkeletonBinary { - static public final int TIMELINE_SCALE = 0; - static public final int TIMELINE_ROTATE = 1; - static public final int TIMELINE_TRANSLATE = 2; - static public final int TIMELINE_ATTACHMENT = 3; - static public final int TIMELINE_COLOR = 4; + static public final int TIMELINE_ROTATE = 0; + static public final int TIMELINE_TRANSLATE = 1; + static public final int TIMELINE_SCALE = 2; + static public final int TIMELINE_SHEAR = 3; + static public final int TIMELINE_ATTACHMENT = 4; + static public final int TIMELINE_COLOR = 5; static public final int CURVE_LINEAR = 0; static public final int CURVE_STEPPED = 1; @@ -159,14 +162,16 @@ public class SkeletonBinary { String name = input.readString(); BoneData parent = i == 0 ? null : skeletonData.bones.get(input.readInt(true)); BoneData boneData = new BoneData(name, parent); + boneData.rotation = input.readFloat(); boneData.x = input.readFloat() * scale; boneData.y = input.readFloat() * scale; boneData.scaleX = input.readFloat(); boneData.scaleY = input.readFloat(); - boneData.rotation = input.readFloat(); + boneData.shearX = input.readFloat(); + boneData.shearY = input.readFloat(); boneData.length = input.readFloat() * scale; - boneData.inheritScale = input.readBoolean(); boneData.inheritRotation = input.readBoolean(); + boneData.inheritScale = input.readBoolean(); if (nonessential) Color.rgba8888ToColor(boneData.color, input.readInt()); skeletonData.bones.add(boneData); } @@ -187,9 +192,16 @@ public class SkeletonBinary { TransformConstraintData transformConstraintData = new TransformConstraintData(input.readString()); transformConstraintData.bone = skeletonData.bones.get(input.readInt(true)); transformConstraintData.target = skeletonData.bones.get(input.readInt(true)); + transformConstraintData.offsetRotation = input.readFloat(); + transformConstraintData.offsetX = input.readFloat(); + transformConstraintData.offsetY = input.readFloat(); + transformConstraintData.offsetScaleX = input.readFloat(); + transformConstraintData.offsetScaleY = input.readFloat(); + transformConstraintData.offsetShearY = input.readFloat(); + transformConstraintData.rotateMix = input.readFloat(); transformConstraintData.translateMix = input.readFloat(); - transformConstraintData.x = input.readFloat(); - transformConstraintData.y = input.readFloat(); + transformConstraintData.scaleMix = input.readFloat(); + transformConstraintData.shearMix = input.readFloat(); skeletonData.transformConstraints.add(transformConstraintData); } @@ -291,11 +303,11 @@ public class SkeletonBinary { switch (type) { case region: { String path = input.readString(); + float rotation = input.readFloat(); float x = input.readFloat(); float y = input.readFloat(); float scaleX = input.readFloat(); float scaleY = input.readFloat(); - float rotation = input.readFloat(); float width = input.readFloat(); float height = input.readFloat(); int color = input.readInt(); @@ -532,11 +544,14 @@ public class SkeletonBinary { break; } case TIMELINE_TRANSLATE: - case TIMELINE_SCALE: { + case TIMELINE_SCALE: + case TIMELINE_SHEAR: { TranslateTimeline timeline; float timelineScale = 1; if (timelineType == TIMELINE_SCALE) timeline = new ScaleTimeline(frameCount); + else if (timelineType == TIMELINE_SHEAR) + timeline = new ShearTimeline(frameCount); else { timeline = new TranslateTimeline(frameCount); timelineScale = scale; @@ -555,12 +570,12 @@ public class SkeletonBinary { } } - // IK timelines. + // IK constraint timelines. for (int i = 0, n = input.readInt(true); i < n; i++) { - IkConstraintData ikConstraint = skeletonData.ikConstraints.get(input.readInt(true)); + IkConstraintData constraint = skeletonData.ikConstraints.get(input.readInt(true)); int frameCount = input.readInt(true); IkConstraintTimeline timeline = new IkConstraintTimeline(frameCount); - timeline.ikConstraintIndex = skeletonData.getIkConstraints().indexOf(ikConstraint, true); + timeline.ikConstraintIndex = skeletonData.getIkConstraints().indexOf(constraint, true); for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) { timeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readByte()); if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline); @@ -569,6 +584,21 @@ public class SkeletonBinary { duration = Math.max(duration, timeline.getFrames()[frameCount * 3 - 3]); } + // Transform constraint timelines. + for (int i = 0, n = input.readInt(true); i < n; i++) { + TransformConstraintData constraint = skeletonData.transformConstraints.get(input.readInt(true)); + int frameCount = input.readInt(true); + TransformConstraintTimeline timeline = new TransformConstraintTimeline(frameCount); + timeline.transformConstraintIndex = skeletonData.getTransformConstraints().indexOf(constraint, true); + for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) { + timeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readFloat(), input.readFloat(), + input.readFloat()); + if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline); + } + timelines.add(timeline); + duration = Math.max(duration, timeline.getFrames()[frameCount * 5 - 5]); + } + // FFD timelines. for (int i = 0, n = input.readInt(true); i < n; i++) { Skin skin = skeletonData.skins.get(input.readInt(true)); diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java index 3b9872271..5e97caabc 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java @@ -49,7 +49,9 @@ import com.esotericsoftware.spine.Animation.FfdTimeline; import com.esotericsoftware.spine.Animation.IkConstraintTimeline; import com.esotericsoftware.spine.Animation.RotateTimeline; import com.esotericsoftware.spine.Animation.ScaleTimeline; +import com.esotericsoftware.spine.Animation.ShearTimeline; import com.esotericsoftware.spine.Animation.Timeline; +import com.esotericsoftware.spine.Animation.TransformConstraintTimeline; import com.esotericsoftware.spine.Animation.TranslateTimeline; import com.esotericsoftware.spine.attachments.AtlasAttachmentLoader; import com.esotericsoftware.spine.attachments.Attachment; @@ -117,6 +119,8 @@ public class SkeletonJson { boneData.rotation = boneMap.getFloat("rotation", 0); boneData.scaleX = boneMap.getFloat("scaleX", 1); boneData.scaleY = boneMap.getFloat("scaleY", 1); + boneData.shearX = boneMap.getFloat("shearX", 0); + boneData.shearY = boneMap.getFloat("shearY", 0); boneData.inheritScale = boneMap.getBoolean("inheritScale", true); boneData.inheritRotation = boneMap.getBoolean("inheritRotation", true); @@ -159,9 +163,17 @@ public class SkeletonJson { transformConstraintData.target = skeletonData.findBone(targetName); if (transformConstraintData.target == null) throw new SerializationException("Target bone not found: " + targetName); + transformConstraintData.offsetRotation = transformMap.getFloat("rotation", 0); + transformConstraintData.offsetX = transformMap.getFloat("x", 0) * scale; + transformConstraintData.offsetY = transformMap.getFloat("y", 0) * scale; + transformConstraintData.offsetScaleX = transformMap.getFloat("scaleX", 0) * scale; + transformConstraintData.offsetScaleY = transformMap.getFloat("scaleY", 0) * scale; + transformConstraintData.offsetShearY = transformMap.getFloat("shearY", 0) * scale; + + transformConstraintData.rotateMix = transformMap.getFloat("rotateMix", 1); transformConstraintData.translateMix = transformMap.getFloat("translateMix", 1); - transformConstraintData.x = transformMap.getFloat("x", 0) * scale; - transformConstraintData.y = transformMap.getFloat("y", 0) * scale; + transformConstraintData.scaleMix = transformMap.getFloat("scaleMix", 1); + transformConstraintData.shearMix = transformMap.getFloat("shearMix", 1); skeletonData.transformConstraints.add(transformConstraintData); } @@ -422,11 +434,13 @@ public class SkeletonJson { timelines.add(timeline); duration = Math.max(duration, timeline.getFrames()[timeline.getFrameCount() * 2 - 2]); - } else if (timelineName.equals("translate") || timelineName.equals("scale")) { + } else if (timelineName.equals("translate") || timelineName.equals("scale") || timelineName.equals("shear")) { TranslateTimeline timeline; float timelineScale = 1; if (timelineName.equals("scale")) timeline = new ScaleTimeline(timelineMap.size); + else if (timelineName.equals("shear")) + timeline = new ShearTimeline(timelineMap.size); else { timeline = new TranslateTimeline(timelineMap.size); timelineScale = scale; @@ -448,14 +462,14 @@ public class SkeletonJson { } } - // IK timelines. - for (JsonValue ikMap = map.getChild("ik"); ikMap != null; ikMap = ikMap.next) { - IkConstraintData ikConstraint = skeletonData.findIkConstraint(ikMap.name); - IkConstraintTimeline timeline = new IkConstraintTimeline(ikMap.size); - timeline.ikConstraintIndex = skeletonData.getIkConstraints().indexOf(ikConstraint, true); + // IK constraint timelines. + for (JsonValue constraintMap = map.getChild("ik"); constraintMap != null; constraintMap = constraintMap.next) { + IkConstraintData constraint = skeletonData.findIkConstraint(constraintMap.name); + IkConstraintTimeline timeline = new IkConstraintTimeline(constraintMap.size); + timeline.ikConstraintIndex = skeletonData.getIkConstraints().indexOf(constraint, true); int frameIndex = 0; - for (JsonValue valueMap = ikMap.child; valueMap != null; valueMap = valueMap.next) { - timeline.setFrame(frameIndex, valueMap.getFloat("time"), valueMap.getFloat("mix"), + for (JsonValue valueMap = constraintMap.child; valueMap != null; valueMap = valueMap.next) { + timeline.setFrame(frameIndex, valueMap.getFloat("time"), valueMap.getFloat("mix", 1), valueMap.getBoolean("bendPositive") ? 1 : -1); readCurve(timeline, frameIndex, valueMap); frameIndex++; @@ -464,6 +478,22 @@ public class SkeletonJson { duration = Math.max(duration, timeline.getFrames()[timeline.getFrameCount() * 3 - 3]); } + // Transform constraint timelines. + for (JsonValue constraintMap = map.getChild("transform"); constraintMap != null; constraintMap = constraintMap.next) { + TransformConstraintData constraint = skeletonData.findTransformConstraint(constraintMap.name); + TransformConstraintTimeline timeline = new TransformConstraintTimeline(constraintMap.size); + timeline.transformConstraintIndex = skeletonData.getTransformConstraints().indexOf(constraint, true); + int frameIndex = 0; + for (JsonValue valueMap = constraintMap.child; valueMap != null; valueMap = valueMap.next) { + timeline.setFrame(frameIndex, valueMap.getFloat("time"), valueMap.getFloat("rotateMix", 1), + valueMap.getFloat("translateMix", 1), valueMap.getFloat("scaleMix", 1), valueMap.getFloat("shearMix", 1)); + readCurve(timeline, frameIndex, valueMap); + frameIndex++; + } + timelines.add(timeline); + duration = Math.max(duration, timeline.getFrames()[timeline.getFrameCount() * 5 - 5]); + } + // FFD timelines. for (JsonValue ffdMap = map.getChild("ffd"); ffdMap != null; ffdMap = ffdMap.next) { Skin skin = skeletonData.findSkin(ffdMap.name); diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonMeshRenderer.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonMeshRenderer.java index 403f5cea6..9033b90c8 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonMeshRenderer.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonMeshRenderer.java @@ -84,6 +84,7 @@ public class SkeletonMeshRenderer extends SkeletonRenderer { attachmentSkeleton.setPosition(skeleton.getX() + bone.getWorldX(), skeleton.getY() + bone.getWorldY()); // rootBone.setScaleX(1 + bone.getWorldScaleX() - oldScaleX); // rootBone.setScaleY(1 + bone.getWorldScaleY() - oldScaleY); + // Set shear. rootBone.setRotation(oldRotation + bone.getWorldRotationX()); attachmentSkeleton.updateWorldTransform(); diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java index 2f3aba1a6..eee9055b3 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java @@ -78,6 +78,7 @@ public class SkeletonRenderer { attachmentSkeleton.setPosition(skeleton.getX() + bone.getWorldX(), skeleton.getY() + bone.getWorldY()); // rootBone.setScaleX(1 + bone.getWorldScaleX() - oldScaleX); // rootBone.setScaleY(1 + bone.getWorldScaleY() - oldScaleY); + // Set shear. rootBone.setRotation(oldRotation + bone.getWorldRotationX()); attachmentSkeleton.updateWorldTransform(); diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraint.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraint.java index 2381d551b..65a796eeb 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraint.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraint.java @@ -1,19 +1,25 @@ package com.esotericsoftware.spine; +import static com.badlogic.gdx.math.MathUtils.*; + import com.badlogic.gdx.math.Vector2; public class TransformConstraint implements Updatable { final TransformConstraintData data; Bone bone, target; - float translateMix, x, y; + float rotateMix, translateMix, scaleMix, shearMix; + float offsetRotation, offsetX, offsetY, offsetScaleX, offsetScaleY, offsetShearY; final Vector2 temp = new Vector2(); public TransformConstraint (TransformConstraintData data, Skeleton skeleton) { this.data = data; translateMix = data.translateMix; - x = data.x; - y = data.y; + rotateMix = data.rotateMix; + scaleMix = data.scaleMix; + shearMix = data.shearMix; + offsetX = data.offsetX; + offsetY = data.offsetY; if (skeleton != null) { bone = skeleton.findBone(data.bone.name); @@ -27,8 +33,11 @@ public class TransformConstraint implements Updatable { bone = skeleton.bones.get(constraint.bone.skeleton.bones.indexOf(constraint.bone, true)); target = skeleton.bones.get(constraint.target.skeleton.bones.indexOf(constraint.target, true)); translateMix = constraint.translateMix; - x = constraint.x; - y = constraint.y; + rotateMix = constraint.rotateMix; + scaleMix = constraint.scaleMix; + shearMix = constraint.shearMix; + offsetX = constraint.offsetX; + offsetY = constraint.offsetY; } public void apply () { @@ -36,11 +45,53 @@ public class TransformConstraint implements Updatable { } 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 = atan2(target.c, target.a) - atan2(c, a) + offsetRotation * degRad; + if (r > PI) + r -= PI2; + else if (r < -PI) r += PI2; + r *= rotateMix; + float cos = cos(r), sin = 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) * scaleMix) / bs : 0) + offsetScaleX; + 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) * scaleMix) / bs : 0) + offsetScaleY; + bone.b *= s; + bone.d *= s; + } + + if (shearMix > 0) { + float b = bone.b, d = bone.d; + float by = atan2(d, b); + float r = (atan2(target.d, target.b) - atan2(target.c, target.a)) - (by - atan2(bone.c, bone.a)); + if (r > PI) + r -= PI2; + else if (r < -PI) r += PI2; + r = by + r * shearMix; + float s = (float)Math.sqrt(b * b + d * d); + bone.b = cos(r + offsetShearY * degRad) * s; + bone.d = sin(r + offsetShearY * degRad) * s; + } + float translateMix = this.translateMix; if (translateMix > 0) { Vector2 temp = this.temp; - target.localToWorld(temp.set(x, y)); - Bone bone = this.bone; + target.localToWorld(temp.set(offsetX, offsetY)); bone.worldX += (temp.x - bone.worldX) * translateMix; bone.worldY += (temp.y - bone.worldY) * translateMix; } @@ -62,6 +113,14 @@ public class TransformConstraint implements Updatable { this.target = target; } + public float getRotateMix () { + return rotateMix; + } + + public void setRotateMix (float rotateMix) { + this.rotateMix = rotateMix; + } + public float getTranslateMix () { return translateMix; } @@ -70,6 +129,70 @@ public class TransformConstraint implements Updatable { this.translateMix = translateMix; } + public float getScaleMix () { + return scaleMix; + } + + public void setScaleMix (float scaleMix) { + this.scaleMix = scaleMix; + } + + public float getShearMix () { + return shearMix; + } + + public void setShearMix (float shearMix) { + this.shearMix = shearMix; + } + + public float getOffsetRotation () { + return offsetRotation; + } + + public void setOffsetRotation (float offsetRotation) { + this.offsetRotation = offsetRotation; + } + + public float getOffsetX () { + return offsetX; + } + + public void setOffsetX (float offsetX) { + this.offsetX = offsetX; + } + + public float getOffsetY () { + return offsetY; + } + + public void setOffsetY (float offsetY) { + this.offsetY = offsetY; + } + + public float getOffsetScaleX () { + return offsetScaleX; + } + + public void setOffsetScaleX (float offsetScaleX) { + this.offsetScaleX = offsetScaleX; + } + + public float getOffsetScaleY () { + return offsetScaleY; + } + + public void setOffsetScaleY (float offsetScaleY) { + this.offsetScaleY = offsetScaleY; + } + + public float getOffsetShearY () { + return offsetShearY; + } + + public void setOffsetShearY (float offsetShearY) { + this.offsetShearY = offsetShearY; + } + public TransformConstraintData getData () { return data; } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraintData.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraintData.java index 53a140279..20d652603 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraintData.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraintData.java @@ -4,8 +4,8 @@ package com.esotericsoftware.spine; public class TransformConstraintData { final String name; BoneData bone, target; - float translateMix; - float x, y; + float rotateMix, translateMix, scaleMix, shearMix; + float offsetRotation, offsetX, offsetY, offsetScaleX, offsetScaleY, offsetShearY; public TransformConstraintData (String name) { this.name = name; @@ -31,6 +31,14 @@ public class TransformConstraintData { this.target = target; } + public float getRotateMix () { + return rotateMix; + } + + public void setRotateMix (float rotateMix) { + this.rotateMix = rotateMix; + } + public float getTranslateMix () { return translateMix; } @@ -39,20 +47,68 @@ public class TransformConstraintData { this.translateMix = translateMix; } - public float getX () { - return x; + public float getScaleMix () { + return scaleMix; } - public void setX (float x) { - this.x = x; + public void setScaleMix (float scaleMix) { + this.scaleMix = scaleMix; } - public float getY () { - return y; + public float getShearMix () { + return shearMix; } - public void setY (float y) { - this.y = y; + public void setShearMix (float shearMix) { + this.shearMix = shearMix; + } + + public float getOffsetRotation () { + return offsetRotation; + } + + public void setOffsetRotation (float offsetRotation) { + this.offsetRotation = offsetRotation; + } + + public float getOffsetX () { + return offsetX; + } + + public void setOffsetX (float offsetX) { + this.offsetX = offsetX; + } + + public float getOffsetY () { + return offsetY; + } + + public void setOffsetY (float offsetY) { + this.offsetY = offsetY; + } + + public float getOffsetScaleX () { + return offsetScaleX; + } + + public void setOffsetScaleX (float offsetScaleX) { + this.offsetScaleX = offsetScaleX; + } + + public float getOffsetScaleY () { + return offsetScaleY; + } + + public void setOffsetScaleY (float offsetScaleY) { + this.offsetScaleY = offsetScaleY; + } + + public float getOffsetShearY () { + return offsetShearY; + } + + public void setOffsetShearY (float offsetShearY) { + this.offsetShearY = offsetShearY; } public String toString () { diff --git a/spine-love/README.md b/spine-love/README.md index a725ae6bf..052613780 100644 --- a/spine-love/README.md +++ b/spine-love/README.md @@ -10,7 +10,7 @@ The Spine Runtimes are developed with the intent to be used with data exported f ## Spine version -spine-love works with data exported from Spine 2.1.27. Updating spine-love to [v3.0](https://trello.com/c/tF8UykBM/72-update-runtimes-to-support-v3-0-skewing-scale) and [v3.1](https://trello.com/c/bERJAFEq/73-update-runtimes-to-support-v3-1-linked-meshes) is in progress. +spine-love works with data exported from Spine 2.1.27. Updating spine-love to [v3.0](https://trello.com/c/tF8UykBM/72-update-runtimes-to-support-v3-0-skewing-scale), [v3.1](https://trello.com/c/bERJAFEq/73-update-runtimes-to-support-v3-1-linked-meshes), and [v3.2](https://trello.com/c/k7KtGdPW/76-update-runtimes-to-support-v3-2-shearing) is in progress. spine-love supports all Spine features except for rendering meshes. diff --git a/spine-lua/README.md b/spine-lua/README.md index b43c97b87..2cd62c597 100644 --- a/spine-lua/README.md +++ b/spine-lua/README.md @@ -10,7 +10,7 @@ The Spine Runtimes are developed with the intent to be used with data exported f ## Spine version -spine-lua works with data exported from Spine 2.1.27. Updating spine-lua to [v3.0](https://trello.com/c/tF8UykBM/72-update-runtimes-to-support-v3-0-skewing-scale) and [v3.1](https://trello.com/c/bERJAFEq/73-update-runtimes-to-support-v3-1-linked-meshes) is in progress. +spine-lua works with data exported from Spine 2.1.27. Updating spine-lua to [v3.0](https://trello.com/c/tF8UykBM/72-update-runtimes-to-support-v3-0-skewing-scale), [v3.1](https://trello.com/c/bERJAFEq/73-update-runtimes-to-support-v3-1-linked-meshes), and [v3.2](https://trello.com/c/k7KtGdPW/76-update-runtimes-to-support-v3-2-shearing) is in progress. spine-lua supports all Spine features. diff --git a/spine-monogame/README.md b/spine-monogame/README.md index debccbf07..5d05837c9 100644 --- a/spine-monogame/README.md +++ b/spine-monogame/README.md @@ -10,7 +10,7 @@ The Spine Runtimes are developed with the intent to be used with data exported f ## Spine version -spine-monogame works with data exported from the latest version of Spine. +spine-monogame works with data exported from Spine 3.1.08. Updating spine-monogame to [v3.2](https://trello.com/c/k7KtGdPW/76-update-runtimes-to-support-v3-2-shearing) is in progress. spine-monogame supports all Spine features. diff --git a/spine-sfml/README.md b/spine-sfml/README.md index 395010cd0..9c73517f7 100644 --- a/spine-sfml/README.md +++ b/spine-sfml/README.md @@ -10,7 +10,7 @@ The Spine Runtimes are developed with the intent to be used with data exported f ## Spine version -spine-sfml works with data exported from the latest version of Spine. +spine-sfml works with data exported from Spine 3.1.08. Updating spine-sfml to [v3.2](https://trello.com/c/k7KtGdPW/76-update-runtimes-to-support-v3-2-shearing) is in progress. spine-sfml supports all Spine features. diff --git a/spine-starling/README.md b/spine-starling/README.md index fccbc8469..d48df26f8 100644 --- a/spine-starling/README.md +++ b/spine-starling/README.md @@ -10,7 +10,7 @@ The Spine Runtimes are developed with the intent to be used with data exported f ## Spine version -spine-starling works with data exported from the latest version of Spine. +spine-starling works with data exported from Spine 3.1.08. Updating spine-starling to [v3.2](https://trello.com/c/k7KtGdPW/76-update-runtimes-to-support-v3-2-shearing) is in progress. spine-starling supports all Spine features. diff --git a/spine-threejs/README.md b/spine-threejs/README.md index de48d2f0b..381c88a1d 100644 --- a/spine-threejs/README.md +++ b/spine-threejs/README.md @@ -10,7 +10,7 @@ The Spine Runtimes are developed with the intent to be used with data exported f ## Spine version -spine-threejs works with data exported from the latest version of Spine. +spine-threejs works with data exported from Spine 3.1.08. Updating spine-threejs to [v3.2](https://trello.com/c/k7KtGdPW/76-update-runtimes-to-support-v3-2-shearing) is in progress. spine-threejs supports all Spine features except for rendering meshes. diff --git a/spine-turbulenz/README.md b/spine-turbulenz/README.md index 10d101133..5fc79577f 100644 --- a/spine-turbulenz/README.md +++ b/spine-turbulenz/README.md @@ -10,7 +10,7 @@ The Spine Runtimes are developed with the intent to be used with data exported f ## Spine version -spine-turbulenz works with data exported from the latest version of Spine. +spine-turbulenz works with data exported from Spine 3.1.08. Updating spine-turbulenz to [v3.2](https://trello.com/c/k7KtGdPW/76-update-runtimes-to-support-v3-2-shearing) is in progress. spine-turbulenz supports all Spine features except for rendering meshes. diff --git a/spine-unity/README.md b/spine-unity/README.md index 465178b58..b52cff4b6 100644 --- a/spine-unity/README.md +++ b/spine-unity/README.md @@ -14,7 +14,7 @@ The Spine Runtimes are developed with the intent to be used with data exported f ## Spine version -spine-unity works with data exported from the latest version of Spine. +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 supports all Spine features.