diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/TestHarness.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/TestHarness.java
index 8ace69cf3..06c485253 100644
--- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/TestHarness.java
+++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/TestHarness.java
@@ -50,7 +50,7 @@ public class TestHarness extends ApplicationAdapter {
skeleton = new Skeleton(skeletonData);
skeleton.setPosition(320, 590);
- skeleton.flipY = true;
+ skeleton.setScaleY(-1);
AnimationStateData stateData = new AnimationStateData(skeletonData);
state = new AnimationState(stateData);
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 54d397a3c..1994c6911 100644
--- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java
+++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java
@@ -30,13 +30,12 @@
package com.esotericsoftware.spine;
-import static com.esotericsoftware.spine.utils.SpineUtils.*;
import static com.badlogic.gdx.math.Matrix3.*;
+import static com.esotericsoftware.spine.utils.SpineUtils.*;
import com.badlogic.gdx.math.Matrix3;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;
-import com.esotericsoftware.spine.BoneData.TransformMode;
/** Stores a bone's current pose.
*
@@ -111,28 +110,14 @@ public class Bone implements Updatable {
Bone parent = this.parent;
if (parent == null) { // Root bone.
- float rotationY = rotation + 90 + shearY;
- float la = cosDeg(rotation + shearX) * scaleX;
- float lb = cosDeg(rotationY) * scaleY;
- float lc = sinDeg(rotation + shearX) * scaleX;
- float ld = sinDeg(rotationY) * scaleY;
Skeleton skeleton = this.skeleton;
- if (skeleton.flipX) {
- x = -x;
- la = -la;
- lb = -lb;
- }
- if (skeleton.flipY) {
- y = -y;
- lc = -lc;
- ld = -ld;
- }
- a = la;
- b = lb;
- c = lc;
- d = ld;
- worldX = x + skeleton.x;
- worldY = y + skeleton.y;
+ float rotationY = rotation + 90 + shearY, sx = skeleton.scaleX, sy = skeleton.scaleY;
+ a = cosDeg(rotation + shearX) * scaleX * sx;
+ b = cosDeg(rotationY) * scaleY * sy;
+ c = sinDeg(rotation + shearX) * scaleX * sx;
+ d = sinDeg(rotationY) * scaleY * sy;
+ worldX = x * sx + skeleton.x;
+ worldY = y * sy + skeleton.y;
return;
}
@@ -188,8 +173,8 @@ public class Bone implements Updatable {
case noScale:
case noScaleOrReflection: {
float cos = cosDeg(rotation), sin = sinDeg(rotation);
- float za = pa * cos + pb * sin;
- float zc = pc * cos + pd * sin;
+ float za = (pa * cos + pb * sin) / skeleton.scaleX;
+ float zc = (pc * cos + pd * sin) / skeleton.scaleY;
float s = (float)Math.sqrt(za * za + zc * zc);
if (s > 0.00001f) s = 1 / s;
za *= s;
@@ -201,26 +186,18 @@ public class Bone implements Updatable {
float la = cosDeg(shearX) * scaleX;
float lb = cosDeg(90 + shearY) * scaleY;
float lc = sinDeg(shearX) * scaleX;
- float ld = sinDeg(90 + shearY) * scaleY;
- if (data.transformMode != TransformMode.noScaleOrReflection ? pa * pd - pb * pc < 0 : skeleton.flipX != skeleton.flipY) {
- zb = -zb;
- zd = -zd;
- }
+ float ld = sinDeg(90 + shearY) * scaleY;
a = za * la + zb * lc;
b = za * lb + zb * ld;
c = zc * la + zd * lc;
d = zc * lb + zd * ld;
- return;
+ break;
}
}
- if (skeleton.flipX) {
- a = -a;
- b = -b;
- }
- if (skeleton.flipY) {
- c = -c;
- d = -d;
- }
+ a *= skeleton.scaleX;
+ b *= skeleton.scaleX;
+ c *= skeleton.scaleY;
+ d *= skeleton.scaleY;
}
/** Sets this bone's local transform to the setup pose. */
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 9bc28c918..e267fa1a2 100644
--- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java
+++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java
@@ -30,14 +30,14 @@
package com.esotericsoftware.spine;
-import static com.esotericsoftware.spine.utils.SpineUtils.cosDeg;
-import static com.esotericsoftware.spine.utils.SpineUtils.sinDeg;
+import static com.esotericsoftware.spine.utils.SpineUtils.*;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.FloatArray;
import com.badlogic.gdx.utils.ObjectMap.Entry;
+
import com.esotericsoftware.spine.Skin.Key;
import com.esotericsoftware.spine.attachments.Attachment;
import com.esotericsoftware.spine.attachments.MeshAttachment;
@@ -61,7 +61,7 @@ public class Skeleton {
Skin skin;
final Color color;
float time;
- boolean flipX, flipY;
+ float scaleX = 1, scaleY = 1;
float x, y;
public Skeleton (SkeletonData data) {
@@ -150,8 +150,8 @@ public class Skeleton {
skin = skeleton.skin;
color = new Color(skeleton.color);
time = skeleton.time;
- flipX = skeleton.flipX;
- flipY = skeleton.flipY;
+ scaleX = skeleton.scaleX;
+ scaleY = skeleton.scaleY;
updateCache();
}
@@ -331,9 +331,9 @@ public class Skeleton {
for (int i = 0, n = updateCache.size; i < n; i++)
updateCache.get(i).update();
}
-
- /** Updates the world transform for each bone and applies all constraints. The
- * root bone will be temporarily parented to the specified bone.
+
+ /** Updates the world transform for each bone and applies all constraints. The root bone will be temporarily parented to the
+ * specified bone.
*
* See World transforms in the Spine
* Runtimes Guide. */
@@ -353,7 +353,7 @@ public class Skeleton {
bone.ashearY = bone.shearY;
bone.appliedValid = true;
}
-
+
// Apply the parent bone transform to the root bone. The root bone
// always inherits scale, rotation and reflection.
Bone rootBone = getRootBone();
@@ -366,20 +366,11 @@ public class Skeleton {
float lb = cosDeg(rotationY) * rootBone.scaleY;
float lc = sinDeg(rootBone.rotation + rootBone.shearX) * rootBone.scaleX;
float ld = sinDeg(rotationY) * rootBone.scaleY;
- rootBone.a = pa * la + pb * lc;
- rootBone.b = pa * lb + pb * ld;
- rootBone.c = pc * la + pd * lc;
- rootBone.d = pc * lb + pd * ld;
-
- if (flipY) {
- rootBone.a = -rootBone.a;
- rootBone.b = -rootBone.b;
- }
- if (flipX) {
- rootBone.c = -rootBone.c;
- rootBone.d = -rootBone.d;
- }
-
+ rootBone.a = (pa * la + pb * lc) * scaleX;
+ rootBone.b = (pa * lb + pb * ld) * scaleX;
+ rootBone.c = (pc * la + pd * lc) * scaleY;
+ rootBone.d = (pc * lb + pd * ld) * scaleY;
+
// Update everything except root bone.
Array updateCache = this.updateCache;
for (int i = 0, n = updateCache.size; i < n; i++) {
@@ -685,29 +676,29 @@ public class Skeleton {
this.color.set(color);
}
- /** If true, the entire skeleton is flipped over the Y axis. This affects all bones, even if the bone's transform mode
- * disallows scale inheritance. */
- public boolean getFlipX () {
- return flipX;
+ /** Scales the entire skeleton on the X axis. This affects all bones, even if the bone's transform mode disallows scale
+ * inheritance. */
+ public float getScaleX () {
+ return scaleX;
}
- public void setFlipX (boolean flipX) {
- this.flipX = flipX;
+ public void setScaleX (float scaleX) {
+ this.scaleX = scaleX;
}
- /** If true, the entire skeleton is flipped over the X axis. This affects all bones, even if the bone's transform mode
- * disallows scale inheritance. */
- public boolean getFlipY () {
- return flipY;
+ /** Scales the entire skeleton on the Y axis. This affects all bones, even if the bone's transform mode disallows scale
+ * inheritance. */
+ public float getScaleY () {
+ return scaleY;
}
- public void setFlipY (boolean flipY) {
- this.flipY = flipY;
+ public void setScaleY (float scaleY) {
+ this.scaleY = scaleY;
}
- public void setFlip (boolean flipX, boolean flipY) {
- this.flipX = flipX;
- this.flipY = flipY;
+ public void setScale (float scaleX, float scaleY) {
+ this.scaleX = scaleX;
+ this.scaleY = scaleY;
}
/** Sets the skeleton X position, which is added to the root bone worldX position. */
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
index 4709802e5..a22286cb9 100644
--- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/SkeletonActorPool.java
+++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/SkeletonActorPool.java
@@ -70,7 +70,7 @@ public class SkeletonActorPool extends Pool {
protected void reset (Skeleton skeleton) {
skeleton.setColor(Color.WHITE);
- skeleton.setFlip(false, false);
+ skeleton.setScale(1, 1);
skeleton.setSkin((Skin)null);
skeleton.setSkin(SkeletonActorPool.this.skeletonData.getDefaultSkin());
skeleton.setToSetupPose();
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 1e4bc020d..f6cd0a632 100644
--- a/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java
+++ b/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java
@@ -192,11 +192,11 @@ public class SkeletonViewer extends ApplicationAdapter {
String extension = skeletonFile.extension();
if (extension.equalsIgnoreCase("json") || extension.equalsIgnoreCase("txt")) {
SkeletonJson json = new SkeletonJson(atlas);
- json.setScale(ui.scaleSlider.getValue());
+ json.setScale(ui.loadScaleSlider.getValue());
skeletonData = json.readSkeletonData(skeletonFile);
} else {
SkeletonBinary binary = new SkeletonBinary(atlas);
- binary.setScale(ui.scaleSlider.getValue());
+ binary.setScale(ui.loadScaleSlider.getValue());
skeletonData = binary.readSkeletonData(skeletonFile);
if (skeletonData.getBones().size == 0) throw new Exception("No bones in skeleton data.");
}
@@ -311,7 +311,10 @@ public class SkeletonViewer extends ApplicationAdapter {
renderer.setPremultipliedAlpha(ui.premultipliedCheckbox.isChecked());
batch.setPremultipliedAlpha(ui.premultipliedCheckbox.isChecked());
- skeleton.setFlip(ui.flipXCheckbox.isChecked(), ui.flipYCheckbox.isChecked());
+ float scaleX = ui.xScaleSlider.getValue(), scaleY = ui.yScaleSlider.getValue();
+ if (skeleton.scaleX == 0) skeleton.scaleX = 0.01f;
+ if (skeleton.scaleY == 0) skeleton.scaleY = 0.01f;
+ skeleton.setScale(scaleX, scaleY);
delta = Math.min(delta, 0.032f) * ui.speedSlider.getValue();
skeleton.update(delta);
@@ -412,16 +415,21 @@ public class SkeletonViewer extends ApplicationAdapter {
TextButton openButton = new TextButton("Open", skin);
TextButton minimizeButton = new TextButton("-", skin);
- Slider scaleSlider = new Slider(0.1f, 3, 0.01f, false, skin);
- Label scaleLabel = new Label("1.0", skin);
- TextButton scaleResetButton = new TextButton("Reset", skin);
+ Slider loadScaleSlider = new Slider(0.1f, 3, 0.01f, false, skin);
+ Label loadScaleLabel = new Label("100%", skin);
+ TextButton loadScaleResetButton = new TextButton("Reset", skin);
Slider zoomSlider = new Slider(0.01f, 10, 0.01f, false, skin);
- Label zoomLabel = new Label("1.0", skin);
+ Label zoomLabel = new Label("100%", skin);
TextButton zoomResetButton = new TextButton("Reset", skin);
- CheckBox flipXCheckbox = new CheckBox("X", skin);
- CheckBox flipYCheckbox = new CheckBox("Y", skin);
+ Slider xScaleSlider = new Slider(-2, 2, 0.01f, false, skin);
+ Label xScaleLabel = new Label("100%", skin);
+ TextButton xScaleResetButton = new TextButton("Reset", skin);
+
+ Slider yScaleSlider = new Slider(-2, 2, 0.01f, false, skin);
+ Label yScaleLabel = new Label("100%", skin);
+ TextButton yScaleResetButton = new TextButton("Reset", skin);
CheckBox debugBonesCheckbox = new CheckBox("Bones", skin);
CheckBox debugRegionsCheckbox = new CheckBox("Regions", skin);
@@ -448,17 +456,17 @@ public class SkeletonViewer extends ApplicationAdapter {
CheckBox addCheckbox = new CheckBox("Add", skin);
Slider alphaSlider = new Slider(0, 1, 0.01f, false, skin);
- Label alphaLabel = new Label("1.0", skin);
+ Label alphaLabel = new Label("100%", skin);
List animationList = new List(skin);
ScrollPane animationScroll = new ScrollPane(animationList, skin, "bg");
Slider speedSlider = new Slider(0, 3, 0.01f, false, skin);
- Label speedLabel = new Label("1.0", skin);
+ Label speedLabel = new Label("1.0x", skin);
TextButton speedResetButton = new TextButton("Reset", skin);
Slider mixSlider = new Slider(0, 4, 0.01f, false, skin);
- Label mixLabel = new Label("0.3", skin);
+ Label mixLabel = new Label("0.3s", skin);
Label statusLabel = new Label("", skin);
WidgetGroup toasts = new WidgetGroup();
@@ -483,21 +491,27 @@ public class SkeletonViewer extends ApplicationAdapter {
loopCheckbox.setChecked(true);
- scaleSlider.setValue(1);
- scaleSlider.setSnapToValues(new float[] {0.5f, 1, 1.5f, 2, 2.5f, 3, 3.5f}, 0.01f);
+ loadScaleSlider.setValue(1);
+ loadScaleSlider.setSnapToValues(new float[] {0.5f, 1, 1.5f, 2, 2.5f}, 0.09f);
zoomSlider.setValue(1);
- zoomSlider.setSnapToValues(new float[] {0.5f, 1, 1.5f, 2, 2.5f, 3, 3.5f}, 0.01f);
+ zoomSlider.setSnapToValues(new float[] {1, 2}, 0.30f);
+
+ xScaleSlider.setValue(1);
+ xScaleSlider.setSnapToValues(new float[] {-1.5f, -1, -0.5f, 0.5f, 1, 1.5f}, 0.12f);
+
+ yScaleSlider.setValue(1);
+ yScaleSlider.setSnapToValues(new float[] {-1.5f, -1, -0.5f, 0.5f, 1, 1.5f}, 0.12f);
mixSlider.setValue(0.3f);
- mixSlider.setSnapToValues(new float[] {1, 1.5f, 2, 2.5f, 3, 3.5f}, 0.1f);
+ mixSlider.setSnapToValues(new float[] {1, 1.5f, 2, 2.5f, 3, 3.5f}, 0.12f);
speedSlider.setValue(1);
- speedSlider.setSnapToValues(new float[] {0.5f, 0.75f, 1, 1.25f, 1.5f, 2, 2.5f}, 0.01f);
+ speedSlider.setSnapToValues(new float[] {0.5f, 0.75f, 1, 1.25f, 1.5f, 2, 2.5f}, 0.09f);
alphaSlider.setValue(1);
alphaSlider.setDisabled(true);
-
+
addCheckbox.setDisabled(true);
window.setMovable(false);
@@ -519,12 +533,12 @@ public class SkeletonViewer extends ApplicationAdapter {
root.defaults().space(6);
root.columnDefaults(0).top().right().padTop(3);
root.columnDefaults(1).left();
- root.add("Scale:");
+ root.add("Load scale:");
{
Table table = table();
- table.add(scaleLabel).width(29);
- table.add(scaleSlider).growX();
- table.add(scaleResetButton);
+ table.add(loadScaleLabel).width(29);
+ table.add(loadScaleSlider).growX();
+ table.add(loadScaleResetButton);
root.add(table).fill().row();
}
root.add("Zoom:");
@@ -535,8 +549,22 @@ public class SkeletonViewer extends ApplicationAdapter {
table.add(zoomResetButton);
root.add(table).fill().row();
}
- root.add("Flip:");
- root.add(table(flipXCheckbox, flipYCheckbox)).row();
+ root.add("Scale X:");
+ {
+ Table table = table();
+ table.add(xScaleLabel).width(29);
+ table.add(xScaleSlider).growX();
+ table.add(xScaleResetButton).row();
+ root.add(table).fill().row();
+ }
+ root.add("Scale Y:");
+ {
+ Table table = table();
+ table.add(yScaleLabel).width(29);
+ table.add(yScaleSlider).growX();
+ table.add(yScaleResetButton);
+ root.add(table).fill().row();
+ }
root.add("Debug:");
root.add(table(debugBonesCheckbox, debugRegionsCheckbox, debugBoundingBoxesCheckbox)).row();
root.add();
@@ -677,25 +705,25 @@ public class SkeletonViewer extends ApplicationAdapter {
}
});
- scaleSlider.addListener(new ChangeListener() {
+ loadScaleSlider.addListener(new ChangeListener() {
public void changed (ChangeEvent event, Actor actor) {
- scaleLabel.setText(Float.toString((int)(scaleSlider.getValue() * 100) / 100f));
- if (!scaleSlider.isDragging()) loadSkeleton(skeletonFile);
+ loadScaleLabel.setText(Integer.toString((int)(loadScaleSlider.getValue() * 100)) + "%");
+ if (!loadScaleSlider.isDragging()) loadSkeleton(skeletonFile);
}
});
- scaleResetButton.addListener(new ChangeListener() {
+ loadScaleResetButton.addListener(new ChangeListener() {
public void changed (ChangeEvent event, Actor actor) {
resetCameraPosition();
- if (scaleSlider.getValue() == 1)
+ if (loadScaleSlider.getValue() == 1)
loadSkeleton(skeletonFile);
else
- scaleSlider.setValue(1);
+ loadScaleSlider.setValue(1);
}
});
zoomSlider.addListener(new ChangeListener() {
public void changed (ChangeEvent event, Actor actor) {
- zoomLabel.setText(Float.toString((int)(zoomSlider.getValue() * 100) / 100f));
+ zoomLabel.setText(Integer.toString((int)(zoomSlider.getValue() * 100)) + "%");
float newZoom = 1 / zoomSlider.getValue();
camera.position.x -= window.getWidth() / 2 * (newZoom - camera.zoom);
camera.zoom = newZoom;
@@ -710,9 +738,33 @@ public class SkeletonViewer extends ApplicationAdapter {
}
});
+ xScaleSlider.addListener(new ChangeListener() {
+ public void changed (ChangeEvent event, Actor actor) {
+ if (xScaleSlider.getValue() == 0) xScaleSlider.setValue(0.01f);
+ xScaleLabel.setText(Integer.toString((int)(xScaleSlider.getValue() * 100)) + "%");
+ }
+ });
+ xScaleResetButton.addListener(new ChangeListener() {
+ public void changed (ChangeEvent event, Actor actor) {
+ xScaleSlider.setValue(1);
+ }
+ });
+
+ yScaleSlider.addListener(new ChangeListener() {
+ public void changed (ChangeEvent event, Actor actor) {
+ if (yScaleSlider.getValue() == 0) yScaleSlider.setValue(0.01f);
+ yScaleLabel.setText(Integer.toString((int)(yScaleSlider.getValue() * 100)) + "%");
+ }
+ });
+ yScaleResetButton.addListener(new ChangeListener() {
+ public void changed (ChangeEvent event, Actor actor) {
+ yScaleSlider.setValue(1);
+ }
+ });
+
speedSlider.addListener(new ChangeListener() {
public void changed (ChangeEvent event, Actor actor) {
- speedLabel.setText(Float.toString((int)(speedSlider.getValue() * 100) / 100f));
+ speedLabel.setText(Float.toString((int)(speedSlider.getValue() * 100) / 100f) + "x");
}
});
speedResetButton.addListener(new ChangeListener() {
@@ -723,7 +775,7 @@ public class SkeletonViewer extends ApplicationAdapter {
alphaSlider.addListener(new ChangeListener() {
public void changed (ChangeEvent event, Actor actor) {
- alphaLabel.setText(Float.toString((int)(alphaSlider.getValue() * 100) / 100f));
+ alphaLabel.setText(Integer.toString((int)(alphaSlider.getValue() * 100)) + "%");
int track = trackButtons.getCheckedIndex();
if (track > 0) {
TrackEntry current = state.getCurrent(track);
@@ -737,7 +789,7 @@ public class SkeletonViewer extends ApplicationAdapter {
mixSlider.addListener(new ChangeListener() {
public void changed (ChangeEvent event, Actor actor) {
- mixLabel.setText(Float.toString((int)(mixSlider.getValue() * 100) / 100f));
+ mixLabel.setText(Float.toString((int)(mixSlider.getValue() * 100) / 100f) + "s");
if (state != null) state.getData().setDefaultMix(mixSlider.getValue());
}
});
@@ -878,8 +930,8 @@ public class SkeletonViewer extends ApplicationAdapter {
speedSlider.addListener(savePrefsListener);
speedResetButton.addListener(savePrefsListener);
mixSlider.addListener(savePrefsListener);
- scaleSlider.addListener(savePrefsListener);
- scaleResetButton.addListener(savePrefsListener);
+ loadScaleSlider.addListener(savePrefsListener);
+ loadScaleResetButton.addListener(savePrefsListener);
zoomSlider.addListener(savePrefsListener);
zoomResetButton.addListener(savePrefsListener);
animationList.addListener(savePrefsListener);
@@ -942,7 +994,7 @@ public class SkeletonViewer extends ApplicationAdapter {
prefs.putBoolean("add", addCheckbox.isChecked());
prefs.putFloat("speed", speedSlider.getValue());
prefs.putFloat("mix", mixSlider.getValue());
- prefs.putFloat("scale", scaleSlider.getValue());
+ prefs.putFloat("scale", loadScaleSlider.getValue());
prefs.putFloat("zoom", zoomSlider.getValue());
prefs.putFloat("x", camera.position.x);
prefs.putFloat("y", camera.position.y);
@@ -978,7 +1030,7 @@ public class SkeletonViewer extends ApplicationAdapter {
camera.position.x = prefs.getFloat("x", 0);
camera.position.y = prefs.getFloat("y", 0);
- scaleSlider.setValue(prefs.getFloat("scale", 1));
+ loadScaleSlider.setValue(prefs.getFloat("scale", 1));
animationList.setSelected(prefs.getString("animationName", null));
skinList.setSelected(prefs.getString("skinName", null));
} catch (Exception ex) {