Adjusted line endings.

This commit is contained in:
NathanSweet 2014-02-05 18:09:06 +01:00
parent 37ee75a424
commit 66a336e219
3 changed files with 687 additions and 687 deletions

View File

@ -41,8 +41,8 @@ namespace Spine {
public float TimeScale { get { return timeScale; } set { timeScale = value; } } public float TimeScale { get { return timeScale; } set { timeScale = value; } }
public delegate void StartEndDelegate(AnimationState state, int trackIndex); public delegate void StartEndDelegate(AnimationState state, int trackIndex);
public event StartEndDelegate Start; public event StartEndDelegate Start;
public event StartEndDelegate End; public event StartEndDelegate End;
public delegate void EventDelegate(AnimationState state, int trackIndex, Event e); public delegate void EventDelegate(AnimationState state, int trackIndex, Event e);
public event EventDelegate Event; public event EventDelegate Event;
@ -262,8 +262,8 @@ namespace Spine {
public float TimeScale { get { return timeScale; } set { timeScale = value; } } public float TimeScale { get { return timeScale; } set { timeScale = value; } }
public bool Loop { get { return loop; } set { loop = value; } } public bool Loop { get { return loop; } set { loop = value; } }
public event AnimationState.StartEndDelegate Start; public event AnimationState.StartEndDelegate Start;
public event AnimationState.StartEndDelegate End; public event AnimationState.StartEndDelegate End;
public event AnimationState.EventDelegate Event; public event AnimationState.EventDelegate Event;
public event AnimationState.CompleteDelegate Complete; public event AnimationState.CompleteDelegate Complete;

View File

@ -1,276 +1,276 @@
/****************************************************************************** /******************************************************************************
* Spine Runtimes Software License * Spine Runtimes Software License
* Version 2 * Version 2
* *
* Copyright (c) 2013, Esoteric Software * Copyright (c) 2013, Esoteric Software
* All rights reserved. * All rights reserved.
* *
* You are granted a perpetual, non-exclusive, non-sublicensable and * You are granted a perpetual, non-exclusive, non-sublicensable and
* non-transferable license to install, execute and perform the Spine Runtimes * non-transferable license to install, execute and perform the Spine Runtimes
* Software (the "Software") solely for internal use. Without the written * Software (the "Software") solely for internal use. Without the written
* permission of Esoteric Software, you may not (a) modify, translate, adapt or * permission of Esoteric Software, you may not (a) modify, translate, adapt or
* otherwise create derivative works, improvements of the Software or develop * otherwise create derivative works, improvements of the Software or develop
* new applications using the Software or (b) remove, delete, alter or obscure * new applications using the Software or (b) remove, delete, alter or obscure
* any trademarks or any copyright, trademark, patent or other intellectual * any trademarks or any copyright, trademark, patent or other intellectual
* property or proprietary rights notices on or in the Software, including * property or proprietary rights notices on or in the Software, including
* any copy thereof. Redistributions in binary or source form must include * any copy thereof. Redistributions in binary or source form must include
* this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * 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 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/ *****************************************************************************/
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
#if WINDOWS_STOREAPP #if WINDOWS_STOREAPP
using System.Threading.Tasks; using System.Threading.Tasks;
using Windows.Storage; using Windows.Storage;
#endif #endif
namespace Spine { namespace Spine {
public class Atlas { public class Atlas {
List<AtlasPage> pages = new List<AtlasPage>(); List<AtlasPage> pages = new List<AtlasPage>();
List<AtlasRegion> regions = new List<AtlasRegion>(); List<AtlasRegion> regions = new List<AtlasRegion>();
TextureLoader textureLoader; TextureLoader textureLoader;
#if WINDOWS_STOREAPP #if WINDOWS_STOREAPP
private async Task ReadFile(string path, TextureLoader textureLoader) { private async Task ReadFile(string path, TextureLoader textureLoader) {
var folder = Windows.ApplicationModel.Package.Current.InstalledLocation; var folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
var file = await folder.GetFileAsync(path).AsTask().ConfigureAwait(false); var file = await folder.GetFileAsync(path).AsTask().ConfigureAwait(false);
using (var reader = new StreamReader(await file.OpenStreamForReadAsync().ConfigureAwait(false))) { using (var reader = new StreamReader(await file.OpenStreamForReadAsync().ConfigureAwait(false))) {
try { try {
Load(reader, Path.GetDirectoryName(path), textureLoader); Load(reader, Path.GetDirectoryName(path), textureLoader);
} catch (Exception ex) { } catch (Exception ex) {
throw new Exception("Error reading atlas file: " + path, ex); throw new Exception("Error reading atlas file: " + path, ex);
} }
} }
} }
public Atlas(String path, TextureLoader textureLoader) { public Atlas(String path, TextureLoader textureLoader) {
this.ReadFile(path, textureLoader).Wait(); this.ReadFile(path, textureLoader).Wait();
} }
#else #else
public Atlas (String path, TextureLoader textureLoader) { public Atlas (String path, TextureLoader textureLoader) {
#if WINDOWS_PHONE #if WINDOWS_PHONE
Stream stream = Microsoft.Xna.Framework.TitleContainer.OpenStream(path); Stream stream = Microsoft.Xna.Framework.TitleContainer.OpenStream(path);
using (StreamReader reader = new StreamReader(stream)) using (StreamReader reader = new StreamReader(stream))
{ {
#else #else
using (StreamReader reader = new StreamReader(path)) { using (StreamReader reader = new StreamReader(path)) {
#endif #endif
try { try {
Load(reader, Path.GetDirectoryName(path), textureLoader); Load(reader, Path.GetDirectoryName(path), textureLoader);
} catch (Exception ex) { } catch (Exception ex) {
throw new Exception("Error reading atlas file: " + path, ex); throw new Exception("Error reading atlas file: " + path, ex);
} }
} }
} }
#endif #endif
public Atlas (TextReader reader, String dir, TextureLoader textureLoader) { public Atlas (TextReader reader, String dir, TextureLoader textureLoader) {
Load(reader, dir, textureLoader); Load(reader, dir, textureLoader);
} }
public Atlas (List<AtlasPage> pages, List<AtlasRegion> regions) { public Atlas (List<AtlasPage> pages, List<AtlasRegion> regions) {
this.pages = pages; this.pages = pages;
this.regions = regions; this.regions = regions;
this.textureLoader = null; this.textureLoader = null;
} }
private void Load (TextReader reader, String imagesDir, TextureLoader textureLoader) { private void Load (TextReader reader, String imagesDir, TextureLoader textureLoader) {
if (textureLoader == null) throw new ArgumentNullException("textureLoader cannot be null."); if (textureLoader == null) throw new ArgumentNullException("textureLoader cannot be null.");
this.textureLoader = textureLoader; this.textureLoader = textureLoader;
String[] tuple = new String[4]; String[] tuple = new String[4];
AtlasPage page = null; AtlasPage page = null;
while (true) { while (true) {
String line = reader.ReadLine(); String line = reader.ReadLine();
if (line == null) break; if (line == null) break;
if (line.Trim().Length == 0) if (line.Trim().Length == 0)
page = null; page = null;
else if (page == null) { else if (page == null) {
page = new AtlasPage(); page = new AtlasPage();
page.name = line; page.name = line;
page.format = (Format)Enum.Parse(typeof(Format), readValue(reader), false); page.format = (Format)Enum.Parse(typeof(Format), readValue(reader), false);
readTuple(reader, tuple); readTuple(reader, tuple);
page.minFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), tuple[0], false); page.minFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), tuple[0], false);
page.magFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), tuple[1], false); page.magFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), tuple[1], false);
String direction = readValue(reader); String direction = readValue(reader);
page.uWrap = TextureWrap.ClampToEdge; page.uWrap = TextureWrap.ClampToEdge;
page.vWrap = TextureWrap.ClampToEdge; page.vWrap = TextureWrap.ClampToEdge;
if (direction == "x") if (direction == "x")
page.uWrap = TextureWrap.Repeat; page.uWrap = TextureWrap.Repeat;
else if (direction == "y") else if (direction == "y")
page.vWrap = TextureWrap.Repeat; page.vWrap = TextureWrap.Repeat;
else if (direction == "xy") else if (direction == "xy")
page.uWrap = page.vWrap = TextureWrap.Repeat; page.uWrap = page.vWrap = TextureWrap.Repeat;
textureLoader.Load(page, Path.Combine(imagesDir, line)); textureLoader.Load(page, Path.Combine(imagesDir, line));
pages.Add(page); pages.Add(page);
} else { } else {
AtlasRegion region = new AtlasRegion(); AtlasRegion region = new AtlasRegion();
region.name = line; region.name = line;
region.page = page; region.page = page;
region.rotate = Boolean.Parse(readValue(reader)); region.rotate = Boolean.Parse(readValue(reader));
readTuple(reader, tuple); readTuple(reader, tuple);
int x = int.Parse(tuple[0]); int x = int.Parse(tuple[0]);
int y = int.Parse(tuple[1]); int y = int.Parse(tuple[1]);
readTuple(reader, tuple); readTuple(reader, tuple);
int width = int.Parse(tuple[0]); int width = int.Parse(tuple[0]);
int height = int.Parse(tuple[1]); int height = int.Parse(tuple[1]);
region.u = x / (float)page.width; region.u = x / (float)page.width;
region.v = y / (float)page.height; region.v = y / (float)page.height;
if (region.rotate) { if (region.rotate) {
region.u2 = (x + height) / (float)page.width; region.u2 = (x + height) / (float)page.width;
region.v2 = (y + width) / (float)page.height; region.v2 = (y + width) / (float)page.height;
} else { } else {
region.u2 = (x + width) / (float)page.width; region.u2 = (x + width) / (float)page.width;
region.v2 = (y + height) / (float)page.height; region.v2 = (y + height) / (float)page.height;
} }
region.x = x; region.x = x;
region.y = y; region.y = y;
region.width = Math.Abs(width); region.width = Math.Abs(width);
region.height = Math.Abs(height); region.height = Math.Abs(height);
if (readTuple(reader, tuple) == 4) { // split is optional if (readTuple(reader, tuple) == 4) { // split is optional
region.splits = new int[] {int.Parse(tuple[0]), int.Parse(tuple[1]), region.splits = new int[] {int.Parse(tuple[0]), int.Parse(tuple[1]),
int.Parse(tuple[2]), int.Parse(tuple[3])}; int.Parse(tuple[2]), int.Parse(tuple[3])};
if (readTuple(reader, tuple) == 4) { // pad is optional, but only present with splits if (readTuple(reader, tuple) == 4) { // pad is optional, but only present with splits
region.pads = new int[] {int.Parse(tuple[0]), int.Parse(tuple[1]), region.pads = new int[] {int.Parse(tuple[0]), int.Parse(tuple[1]),
int.Parse(tuple[2]), int.Parse(tuple[3])}; int.Parse(tuple[2]), int.Parse(tuple[3])};
readTuple(reader, tuple); readTuple(reader, tuple);
} }
} }
region.originalWidth = int.Parse(tuple[0]); region.originalWidth = int.Parse(tuple[0]);
region.originalHeight = int.Parse(tuple[1]); region.originalHeight = int.Parse(tuple[1]);
readTuple(reader, tuple); readTuple(reader, tuple);
region.offsetX = int.Parse(tuple[0]); region.offsetX = int.Parse(tuple[0]);
region.offsetY = int.Parse(tuple[1]); region.offsetY = int.Parse(tuple[1]);
region.index = int.Parse(readValue(reader)); region.index = int.Parse(readValue(reader));
regions.Add(region); regions.Add(region);
} }
} }
} }
static String readValue (TextReader reader) { static String readValue (TextReader reader) {
String line = reader.ReadLine(); String line = reader.ReadLine();
int colon = line.IndexOf(':'); int colon = line.IndexOf(':');
if (colon == -1) throw new Exception("Invalid line: " + line); if (colon == -1) throw new Exception("Invalid line: " + line);
return line.Substring(colon + 1).Trim(); return line.Substring(colon + 1).Trim();
} }
/// <summary>Returns the number of tuple values read (2 or 4).</summary> /// <summary>Returns the number of tuple values read (2 or 4).</summary>
static int readTuple (TextReader reader, String[] tuple) { static int readTuple (TextReader reader, String[] tuple) {
String line = reader.ReadLine(); String line = reader.ReadLine();
int colon = line.IndexOf(':'); int colon = line.IndexOf(':');
if (colon == -1) throw new Exception("Invalid line: " + line); if (colon == -1) throw new Exception("Invalid line: " + line);
int i = 0, lastMatch = colon + 1; int i = 0, lastMatch = colon + 1;
for (; i < 3; i++) { for (; i < 3; i++) {
int comma = line.IndexOf(',', lastMatch); int comma = line.IndexOf(',', lastMatch);
if (comma == -1) { if (comma == -1) {
if (i == 0) throw new Exception("Invalid line: " + line); if (i == 0) throw new Exception("Invalid line: " + line);
break; break;
} }
tuple[i] = line.Substring(lastMatch, comma - lastMatch).Trim(); tuple[i] = line.Substring(lastMatch, comma - lastMatch).Trim();
lastMatch = comma + 1; lastMatch = comma + 1;
} }
tuple[i] = line.Substring(lastMatch).Trim(); tuple[i] = line.Substring(lastMatch).Trim();
return i + 1; return i + 1;
} }
/// <summary>Returns the first region found with the specified name. This method uses string comparison to find the region, so the result /// <summary>Returns the first region found with the specified name. This method uses string comparison to find the region, so the result
/// should be cached rather than calling this method multiple times.</summary> /// should be cached rather than calling this method multiple times.</summary>
/// <returns>The region, or null.</returns> /// <returns>The region, or null.</returns>
public AtlasRegion FindRegion (String name) { public AtlasRegion FindRegion (String name) {
for (int i = 0, n = regions.Count; i < n; i++) for (int i = 0, n = regions.Count; i < n; i++)
if (regions[i].name == name) return regions[i]; if (regions[i].name == name) return regions[i];
return null; return null;
} }
public void Dispose () { public void Dispose () {
if (textureLoader == null) return; if (textureLoader == null) return;
for (int i = 0, n = pages.Count; i < n; i++) for (int i = 0, n = pages.Count; i < n; i++)
textureLoader.Unload(pages[i].rendererObject); textureLoader.Unload(pages[i].rendererObject);
} }
} }
public enum Format { public enum Format {
Alpha, Alpha,
Intensity, Intensity,
LuminanceAlpha, LuminanceAlpha,
RGB565, RGB565,
RGBA4444, RGBA4444,
RGB888, RGB888,
RGBA8888 RGBA8888
} }
public enum TextureFilter { public enum TextureFilter {
Nearest, Nearest,
Linear, Linear,
MipMap, MipMap,
MipMapNearestNearest, MipMapNearestNearest,
MipMapLinearNearest, MipMapLinearNearest,
MipMapNearestLinear, MipMapNearestLinear,
MipMapLinearLinear MipMapLinearLinear
} }
public enum TextureWrap { public enum TextureWrap {
MirroredRepeat, MirroredRepeat,
ClampToEdge, ClampToEdge,
Repeat Repeat
} }
public class AtlasPage { public class AtlasPage {
public String name; public String name;
public Format format; public Format format;
public TextureFilter minFilter; public TextureFilter minFilter;
public TextureFilter magFilter; public TextureFilter magFilter;
public TextureWrap uWrap; public TextureWrap uWrap;
public TextureWrap vWrap; public TextureWrap vWrap;
public Object rendererObject; public Object rendererObject;
public int width, height; public int width, height;
} }
public class AtlasRegion { public class AtlasRegion {
public AtlasPage page; public AtlasPage page;
public String name; public String name;
public int x, y, width, height; public int x, y, width, height;
public float u, v, u2, v2; public float u, v, u2, v2;
public float offsetX, offsetY; public float offsetX, offsetY;
public int originalWidth, originalHeight; public int originalWidth, originalHeight;
public int index; public int index;
public bool rotate; public bool rotate;
public int[] splits; public int[] splits;
public int[] pads; public int[] pads;
} }
public interface TextureLoader { public interface TextureLoader {
void Load (AtlasPage page, String path); void Load (AtlasPage page, String path);
void Unload (Object texture); void Unload (Object texture);
} }
} }

