From 549e9ae67bc33889c329baf82e0d9214d735c15f Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Tue, 10 Nov 2020 17:43:47 -0800 Subject: [PATCH] [libgdx] Added separate keying for translateX/Y, scaleX/Y, shearX/Y, and colorRGB/A. The first editor version able to export data in this format is 4.0.24-beta. EsotericSoftware/spine-editor#26 EsotericSoftware/spine-editor#27 --- .../com/esotericsoftware/spine/Animation.java | 654 +++++++++++++++++- .../spine/SkeletonBinary.java | 139 +++- .../esotericsoftware/spine/SkeletonJson.java | 129 +++- 3 files changed, 870 insertions(+), 52 deletions(-) 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 07da9f958..d9902b99a 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java @@ -187,9 +187,9 @@ public class Animation { in, out } - static enum Property { + static private enum Property { rotate, translateX, translateY, scaleX, scaleY, shearX, shearY, // - rgba, rgb2, // + rgb, alpha, rgb2, // attachment, deform, // event, drawOrder, // ikConstraint, transformConstraint, // @@ -560,6 +560,98 @@ public class Animation { } } + /** Changes a bone's local {@link Bone#getX()}. */ + static public class TranslateXTimeline extends CurveTimeline1 implements BoneTimeline { + final int boneIndex; + + public TranslateXTimeline (int frameCount, int bezierCount, int boneIndex) { + super(frameCount, bezierCount, Property.translateX.ordinal() + "|" + boneIndex); + this.boneIndex = boneIndex; + } + + public int getBoneIndex () { + return boneIndex; + } + + public void apply (Skeleton skeleton, float lastTime, float time, @Null Array events, float alpha, MixBlend blend, + MixDirection direction) { + + Bone bone = skeleton.bones.get(boneIndex); + if (!bone.active) return; + + float[] frames = this.frames; + if (time < frames[0]) { // Time is before first frame. + switch (blend) { + case setup: + bone.x = bone.data.x; + return; + case first: + bone.x += (bone.data.x - bone.x) * alpha; + } + return; + } + + float x = getCurveValue(time); + switch (blend) { + case setup: + bone.x = bone.data.x + x * alpha; + break; + case first: + case replace: + bone.x += (bone.data.x + x - bone.x) * alpha; + break; + case add: + bone.x += x * alpha; + } + } + } + + /** Changes a bone's local {@link Bone#getY()}. */ + static public class TranslateYTimeline extends CurveTimeline1 implements BoneTimeline { + final int boneIndex; + + public TranslateYTimeline (int frameCount, int bezierCount, int boneIndex) { + super(frameCount, bezierCount, Property.translateY.ordinal() + "|" + boneIndex); + this.boneIndex = boneIndex; + } + + public int getBoneIndex () { + return boneIndex; + } + + public void apply (Skeleton skeleton, float lastTime, float time, @Null Array events, float alpha, MixBlend blend, + MixDirection direction) { + + Bone bone = skeleton.bones.get(boneIndex); + if (!bone.active) return; + + float[] frames = this.frames; + if (time < frames[0]) { // Time is before first frame. + switch (blend) { + case setup: + bone.y = bone.data.y; + return; + case first: + bone.y += (bone.data.y - bone.y) * alpha; + } + return; + } + + float y = getCurveValue(time); + switch (blend) { + case setup: + bone.y = bone.data.y + y * alpha; + break; + case first: + case replace: + bone.y += (bone.data.y + y - bone.y) * alpha; + break; + case add: + bone.y += y * alpha; + } + } + } + /** Changes a bone's local {@link Bone#getScaleX()} and {@link Bone#getScaleY()}. */ static public class ScaleTimeline extends CurveTimeline2 implements BoneTimeline { final int boneIndex; @@ -675,6 +767,156 @@ public class Animation { } } + /** Changes a bone's local {@link Bone#getScaleX()}. */ + static public class ScaleXTimeline extends CurveTimeline1 implements BoneTimeline { + final int boneIndex; + + public ScaleXTimeline (int frameCount, int bezierCount, int boneIndex) { + super(frameCount, bezierCount, Property.scaleX.ordinal() + "|" + boneIndex); + this.boneIndex = boneIndex; + } + + public int getBoneIndex () { + return boneIndex; + } + + public void apply (Skeleton skeleton, float lastTime, float time, @Null Array events, float alpha, MixBlend blend, + MixDirection direction) { + + Bone bone = skeleton.bones.get(boneIndex); + if (!bone.active) return; + + float[] frames = this.frames; + if (time < frames[0]) { // Time is before first frame. + switch (blend) { + case setup: + bone.scaleX = bone.data.scaleX; + return; + case first: + bone.scaleX += (bone.data.scaleX - bone.scaleX) * alpha; + } + return; + } + + float x = getCurveValue(time) * bone.data.scaleX; + if (alpha == 1) { + if (blend == add) + bone.scaleX += x - bone.data.scaleX; + else + bone.scaleX = x; + } else { + // Mixing out uses sign of setup or current pose, else use sign of key. + float bx; + if (direction == out) { + switch (blend) { + case setup: + bx = bone.data.scaleX; + bone.scaleX = bx + (Math.abs(x) * Math.signum(bx) - bx) * alpha; + break; + case first: + case replace: + bx = bone.scaleX; + bone.scaleX = bx + (Math.abs(x) * Math.signum(bx) - bx) * alpha; + break; + case add: + bx = bone.scaleX; + bone.scaleX = bx + (Math.abs(x) * Math.signum(bx) - bone.data.scaleX) * alpha; + } + } else { + switch (blend) { + case setup: + bx = Math.abs(bone.data.scaleX) * Math.signum(x); + bone.scaleX = bx + (x - bx) * alpha; + break; + case first: + case replace: + bx = Math.abs(bone.scaleX) * Math.signum(x); + bone.scaleX = bx + (x - bx) * alpha; + break; + case add: + bx = Math.signum(x); + bone.scaleX = Math.abs(bone.scaleX) * bx + (x - Math.abs(bone.data.scaleX) * bx) * alpha; + } + } + } + } + } + + /** Changes a bone's local {@link Bone#getScaleY()}. */ + static public class ScaleYTimeline extends CurveTimeline1 implements BoneTimeline { + final int boneIndex; + + public ScaleYTimeline (int frameCount, int bezierCount, int boneIndex) { + super(frameCount, bezierCount, Property.scaleY.ordinal() + "|" + boneIndex); + this.boneIndex = boneIndex; + } + + public int getBoneIndex () { + return boneIndex; + } + + public void apply (Skeleton skeleton, float lastTime, float time, @Null Array events, float alpha, MixBlend blend, + MixDirection direction) { + + Bone bone = skeleton.bones.get(boneIndex); + if (!bone.active) return; + + float[] frames = this.frames; + if (time < frames[0]) { // Time is before first frame. + switch (blend) { + case setup: + bone.scaleY = bone.data.scaleY; + return; + case first: + bone.scaleY += (bone.data.scaleY - bone.scaleY) * alpha; + } + return; + } + + float y = getCurveValue(time) * bone.data.scaleY; + if (alpha == 1) { + if (blend == add) + bone.scaleY += y - bone.data.scaleY; + else + bone.scaleY = y; + } else { + // Mixing out uses sign of setup or current pose, else use sign of key. + float by; + if (direction == out) { + switch (blend) { + case setup: + by = bone.data.scaleY; + bone.scaleY = by + (Math.abs(y) * Math.signum(by) - by) * alpha; + break; + case first: + case replace: + by = bone.scaleY; + bone.scaleY = by + (Math.abs(y) * Math.signum(by) - by) * alpha; + break; + case add: + by = bone.scaleY; + bone.scaleY = by + (Math.abs(y) * Math.signum(by) - bone.data.scaleY) * alpha; + } + } else { + switch (blend) { + case setup: + by = Math.abs(bone.data.scaleY) * Math.signum(y); + bone.scaleY = by + (y - by) * alpha; + break; + case first: + case replace: + by = Math.abs(bone.scaleY) * Math.signum(y); + bone.scaleY = by + (y - by) * alpha; + break; + case add: + by = Math.signum(y); + bone.scaleY = Math.abs(bone.scaleY) * by + (y - Math.abs(bone.data.scaleY) * by) * alpha; + } + } + } + } + } + /** Changes a bone's local {@link Bone#getShearX()} and {@link Bone#getShearY()}. */ static public class ShearTimeline extends CurveTimeline2 implements BoneTimeline { final int boneIndex; @@ -747,16 +989,109 @@ public class Animation { } } + /** Changes a bone's local {@link Bone#getShearX()}. */ + static public class ShearXTimeline extends CurveTimeline1 implements BoneTimeline { + final int boneIndex; + + public ShearXTimeline (int frameCount, int bezierCount, int boneIndex) { + super(frameCount, bezierCount, Property.shearX.ordinal() + "|" + boneIndex); + this.boneIndex = boneIndex; + } + + public int getBoneIndex () { + return boneIndex; + } + + public void apply (Skeleton skeleton, float lastTime, float time, @Null Array events, float alpha, MixBlend blend, + MixDirection direction) { + + Bone bone = skeleton.bones.get(boneIndex); + if (!bone.active) return; + + float[] frames = this.frames; + if (time < frames[0]) { // Time is before first frame. + switch (blend) { + case setup: + bone.shearX = bone.data.shearX; + return; + case first: + bone.shearX += (bone.data.shearX - bone.shearX) * alpha; + } + return; + } + + float x = getCurveValue(time); + switch (blend) { + case setup: + bone.shearX = bone.data.shearX + x * alpha; + break; + case first: + case replace: + bone.shearX += (bone.data.shearX + x - bone.shearX) * alpha; + break; + case add: + bone.shearX += x * alpha; + } + } + } + + /** Changes a bone's local {@link Bone#getShearY()}. */ + static public class ShearYTimeline extends CurveTimeline1 implements BoneTimeline { + final int boneIndex; + + public ShearYTimeline (int frameCount, int bezierCount, int boneIndex) { + super(frameCount, bezierCount, Property.shearY.ordinal() + "|" + boneIndex); + this.boneIndex = boneIndex; + } + + public int getBoneIndex () { + return boneIndex; + } + + public void apply (Skeleton skeleton, float lastTime, float time, @Null Array events, float alpha, MixBlend blend, + MixDirection direction) { + + Bone bone = skeleton.bones.get(boneIndex); + if (!bone.active) return; + + float[] frames = this.frames; + if (time < frames[0]) { // Time is before first frame. + switch (blend) { + case setup: + bone.shearY = bone.data.shearY; + return; + case first: + bone.shearY += (bone.data.shearY - bone.shearY) * alpha; + } + return; + } + + float y = getCurveValue(time); + switch (blend) { + case setup: + bone.shearY = bone.data.shearY + y * alpha; + break; + case first: + case replace: + bone.shearY += (bone.data.shearY + y - bone.shearY) * alpha; + break; + case add: + bone.shearY += y * alpha; + } + } + } + /** Changes a slot's {@link Slot#getColor()}. */ - static public class ColorTimeline extends CurveTimeline implements SlotTimeline { + static public class RGBATimeline extends CurveTimeline implements SlotTimeline { static public final int ENTRIES = 5; static private final int R = 1, G = 2, B = 3, A = 4; final int slotIndex; - public ColorTimeline (int frameCount, int bezierCount, int slotIndex) { + public RGBATimeline (int frameCount, int bezierCount, int slotIndex) { super(frameCount, bezierCount, // - Property.rgba.ordinal() + "|" + slotIndex); + Property.rgb.ordinal() + "|" + slotIndex, // + Property.alpha.ordinal() + "|" + slotIndex); this.slotIndex = slotIndex; } @@ -788,12 +1123,12 @@ public class Animation { float[] frames = this.frames; if (time < frames[0]) { // Time is before first frame. + Color color = slot.color, setup = slot.data.color; switch (blend) { case setup: - slot.color.set(slot.data.color); + color.set(setup); return; case first: - Color color = slot.color, setup = slot.data.color; color.add((setup.r - color.r) * alpha, (setup.g - color.g) * alpha, (setup.b - color.b) * alpha, (setup.a - color.a) * alpha); } @@ -828,26 +1163,170 @@ public class Animation { a = getBezierValue(time, i, A, curveType + BEZIER_SIZE * 3 - BEZIER); } + Color color = slot.color; if (alpha == 1) slot.color.set(r, g, b, a); else { - Color color = slot.color; if (blend == setup) color.set(slot.data.color); color.add((r - color.r) * alpha, (g - color.g) * alpha, (b - color.b) * alpha, (a - color.a) * alpha); } } } + /** Changes the RGB for a slot's {@link Slot#getColor()}. */ + static public class RGBTimeline extends CurveTimeline implements SlotTimeline { + static public final int ENTRIES = 4; + static private final int R = 1, G = 2, B = 3; + + final int slotIndex; + + public RGBTimeline (int frameCount, int bezierCount, int slotIndex) { + super(frameCount, bezierCount, Property.rgb.ordinal() + "|" + slotIndex); + this.slotIndex = slotIndex; + } + + public int getFrameEntries () { + return ENTRIES; + } + + public int getSlotIndex () { + return slotIndex; + } + + /** Sets the time and color for the specified frame. + * @param frame Between 0 and frameCount, inclusive. + * @param time The frame time in seconds. */ + public void setFrame (int frame, float time, float r, float g, float b) { + frame <<= 2; + frames[frame] = time; + frames[frame + R] = r; + frames[frame + G] = g; + frames[frame + B] = b; + } + + public void apply (Skeleton skeleton, float lastTime, float time, @Null Array events, float alpha, MixBlend blend, + MixDirection direction) { + + Slot slot = skeleton.slots.get(slotIndex); + if (!slot.bone.active) return; + + float[] frames = this.frames; + if (time < frames[0]) { // Time is before first frame. + Color color = slot.color, setup = slot.data.color; + switch (blend) { + case setup: + color.r = setup.r; + color.g = setup.g; + color.b = setup.b; + return; + case first: + color.r += (setup.r - color.r) * alpha; + color.g += (setup.g - color.g) * alpha; + color.b += (setup.b - color.b) * alpha; + } + return; + } + + float r, g, b; + int i = search(frames, time, ENTRIES), curveType = (int)curves[i >> 2]; + switch (curveType) { + case LINEAR: + float before = frames[i]; + r = frames[i + R]; + g = frames[i + G]; + b = frames[i + B]; + float t = (time - before) / (frames[i + ENTRIES] - before); + r += (frames[i + ENTRIES + R] - r) * t; + g += (frames[i + ENTRIES + G] - g) * t; + b += (frames[i + ENTRIES + B] - b) * t; + break; + case STEPPED: + r = frames[i + R]; + g = frames[i + G]; + b = frames[i + B]; + break; + default: + r = getBezierValue(time, i, R, curveType - BEZIER); + g = getBezierValue(time, i, G, curveType + BEZIER_SIZE - BEZIER); + b = getBezierValue(time, i, B, curveType + BEZIER_SIZE * 2 - BEZIER); + } + + Color color = slot.color; + if (alpha == 1) { + color.r = r; + color.g = g; + color.b = b; + } else { + if (blend == setup) { + Color setup = slot.data.color; + color.r = setup.r; + color.g = setup.g; + color.b = setup.b; + } + color.r += (r - color.r) * alpha; + color.g += (g - color.g) * alpha; + color.b += (b - color.b) * alpha; + } + } + } + + /** Changes the alpha for a slot's {@link Slot#getColor()}. */ + static public class AlphaTimeline extends CurveTimeline1 implements SlotTimeline { + final int slotIndex; + + public AlphaTimeline (int frameCount, int bezierCount, int slotIndex) { + super(frameCount, bezierCount, Property.alpha.ordinal() + "|" + slotIndex); + this.slotIndex = slotIndex; + } + + public int getSlotIndex () { + return slotIndex; + } + + public void apply (Skeleton skeleton, float lastTime, float time, @Null Array events, float alpha, MixBlend blend, + MixDirection direction) { + + Slot slot = skeleton.slots.get(slotIndex); + if (!slot.bone.active) return; + + float[] frames = this.frames; + if (time < frames[0]) { // Time is before first frame. + Color color = slot.color, setup = slot.data.color; + switch (blend) { + case setup: + color.r = setup.r; + color.g = setup.g; + color.b = setup.b; + return; + case first: + color.r += (setup.r - color.r) * alpha; + color.g += (setup.g - color.g) * alpha; + color.b += (setup.b - color.b) * alpha; + } + return; + } + + float a = getCurveValue(time); + if (alpha == 1) + slot.color.a = a; + else { + if (blend == setup) slot.color.a = slot.data.color.a; + slot.color.a += (a - slot.color.a) * alpha; + } + } + } + /** Changes a slot's {@link Slot#getColor()} and {@link Slot#getDarkColor()} for two color tinting. */ - static public class TwoColorTimeline extends CurveTimeline implements SlotTimeline { + static public class RGBA2Timeline extends CurveTimeline implements SlotTimeline { static public final int ENTRIES = 8; static private final int R = 1, G = 2, B = 3, A = 4, R2 = 5, G2 = 6, B2 = 7; final int slotIndex; - public TwoColorTimeline (int frameCount, int bezierCount, int slotIndex) { + public RGBA2Timeline (int frameCount, int bezierCount, int slotIndex) { super(frameCount, bezierCount, // - Property.rgba.ordinal() + "|" + slotIndex, // + Property.rgb.ordinal() + "|" + slotIndex, // + Property.alpha.ordinal() + "|" + slotIndex, // Property.rgb2.ordinal() + "|" + slotIndex); this.slotIndex = slotIndex; } @@ -885,16 +1364,20 @@ public class Animation { float[] frames = this.frames; if (time < frames[0]) { // Time is before first frame. + Color light = slot.color, dark = slot.darkColor, setupLight = slot.data.color, setupDark = slot.data.darkColor; switch (blend) { case setup: - slot.color.set(slot.data.color); - slot.darkColor.set(slot.data.darkColor); + light.set(setupLight); + dark.r = setupDark.r; + dark.g = setupDark.g; + dark.b = setupDark.b; return; case first: - Color light = slot.color, dark = slot.darkColor, setupLight = slot.data.color, setupDark = slot.data.darkColor; light.add((setupLight.r - light.r) * alpha, (setupLight.g - light.g) * alpha, (setupLight.b - light.b) * alpha, (setupLight.a - light.a) * alpha); - dark.add((setupDark.r - dark.r) * alpha, (setupDark.g - dark.g) * alpha, (setupDark.b - dark.b) * alpha, 0); + dark.r += (setupDark.r - dark.r) * alpha; + dark.g += (setupDark.g - dark.g) * alpha; + dark.b += (setupDark.b - dark.b) * alpha; } return; } @@ -939,17 +1422,152 @@ public class Animation { b2 = getBezierValue(time, i, B2, curveType + BEZIER_SIZE * 6 - BEZIER); } + Color light = slot.color, dark = slot.darkColor; if (alpha == 1) { slot.color.set(r, g, b, a); - slot.darkColor.set(r2, g2, b2, 1); + dark.r = r2; + dark.g = g2; + dark.b = b2; } else { - Color light = slot.color, dark = slot.darkColor; if (blend == setup) { light.set(slot.data.color); dark.set(slot.data.darkColor); } light.add((r - light.r) * alpha, (g - light.g) * alpha, (b - light.b) * alpha, (a - light.a) * alpha); - dark.add((r2 - dark.r) * alpha, (g2 - dark.g) * alpha, (b2 - dark.b) * alpha, 0); + dark.r += (r2 - dark.r) * alpha; + dark.g += (g2 - dark.g) * alpha; + dark.b += (b2 - dark.b) * alpha; + } + } + } + + /** Changes the RGB for a slot's {@link Slot#getColor()} and {@link Slot#getDarkColor()} for two color tinting. */ + static public class RGB2Timeline extends CurveTimeline implements SlotTimeline { + static public final int ENTRIES = 7; + static private final int R = 1, G = 2, B = 3, R2 = 5, G2 = 6, B2 = 7; + + final int slotIndex; + + public RGB2Timeline (int frameCount, int bezierCount, int slotIndex) { + super(frameCount, bezierCount, // + Property.rgb.ordinal() + "|" + slotIndex, // + Property.rgb2.ordinal() + "|" + slotIndex); + this.slotIndex = slotIndex; + } + + public int getFrameEntries () { + return ENTRIES; + } + + /** The index of the slot in {@link Skeleton#getSlots()} that will be changed when this timeline is applied. The + * {@link Slot#getDarkColor()} must not be null. */ + public int getSlotIndex () { + return slotIndex; + } + + /** Sets the time, light color, and dark color for the specified frame. + * @param frame Between 0 and frameCount, inclusive. + * @param time The frame time in seconds. */ + public void setFrame (int frame, float time, float r, float g, float b, float r2, float g2, float b2) { + frame *= ENTRIES; + frames[frame] = time; + frames[frame + R] = r; + frames[frame + G] = g; + frames[frame + B] = b; + frames[frame + R2] = r2; + frames[frame + G2] = g2; + frames[frame + B2] = b2; + } + + public void apply (Skeleton skeleton, float lastTime, float time, @Null Array events, float alpha, MixBlend blend, + MixDirection direction) { + + Slot slot = skeleton.slots.get(slotIndex); + if (!slot.bone.active) return; + + float[] frames = this.frames; + if (time < frames[0]) { // Time is before first frame. + Color light = slot.color, dark = slot.darkColor, setupLight = slot.data.color, setupDark = slot.data.darkColor; + switch (blend) { + case setup: + light.r = setupLight.r; + light.g = setupLight.g; + light.b = setupLight.b; + dark.r = setupDark.r; + dark.g = setupDark.g; + dark.b = setupDark.b; + return; + case first: + light.r += (setupLight.r - light.r) * alpha; + light.g += (setupLight.g - light.g) * alpha; + light.b += (setupLight.b - light.b) * alpha; + dark.r += (setupDark.r - dark.r) * alpha; + dark.g += (setupDark.g - dark.g) * alpha; + dark.b += (setupDark.b - dark.b) * alpha; + } + return; + } + + float r, g, b, r2, g2, b2; + int i = search(frames, time, ENTRIES), curveType = (int)curves[i / ENTRIES]; + switch (curveType) { + case LINEAR: + float before = frames[i]; + r = frames[i + R]; + g = frames[i + G]; + b = frames[i + B]; + r2 = frames[i + R2]; + g2 = frames[i + G2]; + b2 = frames[i + B2]; + float t = (time - before) / (frames[i + ENTRIES] - before); + r += (frames[i + ENTRIES + R] - r) * t; + g += (frames[i + ENTRIES + G] - g) * t; + b += (frames[i + ENTRIES + B] - b) * t; + r2 += (frames[i + ENTRIES + R2] - r2) * t; + g2 += (frames[i + ENTRIES + G2] - g2) * t; + b2 += (frames[i + ENTRIES + B2] - b2) * t; + break; + case STEPPED: + r = frames[i + R]; + g = frames[i + G]; + b = frames[i + B]; + r2 = frames[i + R2]; + g2 = frames[i + G2]; + b2 = frames[i + B2]; + break; + default: + r = getBezierValue(time, i, R, curveType - BEZIER); + g = getBezierValue(time, i, G, curveType + BEZIER_SIZE - BEZIER); + b = getBezierValue(time, i, B, curveType + BEZIER_SIZE * 2 - BEZIER); + r2 = getBezierValue(time, i, R2, curveType + BEZIER_SIZE * 3 - BEZIER); + g2 = getBezierValue(time, i, G2, curveType + BEZIER_SIZE * 4 - BEZIER); + b2 = getBezierValue(time, i, B2, curveType + BEZIER_SIZE * 5 - BEZIER); + } + + Color light = slot.color, dark = slot.darkColor; + if (alpha == 1) { + light.r = r; + light.g = g; + light.b = b; + dark.r = r2; + dark.g = g2; + dark.b = b2; + } else { + if (blend == setup) { + Color setupLight = slot.data.color, setupDark = slot.data.darkColor; + light.r = setupLight.r; + light.g = setupLight.g; + light.b = setupLight.b; + dark.r = setupDark.r; + dark.g = setupDark.g; + dark.b = setupDark.b; + } + light.r += (r - light.r) * alpha; + light.g += (g - light.g) * alpha; + light.b += (b - light.b) * alpha; + dark.r += (r2 - dark.r) * alpha; + dark.g += (g2 - dark.g) * alpha; + dark.b += (b2 - dark.b) * alpha; } } } 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 073542e68..a6afd6a5d 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java @@ -42,8 +42,8 @@ import com.badlogic.gdx.utils.IntArray; import com.badlogic.gdx.utils.Null; import com.badlogic.gdx.utils.SerializationException; +import com.esotericsoftware.spine.Animation.AlphaTimeline; import com.esotericsoftware.spine.Animation.AttachmentTimeline; -import com.esotericsoftware.spine.Animation.ColorTimeline; import com.esotericsoftware.spine.Animation.CurveTimeline; import com.esotericsoftware.spine.Animation.CurveTimeline1; import com.esotericsoftware.spine.Animation.CurveTimeline2; @@ -54,13 +54,22 @@ import com.esotericsoftware.spine.Animation.IkConstraintTimeline; import com.esotericsoftware.spine.Animation.PathConstraintMixTimeline; import com.esotericsoftware.spine.Animation.PathConstraintPositionTimeline; import com.esotericsoftware.spine.Animation.PathConstraintSpacingTimeline; +import com.esotericsoftware.spine.Animation.RGB2Timeline; +import com.esotericsoftware.spine.Animation.RGBA2Timeline; +import com.esotericsoftware.spine.Animation.RGBATimeline; +import com.esotericsoftware.spine.Animation.RGBTimeline; import com.esotericsoftware.spine.Animation.RotateTimeline; import com.esotericsoftware.spine.Animation.ScaleTimeline; +import com.esotericsoftware.spine.Animation.ScaleXTimeline; +import com.esotericsoftware.spine.Animation.ScaleYTimeline; import com.esotericsoftware.spine.Animation.ShearTimeline; +import com.esotericsoftware.spine.Animation.ShearXTimeline; +import com.esotericsoftware.spine.Animation.ShearYTimeline; import com.esotericsoftware.spine.Animation.Timeline; import com.esotericsoftware.spine.Animation.TransformConstraintTimeline; import com.esotericsoftware.spine.Animation.TranslateTimeline; -import com.esotericsoftware.spine.Animation.TwoColorTimeline; +import com.esotericsoftware.spine.Animation.TranslateXTimeline; +import com.esotericsoftware.spine.Animation.TranslateYTimeline; import com.esotericsoftware.spine.BoneData.TransformMode; import com.esotericsoftware.spine.PathConstraintData.PositionMode; import com.esotericsoftware.spine.PathConstraintData.RotateMode; @@ -85,12 +94,21 @@ import com.esotericsoftware.spine.attachments.VertexAttachment; public class SkeletonBinary extends SkeletonLoader { static public final int BONE_ROTATE = 0; static public final int BONE_TRANSLATE = 1; - static public final int BONE_SCALE = 2; - static public final int BONE_SHEAR = 3; + static public final int BONE_TRANSLATEX = 2; + static public final int BONE_TRANSLATEY = 3; + static public final int BONE_SCALE = 4; + static public final int BONE_SCALEX = 5; + static public final int BONE_SCALEY = 6; + static public final int BONE_SHEAR = 7; + static public final int BONE_SHEARX = 8; + static public final int BONE_SHEARY = 9; static public final int SLOT_ATTACHMENT = 0; - static public final int SLOT_COLOR = 1; - static public final int SLOT_TWO_COLOR = 2; + static public final int SLOT_RGBA = 1; + static public final int SLOT_RGB = 2; + static public final int SLOT_ALPHA = 3; + static public final int SLOT_RGBA2 = 4; + static public final int SLOT_RGB2 = 5; static public final int PATH_POSITION = 0; static public final int PATH_SPACING = 1; @@ -493,7 +511,7 @@ public class SkeletonBinary extends SkeletonLoader { if (nonessential) Color.rgba8888ToColor(point.getColor(), color); return point; } - case clipping: { + case clipping: int endSlotIndex = input.readInt(true); int vertexCount = input.readInt(true); Vertices vertices = readVertices(input, vertexCount); @@ -508,7 +526,6 @@ public class SkeletonBinary extends SkeletonLoader { if (nonessential) Color.rgba8888ToColor(clip.getColor(), color); return clip; } - } return null; } @@ -574,8 +591,8 @@ public class SkeletonBinary extends SkeletonLoader { timelines.add(timeline); break; } - case SLOT_COLOR: { - ColorTimeline timeline = new ColorTimeline(frameCount, input.readInt(true), slotIndex); + case SLOT_RGBA: { + RGBATimeline timeline = new RGBATimeline(frameCount, input.readInt(true), slotIndex); float time = input.readFloat(); float r = input.read() / 255f, g = input.read() / 255f; float b = input.read() / 255f, a = input.read() / 255f; @@ -604,21 +621,48 @@ public class SkeletonBinary extends SkeletonLoader { timelines.add(timeline); break; } - case SLOT_TWO_COLOR: { - TwoColorTimeline timeline = new TwoColorTimeline(frameCount, input.readInt(true), slotIndex); + case SLOT_ALPHA: + timelines.add(readTimeline(input, new AlphaTimeline(frameCount, input.readInt(true), input.readInt(true)), 1)); + break; + case SLOT_RGB: { + RGBTimeline timeline = new RGBTimeline(frameCount, input.readInt(true), slotIndex); + float time = input.readFloat(); + float r = input.read() / 255f, g = input.read() / 255f, b = input.read() / 255f; + for (int frame = 0, bezier = 0;; frame++) { + timeline.setFrame(frame, time, r, g, b); + if (frame == frameLast) break; + float time2 = input.readFloat(); + float r2 = input.read() / 255f, g2 = input.read() / 255f, b2 = input.read() / 255f; + switch (input.readByte()) { + case CURVE_STEPPED: + timeline.setStepped(frame); + break; + case CURVE_BEZIER: + setBezier(input, timeline, bezier++, frame, 0, time, time2, r, r2, 1); + setBezier(input, timeline, bezier++, frame, 1, time, time2, g, g2, 1); + setBezier(input, timeline, bezier++, frame, 2, time, time2, b, b2, 1); + } + time = time2; + r = r2; + g = g2; + b = b2; + } + timelines.add(timeline); + break; + } + case SLOT_RGBA2: { + RGBA2Timeline timeline = new RGBA2Timeline(frameCount, input.readInt(true), slotIndex); float time = input.readFloat(); float r = input.read() / 255f, g = input.read() / 255f; float b = input.read() / 255f, a = input.read() / 255f; - float r2 = input.read() / 255f, g2 = input.read() / 255f; - float b2 = input.read() / 255f; + float r2 = input.read() / 255f, g2 = input.read() / 255f, b2 = input.read() / 255f; for (int frame = 0, bezier = 0;; frame++) { timeline.setFrame(frame, time, r, g, b, a, r2, g2, b2); if (frame == frameLast) break; float time2 = input.readFloat(); float nr = input.read() / 255f, ng = input.read() / 255f; float nb = input.read() / 255f, na = input.read() / 255f; - float nr2 = input.read() / 255f, ng2 = input.read() / 255f; - float nb2 = input.read() / 255f; + float nr2 = input.read() / 255f, ng2 = input.read() / 255f, nb2 = input.read() / 255f; switch (input.readByte()) { case CURVE_STEPPED: timeline.setStepped(frame); @@ -644,6 +688,39 @@ public class SkeletonBinary extends SkeletonLoader { timelines.add(timeline); break; } + case SLOT_RGB2: + RGB2Timeline timeline = new RGB2Timeline(frameCount, input.readInt(true), slotIndex); + float time = input.readFloat(); + float r = input.read() / 255f, g = input.read() / 255f, b = input.read() / 255f; + float r2 = input.read() / 255f, g2 = input.read() / 255f, b2 = input.read() / 255f; + for (int frame = 0, bezier = 0;; frame++) { + timeline.setFrame(frame, time, r, g, b, r2, g2, b2); + if (frame == frameLast) break; + float time2 = input.readFloat(); + float nr = input.read() / 255f, ng = input.read() / 255f, nb = input.read() / 255f; + float nr2 = input.read() / 255f, ng2 = input.read() / 255f, nb2 = input.read() / 255f; + switch (input.readByte()) { + case CURVE_STEPPED: + timeline.setStepped(frame); + break; + case CURVE_BEZIER: + setBezier(input, timeline, bezier++, frame, 0, time, time2, r, nr, 1); + setBezier(input, timeline, bezier++, frame, 1, time, time2, g, ng, 1); + setBezier(input, timeline, bezier++, frame, 2, time, time2, b, nb, 1); + setBezier(input, timeline, bezier++, frame, 4, time, time2, r2, nr2, 1); + setBezier(input, timeline, bezier++, frame, 5, time, time2, g2, ng2, 1); + setBezier(input, timeline, bezier++, frame, 6, time, time2, b2, nb2, 1); + } + time = time2; + r = nr; + g = ng; + b = nb; + r2 = nr2; + g2 = ng2; + b2 = nb2; + } + timelines.add(timeline); + break; } } } @@ -652,19 +729,37 @@ public class SkeletonBinary extends SkeletonLoader { for (int i = 0, n = input.readInt(true); i < n; i++) { int boneIndex = input.readInt(true); for (int ii = 0, nn = input.readInt(true); ii < nn; ii++) { - switch (input.readByte()) { + int type = input.readByte(), frameCount = input.readInt(true), bezierCount = input.readInt(true); + switch (type) { case BONE_ROTATE: - timelines.add(readTimeline(input, new RotateTimeline(input.readInt(true), input.readInt(true), boneIndex), 1)); + timelines.add(readTimeline(input, new RotateTimeline(frameCount, bezierCount, boneIndex), 1)); break; case BONE_TRANSLATE: - timelines - .add(readTimeline(input, new TranslateTimeline(input.readInt(true), input.readInt(true), boneIndex), scale)); + timelines.add(readTimeline(input, new TranslateTimeline(frameCount, bezierCount, boneIndex), scale)); + break; + case BONE_TRANSLATEX: + timelines.add(readTimeline(input, new TranslateXTimeline(frameCount, bezierCount, boneIndex), scale)); + break; + case BONE_TRANSLATEY: + timelines.add(readTimeline(input, new TranslateYTimeline(frameCount, bezierCount, boneIndex), scale)); break; case BONE_SCALE: - timelines.add(readTimeline(input, new ScaleTimeline(input.readInt(true), input.readInt(true), boneIndex), 1)); + timelines.add(readTimeline(input, new ScaleTimeline(frameCount, bezierCount, boneIndex), 1)); + break; + case BONE_SCALEX: + timelines.add(readTimeline(input, new ScaleXTimeline(frameCount, bezierCount, boneIndex), 1)); + break; + case BONE_SCALEY: + timelines.add(readTimeline(input, new ScaleYTimeline(frameCount, bezierCount, boneIndex), 1)); break; case BONE_SHEAR: - timelines.add(readTimeline(input, new ShearTimeline(input.readInt(true), input.readInt(true), boneIndex), 1)); + timelines.add(readTimeline(input, new ShearTimeline(frameCount, bezierCount, boneIndex), 1)); + break; + case BONE_SHEARX: + timelines.add(readTimeline(input, new ShearXTimeline(frameCount, bezierCount, boneIndex), 1)); + break; + case BONE_SHEARY: + timelines.add(readTimeline(input, new ShearYTimeline(frameCount, bezierCount, boneIndex), 1)); } } } 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 f6823cc30..d08cb541e 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java @@ -41,8 +41,8 @@ import com.badlogic.gdx.utils.JsonReader; import com.badlogic.gdx.utils.JsonValue; import com.badlogic.gdx.utils.SerializationException; +import com.esotericsoftware.spine.Animation.AlphaTimeline; import com.esotericsoftware.spine.Animation.AttachmentTimeline; -import com.esotericsoftware.spine.Animation.ColorTimeline; import com.esotericsoftware.spine.Animation.CurveTimeline; import com.esotericsoftware.spine.Animation.CurveTimeline1; import com.esotericsoftware.spine.Animation.CurveTimeline2; @@ -53,13 +53,22 @@ import com.esotericsoftware.spine.Animation.IkConstraintTimeline; import com.esotericsoftware.spine.Animation.PathConstraintMixTimeline; import com.esotericsoftware.spine.Animation.PathConstraintPositionTimeline; import com.esotericsoftware.spine.Animation.PathConstraintSpacingTimeline; +import com.esotericsoftware.spine.Animation.RGB2Timeline; +import com.esotericsoftware.spine.Animation.RGBA2Timeline; +import com.esotericsoftware.spine.Animation.RGBATimeline; +import com.esotericsoftware.spine.Animation.RGBTimeline; import com.esotericsoftware.spine.Animation.RotateTimeline; import com.esotericsoftware.spine.Animation.ScaleTimeline; +import com.esotericsoftware.spine.Animation.ScaleXTimeline; +import com.esotericsoftware.spine.Animation.ScaleYTimeline; import com.esotericsoftware.spine.Animation.ShearTimeline; +import com.esotericsoftware.spine.Animation.ShearXTimeline; +import com.esotericsoftware.spine.Animation.ShearYTimeline; import com.esotericsoftware.spine.Animation.Timeline; import com.esotericsoftware.spine.Animation.TransformConstraintTimeline; import com.esotericsoftware.spine.Animation.TranslateTimeline; -import com.esotericsoftware.spine.Animation.TwoColorTimeline; +import com.esotericsoftware.spine.Animation.TranslateXTimeline; +import com.esotericsoftware.spine.Animation.TranslateYTimeline; import com.esotericsoftware.spine.BoneData.TransformMode; import com.esotericsoftware.spine.PathConstraintData.PositionMode; import com.esotericsoftware.spine.PathConstraintData.RotateMode; @@ -435,7 +444,7 @@ public class SkeletonJson extends SkeletonLoader { if (color != null) Color.valueOf(color, point.getColor()); return point; } - case clipping: { + case clipping: ClippingAttachment clip = attachmentLoader.newClippingAttachment(skin, name); if (clip == null) return null; @@ -452,7 +461,6 @@ public class SkeletonJson extends SkeletonLoader { if (color != null) Color.valueOf(color, clip.getColor()); return clip; } - } return null; } @@ -502,10 +510,10 @@ public class SkeletonJson extends SkeletonLoader { timeline.setFrame(frame, keyMap.getFloat("time", 0), keyMap.getString("name")); timelines.add(timeline); - } else if (timelineName.equals("color")) { - ColorTimeline timeline = new ColorTimeline(timelineMap.size, timelineMap.size << 2, slot.index); + } else if (timelineName.equals("rgba")) { + RGBATimeline timeline = new RGBATimeline(timelineMap.size, timelineMap.size << 2, slot.index); float time = keyMap.getFloat("time", 0); - String color = keyMap.getString("color"); + String color = keyMap.getString("rgba"); float r = Integer.parseInt(color.substring(0, 2), 16) / 255f; float g = Integer.parseInt(color.substring(2, 4), 16) / 255f; float b = Integer.parseInt(color.substring(4, 6), 16) / 255f; @@ -518,7 +526,7 @@ public class SkeletonJson extends SkeletonLoader { break; } float time2 = nextMap.getFloat("time", 0); - color = nextMap.getString("color"); + color = nextMap.getString("rgba"); float nr = Integer.parseInt(color.substring(0, 2), 16) / 255f; float ng = Integer.parseInt(color.substring(2, 4), 16) / 255f; float nb = Integer.parseInt(color.substring(4, 6), 16) / 255f; @@ -539,8 +547,44 @@ public class SkeletonJson extends SkeletonLoader { } timelines.add(timeline); - } else if (timelineName.equals("twoColor")) { - TwoColorTimeline timeline = new TwoColorTimeline(timelineMap.size, timelineMap.size * 7, slot.index); + } else if (timelineName.equals("rgb")) { + RGBTimeline timeline = new RGBTimeline(timelineMap.size, timelineMap.size * 3, slot.index); + float time = keyMap.getFloat("time", 0); + String color = keyMap.getString("rgb"); + float r = Integer.parseInt(color.substring(0, 2), 16) / 255f; + float g = Integer.parseInt(color.substring(2, 4), 16) / 255f; + float b = Integer.parseInt(color.substring(4, 6), 16) / 255f; + for (int frame = 0, bezier = 0;; frame++) { + timeline.setFrame(frame, time, r, g, b); + JsonValue nextMap = keyMap.next; + if (nextMap == null) { + timeline.shrink(bezier); + break; + } + float time2 = nextMap.getFloat("time", 0); + color = nextMap.getString("rgb"); + float nr = Integer.parseInt(color.substring(0, 2), 16) / 255f; + float ng = Integer.parseInt(color.substring(2, 4), 16) / 255f; + float nb = Integer.parseInt(color.substring(4, 6), 16) / 255f; + JsonValue curve = keyMap.get("curve"); + if (curve != null) { + bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, r, nr, 1); + bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, g, ng, 1); + bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, b, nb, 1); + } + time = time2; + r = nr; + g = ng; + b = nb; + keyMap = nextMap; + } + timelines.add(timeline); + + } else if (timelineName.equals("alpha")) { + timelines.add(readTimeline(keyMap, new AlphaTimeline(timelineMap.size, timelineMap.size, slot.index), 0, 1)); + + } else if (timelineName.equals("rgba2")) { + RGBA2Timeline timeline = new RGBA2Timeline(timelineMap.size, timelineMap.size * 7, slot.index); float time = keyMap.getFloat("time", 0); String color = keyMap.getString("light"); float r = Integer.parseInt(color.substring(0, 2), 16) / 255f; @@ -590,6 +634,53 @@ public class SkeletonJson extends SkeletonLoader { } timelines.add(timeline); + } else if (timelineName.equals("rgb2")) { + RGB2Timeline timeline = new RGB2Timeline(timelineMap.size, timelineMap.size * 6, slot.index); + float time = keyMap.getFloat("time", 0); + String color = keyMap.getString("light"); + float r = Integer.parseInt(color.substring(0, 2), 16) / 255f; + float g = Integer.parseInt(color.substring(2, 4), 16) / 255f; + float b = Integer.parseInt(color.substring(4, 6), 16) / 255f; + color = keyMap.getString("dark"); + float r2 = Integer.parseInt(color.substring(0, 2), 16) / 255f; + float g2 = Integer.parseInt(color.substring(2, 4), 16) / 255f; + float b2 = Integer.parseInt(color.substring(4, 6), 16) / 255f; + for (int frame = 0, bezier = 0;; frame++) { + timeline.setFrame(frame, time, r, g, b, r2, g2, b2); + JsonValue nextMap = keyMap.next; + if (nextMap == null) { + timeline.shrink(bezier); + break; + } + float time2 = nextMap.getFloat("time", 0); + color = nextMap.getString("light"); + float nr = Integer.parseInt(color.substring(0, 2), 16) / 255f; + float ng = Integer.parseInt(color.substring(2, 4), 16) / 255f; + float nb = Integer.parseInt(color.substring(4, 6), 16) / 255f; + color = nextMap.getString("dark"); + float nr2 = Integer.parseInt(color.substring(0, 2), 16) / 255f; + float ng2 = Integer.parseInt(color.substring(2, 4), 16) / 255f; + float nb2 = Integer.parseInt(color.substring(4, 6), 16) / 255f; + JsonValue curve = keyMap.get("curve"); + if (curve != null) { + bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, r, nr, 1); + bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, g, ng, 1); + bezier = readCurve(curve, timeline, bezier, frame, 2, time, time2, b, nb, 1); + bezier = readCurve(curve, timeline, bezier, frame, 4, time, time2, r2, nr2, 1); + bezier = readCurve(curve, timeline, bezier, frame, 5, time, time2, g2, ng2, 1); + bezier = readCurve(curve, timeline, bezier, frame, 6, time, time2, b2, nb2, 1); + } + time = time2; + r = nr; + g = ng; + b = nb; + r2 = nr2; + g2 = ng2; + b2 = nb2; + keyMap = nextMap; + } + timelines.add(timeline); + } else throw new RuntimeException("Invalid timeline type for a slot: " + timelineName + " (" + slotMap.name + ")"); } @@ -609,13 +700,27 @@ public class SkeletonJson extends SkeletonLoader { else if (timelineName.equals("translate")) { TranslateTimeline timeline = new TranslateTimeline(timelineMap.size, timelineMap.size << 1, bone.index); timelines.add(readTimeline(keyMap, timeline, "x", "y", 0, scale)); + } else if (timelineName.equals("translatex")) { + timelines + .add(readTimeline(keyMap, new TranslateXTimeline(timelineMap.size, timelineMap.size, bone.index), 0, scale)); + } else if (timelineName.equals("translatey")) { + timelines + .add(readTimeline(keyMap, new TranslateYTimeline(timelineMap.size, timelineMap.size, bone.index), 0, scale)); } else if (timelineName.equals("scale")) { ScaleTimeline timeline = new ScaleTimeline(timelineMap.size, timelineMap.size << 1, bone.index); timelines.add(readTimeline(keyMap, timeline, "x", "y", 1, 1)); - } else if (timelineName.equals("shear")) { + } else if (timelineName.equals("scalex")) + timelines.add(readTimeline(keyMap, new ScaleXTimeline(timelineMap.size, timelineMap.size, bone.index), 1, 1)); + else if (timelineName.equals("scaley")) + timelines.add(readTimeline(keyMap, new ScaleYTimeline(timelineMap.size, timelineMap.size, bone.index), 1, 1)); + else if (timelineName.equals("shear")) { ShearTimeline timeline = new ShearTimeline(timelineMap.size, timelineMap.size << 1, bone.index); timelines.add(readTimeline(keyMap, timeline, "x", "y", 0, 1)); - } else + } else if (timelineName.equals("shearx")) + timelines.add(readTimeline(keyMap, new ShearXTimeline(timelineMap.size, timelineMap.size, bone.index), 0, 1)); + else if (timelineName.equals("sheary")) + timelines.add(readTimeline(keyMap, new ShearYTimeline(timelineMap.size, timelineMap.size, bone.index), 0, 1)); + else throw new RuntimeException("Invalid timeline type for a bone: " + timelineName + " (" + boneMap.name + ")"); } }