IK is complete for spine-libgdx when exporting from Spine 1.9.06+.

This commit is contained in:
NathanSweet 2014-07-23 22:33:45 +02:00
parent 148715426f
commit 8a46d48e80
6 changed files with 126 additions and 36 deletions

View File

@ -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;
}

View File

@ -29,10 +29,10 @@ public class IkConstraint {
}
/** Copy constructor. */
public IkConstraint (IkConstraint ikConstraint) {
public IkConstraint (IkConstraint ikConstraint, Array<Bone> 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;
}

View File

@ -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<Bone> 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<Array<Bone>> updateBonesCache = this.updateBonesCache;
Array<IkConstraint> ikConstraints = this.ikConstraints;
int i = 0, last = updateBonesCache.size - 1;
while (true) {
Array<Bone> 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<Bone> 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<Bone> 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<Bone> 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 () {

View File

@ -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++) {

View File

@ -41,6 +41,8 @@ public class SkeletonData {
final Array<EventData> events = new Array();
final Array<Animation> animations = new Array();
final Array<IkConstraintData> 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();
}

View File

@ -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");