View File

@ -1,413 +1,413 @@
/****************************************************************************** /******************************************************************************
* Spine Runtimes Software License * Spine Runtimes Software License
* Version 2 * Version 2
* *
* Copyright (c) 2013, Esoteric Software * Copyright (c) 2013, Esoteric Software
* All rights reserved. * All rights reserved.
* *
* You are granted a perpetual, non-exclusive, non-sublicensable and * You are granted a perpetual, non-exclusive, non-sublicensable and
* non-transferable license to install, execute and perform the Spine Runtimes * non-transferable license to install, execute and perform the Spine Runtimes
* Software (the "Software") solely for internal use. Without the written * Software (the "Software") solely for internal use. Without the written
* permission of Esoteric Software, you may not (a) modify, translate, adapt or * permission of Esoteric Software, you may not (a) modify, translate, adapt or
* otherwise create derivative works, improvements of the Software or develop * otherwise create derivative works, improvements of the Software or develop
* new applications using the Software or (b) remove, delete, alter or obscure * new applications using the Software or (b) remove, delete, alter or obscure
* any trademarks or any copyright, trademark, patent or other intellectual * any trademarks or any copyright, trademark, patent or other intellectual
* property or proprietary rights notices on or in the Software, including * property or proprietary rights notices on or in the Software, including
* any copy thereof. Redistributions in binary or source form must include * any copy thereof. Redistributions in binary or source form must include
* this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * 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 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/ *****************************************************************************/
using System; using System;
using System.IO; using System.IO;
using System.Collections.Generic; using System.Collections.Generic;
#if WINDOWS_STOREAPP #if WINDOWS_STOREAPP
using System.Threading.Tasks; using System.Threading.Tasks;
using Windows.Storage; using Windows.Storage;
#endif #endif
namespace Spine { namespace Spine {
public class SkeletonJson { public class SkeletonJson {
private AttachmentLoader attachmentLoader; private AttachmentLoader attachmentLoader;
public float Scale { get; set; } public float Scale { get; set; }
public SkeletonJson (Atlas atlas) public SkeletonJson (Atlas atlas)
: this(new AtlasAttachmentLoader(atlas)) { : this(new AtlasAttachmentLoader(atlas)) {
} }
public SkeletonJson (AttachmentLoader attachmentLoader) { public SkeletonJson (AttachmentLoader attachmentLoader) {
if (attachmentLoader == null) throw new ArgumentNullException("attachmentLoader cannot be null."); if (attachmentLoader == null) throw new ArgumentNullException("attachmentLoader cannot be null.");
this.attachmentLoader = attachmentLoader; this.attachmentLoader = attachmentLoader;
Scale = 1; Scale = 1;
} }
#if WINDOWS_STOREAPP #if WINDOWS_STOREAPP
private async Task<SkeletonData> ReadFile(string path) { private async Task<SkeletonData> ReadFile(string path) {
var folder = Windows.ApplicationModel.Package.Current.InstalledLocation; var folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
var file = await folder.GetFileAsync(path).AsTask().ConfigureAwait(false); var file = await folder.GetFileAsync(path).AsTask().ConfigureAwait(false);
using (var reader = new StreamReader(await file.OpenStreamForReadAsync().ConfigureAwait(false))) { using (var reader = new StreamReader(await file.OpenStreamForReadAsync().ConfigureAwait(false))) {
SkeletonData skeletonData = ReadSkeletonData(reader); SkeletonData skeletonData = ReadSkeletonData(reader);
skeletonData.Name = Path.GetFileNameWithoutExtension(path); skeletonData.Name = Path.GetFileNameWithoutExtension(path);
return skeletonData; return skeletonData;
} }
} }
public SkeletonData ReadSkeletonData (String path) { public SkeletonData ReadSkeletonData (String path) {
return this.ReadFile(path).Result; return this.ReadFile(path).Result;
} }
#else #else
public SkeletonData ReadSkeletonData (String path) { public SkeletonData ReadSkeletonData (String path) {
#if WINDOWS_PHONE #if WINDOWS_PHONE
Stream stream = Microsoft.Xna.Framework.TitleContainer.OpenStream(path); Stream stream = Microsoft.Xna.Framework.TitleContainer.OpenStream(path);
using (StreamReader reader = new StreamReader(stream)) using (StreamReader reader = new StreamReader(stream))
{ {
#else #else
using (StreamReader reader = new StreamReader(path)) { using (StreamReader reader = new StreamReader(path)) {
#endif #endif
SkeletonData skeletonData = ReadSkeletonData(reader); SkeletonData skeletonData = ReadSkeletonData(reader);
skeletonData.name = Path.GetFileNameWithoutExtension(path); skeletonData.name = Path.GetFileNameWithoutExtension(path);
return skeletonData; return skeletonData;
} }
} }
#endif #endif
public SkeletonData ReadSkeletonData (TextReader reader) { public SkeletonData ReadSkeletonData (TextReader reader) {
if (reader == null) throw new ArgumentNullException("reader cannot be null."); if (reader == null) throw new ArgumentNullException("reader cannot be null.");
SkeletonData skeletonData = new SkeletonData(); SkeletonData skeletonData = new SkeletonData();
var root = Json.Deserialize(reader) as Dictionary<String, Object>; var root = Json.Deserialize(reader) as Dictionary<String, Object>;
if (root == null) throw new Exception("Invalid JSON."); if (root == null) throw new Exception("Invalid JSON.");
// Bones. // Bones.
foreach (Dictionary<String, Object> boneMap in (List<Object>)root["bones"]) { foreach (Dictionary<String, Object> boneMap in (List<Object>)root["bones"]) {
BoneData parent = null; BoneData parent = null;
if (boneMap.ContainsKey("parent")) { if (boneMap.ContainsKey("parent")) {
parent = skeletonData.FindBone((String)boneMap["parent"]); parent = skeletonData.FindBone((String)boneMap["parent"]);
if (parent == null) if (parent == null)
throw new Exception("Parent bone not found: " + boneMap["parent"]); throw new Exception("Parent bone not found: " + boneMap["parent"]);
} }
BoneData boneData = new BoneData((String)boneMap["name"], parent); BoneData boneData = new BoneData((String)boneMap["name"], parent);
boneData.length = GetFloat(boneMap, "length", 0) * Scale; boneData.length = GetFloat(boneMap, "length", 0) * Scale;
boneData.x = GetFloat(boneMap, "x", 0) * Scale; boneData.x = GetFloat(boneMap, "x", 0) * Scale;
boneData.y = GetFloat(boneMap, "y", 0) * Scale; boneData.y = GetFloat(boneMap, "y", 0) * Scale;
boneData.rotation = GetFloat(boneMap, "rotation", 0); boneData.rotation = GetFloat(boneMap, "rotation", 0);
boneData.scaleX = GetFloat(boneMap, "scaleX", 1); boneData.scaleX = GetFloat(boneMap, "scaleX", 1);
boneData.scaleY = GetFloat(boneMap, "scaleY", 1); boneData.scaleY = GetFloat(boneMap, "scaleY", 1);
boneData.inheritScale = GetBoolean(boneMap, "inheritScale", true); boneData.inheritScale = GetBoolean(boneMap, "inheritScale", true);
boneData.inheritRotation = GetBoolean(boneMap, "inheritRotation", true); boneData.inheritRotation = GetBoolean(boneMap, "inheritRotation", true);
skeletonData.AddBone(boneData); skeletonData.AddBone(boneData);
} }
// Slots. // Slots.
if (root.ContainsKey("slots")) { if (root.ContainsKey("slots")) {
foreach (Dictionary<String, Object> slotMap in (List<Object>)root["slots"]) { foreach (Dictionary<String, Object> slotMap in (List<Object>)root["slots"]) {
String slotName = (String)slotMap["name"]; String slotName = (String)slotMap["name"];
String boneName = (String)slotMap["bone"]; String boneName = (String)slotMap["bone"];
BoneData boneData = skeletonData.FindBone(boneName); BoneData boneData = skeletonData.FindBone(boneName);
if (boneData == null) if (boneData == null)
throw new Exception("Slot bone not found: " + boneName); throw new Exception("Slot bone not found: " + boneName);
SlotData slotData = new SlotData(slotName, boneData); SlotData slotData = new SlotData(slotName, boneData);
if (slotMap.ContainsKey("color")) { if (slotMap.ContainsKey("color")) {
String color = (String)slotMap["color"]; String color = (String)slotMap["color"];
slotData.r = ToColor(color, 0); slotData.r = ToColor(color, 0);
slotData.g = ToColor(color, 1); slotData.g = ToColor(color, 1);
slotData.b = ToColor(color, 2); slotData.b = ToColor(color, 2);
slotData.a = ToColor(color, 3); slotData.a = ToColor(color, 3);
} }
if (slotMap.ContainsKey("attachment")) if (slotMap.ContainsKey("attachment"))
slotData.attachmentName = (String)slotMap["attachment"]; slotData.attachmentName = (String)slotMap["attachment"];
if (slotMap.ContainsKey("additive")) if (slotMap.ContainsKey("additive"))
slotData.additiveBlending = (bool)slotMap["additive"]; slotData.additiveBlending = (bool)slotMap["additive"];
skeletonData.AddSlot(slotData); skeletonData.AddSlot(slotData);
} }
} }
// Skins. // Skins.
if (root.ContainsKey("skins")) { if (root.ContainsKey("skins")) {
foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)root["skins"]) { foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)root["skins"]) {
Skin skin = new Skin(entry.Key); Skin skin = new Skin(entry.Key);
foreach (KeyValuePair<String, Object> slotEntry in (Dictionary<String, Object>)entry.Value) { foreach (KeyValuePair<String, Object> slotEntry in (Dictionary<String, Object>)entry.Value) {
int slotIndex = skeletonData.FindSlotIndex(slotEntry.Key); int slotIndex = skeletonData.FindSlotIndex(slotEntry.Key);
foreach (KeyValuePair<String, Object> attachmentEntry in ((Dictionary<String, Object>)slotEntry.Value)) { foreach (KeyValuePair<String, Object> attachmentEntry in ((Dictionary<String, Object>)slotEntry.Value)) {
Attachment attachment = ReadAttachment(skin, attachmentEntry.Key, (Dictionary<String, Object>)attachmentEntry.Value); Attachment attachment = ReadAttachment(skin, attachmentEntry.Key, (Dictionary<String, Object>)attachmentEntry.Value);
if (attachment != null) skin.AddAttachment(slotIndex, attachmentEntry.Key, attachment); if (attachment != null) skin.AddAttachment(slotIndex, attachmentEntry.Key, attachment);
} }
} }
skeletonData.AddSkin(skin); skeletonData.AddSkin(skin);
if (skin.name == "default") if (skin.name == "default")
skeletonData.defaultSkin = skin; skeletonData.defaultSkin = skin;
} }
} }
// Events. // Events.
if (root.ContainsKey("events")) { if (root.ContainsKey("events")) {
foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)root["events"]) { foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)root["events"]) {
var entryMap = (Dictionary<String, Object>)entry.Value; var entryMap = (Dictionary<String, Object>)entry.Value;
EventData eventData = new EventData(entry.Key); EventData eventData = new EventData(entry.Key);
eventData.Int = GetInt(entryMap, "int", 0); eventData.Int = GetInt(entryMap, "int", 0);
eventData.Float = GetFloat(entryMap, "float", 0); eventData.Float = GetFloat(entryMap, "float", 0);
eventData.String = GetString(entryMap, "string", null); eventData.String = GetString(entryMap, "string", null);
skeletonData.AddEvent(eventData); skeletonData.AddEvent(eventData);
} }
} }
// Animations. // Animations.
if (root.ContainsKey("animations")) { if (root.ContainsKey("animations")) {
foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)root["animations"]) foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)root["animations"])
ReadAnimation(entry.Key, (Dictionary<String, Object>)entry.Value, skeletonData); ReadAnimation(entry.Key, (Dictionary<String, Object>)entry.Value, skeletonData);
} }
skeletonData.bones.TrimExcess(); skeletonData.bones.TrimExcess();
skeletonData.slots.TrimExcess(); skeletonData.slots.TrimExcess();
skeletonData.skins.TrimExcess(); skeletonData.skins.TrimExcess();
skeletonData.animations.TrimExcess(); skeletonData.animations.TrimExcess();
return skeletonData; return skeletonData;
} }
private Attachment ReadAttachment (Skin skin, String name, Dictionary<String, Object> map) { private Attachment ReadAttachment (Skin skin, String name, Dictionary<String, Object> map) {
if (map.ContainsKey("name")) if (map.ContainsKey("name"))
name = (String)map["name"]; name = (String)map["name"];
AttachmentType type = AttachmentType.region; AttachmentType type = AttachmentType.region;
if (map.ContainsKey("type")) if (map.ContainsKey("type"))
type = (AttachmentType)Enum.Parse(typeof(AttachmentType), (String)map["type"], false); type = (AttachmentType)Enum.Parse(typeof(AttachmentType), (String)map["type"], false);
Attachment attachment = attachmentLoader.NewAttachment(skin, type, name); Attachment attachment = attachmentLoader.NewAttachment(skin, type, name);
RegionAttachment regionAttachment = attachment as RegionAttachment; RegionAttachment regionAttachment = attachment as RegionAttachment;
if (regionAttachment != null) { if (regionAttachment != null) {
regionAttachment.x = GetFloat(map, "x", 0) * Scale; regionAttachment.x = GetFloat(map, "x", 0) * Scale;
regionAttachment.y = GetFloat(map, "y", 0) * Scale; regionAttachment.y = GetFloat(map, "y", 0) * Scale;
regionAttachment.scaleX = GetFloat(map, "scaleX", 1); regionAttachment.scaleX = GetFloat(map, "scaleX", 1);
regionAttachment.scaleY = GetFloat(map, "scaleY", 1); regionAttachment.scaleY = GetFloat(map, "scaleY", 1);
regionAttachment.rotation = GetFloat(map, "rotation", 0); regionAttachment.rotation = GetFloat(map, "rotation", 0);
regionAttachment.width = GetFloat(map, "width", 32) * Scale; regionAttachment.width = GetFloat(map, "width", 32) * Scale;
regionAttachment.height = GetFloat(map, "height", 32) * Scale; regionAttachment.height = GetFloat(map, "height", 32) * Scale;
regionAttachment.UpdateOffset(); regionAttachment.UpdateOffset();
} }
BoundingBoxAttachment boundingBox = attachment as BoundingBoxAttachment; BoundingBoxAttachment boundingBox = attachment as BoundingBoxAttachment;
if (boundingBox != null) { if (boundingBox != null) {
List<Object> values = (List<Object>)map["vertices"]; List<Object> values = (List<Object>)map["vertices"];
float[] vertices = new float[values.Count]; float[] vertices = new float[values.Count];
for (int i = 0, n = values.Count; i < n; i++) for (int i = 0, n = values.Count; i < n; i++)
vertices[i] = (float)values[i] * Scale; vertices[i] = (float)values[i] * Scale;
boundingBox.Vertices = vertices; boundingBox.Vertices = vertices;
} }
return attachment; return attachment;
} }
private float GetFloat (Dictionary<String, Object> map, String name, float defaultValue) { private float GetFloat (Dictionary<String, Object> map, String name, float defaultValue) {
if (!map.ContainsKey(name)) if (!map.ContainsKey(name))
return defaultValue; return defaultValue;
return (float)map[name]; return (float)map[name];
} }
private int GetInt (Dictionary<String, Object> map, String name, int defaultValue) { private int GetInt (Dictionary<String, Object> map, String name, int defaultValue) {
if (!map.ContainsKey(name)) if (!map.ContainsKey(name))
return defaultValue; return defaultValue;
return (int)(float)map[name]; return (int)(float)map[name];
} }
private bool GetBoolean (Dictionary<String, Object> map, String name, bool defaultValue) { private bool GetBoolean (Dictionary<String, Object> map, String name, bool defaultValue) {
if (!map.ContainsKey(name)) if (!map.ContainsKey(name))
return defaultValue; return defaultValue;
return (bool)map[name]; return (bool)map[name];
} }
private String GetString (Dictionary<String, Object> map, String name, String defaultValue) { private String GetString (Dictionary<String, Object> map, String name, String defaultValue) {
if (!map.ContainsKey(name)) if (!map.ContainsKey(name))
return defaultValue; return defaultValue;
return (String)map[name]; return (String)map[name];
} }
public static float ToColor (String hexString, int colorIndex) { public static float ToColor (String hexString, int colorIndex) {
if (hexString.Length != 8) if (hexString.Length != 8)
throw new ArgumentException("Color hexidecimal length must be 8, recieved: " + hexString); throw new ArgumentException("Color hexidecimal length must be 8, recieved: " + hexString);
return Convert.ToInt32(hexString.Substring(colorIndex * 2, 2), 16) / (float)255; return Convert.ToInt32(hexString.Substring(colorIndex * 2, 2), 16) / (float)255;
} }
private void ReadAnimation (String name, Dictionary<String, Object> map, SkeletonData skeletonData) { private void ReadAnimation (String name, Dictionary<String, Object> map, SkeletonData skeletonData) {
var timelines = new List<Timeline>(); var timelines = new List<Timeline>();
float duration = 0; float duration = 0;
if (map.ContainsKey("bones")) { if (map.ContainsKey("bones")) {
foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)map["bones"]) { foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)map["bones"]) {
String boneName = entry.Key; String boneName = entry.Key;
int boneIndex = skeletonData.FindBoneIndex(boneName); int boneIndex = skeletonData.FindBoneIndex(boneName);
if (boneIndex == -1) if (boneIndex == -1)
throw new Exception("Bone not found: " + boneName); throw new Exception("Bone not found: " + boneName);
var timelineMap = (Dictionary<String, Object>)entry.Value; var timelineMap = (Dictionary<String, Object>)entry.Value;
foreach (KeyValuePair<String, Object> timelineEntry in timelineMap) { foreach (KeyValuePair<String, Object> timelineEntry in timelineMap) {
var values = (List<Object>)timelineEntry.Value; var values = (List<Object>)timelineEntry.Value;
String timelineName = (String)timelineEntry.Key; String timelineName = (String)timelineEntry.Key;
if (timelineName.Equals("rotate")) { if (timelineName.Equals("rotate")) {
RotateTimeline timeline = new RotateTimeline(values.Count); RotateTimeline timeline = new RotateTimeline(values.Count);
timeline.boneIndex = boneIndex; timeline.boneIndex = boneIndex;
int frameIndex = 0; int frameIndex = 0;
foreach (Dictionary<String, Object> valueMap in values) { foreach (Dictionary<String, Object> valueMap in values) {
float time = (float)valueMap["time"]; float time = (float)valueMap["time"];
timeline.SetFrame(frameIndex, time, (float)valueMap["angle"]); timeline.SetFrame(frameIndex, time, (float)valueMap["angle"]);
ReadCurve(timeline, frameIndex, valueMap); ReadCurve(timeline, frameIndex, valueMap);
frameIndex++; frameIndex++;
} }
timelines.Add(timeline); timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[timeline.FrameCount * 2 - 2]); duration = Math.Max(duration, timeline.frames[timeline.FrameCount * 2 - 2]);
} else if (timelineName.Equals("translate") || timelineName.Equals("scale")) { } else if (timelineName.Equals("translate") || timelineName.Equals("scale")) {
TranslateTimeline timeline; TranslateTimeline timeline;
float timelineScale = 1; float timelineScale = 1;
if (timelineName.Equals("scale")) if (timelineName.Equals("scale"))
timeline = new ScaleTimeline(values.Count); timeline = new ScaleTimeline(values.Count);
else { else {
timeline = new TranslateTimeline(values.Count); timeline = new TranslateTimeline(values.Count);
timelineScale = Scale; timelineScale = Scale;
} }
timeline.boneIndex = boneIndex; timeline.boneIndex = boneIndex;
int frameIndex = 0; int frameIndex = 0;
foreach (Dictionary<String, Object> valueMap in values) { foreach (Dictionary<String, Object> valueMap in values) {
float time = (float)valueMap["time"]; float time = (float)valueMap["time"];
float x = valueMap.ContainsKey("x") ? (float)valueMap["x"] : 0; float x = valueMap.ContainsKey("x") ? (float)valueMap["x"] : 0;
float y = valueMap.ContainsKey("y") ? (float)valueMap["y"] : 0; float y = valueMap.ContainsKey("y") ? (float)valueMap["y"] : 0;
timeline.SetFrame(frameIndex, time, (float)x * timelineScale, (float)y * timelineScale); timeline.SetFrame(frameIndex, time, (float)x * timelineScale, (float)y * timelineScale);
ReadCurve(timeline, frameIndex, valueMap); ReadCurve(timeline, frameIndex, valueMap);
frameIndex++; frameIndex++;
} }
timelines.Add(timeline); timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[timeline.FrameCount * 3 - 3]); duration = Math.Max(duration, timeline.frames[timeline.FrameCount * 3 - 3]);
} else } else
throw new Exception("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"); throw new Exception("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")");
} }
} }
} }
if (map.ContainsKey("slots")) { if (map.ContainsKey("slots")) {
foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)map["slots"]) { foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)map["slots"]) {
String slotName = entry.Key; String slotName = entry.Key;
int slotIndex = skeletonData.FindSlotIndex(slotName); int slotIndex = skeletonData.FindSlotIndex(slotName);
var timelineMap = (Dictionary<String, Object>)entry.Value; var timelineMap = (Dictionary<String, Object>)entry.Value;
foreach (KeyValuePair<String, Object> timelineEntry in timelineMap) { foreach (KeyValuePair<String, Object> timelineEntry in timelineMap) {
var values = (List<Object>)timelineEntry.Value; var values = (List<Object>)timelineEntry.Value;
String timelineName = (String)timelineEntry.Key; String timelineName = (String)timelineEntry.Key;
if (timelineName.Equals("color")) { if (timelineName.Equals("color")) {
ColorTimeline timeline = new ColorTimeline(values.Count); ColorTimeline timeline = new ColorTimeline(values.Count);
timeline.slotIndex = slotIndex; timeline.slotIndex = slotIndex;
int frameIndex = 0; int frameIndex = 0;
foreach (Dictionary<String, Object> valueMap in values) { foreach (Dictionary<String, Object> valueMap in values) {
float time = (float)valueMap["time"]; float time = (float)valueMap["time"];
String c = (String)valueMap["color"]; String c = (String)valueMap["color"];
timeline.setFrame(frameIndex, time, ToColor(c, 0), ToColor(c, 1), ToColor(c, 2), ToColor(c, 3)); timeline.setFrame(frameIndex, time, ToColor(c, 0), ToColor(c, 1), ToColor(c, 2), ToColor(c, 3));
ReadCurve(timeline, frameIndex, valueMap); ReadCurve(timeline, frameIndex, valueMap);
frameIndex++; frameIndex++;
} }
timelines.Add(timeline); timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[timeline.FrameCount * 5 - 5]); duration = Math.Max(duration, timeline.frames[timeline.FrameCount * 5 - 5]);
} else if (timelineName.Equals("attachment")) { } else if (timelineName.Equals("attachment")) {
AttachmentTimeline timeline = new AttachmentTimeline(values.Count); AttachmentTimeline timeline = new AttachmentTimeline(values.Count);
timeline.slotIndex = slotIndex; timeline.slotIndex = slotIndex;
int frameIndex = 0; int frameIndex = 0;
foreach (Dictionary<String, Object> valueMap in values) { foreach (Dictionary<String, Object> valueMap in values) {
float time = (float)valueMap["time"]; float time = (float)valueMap["time"];
timeline.setFrame(frameIndex++, time, (String)valueMap["name"]); timeline.setFrame(frameIndex++, time, (String)valueMap["name"]);
} }
timelines.Add(timeline); timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]); duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]);
} else } else
throw new Exception("Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"); throw new Exception("Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")");
} }
} }
} }
if (map.ContainsKey("events")) { if (map.ContainsKey("events")) {
var eventsMap = (List<Object>)map["events"]; var eventsMap = (List<Object>)map["events"];
EventTimeline timeline = new EventTimeline(eventsMap.Count); EventTimeline timeline = new EventTimeline(eventsMap.Count);
int frameIndex = 0; int frameIndex = 0;
foreach (Dictionary<String, Object> eventMap in eventsMap) { foreach (Dictionary<String, Object> eventMap in eventsMap) {
EventData eventData = skeletonData.FindEvent((String)eventMap["name"]); EventData eventData = skeletonData.FindEvent((String)eventMap["name"]);
if (eventData == null) throw new Exception("Event not found: " + eventMap["name"]); if (eventData == null) throw new Exception("Event not found: " + eventMap["name"]);
Event e = new Event(eventData); Event e = new Event(eventData);
e.Int = GetInt(eventMap, "int", eventData.Int); e.Int = GetInt(eventMap, "int", eventData.Int);
e.Float = GetFloat(eventMap, "float", eventData.Float); e.Float = GetFloat(eventMap, "float", eventData.Float);
e.String = GetString(eventMap, "string", eventData.String); e.String = GetString(eventMap, "string", eventData.String);
timeline.setFrame(frameIndex++, (float)eventMap["time"], e); timeline.setFrame(frameIndex++, (float)eventMap["time"], e);
} }
timelines.Add(timeline); timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]); duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]);
} }
if (map.ContainsKey("draworder")) { if (map.ContainsKey("draworder")) {
var values = (List<Object>)map["draworder"]; var values = (List<Object>)map["draworder"];
DrawOrderTimeline timeline = new DrawOrderTimeline(values.Count); DrawOrderTimeline timeline = new DrawOrderTimeline(values.Count);
int slotCount = skeletonData.slots.Count; int slotCount = skeletonData.slots.Count;
int frameIndex = 0; int frameIndex = 0;
foreach (Dictionary<String, Object> drawOrderMap in values) { foreach (Dictionary<String, Object> drawOrderMap in values) {
int[] drawOrder = null; int[] drawOrder = null;
if (drawOrderMap.ContainsKey("offsets")) { if (drawOrderMap.ContainsKey("offsets")) {
drawOrder = new int[slotCount]; drawOrder = new int[slotCount];
for (int i = slotCount - 1; i >= 0; i--) for (int i = slotCount - 1; i >= 0; i--)
drawOrder[i] = -1; drawOrder[i] = -1;
List<Object> offsets = (List<Object>)drawOrderMap["offsets"]; List<Object> offsets = (List<Object>)drawOrderMap["offsets"];
int[] unchanged = new int[slotCount - offsets.Count]; int[] unchanged = new int[slotCount - offsets.Count];
int originalIndex = 0, unchangedIndex = 0; int originalIndex = 0, unchangedIndex = 0;
foreach (Dictionary<String, Object> offsetMap in offsets) { foreach (Dictionary<String, Object> offsetMap in offsets) {
int slotIndex = skeletonData.FindSlotIndex((String)offsetMap["slot"]); int slotIndex = skeletonData.FindSlotIndex((String)offsetMap["slot"]);
if (slotIndex == -1) throw new Exception("Slot not found: " + offsetMap["slot"]); if (slotIndex == -1) throw new Exception("Slot not found: " + offsetMap["slot"]);
// Collect unchanged items. // Collect unchanged items.
while (originalIndex != slotIndex) while (originalIndex != slotIndex)
unchanged[unchangedIndex++] = originalIndex++; unchanged[unchangedIndex++] = originalIndex++;
// Set changed items. // Set changed items.
drawOrder[originalIndex + (int)(float)offsetMap["offset"]] = originalIndex++; drawOrder[originalIndex + (int)(float)offsetMap["offset"]] = originalIndex++;
} }
// Collect remaining unchanged items. // Collect remaining unchanged items.
while (originalIndex < slotCount) while (originalIndex < slotCount)
unchanged[unchangedIndex++] = originalIndex++; unchanged[unchangedIndex++] = originalIndex++;
// Fill in unchanged items. // Fill in unchanged items.
for (int i = slotCount - 1; i >= 0; i--) for (int i = slotCount - 1; i >= 0; i--)
if (drawOrder[i] == -1) drawOrder[i] = unchanged[--unchangedIndex]; if (drawOrder[i] == -1) drawOrder[i] = unchanged[--unchangedIndex];
} }
timeline.setFrame(frameIndex++, (float)drawOrderMap["time"], drawOrder); timeline.setFrame(frameIndex++, (float)drawOrderMap["time"], drawOrder);
} }
timelines.Add(timeline); timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]); duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]);
} }
timelines.TrimExcess(); timelines.TrimExcess();
skeletonData.AddAnimation(new Animation(name, timelines, duration)); skeletonData.AddAnimation(new Animation(name, timelines, duration));
} }
private void ReadCurve (CurveTimeline timeline, int frameIndex, Dictionary<String, Object> valueMap) { private void ReadCurve (CurveTimeline timeline, int frameIndex, Dictionary<String, Object> valueMap) {
if (!valueMap.ContainsKey("curve")) if (!valueMap.ContainsKey("curve"))
return; return;
Object curveObject = valueMap["curve"]; Object curveObject = valueMap["curve"];
if (curveObject.Equals("stepped")) if (curveObject.Equals("stepped"))
timeline.SetStepped(frameIndex); timeline.SetStepped(frameIndex);
else if (curveObject is List<Object>) { else if (curveObject is List<Object>) {
List<Object> curve = (List<Object>)curveObject; List<Object> curve = (List<Object>)curveObject;
timeline.SetCurve(frameIndex, (float)curve[0], (float)curve[1], (float)curve[2], (float)curve[3]); timeline.SetCurve(frameIndex, (float)curve[0], (float)curve[1], (float)curve[2], (float)curve[3]);
} }
} }
} }
} }