From 8a46d48e809c05d6014c75e179a35c727546b6ba Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Wed, 23 Jul 2014 22:33:45 +0200 Subject: [PATCH] IK is complete for spine-libgdx when exporting from Spine 1.9.06+. --- .../src/com/esotericsoftware/spine/Bone.java | 54 +++++++++---------- .../esotericsoftware/spine/IkConstraint.java | 6 +-- .../com/esotericsoftware/spine/Skeleton.java | 32 +++++++++-- .../spine/SkeletonBinary.java | 5 ++ .../esotericsoftware/spine/SkeletonData.java | 35 ++++++++++++ .../esotericsoftware/spine/SkeletonJson.java | 30 +++++++++++ 6 files changed, 126 insertions(+), 36 deletions(-) diff --git a/spine-libgdx/src/com/esotericsoftware/spine/Bone.java b/spine-libgdx/src/com/esotericsoftware/spine/Bone.java index 1fe7a7081..f438b612b 100644 --- a/spine-libgdx/src/com/esotericsoftware/spine/Bone.java +++ b/spine-libgdx/src/com/esotericsoftware/spine/Bone.java @@ -42,10 +42,11 @@ 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, worldCos, worldSin; + float worldRotation; float worldScaleX, worldScaleY; /** @param parent May be null. */ @@ -68,10 +69,12 @@ 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. */ - public void updateWorldTransform (boolean flipX, boolean flipY) { + public void updateWorldTransform () { Bone parent = this.parent; float x = this.x, y = this.y; if (parent != null) { @@ -92,21 +95,21 @@ public class Bone { worldScaleY = scaleY; worldRotation = rotationIK; } - float cos = MathUtils.cosDeg(worldRotation); - float sin = MathUtils.sinDeg(worldRotation); - worldCos = cos; - worldSin = sin; - m00 = cos * worldScaleX; - m10 = sin * worldScaleX; - m01 = -sin * worldScaleY; - m11 = cos * worldScaleY; + float cos = MathUtils.cosDeg(worldRotation) * worldScaleX; + float sin = MathUtils.sinDeg(worldRotation) * worldScaleY; if (flipX) { - m00 = -m00; - m01 = -m01; + m00 = -cos; + m01 = sin; + } else { + m00 = cos; + m01 = -sin; } if (flipY) { - m10 = -m10; - m11 = -m11; + m10 = -sin; + m11 = -cos; + } else { + m10 = sin; + m11 = cos; } } @@ -221,14 +224,6 @@ public class Bone { return worldRotation; } - public float getWorldCos () { - return worldCos; - } - - public float getWorldSin () { - return worldSin; - } - public float getWorldScaleX () { return worldScaleX; } @@ -253,12 +248,15 @@ public class Bone { } public Vector2 worldToLocal (Vector2 world) { - float x = world.x - worldX; - float y = world.y - worldY; - float cos = worldCos; - float sin = -worldSin; - world.x = (x * cos - y * sin) / worldScaleX; - world.y = (x * sin + y * cos) / worldScaleY; + float x = world.x - worldX, y = world.y - worldY; + float m00 = this.m00, m10 = this.m10, m01 = this.m01, m11 = this.m11; + if (flipX != flipY) { + m00 *= -1; + m11 *= -1; + } + float invDet = 1 / (m00 * m11 - m01 * m10); + world.x = (x * m00 * invDet - y * m01 * invDet); + world.y = (y * m11 * invDet - x * m10 * invDet); return world; } diff --git a/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java b/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java index 8950495c3..aba1ea19a 100644 --- a/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java +++ b/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java @@ -29,10 +29,10 @@ public class IkConstraint { } /** Copy constructor. */ - public IkConstraint (IkConstraint ikConstraint) { + public IkConstraint (IkConstraint ikConstraint, Array bones, Bone target) { data = ikConstraint.data; - bones = new Array(ikConstraint.bones); - target = ikConstraint.target; + this.bones = bones; + this.target = target; mix = ikConstraint.mix; bendDirection = ikConstraint.bendDirection; } diff --git a/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java b/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java index 814a18acf..449193c72 100644 --- a/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java +++ b/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java @@ -98,12 +98,19 @@ public class Skeleton { drawOrder.add(slots.get(skeleton.slots.indexOf(slot, true))); ikConstraints = new Array(skeleton.ikConstraints.size); - for (IkConstraint ikConstraint : skeleton.ikConstraints) - ikConstraints.add(new IkConstraint(ikConstraint)); + for (IkConstraint ikConstraint : skeleton.ikConstraints) { + Bone target = bones.get(skeleton.bones.indexOf(ikConstraint.target, true)); + Array ikBones = new Array(ikConstraint.bones.size); + for (Bone bone : ikConstraint.bones) + ikBones.add(bones.get(skeleton.bones.indexOf(bone, true))); + ikConstraints.add(new IkConstraint(ikConstraint, ikBones, target)); + } skin = skeleton.skin; color = new Color(skeleton.color); time = skeleton.time; + flipX = skeleton.flipX; + flipY = skeleton.flipY; updateCache(); } @@ -155,15 +162,13 @@ public class Skeleton { Bone bone = bones.get(i); bone.rotationIK = bone.rotation; } - boolean flipX = this.flipX; - boolean flipY = this.flipY; Array> updateBonesCache = this.updateBonesCache; Array ikConstraints = this.ikConstraints; int i = 0, last = updateBonesCache.size - 1; while (true) { Array updateBones = updateBonesCache.get(i); for (int ii = 0, nn = updateBones.size; ii < nn; ii++) - updateBones.get(ii).updateWorldTransform(flipX, flipY); + updateBones.get(ii).updateWorldTransform(); if (i == last) break; ikConstraints.get(i).apply(); i++; @@ -360,7 +365,11 @@ public class Skeleton { } public void setFlipX (boolean flipX) { +// if (this.flipX == flipX) return; this.flipX = flipX; + Array bones = this.bones; + for (int i = 0, n = bones.size; i < n; i++) + bones.get(i).flipX = flipX; } public boolean getFlipY () { @@ -368,7 +377,20 @@ public class Skeleton { } public void setFlipY (boolean flipY) { + if (this.flipY == flipY) return; this.flipY = flipY; + Array bones = this.bones; + for (int i = 0, n = bones.size; i < n; i++) + bones.get(i).flipY = flipY; + } + + public void setFlip (boolean flipX, boolean flipY) { + Array bones = this.bones; + for (int i = 0, n = bones.size; i < n; i++) { + Bone bone = bones.get(i); + bone.flipX = flipX; + bone.flipY = flipY; + } } public float getX () { diff --git a/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java b/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java index 886f93978..cb6ac9588 100644 --- a/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java +++ b/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java @@ -107,6 +107,11 @@ public class SkeletonBinary { DataInput input = new DataInput(file.read(512)); try { + skeletonData.version = input.readString(); + skeletonData.hash = input.readString(); + skeletonData.width = input.readFloat(); + skeletonData.height = input.readFloat(); + boolean nonessential = input.readBoolean(); // Bones. for (int i = 0, n = input.readInt(true); i < n; i++) { diff --git a/spine-libgdx/src/com/esotericsoftware/spine/SkeletonData.java b/spine-libgdx/src/com/esotericsoftware/spine/SkeletonData.java index df840988d..12026e133 100644 --- a/spine-libgdx/src/com/esotericsoftware/spine/SkeletonData.java +++ b/spine-libgdx/src/com/esotericsoftware/spine/SkeletonData.java @@ -41,6 +41,8 @@ public class SkeletonData { final Array events = new Array(); final Array animations = new Array(); final Array ikConstraints = new Array(); + float width, height; + String version, hash; // --- Bones. @@ -179,6 +181,39 @@ public class SkeletonData { this.name = name; } + public float getWidth () { + return width; + } + + public void setWidth (float width) { + this.width = width; + } + + public float getHeight () { + return height; + } + + public void setHeight (float height) { + this.height = height; + } + + /** Returns the Spine version used to export this data. */ + public String getVersion () { + return version; + } + + public void setVersion (String version) { + this.version = version; + } + + public String getHash () { + return hash; + } + + public void setHash (String hash) { + this.hash = hash; + } + public String toString () { return name != null ? name : super.toString(); } diff --git a/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java b/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java index 4a07858a9..f61203b32 100644 --- a/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java +++ b/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java @@ -90,6 +90,15 @@ public class SkeletonJson { JsonValue root = new JsonReader().parse(file); + // Skeleton. + JsonValue skeletonMap = root.get("skeleton"); + if (skeletonMap != null) { + skeletonData.version = skeletonMap.getString("spine"); + skeletonData.hash = skeletonMap.getString("hash"); + skeletonData.width = skeletonMap.getFloat("width"); + skeletonData.height = skeletonMap.getFloat("height"); + } + // Bones. for (JsonValue boneMap = root.getChild("bones"); boneMap != null; boneMap = boneMap.next) { BoneData parent = null; @@ -114,6 +123,27 @@ public class SkeletonJson { skeletonData.getBones().add(boneData); } + // IK. + for (JsonValue ikMap = root.getChild("ik"); ikMap != null; ikMap = ikMap.next) { + IkConstraintData ikConstraintData = new IkConstraintData(ikMap.getString("name")); + + for (JsonValue boneMap = ikMap.getChild("bones"); boneMap != null; boneMap = boneMap.next) { + String boneName = boneMap.asString(); + BoneData bone = skeletonData.findBone(boneName); + if (bone == null) throw new SerializationException("IK bone not found: " + boneName); + ikConstraintData.getBones().add(bone); + } + + String targetName = ikMap.getString("target"); + ikConstraintData.setTarget(skeletonData.findBone(targetName)); + if (ikConstraintData.getTarget() == null) throw new SerializationException("Target bone not found: " + targetName); + + ikConstraintData.setBendDirection(ikMap.getBoolean("bendPositive", true) ? 1 : -1); + ikConstraintData.setMix(ikMap.getFloat("mix", 1)); + + skeletonData.getIkConstraints().add(ikConstraintData); + } + // Slots. for (JsonValue slotMap = root.getChild("slots"); slotMap != null; slotMap = slotMap.next) { String slotName = slotMap.getString("name");