Updated for Spine 3.0.00.

This commit is contained in:
NathanSweet 2016-01-11 00:30:49 +01:00
parent f6e4027ef1
commit 37f2f9e880
34 changed files with 1367 additions and 733 deletions

View File

@ -1,7 +1,7 @@
Spine Runtimes Software License Spine Runtimes Software License
Version 2.3 Version 2.4
Copyright (c) 2013-2015, Esoteric Software Copyright (c) 2013-2016, Esoteric Software
All rights reserved. All rights reserved.
You are granted a perpetual, non-exclusive, non-sublicensable and You are granted a perpetual, non-exclusive, non-sublicensable and
@ -9,8 +9,8 @@ non-transferable license to use, install, execute and perform the Spine
Runtimes Software (the "Software") and derivative works solely for personal Runtimes Software (the "Software") and derivative works solely for personal
or internal use. Without the written permission of Esoteric Software (see or internal use. Without the written permission of Esoteric Software (see
Section 2 of the Spine Software License Agreement), you may not (a) modify, Section 2 of the Spine Software License Agreement), you may not (a) modify,
translate, adapt or otherwise create derivative works, improvements of the translate, adapt or otherwise create derivative works, improvements of
Software or develop new applications using the Software or (b) remove, the Software or develop new applications using the Software or (b) remove,
delete, alter or obscure any trademarks or any copyright, trademark, patent delete, alter or obscure any trademarks or any copyright, trademark, patent
or other intellectual property or proprietary rights notices on or in the or other intellectual property or proprietary rights notices on or in the
Software, including any copy thereof. Redistributions in binary or source Software, including any copy thereof. Redistributions in binary or source

View File

@ -67,8 +67,8 @@ public class BonePlotting {
while (time < animation.getDuration()) { while (time < animation.getDuration()) {
animation.apply(skeleton, time, time, false, null); animation.apply(skeleton, time, time, false, null);
skeleton.updateWorldTransform(); skeleton.updateWorldTransform();
System.out.println(animation.getName() + "," + bone.getWorldX() + "," + bone.getWorldY() + "," System.out
+ bone.getWorldRotation() + "," + bone.getWorldScaleX() + "," + bone.getWorldScaleY()); .println(animation.getName() + "," + bone.getWorldX() + "," + bone.getWorldY() + "," + bone.getWorldRotationX());
time += fps; time += fps;
} }
} }

View File

@ -160,7 +160,7 @@ public class Box2DExample extends ApplicationAdapter {
if (attachment.body == null) continue; if (attachment.body == null) continue;
float x = skeleton.x + slot.getBone().getWorldX(); float x = skeleton.x + slot.getBone().getWorldX();
float y = skeleton.y + slot.getBone().getWorldY(); float y = skeleton.y + slot.getBone().getWorldY();
float rotation = slot.getBone().getWorldRotation(); float rotation = slot.getBone().getWorldRotationX();
attachment.body.setTransform(x, y, rotation * MathUtils.degRad); attachment.body.setTransform(x, y, rotation * MathUtils.degRad);
} }

View File

