소스 검색

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

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

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

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

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

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

@@ -94,8 +94,12 @@ void SpriteBatchItem::resetState( void )
     mLogicalPosition.resetState();
     mLogicalPosition.resetState();
 
 
     mVisible = true;
     mVisible = true;
+    mExplicitMode = false;
 
 
     mLocalPosition.SetZero();
     mLocalPosition.SetZero();
+    for (U32 i = 0; i < 4; i++)
+        mExplicitVerts[i].SetZero();
+
     mDepth = 0.0f;
     mDepth = 0.0f;
     mLocalAngle = 0.0f;
     mLocalAngle = 0.0f;
     setSize( Vector2( 1.0f, 1.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 )
 void SpriteBatchItem::updateLocalTransform( void )
 {
 {
     // Debug Profiling.
     // Debug Profiling.
@@ -248,10 +264,20 @@ void SpriteBatchItem::updateLocalTransform( void )
     const F32 halfHeight = mSize.y * 0.5f;
     const F32 halfHeight = mSize.y * 0.5f;
 
 
     // Set local size vertices.
     // 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.
     // Calculate local OOBB.
     CoreMath::mCalculateOOBB( mLocalOOBB, localTransform, mLocalOOBB );
     CoreMath::mCalculateOOBB( mLocalOOBB, localTransform, mLocalOOBB );
@@ -713,4 +739,4 @@ void SpriteBatchItem::WriteCustomTamlSchema( const AbstractClassRep* pClassRep,
     pBatchItemLogicalPosition->SetAttribute( "name", spriteLogicalPositionName );
     pBatchItemLogicalPosition->SetAttribute( "name", spriteLogicalPositionName );
     pBatchItemLogicalPosition->SetAttribute( "type", "xs:string" );
     pBatchItemLogicalPosition->SetAttribute( "type", "xs:string" );
     pBatchItemComplexTypeElement->LinkEndChild( pBatchItemLogicalPosition );
     pBatchItemComplexTypeElement->LinkEndChild( pBatchItemLogicalPosition );
-}
+}

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

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