Przeglądaj źródła

Merge pull request #146 from lilligreen/spine

Initial implementation of Spine support. Note, this will temporarily not work on Windows platforms. The VS solutions need to be updated. I'll take care of that.
Mich 11 lat temu
rodzic
commit
8cf6e7d132
77 zmienionych plików z 10488 dodań i 20 usunięć
  1. 171 15
      engine/compilers/Xcode/Torque2D.xcodeproj/project.pbxproj
  2. 158 0
      engine/compilers/Xcode_iOS/Torque2D.xcodeproj/project.pbxproj
  3. 404 0
      engine/source/2d/assets/SkeletonAsset.cc
  4. 107 0
      engine/source/2d/assets/SkeletonAsset.h
  5. 87 0
      engine/source/2d/assets/SkeletonAsset_ScriptBinding.h
  6. 28 0
      engine/source/2d/core/SpriteBatch.cc
  7. 1 0
      engine/source/2d/core/SpriteBatch.h
  8. 31 5
      engine/source/2d/core/SpriteBatchItem.cc
  9. 7 0
      engine/source/2d/core/SpriteBatchItem.h
  10. 458 0
      engine/source/2d/sceneobject/Skeleton.cc
  11. 148 0
      engine/source/2d/sceneobject/Skeleton.h
  12. 216 0
      engine/source/2d/sceneobject/Skeleton_ScriptBinding.h
  13. 634 0
      engine/source/spine/Animation.c
  14. 249 0
      engine/source/spine/Animation.h
  15. 282 0
      engine/source/spine/AnimationState.c
  16. 116 0
      engine/source/spine/AnimationState.h
  17. 149 0
      engine/source/spine/AnimationStateData.c
  18. 66 0
      engine/source/spine/AnimationStateData.h
  19. 338 0
      engine/source/spine/Atlas.c
  20. 136 0
      engine/source/spine/Atlas.h
  21. 67 0
      engine/source/spine/AtlasAttachmentLoader.c
  22. 55 0
      engine/source/spine/AtlasAttachmentLoader.h
  23. 54 0
      engine/source/spine/Attachment.c
  24. 62 0
      engine/source/spine/Attachment.h
  25. 76 0
      engine/source/spine/AttachmentLoader.c
  26. 69 0
      engine/source/spine/AttachmentLoader.h
  27. 99 0
      engine/source/spine/Bone.c
  28. 75 0
      engine/source/spine/Bone.h
  29. 46 0
      engine/source/spine/BoneData.c
  30. 60 0
      engine/source/spine/BoneData.h
  31. 60 0
      engine/source/spine/BoundingBoxAttachment.c
  32. 60 0
      engine/source/spine/BoundingBoxAttachment.h
  33. 41 0
      engine/source/spine/Event.c
  34. 59 0
      engine/source/spine/Event.h
  35. 42 0
      engine/source/spine/EventData.c
  36. 57 0
      engine/source/spine/EventData.h
  37. 358 0
      engine/source/spine/Json.c
  38. 76 0
      engine/source/spine/Json.h
  39. 115 0
      engine/source/spine/RegionAttachment.c
  40. 76 0
      engine/source/spine/RegionAttachment.h
  41. 206 0
      engine/source/spine/Skeleton.c
  42. 119 0
      engine/source/spine/Skeleton.h
  43. 203 0
      engine/source/spine/SkeletonBounds.c
  44. 110 0
      engine/source/spine/SkeletonBounds.h
  45. 109 0
      engine/source/spine/SkeletonData.c
  46. 92 0
      engine/source/spine/SkeletonData.h
  47. 500 0
      engine/source/spine/SkeletonJson.c
  48. 68 0
      engine/source/spine/SkeletonJson.h
  49. 117 0
      engine/source/spine/Skin.c
  50. 72 0
      engine/source/spine/Skin.h
  51. 82 0
      engine/source/spine/Slot.c
  52. 75 0
      engine/source/spine/Slot.h
  53. 55 0
      engine/source/spine/SlotData.c
  54. 63 0
      engine/source/spine/SlotData.h
  55. 68 0
      engine/source/spine/extension.c
  56. 164 0
      engine/source/spine/extension.h
  57. 53 0
      engine/source/spine/spine.h
  58. 4 0
      modules/SpineToy/1/assets/goblins/goblins.asset.taml
  59. 284 0
      modules/SpineToy/1/assets/goblins/goblins.atlas
  60. 465 0
      modules/SpineToy/1/assets/goblins/goblins.json
  61. BIN
      modules/SpineToy/1/assets/goblins/goblins.png
  62. 4 0
      modules/SpineToy/1/assets/powerup/powerup.asset.taml
  63. 25 0
      modules/SpineToy/1/assets/powerup/powerup.atlas
  64. 487 0
      modules/SpineToy/1/assets/powerup/powerup.json
  65. BIN
      modules/SpineToy/1/assets/powerup/powerup.png
  66. 166 0
      modules/SpineToy/1/assets/spineboy/spineboy.atlas
  67. 775 0
      modules/SpineToy/1/assets/spineboy/spineboy.json
  68. BIN
      modules/SpineToy/1/assets/spineboy/spineboy.png
  69. 4 0
      modules/SpineToy/1/assets/spineboy/test.asset.taml
  70. 3 0
      modules/SpineToy/1/assets/spinosaurus/background.asset.taml
  71. BIN
      modules/SpineToy/1/assets/spinosaurus/background.jpg
  72. 4 0
      modules/SpineToy/1/assets/spinosaurus/spinosaurus.asset.taml
  73. 39 0
      modules/SpineToy/1/assets/spinosaurus/spinosaurus.atlas
  74. 430 0
      modules/SpineToy/1/assets/spinosaurus/spinosaurus.json
  75. BIN
      modules/SpineToy/1/assets/spinosaurus/spinosaurus.png
  76. 234 0
      modules/SpineToy/1/main.cs
  77. 15 0
      modules/SpineToy/1/module.taml

+ 171 - 15
engine/compilers/Xcode/Torque2D.xcodeproj/project.pbxproj

@@ -7,6 +7,30 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		27908DFA18A3F8CB002D41BD /* Animation.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DCD18A3F8CB002D41BD /* Animation.c */; };
+		27908DFB18A3F8CB002D41BD /* AnimationState.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DCF18A3F8CB002D41BD /* AnimationState.c */; };
+		27908DFC18A3F8CB002D41BD /* AnimationStateData.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DD118A3F8CB002D41BD /* AnimationStateData.c */; };
+		27908DFD18A3F8CB002D41BD /* Atlas.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DD318A3F8CB002D41BD /* Atlas.c */; };
+		27908DFE18A3F8CB002D41BD /* AtlasAttachmentLoader.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DD518A3F8CB002D41BD /* AtlasAttachmentLoader.c */; };
+		27908DFF18A3F8CB002D41BD /* Attachment.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DD718A3F8CB002D41BD /* Attachment.c */; };
+		27908E0018A3F8CB002D41BD /* AttachmentLoader.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DD918A3F8CB002D41BD /* AttachmentLoader.c */; };
+		27908E0118A3F8CB002D41BD /* Bone.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DDB18A3F8CB002D41BD /* Bone.c */; };
+		27908E0218A3F8CB002D41BD /* BoneData.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DDD18A3F8CB002D41BD /* BoneData.c */; };
+		27908E0318A3F8CB002D41BD /* BoundingBoxAttachment.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DDF18A3F8CB002D41BD /* BoundingBoxAttachment.c */; };
+		27908E0418A3F8CB002D41BD /* Event.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DE118A3F8CB002D41BD /* Event.c */; };
+		27908E0518A3F8CB002D41BD /* EventData.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DE318A3F8CB002D41BD /* EventData.c */; };
+		27908E0618A3F8CB002D41BD /* extension.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DE518A3F8CB002D41BD /* extension.c */; };
+		27908E0718A3F8CB002D41BD /* Json.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DE718A3F8CB002D41BD /* Json.c */; };
+		27908E0818A3F8CB002D41BD /* RegionAttachment.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DE918A3F8CB002D41BD /* RegionAttachment.c */; };
+		27908E0918A3F8CB002D41BD /* Skeleton.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DEB18A3F8CB002D41BD /* Skeleton.c */; };
+		27908E0A18A3F8CB002D41BD /* SkeletonBounds.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DED18A3F8CB002D41BD /* SkeletonBounds.c */; };
+		27908E0B18A3F8CB002D41BD /* SkeletonData.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DEF18A3F8CB002D41BD /* SkeletonData.c */; };
+		27908E0C18A3F8CB002D41BD /* SkeletonJson.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DF118A3F8CB002D41BD /* SkeletonJson.c */; };
+		27908E0D18A3F8CB002D41BD /* Skin.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DF318A3F8CB002D41BD /* Skin.c */; };
+		27908E0E18A3F8CB002D41BD /* Slot.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DF518A3F8CB002D41BD /* Slot.c */; };
+		27908E0F18A3F8CB002D41BD /* SlotData.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DF718A3F8CB002D41BD /* SlotData.c */; };
+		27908E1318A3F904002D41BD /* SkeletonAsset.cc in Sources */ = {isa = PBXBuildFile; fileRef = 27908E1118A3F904002D41BD /* SkeletonAsset.cc */; };
+		27908E1718A3F91F002D41BD /* Skeleton.cc in Sources */ = {isa = PBXBuildFile; fileRef = 27908E1518A3F91F002D41BD /* Skeleton.cc */; };
 		2A03300D165D1D2100E9CD70 /* unitTesting.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2A03300B165D1D2100E9CD70 /* unitTesting.cc */; };
 		2A033011165D1D4100E9CD70 /* platformFileIoTests.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2A033010165D1D4100E9CD70 /* platformFileIoTests.cc */; };
 		2A25739016A48DAC00363C6F /* ParticlePlayer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2A25738E16A48DAC00363C6F /* ParticlePlayer.cc */; };
@@ -472,7 +496,57 @@
 /* End PBXCopyFilesBuildPhase section */
 
 /* Begin PBXFileReference section */
-		04CC626B1795512500F9C119 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = ../../../../../../../System/Library/Frameworks/AVFoundation.framework; sourceTree = "<group>"; };
+		27908DCD18A3F8CB002D41BD /* Animation.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Animation.c; path = ../../../source/spine/Animation.c; sourceTree = "<group>"; };
+		27908DCE18A3F8CB002D41BD /* Animation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Animation.h; path = ../../../source/spine/Animation.h; sourceTree = "<group>"; };
+		27908DCF18A3F8CB002D41BD /* AnimationState.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = AnimationState.c; path = ../../../source/spine/AnimationState.c; sourceTree = "<group>"; };
+		27908DD018A3F8CB002D41BD /* AnimationState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AnimationState.h; path = ../../../source/spine/AnimationState.h; sourceTree = "<group>"; };
+		27908DD118A3F8CB002D41BD /* AnimationStateData.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = AnimationStateData.c; path = ../../../source/spine/AnimationStateData.c; sourceTree = "<group>"; };
+		27908DD218A3F8CB002D41BD /* AnimationStateData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AnimationStateData.h; path = ../../../source/spine/AnimationStateData.h; sourceTree = "<group>"; };
+		27908DD318A3F8CB002D41BD /* Atlas.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Atlas.c; path = ../../../source/spine/Atlas.c; sourceTree = "<group>"; };
+		27908DD418A3F8CB002D41BD /* Atlas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Atlas.h; path = ../../../source/spine/Atlas.h; sourceTree = "<group>"; };
+		27908DD518A3F8CB002D41BD /* AtlasAttachmentLoader.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = AtlasAttachmentLoader.c; path = ../../../source/spine/AtlasAttachmentLoader.c; sourceTree = "<group>"; };
+		27908DD618A3F8CB002D41BD /* AtlasAttachmentLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AtlasAttachmentLoader.h; path = ../../../source/spine/AtlasAttachmentLoader.h; sourceTree = "<group>"; };
+		27908DD718A3F8CB002D41BD /* Attachment.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Attachment.c; path = ../../../source/spine/Attachment.c; sourceTree = "<group>"; };
+		27908DD818A3F8CB002D41BD /* Attachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Attachment.h; path = ../../../source/spine/Attachment.h; sourceTree = "<group>"; };
+		27908DD918A3F8CB002D41BD /* AttachmentLoader.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = AttachmentLoader.c; path = ../../../source/spine/AttachmentLoader.c; sourceTree = "<group>"; };
+		27908DDA18A3F8CB002D41BD /* AttachmentLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AttachmentLoader.h; path = ../../../source/spine/AttachmentLoader.h; sourceTree = "<group>"; };
+		27908DDB18A3F8CB002D41BD /* Bone.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Bone.c; path = ../../../source/spine/Bone.c; sourceTree = "<group>"; };
+		27908DDC18A3F8CB002D41BD /* Bone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Bone.h; path = ../../../source/spine/Bone.h; sourceTree = "<group>"; };
+		27908DDD18A3F8CB002D41BD /* BoneData.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = BoneData.c; path = ../../../source/spine/BoneData.c; sourceTree = "<group>"; };
+		27908DDE18A3F8CB002D41BD /* BoneData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BoneData.h; path = ../../../source/spine/BoneData.h; sourceTree = "<group>"; };
+		27908DDF18A3F8CB002D41BD /* BoundingBoxAttachment.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = BoundingBoxAttachment.c; path = ../../../source/spine/BoundingBoxAttachment.c; sourceTree = "<group>"; };
+		27908DE018A3F8CB002D41BD /* BoundingBoxAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BoundingBoxAttachment.h; path = ../../../source/spine/BoundingBoxAttachment.h; sourceTree = "<group>"; };
+		27908DE118A3F8CB002D41BD /* Event.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Event.c; path = ../../../source/spine/Event.c; sourceTree = "<group>"; };
+		27908DE218A3F8CB002D41BD /* Event.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Event.h; path = ../../../source/spine/Event.h; sourceTree = "<group>"; };
+		27908DE318A3F8CB002D41BD /* EventData.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = EventData.c; path = ../../../source/spine/EventData.c; sourceTree = "<group>"; };
+		27908DE418A3F8CB002D41BD /* EventData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EventData.h; path = ../../../source/spine/EventData.h; sourceTree = "<group>"; };
+		27908DE518A3F8CB002D41BD /* extension.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = extension.c; path = ../../../source/spine/extension.c; sourceTree = "<group>"; };
+		27908DE618A3F8CB002D41BD /* extension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = extension.h; path = ../../../source/spine/extension.h; sourceTree = "<group>"; };
+		27908DE718A3F8CB002D41BD /* Json.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Json.c; path = ../../../source/spine/Json.c; sourceTree = "<group>"; };
+		27908DE818A3F8CB002D41BD /* Json.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Json.h; path = ../../../source/spine/Json.h; sourceTree = "<group>"; };
+		27908DE918A3F8CB002D41BD /* RegionAttachment.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = RegionAttachment.c; path = ../../../source/spine/RegionAttachment.c; sourceTree = "<group>"; };
+		27908DEA18A3F8CB002D41BD /* RegionAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegionAttachment.h; path = ../../../source/spine/RegionAttachment.h; sourceTree = "<group>"; };
+		27908DEB18A3F8CB002D41BD /* Skeleton.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Skeleton.c; path = ../../../source/spine/Skeleton.c; sourceTree = "<group>"; };
+		27908DEC18A3F8CB002D41BD /* Skeleton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Skeleton.h; path = ../../../source/spine/Skeleton.h; sourceTree = "<group>"; };
+		27908DED18A3F8CB002D41BD /* SkeletonBounds.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SkeletonBounds.c; path = ../../../source/spine/SkeletonBounds.c; sourceTree = "<group>"; };
+		27908DEE18A3F8CB002D41BD /* SkeletonBounds.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkeletonBounds.h; path = ../../../source/spine/SkeletonBounds.h; sourceTree = "<group>"; };
+		27908DEF18A3F8CB002D41BD /* SkeletonData.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SkeletonData.c; path = ../../../source/spine/SkeletonData.c; sourceTree = "<group>"; };
+		27908DF018A3F8CB002D41BD /* SkeletonData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkeletonData.h; path = ../../../source/spine/SkeletonData.h; sourceTree = "<group>"; };
+		27908DF118A3F8CB002D41BD /* SkeletonJson.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SkeletonJson.c; path = ../../../source/spine/SkeletonJson.c; sourceTree = "<group>"; };
+		27908DF218A3F8CB002D41BD /* SkeletonJson.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkeletonJson.h; path = ../../../source/spine/SkeletonJson.h; sourceTree = "<group>"; };
+		27908DF318A3F8CB002D41BD /* Skin.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Skin.c; path = ../../../source/spine/Skin.c; sourceTree = "<group>"; };
+		27908DF418A3F8CB002D41BD /* Skin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Skin.h; path = ../../../source/spine/Skin.h; sourceTree = "<group>"; };
+		27908DF518A3F8CB002D41BD /* Slot.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Slot.c; path = ../../../source/spine/Slot.c; sourceTree = "<group>"; };
+		27908DF618A3F8CB002D41BD /* Slot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Slot.h; path = ../../../source/spine/Slot.h; sourceTree = "<group>"; };
+		27908DF718A3F8CB002D41BD /* SlotData.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SlotData.c; path = ../../../source/spine/SlotData.c; sourceTree = "<group>"; };
+		27908DF818A3F8CB002D41BD /* SlotData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SlotData.h; path = ../../../source/spine/SlotData.h; sourceTree = "<group>"; };
+		27908DF918A3F8CB002D41BD /* spine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = spine.h; path = ../../../source/spine/spine.h; sourceTree = "<group>"; };
+		27908E1018A3F904002D41BD /* SkeletonAsset_ScriptBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkeletonAsset_ScriptBinding.h; sourceTree = "<group>"; };
+		27908E1118A3F904002D41BD /* SkeletonAsset.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkeletonAsset.cc; sourceTree = "<group>"; };
+		27908E1218A3F904002D41BD /* SkeletonAsset.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkeletonAsset.h; sourceTree = "<group>"; };
+		27908E1418A3F91F002D41BD /* Skeleton_ScriptBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Skeleton_ScriptBinding.h; sourceTree = "<group>"; };
+		27908E1518A3F91F002D41BD /* Skeleton.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Skeleton.cc; sourceTree = "<group>"; };
+		27908E1618A3F91F002D41BD /* Skeleton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Skeleton.h; sourceTree = "<group>"; };
 		2797C9E117F4E12500625B51 /* eaxtypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = eaxtypes.h; sourceTree = "<group>"; };
 		2A03300B165D1D2100E9CD70 /* unitTesting.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = unitTesting.cc; path = ../../../source/testing/unitTesting.cc; sourceTree = "<group>"; };
 		2A03300C165D1D2100E9CD70 /* unitTesting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = unitTesting.h; path = ../../../source/testing/unitTesting.h; sourceTree = "<group>"; };
@@ -1477,6 +1551,58 @@
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
+		27908DCC18A3F895002D41BD /* spine */ = {
+			isa = PBXGroup;
+			children = (
+				27908DCD18A3F8CB002D41BD /* Animation.c */,
+				27908DCE18A3F8CB002D41BD /* Animation.h */,
+				27908DCF18A3F8CB002D41BD /* AnimationState.c */,
+				27908DD018A3F8CB002D41BD /* AnimationState.h */,
+				27908DD118A3F8CB002D41BD /* AnimationStateData.c */,
+				27908DD218A3F8CB002D41BD /* AnimationStateData.h */,
+				27908DD318A3F8CB002D41BD /* Atlas.c */,
+				27908DD418A3F8CB002D41BD /* Atlas.h */,
+				27908DD518A3F8CB002D41BD /* AtlasAttachmentLoader.c */,
+				27908DD618A3F8CB002D41BD /* AtlasAttachmentLoader.h */,
+				27908DD718A3F8CB002D41BD /* Attachment.c */,
+				27908DD818A3F8CB002D41BD /* Attachment.h */,
+				27908DD918A3F8CB002D41BD /* AttachmentLoader.c */,
+				27908DDA18A3F8CB002D41BD /* AttachmentLoader.h */,
+				27908DDB18A3F8CB002D41BD /* Bone.c */,
+				27908DDC18A3F8CB002D41BD /* Bone.h */,
+				27908DDD18A3F8CB002D41BD /* BoneData.c */,
+				27908DDE18A3F8CB002D41BD /* BoneData.h */,
+				27908DDF18A3F8CB002D41BD /* BoundingBoxAttachment.c */,
+				27908DE018A3F8CB002D41BD /* BoundingBoxAttachment.h */,
+				27908DE118A3F8CB002D41BD /* Event.c */,
+				27908DE218A3F8CB002D41BD /* Event.h */,
+				27908DE318A3F8CB002D41BD /* EventData.c */,
+				27908DE418A3F8CB002D41BD /* EventData.h */,
+				27908DE518A3F8CB002D41BD /* extension.c */,
+				27908DE618A3F8CB002D41BD /* extension.h */,
+				27908DE718A3F8CB002D41BD /* Json.c */,
+				27908DE818A3F8CB002D41BD /* Json.h */,
+				27908DE918A3F8CB002D41BD /* RegionAttachment.c */,
+				27908DEA18A3F8CB002D41BD /* RegionAttachment.h */,
+				27908DEB18A3F8CB002D41BD /* Skeleton.c */,
+				27908DEC18A3F8CB002D41BD /* Skeleton.h */,
+				27908DED18A3F8CB002D41BD /* SkeletonBounds.c */,
+				27908DEE18A3F8CB002D41BD /* SkeletonBounds.h */,
+				27908DEF18A3F8CB002D41BD /* SkeletonData.c */,
+				27908DF018A3F8CB002D41BD /* SkeletonData.h */,
+				27908DF118A3F8CB002D41BD /* SkeletonJson.c */,
+				27908DF218A3F8CB002D41BD /* SkeletonJson.h */,
+				27908DF318A3F8CB002D41BD /* Skin.c */,
+				27908DF418A3F8CB002D41BD /* Skin.h */,
+				27908DF518A3F8CB002D41BD /* Slot.c */,
+				27908DF618A3F8CB002D41BD /* Slot.h */,
+				27908DF718A3F8CB002D41BD /* SlotData.c */,
+				27908DF818A3F8CB002D41BD /* SlotData.h */,
+				27908DF918A3F8CB002D41BD /* spine.h */,
+			);
+			name = spine;
+			sourceTree = "<group>";
+		};
 		2A033005165D1CB100E9CD70 /* testing */ = {
 			isa = PBXGroup;
 			children = (
@@ -1941,7 +2067,6 @@
 		869FF8AD1651518C002FE082 = {
 			isa = PBXGroup;
 			children = (
-				04CC626B1795512500F9C119 /* AVFoundation.framework */,
 				8652F29F16C146CF00639EFE /* torque2d.icns */,
 				869FF8C21651518C002FE082 /* Torque2D */,
 				865A2351165188AF00527C44 /* platformOSX */,
@@ -1981,7 +2106,6 @@
 		869FF8C21651518C002FE082 /* Torque2D */ = {
 			isa = PBXGroup;
 			children = (
-				86BC815516518D4600D96ADF /* torqueConfig.h */,
 				86BC7E7516518D4600D96ADF /* 2d */,
 				86BC7EE016518D4600D96ADF /* algorithm */,
 				86BC7EE716518D4600D96ADF /* assets */,
@@ -2004,8 +2128,10 @@
 				86BC80EF16518D4600D96ADF /* persistence */,
 				86BC82ED16518F6800D96ADF /* platform */,
 				86BC812C16518D4600D96ADF /* sim */,
+				27908DCC18A3F895002D41BD /* spine */,
 				86BC814816518D4600D96ADF /* string */,
 				2A033005165D1CB100E9CD70 /* testing */,
+				86BC815516518D4600D96ADF /* torqueConfig.h */,
 			);
 			path = Torque2D;
 			sourceTree = "<group>";
@@ -2025,10 +2151,10 @@
 		86BC7E7516518D4600D96ADF /* 2d */ = {
 			isa = PBXGroup;
 			children = (
-				2AE2938016EF4BFA0015E200 /* experimental */,
-				2AB4F1CF16D55B7300C9A27B /* controllers */,
 				86BC7E7616518D4600D96ADF /* assets */,
+				2AB4F1CF16D55B7300C9A27B /* controllers */,
 				86BC7E8016518D4600D96ADF /* core */,
+				2AE2938016EF4BFA0015E200 /* experimental */,
 				86BC7E9616518D4600D96ADF /* gui */,
 				86BC7EA216518D4600D96ADF /* scene */,
 				86BC7EB716518D4600D96ADF /* sceneobject */,
@@ -2040,6 +2166,9 @@
 		86BC7E7616518D4600D96ADF /* assets */ = {
 			isa = PBXGroup;
 			children = (
+				27908E1018A3F904002D41BD /* SkeletonAsset_ScriptBinding.h */,
+				27908E1118A3F904002D41BD /* SkeletonAsset.cc */,
+				27908E1218A3F904002D41BD /* SkeletonAsset.h */,
 				2AF80CFF16A80CB400CE13F1 /* ParticleAssetEmitter_ScriptBinding.h */,
 				2AE5B54016A6D860006908D5 /* ParticleAssetFieldCollection.cc */,
 				2AE5B54116A6D860006908D5 /* ParticleAssetFieldCollection.h */,
@@ -2144,31 +2273,34 @@
 		86BC7EB716518D4600D96ADF /* sceneobject */ = {
 			isa = PBXGroup;
 			children = (
-				2AA6865A16D69943003CEF0A /* SceneObjectList.cc */,
-				2AA6865B16D69943003CEF0A /* SceneObjectList.h */,
-				2AA6865C16D69943003CEF0A /* SceneObjectSet_ScriptBinding.h */,
-				2AA6865D16D69943003CEF0A /* SceneObjectSet.cc */,
-				2AA6865E16D69943003CEF0A /* SceneObjectSet.h */,
-				2AC4404216B0142B00FC4091 /* ImageFont_ScriptBinding.h */,
+				86BC7EBB16518D4600D96ADF /* CompositeSprite.cc */,
+				86BC7EBC16518D4600D96ADF /* CompositeSprite.h */,
+				86BC7EBD16518D4600D96ADF /* CompositeSprite_ScriptBinding.h */,
 				2AC4404316B0142B00FC4091 /* ImageFont.cc */,
 				2AC4404416B0142B00FC4091 /* ImageFont.h */,
-				2A25738D16A48DAC00363C6F /* ParticlePlayer_ScriptBinding.h */,
+				2AC4404216B0142B00FC4091 /* ImageFont_ScriptBinding.h */,
 				2A25738E16A48DAC00363C6F /* ParticlePlayer.cc */,
 				2A25738F16A48DAC00363C6F /* ParticlePlayer.h */,
-				86BC7EBB16518D4600D96ADF /* CompositeSprite.cc */,
-				86BC7EBC16518D4600D96ADF /* CompositeSprite.h */,
-				86BC7EBD16518D4600D96ADF /* CompositeSprite_ScriptBinding.h */,
+				2A25738D16A48DAC00363C6F /* ParticlePlayer_ScriptBinding.h */,
 				86BC7EC316518D4600D96ADF /* SceneObject.cc */,
 				86BC7EC416518D4600D96ADF /* SceneObject.h */,
 				86BC7EC516518D4600D96ADF /* SceneObject_ScriptBinding.h */,
+				2AA6865A16D69943003CEF0A /* SceneObjectList.cc */,
+				2AA6865B16D69943003CEF0A /* SceneObjectList.h */,
 				86BC7EC816518D4600D96ADF /* SceneObjectMoveToEvent.h */,
 				86BC7EC916518D4600D96ADF /* SceneObjectRotateToEvent.h */,
+				2AA6865D16D69943003CEF0A /* SceneObjectSet.cc */,
+				2AA6865E16D69943003CEF0A /* SceneObjectSet.h */,
+				2AA6865C16D69943003CEF0A /* SceneObjectSet_ScriptBinding.h */,
 				86BC7ECE16518D4600D96ADF /* Scroller.cc */,
 				86BC7ECF16518D4600D96ADF /* Scroller.h */,
 				86BC7ED016518D4600D96ADF /* Scroller_ScriptBinding.h */,
 				86BC7ED116518D4600D96ADF /* ShapeVector.cc */,
 				86BC7ED216518D4600D96ADF /* ShapeVector.h */,
 				86BC7ED316518D4600D96ADF /* ShapeVector_ScriptBinding.h */,
+				27908E1518A3F91F002D41BD /* Skeleton.cc */,
+				27908E1618A3F91F002D41BD /* Skeleton.h */,
+				27908E1418A3F91F002D41BD /* Skeleton_ScriptBinding.h */,
 				86BC7ED416518D4600D96ADF /* Sprite.cc */,
 				86BC7ED516518D4600D96ADF /* Sprite.h */,
 				86BC7ED616518D4600D96ADF /* Sprite_ScriptBinding.h */,
@@ -3175,6 +3307,7 @@
 				86D7707F1656873C0046D71F /* SimXMLDocument.cpp in Sources */,
 				86D770801656873C0046D71F /* taml.cc in Sources */,
 				86D770841656873C0046D71F /* tamlWriteNode.cc in Sources */,
+				27908E0618A3F8CB002D41BD /* extension.c in Sources */,
 				86D770881656873C0046D71F /* tinystr.cpp in Sources */,
 				86D770891656873C0046D71F /* tinyxml.cpp in Sources */,
 				86D7708A1656873C0046D71F /* tinyxmlerror.cpp in Sources */,
@@ -3182,6 +3315,7 @@
 				86D7708D1656873C0046D71F /* CursorManager.cc in Sources */,
 				86D7708E1656873C0046D71F /* platform.cc in Sources */,
 				86D7708F1656873C0046D71F /* platformAssert.cc in Sources */,
+				27908E1318A3F904002D41BD /* SkeletonAsset.cc in Sources */,
 				86D770901656873C0046D71F /* platformCPU.cc in Sources */,
 				86D770911656873C0046D71F /* platformFileIO.cc in Sources */,
 				86D770921656873C0046D71F /* platformFont.cc in Sources */,
@@ -3191,6 +3325,7 @@
 				86D770961656873C0046D71F /* platformVideo.cc in Sources */,
 				86D770971656873C0046D71F /* Tickable.cc in Sources */,
 				86D770981656873C0046D71F /* popupMenu.cc in Sources */,
+				27908DFB18A3F8CB002D41BD /* AnimationState.c in Sources */,
 				86D770991656873C0046D71F /* msgBox.cpp in Sources */,
 				86D770AA1656873C0046D71F /* scriptGroup.cc in Sources */,
 				86D770AB1656873C0046D71F /* scriptObject.cc in Sources */,
@@ -3198,6 +3333,7 @@
 				86D770AD1656873C0046D71F /* simConsoleEvent.cc in Sources */,
 				86D770AE1656873C0046D71F /* simConsoleThreadExecEvent.cc in Sources */,
 				86D770AF1656873C0046D71F /* simDatablock.cc in Sources */,
+				27908DFF18A3F8CB002D41BD /* Attachment.c in Sources */,
 				86D770B01656873C0046D71F /* simDictionary.cc in Sources */,
 				86D770B11656873C0046D71F /* simFieldDictionary.cc in Sources */,
 				86D770B21656873C0046D71F /* simManager.cc in Sources */,
@@ -3221,7 +3357,9 @@
 				86D77046165687220046D71F /* memStream.cc in Sources */,
 				86D77047165687220046D71F /* nStream.cc in Sources */,
 				86D77048165687220046D71F /* resizeStream.cc in Sources */,
+				27908E1718A3F91F002D41BD /* Skeleton.cc in Sources */,
 				86D77049165687220046D71F /* resourceDictionary.cc in Sources */,
+				27908E0018A3F8CB002D41BD /* AttachmentLoader.c in Sources */,
 				86D7704A165687220046D71F /* resourceManager.cc in Sources */,
 				86D7704B165687220046D71F /* streamObject.cc in Sources */,
 				86D7704C165687220046D71F /* centralDir.cc in Sources */,
@@ -3248,6 +3386,7 @@
 				86D76FBF165687060046D71F /* consoleNamespace.cc in Sources */,
 				86D76FC0165687060046D71F /* consoleBaseType.cc in Sources */,
 				86D76FC1165687060046D71F /* ConsoleTypeValidators.cc in Sources */,
+				27908E0518A3F8CB002D41BD /* EventData.c in Sources */,
 				86D76FC2165687060046D71F /* Package.cc in Sources */,
 				86D76FC3165687060046D71F /* astAlloc.cc in Sources */,
 				86D76FC4165687060046D71F /* astNodes.cc in Sources */,
@@ -3264,6 +3403,7 @@
 				86D76FCF165687060046D71F /* consoleParser.cc in Sources */,
 				86D76FD0165687060046D71F /* consoleTypes.cc in Sources */,
 				86D76FD1165687060046D71F /* profiler.cc in Sources */,
+				27908E0A18A3F8CB002D41BD /* SkeletonBounds.c in Sources */,
 				86D76FD2165687060046D71F /* RemoteDebugger1.cc in Sources */,
 				86D76FD3165687060046D71F /* RemoteDebuggerBase.cc in Sources */,
 				86D76FD4165687060046D71F /* RemoteDebuggerBridge.cc in Sources */,
@@ -3298,7 +3438,9 @@
 				86D77006165687060046D71F /* guiAutoScrollCtrl.cc in Sources */,
 				86D77007165687060046D71F /* guiCtrlArrayCtrl.cc in Sources */,
 				86D77008165687060046D71F /* guiDragAndDropCtrl.cc in Sources */,
+				27908E0418A3F8CB002D41BD /* Event.c in Sources */,
 				86D77009165687060046D71F /* guiDynamicCtrlArrayCtrl.cc in Sources */,
+				27908DFA18A3F8CB002D41BD /* Animation.c in Sources */,
 				86D7700A165687060046D71F /* guiFormCtrl.cc in Sources */,
 				86D7700B165687060046D71F /* guiFrameCtrl.cc in Sources */,
 				86D7700C165687060046D71F /* guiPaneCtrl.cc in Sources */,
@@ -3308,6 +3450,7 @@
 				86D77010165687060046D71F /* guiTabBookCtrl.cc in Sources */,
 				86D77011165687060046D71F /* guiWindowCtrl.cc in Sources */,
 				86D77012165687060046D71F /* guiControlListPopup.cc in Sources */,
+				27908E0F18A3F8CB002D41BD /* SlotData.c in Sources */,
 				86D77013165687060046D71F /* guiDebugger.cc in Sources */,
 				86D77014165687060046D71F /* guiEditCtrl.cc in Sources */,
 				86D77015165687060046D71F /* guiFilterCtrl.cc in Sources */,
@@ -3321,6 +3464,7 @@
 				86D7701D165687060046D71F /* guiBackgroundCtrl.cc in Sources */,
 				86D7701E165687060046D71F /* guiBitmapBorderCtrl.cc in Sources */,
 				86D7701F165687060046D71F /* guiBitmapCtrl.cc in Sources */,
+				27908E0E18A3F8CB002D41BD /* Slot.c in Sources */,
 				86D77020165687060046D71F /* guiBubbleTextCtrl.cc in Sources */,
 				86D77021165687060046D71F /* guiCanvas.cc in Sources */,
 				86D77022165687060046D71F /* guiColorPicker.cc in Sources */,
@@ -3356,6 +3500,8 @@
 				86D76F9C165686D80046D71F /* assetFieldTypes.cc in Sources */,
 				86D76F9D165686D80046D71F /* assetManager.cc in Sources */,
 				86D76F9F165686D80046D71F /* assetQuery.cc in Sources */,
+				27908E0918A3F8CB002D41BD /* Skeleton.c in Sources */,
+				27908E0118A3F8CB002D41BD /* Bone.c in Sources */,
 				86D76FA1165686D80046D71F /* assetTagsManifest.cc in Sources */,
 				86D76FA2165686D80046D71F /* audio.cc in Sources */,
 				86D76FA3165686D80046D71F /* AudioAsset.cc in Sources */,
@@ -3423,6 +3569,7 @@
 				865A2293165187B600527C44 /* b2ChainAndCircleContact.cpp in Sources */,
 				865A2294165187B600527C44 /* b2ChainAndPolygonContact.cpp in Sources */,
 				865A2295165187B600527C44 /* b2CircleContact.cpp in Sources */,
+				27908E0D18A3F8CB002D41BD /* Skin.c in Sources */,
 				865A2296165187B600527C44 /* b2Contact.cpp in Sources */,
 				865A2297165187B600527C44 /* b2ContactSolver.cpp in Sources */,
 				865A2298165187B600527C44 /* b2EdgeAndCircleContact.cpp in Sources */,
@@ -3430,6 +3577,7 @@
 				865A229A165187B600527C44 /* b2PolygonAndCircleContact.cpp in Sources */,
 				865A229B165187B600527C44 /* b2PolygonContact.cpp in Sources */,
 				865A229C165187B600527C44 /* b2DistanceJoint.cpp in Sources */,
+				27908DFD18A3F8CB002D41BD /* Atlas.c in Sources */,
 				865A229D165187B600527C44 /* b2FrictionJoint.cpp in Sources */,
 				865A229E165187B600527C44 /* b2GearJoint.cpp in Sources */,
 				865A229F165187B600527C44 /* b2Joint.cpp in Sources */,
@@ -3438,6 +3586,7 @@
 				865A22A2165187B600527C44 /* b2PrismaticJoint.cpp in Sources */,
 				865A22A3165187B600527C44 /* b2PulleyJoint.cpp in Sources */,
 				865A22A4165187B600527C44 /* b2RevoluteJoint.cpp in Sources */,
+				27908DFE18A3F8CB002D41BD /* AtlasAttachmentLoader.c in Sources */,
 				865A22A5165187B600527C44 /* b2RopeJoint.cpp in Sources */,
 				865A22A6165187B600527C44 /* b2WeldJoint.cpp in Sources */,
 				865A22A7165187B600527C44 /* b2WheelJoint.cpp in Sources */,
@@ -3457,6 +3606,7 @@
 				865A2311165187FF00527C44 /* jcphuff.c in Sources */,
 				865A2312165187FF00527C44 /* jcprepct.c in Sources */,
 				865A2313165187FF00527C44 /* jcsample.c in Sources */,
+				27908E0818A3F8CB002D41BD /* RegionAttachment.c in Sources */,
 				865A2314165187FF00527C44 /* jctrans.c in Sources */,
 				865A2315165187FF00527C44 /* jdapimin.c in Sources */,
 				865A2316165187FF00527C44 /* jdapistd.c in Sources */,
@@ -3475,6 +3625,7 @@
 				865A2323165187FF00527C44 /* jdpostct.c in Sources */,
 				865A2324165187FF00527C44 /* jdsample.c in Sources */,
 				865A2325165187FF00527C44 /* jdtrans.c in Sources */,
+				27908DFC18A3F8CB002D41BD /* AnimationStateData.c in Sources */,
 				865A2326165187FF00527C44 /* jerror.c in Sources */,
 				865A2327165187FF00527C44 /* jfdctflt.c in Sources */,
 				865A2328165187FF00527C44 /* jfdctfst.c in Sources */,
@@ -3487,9 +3638,11 @@
 				865A232F165187FF00527C44 /* jmemmgr.c in Sources */,
 				865A2330165187FF00527C44 /* jquant1.c in Sources */,
 				865A2331165187FF00527C44 /* jquant2.c in Sources */,
+				27908E0218A3F8CB002D41BD /* BoneData.c in Sources */,
 				865A2332165187FF00527C44 /* jutils.c in Sources */,
 				865A23421651881300527C44 /* png.c in Sources */,
 				865A23431651881300527C44 /* pngerror.c in Sources */,
+				27908E0718A3F8CB002D41BD /* Json.c in Sources */,
 				865A23441651881300527C44 /* pngget.c in Sources */,
 				865A23451651881300527C44 /* pngmem.c in Sources */,
 				865A23461651881300527C44 /* pngpread.c in Sources */,
@@ -3507,6 +3660,7 @@
 				865A235B16518AD300527C44 /* main.mm in Sources */,
 				86063A251654180000362D83 /* platformOSX.mm in Sources */,
 				866381D8165550FF00C8C551 /* osxFileIO.mm in Sources */,
+				27908E0B18A3F8CB002D41BD /* SkeletonData.c in Sources */,
 				866381DA1655562400C8C551 /* osxMemory.mm in Sources */,
 				866381DC165556AD00C8C551 /* osxMath.mm in Sources */,
 				866381E51655615200C8C551 /* osxInput.mm in Sources */,
@@ -3521,9 +3675,11 @@
 				8694ADC81656B06B0080ABAC /* osxEvents.mm in Sources */,
 				8694ADC91656B06B0080ABAC /* osxWindow.mm in Sources */,
 				8694ADD11656B7FC0080ABAC /* osxVideo.mm in Sources */,
+				27908E0C18A3F8CB002D41BD /* SkeletonJson.c in Sources */,
 				8694ADD51656BDE60080ABAC /* osxGL.mm in Sources */,
 				8658B174165A7BFB0087ABC1 /* osxCPU.mm in Sources */,
 				8658B175165A7BFB0087ABC1 /* osxNetwork.mm in Sources */,
+				27908E0318A3F8CB002D41BD /* BoundingBoxAttachment.c in Sources */,
 				8658B176165A7BFB0087ABC1 /* osxString.mm in Sources */,
 				86EC5AC7165C1E0100757872 /* osxTorqueView.mm in Sources */,
 				2A03300D165D1D2100E9CD70 /* unitTesting.cc in Sources */,

+ 158 - 0
engine/compilers/Xcode_iOS/Torque2D.xcodeproj/project.pbxproj

@@ -7,6 +7,30 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		27908E1B18A3FA9C002D41BD /* SkeletonAsset.cc in Sources */ = {isa = PBXBuildFile; fileRef = 27908E1918A3FA9C002D41BD /* SkeletonAsset.cc */; };
+		27908E1F18A3FAB1002D41BD /* Skeleton.cc in Sources */ = {isa = PBXBuildFile; fileRef = 27908E1D18A3FAB1002D41BD /* Skeleton.cc */; };
+		27908E4E18A3FAE1002D41BD /* Animation.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908E2118A3FAE1002D41BD /* Animation.c */; };
+		27908E4F18A3FAE1002D41BD /* AnimationState.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908E2318A3FAE1002D41BD /* AnimationState.c */; };
+		27908E5018A3FAE1002D41BD /* AnimationStateData.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908E2518A3FAE1002D41BD /* AnimationStateData.c */; };
+		27908E5118A3FAE1002D41BD /* Atlas.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908E2718A3FAE1002D41BD /* Atlas.c */; };
+		27908E5218A3FAE1002D41BD /* AtlasAttachmentLoader.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908E2918A3FAE1002D41BD /* AtlasAttachmentLoader.c */; };
+		27908E5318A3FAE1002D41BD /* Attachment.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908E2B18A3FAE1002D41BD /* Attachment.c */; };
+		27908E5418A3FAE1002D41BD /* AttachmentLoader.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908E2D18A3FAE1002D41BD /* AttachmentLoader.c */; };
+		27908E5518A3FAE1002D41BD /* Bone.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908E2F18A3FAE1002D41BD /* Bone.c */; };
+		27908E5618A3FAE1002D41BD /* BoneData.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908E3118A3FAE1002D41BD /* BoneData.c */; };
+		27908E5718A3FAE1002D41BD /* BoundingBoxAttachment.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908E3318A3FAE1002D41BD /* BoundingBoxAttachment.c */; };
+		27908E5818A3FAE1002D41BD /* Event.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908E3518A3FAE1002D41BD /* Event.c */; };
+		27908E5918A3FAE1002D41BD /* EventData.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908E3718A3FAE1002D41BD /* EventData.c */; };
+		27908E5A18A3FAE1002D41BD /* extension.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908E3918A3FAE1002D41BD /* extension.c */; };
+		27908E5B18A3FAE1002D41BD /* Json.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908E3B18A3FAE1002D41BD /* Json.c */; };
+		27908E5C18A3FAE1002D41BD /* RegionAttachment.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908E3D18A3FAE1002D41BD /* RegionAttachment.c */; };
+		27908E5D18A3FAE1002D41BD /* Skeleton.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908E3F18A3FAE1002D41BD /* Skeleton.c */; };
+		27908E5E18A3FAE1002D41BD /* SkeletonBounds.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908E4118A3FAE1002D41BD /* SkeletonBounds.c */; };
+		27908E5F18A3FAE1002D41BD /* SkeletonData.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908E4318A3FAE1002D41BD /* SkeletonData.c */; };
+		27908E6018A3FAE1002D41BD /* SkeletonJson.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908E4518A3FAE1002D41BD /* SkeletonJson.c */; };
+		27908E6118A3FAE1002D41BD /* Skin.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908E4718A3FAE1002D41BD /* Skin.c */; };
+		27908E6218A3FAE1002D41BD /* Slot.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908E4918A3FAE1002D41BD /* Slot.c */; };
+		27908E6318A3FAE1002D41BD /* SlotData.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908E4B18A3FAE1002D41BD /* SlotData.c */; };
 		2AA3655F16F3553E00E7A900 /* ImageFrameProvider.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2AA3655B16F3553E00E7A900 /* ImageFrameProvider.cc */; };
 		2AA3656016F3553E00E7A900 /* ImageFrameProviderCore.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2AA3655D16F3553E00E7A900 /* ImageFrameProviderCore.cc */; };
 		2AA6866A16D69968003CEF0A /* SceneObjectList.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2AA6866516D69968003CEF0A /* SceneObjectList.cc */; };
@@ -487,6 +511,57 @@
 /* End PBXBuildFile section */
 
 /* Begin PBXFileReference section */
+		27908E1818A3FA9C002D41BD /* SkeletonAsset_ScriptBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkeletonAsset_ScriptBinding.h; sourceTree = "<group>"; };
+		27908E1918A3FA9C002D41BD /* SkeletonAsset.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkeletonAsset.cc; sourceTree = "<group>"; };
+		27908E1A18A3FA9C002D41BD /* SkeletonAsset.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkeletonAsset.h; sourceTree = "<group>"; };
+		27908E1C18A3FAB1002D41BD /* Skeleton_ScriptBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Skeleton_ScriptBinding.h; sourceTree = "<group>"; };
+		27908E1D18A3FAB1002D41BD /* Skeleton.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Skeleton.cc; sourceTree = "<group>"; };
+		27908E1E18A3FAB1002D41BD /* Skeleton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Skeleton.h; sourceTree = "<group>"; };
+		27908E2118A3FAE1002D41BD /* Animation.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Animation.c; path = ../../../source/spine/Animation.c; sourceTree = "<group>"; };
+		27908E2218A3FAE1002D41BD /* Animation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Animation.h; path = ../../../source/spine/Animation.h; sourceTree = "<group>"; };
+		27908E2318A3FAE1002D41BD /* AnimationState.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = AnimationState.c; path = ../../../source/spine/AnimationState.c; sourceTree = "<group>"; };
+		27908E2418A3FAE1002D41BD /* AnimationState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AnimationState.h; path = ../../../source/spine/AnimationState.h; sourceTree = "<group>"; };
+		27908E2518A3FAE1002D41BD /* AnimationStateData.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = AnimationStateData.c; path = ../../../source/spine/AnimationStateData.c; sourceTree = "<group>"; };
+		27908E2618A3FAE1002D41BD /* AnimationStateData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AnimationStateData.h; path = ../../../source/spine/AnimationStateData.h; sourceTree = "<group>"; };
+		27908E2718A3FAE1002D41BD /* Atlas.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Atlas.c; path = ../../../source/spine/Atlas.c; sourceTree = "<group>"; };
+		27908E2818A3FAE1002D41BD /* Atlas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Atlas.h; path = ../../../source/spine/Atlas.h; sourceTree = "<group>"; };
+		27908E2918A3FAE1002D41BD /* AtlasAttachmentLoader.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = AtlasAttachmentLoader.c; path = ../../../source/spine/AtlasAttachmentLoader.c; sourceTree = "<group>"; };
+		27908E2A18A3FAE1002D41BD /* AtlasAttachmentLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AtlasAttachmentLoader.h; path = ../../../source/spine/AtlasAttachmentLoader.h; sourceTree = "<group>"; };
+		27908E2B18A3FAE1002D41BD /* Attachment.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Attachment.c; path = ../../../source/spine/Attachment.c; sourceTree = "<group>"; };
+		27908E2C18A3FAE1002D41BD /* Attachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Attachment.h; path = ../../../source/spine/Attachment.h; sourceTree = "<group>"; };
+		27908E2D18A3FAE1002D41BD /* AttachmentLoader.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = AttachmentLoader.c; path = ../../../source/spine/AttachmentLoader.c; sourceTree = "<group>"; };
+		27908E2E18A3FAE1002D41BD /* AttachmentLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AttachmentLoader.h; path = ../../../source/spine/AttachmentLoader.h; sourceTree = "<group>"; };
+		27908E2F18A3FAE1002D41BD /* Bone.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Bone.c; path = ../../../source/spine/Bone.c; sourceTree = "<group>"; };
+		27908E3018A3FAE1002D41BD /* Bone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Bone.h; path = ../../../source/spine/Bone.h; sourceTree = "<group>"; };
+		27908E3118A3FAE1002D41BD /* BoneData.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = BoneData.c; path = ../../../source/spine/BoneData.c; sourceTree = "<group>"; };
+		27908E3218A3FAE1002D41BD /* BoneData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BoneData.h; path = ../../../source/spine/BoneData.h; sourceTree = "<group>"; };
+		27908E3318A3FAE1002D41BD /* BoundingBoxAttachment.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = BoundingBoxAttachment.c; path = ../../../source/spine/BoundingBoxAttachment.c; sourceTree = "<group>"; };
+		27908E3418A3FAE1002D41BD /* BoundingBoxAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BoundingBoxAttachment.h; path = ../../../source/spine/BoundingBoxAttachment.h; sourceTree = "<group>"; };
+		27908E3518A3FAE1002D41BD /* Event.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Event.c; path = ../../../source/spine/Event.c; sourceTree = "<group>"; };
+		27908E3618A3FAE1002D41BD /* Event.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Event.h; path = ../../../source/spine/Event.h; sourceTree = "<group>"; };
+		27908E3718A3FAE1002D41BD /* EventData.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = EventData.c; path = ../../../source/spine/EventData.c; sourceTree = "<group>"; };
+		27908E3818A3FAE1002D41BD /* EventData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EventData.h; path = ../../../source/spine/EventData.h; sourceTree = "<group>"; };
+		27908E3918A3FAE1002D41BD /* extension.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = extension.c; path = ../../../source/spine/extension.c; sourceTree = "<group>"; };
+		27908E3A18A3FAE1002D41BD /* extension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = extension.h; path = ../../../source/spine/extension.h; sourceTree = "<group>"; };
+		27908E3B18A3FAE1002D41BD /* Json.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Json.c; path = ../../../source/spine/Json.c; sourceTree = "<group>"; };
+		27908E3C18A3FAE1002D41BD /* Json.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Json.h; path = ../../../source/spine/Json.h; sourceTree = "<group>"; };
+		27908E3D18A3FAE1002D41BD /* RegionAttachment.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = RegionAttachment.c; path = ../../../source/spine/RegionAttachment.c; sourceTree = "<group>"; };
+		27908E3E18A3FAE1002D41BD /* RegionAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegionAttachment.h; path = ../../../source/spine/RegionAttachment.h; sourceTree = "<group>"; };
+		27908E3F18A3FAE1002D41BD /* Skeleton.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Skeleton.c; path = ../../../source/spine/Skeleton.c; sourceTree = "<group>"; };
+		27908E4018A3FAE1002D41BD /* Skeleton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Skeleton.h; path = ../../../source/spine/Skeleton.h; sourceTree = "<group>"; };
+		27908E4118A3FAE1002D41BD /* SkeletonBounds.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SkeletonBounds.c; path = ../../../source/spine/SkeletonBounds.c; sourceTree = "<group>"; };
+		27908E4218A3FAE1002D41BD /* SkeletonBounds.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkeletonBounds.h; path = ../../../source/spine/SkeletonBounds.h; sourceTree = "<group>"; };
+		27908E4318A3FAE1002D41BD /* SkeletonData.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SkeletonData.c; path = ../../../source/spine/SkeletonData.c; sourceTree = "<group>"; };
+		27908E4418A3FAE1002D41BD /* SkeletonData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkeletonData.h; path = ../../../source/spine/SkeletonData.h; sourceTree = "<group>"; };
+		27908E4518A3FAE1002D41BD /* SkeletonJson.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SkeletonJson.c; path = ../../../source/spine/SkeletonJson.c; sourceTree = "<group>"; };
+		27908E4618A3FAE1002D41BD /* SkeletonJson.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkeletonJson.h; path = ../../../source/spine/SkeletonJson.h; sourceTree = "<group>"; };
+		27908E4718A3FAE1002D41BD /* Skin.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Skin.c; path = ../../../source/spine/Skin.c; sourceTree = "<group>"; };
+		27908E4818A3FAE1002D41BD /* Skin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Skin.h; path = ../../../source/spine/Skin.h; sourceTree = "<group>"; };
+		27908E4918A3FAE1002D41BD /* Slot.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Slot.c; path = ../../../source/spine/Slot.c; sourceTree = "<group>"; };
+		27908E4A18A3FAE1002D41BD /* Slot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Slot.h; path = ../../../source/spine/Slot.h; sourceTree = "<group>"; };
+		27908E4B18A3FAE1002D41BD /* SlotData.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SlotData.c; path = ../../../source/spine/SlotData.c; sourceTree = "<group>"; };
+		27908E4C18A3FAE1002D41BD /* SlotData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SlotData.h; path = ../../../source/spine/SlotData.h; sourceTree = "<group>"; };
+		27908E4D18A3FAE1002D41BD /* spine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = spine.h; path = ../../../source/spine/spine.h; sourceTree = "<group>"; };
 		2AA3655B16F3553E00E7A900 /* ImageFrameProvider.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ImageFrameProvider.cc; sourceTree = "<group>"; };
 		2AA3655C16F3553E00E7A900 /* ImageFrameProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageFrameProvider.h; sourceTree = "<group>"; };
 		2AA3655D16F3553E00E7A900 /* ImageFrameProviderCore.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ImageFrameProviderCore.cc; sourceTree = "<group>"; };
@@ -1542,6 +1617,58 @@
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
+		27908E2018A3FABA002D41BD /* spine */ = {
+			isa = PBXGroup;
+			children = (
+				27908E2118A3FAE1002D41BD /* Animation.c */,
+				27908E2218A3FAE1002D41BD /* Animation.h */,
+				27908E2318A3FAE1002D41BD /* AnimationState.c */,
+				27908E2418A3FAE1002D41BD /* AnimationState.h */,
+				27908E2518A3FAE1002D41BD /* AnimationStateData.c */,
+				27908E2618A3FAE1002D41BD /* AnimationStateData.h */,
+				27908E2718A3FAE1002D41BD /* Atlas.c */,
+				27908E2818A3FAE1002D41BD /* Atlas.h */,
+				27908E2918A3FAE1002D41BD /* AtlasAttachmentLoader.c */,
+				27908E2A18A3FAE1002D41BD /* AtlasAttachmentLoader.h */,
+				27908E2B18A3FAE1002D41BD /* Attachment.c */,
+				27908E2C18A3FAE1002D41BD /* Attachment.h */,
+				27908E2D18A3FAE1002D41BD /* AttachmentLoader.c */,
+				27908E2E18A3FAE1002D41BD /* AttachmentLoader.h */,
+				27908E2F18A3FAE1002D41BD /* Bone.c */,
+				27908E3018A3FAE1002D41BD /* Bone.h */,
+				27908E3118A3FAE1002D41BD /* BoneData.c */,
+				27908E3218A3FAE1002D41BD /* BoneData.h */,
+				27908E3318A3FAE1002D41BD /* BoundingBoxAttachment.c */,
+				27908E3418A3FAE1002D41BD /* BoundingBoxAttachment.h */,
+				27908E3518A3FAE1002D41BD /* Event.c */,
+				27908E3618A3FAE1002D41BD /* Event.h */,
+				27908E3718A3FAE1002D41BD /* EventData.c */,
+				27908E3818A3FAE1002D41BD /* EventData.h */,
+				27908E3918A3FAE1002D41BD /* extension.c */,
+				27908E3A18A3FAE1002D41BD /* extension.h */,
+				27908E3B18A3FAE1002D41BD /* Json.c */,
+				27908E3C18A3FAE1002D41BD /* Json.h */,
+				27908E3D18A3FAE1002D41BD /* RegionAttachment.c */,
+				27908E3E18A3FAE1002D41BD /* RegionAttachment.h */,
+				27908E3F18A3FAE1002D41BD /* Skeleton.c */,
+				27908E4018A3FAE1002D41BD /* Skeleton.h */,
+				27908E4118A3FAE1002D41BD /* SkeletonBounds.c */,
+				27908E4218A3FAE1002D41BD /* SkeletonBounds.h */,
+				27908E4318A3FAE1002D41BD /* SkeletonData.c */,
+				27908E4418A3FAE1002D41BD /* SkeletonData.h */,
+				27908E4518A3FAE1002D41BD /* SkeletonJson.c */,
+				27908E4618A3FAE1002D41BD /* SkeletonJson.h */,
+				27908E4718A3FAE1002D41BD /* Skin.c */,
+				27908E4818A3FAE1002D41BD /* Skin.h */,
+				27908E4918A3FAE1002D41BD /* Slot.c */,
+				27908E4A18A3FAE1002D41BD /* Slot.h */,
+				27908E4B18A3FAE1002D41BD /* SlotData.c */,
+				27908E4C18A3FAE1002D41BD /* SlotData.h */,
+				27908E4D18A3FAE1002D41BD /* spine.h */,
+			);
+			name = spine;
+			sourceTree = "<group>";
+		};
 		2AB4C1A816DE9F5000B02479 /* core */ = {
 			isa = PBXGroup;
 			children = (
@@ -1752,6 +1879,9 @@
 		867BACF916AEC9050033868F /* assets */ = {
 			isa = PBXGroup;
 			children = (
+				27908E1818A3FA9C002D41BD /* SkeletonAsset_ScriptBinding.h */,
+				27908E1918A3FA9C002D41BD /* SkeletonAsset.cc */,
+				27908E1A18A3FA9C002D41BD /* SkeletonAsset.h */,
 				867BACFA16AEC9050033868F /* AnimationAsset.cc */,
 				867BACFB16AEC9050033868F /* AnimationAsset.h */,
 				867BACFC16AEC9050033868F /* AnimationAsset_ScriptBinding.h */,
@@ -1856,6 +1986,9 @@
 		867BAD4516AEC9050033868F /* sceneobject */ = {
 			isa = PBXGroup;
 			children = (
+				27908E1C18A3FAB1002D41BD /* Skeleton_ScriptBinding.h */,
+				27908E1D18A3FAB1002D41BD /* Skeleton.cc */,
+				27908E1E18A3FAB1002D41BD /* Skeleton.h */,
 				2AA6866516D69968003CEF0A /* SceneObjectList.cc */,
 				2AA6866616D69968003CEF0A /* SceneObjectList.h */,
 				2AA6866716D69968003CEF0A /* SceneObjectSet_ScriptBinding.h */,
@@ -3113,6 +3246,7 @@
 				867BAF5316AEC9050033868F /* persistence */,
 				867BAF7216AEC9050033868F /* platform */,
 				867BAFB516AEC9050033868F /* sim */,
+				27908E2018A3FABA002D41BD /* spine */,
 				867BAFD116AEC9050033868F /* string */,
 				867BAFDE16AEC9050033868F /* torqueConfig.h */,
 			);
@@ -3224,6 +3358,7 @@
 				867BACD416AEC8BB0033868F /* iOSAudio.mm in Sources */,
 				867BACD516AEC8BB0033868F /* iOSConsole.mm in Sources */,
 				867BACD616AEC8BB0033868F /* iOSCPUInfo.mm in Sources */,
+				27908E1B18A3FA9C002D41BD /* SkeletonAsset.cc in Sources */,
 				867BACD716AEC8BB0033868F /* iOSDialogs.mm in Sources */,
 				867BACD816AEC8BB0033868F /* iOSEvents.mm in Sources */,
 				867BACD916AEC8BB0033868F /* iOSFileio.mm in Sources */,
@@ -3275,6 +3410,7 @@
 				867BAFF516AEC9050033868F /* DebugDraw.cc in Sources */,
 				867BAFF616AEC9050033868F /* Scene.cc in Sources */,
 				867BAFF716AEC9050033868F /* SceneRenderFactories.cpp in Sources */,
+				27908E5618A3FAE1002D41BD /* BoneData.c in Sources */,
 				867BAFF816AEC9050033868F /* SceneRenderQueue.cpp in Sources */,
 				867BAFF916AEC9050033868F /* WorldQuery.cc in Sources */,
 				867BAFFB16AEC9050033868F /* CompositeSprite.cc in Sources */,
@@ -3282,6 +3418,7 @@
 				867BAFFE16AEC9050033868F /* SceneObject.cc in Sources */,
 				867BB00116AEC9050033868F /* Scroller.cc in Sources */,
 				867BB00216AEC9050033868F /* ShapeVector.cc in Sources */,
+				27908E5018A3FAE1002D41BD /* AnimationStateData.c in Sources */,
 				867BB00316AEC9050033868F /* Sprite.cc in Sources */,
 				867BB00416AEC9050033868F /* Trigger.cc in Sources */,
 				867BB00516AEC9050033868F /* crc.cc in Sources */,
@@ -3292,8 +3429,10 @@
 				867BB00B16AEC9050033868F /* assetQuery.cc in Sources */,
 				867BB00D16AEC9050033868F /* assetTagsManifest.cc in Sources */,
 				867BB00E16AEC9050033868F /* audio.cc in Sources */,
+				27908E5418A3FAE1002D41BD /* AttachmentLoader.c in Sources */,
 				867BB00F16AEC9050033868F /* AudioAsset.cc in Sources */,
 				867BB01016AEC9050033868F /* audioBuffer.cc in Sources */,
+				27908E6218A3FAE1002D41BD /* Slot.c in Sources */,
 				867BB01116AEC9050033868F /* audioDataBlock.cc in Sources */,
 				867BB01316AEC9050033868F /* audioStreamSourceFactory.cc in Sources */,
 				867BB01416AEC9050033868F /* wavStreamSource.cc in Sources */,
@@ -3332,6 +3471,7 @@
 				867BB04116AEC9050033868F /* RemoteDebuggerBase.cc in Sources */,
 				867BB04216AEC9050033868F /* RemoteDebuggerBridge.cc in Sources */,
 				867BB04316AEC9050033868F /* telnetDebugger.cc in Sources */,
+				27908E5A18A3FAE1002D41BD /* extension.c in Sources */,
 				867BB04416AEC9050033868F /* delegateSignal.cpp in Sources */,
 				867BB04516AEC9050033868F /* defaultGame.cc in Sources */,
 				867BB04616AEC9050033868F /* gameConnection.cc in Sources */,
@@ -3362,8 +3502,10 @@
 				867BB06016AEC9050033868F /* guiRadioCtrl.cc in Sources */,
 				867BB06116AEC9050033868F /* guiToolboxButtonCtrl.cc in Sources */,
 				867BB06216AEC9050033868F /* guiAutoScrollCtrl.cc in Sources */,
+				27908E6318A3FAE1002D41BD /* SlotData.c in Sources */,
 				867BB06316AEC9050033868F /* guiCtrlArrayCtrl.cc in Sources */,
 				867BB06416AEC9050033868F /* guiDragAndDropCtrl.cc in Sources */,
+				27908E5B18A3FAE1002D41BD /* Json.c in Sources */,
 				867BB06516AEC9050033868F /* guiDynamicCtrlArrayCtrl.cc in Sources */,
 				867BB06616AEC9050033868F /* guiFormCtrl.cc in Sources */,
 				867BB06716AEC9050033868F /* guiFrameCtrl.cc in Sources */,
@@ -3379,7 +3521,9 @@
 				867BB07116AEC9050033868F /* guiFilterCtrl.cc in Sources */,
 				867BB07316AEC9050033868F /* guiImageList.cc in Sources */,
 				867BB07416AEC9050033868F /* guiInspector.cc in Sources */,
+				27908E1F18A3FAB1002D41BD /* Skeleton.cc in Sources */,
 				867BB07516AEC9050033868F /* guiInspectorTypes.cc in Sources */,
+				27908E5F18A3FAE1002D41BD /* SkeletonData.c in Sources */,
 				867BB07616AEC9050033868F /* guiMenuBar.cc in Sources */,
 				867BB07716AEC9050033868F /* guiSeparatorCtrl.cc in Sources */,
 				867BB07816AEC9050033868F /* guiArrayCtrl.cc in Sources */,
@@ -3404,6 +3548,7 @@
 				867BB08C16AEC9050033868F /* guiPopUpCtrlEx.cc in Sources */,
 				867BB08D16AEC9050033868F /* guiProgressCtrl.cc in Sources */,
 				867BB08E16AEC9050033868F /* guiScriptNotifyControl.cc in Sources */,
+				27908E5918A3FAE1002D41BD /* EventData.c in Sources */,
 				867BB09016AEC9050033868F /* guiTabPageCtrl.cc in Sources */,
 				867BB09116AEC9050033868F /* guiTextCtrl.cc in Sources */,
 				867BB09216AEC9050033868F /* guiTextEditCtrl.cc in Sources */,
@@ -3429,6 +3574,7 @@
 				867BB0A716AEC9050033868F /* streamObject.cc in Sources */,
 				8698388618872BF500D370A0 /* mPoint.cpp in Sources */,
 				867BB0A816AEC9050033868F /* centralDir.cc in Sources */,
+				27908E5118A3FAE1002D41BD /* Atlas.c in Sources */,
 				867BB0A916AEC9050033868F /* compressor.cc in Sources */,
 				867BB0AA16AEC9050033868F /* deflate.cc in Sources */,
 				867BB0AB16AEC9050033868F /* extraField.cc in Sources */,
@@ -3437,6 +3583,7 @@
 				867BB0B116AEC9050033868F /* zipArchive.cc in Sources */,
 				867BB0B216AEC9050033868F /* zipCryptStream.cc in Sources */,
 				867BB0B316AEC9050033868F /* zipObject.cc in Sources */,
+				27908E5518A3FAE1002D41BD /* Bone.c in Sources */,
 				867BB0B416AEC9050033868F /* zipSubStream.cc in Sources */,
 				867BB0B516AEC9050033868F /* zipTempStream.cc in Sources */,
 				867BB0B616AEC9050033868F /* mathTypes.cc in Sources */,
@@ -3453,11 +3600,13 @@
 				867BB0C516AEC9050033868F /* mQuat.cc in Sources */,
 				867BB0C616AEC9050033868F /* mRandom.cc in Sources */,
 				867BB0C716AEC9050033868F /* mSolver.cc in Sources */,
+				27908E6118A3FAE1002D41BD /* Skin.c in Sources */,
 				867BB0C816AEC9050033868F /* mSplinePatch.cc in Sources */,
 				867BB0C916AEC9050033868F /* rectClipper.cpp in Sources */,
 				867BB0CA16AEC9050033868F /* dataChunker.cc in Sources */,
 				867BB0CC16AEC9050033868F /* dispatcher.cc in Sources */,
 				867BB0CD16AEC9050033868F /* eventManager.cc in Sources */,
+				27908E5218A3FAE1002D41BD /* AtlasAttachmentLoader.c in Sources */,
 				867BB0CE16AEC9050033868F /* message.cc in Sources */,
 				867BB0CF16AEC9050033868F /* messageForwarder.cc in Sources */,
 				867BB0D016AEC9050033868F /* scriptMsgListener.cc in Sources */,
@@ -3485,6 +3634,7 @@
 				867BB0E916AEC9050033868F /* tamlWriteNode.cc in Sources */,
 				867BB0ED16AEC9050033868F /* tinystr.cpp in Sources */,
 				867BB0EE16AEC9050033868F /* tinyxml.cpp in Sources */,
+				27908E5718A3FAE1002D41BD /* BoundingBoxAttachment.c in Sources */,
 				867BB0EF16AEC9050033868F /* tinyxmlerror.cpp in Sources */,
 				867BB0F016AEC9050033868F /* tinyxmlparser.cpp in Sources */,
 				867BB0F116AEC9050033868F /* CursorManager.cc in Sources */,
@@ -3518,6 +3668,7 @@
 				867BB10F16AEC9050033868F /* stringBuffer.cc in Sources */,
 				867BB11016AEC9050033868F /* stringStack.cc in Sources */,
 				867BB11116AEC9050033868F /* stringTable.cc in Sources */,
+				27908E5D18A3FAE1002D41BD /* Skeleton.c in Sources */,
 				867BB11216AEC9050033868F /* stringUnit.cpp in Sources */,
 				867BB11316AEC9050033868F /* unicode.cc in Sources */,
 				867BB1AF16AEC9FC0033868F /* b2BroadPhase.cpp in Sources */,
@@ -3539,12 +3690,14 @@
 				867BB1BF16AEC9FC0033868F /* b2StackAllocator.cpp in Sources */,
 				867BB1C016AEC9FC0033868F /* b2Timer.cpp in Sources */,
 				867BB1C116AEC9FC0033868F /* b2Body.cpp in Sources */,
+				27908E6018A3FAE1002D41BD /* SkeletonJson.c in Sources */,
 				867BB1C216AEC9FC0033868F /* b2ContactManager.cpp in Sources */,
 				867BB1C316AEC9FC0033868F /* b2Fixture.cpp in Sources */,
 				867BB1C416AEC9FC0033868F /* b2Island.cpp in Sources */,
 				867BB1C516AEC9FC0033868F /* b2World.cpp in Sources */,
 				867BB1C616AEC9FC0033868F /* b2WorldCallbacks.cpp in Sources */,
 				867BB1C716AEC9FC0033868F /* b2ChainAndCircleContact.cpp in Sources */,
+				27908E4E18A3FAE1002D41BD /* Animation.c in Sources */,
 				867BB1C816AEC9FC0033868F /* b2ChainAndPolygonContact.cpp in Sources */,
 				867BB1C916AEC9FC0033868F /* b2CircleContact.cpp in Sources */,
 				867BB1CA16AEC9FC0033868F /* b2Contact.cpp in Sources */,
@@ -3566,11 +3719,13 @@
 				867BB1DA16AEC9FC0033868F /* b2WeldJoint.cpp in Sources */,
 				867BB1DB16AEC9FC0033868F /* b2WheelJoint.cpp in Sources */,
 				867BB20816AEC9FC0033868F /* b2Rope.cpp in Sources */,
+				27908E5C18A3FAE1002D41BD /* RegionAttachment.c in Sources */,
 				867BB21816AECA070033868F /* png.c in Sources */,
 				867BB21916AECA070033868F /* pngerror.c in Sources */,
 				867BB21A16AECA070033868F /* pngget.c in Sources */,
 				867BB21B16AECA070033868F /* pngmem.c in Sources */,
 				867BB21C16AECA070033868F /* pngpread.c in Sources */,
+				27908E5E18A3FAE1002D41BD /* SkeletonBounds.c in Sources */,
 				867BB21D16AECA070033868F /* pngread.c in Sources */,
 				867BB21E16AECA070033868F /* pngrio.c in Sources */,
 				867BB21F16AECA070033868F /* pngrtran.c in Sources */,
@@ -3583,6 +3738,7 @@
 				867BB22616AECA070033868F /* pngwutil.c in Sources */,
 				867BB25516AECA110033868F /* jcapimin.c in Sources */,
 				867BB25616AECA110033868F /* jcapistd.c in Sources */,
+				27908E5318A3FAE1002D41BD /* Attachment.c in Sources */,
 				867BB25716AECA110033868F /* jccoefct.c in Sources */,
 				867BB25816AECA110033868F /* jccolor.c in Sources */,
 				867BB25916AECA110033868F /* jcdctmgr.c in Sources */,
@@ -3603,6 +3759,7 @@
 				867BB26816AECA110033868F /* jdatasrc.c in Sources */,
 				867BB26916AECA110033868F /* jdcoefct.c in Sources */,
 				867BB26A16AECA110033868F /* jdcolor.c in Sources */,
+				27908E4F18A3FAE1002D41BD /* AnimationState.c in Sources */,
 				867BB26B16AECA110033868F /* jddctmgr.c in Sources */,
 				867BB26C16AECA110033868F /* jdhuff.c in Sources */,
 				867BB26D16AECA110033868F /* jdinput.c in Sources */,
@@ -3633,6 +3790,7 @@
 				86555D3816B2C2B400881446 /* T2DView.mm in Sources */,
 				86555D3916B2C2B400881446 /* T2DViewController.mm in Sources */,
 				2AF1C54B16B439D900C1CF3A /* declaredAssets.cc in Sources */,
+				27908E5818A3FAE1002D41BD /* Event.c in Sources */,
 				2AF1C54C16B439D900C1CF3A /* referencedAssets.cc in Sources */,
 				2AB97A2116B66BE50080F940 /* tamlCustom.cc in Sources */,
 				33230F1656FA2C7C493DA2D2 /* guiSliderCtrl.cc in Sources */,

+ 404 - 0
engine/source/2d/assets/SkeletonAsset.cc

@@ -0,0 +1,404 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef _CONSOLE_H_
+#include "console/console.h"
+#endif
+
+#ifndef _CONSOLEINTERNAL_H_
+#include "console/consoleInternal.h"
+#endif
+
+#ifndef _GBITMAP_H_
+#include "graphics/gBitmap.h"
+#endif
+
+#ifndef _UTILITY_H_
+#include "2d/core/Utility.h"
+#endif
+
+#ifndef _SCENE_OBJECT_H_
+#include "2d/sceneobject/SceneObject.h"
+#endif
+
+#ifndef _SKELETON_ASSET_H_
+#include "2d/assets/SkeletonAsset.h"
+#endif
+
+// Script bindings.
+#include "SkeletonAsset_ScriptBinding.h"
+
+//------------------------------------------------------------------------------
+
+IMPLEMENT_CONOBJECT(SkeletonAsset);
+
+//------------------------------------------------------------------------------
+
+ConsoleType( skeletonAssetPtr, TypeSkeletonAssetPtr, sizeof(AssetPtr<SkeletonAsset>), ASSET_ID_FIELD_PREFIX )
+
+//-----------------------------------------------------------------------------
+
+ConsoleGetType( TypeSkeletonAssetPtr )
+{
+    // Fetch asset Id.
+    return (*((AssetPtr<SkeletonAsset>*)dptr)).getAssetId();
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleSetType( TypeSkeletonAssetPtr )
+{
+    // Was a single argument specified?
+    if( argc == 1 )
+    {
+        // Yes, so fetch field value.
+        const char* pFieldValue = argv[0];
+
+        // Fetch asset pointer.
+        AssetPtr<SkeletonAsset>* pAssetPtr = dynamic_cast<AssetPtr<SkeletonAsset>*>((AssetPtrBase*)(dptr));
+
+        // Is the asset pointer the correct type?
+        if (pAssetPtr == NULL )
+        {
+            // No, so fail.
+            Con::warnf( "(TypeSkeletonAssetPtr) - Failed to set asset Id '%d'.", pFieldValue );
+            return;
+        }
+
+        // Set asset.
+        pAssetPtr->setAssetId( pFieldValue );
+
+        return;
+   }
+
+    // Warn.
+    Con::warnf( "(TypeSkeletonAssetPtr) - Cannot set multiple args to a single asset." );
+}
+
+
+//------------------------------------------------------------------------------
+
+SkeletonAsset::SkeletonAsset() :    mSkeletonFile(StringTable->EmptyString),
+                                    mAtlasFile(StringTable->EmptyString),
+                                    mScale(1),
+                                    mAtlasDirty(true),
+                                    mAtlas(NULL),
+                                    mSkeletonData(NULL),
+                                    mStateData(NULL)
+{
+}
+
+//------------------------------------------------------------------------------
+
+SkeletonAsset::~SkeletonAsset()
+{
+    spAnimationStateData_dispose(mStateData);
+    spSkeletonData_dispose(mSkeletonData);
+    spAtlas_dispose(mAtlas);
+}
+
+//------------------------------------------------------------------------------
+
+void SkeletonAsset::initPersistFields()
+{
+    // Call parent.
+    Parent::initPersistFields();
+
+    // Fields.
+    addProtectedField("AtlasFile", TypeAssetLooseFilePath, Offset(mAtlasFile, SkeletonAsset), &setAtlasFile, &defaultProtectedGetFn, &writeAtlasFile, "The loose file pointing to the .atlas file used for skinning");
+    addProtectedField("SkeletonFile", TypeAssetLooseFilePath, Offset(mSkeletonFile, SkeletonAsset), &setSkeletonFile, &defaultProtectedGetFn, &writeSkeletonFile, "The loose file produced by the editor, which is fed into this asset");
+    addProtectedField("Scale", TypeF32, Offset(mScale, SkeletonAsset), &setScale, &defaultProtectedGetFn, &writeScale, "");
+}
+
+//------------------------------------------------------------------------------
+
+bool SkeletonAsset::onAdd()
+{
+    // Call Parent.
+    if (!Parent::onAdd())
+       return false;
+
+    return true;
+}
+
+//------------------------------------------------------------------------------
+
+void SkeletonAsset::onRemove()
+{
+    // Call Parent.
+    Parent::onRemove();
+}
+
+//------------------------------------------------------------------------------
+
+void SkeletonAsset::setSkeletonFile( const char* pSkeletonFile )
+{
+    // Sanity!
+    AssertFatal( pSkeletonFile != NULL, "Cannot use a NULL skeleton file." );
+
+    // Fetch skeleton file.
+    pSkeletonFile = StringTable->insert( pSkeletonFile );
+
+    // Ignore no change.
+    if (pSkeletonFile == mSkeletonFile )
+        return;
+
+    // Update.
+    mSkeletonFile = getOwned() ? expandAssetFilePath( pSkeletonFile ) : StringTable->insert( pSkeletonFile );
+
+    // Refresh the asset.
+    refreshAsset();
+}
+
+//------------------------------------------------------------------------------
+
+void SkeletonAsset::setAtlasFile( const char* pAtlasFile )
+{
+    // Sanity!
+    AssertFatal( pAtlasFile != NULL, "Cannot use a NULL atlas file." );
+
+    // Fetch atlas file.
+    pAtlasFile = StringTable->insert( pAtlasFile );
+
+    // Ignore no change.
+    if (pAtlasFile == mAtlasFile )
+        return;
+
+    // Update.
+    mAtlasFile = getOwned() ? expandAssetFilePath( pAtlasFile ) : StringTable->insert( pAtlasFile );
+    mAtlasDirty = true;
+
+    // Refresh the asset.
+    refreshAsset();
+}
+
+//------------------------------------------------------------------------------
+
+void SkeletonAsset::setScale( F32 fScale)
+{
+    // Ignore no change.
+    if (fScale == mScale )
+        return;
+
+    mScale = fScale;
+
+    // Scale has been set, refresh the asset based on this
+    refreshAsset();
+}
+
+//------------------------------------------------------------------------------
+
+void SkeletonAsset::copyTo(SimObject* object)
+{
+    // Call to parent.
+    Parent::copyTo(object);
+
+    // Cast to asset.
+    SkeletonAsset* pAsset = static_cast<SkeletonAsset*>(object);
+
+    // Sanity!
+    AssertFatal(pAsset != NULL, "SkeletonAsset::copyTo() - Object is not the correct type.");
+
+    // Copy state.
+    pAsset->setAtlasFile( getAtlasFile() );
+    pAsset->setSkeletonFile( getSkeletonFile() );
+    pAsset->setScale( getScale() );
+}
+
+//------------------------------------------------------------------------------
+
+void SkeletonAsset::initializeAsset( void )
+{
+    // Call parent.
+    Parent::initializeAsset();
+
+    // Ensure the skeleton file is expanded.
+    mSkeletonFile = expandAssetFilePath( mSkeletonFile );
+
+    // Ensure the skeleton file is expanded.
+    mAtlasFile = expandAssetFilePath( mAtlasFile );
+
+    // Build the atlas data
+    if (mAtlasDirty)
+        buildAtlasData();
+
+    // Build the skeleton data
+    buildSkeletonData();
+}
+
+//------------------------------------------------------------------------------
+
+void SkeletonAsset::onAssetRefresh( void )
+{
+    // Ignore if not yet added to the sim.
+    if (!isProperlyAdded() )
+        return;
+
+    // Call parent.
+    Parent::onAssetRefresh();
+
+    // Reset any states or data
+    if (mAtlasDirty)
+        buildAtlasData();
+
+    buildSkeletonData();
+}
+
+//-----------------------------------------------------------------------------
+
+void SkeletonAsset::buildAtlasData( void )
+{
+    // If the atlas data was previously created, need to release it
+    if (mAtlas)
+        spAtlas_dispose(mAtlas);
+
+    // If we are using a .atlas file
+    if (mAtlasFile != StringTable->EmptyString)
+        mAtlas = spAtlas_readAtlasFile(mAtlasFile);
+
+    // Atlas load failure
+    AssertFatal(mAtlas != NULL, "SkeletonAsset::buildAtlasData() - Atlas was not loaded.");
+
+    spAtlasPage* currentPage = mAtlas->pages;
+
+    while (currentPage != NULL)
+    {
+        // Allocate a new ImageAsset. If we have multiple atlases, we would loop this multiple times
+        ImageAsset* pImageAsset = new ImageAsset();
+
+        const char* imageFilePath = expandAssetFilePath(currentPage->name);
+        
+        // Point to the raw file (png or jpg)
+        pImageAsset->setImageFile( imageFilePath);        
+
+        // Enable Explicit Mode so we can use region coordinates
+        pImageAsset->setExplicitMode( true );
+
+        spAtlasRegion* currentRegion = mAtlas->regions;
+
+        // Add it to the AssetDatabase, making it accessible everywhere
+        mImageAsset = AssetDatabase.addPrivateAsset( pImageAsset );
+
+        // Loop through the Atlas information to create cell regions
+        while (currentRegion != NULL)
+        {
+            pImageAsset->addExplicitCell( currentRegion->x, currentRegion->y, currentRegion->width, currentRegion->height, currentRegion->name );
+
+            currentRegion = currentRegion->next;
+        }
+
+        mImageAsset->forceCalculation();
+        currentPage = currentPage->next;
+    }
+
+    mAtlasDirty = false;
+}
+
+//-----------------------------------------------------------------------------
+
+void SkeletonAsset::buildSkeletonData( void )
+{
+    // Atlas load failure
+    AssertFatal(mAtlas != NULL, "SkeletonAsset::buildSkeletonData() - Atlas was not loaded.");
+    
+    // Clear state data
+    if (mStateData)
+        spAnimationStateData_dispose(mStateData);
+
+    // Clear skeleton data
+    if (mSkeletonData)
+        spSkeletonData_dispose(mSkeletonData);
+    
+    spSkeletonJson* json = spSkeletonJson_create(mAtlas);
+    json->scale = mScale;
+    mSkeletonData = spSkeletonJson_readSkeletonDataFile(json, mSkeletonFile);
+
+    if (!mSkeletonData)
+    {
+        spAtlas_dispose(mAtlas);
+        mAtlas = 0;
+
+        // Report json->error message
+        AssertFatal(mSkeletonData != NULL, "SkeletonAsset::buildSkeletonData() - Skeleton data was not valid.");
+    }
+
+    spSkeletonJson_dispose(json);
+
+    mStateData = spAnimationStateData_create(mSkeletonData);
+}
+
+//-----------------------------------------------------------------------------
+
+bool SkeletonAsset::isAssetValid( void ) const
+{
+    return ((mAtlas != NULL) && (mSkeletonData != NULL) && (mStateData != NULL) && mImageAsset.notNull());
+}
+
+//-----------------------------------------------------------------------------
+
+void SkeletonAsset::onTamlPreWrite( void )
+{
+    // Call parent.
+    Parent::onTamlPreWrite();
+
+    // Ensure the skeleton file is collapsed.
+    mSkeletonFile = collapseAssetFilePath( mSkeletonFile );
+
+    // Ensure the atlas file is collapsed.
+    mAtlasFile = collapseAssetFilePath( mAtlasFile );
+}
+
+//-----------------------------------------------------------------------------
+
+void SkeletonAsset::onTamlPostWrite( void )
+{
+    // Call parent.
+    Parent::onTamlPostWrite();
+
+    // Ensure the skeleton file is expanded.
+    mSkeletonFile = expandAssetFilePath( mSkeletonFile );
+
+    // Ensure the atlas file is expanded.
+    mAtlasFile = expandAssetFilePath( mAtlasFile );
+}
+
+//------------------------------------------------------------------------------
+
+void SkeletonAsset::onTamlCustomWrite( TamlCustomNodes& customNodes )
+{
+    // Debug Profiling.
+    PROFILE_SCOPE(SkeletonAsset_OnTamlCustomWrite);
+
+    // Call parent.
+    Parent::onTamlCustomWrite( customNodes );
+}
+
+//-----------------------------------------------------------------------------
+
+void SkeletonAsset::onTamlCustomRead( const TamlCustomNodes& customNodes )
+{
+    // Debug Profiling.
+    PROFILE_SCOPE(SkeletonAsset_OnTamlCustomRead);
+
+    // Call parent.
+    Parent::onTamlCustomRead( customNodes );
+}

+ 107 - 0
engine/source/2d/assets/SkeletonAsset.h

@@ -0,0 +1,107 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef _SKELETON_ASSET_H_
+#define _SKELETON_ASSET_H_
+
+#ifndef _ASSET_PTR_H_
+#include "assets/assetPtr.h"
+#endif
+
+#ifndef _IMAGE_ASSET_H_
+#include "2d/assets/imageAsset.h"
+#endif
+
+#ifndef SPINE_SPINE_H_
+#include "spine/spine.h"
+#endif
+
+//-----------------------------------------------------------------------------
+
+DefineConsoleType( TypeSkeletonAssetPtr )
+
+//-----------------------------------------------------------------------------
+
+class SkeletonAsset : public AssetBase
+{
+private:
+    typedef AssetBase Parent;
+    bool                            mAtlasDirty;
+
+public:
+    StringTableEntry                mSkeletonFile;
+    StringTableEntry                mAtlasFile;
+    F32                           mScale;
+    AssetPtr<ImageAsset>            mImageAsset;
+    spAtlas*                 mAtlas;
+    spSkeletonData*          mSkeletonData;
+    spAnimationStateData*    mStateData;
+
+public:
+    SkeletonAsset();
+    virtual ~SkeletonAsset();
+
+    /// Core.
+    static void initPersistFields();
+    virtual bool onAdd();
+    virtual void onRemove();
+    virtual void copyTo(SimObject* object);
+
+    void                    setSkeletonFile( const char* pSkeletonFile );
+    inline StringTableEntry getSkeletonFile( void ) const                   { return mSkeletonFile; }
+
+    void                    setAtlasFile( const char* pAtlasFile );
+    inline StringTableEntry getAtlasFile( void ) const                      { return mAtlasFile; }
+
+    void                    setScale( const F32 fScale );
+    inline F32              getScale( void ) const                          { return mScale; }
+    
+    virtual bool            isAssetValid( void ) const;
+
+    /// Declare Console Object.
+    DECLARE_CONOBJECT(SkeletonAsset);
+
+private:
+    void buildAtlasData( void );
+    void buildSkeletonData( void );
+
+protected:
+    virtual void initializeAsset( void );
+    virtual void onAssetRefresh( void );
+
+    /// Taml callbacks.
+    virtual void onTamlPreWrite( void );
+    virtual void onTamlPostWrite( void );
+    virtual void onTamlCustomWrite( TamlCustomNodes& customNodes );
+    virtual void onTamlCustomRead( const TamlCustomNodes& customNodes );
+
+
+protected:
+    static bool setSkeletonFile( void* obj, const char* data )              { static_cast<SkeletonAsset*>(obj)->setSkeletonFile(data); return false; }
+    static bool writeSkeletonFile( void* obj, StringTableEntry pFieldName ) { return static_cast<SkeletonAsset*>(obj)->getSkeletonFile() != StringTable->EmptyString; }
+    static bool setAtlasFile( void* obj, const char* data )                 { static_cast<SkeletonAsset*>(obj)->setAtlasFile(data); return false; }
+    static bool writeAtlasFile( void* obj, StringTableEntry pFieldName )    { return static_cast<SkeletonAsset*>(obj)->getAtlasFile() != StringTable->EmptyString; }
+    static bool setScale( void* obj, const char* data )                     { static_cast<SkeletonAsset*>(obj)->setScale(dAtof(data)); return false; }
+    static bool writeScale( void* obj, StringTableEntry pFieldName )        { return static_cast<SkeletonAsset*>(obj)->getScale() != 1.0f; }
+};
+
+#endif // _SKELETON_ASSET_H_

+ 87 - 0
engine/source/2d/assets/SkeletonAsset_ScriptBinding.h

@@ -0,0 +1,87 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+ConsoleMethodGroupBeginWithDocs(SkeletonAsset, AssetBase)
+
+/*! Sets the atlas file.
+    @return No return value.
+*/
+ConsoleMethodWithDocs(SkeletonAsset, setAtlasFile, ConsoleVoid, 3, 3, (AtlasFile))
+{
+    object->setAtlasFile( argv[2] );
+}
+
+//-----------------------------------------------------------------------------
+
+/*! Gets the atlas file.
+    @return Returns the atlas file.
+*/
+ConsoleMethodWithDocs(SkeletonAsset, getAtlasFile, ConsoleString, 2, 2, ())
+{
+    return object->getAtlasFile();
+}
+
+//------------------------------------------------------------------------------
+
+/*! Sets the skeleton file.
+    @return No return value.
+*/
+ConsoleMethodWithDocs(SkeletonAsset, setSkeletonFile, ConsoleVoid, 3, 3, (SkeletonFile))
+{
+    object->setSkeletonFile( argv[2] );
+}
+
+//-----------------------------------------------------------------------------
+
+/*! Gets the skeleton file.
+    @return Returns the skeleton file.
+*/
+ConsoleMethodWithDocs(SkeletonAsset, getSkeletonFile, ConsoleString, 2, 2, ())
+{
+    return object->getSkeletonFile();
+}
+
+//------------------------------------------------------------------------------
+
+/*! Sets the scale for the skeleton size.
+	@param scale The scale for the skeleton size.
+    @return No return value.
+*/
+ConsoleMethodWithDocs(SkeletonAsset, setScale, ConsoleVoid, 3, 3, (float scale))
+{
+    // Compilier complains that setScale can't be found. Not sure how to fix.
+    //object->setScale( argv[2] );
+}
+
+//-----------------------------------------------------------------------------
+
+/*! Gets the scale for the skeleton size.
+    @return Returns the scale for the skeleton size.
+*/
+ConsoleMethodWithDocs(SkeletonAsset, getScale, ConsoleFloat, 2, 2, ())
+{
+    return object->getScale();
+}
+
+//------------------------------------------------------------------------------
+
+ConsoleMethodGroupEndWithDocs(SkeletonAsset)

+ 28 - 0
engine/source/2d/core/SpriteBatch.cc

@@ -1023,6 +1023,34 @@ SpriteBatchItem* SpriteBatch::createSprite( void )
 
 //------------------------------------------------------------------------------
 
+SpriteBatchItem* SpriteBatch::createSprite( const Vector2* explicitVertices  )
+{
+    // Debug Profiling.
+    PROFILE_SCOPE(SpriteBatch_CreateSprite);
+
+    // Allocate batch Id.
+    const U32 batchId = ++mMasterBatchId;
+
+    // Create sprite batch item,
+    SpriteBatchItem* pSpriteBatchItem = SpriteBatchItemFactory.createObject();
+
+    // Set batch parent.
+    pSpriteBatchItem->setBatchParent( this, batchId );
+
+    // Set explicit mode.
+    pSpriteBatchItem->setExplicitMode( true );
+
+    // Set explicit vertices.
+
+
+    // Create sprite batch item,
+    mSprites.insert( batchId, pSpriteBatchItem );
+
+    return pSpriteBatchItem;
+}
+
+//------------------------------------------------------------------------------
+
 SpriteBatchItem* SpriteBatch::findSpritePosition( const SpriteBatchItem::LogicalPosition& logicalPosition )
 {
     // Debug Profiling.

+ 1 - 0
engine/source/2d/core/SpriteBatch.h

@@ -183,6 +183,7 @@ public:
 
 protected:
     SpriteBatchItem* createSprite( void );
+    SpriteBatchItem* createSprite( const Vector2* explicitVertices );
     SpriteBatchItem* findSpritePosition( const SpriteBatchItem::LogicalPosition& logicalPosition );
     SpriteBatchItem* findSpriteId( const U32 batchId );
     SpriteBatchItem* findSpriteName( const char* pName );

+ 31 - 5
engine/source/2d/core/SpriteBatchItem.cc

@@ -94,8 +94,12 @@ void SpriteBatchItem::resetState( void )
     mLogicalPosition.resetState();
 
     mVisible = true;
+    mExplicitMode = false;
 
     mLocalPosition.SetZero();
+    for (U32 i = 0; i < 4; i++)
+        mExplicitVerts[i].SetZero();
+
     mDepth = 0.0f;
     mLocalAngle = 0.0f;
     setSize( Vector2( 1.0f, 1.0f ) );
@@ -226,6 +230,18 @@ void SpriteBatchItem::render( BatchRender* pBatchRenderer, const SceneRenderRequ
 
 //------------------------------------------------------------------------------
 
+void SpriteBatchItem::setExplicitVertices( const Vector2* explicitVertices )
+{
+    mExplicitMode = true;
+
+    mExplicitVerts[0] = explicitVertices[0];
+    mExplicitVerts[1] = explicitVertices[1];
+    mExplicitVerts[2] = explicitVertices[2];
+    mExplicitVerts[3] = explicitVertices[3];
+}
+
+//------------------------------------------------------------------------------
+
 void SpriteBatchItem::updateLocalTransform( void )
 {
     // Debug Profiling.
@@ -248,10 +264,20 @@ void SpriteBatchItem::updateLocalTransform( void )
     const F32 halfHeight = mSize.y * 0.5f;
 
     // Set local size vertices.
-    mLocalOOBB[0].Set( -halfWidth, -halfHeight );
-    mLocalOOBB[1].Set( +halfWidth, -halfHeight );
-    mLocalOOBB[2].Set( +halfWidth, +halfHeight );
-    mLocalOOBB[3].Set( -halfWidth, +halfHeight );
+    if (!mExplicitMode)
+    {
+        mLocalOOBB[0].Set( -halfWidth, -halfHeight );
+        mLocalOOBB[1].Set( +halfWidth, -halfHeight );
+        mLocalOOBB[2].Set( +halfWidth, +halfHeight );
+        mLocalOOBB[3].Set( -halfWidth, +halfHeight );
+    }
+    else
+    {
+        mLocalOOBB[0] = mExplicitVerts[0];
+        mLocalOOBB[1] = mExplicitVerts[1];
+        mLocalOOBB[2] = mExplicitVerts[2];
+        mLocalOOBB[3] = mExplicitVerts[3];
+    }
 
     // Calculate local OOBB.
     CoreMath::mCalculateOOBB( mLocalOOBB, localTransform, mLocalOOBB );
@@ -713,4 +739,4 @@ void SpriteBatchItem::WriteCustomTamlSchema( const AbstractClassRep* pClassRep,
     pBatchItemLogicalPosition->SetAttribute( "name", spriteLogicalPositionName );
     pBatchItemLogicalPosition->SetAttribute( "type", "xs:string" );
     pBatchItemComplexTypeElement->LinkEndChild( pBatchItemLogicalPosition );
-}
+}

+ 7 - 0
engine/source/2d/core/SpriteBatchItem.h

@@ -191,8 +191,10 @@ protected:
     LogicalPosition     mLogicalPosition;
 
     bool                mVisible;
+    bool                mExplicitMode;
 
     Vector2             mLocalPosition;
+    Vector2             mExplicitVerts[4];
     F32                 mLocalAngle;
     Vector2             mSize;
     F32                 mDepth;
@@ -240,9 +242,14 @@ public:
     inline void setVisible( const bool visible ) { mVisible = visible; }
     inline bool getVisible( void ) const { return mVisible; }
 
+    inline void setExplicitMode( const bool explicitMode ) { mExplicitMode = explicitMode; }
+    inline bool getExplicitMode( void ) const { return mExplicitMode; }
+
     inline void setLocalPosition( const Vector2& localPosition ) { mLocalPosition = localPosition; mLocalTransformDirty = true; }
     inline Vector2 getLocalPosition( void ) const { return mLocalPosition; }
 
+    void setExplicitVertices( const Vector2* explicitVertices );
+
     inline void setLocalAngle( const F32 localAngle ) { mLocalAngle = localAngle; mLocalTransformDirty = true; }
     inline F32 getLocalAngle( void ) const { return mLocalAngle; }
 

+ 458 - 0
engine/source/2d/sceneobject/Skeleton.cc

@@ -0,0 +1,458 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef _SKELETON_H_
+#include "2d/sceneobject/Skeleton.h"
+#endif
+
+#include "spine/extension.h"
+
+// Script bindings.
+#include "2d/sceneobject/Skeleton_ScriptBinding.h"
+
+//-----------------------------------------------------------------------------
+
+void _spAtlasPage_createTexture (spAtlasPage* self, const char* path) {
+}
+void _spAtlasPage_disposeTexture (spAtlasPage* self) {
+}
+
+char* _spUtil_readFile (const char* path, int* length) {
+    return _readFile(path, length);
+}
+
+//-----------------------------------------------------------------------------
+
+IMPLEMENT_CONOBJECT(Skeleton);
+
+//------------------------------------------------------------------------------
+
+Skeleton::Skeleton() :      mPreTickTime( 0.0f ),
+                            mPostTickTime( 0.0f ),
+                            mTimeScale(1),
+                            mLastFrameTime(0),
+                            mTotalAnimationTime(0),
+                            mSkeleton(NULL),
+                            mState(NULL),
+                            mAnimationCycle(false),
+                            mAnimationFinished(true),
+                            mAnimationDuration(0.0)
+{
+    mCurrentAnimation = StringTable->insert("");
+    mSkeletonScale.SetZero();
+    mSkeletonOffset.SetZero();
+}
+
+//------------------------------------------------------------------------------
+
+Skeleton::~Skeleton()
+{
+    if (mSkeleton) {
+        spSkeleton_dispose(mSkeleton);
+        mSkeleton = NULL;
+    }
+    if (mState) {
+        spAnimationState_dispose(mState);
+        mState = NULL;
+    }
+}
+
+//------------------------------------------------------------------------------
+
+void Skeleton::initPersistFields()
+{
+    // Call parent.
+    Parent::initPersistFields();
+    
+    addProtectedField("Asset", TypeSkeletonAssetPtr, Offset(mSkeletonAsset, Skeleton), &setSkeletonAsset, &getSkeletonAsset, &writeSkeletonAsset, "The skeleton asset ID used for the skeleton.");
+    addProtectedField("AnimationName", TypeString, Offset(mCurrentAnimation, Skeleton), &setCurrentAnimation, &getCurrentAnimation, &writeCurrentAnimation, "The animation name to play.");
+    addProtectedField("Skin", TypeString, Offset(mCurrentSkin, Skeleton), &setCurrentSkin, &getCurrentSkin, &writeCurrentSkin, "The skin to use.");
+    addProtectedField("RootBoneScale", TypeVector2, NULL, &setSkeletonScale, &getSkeletonScale, &writeSkeletonScale, "Scaling of the skeleton's root bone");
+    addProtectedField("RootBoneOffset", TypeVector2, NULL, &setSkeletonOffset, &getSkeletonOffset, &writeSkeletonOffset, "X/Y offset of the skeleton's root bone");
+    addProtectedField("AnimationCycle", TypeBool, Offset(mAnimationCycle, Skeleton), &setAnimationCycle, &defaultProtectedGetFn, &writeAnimationCycle, "Whether the animation loops or not");
+}
+
+//-----------------------------------------------------------------------------
+
+void Skeleton::preIntegrate( const F32 totalTime, const F32 elapsedTime, DebugStats* pDebugStats )
+{
+    // Note tick times.
+    mPreTickTime = mPostTickTime;
+    mPostTickTime = totalTime;
+    
+    // Update composition at pre-tick time.
+    updateComposition( mPreTickTime );
+    
+    // Are the spatials dirty?
+    if ( getSpatialDirty() )
+    {
+        // Yes, so update the world transform.
+        setBatchTransform( getRenderTransform() );
+    }
+    
+    // Are the render extents dirty?
+    if ( getLocalExtentsDirty() )
+    {
+        // Yes, so set size as local extents.
+        setSize( getLocalExtents() );
+    }
+    
+    // Call parent.
+    Parent::preIntegrate( totalTime, elapsedTime, pDebugStats );
+}
+
+//-----------------------------------------------------------------------------
+
+void Skeleton::integrateObject( const F32 totalTime, const F32 elapsedTime, DebugStats* pDebugStats )
+{
+    // Call Parent.
+    Parent::integrateObject( totalTime, elapsedTime, pDebugStats );
+    
+    // Finish if the spatials are NOT dirty.
+    if ( !getSpatialDirty() )
+        return;
+    
+    // Update the batch world transform.
+    setBatchTransform( getRenderTransform() );
+}
+
+//-----------------------------------------------------------------------------
+
+void Skeleton::interpolateObject( const F32 timeDelta )
+{
+    // Call parent.
+    Parent::interpolateObject( timeDelta );
+    
+    // Update composition time (interpolated).
+    updateComposition( (timeDelta * mPreTickTime) + ((1.0f-timeDelta) * mPostTickTime) );
+    
+    // Finish if the spatials are NOT dirty.
+    if ( !getSpatialDirty() )
+        return;
+    
+    // Update the batch world transform.
+    setBatchTransform( getRenderTransform() );
+}
+
+//------------------------------------------------------------------------------
+
+void Skeleton::copyTo(SimObject* object)
+{
+    // Call to parent.
+    Parent::copyTo(object);
+    
+    // Fetch object.
+    Skeleton* pComposite = dynamic_cast<Skeleton*>(object);
+    
+    // Sanity!
+    AssertFatal(pComposite != NULL, "Skeleton::copyTo() - Object is not the correct type.");
+    
+    // Copy state.
+    pComposite->setSkeletonAsset( getSkeletonAsset() );
+    pComposite->setCurrentAnimation( getCurrentAnimation(), getAnimationCycle() );
+    pComposite->setCurrentSkin( getCurrentSkin() );
+    pComposite->setSkeletonScale( getSkeletonScale() );
+    pComposite->setSkeletonOffset( getSkeletonOffset() );
+}
+
+//-----------------------------------------------------------------------------
+
+void Skeleton::scenePrepareRender( const SceneRenderState* pSceneRenderState, SceneRenderQueue* pSceneRenderQueue )
+{
+    // Prepare render.
+    SpriteBatch::prepareRender( this, pSceneRenderState, pSceneRenderQueue );
+}
+
+//-----------------------------------------------------------------------------
+
+void Skeleton::sceneRender( const SceneRenderState* pSceneRenderState, const SceneRenderRequest* pSceneRenderRequest, BatchRender* pBatchRenderer )
+{
+    // Render.
+    SpriteBatch::render( pSceneRenderState, pSceneRenderRequest, pBatchRenderer );
+
+}
+
+//-----------------------------------------------------------------------------
+
+bool Skeleton::setSkeletonAsset( const char* pSkeletonAssetId )
+{
+    // Sanity!
+    AssertFatal( pSkeletonAssetId != NULL, "Cannot use a NULL asset Id." );
+    
+    // Fetch the asset Id.
+    mSkeletonAsset = pSkeletonAssetId;
+    
+    // Generate composition.
+    generateComposition();
+    
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+bool Skeleton::setCurrentAnimation( const char* pAnimation, const bool isLooping )
+{
+    // Make sure an asset was loaded.
+    if (mSkeletonAsset.isNull())
+        return false;
+
+    // Set the animation.
+    mCurrentAnimation = StringTable->insert(pAnimation);
+
+    mAnimationCycle = isLooping;
+    
+    // Generate composition.
+    generateComposition();
+
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+bool Skeleton::setMix( const char* pFromAnimation, const char* pToAnimation, float time)
+{
+    if (mSkeletonAsset.isNull())
+    {
+        Con::warnf("Skeleton::setMix() - Cannot mix. No asset assigned");
+        return false;
+    }
+    
+    // Check for valid animation state data
+    AssertFatal( mSkeletonAsset->mStateData != NULL, "Skeleton::setMix() - Animation state data invalid" );
+    
+    // Check to see if the "from animation" is valid
+    spAnimation* from = spSkeletonData_findAnimation(mSkeleton->data, pFromAnimation);
+    
+    if (!from)
+    {
+        Con::warnf("Skeleton::setMix() - Animation %s does not exist.", pFromAnimation);
+        return false;
+    }
+    
+    // Check to see if the "to animation" is valid
+	spAnimation* to = spSkeletonData_findAnimation(mSkeleton->data, pToAnimation);
+	
+    if (!to)
+    {
+        Con::warnf("Skeleton::setMix() - Animation %s does not exist.", pToAnimation);
+        return false;
+    }
+    
+    // Check to see if a valid mix time was passsed
+    if (time < 0.0f)
+    {
+        Con::warnf("Skeleton::setMix() - Invalid time set, %f", time);
+        return false;
+    }
+    
+    spAnimationStateData_setMixByName(mSkeletonAsset->mStateData, pFromAnimation, pToAnimation, time);
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+bool Skeleton::setCurrentSkin( const char* pSkin )
+{
+    if (mSkeletonAsset.isNull() || !mSkeleton)
+    {
+        Con::errorf("Skeleton::setCurrentSkin() - Skeleton Asset was null or skeleton was not built");
+        return false;
+    }
+
+    S32 result = spSkeleton_setSkinByName(mSkeleton, pSkin);
+
+    if (result)
+    {
+        spSkeleton_setSlotsToSetupPose(mSkeleton);
+        return true;
+    }
+    else
+    {
+        Con::errorf("Skeleton::setCurrentSkin() - Skin %s not found", pSkin);
+        return false;
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+void Skeleton::setSkeletonScale(const Vector2& scale)
+{
+    mSkeletonScale = scale;
+
+    if (!mSkeleton)
+        return;
+
+    if (mSkeletonScale.notZero())
+    {
+        spBone* rootBone = mSkeleton->root;
+        rootBone->scaleX = mSkeletonScale.x;
+        rootBone->scaleY = mSkeletonScale.y;
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+void Skeleton::setSkeletonOffset(const Vector2& offset)
+{
+    mSkeletonOffset = offset;
+
+     if (!mSkeleton)
+        return;
+
+    if (mSkeletonOffset.notZero())
+    {
+        spBone* rootBone = mSkeleton->root;
+        rootBone->x = mSkeletonOffset.x;
+        rootBone->y = mSkeletonOffset.y;
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+void Skeleton::generateComposition( void )
+{
+    // Clear existing visualization
+    clearSprites();
+    mSkeletonSprites.clear();
+
+    // Finish if skeleton asset isn't available.
+    if ( mSkeletonAsset.isNull() )
+        return;
+
+    // Generate visualization.  
+    if ((*mSkeletonAsset).mImageAsset.isNull())
+    {
+        Con::warnf( "Skeleton::generateComposition() - Image asset was NULL, so nothing can be added to the composition.");
+        return;
+    }
+
+    if (!mSkeleton)
+        mSkeleton = spSkeleton_create(mSkeletonAsset->mSkeletonData);
+
+    if (!mState)
+        mState = spAnimationState_create(mSkeletonAsset->mStateData);
+
+    if (mCurrentAnimation != StringTable->EmptyString)
+    {
+        spAnimationState_setAnimationByName(mState, 0, mCurrentAnimation, mAnimationCycle);
+        mAnimationDuration = mState->tracks[0]->animation->duration;
+        mAnimationFinished = false;
+        mTotalAnimationTime = mLastFrameTime + mAnimationDuration;
+    }
+    
+    if (mSkeletonScale.notZero())
+    {
+        spBone* rootBone = mSkeleton->root;
+        rootBone->scaleX = mSkeletonScale.x;
+        rootBone->scaleY = mSkeletonScale.y;
+    }
+    
+    if (mSkeletonOffset.notZero())
+    {
+        spBone* rootBone = mSkeleton->root;
+        rootBone->x = mSkeletonOffset.x;
+        rootBone->y = mSkeletonOffset.y;
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+void Skeleton::updateComposition( const F32 time )
+{
+    // Update position/orientation/state of visualization
+    float delta = (time - mLastFrameTime) * mTimeScale;
+    mLastFrameTime = time;
+
+    spSkeleton_update(mSkeleton, delta);
+    
+    if (!mAnimationFinished)
+    {
+        spAnimationState_update(mState, delta);
+        spAnimationState_apply(mState, mSkeleton);
+    }
+
+    spSkeleton_updateWorldTransform(mSkeleton);
+
+    // Get the ImageAsset used by the sprites
+    StringTableEntry assetId = (*mSkeletonAsset).mImageAsset.getAssetId();
+
+    clearSprites();
+
+    Vector2 vertices[4];
+
+    F32 vertexPositions[8];
+    for (int i = 0; i < mSkeleton->slotCount; ++i)
+    {
+        spSlot* slot = mSkeleton->slots[i];
+        spAttachment* attachment = slot->attachment;
+        
+        if (!attachment || attachment->type != ATTACHMENT_REGION)
+            continue;
+        
+        spRegionAttachment* regionAttachment = (spRegionAttachment*)attachment;
+        spRegionAttachment_computeWorldVertices(regionAttachment, slot->skeleton->x, slot->skeleton->y, slot->bone, vertexPositions);
+
+        SpriteBatchItem* pSprite = SpriteBatch::createSprite();
+		  
+        pSprite->setSrcBlendFactor(GL_ONE);
+        pSprite->setDstBlendFactor(GL_ONE_MINUS_SRC_ALPHA);
+
+        F32 alpha = mSkeleton->a * slot->a;
+        pSprite->setBlendColor(ColorF(
+            mSkeleton->r * slot->r * alpha,
+            mSkeleton->g * slot->g * alpha,
+            mSkeleton->b * slot->b * alpha,
+            alpha
+        ));
+
+        vertices[0].x = vertexPositions[VERTEX_X1];
+        vertices[0].y = vertexPositions[VERTEX_Y1];
+        vertices[1].x = vertexPositions[VERTEX_X4];
+        vertices[1].y = vertexPositions[VERTEX_Y4];
+        vertices[2].x = vertexPositions[VERTEX_X3];
+        vertices[2].y = vertexPositions[VERTEX_Y3];
+        vertices[3].x = vertexPositions[VERTEX_X2];
+        vertices[3].y = vertexPositions[VERTEX_Y2];
+        pSprite->setExplicitVertices(vertices);
+
+        pSprite->setImage(assetId);
+        pSprite->setImageFrameByName(attachment->name);
+    }
+
+    if (mLastFrameTime >= mTotalAnimationTime)
+        mAnimationFinished = true;
+    
+    if (mAnimationFinished && !mAnimationCycle)
+    {
+        onAnimationFinished();
+    }
+    else
+    {
+        mAnimationFinished = false;
+    }
+}
+
+void Skeleton::onAnimationFinished()
+{
+    // Do script callback.
+    Con::executef( this, 2, "onAnimationFinished", mCurrentAnimation );
+}

+ 148 - 0
engine/source/2d/sceneobject/Skeleton.h

@@ -0,0 +1,148 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef _SKELETON_H_
+#define _SKELETON_H_
+
+#ifndef _SPRITE_BATCH_H_
+#include "2d/core/SpriteBatch.h"
+#endif
+
+#ifndef _SCENE_OBJECT_H_
+#include "2d/sceneobject/SceneObject.h"
+#endif
+
+#ifndef _SKELETON_ASSET_H_
+#include "2d/assets/SkeletonAsset.h"
+#endif
+
+//------------------------------------------------------------------------------
+
+class Skeleton : public SceneObject, public SpriteBatch
+{
+protected:
+    typedef SceneObject Parent;
+    
+private:
+    typedef Vector<SpriteBatchItem*> typeSkeletonSpritesVector;
+    typeSkeletonSpritesVector   mSkeletonSprites;
+
+    AssetPtr<SkeletonAsset>     mSkeletonAsset;
+    spSkeleton*          mSkeleton;
+    spAnimationState*    mState;
+    
+    F32                         mPreTickTime;
+    F32                         mPostTickTime;    
+    F32                         mTimeScale;
+    F32                         mLastFrameTime;
+    F32                         mAnimationDuration;
+    F32                         mTotalAnimationTime;
+
+    bool                        mAnimationFinished;
+    bool                        mAnimationCycle;
+    Vector2                     mSkeletonScale;
+    Vector2                     mSkeletonOffset;
+
+    StringTableEntry            mCurrentAnimation;
+    StringTableEntry            mCurrentSkin;
+
+    
+    
+public:
+    Skeleton();
+    virtual ~Skeleton();
+    
+    static void initPersistFields();
+    
+    virtual void preIntegrate( const F32 totalTime, const F32 elapsedTime, DebugStats* pDebugStats );
+    virtual void integrateObject( const F32 totalTime, const F32 elapsedTime, DebugStats* pDebugStats );
+    virtual void interpolateObject( const F32 timeDelta );
+    
+    virtual void copyTo( SimObject* object );
+    
+    virtual bool canPrepareRender( void ) const { return true; }
+    virtual bool validRender( void ) const { return mSkeletonAsset.notNull(); }
+    virtual bool shouldRender( void ) const { return true; }
+    virtual void scenePrepareRender( const SceneRenderState* pSceneRenderState, SceneRenderQueue* pSceneRenderQueue );
+    virtual void sceneRender( const SceneRenderState* pSceneRenderState, const SceneRenderRequest* pSceneRenderRequest, BatchRender* pBatchRenderer );
+    
+    bool setSkeletonAsset( const char* pSkeletonAssetId );
+    inline StringTableEntry getSkeletonAsset( void ) const { return mSkeletonAsset.getAssetId(); }
+    
+    inline bool setCurrentAnimation( const char* pAnimation ) { return setCurrentAnimation( pAnimation, mAnimationCycle ); }
+    bool setCurrentAnimation( const char* pAnimation, const bool isLooping = false);
+    inline StringTableEntry getCurrentAnimation( void ) const { return mCurrentAnimation; }
+
+    bool setMix( const char* pFromAnimation, const char* pToAnimation, float time);
+    
+    bool setCurrentSkin( const char* pSkin );
+    inline StringTableEntry getCurrentSkin( void ) const { return mCurrentSkin; }
+
+    void setSkeletonScale( const Vector2& scale );
+    inline void setSkeletonScale( const F32 x, const F32 y ){ setSkeletonScale( Vector2(x, y) ); }
+    inline Vector2 getSkeletonScale( void ) const { return mSkeletonScale; }
+
+    void setSkeletonOffset( const Vector2& scale );
+    inline void setSkeletonOffset( const F32 x, const F32 y ){ setSkeletonOffset( Vector2(x, y) ); }
+    inline Vector2 getSkeletonOffset( void ) const { return mSkeletonOffset; }
+
+    inline F32 getAnimationDuration( void ) const { return mAnimationDuration; }
+    inline bool isAnimationFinished( void ) const { return mAnimationFinished; };
+
+    inline void setAnimationCycle( const bool isLooping ) { mAnimationCycle = isLooping; }
+    inline bool getAnimationCycle( void ) const {return mAnimationCycle; };
+
+    void onAnimationFinished();
+
+    /// Declare Console Object.
+    DECLARE_CONOBJECT( Skeleton );
+    
+protected:
+    void generateComposition( void );
+    void updateComposition( const F32 time );
+    
+protected:
+    static bool setSkeletonAsset( void* obj, const char* data )                  { static_cast<Skeleton*>(obj)->setSkeletonAsset(data); return false; }
+    static const char* getSkeletonAsset(void* obj, const char* data)             { return static_cast<Skeleton*>(obj)->getSkeletonAsset(); }
+    static bool writeSkeletonAsset( void* obj, StringTableEntry pFieldName )     { return static_cast<Skeleton*>(obj)->mSkeletonAsset.notNull(); }
+
+    static bool setCurrentAnimation( void* obj, const char* data )               { static_cast<Skeleton*>(obj)->setCurrentAnimation(data, static_cast<Skeleton*>(obj)->getAnimationCycle()); return false; }
+    static const char* getCurrentAnimation(void* obj, const char* data)          { return static_cast<Skeleton*>(obj)->getCurrentAnimation(); }
+    static bool writeCurrentAnimation( void*obj, StringTableEntry pAnimation )   { return static_cast<Skeleton*>(obj)->getCurrentAnimation() != StringTable->EmptyString; }
+
+    static bool setCurrentSkin( void* obj, const char* data )                    { static_cast<Skeleton*>(obj)->setCurrentSkin(data); return false; }
+    static const char* getCurrentSkin(void* obj, const char* data)               { return static_cast<Skeleton*>(obj)->getCurrentSkin(); }
+    static bool writeCurrentSkin( void*obj, StringTableEntry pSkin )             { return static_cast<Skeleton*>(obj)->getCurrentSkin() != StringTable->EmptyString; }
+
+    static bool setSkeletonScale(void* obj, const char* data)                    { static_cast<Skeleton*>(obj)->setSkeletonScale(Vector2(data)); return false; }
+    static const char* getSkeletonScale(void* obj, const char* data)             { return static_cast<Skeleton*>(obj)->getSkeletonScale().scriptThis(); }
+    static bool writeSkeletonScale( void* obj, StringTableEntry pFieldName )     { return static_cast<Skeleton*>(obj)->getSkeletonScale().notZero(); }
+
+    static bool setSkeletonOffset(void* obj, const char* data)                   { static_cast<Skeleton*>(obj)->setSkeletonOffset(Vector2(data)); return false; }
+    static const char* getSkeletonOffset(void* obj, const char* data)            { return static_cast<Skeleton*>(obj)->getSkeletonOffset().scriptThis(); }
+    static bool writeSkeletonOffset( void* obj, StringTableEntry pFieldName )    { return static_cast<Skeleton*>(obj)->getSkeletonOffset().notZero(); }
+
+    static bool setAnimationCycle( void* obj, const char* data )                 { static_cast<Skeleton*>(obj)->setAnimationCycle( dAtob(data) ); return false; }    
+    static bool writeAnimationCycle( void* obj, StringTableEntry pFieldName )    { return static_cast<Skeleton*>(obj)->getAnimationCycle() == false; }
+};
+
+#endif // _SKELETON_H_

+ 216 - 0
engine/source/2d/sceneobject/Skeleton_ScriptBinding.h

@@ -0,0 +1,216 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+ConsoleMethodGroupBeginWithDocs(Skeleton, SceneObject)
+
+/*! Sets the skeleton asset Id to use.
+    @param skeletonAssetId The skeleton asset Id to use.
+    @return No return value.
+*/
+ConsoleMethodWithDocs(Skeleton, setSkeletonAsset, ConsoleVoid, 3, 3, (skeletonAssetId?))
+{
+    object->setSkeletonAsset( argv[2] );
+}
+
+//-----------------------------------------------------------------------------
+
+/*! Gets the skeleton asset Id.
+    @return The skeleton asset Id.
+*/
+ConsoleMethodWithDocs(Skeleton, getSkeletonAsset, ConsoleString, 2, 2, ())
+{
+    return object->getSkeletonAsset();
+}
+
+//-----------------------------------------------------------------------------
+
+/*! Sets the animation for the object.
+    @param animationName String containing animation name.
+    @param cycle Optional bool to determine whether the animation should loop.
+    @return Returns true on success."
+*/
+ConsoleMethodWithDocs(Skeleton, setAnimation, ConsoleBool, 3, 4, (animationName, [cycle]))
+{
+    // Determine looping
+    bool shouldLoop = argc >= 4 ? dAtob(argv[3]) : false;
+    
+    return object->setCurrentAnimation(argv[2], shouldLoop);
+}
+
+//-----------------------------------------------------------------------------
+
+/*! Gets the name of the current animation.
+    @return String containing the animation name.
+*/
+ConsoleMethodWithDocs(Skeleton, getAnimation, ConsoleString, 2, 2, ())
+{
+    return object->getCurrentAnimation();
+}
+
+//-----------------------------------------------------------------------------
+
+/*! Sets the skin for the skeleton.
+    @return No return value.
+*/
+ConsoleMethodWithDocs(Skeleton, setSkin, ConsoleVoid, 3, 3, (skinName))
+{
+    object->setCurrentSkin(argv[2]);
+}
+
+//-----------------------------------------------------------------------------
+
+/*! Gets the name of the current skin.
+    @return String containing the skin name.
+*/
+ConsoleMethodWithDocs(Skeleton, getSkin, ConsoleString, 2, 2, ())
+{
+    return object->getCurrentSkin();
+}
+
+//-----------------------------------------------------------------------------
+
+/*! Sets scaling of the skeleton's root bone.
+    @param scaleX Base x coordinate scale.
+    @param scaleY Base y coordinate scale.
+    @return No return value.
+*/
+ConsoleMethodWithDocs(Skeleton, setRootBoneScale, ConsoleVoid, 3, 4, (float scaleX, float scaleY))
+{
+    F32 scaleX, scaleY;
+
+    const U32 elementCount = Utility::mGetStringElementCount(argv[2]);
+
+    // ("width height")
+    if ((elementCount == 2) && (argc == 3))
+    {
+        scaleX = dAtof(Utility::mGetStringElement(argv[2], 0));
+        scaleY = dAtof(Utility::mGetStringElement(argv[2], 1));
+    }
+
+    // (width, [height])
+    else if (elementCount == 1)
+    {
+        scaleX = dAtof(argv[2]);
+
+        if (argc > 3)
+            scaleY = dAtof(argv[3]);
+        else
+            scaleY = scaleX;
+    }
+
+    // Invalid
+    else
+    {
+        Con::warnf("Skeleton::setRootBoneScale() - Invalid number of parameters!");
+        return;
+    }
+    
+    // Set Size.
+    object->setSkeletonScale(Vector2(scaleX, scaleY));
+}  
+
+//-----------------------------------------------------------------------------
+
+/*! Gets the skeleton's root bone scale.
+    @return (float x/y height) The x and y scale of the object's root bone.
+*/
+ConsoleMethodWithDocs(Skeleton, getRootBoneScale, ConsoleString, 2, 2, ())
+{
+    return object->getSkeletonScale().scriptThis();
+}
+
+//-----------------------------------------------------------------------------
+
+/*! Sets local offset of the skeleton's root bone.
+    @param x Base x coordinate.
+    @param y Base y coordinate.
+    @return No return value.
+*/
+ConsoleMethodWithDocs(Skeleton, setRootBoneOffset, ConsoleVoid, 3, 4, (float x, float y))
+{
+    F32 x, y;
+
+    const U32 elementCount = Utility::mGetStringElementCount(argv[2]);
+
+    // ("x y")
+    if ((elementCount == 2) && (argc == 3))
+    {
+        x = dAtof(Utility::mGetStringElement(argv[2], 0));
+        y = dAtof(Utility::mGetStringElement(argv[2], 1));
+    }
+
+    // (x, [y])
+    else if (elementCount == 1)
+    {
+        x = dAtof(argv[2]);
+
+        if (argc > 3)
+            y = dAtof(argv[3]);
+        else
+            y = x;
+    }
+
+    // Invalid
+    else
+    {
+        Con::warnf("Skeleton::setRootBoneOffset() - Invalid number of parameters!");
+        return;
+    }
+    
+    // Set Size.
+    object->setSkeletonOffset(Vector2(x, y));
+}  
+
+//-----------------------------------------------------------------------------
+
+/*! Gets the skeleton's root bone offset.
+    @return (float x/y) The x and y offset of the object's root bone.
+*/
+ConsoleMethodWithDocs(Skeleton, getRootBoneOffset, ConsoleString, 2, 2, ())
+{
+    return object->getSkeletonOffset().scriptThis();
+}
+
+//-----------------------------------------------------------------------------
+
+/*! Gets the duration of the current animation.
+    @return Duration of the animation in seconds.
+*/
+ConsoleMethodWithDocs(Skeleton, getAnimationDuration, ConsoleFloat, 2, 2, ())
+{
+    return object->getAnimationDuration();
+}
+
+//-----------------------------------------------------------------------------
+
+/*! Mixes the current animation with another.
+    @param animation The name of the animation to mix.
+    @param time The time to start mixing.
+*/
+ConsoleMethodWithDocs(Skeleton, setMix, ConsoleBool, 5, 5, (fromAnimation, toAnimation, time))
+{
+    Con::printf("Mixing %s with %s at %f", argv[2], argv[3], dAtof(argv[4]));
+    
+    return object->setMix(argv[2], argv[3], dAtof(argv[4]));
+}
+
+ConsoleMethodGroupEndWithDocs(Skeleton)

+ 634 - 0
engine/source/spine/Animation.c

@@ -0,0 +1,634 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#include <spine/Animation.h>
+#include <limits.h>
+#include <spine/extension.h>
+
+spAnimation* spAnimation_create (const char* name, int timelineCount) {
+	spAnimation* self = NEW(spAnimation);
+	MALLOC_STR(self->name, name);
+	self->timelineCount = timelineCount;
+	self->timelines = MALLOC(spTimeline*, timelineCount);
+	return self;
+}
+
+void spAnimation_dispose (spAnimation* self) {
+	int i;
+	for (i = 0; i < self->timelineCount; ++i)
+		spTimeline_dispose(self->timelines[i]);
+	FREE(self->timelines);
+	FREE(self->name);
+	FREE(self);
+}
+
+void spAnimation_apply (const spAnimation* self, spSkeleton* skeleton, float lastTime, float time, int loop, spEvent** events,
+		int* eventCount) {
+	int i, n = self->timelineCount;
+
+	if (loop && self->duration) {
+		time = FMOD(time, self->duration);
+		lastTime = FMOD(lastTime, self->duration);
+	}
+
+	for (i = 0; i < n; ++i)
+		spTimeline_apply(self->timelines[i], skeleton, lastTime, time, events, eventCount, 1);
+}
+
+void spAnimation_mix (const spAnimation* self, spSkeleton* skeleton, float lastTime, float time, int loop, spEvent** events,
+		int* eventCount, float alpha) {
+	int i, n = self->timelineCount;
+
+	if (loop && self->duration) {
+		time = FMOD(time, self->duration);
+		lastTime = FMOD(lastTime, self->duration);
+	}
+
+	for (i = 0; i < n; ++i)
+		spTimeline_apply(self->timelines[i], skeleton, lastTime, time, events, eventCount, alpha);
+}
+
+/**/
+
+typedef struct _spTimelineVtable {
+	void (*apply) (const spTimeline* self, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents,
+			int* eventCount, float alpha);
+	void (*dispose) (spTimeline* self);
+} _spTimelineVtable;
+
+void _spTimeline_init (spTimeline* self, spTimelineType type, /**/
+void (*dispose) (spTimeline* self), /**/
+		void (*apply) (const spTimeline* self, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents,
+				int* eventCount, float alpha)) {
+	CONST_CAST(spTimelineType, self->type) = type;
+	CONST_CAST(_spTimelineVtable*, self->vtable) = NEW(_spTimelineVtable);
+	VTABLE(spTimeline, self)->dispose = dispose;
+	VTABLE(spTimeline, self)->apply = apply;
+}
+
+void _spTimeline_deinit (spTimeline* self) {
+	FREE(self->vtable);
+}
+
+void spTimeline_dispose (spTimeline* self) {
+	VTABLE(spTimeline, self)->dispose(self);
+}
+
+void spTimeline_apply (const spTimeline* self, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents,
+		int* eventCount, float alpha) {
+	VTABLE(spTimeline, self)->apply(self, skeleton, lastTime, time, firedEvents, eventCount, alpha);
+}
+
+/**/
+
+static const float CURVE_LINEAR = 0;
+static const float CURVE_STEPPED = -1;
+static const int CURVE_SEGMENTS = 10;
+
+void _spCurveTimeline_init (spCurveTimeline* self, spTimelineType type, int frameCount, /**/
+void (*dispose) (spTimeline* self), /**/
+		void (*apply) (const spTimeline* self, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents,
+				int* eventCount, float alpha)) {
+	_spTimeline_init(SUPER(self), type, dispose, apply);
+	self->curves = CALLOC(float, (frameCount - 1) * 6);
+}
+
+void _spCurveTimeline_deinit (spCurveTimeline* self) {
+	_spTimeline_deinit(SUPER(self));
+	FREE(self->curves);
+}
+
+void spCurveTimeline_setLinear (spCurveTimeline* self, int frameIndex) {
+	self->curves[frameIndex * 6] = CURVE_LINEAR;
+}
+
+void spCurveTimeline_setStepped (spCurveTimeline* self, int frameIndex) {
+	self->curves[frameIndex * 6] = CURVE_STEPPED;
+}
+
+void spCurveTimeline_setCurve (spCurveTimeline* self, int frameIndex, float cx1, float cy1, float cx2, float cy2) {
+	float subdiv_step = 1.0f / CURVE_SEGMENTS;
+	float subdiv_step2 = subdiv_step * subdiv_step;
+	float subdiv_step3 = subdiv_step2 * subdiv_step;
+	float pre1 = 3 * subdiv_step;
+	float pre2 = 3 * subdiv_step2;
+	float pre4 = 6 * subdiv_step2;
+	float pre5 = 6 * subdiv_step3;
+	float tmp1x = -cx1 * 2 + cx2;
+	float tmp1y = -cy1 * 2 + cy2;
+	float tmp2x = (cx1 - cx2) * 3 + 1;
+	float tmp2y = (cy1 - cy2) * 3 + 1;
+	int i = frameIndex * 6;
+	self->curves[i] = cx1 * pre1 + tmp1x * pre2 + tmp2x * subdiv_step3;
+	self->curves[i + 1] = cy1 * pre1 + tmp1y * pre2 + tmp2y * subdiv_step3;
+	self->curves[i + 2] = tmp1x * pre4 + tmp2x * pre5;
+	self->curves[i + 3] = tmp1y * pre4 + tmp2y * pre5;
+	self->curves[i + 4] = tmp2x * pre5;
+	self->curves[i + 5] = tmp2y * pre5;
+}
+
+float spCurveTimeline_getCurvePercent (const spCurveTimeline* self, int frameIndex, float percent) {
+	float dfy;
+	float ddfx;
+	float ddfy;
+	float dddfx;
+	float dddfy;
+	float x, y;
+	int i;
+	int curveIndex = frameIndex * 6;
+	float dfx = self->curves[curveIndex];
+	if (dfx == CURVE_LINEAR) return percent;
+	if (dfx == CURVE_STEPPED) return 0;
+	dfy = self->curves[curveIndex + 1];
+	ddfx = self->curves[curveIndex + 2];
+	ddfy = self->curves[curveIndex + 3];
+	dddfx = self->curves[curveIndex + 4];
+	dddfy = self->curves[curveIndex + 5];
+	x = dfx, y = dfy;
+	i = CURVE_SEGMENTS - 2;
+	while (1) {
+		if (x >= percent) {
+			float lastX = x - dfx;
+			float lastY = y - dfy;
+			return lastY + (y - lastY) * (percent - lastX) / (x - lastX);
+		}
+		if (i == 0) break;
+		i--;
+		dfx += ddfx;
+		dfy += ddfy;
+		ddfx += dddfx;
+		ddfy += dddfy;
+		x += dfx;
+		y += dfy;
+	}
+	return y + (1 - y) * (percent - x) / (1 - x); /* Last point is 1,1. */
+}
+
+/* @param target After the first and before the last entry. */
+static int binarySearch (float *values, int valuesLength, float target, int step) {
+	int low = 0, current;
+	int high = valuesLength / step - 2;
+	if (high == 0) return step;
+	current = high >> 1;
+	while (1) {
+		if (values[(current + 1) * step] <= target)
+			low = current + 1;
+		else
+			high = current;
+		if (low == high) return (low + 1) * step;
+		current = (low + high) >> 1;
+	}
+	return 0;
+}
+
+/*static int linearSearch (float *values, int valuesLength, float target, int step) {
+ int i, last = valuesLength - step;
+ for (i = 0; i <= last; i += step) {
+ if (values[i] <= target) continue;
+ return i;
+ }
+ return -1;
+ }*/
+
+/**/
+
+void _spBaseTimeline_dispose (spTimeline* timeline) {
+	struct spBaseTimeline* self = SUB_CAST(struct spBaseTimeline, timeline);
+	_spCurveTimeline_deinit(SUPER(self));
+	FREE(self->frames);
+	FREE(self);
+}
+
+/* Many timelines have structure identical to struct spBaseTimeline and extend spCurveTimeline. **/
+struct spBaseTimeline* _spBaseTimeline_create (int frameCount, spTimelineType type, int frameSize, /**/
+		void (*apply) (const spTimeline* self, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents,
+				int* eventCount, float alpha)) {
+	struct spBaseTimeline* self = NEW(struct spBaseTimeline);
+	_spCurveTimeline_init(SUPER(self), type, frameCount, _spBaseTimeline_dispose, apply);
+
+	CONST_CAST(int, self->framesLength) = frameCount * frameSize;
+	CONST_CAST(float*, self->frames) = CALLOC(float, self->framesLength);
+
+	return self;
+}
+
+/**/
+
+static const int ROTATE_LAST_FRAME_TIME = -2;
+static const int ROTATE_FRAME_VALUE = 1;
+
+void _spRotateTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents,
+		int* eventCount, float alpha) {
+	spBone *bone;
+	int frameIndex;
+	float lastFrameValue, frameTime, percent, amount;
+
+	spRotateTimeline* self = SUB_CAST(spRotateTimeline, timeline);
+
+	if (time < self->frames[0]) return; /* Time is before first frame. */
+
+	bone = skeleton->bones[self->boneIndex];
+
+	if (time >= self->frames[self->framesLength - 2]) { /* Time is after last frame. */
+		float amount = bone->data->rotation + self->frames[self->framesLength - 1] - bone->rotation;
+		while (amount > 180)
+			amount -= 360;
+		while (amount < -180)
+			amount += 360;
+		bone->rotation += amount * alpha;
+		return;
+	}
+
+	/* Interpolate between the last frame and the current frame. */
+	frameIndex = binarySearch(self->frames, self->framesLength, time, 2);
+	lastFrameValue = self->frames[frameIndex - 1];
+	frameTime = self->frames[frameIndex];
+	percent = 1 - (time - frameTime) / (self->frames[frameIndex + ROTATE_LAST_FRAME_TIME] - frameTime);
+	percent = spCurveTimeline_getCurvePercent(SUPER(self), frameIndex / 2 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
+
+	amount = self->frames[frameIndex + ROTATE_FRAME_VALUE] - lastFrameValue;
+	while (amount > 180)
+		amount -= 360;
+	while (amount < -180)
+		amount += 360;
+	amount = bone->data->rotation + (lastFrameValue + amount * percent) - bone->rotation;
+	while (amount > 180)
+		amount -= 360;
+	while (amount < -180)
+		amount += 360;
+	bone->rotation += amount * alpha;
+}
+
+spRotateTimeline* spRotateTimeline_create (int frameCount) {
+	return _spBaseTimeline_create(frameCount, TIMELINE_ROTATE, 2, _spRotateTimeline_apply);
+}
+
+void spRotateTimeline_setFrame (spRotateTimeline* self, int frameIndex, float time, float angle) {
+	frameIndex *= 2;
+	self->frames[frameIndex] = time;
+	self->frames[frameIndex + 1] = angle;
+}
+
+/**/
+
+static const int TRANSLATE_LAST_FRAME_TIME = -3;
+static const int TRANSLATE_FRAME_X = 1;
+static const int TRANSLATE_FRAME_Y = 2;
+
+void _spTranslateTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton, float lastTime, float time,
+		spEvent** firedEvents, int* eventCount, float alpha) {
+	spBone *bone;
+	int frameIndex;
+	float lastFrameX, lastFrameY, frameTime, percent;
+
+	spTranslateTimeline* self = SUB_CAST(spTranslateTimeline, timeline);
+
+	if (time < self->frames[0]) return; /* Time is before first frame. */
+
+	bone = skeleton->bones[self->boneIndex];
+
+	if (time >= self->frames[self->framesLength - 3]) { /* Time is after last frame. */
+		bone->x += (bone->data->x + self->frames[self->framesLength - 2] - bone->x) * alpha;
+		bone->y += (bone->data->y + self->frames[self->framesLength - 1] - bone->y) * alpha;
+		return;
+	}
+
+	/* Interpolate between the last frame and the current frame. */
+	frameIndex = binarySearch(self->frames, self->framesLength, time, 3);
+	lastFrameX = self->frames[frameIndex - 2];
+	lastFrameY = self->frames[frameIndex - 1];
+	frameTime = self->frames[frameIndex];
+	percent = 1 - (time - frameTime) / (self->frames[frameIndex + TRANSLATE_LAST_FRAME_TIME] - frameTime);
+	percent = spCurveTimeline_getCurvePercent(SUPER(self), frameIndex / 3 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
+
+	bone->x += (bone->data->x + lastFrameX + (self->frames[frameIndex + TRANSLATE_FRAME_X] - lastFrameX) * percent - bone->x)
+			* alpha;
+	bone->y += (bone->data->y + lastFrameY + (self->frames[frameIndex + TRANSLATE_FRAME_Y] - lastFrameY) * percent - bone->y)
+			* alpha;
+}
+
+spTranslateTimeline* spTranslateTimeline_create (int frameCount) {
+	return _spBaseTimeline_create(frameCount, TIMELINE_TRANLATE, 3, _spTranslateTimeline_apply);
+}
+
+void spTranslateTimeline_setFrame (spTranslateTimeline* self, int frameIndex, float time, float x, float y) {
+	frameIndex *= 3;
+	self->frames[frameIndex] = time;
+	self->frames[frameIndex + 1] = x;
+	self->frames[frameIndex + 2] = y;
+}
+
+/**/
+
+void _spScaleTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents,
+		int* eventCount, float alpha) {
+	spBone *bone;
+	int frameIndex;
+	float lastFrameX, lastFrameY, frameTime, percent;
+
+	spScaleTimeline* self = SUB_CAST(spScaleTimeline, timeline);
+
+	if (time < self->frames[0]) return; /* Time is before first frame. */
+
+	bone = skeleton->bones[self->boneIndex];
+	if (time >= self->frames[self->framesLength - 3]) { /* Time is after last frame. */
+		bone->scaleX += (bone->data->scaleX - 1 + self->frames[self->framesLength - 2] - bone->scaleX) * alpha;
+		bone->scaleY += (bone->data->scaleY - 1 + self->frames[self->framesLength - 1] - bone->scaleY) * alpha;
+		return;
+	}
+
+	/* Interpolate between the last frame and the current frame. */
+	frameIndex = binarySearch(self->frames, self->framesLength, time, 3);
+	lastFrameX = self->frames[frameIndex - 2];
+	lastFrameY = self->frames[frameIndex - 1];
+	frameTime = self->frames[frameIndex];
+	percent = 1 - (time - frameTime) / (self->frames[frameIndex + TRANSLATE_LAST_FRAME_TIME] - frameTime);
+	percent = spCurveTimeline_getCurvePercent(SUPER(self), frameIndex / 3 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
+
+	bone->scaleX += (bone->data->scaleX - 1 + lastFrameX + (self->frames[frameIndex + TRANSLATE_FRAME_X] - lastFrameX) * percent
+			- bone->scaleX) * alpha;
+	bone->scaleY += (bone->data->scaleY - 1 + lastFrameY + (self->frames[frameIndex + TRANSLATE_FRAME_Y] - lastFrameY) * percent
+			- bone->scaleY) * alpha;
+}
+
+spScaleTimeline* spScaleTimeline_create (int frameCount) {
+	return _spBaseTimeline_create(frameCount, TIMELINE_SCALE, 3, _spScaleTimeline_apply);
+}
+
+void spScaleTimeline_setFrame (spScaleTimeline* self, int frameIndex, float time, float x, float y) {
+	spTranslateTimeline_setFrame(self, frameIndex, time, x, y);
+}
+
+/**/
+
+static const int COLOR_LAST_FRAME_TIME = -5;
+static const int COLOR_FRAME_R = 1;
+static const int COLOR_FRAME_G = 2;
+static const int COLOR_FRAME_B = 3;
+static const int COLOR_FRAME_A = 4;
+
+void _spColorTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents,
+		int* eventCount, float alpha) {
+	spSlot *slot;
+	int frameIndex;
+	float lastFrameR, lastFrameG, lastFrameB, lastFrameA, percent, frameTime;
+	float r, g, b, a;
+	spColorTimeline* self = (spColorTimeline*)timeline;
+
+	if (time < self->frames[0]) return; /* Time is before first frame. */
+
+	slot = skeleton->slots[self->slotIndex];
+
+	if (time >= self->frames[self->framesLength - 5]) { /* Time is after last frame. */
+		int i = self->framesLength - 1;
+		slot->r = self->frames[i - 3];
+		slot->g = self->frames[i - 2];
+		slot->b = self->frames[i - 1];
+		slot->a = self->frames[i];
+		return;
+	}
+
+	/* Interpolate between the last frame and the current frame. */
+	frameIndex = binarySearch(self->frames, self->framesLength, time, 5);
+	lastFrameR = self->frames[frameIndex - 4];
+	lastFrameG = self->frames[frameIndex - 3];
+	lastFrameB = self->frames[frameIndex - 2];
+	lastFrameA = self->frames[frameIndex - 1];
+	frameTime = self->frames[frameIndex];
+	percent = 1 - (time - frameTime) / (self->frames[frameIndex + COLOR_LAST_FRAME_TIME] - frameTime);
+	percent = spCurveTimeline_getCurvePercent(SUPER(self), frameIndex / 5 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
+
+	r = lastFrameR + (self->frames[frameIndex + COLOR_FRAME_R] - lastFrameR) * percent;
+	g = lastFrameG + (self->frames[frameIndex + COLOR_FRAME_G] - lastFrameG) * percent;
+	b = lastFrameB + (self->frames[frameIndex + COLOR_FRAME_B] - lastFrameB) * percent;
+	a = lastFrameA + (self->frames[frameIndex + COLOR_FRAME_A] - lastFrameA) * percent;
+	if (alpha < 1) {
+		slot->r += (r - slot->r) * alpha;
+		slot->g += (g - slot->g) * alpha;
+		slot->b += (b - slot->b) * alpha;
+		slot->a += (a - slot->a) * alpha;
+	} else {
+		slot->r = r;
+		slot->g = g;
+		slot->b = b;
+		slot->a = a;
+	}
+}
+
+spColorTimeline* spColorTimeline_create (int frameCount) {
+	return (spColorTimeline*)_spBaseTimeline_create(frameCount, TIMELINE_COLOR, 5, _spColorTimeline_apply);
+}
+
+void spColorTimeline_setFrame (spColorTimeline* self, int frameIndex, float time, float r, float g, float b, float a) {
+	frameIndex *= 5;
+	self->frames[frameIndex] = time;
+	self->frames[frameIndex + 1] = r;
+	self->frames[frameIndex + 2] = g;
+	self->frames[frameIndex + 3] = b;
+	self->frames[frameIndex + 4] = a;
+}
+
+/**/
+
+void _spAttachmentTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton, float lastTime, float time,
+		spEvent** firedEvents, int* eventCount, float alpha) {
+	int frameIndex;
+	const char* attachmentName;
+	spAttachmentTimeline* self = (spAttachmentTimeline*)timeline;
+
+	if (time < self->frames[0]) return; /* Time is before first frame. */
+
+	if (time >= self->frames[self->framesLength - 1]) /* Time is after last frame. */
+		frameIndex = self->framesLength - 1;
+	else
+		frameIndex = binarySearch(self->frames, self->framesLength, time, 1) - 1;
+
+	attachmentName = self->attachmentNames[frameIndex];
+	spSlot_setAttachment(skeleton->slots[self->slotIndex],
+			attachmentName ? spSkeleton_getAttachmentForSlotIndex(skeleton, self->slotIndex, attachmentName) : 0);
+}
+
+void _spAttachmentTimeline_dispose (spTimeline* timeline) {
+	spAttachmentTimeline* self = SUB_CAST(spAttachmentTimeline, timeline);
+	int i;
+
+	_spTimeline_deinit(timeline);
+
+	for (i = 0; i < self->framesLength; ++i)
+		FREE(self->attachmentNames[i]);
+	FREE(self->attachmentNames);
+	FREE(self->frames);
+	FREE(self);
+}
+
+spAttachmentTimeline* spAttachmentTimeline_create (int frameCount) {
+	spAttachmentTimeline* self = NEW(spAttachmentTimeline);
+	_spTimeline_init(SUPER(self), TIMELINE_ATTACHMENT, _spAttachmentTimeline_dispose, _spAttachmentTimeline_apply);
+
+	CONST_CAST(int, self->framesLength) = frameCount;
+	CONST_CAST(float*, self->frames) = CALLOC(float, frameCount);
+	CONST_CAST(char**, self->attachmentNames) = CALLOC(char*, frameCount);
+
+	return self;
+}
+
+void spAttachmentTimeline_setFrame (spAttachmentTimeline* self, int frameIndex, float time, const char* attachmentName) {
+	self->frames[frameIndex] = time;
+
+	FREE(self->attachmentNames[frameIndex]);
+	if (attachmentName)
+		MALLOC_STR(self->attachmentNames[frameIndex], attachmentName);
+	else
+		self->attachmentNames[frameIndex] = 0;
+}
+
+/**/
+
+/** Fires events for frames > lastTime and <= time. */
+void _spEventTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents,
+		int* eventCount, float alpha) {
+	spEventTimeline* self = (spEventTimeline*)timeline;
+	int frameIndex;
+	if (!firedEvents) return;
+
+	if (lastTime > time) { /* Fire events after last time for looped animations. */
+		_spEventTimeline_apply(timeline, skeleton, lastTime, (float)INT_MAX, firedEvents, eventCount, alpha);
+		lastTime = -1;
+	} else if (lastTime >= self->frames[self->framesLength - 1]) /* Last time is after last frame. */
+		return;
+	if (time < self->frames[0]) return; /* Time is before first frame. */
+
+	if (lastTime < self->frames[0])
+		frameIndex = 0;
+	else {
+		float frame;
+		frameIndex = binarySearch(self->frames, self->framesLength, lastTime, 1);
+		frame = self->frames[frameIndex];
+		while (frameIndex > 0) { /* Fire multiple events with the same frame. */
+			if (self->frames[frameIndex - 1] != frame) break;
+			frameIndex--;
+		}
+	}
+	for (; frameIndex < self->framesLength && time >= self->frames[frameIndex]; frameIndex++) {
+		firedEvents[*eventCount] = self->events[frameIndex];
+		(*eventCount)++;
+	}
+}
+
+void _spEventTimeline_dispose (spTimeline* timeline) {
+	spEventTimeline* self = SUB_CAST(spEventTimeline, timeline);
+	int i;
+
+	_spTimeline_deinit(timeline);
+
+	for (i = 0; i < self->framesLength; ++i)
+		spEvent_dispose(self->events[i]);
+	FREE(self->events);
+	FREE(self->frames);
+	FREE(self);
+}
+
+spEventTimeline* spEventTimeline_create (int frameCount) {
+	spEventTimeline* self = NEW(spEventTimeline);
+	_spTimeline_init(SUPER(self), TIMELINE_EVENT, _spEventTimeline_dispose, _spEventTimeline_apply);
+
+	CONST_CAST(int, self->framesLength) = frameCount;
+	CONST_CAST(float*, self->frames) = CALLOC(float, frameCount);
+	CONST_CAST(spEvent**, self->events) = CALLOC(spEvent*, frameCount);
+
+	return self;
+}
+
+void spEventTimeline_setFrame (spEventTimeline* self, int frameIndex, float time, spEvent* event) {
+	self->frames[frameIndex] = time;
+
+	FREE(self->events[frameIndex]);
+	self->events[frameIndex] = event;
+}
+
+/**/
+
+void _spDrawOrderTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton, float lastTime, float time,
+		spEvent** firedEvents, int* eventCount, float alpha) {
+	int i;
+	int frameIndex;
+	const int* drawOrderToSetupIndex;
+	spDrawOrderTimeline* self = (spDrawOrderTimeline*)timeline;
+
+	if (time < self->frames[0]) return; /* Time is before first frame. */
+
+	if (time >= self->frames[self->framesLength - 1]) /* Time is after last frame. */
+		frameIndex = self->framesLength - 1;
+	else
+		frameIndex = binarySearch(self->frames, self->framesLength, time, 1) - 1;
+
+	drawOrderToSetupIndex = self->drawOrders[frameIndex];
+	if (!drawOrderToSetupIndex)
+		memcpy(skeleton->drawOrder, skeleton->slots, self->slotCount * sizeof(int));
+	else {
+		for (i = 0; i < self->slotCount; i++)
+			skeleton->drawOrder[i] = skeleton->slots[drawOrderToSetupIndex[i]];
+	}
+}
+
+void _spDrawOrderTimeline_dispose (spTimeline* timeline) {
+	spDrawOrderTimeline* self = SUB_CAST(spDrawOrderTimeline, timeline);
+	int i;
+
+	_spTimeline_deinit(timeline);
+
+	for (i = 0; i < self->framesLength; ++i)
+		FREE(self->drawOrders[i]);
+	FREE(self->drawOrders);
+	FREE(self->frames);
+	FREE(self);
+}
+
+spDrawOrderTimeline* spDrawOrderTimeline_create (int frameCount, int slotCount) {
+	spDrawOrderTimeline* self = NEW(spDrawOrderTimeline);
+	_spTimeline_init(SUPER(self), TIMELINE_DRAWORDER, _spDrawOrderTimeline_dispose, _spDrawOrderTimeline_apply);
+
+	CONST_CAST(int, self->framesLength) = frameCount;
+	CONST_CAST(float*, self->frames) = CALLOC(float, frameCount);
+	CONST_CAST(int**, self->drawOrders) = CALLOC(int*, frameCount);
+	CONST_CAST(int, self->slotCount) = slotCount;
+
+	return self;
+}
+
+void spDrawOrderTimeline_setFrame (spDrawOrderTimeline* self, int frameIndex, float time, const int* drawOrder) {
+	self->frames[frameIndex] = time;
+
+	FREE(self->drawOrders[frameIndex]);
+	if (!drawOrder)
+		self->drawOrders[frameIndex] = 0;
+	else {
+		self->drawOrders[frameIndex] = MALLOC(int, self->slotCount);
+		memcpy(CONST_CAST(int*, self->drawOrders[frameIndex]), drawOrder, self->slotCount * sizeof(int));
+	}
+}

+ 249 - 0
engine/source/spine/Animation.h

@@ -0,0 +1,249 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef SPINE_ANIMATION_H_
+#define SPINE_ANIMATION_H_
+
+#include <spine/Event.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct spTimeline spTimeline;
+struct spSkeleton;
+
+typedef struct {
+	const char* const name;
+	float duration;
+
+	int timelineCount;
+	spTimeline** timelines;
+} spAnimation;
+
+spAnimation* spAnimation_create (const char* name, int timelineCount);
+void spAnimation_dispose (spAnimation* self);
+
+/** Poses the skeleton at the specified time for this animation.
+ * @param lastTime The last time the animation was applied.
+ * @param events Any triggered events are added. */
+void spAnimation_apply (const spAnimation* self, struct spSkeleton* skeleton, float lastTime, float time, int loop,
+		spEvent** events, int* eventCount);
+
+/** Poses the skeleton at the specified time for this animation mixed with the current pose.
+ * @param lastTime The last time the animation was applied.
+ * @param events Any triggered events are added.
+ * @param alpha The amount of this animation that affects the current pose. */
+void spAnimation_mix (const spAnimation* self, struct spSkeleton* skeleton, float lastTime, float time, int loop,
+		spEvent** events, int* eventCount, float alpha);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spAnimation Animation;
+#define Animation_create(...) spAnimation_create(__VA_ARGS__)
+#define Animation_dispose(...) spAnimation_dispose(__VA_ARGS__)
+#define Animation_apply(...) spAnimation_apply(__VA_ARGS__)
+#define Animation_mix(...) spAnimation_mix(__VA_ARGS__)
+#endif
+
+/**/
+
+typedef enum {
+	TIMELINE_SCALE, TIMELINE_ROTATE, TIMELINE_TRANLATE, TIMELINE_COLOR, TIMELINE_ATTACHMENT, TIMELINE_EVENT, TIMELINE_DRAWORDER
+} spTimelineType;
+
+struct spTimeline {
+	const spTimelineType type;
+
+	const void* const vtable;
+};
+
+void spTimeline_dispose (spTimeline* self);
+void spTimeline_apply (const spTimeline* self, struct spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents,
+		int* eventCount, float alpha);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spTimeline Timeline;
+#define Timeline_dispose(...) spTimeline_dispose(__VA_ARGS__)
+#define Timeline_apply(...) spTimeline_apply(__VA_ARGS__)
+#endif
+
+/**/
+
+typedef struct {
+	spTimeline super;
+	float* curves; /* dfx, dfy, ddfx, ddfy, dddfx, dddfy, ... */
+} spCurveTimeline;
+
+void spCurveTimeline_setLinear (spCurveTimeline* self, int frameIndex);
+void spCurveTimeline_setStepped (spCurveTimeline* self, int frameIndex);
+
+/* Sets the control handle positions for an interpolation bezier curve used to transition from this keyframe to the next.
+ * cx1 and cx2 are from 0 to 1, representing the percent of time between the two keyframes. cy1 and cy2 are the percent of
+ * the difference between the keyframe's values. */
+void spCurveTimeline_setCurve (spCurveTimeline* self, int frameIndex, float cx1, float cy1, float cx2, float cy2);
+float spCurveTimeline_getCurvePercent (const spCurveTimeline* self, int frameIndex, float percent);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spCurveTimeline CurveTimeline;
+#define CurveTimeline_setLinear(...) spCurveTimeline_setLinear(__VA_ARGS__)
+#define CurveTimeline_setStepped(...) spCurveTimeline_setStepped(__VA_ARGS__)
+#define CurveTimeline_setCurve(...) spCurveTimeline_setCurve(__VA_ARGS__)
+#define CurveTimeline_getCurvePercent(...) spCurveTimeline_getCurvePercent(__VA_ARGS__)
+#endif
+
+/**/
+
+typedef struct spBaseTimeline {
+	spCurveTimeline super;
+	int const framesLength;
+	float* const frames; /* time, angle, ... for rotate. time, x, y, ... for translate and scale. */
+	int boneIndex;
+} spRotateTimeline;
+
+spRotateTimeline* spRotateTimeline_create (int frameCount);
+
+void spRotateTimeline_setFrame (spRotateTimeline* self, int frameIndex, float time, float angle);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spRotateTimeline RotateTimeline;
+#define RotateTimeline_create(...) spRotateTimeline_create(__VA_ARGS__)
+#define RotateTimeline_setFrame(...) spRotateTimeline_setFrame(__VA_ARGS__)
+#endif
+
+/**/
+
+typedef struct spBaseTimeline spTranslateTimeline;
+
+spTranslateTimeline* spTranslateTimeline_create (int frameCount);
+
+void spTranslateTimeline_setFrame (spTranslateTimeline* self, int frameIndex, float time, float x, float y);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spTranslateTimeline TranslateTimeline;
+#define TranslateTimeline_create(...) spTranslateTimeline_create(__VA_ARGS__)
+#define TranslateTimeline_setFrame(...) spTranslateTimeline_setFrame(__VA_ARGS__)
+#endif
+
+/**/
+
+typedef struct spBaseTimeline spScaleTimeline;
+
+spScaleTimeline* spScaleTimeline_create (int frameCount);
+
+void spScaleTimeline_setFrame (spScaleTimeline* self, int frameIndex, float time, float x, float y);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spScaleTimeline ScaleTimeline;
+#define ScaleTimeline_create(...) spScaleTimeline_create(__VA_ARGS__)
+#define ScaleTimeline_setFrame(...) spScaleTimeline_setFrame(__VA_ARGS__)
+#endif
+
+/**/
+
+typedef struct {
+	spCurveTimeline super;
+	int const framesLength;
+	float* const frames; /* time, r, g, b, a, ... */
+	int slotIndex;
+} spColorTimeline;
+
+spColorTimeline* spColorTimeline_create (int frameCount);
+
+void spColorTimeline_setFrame (spColorTimeline* self, int frameIndex, float time, float r, float g, float b, float a);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spColorTimeline ColorTimeline;
+#define ColorTimeline_create(...) spColorTimeline_create(__VA_ARGS__)
+#define ColorTimeline_setFrame(...) spColorTimeline_setFrame(__VA_ARGS__)
+#endif
+
+/**/
+
+typedef struct {
+	spTimeline super;
+	int const framesLength;
+	float* const frames; /* time, ... */
+	int slotIndex;
+	const char** const attachmentNames;
+} spAttachmentTimeline;
+
+spAttachmentTimeline* spAttachmentTimeline_create (int frameCount);
+
+/* @param attachmentName May be 0. */
+void spAttachmentTimeline_setFrame (spAttachmentTimeline* self, int frameIndex, float time, const char* attachmentName);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spAttachmentTimeline AttachmentTimeline;
+#define AttachmentTimeline_create(...) spAttachmentTimeline_create(__VA_ARGS__)
+#define AttachmentTimeline_setFrame(...) spAttachmentTimeline_setFrame(__VA_ARGS__)
+#endif
+
+/**/
+
+typedef struct {
+	spTimeline super;
+	int const framesLength;
+	float* const frames; /* time, ... */
+	spEvent** const events;
+} spEventTimeline;
+
+spEventTimeline* spEventTimeline_create (int frameCount);
+
+void spEventTimeline_setFrame (spEventTimeline* self, int frameIndex, float time, spEvent* event);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spEventTimeline EventTimeline;
+#define EventTimeline_create(...) spEventTimeline_create(__VA_ARGS__)
+#define EventTimeline_setFrame(...) spEventTimeline_setFrame(__VA_ARGS__)
+#endif
+
+/**/
+
+typedef struct {
+	spTimeline super;
+	int const framesLength;
+	float* const frames; /* time, ... */
+	const int** const drawOrders;
+	int const slotCount;
+} spDrawOrderTimeline;
+
+spDrawOrderTimeline* spDrawOrderTimeline_create (int frameCount, int slotCount);
+
+void spDrawOrderTimeline_setFrame (spDrawOrderTimeline* self, int frameIndex, float time, const int* drawOrder);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spDrawOrderTimeline DrawOrderTimeline;
+#define DrawOrderTimeline_create(...) spDrawOrderTimeline_create(__VA_ARGS__)
+#define DrawOrderTimeline_setFrame(...) spDrawOrderTimeline_setFrame(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_ANIMATION_H_ */

+ 282 - 0
engine/source/spine/AnimationState.c

@@ -0,0 +1,282 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#include <spine/Animation.h>
+#include <spine/AnimationState.h>
+#include <spine/AnimationStateData.h>
+#include <spine/Event.h>
+#include <spine/extension.h>
+#include <spine/Skeleton.h>
+#include <spine/SkeletonData.h>
+#include <string.h>
+
+spTrackEntry* _spTrackEntry_create () {
+	spTrackEntry* entry = NEW(spTrackEntry);
+	entry->timeScale = 1;
+	entry->lastTime = -1;
+	return entry;
+}
+
+void _spTrackEntry_dispose (spTrackEntry* entry) {
+	FREE(entry);
+}
+
+void _spTrackEntry_disposeAll (spTrackEntry* entry) {
+	while (entry) {
+		spTrackEntry* next = entry->next;
+		_spTrackEntry_dispose(entry);
+		entry = next;
+	}
+}
+
+/**/
+
+typedef struct {
+	spAnimationState super;
+	spEvent** events;
+} _spAnimationState;
+
+void _spAnimationState_setCurrent (spAnimationState* self, int index, spTrackEntry* entry);
+
+spAnimationState* spAnimationState_create (spAnimationStateData* data) {
+	_spAnimationState* internal = NEW(_spAnimationState);
+	spAnimationState* self = SUPER(internal);
+	internal->events = MALLOC(spEvent*, 64);
+	self->timeScale = 1;
+	CONST_CAST(spAnimationStateData*, self->data) = data;
+	return self;
+}
+
+void spAnimationState_dispose (spAnimationState* self) {
+	int i;
+	_spAnimationState* internal = SUB_CAST(_spAnimationState, self);
+	FREE(internal->events);
+	for (i = 0; i < self->trackCount; i++)
+		_spTrackEntry_disposeAll(self->tracks[i]);
+	FREE(self->tracks);
+	FREE(self);
+}
+
+void spAnimationState_update (spAnimationState* self, float delta) {
+	int i;
+	float trackDelta;
+	delta *= self->timeScale;
+	for (i = 0; i < self->trackCount; i++) {
+		spTrackEntry* current = self->tracks[i];
+		if (!current) continue;
+
+		trackDelta = delta * current->timeScale;
+		current->time += trackDelta;
+		if (current->previous) {
+			current->previous->time += trackDelta;
+			current->mixTime += trackDelta;
+		}
+
+		if (current->next) {
+			if (current->lastTime >= current->next->delay) _spAnimationState_setCurrent(self, i, current->next);
+		} else {
+			/* End non-looping animation when it reaches its end time and there is no next entry. */
+			if (!current->loop && current->lastTime >= current->endTime) spAnimationState_clearTrack(self, i);
+		}
+	}
+}
+
+void spAnimationState_apply (spAnimationState* self, spSkeleton* skeleton) {
+	_spAnimationState* internal = SUB_CAST(_spAnimationState, self);
+
+	int i, ii;
+	int eventCount;
+	float time;
+	spTrackEntry* previous;
+	for (i = 0; i < self->trackCount; i++) {
+		spTrackEntry* current = self->tracks[i];
+		if (!current) continue;
+
+		eventCount = 0;
+
+		time = current->time;
+		if (!current->loop && time > current->endTime) time = current->endTime;
+
+		previous = current->previous;
+		if (!previous) {
+			spAnimation_apply(current->animation, skeleton, current->lastTime, time, current->loop, internal->events, &eventCount);
+		} else {
+			float alpha = current->mixTime / current->mixDuration;
+
+			float previousTime = previous->time;
+			if (!previous->loop && previousTime > previous->endTime) previousTime = previous->endTime;
+			spAnimation_apply(previous->animation, skeleton, previousTime, previousTime, previous->loop, 0, 0);
+
+			if (alpha >= 1) {
+				alpha = 1;
+				_spTrackEntry_dispose(current->previous);
+				current->previous = 0;
+			}
+			spAnimation_mix(current->animation, skeleton, current->lastTime, time, current->loop, internal->events, &eventCount,
+					alpha);
+		}
+
+		for (ii = 0; ii < eventCount; ii++) {
+			spEvent* event = internal->events[ii];
+			if (current->listener) current->listener(self, i, ANIMATION_EVENT, event, 0);
+			if (self->listener) self->listener(self, i, ANIMATION_EVENT, event, 0);
+		}
+
+		/* Check if completed the animation or a loop iteration. */
+		if (current->loop ? (FMOD(current->lastTime, current->endTime) > FMOD(time, current->endTime))
+				: (current->lastTime < current->endTime && time >= current->endTime)) {
+			int count = (int)(time / current->endTime);
+			if (current->listener) current->listener(self, i, ANIMATION_COMPLETE, 0, count);
+			if (self->listener) self->listener(self, i, ANIMATION_COMPLETE, 0, count);
+			if (i >= self->trackCount || self->tracks[i] != current) continue;
+		}
+
+		if (i >= self->trackCount || self->tracks[i] != current) continue;
+		current->lastTime = current->time;
+	}
+}
+
+void spAnimationState_clearTracks (spAnimationState* self) {
+	int i;
+	for (i = 0; i < self->trackCount; i++)
+		spAnimationState_clearTrack(self, i);
+	self->trackCount = 0;
+}
+
+void spAnimationState_clearTrack (spAnimationState* self, int trackIndex) {
+	spTrackEntry* current;
+	if (trackIndex >= self->trackCount) return;
+	current = self->tracks[trackIndex];
+	if (!current) return;
+
+	if (current->listener) current->listener(self, trackIndex, ANIMATION_END, 0, 0);
+	if (self->listener) self->listener(self, trackIndex, ANIMATION_END, 0, 0);
+
+	self->tracks[trackIndex] = 0;
+	if (current->previous) _spTrackEntry_dispose(current->previous);
+	_spTrackEntry_disposeAll(current);
+}
+
+spTrackEntry* _spAnimationState_expandToIndex (spAnimationState* self, int index) {
+	spTrackEntry** newTracks;
+	if (index < self->trackCount) return self->tracks[index];
+	newTracks = CALLOC(spTrackEntry*, index + 1);
+	memcpy(newTracks, self->tracks, self->trackCount * sizeof(spTrackEntry*));
+	FREE(self->tracks);
+	self->tracks = newTracks;
+	self->trackCount = index + 1;
+	return 0;
+}
+
+void _spAnimationState_setCurrent (spAnimationState* self, int index, spTrackEntry* entry) {
+	spTrackEntry* current = _spAnimationState_expandToIndex(self, index);
+	if (current) {
+		spTrackEntry* previous = current->previous;
+		current->previous = 0;
+
+		if (current->listener) current->listener(self, index, ANIMATION_END, 0, 0);
+		if (self->listener) self->listener(self, index, ANIMATION_END, 0, 0);
+
+		entry->mixDuration = spAnimationStateData_getMix(self->data, current->animation, entry->animation);
+		if (entry->mixDuration > 0) {
+			entry->mixTime = 0;
+			/* If a mix is in progress, mix from the closest animation. */
+			if (previous && current->mixTime / current->mixDuration < 0.5f) {
+				entry->previous = previous;
+				previous = current;
+			} else
+				entry->previous = current;
+		} else
+			_spTrackEntry_dispose(current);
+
+		if (previous) _spTrackEntry_dispose(previous);
+	}
+
+	self->tracks[index] = entry;
+
+	if (entry->listener) entry->listener(self, index, ANIMATION_START, 0, 0);
+	if (self->listener) self->listener(self, index, ANIMATION_START, 0, 0);
+}
+
+spTrackEntry* spAnimationState_setAnimationByName (spAnimationState* self, int trackIndex, const char* animationName,
+		int/*bool*/loop) {
+	spAnimation* animation = spSkeletonData_findAnimation(self->data->skeletonData, animationName);
+	return spAnimationState_setAnimation(self, trackIndex, animation, loop);
+}
+
+spTrackEntry* spAnimationState_setAnimation (spAnimationState* self, int trackIndex, spAnimation* animation, int/*bool*/loop) {
+	spTrackEntry* entry;
+	spTrackEntry* current = _spAnimationState_expandToIndex(self, trackIndex);
+	if (current) _spTrackEntry_disposeAll(current->next);
+
+	entry = _spTrackEntry_create();
+	entry->animation = animation;
+	entry->loop = loop;
+	entry->endTime = animation->duration;
+	_spAnimationState_setCurrent(self, trackIndex, entry);
+	return entry;
+}
+
+spTrackEntry* spAnimationState_addAnimationByName (spAnimationState* self, int trackIndex, const char* animationName,
+		int/*bool*/loop, float delay) {
+	spAnimation* animation = spSkeletonData_findAnimation(self->data->skeletonData, animationName);
+	return spAnimationState_addAnimation(self, trackIndex, animation, loop, delay);
+}
+
+spTrackEntry* spAnimationState_addAnimation (spAnimationState* self, int trackIndex, spAnimation* animation, int/*bool*/loop,
+		float delay) {
+	spTrackEntry* last;
+
+	spTrackEntry* entry = _spTrackEntry_create();
+	entry->animation = animation;
+	entry->loop = loop;
+	entry->endTime = animation->duration;
+
+	last = _spAnimationState_expandToIndex(self, trackIndex);
+	if (last) {
+		while (last->next)
+			last = last->next;
+		last->next = entry;
+	} else
+		self->tracks[trackIndex] = entry;
+
+	if (delay <= 0) {
+		if (last)
+			delay += last->endTime - spAnimationStateData_getMix(self->data, last->animation, animation);
+		else
+			delay = 0;
+	}
+	entry->delay = delay;
+
+	return entry;
+}
+
+spTrackEntry* spAnimationState_getCurrent (spAnimationState* self, int trackIndex) {
+	if (trackIndex >= self->trackCount) return 0;
+	return self->tracks[trackIndex];
+}

+ 116 - 0
engine/source/spine/AnimationState.h

@@ -0,0 +1,116 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef SPINE_ANIMATIONSTATE_H_
+#define SPINE_ANIMATIONSTATE_H_
+
+#include <spine/Animation.h>
+#include <spine/AnimationStateData.h>
+#include <spine/Event.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+	ANIMATION_START, ANIMATION_END, ANIMATION_COMPLETE, ANIMATION_EVENT
+} spEventType;
+
+typedef struct spAnimationState spAnimationState;
+
+typedef void (*spAnimationStateListener) (spAnimationState* state, int trackIndex, spEventType type, spEvent* event,
+		int loopCount);
+
+typedef struct spTrackEntry spTrackEntry;
+struct spTrackEntry {
+	spTrackEntry* next;
+	spTrackEntry* previous;
+	spAnimation* animation;
+	int/*bool*/loop;
+	float delay, time, lastTime, endTime, timeScale;
+	spAnimationStateListener listener;
+	float mixTime, mixDuration;
+};
+
+struct spAnimationState {
+	spAnimationStateData* const data;
+	float timeScale;
+	spAnimationStateListener listener;
+	void* context;
+
+	int trackCount;
+	spTrackEntry** tracks;
+};
+
+/* @param data May be 0 for no mixing. */
+spAnimationState* spAnimationState_create (spAnimationStateData* data);
+void spAnimationState_dispose (spAnimationState* self);
+
+void spAnimationState_update (spAnimationState* self, float delta);
+void spAnimationState_apply (spAnimationState* self, struct spSkeleton* skeleton);
+
+void spAnimationState_clearTracks (spAnimationState* self);
+void spAnimationState_clearTrack (spAnimationState* self, int trackIndex);
+
+/** Set the current animation. Any queued animations are cleared. */
+spTrackEntry* spAnimationState_setAnimationByName (spAnimationState* self, int trackIndex, const char* animationName,
+		int/*bool*/loop);
+spTrackEntry* spAnimationState_setAnimation (spAnimationState* self, int trackIndex, spAnimation* animation, int/*bool*/loop);
+
+/** Adds an animation to be played delay seconds after the current or last queued animation, taking into account any mix
+ * duration. */
+spTrackEntry* spAnimationState_addAnimationByName (spAnimationState* self, int trackIndex, const char* animationName,
+		int/*bool*/loop, float delay);
+spTrackEntry* spAnimationState_addAnimation (spAnimationState* self, int trackIndex, spAnimation* animation, int/*bool*/loop,
+		float delay);
+
+spTrackEntry* spAnimationState_getCurrent (spAnimationState* self, int trackIndex);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spEventType EventType;
+typedef spAnimationStateListener AnimationStateListener;
+typedef spTrackEntry TrackEntry;
+typedef spAnimationState AnimationState;
+#define AnimationState_create(...) spAnimationState_create(__VA_ARGS__)
+#define AnimationState_dispose(...) spAnimationState_dispose(__VA_ARGS__)
+#define AnimationState_update(...) spAnimationState_update(__VA_ARGS__)
+#define AnimationState_apply(...) spAnimationState_apply(__VA_ARGS__)
+#define AnimationState_clearTracks(...) spAnimationState_clearTracks(__VA_ARGS__)
+#define AnimationState_clearTrack(...) spAnimationState_clearTrack(__VA_ARGS__)
+#define AnimationState_setAnimationByName(...) spAnimationState_setAnimationByName(__VA_ARGS__)
+#define AnimationState_setAnimation(...) spAnimationState_setAnimation(__VA_ARGS__)
+#define AnimationState_addAnimationByName(...) spAnimationState_addAnimationByName(__VA_ARGS__)
+#define AnimationState_addAnimation(...) spAnimationState_addAnimation(__VA_ARGS__)
+#define AnimationState_getCurrent(...) spAnimationState_getCurrent(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_ANIMATIONSTATE_H_ */

+ 149 - 0
engine/source/spine/AnimationStateData.c

@@ -0,0 +1,149 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#include <spine/AnimationStateData.h>
+#include <spine/extension.h>
+
+typedef struct _ToEntry _ToEntry;
+struct _ToEntry {
+	spAnimation* animation;
+	float duration;
+	_ToEntry* next;
+};
+
+_ToEntry* _ToEntry_create (spAnimation* to, float duration) {
+	_ToEntry* self = NEW(_ToEntry);
+	self->animation = to;
+	self->duration = duration;
+	return self;
+}
+
+void _ToEntry_dispose (_ToEntry* self) {
+	FREE(self);
+}
+
+/**/
+
+typedef struct _FromEntry _FromEntry;
+struct _FromEntry {
+	spAnimation* animation;
+	_ToEntry* toEntries;
+	_FromEntry* next;
+};
+
+_FromEntry* _FromEntry_create (spAnimation* from) {
+	_FromEntry* self = NEW(_FromEntry);
+	self->animation = from;
+	return self;
+}
+
+void _FromEntry_dispose (_FromEntry* self) {
+	FREE(self);
+}
+
+/**/
+
+spAnimationStateData* spAnimationStateData_create (spSkeletonData* skeletonData) {
+	spAnimationStateData* self = NEW(spAnimationStateData);
+	CONST_CAST(spSkeletonData*, self->skeletonData) = skeletonData;
+	return self;
+}
+
+void spAnimationStateData_dispose (spAnimationStateData* self) {
+	_ToEntry* toEntry;
+	_ToEntry* nextToEntry;
+	_FromEntry* nextFromEntry;
+
+	_FromEntry* fromEntry = (_FromEntry*)self->entries;
+	while (fromEntry) {
+		toEntry = fromEntry->toEntries;
+		while (toEntry) {
+			nextToEntry = toEntry->next;
+			_ToEntry_dispose(toEntry);
+			toEntry = nextToEntry;
+		}
+		nextFromEntry = fromEntry->next;
+		_FromEntry_dispose(fromEntry);
+		fromEntry = nextFromEntry;
+	}
+
+	FREE(self);
+}
+
+void spAnimationStateData_setMixByName (spAnimationStateData* self, const char* fromName, const char* toName, float duration) {
+	spAnimation* to;
+	spAnimation* from = spSkeletonData_findAnimation(self->skeletonData, fromName);
+	if (!from) return;
+	to = spSkeletonData_findAnimation(self->skeletonData, toName);
+	if (!to) return;
+	spAnimationStateData_setMix(self, from, to, duration);
+}
+
+void spAnimationStateData_setMix (spAnimationStateData* self, spAnimation* from, spAnimation* to, float duration) {
+	/* Find existing FromEntry. */
+	_ToEntry* toEntry;
+	_FromEntry* fromEntry = (_FromEntry*)self->entries;
+	while (fromEntry) {
+		if (fromEntry->animation == from) {
+			/* Find existing ToEntry. */
+			toEntry = fromEntry->toEntries;
+			while (toEntry) {
+				if (toEntry->animation == to) {
+					toEntry->duration = duration;
+					return;
+				}
+				toEntry = toEntry->next;
+			}
+			break; /* Add new ToEntry to the existing FromEntry. */
+		}
+		fromEntry = fromEntry->next;
+	}
+	if (!fromEntry) {
+		fromEntry = _FromEntry_create(from);
+		fromEntry->next = (_FromEntry*)self->entries;
+		CONST_CAST(_FromEntry*, self->entries) = fromEntry;
+	}
+	toEntry = _ToEntry_create(to, duration);
+	toEntry->next = fromEntry->toEntries;
+	fromEntry->toEntries = toEntry;
+}
+
+float spAnimationStateData_getMix (spAnimationStateData* self, spAnimation* from, spAnimation* to) {
+	_FromEntry* fromEntry = (_FromEntry*)self->entries;
+	while (fromEntry) {
+		if (fromEntry->animation == from) {
+			_ToEntry* toEntry = fromEntry->toEntries;
+			while (toEntry) {
+				if (toEntry->animation == to) return toEntry->duration;
+				toEntry = toEntry->next;
+			}
+		}
+		fromEntry = fromEntry->next;
+	}
+	return self->defaultMix;
+}

+ 66 - 0
engine/source/spine/AnimationStateData.h

@@ -0,0 +1,66 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef SPINE_ANIMATIONSTATEDATA_H_
+#define SPINE_ANIMATIONSTATEDATA_H_
+
+#include <spine/Animation.h>
+#include <spine/SkeletonData.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+	spSkeletonData* const skeletonData;
+	float defaultMix;
+	const void* const entries;
+} spAnimationStateData;
+
+spAnimationStateData* spAnimationStateData_create (spSkeletonData* skeletonData);
+void spAnimationStateData_dispose (spAnimationStateData* self);
+
+void spAnimationStateData_setMixByName (spAnimationStateData* self, const char* fromName, const char* toName, float duration);
+void spAnimationStateData_setMix (spAnimationStateData* self, spAnimation* from, spAnimation* to, float duration);
+/* Returns 0 if there is no mixing between the animations. */
+float spAnimationStateData_getMix (spAnimationStateData* self, spAnimation* from, spAnimation* to);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spAnimationStateData AnimationStateData;
+#define AnimationStateData_create(...) spAnimationStateData_create(__VA_ARGS__)
+#define AnimationStateData_dispose(...) spAnimationStateData_dispose(__VA_ARGS__)
+#define AnimationStateData_setMixByName(...) spAnimationStateData_setMixByName(__VA_ARGS__)
+#define AnimationStateData_setMix(...) spAnimationStateData_setMix(__VA_ARGS__)
+#define AnimationStateData_getMix(...) spAnimationStateData_getMix(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_ANIMATIONSTATEDATA_H_ */

+ 338 - 0
engine/source/spine/Atlas.c

@@ -0,0 +1,338 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#include <spine/Atlas.h>
+#include <ctype.h>
+#include <spine/extension.h>
+
+spAtlasPage* spAtlasPage_create (const char* name) {
+	spAtlasPage* self = NEW(spAtlasPage);
+	MALLOC_STR(self->name, name);
+	return self;
+}
+
+void spAtlasPage_dispose (spAtlasPage* self) {
+	_spAtlasPage_disposeTexture(self);
+	FREE(self->name);
+	FREE(self);
+}
+
+/**/
+
+spAtlasRegion* spAtlasRegion_create () {
+	return NEW(spAtlasRegion) ;
+}
+
+void spAtlasRegion_dispose (spAtlasRegion* self) {
+	FREE(self->name);
+	FREE(self->splits);
+	FREE(self->pads);
+	FREE(self);
+}
+
+/**/
+
+typedef struct {
+	const char* begin;
+	const char* end;
+} Str;
+
+static void trim (Str* str) {
+	while (isspace(*str->begin) && str->begin < str->end)
+		(str->begin)++;
+	if (str->begin == str->end) return;
+	str->end--;
+	while (isspace(*str->end) && str->end >= str->begin)
+		str->end--;
+	str->end++;
+}
+
+/* Tokenize string without modification. Returns 0 on failure. */
+static int readLine (const char* begin, const char* end, Str* str) {
+	static const char* nextStart;
+	if (begin) {
+		nextStart = begin;
+		return 1;
+	}
+	if (nextStart == end) return 0;
+	str->begin = nextStart;
+
+	/* Find next delimiter. */
+	while (nextStart != end && *nextStart != '\n')
+		nextStart++;
+
+	str->end = nextStart;
+	trim(str);
+
+	if (nextStart != end) nextStart++;
+	return 1;
+}
+
+/* Moves str->begin past the first occurence of c. Returns 0 on failure. */
+static int beginPast (Str* str, char c) {
+	const char* begin = str->begin;
+	while (1) {
+		char lastSkippedChar = *begin;
+		if (begin == str->end) return 0;
+		begin++;
+		if (lastSkippedChar == c) break;
+	}
+	str->begin = begin;
+	return 1;
+}
+
+/* Returns 0 on failure. */
+static int readValue (const char* end, Str* str) {
+	readLine(0, end, str);
+	if (!beginPast(str, ':')) return 0;
+	trim(str);
+	return 1;
+}
+
+/* Returns the number of tuple values read (2, 4, or 0 for failure). */
+static int readTuple (const char* end, Str tuple[]) {
+	int i;
+	Str str;
+	readLine(0, end, &str);
+	if (!beginPast(&str, ':')) return 0;
+
+	for (i = 0; i < 3; ++i) {
+		tuple[i].begin = str.begin;
+		if (!beginPast(&str, ',')) {
+			if (i == 0) return 0;
+			break;
+		}
+		tuple[i].end = str.begin - 2;
+		trim(&tuple[i]);
+	}
+	tuple[i].begin = str.begin;
+	tuple[i].end = str.end;
+	trim(&tuple[i]);
+	return i + 1;
+}
+
+static char* mallocString (Str* str) {
+	int length = str->end - str->begin;
+	char* string = MALLOC(char, length + 1);
+	memcpy(string, str->begin, length);
+	string[length] = '\0';
+	return string;
+}
+
+static int indexOf (const char** array, int count, Str* str) {
+	int length = str->end - str->begin;
+	int i;
+	for (i = count - 1; i >= 0; i--)
+		if (strncmp(array[i], str->begin, length) == 0) return i;
+	return -1;
+}
+
+static int equals (Str* str, const char* other) {
+	return strncmp(other, str->begin, str->end - str->begin) == 0;
+}
+
+static int toInt (Str* str) {
+	return strtol(str->begin, (char**)&str->end, 10);
+}
+
+static spAtlas* abortAtlas (spAtlas* self) {
+	spAtlas_dispose(self);
+	return 0;
+}
+
+static const char* formatNames[] = {"Alpha", "Intensity", "LuminanceAlpha", "RGB565", "RGBA4444", "RGB888", "RGBA8888"};
+static const char* textureFilterNames[] = {"Nearest", "Linear", "MipMap", "MipMapNearestNearest", "MipMapLinearNearest",
+		"MipMapNearestLinear", "MipMapLinearLinear"};
+
+spAtlas* spAtlas_readAtlas (const char* begin, int length, const char* dir) {
+	int count;
+	const char* end = begin + length;
+	int dirLength = strlen(dir);
+	int needsSlash = dirLength > 0 && dir[dirLength - 1] != '/' && dir[dirLength - 1] != '\\';
+
+	spAtlas* self = NEW(spAtlas);
+
+	spAtlasPage *page = 0;
+	spAtlasPage *lastPage = 0;
+	spAtlasRegion *lastRegion = 0;
+	Str str;
+	Str tuple[4];
+	readLine(begin, 0, 0);
+	while (readLine(0, end, &str)) {
+		if (str.end - str.begin == 0) {
+			page = 0;
+		} else if (!page) {
+			char* name = mallocString(&str);
+			char* path = MALLOC(char, dirLength + needsSlash + strlen(name) + 1);
+			memcpy(path, dir, dirLength);
+			if (needsSlash) path[dirLength] = '/';
+			strcpy(path + dirLength + needsSlash, name);
+
+			page = spAtlasPage_create(name);
+			FREE(name);
+			if (lastPage)
+				lastPage->next = page;
+			else
+				self->pages = page;
+			lastPage = page;
+
+			if (!readValue(end, &str)) return abortAtlas(self);
+			page->format = (spAtlasFormat)indexOf(formatNames, 7, &str);
+
+			if (!readTuple(end, tuple)) return abortAtlas(self);
+			page->minFilter = (spAtlasFilter)indexOf(textureFilterNames, 7, tuple);
+			page->magFilter = (spAtlasFilter)indexOf(textureFilterNames, 7, tuple + 1);
+
+			if (!readValue(end, &str)) return abortAtlas(self);
+			if (!equals(&str, "none")) {
+				page->uWrap = *str.begin == 'x' ? ATLAS_REPEAT : (*str.begin == 'y' ? ATLAS_CLAMPTOEDGE : ATLAS_REPEAT);
+				page->vWrap = *str.begin == 'x' ? ATLAS_CLAMPTOEDGE : (*str.begin == 'y' ? ATLAS_REPEAT : ATLAS_REPEAT);
+			}
+
+			_spAtlasPage_createTexture(page, path);
+			FREE(path);
+		} else {
+			spAtlasRegion *region = spAtlasRegion_create();
+			if (lastRegion)
+				lastRegion->next = region;
+			else
+				self->regions = region;
+			lastRegion = region;
+
+			region->page = page;
+			region->name = mallocString(&str);
+
+			if (!readValue(end, &str)) return abortAtlas(self);
+			region->rotate = equals(&str, "true");
+
+			if (readTuple(end, tuple) != 2) return abortAtlas(self);
+			region->x = toInt(tuple);
+			region->y = toInt(tuple + 1);
+
+			if (readTuple(end, tuple) != 2) return abortAtlas(self);
+			region->width = toInt(tuple);
+			region->height = toInt(tuple + 1);
+
+			region->u = region->x / (float)page->width;
+			region->v = region->y / (float)page->height;
+			if (region->rotate) {
+				region->u2 = (region->x + region->height) / (float)page->width;
+				region->v2 = (region->y + region->width) / (float)page->height;
+			} else {
+				region->u2 = (region->x + region->width) / (float)page->width;
+				region->v2 = (region->y + region->height) / (float)page->height;
+			}
+
+			if (!(count = readTuple(end, tuple))) return abortAtlas(self);
+			if (count == 4) { /* split is optional */
+				region->splits = MALLOC(int, 4);
+				region->splits[0] = toInt(tuple);
+				region->splits[1] = toInt(tuple + 1);
+				region->splits[2] = toInt(tuple + 2);
+				region->splits[3] = toInt(tuple + 3);
+
+				if (!(count = readTuple(end, tuple))) return abortAtlas(self);
+				if (count == 4) { /* pad is optional, but only present with splits */
+					region->pads = MALLOC(int, 4);
+					region->pads[0] = toInt(tuple);
+					region->pads[1] = toInt(tuple + 1);
+					region->pads[2] = toInt(tuple + 2);
+					region->pads[3] = toInt(tuple + 3);
+
+					if (!readTuple(end, tuple)) return abortAtlas(self);
+				}
+			}
+
+			region->originalWidth = toInt(tuple);
+			region->originalHeight = toInt(tuple + 1);
+
+			readTuple(end, tuple);
+			region->offsetX = toInt(tuple);
+			region->offsetY = toInt(tuple + 1);
+
+			if (!readValue(end, &str)) return abortAtlas(self);
+			region->index = toInt(&str);
+		}
+	}
+
+	return self;
+}
+
+spAtlas* spAtlas_readAtlasFile (const char* path) {
+	int dirLength;
+	char *dir;
+	int length;
+	const char* data;
+
+	spAtlas* atlas = 0;
+
+	/* Get directory from atlas path. */
+	const char* lastForwardSlash = strrchr(path, '/');
+	const char* lastBackwardSlash = strrchr(path, '\\');
+	const char* lastSlash = lastForwardSlash > lastBackwardSlash ? lastForwardSlash : lastBackwardSlash;
+	if (lastSlash == path) lastSlash++; /* Never drop starting slash. */
+	dirLength = lastSlash ? lastSlash - path : 0;
+	dir = MALLOC(char, dirLength + 1);
+	memcpy(dir, path, dirLength);
+	dir[dirLength] = '\0';
+
+	data = _spUtil_readFile(path, &length);
+	if (data) atlas = spAtlas_readAtlas(data, length, dir);
+
+	FREE(data);
+	FREE(dir);
+	return atlas;
+}
+
+void spAtlas_dispose (spAtlas* self) {
+	spAtlasRegion* region, *nextRegion;
+	spAtlasPage* page = self->pages;
+	while (page) {
+		spAtlasPage* nextPage = page->next;
+		spAtlasPage_dispose(page);
+		page = nextPage;
+	}
+
+	region = self->regions;
+	while (region) {
+		nextRegion = region->next;
+		spAtlasRegion_dispose(region);
+		region = nextRegion;
+	}
+
+	FREE(self);
+}
+
+spAtlasRegion* spAtlas_findRegion (const spAtlas* self, const char* name) {
+	spAtlasRegion* region = self->regions;
+	while (region) {
+		if (strcmp(region->name, name) == 0) return region;
+		region = region->next;
+	}
+	return 0;
+}

+ 136 - 0
engine/source/spine/Atlas.h

@@ -0,0 +1,136 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef SPINE_ATLAS_H_
+#define SPINE_ATLAS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+	ATLAS_ALPHA, ATLAS_INTENSITY, ATLAS_LUMINANCE_ALPHA, ATLAS_RGB565, ATLAS_RGBA4444, ATLAS_RGB888, ATLAS_RGBA8888
+} spAtlasFormat;
+
+typedef enum {
+	ATLAS_NEAREST,
+	ATLAS_LINEAR,
+	ATLAS_MIPMAP,
+	ATLAS_MIPMAP_NEAREST_NEAREST,
+	ATLAS_MIPMAP_LINEAR_NEAREST,
+	ATLAS_MIPMAP_NEAREST_LINEAR,
+	ATLAS_MIPMAP_LINEAR_LINEAR
+} spAtlasFilter;
+
+typedef enum {
+	ATLAS_MIRROREDREPEAT, ATLAS_CLAMPTOEDGE, ATLAS_REPEAT
+} spAtlasWrap;
+
+typedef struct spAtlasPage spAtlasPage;
+struct spAtlasPage {
+	const char* name;
+	spAtlasFormat format;
+	spAtlasFilter minFilter, magFilter;
+	spAtlasWrap uWrap, vWrap;
+
+	void* rendererObject;
+	int width, height;
+
+	spAtlasPage* next;
+};
+
+spAtlasPage* spAtlasPage_create (const char* name);
+void spAtlasPage_dispose (spAtlasPage* self);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spAtlasFormat AtlasFormat;
+typedef spAtlasFilter AtlasFilter;
+typedef spAtlasWrap AtlasWrap;
+typedef spAtlasPage AtlasPage;
+#define AtlasPage_create(...) spAtlasPage_create(__VA_ARGS__)
+#define AtlasPage_dispose(...) spAtlasPage_dispose(__VA_ARGS__)
+#endif
+
+/**/
+
+typedef struct spAtlasRegion spAtlasRegion;
+struct spAtlasRegion {
+	const char* name;
+	int x, y, width, height;
+	float u, v, u2, v2;
+	int offsetX, offsetY;
+	int originalWidth, originalHeight;
+	int index;
+	int/*bool*/rotate;
+	int/*bool*/flip;
+	int* splits;
+	int* pads;
+
+	spAtlasPage* page;
+
+	spAtlasRegion* next;
+};
+
+spAtlasRegion* spAtlasRegion_create ();
+void spAtlasRegion_dispose (spAtlasRegion* self);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spAtlasRegion AtlasRegion;
+#define AtlasRegion_create(...) spAtlasRegion_create(__VA_ARGS__)
+#define AtlasRegion_dispose(...) spAtlasRegion_dispose(__VA_ARGS__)
+#endif
+
+/**/
+
+typedef struct {
+	spAtlasPage* pages;
+	spAtlasRegion* regions;
+} spAtlas;
+
+/* Image files referenced in the atlas file will be prefixed with dir. */
+spAtlas* spAtlas_readAtlas (const char* data, int length, const char* dir);
+/* Image files referenced in the atlas file will be prefixed with the directory containing the atlas file. */
+spAtlas* spAtlas_readAtlasFile (const char* path);
+void spAtlas_dispose (spAtlas* atlas);
+
+/* Returns 0 if the region was not found. */
+spAtlasRegion* spAtlas_findRegion (const spAtlas* self, const char* name);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spAtlas Atlas;
+#define Atlas_readAtlas(...) spAtlas_readAtlas(__VA_ARGS__)
+#define Atlas_readAtlasFile(...) spAtlas_readAtlasFile(__VA_ARGS__)
+#define Atlas_dispose(...) spAtlas_dispose(__VA_ARGS__)
+#define Atlas_findRegion(...) spAtlas_findRegion(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_ATLAS_H_ */

+ 67 - 0
engine/source/spine/AtlasAttachmentLoader.c

@@ -0,0 +1,67 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#include <spine/AtlasAttachmentLoader.h>
+#include <spine/extension.h>
+
+spAttachment* _spAtlasAttachmentLoader_newAttachment (spAttachmentLoader* loader, spSkin* skin, spAttachmentType type,
+		const char* name) {
+	spAtlasAttachmentLoader* self = SUB_CAST(spAtlasAttachmentLoader, loader);
+	switch (type) {
+	case ATTACHMENT_REGION: {
+		spRegionAttachment* attachment;
+		spAtlasRegion* region = spAtlas_findRegion(self->atlas, name);
+		if (!region) {
+			_spAttachmentLoader_setError(loader, "Region not found: ", name);
+			return 0;
+		}
+		attachment = spRegionAttachment_create(name);
+		attachment->rendererObject = region;
+		spRegionAttachment_setUVs(attachment, region->u, region->v, region->u2, region->v2, region->rotate);
+		attachment->regionOffsetX = region->offsetX;
+		attachment->regionOffsetY = region->offsetY;
+		attachment->regionWidth = region->width;
+		attachment->regionHeight = region->height;
+		attachment->regionOriginalWidth = region->originalWidth;
+		attachment->regionOriginalHeight = region->originalHeight;
+		return SUPER(attachment);
+	}
+	case ATTACHMENT_BOUNDING_BOX:
+		return SUPER(spBoundingBoxAttachment_create(name));
+	default:
+		_spAttachmentLoader_setUnknownTypeError(loader, type);
+		return 0;
+	}
+}
+
+spAtlasAttachmentLoader* spAtlasAttachmentLoader_create (spAtlas* atlas) {
+	spAtlasAttachmentLoader* self = NEW(spAtlasAttachmentLoader);
+	_spAttachmentLoader_init(SUPER(self), _spAttachmentLoader_deinit, _spAtlasAttachmentLoader_newAttachment);
+	self->atlas = atlas;
+	return self;
+}

+ 55 - 0
engine/source/spine/AtlasAttachmentLoader.h

@@ -0,0 +1,55 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef SPINE_ATLASATTACHMENTLOADER_H_
+#define SPINE_ATLASATTACHMENTLOADER_H_
+
+#include <spine/AttachmentLoader.h>
+#include <spine/Atlas.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+	spAttachmentLoader super;
+	spAtlas* atlas;
+} spAtlasAttachmentLoader;
+
+spAtlasAttachmentLoader* spAtlasAttachmentLoader_create (spAtlas* atlas);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spAtlasAttachmentLoader AtlasAttachmentLoader;
+#define AtlasAttachmentLoader_create(...) spAtlasAttachmentLoader_create(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_ATLASATTACHMENTLOADER_H_ */

+ 54 - 0
engine/source/spine/Attachment.c

@@ -0,0 +1,54 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#include <spine/Attachment.h>
+#include <spine/extension.h>
+#include <spine/Slot.h>
+
+typedef struct _spAttachmentVtable {
+	void (*dispose) (spAttachment* self);
+} _spAttachmentVtable;
+
+void _spAttachment_init (spAttachment* self, const char* name, spAttachmentType type, /**/
+		void (*dispose) (spAttachment* self)) {
+
+	CONST_CAST(_spAttachmentVtable*, self->vtable) = NEW(_spAttachmentVtable);
+	VTABLE(spAttachment, self) ->dispose = dispose;
+
+	MALLOC_STR(self->name, name);
+	self->type = type;
+}
+
+void _spAttachment_deinit (spAttachment* self) {
+	FREE(self->vtable);
+	FREE(self->name);
+}
+
+void spAttachment_dispose (spAttachment* self) {
+	VTABLE(spAttachment, self) ->dispose(self);
+}

+ 62 - 0
engine/source/spine/Attachment.h

@@ -0,0 +1,62 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef SPINE_ATTACHMENT_H_
+#define SPINE_ATTACHMENT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct spSlot;
+
+typedef enum {
+	ATTACHMENT_REGION, ATTACHMENT_REGION_SEQUENCE, ATTACHMENT_BOUNDING_BOX
+} spAttachmentType;
+
+typedef struct spAttachment spAttachment;
+struct spAttachment {
+	const char* const name;
+	spAttachmentType type;
+
+	const void* const vtable;
+};
+
+void spAttachment_dispose (spAttachment* self);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spAttachmentType AttachmentType;
+typedef spAttachment Attachment;
+#define Attachment_dispose(...) spAttachment_dispose(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_ATTACHMENT_H_ */

+ 76 - 0
engine/source/spine/AttachmentLoader.c

@@ -0,0 +1,76 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#include <spine/AttachmentLoader.h>
+#include <stdio.h>
+#include <spine/extension.h>
+
+typedef struct _spAttachmentLoaderVtable {
+	spAttachment* (*newAttachment) (spAttachmentLoader* self, spSkin* skin, spAttachmentType type, const char* name);
+	void (*dispose) (spAttachmentLoader* self);
+} _spAttachmentLoaderVtable;
+
+void _spAttachmentLoader_init (spAttachmentLoader* self, /**/
+void (*dispose) (spAttachmentLoader* self), /**/
+spAttachment* (*newAttachment) (spAttachmentLoader* self, spSkin* skin, spAttachmentType type, const char* name)) {
+	CONST_CAST(_spAttachmentLoaderVtable*, self->vtable) = NEW(_spAttachmentLoaderVtable);
+	VTABLE(spAttachmentLoader, self)->dispose = dispose;
+	VTABLE(spAttachmentLoader, self)->newAttachment = newAttachment;
+}
+
+void _spAttachmentLoader_deinit (spAttachmentLoader* self) {
+	FREE(self->vtable);
+	FREE(self->error1);
+	FREE(self->error2);
+}
+
+void spAttachmentLoader_dispose (spAttachmentLoader* self) {
+	VTABLE(spAttachmentLoader, self)->dispose(self);
+	FREE(self);
+}
+
+spAttachment* spAttachmentLoader_newAttachment (spAttachmentLoader* self, spSkin* skin, spAttachmentType type, const char* name) {
+	FREE(self->error1);
+	FREE(self->error2);
+	self->error1 = 0;
+	self->error2 = 0;
+	return VTABLE(spAttachmentLoader, self)->newAttachment(self, skin, type, name);
+}
+
+void _spAttachmentLoader_setError (spAttachmentLoader* self, const char* error1, const char* error2) {
+	FREE(self->error1);
+	FREE(self->error2);
+	MALLOC_STR(self->error1, error1);
+	MALLOC_STR(self->error2, error2);
+}
+
+void _spAttachmentLoader_setUnknownTypeError (spAttachmentLoader* self, spAttachmentType type) {
+	char buffer[16];
+	sprintf(buffer, "%d", type);
+	_spAttachmentLoader_setError(self, "Unknown attachment type: ", buffer);
+}

+ 69 - 0
engine/source/spine/AttachmentLoader.h

@@ -0,0 +1,69 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef SPINE_ATTACHMENTLOADER_H_
+#define SPINE_ATTACHMENTLOADER_H_
+
+#include <spine/Attachment.h>
+#include <spine/Skin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct spAttachmentLoader spAttachmentLoader;
+struct spAttachmentLoader {
+	const char* error1;
+	const char* error2;
+
+	const void* const vtable;
+#ifdef __cplusplus
+	spAttachmentLoader () :
+					error1(0),
+					error2(0),
+					vtable(0) {
+	}
+#endif
+};
+
+void spAttachmentLoader_dispose (spAttachmentLoader* self);
+
+/* Returns 0 to not load an attachment. If 0 is returned and spAttachmentLoader.error1 is set, an error occurred. */
+spAttachment* spAttachmentLoader_newAttachment (spAttachmentLoader* self, spSkin* skin, spAttachmentType type, const char* name);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spAttachmentLoader AttachmentLoader;
+#define AttachmentLoader_dispose(...) spAttachmentLoader_dispose(__VA_ARGS__)
+#define AttachmentLoader_newAttachment(...) spAttachmentLoader_newAttachment(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_ATTACHMENTLOADER_H_ */

+ 99 - 0
engine/source/spine/Bone.c

@@ -0,0 +1,99 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#include <spine/Bone.h>
+#include <spine/extension.h>
+
+static int yDown;
+
+void spBone_setYDown (int value) {
+	yDown = value;
+}
+
+spBone* spBone_create (spBoneData* data, spBone* parent) {
+	spBone* self = NEW(spBone);
+	CONST_CAST(spBoneData*, self->data) = data;
+	CONST_CAST(spBone*, self->parent) = parent;
+	spBone_setToSetupPose(self);
+	return self;
+}
+
+void spBone_dispose (spBone* self) {
+	FREE(self);
+}
+
+void spBone_setToSetupPose (spBone* self) {
+	self->x = self->data->x;
+	self->y = self->data->y;
+	self->rotation = self->data->rotation;
+	self->scaleX = self->data->scaleX;
+	self->scaleY = self->data->scaleY;
+}
+
+void spBone_updateWorldTransform (spBone* self, int flipX, int flipY) {
+	float radians, cosine, sine;
+	if (self->parent) {
+		CONST_CAST(float, self->worldX) = self->x * self->parent->m00 + self->y * self->parent->m01 + self->parent->worldX;
+		CONST_CAST(float, self->worldY) = self->x * self->parent->m10 + self->y * self->parent->m11 + self->parent->worldY;
+		if (self->data->inheritScale) {
+			CONST_CAST(float, self->worldScaleX) = self->parent->worldScaleX * self->scaleX;
+			CONST_CAST(float, self->worldScaleY) = self->parent->worldScaleY * self->scaleY;
+		} else {
+			CONST_CAST(float, self->worldScaleX) = self->scaleX;
+			CONST_CAST(float, self->worldScaleY) = self->scaleY;
+		}
+		CONST_CAST(float, self->worldRotation) =
+				self->data->inheritRotation ? self->parent->worldRotation + self->rotation : self->rotation;
+	} else {
+		CONST_CAST(float, self->worldX) = flipX ? -self->x : self->x;
+		CONST_CAST(float, self->worldY) = flipY != yDown ? -self->y : self->y;
+		CONST_CAST(float, self->worldScaleX) = self->scaleX;
+		CONST_CAST(float, self->worldScaleY) = self->scaleY;
+		CONST_CAST(float, self->worldRotation) = self->rotation;
+	}
+	radians = (float)(self->worldRotation * 3.1415926535897932385 / 180);
+#ifdef __STDC_VERSION__
+	cosine = cosf(radians);
+	sine = sinf(radians);
+#else
+	cosine = (float)cos(radians);
+	sine = (float)sin(radians);
+#endif
+	CONST_CAST(float, self->m00) = cosine * self->worldScaleX;
+	CONST_CAST(float, self->m10) = sine * self->worldScaleX;
+	CONST_CAST(float, self->m01) = -sine * self->worldScaleY;
+	CONST_CAST(float, self->m11) = cosine * self->worldScaleY;
+	if (flipX) {
+		CONST_CAST(float, self->m00) = -self->m00;
+		CONST_CAST(float, self->m01) = -self->m01;
+	}
+	if (flipY != yDown) {
+		CONST_CAST(float, self->m10) = -self->m10;
+		CONST_CAST(float, self->m11) = -self->m11;
+	}
+}

+ 75 - 0
engine/source/spine/Bone.h

@@ -0,0 +1,75 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef SPINE_BONE_H_
+#define SPINE_BONE_H_
+
+#include <spine/BoneData.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct spBone spBone;
+struct spBone {
+	spBoneData* const data;
+	spBone* const parent;
+	float x, y;
+	float rotation;
+	float scaleX, scaleY;
+
+	float const m00, m01, worldX; /* a b x */
+	float const m10, m11, worldY; /* c d y */
+	float const worldRotation;
+	float const worldScaleX, worldScaleY;
+};
+
+void spBone_setYDown (int/*bool*/yDown);
+
+/* @param parent May be 0. */
+spBone* spBone_create (spBoneData* data, spBone* parent);
+void spBone_dispose (spBone* self);
+
+void spBone_setToSetupPose (spBone* self);
+
+void spBone_updateWorldTransform (spBone* self, int/*bool*/flipX, int/*bool*/flipY);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spBone Bone;
+#define Bone_setYDown(...) spBone_setYDown(__VA_ARGS__)
+#define Bone_create(...) spBone_create(__VA_ARGS__)
+#define Bone_dispose(...) spBone_dispose(__VA_ARGS__)
+#define Bone_setToSetupPose(...) spBone_setToSetupPose(__VA_ARGS__)
+#define Bone_updateWorldTransform(...) spBone_updateWorldTransform(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_BONE_H_ */

+ 46 - 0
engine/source/spine/BoneData.c

@@ -0,0 +1,46 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#include <spine/BoneData.h>
+#include <spine/extension.h>
+
+spBoneData* spBoneData_create (const char* name, spBoneData* parent) {
+	spBoneData* self = NEW(spBoneData);
+	MALLOC_STR(self->name, name);
+	CONST_CAST(spBoneData*, self->parent) = parent;
+	self->scaleX = 1;
+	self->scaleY = 1;
+	self->inheritScale = 1;
+	self->inheritRotation = 1;
+	return self;
+}
+
+void spBoneData_dispose (spBoneData* self) {
+	FREE(self->name);
+	FREE(self);
+}

+ 60 - 0
engine/source/spine/BoneData.h

@@ -0,0 +1,60 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef SPINE_BONEDATA_H_
+#define SPINE_BONEDATA_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct spBoneData spBoneData;
+struct spBoneData {
+	const char* const name;
+	spBoneData* const parent;
+	float length;
+	float x, y;
+	float rotation;
+	float scaleX, scaleY;
+	int/*bool*/inheritScale, inheritRotation;
+};
+
+spBoneData* spBoneData_create (const char* name, spBoneData* parent);
+void spBoneData_dispose (spBoneData* self);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spBoneData BoneData;
+#define BoneData_create(...) spBoneData_create(__VA_ARGS__)
+#define BoneData_dispose(...) spBoneData_dispose(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_BONEDATA_H_ */

+ 60 - 0
engine/source/spine/BoundingBoxAttachment.c

@@ -0,0 +1,60 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#include <spine/BoundingBoxAttachment.h>
+#include <spine/extension.h>
+
+void _spBoundingBoxAttachment_dispose (spAttachment* attachment) {
+	spBoundingBoxAttachment* self = SUB_CAST(spBoundingBoxAttachment, attachment);
+
+	_spAttachment_deinit(attachment);
+
+	FREE(self->vertices);
+	FREE(self);
+}
+
+spBoundingBoxAttachment* spBoundingBoxAttachment_create (const char* name) {
+	spBoundingBoxAttachment* self = NEW(spBoundingBoxAttachment);
+	_spAttachment_init(SUPER(self), name, ATTACHMENT_BOUNDING_BOX, _spBoundingBoxAttachment_dispose);
+	return self;
+}
+
+void spBoundingBoxAttachment_computeWorldVertices (spBoundingBoxAttachment* self, float x, float y, spBone* bone, float* worldVertices) {
+	int i;
+	float px, py;
+	float* vertices = self->vertices;
+
+	x += bone->worldX;
+	y += bone->worldY;
+	for (i = 0; i < self->verticesCount; i += 2) {
+		px = vertices[i];
+		py = vertices[i + 1];
+		worldVertices[i] = px * bone->m00 + py * bone->m01 + x;
+		worldVertices[i + 1] = px * bone->m10 + py * bone->m11 + y;
+	}
+}

+ 60 - 0
engine/source/spine/BoundingBoxAttachment.h

@@ -0,0 +1,60 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef SPINE_BOUNDINGBOXATTACHMENT_H_
+#define SPINE_BOUNDINGBOXATTACHMENT_H_
+
+#include <spine/Attachment.h>
+#include <spine/Atlas.h>
+#include <spine/Slot.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct spBoundingBoxAttachment spBoundingBoxAttachment;
+struct spBoundingBoxAttachment {
+	spAttachment super;
+	int verticesCount;
+	float* vertices;
+};
+
+spBoundingBoxAttachment* spBoundingBoxAttachment_create (const char* name);
+void spBoundingBoxAttachment_computeWorldVertices (spBoundingBoxAttachment* self, float x, float y, spBone* bone, float* vertices);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spBoundingBoxAttachment BoundingBoxAttachment;
+#define BoundingBoxAttachment_create(...) spBoundingBoxAttachment_create(__VA_ARGS__)
+#define BoundingBoxAttachment_computeWorldVertices(...) spBoundingBoxAttachment_computeWorldVertices(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_BOUNDINGBOXATTACHMENT_H_ */

+ 41 - 0
engine/source/spine/Event.c

@@ -0,0 +1,41 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#include <spine/Event.h>
+#include <spine/extension.h>
+
+spEvent* spEvent_create (spEventData* data) {
+	spEvent* self = NEW(spEvent);
+	CONST_CAST(spEventData*, self->data) = data;
+	return self;
+}
+
+void spEvent_dispose (spEvent* self) {
+	FREE(self->stringValue);
+	FREE(self);
+}

+ 59 - 0
engine/source/spine/Event.h

@@ -0,0 +1,59 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef SPINE_EVENT_H_
+#define SPINE_EVENT_H_
+
+#include <spine/EventData.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct spEvent spEvent;
+struct spEvent {
+	spEventData* const data;
+	int intValue;
+	float floatValue;
+	const char* stringValue;
+};
+
+spEvent* spEvent_create (spEventData* data);
+void spEvent_dispose (spEvent* self);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spEvent Event;
+#define Event_create(...) spEvent_create(__VA_ARGS__)
+#define Event_dispose(...) spEvent_dispose(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_EVENT_H_ */

+ 42 - 0
engine/source/spine/EventData.c

@@ -0,0 +1,42 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#include <spine/EventData.h>
+#include <spine/extension.h>
+
+spEventData* spEventData_create (const char* name) {
+	spEventData* self = NEW(spEventData);
+	MALLOC_STR(self->name, name);
+	return self;
+}
+
+void spEventData_dispose (spEventData* self) {
+	FREE(self->stringValue);
+	FREE(self->name);
+	FREE(self);
+}

+ 57 - 0
engine/source/spine/EventData.h

@@ -0,0 +1,57 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef SPINE_EVENTDATA_H_
+#define SPINE_EVENTDATA_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct spEventData spEventData;
+struct spEventData {
+	const char* const name;
+	int intValue;
+	float floatValue;
+	const char* stringValue;
+};
+
+spEventData* spEventData_create (const char* name);
+void spEventData_dispose (spEventData* self);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spEventData EventData;
+#define EventData_create(...) spEventData_create(__VA_ARGS__)
+#define EventData_dispose(...) spEventData_dispose(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_EVENTDATA_H_ */

+ 358 - 0
engine/source/spine/Json.c

@@ -0,0 +1,358 @@
+/*
+ Copyright (c) 2009 Dave Gamble
+
+ Permission is hereby granted, dispose of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ */
+
+/* Json */
+/* JSON parser in C. */
+
+#include "Json.h"
+#include <stdio.h>
+#include <ctype.h>
+#include <spine/extension.h>
+
+static const char* ep;
+
+const char* Json_getError (void) {
+	return ep;
+}
+
+static int Json_strcasecmp (const char* s1, const char* s2) {
+	if (!s1) return (s1 == s2) ? 0 : 1;
+	if (!s2) return 1;
+	for (; tolower(*s1) == tolower(*s2); ++s1, ++s2)
+		if (*s1 == 0) return 0;
+	return tolower(*(const unsigned char*)s1) - tolower(*(const unsigned char*)s2);
+}
+
+/* Internal constructor. */
+static Json *Json_new (void) {
+	return (Json*)CALLOC(Json, 1);
+}
+
+/* Delete a Json structure. */
+void Json_dispose (Json *c) {
+	Json *next;
+	while (c) {
+		next = c->next;
+		if (c->child) Json_dispose(c->child);
+		if (c->valueString) FREE(c->valueString);
+		if (c->name) FREE(c->name);
+		FREE(c);
+		c = next;
+	}
+}
+
+/* Parse the input text to generate a number, and populate the result into item. */
+static const char* parse_number (Json *item, const char* num) {
+	float n = 0, sign = 1, scale = 0;
+	int subscale = 0, signsubscale = 1;
+
+	/* Could use sscanf for this? */
+	if (*num == '-') sign = -1, num++; /* Has sign? */
+	if (*num == '0') num++; /* is zero */
+	if (*num >= '1' && *num <= '9') do
+		n = (n * 10.0f) + (*num++ - '0');
+	while (*num >= '0' && *num <= '9'); /* Number? */
+	if (*num == '.' && num[1] >= '0' && num[1] <= '9') {
+		num++;
+		do
+			n = (n * 10.0f) + (*num++ - '0'), scale--;
+		while (*num >= '0' && *num <= '9');
+	} /* Fractional part? */
+	if (*num == 'e' || *num == 'E') /* Exponent? */
+	{
+		num++;
+		if (*num == '+')
+			num++;
+		else if (*num == '-') signsubscale = -1, num++; /* With sign? */
+		while (*num >= '0' && *num <= '9')
+			subscale = (subscale * 10) + (*num++ - '0'); /* Number? */
+	}
+
+	n = sign * n * (float)pow(10.0f, (scale + subscale * signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */
+
+	item->valueFloat = n;
+	item->valueInt = (int)n;
+	item->type = Json_Number;
+	return num;
+}
+
+/* Parse the input text into an unescaped cstring, and populate item. */
+static const unsigned char firstByteMark[7] = {0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC};
+static const char* parse_string (Json *item, const char* str) {
+	const char* ptr = str + 1;
+	char* ptr2;
+	char* out;
+	int len = 0;
+	unsigned uc, uc2;
+	if (*str != '\"') {
+		ep = str;
+		return 0;
+	} /* not a string! */
+
+	while (*ptr != '\"' && *ptr && ++len)
+		if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */
+
+	out = (char*)malloc(len + 1); /* This is how long we need for the string, roughly. */
+	if (!out) return 0;
+
+	ptr = str + 1;
+	ptr2 = out;
+	while (*ptr != '\"' && *ptr) {
+		if (*ptr != '\\')
+			*ptr2++ = *ptr++;
+		else {
+			ptr++;
+			switch (*ptr) {
+			case 'b':
+				*ptr2++ = '\b';
+				break;
+			case 'f':
+				*ptr2++ = '\f';
+				break;
+			case 'n':
+				*ptr2++ = '\n';
+				break;
+			case 'r':
+				*ptr2++ = '\r';
+				break;
+			case 't':
+				*ptr2++ = '\t';
+				break;
+			case 'u': /* transcode utf16 to utf8. */
+				sscanf(ptr + 1, "%4x", &uc);
+				ptr += 4; /* get the unicode char. */
+
+				if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0) break; /* check for invalid.	*/
+
+				if (uc >= 0xD800 && uc <= 0xDBFF) /* UTF16 surrogate pairs.	*/
+				{
+					if (ptr[1] != '\\' || ptr[2] != 'u') break; /* missing second-half of surrogate.	*/
+					sscanf(ptr + 3, "%4x", &uc2);
+					ptr += 6;
+					if (uc2 < 0xDC00 || uc2 > 0xDFFF) break; /* invalid second-half of surrogate.	*/
+					uc = 0x10000 + (((uc & 0x3FF) << 10) | (uc2 & 0x3FF));
+				}
+
+				len = 4;
+				if (uc < 0x80)
+					len = 1;
+				else if (uc < 0x800)
+					len = 2;
+				else if (uc < 0x10000) len = 3;
+				ptr2 += len;
+
+				switch (len) {
+				case 4:
+					*--ptr2 = ((uc | 0x80) & 0xBF);
+					uc >>= 6;
+				case 3:
+					*--ptr2 = ((uc | 0x80) & 0xBF);
+					uc >>= 6;
+				case 2:
+					*--ptr2 = ((uc | 0x80) & 0xBF);
+					uc >>= 6;
+				case 1:
+					*--ptr2 = (uc | firstByteMark[len]);
+				}
+				ptr2 += len;
+				break;
+			default:
+				*ptr2++ = *ptr;
+				break;
+			}
+			ptr++;
+		}
+	}
+	*ptr2 = 0;
+	if (*ptr == '\"') ptr++;
+	item->valueString = out;
+	item->type = Json_String;
+	return ptr;
+}
+
+/* Predeclare these prototypes. */
+static const char* parse_value (Json *item, const char* value);
+static const char* parse_array (Json *item, const char* value);
+static const char* parse_object (Json *item, const char* value);
+
+/* Utility to jump whitespace and cr/lf */
+static const char* skip (const char* in) {
+	while (in && *in && (unsigned char)*in <= 32)
+		in++;
+	return in;
+}
+
+/* Parse an object - create a new root, and populate. */
+Json *Json_create (const char* value) {
+	const char* end = 0;
+	Json *c = Json_new();
+	ep = 0;
+	if (!c) return 0; /* memory fail */
+
+	end = parse_value(c, skip(value));
+	if (!end) {
+		Json_dispose(c);
+		return 0;
+	} /* parse failure. ep is set. */
+
+	return c;
+}
+
+/* Parser core - when encountering text, process appropriately. */
+static const char* parse_value (Json *item, const char* value) {
+	if (!value) return 0; /* Fail on null. */
+	if (!strncmp(value, "null", 4)) {
+		item->type = Json_NULL;
+		return value + 4;
+	}
+	if (!strncmp(value, "false", 5)) {
+		item->type = Json_False;
+		return value + 5;
+	}
+	if (!strncmp(value, "true", 4)) {
+		item->type = Json_True;
+		item->valueInt = 1;
+		return value + 4;
+	}
+	if (*value == '\"') {
+		return parse_string(item, value);
+	}
+	if (*value == '-' || (*value >= '0' && *value <= '9')) {
+		return parse_number(item, value);
+	}
+	if (*value == '[') {
+		return parse_array(item, value);
+	}
+	if (*value == '{') {
+		return parse_object(item, value);
+	}
+
+	ep = value;
+	return 0; /* failure. */
+}
+
+/* Build an array from input text. */
+static const char* parse_array (Json *item, const char* value) {
+	Json *child;
+	if (*value != '[') {
+		ep = value;
+		return 0;
+	} /* not an array! */
+
+	item->type = Json_Array;
+	value = skip(value + 1);
+	if (*value == ']') return value + 1; /* empty array. */
+
+	item->child = child = Json_new();
+	if (!item->child) return 0; /* memory fail */
+	value = skip(parse_value(child, skip(value))); /* skip any spacing, get the value. */
+	if (!value) return 0;
+	item->size = 1;
+
+	while (*value == ',') {
+		Json *new_item;
+		if (!(new_item = Json_new())) return 0; /* memory fail */
+		child->next = new_item;
+		new_item->prev = child;
+		child = new_item;
+		value = skip(parse_value(child, skip(value + 1)));
+		if (!value) return 0; /* memory fail */
+		item->size++;
+	}
+
+	if (*value == ']') return value + 1; /* end of array */
+	ep = value;
+	return 0; /* malformed. */
+}
+
+/* Build an object from the text. */
+static const char* parse_object (Json *item, const char* value) {
+	Json *child;
+	if (*value != '{') {
+		ep = value;
+		return 0;
+	} /* not an object! */
+
+	item->type = Json_Object;
+	value = skip(value + 1);
+	if (*value == '}') return value + 1; /* empty array. */
+
+	item->child = child = Json_new();
+	if (!item->child) return 0;
+	value = skip(parse_string(child, skip(value)));
+	if (!value) return 0;
+	child->name = child->valueString;
+	child->valueString = 0;
+	if (*value != ':') {
+		ep = value;
+		return 0;
+	} /* fail! */
+	value = skip(parse_value(child, skip(value + 1))); /* skip any spacing, get the value. */
+	if (!value) return 0;
+	item->size = 1;
+
+	while (*value == ',') {
+		Json *new_item;
+		if (!(new_item = Json_new())) return 0; /* memory fail */
+		child->next = new_item;
+		new_item->prev = child;
+		child = new_item;
+		value = skip(parse_string(child, skip(value + 1)));
+		if (!value) return 0;
+		child->name = child->valueString;
+		child->valueString = 0;
+		if (*value != ':') {
+			ep = value;
+			return 0;
+		} /* fail! */
+		value = skip(parse_value(child, skip(value + 1))); /* skip any spacing, get the value. */
+		if (!value) return 0;
+		item->size++;
+	}
+
+	if (*value == '}') return value + 1; /* end of array */
+	ep = value;
+	return 0; /* malformed. */
+}
+
+Json *Json_getItem (Json *object, const char* string) {
+	Json *c = object->child;
+	while (c && Json_strcasecmp(c->name, string))
+		c = c->next;
+	return c;
+}
+
+const char* Json_getString (Json* object, const char* name, const char* defaultValue) {
+	object = Json_getItem(object, name);
+	if (object) return object->valueString;
+	return defaultValue;
+}
+
+float Json_getFloat (Json* value, const char* name, float defaultValue) {
+	value = Json_getItem(value, name);
+	return value ? value->valueFloat : defaultValue;
+}
+
+int Json_getInt (Json* value, const char* name, int defaultValue) {
+	value = Json_getItem(value, name);
+	return value ? value->valueInt : defaultValue;
+}

+ 76 - 0
engine/source/spine/Json.h

@@ -0,0 +1,76 @@
+/*
+ Copyright (c) 2009 Dave Gamble
+ 
+ Permission is hereby granted, dispose of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+ 
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+ 
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ */
+
+/* Esoteric Software: Removed everything except parsing, shorter method names, more get methods, double to float, formatted. */
+
+#ifndef SPINE_JSON_H_
+#define SPINE_JSON_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Json Types: */
+#define Json_False 0
+#define Json_True 1
+#define Json_NULL 2
+#define Json_Number 3
+#define Json_String 4
+#define Json_Array 5
+#define Json_Object 6
+
+/* The Json structure: */
+typedef struct Json {
+	struct Json* next;
+	struct Json* prev; /* next/prev allow you to walk array/object chains. Alternatively, use getSize/getItem */
+	struct Json* child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
+
+	int type; /* The type of the item, as above. */
+	int size; /* The number of children. */
+
+	const char* valueString; /* The item's string, if type==Json_String */
+	int valueInt; /* The item's number, if type==Json_Number */
+	float valueFloat; /* The item's number, if type==Json_Number */
+
+	const char* name; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
+} Json;
+
+/* Supply a block of JSON, and this returns a Json object you can interrogate. Call Json_dispose when finished. */
+Json* Json_create (const char* value);
+
+/* Delete a Json entity and all subentities. */
+void Json_dispose (Json* json);
+
+/* Get item "string" from object. Case insensitive. */
+Json* Json_getItem (Json* json, const char* string);
+const char* Json_getString (Json* json, const char* name, const char* defaultValue);
+float Json_getFloat (Json* json, const char* name, float defaultValue);
+int Json_getInt (Json* json, const char* name, int defaultValue);
+
+/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when Json_create() returns 0. 0 when Json_create() succeeds. */
+const char* Json_getError (void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_JSON_H_ */

+ 115 - 0
engine/source/spine/RegionAttachment.c

@@ -0,0 +1,115 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#include <spine/RegionAttachment.h>
+#include <spine/extension.h>
+
+void _spRegionAttachment_dispose (spAttachment* attachment) {
+	spRegionAttachment* self = SUB_CAST(spRegionAttachment, attachment);
+
+	_spAttachment_deinit(attachment);
+
+	FREE(self);
+}
+
+spRegionAttachment* spRegionAttachment_create (const char* name) {
+	spRegionAttachment* self = NEW(spRegionAttachment);
+	self->scaleX = 1;
+	self->scaleY = 1;
+	_spAttachment_init(SUPER(self), name, ATTACHMENT_REGION, _spRegionAttachment_dispose);
+	return self;
+}
+
+void spRegionAttachment_setUVs (spRegionAttachment* self, float u, float v, float u2, float v2, int/*bool*/rotate) {
+	if (rotate) {
+		self->uvs[VERTEX_X2] = u;
+		self->uvs[VERTEX_Y2] = v2;
+		self->uvs[VERTEX_X3] = u;
+		self->uvs[VERTEX_Y3] = v;
+		self->uvs[VERTEX_X4] = u2;
+		self->uvs[VERTEX_Y4] = v;
+		self->uvs[VERTEX_X1] = u2;
+		self->uvs[VERTEX_Y1] = v2;
+	} else {
+		self->uvs[VERTEX_X1] = u;
+		self->uvs[VERTEX_Y1] = v2;
+		self->uvs[VERTEX_X2] = u;
+		self->uvs[VERTEX_Y2] = v;
+		self->uvs[VERTEX_X3] = u2;
+		self->uvs[VERTEX_Y3] = v;
+		self->uvs[VERTEX_X4] = u2;
+		self->uvs[VERTEX_Y4] = v2;
+	}
+}
+
+void spRegionAttachment_updateOffset (spRegionAttachment* self) {
+	float regionScaleX = self->width / self->regionOriginalWidth * self->scaleX;
+	float regionScaleY = self->height / self->regionOriginalHeight * self->scaleY;
+	float localX = -self->width / 2 * self->scaleX + self->regionOffsetX * regionScaleX;
+	float localY = -self->height / 2 * self->scaleY + self->regionOffsetY * regionScaleY;
+	float localX2 = localX + self->regionWidth * regionScaleX;
+	float localY2 = localY + self->regionHeight * regionScaleY;
+	float radians = (float)(self->rotation * 3.1415926535897932385 / 180);
+#ifdef __STDC_VERSION__
+	float cosine = cosf(radians);
+	float sine = sinf(radians);
+#else
+	float cosine = (float)cos(radians);
+	float sine = (float)sin(radians);
+#endif
+	float localXCos = localX * cosine + self->x;
+	float localXSin = localX * sine;
+	float localYCos = localY * cosine + self->y;
+	float localYSin = localY * sine;
+	float localX2Cos = localX2 * cosine + self->x;
+	float localX2Sin = localX2 * sine;
+	float localY2Cos = localY2 * cosine + self->y;
+	float localY2Sin = localY2 * sine;
+	self->offset[VERTEX_X1] = localXCos - localYSin;
+	self->offset[VERTEX_Y1] = localYCos + localXSin;
+	self->offset[VERTEX_X2] = localXCos - localY2Sin;
+	self->offset[VERTEX_Y2] = localY2Cos + localXSin;
+	self->offset[VERTEX_X3] = localX2Cos - localY2Sin;
+	self->offset[VERTEX_Y3] = localY2Cos + localX2Sin;
+	self->offset[VERTEX_X4] = localX2Cos - localYSin;
+	self->offset[VERTEX_Y4] = localYCos + localX2Sin;
+}
+
+void spRegionAttachment_computeWorldVertices (spRegionAttachment* self, float x, float y, spBone* bone, float* vertices) {
+	float* offset = self->offset;
+	x += bone->worldX;
+	y += bone->worldY;
+	vertices[VERTEX_X1] = offset[VERTEX_X1] * bone->m00 + offset[VERTEX_Y1] * bone->m01 + x;
+	vertices[VERTEX_Y1] = offset[VERTEX_X1] * bone->m10 + offset[VERTEX_Y1] * bone->m11 + y;
+	vertices[VERTEX_X2] = offset[VERTEX_X2] * bone->m00 + offset[VERTEX_Y2] * bone->m01 + x;
+	vertices[VERTEX_Y2] = offset[VERTEX_X2] * bone->m10 + offset[VERTEX_Y2] * bone->m11 + y;
+	vertices[VERTEX_X3] = offset[VERTEX_X3] * bone->m00 + offset[VERTEX_Y3] * bone->m01 + x;
+	vertices[VERTEX_Y3] = offset[VERTEX_X3] * bone->m10 + offset[VERTEX_Y3] * bone->m11 + y;
+	vertices[VERTEX_X4] = offset[VERTEX_X4] * bone->m00 + offset[VERTEX_Y4] * bone->m01 + x;
+	vertices[VERTEX_Y4] = offset[VERTEX_X4] * bone->m10 + offset[VERTEX_Y4] * bone->m11 + y;
+}

+ 76 - 0
engine/source/spine/RegionAttachment.h

@@ -0,0 +1,76 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef SPINE_REGIONATTACHMENT_H_
+#define SPINE_REGIONATTACHMENT_H_
+
+#include <spine/Attachment.h>
+#include <spine/Atlas.h>
+#include <spine/Slot.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+	VERTEX_X1 = 0, VERTEX_Y1, VERTEX_X2, VERTEX_Y2, VERTEX_X3, VERTEX_Y3, VERTEX_X4, VERTEX_Y4
+} spVertexIndex;
+
+typedef struct spRegionAttachment spRegionAttachment;
+struct spRegionAttachment {
+	spAttachment super;
+	float x, y, scaleX, scaleY, rotation, width, height;
+
+	void* rendererObject;
+	int regionOffsetX, regionOffsetY; /* Pixels stripped from the bottom left, unrotated. */
+	int regionWidth, regionHeight; /* Unrotated, stripped pixel size. */
+	int regionOriginalWidth, regionOriginalHeight; /* Unrotated, unstripped pixel size. */
+
+	float offset[8];
+	float uvs[8];
+};
+
+spRegionAttachment* spRegionAttachment_create (const char* name);
+void spRegionAttachment_setUVs (spRegionAttachment* self, float u, float v, float u2, float v2, int/*bool*/rotate);
+void spRegionAttachment_updateOffset (spRegionAttachment* self);
+void spRegionAttachment_computeWorldVertices (spRegionAttachment* self, float x, float y, spBone* bone, float* vertices);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spVertexIndex VertexIndex;
+typedef spRegionAttachment RegionAttachment;
+#define RegionAttachment_create(...) spRegionAttachment_create(__VA_ARGS__)
+#define RegionAttachment_setUVs(...) spRegionAttachment_setUVs(__VA_ARGS__)
+#define RegionAttachment_updateOffset(...) spRegionAttachment_updateOffset(__VA_ARGS__)
+#define RegionAttachment_computeWorldVertices(...) spRegionAttachment_computeWorldVertices(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_REGIONATTACHMENT_H_ */

+ 206 - 0
engine/source/spine/Skeleton.c

@@ -0,0 +1,206 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#include <spine/Skeleton.h>
+#include <string.h>
+#include <spine/extension.h>
+
+spSkeleton* spSkeleton_create (spSkeletonData* data) {
+	int i, ii;
+
+	spSkeleton* self = NEW(spSkeleton);
+	CONST_CAST(spSkeletonData*, self->data) = data;
+
+	self->boneCount = self->data->boneCount;
+	self->bones = MALLOC(spBone*, self->boneCount);
+
+	for (i = 0; i < self->boneCount; ++i) {
+		spBoneData* boneData = self->data->bones[i];
+		spBone* parent = 0;
+		if (boneData->parent) {
+			/* Find parent bone. */
+			for (ii = 0; ii < self->boneCount; ++ii) {
+				if (data->bones[ii] == boneData->parent) {
+					parent = self->bones[ii];
+					break;
+				}
+			}
+		}
+		self->bones[i] = spBone_create(boneData, parent);
+	}
+	CONST_CAST(spBone*, self->root) = self->bones[0];
+
+	self->slotCount = data->slotCount;
+	self->slots = MALLOC(spSlot*, self->slotCount);
+	for (i = 0; i < self->slotCount; ++i) {
+		spSlotData *slotData = data->slots[i];
+
+		/* Find bone for the slotData's boneData. */
+		spBone* bone = 0;
+		for (ii = 0; ii < self->boneCount; ++ii) {
+			if (data->bones[ii] == slotData->boneData) {
+				bone = self->bones[ii];
+				break;
+			}
+		}
+		self->slots[i] = spSlot_create(slotData, self, bone);
+	}
+
+	self->drawOrder = MALLOC(spSlot*, self->slotCount);
+	memcpy(self->drawOrder, self->slots, sizeof(spSlot*) * self->slotCount);
+
+	self->r = 1;
+	self->g = 1;
+	self->b = 1;
+	self->a = 1;
+
+	return self;
+}
+
+void spSkeleton_dispose (spSkeleton* self) {
+	int i;
+	for (i = 0; i < self->boneCount; ++i)
+		spBone_dispose(self->bones[i]);
+	FREE(self->bones);
+
+	for (i = 0; i < self->slotCount; ++i)
+		spSlot_dispose(self->slots[i]);
+	FREE(self->slots);
+
+	FREE(self->drawOrder);
+	FREE(self);
+}
+
+void spSkeleton_updateWorldTransform (const spSkeleton* self) {
+	int i;
+	for (i = 0; i < self->boneCount; ++i)
+		spBone_updateWorldTransform(self->bones[i], self->flipX, self->flipY);
+}
+
+void spSkeleton_setToSetupPose (const spSkeleton* self) {
+	spSkeleton_setBonesToSetupPose(self);
+	spSkeleton_setSlotsToSetupPose(self);
+}
+
+void spSkeleton_setBonesToSetupPose (const spSkeleton* self) {
+	int i;
+	for (i = 0; i < self->boneCount; ++i)
+		spBone_setToSetupPose(self->bones[i]);
+}
+
+void spSkeleton_setSlotsToSetupPose (const spSkeleton* self) {
+	int i;
+	memcpy(self->drawOrder, self->slots, self->slotCount * sizeof(spSlot*));
+	for (i = 0; i < self->slotCount; ++i)
+		spSlot_setToSetupPose(self->slots[i]);
+}
+
+spBone* spSkeleton_findBone (const spSkeleton* self, const char* boneName) {
+	int i;
+	for (i = 0; i < self->boneCount; ++i)
+		if (strcmp(self->data->bones[i]->name, boneName) == 0) return self->bones[i];
+	return 0;
+}
+
+int spSkeleton_findBoneIndex (const spSkeleton* self, const char* boneName) {
+	int i;
+	for (i = 0; i < self->boneCount; ++i)
+		if (strcmp(self->data->bones[i]->name, boneName) == 0) return i;
+	return -1;
+}
+
+spSlot* spSkeleton_findSlot (const spSkeleton* self, const char* slotName) {
+	int i;
+	for (i = 0; i < self->slotCount; ++i)
+		if (strcmp(self->data->slots[i]->name, slotName) == 0) return self->slots[i];
+	return 0;
+}
+
+int spSkeleton_findSlotIndex (const spSkeleton* self, const char* slotName) {
+	int i;
+	for (i = 0; i < self->slotCount; ++i)
+		if (strcmp(self->data->slots[i]->name, slotName) == 0) return i;
+	return -1;
+}
+
+int spSkeleton_setSkinByName (spSkeleton* self, const char* skinName) {
+	spSkin *skin;
+	if (!skinName) {
+		spSkeleton_setSkin(self, 0);
+		return 1;
+	}
+	skin = spSkeletonData_findSkin(self->data, skinName);
+	if (!skin) return 0;
+	spSkeleton_setSkin(self, skin);
+	return 1;
+}
+
+void spSkeleton_setSkin (spSkeleton* self, spSkin* newSkin) {
+	if (self->skin && newSkin) spSkin_attachAll(newSkin, self, self->skin);
+	CONST_CAST(spSkin*, self->skin) = newSkin;
+}
+
+spAttachment* spSkeleton_getAttachmentForSlotName (const spSkeleton* self, const char* slotName, const char* attachmentName) {
+	int slotIndex = spSkeletonData_findSlotIndex(self->data, slotName);
+	return spSkeleton_getAttachmentForSlotIndex(self, slotIndex, attachmentName);
+}
+
+spAttachment* spSkeleton_getAttachmentForSlotIndex (const spSkeleton* self, int slotIndex, const char* attachmentName) {
+	if (slotIndex == -1) return 0;
+	if (self->skin) {
+		spAttachment *attachment = spSkin_getAttachment(self->skin, slotIndex, attachmentName);
+		if (attachment) return attachment;
+	}
+	if (self->data->defaultSkin) {
+		spAttachment *attachment = spSkin_getAttachment(self->data->defaultSkin, slotIndex, attachmentName);
+		if (attachment) return attachment;
+	}
+	return 0;
+}
+
+int spSkeleton_setAttachment (spSkeleton* self, const char* slotName, const char* attachmentName) {
+	int i;
+	for (i = 0; i < self->slotCount; ++i) {
+		spSlot *slot = self->slots[i];
+		if (strcmp(slot->data->name, slotName) == 0) {
+			if (!attachmentName)
+				spSlot_setAttachment(slot, 0);
+			else {
+				spAttachment* attachment = spSkeleton_getAttachmentForSlotIndex(self, i, attachmentName);
+				if (!attachment) return 0;
+				spSlot_setAttachment(slot, attachment);
+			}
+			return 1;
+		}
+	}
+	return 0;
+}
+
+void spSkeleton_update (spSkeleton* self, float deltaTime) {
+	self->time += deltaTime;
+}

+ 119 - 0
engine/source/spine/Skeleton.h

@@ -0,0 +1,119 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef SPINE_SKELETON_H_
+#define SPINE_SKELETON_H_
+
+#include <spine/SkeletonData.h>
+#include <spine/Slot.h>
+#include <spine/Skin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct spSkeleton spSkeleton;
+struct spSkeleton {
+	spSkeletonData* const data;
+
+	int boneCount;
+	spBone** bones;
+	spBone* const root;
+
+	int slotCount;
+	spSlot** slots;
+	spSlot** drawOrder;
+
+	spSkin* const skin;
+	float r, g, b, a;
+	float time;
+	int/*bool*/flipX, flipY;
+	float x, y;
+};
+
+spSkeleton* spSkeleton_create (spSkeletonData* data);
+void spSkeleton_dispose (spSkeleton* self);
+
+void spSkeleton_updateWorldTransform (const spSkeleton* self);
+
+void spSkeleton_setToSetupPose (const spSkeleton* self);
+void spSkeleton_setBonesToSetupPose (const spSkeleton* self);
+void spSkeleton_setSlotsToSetupPose (const spSkeleton* self);
+
+/* Returns 0 if the bone was not found. */
+spBone* spSkeleton_findBone (const spSkeleton* self, const char* boneName);
+/* Returns -1 if the bone was not found. */
+int spSkeleton_findBoneIndex (const spSkeleton* self, const char* boneName);
+
+/* Returns 0 if the slot was not found. */
+spSlot* spSkeleton_findSlot (const spSkeleton* self, const char* slotName);
+/* Returns -1 if the slot was not found. */
+int spSkeleton_findSlotIndex (const spSkeleton* self, const char* slotName);
+
+/* Sets the skin used to look up attachments not found in the SkeletonData defaultSkin. Attachments from the new skin are
+ * attached if the corresponding attachment from the old skin was attached.
+ * @param skin May be 0.*/
+void spSkeleton_setSkin (spSkeleton* self, spSkin* skin);
+/* Returns 0 if the skin was not found. See spSkeleton_setSkin.
+ * @param skinName May be 0. */
+int spSkeleton_setSkinByName (spSkeleton* self, const char* skinName);
+
+/* Returns 0 if the slot or attachment was not found. */
+spAttachment* spSkeleton_getAttachmentForSlotName (const spSkeleton* self, const char* slotName, const char* attachmentName);
+/* Returns 0 if the slot or attachment was not found. */
+spAttachment* spSkeleton_getAttachmentForSlotIndex (const spSkeleton* self, int slotIndex, const char* attachmentName);
+/* Returns 0 if the slot or attachment was not found. */
+int spSkeleton_setAttachment (spSkeleton* self, const char* slotName, const char* attachmentName);
+
+void spSkeleton_update (spSkeleton* self, float deltaTime);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spSkeleton Skeleton;
+#define Skeleton_create(...) spSkeleton_create(__VA_ARGS__)
+#define Skeleton_dispose(...) spSkeleton_dispose(__VA_ARGS__)
+#define Skeleton_updateWorldTransform(...) spSkeleton_updateWorldTransform(__VA_ARGS__)
+#define Skeleton_setToSetupPose(...) spSkeleton_setToSetupPose(__VA_ARGS__)
+#define Skeleton_setBonesToSetupPose(...) spSkeleton_setBonesToSetupPose(__VA_ARGS__)
+#define Skeleton_setSlotsToSetupPose(...) spSkeleton_setSlotsToSetupPose(__VA_ARGS__)
+#define Skeleton_findBone(...) spSkeleton_findBone(__VA_ARGS__)
+#define Skeleton_findBoneIndex(...) spSkeleton_findBoneIndex(__VA_ARGS__)
+#define Skeleton_findSlot(...) spSkeleton_findSlot(__VA_ARGS__)
+#define Skeleton_findSlotIndex(...) spSkeleton_findSlotIndex(__VA_ARGS__)
+#define Skeleton_setSkin(...) spSkeleton_setSkin(__VA_ARGS__)
+#define Skeleton_setSkinByName(...) spSkeleton_setSkinByName(__VA_ARGS__)
+#define Skeleton_getAttachmentForSlotName(...) spSkeleton_getAttachmentForSlotName(__VA_ARGS__)
+#define Skeleton_getAttachmentForSlotIndex(...) spSkeleton_getAttachmentForSlotIndex(__VA_ARGS__)
+#define Skeleton_setAttachment(...) spSkeleton_setAttachment(__VA_ARGS__)
+#define Skeleton_update(...) spSkeleton_update(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_SKELETON_H_*/

+ 203 - 0
engine/source/spine/SkeletonBounds.c

@@ -0,0 +1,203 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#include <spine/SkeletonBounds.h>
+#include <limits.h>
+#include <spine/extension.h>
+
+spPolygon* spPolygon_create (int capacity) {
+	spPolygon* self = NEW(spPolygon);
+	self->capacity = capacity;
+	CONST_CAST(float*, self->vertices) = MALLOC(float, capacity);
+	return self;
+}
+
+void spPolygon_dispose (spPolygon* self) {
+	FREE(self->vertices);
+	FREE(self);
+}
+
+int/*bool*/spPolygon_containsPoint (spPolygon* self, float x, float y) {
+	int prevIndex = self->count - 2;
+	int inside = 0;
+	int i;
+	for (i = 0; i < self->count; i += 2) {
+		float vertexY = self->vertices[i + 1];
+		float prevY = self->vertices[prevIndex + 1];
+		if ((vertexY < y && prevY >= y) || (prevY < y && vertexY >= y)) {
+			float vertexX = self->vertices[i];
+			if (vertexX + (y - vertexY) / (prevY - vertexY) * (self->vertices[prevIndex] - vertexX) < x) inside = !inside;
+		}
+		prevIndex = i;
+	}
+	return inside;
+}
+
+int/*bool*/spPolygon_intersectsSegment (spPolygon* self, float x1, float y1, float x2, float y2) {
+	float width12 = x1 - x2, height12 = y1 - y2;
+	float det1 = x1 * y2 - y1 * x2;
+	float x3 = self->vertices[self->count - 2], y3 = self->vertices[self->count - 1];
+	int i;
+	for (i = 0; i < self->count; i += 2) {
+		float x4 = self->vertices[i], y4 = self->vertices[i + 1];
+		float det2 = x3 * y4 - y3 * x4;
+		float width34 = x3 - x4, height34 = y3 - y4;
+		float det3 = width12 * height34 - height12 * width34;
+		float x = (det1 * width34 - width12 * det2) / det3;
+		if (((x >= x3 && x <= x4) || (x >= x4 && x <= x3)) && ((x >= x1 && x <= x2) || (x >= x2 && x <= x1))) {
+			float y = (det1 * height34 - height12 * det2) / det3;
+			if (((y >= y3 && y <= y4) || (y >= y4 && y <= y3)) && ((y >= y1 && y <= y2) || (y >= y2 && y <= y1))) return 1;
+		}
+		x3 = x4;
+		y3 = y4;
+	}
+	return 0;
+}
+
+/**/
+
+typedef struct {
+	spSkeletonBounds super;
+	int capacity;
+} _spSkeletonBounds;
+
+spSkeletonBounds* spSkeletonBounds_create () {
+	return SUPER(NEW(_spSkeletonBounds));
+}
+
+void spSkeletonBounds_dispose (spSkeletonBounds* self) {
+	int i;
+	for (i = 0; i < SUB_CAST(_spSkeletonBounds, self)->capacity; ++i)
+		if (self->polygons[i]) spPolygon_dispose(self->polygons[i]);
+	FREE(self->polygons);
+	FREE(self->boundingBoxes);
+	FREE(self);
+}
+
+void spSkeletonBounds_update (spSkeletonBounds* self, spSkeleton* skeleton, int/*bool*/updateAabb) {
+	int i;
+
+	_spSkeletonBounds* internal = SUB_CAST(_spSkeletonBounds, self);
+	if (internal->capacity < skeleton->slotCount) {
+		spPolygon** newPolygons;
+
+		FREE(self->boundingBoxes);
+		self->boundingBoxes = MALLOC(spBoundingBoxAttachment*, skeleton->slotCount);
+
+		newPolygons = CALLOC(spPolygon*, skeleton->slotCount);
+		memcpy(newPolygons, self->polygons, internal->capacity);
+		FREE(self->polygons);
+		self->polygons = newPolygons;
+
+		internal->capacity = skeleton->slotCount;
+	}
+
+	self->minX = (float)INT_MAX;
+	self->minY = (float)INT_MAX;
+	self->maxX = (float)INT_MIN;
+	self->maxY = (float)INT_MIN;
+
+	self->count = 0;
+	for (i = 0; i < skeleton->slotCount; ++i) {
+		spPolygon* polygon;
+		spBoundingBoxAttachment* boundingBox;
+
+		spSlot* slot = skeleton->slots[i];
+		spAttachment* attachment = slot->attachment;
+		if (!attachment || attachment->type != ATTACHMENT_BOUNDING_BOX) continue;
+		boundingBox = (spBoundingBoxAttachment*)attachment;
+		self->boundingBoxes[self->count] = boundingBox;
+
+		polygon = self->polygons[self->count];
+		if (!polygon || polygon->capacity < boundingBox->verticesCount) {
+			if (polygon) spPolygon_dispose(polygon);
+			self->polygons[self->count] = polygon = spPolygon_create(boundingBox->verticesCount);
+		}
+		polygon->count = boundingBox->verticesCount;
+		spBoundingBoxAttachment_computeWorldVertices(boundingBox, skeleton->x, skeleton->y, slot->bone, polygon->vertices);
+
+		if (updateAabb) {
+			int ii = 0;
+			for (; ii < polygon->count; ii += 2) {
+				float x = polygon->vertices[ii];
+				float y = polygon->vertices[ii + 1];
+				if (x < self->minX) self->minX = x;
+				if (y < self->minY) self->minY = y;
+				if (x > self->maxX) self->maxX = x;
+				if (y > self->maxY) self->maxY = y;
+			}
+		}
+
+		++self->count;
+	}
+}
+
+int/*bool*/spSkeletonBounds_aabbContainsPoint (spSkeletonBounds* self, float x, float y) {
+	return x >= self->minX && x <= self->maxX && y >= self->minY && y <= self->maxY;
+}
+
+int/*bool*/spSkeletonBounds_aabbIntersectsSegment (spSkeletonBounds* self, float x1, float y1, float x2, float y2) {
+	float m, x, y;
+	if ((x1 <= self->minX && x2 <= self->minX) || (y1 <= self->minY && y2 <= self->minY) || (x1 >= self->maxX && x2 >= self->maxX)
+			|| (y1 >= self->maxY && y2 >= self->maxY)) return 0;
+	m = (y2 - y1) / (x2 - x1);
+	y = m * (self->minX - x1) + y1;
+	if (y > self->minY && y < self->maxY) return 1;
+	y = m * (self->maxX - x1) + y1;
+	if (y > self->minY && y < self->maxY) return 1;
+	x = (self->minY - y1) / m + x1;
+	if (x > self->minX && x < self->maxX) return 1;
+	x = (self->maxY - y1) / m + x1;
+	if (x > self->minX && x < self->maxX) return 1;
+	return 0;
+}
+
+int/*bool*/spSkeletonBounds_aabbIntersectsSkeleton (spSkeletonBounds* self, spSkeletonBounds* bounds) {
+	return self->minX < bounds->maxX && self->maxX > bounds->minX && self->minY < bounds->maxY && self->maxY > bounds->minY;
+}
+
+spBoundingBoxAttachment* spSkeletonBounds_containsPoint (spSkeletonBounds* self, float x, float y) {
+	int i;
+	for (i = 0; i < self->count; ++i)
+		if (spPolygon_containsPoint(self->polygons[i], x, y)) return self->boundingBoxes[i];
+	return 0;
+}
+
+spBoundingBoxAttachment* spSkeletonBounds_intersectsSegment (spSkeletonBounds* self, float x1, float y1, float x2, float y2) {
+	int i;
+	for (i = 0; i < self->count; ++i)
+		if (spPolygon_intersectsSegment(self->polygons[i], x1, y1, x2, y2)) return self->boundingBoxes[i];
+	return 0;
+}
+
+spPolygon* spSkeletonBounds_getPolygon (spSkeletonBounds* self, spBoundingBoxAttachment* boundingBox) {
+	int i;
+	for (i = 0; i < self->count; ++i)
+		if (self->boundingBoxes[i] == boundingBox) return self->polygons[i];
+	return 0;
+}

+ 110 - 0
engine/source/spine/SkeletonBounds.h

@@ -0,0 +1,110 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef SPINE_SKELETONBOUNDS_H_
+#define SPINE_SKELETONBOUNDS_H_
+
+#include <spine/BoundingBoxAttachment.h>
+#include <spine/Skeleton.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+	float* const vertices;
+	int count;
+	int capacity;
+} spPolygon;
+
+spPolygon* spPolygon_create (int capacity);
+void spPolygon_dispose (spPolygon* self);
+
+int/*bool*/spPolygon_containsPoint (spPolygon* polygon, float x, float y);
+int/*bool*/spPolygon_intersectsSegment (spPolygon* polygon, float x1, float y1, float x2, float y2);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spPolygon Polygon;
+#define Polygon_create(...) spPolygon_create(__VA_ARGS__)
+#define Polygon_dispose(...) spPolygon_dispose(__VA_ARGS__)
+#define Polygon_containsPoint(...) spPolygon_containsPoint(__VA_ARGS__)
+#define Polygon_intersectsSegment(...) spPolygon_intersectsSegment(__VA_ARGS__)
+#endif
+
+/**/
+
+typedef struct {
+	int count;
+	spBoundingBoxAttachment** boundingBoxes;
+	spPolygon** polygons;
+
+	float minX, minY, maxX, maxY;
+} spSkeletonBounds;
+
+spSkeletonBounds* spSkeletonBounds_create ();
+void spSkeletonBounds_dispose (spSkeletonBounds* self);
+void spSkeletonBounds_update (spSkeletonBounds* self, spSkeleton* skeleton, int/*bool*/updateAabb);
+
+/** Returns true if the axis aligned bounding box contains the point. */
+int/*bool*/spSkeletonBounds_aabbContainsPoint (spSkeletonBounds* self, float x, float y);
+
+/** Returns true if the axis aligned bounding box intersects the line segment. */
+int/*bool*/spSkeletonBounds_aabbIntersectsSegment (spSkeletonBounds* self, float x1, float y1, float x2, float y2);
+
+/** Returns true if the axis aligned bounding box intersects the axis aligned bounding box of the specified bounds. */
+int/*bool*/spSkeletonBounds_aabbIntersectsSkeleton (spSkeletonBounds* self, spSkeletonBounds* bounds);
+
+/** Returns the first bounding box attachment that contains the point, or null. When doing many checks, it is usually more
+ * efficient to only call this method if spSkeletonBounds_aabbContainsPoint returns true. */
+spBoundingBoxAttachment* spSkeletonBounds_containsPoint (spSkeletonBounds* self, float x, float y);
+
+/** Returns the first bounding box attachment that contains the line segment, or null. When doing many checks, it is usually
+ * more efficient to only call this method if spSkeletonBounds_aabbIntersectsSegment returns true. */
+spBoundingBoxAttachment* spSkeletonBounds_intersectsSegment (spSkeletonBounds* self, float x1, float y1, float x2, float y2);
+
+/** Returns the polygon for the specified bounding box, or null. */
+spPolygon* spSkeletonBounds_getPolygon (spSkeletonBounds* self, spBoundingBoxAttachment* boundingBox);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spSkeletonBounds SkeletonBounds;
+#define SkeletonBounds_create(...) spSkeletonBounds_create(__VA_ARGS__)
+#define SkeletonBounds_dispose(...) spSkeletonBounds_dispose(__VA_ARGS__)
+#define SkeletonBounds_update(...) spSkeletonBounds_update(__VA_ARGS__)
+#define SkeletonBounds_aabbContainsPoint(...) spSkeletonBounds_aabbContainsPoint(__VA_ARGS__)
+#define SkeletonBounds_aabbIntersectsSegment(...) spSkeletonBounds_aabbIntersectsSegment(__VA_ARGS__)
+#define SkeletonBounds_aabbIntersectsSkeleton(...) spSkeletonBounds_aabbIntersectsSkeleton(__VA_ARGS__)
+#define SkeletonBounds_containsPoint(...) spSkeletonBounds_containsPoint(__VA_ARGS__)
+#define SkeletonBounds_intersectsSegment(...) spSkeletonBounds_intersectsSegment(__VA_ARGS__)
+#define SkeletonBounds_getPolygon(...) spSkeletonBounds_getPolygon(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_SKELETONBOUNDS_H_ */

+ 109 - 0
engine/source/spine/SkeletonData.c

@@ -0,0 +1,109 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#include <spine/SkeletonData.h>
+#include <string.h>
+#include <spine/extension.h>
+
+spSkeletonData* spSkeletonData_create () {
+	return NEW(spSkeletonData);
+}
+
+void spSkeletonData_dispose (spSkeletonData* self) {
+	int i;
+	for (i = 0; i < self->boneCount; ++i)
+		spBoneData_dispose(self->bones[i]);
+	FREE(self->bones);
+
+	for (i = 0; i < self->slotCount; ++i)
+		spSlotData_dispose(self->slots[i]);
+	FREE(self->slots);
+
+	for (i = 0; i < self->skinCount; ++i)
+		spSkin_dispose(self->skins[i]);
+	FREE(self->skins);
+
+	for (i = 0; i < self->animationCount; ++i)
+		spAnimation_dispose(self->animations[i]);
+	FREE(self->animations);
+
+	for (i = 0; i < self->eventCount; ++i)
+		spEventData_dispose(self->events[i]);
+	FREE(self->events);
+
+	FREE(self);
+}
+
+spBoneData* spSkeletonData_findBone (const spSkeletonData* self, const char* boneName) {
+	int i;
+	for (i = 0; i < self->boneCount; ++i)
+		if (strcmp(self->bones[i]->name, boneName) == 0) return self->bones[i];
+	return 0;
+}
+
+int spSkeletonData_findBoneIndex (const spSkeletonData* self, const char* boneName) {
+	int i;
+	for (i = 0; i < self->boneCount; ++i)
+		if (strcmp(self->bones[i]->name, boneName) == 0) return i;
+	return -1;
+}
+
+spSlotData* spSkeletonData_findSlot (const spSkeletonData* self, const char* slotName) {
+	int i;
+	for (i = 0; i < self->slotCount; ++i)
+		if (strcmp(self->slots[i]->name, slotName) == 0) return self->slots[i];
+	return 0;
+}
+
+int spSkeletonData_findSlotIndex (const spSkeletonData* self, const char* slotName) {
+	int i;
+	for (i = 0; i < self->slotCount; ++i)
+		if (strcmp(self->slots[i]->name, slotName) == 0) return i;
+	return -1;
+}
+
+spSkin* spSkeletonData_findSkin (const spSkeletonData* self, const char* skinName) {
+	int i;
+	for (i = 0; i < self->skinCount; ++i)
+		if (strcmp(self->skins[i]->name, skinName) == 0) return self->skins[i];
+	return 0;
+}
+
+spEventData* spSkeletonData_findEvent (const spSkeletonData* self, const char* eventName) {
+	int i;
+	for (i = 0; i < self->eventCount; ++i)
+		if (strcmp(self->events[i]->name, eventName) == 0) return self->events[i];
+	return 0;
+}
+
+spAnimation* spSkeletonData_findAnimation (const spSkeletonData* self, const char* animationName) {
+	int i;
+	for (i = 0; i < self->animationCount; ++i)
+		if (strcmp(self->animations[i]->name, animationName) == 0) return self->animations[i];
+	return 0;
+}

+ 92 - 0
engine/source/spine/SkeletonData.h

@@ -0,0 +1,92 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef SPINE_SKELETONDATA_H_
+#define SPINE_SKELETONDATA_H_
+
+#include <spine/BoneData.h>
+#include <spine/SlotData.h>
+#include <spine/Skin.h>
+#include <spine/EventData.h>
+#include <spine/Animation.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+	int boneCount;
+	spBoneData** bones;
+
+	int slotCount;
+	spSlotData** slots;
+
+	int skinCount;
+	spSkin** skins;
+	spSkin* defaultSkin;
+
+	int eventCount;
+	spEventData** events;
+
+	int animationCount;
+	spAnimation** animations;
+} spSkeletonData;
+
+spSkeletonData* spSkeletonData_create ();
+void spSkeletonData_dispose (spSkeletonData* self);
+
+spBoneData* spSkeletonData_findBone (const spSkeletonData* self, const char* boneName);
+int spSkeletonData_findBoneIndex (const spSkeletonData* self, const char* boneName);
+
+spSlotData* spSkeletonData_findSlot (const spSkeletonData* self, const char* slotName);
+int spSkeletonData_findSlotIndex (const spSkeletonData* self, const char* slotName);
+
+spSkin* spSkeletonData_findSkin (const spSkeletonData* self, const char* skinName);
+
+spEventData* spSkeletonData_findEvent (const spSkeletonData* self, const char* eventName);
+
+spAnimation* spSkeletonData_findAnimation (const spSkeletonData* self, const char* animationName);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spSkeletonData SkeletonData;
+#define SkeletonData_create(...) spSkeletonData_create(__VA_ARGS__)
+#define SkeletonData_dispose(...) spSkeletonData_dispose(__VA_ARGS__)
+#define SkeletonData_findBone(...) spSkeletonData_findBone(__VA_ARGS__)
+#define SkeletonData_findBoneIndex(...) spSkeletonData_findBoneIndex(__VA_ARGS__)
+#define SkeletonData_findSlot(...) spSkeletonData_findSlot(__VA_ARGS__)
+#define SkeletonData_findSlotIndex(...) spSkeletonData_findSlotIndex(__VA_ARGS__)
+#define SkeletonData_findSkin(...) spSkeletonData_findSkin(__VA_ARGS__)
+#define SkeletonData_findEvent(...) spSkeletonData_findEvent(__VA_ARGS__)
+#define SkeletonData_findAnimation(...) spSkeletonData_findAnimation(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_SKELETONDATA_H_ */

+ 500 - 0
engine/source/spine/SkeletonJson.c

@@ -0,0 +1,500 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#include <spine/SkeletonJson.h>
+#include <stdio.h>
+#include "Json.h"
+#include <spine/extension.h>
+#include <spine/RegionAttachment.h>
+#include <spine/AtlasAttachmentLoader.h>
+
+typedef struct {
+	spSkeletonJson super;
+	int ownsLoader;
+} _spSkeletonJson;
+
+spSkeletonJson* spSkeletonJson_createWithLoader (spAttachmentLoader* attachmentLoader) {
+	spSkeletonJson* self = SUPER(NEW(_spSkeletonJson));
+	self->scale = 1;
+	self->attachmentLoader = attachmentLoader;
+	return self;
+}
+
+spSkeletonJson* spSkeletonJson_create (spAtlas* atlas) {
+	spAtlasAttachmentLoader* attachmentLoader = spAtlasAttachmentLoader_create(atlas);
+	spSkeletonJson* self = spSkeletonJson_createWithLoader(SUPER(attachmentLoader));
+	SUB_CAST(_spSkeletonJson, self)->ownsLoader = 1;
+	return self;
+}
+
+void spSkeletonJson_dispose (spSkeletonJson* self) {
+	if (SUB_CAST(_spSkeletonJson, self)->ownsLoader) spAttachmentLoader_dispose(self->attachmentLoader);
+	FREE(self->error);
+	FREE(self);
+}
+
+void _spSkeletonJson_setError (spSkeletonJson* self, Json* root, const char* value1, const char* value2) {
+	char message[256];
+	int length;
+	FREE(self->error);
+	strcpy(message, value1);
+	length = strlen(value1);
+	if (value2) strncat(message + length, value2, 256 - length);
+	MALLOC_STR(self->error, message);
+	if (root) Json_dispose(root);
+}
+
+static float toColor (const char* value, int index) {
+	char digits[3];
+	char *error;
+	int color;
+
+	if (strlen(value) != 8) return -1;
+	value += index * 2;
+
+	digits[0] = *value;
+	digits[1] = *(value + 1);
+	digits[2] = '\0';
+	color = strtoul(digits, &error, 16);
+	if (*error != 0) return -1;
+	return color / (float)255;
+}
+
+static void readCurve (spCurveTimeline* timeline, int frameIndex, Json* frame) {
+	Json* curve = Json_getItem(frame, "curve");
+	if (!curve) return;
+	if (curve->type == Json_String && strcmp(curve->valueString, "stepped") == 0)
+		spCurveTimeline_setStepped(timeline, frameIndex);
+	else if (curve->type == Json_Array) {
+		Json* child0 = curve->child;
+		Json* child1 = child0->next;
+		Json* child2 = child1->next;
+		Json* child3 = child2->next;
+		spCurveTimeline_setCurve(timeline, frameIndex, child0->valueFloat, child1->valueFloat, child2->valueFloat,
+				child3->valueFloat);
+	}
+}
+
+static spAnimation* _spSkeletonJson_readAnimation (spSkeletonJson* self, Json* root, spSkeletonData *skeletonData) {
+	int i;
+	spAnimation* animation;
+
+	Json* bones = Json_getItem(root, "bones");
+	Json* slots = Json_getItem(root, "slots");
+	Json* drawOrder = Json_getItem(root, "draworder");
+	Json* events = Json_getItem(root, "events");
+	Json *boneMap, *slotMap;
+
+	int timelineCount = 0;
+	for (boneMap = bones ? bones->child : 0; boneMap; boneMap = boneMap->next)
+		timelineCount += boneMap->size;
+	for (slotMap = slots ? slots->child : 0; slotMap; slotMap = slotMap->next)
+		timelineCount += slotMap->size;
+	if (events) ++timelineCount;
+	if (drawOrder) ++timelineCount;
+
+	animation = spAnimation_create(root->name, timelineCount);
+	animation->timelineCount = 0;
+	skeletonData->animations[skeletonData->animationCount] = animation;
+	++skeletonData->animationCount;
+
+	for (boneMap = bones ? bones->child : 0; boneMap; boneMap = boneMap->next) {
+		Json *timelineArray;
+
+		int boneIndex = spSkeletonData_findBoneIndex(skeletonData, boneMap->name);
+		if (boneIndex == -1) {
+			spAnimation_dispose(animation);
+			_spSkeletonJson_setError(self, root, "spBone not found: ", boneMap->name);
+			return 0;
+		}
+
+		for (timelineArray = boneMap->child; timelineArray; timelineArray = timelineArray->next) {
+			Json* frame;
+			float duration;
+
+			if (strcmp(timelineArray->name, "rotate") == 0) {
+				spRotateTimeline *timeline = spRotateTimeline_create(timelineArray->size);
+				timeline->boneIndex = boneIndex;
+				for (frame = timelineArray->child, i = 0; frame; frame = frame->next, ++i) {
+					spRotateTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0), Json_getFloat(frame, "angle", 0));
+					readCurve(SUPER(timeline), i, frame);
+				}
+				animation->timelines[animation->timelineCount++] = (spTimeline*)timeline;
+				duration = timeline->frames[timelineArray->size * 2 - 2];
+				if (duration > animation->duration) animation->duration = duration;
+
+			} else {
+				int isScale = strcmp(timelineArray->name, "scale") == 0;
+				if (isScale || strcmp(timelineArray->name, "translate") == 0) {
+					float scale = isScale ? 1 : self->scale;
+					spTranslateTimeline *timeline =
+							isScale ? spScaleTimeline_create(timelineArray->size) : spTranslateTimeline_create(timelineArray->size);
+					timeline->boneIndex = boneIndex;
+					for (frame = timelineArray->child, i = 0; frame; frame = frame->next, ++i) {
+						spTranslateTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0), Json_getFloat(frame, "x", 0) * scale,
+								Json_getFloat(frame, "y", 0) * scale);
+						readCurve(SUPER(timeline), i, frame);
+					}
+					animation->timelines[animation->timelineCount++] = (spTimeline*)timeline;
+					duration = timeline->frames[timelineArray->size * 3 - 3];
+					if (duration > animation->duration) animation->duration = duration;
+				} else {
+					spAnimation_dispose(animation);
+					_spSkeletonJson_setError(self, 0, "Invalid timeline type for a bone: ", timelineArray->name);
+					return 0;
+				}
+			}
+		}
+	}
+
+	for (slotMap = slots ? slots->child : 0; slotMap; slotMap = slotMap->next) {
+		Json *timelineArray;
+
+		int slotIndex = spSkeletonData_findSlotIndex(skeletonData, slotMap->name);
+		if (slotIndex == -1) {
+			spAnimation_dispose(animation);
+			_spSkeletonJson_setError(self, root, "Slot not found: ", slotMap->name);
+			return 0;
+		}
+
+		for (timelineArray = slotMap->child; timelineArray; timelineArray = timelineArray->next) {
+			Json* frame;
+			float duration;
+
+			if (strcmp(timelineArray->name, "color") == 0) {
+				spColorTimeline *timeline = spColorTimeline_create(timelineArray->size);
+				timeline->slotIndex = slotIndex;
+				for (frame = timelineArray->child, i = 0; frame; frame = frame->next, ++i) {
+					const char* s = Json_getString(frame, "color", 0);
+					spColorTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0), toColor(s, 0), toColor(s, 1), toColor(s, 2),
+							toColor(s, 3));
+					readCurve(SUPER(timeline), i, frame);
+				}
+				animation->timelines[animation->timelineCount++] = (spTimeline*)timeline;
+				duration = timeline->frames[timelineArray->size * 5 - 5];
+				if (duration > animation->duration) animation->duration = duration;
+
+			} else if (strcmp(timelineArray->name, "attachment") == 0) {
+				spAttachmentTimeline *timeline = spAttachmentTimeline_create(timelineArray->size);
+				timeline->slotIndex = slotIndex;
+				for (frame = timelineArray->child, i = 0; frame; frame = frame->next, ++i) {
+					Json* name = Json_getItem(frame, "name");
+					spAttachmentTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0),
+							name->type == Json_NULL ? 0 : name->valueString);
+				}
+				animation->timelines[animation->timelineCount++] = (spTimeline*)timeline;
+				duration = timeline->frames[timelineArray->size - 1];
+				if (duration > animation->duration) animation->duration = duration;
+
+			} else {
+				spAnimation_dispose(animation);
+				_spSkeletonJson_setError(self, 0, "Invalid timeline type for a slot: ", timelineArray->name);
+				return 0;
+			}
+		}
+	}
+
+	if (events) {
+		Json* frame;
+		float duration;
+
+		spEventTimeline* timeline = spEventTimeline_create(events->size);
+		for (frame = events->child, i = 0; frame; frame = frame->next, ++i) {
+			spEvent* event;
+			const char* stringValue;
+			spEventData* eventData = spSkeletonData_findEvent(skeletonData, Json_getString(frame, "name", 0));
+			if (!eventData) {
+				spAnimation_dispose(animation);
+				_spSkeletonJson_setError(self, 0, "Event not found: ", Json_getString(frame, "name", 0));
+				return 0;
+			}
+			event = spEvent_create(eventData);
+			event->intValue = Json_getInt(frame, "int", eventData->intValue);
+			event->floatValue = Json_getFloat(frame, "float", eventData->floatValue);
+			stringValue = Json_getString(frame, "string", eventData->stringValue);
+			if (stringValue) MALLOC_STR(event->stringValue, stringValue);
+			spEventTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0), event);
+		}
+		animation->timelines[animation->timelineCount++] = (spTimeline*)timeline;
+		duration = timeline->frames[events->size - 1];
+		if (duration > animation->duration) animation->duration = duration;
+	}
+
+	if (drawOrder) {
+		Json* frame;
+		float duration;
+
+		spDrawOrderTimeline* timeline = spDrawOrderTimeline_create(drawOrder->size, skeletonData->slotCount);
+		for (frame = drawOrder->child, i = 0; frame; frame = frame->next, ++i) {
+			int ii;
+			int* drawOrder = 0;
+			Json* offsets = Json_getItem(frame, "offsets");
+			if (offsets) {
+				Json* offsetMap;
+				int* unchanged = MALLOC(int, skeletonData->slotCount - offsets->size);
+				int originalIndex = 0, unchangedIndex = 0;
+
+				drawOrder = MALLOC(int, skeletonData->slotCount);
+				for (ii = skeletonData->slotCount - 1; ii >= 0; --ii)
+					drawOrder[ii] = -1;
+
+				for (offsetMap = offsets->child; offsetMap; offsetMap = offsetMap->next) {
+					int slotIndex = spSkeletonData_findSlotIndex(skeletonData, Json_getString(offsetMap, "slot", 0));
+					if (slotIndex == -1) {
+						spAnimation_dispose(animation);
+						_spSkeletonJson_setError(self, 0, "Slot not found: ", Json_getString(offsetMap, "slot", 0));
+						return 0;
+					}
+					/* Collect unchanged items. */
+					while (originalIndex != slotIndex)
+						unchanged[unchangedIndex++] = originalIndex++;
+					/* Set changed items. */
+					drawOrder[originalIndex + Json_getInt(offsetMap, "offset", 0)] = originalIndex;
+					++originalIndex;
+				}
+				/* Collect remaining unchanged items. */
+				while (originalIndex < skeletonData->slotCount)
+					unchanged[unchangedIndex++] = originalIndex++;
+				/* Fill in unchanged items. */
+				for (ii = skeletonData->slotCount - 1; ii >= 0; ii--)
+					if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex];
+				FREE(unchanged);
+			}
+			spDrawOrderTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0), drawOrder);
+			FREE(drawOrder);
+		}
+		animation->timelines[animation->timelineCount++] = (spTimeline*)timeline;
+		duration = timeline->frames[drawOrder->size - 1];
+		if (duration > animation->duration) animation->duration = duration;
+	}
+
+	return animation;
+}
+
+spSkeletonData* spSkeletonJson_readSkeletonDataFile (spSkeletonJson* self, const char* path) {
+	int length;
+	spSkeletonData* skeletonData;
+	const char* json = _spUtil_readFile(path, &length);
+	if (!json) {
+		_spSkeletonJson_setError(self, 0, "Unable to read skeleton file: ", path);
+		return 0;
+	}
+	skeletonData = spSkeletonJson_readSkeletonData(self, json);
+	FREE(json);
+	return skeletonData;
+}
+
+spSkeletonData* spSkeletonJson_readSkeletonData (spSkeletonJson* self, const char* json) {
+	int i;
+	spSkeletonData* skeletonData;
+	Json *root, *bones, *boneMap, *slots, *skins, *animations, *events;
+
+	FREE(self->error);
+	CONST_CAST(char*, self->error) = 0;
+
+	root = Json_create(json);
+	if (!root) {
+		_spSkeletonJson_setError(self, 0, "Invalid skeleton JSON: ", Json_getError());
+		return 0;
+	}
+
+	skeletonData = spSkeletonData_create();
+
+	bones = Json_getItem(root, "bones");
+	skeletonData->bones = MALLOC(spBoneData*, bones->size);
+	for (boneMap = bones->child, i = 0; boneMap; boneMap = boneMap->next, ++i) {
+		spBoneData* boneData;
+
+		spBoneData* parent = 0;
+		const char* parentName = Json_getString(boneMap, "parent", 0);
+		if (parentName) {
+			parent = spSkeletonData_findBone(skeletonData, parentName);
+			if (!parent) {
+				spSkeletonData_dispose(skeletonData);
+				_spSkeletonJson_setError(self, root, "Parent bone not found: ", parentName);
+				return 0;
+			}
+		}
+
+		boneData = spBoneData_create(Json_getString(boneMap, "name", 0), parent);
+		boneData->length = Json_getFloat(boneMap, "length", 0) * self->scale;
+		boneData->x = Json_getFloat(boneMap, "x", 0) * self->scale;
+		boneData->y = Json_getFloat(boneMap, "y", 0) * self->scale;
+		boneData->rotation = Json_getFloat(boneMap, "rotation", 0);
+		boneData->scaleX = Json_getFloat(boneMap, "scaleX", 1);
+		boneData->scaleY = Json_getFloat(boneMap, "scaleY", 1);
+		boneData->inheritScale = Json_getInt(boneMap, "inheritScale", 1);
+		boneData->inheritRotation = Json_getInt(boneMap, "inheritRotation", 1);
+
+		skeletonData->bones[i] = boneData;
+		++skeletonData->boneCount;
+	}
+
+	slots = Json_getItem(root, "slots");
+	if (slots) {
+		Json *slotMap;
+		skeletonData->slots = MALLOC(spSlotData*, slots->size);
+		for (slotMap = slots->child, i = 0; slotMap; slotMap = slotMap->next, ++i) {
+			spSlotData* slotData;
+			const char* color;
+			Json *attachmentItem;
+
+			const char* boneName = Json_getString(slotMap, "bone", 0);
+			spBoneData* boneData = spSkeletonData_findBone(skeletonData, boneName);
+			if (!boneData) {
+				spSkeletonData_dispose(skeletonData);
+				_spSkeletonJson_setError(self, root, "spSlot bone not found: ", boneName);
+				return 0;
+			}
+
+			slotData = spSlotData_create(Json_getString(slotMap, "name", 0), boneData);
+
+			color = Json_getString(slotMap, "color", 0);
+			if (color) {
+				slotData->r = toColor(color, 0);
+				slotData->g = toColor(color, 1);
+				slotData->b = toColor(color, 2);
+				slotData->a = toColor(color, 3);
+			}
+
+			attachmentItem = Json_getItem(slotMap, "attachment");
+			if (attachmentItem) spSlotData_setAttachmentName(slotData, attachmentItem->valueString);
+
+			slotData->additiveBlending = Json_getInt(slotMap, "additive", 0);
+
+			skeletonData->slots[i] = slotData;
+			++skeletonData->slotCount;
+		}
+	}
+
+	skins = Json_getItem(root, "skins");
+	if (skins) {
+		Json *slotMap;
+		skeletonData->skins = MALLOC(spSkin*, skins->size);
+		for (slotMap = skins->child, i = 0; slotMap; slotMap = slotMap->next, ++i) {
+			Json *attachmentsMap;
+			spSkin *skin = spSkin_create(slotMap->name);
+
+			skeletonData->skins[i] = skin;
+			++skeletonData->skinCount;
+			if (strcmp(slotMap->name, "default") == 0) skeletonData->defaultSkin = skin;
+
+			for (attachmentsMap = slotMap->child; attachmentsMap; attachmentsMap = attachmentsMap->next) {
+				int slotIndex = spSkeletonData_findSlotIndex(skeletonData, attachmentsMap->name);
+				Json *attachmentMap;
+
+				for (attachmentMap = attachmentsMap->child; attachmentMap; attachmentMap = attachmentMap->next) {
+					spAttachment* attachment;
+					const char* skinAttachmentName = attachmentMap->name;
+					const char* attachmentName = Json_getString(attachmentMap, "name", skinAttachmentName);
+
+					const char* typeString = Json_getString(attachmentMap, "type", "region");
+					spAttachmentType type;
+					if (strcmp(typeString, "region") == 0)
+						type = ATTACHMENT_REGION;
+					else if (strcmp(typeString, "boundingbox") == 0)
+						type = ATTACHMENT_BOUNDING_BOX;
+					else if (strcmp(typeString, "regionsequence") == 0)
+						type = ATTACHMENT_REGION_SEQUENCE;
+					else {
+						spSkeletonData_dispose(skeletonData);
+						_spSkeletonJson_setError(self, root, "Unknown attachment type: ", typeString);
+						return 0;
+					}
+
+					attachment = spAttachmentLoader_newAttachment(self->attachmentLoader, skin, type, attachmentName);
+					if (!attachment) {
+						if (self->attachmentLoader->error1) {
+							spSkeletonData_dispose(skeletonData);
+							_spSkeletonJson_setError(self, root, self->attachmentLoader->error1, self->attachmentLoader->error2);
+							return 0;
+						}
+						continue;
+					}
+
+					switch (attachment->type) {
+					case ATTACHMENT_REGION:
+					case ATTACHMENT_REGION_SEQUENCE: {
+						spRegionAttachment* regionAttachment = (spRegionAttachment*)attachment;
+						regionAttachment->x = Json_getFloat(attachmentMap, "x", 0) * self->scale;
+						regionAttachment->y = Json_getFloat(attachmentMap, "y", 0) * self->scale;
+						regionAttachment->scaleX = Json_getFloat(attachmentMap, "scaleX", 1);
+						regionAttachment->scaleY = Json_getFloat(attachmentMap, "scaleY", 1);
+						regionAttachment->rotation = Json_getFloat(attachmentMap, "rotation", 0);
+						regionAttachment->width = Json_getFloat(attachmentMap, "width", 32) * self->scale;
+						regionAttachment->height = Json_getFloat(attachmentMap, "height", 32) * self->scale;
+						spRegionAttachment_updateOffset(regionAttachment);
+						break;
+					}
+					case ATTACHMENT_BOUNDING_BOX: {
+						spBoundingBoxAttachment* box = (spBoundingBoxAttachment*)attachment;
+						Json* verticesArray = Json_getItem(attachmentMap, "vertices");
+						Json* vertex;
+						int i;
+						box->verticesCount = verticesArray->size;
+						box->vertices = MALLOC(float, verticesArray->size);
+						for (vertex = verticesArray->child, i = 0; vertex; vertex = vertex->next, ++i)
+							box->vertices[i] = vertex->valueFloat * self->scale;
+						break;
+					}
+					}
+
+					spSkin_addAttachment(skin, slotIndex, skinAttachmentName, attachment);
+				}
+			}
+		}
+	}
+
+	/* Events. */
+	events = Json_getItem(root, "events");
+	if (events) {
+		Json *eventMap;
+		const char* stringValue;
+		skeletonData->events = MALLOC(spEventData*, events->size);
+		for (eventMap = events->child; eventMap; eventMap = eventMap->next) {
+			spEventData* eventData = spEventData_create(eventMap->name);
+			eventData->intValue = Json_getInt(eventMap, "int", 0);
+			eventData->floatValue = Json_getFloat(eventMap, "float", 0);
+			stringValue = Json_getString(eventMap, "string", 0);
+			if (stringValue) MALLOC_STR(eventData->stringValue, stringValue);
+			skeletonData->events[skeletonData->eventCount++] = eventData;
+		}
+	}
+
+	/* Animations. */
+	animations = Json_getItem(root, "animations");
+	if (animations) {
+		Json *animationMap;
+		skeletonData->animations = MALLOC(spAnimation*, animations->size);
+		for (animationMap = animations->child; animationMap; animationMap = animationMap->next)
+			_spSkeletonJson_readAnimation(self, animationMap, skeletonData);
+	}
+
+	Json_dispose(root);
+	return skeletonData;
+}

+ 68 - 0
engine/source/spine/SkeletonJson.h

@@ -0,0 +1,68 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef SPINE_SKELETONJSON_H_
+#define SPINE_SKELETONJSON_H_
+
+#include <spine/Attachment.h>
+#include <spine/AttachmentLoader.h>
+#include <spine/SkeletonData.h>
+#include <spine/Atlas.h>
+#include <spine/Animation.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+	float scale;
+	spAttachmentLoader* attachmentLoader;
+	const char* const error;
+} spSkeletonJson;
+
+spSkeletonJson* spSkeletonJson_createWithLoader (spAttachmentLoader* attachmentLoader);
+spSkeletonJson* spSkeletonJson_create (spAtlas* atlas);
+void spSkeletonJson_dispose (spSkeletonJson* self);
+
+spSkeletonData* spSkeletonJson_readSkeletonData (spSkeletonJson* self, const char* json);
+spSkeletonData* spSkeletonJson_readSkeletonDataFile (spSkeletonJson* self, const char* path);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spSkeletonJson SkeletonJson;
+#define SkeletonJson_createWithLoader(...) spSkeletonJson_createWithLoader(__VA_ARGS__)
+#define SkeletonJson_create(...) spSkeletonJson_create(__VA_ARGS__)
+#define SkeletonJson_dispose(...) spSkeletonJson_dispose(__VA_ARGS__)
+#define SkeletonJson_readSkeletonData(...) spSkeletonJson_readSkeletonData(__VA_ARGS__)
+#define SkeletonJson_readSkeletonDataFile(...) spSkeletonJson_readSkeletonDataFile(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_SKELETONJSON_H_ */

+ 117 - 0
engine/source/spine/Skin.c

@@ -0,0 +1,117 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#include <spine/Skin.h>
+#include <spine/extension.h>
+
+typedef struct _Entry _Entry;
+struct _Entry {
+	int slotIndex;
+	const char* name;
+	spAttachment* attachment;
+	_Entry* next;
+};
+
+_Entry* _Entry_create (int slotIndex, const char* name, spAttachment* attachment) {
+	_Entry* self = NEW(_Entry);
+	self->slotIndex = slotIndex;
+	MALLOC_STR(self->name, name);
+	self->attachment = attachment;
+	return self;
+}
+
+void _Entry_dispose (_Entry* self) {
+	spAttachment_dispose(self->attachment);
+	FREE(self->name);
+	FREE(self);
+}
+
+/**/
+
+typedef struct {
+	spSkin super;
+	_Entry* entries;
+} _spSkin;
+
+spSkin* spSkin_create (const char* name) {
+	spSkin* self = SUPER(NEW(_spSkin));
+	MALLOC_STR(self->name, name);
+	return self;
+}
+
+void spSkin_dispose (spSkin* self) {
+	_Entry* entry = SUB_CAST(_spSkin, self)->entries;
+	while (entry) {
+		_Entry* nextEntry = entry->next;
+		_Entry_dispose(entry);
+		entry = nextEntry;
+	}
+
+	FREE(self->name);
+	FREE(self);
+}
+
+void spSkin_addAttachment (spSkin* self, int slotIndex, const char* name, spAttachment* attachment) {
+	_Entry* newEntry = _Entry_create(slotIndex, name, attachment);
+	newEntry->next = SUB_CAST(_spSkin, self)->entries;
+	SUB_CAST(_spSkin, self)->entries = newEntry;
+}
+
+spAttachment* spSkin_getAttachment (const spSkin* self, int slotIndex, const char* name) {
+	const _Entry* entry = SUB_CAST(_spSkin, self)->entries;
+	while (entry) {
+		if (entry->slotIndex == slotIndex && strcmp(entry->name, name) == 0) return entry->attachment;
+		entry = entry->next;
+	}
+	return 0;
+}
+
+const char* spSkin_getAttachmentName (const spSkin* self, int slotIndex, int attachmentIndex) {
+	const _Entry* entry = SUB_CAST(_spSkin, self)->entries;
+	int i = 0;
+	while (entry) {
+		if (entry->slotIndex == slotIndex) {
+			if (i == attachmentIndex) return entry->name;
+			i++;
+		}
+		entry = entry->next;
+	}
+	return 0;
+}
+
+void spSkin_attachAll (const spSkin* self, spSkeleton* skeleton, const spSkin* oldSkin) {
+	const _Entry *entry = SUB_CAST(_spSkin, oldSkin)->entries;
+	while (entry) {
+		spSlot *slot = skeleton->slots[entry->slotIndex];
+		if (slot->attachment == entry->attachment) {
+			spAttachment *attachment = spSkin_getAttachment(self, entry->slotIndex, entry->name);
+			if (attachment) spSlot_setAttachment(slot, attachment);
+		}
+		entry = entry->next;
+	}
+}

+ 72 - 0
engine/source/spine/Skin.h

@@ -0,0 +1,72 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef SPINE_SKIN_H_
+#define SPINE_SKIN_H_
+
+#include <spine/Attachment.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct spSkeleton;
+
+typedef struct {
+	const char* const name;
+} spSkin;
+
+spSkin* spSkin_create (const char* name);
+void spSkin_dispose (spSkin* self);
+
+/* The Skin owns the attachment. */
+void spSkin_addAttachment (spSkin* self, int slotIndex, const char* name, spAttachment* attachment);
+/* Returns 0 if the attachment was not found. */
+spAttachment* spSkin_getAttachment (const spSkin* self, int slotIndex, const char* name);
+
+/* Returns 0 if the slot or attachment was not found. */
+const char* spSkin_getAttachmentName (const spSkin* self, int slotIndex, int attachmentIndex);
+
+/** Attach each attachment in this skin if the corresponding attachment in oldSkin is currently attached. */
+void spSkin_attachAll (const spSkin* self, struct spSkeleton* skeleton, const spSkin* oldspSkin);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spSkin Skin;
+#define Skin_create(...) spSkin_create(__VA_ARGS__)
+#define Skin_dispose(...) spSkin_dispose(__VA_ARGS__)
+#define Skin_addAttachment(...) spSkin_addAttachment(__VA_ARGS__)
+#define Skin_getAttachment(...) spSkin_getAttachment(__VA_ARGS__)
+#define Skin_getAttachmentName(...) spSkin_getAttachmentName(__VA_ARGS__)
+#define Skin_attachAll(...) spSkin_attachAll(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_SKIN_H_ */

+ 82 - 0
engine/source/spine/Slot.c

@@ -0,0 +1,82 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#include <spine/Slot.h>
+#include <spine/extension.h>
+#include <spine/Skeleton.h>
+
+typedef struct {
+	spSlot super;
+	float attachmentTime;
+} _spSlot;
+
+spSlot* spSlot_create (spSlotData* data, spSkeleton* skeleton, spBone* bone) {
+	spSlot* self = SUPER(NEW(_spSlot));
+	CONST_CAST(spSlotData*, self->data) = data;
+	CONST_CAST(spSkeleton*, self->skeleton) = skeleton;
+	CONST_CAST(spBone*, self->bone) = bone;
+	spSlot_setToSetupPose(self);
+	return self;
+}
+
+void spSlot_dispose (spSlot* self) {
+	FREE(self);
+}
+
+void spSlot_setAttachment (spSlot* self, spAttachment* attachment) {
+	CONST_CAST(spAttachment*, self->attachment) = attachment;
+	SUB_CAST(_spSlot, self) ->attachmentTime = self->skeleton->time;
+}
+
+void spSlot_setAttachmentTime (spSlot* self, float time) {
+	SUB_CAST(_spSlot, self) ->attachmentTime = self->skeleton->time - time;
+}
+
+float spSlot_getAttachmentTime (const spSlot* self) {
+	return self->skeleton->time - SUB_CAST(_spSlot, self) ->attachmentTime;
+}
+
+void spSlot_setToSetupPose (spSlot* self) {
+	spAttachment* attachment = 0;
+	self->r = self->data->r;
+	self->g = self->data->g;
+	self->b = self->data->b;
+	self->a = self->data->a;
+
+	if (self->data->attachmentName) {
+		/* Find slot index. */
+		int i;
+		for (i = 0; i < self->skeleton->data->slotCount; ++i) {
+			if (self->data == self->skeleton->data->slots[i]) {
+				attachment = spSkeleton_getAttachmentForSlotIndex(self->skeleton, i, self->data->attachmentName);
+				break;
+			}
+		}
+	}
+	spSlot_setAttachment(self, attachment);
+}

+ 75 - 0
engine/source/spine/Slot.h

@@ -0,0 +1,75 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef SPINE_SLOT_H_
+#define SPINE_SLOT_H_
+
+#include <spine/Bone.h>
+#include <spine/Attachment.h>
+#include <spine/SlotData.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct spSkeleton;
+
+typedef struct spSlot {
+	spSlotData* const data;
+	struct spSkeleton* const skeleton;
+	spBone* const bone;
+	float r, g, b, a;
+	spAttachment* const attachment;
+} spSlot;
+
+spSlot* spSlot_create (spSlotData* data, struct spSkeleton* skeleton, spBone* bone);
+void spSlot_dispose (spSlot* self);
+
+/* @param attachment May be 0 to clear the attachment for the slot. */
+void spSlot_setAttachment (spSlot* self, spAttachment* attachment);
+
+void spSlot_setAttachmentTime (spSlot* self, float time);
+float spSlot_getAttachmentTime (const spSlot* self);
+
+void spSlot_setToSetupPose (spSlot* self);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spSlot Slot;
+#define Slot_create(...) spSlot_create(__VA_ARGS__)
+#define Slot_dispose(...) spSlot_dispose(__VA_ARGS__)
+#define Slot_setAttachment(...) spSlot_setAttachment(__VA_ARGS__)
+#define Slot_setAttachmentTime(...) spSlot_setAttachmentTime(__VA_ARGS__)
+#define Slot_getAttachmentTime(...) spSlot_getAttachmentTime(__VA_ARGS__)
+#define Slot_setToSetupPose(...) spSlot_setToSetupPose(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_SLOT_H_ */

+ 55 - 0
engine/source/spine/SlotData.c

@@ -0,0 +1,55 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#include <spine/SlotData.h>
+#include <spine/extension.h>
+
+spSlotData* spSlotData_create (const char* name, spBoneData* boneData) {
+	spSlotData* self = NEW(spSlotData);
+	MALLOC_STR(self->name, name);
+	CONST_CAST(spBoneData*, self->boneData) = boneData;
+	self->r = 1;
+	self->g = 1;
+	self->b = 1;
+	self->a = 1;
+	return self;
+}
+
+void spSlotData_dispose (spSlotData* self) {
+	FREE(self->name);
+	FREE(self->attachmentName);
+	FREE(self);
+}
+
+void spSlotData_setAttachmentName (spSlotData* self, const char* attachmentName) {
+	FREE(self->attachmentName);
+	if (attachmentName)
+		MALLOC_STR(self->attachmentName, attachmentName);
+	else
+		CONST_CAST(char*, self->attachmentName) = 0;
+}

+ 63 - 0
engine/source/spine/SlotData.h

@@ -0,0 +1,63 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef SPINE_SLOTDATA_H_
+#define SPINE_SLOTDATA_H_
+
+#include <spine/BoneData.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+	const char* const name;
+	const spBoneData* const boneData;
+	const char* const attachmentName;
+	float r, g, b, a;
+	int/*bool*/additiveBlending;
+} spSlotData;
+
+spSlotData* spSlotData_create (const char* name, spBoneData* boneData);
+void spSlotData_dispose (spSlotData* self);
+
+/* @param attachmentName May be 0 for no setup pose attachment. */
+void spSlotData_setAttachmentName (spSlotData* self, const char* attachmentName);
+
+#ifdef SPINE_SHORT_NAMES
+typedef spSlotData SlotData;
+#define SlotData_create(...) spSlotData_create(__VA_ARGS__)
+#define SlotData_dispose(...) spSlotData_dispose(__VA_ARGS__)
+#define SlotData_setAttachmentName(...) spSlotData_setAttachmentName(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_SLOTDATA_H_ */

+ 68 - 0
engine/source/spine/extension.c

@@ -0,0 +1,68 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#include <spine/extension.h>
+#include <stdio.h>
+
+static void* (*mallocFunc) (size_t size) = malloc;
+static void (*freeFunc) (void* ptr) = free;
+
+void* _malloc (size_t size) {
+	return mallocFunc(size);
+}
+void* _calloc (size_t num, size_t size) {
+	void* ptr = mallocFunc(num * size);
+	if (ptr) memset(ptr, 0, num * size);
+	return ptr;
+}
+void _free (void* ptr) {
+	freeFunc(ptr);
+}
+
+void _setMalloc (void* (*malloc) (size_t size)) {
+	mallocFunc = malloc;
+}
+void _setFree (void (*free) (void* ptr)) {
+	freeFunc = free;
+}
+
+char* _readFile (const char* path, int* length) {
+	char *data;
+	FILE *file = fopen(path, "rb");
+	if (!file) return 0;
+
+	fseek(file, 0, SEEK_END);
+	*length = ftell(file);
+	fseek(file, 0, SEEK_SET);
+
+	data = MALLOC(char, *length);
+	fread(data, 1, *length, file);
+	fclose(file);
+
+	return data;
+}

+ 164 - 0
engine/source/spine/extension.h

@@ -0,0 +1,164 @@
+/*
+ Implementation notes:
+
+ - An OOP style is used where each "class" is made up of a struct and a number of functions prefixed with the struct name.
+
+ - struct fields that are const are readonly. Either they are set in a create function and can never be changed, or they can only
+ be changed by calling a function.
+
+ - Inheritance is done using a struct field named "super" as the first field, allowing the struct to be cast to its "super class".
+ This works because a pointer to a struct is guaranteed to be a pointer to the first struct field.
+
+ - Classes intended for inheritance provide init/deinit functions which subclasses must call in their create/dispose functions.
+
+ - Polymorphism is done by a base class providing function pointers in its init function. The public API delegates to this
+ function.
+
+ - Subclasses do not provide a dispose function, instead the base class' dispose function should be used, which will delegate to
+ a dispose function pointer.
+
+ - Classes not designed for inheritance cannot be extended because they may use an internal subclass to hide private data and don't
+ expose function pointers.
+
+ - The public API hides implementation details, such as init/deinit functions. An internal API is exposed by extension.h to allow
+ classes to be extended. Internal functions begin with underscore (_).
+
+ - OOP in C tends to lose type safety. Macros for casting are provided in extension.h to give context for why a cast is being done.
+
+ - If SPINE_SHORT_NAMES is defined, the "sp" prefix for all class names is optional.
+ */
+
+#ifndef SPINE_EXTENSION_H_
+#define SPINE_EXTENSION_H_
+
+/* All allocation uses these. */
+#define MALLOC(TYPE,COUNT) ((TYPE*)_malloc(sizeof(TYPE) * COUNT))
+#define CALLOC(TYPE,COUNT) ((TYPE*)_calloc(COUNT, sizeof(TYPE)))
+#define NEW(TYPE) CALLOC(TYPE,1)
+
+/* Gets the direct super class. Type safe. */
+#define SUPER(VALUE) (&VALUE->super)
+
+/* Cast to a super class. Not type safe, use with care. Prefer SUPER() where possible. */
+#define SUPER_CAST(TYPE,VALUE) ((TYPE*)VALUE)
+
+/* Cast to a sub class. Not type safe, use with care. */
+#define SUB_CAST(TYPE,VALUE) ((TYPE*)VALUE)
+
+/* Casts away const. Can be used as an lvalue. Not type safe, use with care. */
+#define CONST_CAST(TYPE,VALUE) (*(TYPE*)&VALUE)
+
+/* Gets the vtable for the specified type. Not type safe, use with care. */
+#define VTABLE(TYPE,VALUE) ((_##TYPE##Vtable*)((TYPE*)VALUE)->vtable)
+
+/* Frees memory. Can be used on const types. */
+#define FREE(VALUE) _free((void*)VALUE)
+
+/* Allocates a new char[], assigns it to TO, and copies FROM to it. Can be used on const types. */
+#define MALLOC_STR(TO,FROM) strcpy(CONST_CAST(char*, TO) = (char*)malloc(strlen(FROM) + 1), FROM)
+
+#ifdef __STDC_VERSION__
+#define FMOD(A,B) fmodf(A, B)
+#else
+#define FMOD(A,B) (float)fmod(A, B)
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <spine/Skeleton.h>
+#include <spine/RegionAttachment.h>
+#include <spine/BoundingBoxAttachment.h>
+#include <spine/Animation.h>
+#include <spine/Atlas.h>
+#include <spine/AttachmentLoader.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Functions that must be implemented:
+ */
+
+void _spAtlasPage_createTexture (spAtlasPage* self, const char* path);
+void _spAtlasPage_disposeTexture (spAtlasPage* self);
+char* _spUtil_readFile (const char* path, int* length);
+
+#ifdef SPINE_SHORT_NAMES
+#define _AtlasPage_createTexture(...) _spAtlasPage_createTexture(__VA_ARGS__)
+#define _AtlasPage_disposeTexture(...) _spAtlasPage_disposeTexture(__VA_ARGS__)
+#define _Util_readFile(...) _spUtil_readFile(__VA_ARGS__)
+#endif
+
+/*
+ * Internal API available for extension:
+ */
+
+void* _malloc (size_t size);
+void* _calloc (size_t num, size_t size);
+void _free (void* ptr);
+
+void _setMalloc (void* (*_malloc) (size_t size));
+void _setFree (void (*_free) (void* ptr));
+
+char* _readFile (const char* path, int* length);
+
+/**/
+
+void _spAttachmentLoader_init (spAttachmentLoader* self, /**/
+void (*dispose) (spAttachmentLoader* self), /**/
+spAttachment* (*newAttachment) (spAttachmentLoader* self, spSkin* skin, spAttachmentType type, const char* name));
+void _spAttachmentLoader_deinit (spAttachmentLoader* self);
+void _spAttachmentLoader_setError (spAttachmentLoader* self, const char* error1, const char* error2);
+void _spAttachmentLoader_setUnknownTypeError (spAttachmentLoader* self, spAttachmentType type);
+
+#ifdef SPINE_SHORT_NAMES
+#define _AttachmentLoader_init(...) _spAttachmentLoader_init(__VA_ARGS__)
+#define _AttachmentLoader_deinit(...) _spAttachmentLoader_deinit(__VA_ARGS__)
+#define _AttachmentLoader_setError(...) _spAttachmentLoader_setError(__VA_ARGS__)
+#define _AttachmentLoader_setUnknownTypeError(...) _spAttachmentLoader_setUnknownTypeError(__VA_ARGS__)
+#endif
+
+/**/
+
+void _spAttachment_init (spAttachment* self, const char* name, spAttachmentType type, /**/
+void (*dispose) (spAttachment* self));
+void _spAttachment_deinit (spAttachment* self);
+
+#ifdef SPINE_SHORT_NAMES
+#define _Attachment_init(...) _spAttachment_init(__VA_ARGS__)
+#define _Attachment_deinit(...) _spAttachment_deinit(__VA_ARGS__)
+#endif
+
+/**/
+
+void _spTimeline_init (spTimeline* self, spTimelineType type, /**/
+void (*dispose) (spTimeline* self), /**/
+		void (*apply) (const spTimeline* self, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents,
+				int* eventCount, float alpha));
+void _spTimeline_deinit (spTimeline* self);
+
+#ifdef SPINE_SHORT_NAMES
+#define _Timeline_init(...) _spTimeline_init(__VA_ARGS__)
+#define _Timeline_deinit(...) _spTimeline_deinit(__VA_ARGS__)
+#endif
+
+/**/
+
+void _spCurveTimeline_init (spCurveTimeline* self, spTimelineType type, int frameCount, /**/
+void (*dispose) (spTimeline* self), /**/
+		void (*apply) (const spTimeline* self, spSkeleton* skeleton, float lastTime, float time, spEvent** firedEvents,
+				int* eventCount, float alpha));
+void _spCurveTimeline_deinit (spCurveTimeline* self);
+
+#ifdef SPINE_SHORT_NAMES
+#define _CurveTimeline_init(...) _spCurveTimeline_init(__VA_ARGS__)
+#define _CurveTimeline_deinit(...) _spCurveTimeline_deinit(__VA_ARGS__)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_EXTENSION_H_ */

+ 53 - 0
engine/source/spine/spine.h

@@ -0,0 +1,53 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to install, execute and perform the Spine Runtimes
+ * Software (the "Software") solely for internal use. Without the written
+ * permission of Esoteric Software, you may not (a) modify, translate, adapt or
+ * otherwise create derivative works, improvements of the Software or develop
+ * new applications using the Software or (b) remove, delete, alter or obscure
+ * any trademarks or any copyright, trademark, patent or other intellectual
+ * property or proprietary rights notices on or in the Software, including
+ * any copy thereof. Redistributions in binary or source form must include
+ * this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * 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
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef SPINE_SPINE_H_
+#define SPINE_SPINE_H_
+
+#include <spine/Animation.h>
+#include <spine/AnimationState.h>
+#include <spine/AnimationStateData.h>
+#include <spine/Atlas.h>
+#include <spine/AtlasAttachmentLoader.h>
+#include <spine/Attachment.h>
+#include <spine/AttachmentLoader.h>
+#include <spine/Bone.h>
+#include <spine/BoneData.h>
+#include <spine/RegionAttachment.h>
+#include <spine/BoundingBoxAttachment.h>
+#include <spine/Skeleton.h>
+#include <spine/SkeletonBounds.h>
+#include <spine/SkeletonData.h>
+#include <spine/SkeletonJson.h>
+#include <spine/Skin.h>
+#include <spine/Slot.h>
+#include <spine/SlotData.h>
+#include <spine/Event.h>
+#include <spine/EventData.h>
+
+#endif /* SPINE_SPINE_H_ */

+ 4 - 0
modules/SpineToy/1/assets/goblins/goblins.asset.taml

@@ -0,0 +1,4 @@
+<SkeletonAsset
+    AssetName="goblins"
+    AtlasFile="goblins.atlas"
+    SkeletonFile="goblins.json"/>

+ 284 - 0
modules/SpineToy/1/assets/goblins/goblins.atlas

@@ -0,0 +1,284 @@
+goblins.png
+format: RGBA8888
+filter: Linear,Linear
+repeat: none
+gg-left-upper-leg
+  rotate: false
+  xy: 26, 300
+  size: 33, 70
+  orig: 33, 70
+  offset: 0, 0
+  index: -1
+undies
+  rotate: false
+  xy: 59, 477
+  size: 36, 29
+  orig: 36, 29
+  offset: 0, 0
+  index: -1
+gg-left-foot
+  rotate: false
+  xy: 164, 207
+  size: 65, 31
+  orig: 65, 31
+  offset: 0, 0
+  index: -1
+gg-torso
+  rotate: false
+  xy: 59, 153
+  size: 68, 96
+  orig: 68, 96
+  offset: 0, 0
+  index: -1
+gg-head
+  rotate: false
+  xy: 59, 2
+  size: 103, 81
+  orig: 103, 81
+  offset: 0, 0
+  index: -1
+gg-dagger
+  rotate: false
+  xy: 2, 372
+  size: 26, 108
+  orig: 26, 108
+  offset: 0, 0
+  index: -1
+gg-right-upper-leg
+  rotate: false
+  xy: 135, 443
+  size: 34, 63
+  orig: 34, 63
+  offset: 0, 0
+  index: -1
+torso
+  rotate: false
+  xy: 164, 76
+  size: 68, 96
+  orig: 68, 96
+  offset: 0, 0
+  index: -1
+left-shoulder
+  rotate: false
+  xy: 26, 2
+  size: 29, 44
+  orig: 29, 44
+  offset: 0, 0
+  index: -1
+gg-right-hand
+  rotate: false
+  xy: 215, 394
+  size: 36, 37
+  orig: 36, 37
+  offset: 0, 0
+  index: -1
+right-upper-leg
+  rotate: false
+  xy: 103, 378
+  size: 34, 63
+  orig: 34, 63
+  offset: 0, 0
+  index: -1
+gg-dagger-tip
+  rotate: false
+  xy: 236, 2
+  size: 17, 17
+  orig: 17, 17
+  offset: 0, 0
+  index: -1
+gg-right-lower-leg
+  rotate: false
+  xy: 30, 378
+  size: 36, 76
+  orig: 36, 76
+  offset: 0, 0
+  index: -1
+gg-right-arm
+  rotate: false
+  xy: 26, 48
+  size: 28, 50
+  orig: 28, 50
+  offset: 0, 0
+  index: -1
+gg-pelvis
+  rotate: false
+  xy: 61, 333
+  size: 62, 43
+  orig: 62, 43
+  offset: 0, 0
+  index: -1
+pelvis
+  rotate: false
+  xy: 164, 275
+  size: 62, 43
+  orig: 62, 43
+  offset: 0, 0
+  index: -1
+left-hand
+  rotate: false
+  xy: 139, 378
+  size: 36, 41
+  orig: 36, 41
+  offset: 0, 0
+  index: -1
+right-lower-leg
+  rotate: false
+  xy: 126, 300
+  size: 36, 76
+  orig: 36, 76
+  offset: 0, 0
+  index: -1
+gg-left-shoulder
+  rotate: false
+  xy: 26, 100
+  size: 28, 46
+  orig: 28, 46
+  offset: 0, 0
+  index: -1
+gg-neck
+  rotate: false
+  xy: 164, 320
+  size: 35, 41
+  orig: 35, 41
+  offset: 0, 0
+  index: -1
+undie-straps
+  rotate: false
+  xy: 2, 482
+  size: 55, 19
+  orig: 55, 19
+  offset: 0, 0
+  index: -1
+right-shoulder
+  rotate: false
+  xy: 26, 251
+  size: 39, 45
+  orig: 39, 45
+  offset: 0, 0
+  index: -1
+right-arm
+  rotate: false
+  xy: 30, 148
+  size: 23, 50
+  orig: 23, 50
+  offset: 0, 0
+  index: -1
+gg-left-lower-leg
+  rotate: false
+  xy: 68, 378
+  size: 33, 70
+  orig: 33, 70
+  offset: 0, 0
+  index: -1
+gg-undies
+  rotate: false
+  xy: 171, 475
+  size: 36, 29
+  orig: 36, 29
+  offset: 0, 0
+  index: -1
+gg-undie-straps
+  rotate: false
+  xy: 30, 456
+  size: 55, 19
+  orig: 55, 19
+  offset: 0, 0
+  index: -1
+right-hand
+  rotate: false
+  xy: 177, 394
+  size: 36, 37
+  orig: 36, 37
+  offset: 0, 0
+  index: -1
+left-foot
+  rotate: false
+  xy: 164, 174
+  size: 65, 31
+  orig: 65, 31
+  offset: 0, 0
+  index: -1
+gg-left-hand
+  rotate: false
+  xy: 171, 433
+  size: 35, 40
+  orig: 35, 40
+  offset: 0, 0
+  index: -1
+gg-right-shoulder
+  rotate: false
+  xy: 67, 251
+  size: 39, 45
+  orig: 39, 45
+  offset: 0, 0
+  index: -1
+gg-left-arm
+  rotate: false
+  xy: 201, 357
+  size: 37, 35
+  orig: 37, 35
+  offset: 0, 0
+  index: -1
+neck
+  rotate: false
+  xy: 97, 450
+  size: 36, 41
+  orig: 36, 41
+  offset: 0, 0
+  index: -1
+right-foot
+  rotate: false
+  xy: 61, 298
+  size: 63, 33
+  orig: 63, 33
+  offset: 0, 0
+  index: -1
+left-upper-leg
+  rotate: false
+  xy: 129, 153
+  size: 33, 73
+  orig: 33, 73
+  offset: 0, 0
+  index: -1
+left-arm
+  rotate: false
+  xy: 201, 320
+  size: 37, 35
+  orig: 37, 35
+  offset: 0, 0
+  index: -1
+left-lower-leg
+  rotate: false
+  xy: 129, 228
+  size: 33, 70
+  orig: 33, 70
+  offset: 0, 0
+  index: -1
+head
+  rotate: false
+  xy: 59, 85
+  size: 103, 66
+  orig: 103, 66
+  offset: 0, 0
+  index: -1
+gg-right-foot
+  rotate: false
+  xy: 164, 240
+  size: 63, 33
+  orig: 63, 33
+  offset: 0, 0
+  index: -1
+spear
+  rotate: false
+  xy: 2, 2
+  size: 22, 368
+  orig: 22, 368
+  offset: 0, 0
+  index: -1
+shield
+  rotate: false
+  xy: 164, 2
+  size: 70, 72
+  orig: 70, 72
+  offset: 0, 0
+  index: -1

+ 465 - 0
modules/SpineToy/1/assets/goblins/goblins.json

@@ -0,0 +1,465 @@
+{
+"bones": [
+	{ "name": "root", "y": -107.99 },
+	{ "name": "hip", "parent": "root", "x": 0.64, "y": 114.41 },
+	{ "name": "left upper leg", "parent": "hip", "length": 50.39, "x": 14.45, "y": 2.81, "rotation": -89.09 },
+	{ "name": "left lower leg", "parent": "left upper leg", "length": 49.89, "x": 56.34, "y": 0.98, "rotation": -16.65 },
+	{ "name": "left foot", "parent": "left lower leg", "length": 46.5, "x": 58.94, "y": -7.61, "rotation": 102.43 },
+	{ "name": "right upper leg", "parent": "hip", "length": 42.45, "x": -20.07, "y": -6.83, "rotation": -97.49 },
+	{ "name": "right lower leg", "parent": "right upper leg", "length": 58.52, "x": 42.99, "y": -0.61, "rotation": -14.34 },
+	{ "name": "right foot", "parent": "right lower leg", "length": 45.45, "x": 64.88, "y": 0.04, "rotation": 110.3 },
+	{ "name": "torso", "parent": "hip", "length": 85.82, "x": -6.42, "y": 1.97, "rotation": 93.92 },
+	{ "name": "neck", "parent": "torso", "length": 18.38, "x": 81.67, "y": -6.34, "rotation": -1.51 },
+	{ "name": "head", "parent": "neck", "length": 68.28, "x": 20.93, "y": 11.59, "rotation": -13.92 },
+	{ "name": "right shoulder", "parent": "torso", "length": 37.24, "x": 76.02, "y": 18.14, "rotation": 133.88 },
+	{ "name": "right arm", "parent": "right shoulder", "length": 36.74, "x": 37.6, "y": 0.31, "rotation": 36.32 },
+	{ "name": "right hand", "parent": "right arm", "length": 15.32, "x": 36.9, "y": 0.34, "rotation": 2.35 },
+	{ "name": "left shoulder", "parent": "torso", "length": 35.43, "x": 74.04, "y": -20.38, "rotation": -156.96 },
+	{ "name": "left arm", "parent": "left shoulder", "length": 35.62, "x": 37.85, "y": -2.34, "rotation": 28.16 },
+	{ "name": "left hand", "parent": "left arm", "length": 11.52, "x": 35.62, "y": 0.07, "rotation": 2.7 },
+	{ "name": "pelvis", "parent": "hip", "x": 1.41, "y": -6.57 }
+],
+"slots": [
+	{ "name": "left shoulder", "bone": "left shoulder", "attachment": "left shoulder" },
+	{ "name": "left arm", "bone": "left arm", "attachment": "left arm" },
+	{ "name": "left hand item", "bone": "left hand", "attachment": "left hand item" },
+	{ "name": "left hand", "bone": "left hand", "attachment": "left hand" },
+	{ "name": "left foot", "bone": "left foot", "attachment": "left foot" },
+	{ "name": "left lower leg", "bone": "left lower leg", "attachment": "left lower leg" },
+	{ "name": "left upper leg", "bone": "left upper leg", "attachment": "left upper leg" },
+	{ "name": "neck", "bone": "neck", "attachment": "neck" },
+	{ "name": "torso", "bone": "torso", "attachment": "torso" },
+	{ "name": "pelvis", "bone": "pelvis", "attachment": "pelvis" },
+	{ "name": "right foot", "bone": "right foot", "attachment": "right foot" },
+	{ "name": "right lower leg", "bone": "right lower leg", "attachment": "right lower leg" },
+	{ "name": "undie straps", "bone": "pelvis", "attachment": "undie straps" },
+	{ "name": "undies", "bone": "pelvis", "attachment": "undies" },
+	{ "name": "right upper leg", "bone": "right upper leg", "attachment": "right upper leg" },
+	{ "name": "head", "bone": "head", "attachment": "head" },
+	{ "name": "eyes", "bone": "head" },
+	{ "name": "right shoulder", "bone": "right shoulder", "attachment": "right shoulder" },
+	{ "name": "right arm", "bone": "right arm", "attachment": "right arm" },
+	{ "name": "right hand item 2", "bone": "right hand", "attachment": "right hand item 2" },
+	{ "name": "right hand", "bone": "right hand", "attachment": "right hand" },
+	{ "name": "right hand item", "bone": "right hand", "attachment": "right hand item" }
+],
+"skins": {
+	"goblin": {
+		"neck": {
+			"neck": { "x": 10.1, "y": 0.42, "rotation": -93.69, "width": 36, "height": 41 }
+		},
+		"undies": {
+			"undies": { "x": 6.3, "y": 0.12, "rotation": 1.27, "width": 36, "height": 29 }
+		},
+		"right hand": {
+			"right hand": { "name": "right-hand", "x": 7.88, "y": 2.78, "rotation": 91.96, "width": 36, "height": 37 }
+		},
+		"right arm": {
+			"right arm": { "name": "right-arm", "x": 16.44, "y": -1.04, "rotation": 94.32, "width": 23, "height": 50 }
+		},
+		"head": {
+			"head": { "x": 25.73, "y": 2.33, "rotation": -92.29, "width": 103, "height": 66 }
+		},
+		"left shoulder": {
+			"left shoulder": { "name": "left-shoulder", "x": 15.56, "y": -2.26, "rotation": 62.01, "width": 29, "height": 44 }
+		},
+		"left arm": {
+			"left arm": { "name": "left-arm", "x": 16.7, "y": -1.69, "scaleX": 1.057, "scaleY": 1.057, "rotation": 33.84, "width": 37, "height": 35 }
+		},
+		"left hand": {
+			"left hand": { "name": "left-hand", "x": 3.47, "y": 3.41, "scaleX": 0.892, "scaleY": 0.892, "rotation": 31.14, "width": 36, "height": 41 }
+		},
+		"right lower leg": {
+			"right lower leg": { "name": "right-lower-leg", "x": 25.68, "y": -3.15, "rotation": 111.83, "width": 36, "height": 76 }
+		},
+		"right upper leg": {
+			"right upper leg": { "name": "right-upper-leg", "x": 20.35, "y": 1.47, "rotation": 97.49, "width": 34, "height": 63 }
+		},
+		"pelvis": {
+			"pelvis": { "x": -5.61, "y": 0.76, "width": 62, "height": 43 }
+		},
+		"left lower leg": {
+			"left lower leg": { "name": "left-lower-leg", "x": 23.58, "y": -2.06, "rotation": 105.75, "width": 33, "height": 70 }
+		},
+		"left upper leg": {
+			"left upper leg": { "name": "left-upper-leg", "x": 29.68, "y": -3.87, "rotation": 89.09, "width": 33, "height": 73 }
+		},
+		"torso": {
+			"torso": { "x": 38.09, "y": -3.87, "rotation": -94.95, "width": 68, "height": 96 }
+		},
+		"right shoulder": {
+			"right shoulder": { "name": "right-shoulder", "x": 15.68, "y": -1.03, "rotation": 130.65, "width": 39, "height": 45 }
+		},
+		"right foot": {
+			"right foot": { "name": "right-foot", "x": 23.56, "y": 9.8, "rotation": 1.52, "width": 63, "height": 33 }
+		},
+		"left foot": {
+			"left foot": { "name": "left-foot", "x": 24.85, "y": 8.74, "rotation": 3.32, "width": 65, "height": 31 }
+		},
+		"right hand item": {
+			"right hand item": { "name": "shield", "x": -0.47, "y": 1.1, "rotation": 91.16, "width": 70, "height": 72 }
+		},
+		"left hand item": {
+			"left hand item": { "name": "spear", "x": -4.55, "y": 39.2, "rotation": 13.04, "width": 22, "height": 368 }
+		},
+		"undie straps": {
+			"undie straps": { "name": "undie-straps", "x": -3.87, "y": 13.1, "scaleX": 1.089, "width": 55, "height": 19 }
+		}
+	},
+	"goblingirl": {
+		"left upper leg": {
+			"left upper leg": { "name": "gg-left-upper-leg", "x": 30.21, "y": -2.95, "rotation": 89.09, "width": 33, "height": 70 }
+		},
+		"left lower leg": {
+			"left lower leg": { "name": "gg-left-lower-leg", "x": 25.02, "y": -0.6, "rotation": 105.75, "width": 33, "height": 70 }
+		},
+		"left foot": {
+			"left foot": { "name": "gg-left-foot", "x": 25.17, "y": 7.92, "rotation": 3.32, "width": 65, "height": 31 }
+		},
+		"right upper leg": {
+			"right upper leg": { "name": "gg-right-upper-leg", "x": 19.69, "y": 2.13, "rotation": 97.49, "width": 34, "height": 63 }
+		},
+		"right lower leg": {
+			"right lower leg": { "name": "gg-right-lower-leg", "x": 26.15, "y": -3.27, "rotation": 111.83, "width": 36, "height": 76 }
+		},
+		"right foot": {
+			"right foot": { "name": "gg-right-foot", "x": 23.46, "y": 9.66, "rotation": 1.52, "width": 63, "height": 33 }
+		},
+		"torso": {
+			"torso": { "name": "gg-torso", "x": 36.28, "y": -5.14, "rotation": -95.74, "width": 68, "height": 96 }
+		},
+		"left shoulder": {
+			"left shoulder": { "name": "gg-left-shoulder", "x": 19.8, "y": -0.42, "rotation": 61.21, "width": 28, "height": 46 }
+		},
+		"left arm": {
+			"left arm": { "name": "gg-left-arm", "x": 19.64, "y": -2.42, "rotation": 33.05, "width": 37, "height": 35 }
+		},
+		"left hand": {
+			"left hand": { "name": "gg-left-hand", "x": 4.34, "y": 2.39, "scaleX": 0.896, "scaleY": 0.896, "rotation": 30.34, "width": 35, "height": 40 }
+		},
+		"neck": {
+			"neck": { "name": "gg-neck", "x": 6.16, "y": -3.14, "rotation": -98.86, "width": 35, "height": 41 }
+		},
+		"head": {
+			"head": { "name": "gg-head", "x": 27.71, "y": -4.32, "rotation": -85.58, "width": 103, "height": 81 }
+		},
+		"right shoulder": {
+			"right shoulder": { "name": "gg-right-shoulder", "x": 14.46, "y": 0.45, "rotation": 129.85, "width": 39, "height": 45 }
+		},
+		"right arm": {
+			"right arm": { "name": "gg-right-arm", "x": 16.85, "y": -0.66, "rotation": 93.52, "width": 28, "height": 50 }
+		},
+		"right hand": {
+			"right hand": { "name": "gg-right-hand", "x": 7.21, "y": 3.43, "rotation": 91.16, "width": 36, "height": 37 }
+		},
+		"pelvis": {
+			"pelvis": { "name": "gg-pelvis", "x": -3.87, "y": 3.18, "width": 62, "height": 43 }
+		},
+		"undie straps": {
+			"undie straps": { "name": "gg-undie-straps", "x": -1.51, "y": 14.18, "width": 55, "height": 19 }
+		},
+		"undies": {
+			"undies": { "name": "gg-undies", "x": 5.4, "y": 1.7, "width": 36, "height": 29 }
+		},
+		"left hand item": {
+			"left hand item": { "name": "gg-dagger", "x": 7.88, "y": -23.45, "rotation": 10.47, "width": 26, "height": 108 }
+		},
+		"right hand item 2": {
+			"right hand item 2": { "name": "gg-dagger", "x": 7.17, "y": -22.38, "rotation": -5.27, "width": 26, "height": 108 }
+		},
+		"right hand item": {
+			"right hand item": { "name": "gg-dagger-tip", "x": 13.45, "y": 22.07, "rotation": -3.23, "width": 17, "height": 17 }
+		}
+	}
+},
+"animations": {
+	"walk": {
+		"bones": {
+			"left upper leg": {
+				"rotate": [
+					{ "time": 0, "angle": -26.55 },
+					{ "time": 0.1333, "angle": -8.78 },
+					{ "time": 0.2666, "angle": 9.51 },
+					{ "time": 0.4, "angle": 30.74 },
+					{ "time": 0.5333, "angle": 25.33 },
+					{ "time": 0.6666, "angle": 26.11 },
+					{ "time": 0.8, "angle": -7.7 },
+					{ "time": 0.9333, "angle": -21.19 },
+					{ "time": 1.0666, "angle": -26.55 }
+				],
+				"translate": [
+					{ "time": 0, "x": -1.32, "y": 1.7 },
+					{ "time": 0.4, "x": -0.06, "y": 2.42 },
+					{ "time": 1.0666, "x": -1.32, "y": 1.7 }
+				]
+			},
+			"right upper leg": {
+				"rotate": [
+					{ "time": 0, "angle": 42.45 },
+					{ "time": 0.1333, "angle": 52.1 },
+					{ "time": 0.2666, "angle": 8.53 },
+					{ "time": 0.5333, "angle": -16.93 },
+					{ "time": 0.6666, "angle": 1.89 },
+					{
+						"time": 0.8,
+						"angle": 28.06,
+						"curve": [ 0.462, 0.11, 1, 1 ]
+					},
+					{
+						"time": 0.9333,
+						"angle": 58.68,
+						"curve": [ 0.5, 0.02, 1, 1 ]
+					},
+					{ "time": 1.0666, "angle": 42.45 }
+				],
+				"translate": [
+					{ "time": 0, "x": 6.23, "y": 0 },
+					{ "time": 0.2666, "x": 2.14, "y": 2.4 },
+					{ "time": 0.5333, "x": 2.44, "y": 4.8 },
+					{ "time": 1.0666, "x": 6.23, "y": 0 }
+				]
+			},
+			"left lower leg": {
+				"rotate": [
+					{ "time": 0, "angle": -22.98 },
+					{ "time": 0.1333, "angle": -63.5 },
+					{ "time": 0.2666, "angle": -73.76 },
+					{ "time": 0.5333, "angle": 5.11 },
+					{ "time": 0.6666, "angle": -28.29 },
+					{ "time": 0.8, "angle": 4.08 },
+					{ "time": 0.9333, "angle": 3.53 },
+					{ "time": 1.0666, "angle": -22.98 }
+				],
+				"translate": [
+					{ "time": 0, "x": 0, "y": 0 },
+					{ "time": 0.2666, "x": 2.55, "y": -0.47 },
+					{ "time": 0.5333, "x": 0, "y": 0, "curve": "stepped" },
+					{ "time": 1.0666, "x": 0, "y": 0 }
+				]
+			},
+			"left foot": {
+				"rotate": [
+					{ "time": 0, "angle": -3.69 },
+					{ "time": 0.1333, "angle": -10.42 },
+					{ "time": 0.2666, "angle": -5.01 },
+					{ "time": 0.4, "angle": 3.87 },
+					{ "time": 0.5333, "angle": -3.87 },
+					{ "time": 0.6666, "angle": 2.78 },
+					{ "time": 0.8, "angle": 1.68 },
+					{ "time": 0.9333, "angle": -8.54 },
+					{ "time": 1.0666, "angle": -3.69 }
+				]
+			},
+			"right shoulder": {
+				"rotate": [
+					{
+						"time": 0,
+						"angle": 5.29,
+						"curve": [ 0.264, 0, 0.75, 1 ]
+					},
+					{ "time": 0.6666, "angle": 6.65 },
+					{ "time": 1.0666, "angle": 5.29 }
+				]
+			},
+			"right arm": {
+				"rotate": [
+					{
+						"time": 0,
+						"angle": -4.02,
+						"curve": [ 0.267, 0, 0.804, 0.99 ]
+					},
+					{
+						"time": 0.6666,
+						"angle": 19.78,
+						"curve": [ 0.307, 0, 0.787, 0.99 ]
+					},
+					{ "time": 1.0666, "angle": -4.02 }
+				]
+			},
+			"right hand": {
+				"rotate": [
+					{ "time": 0, "angle": 8.98 },
+					{ "time": 0.6666, "angle": 0.51 },
+					{ "time": 1.0666, "angle": 8.98 }
+				]
+			},
+			"left shoulder": {
+				"rotate": [
+					{
+						"time": 0,
+						"angle": 6.25,
+						"curve": [ 0.339, 0, 0.683, 1 ]
+					},
+					{
+						"time": 0.5333,
+						"angle": -11.78,
+						"curve": [ 0.281, 0, 0.686, 0.99 ]
+					},
+					{ "time": 1.0666, "angle": 6.25 }
+				],
+				"translate": [
+					{ "time": 0, "x": 1.15, "y": 0.23 }
+				]
+			},
+			"left hand": {
+				"rotate": [
+					{
+						"time": 0,
+						"angle": -21.23,
+						"curve": [ 0.295, 0, 0.755, 0.98 ]
+					},
+					{
+						"time": 0.5333,
+						"angle": -27.28,
+						"curve": [ 0.241, 0, 0.75, 0.97 ]
+					},
+					{ "time": 1.0666, "angle": -21.23 }
+				]
+			},
+			"left arm": {
+				"rotate": [
+					{
+						"time": 0,
+						"angle": 28.37,
+						"curve": [ 0.339, 0, 0.683, 1 ]
+					},
+					{
+						"time": 0.5333,
+						"angle": 60.09,
+						"curve": [ 0.281, 0, 0.686, 0.99 ]
+					},
+					{ "time": 1.0666, "angle": 28.37 }
+				]
+			},
+			"torso": {
+				"rotate": [
+					{ "time": 0, "angle": -10.28 },
+					{
+						"time": 0.1333,
+						"angle": -15.38,
+						"curve": [ 0.545, 0, 0.818, 1 ]
+					},
+					{
+						"time": 0.4,
+						"angle": -9.78,
+						"curve": [ 0.58, 0.17, 0.669, 0.99 ]
+					},
+					{
+						"time": 0.6666,
+						"angle": -15.75,
+						"curve": [ 0.235, 0.01, 0.795, 1 ]
+					},
+					{
+						"time": 0.9333,
+						"angle": -7.06,
+						"curve": [ 0.209, 0, 0.816, 0.98 ]
+					},
+					{ "time": 1.0666, "angle": -10.28 }
+				],
+				"translate": [
+					{ "time": 0, "x": -1.29, "y": 1.68 }
+				]
+			},
+			"right foot": {
+				"rotate": [
+					{ "time": 0, "angle": -5.25 },
+					{ "time": 0.2666, "angle": -1.91 },
+					{ "time": 0.4, "angle": -6.45 },
+					{ "time": 0.5333, "angle": -5.39 },
+					{ "time": 0.8, "angle": -11.68 },
+					{ "time": 0.9333, "angle": 0.46 },
+					{ "time": 1.0666, "angle": -5.25 }
+				]
+			},
+			"right lower leg": {
+				"rotate": [
+					{
+						"time": 0,
+						"angle": -3.39,
+						"curve": [ 0.316, 0.01, 0.741, 0.98 ]
+					},
+					{
+						"time": 0.1333,
+						"angle": -45.53,
+						"curve": [ 0.229, 0, 0.738, 0.97 ]
+					},
+					{ "time": 0.2666, "angle": -4.83 },
+					{ "time": 0.5333, "angle": -19.53 },
+					{ "time": 0.6666, "angle": -64.8 },
+					{
+						"time": 0.8,
+						"angle": -82.56,
+						"curve": [ 0.557, 0.18, 1, 1 ]
+					},
+					{ "time": 1.0666, "angle": -3.39 }
+				],
+				"translate": [
+					{ "time": 0, "x": 0, "y": 0, "curve": "stepped" },
+					{ "time": 0.5333, "x": 0, "y": 0 },
+					{ "time": 0.6666, "x": 2.18, "y": 0.21 },
+					{ "time": 1.0666, "x": 0, "y": 0 }
+				]
+			},
+			"hip": {
+				"rotate": [
+					{ "time": 0, "angle": 0, "curve": "stepped" },
+					{ "time": 1.0666, "angle": 0 }
+				],
+				"translate": [
+					{ "time": 0, "x": 0, "y": -4.16 },
+					{
+						"time": 0.1333,
+						"x": 0,
+						"y": -7.05,
+						"curve": [ 0.359, 0.47, 0.646, 0.74 ]
+					},
+					{ "time": 0.4, "x": 0, "y": 6.78 },
+					{ "time": 0.5333, "x": 0, "y": -6.13 },
+					{
+						"time": 0.6666,
+						"x": 0,
+						"y": -7.05,
+						"curve": [ 0.359, 0.47, 0.646, 0.74 ]
+					},
+					{ "time": 0.9333, "x": 0, "y": 6.78 },
+					{ "time": 1.0666, "x": 0, "y": -4.16 }
+				]
+			},
+			"neck": {
+				"rotate": [
+					{ "time": 0, "angle": 3.6 },
+					{ "time": 0.1333, "angle": 17.49 },
+					{ "time": 0.2666, "angle": 6.1 },
+					{ "time": 0.4, "angle": 3.45 },
+					{ "time": 0.5333, "angle": 5.17 },
+					{ "time": 0.6666, "angle": 18.36 },
+					{ "time": 0.8, "angle": 6.09 },
+					{ "time": 0.9333, "angle": 2.28 },
+					{ "time": 1.0666, "angle": 3.6 }
+				]
+			},
+			"head": {
+				"rotate": [
+					{
+						"time": 0,
+						"angle": 3.6,
+						"curve": [ 0, 0, 0.704, 1.17 ]
+					},
+					{ "time": 0.1333, "angle": -0.2 },
+					{ "time": 0.2666, "angle": 6.1 },
+					{ "time": 0.4, "angle": 3.45 },
+					{
+						"time": 0.5333,
+						"angle": 5.17,
+						"curve": [ 0, 0, 0.704, 1.61 ]
+					},
+					{ "time": 0.7, "angle": 1.1 },
+					{ "time": 0.8, "angle": 6.09 },
+					{ "time": 0.9333, "angle": 2.28 },
+					{ "time": 1.0666, "angle": 3.6 }
+				]
+			}
+		}
+	}
+}
+}

BIN
modules/SpineToy/1/assets/goblins/goblins.png


+ 4 - 0
modules/SpineToy/1/assets/powerup/powerup.asset.taml

@@ -0,0 +1,4 @@
+<SkeletonAsset
+        AssetName="powerup"
+		AtlasFile="powerup.atlas"
+        SkeletonFile="powerup.json"/>

+ 25 - 0
modules/SpineToy/1/assets/powerup/powerup.atlas

@@ -0,0 +1,25 @@
+powerup.png
+format: RGBA8888
+filter: Linear,Linear
+repeat: none
+star
+  rotate: false
+  xy: 266, 2
+  size: 76, 72
+  orig: 76, 72
+  offset: 0, 0
+  index: -1
+wing
+  rotate: false
+  xy: 2, 2
+  size: 137, 150
+  orig: 137, 150
+  offset: 0, 0
+  index: -1
+token
+  rotate: false
+  xy: 141, 2
+  size: 123, 122
+  orig: 123, 122
+  offset: 0, 0
+  index: -1

+ 487 - 0
modules/SpineToy/1/assets/powerup/powerup.json

@@ -0,0 +1,487 @@
+{
+"bones": [
+	{ "name": "root" },
+	{ "name": "powerup", "parent": "root", "x": 1.48, "y": 134.02 },
+	{ "name": "stars", "parent": "root", "x": 1.22, "y": 191.25 },
+	{ "name": "tokenRoot", "parent": "powerup", "x": -0.47, "y": 56.13 },
+	{ "name": "right wing", "parent": "tokenRoot", "x": 86.82, "y": 7.12 },
+	{ "name": "left wing", "parent": "tokenRoot", "x": -91.06, "y": 7.8 },
+	{ "name": "token", "parent": "tokenRoot", "x": -1.18, "y": -1.81, "scaleX": 0.771, "scaleY": 0.771 },
+	{ "name": "star7", "parent": "stars", "x": -5.85, "y": -35.84 },
+	{ "name": "star5", "parent": "stars", "x": 11.5, "y": -12.28 },
+	{ "name": "star4", "parent": "stars", "x": 10.08, "y": 14.52 },
+	{ "name": "star3", "parent": "stars", "x": -0.84, "y": -3.78 },
+	{ "name": "star2", "parent": "stars", "x": -1.36, "y": -4.16 },
+	{ "name": "star", "parent": "stars", "x": 2.84, "y": 4.76 },
+	{ "name": "star1", "parent": "stars", "x": -2.19, "y": -2.04 },
+	{ "name": "star6", "parent": "stars", "x": -20.73, "y": -23.44 },
+	{ "name": "star8", "parent": "stars", "x": 8.41, "y": -10.05 },
+	{ "name": "star9", "parent": "stars", "x": -1.62, "y": -7.34 }
+],
+"slots": [
+	{ "name": "right wing", "bone": "right wing", "attachment": "wing" },
+	{ "name": "left wing", "bone": "left wing", "attachment": "wing" },
+	{ "name": "star", "bone": "star", "color": "ffffff00", "attachment": "star" },
+	{ "name": "star1", "bone": "star1", "color": "ffa5a500", "attachment": "star" },
+	{ "name": "star2", "bone": "star2", "color": "ffffff00", "attachment": "star" },
+	{ "name": "star3", "bone": "star3", "color": "ffffff00", "attachment": "star" },
+	{ "name": "star4", "bone": "star4", "color": "ffffff00", "attachment": "star" },
+	{ "name": "star5", "bone": "star5", "color": "ffffff00", "attachment": "star" },
+	{ "name": "star6", "bone": "star6", "color": "ffffff00", "attachment": "star" },
+	{ "name": "star7", "bone": "star7", "color": "ffffff00", "attachment": "star" },
+	{ "name": "star8", "bone": "star8", "color": "ffffff00", "attachment": "star" },
+	{ "name": "star9", "bone": "star9", "color": "ffffff00", "attachment": "star" },
+	{ "name": "token", "bone": "token", "attachment": "token" }
+],
+"skins": {
+	"default": {
+		"right wing": {
+			"wing": { "x": 49.55, "y": 42.32, "width": 137, "height": 150 }
+		},
+		"left wing": {
+			"wing": { "x": -48.74, "y": 42.16, "scaleX": -1, "width": 137, "height": 150 }
+		},
+		"star": {
+			"star": { "width": 76, "height": 72 }
+		},
+		"star1": {
+			"star": { "width": 76, "height": 72 }
+		},
+		"star2": {
+			"star": { "width": 76, "height": 72 }
+		},
+		"star3": {
+			"star": { "width": 76, "height": 72 }
+		},
+		"star4": {
+			"star": { "width": 76, "height": 72 }
+		},
+		"star5": {
+			"star": { "width": 76, "height": 72 }
+		},
+		"star6": {
+			"star": { "width": 76, "height": 72 }
+		},
+		"star7": {
+			"star": { "width": 76, "height": 72 }
+		},
+		"star8": {
+			"star": { "width": 76, "height": 72 }
+		},
+		"star9": {
+			"star": { "width": 76, "height": 72 }
+		},
+		"token": {
+			"token": { "x": 1.18, "y": 1.81, "width": 123, "height": 122 }
+		}
+	}
+},
+"animations": {
+	"Animation": {
+		"bones": {
+			"powerup": {
+				"translate": [
+					{
+						"time": 0,
+						"x": -18.55,
+						"y": 0,
+						"curve": [ 0.385, 0, 0.689, 0.95 ]
+					},
+					{
+						"time": 0.3333,
+						"x": 0,
+						"y": -34.69,
+						"curve": [ 0.235, 0.66, 1, 1 ]
+					},
+					{ "time": 0.6666, "x": 22.04, "y": 0 },
+					{
+						"time": 1,
+						"x": 0,
+						"y": -34.69,
+						"curve": [ 0.235, 0.66, 1, 1 ]
+					},
+					{ "time": 1.3333, "x": -18.55, "y": 0 }
+				]
+			},
+			"left wing": {
+				"rotate": [
+					{
+						"time": 0,
+						"angle": 45.23,
+						"curve": [ 0.393, 0, 0.629, 1 ]
+					},
+					{
+						"time": 0.1666,
+						"angle": -38.74,
+						"curve": [ 0.163, 0.91, 1, 1 ]
+					},
+					{
+						"time": 0.3333,
+						"angle": 45.23,
+						"curve": [ 0.393, 0, 0.629, 1 ]
+					},
+					{
+						"time": 0.5,
+						"angle": -38.74,
+						"curve": [ 0.163, 0.91, 1, 1 ]
+					},
+					{
+						"time": 0.6666,
+						"angle": 45.23,
+						"curve": [ 0.393, 0, 0.629, 1 ]
+					},
+					{
+						"time": 0.8333,
+						"angle": -38.74,
+						"curve": [ 0.163, 0.91, 1, 1 ]
+					},
+					{
+						"time": 1,
+						"angle": 45.23,
+						"curve": [ 0.393, 0, 0.629, 1 ]
+					},
+					{
+						"time": 1.1666,
+						"angle": -38.74,
+						"curve": [ 0.163, 0.91, 1, 1 ]
+					},
+					{ "time": 1.3333, "angle": 45.23 }
+				],
+				"scale": [
+					{ "time": 0, "x": 1.098, "y": 1 },
+					{ "time": 0.1666, "x": 1, "y": 0.826 },
+					{ "time": 0.3333, "x": 1.098, "y": 1 },
+					{ "time": 0.5, "x": 1, "y": 0.826 },
+					{ "time": 0.6666, "x": 1.098, "y": 1 },
+					{ "time": 0.8333, "x": 1, "y": 0.826 },
+					{ "time": 1, "x": 1.098, "y": 1 },
+					{ "time": 1.1666, "x": 1, "y": 0.826 },
+					{ "time": 1.3333, "x": 1.098, "y": 1 }
+				]
+			},
+			"right wing": {
+				"rotate": [
+					{
+						"time": 0,
+						"angle": -39.45,
+						"curve": [ 0.327, 0, 0.637, 1 ]
+					},
+					{
+						"time": 0.1666,
+						"angle": 37.67,
+						"curve": [ 0.163, 0.91, 1, 1 ]
+					},
+					{
+						"time": 0.3333,
+						"angle": -39.45,
+						"curve": [ 0.327, 0, 0.637, 1 ]
+					},
+					{
+						"time": 0.5,
+						"angle": 37.67,
+						"curve": [ 0.163, 0.91, 1, 1 ]
+					},
+					{
+						"time": 0.6666,
+						"angle": -39.45,
+						"curve": [ 0.327, 0, 0.637, 1 ]
+					},
+					{
+						"time": 0.8333,
+						"angle": 37.67,
+						"curve": [ 0.163, 0.91, 1, 1 ]
+					},
+					{
+						"time": 1,
+						"angle": -39.45,
+						"curve": [ 0.327, 0, 0.637, 1 ]
+					},
+					{
+						"time": 1.1666,
+						"angle": 37.67,
+						"curve": [ 0.163, 0.91, 1, 1 ]
+					},
+					{ "time": 1.3333, "angle": -39.45 }
+				],
+				"scale": [
+					{ "time": 0, "x": 1.09, "y": 1 },
+					{ "time": 0.1666, "x": 1, "y": 0.819 },
+					{ "time": 0.3333, "x": 1.09, "y": 1 },
+					{ "time": 0.5, "x": 1, "y": 0.819 },
+					{ "time": 0.6666, "x": 1.09, "y": 1 },
+					{ "time": 0.8333, "x": 1, "y": 0.819 },
+					{ "time": 1, "x": 1.09, "y": 1 },
+					{ "time": 1.1666, "x": 1, "y": 0.819 },
+					{ "time": 1.3333, "x": 1.09, "y": 1 }
+				]
+			},
+			"tokenRoot": {
+				"rotate": [
+					{ "time": 0, "angle": 11.29 },
+					{ "time": 0.3333, "angle": 2.5 },
+					{ "time": 0.6666, "angle": -12.5 },
+					{ "time": 1, "angle": 0.63 },
+					{ "time": 1.3333, "angle": 11.29 }
+				]
+			},
+			"token": {
+				"rotate": [
+					{ "time": 0, "angle": -0.5 },
+					{ "time": 0.6666, "angle": 5.86 },
+					{ "time": 1.3333, "angle": -0.5 }
+				],
+				"scale": [
+					{ "time": 0, "x": 1.139, "y": 1.139 },
+					{ "time": 0.3333, "x": 1, "y": 1 },
+					{ "time": 0.6666, "x": 1.139, "y": 1.139 },
+					{ "time": 1, "x": 1, "y": 1 },
+					{ "time": 1.3333, "x": 1.139, "y": 1.139 }
+				]
+			},
+			"star": {
+				"rotate": [
+					{ "time": 0, "angle": 28.25 },
+					{ "time": 0.6666, "angle": -42.08 }
+				],
+				"translate": [
+					{ "time": 0, "x": 0, "y": 0 },
+					{ "time": 0.6666, "x": 86.68, "y": 68.32 }
+				],
+				"scale": [
+					{ "time": 0, "x": 0.439, "y": 0.439 },
+					{ "time": 0.6666, "x": 1, "y": 1 }
+				]
+			},
+			"star1": {
+				"rotate": [
+					{ "time": 0.2666, "angle": 13.37 },
+					{ "time": 0.9333, "angle": 89.61 }
+				],
+				"translate": [
+					{ "time": 0.2666, "x": -12.16, "y": -2.27 },
+					{ "time": 0.9333, "x": -76.4, "y": 67.76 }
+				],
+				"scale": [
+					{ "time": 0.2666, "x": 0.391, "y": 0.391 },
+					{ "time": 0.9333, "x": 0.815, "y": 0.815 }
+				]
+			},
+			"star2": {
+				"rotate": [
+					{ "time": 0.1333, "angle": -42.67 },
+					{ "time": 0.8, "angle": 88.41 }
+				],
+				"translate": [
+					{ "time": 0.1333, "x": -18.32, "y": -18.91 },
+					{ "time": 0.8, "x": -57.85, "y": -97.4 }
+				],
+				"scale": [
+					{ "time": 0.1333, "x": 0.211, "y": 0.211 },
+					{ "time": 0.8, "x": 0.652, "y": 0.652 }
+				]
+			},
+			"star3": {
+				"rotate": [
+					{ "time": 0.4333, "angle": 57.71 },
+					{ "time": 1.1, "angle": -105.15 }
+				],
+				"translate": [
+					{ "time": 0.4333, "x": 36.1, "y": -40.52 },
+					{ "time": 1.1, "x": 69, "y": -78.3 }
+				],
+				"scale": [
+					{ "time": 0.4333, "x": 0.24, "y": 0.24 },
+					{ "time": 1.1, "x": 0.776, "y": 0.776 }
+				]
+			},
+			"star4": {
+				"rotate": [
+					{ "time": 0.6, "angle": 37.65 },
+					{ "time": 1.2666, "angle": -102.15 }
+				],
+				"translate": [
+					{ "time": 0.6, "x": 8.65, "y": -12.98 },
+					{ "time": 1.2666, "x": -0.99, "y": 82.82 }
+				],
+				"scale": [
+					{ "time": 0.6, "x": 0.275, "y": 0.275 },
+					{ "time": 1.2666, "x": 1.081, "y": 1.081 }
+				]
+			},
+			"star5": {
+				"rotate": [
+					{ "time": 0, "angle": 18.27 },
+					{ "time": 0.3333, "angle": 83.73, "curve": "stepped" },
+					{ "time": 1, "angle": -47.17 },
+					{ "time": 1.3333, "angle": 18.27 }
+				],
+				"translate": [
+					{ "time": 0, "x": -67.74, "y": -7.78 },
+					{ "time": 0.3333, "x": -102.83, "y": -22.36, "curve": "stepped" },
+					{ "time": 1, "x": -32.63, "y": 6.8 },
+					{ "time": 1.3333, "x": -67.74, "y": -7.78 }
+				],
+				"scale": [
+					{ "time": 0, "x": 0.565, "y": 0.565 },
+					{ "time": 0.3333, "x": 0.791, "y": 0.791, "curve": "stepped" },
+					{ "time": 1, "x": 0.34, "y": 0.34 },
+					{ "time": 1.3333, "x": 0.565, "y": 0.565 }
+				]
+			},
+			"star6": {
+				"rotate": [
+					{ "time": 0, "angle": 25.23 },
+					{ "time": 0.5666, "angle": -73.06, "curve": "stepped" },
+					{ "time": 1.2333, "angle": 42.58 },
+					{ "time": 1.3333, "angle": 25.23 }
+				],
+				"translate": [
+					{ "time": 0, "x": 4.55, "y": -14.21 },
+					{ "time": 0.5666, "x": 30.36, "y": -94.75, "curve": "stepped" },
+					{ "time": 1.2333, "x": 0, "y": 0 },
+					{ "time": 1.3333, "x": 4.55, "y": -14.21 }
+				],
+				"scale": [
+					{ "time": 0, "x": 0.337, "y": 0.337 },
+					{ "time": 0.5666, "x": 0.955, "y": 0.955, "curve": "stepped" },
+					{ "time": 1.2333, "x": 0.227, "y": 0.227 },
+					{ "time": 1.3333, "x": 0.337, "y": 0.337 }
+				]
+			},
+			"star7": {
+				"rotate": [
+					{ "time": 0.6333, "angle": 41.11 },
+					{ "time": 1.3, "angle": -89.84 }
+				],
+				"translate": [
+					{ "time": 0.6333, "x": 20.27, "y": 32.75 },
+					{ "time": 1.3, "x": -67.79, "y": -68.56 }
+				],
+				"scale": [
+					{ "time": 0.6333, "x": 0.258, "y": 0.258 },
+					{ "time": 1.3, "x": 0.999, "y": 0.999 }
+				]
+			},
+			"star9": {
+				"rotate": [
+					{ "time": 0, "angle": 49.48 },
+					{ "time": 0.1333, "angle": 71.89, "curve": "stepped" },
+					{ "time": 0.8, "angle": -40.17 },
+					{ "time": 1.3333, "angle": 49.48 }
+				],
+				"translate": [
+					{ "time": 0, "x": -78.75, "y": 77.18 },
+					{ "time": 0.1333, "x": -98.44, "y": 96.47, "curve": "stepped" },
+					{ "time": 0.8, "x": 0, "y": 0 },
+					{ "time": 1.3333, "x": -78.75, "y": 77.18 }
+				],
+				"scale": [
+					{ "time": 0, "x": 0.772, "y": 0.772 },
+					{ "time": 0.1333, "x": 0.91, "y": 0.91, "curve": "stepped" },
+					{ "time": 0.8, "x": 0.221, "y": 0.221 },
+					{ "time": 1.3333, "x": 0.772, "y": 0.772 }
+				]
+			},
+			"star8": {
+				"rotate": [
+					{ "time": 0, "angle": -60.17 },
+					{ "time": 0.2666, "angle": -109.83, "curve": "stepped" },
+					{ "time": 0.9333, "angle": 14.31 },
+					{ "time": 1.3333, "angle": -60.17 }
+				],
+				"translate": [
+					{ "time": 0, "x": 44.63, "y": -41.66 },
+					{ "time": 0.2666, "x": 74.39, "y": -69.43, "curve": "stepped" },
+					{ "time": 0.9333, "x": 0, "y": 0 },
+					{ "time": 1.3333, "x": 44.63, "y": -41.66 }
+				],
+				"scale": [
+					{ "time": 0, "x": 0.452, "y": 0.452 },
+					{ "time": 0.2666, "x": 0.672, "y": 0.672, "curve": "stepped" },
+					{ "time": 0.9333, "x": 0.123, "y": 0.123 },
+					{ "time": 1.3333, "x": 0.452, "y": 0.452 }
+				]
+			}
+		},
+		"slots": {
+			"star7": {
+				"color": [
+					{ "time": 0.6333, "color": "ffffff00" },
+					{ "time": 1.1666, "color": "ffffffff" },
+					{ "time": 1.3, "color": "ffffff00" }
+				]
+			},
+			"star1": {
+				"color": [
+					{ "time": 0.2666, "color": "fff9f600" },
+					{ "time": 0.8, "color": "ffffffff" },
+					{ "time": 0.9333, "color": "fff0f200" }
+				]
+			},
+			"star4": {
+				"color": [
+					{ "time": 0.6, "color": "ffffff00" },
+					{ "time": 1.1333, "color": "ffffffff" },
+					{ "time": 1.2666, "color": "ffffff00" }
+				]
+			},
+			"star3": {
+				"color": [
+					{ "time": 0.4333, "color": "ffffff00" },
+					{ "time": 0.9666, "color": "ffffffff" },
+					{ "time": 1.1, "color": "ffffff00" }
+				]
+			},
+			"star8": {
+				"color": [
+					{ "time": 0, "color": "ffffffbf" },
+					{ "time": 0.1333, "color": "ffffffff" },
+					{ "time": 0.2666, "color": "ffffff00", "curve": "stepped" },
+					{ "time": 0.9333, "color": "ffffff00" },
+					{ "time": 1.3333, "color": "ffffffbf" }
+				]
+			},
+			"star": {
+				"color": [
+					{ "time": 0, "color": "ffffff00" },
+					{ "time": 0.5333, "color": "ffffffff" },
+					{ "time": 0.6666, "color": "ffffff00" }
+				]
+			},
+			"star9": {
+				"color": [
+					{ "time": 0, "color": "ffffffff" },
+					{ "time": 0.1333, "color": "ffffff00", "curve": "stepped" },
+					{ "time": 0.8, "color": "ffffff00" },
+					{ "time": 1.3333, "color": "ffffffff" }
+				]
+			},
+			"star2": {
+				"color": [
+					{ "time": 0.1333, "color": "ffffff00" },
+					{ "time": 0.6666, "color": "ffffffff" },
+					{ "time": 0.8, "color": "ffffff00" }
+				]
+			},
+			"star5": {
+				"color": [
+					{ "time": 0, "color": "ffffff9f" },
+					{ "time": 0.2, "color": "ffffffff" },
+					{ "time": 0.3333, "color": "ffffff00", "curve": "stepped" },
+					{ "time": 1, "color": "ffffff00" },
+					{ "time": 1.3333, "color": "ffffff9f" }
+				]
+			},
+			"star6": {
+				"color": [
+					{ "time": 0, "color": "ffffff2f" },
+					{ "time": 0.4333, "color": "ffffffff" },
+					{ "time": 0.5666, "color": "ffffff00", "curve": "stepped" },
+					{ "time": 1.2333, "color": "ffffff00" },
+					{ "time": 1.3333, "color": "ffffff2f" }
+				]
+			}
+		}
+	}
+}
+}

BIN
modules/SpineToy/1/assets/powerup/powerup.png


+ 166 - 0
modules/SpineToy/1/assets/spineboy/spineboy.atlas

@@ -0,0 +1,166 @@
+
+spineboy.png
+format: RGBA8888
+filter: Linear,Linear
+repeat: none
+head
+  rotate: false
+  xy: 1, 122
+  size: 121, 132
+  orig: 121, 132
+  offset: 0, 0
+  index: -1
+torso
+  rotate: false
+  xy: 1, 28
+  size: 68, 92
+  orig: 68, 92
+  offset: 0, 0
+  index: -1
+left-pant-bottom
+  rotate: false
+  xy: 1, 4
+  size: 44, 22
+  orig: 44, 22
+  offset: 0, 0
+  index: -1
+right-pant-bottom
+  rotate: false
+  xy: 47, 8
+  size: 46, 18
+  orig: 46, 18
+  offset: 0, 0
+  index: -1
+right-upper-leg
+  rotate: false
+  xy: 71, 50
+  size: 44, 70
+  orig: 44, 70
+  offset: 0, 0
+  index: -1
+pelvis
+  rotate: false
+  xy: 95, 1
+  size: 63, 47
+  orig: 63, 47
+  offset: 0, 0
+  index: -1
+left-upper-leg
+  rotate: false
+  xy: 117, 53
+  size: 33, 67
+  orig: 33, 67
+  offset: 0, 0
+  index: -1
+right-foot
+  rotate: false
+  xy: 160, 224
+  size: 67, 30
+  orig: 67, 30
+  offset: 0, 0
+  index: -1
+left-shoulder
+  rotate: false
+  xy: 124, 201
+  size: 34, 53
+  orig: 34, 53
+  offset: 0, 0
+  index: -1
+left-ankle
+  rotate: false
+  xy: 229, 222
+  size: 25, 32
+  orig: 25, 32
+  offset: 0, 0
+  index: -1
+left-foot
+  rotate: false
+  xy: 160, 192
+  size: 65, 30
+  orig: 65, 30
+  offset: 0, 0
+  index: -1
+neck
+  rotate: false
+  xy: 124, 171
+  size: 34, 28
+  orig: 34, 28
+  offset: 0, 0
+  index: -1
+right-arm
+  rotate: false
+  xy: 124, 124
+  size: 21, 45
+  orig: 21, 45
+  offset: 0, 0
+  index: -1
+right-ankle
+  rotate: false
+  xy: 227, 190
+  size: 25, 30
+  orig: 25, 30
+  offset: 0, 0
+  index: -1
+left-hand
+  rotate: false
+  xy: 147, 131
+  size: 35, 38
+  orig: 35, 38
+  offset: 0, 0
+  index: -1
+left-arm
+  rotate: false
+  xy: 184, 161
+  size: 35, 29
+  orig: 35, 29
+  offset: 0, 0
+  index: -1
+eyes-closed
+  rotate: false
+  xy: 221, 161
+  size: 34, 27
+  orig: 34, 27
+  offset: 0, 0
+  index: -1
+right-lower-leg
+  rotate: false
+  xy: 152, 65
+  size: 51, 64
+  orig: 51, 64
+  offset: 0, 0
+  index: -1
+right-foot-idle
+  rotate: false
+  xy: 184, 131
+  size: 53, 28
+  orig: 53, 28
+  offset: 0, 0
+  index: -1
+left-lower-leg
+  rotate: false
+  xy: 205, 65
+  size: 49, 64
+  orig: 49, 64
+  offset: 0, 0
+  index: -1
+right-shoulder
+  rotate: false
+  xy: 160, 12
+  size: 52, 51
+  orig: 52, 51
+  offset: 0, 0
+  index: -1
+eyes
+  rotate: false
+  xy: 214, 36
+  size: 34, 27
+  orig: 34, 27
+  offset: 0, 0
+  index: -1
+right-hand
+  rotate: false
+  xy: 214, 2
+  size: 32, 32
+  orig: 32, 32
+  offset: 0, 0
+  index: -1

+ 775 - 0
modules/SpineToy/1/assets/spineboy/spineboy.json

@@ -0,0 +1,775 @@
+{
+"bones": [
+	{ "name": "root" },
+	{ "name": "hip", "parent": "root", "x": 0.64, "y": 114.41 },
+	{ "name": "left upper leg", "parent": "hip", "length": 50.39, "x": 14.45, "y": 2.81, "rotation": -89.09 },
+	{ "name": "left lower leg", "parent": "left upper leg", "length": 56.45, "x": 51.78, "y": 3.46, "rotation": -16.65 },
+	{ "name": "left foot", "parent": "left lower leg", "length": 46.5, "x": 64.02, "y": -8.67, "rotation": 102.43 },
+	{ "name": "right upper leg", "parent": "hip", "length": 45.76, "x": -18.27, "rotation": -101.13 },
+	{ "name": "right lower leg", "parent": "right upper leg", "length": 58.52, "x": 50.21, "y": 0.6, "rotation": -10.7 },
+	{ "name": "right foot", "parent": "right lower leg", "length": 45.45, "x": 64.88, "y": 0.04, "rotation": 110.3 },
+	{ "name": "torso", "parent": "hip", "length": 85.82, "x": -6.42, "y": 1.97, "rotation": 94.95 },
+	{ "name": "neck", "parent": "torso", "length": 18.38, "x": 83.64, "y": -1.78, "rotation": 0.9 },
+	{ "name": "head", "parent": "neck", "length": 68.28, "x": 19.09, "y": 6.97, "rotation": -8.94 },
+	{ "name": "right shoulder", "parent": "torso", "length": 49.95, "x": 81.9, "y": 6.79, "rotation": 130.6 },
+	{ "name": "right arm", "parent": "right shoulder", "length": 36.74, "x": 49.95, "y": -0.12, "rotation": 40.12 },
+	{ "name": "right hand", "parent": "right arm", "length": 15.32, "x": 36.9, "y": 0.34, "rotation": 2.35 },
+	{ "name": "left shoulder", "parent": "torso", "length": 44.19, "x": 78.96, "y": -15.75, "rotation": -156.96 },
+	{ "name": "left arm", "parent": "left shoulder", "length": 35.62, "x": 44.19, "y": -0.01, "rotation": 28.16 },
+	{ "name": "left hand", "parent": "left arm", "length": 11.52, "x": 35.62, "y": 0.07, "rotation": 2.7 },
+	{ "name": "pelvis", "parent": "hip", "x": 1.41, "y": -6.57 }
+],
+"slots": [
+	{ "name": "left shoulder", "bone": "left shoulder", "attachment": "left-shoulder" },
+	{ "name": "left arm", "bone": "left arm", "attachment": "left-arm" },
+	{ "name": "left hand", "bone": "left hand", "attachment": "left-hand" },
+	{ "name": "left foot", "bone": "left foot", "attachment": "left-foot" },
+	{ "name": "left lower leg", "bone": "left lower leg", "attachment": "left-lower-leg" },
+	{ "name": "left upper leg", "bone": "left upper leg", "attachment": "left-upper-leg" },
+	{ "name": "pelvis", "bone": "pelvis", "attachment": "pelvis" },
+	{ "name": "right foot", "bone": "right foot", "attachment": "right-foot" },
+	{ "name": "right lower leg", "bone": "right lower leg", "attachment": "right-lower-leg" },
+	{ "name": "right upper leg", "bone": "right upper leg", "attachment": "right-upper-leg" },
+	{ "name": "torso", "bone": "torso", "attachment": "torso" },
+	{ "name": "neck", "bone": "neck", "attachment": "neck" },
+	{ "name": "head", "bone": "head", "attachment": "head" },
+	{ "name": "eyes", "bone": "head", "attachment": "eyes" },
+	{ "name": "right shoulder", "bone": "right shoulder", "attachment": "right-shoulder" },
+	{ "name": "right arm", "bone": "right arm", "attachment": "right-arm" },
+	{ "name": "right hand", "bone": "right hand", "attachment": "right-hand" }
+],
+"skins": {
+	"default": {
+		"left shoulder": {
+			"left-shoulder": { "x": 23.74, "y": 0.11, "rotation": 62.01, "width": 34, "height": 53 }
+		},
+		"left arm": {
+			"left-arm": { "x": 15.11, "y": -0.44, "rotation": 33.84, "width": 35, "height": 29 }
+		},
+		"left hand": {
+			"left-hand": { "x": 0.75, "y": 1.86, "rotation": 31.14, "width": 35, "height": 38 }
+		},
+		"left foot": {
+			"left-foot": { "x": 24.35, "y": 8.88, "rotation": 3.32, "width": 65, "height": 30 }
+		},
+		"left lower leg": {
+			"left-lower-leg": { "x": 24.55, "y": -1.92, "rotation": 105.75, "width": 49, "height": 64 }
+		},
+		"left upper leg": {
+			"left-upper-leg": { "x": 26.12, "y": -1.85, "rotation": 89.09, "width": 33, "height": 67 }
+		},
+		"pelvis": {
+			"pelvis": { "x": -4.83, "y": 10.62, "width": 63, "height": 47 }
+		},
+		"right foot": {
+			"right-foot": { "x": 19.02, "y": 8.47, "rotation": 1.52, "width": 67, "height": 30 }
+		},
+		"right lower leg": {
+			"right-lower-leg": { "x": 23.28, "y": -2.59, "rotation": 111.83, "width": 51, "height": 64 }
+		},
+		"right upper leg": {
+			"right-upper-leg": { "x": 23.03, "y": 0.25, "rotation": 101.13, "width": 44, "height": 70 }
+		},
+		"torso": {
+			"torso": { "x": 44.57, "y": -7.08, "rotation": -94.95, "width": 68, "height": 92 }
+		},
+		"neck": {
+			"neck": { "x": 9.42, "y": -3.66, "rotation": -100.15, "width": 34, "height": 28 }
+		},
+		"head": {
+			"head": { "x": 53.94, "y": -5.75, "rotation": -86.9, "width": 121, "height": 132 }
+		},
+		"eyes": {
+			"eyes": { "x": 28.94, "y": -32.92, "rotation": -86.9, "width": 34, "height": 27 },
+			"eyes-closed": { "x": 28.77, "y": -32.86, "rotation": -86.9, "width": 34, "height": 27 }
+		},
+		"right shoulder": {
+			"right-shoulder": { "x": 25.86, "y": 0.03, "rotation": 134.44, "width": 52, "height": 51 }
+		},
+		"right arm": {
+			"right-arm": { "x": 18.34, "y": -2.64, "rotation": 94.32, "width": 21, "height": 45 }
+		},
+		"right hand": {
+			"right-hand": { "x": 6.82, "y": 1.25, "rotation": 91.96, "width": 32, "height": 32 }
+		}
+	}
+},
+"animations": {
+	"walk": {
+		"bones": {
+			"left upper leg": {
+				"rotate": [
+					{ "time": 0, "angle": -26.55 },
+					{ "time": 0.1333, "angle": -8.78 },
+					{ "time": 0.2666, "angle": 9.51 },
+					{ "time": 0.4, "angle": 30.74 },
+					{ "time": 0.5333, "angle": 25.33 },
+					{ "time": 0.6666, "angle": 26.11 },
+					{ "time": 0.8, "angle": -7.7 },
+					{ "time": 0.9333, "angle": -21.19 },
+					{ "time": 1.0666, "angle": -26.55 }
+				],
+				"translate": [
+					{ "time": 0, "x": -3, "y": -2.25 },
+					{ "time": 0.4, "x": -2.18, "y": -2.25 },
+					{ "time": 1.0666, "x": -3, "y": -2.25 }
+				]
+			},
+			"right upper leg": {
+				"rotate": [
+					{ "time": 0, "angle": 42.45 },
+					{ "time": 0.1333, "angle": 52.1 },
+					{ "time": 0.2666, "angle": 5.96 },
+					{ "time": 0.5333, "angle": -16.93 },
+					{ "time": 0.6666, "angle": 1.89 },
+					{
+						"time": 0.8,
+						"angle": 28.06,
+						"curve": [ 0.462, 0.11, 1, 1 ]
+					},
+					{
+						"time": 0.9333,
+						"angle": 58.68,
+						"curve": [ 0.5, 0.02, 1, 1 ]
+					},
+					{ "time": 1.0666, "angle": 42.45 }
+				],
+				"translate": [
+					{ "time": 0, "x": 8.11, "y": -2.36 },
+					{ "time": 0.1333, "x": 10.03, "y": -2.56 },
+					{ "time": 0.4, "x": 2.76, "y": -2.97 },
+					{ "time": 0.5333, "x": 2.76, "y": -2.81 },
+					{ "time": 0.9333, "x": 8.67, "y": -2.54 },
+					{ "time": 1.0666, "x": 8.11, "y": -2.36 }
+				]
+			},
+			"left lower leg": {
+				"rotate": [
+					{ "time": 0, "angle": -10.21 },
+					{ "time": 0.1333, "angle": -55.64 },
+					{ "time": 0.2666, "angle": -68.12 },
+					{ "time": 0.5333, "angle": 5.11 },
+					{ "time": 0.6666, "angle": -28.29 },
+					{ "time": 0.8, "angle": 4.08 },
+					{ "time": 0.9333, "angle": 3.53 },
+					{ "time": 1.0666, "angle": -10.21 }
+				]
+			},
+			"left foot": {
+				"rotate": [
+					{ "time": 0, "angle": -3.69 },
+					{ "time": 0.1333, "angle": -10.42 },
+					{ "time": 0.2666, "angle": -17.14 },
+					{ "time": 0.4, "angle": -2.83 },
+					{ "time": 0.5333, "angle": -3.87 },
+					{ "time": 0.6666, "angle": 2.78 },
+					{ "time": 0.8, "angle": 1.68 },
+					{ "time": 0.9333, "angle": -8.54 },
+					{ "time": 1.0666, "angle": -3.69 }
+				]
+			},
+			"right shoulder": {
+				"rotate": [
+					{
+						"time": 0,
+						"angle": 20.89,
+						"curve": [ 0.264, 0, 0.75, 1 ]
+					},
+					{
+						"time": 0.1333,
+						"angle": 3.72,
+						"curve": [ 0.272, 0, 0.841, 1 ]
+					},
+					{ "time": 0.6666, "angle": -278.28 },
+					{ "time": 1.0666, "angle": 20.89 }
+				],
+				"translate": [
+					{ "time": 0, "x": -7.84, "y": 7.19 },
+					{ "time": 0.1333, "x": -6.36, "y": 6.42 },
+					{ "time": 0.6666, "x": -11.07, "y": 5.25 },
+					{ "time": 1.0666, "x": -7.84, "y": 7.19 }
+				]
+			},
+			"right arm": {
+				"rotate": [
+					{
+						"time": 0,
+						"angle": -4.02,
+						"curve": [ 0.267, 0, 0.804, 0.99 ]
+					},
+					{
+						"time": 0.1333,
+						"angle": -13.99,
+						"curve": [ 0.341, 0, 1, 1 ]
+					},
+					{
+						"time": 0.6666,
+						"angle": 36.54,
+						"curve": [ 0.307, 0, 0.787, 0.99 ]
+					},
+					{ "time": 1.0666, "angle": -4.02 }
+				]
+			},
+			"right hand": {
+				"rotate": [
+					{ "time": 0, "angle": 22.92 },
+					{ "time": 0.4, "angle": -8.97 },
+					{ "time": 0.6666, "angle": 0.51 },
+					{ "time": 1.0666, "angle": 22.92 }
+				]
+			},
+			"left shoulder": {
+				"rotate": [
+					{ "time": 0, "angle": -1.47 },
+					{ "time": 0.1333, "angle": 13.6 },
+					{ "time": 0.6666, "angle": 280.74 },
+					{ "time": 1.0666, "angle": -1.47 }
+				],
+				"translate": [
+					{ "time": 0, "x": -1.76, "y": 0.56 },
+					{ "time": 0.6666, "x": -2.47, "y": 8.14 },
+					{ "time": 1.0666, "x": -1.76, "y": 0.56 }
+				]
+			},
+			"left hand": {
+				"rotate": [
+					{
+						"time": 0,
+						"angle": 11.58,
+						"curve": [ 0.169, 0.37, 0.632, 1.55 ]
+					},
+					{
+						"time": 0.1333,
+						"angle": 28.13,
+						"curve": [ 0.692, 0, 0.692, 0.99 ]
+					},
+					{
+						"time": 0.6666,
+						"angle": -27.42,
+						"curve": [ 0.117, 0.41, 0.738, 1.76 ]
+					},
+					{ "time": 0.8, "angle": -36.32 },
+					{ "time": 1.0666, "angle": 11.58 }
+				]
+			},
+			"left arm": {
+				"rotate": [
+					{ "time": 0, "angle": -8.27 },
+					{ "time": 0.1333, "angle": 18.43 },
+					{ "time": 0.6666, "angle": 0.88 },
+					{ "time": 1.0666, "angle": -8.27 }
+				]
+			},
+			"torso": {
+				"rotate": [
+					{ "time": 0, "angle": -10.28 },
+					{
+						"time": 0.1333,
+						"angle": -15.38,
+						"curve": [ 0.545, 0, 1, 1 ]
+					},
+					{
+						"time": 0.4,
+						"angle": -9.78,
+						"curve": [ 0.58, 0.17, 1, 1 ]
+					},
+					{ "time": 0.6666, "angle": -15.75 },
+					{ "time": 0.9333, "angle": -7.06 },
+					{ "time": 1.0666, "angle": -10.28 }
+				],
+				"translate": [
+					{ "time": 0, "x": -3.67, "y": 1.68 },
+					{ "time": 0.1333, "x": -3.67, "y": 0.68 },
+					{ "time": 0.4, "x": -3.67, "y": 1.97 },
+					{ "time": 0.6666, "x": -3.67, "y": -0.14 },
+					{ "time": 1.0666, "x": -3.67, "y": 1.68 }
+				]
+			},
+			"right foot": {
+				"rotate": [
+					{ "time": 0, "angle": -5.25 },
+					{ "time": 0.2666, "angle": -4.08 },
+					{ "time": 0.4, "angle": -6.45 },
+					{ "time": 0.5333, "angle": -5.39 },
+					{ "time": 0.8, "angle": -11.68 },
+					{ "time": 0.9333, "angle": 0.46 },
+					{ "time": 1.0666, "angle": -5.25 }
+				]
+			},
+			"right lower leg": {
+				"rotate": [
+					{ "time": 0, "angle": -3.39 },
+					{ "time": 0.1333, "angle": -45.53 },
+					{ "time": 0.2666, "angle": -2.59 },
+					{ "time": 0.5333, "angle": -19.53 },
+					{ "time": 0.6666, "angle": -64.8 },
+					{
+						"time": 0.8,
+						"angle": -82.56,
+						"curve": [ 0.557, 0.18, 1, 1 ]
+					},
+					{ "time": 1.0666, "angle": -3.39 }
+				]
+			},
+			"hip": {
+				"rotate": [
+					{ "time": 0, "angle": 0, "curve": "stepped" },
+					{ "time": 1.0666, "angle": 0 }
+				],
+				"translate": [
+					{ "time": 0, "x": 0, "y": 0 },
+					{
+						"time": 0.1333,
+						"x": 0,
+						"y": -7.61,
+						"curve": [ 0.272, 0.86, 1, 1 ]
+					},
+					{ "time": 0.4, "x": 0, "y": 8.7 },
+					{ "time": 0.5333, "x": 0, "y": -0.41 },
+					{
+						"time": 0.6666,
+						"x": 0,
+						"y": -7.05,
+						"curve": [ 0.235, 0.89, 1, 1 ]
+					},
+					{ "time": 0.8, "x": 0, "y": 2.92 },
+					{ "time": 0.9333, "x": 0, "y": 6.78 },
+					{ "time": 1.0666, "x": 0, "y": 0 }
+				]
+			},
+			"neck": {
+				"rotate": [
+					{ "time": 0, "angle": 3.6 },
+					{ "time": 0.1333, "angle": 17.49 },
+					{ "time": 0.2666, "angle": 6.1 },
+					{ "time": 0.4, "angle": 3.45 },
+					{ "time": 0.5333, "angle": 5.17 },
+					{ "time": 0.6666, "angle": 18.36 },
+					{ "time": 0.8, "angle": 6.09 },
+					{ "time": 0.9333, "angle": 2.28 },
+					{ "time": 1.0666, "angle": 3.6 }
+				]
+			},
+			"head": {
+				"rotate": [
+					{
+						"time": 0,
+						"angle": 3.6,
+						"curve": [ 0, 0, 0.704, 1.61 ]
+					},
+					{ "time": 0.1666, "angle": -0.2 },
+					{ "time": 0.2666, "angle": 6.1 },
+					{ "time": 0.4, "angle": 3.45 },
+					{
+						"time": 0.5333,
+						"angle": 5.17,
+						"curve": [ 0, 0, 0.704, 1.61 ]
+					},
+					{ "time": 0.7, "angle": 1.1 },
+					{ "time": 0.8, "angle": 6.09 },
+					{ "time": 0.9333, "angle": 2.28 },
+					{ "time": 1.0666, "angle": 3.6 }
+				]
+			}
+		}
+	},
+	"jump": {
+		"bones": {
+			"hip": {
+				"rotate": [
+					{ "time": 0, "angle": 0, "curve": "stepped" },
+					{ "time": 0.9333, "angle": 0, "curve": "stepped" },
+					{ "time": 1.3666, "angle": 0 }
+				],
+				"translate": [
+					{ "time": 0, "x": -11.57, "y": -3 },
+					{ "time": 0.2333, "x": -16.2, "y": -19.43 },
+					{
+						"time": 0.3333,
+						"x": 7.66,
+						"y": -8.48,
+						"curve": [ 0.057, 0.06, 0.712, 1 ]
+					},
+					{ "time": 0.3666, "x": 15.38, "y": 5.01 },
+					{ "time": 0.4666, "x": -7.84, "y": 57.22 },
+					{
+						"time": 0.6,
+						"x": -10.81,
+						"y": 96.34,
+						"curve": [ 0.241, 0, 1, 1 ]
+					},
+					{ "time": 0.7333, "x": -7.01, "y": 54.7 },
+					{ "time": 0.8, "x": -10.58, "y": 32.2 },
+					{ "time": 0.9333, "x": -31.99, "y": 0.45 },
+					{ "time": 1.0666, "x": -12.48, "y": -29.47 },
+					{ "time": 1.3666, "x": -11.57, "y": -3 }
+				],
+				"scale": [
+					{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
+					{ "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" },
+					{ "time": 1.3666, "x": 1, "y": 1 }
+				]
+			},
+			"left upper leg": {
+				"rotate": [
+					{ "time": 0, "angle": 17.13 },
+					{ "time": 0.2333, "angle": 44.35 },
+					{ "time": 0.3333, "angle": 16.46 },
+					{ "time": 0.4, "angle": -9.88 },
+					{ "time": 0.4666, "angle": -11.42 },
+					{ "time": 0.5666, "angle": 23.46 },
+					{ "time": 0.7666, "angle": 71.82 },
+					{ "time": 0.9333, "angle": 65.53 },
+					{ "time": 1.0666, "angle": 51.01 },
+					{ "time": 1.3666, "angle": 17.13 }
+				],
+				"translate": [
+					{ "time": 0, "x": -3, "y": -2.25, "curve": "stepped" },
+					{ "time": 0.9333, "x": -3, "y": -2.25, "curve": "stepped" },
+					{ "time": 1.3666, "x": -3, "y": -2.25 }
+				],
+				"scale": [
+					{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
+					{ "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" },
+					{ "time": 1.3666, "x": 1, "y": 1 }
+				]
+			},
+			"left lower leg": {
+				"rotate": [
+					{ "time": 0, "angle": -16.25 },
+					{ "time": 0.2333, "angle": -52.21 },
+					{ "time": 0.4, "angle": 15.04 },
+					{ "time": 0.4666, "angle": -8.95 },
+					{ "time": 0.5666, "angle": -39.53 },
+					{ "time": 0.7666, "angle": -27.27 },
+					{ "time": 0.9333, "angle": -3.52 },
+					{ "time": 1.0666, "angle": -61.92 },
+					{ "time": 1.3666, "angle": -16.25 }
+				],
+				"translate": [
+					{ "time": 0, "x": 0, "y": 0, "curve": "stepped" },
+					{ "time": 0.9333, "x": 0, "y": 0, "curve": "stepped" },
+					{ "time": 1.3666, "x": 0, "y": 0 }
+				],
+				"scale": [
+					{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
+					{ "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" },
+					{ "time": 1.3666, "x": 1, "y": 1 }
+				]
+			},
+			"left foot": {
+				"rotate": [
+					{ "time": 0, "angle": 0.33 },
+					{ "time": 0.2333, "angle": 6.2 },
+					{ "time": 0.3333, "angle": 14.73 },
+					{ "time": 0.4, "angle": -15.54 },
+					{ "time": 0.4333, "angle": -21.2 },
+					{ "time": 0.5666, "angle": -7.55 },
+					{ "time": 0.7666, "angle": -0.67 },
+					{ "time": 0.9333, "angle": -0.58 },
+					{ "time": 1.0666, "angle": 14.64 },
+					{ "time": 1.3666, "angle": 0.33 }
+				],
+				"translate": [
+					{ "time": 0, "x": 0, "y": 0, "curve": "stepped" },
+					{ "time": 0.9333, "x": 0, "y": 0, "curve": "stepped" },
+					{ "time": 1.3666, "x": 0, "y": 0 }
+				],
+				"scale": [
+					{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
+					{ "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" },
+					{ "time": 1.3666, "x": 1, "y": 1 }
+				]
+			},
+			"right upper leg": {
+				"rotate": [
+					{ "time": 0, "angle": 25.97 },
+					{ "time": 0.2333, "angle": 46.43 },
+					{ "time": 0.3333, "angle": 22.61 },
+					{ "time": 0.4, "angle": 2.13 },
+					{
+						"time": 0.4666,
+						"angle": 0.04,
+						"curve": [ 0, 0, 0.637, 0.98 ]
+					},
+					{ "time": 0.6, "angle": 65.55 },
+					{ "time": 0.7666, "angle": 64.93 },
+					{ "time": 0.9333, "angle": 41.08 },
+					{ "time": 1.0666, "angle": 66.25 },
+					{ "time": 1.3666, "angle": 25.97 }
+				],
+				"translate": [
+					{ "time": 0, "x": 5.74, "y": 0.61 },
+					{ "time": 0.2333, "x": 4.79, "y": 1.79 },
+					{ "time": 0.3333, "x": 6.05, "y": -4.55 },
+					{ "time": 0.9333, "x": 4.79, "y": 1.79, "curve": "stepped" },
+					{ "time": 1.0666, "x": 4.79, "y": 1.79 },
+					{ "time": 1.3666, "x": 5.74, "y": 0.61 }
+				],
+				"scale": [
+					{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
+					{ "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" },
+					{ "time": 1.3666, "x": 1, "y": 1 }
+				]
+			},
+			"right lower leg": {
+				"rotate": [
+					{ "time": 0, "angle": -27.46 },
+					{ "time": 0.2333, "angle": -64.03 },
+					{ "time": 0.4, "angle": -48.36 },
+					{ "time": 0.5666, "angle": -76.86 },
+					{ "time": 0.7666, "angle": -26.89 },
+					{ "time": 0.9, "angle": -18.97 },
+					{ "time": 0.9333, "angle": -14.18 },
+					{ "time": 1.0666, "angle": -80.45 },
+					{ "time": 1.3666, "angle": -27.46 }
+				],
+				"translate": [
+					{ "time": 0, "x": 0, "y": 0, "curve": "stepped" },
+					{ "time": 0.9333, "x": 0, "y": 0, "curve": "stepped" },
+					{ "time": 1.3666, "x": 0, "y": 0 }
+				],
+				"scale": [
+					{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
+					{ "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" },
+					{ "time": 1.3666, "x": 1, "y": 1 }
+				]
+			},
+			"right foot": {
+				"rotate": [
+					{ "time": 0, "angle": 1.08 },
+					{ "time": 0.2333, "angle": 16.02 },
+					{ "time": 0.3, "angle": 12.94 },
+					{ "time": 0.3333, "angle": 15.16 },
+					{ "time": 0.4, "angle": -14.7 },
+					{ "time": 0.4333, "angle": -12.85 },
+					{ "time": 0.4666, "angle": -19.18 },
+					{ "time": 0.5666, "angle": -15.82 },
+					{ "time": 0.6, "angle": -3.59 },
+					{ "time": 0.7666, "angle": -3.56 },
+					{ "time": 0.9333, "angle": 1.86 },
+					{ "time": 1.0666, "angle": 16.02 },
+					{ "time": 1.3666, "angle": 1.08 }
+				],
+				"translate": [
+					{ "time": 0, "x": 0, "y": 0, "curve": "stepped" },
+					{ "time": 0.9333, "x": 0, "y": 0, "curve": "stepped" },
+					{ "time": 1.3666, "x": 0, "y": 0 }
+				],
+				"scale": [
+					{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
+					{ "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" },
+					{ "time": 1.3666, "x": 1, "y": 1 }
+				]
+			},
+			"torso": {
+				"rotate": [
+					{ "time": 0, "angle": -13.35 },
+					{ "time": 0.2333, "angle": -48.95 },
+					{ "time": 0.4333, "angle": -35.77 },
+					{ "time": 0.6, "angle": -4.59 },
+					{ "time": 0.7666, "angle": 14.61 },
+					{ "time": 0.9333, "angle": 15.74 },
+					{ "time": 1.0666, "angle": -32.44 },
+					{ "time": 1.3666, "angle": -13.35 }
+				],
+				"translate": [
+					{ "time": 0, "x": -3.67, "y": 1.68, "curve": "stepped" },
+					{ "time": 0.9333, "x": -3.67, "y": 1.68, "curve": "stepped" },
+					{ "time": 1.3666, "x": -3.67, "y": 1.68 }
+				],
+				"scale": [
+					{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
+					{ "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" },
+					{ "time": 1.3666, "x": 1, "y": 1 }
+				]
+			},
+			"neck": {
+				"rotate": [
+					{ "time": 0, "angle": 12.78 },
+					{ "time": 0.2333, "angle": 16.46 },
+					{ "time": 0.4, "angle": 26.49 },
+					{ "time": 0.6, "angle": 15.51 },
+					{ "time": 0.7666, "angle": 1.34 },
+					{ "time": 0.9333, "angle": 2.35 },
+					{ "time": 1.0666, "angle": 6.08 },
+					{ "time": 1.3, "angle": 21.23 },
+					{ "time": 1.3666, "angle": 12.78 }
+				],
+				"translate": [
+					{ "time": 0, "x": 0, "y": 0, "curve": "stepped" },
+					{ "time": 0.9333, "x": 0, "y": 0, "curve": "stepped" },
+					{ "time": 1.3666, "x": 0, "y": 0 }
+				],
+				"scale": [
+					{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
+					{ "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" },
+					{ "time": 1.3666, "x": 1, "y": 1 }
+				]
+			},
+			"head": {
+				"rotate": [
+					{ "time": 0, "angle": 5.19 },
+					{ "time": 0.2333, "angle": 20.27 },
+					{ "time": 0.4, "angle": 15.27 },
+					{ "time": 0.6, "angle": -24.69 },
+					{ "time": 0.7666, "angle": -11.02 },
+					{ "time": 0.9333, "angle": -24.38 },
+					{ "time": 1.0666, "angle": 11.99 },
+					{ "time": 1.3, "angle": 4.86 },
+					{ "time": 1.3666, "angle": 5.19 }
+				],
+				"translate": [
+					{ "time": 0, "x": 0, "y": 0, "curve": "stepped" },
+					{ "time": 0.9333, "x": 0, "y": 0, "curve": "stepped" },
+					{ "time": 1.3666, "x": 0, "y": 0 }
+				],
+				"scale": [
+					{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
+					{ "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" },
+					{ "time": 1.3666, "x": 1, "y": 1 }
+				]
+			},
+			"left shoulder": {
+				"rotate": [
+					{
+						"time": 0,
+						"angle": 0.05,
+						"curve": [ 0, 0, 0.62, 1 ]
+					},
+					{
+						"time": 0.2333,
+						"angle": 279.66,
+						"curve": [ 0.218, 0.67, 0.66, 0.99 ]
+					},
+					{
+						"time": 0.5,
+						"angle": 62.27,
+						"curve": [ 0.462, 0, 0.764, 0.58 ]
+					},
+					{ "time": 0.9333, "angle": 28.91 },
+					{ "time": 1.0666, "angle": -8.62 },
+					{ "time": 1.1666, "angle": -18.43 },
+					{ "time": 1.3666, "angle": 0.05 }
+				],
+				"translate": [
+					{ "time": 0, "x": -1.76, "y": 0.56, "curve": "stepped" },
+					{ "time": 0.9333, "x": -1.76, "y": 0.56, "curve": "stepped" },
+					{ "time": 1.3666, "x": -1.76, "y": 0.56 }
+				],
+				"scale": [
+					{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
+					{ "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" },
+					{ "time": 1.3666, "x": 1, "y": 1 }
+				]
+			},
+			"left hand": {
+				"rotate": [
+					{ "time": 0, "angle": 11.58, "curve": "stepped" },
+					{ "time": 0.9333, "angle": 11.58, "curve": "stepped" },
+					{ "time": 1.3666, "angle": 11.58 }
+				],
+				"translate": [
+					{ "time": 0, "x": 0, "y": 0, "curve": "stepped" },
+					{ "time": 0.9333, "x": 0, "y": 0, "curve": "stepped" },
+					{ "time": 1.3666, "x": 0, "y": 0 }
+				],
+				"scale": [
+					{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
+					{ "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" },
+					{ "time": 1.3666, "x": 1, "y": 1 }
+				]
+			},
+			"left arm": {
+				"rotate": [
+					{ "time": 0, "angle": 0.51 },
+					{ "time": 0.4333, "angle": 12.82 },
+					{ "time": 0.6, "angle": 47.55 },
+					{ "time": 0.9333, "angle": 12.82 },
+					{ "time": 1.1666, "angle": -6.5 },
+					{ "time": 1.3666, "angle": 0.51 }
+				],
+				"translate": [
+					{ "time": 0, "x": 0, "y": 0, "curve": "stepped" },
+					{ "time": 0.9333, "x": 0, "y": 0, "curve": "stepped" },
+					{ "time": 1.3666, "x": 0, "y": 0 }
+				],
+				"scale": [
+					{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
+					{ "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" },
+					{ "time": 1.3666, "x": 1, "y": 1 }
+				]
+			},
+			"right shoulder": {
+				"rotate": [
+					{
+						"time": 0,
+						"angle": 43.82,
+						"curve": [ 0, 0, 0.62, 1 ]
+					},
+					{
+						"time": 0.2333,
+						"angle": -8.74,
+						"curve": [ 0.304, 0.58, 0.709, 0.97 ]
+					},
+					{
+						"time": 0.5333,
+						"angle": -208.02,
+						"curve": [ 0.462, 0, 0.764, 0.58 ]
+					},
+					{ "time": 0.9333, "angle": -246.72 },
+					{ "time": 1.0666, "angle": -307.13 },
+					{ "time": 1.1666, "angle": 37.15 },
+					{ "time": 1.3666, "angle": 43.82 }
+				],
+				"translate": [
+					{ "time": 0, "x": -7.84, "y": 7.19, "curve": "stepped" },
+					{ "time": 0.9333, "x": -7.84, "y": 7.19, "curve": "stepped" },
+					{ "time": 1.3666, "x": -7.84, "y": 7.19 }
+				],
+				"scale": [
+					{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
+					{ "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" },
+					{ "time": 1.3666, "x": 1, "y": 1 }
+				]
+			},
+			"right arm": {
+				"rotate": [
+					{ "time": 0, "angle": -4.02 },
+					{ "time": 0.6, "angle": 17.5 },
+					{ "time": 0.9333, "angle": -4.02 },
+					{ "time": 1.1666, "angle": -16.72 },
+					{ "time": 1.3666, "angle": -4.02 }
+				],
+				"translate": [
+					{ "time": 0, "x": 0, "y": 0, "curve": "stepped" },
+					{ "time": 0.9333, "x": 0, "y": 0, "curve": "stepped" },
+					{ "time": 1.3666, "x": 0, "y": 0 }
+				],
+				"scale": [
+					{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
+					{ "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" },
+					{ "time": 1.3666, "x": 1, "y": 1 }
+				]
+			},
+			"right hand": {
+				"rotate": [
+					{ "time": 0, "angle": 22.92, "curve": "stepped" },
+					{ "time": 0.9333, "angle": 22.92, "curve": "stepped" },
+					{ "time": 1.3666, "angle": 22.92 }
+				],
+				"translate": [
+					{ "time": 0, "x": 0, "y": 0, "curve": "stepped" },
+					{ "time": 0.9333, "x": 0, "y": 0, "curve": "stepped" },
+					{ "time": 1.3666, "x": 0, "y": 0 }
+				],
+				"scale": [
+					{ "time": 0, "x": 1, "y": 1, "curve": "stepped" },
+					{ "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" },
+					{ "time": 1.3666, "x": 1, "y": 1 }
+				]
+			}
+		}
+	}
+}
+}

BIN
modules/SpineToy/1/assets/spineboy/spineboy.png


+ 4 - 0
modules/SpineToy/1/assets/spineboy/test.asset.taml

@@ -0,0 +1,4 @@
+<SkeletonAsset
+        AssetName="TestSkeleton"
+		AtlasFile="spineboy.atlas"
+        SkeletonFile="spineboy.json" />

+ 3 - 0
modules/SpineToy/1/assets/spinosaurus/background.asset.taml

@@ -0,0 +1,3 @@
+<ImageAsset
+    AssetName="background"
+    ImageFile="background.jpg" />

BIN
modules/SpineToy/1/assets/spinosaurus/background.jpg


+ 4 - 0
modules/SpineToy/1/assets/spinosaurus/spinosaurus.asset.taml

@@ -0,0 +1,4 @@
+<SkeletonAsset
+        AssetName="spinosaurus"
+		AtlasFile="spinosaurus.atlas"
+        SkeletonFile="spinosaurus.json"/>

+ 39 - 0
modules/SpineToy/1/assets/spinosaurus/spinosaurus.atlas

@@ -0,0 +1,39 @@
+spinosaurus.png
+format: RGBA8888
+filter: Linear,Linear
+repeat: none
+logo
+  rotate: false
+  xy: 2, 526
+  size: 917, 323
+  orig: 917, 323
+  offset: 0, 0
+  index: -1
+play
+  rotate: false
+  xy: 2, 851
+  size: 218, 77
+  orig: 218, 77
+  offset: 0, 0
+  index: -1
+quit
+  rotate: false
+  xy: 222, 851
+  size: 216, 77
+  orig: 216, 77
+  offset: 0, 0
+  index: -1
+leaves
+  rotate: false
+  xy: 2, 2
+  size: 1042, 522
+  orig: 1042, 522
+  offset: 0, 0
+  index: -1
+settings
+  rotate: false
+  xy: 440, 851
+  size: 446, 76
+  orig: 446, 76
+  offset: 0, 0
+  index: -1

+ 430 - 0
modules/SpineToy/1/assets/spinosaurus/spinosaurus.json

@@ -0,0 +1,430 @@
+{
+"bones": [
+	{ "name": "root", "y": -526.31 },
+	{ "name": "logo", "parent": "root", "y": 1332.02 },
+	{ "name": "play", "parent": "root", "x": 3.02, "y": -83.61 },
+	{ "name": "settings", "parent": "root", "x": 3.02, "y": -144.37 },
+	{ "name": "quit", "parent": "root", "x": -2.85, "y": -201.21 },
+	{ "name": "leaves", "parent": "root", "x": -0.16, "y": 1310.1 }
+],
+"slots": [
+	{ "name": "leaves", "bone": "leaves", "attachment": "leaves" },
+	{ "name": "logo", "bone": "logo", "attachment": "logo" },
+	{ "name": "play", "bone": "play", "color": "fff99dff", "attachment": "play" },
+	{ "name": "quit", "bone": "quit", "color": "fff99dff", "attachment": "quit" },
+	{ "name": "settings", "bone": "settings", "color": "fff99dff", "attachment": "settings" }
+],
+"skins": {
+	"default": {
+		"leaves": {
+			"leaves": { "y": -28.54, "width": 1042, "height": 522 }
+		},
+		"logo": {
+			"logo": { "y": -20.39, "width": 917, "height": 323 }
+		},
+		"play": {
+			"play": { "width": 218, "height": 77 }
+		},
+		"quit": {
+			"quit": { "width": 216, "height": 77 }
+		},
+		"settings": {
+			"settings": { "width": 446, "height": 76 }
+		}
+	}
+},
+"animations": {
+	"Animation": {
+		"bones": {
+			"logo": {
+				"translate": [
+					{
+						"time": 0,
+						"x": 0,
+						"y": 40.8,
+						"curve": [ 0.043, 0.15, 0.344, 1.26 ]
+					},
+					{
+						"time": 0.3333,
+						"x": 0,
+						"y": -597.71,
+						"curve": [ 0.425, 0.17, 0.591, 0.99 ]
+					},
+					{
+						"time": 1,
+						"x": 0,
+						"y": -583.71,
+						"curve": [ 0.402, 0, 0.603, 1 ]
+					},
+					{
+						"time": 1.6666,
+						"x": 0,
+						"y": -592.11,
+						"curve": [ 0.408, 0.01, 0.655, 0.98 ]
+					},
+					{
+						"time": 2.3333,
+						"x": 0,
+						"y": -583.71,
+						"curve": [ 0.402, 0, 0.603, 0.99 ]
+					},
+					{
+						"time": 3,
+						"x": 0,
+						"y": -592.11,
+						"curve": [ 0.408, 0.01, 0.655, 0.98 ]
+					},
+					{ "time": 3.6666, "x": 0, "y": -583.71 }
+				],
+				"scale": [
+					{
+						"time": 0,
+						"x": 0.342,
+						"y": 0.342,
+						"curve": [ 0, 0, 0.744, 0.4 ]
+					},
+					{
+						"time": 0.3333,
+						"x": 1,
+						"y": 1,
+						"curve": [ 0.189, 0.37, 0.658, 0.98 ]
+					},
+					{
+						"time": 1,
+						"x": 1.02,
+						"y": 1.03,
+						"curve": [ 0.37, 0, 0.66, 0.99 ]
+					},
+					{
+						"time": 1.6666,
+						"x": 1,
+						"y": 1,
+						"curve": [ 0.387, 0.01, 0.686, 1 ]
+					},
+					{
+						"time": 2.3333,
+						"x": 1.02,
+						"y": 1.03,
+						"curve": [ 0.37, 0, 0.66, 0.99 ]
+					},
+					{
+						"time": 3,
+						"x": 1,
+						"y": 1,
+						"curve": [ 0.387, 0.01, 0.686, 1 ]
+					},
+					{ "time": 3.6666, "x": 1.02, "y": 1.03 }
+				]
+			},
+			"play": {
+				"translate": [
+					{ "time": 0, "x": 0, "y": 0, "curve": "stepped" },
+					{
+						"time": 0.6666,
+						"x": 0,
+						"y": 0,
+						"curve": [ 0, 0, 0.448, 1.62 ]
+					},
+					{ "time": 0.8666, "x": 0, "y": 480.2 }
+				],
+				"scale": [
+					{
+						"time": 1.3333,
+						"x": 1,
+						"y": 1,
+						"curve": [ 0, 0, 0.525, 2.46 ]
+					},
+					{ "time": 1.5666, "x": 1.231, "y": 1.231, "curve": "stepped" },
+					{
+						"time": 1.8333,
+						"x": 1.231,
+						"y": 1.231,
+						"curve": [ 0, 0, 0.525, 2.46 ]
+					},
+					{ "time": 2.0666, "x": 1, "y": 1 }
+				]
+			},
+			"settings": {
+				"translate": [
+					{
+						"time": 0.8,
+						"x": 0,
+						"y": 0,
+						"curve": [ 0, 0, 0.448, 1.62 ]
+					},
+					{ "time": 1, "x": 0, "y": 431.2 }
+				],
+				"scale": [
+					{
+						"time": 1.8666,
+						"x": 1,
+						"y": 1,
+						"curve": [ 0, 0, 0.525, 2.46 ]
+					},
+					{ "time": 2.1, "x": 1.23, "y": 1.23, "curve": "stepped" },
+					{
+						"time": 2.3666,
+						"x": 1.23,
+						"y": 1.23,
+						"curve": [ 0, 0, 0.525, 2.46 ]
+					},
+					{ "time": 2.6, "x": 1, "y": 1 }
+				]
+			},
+			"quit": {
+				"translate": [
+					{
+						"time": 0.9333,
+						"x": 0,
+						"y": 0,
+						"curve": [ 0, 0, 0.448, 1.62 ]
+					},
+					{ "time": 1.1333, "x": 0, "y": 376.32 }
+				],
+				"scale": [
+					{
+						"time": 2.4,
+						"x": 1,
+						"y": 1,
+						"curve": [ 0, 0, 0.525, 2.46 ]
+					},
+					{ "time": 2.6333, "x": 1.23, "y": 1.23 }
+				]
+			},
+			"leaves": {
+				"translate": [
+					{
+						"time": 0,
+						"x": 0,
+						"y": 0,
+						"curve": [ 0.043, 0.15, 0.344, 1.26 ]
+					},
+					{
+						"time": 0.3333,
+						"x": 0.16,
+						"y": -601.23,
+						"curve": [ 0.425, 0.17, 0.591, 0.99 ]
+					},
+					{
+						"time": 1.3333,
+						"x": 0.16,
+						"y": -580.83,
+						"curve": [ 0.402, 0, 0.603, 1 ]
+					},
+					{ "time": 2, "x": 0.16, "y": -595.11 },
+					{
+						"time": 2.6666,
+						"x": 0.16,
+						"y": -580.83,
+						"curve": [ 0.402, 0, 0.603, 1 ]
+					},
+					{ "time": 3.3333, "x": 0.16, "y": -595.11 }
+				],
+				"scale": [
+					{
+						"time": 0,
+						"x": 0.34,
+						"y": 0.34,
+						"curve": [ 0, 0, 0.744, 0.4 ]
+					},
+					{ "time": 0.3333, "x": 1, "y": 1 },
+					{ "time": 1.3333, "x": 1.03, "y": 1.02 },
+					{ "time": 2, "x": 1, "y": 1 },
+					{ "time": 2.6666, "x": 1.03, "y": 1.02 },
+					{ "time": 3.3333, "x": 1, "y": 1 }
+				]
+			}
+		},
+		"slots": {
+			"logo": {
+				"color": [
+					{ "time": 0, "color": "ffffff00" },
+					{ "time": 0.3333, "color": "ffffffff" }
+				]
+			},
+			"play": {
+				"color": [
+					{ "time": 1.3333, "color": "fff99dff" },
+					{ "time": 1.5666, "color": "ffffffff", "curve": "stepped" },
+					{ "time": 1.8333, "color": "ffffffff" },
+					{ "time": 2.0666, "color": "fff99dff" }
+				]
+			},
+			"quit": {
+				"color": [
+					{ "time": 2.4, "color": "fff99dff" },
+					{ "time": 2.6333, "color": "ffffffff" }
+				]
+			},
+			"settings": {
+				"color": [
+					{ "time": 1.8666, "color": "fff99dff" },
+					{ "time": 2.1, "color": "ffffffff", "curve": "stepped" },
+					{ "time": 2.3666, "color": "ffffffff" },
+					{ "time": 2.6, "color": "fff99dff" }
+				]
+			},
+			"leaves": {
+				"color": [
+					{ "time": 0, "color": "ffffff00" },
+					{ "time": 0.3333, "color": "ffffffff" }
+				]
+			}
+		}
+	},
+	"loop": {
+		"bones": {
+			"logo": {
+				"translate": [
+					{
+						"time": 0,
+						"x": 0,
+						"y": -597.71,
+						"curve": [ 0.425, 0.17, 0.591, 0.99 ]
+					},
+					{
+						"time": 1,
+						"x": 0,
+						"y": -583.71,
+						"curve": [ 0.402, 0, 0.603, 1 ]
+					},
+					{
+						"time": 1.6666,
+						"x": 0,
+						"y": -592.11,
+						"curve": [ 0.408, 0.01, 0.655, 0.98 ]
+					},
+					{
+						"time": 2.3333,
+						"x": 0,
+						"y": -583.71,
+						"curve": [ 0.402, 0, 0.603, 0.99 ]
+					},
+					{
+						"time": 3,
+						"x": 0,
+						"y": -592.11,
+						"curve": [ 0.408, 0.01, 0.655, 0.98 ]
+					},
+					{ "time": 3.6666, "x": 0, "y": -583.71 }
+				],
+				"scale": [
+					{
+						"time": 0,
+						"x": 1,
+						"y": 1,
+						"curve": [ 0.189, 0.37, 0.658, 0.98 ]
+					},
+					{
+						"time": 1,
+						"x": 1.02,
+						"y": 1.03,
+						"curve": [ 0.37, 0, 0.66, 0.99 ]
+					},
+					{
+						"time": 1.6666,
+						"x": 1,
+						"y": 1,
+						"curve": [ 0.387, 0.01, 0.686, 1 ]
+					},
+					{
+						"time": 2.3333,
+						"x": 1.02,
+						"y": 1.03,
+						"curve": [ 0.37, 0, 0.66, 0.99 ]
+					},
+					{
+						"time": 3,
+						"x": 1,
+						"y": 1,
+						"curve": [ 0.387, 0.01, 0.686, 1 ]
+					},
+					{ "time": 3.6666, "x": 1.02, "y": 1.03 }
+				]
+			},
+			"play": {
+				"translate": [
+					{ "time": 0, "x": 0, "y": 480.2 }
+				],
+				"scale": [
+					{ "time": 0, "x": 1, "y": 1 }
+				]
+			},
+			"settings": {
+				"translate": [
+					{ "time": 0, "x": 0, "y": 431.2 }
+				],
+				"scale": [
+					{ "time": 0, "x": 1, "y": 1 }
+				]
+			},
+			"quit": {
+				"translate": [
+					{ "time": 0, "x": 0, "y": 376.32 }
+				],
+				"scale": [
+					{ "time": 0, "x": 1.23, "y": 1.23 }
+				]
+			},
+			"leaves": {
+				"translate": [
+					{
+						"time": 0,
+						"x": 0.16,
+						"y": -601.23,
+						"curve": [ 0.425, 0.17, 0.591, 0.99 ]
+					},
+					{
+						"time": 1.3333,
+						"x": 0.16,
+						"y": -580.83,
+						"curve": [ 0.402, 0, 0.603, 1 ]
+					},
+					{ "time": 2, "x": 0.16, "y": -595.11 },
+					{
+						"time": 2.6666,
+						"x": 0.16,
+						"y": -580.83,
+						"curve": [ 0.402, 0, 0.603, 1 ]
+					},
+					{ "time": 3.3333, "x": 0.16, "y": -595.11 }
+				],
+				"scale": [
+					{ "time": 0, "x": 1, "y": 1 },
+					{ "time": 1.3333, "x": 1.03, "y": 1.02 },
+					{ "time": 2, "x": 1, "y": 1 },
+					{ "time": 2.6666, "x": 1.03, "y": 1.02 },
+					{ "time": 3.3333, "x": 1, "y": 1 }
+				]
+			}
+		},
+		"slots": {
+			"logo": {
+				"color": [
+					{ "time": 0, "color": "ffffffff" }
+				]
+			},
+			"play": {
+				"color": [
+					{ "time": 0, "color": "fff99dff" }
+				]
+			},
+			"quit": {
+				"color": [
+					{ "time": 0, "color": "ffffffff" }
+				]
+			},
+			"settings": {
+				"color": [
+					{ "time": 0, "color": "fff99dff" }
+				]
+			},
+			"leaves": {
+				"color": [
+					{ "time": 0, "color": "ffffffff" }
+				]
+			}
+		}
+	}
+}
+}

BIN
modules/SpineToy/1/assets/spinosaurus/spinosaurus.png


+ 234 - 0
modules/SpineToy/1/main.cs

@@ -0,0 +1,234 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2013 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+function SpineToy::create(%this)
+{
+    // Set the sandbox drag mode availability.
+    Sandbox.allowManipulation( pan );
+
+    // Set the manipulation mode.
+    Sandbox.useManipulation( pan );
+
+    %this.asset = "SpineToy:goblins";
+    %this.skin = "goblin";
+    %this.animation = "walk";
+    
+    addSelectionOption( "goblin,goblingirl", "Select Skin", 4, "setSkin", false, "Sets the skin for the skeleton object." );
+    
+    // Reset the toy.
+    SpineToy.reset();
+}
+
+//-----------------------------------------------------------------------------
+
+function SpineToy::destroy(%this)
+{
+}
+
+//-----------------------------------------------------------------------------
+
+function SpineToy::reset(%this)
+{
+    // Clear the scene.
+    SandboxScene.clear();
+
+    if (%this.resetSchedule !$= "")
+        cancel(%this.resetSchedule);
+        
+    // Set the camera size.
+    SandboxWindow.setCameraSize( 40, 30 );
+
+    %this.createBackground();
+    %this.createGoblin();
+}
+
+//-----------------------------------------------------------------------------
+
+function SpineToy::setSkeleton(%this, %value)
+{
+    %this.asset = %value;
+    
+    if (%value $= "SpineToy:goblins")
+        %this.setSkin("goblin");
+    else
+        %this.setSkin("default");
+}
+
+//-----------------------------------------------------------------------------
+
+function SpineToy::setSkin(%this, %value)
+{
+    %this.skin = %value;
+    %this.walker.Skin = %this.skin;
+}
+
+//-----------------------------------------------------------------------------
+
+function SpineToy::createGoblin(%this)
+{
+    // Create the skeleton object
+    %goblin = new Skeleton();
+    
+    // Assign it an asset
+    %goblin.Asset = %this.asset;
+    %goblin.Skin = %this.skin;
+        
+    // Set the animation name
+    %goblin.setAnimation(%this.animation, true);
+    
+    %goblin.RootBoneScale = 0.025;
+    %goblin.setRootBoneOffset(0, -5);
+    
+    %goblin.position = "-25 -8";
+    %goblin.SceneLayer = 29;
+    %goblin.setLinearVelocity(7.5, 0);
+        
+    %this.walker = %goblin;
+    
+    %this.resetSchedule = %this.schedule(8000, resetWalker);
+
+    // Add it to the scene
+    SandboxScene.add(%goblin);
+}
+
+//-----------------------------------------------------------------------------
+
+function SpineToy::createSpineBoy(%this)
+{
+    // Create the skeleton object
+    %object = new Skeleton()
+    {
+        class = "SpineBoy";
+    };
+
+    %object.Asset = "SpineToy:TestSkeleton";
+    %object.setAnimation("walk", true);
+    %object.RootBoneScale = 0.025;
+
+    %object.SceneLayer = 29;
+    %object.Position = 0;
+    %object.setMix("walk", "jump", 0.2);
+    %object.setMix("jump", "walk", 0.4);
+
+    SandboxScene.add(%object);
+
+    %object.schedule(4000, "doJump");
+}
+
+//-----------------------------------------------------------------------------
+
+function SpineBoy::onAnimationFinished(%this, %animationName)
+{
+    if (%animationName $= "jump")
+    {
+        %this.setAnimation("walk", true);
+        %this.schedule(4000, "doJump");
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+function SpineBoy::doJump(%this)
+{
+    %this.setAnimation("jump", false);
+}
+
+//-----------------------------------------------------------------------------
+
+function SpineToy::resetWalker(%this)
+{
+    %this.walker.setPosition("-25 -8");
+    %this.resetSchedule = %this.schedule(8000, resetWalker);
+}
+
+//-----------------------------------------------------------------------------
+
+function SpineToy::createBackground(%this)
+{
+    // Create the sprite.
+    %object = new Sprite();
+    
+    // Set the sprite as "static" so it is not affected by gravity.
+    %object.setBodyType( static );
+       
+    // Always try to configure a scene-object prior to adding it to a scene for best performance.
+
+    // Set the position.
+    %object.Position = "0 0";
+
+    // Set the size.        
+    %object.Size = "40 30";
+    
+    // Set to the furthest background layer.
+    %object.SceneLayer = 31;
+    
+    // Set an image.
+    %object.Image = "SpineToy:background";
+            
+    // Add the sprite to the scene.
+    SandboxScene.add( %object );
+    
+    %this.createPowerup(-14, 6);
+    %this.createPowerup(14, 6);
+    
+    // Create the skeleton object
+    %animatedMenu = new Skeleton();
+    
+    // Assign it an asset
+    %animatedMenu.Asset = "SpineToy:spinosaurus";
+
+    // Set properties    
+    %animatedMenu.setAnimation("Animation", false);
+    %duration = %animatedMenu.getAnimationDuration();
+    %animatedMenu.schedule(%duration*1000, "setAnimation", "loop", true);
+    %animatedMenu.position = "0 0";
+    %animatedMenu.SceneLayer = 30;
+    %animatedMenu.RootBoneScale = 0.025;
+    %animatedMenu.setRootBoneOffset(0, -10);
+    
+    // Add it to the scene
+    SandboxScene.add(%animatedMenu);
+}
+
+//-----------------------------------------------------------------------------
+
+function SpineToy::createPowerup(%this, %xPos, %yPos)
+{
+    // Create the skeleton object
+    %powerup = new Skeleton();
+    
+    // Assign it an asset
+    %powerup.Asset = "SpineToy:powerup";
+
+    // Set properties    
+    %powerup.setAnimation("Animation", true);    
+    %powerup.position = %xPos SPC %yPos;
+    %powerup.SceneLayer = 30;
+    
+    %powerup.RootBoneScale = 0.025;
+    %powerup.setRootBoneOffset(0, -5);
+    
+    // Add it to the scene
+    SandboxScene.add(%powerup);
+}
+
+//-----------------------------------------------------------------------------

+ 15 - 0
modules/SpineToy/1/module.taml

@@ -0,0 +1,15 @@
+<ModuleDefinition
+	ModuleId="SpineToy"
+	VersionId="1"
+	Description="Demonstrates creating a Spine sprite."
+	Dependencies="ToyAssets=1"
+	Type="toy"
+	ToyCategoryIndex="3"
+	ScriptFile="main.cs"
+	CreateFunction="create"
+	DestroyFunction="destroy">
+      <DeclaredAssets
+        Path="assets"
+        Extension="asset.taml"
+        Recurse="true"/>
+</ModuleDefinition>