diff --git a/spine-cpp/spine-cpp/src/spine/AtlasAttachmentLoader.cpp b/spine-cpp/spine-cpp/src/spine/AtlasAttachmentLoader.cpp index 97804630e..ad6c3c519 100644 --- a/spine-cpp/spine-cpp/src/spine/AtlasAttachmentLoader.cpp +++ b/spine-cpp/spine-cpp/src/spine/AtlasAttachmentLoader.cpp @@ -53,7 +53,7 @@ RegionAttachment *AtlasAttachmentLoader::newRegionAttachment(Skin &skin, const S SP_UNUSED(skin); AtlasRegion *regionP = findRegion(path); - assert(regionP != NULL); + if (!regionP) return NULL; AtlasRegion ®ion = *regionP; @@ -75,7 +75,7 @@ MeshAttachment *AtlasAttachmentLoader::newMeshAttachment(Skin &skin, const Strin SP_UNUSED(skin); AtlasRegion *regionP = findRegion(path); - assert(regionP != NULL); + if (!regionP) return NULL; AtlasRegion ®ion = *regionP; diff --git a/spine-cpp/spine-cpp/src/spine/SkeletonJson.cpp b/spine-cpp/spine-cpp/src/spine/SkeletonJson.cpp index febbe1c13..6ef1d5be9 100644 --- a/spine-cpp/spine-cpp/src/spine/SkeletonJson.cpp +++ b/spine-cpp/spine-cpp/src/spine/SkeletonJson.cpp @@ -506,6 +506,12 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) { case AttachmentType_Linkedmesh: { attachment = _attachmentLoader->newMeshAttachment(*skin, attachmentName, attachmentPath); + if (!attachment) { + delete skeletonData; + setError(root, "Error reading attachment: ", skinAttachmentName); + return NULL; + } + MeshAttachment *mesh = static_cast(attachment); mesh->_path = attachmentPath; 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) { diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpinePlugin.cpp b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpinePlugin.cpp index 993fc2757..8519c609d 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpinePlugin.cpp +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpinePlugin.cpp @@ -31,6 +31,8 @@ #include "SpinePluginPrivatePCH.h" #include "spine/Extension.h" +DEFINE_LOG_CATEGORY(SpineLog); + class FSpinePlugin : public SpinePlugin { virtual void StartupModule() override; virtual void ShutdownModule() override; diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonAnimationComponent.cpp b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonAnimationComponent.cpp index f6d44311e..48e89141f 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonAnimationComponent.cpp +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonAnimationComponent.cpp @@ -109,12 +109,14 @@ void USpineSkeletonAnimationComponent::CheckState () { if (Atlas && SkeletonData) { spine::SkeletonData *data = SkeletonData->GetSkeletonData(Atlas->GetAtlas(false), false); - skeleton = new (__FILE__, __LINE__) Skeleton(data); - AnimationStateData* stateData = SkeletonData->GetAnimationStateData(Atlas->GetAtlas(false)); - state = new (__FILE__, __LINE__) AnimationState(stateData); - state->setRendererObject((void*)this); - state->setListener(callback); - trackEntries.Empty(); + if (data) { + skeleton = new (__FILE__, __LINE__) Skeleton(data); + AnimationStateData* stateData = SkeletonData->GetAnimationStateData(Atlas->GetAtlas(false)); + state = new (__FILE__, __LINE__) AnimationState(stateData); + state->setRendererObject((void*)this); + state->setListener(callback); + trackEntries.Empty(); + } } lastAtlas = Atlas; diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonDataAsset.cpp b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonDataAsset.cpp index 5a547359d..51178d256 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonDataAsset.cpp +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonDataAsset.cpp @@ -33,6 +33,7 @@ #include #include #include +#include "Runtime/Core/Public/Misc/MessageDialog.h" #define LOCTEXT_NAMESPACE "Spine" @@ -105,10 +106,22 @@ SkeletonData* USpineSkeletonDataAsset::GetSkeletonData (Atlas* Atlas, bool Force if (skeletonDataFileName.GetPlainNameString().Contains(TEXT(".json"))) { SkeletonJson* json = new (__FILE__, __LINE__) SkeletonJson(Atlas); this->skeletonData = json->readSkeletonData((const char*)rawData.GetData()); + if (!skeletonData) { +#if WITH_EDITORONLY_DATA + FMessageDialog::Debugf(FText::FromString(UTF8_TO_TCHAR(json->getError().buffer()))); +#endif + UE_LOG(SpineLog, Error, TEXT("Couldn't load skeleton data and atlas: %s"), UTF8_TO_TCHAR(json->getError().buffer())); + } delete json; } else { SkeletonBinary* binary = new (__FILE__, __LINE__) SkeletonBinary(Atlas); this->skeletonData = binary->readSkeletonData((const unsigned char*)rawData.GetData(), (int)rawData.Num()); + if (!skeletonData) { +#if WITH_EDITORONLY_DATA + FMessageDialog::Debugf(FText::FromString(UTF8_TO_TCHAR(binary->getError().buffer()))); +#endif + UE_LOG(SpineLog, Error, TEXT("Couldn't load skeleton data and atlas: %s"), UTF8_TO_TCHAR(binary->getError().buffer())); + } delete binary; } if (animationStateData) { diff --git a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpinePlugin.h b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpinePlugin.h index 6011f932d..0d16143f7 100644 --- a/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpinePlugin.h +++ b/spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpinePlugin.h @@ -32,6 +32,8 @@ #include "ModuleManager.h" +DECLARE_LOG_CATEGORY_EXTERN(SpineLog, Log, All); + class SPINEPLUGIN_API SpinePlugin : public IModuleInterface { public: diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Modules/AttachmentTools/AttachmentTools.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Modules/AttachmentTools/AttachmentTools.cs index 7d0b2d0e1..601f06685 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Modules/AttachmentTools/AttachmentTools.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Modules/AttachmentTools/AttachmentTools.cs @@ -211,6 +211,8 @@ namespace Spine.Unity.Modules.AttachmentTools { internal const bool UseMipMaps = false; internal const float DefaultScale = 0.01f; + const int NonrenderingRegion = -1; + public static AtlasRegion ToAtlasRegion (this Texture2D t, Material materialPropertySource, float scale = DefaultScale) { return t.ToAtlasRegion(materialPropertySource.shader, scale, materialPropertySource); } @@ -395,7 +397,7 @@ namespace Spine.Unity.Modules.AttachmentTools { /// The List(Attachment) to populate with the newly created Attachment objects. /// /// May be null. If no Material property source is provided, no special - public static void GetRepackedAttachments (List sourceAttachments, List outputAttachments, Material materialPropertySource, out Material outputMaterial, out Texture2D outputTexture, int maxAtlasSize = 1024, int padding = 2, TextureFormat textureFormat = SpineTextureFormat, bool mipmaps = UseMipMaps, string newAssetName = "Repacked Attachments", bool clearCache = false) { + public static void GetRepackedAttachments (List sourceAttachments, List outputAttachments, Material materialPropertySource, out Material outputMaterial, out Texture2D outputTexture, int maxAtlasSize = 1024, int padding = 2, TextureFormat textureFormat = SpineTextureFormat, bool mipmaps = UseMipMaps, string newAssetName = "Repacked Attachments", bool clearCache = false, bool useOriginalNonrenderables = true) { if (sourceAttachments == null) throw new System.ArgumentNullException("sourceAttachments"); if (outputAttachments == null) throw new System.ArgumentNullException("outputAttachments"); @@ -411,9 +413,9 @@ namespace Spine.Unity.Modules.AttachmentTools { int newRegionIndex = 0; for (int i = 0, n = sourceAttachments.Count; i < n; i++) { var originalAttachment = sourceAttachments[i]; - var newAttachment = originalAttachment.GetClone(true); - if (IsRenderable(newAttachment)) { - + + if (IsRenderable(originalAttachment)) { + var newAttachment = originalAttachment.GetClone(true); var region = newAttachment.GetRegion(); int existingIndex; if (existingRegions.TryGetValue(region, out existingIndex)) { @@ -427,6 +429,9 @@ namespace Spine.Unity.Modules.AttachmentTools { } outputAttachments[i] = newAttachment; + } else { + outputAttachments[i] = useOriginalNonrenderables ? originalAttachment : originalAttachment.GetClone(true); + regionIndexes.Add(NonrenderingRegion); // Output attachments pairs with regionIndexes list 1:1. Pad with a sentinel if the attachment doesn't have a region. } } @@ -460,7 +465,8 @@ namespace Spine.Unity.Modules.AttachmentTools { // Map the cloned attachments to the repacked atlas. for (int i = 0, n = outputAttachments.Count; i < n; i++) { var a = outputAttachments[i]; - a.SetRegion(repackedRegions[regionIndexes[i]]); + if (IsRenderable(a)) + a.SetRegion(repackedRegions[regionIndexes[i]]); } // Clean up. @@ -474,14 +480,14 @@ namespace Spine.Unity.Modules.AttachmentTools { ///

/// Creates and populates a duplicate skin with cloned attachments that are backed by a new packed texture atlas comprised of all the regions from the original skin. /// No Spine.Atlas object is created so there is no way to find AtlasRegions except through the Attachments using them. - public static Skin GetRepackedSkin (this Skin o, string newName, Material materialPropertySource, out Material outputMaterial, out Texture2D outputTexture, int maxAtlasSize = 1024, int padding = 2, TextureFormat textureFormat = SpineTextureFormat, bool mipmaps = UseMipMaps) { - return GetRepackedSkin(o, newName, materialPropertySource.shader, out outputMaterial, out outputTexture, maxAtlasSize, padding, textureFormat, mipmaps, materialPropertySource); + public static Skin GetRepackedSkin (this Skin o, string newName, Material materialPropertySource, out Material outputMaterial, out Texture2D outputTexture, int maxAtlasSize = 1024, int padding = 2, TextureFormat textureFormat = SpineTextureFormat, bool mipmaps = UseMipMaps, bool useOriginalNonrenderables = true) { + return GetRepackedSkin(o, newName, materialPropertySource.shader, out outputMaterial, out outputTexture, maxAtlasSize, padding, textureFormat, mipmaps, materialPropertySource, useOriginalNonrenderables); } /// /// Creates and populates a duplicate skin with cloned attachments that are backed by a new packed texture atlas comprised of all the regions from the original skin. /// No Spine.Atlas object is created so there is no way to find AtlasRegions except through the Attachments using them. - public static Skin GetRepackedSkin (this Skin o, string newName, Shader shader, out Material outputMaterial, out Texture2D outputTexture, int maxAtlasSize = 1024, int padding = 2, TextureFormat textureFormat = SpineTextureFormat, bool mipmaps = UseMipMaps, Material materialPropertySource = null, bool clearCache = false) { + public static Skin GetRepackedSkin (this Skin o, string newName, Shader shader, out Material outputMaterial, out Texture2D outputTexture, int maxAtlasSize = 1024, int padding = 2, TextureFormat textureFormat = SpineTextureFormat, bool mipmaps = UseMipMaps, Material materialPropertySource = null, bool clearCache = false, bool useOriginalNonrenderables = true) { if (o == null) throw new System.NullReferenceException("Skin was null"); var skinAttachments = o.Attachments; var newSkin = new Skin(newName); @@ -495,10 +501,13 @@ namespace Spine.Unity.Modules.AttachmentTools { var texturesToPack = new List(); var originalRegions = new List(); int newRegionIndex = 0; - foreach (var kvp in skinAttachments) { - var newAttachment = kvp.Value.GetClone(true); - if (IsRenderable(newAttachment)) { + foreach (var skinEntry in skinAttachments) { + var originalKey = skinEntry.Key; + var originalAttachment = skinEntry.Value; + Attachment newAttachment; + if (IsRenderable(originalAttachment)) { + newAttachment = originalAttachment.GetClone(true); var region = newAttachment.GetRegion(); int existingIndex; if (existingRegions.TryGetValue(region, out existingIndex)) { @@ -512,9 +521,10 @@ namespace Spine.Unity.Modules.AttachmentTools { } repackedAttachments.Add(newAttachment); - } - var key = kvp.Key; - newSkin.AddAttachment(key.slotIndex, key.name, newAttachment); + newSkin.AddAttachment(originalKey.slotIndex, originalKey.name, newAttachment); + } else { + newSkin.AddAttachment(originalKey.slotIndex, originalKey.name, useOriginalNonrenderables ? originalAttachment : originalAttachment.GetClone(true)); + } } // Fill a new texture with the collected attachment textures. @@ -546,7 +556,8 @@ namespace Spine.Unity.Modules.AttachmentTools { // Map the cloned attachments to the repacked atlas. for (int i = 0, n = repackedAttachments.Count; i < n; i++) { var a = repackedAttachments[i]; - a.SetRegion(repackedRegions[regionIndexes[i]]); + if (IsRenderable(a)) + a.SetRegion(repackedRegions[regionIndexes[i]]); } // Clean up.