From 22b2086f39e65329f5dd75863eda0a8d9a9dcdc7 Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Sat, 4 Oct 2014 13:01:52 +0200 Subject: [PATCH] Flip for bones. Flip timelines now affect bones. "draworder" in JSON -> "drawOrder". Falls back to "draworder" to not break existing JSON (for the time being). --- spine-as3/spine-as3/src/spine/SkeletonJson.as | 3 +- spine-c/include/spine/Animation.h | 3 +- spine-c/include/spine/Bone.h | 2 + spine-c/include/spine/BoneData.h | 1 + spine-c/src/spine/Animation.c | 9 ++-- spine-c/src/spine/Bone.c | 13 +++-- spine-c/src/spine/SkeletonJson.c | 34 +++++-------- spine-csharp/src/Animation.cs | 20 ++++---- spine-csharp/src/Bone.cs | 24 ++++++--- spine-csharp/src/BoneData.cs | 3 ++ spine-csharp/src/SkeletonJson.cs | 50 +++++++++---------- spine-js/spine.js | 3 +- .../com/esotericsoftware/spine/Animation.java | 21 +++++--- .../src/com/esotericsoftware/spine/Bone.java | 46 ++++++++++++++--- .../com/esotericsoftware/spine/BoneData.java | 19 +++++++ .../spine/SkeletonBinary.java | 40 +++++++-------- .../esotericsoftware/spine/SkeletonJson.java | 39 +++++++-------- .../spine/SkeletonViewer.java | 2 - spine-lua/SkeletonJson.lua | 3 +- 19 files changed, 202 insertions(+), 133 deletions(-) diff --git a/spine-as3/spine-as3/src/spine/SkeletonJson.as b/spine-as3/spine-as3/src/spine/SkeletonJson.as index d5b6ebd06..e51ac89b5 100644 --- a/spine-as3/spine-as3/src/spine/SkeletonJson.as +++ b/spine-as3/spine-as3/src/spine/SkeletonJson.as @@ -469,7 +469,8 @@ public class SkeletonJson { } } - var drawOrderValues:Object = map["draworder"]; + var drawOrderValues:Object = map["drawOrder"]; + if (!drawOrderValues) drawOrderValues = map["draworder"]; if (drawOrderValues) { var drawOrderTimeline:DrawOrderTimeline = new DrawOrderTimeline(drawOrderValues.length); var slotCount:int = skeletonData.slots.length; diff --git a/spine-c/include/spine/Animation.h b/spine-c/include/spine/Animation.h index 769134c9a..2e9a0d343 100644 --- a/spine-c/include/spine/Animation.h +++ b/spine-c/include/spine/Animation.h @@ -307,10 +307,11 @@ typedef spIkConstraintTimeline IkConstraintTimeline; /**/ typedef struct { - spCurveTimeline super; + spTimeline super; int const x; int const framesCount; float* const frames; /* time, flip, ... */ + int boneIndex; } spFlipTimeline; spFlipTimeline* spFlipTimeline_create (int framesCount, int/*bool*/x); diff --git a/spine-c/include/spine/Bone.h b/spine-c/include/spine/Bone.h index 5e8b1f7bf..43436168e 100644 --- a/spine-c/include/spine/Bone.h +++ b/spine-c/include/spine/Bone.h @@ -47,11 +47,13 @@ struct spBone { float x, y; float rotation, rotationIK; float scaleX, scaleY; + int/*bool*/flipX, flipY; float const m00, m01, worldX; /* a b x */ float const m10, m11, worldY; /* c d y */ float const worldRotation; float const worldScaleX, worldScaleY; + int/*bool*/const worldFlipX, worldFlipY; }; void spBone_setYDown (int/*bool*/yDown); diff --git a/spine-c/include/spine/BoneData.h b/spine-c/include/spine/BoneData.h index 77cb90937..0df381dd9 100644 --- a/spine-c/include/spine/BoneData.h +++ b/spine-c/include/spine/BoneData.h @@ -43,6 +43,7 @@ struct spBoneData { float x, y; float rotation; float scaleX, scaleY; + int/*bool*/flipX, flipY; int/*bool*/inheritScale, inheritRotation; }; diff --git a/spine-c/src/spine/Animation.c b/spine-c/src/spine/Animation.c index 0178cfa85..af34f904c 100644 --- a/spine-c/src/spine/Animation.c +++ b/spine-c/src/spine/Animation.c @@ -808,22 +808,21 @@ void _spFlipTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton, fl if (self->frames[frameIndex] <= lastTime) return; if (self->x) - skeleton->flipX = self->frames[frameIndex + 1]; + skeleton->bones[self->boneIndex]->flipX = self->frames[frameIndex + 1]; else - skeleton->flipY = self->frames[frameIndex + 1]; + skeleton->bones[self->boneIndex]->flipY = self->frames[frameIndex + 1]; } void _spFlipTimeline_dispose (spTimeline* timeline) { spFlipTimeline* self = SUB_CAST(spFlipTimeline, timeline); - _spCurveTimeline_deinit(SUPER(self)); + _spTimeline_deinit(SUPER(self)); FREE(self->frames); FREE(self); } spFlipTimeline* spFlipTimeline_create (int framesCount, int/*bool*/x) { spFlipTimeline* self = NEW(spFlipTimeline); - _spCurveTimeline_init(SUPER(self), x ? SP_TIMELINE_FLIPX : SP_TIMELINE_FLIPY, framesCount, - _spFlipTimeline_dispose, _spFlipTimeline_apply); + _spTimeline_init(SUPER(self), x ? SP_TIMELINE_FLIPX : SP_TIMELINE_FLIPY, _spFlipTimeline_dispose, _spFlipTimeline_apply); CONST_CAST(int, self->x) = x; CONST_CAST(int, self->framesCount) = framesCount << 1; CONST_CAST(float*, self->frames) = CALLOC(float, self->framesCount); diff --git a/spine-c/src/spine/Bone.c b/spine-c/src/spine/Bone.c index 0e91facc5..ad1e74da5 100644 --- a/spine-c/src/spine/Bone.c +++ b/spine-c/src/spine/Bone.c @@ -64,24 +64,29 @@ void spBone_updateWorldTransform (spBone* self) { } CONST_CAST(float, self->worldRotation) = self->data->inheritRotation ? self->parent->worldRotation + self->rotationIK : self->rotationIK; + CONST_CAST(int, self->worldFlipX) = self->parent->worldFlipX ^ self->flipX; + CONST_CAST(int, self->worldFlipY) = self->parent->worldFlipY ^ self->flipY; } else { + int skeletonFlipX = self->skeleton->flipX, skeletonFlipY = self->skeleton->flipY; CONST_CAST(float, self->worldX) = self->skeleton->flipX ? -self->x : self->x; CONST_CAST(float, self->worldY) = self->skeleton->flipY != yDown ? -self->y : self->y; CONST_CAST(float, self->worldScaleX) = self->scaleX; CONST_CAST(float, self->worldScaleY) = self->scaleY; CONST_CAST(float, self->worldRotation) = self->rotationIK; + CONST_CAST(int, self->worldFlipX) = skeletonFlipX ^ self->flipX; + CONST_CAST(int, self->worldFlipY) = skeletonFlipY ^ self->flipY; } radians = self->worldRotation * DEG_RAD; cosine = COS(radians); sine = SIN(radians); - if (self->skeleton->flipX) { + if (self->worldFlipX) { CONST_CAST(float, self->m00) = -cosine * self->worldScaleX; CONST_CAST(float, self->m01) = sine * self->worldScaleY; } else { CONST_CAST(float, self->m00) = cosine * self->worldScaleX; CONST_CAST(float, self->m01) = -sine * self->worldScaleY; } - if (self->skeleton->flipY != yDown) { + if (self->worldFlipY != yDown) { CONST_CAST(float, self->m10) = -sine * self->worldScaleX; CONST_CAST(float, self->m11) = -cosine * self->worldScaleY; } else { @@ -97,13 +102,15 @@ void spBone_setToSetupPose (spBone* self) { self->rotationIK = self->rotation; self->scaleX = self->data->scaleX; self->scaleY = self->data->scaleY; + self->flipX = self->data->flipX; + self->flipY = self->data->flipY; } void spBone_worldToLocal (spBone* self, float worldX, float worldY, float* localX, float* localY) { float invDet; float dx = worldX - self->worldX, dy = worldY - self->worldY; float m00 = self->m00, m11 = self->m11; - if (self->skeleton->flipX != (self->skeleton->flipY != yDown)) { + if (self->worldFlipX != (self->worldFlipY != yDown)) { m00 *= -1; m11 *= -1; } diff --git a/spine-c/src/spine/SkeletonJson.c b/spine-c/src/spine/SkeletonJson.c index 33ae757c1..1ca9c81d1 100644 --- a/spine-c/src/spine/SkeletonJson.c +++ b/spine-c/src/spine/SkeletonJson.c @@ -111,7 +111,8 @@ static spAnimation* _spSkeletonJson_readAnimation (spSkeletonJson* self, Json* r Json* slots = Json_getItem(root, "slots"); Json* ik = Json_getItem(root, "ik"); Json* ffd = Json_getItem(root, "ffd"); - Json* drawOrder = Json_getItem(root, "draworder"); + Json* drawOrder = Json_getItem(root, "drawOrder"); + if (!drawOrder) drawOrder = Json_getItem(root, "draworder"); Json* events = Json_getItem(root, "events"); Json* flipX = Json_getItem(root, "flipx"); Json* flipY = Json_getItem(root, "flipy"); @@ -218,6 +219,17 @@ static spAnimation* _spSkeletonJson_readAnimation (spSkeletonJson* self, Json* r animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline); duration = timeline->frames[timelineArray->size * 3 - 3]; if (duration > animation->duration) animation->duration = duration; + } else if (strcmp(timelineArray->name, "flipX") == 0 || strcmp(timelineArray->name, "flipY") == 0) { + int x = strcmp(timelineArray->name, "flipX") == 0; + char* field = x ? "x" : "y"; + spFlipTimeline *timeline = spFlipTimeline_create(timelineArray->size, x); + timeline->boneIndex = boneIndex; + for (frame = timelineArray->child, i = 0; frame; frame = frame->next, ++i) + spFlipTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0), Json_getInt(frame, field, 0)); + animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline); + duration = timeline->frames[timelineArray->size * 2 - 2]; + if (duration > animation->duration) animation->duration = duration; + } else { spAnimation_dispose(animation); _spSkeletonJson_setError(self, 0, "Invalid timeline type for a bone: ", timelineArray->name); @@ -316,26 +328,6 @@ static spAnimation* _spSkeletonJson_readAnimation (spSkeletonJson* self, Json* r } } - /* Flip timelines. */ - if (flipX) { - Json* frame; - spFlipTimeline* timeline = spFlipTimeline_create(flipX->size, 1); - for (frame = flipX->child, i = 0; frame; frame = frame->next, ++i) - spFlipTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0), Json_getInt(frame, "x", 0)); - animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline); - duration = timeline->frames[flipX->size * 2 - 2]; - if (duration > animation->duration) animation->duration = duration; - } - if (flipY) { - Json* frame; - spFlipTimeline* timeline = spFlipTimeline_create(flipY->size, 0); - for (frame = flipY->child, i = 0; frame; frame = frame->next, ++i) - spFlipTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0), Json_getInt(frame, "y", 0)); - animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline); - duration = timeline->frames[flipY->size * 2 - 2]; - if (duration > animation->duration) animation->duration = duration; - } - /* Draw order timeline. */ if (drawOrder) { spDrawOrderTimeline* timeline = spDrawOrderTimeline_create(drawOrder->size, skeletonData->slotsCount); diff --git a/spine-csharp/src/Animation.cs b/spine-csharp/src/Animation.cs index 6a58554e8..b3843cfd3 100644 --- a/spine-csharp/src/Animation.cs +++ b/spine-csharp/src/Animation.cs @@ -670,13 +670,15 @@ namespace Spine { } } - public class FlipXTimeline : CurveTimeline { + public class FlipXTimeline : Timeline { + internal int boneIndex; internal float[] frames; + public int BoneIndex { get { return boneIndex; } set { boneIndex = value; } } public float[] Frames { get { return frames; } set { frames = value; } } // time, flip, ... + public int FrameCount { get { return frames.Length >> 1; } } - public FlipXTimeline (int frameCount) - : base(frameCount) { + public FlipXTimeline (int frameCount) { frames = new float[frameCount << 1]; } @@ -687,7 +689,7 @@ namespace Spine { frames[frameIndex + 1] = flip ? 1 : 0; } - override public void Apply (Skeleton skeleton, float lastTime, float time, List firedEvents, float alpha) { + public void Apply (Skeleton skeleton, float lastTime, float time, List firedEvents, float alpha) { float[] frames = this.frames; if (time < frames[0]) { if (lastTime > time) Apply(skeleton, lastTime, int.MaxValue, null, 0); @@ -698,11 +700,11 @@ namespace Spine { int frameIndex = (time >= frames[frames.Length - 2] ? frames.Length : Animation.binarySearch(frames, time, 2)) - 2; if (frames[frameIndex] <= lastTime) return; - Flip(skeleton, frames[frameIndex + 1] != 0); + SetFlip(skeleton.bones[boneIndex], frames[frameIndex + 1] != 0); } - virtual protected void Flip (Skeleton skeleton, bool flip) { - skeleton.flipX = flip; + virtual protected void SetFlip (Bone bone, bool flip) { + bone.flipX = flip; } } @@ -711,8 +713,8 @@ namespace Spine { : base(frameCount) { } - override protected void Flip (Skeleton skeleton, bool flip) { - skeleton.flipY = flip; + override protected void SetFlip (Bone bone, bool flip) { + bone.flipY = flip; } } } diff --git a/spine-csharp/src/Bone.cs b/spine-csharp/src/Bone.cs index 058493342..7041346c4 100644 --- a/spine-csharp/src/Bone.cs +++ b/spine-csharp/src/Bone.cs @@ -40,8 +40,10 @@ namespace Spine { internal Bone parent; internal List children; internal float x, y, rotation, rotationIK, scaleX, scaleY; + internal bool flipX, flipY; internal float m00, m01, m10, m11; internal float worldX, worldY, worldRotation, worldScaleX, worldScaleY; + internal bool worldFlipX, worldFlipY; public BoneData Data { get { return data; } } public Skeleton Skeleton { get { return skeleton; } } @@ -55,6 +57,8 @@ namespace Spine { public float RotationIK { get { return rotationIK; } set { rotationIK = value; } } public float ScaleX { get { return scaleX; } set { scaleX = value; } } public float ScaleY { get { return scaleY; } set { scaleY = value; } } + public bool FlipX { get { return flipX; } set { flipX = value; } } + public bool FlipY { get { return flipY; } set { flipY = value; } } public float M00 { get { return m00; } } public float M01 { get { return m01; } } @@ -65,6 +69,8 @@ namespace Spine { public float WorldRotation { get { return worldRotation; } } public float WorldScaleX { get { return worldScaleX; } } public float WorldScaleY { get { return worldScaleY; } } + public bool WorldFlipX { get { return worldFlipX; } set { worldFlipX = value; } } + public bool WorldFlipY { get { return worldFlipY; } set { worldFlipY = value; } } /// May be null. public Bone (BoneData data, Skeleton skeleton, Bone parent) { @@ -93,24 +99,29 @@ namespace Spine { worldScaleY = scaleY; } worldRotation = data.inheritRotation ? parent.worldRotation + rotationIK : rotationIK; + worldFlipX = parent.worldFlipX ^ flipX; + worldFlipY = parent.worldFlipY ^ flipY; } else { - worldX = skeleton.flipX ? -x : x; - worldY = skeleton.flipY != yDown ? -y : y; + bool skeletonFlipX = skeleton.flipX, skeletonFlipY = skeleton.flipY; + worldX = skeletonFlipX ? -x : x; + worldY = skeletonFlipY != yDown ? -y : y; worldScaleX = scaleX; worldScaleY = scaleY; worldRotation = rotationIK; + worldFlipX = skeletonFlipX ^ flipX; + worldFlipY = skeletonFlipY ^ flipY; } float radians = worldRotation * (float)Math.PI / 180; float cos = (float)Math.Cos(radians); float sin = (float)Math.Sin(radians); - if (skeleton.flipX) { + if (worldFlipX) { m00 = -cos * worldScaleX; m01 = sin * worldScaleY; } else { m00 = cos * worldScaleX; m01 = -sin * worldScaleY; } - if (skeleton.flipY != yDown) { + if (worldFlipY != yDown) { m10 = -sin * worldScaleX; m11 = -cos * worldScaleY; } else { @@ -127,13 +138,14 @@ namespace Spine { rotationIK = rotation; scaleX = data.scaleX; scaleY = data.scaleY; + flipX = data.flipX; + flipY = data.flipY; } public void worldToLocal (float worldX, float worldY, out float localX, out float localY) { float dx = worldX - this.worldX, dy = worldY - this.worldY; float m00 = this.m00, m10 = this.m10, m01 = this.m01, m11 = this.m11; - Skeleton skeleton = this.skeleton; - if (skeleton.flipX != (skeleton.flipY != yDown)) { + if (worldFlipX != (worldFlipY != yDown)) { m00 = -m00; m11 = -m11; } diff --git a/spine-csharp/src/BoneData.cs b/spine-csharp/src/BoneData.cs index 5d19b266e..7ab9f7fe9 100644 --- a/spine-csharp/src/BoneData.cs +++ b/spine-csharp/src/BoneData.cs @@ -35,6 +35,7 @@ namespace Spine { internal BoneData parent; internal String name; internal float length, x, y, rotation, scaleX = 1, scaleY = 1; + internal bool flipX, flipY; internal bool inheritScale = true, inheritRotation = true; /// May be null. @@ -46,6 +47,8 @@ namespace Spine { public float Rotation { get { return rotation; } set { rotation = value; } } public float ScaleX { get { return scaleX; } set { scaleX = value; } } public float ScaleY { get { return scaleY; } set { scaleY = value; } } + public bool FlipX { get { return flipX; } set { flipX = value; } } + public bool FlipY { get { return flipY; } set { flipY = value; } } public bool InheritScale { get { return inheritScale; } set { inheritScale = value; } } public bool InheritRotation { get { return inheritRotation; } set { inheritRotation = value; } } diff --git a/spine-csharp/src/SkeletonJson.cs b/spine-csharp/src/SkeletonJson.cs index a5494c155..77baa063b 100644 --- a/spine-csharp/src/SkeletonJson.cs +++ b/spine-csharp/src/SkeletonJson.cs @@ -114,6 +114,8 @@ namespace Spine { boneData.rotation = GetFloat(boneMap, "rotation", 0); boneData.scaleX = GetFloat(boneMap, "scaleX", 1); boneData.scaleY = GetFloat(boneMap, "scaleY", 1); + boneData.flipX = GetBoolean(boneMap, "flipX", false); + boneData.flipY = GetBoolean(boneMap, "flipY", false); boneData.inheritScale = GetBoolean(boneMap, "inheritScale", true); boneData.inheritRotation = GetBoolean(boneMap, "inheritRotation", true); skeletonData.bones.Add(boneData); @@ -387,7 +389,7 @@ namespace Spine { foreach (KeyValuePair timelineEntry in timelineMap) { var values = (List)timelineEntry.Value; var timelineName = (String)timelineEntry.Key; - if (timelineName.Equals("color")) { + if (timelineName == "color") { var timeline = new ColorTimeline(values.Count); timeline.slotIndex = slotIndex; @@ -402,7 +404,7 @@ namespace Spine { timelines.Add(timeline); duration = Math.Max(duration, timeline.frames[timeline.FrameCount * 5 - 5]); - } else if (timelineName.Equals("attachment")) { + } else if (timelineName == "attachment") { var timeline = new AttachmentTimeline(values.Count); timeline.slotIndex = slotIndex; @@ -431,7 +433,7 @@ namespace Spine { foreach (KeyValuePair timelineEntry in timelineMap) { var values = (List)timelineEntry.Value; var timelineName = (String)timelineEntry.Key; - if (timelineName.Equals("rotate")) { + if (timelineName == "rotate") { var timeline = new RotateTimeline(values.Count); timeline.boneIndex = boneIndex; @@ -445,10 +447,10 @@ namespace Spine { timelines.Add(timeline); duration = Math.Max(duration, timeline.frames[timeline.FrameCount * 2 - 2]); - } else if (timelineName.Equals("translate") || timelineName.Equals("scale")) { + } else if (timelineName == "translate" || timelineName == "scale") { TranslateTimeline timeline; float timelineScale = 1; - if (timelineName.Equals("scale")) + if (timelineName == "scale") timeline = new ScaleTimeline(values.Count); else { timeline = new TranslateTimeline(values.Count); @@ -468,6 +470,21 @@ namespace Spine { timelines.Add(timeline); duration = Math.Max(duration, timeline.frames[timeline.FrameCount * 3 - 3]); + } else if (timelineName == "flipX" || timelineName == "flipY") { + bool x = timelineName == "flipX"; + var timeline = x ? new FlipXTimeline(values.Count) : new FlipYTimeline(values.Count); + timeline.boneIndex = boneIndex; + + String field = x ? "x" : "y"; + int frameIndex = 0; + foreach (Dictionary valueMap in values) { + float time = (float)valueMap["time"]; + timeline.SetFrame(frameIndex, time, valueMap.ContainsKey(field) ? (bool)valueMap[field] : false); + frameIndex++; + } + timelines.Add(timeline); + duration = Math.Max(duration, timeline.frames[timeline.FrameCount * 2 - 2]); + } else throw new Exception("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"); } @@ -550,27 +567,8 @@ namespace Spine { } } - if (map.ContainsKey("flipx")) { - var flipMap = (List)map["flipx"]; - var timeline = new FlipXTimeline(flipMap.Count); - int frameIndex = 0; - foreach (Dictionary valueMap in flipMap) - timeline.SetFrame(frameIndex++, (float)valueMap["time"], valueMap.ContainsKey("x") ? (bool)valueMap["x"] : false); - timelines.Add(timeline); - duration = Math.Max(duration, timeline.frames[timeline.FrameCount * 2 - 2]); - } - if (map.ContainsKey("flipy")) { - var flipMap = (List)map["flipy"]; - var timeline = new FlipYTimeline(flipMap.Count); - int frameIndex = 0; - foreach (Dictionary valueMap in flipMap) - timeline.SetFrame(frameIndex++, (float)valueMap["time"], valueMap.ContainsKey("y") ? (bool)valueMap["y"] : false); - timelines.Add(timeline); - duration = Math.Max(duration, timeline.frames[timeline.FrameCount * 2 - 2]); - } - - if (map.ContainsKey("draworder")) { - var values = (List)map["draworder"]; + if (map.ContainsKey("drawOrder") || map.ContainsKey("draworder")) { + var values = (List)map[map.ContainsKey("drawOrder") ? "drawOrder" : "draworder"]; var timeline = new DrawOrderTimeline(values.Count); int slotCount = skeletonData.slots.Count; int frameIndex = 0; diff --git a/spine-js/spine.js b/spine-js/spine.js index 22b71d654..9a30c5504 100644 --- a/spine-js/spine.js +++ b/spine-js/spine.js @@ -1712,7 +1712,8 @@ spine.SkeletonJson.prototype = { } } - var drawOrderValues = map["draworder"]; + var drawOrderValues = map["drawOrder"]; + if (!drawOrderValues) drawOrderValues = map["draworder"]; if (drawOrderValues) { var timeline = new spine.DrawOrderTimeline(drawOrderValues.length); var slotCount = skeletonData.slots.length; diff --git a/spine-libgdx/src/com/esotericsoftware/spine/Animation.java b/spine-libgdx/src/com/esotericsoftware/spine/Animation.java index 0e03242af..161102e89 100644 --- a/spine-libgdx/src/com/esotericsoftware/spine/Animation.java +++ b/spine-libgdx/src/com/esotericsoftware/spine/Animation.java @@ -778,12 +778,21 @@ public class Animation { } static public class FlipXTimeline implements Timeline { + int boneIndex; final float[] frames; // time, flip, ... public FlipXTimeline (int frameCount) { frames = new float[frameCount << 1]; } + public void setBoneIndex (int boneIndex) { + this.boneIndex = boneIndex; + } + + public int getBoneIndex () { + return boneIndex; + } + public int getFrameCount () { return frames.length >> 1; } @@ -806,15 +815,13 @@ public class Animation { return; } else if (lastTime > time) // lastTime = -1; - int frameIndex = (time >= frames[frames.length - 2] ? frames.length : binarySearch(frames, time, 2)) - 2; if (frames[frameIndex] <= lastTime) return; - - flip(skeleton, frames[frameIndex + 1] != 0); + setFlip(skeleton.bones.get(boneIndex), frames[frameIndex + 1] != 0); } - protected void flip (Skeleton skeleton, boolean flip) { - skeleton.setFlipX(flip); + protected void setFlip (Bone bone, boolean flip) { + bone.setFlipX(flip); } } @@ -823,8 +830,8 @@ public class Animation { super(frameCount); } - protected void flip (Skeleton skeleton, boolean flip) { - skeleton.setFlipY(flip); + protected void setFlip (Bone bone, boolean flip) { + bone.setFlipY(flip); } } } diff --git a/spine-libgdx/src/com/esotericsoftware/spine/Bone.java b/spine-libgdx/src/com/esotericsoftware/spine/Bone.java index d2e81bea8..46f672b05 100644 --- a/spine-libgdx/src/com/esotericsoftware/spine/Bone.java +++ b/spine-libgdx/src/com/esotericsoftware/spine/Bone.java @@ -43,11 +43,13 @@ public class Bone { float x, y; float rotation, rotationIK; float scaleX, scaleY; + boolean flipX, flipY; float m00, m01, worldX; // a b x float m10, m11, worldY; // c d y float worldRotation; float worldScaleX, worldScaleY; + boolean worldFlipX, worldFlipY; Bone (BoneData data) { this.data = data; @@ -78,6 +80,8 @@ public class Bone { rotationIK = bone.rotationIK; scaleX = bone.scaleX; scaleY = bone.scaleY; + flipX = bone.flipX; + flipY = bone.flipY; } /** Computes the world SRT using the parent bone and the local SRT. */ @@ -96,23 +100,28 @@ public class Bone { worldScaleY = scaleY; } worldRotation = data.inheritRotation ? parent.worldRotation + rotationIK : rotationIK; + worldFlipX = parent.worldFlipX ^ flipX; + worldFlipY = parent.worldFlipY ^ flipY; } else { - worldX = skeleton.flipX ? -x : x; - worldY = skeleton.flipY ? -y : y; + boolean skeletonFlipX = skeleton.flipX, skeletonFlipY = skeleton.flipY; + worldX = skeletonFlipX ? -x : x; + worldY = skeletonFlipY ? -y : y; worldScaleX = scaleX; worldScaleY = scaleY; worldRotation = rotationIK; + worldFlipX = skeletonFlipX ^ flipX; + worldFlipY = skeletonFlipY ^ flipY; } float cos = MathUtils.cosDeg(worldRotation); float sin = MathUtils.sinDeg(worldRotation); - if (skeleton.flipX) { + if (worldFlipX) { m00 = -cos * worldScaleX; m01 = sin * worldScaleY; } else { m00 = cos * worldScaleX; m01 = -sin * worldScaleY; } - if (skeleton.flipY) { + if (worldFlipY) { m10 = -sin * worldScaleX; m11 = -cos * worldScaleY; } else { @@ -129,6 +138,8 @@ public class Bone { rotationIK = rotation; scaleX = data.scaleX; scaleY = data.scaleY; + flipX = data.flipX; + flipY = data.flipY; } public BoneData getData () { @@ -208,6 +219,22 @@ public class Bone { scaleY = scale; } + public boolean getFlipX () { + return flipX; + } + + public void setFlipX (boolean flipX) { + this.flipX = flipX; + } + + public boolean getFlipY () { + return flipY; + } + + public void setFlipY (boolean flipY) { + this.flipY = flipY; + } + public float getM00 () { return m00; } @@ -244,6 +271,14 @@ public class Bone { return worldScaleY; } + public boolean getWorldFlipX () { + return worldFlipX; + } + + public boolean getWorldFlipY () { + return worldFlipY; + } + public Matrix3 getWorldTransform (Matrix3 worldTransform) { if (worldTransform == null) throw new IllegalArgumentException("worldTransform cannot be null."); float[] val = worldTransform.val; @@ -262,8 +297,7 @@ public class Bone { public Vector2 worldToLocal (Vector2 world) { float x = world.x - worldX, y = world.y - worldY; float m00 = this.m00, m10 = this.m10, m01 = this.m01, m11 = this.m11; - Skeleton skeleton = this.skeleton; - if (skeleton.flipX != skeleton.flipY) { + if (worldFlipX != worldFlipY) { m00 = -m00; m11 = -m11; } diff --git a/spine-libgdx/src/com/esotericsoftware/spine/BoneData.java b/spine-libgdx/src/com/esotericsoftware/spine/BoneData.java index 7ace0df16..37c5a5e6c 100644 --- a/spine-libgdx/src/com/esotericsoftware/spine/BoneData.java +++ b/spine-libgdx/src/com/esotericsoftware/spine/BoneData.java @@ -39,6 +39,7 @@ public class BoneData { float x, y; float rotation; float scaleX = 1, scaleY = 1; + boolean flipX, flipY; boolean inheritScale = true, inheritRotation = true; // Nonessential. @@ -63,6 +64,8 @@ public class BoneData { rotation = bone.rotation; scaleX = bone.scaleX; scaleY = bone.scaleY; + flipX = bone.flipX; + flipY = bone.flipY; } /** @return May be null. */ @@ -132,6 +135,22 @@ public class BoneData { this.scaleY = scaleY; } + public boolean getFlipX () { + return flipX; + } + + public void setFlipX (boolean flipX) { + this.flipX = flipX; + } + + public boolean getFlipY () { + return flipY; + } + + public void setFlipY (boolean flipY) { + this.flipY = flipY; + } + public boolean getInheritScale () { return inheritScale; } diff --git a/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java b/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java index 5505ffc1a..3071babdb 100644 --- a/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java +++ b/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java @@ -68,6 +68,8 @@ public class SkeletonBinary { 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_FLIPX = 5; + static public final int TIMELINE_FLIPY = 6; static public final int CURVE_LINEAR = 0; static public final int CURVE_STEPPED = 1; @@ -124,6 +126,8 @@ public class SkeletonBinary { boneData.scaleY = input.readFloat(); boneData.rotation = input.readFloat(); boneData.length = input.readFloat() * scale; + boneData.flipX = input.readBoolean(); + boneData.flipY = input.readBoolean(); boneData.inheritScale = input.readBoolean(); boneData.inheritRotation = input.readBoolean(); if (nonessential) Color.rgba8888ToColor(boneData.color, input.readInt()); @@ -384,7 +388,7 @@ public class SkeletonBinary { break; } case TIMELINE_TRANSLATE: - case TIMELINE_SCALE: + case TIMELINE_SCALE: { TranslateTimeline timeline; float timelineScale = 1; if (timelineType == TIMELINE_SCALE) @@ -403,6 +407,18 @@ public class SkeletonBinary { duration = Math.max(duration, timeline.getFrames()[frameCount * 3 - 3]); break; } + case TIMELINE_FLIPX: + case TIMELINE_FLIPY: { + FlipXTimeline timeline = timelineType == TIMELINE_FLIPX ? new FlipXTimeline(frameCount) : new FlipYTimeline( + frameCount); + timeline.boneIndex = boneIndex; + for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) + timeline.setFrame(frameIndex, input.readFloat(), input.readBoolean()); + timelines.add(timeline); + duration = Math.max(duration, timeline.getFrames()[frameCount * 2 - 2]); + break; + } + } } } @@ -474,28 +490,6 @@ public class SkeletonBinary { } } - // Flip timelines. - int flipCount = input.readInt(true); - if (flipCount > 0) { - FlipXTimeline timeline = new FlipXTimeline(flipCount); - for (int i = 0; i < flipCount; i++) { - float time = input.readFloat(); - timeline.setFrame(i, time, input.readBoolean()); - } - timelines.add(timeline); - duration = Math.max(duration, timeline.getFrames()[flipCount * 2 - 2]); - } - flipCount = input.readInt(true); - if (flipCount > 0) { - FlipYTimeline timeline = new FlipYTimeline(flipCount); - for (int i = 0; i < flipCount; i++) { - float time = input.readFloat(); - timeline.setFrame(i, time, input.readBoolean()); - } - timelines.add(timeline); - duration = Math.max(duration, timeline.getFrames()[flipCount * 2 - 2]); - } - // Draw order timeline. int drawOrderCount = input.readInt(true); if (drawOrderCount > 0) { diff --git a/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java b/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java index e550f78c2..7c8f1235a 100644 --- a/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java +++ b/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java @@ -116,6 +116,8 @@ public class SkeletonJson { boneData.rotation = boneMap.getFloat("rotation", 0); boneData.scaleX = boneMap.getFloat("scaleX", 1); boneData.scaleY = boneMap.getFloat("scaleY", 1); + boneData.flipX = boneMap.getBoolean("flipX", false); + boneData.flipY = boneMap.getBoolean("flipY", false); boneData.inheritScale = boneMap.getBoolean("inheritScale", true); boneData.inheritRotation = boneMap.getBoolean("inheritRotation", true); @@ -385,6 +387,20 @@ public class SkeletonJson { timelines.add(timeline); duration = Math.max(duration, timeline.getFrames()[timeline.getFrameCount() * 3 - 3]); + } else if (timelineName.equals("flipX") || timelineName.equals("flipY")) { + boolean x = timelineName.equals("flipX"); + FlipXTimeline timeline = x ? new FlipXTimeline(timelineMap.size) : new FlipYTimeline(timelineMap.size); + timeline.boneIndex = boneIndex; + + String field = x ? "x" : "y"; + int frameIndex = 0; + for (JsonValue valueMap = timelineMap.child; valueMap != null; valueMap = valueMap.next) { + timeline.setFrame(frameIndex, valueMap.getFloat("time"), valueMap.getBoolean(field, false)); + frameIndex++; + } + timelines.add(timeline); + duration = Math.max(duration, timeline.getFrames()[timeline.getFrameCount() * 2 - 2]); + } else throw new RuntimeException("Invalid timeline type for a bone: " + timelineName + " (" + boneMap.name + ")"); } @@ -460,28 +476,9 @@ public class SkeletonJson { } } - // Flip timelines. - JsonValue flipMap = map.get("flipx"); - if (flipMap != null) { - FlipXTimeline timeline = new FlipXTimeline(flipMap.size); - int frameIndex = 0; - for (JsonValue valueMap = flipMap.child; valueMap != null; valueMap = valueMap.next) - timeline.setFrame(frameIndex++, valueMap.getFloat("time"), valueMap.getBoolean("x", false)); - timelines.add(timeline); - duration = Math.max(duration, timeline.getFrames()[timeline.getFrameCount() * 2 - 2]); - } - flipMap = map.get("flipy"); - if (flipMap != null) { - FlipYTimeline timeline = new FlipYTimeline(flipMap.size); - int frameIndex = 0; - for (JsonValue valueMap = flipMap.child; valueMap != null; valueMap = valueMap.next) - timeline.setFrame(frameIndex++, valueMap.getFloat("time"), valueMap.getBoolean("y", false)); - timelines.add(timeline); - duration = Math.max(duration, timeline.getFrames()[timeline.getFrameCount() * 2 - 2]); - } - // Draw order timeline. - JsonValue drawOrdersMap = map.get("draworder"); + JsonValue drawOrdersMap = map.get("drawOrder"); + if (drawOrdersMap == null) drawOrdersMap = map.get("draworder"); if (drawOrdersMap != null) { DrawOrderTimeline timeline = new DrawOrderTimeline(drawOrdersMap.size); int slotCount = skeletonData.slots.size; diff --git a/spine-libgdx/test/com/esotericsoftware/spine/SkeletonViewer.java b/spine-libgdx/test/com/esotericsoftware/spine/SkeletonViewer.java index 9149998f5..9c87ed72d 100644 --- a/spine-libgdx/test/com/esotericsoftware/spine/SkeletonViewer.java +++ b/spine-libgdx/test/com/esotericsoftware/spine/SkeletonViewer.java @@ -206,8 +206,6 @@ public class SkeletonViewer extends ApplicationAdapter { if (!ui.pauseButton.isChecked()) { state.update(delta); state.apply(skeleton); - ui.flipXCheckbox.setChecked(skeleton.getFlipX()); - ui.flipYCheckbox.setChecked(skeleton.getFlipY()); } skeleton.setPosition(skeletonX, skeletonY); // skeleton.setPosition(0, 0); diff --git a/spine-lua/SkeletonJson.lua b/spine-lua/SkeletonJson.lua index cd99f5f5d..001821220 100755 --- a/spine-lua/SkeletonJson.lua +++ b/spine-lua/SkeletonJson.lua @@ -447,7 +447,8 @@ function SkeletonJson.new (attachmentLoader) end end - local drawOrderValues = map["draworder"] + local drawOrderValues = map["drawOrder"] + if not drawOrderValues then drawOrderValues = map["draworder"] end if drawOrderValues then local timeline = Animation.DrawOrderTimeline.new(#drawOrderValues) local slotCount = #skeletonData.slots