@ -42,7 +42,7 @@ import com.badlogic.gdx.graphics.g2d.TextureAtlas;
public class SimpleTest3 extends ApplicationAdapter { public class SimpleTest3 extends ApplicationAdapter {
OrthographicCamera camera; OrthographicCamera camera;
PolygonSpriteBatch batch; PolygonSpriteBatch batch;
SkeletonRenderer renderer; SkeletonMeshRenderer renderer;
SkeletonRendererDebug debugRenderer; SkeletonRendererDebug debugRenderer;
TextureAtlas atlas; TextureAtlas atlas;
@ -52,7 +52,7 @@ public class SimpleTest3 extends ApplicationAdapter {
public void create () { public void create () {
camera = new OrthographicCamera(); camera = new OrthographicCamera();
batch = new PolygonSpriteBatch(); // Required to render meshes. SpriteBatch can't render meshes. batch = new PolygonSpriteBatch(); // Required to render meshes. SpriteBatch can't render meshes.
renderer = new SkeletonRenderer(); renderer = new SkeletonMeshRenderer();
renderer.setPremultipliedAlpha(true); renderer.setPremultipliedAlpha(true);
debugRenderer = new SkeletonRendererDebug(); debugRenderer = new SkeletonRendererDebug();
debugRenderer.setMeshTriangles(false); debugRenderer.setMeshTriangles(false);

View File

@ -45,7 +45,7 @@ import com.badlogic.gdx.graphics.g2d.TextureAtlas;
public class SkeletonAttachmentTest extends ApplicationAdapter { public class SkeletonAttachmentTest extends ApplicationAdapter {
OrthographicCamera camera; OrthographicCamera camera;
PolygonSpriteBatch batch; PolygonSpriteBatch batch;
SkeletonRenderer renderer; SkeletonMeshRenderer renderer;
Skeleton spineboy, goblin; Skeleton spineboy, goblin;
AnimationState spineboyState, goblinState; AnimationState spineboyState, goblinState;
@ -53,7 +53,7 @@ public class SkeletonAttachmentTest extends ApplicationAdapter {
public void create () { public void create () {
camera = new OrthographicCamera(); camera = new OrthographicCamera();
batch = new PolygonSpriteBatch(); batch = new PolygonSpriteBatch();
renderer = new SkeletonRenderer(); renderer = new SkeletonMeshRenderer();
renderer.setPremultipliedAlpha(true); renderer.setPremultipliedAlpha(true);
{ {
@ -77,9 +77,9 @@ public class SkeletonAttachmentTest extends ApplicationAdapter {
} }
{ {
TextureAtlas atlas = new TextureAtlas(Gdx.files.internal("goblins/goblins-ffd.atlas")); TextureAtlas atlas = new TextureAtlas(Gdx.files.internal("goblins/goblins-mesh.atlas"));
SkeletonJson json = new SkeletonJson(atlas); SkeletonJson json = new SkeletonJson(atlas);
SkeletonData skeletonData = json.readSkeletonData(Gdx.files.internal("goblins/goblins-ffd.json")); SkeletonData skeletonData = json.readSkeletonData(Gdx.files.internal("goblins/goblins-mesh.json"));
goblin = new Skeleton(skeletonData); goblin = new Skeleton(skeletonData);
goblin.setSkin("goblin"); goblin.setSkin("goblin");
goblin.setSlotsToSetupPose(); goblin.setSlotsToSetupPose();

View File

@ -777,62 +777,4 @@ public class Animation {
ikConstraint.bendDirection = (int)frames[frameIndex + PREV_FRAME_BEND_DIRECTION]; ikConstraint.bendDirection = (int)frames[frameIndex + PREV_FRAME_BEND_DIRECTION];
} }
} }
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;
}
public float[] getFrames () {
return frames;
}
/** Sets the time and value of the specified keyframe. */
public void setFrame (int frameIndex, float time, boolean flip) {
frameIndex *= 2;
frames[frameIndex] = time;
frames[frameIndex + 1] = flip ? 1 : 0;
}
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha) {
float[] frames = this.frames;
if (time < frames[0]) {
if (lastTime > time) apply(skeleton, lastTime, Integer.MAX_VALUE, null, 0);
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;
setFlip(skeleton.bones.get(boneIndex), frames[frameIndex + 1] != 0);
}
protected void setFlip (Bone bone, boolean flip) {
bone.setFlipX(flip);
}
}
static public class FlipYTimeline extends FlipXTimeline {
public FlipYTimeline (int frameCount) {
super(frameCount);
}
protected void setFlip (Bone bone, boolean flip) {
bone.setFlipY(flip);
}
}
} }

View File

@ -37,7 +37,7 @@ import com.badlogic.gdx.utils.Pool.Poolable;
/** Stores state for an animation and automatically mixes between animations. */ /** Stores state for an animation and automatically mixes between animations. */
public class AnimationState { public class AnimationState {
private final AnimationStateData data; private AnimationStateData data;
private Array<TrackEntry> tracks = new Array(); private Array<TrackEntry> tracks = new Array();
private final Array<Event> events = new Array(); private final Array<Event> events = new Array();
private final Array<AnimationStateListener> listeners = new Array(); private final Array<AnimationStateListener> listeners = new Array();
@ -49,6 +49,10 @@ public class AnimationState {
} }
}; };
/** Creates an uninitialized AnimationState. The animation state data must be set. */
public AnimationState () {
}
public AnimationState (AnimationStateData data) { public AnimationState (AnimationStateData data) {
if (data == null) throw new IllegalArgumentException("data cannot be null."); if (data == null) throw new IllegalArgumentException("data cannot be null.");
this.data = data; this.data = data;
@ -269,6 +273,10 @@ public class AnimationState {
listeners.removeValue(listener, true); listeners.removeValue(listener, true);
} }
public void clearListeners () {
listeners.clear();
}
public float getTimeScale () { public float getTimeScale () {
return timeScale; return timeScale;
} }
@ -281,6 +289,10 @@ public class AnimationState {
return data; return data;
} }
public void setData (AnimationStateData data) {
this.data = data;
}
/** Returns the list of tracks that have animations, which may contain nulls. */ /** Returns the list of tracks that have animations, which may contain nulls. */
public Array<TrackEntry> getTracks () { public Array<TrackEntry> getTracks () {
return tracks; return tracks;

View File

@ -31,26 +31,23 @@
package com.esotericsoftware.spine; package com.esotericsoftware.spine;
import static com.badlogic.gdx.math.MathUtils.*;
import static com.badlogic.gdx.math.Matrix3.*; import static com.badlogic.gdx.math.Matrix3.*;
import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Matrix3; import com.badlogic.gdx.math.Matrix3;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
public class Bone { public class Bone implements Updatable {
final BoneData data; final BoneData data;
final Skeleton skeleton; final Skeleton skeleton;
final Bone parent; final Bone parent;
float x, y; float x, y, rotation, scaleX, scaleY;
float rotation, rotationIK; float appliedRotation, appliedScaleX, appliedScaleY;
float scaleX, scaleY;
boolean flipX, flipY;
float m00, m01, worldX; // a b x float a, b, worldX;
float m10, m11, worldY; // c d y float c, d, worldY;
float worldRotation; float worldSignX, worldSignY;
float worldScaleX, worldScaleY;
boolean worldFlipX, worldFlipY;
Bone (BoneData data) { Bone (BoneData data) {
this.data = data; this.data = data;
@ -78,69 +75,135 @@ public class Bone {
x = bone.x; x = bone.x;
y = bone.y; y = bone.y;
rotation = bone.rotation; rotation = bone.rotation;
rotationIK = bone.rotationIK;
scaleX = bone.scaleX; scaleX = bone.scaleX;
scaleY = bone.scaleY; scaleY = bone.scaleY;
flipX = bone.flipX;
flipY = bone.flipY;
} }
/** Computes the world SRT using the parent bone and the local SRT. */ /** Computes the world SRT using the parent bone and this bone's local SRT. */
public void updateWorldTransform () { public void updateWorldTransform () {
Skeleton skeleton = this.skeleton; updateWorldTransform(x, y, rotation, scaleX, scaleY);
}
/** Computes the world SRT using the parent bone and the specified local SRT. */
public void updateWorldTransform (float x, float y, float rotation, float scaleX, float scaleY) {
appliedRotation = rotation;
appliedScaleX = scaleX;
appliedScaleY = scaleY;
float cos = MathUtils.cosDeg(rotation), sin = MathUtils.sinDeg(rotation);
float la = cos * scaleX, lb = -sin * scaleY, lc = sin * scaleX, ld = cos * scaleY;
Bone parent = this.parent; Bone parent = this.parent;
float x = this.x, y = this.y; if (parent == null) { // Root bone.
if (parent != null) { Skeleton skeleton = this.skeleton;
worldX = x * parent.m00 + y * parent.m01 + parent.worldX;
worldY = x * parent.m10 + y * parent.m11 + parent.worldY;
if (data.inheritScale) {
worldScaleX = parent.worldScaleX * scaleX;
worldScaleY = parent.worldScaleY * scaleY;
} else {
worldScaleX = scaleX;
worldScaleY = scaleY;
}
worldRotation = data.inheritRotation ? parent.worldRotation + rotationIK : rotationIK;
worldFlipX = parent.worldFlipX ^ flipX;
worldFlipY = parent.worldFlipY ^ flipY;
} else {
boolean skeletonFlipX = skeleton.flipX, skeletonFlipY = skeleton.flipY; boolean skeletonFlipX = skeleton.flipX, skeletonFlipY = skeleton.flipY;
worldX = skeletonFlipX ? -x : x; if (skeletonFlipX) {
worldY = skeletonFlipY ? -y : y; scaleX = -scaleX;
worldScaleX = scaleX; x = -x;
worldScaleY = scaleY; }
worldRotation = rotationIK; if (skeletonFlipY) {
worldFlipX = skeletonFlipX ^ flipX; scaleY = -scaleY;
worldFlipY = skeletonFlipY ^ flipY; y = -y;
}
a = la;
b = lb;
c = lc;
d = ld;
worldX = x;
worldY = y;
worldSignX = Math.signum(scaleX);
worldSignY = Math.signum(scaleY);
return;
} }
float cos = MathUtils.cosDeg(worldRotation);
float sin = MathUtils.sinDeg(worldRotation); float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
if (worldFlipX) { worldX = pa * x + pb * y + parent.worldX;
m00 = -cos * worldScaleX; worldY = pc * x + pd * y + parent.worldY;
m01 = sin * worldScaleY; worldSignX = parent.worldSignX * Math.signum(scaleX);
worldSignY = parent.worldSignY * Math.signum(scaleY);
if (data.inheritRotation && data.inheritScale) {
a = pa * la + pb * lc;
b = pa * lb + pb * ld;
c = pc * la + pd * lc;
d = pc * lb + pd * ld;
} else if (data.inheritRotation) { // No scale inheritance.
Bone p = parent;
pa = 1;
pb = 0;
pc = 0;
pd = 1;
while (p != null) {
cos = MathUtils.cosDeg(p.appliedRotation);
sin = MathUtils.sinDeg(p.appliedRotation);
float a = pa * cos + pb * sin;
float b = pa * -sin + pb * cos;
float c = pc * cos + pd * sin;
float d = pc * -sin + pd * cos;
pa = a;
pb = b;
pc = c;
pd = d;
p = p.parent;
}
a = pa * la + pb * lc;
b = pa * lb + pb * ld;
c = pc * la + pd * lc;
d = pc * lb + pd * ld;
} else if (data.inheritScale) { // No rotation inheritance.
Bone p = parent;
pa = 1;
pb = 0;
pc = 0;
pd = 1;
while (p != null) {
float r = p.rotation;
cos = MathUtils.cosDeg(r);
sin = MathUtils.sinDeg(r);
float psx = p.appliedScaleX, psy = p.appliedScaleY;
float za = cos * psx, zb = -sin * psy, zc = sin * psx, zd = cos * psy;
float temp = pa * za + pb * zc;
pb = pa * zb + pb * zd;
pa = temp;
temp = pc * za + pd * zc;
pd = pc * zb + pd * zd;
pc = temp;
if (psx < 0) r = PI - r;
cos = MathUtils.cosDeg(-r);
sin = MathUtils.sinDeg(-r);
temp = pa * cos + pb * sin;
pb = pa * -sin + pb * cos;
pa = temp;
temp = pc * cos + pd * sin;
pd = pc * -sin + pd * cos;
pc = temp;
p = p.parent;
}
a = pa * la + pb * lc;
b = pa * lb + pb * ld;
c = pc * la + pd * lc;
d = pc * lb + pd * ld;
} else { } else {
m00 = cos * worldScaleX; a = la;
m01 = -sin * worldScaleY; b = lb;
} c = lc;
if (worldFlipY) { d = ld;
m10 = -sin * worldScaleX;
m11 = -cos * worldScaleY;
} else {
m10 = sin * worldScaleX;
m11 = cos * worldScaleY;
} }
} }
/** Same as {@link #updateWorldTransform()}. This method exists for Bone to implement {@link Updatable}. */
public void update () {
updateWorldTransform(x, y, rotation, scaleX, scaleY);
}
public void setToSetupPose () { public void setToSetupPose () {
BoneData data = this.data; BoneData data = this.data;
x = data.x; x = data.x;
y = data.y; y = data.y;
rotation = data.rotation; rotation = data.rotation;
rotationIK = rotation;
scaleX = data.scaleX; scaleX = data.scaleX;
scaleY = data.scaleY; scaleY = data.scaleY;
flipX = data.flipX;
flipY = data.flipY;
} }
public BoneData getData () { public BoneData getData () {
@ -185,15 +248,6 @@ public class Bone {
this.rotation = rotation; 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 () { public float getScaleX () {
return scaleX; return scaleX;
} }
@ -220,36 +274,20 @@ public class Bone {
scaleY = scale; scaleY = scale;
} }
public boolean getFlipX () { public float getA () {
return flipX; return a;
} }
public void setFlipX (boolean flipX) { public float getB () {
this.flipX = flipX; return b;
} }
public boolean getFlipY () { public float getC () {
return flipY; return c;
} }
public void setFlipY (boolean flipY) { public float getD () {
this.flipY = flipY; return d;
}
public float getM00 () {
return m00;
}
public float getM01 () {
return m01;
}
public float getM10 () {
return m10;
}
public float getM11 () {
return m11;
} }
public float getWorldX () { public float getWorldX () {
@ -260,33 +298,37 @@ public class Bone {
return worldY; return worldY;
} }
public float getWorldRotation () { public float getWorldRotationX () {
return worldRotation; return (float)Math.atan2(c, a) * MathUtils.radDeg;
}
public float getWorldRotationY () {
return (float)Math.atan2(d, b) * MathUtils.radDeg;
} }
public float getWorldScaleX () { public float getWorldScaleX () {
return worldScaleX; return (float)Math.sqrt(a * a + b * b) * worldSignX;
} }
public float getWorldScaleY () { public float getWorldScaleY () {
return worldScaleY; return (float)Math.sqrt(c * c + d * d) * worldSignY;
} }
public boolean getWorldFlipX () { public float getWorldSignX () {
return worldFlipX; return worldSignX;
} }
public boolean getWorldFlipY () { public float getWorldSignY () {
return worldFlipY; return worldSignY;
} }
public Matrix3 getWorldTransform (Matrix3 worldTransform) { public Matrix3 getWorldTransform (Matrix3 worldTransform) {
if (worldTransform == null) throw new IllegalArgumentException("worldTransform cannot be null."); if (worldTransform == null) throw new IllegalArgumentException("worldTransform cannot be null.");
float[] val = worldTransform.val; float[] val = worldTransform.val;
val[M00] = m00; val[M00] = a;
val[M01] = m01; val[M01] = b;
val[M10] = m10; val[M10] = c;
val[M11] = m11; val[M11] = d;
val[M02] = worldX; val[M02] = worldX;
val[M12] = worldY; val[M12] = worldY;
val[M20] = 0; val[M20] = 0;
@ -297,21 +339,17 @@ public class Bone {
public Vector2 worldToLocal (Vector2 world) { public Vector2 worldToLocal (Vector2 world) {
float x = world.x - worldX, y = world.y - worldY; float x = world.x - worldX, y = world.y - worldY;
float m00 = this.m00, m10 = this.m10, m01 = this.m01, m11 = this.m11; float a = this.a, b = this.b, c = this.c, d = this.d;
if (worldFlipX != worldFlipY) { float invDet = 1 / (a * d - b * c);
m00 = -m00; world.x = (x * a * invDet - y * b * invDet);
m11 = -m11; world.y = (y * d * invDet - x * c * invDet);
}
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; return world;
} }
public Vector2 localToWorld (Vector2 local) { public Vector2 localToWorld (Vector2 local) {
float x = local.x, y = local.y; float x = local.x, y = local.y;
local.x = x * m00 + y * m01 + worldX; local.x = x * a + y * b + worldX;
local.y = x * m10 + y * m11 + worldY; local.y = x * c + y * d + worldY;
return local; return local;
} }

View File

@ -40,8 +40,7 @@ public class BoneData {
float x, y; float x, y;
float rotation; float rotation;
float scaleX = 1, scaleY = 1; float scaleX = 1, scaleY = 1;
boolean flipX, flipY; boolean inheritScale, inheritRotation;
boolean inheritScale = true, inheritRotation = true;
// Nonessential. // Nonessential.
final Color color = new Color(0.61f, 0.61f, 0.61f, 1); final Color color = new Color(0.61f, 0.61f, 0.61f, 1);
@ -65,8 +64,6 @@ public class BoneData {
rotation = bone.rotation; rotation = bone.rotation;
scaleX = bone.scaleX; scaleX = bone.scaleX;
scaleY = bone.scaleY; scaleY = bone.scaleY;
flipX = bone.flipX;
flipY = bone.flipY;
} }
/** @return May be null. */ /** @return May be null. */
@ -136,22 +133,6 @@ public class BoneData {
this.scaleY = scaleY; 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 () { public boolean getInheritScale () {
return inheritScale; return inheritScale;
} }

View File

@ -33,12 +33,9 @@ package com.esotericsoftware.spine;
import static com.badlogic.gdx.math.MathUtils.*; import static com.badlogic.gdx.math.MathUtils.*;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
public class IkConstraint { public class IkConstraint implements Updatable {
static private final Vector2 temp = new Vector2();
final IkConstraintData data; final IkConstraintData data;
final Array<Bone> bones; final Array<Bone> bones;
Bone target; Bone target;
@ -59,15 +56,21 @@ public class IkConstraint {
} }
/** Copy constructor. */ /** Copy constructor. */
public IkConstraint (IkConstraint ikConstraint, Array<Bone> bones, Bone target) { public IkConstraint (IkConstraint ikConstraint, Skeleton skeleton) {
data = ikConstraint.data; data = ikConstraint.data;
this.bones = bones; bones = new Array(ikConstraint.bones.size);
this.target = target; for (Bone bone : ikConstraint.bones)
bones.add(skeleton.bones.get(bone.skeleton.bones.indexOf(bone, true)));
target = skeleton.bones.get(ikConstraint.target.skeleton.bones.indexOf(ikConstraint.target, true));
mix = ikConstraint.mix; mix = ikConstraint.mix;
bendDirection = ikConstraint.bendDirection; bendDirection = ikConstraint.bendDirection;
} }
public void apply () { public void apply () {
update();
}
public void update () {
Bone target = this.target; Bone target = this.target;
Array<Bone> bones = this.bones; Array<Bone> bones = this.bones;
switch (bones.size) { switch (bones.size) {
@ -113,67 +116,143 @@ public class IkConstraint {
} }
public String toString () { public String toString () {
return data.name; return data.name + " CONSTRAINT";
} }
/** Adjusts the bone rotation so the tip is as close to the target position as possible. The target is specified in the world /** Adjusts the bone rotation so the tip is as close to the target position as possible. The target is specified in the world
* coordinate system. */ * coordinate system. */
static public void apply (Bone bone, float targetX, float targetY, float alpha) { static public void apply (Bone bone, float targetX, float targetY, float alpha) {
float parentRotation = (!bone.data.inheritRotation || bone.parent == null) ? 0 : bone.parent.worldRotation; float parentRotation = bone.parent == null ? 0 : bone.parent.getWorldRotationX();
float rotation = bone.rotation; float rotation = bone.rotation;
float rotationIK = (float)Math.atan2(targetY - bone.worldY, targetX - bone.worldX) * radDeg - parentRotation; float rotationIK = atan2(targetY - bone.worldY, targetX - bone.worldX) * radDeg - parentRotation;
bone.rotationIK = rotation + (rotationIK - rotation) * alpha; if (rotationIK > 180)
rotationIK -= 360;
else if (rotationIK < -180) rotationIK += 360;
bone.updateWorldTransform(bone.x, bone.y, rotation + (rotationIK - rotation) * alpha, bone.scaleX, bone.scaleY);
} }
/** Adjusts the parent and child bone rotations so the tip of the child is as close to the target position as possible. The /** 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. * target is specified in the world coordinate system.
* @param child Any descendant bone of the parent. */ * @param child Any descendant bone of the parent. */
static public void apply (Bone parent, Bone child, float targetX, float targetY, int bendDirection, float alpha) { static public void apply (Bone parent, Bone child, float targetX, float targetY, int bendDir, float alpha) {
float childRotation = child.rotation, parentRotation = parent.rotation; if (alpha == 0) return;
if (alpha == 0) { float px = parent.x, py = parent.y, psx = parent.scaleX, psy = parent.scaleY, csx = child.scaleX, cy = child.y;
child.rotationIK = childRotation; int offset1, offset2, sign2;
parent.rotationIK = parentRotation; if (psx < 0) {
return; psx = -psx;
} offset1 = 180;
Vector2 position = temp; sign2 = -1;
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 { } else {
targetX -= parent.x; offset1 = 0;
targetY -= parent.y; sign2 = 1;
} }
if (child.parent == parent) if (psy < 0) {
position.set(child.x, child.y); psy = -psy;
else sign2 = -sign2;
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) * radDeg - parentRotation - childRotation)
* alpha;
return;
} }
float cos = clamp((targetX * targetX + targetY * targetY - len1 * len1 - len2 * len2) / cosDenom, -1, 1); if (csx < 0) {
float childAngle = (float)Math.acos(cos) * bendDirection; csx = -csx;
float adjacent = len1 + len2 * cos, opposite = len2 * sin(childAngle); offset2 = 180;
float parentAngle = (float)Math.atan2(targetY * adjacent - targetX * opposite, targetX * adjacent + targetY * opposite); } else
float rotation = (parentAngle - offset) * radDeg - parentRotation; offset2 = 0;
if (rotation > 180) Bone pp = parent.parent;
rotation -= 360; float tx, ty, dx, dy;
else if (rotation < -180) // if (pp == null) {
rotation += 360; tx = targetX - px;
parent.rotationIK = parentRotation + rotation * alpha; ty = targetY - py;
rotation = (childAngle + offset) * radDeg - childRotation; dx = child.worldX - px;
if (rotation > 180) dy = child.worldY - py;
rotation -= 360; } else {
else if (rotation < -180) // float a = pp.a, b = pp.b, c = pp.c, d = pp.d, invDet = 1 / (a * d - b * c);
rotation += 360; float wx = pp.worldX, wy = pp.worldY, x = targetX - wx, y = targetY - wy;
child.rotationIK = childRotation + (rotation + parent.worldRotation - child.parent.worldRotation) * alpha; tx = (x * d - y * b) * invDet - px;
ty = (y * a - x * c) * invDet - py;
x = child.worldX - wx;
y = child.worldY - wy;
dx = (x * d - y * b) * invDet - px;
dy = (y * a - x * c) * invDet - py;
}
float l1 = (float)Math.sqrt(dx * dx + dy * dy), l2 = child.data.length * csx, a1, a2;
outer:
if (Math.abs(psx - psy) <= 0.0001f) {
l2 *= psx;
float cos = (tx * tx + ty * ty - l1 * l1 - l2 * l2) / (2 * l1 * l2);
if (cos < -1)
cos = -1;
else if (cos > 1) cos = 1;
a2 = (float)Math.acos(cos) * bendDir;
float a = l1 + l2 * cos, o = l2 * sin(a2);
a1 = atan2(ty * a - tx * o, tx * a + ty * o);
} else {
cy = 0;
float a = psx * l2, b = psy * l2, ta = atan2(ty, tx);
float aa = a * a, bb = b * b, ll = l1 * l1, dd = tx * tx + ty * ty;
float c0 = bb * ll + aa * dd - aa * bb, c1 = -2 * bb * l1, c2 = bb - aa;
float d = c1 * c1 - 4 * c2 * c0;
if (d >= 0) {
float q = (float)Math.sqrt(d);
if (c1 < 0) q = -q;
q = -(c1 + q) / 2;
float r0 = q / c2, r1 = c0 / q;
float r = Math.abs(r0) < Math.abs(r1) ? r0 : r1;
if (r * r <= dd) {
float y = (float)Math.sqrt(dd - r * r) * bendDir;
a1 = ta - atan2(y, r);
a2 = atan2(y / psy, (r - l1) / psx);
break outer;
}
}
float minAngle = 0, minDist = Float.MAX_VALUE, minX = 0, minY = 0;
float maxAngle = 0, maxDist = 0, maxX = 0, maxY = 0;
float x = l1 + a, dist = x * x;
if (dist > maxDist) {
maxAngle = 0;
maxDist = dist;
maxX = x;
}
x = l1 - a;
dist = x * x;
if (dist < minDist) {
minAngle = PI;
minDist = dist;
minX = x;
}
float angle = (float)Math.acos(-a * l1 / (aa - bb));
x = a * cos(angle) + l1;
float y = b * sin(angle);
dist = x * x + y * y;
if (dist < minDist) {
minAngle = angle;
minDist = dist;
minX = x;
minY = y;
}
if (dist > maxDist) {
maxAngle = angle;
maxDist = dist;
maxX = x;
maxY = y;
}
if (dd <= (minDist + maxDist) / 2) {
a1 = ta - atan2(minY * bendDir, minX);
a2 = minAngle * bendDir;
} else {
a1 = ta - atan2(maxY * bendDir, maxX);
a2 = maxAngle * bendDir;
}
}
float offset = atan2(cy, child.x) * sign2;
a1 = (a1 - offset) * radDeg + offset1;
a2 = (a2 + offset) * radDeg * sign2 + offset2;
if (a1 > 180)
a1 -= 360;
else if (a1 < -180) a1 += 360;
if (a2 > 180)
a2 -= 360;
else if (a2 < -180) a2 += 360;
float rotation = parent.rotation;
parent.updateWorldTransform(parent.x, parent.y, rotation + (a1 - rotation) * alpha, parent.scaleX, parent.scaleY);
rotation = child.rotation;
child.updateWorldTransform(child.x, cy, rotation + (a2 - rotation) * alpha, child.scaleX, child.scaleY);
} }
} }

View File

@ -45,7 +45,8 @@ public class Skeleton {
final Array<Slot> slots; final Array<Slot> slots;
Array<Slot> drawOrder; Array<Slot> drawOrder;
final Array<IkConstraint> ikConstraints; final Array<IkConstraint> ikConstraints;
private final Array<Array<Bone>> boneCache = new Array(); final Array<TransformConstraint> transformConstraints;
private final Array<Updatable> updateCache = new Array();
Skin skin; Skin skin;
final Color color; final Color color;
float time; float time;
@ -75,6 +76,10 @@ public class Skeleton {
for (IkConstraintData ikConstraintData : data.ikConstraints) for (IkConstraintData ikConstraintData : data.ikConstraints)
ikConstraints.add(new IkConstraint(ikConstraintData, this)); ikConstraints.add(new IkConstraint(ikConstraintData, this));
transformConstraints = new Array(data.transformConstraints.size);
for (TransformConstraintData transformConstraintData : data.transformConstraints)
transformConstraints.add(new TransformConstraint(transformConstraintData, this));
color = new Color(1, 1, 1, 1); color = new Color(1, 1, 1, 1);
updateCache(); updateCache();
@ -102,13 +107,12 @@ public class Skeleton {
drawOrder.add(slots.get(skeleton.slots.indexOf(slot, true))); drawOrder.add(slots.get(skeleton.slots.indexOf(slot, true)));
ikConstraints = new Array(skeleton.ikConstraints.size); ikConstraints = new Array(skeleton.ikConstraints.size);
for (IkConstraint ikConstraint : skeleton.ikConstraints) { for (IkConstraint ikConstraint : skeleton.ikConstraints)
Bone target = bones.get(skeleton.bones.indexOf(ikConstraint.target, true)); ikConstraints.add(new IkConstraint(ikConstraint, this));
Array<Bone> ikBones = new Array(ikConstraint.bones.size);
for (Bone bone : ikConstraint.bones) transformConstraints = new Array(skeleton.transformConstraints.size);
ikBones.add(bones.get(skeleton.bones.indexOf(bone, true))); for (TransformConstraint transformConstraint : skeleton.transformConstraints)
ikConstraints.add(new IkConstraint(ikConstraint, ikBones, target)); transformConstraints.add(new TransformConstraint(transformConstraint, this));
}
skin = skeleton.skin; skin = skeleton.skin;
color = new Color(skeleton.color); color = new Color(skeleton.color);
@ -119,72 +123,49 @@ public class Skeleton {
updateCache(); updateCache();
} }
/** Caches information about bones and IK constraints. Must be called if bones or IK constraints are added or removed. */ /** Caches information about bones and constraints. Must be called if bones or constraints are added or removed. */
public void updateCache () { public void updateCache () {
Array<Bone> bones = this.bones; Array<Bone> bones = this.bones;
Array<Array<Bone>> boneCache = this.boneCache; Array<Updatable> updateCache = this.updateCache;
Array<IkConstraint> ikConstraints = this.ikConstraints; Array<IkConstraint> ikConstraints = this.ikConstraints;
Array<TransformConstraint> transformConstraints = this.transformConstraints;
int ikConstraintsCount = ikConstraints.size; int ikConstraintsCount = ikConstraints.size;
int transformConstraintsCount = transformConstraints.size;
int arrayCount = ikConstraintsCount + 1; updateCache.clear();
while (boneCache.size < arrayCount)
boneCache.add(new Array());
for (int i = 0; i < arrayCount; i++)
boneCache.get(i).clear();
Array<Bone> nonIkBones = boneCache.first();
outer:
for (int i = 0, n = bones.size; i < n; i++) { for (int i = 0, n = bones.size; i < n; i++) {
Bone bone = bones.get(i); Bone bone = bones.get(i);
Bone current = bone; updateCache.add(bone);
do { for (int ii = 0; ii < transformConstraintsCount; ii++) {
for (int ii = 0; ii < ikConstraintsCount; ii++) { TransformConstraint transformConstraint = transformConstraints.get(ii);
IkConstraint ikConstraint = ikConstraints.get(ii); if (bone == transformConstraint.bone) {
Bone parent = ikConstraint.bones.first(); updateCache.add(transformConstraint);
Bone child = ikConstraint.bones.peek(); break;
while (true) {
if (current == child) {
boneCache.get(ii).add(bone);
boneCache.get(ii + 1).add(bone);
continue outer;
}
if (child == parent) break;
child = child.parent;
}
} }
current = current.parent; }
} while (current != null); for (int ii = 0; ii < ikConstraintsCount; ii++) {
nonIkBones.add(bone); IkConstraint ikConstraint = ikConstraints.get(ii);
if (bone == ikConstraint.bones.peek()) {
updateCache.add(ikConstraint);
break;
}
}
} }
} }
/** Updates the world transform for each bone and applies IK constraints. */ /** Updates the world transform for each bone and applies constraints. */
public void updateWorldTransform () { public void updateWorldTransform () {
Array<Bone> bones = this.bones; Array<Updatable> updateCache = this.updateCache;
for (int i = 0, nn = bones.size; i < nn; i++) { for (int i = 0, n = updateCache.size; i < n; i++)
Bone bone = bones.get(i); updateCache.get(i).update();
bone.rotationIK = bone.rotation;
}
Array<Array<Bone>> boneCache = this.boneCache;
Array<IkConstraint> ikConstraints = this.ikConstraints;
int i = 0, last = ikConstraints.size;
while (true) {
Array<Bone> updateBones = boneCache.get(i);
for (int ii = 0, nn = updateBones.size; ii < nn; ii++)
updateBones.get(ii).updateWorldTransform();
if (i == last) break;
ikConstraints.get(i).apply();
i++;
}
} }
/** Sets the bones and slots to their setup pose values. */ /** Sets the bones, constraints, and slots to their setup pose values. */
public void setToSetupPose () { public void setToSetupPose () {
setBonesToSetupPose(); setBonesToSetupPose();
setSlotsToSetupPose(); setSlotsToSetupPose();
} }
/** Sets the bones and constraints to their setup pose values. */
public void setBonesToSetupPose () { public void setBonesToSetupPose () {
Array<Bone> bones = this.bones; Array<Bone> bones = this.bones;
for (int i = 0, n = bones.size; i < n; i++) for (int i = 0, n = bones.size; i < n; i++)
@ -192,9 +173,17 @@ public class Skeleton {
Array<IkConstraint> ikConstraints = this.ikConstraints; Array<IkConstraint> ikConstraints = this.ikConstraints;
for (int i = 0, n = ikConstraints.size; i < n; i++) { for (int i = 0, n = ikConstraints.size; i < n; i++) {
IkConstraint ikConstraint = ikConstraints.get(i); IkConstraint constraint = ikConstraints.get(i);
ikConstraint.bendDirection = ikConstraint.data.bendDirection; constraint.bendDirection = constraint.data.bendDirection;
ikConstraint.mix = ikConstraint.data.mix; constraint.mix = constraint.data.mix;
}
Array<TransformConstraint> transformConstraints = this.transformConstraints;
for (int i = 0, n = transformConstraints.size; i < n; i++) {
TransformConstraint constraint = transformConstraints.get(i);
constraint.translateMix = constraint.data.translateMix;
constraint.x = constraint.data.x;
constraint.y = constraint.data.y;
} }
} }
@ -350,12 +339,27 @@ public class Skeleton {
} }
/** @return May be null. */ /** @return May be null. */
public IkConstraint findIkConstraint (String ikConstraintName) { public IkConstraint findIkConstraint (String constraintName) {
if (ikConstraintName == null) throw new IllegalArgumentException("ikConstraintName cannot be null."); if (constraintName == null) throw new IllegalArgumentException("constraintName cannot be null.");
Array<IkConstraint> ikConstraints = this.ikConstraints; Array<IkConstraint> ikConstraints = this.ikConstraints;
for (int i = 0, n = ikConstraints.size; i < n; i++) { for (int i = 0, n = ikConstraints.size; i < n; i++) {
IkConstraint ikConstraint = ikConstraints.get(i); IkConstraint ikConstraint = ikConstraints.get(i);
if (ikConstraint.data.name.equals(ikConstraintName)) return ikConstraint; if (ikConstraint.data.name.equals(constraintName)) return ikConstraint;
}
return null;
}
public Array<TransformConstraint> getTransformConstraints () {
return transformConstraints;
}
/** @return May be null. */
public TransformConstraint findTransformConstraint (String constraintName) {
if (constraintName == null) throw new IllegalArgumentException("constraintName cannot be null.");
Array<TransformConstraint> transformConstraints = this.transformConstraints;
for (int i = 0, n = transformConstraints.size; i < n; i++) {
TransformConstraint constraint = transformConstraints.get(i);
if (constraint.data.name.equals(constraintName)) return constraint;
} }
return null; return null;
} }
@ -371,19 +375,13 @@ public class Skeleton {
float[] vertices = null; float[] vertices = null;
Attachment attachment = slot.attachment; Attachment attachment = slot.attachment;
if (attachment instanceof RegionAttachment) { if (attachment instanceof RegionAttachment) {
RegionAttachment region = (RegionAttachment)attachment; vertices = ((RegionAttachment)attachment).updateWorldVertices(slot, false);
region.updateWorldVertices(slot, false);
vertices = region.getWorldVertices();
} else if (attachment instanceof MeshAttachment) { } else if (attachment instanceof MeshAttachment) {
MeshAttachment mesh = (MeshAttachment)attachment; vertices = ((MeshAttachment)attachment).updateWorldVertices(slot, true);
mesh.updateWorldVertices(slot, true);
vertices = mesh.getWorldVertices();
} else if (attachment instanceof SkinnedMeshAttachment) { } else if (attachment instanceof SkinnedMeshAttachment) {
SkinnedMeshAttachment mesh = (SkinnedMeshAttachment)attachment; vertices = ((SkinnedMeshAttachment)attachment).updateWorldVertices(slot, true);
mesh.updateWorldVertices(slot, true);
vertices = mesh.getWorldVertices();
} }
if (vertices != null) { if (vertices != null) {
for (int ii = 0, nn = vertices.length; ii < nn; ii += 5) { for (int ii = 0, nn = vertices.length; ii < nn; ii += 5) {

View File

@ -47,8 +47,6 @@ import com.esotericsoftware.spine.Animation.CurveTimeline;
import com.esotericsoftware.spine.Animation.DrawOrderTimeline; import com.esotericsoftware.spine.Animation.DrawOrderTimeline;
import com.esotericsoftware.spine.Animation.EventTimeline; import com.esotericsoftware.spine.Animation.EventTimeline;
import com.esotericsoftware.spine.Animation.FfdTimeline; import com.esotericsoftware.spine.Animation.FfdTimeline;
import com.esotericsoftware.spine.Animation.FlipXTimeline;
import com.esotericsoftware.spine.Animation.FlipYTimeline;
import com.esotericsoftware.spine.Animation.IkConstraintTimeline; import com.esotericsoftware.spine.Animation.IkConstraintTimeline;
import com.esotericsoftware.spine.Animation.RotateTimeline; import com.esotericsoftware.spine.Animation.RotateTimeline;
import com.esotericsoftware.spine.Animation.ScaleTimeline; import com.esotericsoftware.spine.Animation.ScaleTimeline;
@ -69,8 +67,6 @@ public class SkeletonBinary {
static public final int TIMELINE_TRANSLATE = 2; static public final int TIMELINE_TRANSLATE = 2;
static public final int TIMELINE_ATTACHMENT = 3; static public final int TIMELINE_ATTACHMENT = 3;
static public final int TIMELINE_COLOR = 4; 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_LINEAR = 0;
static public final int CURVE_STEPPED = 1; static public final int CURVE_STEPPED = 1;
@ -135,10 +131,6 @@ public class SkeletonBinary {
boneData.scaleY = input.readFloat(); boneData.scaleY = input.readFloat();
boneData.rotation = input.readFloat(); boneData.rotation = input.readFloat();
boneData.length = input.readFloat() * scale; 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()); if (nonessential) Color.rgba8888ToColor(boneData.color, input.readInt());
skeletonData.bones.add(boneData); skeletonData.bones.add(boneData);
} }
@ -419,17 +411,6 @@ public class SkeletonBinary {
duration = Math.max(duration, timeline.getFrames()[frameCount * 3 - 3]); duration = Math.max(duration, timeline.getFrames()[frameCount * 3 - 3]);
break; 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;
}
} }
} }
} }

View File

@ -42,6 +42,7 @@ public class SkeletonData {
final Array<EventData> events = new Array(); final Array<EventData> events = new Array();
final Array<Animation> animations = new Array(); final Array<Animation> animations = new Array();
final Array<IkConstraintData> ikConstraints = new Array(); final Array<IkConstraintData> ikConstraints = new Array();
final Array<TransformConstraintData> transformConstraints = new Array();
float width, height; float width, height;
String version, hash, imagesPath; String version, hash, imagesPath;
@ -153,19 +154,36 @@ public class SkeletonData {
return null; return null;
} }
// --- IK // --- IK constraints
public Array<IkConstraintData> getIkConstraints () { public Array<IkConstraintData> getIkConstraints () {
return ikConstraints; return ikConstraints;
} }
/** @return May be null. */ /** @return May be null. */
public IkConstraintData findIkConstraint (String ikConstraintName) { public IkConstraintData findIkConstraint (String constraintName) {
if (ikConstraintName == null) throw new IllegalArgumentException("ikConstraintName cannot be null."); if (constraintName == null) throw new IllegalArgumentException("constraintName cannot be null.");
Array<IkConstraintData> ikConstraints = this.ikConstraints; Array<IkConstraintData> ikConstraints = this.ikConstraints;
for (int i = 0, n = ikConstraints.size; i < n; i++) { for (int i = 0, n = ikConstraints.size; i < n; i++) {
IkConstraintData ikConstraint = ikConstraints.get(i); IkConstraintData constraint = ikConstraints.get(i);
if (ikConstraint.name.equals(ikConstraintName)) return ikConstraint; if (constraint.name.equals(constraintName)) return constraint;
}
return null;
}
// --- Transform constraints
public Array<TransformConstraintData> getTransformConstraints () {
return transformConstraints;
}
/** @return May be null. */
public TransformConstraintData findTransformConstraint (String constraintName) {
if (constraintName == null) throw new IllegalArgumentException("constraintName cannot be null.");
Array<TransformConstraintData> transformConstraints = this.transformConstraints;
for (int i = 0, n = transformConstraints.size; i < n; i++) {
TransformConstraintData constraint = transformConstraints.get(i);
if (constraint.name.equals(constraintName)) return constraint;
} }
return null; return null;
} }

View File

@ -46,8 +46,6 @@ import com.esotericsoftware.spine.Animation.CurveTimeline;
import com.esotericsoftware.spine.Animation.DrawOrderTimeline; import com.esotericsoftware.spine.Animation.DrawOrderTimeline;
import com.esotericsoftware.spine.Animation.EventTimeline; import com.esotericsoftware.spine.Animation.EventTimeline;
import com.esotericsoftware.spine.Animation.FfdTimeline; import com.esotericsoftware.spine.Animation.FfdTimeline;
import com.esotericsoftware.spine.Animation.FlipXTimeline;
import com.esotericsoftware.spine.Animation.FlipYTimeline;
import com.esotericsoftware.spine.Animation.IkConstraintTimeline; import com.esotericsoftware.spine.Animation.IkConstraintTimeline;
import com.esotericsoftware.spine.Animation.RotateTimeline; import com.esotericsoftware.spine.Animation.RotateTimeline;
import com.esotericsoftware.spine.Animation.ScaleTimeline; import com.esotericsoftware.spine.Animation.ScaleTimeline;
@ -118,10 +116,6 @@ public class SkeletonJson {
boneData.rotation = boneMap.getFloat("rotation", 0); boneData.rotation = boneMap.getFloat("rotation", 0);
boneData.scaleX = boneMap.getFloat("scaleX", 1); boneData.scaleX = boneMap.getFloat("scaleX", 1);
boneData.scaleY = boneMap.getFloat("scaleY", 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);
String color = boneMap.getString("color", null); String color = boneMap.getString("color", null);
if (color != null) boneData.getColor().set(Color.valueOf(color)); if (color != null) boneData.getColor().set(Color.valueOf(color));
@ -389,20 +383,6 @@ public class SkeletonJson {
timelines.add(timeline); timelines.add(timeline);
duration = Math.max(duration, timeline.getFrames()[timeline.getFrameCount() * 3 - 3]); 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 } else
throw new RuntimeException("Invalid timeline type for a bone: " + timelineName + " (" + boneMap.name + ")"); throw new RuntimeException("Invalid timeline type for a bone: " + timelineName + " (" + boneMap.name + ")");
} }

View File

@ -0,0 +1,108 @@
/******************************************************************************
* Spine Runtimes Software License
* Version 2.3
*
* Copyright (c) 2013-2015, Esoteric Software
* All rights reserved.
*
* You are granted a perpetual, non-exclusive, non-sublicensable and
* non-transferable license to use, install, execute and perform the Spine
* Runtimes Software (the "Software") and derivative works solely for personal
* or internal use. Without the written permission of Esoteric Software (see
* Section 2 of the Spine Software License Agreement), you may not (a) modify,
* translate, adapt or otherwise create derivative works, improvements of the
* Software or develop new applications using the Software or (b) remove,
* delete, alter or obscure any trademarks or any copyright, trademark, patent
* or other intellectual property or proprietary rights notices on or in the
* Software, including any copy thereof. Redistributions in binary or source
* form must include this license and terms.
*
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
package com.esotericsoftware.spine;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.PolygonSpriteBatch;
import com.badlogic.gdx.utils.Array;
import com.esotericsoftware.spine.attachments.Attachment;
import com.esotericsoftware.spine.attachments.MeshAttachment;
import com.esotericsoftware.spine.attachments.RegionAttachment;
import com.esotericsoftware.spine.attachments.SkeletonAttachment;
import com.esotericsoftware.spine.attachments.SkinnedMeshAttachment;
public class SkeletonMeshRenderer extends SkeletonRenderer<PolygonSpriteBatch> {
static private final short[] quadTriangles = {0, 1, 2, 2, 3, 0};
@SuppressWarnings("null")
public void draw (PolygonSpriteBatch batch, Skeleton skeleton) {
boolean premultipliedAlpha = this.premultipliedAlpha;
BlendMode blendMode = null;
float[] vertices = null;
short[] triangles = null;
Array<Slot> drawOrder = skeleton.drawOrder;
for (int i = 0, n = drawOrder.size; i < n; i++) {
Slot slot = drawOrder.get(i);
Attachment attachment = slot.attachment;
Texture texture = null;
if (attachment instanceof RegionAttachment) {
RegionAttachment region = (RegionAttachment)attachment;
vertices = region.updateWorldVertices(slot, premultipliedAlpha);
triangles = quadTriangles;
texture = region.getRegion().getTexture();
} else if (attachment instanceof MeshAttachment) {
MeshAttachment mesh = (MeshAttachment)attachment;
vertices = mesh.updateWorldVertices(slot, premultipliedAlpha);
triangles = mesh.getTriangles();
texture = mesh.getRegion().getTexture();
} else if (attachment instanceof SkinnedMeshAttachment) {
SkinnedMeshAttachment mesh = (SkinnedMeshAttachment)attachment;
vertices = mesh.updateWorldVertices(slot, premultipliedAlpha);
triangles = mesh.getTriangles();
texture = mesh.getRegion().getTexture();
} else if (attachment instanceof SkeletonAttachment) {
Skeleton attachmentSkeleton = ((SkeletonAttachment)attachment).getSkeleton();
if (attachmentSkeleton == null) continue;
Bone bone = slot.getBone();
Bone rootBone = attachmentSkeleton.getRootBone();
float oldScaleX = rootBone.getScaleX();
float oldScaleY = rootBone.getScaleY();
float oldRotation = rootBone.getRotation();
attachmentSkeleton.setPosition(skeleton.getX() + bone.getWorldX(), skeleton.getY() + bone.getWorldY());
// rootBone.setScaleX(1 + bone.getWorldScaleX() - oldScaleX);
// rootBone.setScaleY(1 + bone.getWorldScaleY() - oldScaleY);
rootBone.setRotation(oldRotation + bone.getWorldRotationX());
attachmentSkeleton.updateWorldTransform();
draw(batch, attachmentSkeleton);
attachmentSkeleton.setPosition(0, 0);
rootBone.setScaleX(oldScaleX);
rootBone.setScaleY(oldScaleY);
rootBone.setRotation(oldRotation);
}
if (texture != null) {
BlendMode slotBlendMode = slot.data.getBlendMode();
if (slotBlendMode != blendMode) {
blendMode = slotBlendMode;
batch.setBlendFunction(blendMode.getSource(premultipliedAlpha), blendMode.getDest());
}
batch.draw(texture, vertices, 0, vertices.length, triangles, 0, triangles.length);
}
}
}
}

View File

@ -31,9 +31,7 @@
package com.esotericsoftware.spine; package com.esotericsoftware.spine;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Batch; import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.PolygonSpriteBatch;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import com.esotericsoftware.spine.attachments.Attachment; import com.esotericsoftware.spine.attachments.Attachment;
import com.esotericsoftware.spine.attachments.MeshAttachment; import com.esotericsoftware.spine.attachments.MeshAttachment;
@ -41,78 +39,14 @@ import com.esotericsoftware.spine.attachments.RegionAttachment;
import com.esotericsoftware.spine.attachments.SkeletonAttachment; import com.esotericsoftware.spine.attachments.SkeletonAttachment;
import com.esotericsoftware.spine.attachments.SkinnedMeshAttachment; import com.esotericsoftware.spine.attachments.SkinnedMeshAttachment;
public class SkeletonRenderer { public class SkeletonRenderer<T extends Batch> {
static private final short[] quadTriangles = {0, 1, 2, 2, 3, 0}; boolean premultipliedAlpha;
private boolean premultipliedAlpha; public SkeletonRenderer () {
super();
@SuppressWarnings("null")
public void draw (PolygonSpriteBatch batch, Skeleton skeleton) {
boolean premultipliedAlpha = this.premultipliedAlpha;
BlendMode blendMode = null;
float[] vertices = null;
short[] triangles = null;
Array<Slot> drawOrder = skeleton.drawOrder;
for (int i = 0, n = drawOrder.size; i < n; i++) {
Slot slot = drawOrder.get(i);
Attachment attachment = slot.attachment;
Texture texture = null;
if (attachment instanceof RegionAttachment) {
RegionAttachment region = (RegionAttachment)attachment;
region.updateWorldVertices(slot, premultipliedAlpha);
vertices = region.getWorldVertices();
triangles = quadTriangles;
texture = region.getRegion().getTexture();
} else if (attachment instanceof MeshAttachment) {
MeshAttachment mesh = (MeshAttachment)attachment;
mesh.updateWorldVertices(slot, premultipliedAlpha);
vertices = mesh.getWorldVertices();
triangles = mesh.getTriangles();
texture = mesh.getRegion().getTexture();
} else if (attachment instanceof SkinnedMeshAttachment) {
SkinnedMeshAttachment mesh = (SkinnedMeshAttachment)attachment;
mesh.updateWorldVertices(slot, premultipliedAlpha);
vertices = mesh.getWorldVertices();
triangles = mesh.getTriangles();
texture = mesh.getRegion().getTexture();
} else if (attachment instanceof SkeletonAttachment) {
Skeleton attachmentSkeleton = ((SkeletonAttachment)attachment).getSkeleton();
if (attachmentSkeleton == null) continue;
Bone bone = slot.getBone();
Bone rootBone = attachmentSkeleton.getRootBone();
float oldScaleX = rootBone.getScaleX();
float oldScaleY = rootBone.getScaleY();
float oldRotation = rootBone.getRotation();
attachmentSkeleton.setPosition(skeleton.getX() + bone.getWorldX(), skeleton.getY() + bone.getWorldY());
rootBone.setScaleX(1 + bone.getWorldScaleX() - oldScaleX);
rootBone.setScaleY(1 + bone.getWorldScaleY() - oldScaleY);
rootBone.setRotation(oldRotation + bone.getWorldRotation());
attachmentSkeleton.updateWorldTransform();
draw(batch, attachmentSkeleton);
attachmentSkeleton.setPosition(0, 0);
rootBone.setScaleX(oldScaleX);
rootBone.setScaleY(oldScaleY);
rootBone.setRotation(oldRotation);
}
if (texture != null) {
BlendMode slotBlendMode = slot.data.getBlendMode();
if (slotBlendMode != blendMode) {
blendMode = slotBlendMode;
batch.setBlendFunction(blendMode.getSource(premultipliedAlpha), blendMode.getDest());
}
batch.draw(texture, vertices, 0, vertices.length, triangles, 0, triangles.length);
}
}
} }
public void draw (Batch batch, Skeleton skeleton) { public void draw (T batch, Skeleton skeleton) {
boolean premultipliedAlpha = this.premultipliedAlpha; boolean premultipliedAlpha = this.premultipliedAlpha;
BlendMode blendMode = null; BlendMode blendMode = null;
@ -122,8 +56,7 @@ public class SkeletonRenderer {
Attachment attachment = slot.attachment; Attachment attachment = slot.attachment;
if (attachment instanceof RegionAttachment) { if (attachment instanceof RegionAttachment) {
RegionAttachment regionAttachment = (RegionAttachment)attachment; RegionAttachment regionAttachment = (RegionAttachment)attachment;
regionAttachment.updateWorldVertices(slot, premultipliedAlpha); float[] vertices = regionAttachment.updateWorldVertices(slot, premultipliedAlpha);
float[] vertices = regionAttachment.getWorldVertices();
BlendMode slotBlendMode = slot.data.getBlendMode(); BlendMode slotBlendMode = slot.data.getBlendMode();
if (slotBlendMode != blendMode) { if (slotBlendMode != blendMode) {
blendMode = slotBlendMode; blendMode = slotBlendMode;
@ -132,7 +65,7 @@ public class SkeletonRenderer {
batch.draw(regionAttachment.getRegion().getTexture(), vertices, 0, 20); batch.draw(regionAttachment.getRegion().getTexture(), vertices, 0, 20);
} else if (attachment instanceof MeshAttachment || attachment instanceof SkinnedMeshAttachment) { } else if (attachment instanceof MeshAttachment || attachment instanceof SkinnedMeshAttachment) {
throw new RuntimeException("PolygonSpriteBatch is required to render meshes."); throw new RuntimeException("SkeletonMeshRenderer is required to render meshes.");
} else if (attachment instanceof SkeletonAttachment) { } else if (attachment instanceof SkeletonAttachment) {
Skeleton attachmentSkeleton = ((SkeletonAttachment)attachment).getSkeleton(); Skeleton attachmentSkeleton = ((SkeletonAttachment)attachment).getSkeleton();
@ -143,9 +76,9 @@ public class SkeletonRenderer {
float oldScaleY = rootBone.getScaleY(); float oldScaleY = rootBone.getScaleY();
float oldRotation = rootBone.getRotation(); float oldRotation = rootBone.getRotation();
attachmentSkeleton.setPosition(skeleton.getX() + bone.getWorldX(), skeleton.getY() + bone.getWorldY()); attachmentSkeleton.setPosition(skeleton.getX() + bone.getWorldX(), skeleton.getY() + bone.getWorldY());
rootBone.setScaleX(1 + bone.getWorldScaleX() - oldScaleX); // rootBone.setScaleX(1 + bone.getWorldScaleX() - oldScaleX);
rootBone.setScaleY(1 + bone.getWorldScaleY() - oldScaleY); // rootBone.setScaleY(1 + bone.getWorldScaleY() - oldScaleY);
rootBone.setRotation(oldRotation + bone.getWorldRotation()); rootBone.setRotation(oldRotation + bone.getWorldRotationX());
attachmentSkeleton.updateWorldTransform(); attachmentSkeleton.updateWorldTransform();
draw(batch, attachmentSkeleton); draw(batch, attachmentSkeleton);

View File

@ -87,8 +87,8 @@ public class SkeletonRendererDebug {
for (int i = 0, n = bones.size; i < n; i++) { for (int i = 0, n = bones.size; i < n; i++) {
Bone bone = bones.get(i); Bone bone = bones.get(i);
if (bone.parent == null) continue; if (bone.parent == null) continue;
float x = skeletonX + bone.data.length * bone.m00 + bone.worldX; float x = skeletonX + bone.data.length * bone.a + bone.worldX;
float y = skeletonY + bone.data.length * bone.m10 + bone.worldY; float y = skeletonY + bone.data.length * bone.c + bone.worldY;
shapes.rectLine(skeletonX + bone.worldX, skeletonY + bone.worldY, x, y, boneWidth * scale); shapes.rectLine(skeletonX + bone.worldX, skeletonY + bone.worldY, x, y, boneWidth * scale);
} }
shapes.end(); shapes.end();
@ -105,8 +105,7 @@ public class SkeletonRendererDebug {
Attachment attachment = slot.attachment; Attachment attachment = slot.attachment;
if (attachment instanceof RegionAttachment) { if (attachment instanceof RegionAttachment) {
RegionAttachment regionAttachment = (RegionAttachment)attachment; RegionAttachment regionAttachment = (RegionAttachment)attachment;
regionAttachment.updateWorldVertices(slot, false); float[] vertices = regionAttachment.updateWorldVertices(slot, false);
float[] vertices = regionAttachment.getWorldVertices();
shapes.line(vertices[X1], vertices[Y1], vertices[X2], vertices[Y2]); shapes.line(vertices[X1], vertices[Y1], vertices[X2], vertices[Y2]);
shapes.line(vertices[X2], vertices[Y2], vertices[X3], vertices[Y3]); shapes.line(vertices[X2], vertices[Y2], vertices[X3], vertices[Y3]);
shapes.line(vertices[X3], vertices[Y3], vertices[X4], vertices[Y4]); shapes.line(vertices[X3], vertices[Y3], vertices[X4], vertices[Y4]);

View File

@ -0,0 +1,76 @@
package com.esotericsoftware.spine;
import com.badlogic.gdx.math.Vector2;
public class TransformConstraint implements Updatable {
final TransformConstraintData data;
Bone bone, target;
float translateMix, x, y;
final Vector2 temp = new Vector2();
public TransformConstraint (TransformConstraintData data, Skeleton skeleton) {
this.data = data;
translateMix = data.translateMix;
if (skeleton != null) {
bone = skeleton.findBone(data.bone.name);
target = skeleton.findBone(data.target.name);
}
}
/** Copy constructor. */
public TransformConstraint (TransformConstraint constraint, Skeleton skeleton) {
data = constraint.data;
translateMix = data.translateMix;
bone = skeleton.bones.get(constraint.bone.skeleton.bones.indexOf(constraint.target, true));
target = skeleton.bones.get(constraint.target.skeleton.bones.indexOf(constraint.target, true));
}
public void apply () {
update();
}
public void update () {
float translateMix = this.translateMix;
if (translateMix > 0) {
Bone bone = this.bone;
Vector2 temp = this.temp;
target.localToWorld(temp.set(x, y));
bone.worldX += (temp.x - bone.worldX) * translateMix;
bone.worldY += (temp.y - bone.worldY) * translateMix;
}
}
public Bone getBone () {
return bone;
}
public void setBone (Bone bone) {
this.bone = bone;
}
public Bone getTarget () {
return target;
}
public void setTarget (Bone target) {
this.target = target;
}
public float getTranslateMix () {
return translateMix;
}
public void setTranslateMix (float translateMix) {
this.translateMix = translateMix;
}
public TransformConstraintData getData () {
return data;
}
public String toString () {
return data.name;
}
}

View File

@ -0,0 +1,61 @@
package com.esotericsoftware.spine;
public class TransformConstraintData {
final String name;
BoneData bone, target;
float translateMix;
float x, y;
public TransformConstraintData (String name) {
this.name = name;
}
public String getName () {
return name;
}
public BoneData getBone () {
return bone;
}
public void setBone (BoneData bone) {
this.bone = bone;
}
public BoneData getTarget () {
return target;
}
public void setTarget (BoneData target) {
this.target = target;
}
public float getTranslateMix () {
return translateMix;
}
public void setTranslateMix (float translateMix) {
this.translateMix = translateMix;
}
public float getX () {
return x;
}
public void setX (float x) {
this.x = x;
}
public float getY () {
return y;
}
public void setY (float y) {
this.y = y;
}
public String toString () {
return name;
}
}

View File

@ -0,0 +1,6 @@
package com.esotericsoftware.spine;
public interface Updatable {
public void update ();
}

View File

@ -44,10 +44,10 @@ public class BoundingBoxAttachment extends Attachment {
public void computeWorldVertices (Bone bone, float[] worldVertices) { public void computeWorldVertices (Bone bone, float[] worldVertices) {
Skeleton skeleton = bone.getSkeleton(); Skeleton skeleton = bone.getSkeleton();
float x = skeleton.getX() + bone.getWorldX(), y = skeleton.getY() + bone.getWorldY(); float x = skeleton.getX() + bone.getWorldX(), y = skeleton.getY() + bone.getWorldY();
float m00 = bone.getM00(); float m00 = bone.getA();
float m01 = bone.getM01(); float m01 = bone.getB();
float m10 = bone.getM10(); float m10 = bone.getC();
float m11 = bone.getM11(); float m11 = bone.getD();
float[] vertices = this.vertices; float[] vertices = this.vertices;
for (int i = 0, n = vertices.length; i < n; i += 2) { for (int i = 0, n = vertices.length; i < n; i += 2) {
float px = vertices[i]; float px = vertices[i];

View File

@ -98,7 +98,8 @@ public class MeshAttachment extends Attachment {
} }
} }
public void updateWorldVertices (Slot slot, boolean premultipliedAlpha) { /** @return The updated world vertices. */
public float[] updateWorldVertices (Slot slot, boolean premultipliedAlpha) {
Skeleton skeleton = slot.getSkeleton(); Skeleton skeleton = slot.getSkeleton();
Color skeletonColor = skeleton.getColor(); Color skeletonColor = skeleton.getColor();
Color slotColor = slot.getColor(); Color slotColor = slot.getColor();
@ -117,7 +118,7 @@ public class MeshAttachment extends Attachment {
if (slotVertices.size == vertices.length) vertices = slotVertices.items; if (slotVertices.size == vertices.length) vertices = slotVertices.items;
Bone bone = slot.getBone(); Bone bone = slot.getBone();
float x = skeleton.getX() + bone.getWorldX(), y = skeleton.getY() + bone.getWorldY(); float x = skeleton.getX() + bone.getWorldX(), y = skeleton.getY() + bone.getWorldY();
float m00 = bone.getM00(), m01 = bone.getM01(), m10 = bone.getM10(), m11 = bone.getM11(); float m00 = bone.getA(), m01 = bone.getB(), m10 = bone.getC(), m11 = bone.getD();
for (int v = 0, w = 0, n = worldVertices.length; w < n; v += 2, w += 5) { for (int v = 0, w = 0, n = worldVertices.length; w < n; v += 2, w += 5) {
float vx = vertices[v]; float vx = vertices[v];
float vy = vertices[v + 1]; float vy = vertices[v + 1];
@ -125,6 +126,7 @@ public class MeshAttachment extends Attachment {
worldVertices[w + 1] = vx * m10 + vy * m11 + y; worldVertices[w + 1] = vx * m10 + vy * m11 + y;
worldVertices[w + 2] = color; worldVertices[w + 2] = color;
} }
return worldVertices;
} }
public float[] getWorldVertices () { public float[] getWorldVertices () {

View File

@ -146,7 +146,8 @@ public class RegionAttachment extends Attachment {
return region; return region;
} }
public void updateWorldVertices (Slot slot, boolean premultipliedAlpha) { /** @return The updated world vertices. */
public float[] updateWorldVertices (Slot slot, boolean premultipliedAlpha) {
Skeleton skeleton = slot.getSkeleton(); Skeleton skeleton = slot.getSkeleton();
Color skeletonColor = skeleton.getColor(); Color skeletonColor = skeleton.getColor();
Color slotColor = slot.getColor(); Color slotColor = slot.getColor();
@ -163,7 +164,7 @@ public class RegionAttachment extends Attachment {
float[] offset = this.offset; float[] offset = this.offset;
Bone bone = slot.getBone(); Bone bone = slot.getBone();
float x = skeleton.getX() + bone.getWorldX(), y = skeleton.getY() + bone.getWorldY(); float x = skeleton.getX() + bone.getWorldX(), y = skeleton.getY() + bone.getWorldY();
float m00 = bone.getM00(), m01 = bone.getM01(), m10 = bone.getM10(), m11 = bone.getM11(); float m00 = bone.getA(), m01 = bone.getB(), m10 = bone.getC(), m11 = bone.getD();
float offsetX, offsetY; float offsetX, offsetY;
offsetX = offset[BRX]; offsetX = offset[BRX];
@ -189,6 +190,7 @@ public class RegionAttachment extends Attachment {
vertices[X4] = offsetX * m00 + offsetY * m01 + x; // ur vertices[X4] = offsetX * m00 + offsetY * m01 + x; // ur
vertices[Y4] = offsetX * m10 + offsetY * m11 + y; vertices[Y4] = offsetX * m10 + offsetY * m11 + y;
vertices[C4] = color; vertices[C4] = color;
return vertices;
} }
public float[] getWorldVertices () { public float[] getWorldVertices () {

View File

@ -46,7 +46,7 @@ public class RegionSequenceAttachment extends RegionAttachment {
super(name); super(name);
} }
public void updateWorldVertices (Slot slot, boolean premultipliedAlpha) { public float[] updateWorldVertices (Slot slot, boolean premultipliedAlpha) {
if (regions == null) throw new IllegalStateException("Regions have not been set: " + this); if (regions == null) throw new IllegalStateException("Regions have not been set: " + this);
int frameIndex = (int)(slot.getAttachmentTime() / frameTime); int frameIndex = (int)(slot.getAttachmentTime() / frameTime);
@ -74,7 +74,7 @@ public class RegionSequenceAttachment extends RegionAttachment {
} }
setRegion(regions[frameIndex]); setRegion(regions[frameIndex]);
super.updateWorldVertices(slot, premultipliedAlpha); return super.updateWorldVertices(slot, premultipliedAlpha);
} }
public TextureRegion[] getRegions () { public TextureRegion[] getRegions () {

View File

@ -99,7 +99,8 @@ public class SkinnedMeshAttachment extends Attachment {
} }
} }
public void updateWorldVertices (Slot slot, boolean premultipliedAlpha) { /** @return The updated world vertices. */
public float[] updateWorldVertices (Slot slot, boolean premultipliedAlpha) {
Skeleton skeleton = slot.getSkeleton(); Skeleton skeleton = slot.getSkeleton();
Color skeletonColor = skeleton.getColor(); Color skeletonColor = skeleton.getColor();
Color meshColor = slot.getColor(); Color meshColor = slot.getColor();
@ -126,8 +127,8 @@ public class SkinnedMeshAttachment extends Attachment {
for (; v < nn; v++, b += 3) { for (; v < nn; v++, b += 3) {
Bone bone = (Bone)skeletonBones[bones[v]]; Bone bone = (Bone)skeletonBones[bones[v]];
float vx = weights[b], vy = weights[b + 1], weight = weights[b + 2]; float vx = weights[b], vy = weights[b + 1], weight = weights[b + 2];
wx += (vx * bone.getM00() + vy * bone.getM01() + bone.getWorldX()) * weight; wx += (vx * bone.getA() + vy * bone.getB() + bone.getWorldX()) * weight;
wy += (vx * bone.getM10() + vy * bone.getM11() + bone.getWorldY()) * weight; wy += (vx * bone.getC() + vy * bone.getD() + bone.getWorldY()) * weight;
} }
worldVertices[w] = wx + x; worldVertices[w] = wx + x;
worldVertices[w + 1] = wy + y; worldVertices[w + 1] = wy + y;
@ -141,14 +142,15 @@ public class SkinnedMeshAttachment extends Attachment {
for (; v < nn; v++, b += 3, f += 2) { for (; v < nn; v++, b += 3, f += 2) {
Bone bone = (Bone)skeletonBones[bones[v]]; Bone bone = (Bone)skeletonBones[bones[v]];
float vx = weights[b] + ffd[f], vy = weights[b + 1] + ffd[f + 1], weight = weights[b + 2]; float vx = weights[b] + ffd[f], vy = weights[b + 1] + ffd[f + 1], weight = weights[b + 2];
wx += (vx * bone.getM00() + vy * bone.getM01() + bone.getWorldX()) * weight; wx += (vx * bone.getA() + vy * bone.getB() + bone.getWorldX()) * weight;
wy += (vx * bone.getM10() + vy * bone.getM11() + bone.getWorldY()) * weight; wy += (vx * bone.getC() + vy * bone.getD() + bone.getWorldY()) * weight;
} }
worldVertices[w] = wx + x; worldVertices[w] = wx + x;
worldVertices[w + 1] = wy + y; worldVertices[w + 1] = wy + y;
worldVertices[w + 2] = color; worldVertices[w + 2] = color;
} }
} }
return worldVertices;
} }
public float[] getWorldVertices () { public float[] getWorldVertices () {

View File

@ -0,0 +1,68 @@
package com.esotericsoftware.spine.utils;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.esotericsoftware.spine.AnimationState;
import com.esotericsoftware.spine.Skeleton;
import com.esotericsoftware.spine.SkeletonRenderer;
/** A scene2d actor that draws a skeleton. */
public class SkeletonActor extends Actor {
private SkeletonRenderer renderer;
private Skeleton skeleton;
AnimationState state;
/** Creates an uninitialized SkeletonActor. The renderer, skeleton, and animation state must be set before use. */
public SkeletonActor () {
}
public SkeletonActor (SkeletonRenderer renderer, Skeleton skeleton, AnimationState state) {
this.renderer = renderer;
this.skeleton = skeleton;
this.state = state;
}
public void act (float delta) {
state.update(delta);
state.apply(skeleton);
skeleton.updateWorldTransform();
super.act(delta);
}
public void draw (Batch batch, float parentAlpha) {
Color color = skeleton.getColor();
float oldAlpha = color.a;
skeleton.getColor().a *= parentAlpha;
skeleton.setPosition(getX(), getY());
renderer.draw(batch, skeleton);
color.a = oldAlpha;
}
public SkeletonRenderer getRenderer () {
return renderer;
}
public void setRenderer (SkeletonRenderer renderer) {
this.renderer = renderer;
}
public Skeleton getSkeleton () {
return skeleton;
}
public void setSkeleton (Skeleton skeleton) {
this.skeleton = skeleton;
}
public AnimationState getAnimationState () {
return state;
}
public void setAnimationState (AnimationState state) {
this.state = state;
}
}

View File

@ -0,0 +1,102 @@
package com.esotericsoftware.spine.utils;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Pool;
import com.esotericsoftware.spine.AnimationState;
import com.esotericsoftware.spine.AnimationState.TrackEntry;
import com.esotericsoftware.spine.AnimationStateData;
import com.esotericsoftware.spine.Skeleton;
import com.esotericsoftware.spine.SkeletonData;
import com.esotericsoftware.spine.SkeletonRenderer;
import com.esotericsoftware.spine.Skin;
public class SkeletonActorPool extends Pool<SkeletonActor> {
private SkeletonRenderer renderer;
SkeletonData skeletonData;
AnimationStateData stateData;
private final Pool<Skeleton> skeletonPool;
private final Pool<AnimationState> statePool;
private final Array<SkeletonActor> obtained;
public SkeletonActorPool (SkeletonRenderer renderer, SkeletonData skeletonData, AnimationStateData stateData) {
this(renderer, skeletonData, stateData, 16, Integer.MAX_VALUE);
}
public SkeletonActorPool (SkeletonRenderer renderer, SkeletonData skeletonData, AnimationStateData stateData,
int initialCapacity, int max) {
super(initialCapacity, max);
this.renderer = renderer;
this.skeletonData = skeletonData;
this.stateData = stateData;
obtained = new Array(false, initialCapacity);
skeletonPool = new Pool<Skeleton>(initialCapacity, max) {
protected Skeleton newObject () {
return new Skeleton(SkeletonActorPool.this.skeletonData);
}
protected void reset (Skeleton skeleton) {
skeleton.setColor(Color.WHITE);
skeleton.setFlip(false, false);
skeleton.setSkin((Skin)null);
skeleton.setSkin(SkeletonActorPool.this.skeletonData.getDefaultSkin());
skeleton.setToSetupPose();
}
};
statePool = new Pool<AnimationState>(initialCapacity, max) {
protected AnimationState newObject () {
return new AnimationState(SkeletonActorPool.this.stateData);
}
protected void reset (AnimationState state) {
state.clearTracks();
state.clearListeners();
}
};
}
/** Each obtained skeleton actor that is no longer playing an animation is removed from the stage and returned to the pool. */
public void freeComplete () {
Array<SkeletonActor> obtained = this.obtained;
outer:
for (int i = obtained.size - 1; i >= 0; i--) {
SkeletonActor actor = obtained.get(i);
Array<TrackEntry> tracks = actor.state.getTracks();
for (int ii = 0, nn = tracks.size; ii < nn; ii++)
if (tracks.get(ii) != null) continue outer;
free(actor);
}
}
protected SkeletonActor newObject () {
SkeletonActor actor = new SkeletonActor();
actor.setRenderer(renderer);
return actor;
}
/** This pool keeps a reference to the obtained instance, so it should be returned to the pool via {@link #free(SkeletonActor)}
* , {@link #freeAll(Array)}, or {@link #freeComplete()} to avoid leaking memory. */
public SkeletonActor obtain () {
SkeletonActor actor = super.obtain();
actor.setSkeleton(skeletonPool.obtain());
actor.setAnimationState(statePool.obtain());
obtained.add(actor);
return actor;
}
protected void reset (SkeletonActor actor) {
actor.remove();
obtained.removeValue(actor, true);
skeletonPool.free(actor.getSkeleton());
statePool.free(actor.getAnimationState());
}
public Array<SkeletonActor> getObtained () {
return obtained;
}
}

View File

@ -0,0 +1,28 @@
package com.esotericsoftware.spine.utils;
import com.badlogic.gdx.utils.Pool;
import com.esotericsoftware.spine.Skeleton;
import com.esotericsoftware.spine.SkeletonData;
public class SkeletonPool extends Pool<Skeleton> {
private SkeletonData skeletonData;
public SkeletonPool (SkeletonData skeletonData) {
this.skeletonData = skeletonData;
}
public SkeletonPool (SkeletonData skeletonData, int initialCapacity) {
super(initialCapacity);
this.skeletonData = skeletonData;
}
public SkeletonPool (SkeletonData skeletonData, int initialCapacity, int max) {
super(initialCapacity, max);
this.skeletonData = skeletonData;
}
protected Skeleton newObject () {
return new Skeleton(skeletonData);
}
}

View File

@ -0,0 +1,206 @@
info face="Calibri" size=-12 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=0 aa=1 padding=1,1,2,1 spacing=1,1 outline=0
common lineHeight=14 base=11 scaleW=512 scaleH=512 pages=1 packed=0 alphaChnl=0 redChnl=4 greenChnl=4 blueChnl=4
page id=0 file="font-calibri-12_0.png"
chars count=95
char id=32 x=0 y=0 width=0 height=0 xoffset=0 yoffset=0 xadvance=3 page=0 chnl=15
char id=33 x=494 y=0 width=3 height=11 xoffset=0 yoffset=2 xadvance=4 page=0 chnl=15
char id=34 x=157 y=12 width=5 height=6 xoffset=0 yoffset=2 xadvance=5 page=0 chnl=15
char id=35 x=162 y=0 width=8 height=11 xoffset=-1 yoffset=2 xadvance=6 page=0 chnl=15
char id=36 x=16 y=0 width=6 height=13 xoffset=0 yoffset=1 xadvance=6 page=0 chnl=15
char id=37 x=101 y=0 width=10 height=11 xoffset=-1 yoffset=2 xadvance=9 page=0 chnl=15
char id=38 x=123 y=0 width=9 height=11 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=15
char id=39 x=168 y=12 width=3 height=6 xoffset=0 yoffset=2 xadvance=3 page=0 chnl=15
char id=40 x=51 y=0 width=4 height=13 xoffset=0 yoffset=2 xadvance=4 page=0 chnl=15
char id=41 x=41 y=0 width=4 height=13 xoffset=0 yoffset=2 xadvance=4 page=0 chnl=15
char id=42 x=117 y=12 width=7 height=8 xoffset=-1 yoffset=2 xadvance=6 page=0 chnl=15
char id=43 x=109 y=12 width=7 height=8 xoffset=-1 yoffset=4 xadvance=6 page=0 chnl=15
char id=44 x=163 y=12 width=4 height=6 xoffset=-1 yoffset=9 xadvance=3 page=0 chnl=15
char id=45 x=194 y=12 width=5 height=4 xoffset=-1 yoffset=6 xadvance=4 page=0 chnl=15
char id=46 x=204 y=12 width=3 height=4 xoffset=0 yoffset=9 xadvance=3 page=0 chnl=15
char id=47 x=0 y=0 width=7 height=14 xoffset=-1 yoffset=1 xadvance=5 page=0 chnl=15
char id=48 x=362 y=0 width=7 height=11 xoffset=-1 yoffset=2 xadvance=6 page=0 chnl=15
char id=49 x=370 y=0 width=7 height=11 xoffset=-1 yoffset=2 xadvance=6 page=0 chnl=15
char id=50 x=402 y=0 width=7 height=11 xoffset=-1 yoffset=2 xadvance=6 page=0 chnl=15
char id=51 x=410 y=0 width=7 height=11 xoffset=-1 yoffset=2 xadvance=6 page=0 chnl=15
char id=52 x=234 y=0 width=7 height=11 xoffset=-1 yoffset=2 xadvance=6 page=0 chnl=15
char id=53 x=242 y=0 width=7 height=11 xoffset=-1 yoffset=2 xadvance=6 page=0 chnl=15
char id=54 x=330 y=0 width=7 height=11 xoffset=-1 yoffset=2 xadvance=6 page=0 chnl=15
char id=55 x=258 y=0 width=7 height=11 xoffset=-1 yoffset=2 xadvance=6 page=0 chnl=15
char id=56 x=266 y=0 width=7 height=11 xoffset=-1 yoffset=2 xadvance=6 page=0 chnl=15
char id=57 x=274 y=0 width=7 height=11 xoffset=-1 yoffset=2 xadvance=6 page=0 chnl=15
char id=58 x=105 y=12 width=3 height=9 xoffset=0 yoffset=4 xadvance=3 page=0 chnl=15
char id=59 x=481 y=0 width=4 height=11 xoffset=-1 yoffset=4 xadvance=3 page=0 chnl=15
char id=60 x=133 y=12 width=7 height=8 xoffset=-1 yoffset=4 xadvance=6 page=0 chnl=15
char id=61 x=149 y=12 width=7 height=6 xoffset=-1 yoffset=5 xadvance=6 page=0 chnl=15
char id=62 x=125 y=12 width=7 height=8 xoffset=-1 yoffset=4 xadvance=6 page=0 chnl=15
char id=63 x=468 y=0 width=6 height=11 xoffset=0 yoffset=2 xadvance=6 page=0 chnl=15
char id=64 x=65 y=0 width=11 height=12 xoffset=0 yoffset=2 xadvance=11 page=0 chnl=15
char id=65 x=143 y=0 width=9 height=11 xoffset=-1 yoffset=2 xadvance=7 page=0 chnl=15
char id=66 x=290 y=0 width=7 height=11 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
char id=67 x=298 y=0 width=7 height=11 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
char id=68 x=171 y=0 width=8 height=11 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=15
char id=69 x=461 y=0 width=6 height=11 xoffset=0 yoffset=2 xadvance=6 page=0 chnl=15
char id=70 x=433 y=0 width=6 height=11 xoffset=0 yoffset=2 xadvance=6 page=0 chnl=15
char id=71 x=180 y=0 width=8 height=11 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=15
char id=72 x=216 y=0 width=8 height=11 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=15
char id=73 x=498 y=0 width=3 height=11 xoffset=0 yoffset=2 xadvance=3 page=0 chnl=15
char id=74 x=475 y=0 width=5 height=11 xoffset=-1 yoffset=2 xadvance=4 page=0 chnl=15
char id=75 x=314 y=0 width=7 height=11 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
char id=76 x=440 y=0 width=6 height=11 xoffset=0 yoffset=2 xadvance=6 page=0 chnl=15
char id=77 x=112 y=0 width=10 height=11 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15
char id=78 x=225 y=0 width=8 height=11 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=15
char id=79 x=198 y=0 width=8 height=11 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=15
char id=80 x=282 y=0 width=7 height=11 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
char id=81 x=77 y=0 width=9 height=12 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=15
char id=82 x=306 y=0 width=7 height=11 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
char id=83 x=447 y=0 width=6 height=11 xoffset=0 yoffset=2 xadvance=6 page=0 chnl=15
char id=84 x=250 y=0 width=7 height=11 xoffset=-1 yoffset=2 xadvance=6 page=0 chnl=15
char id=85 x=207 y=0 width=8 height=11 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=15
char id=86 x=133 y=0 width=9 height=11 xoffset=-1 yoffset=2 xadvance=8 page=0 chnl=15
char id=87 x=87 y=0 width=13 height=11 xoffset=-1 yoffset=2 xadvance=12 page=0 chnl=15
char id=88 x=189 y=0 width=8 height=11 xoffset=-1 yoffset=2 xadvance=7 page=0 chnl=15
char id=89 x=322 y=0 width=7 height=11 xoffset=-1 yoffset=2 xadvance=6 page=0 chnl=15
char id=90 x=153 y=0 width=8 height=11 xoffset=-1 yoffset=2 xadvance=7 page=0 chnl=15
char id=91 x=46 y=0 width=4 height=13 xoffset=0 yoffset=2 xadvance=4 page=0 chnl=15
char id=92 x=8 y=0 width=7 height=14 xoffset=-1 yoffset=1 xadvance=5 page=0 chnl=15
char id=93 x=56 y=0 width=4 height=13 xoffset=0 yoffset=2 xadvance=4 page=0 chnl=15
char id=94 x=141 y=12 width=7 height=7 xoffset=-1 yoffset=2 xadvance=6 page=0 chnl=15
char id=95 x=185 y=12 width=8 height=4 xoffset=-1 yoffset=11 xadvance=6 page=0 chnl=15
char id=96 x=180 y=12 width=4 height=5 xoffset=0 yoffset=1 xadvance=4 page=0 chnl=15
char id=97 x=71 y=13 width=7 height=9 xoffset=0 yoffset=4 xadvance=7 page=0 chnl=15
char id=98 x=338 y=0 width=7 height=11 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
char id=99 x=79 y=13 width=6 height=9 xoffset=0 yoffset=4 xadvance=6 page=0 chnl=15
char id=100 x=346 y=0 width=7 height=11 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
char id=101 x=23 y=14 width=7 height=9 xoffset=0 yoffset=4 xadvance=7 page=0 chnl=15
char id=102 x=426 y=0 width=6 height=11 xoffset=-1 yoffset=2 xadvance=4 page=0 chnl=15
char id=103 x=354 y=0 width=7 height=11 xoffset=0 yoffset=4 xadvance=6 page=0 chnl=15
char id=104 x=386 y=0 width=7 height=11 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15
char id=105 x=486 y=0 width=3 height=11 xoffset=0 yoffset=2 xadvance=3 page=0 chnl=15
char id=106 x=29 y=0 width=5 height=13 xoffset=-2 yoffset=2 xadvance=2 page=0 chnl=15
char id=107 x=454 y=0 width=6 height=11 xoffset=0 yoffset=2 xadvance=6 page=0 chnl=15
char id=108 x=490 y=0 width=3 height=11 xoffset=0 yoffset=2 xadvance=3 page=0 chnl=15
char id=109 x=12 y=15 width=10 height=9 xoffset=0 yoffset=4 xadvance=10 page=0 chnl=15
char id=110 x=31 y=14 width=7 height=9 xoffset=0 yoffset=4 xadvance=7 page=0 chnl=15
char id=111 x=39 y=14 width=7 height=9 xoffset=0 yoffset=4 xadvance=7 page=0 chnl=15
char id=112 x=378 y=0 width=7 height=11 xoffset=0 yoffset=4 xadvance=7 page=0 chnl=15
char id=113 x=418 y=0 width=7 height=11 xoffset=0 yoffset=4 xadvance=7 page=0 chnl=15
char id=114 x=93 y=12 width=5 height=9 xoffset=0 yoffset=4 xadvance=4 page=0 chnl=15
char id=115 x=86 y=13 width=6 height=9 xoffset=0 yoffset=4 xadvance=6 page=0 chnl=15
char id=116 x=502 y=0 width=6 height=10 xoffset=-1 yoffset=3 xadvance=5 page=0 chnl=15
char id=117 x=47 y=14 width=7 height=9 xoffset=0 yoffset=4 xadvance=7 page=0 chnl=15
char id=118 x=55 y=14 width=7 height=9 xoffset=-1 yoffset=4 xadvance=6 page=0 chnl=15
char id=119 x=0 y=15 width=11 height=9 xoffset=-1 yoffset=4 xadvance=10 page=0 chnl=15
char id=120 x=63 y=14 width=7 height=9 xoffset=-1 yoffset=4 xadvance=6 page=0 chnl=15
char id=121 x=394 y=0 width=7 height=11 xoffset=-1 yoffset=4 xadvance=5 page=0 chnl=15
char id=122 x=99 y=12 width=5 height=9 xoffset=0 yoffset=4 xadvance=5 page=0 chnl=15
char id=123 x=35 y=0 width=5 height=13 xoffset=-1 yoffset=2 xadvance=4 page=0 chnl=15
char id=124 x=61 y=0 width=3 height=13 xoffset=1 yoffset=2 xadvance=6 page=0 chnl=15
char id=125 x=23 y=0 width=5 height=13 xoffset=0 yoffset=2 xadvance=4 page=0 chnl=15
char id=126 x=172 y=12 width=7 height=5 xoffset=-1 yoffset=4 xadvance=6 page=0 chnl=15
kernings count=104
kerning first=65 second=84 amount=-1
kerning first=65 second=86 amount=-1
kerning first=65 second=89 amount=-1
kerning first=70 second=65 amount=-1
kerning first=70 second=74 amount=-1
kerning first=70 second=44 amount=-1
kerning first=70 second=46 amount=-1
kerning first=75 second=79 amount=-1
kerning first=75 second=81 amount=-1
kerning first=75 second=118 amount=-1
kerning first=75 second=119 amount=-1
kerning first=76 second=84 amount=-1
kerning first=76 second=86 amount=-1
kerning first=76 second=87 amount=-1
kerning first=76 second=89 amount=-1
kerning first=76 second=101 amount=-1
kerning first=80 second=65 amount=-1
kerning first=80 second=74 amount=-1
kerning first=80 second=44 amount=-1
kerning first=80 second=46 amount=-2
kerning first=80 second=47 amount=-1
kerning first=81 second=44 amount=1
kerning first=81 second=47 amount=1
kerning first=84 second=65 amount=-1
kerning first=84 second=97 amount=-1
kerning first=84 second=99 amount=-1
kerning first=84 second=100 amount=-1
kerning first=84 second=101 amount=-1
kerning first=84 second=103 amount=-1
kerning first=84 second=109 amount=-1
kerning first=84 second=110 amount=-1
kerning first=84 second=111 amount=-1
kerning first=84 second=112 amount=-1
kerning first=84 second=113 amount=-1
kerning first=84 second=114 amount=-1
kerning first=84 second=115 amount=-1
kerning first=84 second=117 amount=-1
kerning first=84 second=118 amount=-1
kerning first=84 second=119 amount=-1
kerning first=84 second=120 amount=-1
kerning first=84 second=121 amount=-1
kerning first=84 second=122 amount=-1
kerning first=84 second=44 amount=-1
kerning first=84 second=59 amount=-1
kerning first=84 second=58 amount=-1
kerning first=84 second=46 amount=-1
kerning first=84 second=47 amount=-1
kerning first=86 second=65 amount=-1
kerning first=86 second=97 amount=-1
kerning first=86 second=99 amount=-1
kerning first=86 second=100 amount=-1
kerning first=86 second=101 amount=-1
kerning first=86 second=103 amount=-1
kerning first=86 second=111 amount=-1
kerning first=86 second=113 amount=-1
kerning first=86 second=115 amount=-1
kerning first=86 second=44 amount=-1
kerning first=86 second=59 amount=-1
kerning first=86 second=46 amount=-1
kerning first=86 second=47 amount=-1
kerning first=87 second=65 amount=-1
kerning first=87 second=74 amount=-1
kerning first=87 second=111 amount=-1
kerning first=87 second=44 amount=-1
kerning first=87 second=59 amount=-1
kerning first=87 second=46 amount=-1
kerning first=89 second=65 amount=-1
kerning first=89 second=74 amount=-1
kerning first=89 second=97 amount=-1
kerning first=89 second=99 amount=-1
kerning first=89 second=100 amount=-1
kerning first=89 second=101 amount=-1
kerning first=89 second=103 amount=-1
kerning first=89 second=109 amount=-1
kerning first=89 second=110 amount=-1
kerning first=89 second=111 amount=-1
kerning first=89 second=112 amount=-1
kerning first=89 second=113 amount=-1
kerning first=89 second=114 amount=-1
kerning first=89 second=115 amount=-1
kerning first=89 second=117 amount=-1
kerning first=89 second=122 amount=-1
kerning first=89 second=44 amount=-1
kerning first=89 second=59 amount=-1
kerning first=89 second=58 amount=-1
kerning first=89 second=46 amount=-1
kerning first=89 second=47 amount=-1
kerning first=102 second=44 amount=-1
kerning first=102 second=46 amount=-1
kerning first=102 second=116 amount=1
kerning first=114 second=44 amount=-1
kerning first=114 second=46 amount=-1
kerning first=118 second=44 amount=-1
kerning first=118 second=46 amount=-1
kerning first=119 second=44 amount=-1
kerning first=119 second=46 amount=-1
kerning first=121 second=44 amount=-1
kerning first=121 second=46 amount=-1
kerning first=44 second=84 amount=-1
kerning first=44 second=86 amount=-1
kerning first=44 second=87 amount=-1
kerning first=44 second=89 amount=-1
kerning first=46 second=84 amount=-1
kerning first=46 second=86 amount=-1
kerning first=46 second=87 amount=-1
kerning first=46 second=89 amount=-1

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -1,200 +1,194 @@
skin.png skin.png
size: 256,128 size: 93,139
format: RGBA8888 format: RGBA8888
filter: Linear,Linear filter: Linear,Linear
repeat: none repeat: none
check-off button-disabled
rotate: false rotate: false
xy: 11, 5 xy: 75, 73
size: 14, 14 size: 17, 17
orig: 14, 14 split: 6, 6, 5, 5
pad: -1, -1, 3, 3
orig: 17, 17
offset: 0, 0 offset: 0, 0
index: -1 index: -1
textfield button-down
rotate: false rotate: false
xy: 11, 5 xy: 75, 54
size: 14, 14 size: 17, 17
split: 3, 3, 3, 3 split: 6, 6, 5, 5
orig: 14, 14 pad: -1, -1, 3, 3
orig: 17, 17
offset: 0, 0 offset: 0, 0
index: -1 index: -1
check-on button-over
rotate: false rotate: false
xy: 125, 35 xy: 66, 35
size: 14, 14 size: 17, 17
orig: 14, 14 split: 6, 6, 5, 5
pad: -1, -1, 3, 3
orig: 17, 17
offset: 0, 0 offset: 0, 0
index: -1 index: -1
cursor button-up
rotate: false rotate: false
xy: 23, 1 xy: 24, 30
size: 17, 17
split: 6, 6, 5, 5
pad: -1, -1, 3, 3
orig: 17, 17
offset: 0, 0
index: -1
checkbox-off
rotate: false
xy: 66, 18
size: 15, 15
orig: 19, 18
offset: 0, 2
index: -1
checkbox-offDisabled
rotate: false
xy: 24, 13
size: 15, 15
orig: 19, 18
offset: 0, 2
index: -1
checkbox-on
rotate: false
xy: 1, 5
size: 15, 15
orig: 19, 18
offset: 0, 2
index: -1
checkbox-onDisabled
rotate: false
xy: 66, 1
size: 15, 15
orig: 19, 18
offset: 0, 2
index: -1
group
rotate: false
xy: 27, 72
size: 23, 23
split: 6, 6, 8, 8
pad: 11, 11, 11, 11
orig: 23, 23
offset: 0, 0
index: -1
list-selection
rotate: false
xy: 18, 8
size: 11, 3
split: 5, 5, 1, 1
orig: 11, 3
offset: 0, 0
index: -1
loading
rotate: false
xy: 61, 92
size: 30, 31
orig: 30, 31
offset: 0, 0
index: -1
scrollpane-horiz
rotate: false
xy: 1, 120
size: 58, 18
split: 3, 51, 0, 18
orig: 58, 18
offset: 0, 0
index: -1
scrollpane-vert
rotate: false
xy: 46, 9
size: 18, 58
split: 0, 18, 51, 3
orig: 18, 58
offset: 0, 0
index: -1
selectBox-closed
rotate: false
xy: 1, 97
size: 24, 21
split: 6, 14, 5, 13
pad: 5, 13, 3, 3
orig: 24, 21
offset: 0, 0
index: -1
selectBox-list
rotate: false
xy: 24, 49
size: 20, 21
split: 5, 12, 5, 13
orig: 20, 21
offset: 0, 0
index: -1
selectBox-open
rotate: false
xy: 27, 97
size: 24, 21
split: 6, 14, 5, 13
pad: 5, 13, 3, 3
orig: 24, 21
offset: 0, 0
index: -1
selectBox-over
rotate: false
xy: 1, 74
size: 24, 21
split: 6, 14, 5, 13
pad: 5, 13, 3, 3
orig: 24, 21
offset: 0, 0
index: -1
slider-bg
rotate: false
xy: 61, 125
size: 31, 13
split: 7, 6, 0, 13
pad: 3, 2, -1, -1
orig: 31, 13
offset: 0, 0
index: -1
slider-handle
rotate: false
xy: 53, 101
size: 6, 17
orig: 6, 17
offset: 0, 0
index: -1
textField-cursor
rotate: false
xy: 53, 96
size: 3, 3 size: 3, 3
split: 1, 1, 1, 1 split: 1, 1, 1, 1
orig: 3, 3 orig: 3, 3
offset: 0, 0 offset: 0, 0
index: -1 index: -1
default textField-round
rotate: false rotate: false
xy: 1, 50 xy: 52, 69
size: 253, 77 size: 21, 21
orig: 254, 77 split: 6, 5, 5, 5
offset: 1, 0 pad: -1, -1, 3, 3
index: -1 orig: 21, 21
default-pane
rotate: false
xy: 11, 1
size: 5, 3
split: 1, 1, 1, 1
orig: 5, 3
offset: 0, 0
index: -1
default-rect-pad
rotate: false
xy: 11, 1
size: 5, 3
split: 1, 1, 1, 1
orig: 5, 3
offset: 0, 0
index: -1
default-pane-noborder
rotate: false
xy: 129, 33
size: 1, 1
split: 0, 0, 0, 0
orig: 1, 1
offset: 0, 0
index: -1
default-rect
rotate: false
xy: 38, 25
size: 3, 3
split: 1, 1, 1, 1
orig: 3, 3
offset: 0, 0
index: -1
default-rect-down
rotate: false
xy: 170, 46
size: 3, 3
split: 1, 1, 1, 1
orig: 3, 3
offset: 0, 0
index: -1
default-round
rotate: false
xy: 112, 29
size: 12, 20
split: 5, 5, 5, 4
pad: 4, 4, 1, 1
orig: 12, 20
offset: 0, 0
index: -1
default-round-down
rotate: false
xy: 99, 29
size: 12, 20
split: 5, 5, 5, 4
pad: 4, 4, 1, 1
orig: 12, 20
offset: 0, 0
index: -1
default-round-large
rotate: false
xy: 57, 29
size: 20, 20
split: 5, 5, 5, 4
orig: 20, 20
offset: 0, 0
index: -1
default-scroll
rotate: false
xy: 78, 29
size: 20, 20
split: 2, 2, 2, 2
orig: 20, 20
offset: 0, 0
index: -1
default-select
rotate: false
xy: 29, 29
size: 27, 20
split: 4, 14, 4, 4
orig: 27, 20
offset: 0, 0
index: -1
default-select-selection
rotate: false
xy: 26, 16
size: 3, 3
split: 1, 1, 1, 1
orig: 3, 3
offset: 0, 0
index: -1
default-slider
rotate: false
xy: 29, 20
size: 8, 8
split: 2, 2, 2, 2
orig: 8, 8
offset: 0, 0
index: -1
default-slider-knob
rotate: false
xy: 1, 1
size: 9, 18
orig: 9, 18
offset: 0, 0
index: -1
default-splitpane
rotate: false
xy: 17, 1
size: 5, 3
split: 0, 5, 0, 0
orig: 5, 3
offset: 0, 0
index: -1
default-splitpane-vertical
rotate: false
xy: 125, 29
size: 3, 5
split: 0, 0, 0, 5
orig: 3, 5
offset: 0, 0
index: -1
default-window
rotate: false
xy: 1, 20
size: 27, 29
split: 4, 3, 20, 3
orig: 27, 29
offset: 0, 0
index: -1
selection
rotate: false
xy: 170, 44
size: 1, 1
orig: 1, 1
offset: 0, 0
index: -1
tree-minus
rotate: false
xy: 140, 35
size: 14, 14
orig: 14, 14
offset: 0, 0
index: -1
tree-plus
rotate: false
xy: 155, 35
size: 14, 14
orig: 14, 14
offset: 0, 0 offset: 0, 0
index: -1 index: -1
white white
rotate: false rotate: false
xy: 174, 48 xy: 66, 64
size: 1, 1 size: 3, 3
orig: 1, 1 orig: 3, 3
offset: 0, 0
index: -1
window
rotate: false
xy: 1, 22
size: 21, 50
split: 10, 10, 36, 11
pad: 9, 9, 41, 8
orig: 21, 50
offset: 0, 0 offset: 0, 0
index: -1 index: -1

View File

@ -1,59 +1,66 @@
{ {
com.badlogic.gdx.graphics.g2d.BitmapFont: { default-font: { file: com/badlogic/gdx/utils/arial-15.fnt } }, com.badlogic.gdx.graphics.g2d.BitmapFont: {
default: { file: font-calibri-12.fnt }
}
com.badlogic.gdx.graphics.Color: { com.badlogic.gdx.graphics.Color: {
green: { a: 1, b: 0, g: 1, r: 0 }, white: { r: 1, g: 1, b: 1 },
white: { a: 1, b: 1, g: 1, r: 1 }, black: {},
red: { a: 1, b: 0, g: 0, r: 1 }, disabled: { r: 0.53, g: 0.53, b: 0.53 },
black: { a: 1, b: 0, g: 0, r: 0 } selection: { r: 0.77, g: 1, b: 1, a: 0.25 },
}, },
com.badlogic.gdx.scenes.scene2d.ui.Skin$TintedDrawable: { com.badlogic.gdx.scenes.scene2d.ui.Skin$TintedDrawable: {
dialogDim: { name: white, color: { r: 0, g: 0, b: 0, a: 0.45 } } selection: { name: white, color: selection },
}, dim: { name: white, color: { r: 0, g: 0, b: 0, a: 0.3 } },
com.badlogic.gdx.scenes.scene2d.ui.Button$ButtonStyle: { list-selection: { name: list-selection, color: selection },
default: { down: default-round-down, up: default-round },
toggle: { down: default-round-down, checked: default-round-down, up: default-round }
}, },
com.badlogic.gdx.scenes.scene2d.ui.TextButton$TextButtonStyle: { com.badlogic.gdx.scenes.scene2d.ui.TextButton$TextButtonStyle: {
default: { down: default-round-down, up: default-round, font: default-font, fontColor: white }, default: {
toggle: { down: default-round-down, up: default-round, checked: default-round-down, font: default-font, fontColor: white, downFontColor: red } up: button-up, down: button-down, over: button-over, disabled: button-disabled,
font: default, fontColor: white, disabledFontColor: disabled, pressedOffsetY: -1
},
toggle: {
up: button-up, down: button-down, over: button-over, disabled: button-disabled, checked: button-down,
font: default, fontColor: white, disabledFontColor: disabled, pressedOffsetY: -1
},
},
com.badlogic.gdx.scenes.scene2d.ui.Label$LabelStyle: {
default: { font: default, fontColor: white },
title: { font: default, fontColor: { hex: 00ffccff } },
}
com.badlogic.gdx.scenes.scene2d.ui.TextField$TextFieldStyle: {
default: { background: textField-round, font: default, fontColor: white, disabledFontColor: disabled,
selection: selection, cursor: textField-cursor },
}
com.badlogic.gdx.scenes.scene2d.ui.Window$WindowStyle: {
default: { titleFont: default, titleFontColor: white, background: window },
}, },
com.badlogic.gdx.scenes.scene2d.ui.ScrollPane$ScrollPaneStyle: { com.badlogic.gdx.scenes.scene2d.ui.ScrollPane$ScrollPaneStyle: {
default: { vScroll: default-scroll, hScrollKnob: default-round-large, background: default-rect, hScroll: default-scroll, vScrollKnob: default-round-large } default: { hScrollKnob: scrollpane-horiz, vScrollKnob: scrollpane-vert },
bg: { hScrollKnob: scrollpane-horiz, vScrollKnob: scrollpane-vert, background: textField-round },
},
com.badlogic.gdx.scenes.scene2d.ui.List$ListStyle: {
default: { font: default, selection: list-selection }
}, },
com.badlogic.gdx.scenes.scene2d.ui.SelectBox$SelectBoxStyle: { com.badlogic.gdx.scenes.scene2d.ui.SelectBox$SelectBoxStyle: {
default: { default: {
font: default-font, fontColor: white, background: default-select, background: selectBox-closed, backgroundOver: selectBox-over, backgroundOpen: selectBox-open, font: default, fontColor: white,
scrollStyle: default, scrollStyle: { background: selectBox-list, hScrollKnob: scrollpane-horiz, vScrollKnob: scrollpane-vert },
listStyle: { font: default-font, selection: default-select-selection } listStyle: { font: default, selection: list-selection },
} },
}, },
com.badlogic.gdx.scenes.scene2d.ui.SplitPane$SplitPaneStyle: { com.badlogic.gdx.scenes.scene2d.ui.TextTooltip$TextTooltipStyle: {
default-vertical: { handle: default-splitpane-vertical }, default: {
default-horizontal: { handle: default-splitpane } label: { font: default, fontColor: white },
}, background: group
com.badlogic.gdx.scenes.scene2d.ui.Window$WindowStyle: { },
default: { titleFont: default-font, background: default-window, titleFontColor: white },
dialog: { titleFont: default-font, background: default-window, titleFontColor: white, stageBackground: dialogDim }
},
com.badlogic.gdx.scenes.scene2d.ui.Slider$SliderStyle: {
default-horizontal: { background: default-slider, knob: default-slider-knob }
},
com.badlogic.gdx.scenes.scene2d.ui.Label$LabelStyle: {
default: { font: default-font, fontColor: white }
},
com.badlogic.gdx.scenes.scene2d.ui.TextField$TextFieldStyle: {
default: { selection: selection, background: textfield, font: default-font, fontColor: white, cursor: cursor }
}, },
com.badlogic.gdx.scenes.scene2d.ui.CheckBox$CheckBoxStyle: { com.badlogic.gdx.scenes.scene2d.ui.CheckBox$CheckBoxStyle: {
default: { checkboxOn: check-on, checkboxOff: check-off, font: default-font, fontColor: white } default: {
checkboxOn: checkbox-on, checkboxOff: checkbox-off, checkboxOffDisabled: checkbox-offDisabled,
checkboxOnDisabled: checkbox-onDisabled, font: default, fontColor: white, disabledFontColor: disabled
},
}, },
com.badlogic.gdx.scenes.scene2d.ui.List$ListStyle: { com.badlogic.gdx.scenes.scene2d.ui.Slider$SliderStyle: {
default: { fontColorUnselected: white, selection: selection, fontColorSelected: white, font: default-font } default-horizontal: { background: slider-bg, knob: slider-handle },
}, },
com.badlogic.gdx.scenes.scene2d.ui.Touchpad$TouchpadStyle: {
default: { background: default-pane, knob: default-round-large }
},
com.badlogic.gdx.scenes.scene2d.ui.Tree$TreeStyle: {
default: { minus: tree-minus, plus: tree-plus, selection: default-select-selection }
}
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@ -50,6 +50,7 @@ import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Pixmap; import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Pixmap.Format; import com.badlogic.gdx.graphics.Pixmap.Format;
import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.graphics.g2d.PolygonSpriteBatch; import com.badlogic.gdx.graphics.g2d.PolygonSpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureAtlas; import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion; import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;
@ -83,7 +84,7 @@ public class SkeletonViewer extends ApplicationAdapter {
UI ui; UI ui;
PolygonSpriteBatch batch; PolygonSpriteBatch batch;
SkeletonRenderer renderer; SkeletonMeshRenderer renderer;
SkeletonRendererDebug debugRenderer; SkeletonRendererDebug debugRenderer;
SkeletonData skeletonData; SkeletonData skeletonData;
Skeleton skeleton; Skeleton skeleton;
@ -96,38 +97,49 @@ public class SkeletonViewer extends ApplicationAdapter {
public void create () { public void create () {
ui = new UI(); ui = new UI();
batch = new PolygonSpriteBatch(); batch = new PolygonSpriteBatch();
renderer = new SkeletonRenderer(); renderer = new SkeletonMeshRenderer();
debugRenderer = new SkeletonRendererDebug(); debugRenderer = new SkeletonRendererDebug();
skeletonX = (int)(ui.window.getWidth() + (Gdx.graphics.getWidth() - ui.window.getWidth()) / 2); skeletonX = (int)(ui.window.getWidth() + (Gdx.graphics.getWidth() - ui.window.getWidth()) / 2);
skeletonY = Gdx.graphics.getHeight() / 4; skeletonY = Gdx.graphics.getHeight() / 4;
loadSkeleton( loadSkeleton(
Gdx.files.internal(Gdx.app.getPreferences("spine-skeletontest").getString("lastFile", "spineboy/spineboy.json")), false); Gdx.files.internal(Gdx.app.getPreferences("spine-skeletonviewer").getString("lastFile", "spineboy/spineboy.json")),
false);
} }
void loadSkeleton (FileHandle skeletonFile, boolean reload) { void loadSkeleton (final FileHandle skeletonFile, boolean reload) {
if (skeletonFile == null) return; if (skeletonFile == null) return;
// A regular texture atlas would normally usually be used. This returns a white image for images not found in the atlas.
Pixmap pixmap = new Pixmap(32, 32, Format.RGBA8888);
pixmap.setColor(new Color(1, 1, 1, 0.33f));
pixmap.fill();
final AtlasRegion fake = new AtlasRegion(new Texture(pixmap), 0, 0, 32, 32);
pixmap.dispose();
String atlasFileName = skeletonFile.nameWithoutExtension();
if (atlasFileName.endsWith(".json")) atlasFileName = new FileHandle(atlasFileName).nameWithoutExtension();
FileHandle atlasFile = skeletonFile.sibling(atlasFileName + ".atlas");
if (!atlasFile.exists()) atlasFile = skeletonFile.sibling(atlasFileName + ".atlas.txt");
TextureAtlasData data = !atlasFile.exists() ? null : new TextureAtlasData(atlasFile, atlasFile.parent(), false);
TextureAtlas atlas = new TextureAtlas(data) {
public AtlasRegion findRegion (String name) {
AtlasRegion region = super.findRegion(name);
return region != null ? region : fake;
}
};
try { try {
// A regular texture atlas would normally usually be used. This returns a white image for images not found in the atlas.
Pixmap pixmap = new Pixmap(32, 32, Format.RGBA8888);
pixmap.setColor(new Color(1, 1, 1, 0.33f));
pixmap.fill();
final AtlasRegion fake = new AtlasRegion(new Texture(pixmap), 0, 0, 32, 32);
pixmap.dispose();
String atlasFileName = skeletonFile.nameWithoutExtension();
if (atlasFileName.endsWith(".json")) atlasFileName = new FileHandle(atlasFileName).nameWithoutExtension();
FileHandle atlasFile = skeletonFile.sibling(atlasFileName + ".atlas");
if (!atlasFile.exists()) atlasFile = skeletonFile.sibling(atlasFileName + ".atlas.txt");
TextureAtlasData data = !atlasFile.exists() ? null : new TextureAtlasData(atlasFile, atlasFile.parent(), false);
TextureAtlas atlas = new TextureAtlas(data) {
public AtlasRegion findRegion (String name) {
AtlasRegion region = super.findRegion(name);
if (region == null) {
// Look for separate image file.
FileHandle file = skeletonFile.sibling(name + ".png");
if (file.exists()) {
Texture texture = new Texture(file);
texture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
region = new AtlasRegion(texture, 0, 0, texture.getWidth(), texture.getHeight());
region.name = name;
}
}
return region != null ? region : fake;
}
};
String extension = skeletonFile.extension(); String extension = skeletonFile.extension();
if (extension.equalsIgnoreCase("json") || extension.equalsIgnoreCase("txt")) { if (extension.equalsIgnoreCase("json") || extension.equalsIgnoreCase("txt")) {
SkeletonJson json = new SkeletonJson(atlas); SkeletonJson json = new SkeletonJson(atlas);
@ -137,6 +149,7 @@ public class SkeletonViewer extends ApplicationAdapter {
SkeletonBinary binary = new SkeletonBinary(atlas); SkeletonBinary binary = new SkeletonBinary(atlas);
binary.setScale(ui.scaleSlider.getValue()); binary.setScale(ui.scaleSlider.getValue());
skeletonData = binary.readSkeletonData(skeletonFile); skeletonData = binary.readSkeletonData(skeletonFile);
if (skeletonData.getBones().size == 0) throw new Exception("No bones in skeleton data.");
} }
} catch (Exception ex) { } catch (Exception ex) {
ex.printStackTrace(); ex.printStackTrace();
@ -153,7 +166,7 @@ public class SkeletonViewer extends ApplicationAdapter {
state = new AnimationState(new AnimationStateData(skeletonData)); state = new AnimationState(new AnimationStateData(skeletonData));
this.skeletonFile = skeletonFile; this.skeletonFile = skeletonFile;
Preferences prefs = Gdx.app.getPreferences("spine-skeletontest"); Preferences prefs = Gdx.app.getPreferences("spine-skeletonviewer");
prefs.putString("lastFile", skeletonFile.path()); prefs.putString("lastFile", skeletonFile.path());
prefs.flush(); prefs.flush();
lastModified = skeletonFile.lastModified(); lastModified = skeletonFile.lastModified();
@ -161,7 +174,7 @@ public class SkeletonViewer extends ApplicationAdapter {
// Populate UI. // Populate UI.
ui.skeletonLabel.setText(skeletonFile.name()); ui.window.getTitleLabel().setText(skeletonFile.name());
{ {
Array<String> items = new Array(); Array<String> items = new Array();
for (Skin skin : skeletonData.getSkins()) for (Skin skin : skeletonData.getSkins())
@ -177,8 +190,9 @@ public class SkeletonViewer extends ApplicationAdapter {
// Configure skeleton from UI. // Configure skeleton from UI.
skeleton.setSkin(ui.skinList.getSelected()); if (ui.skinList.getSelected() != null) skeleton.setSkin(ui.skinList.getSelected());
state.setAnimation(0, ui.animationList.getSelected(), ui.loopCheckbox.isChecked()); if (ui.animationList.getSelected() != null)
state.setAnimation(0, ui.animationList.getSelected(), ui.loopCheckbox.isChecked());
if (reload) ui.toast("Reloaded."); if (reload) ui.toast("Reloaded.");
} }
@ -217,6 +231,7 @@ public class SkeletonViewer extends ApplicationAdapter {
// skeleton.getRootBone().setY(skeletonY); // skeleton.getRootBone().setY(skeletonY);
skeleton.updateWorldTransform(); skeleton.updateWorldTransform();
batch.setColor(Color.WHITE);
batch.begin(); batch.begin();
renderer.draw(batch, skeleton); renderer.draw(batch, skeleton);
batch.end(); batch.end();
@ -252,7 +267,7 @@ public class SkeletonViewer extends ApplicationAdapter {
batch.getProjectionMatrix().setToOrtho2D(0, 0, width, height); batch.getProjectionMatrix().setToOrtho2D(0, 0, width, height);
debugRenderer.getShapeRenderer().setProjectionMatrix(batch.getProjectionMatrix()); debugRenderer.getShapeRenderer().setProjectionMatrix(batch.getProjectionMatrix());
ui.stage.getViewport().update(width, height, true); ui.stage.getViewport().update(width, height, true);
if (!ui.minimizeButton.isChecked()) ui.window.setHeight(height); if (!ui.minimizeButton.isChecked()) ui.window.setHeight(height + 8);
} }
class UI { class UI {
@ -262,8 +277,7 @@ public class SkeletonViewer extends ApplicationAdapter {
Window window = new Window("Skeleton", skin); Window window = new Window("Skeleton", skin);
Table root = new Table(skin); Table root = new Table(skin);
TextButton browseButton = new TextButton("Browse", skin); TextButton openButton = new TextButton("Open", skin);
Label skeletonLabel = new Label("", skin);
List<String> animationList = new List(skin); List<String> animationList = new List(skin);
List<String> skinList = new List(skin); List<String> skinList = new List(skin);
CheckBox loopCheckbox = new CheckBox(" Loop", skin); CheckBox loopCheckbox = new CheckBox(" Loop", skin);
@ -305,30 +319,24 @@ public class SkeletonViewer extends ApplicationAdapter {
window.setMovable(false); window.setMovable(false);
window.setResizable(false); window.setResizable(false);
window.setKeepWithinStage(false);
window.setX(-3);
window.setY(-2);
minimizeButton.padTop(-2).padLeft(5); window.getTitleTable().add(openButton).space(3);
minimizeButton.getColor().a = 0.66f; window.getTitleTable().add(minimizeButton).width(20);
window.getTitleTable().add(minimizeButton).size(20, 20);
ScrollPane skinScroll = new ScrollPane(skinList, skin); ScrollPane skinScroll = new ScrollPane(skinList, skin, "bg");
skinScroll.setFadeScrollBars(false); skinScroll.setFadeScrollBars(false);
ScrollPane animationScroll = new ScrollPane(animationList, skin); ScrollPane animationScroll = new ScrollPane(animationList, skin, "bg");
animationScroll.setFadeScrollBars(false); animationScroll.setFadeScrollBars(false);
// Layout. // Layout.
root.pad(2, 4, 4, 4).defaults().space(6); root.defaults().space(6);
root.columnDefaults(0).top().right(); root.columnDefaults(0).top().right().padTop(3);
root.columnDefaults(1).left(); root.columnDefaults(1).left();
root.row().padTop(6);
root.add("Skeleton:");
{
Table table = table();
table.add(skeletonLabel).fillX().expandX();
table.add(browseButton);
root.add(table).fill().row();
}
root.add("Scale:"); root.add("Scale:");
{ {
Table table = table(); Table table = table();
@ -378,7 +386,6 @@ public class SkeletonViewer extends ApplicationAdapter {
stage.addActor(table); stage.addActor(table);
table.pad(10).bottom().right(); table.pad(10).bottom().right();
table.add(toasts); table.add(toasts);
table.debug();
} }
// Events. // Events.
@ -390,7 +397,7 @@ public class SkeletonViewer extends ApplicationAdapter {
} }
}); });
browseButton.addListener(new ChangeListener() { openButton.addListener(new ChangeListener() {
public void changed (ChangeEvent event, Actor actor) { public void changed (ChangeEvent event, Actor actor) {
FileDialog fileDialog = new FileDialog((Frame)null, "Choose skeleton file"); FileDialog fileDialog = new FileDialog((Frame)null, "Choose skeleton file");
fileDialog.setMode(FileDialog.LOAD); fileDialog.setMode(FileDialog.LOAD);
@ -427,11 +434,11 @@ public class SkeletonViewer extends ApplicationAdapter {
public void clicked (InputEvent event, float x, float y) { public void clicked (InputEvent event, float x, float y) {
if (minimizeButton.isChecked()) { if (minimizeButton.isChecked()) {
window.getCells().get(0).setActor(null); window.getCells().get(0).setActor(null);
window.setHeight(20); window.setHeight(37);
minimizeButton.setText("+"); minimizeButton.setText("+");
} else { } else {
window.getCells().get(0).setActor(root); window.getCells().get(0).setActor(root);
ui.window.setHeight(Gdx.graphics.getHeight()); ui.window.setHeight(Gdx.graphics.getHeight() + 8);
minimizeButton.setText("-"); minimizeButton.setText("-");
} }
} }
@ -466,7 +473,11 @@ public class SkeletonViewer extends ApplicationAdapter {
skinList.addListener(new ChangeListener() { skinList.addListener(new ChangeListener() {
public void changed (ChangeEvent event, Actor actor) { public void changed (ChangeEvent event, Actor actor) {
if (skeleton != null) { if (skeleton != null) {
skeleton.setSkin(skinList.getSelected()); String skinName = skinList.getSelected();
if (skinName == null)
skeleton.setSkin((Skin)null);
else
skeleton.setSkin(skinName);
skeleton.setSlotsToSetupPose(); skeleton.setSlotsToSetupPose();
} }
} }
@ -504,7 +515,7 @@ public class SkeletonViewer extends ApplicationAdapter {
delay(5f), // delay(5f), //
parallel(moveBy(0, table.getHeight(), 0.3f), fadeOut(0.3f)), // parallel(moveBy(0, table.getHeight(), 0.3f), fadeOut(0.3f)), //
removeActor() // removeActor() //
)); ));
for (Actor actor : toasts.getChildren()) for (Actor actor : toasts.getChildren())
actor.addAction(moveBy(0, table.getHeight(), 0.3f)); actor.addAction(moveBy(0, table.getHeight(), 0.3f));
toasts.addActor(table); toasts.addActor(table);