mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-04 14:24:53 +08:00
spine-libgdx updated to 3.2.00.
* Added transform constraint rotate, scale, and shear offsets and mixes. * Added TransformConstraintTimeline. * Added bone shearing. * Added ShearTimeline. * Changed Skeleton#updateCache. * Changed JSON and binary formats. Some binary format fields and enums were rearranged for consistency -- sorry, but it's better for the long term. Docs for both are up to date. http://esotericsoftware.com/spine-json-format http://esotericsoftware.com/spine-binary-format
This commit is contained in:
parent
506dc49d8a
commit
8d7f761311
@ -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.
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
|
||||
@ -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<Event> 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<Slot> drawOrder = skeleton.drawOrder;
|
||||
Array<Slot> 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<Event> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 () {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<TransformConstraint> 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -84,6 +84,7 @@ public class SkeletonMeshRenderer extends SkeletonRenderer<PolygonSpriteBatch> {
|
||||
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();
|
||||
|
||||
|
||||
@ -78,6 +78,7 @@ public class SkeletonRenderer<T extends Batch> {
|
||||
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();
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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 () {
|
||||
|
||||
@ -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.
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user