From 2f846cfba5b9b5dfffc682bde0c5c73e1fd66d4a Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Fri, 17 Jun 2016 12:24:38 +0200 Subject: [PATCH] Added JsonRollback, a tool for converting newer JSON so it can be loaded by an older Spine version. --- .../esotericsoftware/spine/JsonRollback.java | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/JsonRollback.java diff --git a/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/JsonRollback.java b/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/JsonRollback.java new file mode 100644 index 000000000..a014bb9a9 --- /dev/null +++ b/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/JsonRollback.java @@ -0,0 +1,104 @@ + +package com.esotericsoftware.spine; + +import com.badlogic.gdx.files.FileHandle; +import com.badlogic.gdx.utils.Array; +import com.badlogic.gdx.utils.Json; +import com.badlogic.gdx.utils.JsonValue; +import com.badlogic.gdx.utils.JsonWriter.OutputType; + +/** Takes Spine JSON data and transforms it to work with an older version of Spine. It supports going from version 3.3.xx to + * 2.1.27. + *

+ * Data can be exported from a Spine project, processed with JsonRollback, then imported into an older version of Spine. However, + * JsonRollback may remove data for features not supported by the older Spine version. Because of this, JsonRollback is only + * intended for situations were work was accidentally done with a newer Spine version and now needs to be imported into an older + * Spine version (eg, if runtime support for the new version is not yet available). + *

+ * Animators should freeze their Spine editor version to match the Spine version supported by the runtime being used. Only when + * the runtime is updated to support a newer Spine version should animators update their Spine editor version to match. */ +public class JsonRollback { + static public void main (String[] args) throws Exception { + if (args.length == 0) { + System.out.println("Usage: [outputJSON]"); + System.exit(0); + } + + JsonValue root = new Json().fromJson(null, new FileHandle(args[0])); + + // In 3.2 skinnedmesh was renamed to weightedmesh. + setValue(root, "skins/*/*/*/type/weightedmesh", "skinnedmesh"); + + // In 3.2 shear was added. + delete(root, "animations/*/bones/*/shear"); + + // In 3.3 ffd was renamed to deform. + rename(root, "animations/*/deform", "ffd"); + + // In 3.3 mesh are now a single type, previously they were skinnedmesh if they had weights. + for (JsonValue value : find(root, "skins/*/*/*/type/mesh".split("/"), 0, new Array())) + if (value.parent.get("uvs").size != value.parent.get("vertices").size) value.set("skinnedmesh"); + + // In 3.3 bounding boxes can be weighted. + for (JsonValue value : find(root, "skins/*/*/*/type/boundingbox".split("/"), 0, new Array())) + if (value.parent.getInt("vertexCount") * 2 != value.parent.get("vertices").size) + value.parent.parent.remove(value.parent.name); + + // In 3.3 paths were added. + for (JsonValue value : find(root, "skins/*/*/*/type/path".split("/"), 0, new Array())) { + String attachment = value.parent.name; + value.parent.parent.remove(attachment); + String slot = value.parent.parent.name; + // Remove path deform timelines. + delete(root, "animations/*/ffd/*/" + slot + "/" + attachment); + } + + // In 3.3 IK constraints no longer require bendPositive. + for (JsonValue value : find(root, "animations/*/ik/*".split("/"), 0, new Array())) + for (JsonValue child = value.child; child != null; child = child.next) + if (!child.has("bendPositive")) child.addChild("bendPositive", new JsonValue(true)); + + // In 3.3 transform constraints can have more than 1 bone. + for (JsonValue child = root.getChild("transform"); child != null; child = child.next) { + JsonValue bones = child.remove("bones"); + if (bones != null) child.addChild("bone", new JsonValue(bones.child.asString())); + } + + if (args.length > 1) + new FileHandle(args[1]).writeString(root.prettyPrint(OutputType.json, 130), false, "UTF-8"); + else + System.out.println(root.prettyPrint(OutputType.json, 130)); + } + + static void setValue (JsonValue root, String path, String newValue) { + for (JsonValue value : find(root, path.split("/"), 0, new Array())) + value.set(newValue); + } + + static void rename (JsonValue root, String path, String newName) { + for (JsonValue value : find(root, path.split("/"), 0, new Array())) + value.name = newName; + } + + static void delete (JsonValue root, String path) { + for (JsonValue value : find(root, path.split("/"), 0, new Array())) + value.parent.remove(value.name); + } + + static Array find (JsonValue current, String[] path, int index, Array values) { + String name = path[index]; + if (current.name == null) { + if (name.equals("*") && index == path.length - 1) + values.add(current); + else if (current.has(name)) return find(current.get(name), path, index, values); + } else if (name.equals("*") || current.name.equals(name)) { + if (++index == path.length || (index == path.length - 1 && current.isString() && current.asString().equals(path[index]))) + values.add(current); + else { + for (JsonValue child = current.child; child != null; child = child.next) + find(child, path, index, values); + } + } + return values; + } +}