mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-06 07:14:55 +08:00
IK constraints for spine-libgdx.
Note Spine doesn't yet export IK data, so spine-libgdx doesn't yet load it. Soon!
This commit is contained in:
parent
88f805a74e
commit
6444e8e934
@ -273,7 +273,7 @@ public class Animation {
|
||||
float prevFrameValue = frames[frameIndex - 1];
|
||||
float frameTime = frames[frameIndex];
|
||||
float percent = MathUtils.clamp(1 - (time - frameTime) / (frames[frameIndex + PREV_FRAME_TIME] - frameTime), 0, 1);
|
||||
percent = getCurvePercent(frameIndex / 2 - 1, percent);
|
||||
percent = getCurvePercent((frameIndex >> 1) - 1, percent);
|
||||
|
||||
float amount = frames[frameIndex + FRAME_VALUE] - prevFrameValue;
|
||||
while (amount > 180)
|
||||
@ -657,7 +657,7 @@ public class Animation {
|
||||
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
|
||||
float[][] frameVertices = this.frameVertices;
|
||||
int vertexCount = frameVertices[0].length;
|
||||
|
||||
@ -700,4 +700,63 @@ public class Animation {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static public class IkConstraintTimeline extends CurveTimeline {
|
||||
static private final int PREV_FRAME_TIME = -2;
|
||||
static private final int FRAME_VALUE = 1;
|
||||
|
||||
int ikConstraintIndex;
|
||||
private final float[] frames; // time, mix, ...
|
||||
private final int[] bendDirections;
|
||||
|
||||
public IkConstraintTimeline (int frameCount) {
|
||||
super(frameCount);
|
||||
frames = new float[frameCount * 2];
|
||||
bendDirections = new int[frameCount];
|
||||
}
|
||||
|
||||
public void setIkConstraintIndex (int ikConstraint) {
|
||||
this.ikConstraintIndex = ikConstraint;
|
||||
}
|
||||
|
||||
public int getIkConstraintIndex () {
|
||||
return ikConstraintIndex;
|
||||
}
|
||||
|
||||
public float[] getFrames () {
|
||||
return frames;
|
||||
}
|
||||
|
||||
/** Sets the time and mix and bend direction of the specified keyframe. */
|
||||
public void setFrame (int frameIndex, float time, float mix, int bendDirection) {
|
||||
bendDirections[frameIndex] = bendDirection;
|
||||
frameIndex *= 2;
|
||||
frames[frameIndex] = time;
|
||||
frames[frameIndex + 1] = mix;
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha) {
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
IkConstraint ikConstraint = skeleton.ikConstraints.get(ikConstraintIndex);
|
||||
|
||||
if (time >= frames[frames.length - 2]) { // Time is after last frame.
|
||||
ikConstraint.mix += (frames[frames.length - 1] - ikConstraint.mix) * alpha;
|
||||
ikConstraint.bendDirection = bendDirections[bendDirections.length - 1];
|
||||
return;
|
||||
}
|
||||
|
||||
// Interpolate between the previous frame and the current frame.
|
||||
int frameIndex = binarySearch(frames, time, 2);
|
||||
float prevFrameValue = frames[frameIndex - 1];
|
||||
float frameTime = frames[frameIndex];
|
||||
float percent = MathUtils.clamp(1 - (time - frameTime) / (frames[frameIndex + PREV_FRAME_TIME] - frameTime), 0, 1);
|
||||
percent = getCurvePercent((frameIndex >> 1) - 1, percent);
|
||||
|
||||
float mix = prevFrameValue + (frames[frameIndex + FRAME_VALUE] - prevFrameValue) * percent;
|
||||
ikConstraint.mix += (mix - ikConstraint.mix) * alpha;
|
||||
ikConstraint.bendDirection = bendDirections[(frameIndex - 2) >> 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,17 +34,18 @@ import static com.badlogic.gdx.math.Matrix3.*;
|
||||
|
||||
import com.badlogic.gdx.math.MathUtils;
|
||||
import com.badlogic.gdx.math.Matrix3;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
|
||||
public class Bone {
|
||||
final BoneData data;
|
||||
final Bone parent;
|
||||
float x, y;
|
||||
float rotation;
|
||||
float rotation, rotationIK;
|
||||
float scaleX, scaleY;
|
||||
|
||||
float m00, m01, worldX; // a b x
|
||||
float m10, m11, worldY; // c d y
|
||||
float worldRotation;
|
||||
float worldRotation, worldCos, worldSin;
|
||||
float worldScaleX, worldScaleY;
|
||||
|
||||
/** @param parent May be null. */
|
||||
@ -64,6 +65,7 @@ public class Bone {
|
||||
x = bone.x;
|
||||
y = bone.y;
|
||||
rotation = bone.rotation;
|
||||
rotationIK = bone.rotationIK;
|
||||
scaleX = bone.scaleX;
|
||||
scaleY = bone.scaleY;
|
||||
}
|
||||
@ -71,6 +73,7 @@ public class Bone {
|
||||
/** Computes the world SRT using the parent bone and the local SRT. */
|
||||
public void updateWorldTransform (boolean flipX, boolean flipY) {
|
||||
Bone parent = this.parent;
|
||||
float x = this.x, y = this.y;
|
||||
if (parent != null) {
|
||||
worldX = x * parent.m00 + y * parent.m01 + parent.worldX;
|
||||
worldY = x * parent.m10 + y * parent.m11 + parent.worldY;
|
||||
@ -81,16 +84,18 @@ public class Bone {
|
||||
worldScaleX = scaleX;
|
||||
worldScaleY = scaleY;
|
||||
}
|
||||
worldRotation = data.inheritRotation ? parent.worldRotation + rotation : rotation;
|
||||
worldRotation = data.inheritRotation ? parent.worldRotation + rotationIK : rotationIK;
|
||||
} else {
|
||||
worldX = flipX ? -x : x;
|
||||
worldY = flipY ? -y : y;
|
||||
worldScaleX = scaleX;
|
||||
worldScaleY = scaleY;
|
||||
worldRotation = rotation;
|
||||
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;
|
||||
@ -110,6 +115,7 @@ public class Bone {
|
||||
x = data.x;
|
||||
y = data.y;
|
||||
rotation = data.rotation;
|
||||
rotationIK = rotation;
|
||||
scaleX = data.scaleX;
|
||||
scaleY = data.scaleY;
|
||||
}
|
||||
@ -143,6 +149,7 @@ public class Bone {
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
/** Returns the forward kinetics rotation. */
|
||||
public float getRotation () {
|
||||
return rotation;
|
||||
}
|
||||
@ -151,6 +158,15 @@ public class Bone {
|
||||
this.rotation = rotation;
|
||||
}
|
||||
|
||||
/** Returns the inverse kinetics rotation, as calculated by any IK constraints. */
|
||||
public float getRotationIK () {
|
||||
return rotationIK;
|
||||
}
|
||||
|
||||
public void setRotationIK (float rotationIK) {
|
||||
this.rotationIK = rotationIK;
|
||||
}
|
||||
|
||||
public float getScaleX () {
|
||||
return scaleX;
|
||||
}
|
||||
@ -205,6 +221,14 @@ public class Bone {
|
||||
return worldRotation;
|
||||
}
|
||||
|
||||
public float getWorldCos () {
|
||||
return worldCos;
|
||||
}
|
||||
|
||||
public float getWorldSin () {
|
||||
return worldSin;
|
||||
}
|
||||
|
||||
public float getWorldScaleX () {
|
||||
return worldScaleX;
|
||||
}
|
||||
@ -228,6 +252,23 @@ public class Bone {
|
||||
return worldTransform;
|
||||
}
|
||||
|
||||
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;
|
||||
return world;
|
||||
}
|
||||
|
||||
public Vector2 localToWorld (Vector2 local) {
|
||||
float x = local.x, y = local.y;
|
||||
local.x = x * m00 + y * m01 + worldX;
|
||||
local.y = x * m10 + y * m11 + worldY;
|
||||
return local;
|
||||
}
|
||||
|
||||
public String toString () {
|
||||
return data.name;
|
||||
}
|
||||
|
||||
@ -98,6 +98,11 @@ public class BoneData {
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public void setPosition (float x, float y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public float getRotation () {
|
||||
return rotation;
|
||||
}
|
||||
@ -122,6 +127,11 @@ public class BoneData {
|
||||
this.scaleY = scaleY;
|
||||
}
|
||||
|
||||
public void setScale (float scaleX, float scaleY) {
|
||||
this.scaleX = scaleX;
|
||||
this.scaleY = scaleY;
|
||||
}
|
||||
|
||||
public boolean getInheritScale () {
|
||||
return inheritScale;
|
||||
}
|
||||
|
||||
132
spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java
Normal file
132
spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java
Normal file
@ -0,0 +1,132 @@
|
||||
|
||||
package com.esotericsoftware.spine;
|
||||
|
||||
import com.badlogic.gdx.math.MathUtils;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
|
||||
public class IkConstraint {
|
||||
static private final Vector2 temp = new Vector2();
|
||||
|
||||
final IkConstraintData data;
|
||||
final Array<Bone> bones;
|
||||
Bone target;
|
||||
float mix = 1;
|
||||
int bendDirection;
|
||||
|
||||
public IkConstraint (IkConstraintData data, Skeleton skeleton) {
|
||||
this.data = data;
|
||||
mix = data.mix;
|
||||
bendDirection = data.bendDirection;
|
||||
|
||||
bones = new Array(data.bones.size);
|
||||
if (skeleton != null) {
|
||||
for (BoneData boneData : data.bones)
|
||||
bones.add(skeleton.findBone(boneData.name));
|
||||
target = skeleton.findBone(data.target.name);
|
||||
}
|
||||
}
|
||||
|
||||
/** Copy constructor. */
|
||||
public IkConstraint (IkConstraint ikConstraint) {
|
||||
data = ikConstraint.data;
|
||||
bones = new Array(ikConstraint.bones);
|
||||
target = ikConstraint.target;
|
||||
mix = ikConstraint.mix;
|
||||
bendDirection = ikConstraint.bendDirection;
|
||||
}
|
||||
|
||||
public void apply () {
|
||||
Bone target = this.target;
|
||||
Array<Bone> bones = this.bones;
|
||||
apply(bones.first(), bones.get(1), target.worldX, target.worldY, bendDirection, mix);
|
||||
}
|
||||
|
||||
public Array<Bone> getBones () {
|
||||
return bones;
|
||||
}
|
||||
|
||||
public Bone getTarget () {
|
||||
return target;
|
||||
}
|
||||
|
||||
public void setTarget (Bone target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public float getMix () {
|
||||
return mix;
|
||||
}
|
||||
|
||||
public void setMix (float mix) {
|
||||
this.mix = mix;
|
||||
}
|
||||
|
||||
public int getBendDirection () {
|
||||
return bendDirection;
|
||||
}
|
||||
|
||||
public void setBendDirection (int bendDirection) {
|
||||
this.bendDirection = bendDirection;
|
||||
}
|
||||
|
||||
public IkConstraintData getData () {
|
||||
return data;
|
||||
}
|
||||
|
||||
public String toString () {
|
||||
return data.name;
|
||||
}
|
||||
|
||||
/** Adjusts the parent and child bone rotations so the tip of the child is as close to the target position as possible. The
|
||||
* target is specified in the world coordinate system.
|
||||
* @param child Any descendant bone of the parent. */
|
||||
static public void apply (Bone parent, Bone child, float targetX, float targetY, int bendDirection, float alpha) {
|
||||
float childRotation = child.rotation, parentRotation = parent.rotation;
|
||||
if (alpha == 0) {
|
||||
child.rotationIK = childRotation;
|
||||
parent.rotationIK = parentRotation;
|
||||
return;
|
||||
}
|
||||
Vector2 position = temp;
|
||||
Bone parentParent = parent.parent;
|
||||
if (parentParent != null) {
|
||||
parentParent.worldToLocal(position.set(targetX, targetY));
|
||||
targetX = (position.x - parent.x) * parentParent.worldScaleX;
|
||||
targetY = (position.y - parent.y) * parentParent.worldScaleY;
|
||||
} else {
|
||||
targetX -= parent.x;
|
||||
targetY -= parent.y;
|
||||
}
|
||||
if (child.parent == parent)
|
||||
position.set(child.x, child.y);
|
||||
else
|
||||
parent.worldToLocal(child.parent.localToWorld(position.set(child.x, child.y)));
|
||||
float childX = position.x * parent.worldScaleX, childY = position.y * parent.worldScaleY;
|
||||
float offset = (float)Math.atan2(childY, childX);
|
||||
float len1 = (float)Math.sqrt(childX * childX + childY * childY), len2 = child.data.length * child.worldScaleX;
|
||||
// Based on code by Ryan Juckett with permission: Copyright (c) 2008-2009 Ryan Juckett, http://www.ryanjuckett.com/
|
||||
float cosDenom = 2 * len1 * len2;
|
||||
if (cosDenom < 0.0001f) {
|
||||
child.rotationIK = childRotation
|
||||
+ ((float)Math.atan2(targetY, targetX) * MathUtils.radDeg - parentRotation - childRotation) * alpha;
|
||||
return;
|
||||
}
|
||||
float cos = MathUtils.clamp((targetX * targetX + targetY * targetY - len1 * len1 - len2 * len2) / cosDenom, -1, 1);
|
||||
float childAngle = (float)Math.acos(cos) * bendDirection;
|
||||
float adjacent = len1 + len2 * cos, opposite = len2 * MathUtils.sin(childAngle);
|
||||
float parentAngle = (float)Math.atan2(targetY * adjacent - targetX * opposite, targetX * adjacent + targetY * opposite);
|
||||
float rotation = (parentAngle - offset) * MathUtils.radDeg - parentRotation;
|
||||
if (rotation > 180)
|
||||
rotation -= 360;
|
||||
else if (rotation < -180) //
|
||||
rotation += 360;
|
||||
parent.rotationIK = parentRotation + rotation * alpha;
|
||||
rotation = (childAngle + offset) * MathUtils.radDeg - childRotation;
|
||||
if (rotation > 180)
|
||||
rotation -= 360;
|
||||
else if (rotation < -180) //
|
||||
rotation += 360;
|
||||
child.rotationIK = childRotation + (rotation + parent.worldRotation - child.parent.worldRotation) * alpha;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
|
||||
package com.esotericsoftware.spine;
|
||||
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
|
||||
public class IkConstraintData {
|
||||
final String name;
|
||||
final Array<BoneData> bones = new Array();
|
||||
BoneData target;
|
||||
int bendDirection = 1;
|
||||
float mix = 1;
|
||||
|
||||
public IkConstraintData (String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName () {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Array<BoneData> getBones () {
|
||||
return bones;
|
||||
}
|
||||
|
||||
public BoneData getTarget () {
|
||||
return target;
|
||||
}
|
||||
|
||||
public void setTarget (BoneData target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public int getBendDirection () {
|
||||
return bendDirection;
|
||||
}
|
||||
|
||||
public void setBendDirection (int bendDirection) {
|
||||
this.bendDirection = bendDirection;
|
||||
}
|
||||
|
||||
public float getMix () {
|
||||
return mix;
|
||||
}
|
||||
|
||||
public void setMix (float mix) {
|
||||
this.mix = mix;
|
||||
}
|
||||
|
||||
public String toString () {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@ -39,6 +39,8 @@ public class Skeleton {
|
||||
final SkeletonData data;
|
||||
final Array<Bone> bones;
|
||||
final Array<Slot> slots;
|
||||
final Array<IkConstraint> ikConstraints;
|
||||
private final Array<Array<Bone>> updateBonesCache = new Array();
|
||||
Array<Slot> drawOrder;
|
||||
Skin skin;
|
||||
final Color color;
|
||||
@ -65,7 +67,13 @@ public class Skeleton {
|
||||
drawOrder.add(slot);
|
||||
}
|
||||
|
||||
ikConstraints = new Array(data.ikConstraints.size);
|
||||
for (IkConstraintData ikConstraintData : data.ikConstraints)
|
||||
ikConstraints.add(new IkConstraint(ikConstraintData, this));
|
||||
|
||||
color = new Color(1, 1, 1, 1);
|
||||
|
||||
updateCache();
|
||||
}
|
||||
|
||||
/** Copy constructor. */
|
||||
@ -82,26 +90,84 @@ public class Skeleton {
|
||||
slots = new Array(skeleton.slots.size);
|
||||
for (Slot slot : skeleton.slots) {
|
||||
Bone bone = bones.get(skeleton.bones.indexOf(slot.bone, true));
|
||||
Slot newSlot = new Slot(slot, this, bone);
|
||||
slots.add(newSlot);
|
||||
slots.add(new Slot(slot, this, bone));
|
||||
}
|
||||
|
||||
drawOrder = new Array(slots.size);
|
||||
for (Slot slot : skeleton.drawOrder)
|
||||
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));
|
||||
|
||||
skin = skeleton.skin;
|
||||
color = new Color(skeleton.color);
|
||||
time = skeleton.time;
|
||||
|
||||
updateCache();
|
||||
}
|
||||
|
||||
/** Updates the world transform for each bone. */
|
||||
/** Caches information about bones and IK constraints. Must be called if bones or IK constraints are added or removed. */
|
||||
public void updateCache () {
|
||||
Array<Array<Bone>> updateBonesCache = this.updateBonesCache;
|
||||
Array<IkConstraint> ikConstraints = this.ikConstraints;
|
||||
int ikConstraintsCount = ikConstraints.size;
|
||||
|
||||
int arrayCount = ikConstraintsCount + 1;
|
||||
updateBonesCache.truncate(arrayCount);
|
||||
for (int i = 0, n = updateBonesCache.size; i < n; i++)
|
||||
updateBonesCache.get(i).clear();
|
||||
while (updateBonesCache.size < arrayCount)
|
||||
updateBonesCache.add(new Array());
|
||||
|
||||
Array<Bone> nonIkBones = updateBonesCache.first();
|
||||
|
||||
outer:
|
||||
for (int i = 0, n = bones.size; i < n; i++) {
|
||||
Bone bone = bones.get(i);
|
||||
Bone current = bone;
|
||||
do {
|
||||
for (int ii = 0; ii < ikConstraintsCount; ii++) {
|
||||
IkConstraint ikConstraint = ikConstraints.get(ii);
|
||||
Bone parent = ikConstraint.bones.first();
|
||||
Bone child = ikConstraint.bones.peek();
|
||||
while (true) {
|
||||
if (current == child) {
|
||||
updateBonesCache.get(ii).add(bone);
|
||||
updateBonesCache.get(ii + 1).add(bone);
|
||||
continue outer;
|
||||
}
|
||||
if (child == parent) break;
|
||||
child = child.parent;
|
||||
}
|
||||
}
|
||||
current = current.parent;
|
||||
} while (current != null);
|
||||
nonIkBones.add(bone);
|
||||
}
|
||||
}
|
||||
|
||||
/** Updates the world transform for each bone and applies IK constraints. */
|
||||
public void updateWorldTransform () {
|
||||
Array<Bone> bones = this.bones;
|
||||
for (int i = 0, nn = bones.size; i < nn; i++) {
|
||||
Bone bone = bones.get(i);
|
||||
bone.rotationIK = bone.rotation;
|
||||
}
|
||||
boolean flipX = this.flipX;
|
||||
boolean flipY = this.flipY;
|
||||
Array<Bone> bones = this.bones;
|
||||
for (int i = 0, n = bones.size; i < n; i++)
|
||||
bones.get(i).updateWorldTransform(flipX, 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);
|
||||
if (i == last) break;
|
||||
ikConstraints.get(i).apply();
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
/** Sets the bones and slots to their setup pose values. */
|
||||
@ -114,6 +180,13 @@ public class Skeleton {
|
||||
Array<Bone> bones = this.bones;
|
||||
for (int i = 0, n = bones.size; i < n; i++)
|
||||
bones.get(i).setToSetupPose();
|
||||
|
||||
Array<IkConstraint> ikConstraints = this.ikConstraints;
|
||||
for (int i = 0, n = ikConstraints.size; i < n; i++) {
|
||||
IkConstraint ikConstraint = ikConstraints.get(i);
|
||||
ikConstraint.bendDirection = ikConstraint.data.bendDirection;
|
||||
ikConstraint.mix = ikConstraint.data.mix;
|
||||
}
|
||||
}
|
||||
|
||||
public void setSlotsToSetupPose () {
|
||||
@ -263,6 +336,21 @@ public class Skeleton {
|
||||
throw new IllegalArgumentException("Slot not found: " + slotName);
|
||||
}
|
||||
|
||||
public Array<IkConstraint> getIkConstraints () {
|
||||
return ikConstraints;
|
||||
}
|
||||
|
||||
/** @return May be null. */
|
||||
public IkConstraint findIkConstraint (String ikConstraintName) {
|
||||
if (ikConstraintName == null) throw new IllegalArgumentException("ikConstraintName cannot be null.");
|
||||
Array<IkConstraint> ikConstraints = this.ikConstraints;
|
||||
for (int i = 0, n = ikConstraints.size; i < n; i++) {
|
||||
IkConstraint ikConstraint = ikConstraints.get(i);
|
||||
if (ikConstraint.data.name.equals(ikConstraintName)) return ikConstraint;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Color getColor () {
|
||||
return color;
|
||||
}
|
||||
|
||||
@ -69,6 +69,7 @@ public class SkeletonBinary {
|
||||
static public final int TIMELINE_EVENT = 5;
|
||||
static public final int TIMELINE_DRAWORDER = 6;
|
||||
static public final int TIMELINE_FFD = 7;
|
||||
static public final int TIMELINE_IK = 8;
|
||||
|
||||
static public final int CURVE_LINEAR = 0;
|
||||
static public final int CURVE_STEPPED = 1;
|
||||
@ -123,7 +124,7 @@ public class SkeletonBinary {
|
||||
boneData.inheritScale = input.readBoolean();
|
||||
boneData.inheritRotation = input.readBoolean();
|
||||
if (nonessential) Color.rgba8888ToColor(boneData.getColor(), input.readInt());
|
||||
skeletonData.addBone(boneData);
|
||||
skeletonData.getBones().add(boneData);
|
||||
}
|
||||
|
||||
// Slots.
|
||||
@ -134,19 +135,19 @@ public class SkeletonBinary {
|
||||
Color.rgba8888ToColor(slotData.getColor(), input.readInt());
|
||||
slotData.attachmentName = input.readString();
|
||||
slotData.additiveBlending = input.readBoolean();
|
||||
skeletonData.addSlot(slotData);
|
||||
skeletonData.getSlots().add(slotData);
|
||||
}
|
||||
|
||||
// Default skin.
|
||||
Skin defaultSkin = readSkin(input, "default", nonessential);
|
||||
if (defaultSkin != null) {
|
||||
skeletonData.defaultSkin = defaultSkin;
|
||||
skeletonData.addSkin(defaultSkin);
|
||||
skeletonData.getSkins().add(defaultSkin);
|
||||
}
|
||||
|
||||
// Skins.
|
||||
for (int i = 0, n = input.readInt(true); i < n; i++)
|
||||
skeletonData.addSkin(readSkin(input, input.readString(), nonessential));
|
||||
skeletonData.getSkins().add(readSkin(input, input.readString(), nonessential));
|
||||
|
||||
// Events.
|
||||
for (int i = 0, n = input.readInt(true); i < n; i++) {
|
||||
@ -154,7 +155,7 @@ public class SkeletonBinary {
|
||||
eventData.intValue = input.readInt(false);
|
||||
eventData.floatValue = input.readFloat();
|
||||
eventData.stringValue = input.readString();
|
||||
skeletonData.addEvent(eventData);
|
||||
skeletonData.getEvents().add(eventData);
|
||||
}
|
||||
|
||||
// Animations.
|
||||
@ -497,7 +498,7 @@ public class SkeletonBinary {
|
||||
}
|
||||
|
||||
timelines.shrink();
|
||||
skeletonData.addAnimation(new Animation(name, timelines, duration));
|
||||
skeletonData.getAnimations().add(new Animation(name, timelines, duration));
|
||||
}
|
||||
|
||||
private void readCurve (DataInput input, int frameIndex, CurveTimeline timeline) throws IOException {
|
||||
|
||||
@ -40,23 +40,10 @@ public class SkeletonData {
|
||||
Skin defaultSkin;
|
||||
final Array<EventData> events = new Array();
|
||||
final Array<Animation> animations = new Array();
|
||||
|
||||
public void clear () {
|
||||
bones.clear();
|
||||
slots.clear();
|
||||
skins.clear();
|
||||
defaultSkin = null;
|
||||
events.clear();
|
||||
animations.clear();
|
||||
}
|
||||
final Array<IkConstraintData> ikConstraints = new Array();
|
||||
|
||||
// --- Bones.
|
||||
|
||||
public void addBone (BoneData bone) {
|
||||
if (bone == null) throw new IllegalArgumentException("bone cannot be null.");
|
||||
bones.add(bone);
|
||||
}
|
||||
|
||||
public Array<BoneData> getBones () {
|
||||
return bones;
|
||||
}
|
||||
@ -83,11 +70,6 @@ public class SkeletonData {
|
||||
|
||||
// --- Slots.
|
||||
|
||||
public void addSlot (SlotData slot) {
|
||||
if (slot == null) throw new IllegalArgumentException("slot cannot be null.");
|
||||
slots.add(slot);
|
||||
}
|
||||
|
||||
public Array<SlotData> getSlots () {
|
||||
return slots;
|
||||
}
|
||||
@ -124,11 +106,6 @@ public class SkeletonData {
|
||||
this.defaultSkin = defaultSkin;
|
||||
}
|
||||
|
||||
public void addSkin (Skin skin) {
|
||||
if (skin == null) throw new IllegalArgumentException("skin cannot be null.");
|
||||
skins.add(skin);
|
||||
}
|
||||
|
||||
/** @return May be null. */
|
||||
public Skin findSkin (String skinName) {
|
||||
if (skinName == null) throw new IllegalArgumentException("skinName cannot be null.");
|
||||
@ -144,11 +121,6 @@ public class SkeletonData {
|
||||
|
||||
// --- Events.
|
||||
|
||||
public void addEvent (EventData eventData) {
|
||||
if (eventData == null) throw new IllegalArgumentException("eventData cannot be null.");
|
||||
events.add(eventData);
|
||||
}
|
||||
|
||||
/** @return May be null. */
|
||||
public EventData findEvent (String eventDataName) {
|
||||
if (eventDataName == null) throw new IllegalArgumentException("eventDataName cannot be null.");
|
||||
@ -163,11 +135,6 @@ public class SkeletonData {
|
||||
|
||||
// --- Animations.
|
||||
|
||||
public void addAnimation (Animation animation) {
|
||||
if (animation == null) throw new IllegalArgumentException("animation cannot be null.");
|
||||
animations.add(animation);
|
||||
}
|
||||
|
||||
public Array<Animation> getAnimations () {
|
||||
return animations;
|
||||
}
|
||||
@ -183,6 +150,23 @@ public class SkeletonData {
|
||||
return null;
|
||||
}
|
||||
|
||||
// --- IK
|
||||
|
||||
public Array<IkConstraintData> getIkConstraints () {
|
||||
return ikConstraints;
|
||||
}
|
||||
|
||||
/** @return May be null. */
|
||||
public IkConstraintData findIkConstraint (String ikConstraintName) {
|
||||
if (ikConstraintName == null) throw new IllegalArgumentException("ikConstraintName cannot be null.");
|
||||
Array<IkConstraintData> ikConstraints = this.ikConstraints;
|
||||
for (int i = 0, n = ikConstraints.size; i < n; i++) {
|
||||
IkConstraintData ikConstraint = ikConstraints.get(i);
|
||||
if (ikConstraint.name.equals(ikConstraintName)) return ikConstraint;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
/** @return May be null. */
|
||||
|
||||
@ -111,7 +111,7 @@ public class SkeletonJson {
|
||||
String color = boneMap.getString("color", null);
|
||||
if (color != null) boneData.getColor().set(Color.valueOf(color));
|
||||
|
||||
skeletonData.addBone(boneData);
|
||||
skeletonData.getBones().add(boneData);
|
||||
}
|
||||
|
||||
// Slots.
|
||||
@ -129,7 +129,7 @@ public class SkeletonJson {
|
||||
|
||||
slotData.additiveBlending = slotMap.getBoolean("additive", false);
|
||||
|
||||
skeletonData.addSlot(slotData);
|
||||
skeletonData.getSlots().add(slotData);
|
||||
}
|
||||
|
||||
// Skins.
|
||||
@ -143,7 +143,7 @@ public class SkeletonJson {
|
||||
if (attachment != null) skin.addAttachment(slotIndex, entry.name, attachment);
|
||||
}
|
||||
}
|
||||
skeletonData.addSkin(skin);
|
||||
skeletonData.getSkins().add(skin);
|
||||
if (skin.name.equals("default")) skeletonData.defaultSkin = skin;
|
||||
}
|
||||
|
||||
@ -153,7 +153,7 @@ public class SkeletonJson {
|
||||
eventData.intValue = eventMap.getInt("int", 0);
|
||||
eventData.floatValue = eventMap.getFloat("float", 0f);
|
||||
eventData.stringValue = eventMap.getString("string", null);
|
||||
skeletonData.addEvent(eventData);
|
||||
skeletonData.getEvents().add(eventData);
|
||||
}
|
||||
|
||||
// Animations.
|
||||
@ -461,7 +461,7 @@ public class SkeletonJson {
|
||||
}
|
||||
|
||||
timelines.shrink();
|
||||
skeletonData.addAnimation(new Animation(name, timelines, duration));
|
||||
skeletonData.getAnimations().add(new Animation(name, timelines, duration));
|
||||
}
|
||||
|
||||
void readCurve (CurveTimeline timeline, int frameIndex, JsonValue valueMap) {
|
||||
|
||||
@ -38,7 +38,7 @@ public interface AttachmentLoader {
|
||||
|
||||
/** @return May be null to not load any attachment. */
|
||||
public MeshAttachment newMeshAttachment (Skin skin, String name, String path);
|
||||
|
||||
|
||||
/** @return May be null to not load any attachment. */
|
||||
public SkinnedMeshAttachment newSkinnedMeshAttachment (Skin skin, String name, String path);
|
||||
|
||||
|
||||
@ -34,7 +34,7 @@ import com.esotericsoftware.spine.Bone;
|
||||
import com.esotericsoftware.spine.Skeleton;
|
||||
import com.esotericsoftware.spine.Slot;
|
||||
|
||||
import static com.badlogic.gdx.graphics.g2d.SpriteBatch.*;
|
||||
import static com.badlogic.gdx.graphics.g2d.Batch.*;
|
||||
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user