diff --git a/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/JsonRollback.java b/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/JsonRollback.java index e597ef48b..ae145ebd0 100644 --- a/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/JsonRollback.java +++ b/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/JsonRollback.java @@ -33,10 +33,12 @@ 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.JsonValue.ValueType; 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. +/** Takes Spine JSON data and transforms it to work with an older version of Spine. Target versions:
+ * 2.1: supports going from version 3.3.xx to 2.1.27.
+ * 3.7: supports going from version 3.8.xx to 3.7.94. *

* 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 @@ -47,67 +49,109 @@ import com.badlogic.gdx.utils.JsonWriter.OutputType; * 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: [outputFile]"); + if (args.length != 2 && args.length != 3) { + System.out.println("Usage: [outputFile]"); + System.exit(0); + } + + String version = args[1]; + if (!version.equals("2.1") && !version.equals("3.7")) { + System.out.println("ERROR: Target version must be: 2.1 or 3.7"); + System.out.println("Usage: [outputFile]"); System.exit(0); } JsonValue root = new Json().fromJson(null, new FileHandle(args[0])); - // In 3.2 skinnedmesh was renamed to weightedmesh. - setValue(root, "skinnedmesh", "skins", "*", "*", "*", "type", "weightedmesh"); + if (version.equals("2.1")) { + // In 3.2 skinnedmesh was renamed to weightedmesh. + setValue(root, "skinnedmesh", "skins", "*", "*", "*", "type", "weightedmesh"); - // In 3.2 shear was added. - delete(root, "animations", "*", "bones", "*", "shear"); + // In 3.2 shear was added. + delete(root, "animations", "*", "bones", "*", "shear"); - // In 3.3 ffd was renamed to deform. - rename(root, "ffd", "animations", "*", "deform"); + // In 3.3 ffd was renamed to deform. + rename(root, "ffd", "animations", "*", "deform"); - // In 3.3 mesh is now a single type, previously they were skinnedmesh if they had weights. - for (JsonValue value : find(root, new Array(), 0, "skins", "*", "*", "*", "type", "mesh")) - if (value.parent.get("uvs").size != value.parent.get("vertices").size) value.set("skinnedmesh"); + // In 3.3 mesh is now a single type, previously they were skinnedmesh if they had weights. + for (JsonValue value : find(root, new Array(), 0, "skins", "*", "*", "*", "type", "mesh")) + if (value.parent.get("uvs").size != value.parent.get("vertices").size) value.set("skinnedmesh"); - // In 3.3 linkedmesh is now a single type, previously they were linkedweightedmesh if they had weights. - for (JsonValue value : find(root, new Array(), 0, "skins", "*", "*", "*", "type", "linkedmesh")) { - String slot = value.parent.parent.name.replaceAll("", ""); - String skinName = value.parent.getString("skin", "default"); - String parentName = value.parent.getString("parent"); - if (find(root, new Array(), 0, - ("skins~~" + skinName + "~~" + slot + "~~" + parentName + "~~type~~skinnedmesh").split("~~")).size > 0) - value.set("weightedlinkedmesh"); + // In 3.3 linkedmesh is now a single type, previously they were linkedweightedmesh if they had weights. + for (JsonValue value : find(root, new Array(), 0, "skins", "*", "*", "*", "type", "linkedmesh")) { + String slot = value.parent.parent.name.replaceAll("", ""); + String skinName = value.parent.getString("skin", "default"); + String parentName = value.parent.getString("parent"); + if (find(root, new Array(), 0, + ("skins~~" + skinName + "~~" + slot + "~~" + parentName + "~~type~~skinnedmesh").split("~~")).size > 0) + value.set("weightedlinkedmesh"); + } + + // In 3.3 bounding boxes can be weighted. + for (JsonValue value : find(root, new Array(), 0, "skins", "*", "*", "*", "type", "boundingbox")) + 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, new Array(), 0, "skins", "*", "*", "*", "type", "path")) { + String attachment = value.parent.name; + value.parent.parent.remove(attachment); + String slot = value.parent.parent.name; + // Also remove path deform timelines. + delete(root, "animations", "*", "ffd", "*", slot, attachment); + } + + // In 3.3 IK constraint timelines no longer require bendPositive. + for (JsonValue value : find(root, new Array(), 0, "animations", "*", "ik", "*")) + 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())); + } + } else if (version.equals("3.7")) { + JsonValue skins = root.get("skins"); + if (skins != null && skins.isArray()) { + JsonValue newSkins = new JsonValue(ValueType.object); + for (JsonValue skinMap = skins.child; skinMap != null; skinMap = skinMap.next) { + JsonValue newSkin = skinMap.get("attachments"); + newSkins.addChild(skinMap.getString("name"), newSkin); + } + root.remove("skins"); + root.addChild("skins", newSkins); + } + + rollbackCurves(root.get("animations")); } - // In 3.3 bounding boxes can be weighted. - for (JsonValue value : find(root, new Array(), 0, "skins", "*", "*", "*", "type", "boundingbox")) - 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, new Array(), 0, "skins", "*", "*", "*", "type", "path")) { - String attachment = value.parent.name; - value.parent.parent.remove(attachment); - String slot = value.parent.parent.name; - // Also remove path deform timelines. - delete(root, "animations", "*", "ffd", "*", slot, attachment); - } - - // In 3.3 IK constraint timelines no longer require bendPositive. - for (JsonValue value : find(root, new Array(), 0, "animations", "*", "ik", "*")) - 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"); + if (args.length == 3) + new FileHandle(args[2]).writeString(root.prettyPrint(OutputType.json, 130), false, "UTF-8"); else System.out.println(root.prettyPrint(OutputType.json, 130)); } + static private void rollbackCurves (JsonValue map) { + if (map == null) return; + JsonValue curve = map.get("curve"); + if (curve == null) { + for (map = map.child; map != null; map = map.next) + rollbackCurves(map); + return; + } + if (curve.isNumber()) { + curve.addChild(new JsonValue(curve.asFloat())); + curve.setType(ValueType.array); + curve.addChild(new JsonValue(map.getFloat("c2", 0))); + curve.addChild(new JsonValue(map.getFloat("c3", 0))); + curve.addChild(new JsonValue(map.getFloat("c4", 0))); + map.remove("c2"); + map.remove("c3"); + map.remove("c4"); + } + } + static void setValue (JsonValue root, String newValue, String... path) { for (JsonValue value : find(root, new Array(), 0, path)) value.set(newValue);