diff --git a/LICENSE b/LICENSE index 815ec1ca1..e14aae4a0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ 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. 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 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, +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 diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/BonePlotting.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/BonePlotting.java index e6457b76c..417544225 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/BonePlotting.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/BonePlotting.java @@ -67,8 +67,8 @@ public class BonePlotting { while (time < animation.getDuration()) { animation.apply(skeleton, time, time, false, null); skeleton.updateWorldTransform(); - System.out.println(animation.getName() + "," + bone.getWorldX() + "," + bone.getWorldY() + "," - + bone.getWorldRotation() + "," + bone.getWorldScaleX() + "," + bone.getWorldScaleY()); + System.out + .println(animation.getName() + "," + bone.getWorldX() + "," + bone.getWorldY() + "," + bone.getWorldRotationX()); time += fps; } } diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/Box2DExample.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/Box2DExample.java index 4eb4c2216..8a1a35896 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/Box2DExample.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/Box2DExample.java @@ -160,7 +160,7 @@ public class Box2DExample extends ApplicationAdapter { if (attachment.body == null) continue; float x = skeleton.x + slot.getBone().getWorldX(); 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); } diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest3.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest3.java index e99cdbc2c..e8c42d431 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest3.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest3.java @@ -42,7 +42,7 @@ import com.badlogic.gdx.graphics.g2d.TextureAtlas; public class SimpleTest3 extends ApplicationAdapter { OrthographicCamera camera; PolygonSpriteBatch batch; - SkeletonRenderer renderer; + SkeletonMeshRenderer renderer; SkeletonRendererDebug debugRenderer; TextureAtlas atlas; @@ -52,7 +52,7 @@ public class SimpleTest3 extends ApplicationAdapter { public void create () { camera = new OrthographicCamera(); batch = new PolygonSpriteBatch(); // Required to render meshes. SpriteBatch can't render meshes. - renderer = new SkeletonRenderer(); + renderer = new SkeletonMeshRenderer(); renderer.setPremultipliedAlpha(true); debugRenderer = new SkeletonRendererDebug(); debugRenderer.setMeshTriangles(false); diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SkeletonAttachmentTest.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SkeletonAttachmentTest.java index 10f553f98..e2afbcf56 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SkeletonAttachmentTest.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SkeletonAttachmentTest.java @@ -45,7 +45,7 @@ import com.badlogic.gdx.graphics.g2d.TextureAtlas; public class SkeletonAttachmentTest extends ApplicationAdapter { OrthographicCamera camera; PolygonSpriteBatch batch; - SkeletonRenderer renderer; + SkeletonMeshRenderer renderer; Skeleton spineboy, goblin; AnimationState spineboyState, goblinState; @@ -53,7 +53,7 @@ public class SkeletonAttachmentTest extends ApplicationAdapter { public void create () { camera = new OrthographicCamera(); batch = new PolygonSpriteBatch(); - renderer = new SkeletonRenderer(); + renderer = new SkeletonMeshRenderer(); 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); - 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.setSkin("goblin"); goblin.setSlotsToSetupPose(); diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java index 20cb87c23..c11d2a362 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java @@ -777,62 +777,4 @@ public class Animation { 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 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); - } - } } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java index f3e40c05d..913bc581d 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java @@ -37,7 +37,7 @@ import com.badlogic.gdx.utils.Pool.Poolable; /** Stores state for an animation and automatically mixes between animations. */ public class AnimationState { - private final AnimationStateData data; + private AnimationStateData data; private Array tracks = new Array(); private final Array events = new Array(); private final Array 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) { if (data == null) throw new IllegalArgumentException("data cannot be null."); this.data = data; @@ -269,6 +273,10 @@ public class AnimationState { listeners.removeValue(listener, true); } + public void clearListeners () { + listeners.clear(); + } + public float getTimeScale () { return timeScale; } @@ -281,6 +289,10 @@ public class AnimationState { return data; } + public void setData (AnimationStateData data) { + this.data = data; + } + /** Returns the list of tracks that have animations, which may contain nulls. */ public Array getTracks () { return tracks; diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java index d1972637d..d1f0198e6 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java @@ -31,26 +31,23 @@ package com.esotericsoftware.spine; +import static com.badlogic.gdx.math.MathUtils.*; import static com.badlogic.gdx.math.Matrix3.*; import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.Matrix3; import com.badlogic.gdx.math.Vector2; -public class Bone { +public class Bone implements Updatable { final BoneData data; final Skeleton skeleton; final Bone parent; - float x, y; - float rotation, rotationIK; - float scaleX, scaleY; - boolean flipX, flipY; + float x, y, rotation, scaleX, scaleY; + float appliedRotation, appliedScaleX, appliedScaleY; - float m00, m01, worldX; // a b x - float m10, m11, worldY; // c d y - float worldRotation; - float worldScaleX, worldScaleY; - boolean worldFlipX, worldFlipY; + float a, b, worldX; + float c, d, worldY; + float worldSignX, worldSignY; Bone (BoneData data) { this.data = data; @@ -78,69 +75,135 @@ public class Bone { x = bone.x; y = bone.y; rotation = bone.rotation; - rotationIK = bone.rotationIK; scaleX = bone.scaleX; scaleY = bone.scaleY; - flipX = bone.flipX; - flipY = bone.flipY; } - /** Computes the world SRT using the parent bone and the local SRT. */ + /** Computes the world SRT using the parent bone and this bone's local SRT. */ 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; - float x = this.x, y = this.y; - if (parent != null) { - worldX = x * parent.m00 + y * parent.m01 + parent.worldX; - worldY = x * parent.m10 + y * parent.m11 + parent.worldY; - 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 { + if (parent == null) { // Root bone. + Skeleton skeleton = this.skeleton; boolean skeletonFlipX = skeleton.flipX, skeletonFlipY = skeleton.flipY; - worldX = skeletonFlipX ? -x : x; - worldY = skeletonFlipY ? -y : y; - worldScaleX = scaleX; - worldScaleY = scaleY; - worldRotation = rotationIK; - worldFlipX = skeletonFlipX ^ flipX; - worldFlipY = skeletonFlipY ^ flipY; + if (skeletonFlipX) { + scaleX = -scaleX; + x = -x; + } + if (skeletonFlipY) { + scaleY = -scaleY; + 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); - if (worldFlipX) { - m00 = -cos * worldScaleX; - m01 = sin * worldScaleY; + + float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d; + worldX = pa * x + pb * y + parent.worldX; + worldY = pc * x + pd * y + parent.worldY; + 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 { - m00 = cos * worldScaleX; - m01 = -sin * worldScaleY; - } - if (worldFlipY) { - m10 = -sin * worldScaleX; - m11 = -cos * worldScaleY; - } else { - m10 = sin * worldScaleX; - m11 = cos * worldScaleY; + a = la; + b = lb; + c = lc; + d = ld; } } + /** 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 () { BoneData data = this.data; x = data.x; y = data.y; rotation = data.rotation; - rotationIK = rotation; scaleX = data.scaleX; scaleY = data.scaleY; - flipX = data.flipX; - flipY = data.flipY; } public BoneData getData () { @@ -185,15 +248,6 @@ public class Bone { this.rotation = rotation; } - /** Returns the inverse kinetics rotation, as calculated by any IK constraints. */ - public float getRotationIK () { - return rotationIK; - } - - public void setRotationIK (float rotationIK) { - this.rotationIK = rotationIK; - } - public float getScaleX () { return scaleX; } @@ -220,36 +274,20 @@ public class Bone { scaleY = scale; } - public boolean getFlipX () { - return flipX; + public float getA () { + return a; } - public void setFlipX (boolean flipX) { - this.flipX = flipX; + public float getB () { + return b; } - public boolean getFlipY () { - return flipY; + public float getC () { + return c; } - public void setFlipY (boolean flipY) { - this.flipY = flipY; - } - - public float getM00 () { - return m00; - } - - public float getM01 () { - return m01; - } - - public float getM10 () { - return m10; - } - - public float getM11 () { - return m11; + public float getD () { + return d; } public float getWorldX () { @@ -260,33 +298,37 @@ public class Bone { return worldY; } - public float getWorldRotation () { - return worldRotation; + public float getWorldRotationX () { + return (float)Math.atan2(c, a) * MathUtils.radDeg; + } + + public float getWorldRotationY () { + return (float)Math.atan2(d, b) * MathUtils.radDeg; } public float getWorldScaleX () { - return worldScaleX; + return (float)Math.sqrt(a * a + b * b) * worldSignX; } public float getWorldScaleY () { - return worldScaleY; + return (float)Math.sqrt(c * c + d * d) * worldSignY; } - public boolean getWorldFlipX () { - return worldFlipX; + public float getWorldSignX () { + return worldSignX; } - public boolean getWorldFlipY () { - return worldFlipY; + public float getWorldSignY () { + return worldSignY; } public Matrix3 getWorldTransform (Matrix3 worldTransform) { if (worldTransform == null) throw new IllegalArgumentException("worldTransform cannot be null."); float[] val = worldTransform.val; - val[M00] = m00; - val[M01] = m01; - val[M10] = m10; - val[M11] = m11; + val[M00] = a; + val[M01] = b; + val[M10] = c; + val[M11] = d; val[M02] = worldX; val[M12] = worldY; val[M20] = 0; @@ -297,21 +339,17 @@ public class Bone { public Vector2 worldToLocal (Vector2 world) { float x = world.x - worldX, y = world.y - worldY; - float m00 = this.m00, m10 = this.m10, m01 = this.m01, m11 = this.m11; - if (worldFlipX != worldFlipY) { - m00 = -m00; - m11 = -m11; - } - float invDet = 1 / (m00 * m11 - m01 * m10); - world.x = (x * m00 * invDet - y * m01 * invDet); - world.y = (y * m11 * invDet - x * m10 * invDet); + float a = this.a, b = this.b, c = this.c, d = this.d; + float invDet = 1 / (a * d - b * c); + world.x = (x * a * invDet - y * b * invDet); + world.y = (y * d * invDet - x * c * invDet); return world; } public Vector2 localToWorld (Vector2 local) { float x = local.x, y = local.y; - local.x = x * m00 + y * m01 + worldX; - local.y = x * m10 + y * m11 + worldY; + local.x = x * a + y * b + worldX; + local.y = x * c + y * d + worldY; return local; } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BoneData.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BoneData.java index dabbf08e0..697aaa5e3 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BoneData.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BoneData.java @@ -40,8 +40,7 @@ public class BoneData { float x, y; float rotation; float scaleX = 1, scaleY = 1; - boolean flipX, flipY; - boolean inheritScale = true, inheritRotation = true; + boolean inheritScale, inheritRotation; // Nonessential. final Color color = new Color(0.61f, 0.61f, 0.61f, 1); @@ -65,8 +64,6 @@ public class BoneData { rotation = bone.rotation; scaleX = bone.scaleX; scaleY = bone.scaleY; - flipX = bone.flipX; - flipY = bone.flipY; } /** @return May be null. */ @@ -136,22 +133,6 @@ public class BoneData { this.scaleY = scaleY; } - public boolean getFlipX () { - return flipX; - } - - public void setFlipX (boolean flipX) { - this.flipX = flipX; - } - - public boolean getFlipY () { - return flipY; - } - - public void setFlipY (boolean flipY) { - this.flipY = flipY; - } - public boolean getInheritScale () { return inheritScale; } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java index 966443894..00ad082fe 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java @@ -33,12 +33,9 @@ package com.esotericsoftware.spine; import static com.badlogic.gdx.math.MathUtils.*; -import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.Array; -public class IkConstraint { - static private final Vector2 temp = new Vector2(); - +public class IkConstraint implements Updatable { final IkConstraintData data; final Array bones; Bone target; @@ -59,15 +56,21 @@ public class IkConstraint { } /** Copy constructor. */ - public IkConstraint (IkConstraint ikConstraint, Array bones, Bone target) { + public IkConstraint (IkConstraint ikConstraint, Skeleton skeleton) { data = ikConstraint.data; - this.bones = bones; - this.target = target; + bones = new Array(ikConstraint.bones.size); + 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; bendDirection = ikConstraint.bendDirection; } public void apply () { + update(); + } + + public void update () { Bone target = this.target; Array bones = this.bones; switch (bones.size) { @@ -113,67 +116,143 @@ public class IkConstraint { } 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 * coordinate system. */ 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 rotationIK = (float)Math.atan2(targetY - bone.worldY, targetX - bone.worldX) * radDeg - parentRotation; - bone.rotationIK = rotation + (rotationIK - rotation) * alpha; + float rotationIK = atan2(targetY - bone.worldY, targetX - bone.worldX) * radDeg - parentRotation; + 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 * target is specified in the world coordinate system. * @param child Any descendant bone of the parent. */ - static public void apply (Bone parent, Bone child, float targetX, float targetY, int bendDirection, float alpha) { - float childRotation = child.rotation, parentRotation = parent.rotation; - if (alpha == 0) { - child.rotationIK = childRotation; - parent.rotationIK = parentRotation; - return; - } - Vector2 position = temp; - Bone parentParent = parent.parent; - if (parentParent != null) { - parentParent.worldToLocal(position.set(targetX, targetY)); - targetX = (position.x - parent.x) * parentParent.worldScaleX; - targetY = (position.y - parent.y) * parentParent.worldScaleY; + static public void apply (Bone parent, Bone child, float targetX, float targetY, int bendDir, float alpha) { + if (alpha == 0) return; + float px = parent.x, py = parent.y, psx = parent.scaleX, psy = parent.scaleY, csx = child.scaleX, cy = child.y; + int offset1, offset2, sign2; + if (psx < 0) { + psx = -psx; + offset1 = 180; + sign2 = -1; } else { - targetX -= parent.x; - targetY -= parent.y; + offset1 = 0; + sign2 = 1; } - if (child.parent == parent) - position.set(child.x, child.y); - else - parent.worldToLocal(child.parent.localToWorld(position.set(child.x, child.y))); - float childX = position.x * parent.worldScaleX, childY = position.y * parent.worldScaleY; - float offset = (float)Math.atan2(childY, childX); - float len1 = (float)Math.sqrt(childX * childX + childY * childY), len2 = child.data.length * child.worldScaleX; - // Based on code by Ryan Juckett with permission: Copyright (c) 2008-2009 Ryan Juckett, http://www.ryanjuckett.com/ - float cosDenom = 2 * len1 * len2; - if (cosDenom < 0.0001f) { - child.rotationIK = childRotation + ((float)Math.atan2(targetY, targetX) * radDeg - parentRotation - childRotation) - * alpha; - return; + if (psy < 0) { + psy = -psy; + sign2 = -sign2; } - float cos = clamp((targetX * targetX + targetY * targetY - len1 * len1 - len2 * len2) / cosDenom, -1, 1); - float childAngle = (float)Math.acos(cos) * bendDirection; - float adjacent = len1 + len2 * cos, opposite = len2 * sin(childAngle); - float parentAngle = (float)Math.atan2(targetY * adjacent - targetX * opposite, targetX * adjacent + targetY * opposite); - float rotation = (parentAngle - offset) * radDeg - parentRotation; - if (rotation > 180) - rotation -= 360; - else if (rotation < -180) // - rotation += 360; - parent.rotationIK = parentRotation + rotation * alpha; - rotation = (childAngle + offset) * radDeg - childRotation; - if (rotation > 180) - rotation -= 360; - else if (rotation < -180) // - rotation += 360; - child.rotationIK = childRotation + (rotation + parent.worldRotation - child.parent.worldRotation) * alpha; + if (csx < 0) { + csx = -csx; + offset2 = 180; + } else + offset2 = 0; + Bone pp = parent.parent; + float tx, ty, dx, dy; + if (pp == null) { + tx = targetX - px; + ty = targetY - py; + dx = child.worldX - px; + dy = child.worldY - py; + } else { + float a = pp.a, b = pp.b, c = pp.c, d = pp.d, invDet = 1 / (a * d - b * c); + float wx = pp.worldX, wy = pp.worldY, x = targetX - wx, y = targetY - wy; + 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); } } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java index 582e78334..4fcc61ec7 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java @@ -45,7 +45,8 @@ public class Skeleton { final Array slots; Array drawOrder; final Array ikConstraints; - private final Array> boneCache = new Array(); + final Array transformConstraints; + private final Array updateCache = new Array(); Skin skin; final Color color; float time; @@ -75,6 +76,10 @@ public class Skeleton { for (IkConstraintData ikConstraintData : data.ikConstraints) 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); updateCache(); @@ -102,13 +107,12 @@ public class Skeleton { drawOrder.add(slots.get(skeleton.slots.indexOf(slot, true))); ikConstraints = new Array(skeleton.ikConstraints.size); - for (IkConstraint ikConstraint : skeleton.ikConstraints) { - Bone target = bones.get(skeleton.bones.indexOf(ikConstraint.target, true)); - Array ikBones = new Array(ikConstraint.bones.size); - for (Bone bone : ikConstraint.bones) - ikBones.add(bones.get(skeleton.bones.indexOf(bone, true))); - ikConstraints.add(new IkConstraint(ikConstraint, ikBones, target)); - } + for (IkConstraint ikConstraint : skeleton.ikConstraints) + ikConstraints.add(new IkConstraint(ikConstraint, this)); + + transformConstraints = new Array(skeleton.transformConstraints.size); + for (TransformConstraint transformConstraint : skeleton.transformConstraints) + transformConstraints.add(new TransformConstraint(transformConstraint, this)); skin = skeleton.skin; color = new Color(skeleton.color); @@ -119,72 +123,49 @@ public class Skeleton { 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 () { Array bones = this.bones; - Array> boneCache = this.boneCache; + Array updateCache = this.updateCache; Array ikConstraints = this.ikConstraints; + Array transformConstraints = this.transformConstraints; int ikConstraintsCount = ikConstraints.size; - - int arrayCount = ikConstraintsCount + 1; - while (boneCache.size < arrayCount) - boneCache.add(new Array()); - for (int i = 0; i < arrayCount; i++) - boneCache.get(i).clear(); - - Array nonIkBones = boneCache.first(); - - outer: + int transformConstraintsCount = transformConstraints.size; + updateCache.clear(); for (int i = 0, n = bones.size; i < n; i++) { Bone bone = bones.get(i); - Bone current = bone; - do { - for (int ii = 0; ii < ikConstraintsCount; ii++) { - IkConstraint ikConstraint = ikConstraints.get(ii); - Bone parent = ikConstraint.bones.first(); - Bone child = ikConstraint.bones.peek(); - while (true) { - if (current == child) { - boneCache.get(ii).add(bone); - boneCache.get(ii + 1).add(bone); - continue outer; - } - if (child == parent) break; - child = child.parent; - } + updateCache.add(bone); + for (int ii = 0; ii < transformConstraintsCount; ii++) { + TransformConstraint transformConstraint = transformConstraints.get(ii); + if (bone == transformConstraint.bone) { + updateCache.add(transformConstraint); + break; } - current = current.parent; - } while (current != null); - nonIkBones.add(bone); + } + for (int ii = 0; ii < ikConstraintsCount; ii++) { + 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 () { - Array bones = this.bones; - for (int i = 0, nn = bones.size; i < nn; i++) { - Bone bone = bones.get(i); - bone.rotationIK = bone.rotation; - } - Array> boneCache = this.boneCache; - Array ikConstraints = this.ikConstraints; - int i = 0, last = ikConstraints.size; - while (true) { - Array 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++; - } + Array updateCache = this.updateCache; + for (int i = 0, n = updateCache.size; i < n; i++) + updateCache.get(i).update(); } - /** Sets the bones and slots to their setup pose values. */ + /** Sets the bones, constraints, and slots to their setup pose values. */ public void setToSetupPose () { setBonesToSetupPose(); setSlotsToSetupPose(); } + /** Sets the bones and constraints to their setup pose values. */ public void setBonesToSetupPose () { Array bones = this.bones; for (int i = 0, n = bones.size; i < n; i++) @@ -192,9 +173,17 @@ public class Skeleton { Array ikConstraints = this.ikConstraints; for (int i = 0, n = ikConstraints.size; i < n; i++) { - IkConstraint ikConstraint = ikConstraints.get(i); - ikConstraint.bendDirection = ikConstraint.data.bendDirection; - ikConstraint.mix = ikConstraint.data.mix; + IkConstraint constraint = ikConstraints.get(i); + constraint.bendDirection = constraint.data.bendDirection; + constraint.mix = constraint.data.mix; + } + + Array 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. */ - public IkConstraint findIkConstraint (String ikConstraintName) { - if (ikConstraintName == null) throw new IllegalArgumentException("ikConstraintName cannot be null."); + public IkConstraint findIkConstraint (String constraintName) { + if (constraintName == null) throw new IllegalArgumentException("constraintName cannot be null."); Array ikConstraints = this.ikConstraints; for (int i = 0, n = ikConstraints.size; i < n; i++) { IkConstraint ikConstraint = ikConstraints.get(i); - if (ikConstraint.data.name.equals(ikConstraintName)) return ikConstraint; + if (ikConstraint.data.name.equals(constraintName)) return ikConstraint; + } + return null; + } + + public Array getTransformConstraints () { + return transformConstraints; + } + + /** @return May be null. */ + public TransformConstraint findTransformConstraint (String constraintName) { + if (constraintName == null) throw new IllegalArgumentException("constraintName cannot be null."); + Array 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; } @@ -371,19 +375,13 @@ public class Skeleton { float[] vertices = null; Attachment attachment = slot.attachment; if (attachment instanceof RegionAttachment) { - RegionAttachment region = (RegionAttachment)attachment; - region.updateWorldVertices(slot, false); - vertices = region.getWorldVertices(); + vertices = ((RegionAttachment)attachment).updateWorldVertices(slot, false); } else if (attachment instanceof MeshAttachment) { - MeshAttachment mesh = (MeshAttachment)attachment; - mesh.updateWorldVertices(slot, true); - vertices = mesh.getWorldVertices(); + vertices = ((MeshAttachment)attachment).updateWorldVertices(slot, true); } else if (attachment instanceof SkinnedMeshAttachment) { - SkinnedMeshAttachment mesh = (SkinnedMeshAttachment)attachment; - mesh.updateWorldVertices(slot, true); - vertices = mesh.getWorldVertices(); + vertices = ((SkinnedMeshAttachment)attachment).updateWorldVertices(slot, true); } if (vertices != null) { for (int ii = 0, nn = vertices.length; ii < nn; ii += 5) { diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java index b032e3c28..bee8523a3 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java @@ -47,8 +47,6 @@ import com.esotericsoftware.spine.Animation.CurveTimeline; import com.esotericsoftware.spine.Animation.DrawOrderTimeline; import com.esotericsoftware.spine.Animation.EventTimeline; 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.RotateTimeline; 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_ATTACHMENT = 3; static public final int TIMELINE_COLOR = 4; - static public final int TIMELINE_FLIPX = 5; - static public final int TIMELINE_FLIPY = 6; static public final int CURVE_LINEAR = 0; static public final int CURVE_STEPPED = 1; @@ -135,10 +131,6 @@ public class SkeletonBinary { boneData.scaleY = input.readFloat(); boneData.rotation = input.readFloat(); boneData.length = input.readFloat() * scale; - boneData.flipX = input.readBoolean(); - boneData.flipY = input.readBoolean(); - boneData.inheritScale = input.readBoolean(); - boneData.inheritRotation = input.readBoolean(); if (nonessential) Color.rgba8888ToColor(boneData.color, input.readInt()); skeletonData.bones.add(boneData); } @@ -419,17 +411,6 @@ public class SkeletonBinary { duration = Math.max(duration, timeline.getFrames()[frameCount * 3 - 3]); break; } - case TIMELINE_FLIPX: - case TIMELINE_FLIPY: { - FlipXTimeline timeline = timelineType == TIMELINE_FLIPX ? new FlipXTimeline(frameCount) : new FlipYTimeline( - frameCount); - timeline.boneIndex = boneIndex; - for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) - timeline.setFrame(frameIndex, input.readFloat(), input.readBoolean()); - timelines.add(timeline); - duration = Math.max(duration, timeline.getFrames()[frameCount * 2 - 2]); - break; - } } } } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonData.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonData.java index cc7b63bcc..4914ebeb0 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonData.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonData.java @@ -42,6 +42,7 @@ public class SkeletonData { final Array events = new Array(); final Array animations = new Array(); final Array ikConstraints = new Array(); + final Array transformConstraints = new Array(); float width, height; String version, hash, imagesPath; @@ -153,19 +154,36 @@ public class SkeletonData { return null; } - // --- IK + // --- IK constraints public Array getIkConstraints () { return ikConstraints; } /** @return May be null. */ - public IkConstraintData findIkConstraint (String ikConstraintName) { - if (ikConstraintName == null) throw new IllegalArgumentException("ikConstraintName cannot be null."); + public IkConstraintData findIkConstraint (String constraintName) { + if (constraintName == null) throw new IllegalArgumentException("constraintName cannot be null."); Array ikConstraints = this.ikConstraints; for (int i = 0, n = ikConstraints.size; i < n; i++) { - IkConstraintData ikConstraint = ikConstraints.get(i); - if (ikConstraint.name.equals(ikConstraintName)) return ikConstraint; + IkConstraintData constraint = ikConstraints.get(i); + if (constraint.name.equals(constraintName)) return constraint; + } + return null; + } + + // --- Transform constraints + + public Array getTransformConstraints () { + return transformConstraints; + } + + /** @return May be null. */ + public TransformConstraintData findTransformConstraint (String constraintName) { + if (constraintName == null) throw new IllegalArgumentException("constraintName cannot be null."); + Array 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; } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java index b5f9c5b90..bf4890efa 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java @@ -46,8 +46,6 @@ import com.esotericsoftware.spine.Animation.CurveTimeline; import com.esotericsoftware.spine.Animation.DrawOrderTimeline; import com.esotericsoftware.spine.Animation.EventTimeline; 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.RotateTimeline; import com.esotericsoftware.spine.Animation.ScaleTimeline; @@ -118,10 +116,6 @@ public class SkeletonJson { boneData.rotation = boneMap.getFloat("rotation", 0); boneData.scaleX = boneMap.getFloat("scaleX", 1); boneData.scaleY = boneMap.getFloat("scaleY", 1); - boneData.flipX = boneMap.getBoolean("flipX", false); - boneData.flipY = boneMap.getBoolean("flipY", false); - boneData.inheritScale = boneMap.getBoolean("inheritScale", true); - boneData.inheritRotation = boneMap.getBoolean("inheritRotation", true); String color = boneMap.getString("color", null); if (color != null) boneData.getColor().set(Color.valueOf(color)); @@ -389,20 +383,6 @@ public class SkeletonJson { timelines.add(timeline); duration = Math.max(duration, timeline.getFrames()[timeline.getFrameCount() * 3 - 3]); - } else if (timelineName.equals("flipX") || timelineName.equals("flipY")) { - boolean x = timelineName.equals("flipX"); - FlipXTimeline timeline = x ? new FlipXTimeline(timelineMap.size) : new FlipYTimeline(timelineMap.size); - timeline.boneIndex = boneIndex; - - String field = x ? "x" : "y"; - int frameIndex = 0; - for (JsonValue valueMap = timelineMap.child; valueMap != null; valueMap = valueMap.next) { - timeline.setFrame(frameIndex, valueMap.getFloat("time"), valueMap.getBoolean(field, false)); - frameIndex++; - } - timelines.add(timeline); - duration = Math.max(duration, timeline.getFrames()[timeline.getFrameCount() * 2 - 2]); - } else throw new RuntimeException("Invalid timeline type for a bone: " + timelineName + " (" + boneMap.name + ")"); } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonMeshRenderer.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonMeshRenderer.java new file mode 100644 index 000000000..628a6f540 --- /dev/null +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonMeshRenderer.java @@ -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 { + 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 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); + } + } + } +} diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java index d3e16a7e5..0a37ca4f9 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java @@ -31,9 +31,7 @@ package com.esotericsoftware.spine; -import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g2d.Batch; -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; @@ -41,78 +39,14 @@ import com.esotericsoftware.spine.attachments.RegionAttachment; import com.esotericsoftware.spine.attachments.SkeletonAttachment; import com.esotericsoftware.spine.attachments.SkinnedMeshAttachment; -public class SkeletonRenderer { - static private final short[] quadTriangles = {0, 1, 2, 2, 3, 0}; +public class SkeletonRenderer { + boolean premultipliedAlpha; - private boolean premultipliedAlpha; - - @SuppressWarnings("null") - public void draw (PolygonSpriteBatch batch, Skeleton skeleton) { - boolean premultipliedAlpha = this.premultipliedAlpha; - BlendMode blendMode = null; - - float[] vertices = null; - short[] triangles = null; - Array 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 SkeletonRenderer () { + super(); } - public void draw (Batch batch, Skeleton skeleton) { + public void draw (T batch, Skeleton skeleton) { boolean premultipliedAlpha = this.premultipliedAlpha; BlendMode blendMode = null; @@ -122,8 +56,7 @@ public class SkeletonRenderer { Attachment attachment = slot.attachment; if (attachment instanceof RegionAttachment) { RegionAttachment regionAttachment = (RegionAttachment)attachment; - regionAttachment.updateWorldVertices(slot, premultipliedAlpha); - float[] vertices = regionAttachment.getWorldVertices(); + float[] vertices = regionAttachment.updateWorldVertices(slot, premultipliedAlpha); BlendMode slotBlendMode = slot.data.getBlendMode(); if (slotBlendMode != blendMode) { blendMode = slotBlendMode; @@ -132,7 +65,7 @@ public class SkeletonRenderer { batch.draw(regionAttachment.getRegion().getTexture(), vertices, 0, 20); } 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) { Skeleton attachmentSkeleton = ((SkeletonAttachment)attachment).getSkeleton(); @@ -143,9 +76,9 @@ public class SkeletonRenderer { 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()); + // rootBone.setScaleX(1 + bone.getWorldScaleX() - oldScaleX); + // rootBone.setScaleY(1 + bone.getWorldScaleY() - oldScaleY); + rootBone.setRotation(oldRotation + bone.getWorldRotationX()); attachmentSkeleton.updateWorldTransform(); draw(batch, attachmentSkeleton); diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRendererDebug.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRendererDebug.java index 44e1d63cf..362064c9a 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRendererDebug.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRendererDebug.java @@ -87,8 +87,8 @@ public class SkeletonRendererDebug { for (int i = 0, n = bones.size; i < n; i++) { Bone bone = bones.get(i); if (bone.parent == null) continue; - float x = skeletonX + bone.data.length * bone.m00 + bone.worldX; - float y = skeletonY + bone.data.length * bone.m10 + bone.worldY; + float x = skeletonX + bone.data.length * bone.a + bone.worldX; + float y = skeletonY + bone.data.length * bone.c + bone.worldY; shapes.rectLine(skeletonX + bone.worldX, skeletonY + bone.worldY, x, y, boneWidth * scale); } shapes.end(); @@ -105,8 +105,7 @@ public class SkeletonRendererDebug { Attachment attachment = slot.attachment; if (attachment instanceof RegionAttachment) { RegionAttachment regionAttachment = (RegionAttachment)attachment; - regionAttachment.updateWorldVertices(slot, false); - float[] vertices = regionAttachment.getWorldVertices(); + float[] vertices = regionAttachment.updateWorldVertices(slot, false); shapes.line(vertices[X1], vertices[Y1], vertices[X2], vertices[Y2]); shapes.line(vertices[X2], vertices[Y2], vertices[X3], vertices[Y3]); shapes.line(vertices[X3], vertices[Y3], vertices[X4], vertices[Y4]); diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraint.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraint.java new file mode 100644 index 000000000..eb9b2d4c8 --- /dev/null +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraint.java @@ -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; + } +} diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraintData.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraintData.java new file mode 100644 index 000000000..53a140279 --- /dev/null +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraintData.java @@ -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; + } +} diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Updatable.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Updatable.java new file mode 100644 index 000000000..a9466aba6 --- /dev/null +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Updatable.java @@ -0,0 +1,6 @@ + +package com.esotericsoftware.spine; + +public interface Updatable { + public void update (); +} diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/BoundingBoxAttachment.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/BoundingBoxAttachment.java index 91f6d7314..4c11f1069 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/BoundingBoxAttachment.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/BoundingBoxAttachment.java @@ -44,10 +44,10 @@ public class BoundingBoxAttachment extends Attachment { public void computeWorldVertices (Bone bone, float[] worldVertices) { Skeleton skeleton = bone.getSkeleton(); float x = skeleton.getX() + bone.getWorldX(), y = skeleton.getY() + bone.getWorldY(); - float m00 = bone.getM00(); - float m01 = bone.getM01(); - float m10 = bone.getM10(); - float m11 = bone.getM11(); + float m00 = bone.getA(); + float m01 = bone.getB(); + float m10 = bone.getC(); + float m11 = bone.getD(); float[] vertices = this.vertices; for (int i = 0, n = vertices.length; i < n; i += 2) { float px = vertices[i]; diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/MeshAttachment.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/MeshAttachment.java index 88b0a2b4c..bbfb90189 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/MeshAttachment.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/MeshAttachment.java @@ -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(); Color skeletonColor = skeleton.getColor(); Color slotColor = slot.getColor(); @@ -117,7 +118,7 @@ public class MeshAttachment extends Attachment { if (slotVertices.size == vertices.length) vertices = slotVertices.items; Bone bone = slot.getBone(); 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) { float vx = vertices[v]; float vy = vertices[v + 1]; @@ -125,6 +126,7 @@ public class MeshAttachment extends Attachment { worldVertices[w + 1] = vx * m10 + vy * m11 + y; worldVertices[w + 2] = color; } + return worldVertices; } public float[] getWorldVertices () { diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/RegionAttachment.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/RegionAttachment.java index aef0a7c7a..c645f58ce 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/RegionAttachment.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/RegionAttachment.java @@ -146,7 +146,8 @@ public class RegionAttachment extends Attachment { 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(); Color skeletonColor = skeleton.getColor(); Color slotColor = slot.getColor(); @@ -163,7 +164,7 @@ public class RegionAttachment extends Attachment { float[] offset = this.offset; Bone bone = slot.getBone(); 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; offsetX = offset[BRX]; @@ -189,6 +190,7 @@ public class RegionAttachment extends Attachment { vertices[X4] = offsetX * m00 + offsetY * m01 + x; // ur vertices[Y4] = offsetX * m10 + offsetY * m11 + y; vertices[C4] = color; + return vertices; } public float[] getWorldVertices () { diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/RegionSequenceAttachment.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/RegionSequenceAttachment.java index 8d7feb754..2e70db9bc 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/RegionSequenceAttachment.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/RegionSequenceAttachment.java @@ -46,7 +46,7 @@ public class RegionSequenceAttachment extends RegionAttachment { 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); int frameIndex = (int)(slot.getAttachmentTime() / frameTime); @@ -74,7 +74,7 @@ public class RegionSequenceAttachment extends RegionAttachment { } setRegion(regions[frameIndex]); - super.updateWorldVertices(slot, premultipliedAlpha); + return super.updateWorldVertices(slot, premultipliedAlpha); } public TextureRegion[] getRegions () { diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/SkinnedMeshAttachment.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/SkinnedMeshAttachment.java index c8119198a..01f4acb87 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/SkinnedMeshAttachment.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/SkinnedMeshAttachment.java @@ -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(); Color skeletonColor = skeleton.getColor(); Color meshColor = slot.getColor(); @@ -126,8 +127,8 @@ public class SkinnedMeshAttachment extends Attachment { for (; v < nn; v++, b += 3) { Bone bone = (Bone)skeletonBones[bones[v]]; float vx = weights[b], vy = weights[b + 1], weight = weights[b + 2]; - wx += (vx * bone.getM00() + vy * bone.getM01() + bone.getWorldX()) * weight; - wy += (vx * bone.getM10() + vy * bone.getM11() + bone.getWorldY()) * weight; + wx += (vx * bone.getA() + vy * bone.getB() + bone.getWorldX()) * weight; + wy += (vx * bone.getC() + vy * bone.getD() + bone.getWorldY()) * weight; } worldVertices[w] = wx + x; worldVertices[w + 1] = wy + y; @@ -141,14 +142,15 @@ public class SkinnedMeshAttachment extends Attachment { for (; v < nn; v++, b += 3, f += 2) { Bone bone = (Bone)skeletonBones[bones[v]]; 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; - wy += (vx * bone.getM10() + vy * bone.getM11() + bone.getWorldY()) * weight; + wx += (vx * bone.getA() + vy * bone.getB() + bone.getWorldX()) * weight; + wy += (vx * bone.getC() + vy * bone.getD() + bone.getWorldY()) * weight; } worldVertices[w] = wx + x; worldVertices[w + 1] = wy + y; worldVertices[w + 2] = color; } } + return worldVertices; } public float[] getWorldVertices () { diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/SkeletonActor.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/SkeletonActor.java new file mode 100644 index 000000000..c77c44c4e --- /dev/null +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/SkeletonActor.java @@ -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; + } +} diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/SkeletonActorPool.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/SkeletonActorPool.java new file mode 100644 index 000000000..680f9cf60 --- /dev/null +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/SkeletonActorPool.java @@ -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 { + private SkeletonRenderer renderer; + SkeletonData skeletonData; + AnimationStateData stateData; + private final Pool skeletonPool; + private final Pool statePool; + private final Array 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(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(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 obtained = this.obtained; + outer: + for (int i = obtained.size - 1; i >= 0; i--) { + SkeletonActor actor = obtained.get(i); + Array 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 getObtained () { + return obtained; + } +} diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/SkeletonPool.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/SkeletonPool.java new file mode 100644 index 000000000..5fd2cecbf --- /dev/null +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/SkeletonPool.java @@ -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 { + 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); + } +} diff --git a/spine-libgdx/spine-skeletonviewer/assets/skin/font-calibri-12.fnt b/spine-libgdx/spine-skeletonviewer/assets/skin/font-calibri-12.fnt new file mode 100644 index 000000000..c4dde4705 --- /dev/null +++ b/spine-libgdx/spine-skeletonviewer/assets/skin/font-calibri-12.fnt @@ -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 diff --git a/spine-libgdx/spine-skeletonviewer/assets/skin/font-calibri-12.png b/spine-libgdx/spine-skeletonviewer/assets/skin/font-calibri-12.png new file mode 100644 index 000000000..1dd1fff22 Binary files /dev/null and b/spine-libgdx/spine-skeletonviewer/assets/skin/font-calibri-12.png differ diff --git a/spine-libgdx/spine-skeletonviewer/assets/skin/skin.atlas b/spine-libgdx/spine-skeletonviewer/assets/skin/skin.atlas index e100bd560..0dea6770c 100644 --- a/spine-libgdx/spine-skeletonviewer/assets/skin/skin.atlas +++ b/spine-libgdx/spine-skeletonviewer/assets/skin/skin.atlas @@ -1,200 +1,194 @@ skin.png -size: 256,128 +size: 93,139 format: RGBA8888 filter: Linear,Linear repeat: none -check-off +button-disabled rotate: false - xy: 11, 5 - size: 14, 14 - orig: 14, 14 + xy: 75, 73 + size: 17, 17 + split: 6, 6, 5, 5 + pad: -1, -1, 3, 3 + orig: 17, 17 offset: 0, 0 index: -1 -textfield +button-down rotate: false - xy: 11, 5 - size: 14, 14 - split: 3, 3, 3, 3 - orig: 14, 14 + xy: 75, 54 + size: 17, 17 + split: 6, 6, 5, 5 + pad: -1, -1, 3, 3 + orig: 17, 17 offset: 0, 0 index: -1 -check-on +button-over rotate: false - xy: 125, 35 - size: 14, 14 - orig: 14, 14 + xy: 66, 35 + size: 17, 17 + split: 6, 6, 5, 5 + pad: -1, -1, 3, 3 + orig: 17, 17 offset: 0, 0 index: -1 -cursor +button-up 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 split: 1, 1, 1, 1 orig: 3, 3 offset: 0, 0 index: -1 -default +textField-round rotate: false - xy: 1, 50 - size: 253, 77 - orig: 254, 77 - offset: 1, 0 - index: -1 -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 + xy: 52, 69 + size: 21, 21 + split: 6, 5, 5, 5 + pad: -1, -1, 3, 3 + orig: 21, 21 offset: 0, 0 index: -1 white rotate: false - xy: 174, 48 - size: 1, 1 - orig: 1, 1 + xy: 66, 64 + size: 3, 3 + 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 index: -1 diff --git a/spine-libgdx/spine-skeletonviewer/assets/skin/skin.json b/spine-libgdx/spine-skeletonviewer/assets/skin/skin.json index d686f5763..587970315 100644 --- a/spine-libgdx/spine-skeletonviewer/assets/skin/skin.json +++ b/spine-libgdx/spine-skeletonviewer/assets/skin/skin.json @@ -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: { - green: { a: 1, b: 0, g: 1, r: 0 }, - white: { a: 1, b: 1, g: 1, r: 1 }, - red: { a: 1, b: 0, g: 0, r: 1 }, - black: { a: 1, b: 0, g: 0, r: 0 } + white: { r: 1, g: 1, b: 1 }, + black: {}, + disabled: { r: 0.53, g: 0.53, b: 0.53 }, + selection: { r: 0.77, g: 1, b: 1, a: 0.25 }, }, com.badlogic.gdx.scenes.scene2d.ui.Skin$TintedDrawable: { - dialogDim: { name: white, color: { r: 0, g: 0, b: 0, a: 0.45 } } -}, -com.badlogic.gdx.scenes.scene2d.ui.Button$ButtonStyle: { - default: { down: default-round-down, up: default-round }, - toggle: { down: default-round-down, checked: default-round-down, up: default-round } + selection: { name: white, color: selection }, + dim: { name: white, color: { r: 0, g: 0, b: 0, a: 0.3 } }, + list-selection: { name: list-selection, color: selection }, }, com.badlogic.gdx.scenes.scene2d.ui.TextButton$TextButtonStyle: { - default: { down: default-round-down, up: default-round, font: default-font, fontColor: white }, - toggle: { down: default-round-down, up: default-round, checked: default-round-down, font: default-font, fontColor: white, downFontColor: red } + default: { + 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: { - 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: { default: { - font: default-font, fontColor: white, background: default-select, - scrollStyle: default, - listStyle: { font: default-font, selection: default-select-selection } - } + background: selectBox-closed, backgroundOver: selectBox-over, backgroundOpen: selectBox-open, font: default, fontColor: white, + scrollStyle: { background: selectBox-list, hScrollKnob: scrollpane-horiz, vScrollKnob: scrollpane-vert }, + listStyle: { font: default, selection: list-selection }, + }, }, -com.badlogic.gdx.scenes.scene2d.ui.SplitPane$SplitPaneStyle: { - default-vertical: { handle: default-splitpane-vertical }, - default-horizontal: { handle: default-splitpane } -}, -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.TextTooltip$TextTooltipStyle: { + default: { + label: { font: default, fontColor: white }, + background: group + }, }, 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: { - default: { fontColorUnselected: white, selection: selection, fontColorSelected: white, font: default-font } +com.badlogic.gdx.scenes.scene2d.ui.Slider$SliderStyle: { + 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 } -} } diff --git a/spine-libgdx/spine-skeletonviewer/assets/skin/skin.png b/spine-libgdx/spine-skeletonviewer/assets/skin/skin.png index add8b6f0c..3ceba5a9a 100644 Binary files a/spine-libgdx/spine-skeletonviewer/assets/skin/skin.png and b/spine-libgdx/spine-skeletonviewer/assets/skin/skin.png differ diff --git a/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java b/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java index a06b03d3a..c8f56325a 100644 --- a/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java +++ b/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java @@ -50,6 +50,7 @@ import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.Pixmap; import com.badlogic.gdx.graphics.Pixmap.Format; 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.TextureAtlas; import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion; @@ -83,7 +84,7 @@ public class SkeletonViewer extends ApplicationAdapter { UI ui; PolygonSpriteBatch batch; - SkeletonRenderer renderer; + SkeletonMeshRenderer renderer; SkeletonRendererDebug debugRenderer; SkeletonData skeletonData; Skeleton skeleton; @@ -96,38 +97,49 @@ public class SkeletonViewer extends ApplicationAdapter { public void create () { ui = new UI(); batch = new PolygonSpriteBatch(); - renderer = new SkeletonRenderer(); + renderer = new SkeletonMeshRenderer(); debugRenderer = new SkeletonRendererDebug(); skeletonX = (int)(ui.window.getWidth() + (Gdx.graphics.getWidth() - ui.window.getWidth()) / 2); skeletonY = Gdx.graphics.getHeight() / 4; 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; - // 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 { + // 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(); if (extension.equalsIgnoreCase("json") || extension.equalsIgnoreCase("txt")) { SkeletonJson json = new SkeletonJson(atlas); @@ -137,6 +149,7 @@ public class SkeletonViewer extends ApplicationAdapter { SkeletonBinary binary = new SkeletonBinary(atlas); binary.setScale(ui.scaleSlider.getValue()); skeletonData = binary.readSkeletonData(skeletonFile); + if (skeletonData.getBones().size == 0) throw new Exception("No bones in skeleton data."); } } catch (Exception ex) { ex.printStackTrace(); @@ -153,7 +166,7 @@ public class SkeletonViewer extends ApplicationAdapter { state = new AnimationState(new AnimationStateData(skeletonData)); this.skeletonFile = skeletonFile; - Preferences prefs = Gdx.app.getPreferences("spine-skeletontest"); + Preferences prefs = Gdx.app.getPreferences("spine-skeletonviewer"); prefs.putString("lastFile", skeletonFile.path()); prefs.flush(); lastModified = skeletonFile.lastModified(); @@ -161,7 +174,7 @@ public class SkeletonViewer extends ApplicationAdapter { // Populate UI. - ui.skeletonLabel.setText(skeletonFile.name()); + ui.window.getTitleLabel().setText(skeletonFile.name()); { Array items = new Array(); for (Skin skin : skeletonData.getSkins()) @@ -177,8 +190,9 @@ public class SkeletonViewer extends ApplicationAdapter { // Configure skeleton from UI. - skeleton.setSkin(ui.skinList.getSelected()); - state.setAnimation(0, ui.animationList.getSelected(), ui.loopCheckbox.isChecked()); + if (ui.skinList.getSelected() != null) skeleton.setSkin(ui.skinList.getSelected()); + if (ui.animationList.getSelected() != null) + state.setAnimation(0, ui.animationList.getSelected(), ui.loopCheckbox.isChecked()); if (reload) ui.toast("Reloaded."); } @@ -217,6 +231,7 @@ public class SkeletonViewer extends ApplicationAdapter { // skeleton.getRootBone().setY(skeletonY); skeleton.updateWorldTransform(); + batch.setColor(Color.WHITE); batch.begin(); renderer.draw(batch, skeleton); batch.end(); @@ -252,7 +267,7 @@ public class SkeletonViewer extends ApplicationAdapter { batch.getProjectionMatrix().setToOrtho2D(0, 0, width, height); debugRenderer.getShapeRenderer().setProjectionMatrix(batch.getProjectionMatrix()); 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 { @@ -262,8 +277,7 @@ public class SkeletonViewer extends ApplicationAdapter { Window window = new Window("Skeleton", skin); Table root = new Table(skin); - TextButton browseButton = new TextButton("Browse", skin); - Label skeletonLabel = new Label("", skin); + TextButton openButton = new TextButton("Open", skin); List animationList = new List(skin); List skinList = new List(skin); CheckBox loopCheckbox = new CheckBox(" Loop", skin); @@ -305,30 +319,24 @@ public class SkeletonViewer extends ApplicationAdapter { window.setMovable(false); window.setResizable(false); + window.setKeepWithinStage(false); + window.setX(-3); + window.setY(-2); - minimizeButton.padTop(-2).padLeft(5); - minimizeButton.getColor().a = 0.66f; - window.getTitleTable().add(minimizeButton).size(20, 20); + window.getTitleTable().add(openButton).space(3); + window.getTitleTable().add(minimizeButton).width(20); - ScrollPane skinScroll = new ScrollPane(skinList, skin); + ScrollPane skinScroll = new ScrollPane(skinList, skin, "bg"); skinScroll.setFadeScrollBars(false); - ScrollPane animationScroll = new ScrollPane(animationList, skin); + ScrollPane animationScroll = new ScrollPane(animationList, skin, "bg"); animationScroll.setFadeScrollBars(false); // Layout. - root.pad(2, 4, 4, 4).defaults().space(6); - root.columnDefaults(0).top().right(); + root.defaults().space(6); + root.columnDefaults(0).top().right().padTop(3); 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:"); { Table table = table(); @@ -378,7 +386,6 @@ public class SkeletonViewer extends ApplicationAdapter { stage.addActor(table); table.pad(10).bottom().right(); table.add(toasts); - table.debug(); } // 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) { FileDialog fileDialog = new FileDialog((Frame)null, "Choose skeleton file"); fileDialog.setMode(FileDialog.LOAD); @@ -427,11 +434,11 @@ public class SkeletonViewer extends ApplicationAdapter { public void clicked (InputEvent event, float x, float y) { if (minimizeButton.isChecked()) { window.getCells().get(0).setActor(null); - window.setHeight(20); + window.setHeight(37); minimizeButton.setText("+"); } else { window.getCells().get(0).setActor(root); - ui.window.setHeight(Gdx.graphics.getHeight()); + ui.window.setHeight(Gdx.graphics.getHeight() + 8); minimizeButton.setText("-"); } } @@ -466,7 +473,11 @@ public class SkeletonViewer extends ApplicationAdapter { skinList.addListener(new ChangeListener() { public void changed (ChangeEvent event, Actor actor) { if (skeleton != null) { - skeleton.setSkin(skinList.getSelected()); + String skinName = skinList.getSelected(); + if (skinName == null) + skeleton.setSkin((Skin)null); + else + skeleton.setSkin(skinName); skeleton.setSlotsToSetupPose(); } } @@ -504,7 +515,7 @@ public class SkeletonViewer extends ApplicationAdapter { delay(5f), // parallel(moveBy(0, table.getHeight(), 0.3f), fadeOut(0.3f)), // removeActor() // - )); + )); for (Actor actor : toasts.getChildren()) actor.addAction(moveBy(0, table.getHeight(), 0.3f)); toasts.addActor(table);