Browse Source

Merge remote-tracking branch 'garagegames/development' into linuxsupport

Cameron Porter 11 years ago
parent
commit
f3b8842b99
100 changed files with 17598 additions and 3932 deletions
  1. BIN
      Leap.dll
  2. BIN
      Leapd.dll
  3. 16 9
      engine/compilers/VisualStudio 2010/Torque 2D.vcxproj
  4. 24 0
      engine/compilers/VisualStudio 2010/Torque 2D.vcxproj.filters
  5. 16 9
      engine/compilers/VisualStudio 2012/Torque 2D.vcxproj
  6. 24 0
      engine/compilers/VisualStudio 2012/Torque 2D.vcxproj.filters
  7. 253 27
      engine/compilers/Xcode/Torque2D.xcodeproj/project.pbxproj
  8. 162 0
      engine/compilers/Xcode_iOS/Torque2D.xcodeproj/project.pbxproj
  9. 4 0
      engine/compilers/android/jni/Android.mk
  10. 4566 0
      engine/lib/LeapSDK/include/Leap.h
  11. 1036 0
      engine/lib/LeapSDK/include/LeapMath.h
  12. BIN
      engine/lib/LeapSDK/lib/libc++/libLeap.dylib
  13. BIN
      engine/lib/LeapSDK/lib/x64/Leap.dll
  14. BIN
      engine/lib/LeapSDK/lib/x64/Leapd.dll
  15. BIN
      engine/lib/LeapSDK/lib/x64/msvcp100.dll
  16. BIN
      engine/lib/LeapSDK/lib/x64/msvcp100d.dll
  17. BIN
      engine/lib/LeapSDK/lib/x64/msvcr100.dll
  18. BIN
      engine/lib/LeapSDK/lib/x64/msvcr100d.dll
  19. BIN
      engine/lib/LeapSDK/lib/x86/Leap.dll
  20. BIN
      engine/lib/LeapSDK/lib/x86/Leap.lib
  21. BIN
      engine/lib/LeapSDK/lib/x86/Leapd.dll
  22. BIN
      engine/lib/LeapSDK/lib/x86/Leapd.lib
  23. BIN
      engine/lib/LeapSDK/lib/x86/msvcp100.dll
  24. BIN
      engine/lib/LeapSDK/lib/x86/msvcp100d.dll
  25. BIN
      engine/lib/LeapSDK/lib/x86/msvcr100.dll
  26. BIN
      engine/lib/LeapSDK/lib/x86/msvcr100d.dll
  27. 124 14
      engine/source/2d/assets/AnimationAsset.cc
  28. 32 16
      engine/source/2d/assets/AnimationAsset.h
  29. 131 0
      engine/source/2d/assets/AnimationAsset_ScriptBinding.h
  30. 290 140
      engine/source/2d/assets/ImageAsset.cc
  31. 38 5
      engine/source/2d/assets/ImageAsset.h
  32. 24 13
      engine/source/2d/assets/ImageAsset_ScriptBinding.h
  33. 100 2
      engine/source/2d/assets/ParticleAssetEmitter.cc
  34. 8 0
      engine/source/2d/assets/ParticleAssetEmitter.h
  35. 43 4
      engine/source/2d/assets/ParticleAssetEmitter_ScriptBinding.h
  36. 404 0
      engine/source/2d/assets/SkeletonAsset.cc
  37. 107 0
      engine/source/2d/assets/SkeletonAsset.h
  38. 87 0
      engine/source/2d/assets/SkeletonAsset_ScriptBinding.h
  39. 169 33
      engine/source/2d/core/ImageFrameProviderCore.cc
  40. 9 2
      engine/source/2d/core/ImageFrameProviderCore.h
  41. 1 0
      engine/source/2d/core/SpriteBase.cc
  42. 10 8
      engine/source/2d/core/SpriteBase.h
  43. 63 7
      engine/source/2d/core/SpriteBase_ScriptBinding.h
  44. 28 0
      engine/source/2d/core/SpriteBatch.cc
  45. 1 0
      engine/source/2d/core/SpriteBatch.h
  46. 31 5
      engine/source/2d/core/SpriteBatchItem.cc
  47. 7 0
      engine/source/2d/core/SpriteBatchItem.h
  48. 1 1
      engine/source/2d/gui/guiSpriteCtrl.cc
  49. 6 3
      engine/source/2d/scene/Scene.cc
  50. 6 2
      engine/source/2d/sceneobject/ParticlePlayer.cc
  51. 3 3
      engine/source/2d/sceneobject/SceneObject_ScriptBinding.h
  52. 458 0
      engine/source/2d/sceneobject/Skeleton.cc
  53. 148 0
      engine/source/2d/sceneobject/Skeleton.h
  54. 216 0
      engine/source/2d/sceneobject/Skeleton_ScriptBinding.h
  55. 1 1
      engine/source/collection/nameTags_ScriptBinding.h
  56. 1 1
      engine/source/gui/editor/guiDebugger.cc
  57. 7 7
      engine/source/gui/guiCanvas.cc
  58. 2292 1853
      engine/source/input/actionMap.cc
  59. 14 3
      engine/source/input/actionMap.h
  60. 176 0
      engine/source/input/leapMotion/LeapMotionManager_ScriptBinding.h
  61. 35 0
      engine/source/input/leapMotion/leapMotionConstants.h
  62. 674 0
      engine/source/input/leapMotion/leapMotionManager.cc
  63. 162 0
      engine/source/input/leapMotion/leapMotionManager.h
  64. 109 0
      engine/source/input/leapMotion/leapMotionUtil.cpp
  65. 48 0
      engine/source/input/leapMotion/leapMotionUtil.h
  66. 13 0
      engine/source/math/mMatrix.h
  67. 55 0
      engine/source/math/mPoint.cpp
  68. 45 0
      engine/source/math/mPoint.h
  69. 120 20
      engine/source/platform/event.h
  70. 123 0
      engine/source/platform/platformInput_ScriptBinding.h
  71. 4 4
      engine/source/platformOSX/osxTorqueView.mm
  72. 102 9
      engine/source/platformWin32/winDInputDevice.cc
  73. 9 1
      engine/source/platformWin32/winDInputDevice.h
  74. 426 1
      engine/source/platformWin32/winDirectInput.cc
  75. 50 0
      engine/source/platformWin32/winDirectInput.h
  76. 4 0
      engine/source/platformWin32/winInput.cc
  77. 5 5
      engine/source/platformWin32/winWindow.cc
  78. 1263 1263
      engine/source/platformiOS/iOSInput.mm
  79. 460 460
      engine/source/platformiOS/iOSMotionManager.mm
  80. 1 1
      engine/source/sim/simObject.cc
  81. 634 0
      engine/source/spine/Animation.c
  82. 249 0
      engine/source/spine/Animation.h
  83. 282 0
      engine/source/spine/AnimationState.c
  84. 116 0
      engine/source/spine/AnimationState.h
  85. 149 0
      engine/source/spine/AnimationStateData.c
  86. 66 0
      engine/source/spine/AnimationStateData.h
  87. 338 0
      engine/source/spine/Atlas.c
  88. 136 0
      engine/source/spine/Atlas.h
  89. 67 0
      engine/source/spine/AtlasAttachmentLoader.c
  90. 55 0
      engine/source/spine/AtlasAttachmentLoader.h
  91. 54 0
      engine/source/spine/Attachment.c
  92. 62 0
      engine/source/spine/Attachment.h
  93. 76 0
      engine/source/spine/AttachmentLoader.c
  94. 69 0
      engine/source/spine/AttachmentLoader.h
  95. 99 0
      engine/source/spine/Bone.c
  96. 75 0
      engine/source/spine/Bone.h
  97. 46 0
      engine/source/spine/BoneData.c
  98. 60 0
      engine/source/spine/BoneData.h
  99. 60 0
      engine/source/spine/BoundingBoxAttachment.c
  100. 60 0
      engine/source/spine/BoundingBoxAttachment.h

BIN
Leap.dll


BIN
Leapd.dll


+ 16 - 9
engine/compilers/VisualStudio 2010/Torque 2D.vcxproj

@@ -72,7 +72,7 @@
     </Midl>
     </Midl>
     <ClCompile>
     <ClCompile>
       <Optimization>Disabled</Optimization>
       <Optimization>Disabled</Optimization>
-      <AdditionalIncludeDirectories>../../Lib/zlib;../../Lib/lpng;../../Lib/ljpeg;../../Lib/openal/win32;../../Source;../../Source/persistence/rapidjson/include;../../source/testing/googleTest;../../source/testing/googleTest/include;../../source/testing/googleTest;../../source/testing/googleTest/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>../../Lib/LeapSDK/include;../../Lib/zlib;../../Lib/lpng;../../Lib/ljpeg;../../Lib/openal/win32;../../Source;../../Source/persistence/rapidjson/include;../../source/testing/googleTest;../../source/testing/googleTest/include;../../source/testing/googleTest;../../source/testing/googleTest/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>TORQUE_DEBUG;TORQUE_DEBUG_GUARD;_CRT_SECURE_NO_DEPRECATE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <PreprocessorDefinitions>TORQUE_DEBUG;TORQUE_DEBUG_GUARD;_CRT_SECURE_NO_DEPRECATE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
       <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
@@ -96,10 +96,10 @@
       <AdditionalIncludeDirectories>../../Lib/MSPlatformSDK/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <AdditionalIncludeDirectories>../../Lib/MSPlatformSDK/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ResourceCompile>
     </ResourceCompile>
     <Link>
     <Link>
-      <AdditionalDependencies>COMCTL32.LIB;COMDLG32.LIB;USER32.LIB;ADVAPI32.LIB;GDI32.LIB;RPCRT4.LIB;WINMM.LIB;WSOCK32.LIB;vfw32.lib;Imm32.lib;unicows.lib;shell32.lib;shlwapi.lib;ole32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>Leapd.lib;COMCTL32.LIB;COMDLG32.LIB;USER32.LIB;ADVAPI32.LIB;GDI32.LIB;RPCRT4.LIB;WINMM.LIB;WSOCK32.LIB;vfw32.lib;Imm32.lib;unicows.lib;shell32.lib;shlwapi.lib;ole32.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <OutputFile>../../../Torque2D_DEBUG.exe</OutputFile>
       <OutputFile>../../../Torque2D_DEBUG.exe</OutputFile>
       <SuppressStartupBanner>true</SuppressStartupBanner>
       <SuppressStartupBanner>true</SuppressStartupBanner>
-      <AdditionalLibraryDirectories>../../Lib/unicode;../../lib/MSPlatformSDK/Lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>../../lib/LeapSDK/lib/x86;../../Lib/unicode;../../lib/MSPlatformSDK/Lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <IgnoreSpecificDefaultLibraries>LIBC;LIBCD;LIBCMTD;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
       <IgnoreSpecificDefaultLibraries>LIBC;LIBCD;LIBCMTD;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <ProgramDatabaseFile>$(IntDir)$(ProjectName).pdb</ProgramDatabaseFile>
       <ProgramDatabaseFile>$(IntDir)$(ProjectName).pdb</ProgramDatabaseFile>
@@ -119,7 +119,7 @@
     <ClCompile>
     <ClCompile>
       <Optimization>MinSpace</Optimization>
       <Optimization>MinSpace</Optimization>
       <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
       <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
-      <AdditionalIncludeDirectories>../../Lib/zlib;../../Lib/lpng;../../Lib/ljpeg;../../Lib/openal/win32;../../Source;../../Source/persistence/rapidjson/include;../../source/testing/googleTest;../../source/testing/googleTest/include;../../source/testing/googleTest;../../source/testing/googleTest/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>../../Lib/LeapSDK/include;../../Lib/zlib;../../Lib/lpng;../../Lib/ljpeg;../../Lib/openal/win32;../../Source;../../Source/persistence/rapidjson/include;../../source/testing/googleTest;../../source/testing/googleTest/include;../../source/testing/googleTest;../../source/testing/googleTest/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>UNICODE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <PreprocessorDefinitions>UNICODE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
       <StringPooling>true</StringPooling>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
@@ -144,10 +144,10 @@
       <AdditionalIncludeDirectories>../../Lib/MSPlatformSDK/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <AdditionalIncludeDirectories>../../Lib/MSPlatformSDK/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ResourceCompile>
     </ResourceCompile>
     <Link>
     <Link>
-      <AdditionalDependencies>COMCTL32.LIB;COMDLG32.LIB;USER32.LIB;ADVAPI32.LIB;GDI32.LIB;WINMM.LIB;WSOCK32.LIB;vfw32.lib;Imm32.lib;unicows.lib;shell32.lib;shlwapi.lib;ole32.lib;RPCRT4.LIB;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>Leap.lib;COMCTL32.LIB;COMDLG32.LIB;USER32.LIB;ADVAPI32.LIB;GDI32.LIB;WINMM.LIB;WSOCK32.LIB;vfw32.lib;Imm32.lib;unicows.lib;shell32.lib;shlwapi.lib;ole32.lib;RPCRT4.LIB;%(AdditionalDependencies)</AdditionalDependencies>
       <OutputFile>../../../Torque2D.exe</OutputFile>
       <OutputFile>../../../Torque2D.exe</OutputFile>
       <SuppressStartupBanner>false</SuppressStartupBanner>
       <SuppressStartupBanner>false</SuppressStartupBanner>
-      <AdditionalLibraryDirectories>../../Lib/unicode;../../Lib/MSPlatformSDK/Lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>../../lib/LeapSDK/lib/x86;../../Lib/unicode;../../Lib/MSPlatformSDK/Lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <IgnoreSpecificDefaultLibraries>LIBC;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
       <IgnoreSpecificDefaultLibraries>LIBC;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
       <GenerateDebugInformation>false</GenerateDebugInformation>
       <GenerateDebugInformation>false</GenerateDebugInformation>
       <ProgramDatabaseFile>$(IntDir)$(ProjectName).pdb</ProgramDatabaseFile>
       <ProgramDatabaseFile>$(IntDir)$(ProjectName).pdb</ProgramDatabaseFile>
@@ -178,7 +178,7 @@
     <ClCompile>
     <ClCompile>
       <Optimization>MinSpace</Optimization>
       <Optimization>MinSpace</Optimization>
       <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
       <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
-      <AdditionalIncludeDirectories>../../Lib/zlib;../../Lib/lpng;../../Lib/ljpeg;../../Lib/openal/win32;../../Source;../../Source/persistence/rapidjson/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>../../Lib/LeapSDK/include;../../Lib/zlib;../../Lib/lpng;../../Lib/ljpeg;../../Lib/openal/win32;../../Source;../../Source/persistence/rapidjson/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>TORQUE_SHIPPING;UNICODE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <PreprocessorDefinitions>TORQUE_SHIPPING;UNICODE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
       <StringPooling>true</StringPooling>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
@@ -203,10 +203,10 @@
       <AdditionalIncludeDirectories>../../Lib/MSPlatformSDK/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <AdditionalIncludeDirectories>../../Lib/MSPlatformSDK/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ResourceCompile>
     </ResourceCompile>
     <Link>
     <Link>
-      <AdditionalDependencies>COMCTL32.LIB;COMDLG32.LIB;USER32.LIB;ADVAPI32.LIB;GDI32.LIB;WINMM.LIB;WSOCK32.LIB;vfw32.lib;Imm32.lib;unicows.lib;shell32.lib;shlwapi.lib;ole32.lib;RPCRT4.LIB;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>Leap.lib;COMCTL32.LIB;COMDLG32.LIB;USER32.LIB;ADVAPI32.LIB;GDI32.LIB;WINMM.LIB;WSOCK32.LIB;vfw32.lib;Imm32.lib;unicows.lib;shell32.lib;shlwapi.lib;ole32.lib;RPCRT4.LIB;%(AdditionalDependencies)</AdditionalDependencies>
       <OutputFile>../../../Torque2D.exe</OutputFile>
       <OutputFile>../../../Torque2D.exe</OutputFile>
       <SuppressStartupBanner>false</SuppressStartupBanner>
       <SuppressStartupBanner>false</SuppressStartupBanner>
-      <AdditionalLibraryDirectories>../../Lib/unicode;../../Lib/MSPlatformSDK/Lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>../../lib/LeapSDK/lib/x86;../../Lib/unicode;../../Lib/MSPlatformSDK/Lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <IgnoreSpecificDefaultLibraries>LIBC;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
       <IgnoreSpecificDefaultLibraries>LIBC;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
       <GenerateDebugInformation>false</GenerateDebugInformation>
       <GenerateDebugInformation>false</GenerateDebugInformation>
       <ProgramDatabaseFile>$(IntDir)$(ProjectName).pdb</ProgramDatabaseFile>
       <ProgramDatabaseFile>$(IntDir)$(ProjectName).pdb</ProgramDatabaseFile>
@@ -404,6 +404,8 @@
     <ClCompile Include="..\..\source\gui\language\lang.cc" />
     <ClCompile Include="..\..\source\gui\language\lang.cc" />
     <ClCompile Include="..\..\source\gui\messageVector.cc" />
     <ClCompile Include="..\..\source\gui\messageVector.cc" />
     <ClCompile Include="..\..\source\input\actionMap.cc" />
     <ClCompile Include="..\..\source\input\actionMap.cc" />
+    <ClCompile Include="..\..\source\input\leapMotion\leapMotionManager.cc" />
+    <ClCompile Include="..\..\source\input\leapMotion\leapMotionUtil.cpp" />
     <ClCompile Include="..\..\source\io\bitStream.cc" />
     <ClCompile Include="..\..\source\io\bitStream.cc" />
     <ClCompile Include="..\..\source\io\bufferStream.cc" />
     <ClCompile Include="..\..\source\io\bufferStream.cc" />
     <ClCompile Include="..\..\source\io\fileObject.cc" />
     <ClCompile Include="..\..\source\io\fileObject.cc" />
@@ -429,6 +431,7 @@
     <ClCompile Include="..\..\source\io\zip\zipSubStream.cc" />
     <ClCompile Include="..\..\source\io\zip\zipSubStream.cc" />
     <ClCompile Include="..\..\source\io\zip\zipTempStream.cc" />
     <ClCompile Include="..\..\source\io\zip\zipTempStream.cc" />
     <ClCompile Include="..\..\source\math\math_ScriptBinding.cc" />
     <ClCompile Include="..\..\source\math\math_ScriptBinding.cc" />
+    <ClCompile Include="..\..\source\math\mPoint.cpp" />
     <ClCompile Include="..\..\source\math\rectClipper.cpp" />
     <ClCompile Include="..\..\source\math\rectClipper.cpp" />
     <ClCompile Include="..\..\source\memory\dataChunker.cc" />
     <ClCompile Include="..\..\source\memory\dataChunker.cc" />
     <ClCompile Include="..\..\source\memory\frameAllocator_ScriptBinding.cc" />
     <ClCompile Include="..\..\source\memory\frameAllocator_ScriptBinding.cc" />
@@ -878,6 +881,10 @@
     <ClInclude Include="..\..\source\gui\messageVector.h" />
     <ClInclude Include="..\..\source\gui\messageVector.h" />
     <ClInclude Include="..\..\source\input\actionMap.h" />
     <ClInclude Include="..\..\source\input\actionMap.h" />
     <ClInclude Include="..\..\source\input\actionMap_ScriptBinding.h" />
     <ClInclude Include="..\..\source\input\actionMap_ScriptBinding.h" />
+    <ClInclude Include="..\..\source\input\leapMotion\leapMotionConstants.h" />
+    <ClInclude Include="..\..\source\input\leapMotion\leapMotionManager.h" />
+    <ClInclude Include="..\..\source\input\leapMotion\LeapMotionManager_ScriptBinding.h" />
+    <ClInclude Include="..\..\source\input\leapMotion\leapMotionUtil.h" />
     <ClInclude Include="..\..\source\io\bitStream.h" />
     <ClInclude Include="..\..\source\io\bitStream.h" />
     <ClInclude Include="..\..\source\io\bufferStream.h" />
     <ClInclude Include="..\..\source\io\bufferStream.h" />
     <ClInclude Include="..\..\source\io\fileObject.h" />
     <ClInclude Include="..\..\source\io\fileObject.h" />

+ 24 - 0
engine/compilers/VisualStudio 2010/Torque 2D.vcxproj.filters

@@ -193,6 +193,9 @@
     <Filter Include="persistence\taml\xml">
     <Filter Include="persistence\taml\xml">
       <UniqueIdentifier>{e8f11d4b-6a54-4467-ae13-794ed56063f9}</UniqueIdentifier>
       <UniqueIdentifier>{e8f11d4b-6a54-4467-ae13-794ed56063f9}</UniqueIdentifier>
     </Filter>
     </Filter>
+    <Filter Include="input\leapMotion">
+      <UniqueIdentifier>{aa5d5692-e20a-4608-b3ee-390b3089fcb2}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\source\audio\audio.cc">
     <ClCompile Include="..\..\source\audio\audio.cc">
@@ -1338,6 +1341,15 @@
     <ClCompile Include="..\..\source\console\Package.cc">
     <ClCompile Include="..\..\source\console\Package.cc">
       <Filter>console</Filter>
       <Filter>console</Filter>
     </ClCompile>
     </ClCompile>
+    <ClCompile Include="..\..\source\input\leapMotion\leapMotionManager.cc">
+      <Filter>input\leapMotion</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\source\input\leapMotion\leapMotionUtil.cpp">
+      <Filter>input\leapMotion</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\source\math\mPoint.cpp">
+      <Filter>math</Filter>
+    </ClCompile>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\source\audio\audio.h">
     <ClInclude Include="..\..\source\audio\audio.h">
@@ -2979,6 +2991,18 @@
     <ClInclude Include="..\..\source\console\Package.h">
     <ClInclude Include="..\..\source\console\Package.h">
       <Filter>console</Filter>
       <Filter>console</Filter>
     </ClInclude>
     </ClInclude>
+    <ClInclude Include="..\..\source\input\leapMotion\leapMotionConstants.h">
+      <Filter>input\leapMotion</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\input\leapMotion\leapMotionManager.h">
+      <Filter>input\leapMotion</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\input\leapMotion\LeapMotionManager_ScriptBinding.h">
+      <Filter>input\leapMotion</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\input\leapMotion\leapMotionUtil.h">
+      <Filter>input\leapMotion</Filter>
+    </ClInclude>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <CustomBuild Include="..\..\source\math\mMath_ASM.asm">
     <CustomBuild Include="..\..\source\math\mMath_ASM.asm">

+ 16 - 9
engine/compilers/VisualStudio 2012/Torque 2D.vcxproj

@@ -75,7 +75,7 @@
     </Midl>
     </Midl>
     <ClCompile>
     <ClCompile>
       <Optimization>Disabled</Optimization>
       <Optimization>Disabled</Optimization>
-      <AdditionalIncludeDirectories>../../Lib/zlib;../../Lib/lpng;../../Lib/ljpeg;../../Lib/openal/win32;../../Source;../../Source/persistence/rapidjson/include;../../Source/persistence/libjson;../../source/testing/googleTest;../../source/testing/googleTest/include;../../source/testing/googleTest;../../source/testing/googleTest/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>../../Lib/LeapSDK/include;../../Lib/zlib;../../Lib/lpng;../../Lib/ljpeg;../../Lib/openal/win32;../../Source;../../Source/persistence/rapidjson/include;../../Source/persistence/libjson;../../source/testing/googleTest;../../source/testing/googleTest/include;../../source/testing/googleTest;../../source/testing/googleTest/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>TORQUE_DEBUG;TORQUE_DEBUG_GUARD;_CRT_SECURE_NO_DEPRECATE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <PreprocessorDefinitions>TORQUE_DEBUG;TORQUE_DEBUG_GUARD;_CRT_SECURE_NO_DEPRECATE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
       <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
@@ -100,10 +100,10 @@
       <AdditionalIncludeDirectories>../../Lib/MSPlatformSDK/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <AdditionalIncludeDirectories>../../Lib/MSPlatformSDK/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ResourceCompile>
     </ResourceCompile>
     <Link>
     <Link>
-      <AdditionalDependencies>COMCTL32.LIB;COMDLG32.LIB;USER32.LIB;ADVAPI32.LIB;GDI32.LIB;RPCRT4.LIB;WINMM.LIB;WSOCK32.LIB;vfw32.lib;Imm32.lib;unicows.lib;shell32.lib;shlwapi.lib;ole32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>Leapd.lib;COMCTL32.LIB;COMDLG32.LIB;USER32.LIB;ADVAPI32.LIB;GDI32.LIB;RPCRT4.LIB;WINMM.LIB;WSOCK32.LIB;vfw32.lib;Imm32.lib;unicows.lib;shell32.lib;shlwapi.lib;ole32.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <OutputFile>../../../Torque2D_DEBUG.exe</OutputFile>
       <OutputFile>../../../Torque2D_DEBUG.exe</OutputFile>
       <SuppressStartupBanner>true</SuppressStartupBanner>
       <SuppressStartupBanner>true</SuppressStartupBanner>
-      <AdditionalLibraryDirectories>../../Lib/unicode;../../lib/MSPlatformSDK/Lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>../../lib/LeapSDK/lib/x86;../../Lib/unicode;../../lib/MSPlatformSDK/Lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <IgnoreSpecificDefaultLibraries>LIBC;LIBCD;LIBCMTD;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
       <IgnoreSpecificDefaultLibraries>LIBC;LIBCD;LIBCMTD;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <ProgramDatabaseFile>$(IntDir)$(ProjectName).pdb</ProgramDatabaseFile>
       <ProgramDatabaseFile>$(IntDir)$(ProjectName).pdb</ProgramDatabaseFile>
@@ -124,7 +124,7 @@
     <ClCompile>
     <ClCompile>
       <Optimization>MinSpace</Optimization>
       <Optimization>MinSpace</Optimization>
       <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
       <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
-      <AdditionalIncludeDirectories>../../Lib/zlib;../../Lib/lpng;../../Lib/ljpeg;../../Lib/openal/win32;../../Source;../../Source/persistence/rapidjson/include;../../Source/persistence/libjson;../../source/testing/googleTest;../../source/testing/googleTest/include;../../source/testing/googleTest;../../source/testing/googleTest/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>../../Lib/LeapSDK/include;../../Lib/zlib;../../Lib/lpng;../../Lib/ljpeg;../../Lib/openal/win32;../../Source;../../Source/persistence/rapidjson/include;../../Source/persistence/libjson;../../source/testing/googleTest;../../source/testing/googleTest/include;../../source/testing/googleTest;../../source/testing/googleTest/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>UNICODE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <PreprocessorDefinitions>UNICODE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
       <StringPooling>true</StringPooling>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
@@ -149,10 +149,10 @@
       <AdditionalIncludeDirectories>../../Lib/MSPlatformSDK/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <AdditionalIncludeDirectories>../../Lib/MSPlatformSDK/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ResourceCompile>
     </ResourceCompile>
     <Link>
     <Link>
-      <AdditionalDependencies>COMCTL32.LIB;COMDLG32.LIB;USER32.LIB;ADVAPI32.LIB;GDI32.LIB;WINMM.LIB;WSOCK32.LIB;vfw32.lib;Imm32.lib;unicows.lib;shell32.lib;shlwapi.lib;ole32.lib;RPCRT4.LIB;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>Leap.lib;COMCTL32.LIB;COMDLG32.LIB;USER32.LIB;ADVAPI32.LIB;GDI32.LIB;WINMM.LIB;WSOCK32.LIB;vfw32.lib;Imm32.lib;unicows.lib;shell32.lib;shlwapi.lib;ole32.lib;RPCRT4.LIB;%(AdditionalDependencies)</AdditionalDependencies>
       <OutputFile>../../../Torque2D.exe</OutputFile>
       <OutputFile>../../../Torque2D.exe</OutputFile>
       <SuppressStartupBanner>false</SuppressStartupBanner>
       <SuppressStartupBanner>false</SuppressStartupBanner>
-      <AdditionalLibraryDirectories>../../Lib/unicode;../../Lib/MSPlatformSDK/Lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>../../lib/LeapSDK/lib/x86;../../Lib/unicode;../../Lib/MSPlatformSDK/Lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <IgnoreSpecificDefaultLibraries>LIBC;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
       <IgnoreSpecificDefaultLibraries>LIBC;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
       <GenerateDebugInformation>false</GenerateDebugInformation>
       <GenerateDebugInformation>false</GenerateDebugInformation>
       <ProgramDatabaseFile>$(IntDir)$(ProjectName).pdb</ProgramDatabaseFile>
       <ProgramDatabaseFile>$(IntDir)$(ProjectName).pdb</ProgramDatabaseFile>
@@ -184,7 +184,7 @@
     <ClCompile>
     <ClCompile>
       <Optimization>MinSpace</Optimization>
       <Optimization>MinSpace</Optimization>
       <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
       <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
-      <AdditionalIncludeDirectories>../../Lib/zlib;../../Lib/lpng;../../Lib/ljpeg;../../Lib/openal/win32;../../Source;../../Source/persistence/rapidjson/include;../../Source/persistence/libjson;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>../../Lib/LeapSDK/include;../../Lib/zlib;../../Lib/lpng;../../Lib/ljpeg;../../Lib/openal/win32;../../Source;../../Source/persistence/rapidjson/include;../../Source/persistence/libjson;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions>TORQUE_SHIPPING;UNICODE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <PreprocessorDefinitions>TORQUE_SHIPPING;UNICODE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
       <StringPooling>true</StringPooling>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
@@ -209,10 +209,10 @@
       <AdditionalIncludeDirectories>../../Lib/MSPlatformSDK/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <AdditionalIncludeDirectories>../../Lib/MSPlatformSDK/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ResourceCompile>
     </ResourceCompile>
     <Link>
     <Link>
-      <AdditionalDependencies>COMCTL32.LIB;COMDLG32.LIB;USER32.LIB;ADVAPI32.LIB;GDI32.LIB;WINMM.LIB;WSOCK32.LIB;vfw32.lib;Imm32.lib;unicows.lib;shell32.lib;shlwapi.lib;ole32.lib;RPCRT4.LIB;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>Leap.lib;COMCTL32.LIB;COMDLG32.LIB;USER32.LIB;ADVAPI32.LIB;GDI32.LIB;WINMM.LIB;WSOCK32.LIB;vfw32.lib;Imm32.lib;unicows.lib;shell32.lib;shlwapi.lib;ole32.lib;RPCRT4.LIB;%(AdditionalDependencies)</AdditionalDependencies>
       <OutputFile>../../../Torque2D.exe</OutputFile>
       <OutputFile>../../../Torque2D.exe</OutputFile>
       <SuppressStartupBanner>false</SuppressStartupBanner>
       <SuppressStartupBanner>false</SuppressStartupBanner>
-      <AdditionalLibraryDirectories>../../Lib/unicode;../../Lib/MSPlatformSDK/Lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>../../lib/LeapSDK/lib/x86;../../Lib/unicode;../../Lib/MSPlatformSDK/Lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <IgnoreSpecificDefaultLibraries>LIBC;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
       <IgnoreSpecificDefaultLibraries>LIBC;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
       <GenerateDebugInformation>false</GenerateDebugInformation>
       <GenerateDebugInformation>false</GenerateDebugInformation>
       <ProgramDatabaseFile>$(IntDir)$(ProjectName).pdb</ProgramDatabaseFile>
       <ProgramDatabaseFile>$(IntDir)$(ProjectName).pdb</ProgramDatabaseFile>
@@ -411,6 +411,8 @@
     <ClCompile Include="..\..\source\gui\language\lang.cc" />
     <ClCompile Include="..\..\source\gui\language\lang.cc" />
     <ClCompile Include="..\..\source\gui\messageVector.cc" />
     <ClCompile Include="..\..\source\gui\messageVector.cc" />
     <ClCompile Include="..\..\source\input\actionMap.cc" />
     <ClCompile Include="..\..\source\input\actionMap.cc" />
+    <ClCompile Include="..\..\source\input\leapMotion\leapMotionManager.cc" />
+    <ClCompile Include="..\..\source\input\leapMotion\leapMotionUtil.cpp" />
     <ClCompile Include="..\..\source\io\bitStream.cc" />
     <ClCompile Include="..\..\source\io\bitStream.cc" />
     <ClCompile Include="..\..\source\io\bufferStream.cc" />
     <ClCompile Include="..\..\source\io\bufferStream.cc" />
     <ClCompile Include="..\..\source\io\fileObject.cc" />
     <ClCompile Include="..\..\source\io\fileObject.cc" />
@@ -436,6 +438,7 @@
     <ClCompile Include="..\..\source\io\zip\zipSubStream.cc" />
     <ClCompile Include="..\..\source\io\zip\zipSubStream.cc" />
     <ClCompile Include="..\..\source\io\zip\zipTempStream.cc" />
     <ClCompile Include="..\..\source\io\zip\zipTempStream.cc" />
     <ClCompile Include="..\..\source\math\math_ScriptBinding.cc" />
     <ClCompile Include="..\..\source\math\math_ScriptBinding.cc" />
+    <ClCompile Include="..\..\source\math\mPoint.cpp" />
     <ClCompile Include="..\..\source\math\rectClipper.cpp" />
     <ClCompile Include="..\..\source\math\rectClipper.cpp" />
     <ClCompile Include="..\..\source\memory\dataChunker.cc" />
     <ClCompile Include="..\..\source\memory\dataChunker.cc" />
     <ClCompile Include="..\..\source\memory\frameAllocator_ScriptBinding.cc" />
     <ClCompile Include="..\..\source\memory\frameAllocator_ScriptBinding.cc" />
@@ -885,6 +888,10 @@
     <ClInclude Include="..\..\source\gui\messageVector.h" />
     <ClInclude Include="..\..\source\gui\messageVector.h" />
     <ClInclude Include="..\..\source\input\actionMap.h" />
     <ClInclude Include="..\..\source\input\actionMap.h" />
     <ClInclude Include="..\..\source\input\actionMap_ScriptBinding.h" />
     <ClInclude Include="..\..\source\input\actionMap_ScriptBinding.h" />
+    <ClInclude Include="..\..\source\input\leapMotion\leapMotionConstants.h" />
+    <ClInclude Include="..\..\source\input\leapMotion\leapMotionManager.h" />
+    <ClInclude Include="..\..\source\input\leapMotion\LeapMotionManager_ScriptBinding.h" />
+    <ClInclude Include="..\..\source\input\leapMotion\leapMotionUtil.h" />
     <ClInclude Include="..\..\source\io\bitStream.h" />
     <ClInclude Include="..\..\source\io\bitStream.h" />
     <ClInclude Include="..\..\source\io\bufferStream.h" />
     <ClInclude Include="..\..\source\io\bufferStream.h" />
     <ClInclude Include="..\..\source\io\fileObject.h" />
     <ClInclude Include="..\..\source\io\fileObject.h" />

+ 24 - 0
engine/compilers/VisualStudio 2012/Torque 2D.vcxproj.filters

@@ -193,6 +193,9 @@
     <Filter Include="persistence\taml\xml">
     <Filter Include="persistence\taml\xml">
       <UniqueIdentifier>{cc1c1416-376b-4686-a4ac-21d1a35c9390}</UniqueIdentifier>
       <UniqueIdentifier>{cc1c1416-376b-4686-a4ac-21d1a35c9390}</UniqueIdentifier>
     </Filter>
     </Filter>
+    <Filter Include="input\leapMotion">
+      <UniqueIdentifier>{addcb993-5059-42bf-a1ea-871f700c67c3}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\source\audio\audio.cc">
     <ClCompile Include="..\..\source\audio\audio.cc">
@@ -1338,6 +1341,15 @@
     <ClCompile Include="..\..\source\console\Package.cc">
     <ClCompile Include="..\..\source\console\Package.cc">
       <Filter>console</Filter>
       <Filter>console</Filter>
     </ClCompile>
     </ClCompile>
+    <ClCompile Include="..\..\source\input\leapMotion\leapMotionManager.cc">
+      <Filter>input\leapMotion</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\source\input\leapMotion\leapMotionUtil.cpp">
+      <Filter>input\leapMotion</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\source\math\mPoint.cpp">
+      <Filter>math</Filter>
+    </ClCompile>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\source\audio\audio.h">
     <ClInclude Include="..\..\source\audio\audio.h">
@@ -2979,6 +2991,18 @@
     <ClInclude Include="..\..\source\console\Package.h">
     <ClInclude Include="..\..\source\console\Package.h">
       <Filter>console</Filter>
       <Filter>console</Filter>
     </ClInclude>
     </ClInclude>
+    <ClInclude Include="..\..\source\input\leapMotion\leapMotionConstants.h">
+      <Filter>input\leapMotion</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\input\leapMotion\leapMotionManager.h">
+      <Filter>input\leapMotion</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\input\leapMotion\LeapMotionManager_ScriptBinding.h">
+      <Filter>input\leapMotion</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\source\input\leapMotion\leapMotionUtil.h">
+      <Filter>input\leapMotion</Filter>
+    </ClInclude>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <CustomBuild Include="..\..\source\math\mMath_ASM.asm">
     <CustomBuild Include="..\..\source\math\mMath_ASM.asm">

+ 253 - 27
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 */; };
@@ -48,6 +72,10 @@
 		8609FE361655716E004662ED /* osxPopupMenu.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8609FE351655716E004662ED /* osxPopupMenu.mm */; };
 		8609FE361655716E004662ED /* osxPopupMenu.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8609FE351655716E004662ED /* osxPopupMenu.mm */; };
 		8609FE38165572EC004662ED /* osxFont.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8609FE37165572EC004662ED /* osxFont.mm */; };
 		8609FE38165572EC004662ED /* osxFont.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8609FE37165572EC004662ED /* osxFont.mm */; };
 		861CD8D01678F6C200DAE1A0 /* fileDialog.cc in Sources */ = {isa = PBXBuildFile; fileRef = 861CD8CF1678F6C200DAE1A0 /* fileDialog.cc */; };
 		861CD8D01678F6C200DAE1A0 /* fileDialog.cc in Sources */ = {isa = PBXBuildFile; fileRef = 861CD8CF1678F6C200DAE1A0 /* fileDialog.cc */; };
+		862BBACD18872BB400E3233A /* libLeap.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 867492F4188727FF00CF0136 /* libLeap.dylib */; };
+		8645C96C1887231C004ED987 /* mPoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8645C96B1887231C004ED987 /* mPoint.cpp */; };
+		8645C97418872339004ED987 /* leapMotionManager.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8645C96F18872339004ED987 /* leapMotionManager.cc */; };
+		8645C97518872339004ED987 /* leapMotionUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8645C97218872339004ED987 /* leapMotionUtil.cpp */; };
 		8652C279165586520052D0CB /* osxAudio.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8652C278165586520052D0CB /* osxAudio.mm */; };
 		8652C279165586520052D0CB /* osxAudio.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8652C278165586520052D0CB /* osxAudio.mm */; };
 		8652F2A016C146CF00639EFE /* torque2d.icns in Resources */ = {isa = PBXBuildFile; fileRef = 8652F29F16C146CF00639EFE /* torque2d.icns */; };
 		8652F2A016C146CF00639EFE /* torque2d.icns in Resources */ = {isa = PBXBuildFile; fileRef = 8652F29F16C146CF00639EFE /* torque2d.icns */; };
 		8658B174165A7BFB0087ABC1 /* osxCPU.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8658B171165A7BFB0087ABC1 /* osxCPU.mm */; };
 		8658B174165A7BFB0087ABC1 /* osxCPU.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8658B171165A7BFB0087ABC1 /* osxCPU.mm */; };
@@ -176,6 +204,7 @@
 		866381DC165556AD00C8C551 /* osxMath.mm in Sources */ = {isa = PBXBuildFile; fileRef = 866381DB165556AD00C8C551 /* osxMath.mm */; };
 		866381DC165556AD00C8C551 /* osxMath.mm in Sources */ = {isa = PBXBuildFile; fileRef = 866381DB165556AD00C8C551 /* osxMath.mm */; };
 		866381E51655615200C8C551 /* osxInput.mm in Sources */ = {isa = PBXBuildFile; fileRef = 866381E41655615200C8C551 /* osxInput.mm */; };
 		866381E51655615200C8C551 /* osxInput.mm in Sources */ = {isa = PBXBuildFile; fileRef = 866381E41655615200C8C551 /* osxInput.mm */; };
 		866381E91655674B00C8C551 /* osxTime.mm in Sources */ = {isa = PBXBuildFile; fileRef = 866381E81655674B00C8C551 /* osxTime.mm */; };
 		866381E91655674B00C8C551 /* osxTime.mm in Sources */ = {isa = PBXBuildFile; fileRef = 866381E81655674B00C8C551 /* osxTime.mm */; };
+		867492F5188727FF00CF0136 /* libLeap.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 867492F4188727FF00CF0136 /* libLeap.dylib */; };
 		86854E341663AAE6009FAFB2 /* osxOpenGLDevice.mm in Sources */ = {isa = PBXBuildFile; fileRef = 86854E331663AAE6009FAFB2 /* osxOpenGLDevice.mm */; };
 		86854E341663AAE6009FAFB2 /* osxOpenGLDevice.mm in Sources */ = {isa = PBXBuildFile; fileRef = 86854E331663AAE6009FAFB2 /* osxOpenGLDevice.mm */; };
 		8694ADC81656B06B0080ABAC /* osxEvents.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8694ADC51656B06B0080ABAC /* osxEvents.mm */; };
 		8694ADC81656B06B0080ABAC /* osxEvents.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8694ADC51656B06B0080ABAC /* osxEvents.mm */; };
 		8694ADC91656B06B0080ABAC /* osxWindow.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8694ADC61656B06B0080ABAC /* osxWindow.mm */; };
 		8694ADC91656B06B0080ABAC /* osxWindow.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8694ADC61656B06B0080ABAC /* osxWindow.mm */; };
@@ -453,8 +482,71 @@
 		B350D172174EF91900033EBB /* audio_ScriptBinding.cc in Sources */ = {isa = PBXBuildFile; fileRef = B350D171174EF91900033EBB /* audio_ScriptBinding.cc */; };
 		B350D172174EF91900033EBB /* audio_ScriptBinding.cc in Sources */ = {isa = PBXBuildFile; fileRef = B350D171174EF91900033EBB /* audio_ScriptBinding.cc */; };
 /* End PBXBuildFile section */
 /* End PBXBuildFile section */
 
 
+/* Begin PBXCopyFilesBuildPhase section */
+		8645C978188723B7004ED987 /* CopyFiles */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = "";
+			dstSubfolderSpec = 6;
+			files = (
+				862BBACD18872BB400E3233A /* libLeap.dylib in CopyFiles */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* 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>"; };
@@ -564,6 +656,13 @@
 		8609FE37165572EC004662ED /* osxFont.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 1; path = osxFont.mm; sourceTree = "<group>"; };
 		8609FE37165572EC004662ED /* osxFont.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 1; path = osxFont.mm; sourceTree = "<group>"; };
 		861CD8CE1678F6C200DAE1A0 /* fileDialog_ScriptBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fileDialog_ScriptBinding.h; sourceTree = "<group>"; };
 		861CD8CE1678F6C200DAE1A0 /* fileDialog_ScriptBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fileDialog_ScriptBinding.h; sourceTree = "<group>"; };
 		861CD8CF1678F6C200DAE1A0 /* fileDialog.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fileDialog.cc; sourceTree = "<group>"; };
 		861CD8CF1678F6C200DAE1A0 /* fileDialog.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fileDialog.cc; sourceTree = "<group>"; };
+		8645C96B1887231C004ED987 /* mPoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mPoint.cpp; sourceTree = "<group>"; };
+		8645C96E18872339004ED987 /* leapMotionConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = leapMotionConstants.h; sourceTree = "<group>"; };
+		8645C96F18872339004ED987 /* leapMotionManager.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = leapMotionManager.cc; sourceTree = "<group>"; };
+		8645C97018872339004ED987 /* leapMotionManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = leapMotionManager.h; sourceTree = "<group>"; };
+		8645C97118872339004ED987 /* LeapMotionManager_ScriptBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LeapMotionManager_ScriptBinding.h; sourceTree = "<group>"; };
+		8645C97218872339004ED987 /* leapMotionUtil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = leapMotionUtil.cpp; sourceTree = "<group>"; };
+		8645C97318872339004ED987 /* leapMotionUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = leapMotionUtil.h; sourceTree = "<group>"; };
 		864ECFEA1652795700012416 /* platform_ScriptBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = platform_ScriptBinding.h; sourceTree = "<group>"; };
 		864ECFEA1652795700012416 /* platform_ScriptBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = platform_ScriptBinding.h; sourceTree = "<group>"; };
 		864ECFED165279E100012416 /* networkProcessList.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = networkProcessList.cc; sourceTree = "<group>"; };
 		864ECFED165279E100012416 /* networkProcessList.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = networkProcessList.cc; sourceTree = "<group>"; };
 		864ECFEE165279E100012416 /* networkProcessList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = networkProcessList.h; sourceTree = "<group>"; };
 		864ECFEE165279E100012416 /* networkProcessList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = networkProcessList.h; sourceTree = "<group>"; };
@@ -744,6 +843,7 @@
 		866381DB165556AD00C8C551 /* osxMath.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = osxMath.mm; sourceTree = "<group>"; };
 		866381DB165556AD00C8C551 /* osxMath.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = osxMath.mm; sourceTree = "<group>"; };
 		866381E41655615200C8C551 /* osxInput.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = osxInput.mm; sourceTree = "<group>"; };
 		866381E41655615200C8C551 /* osxInput.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = osxInput.mm; sourceTree = "<group>"; };
 		866381E81655674B00C8C551 /* osxTime.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = osxTime.mm; sourceTree = "<group>"; };
 		866381E81655674B00C8C551 /* osxTime.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = osxTime.mm; sourceTree = "<group>"; };
+		867492F4188727FF00CF0136 /* libLeap.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libLeap.dylib; path = "../../lib/LeapSDK/lib/libc++/libLeap.dylib"; sourceTree = "<group>"; };
 		86854E321663AAE6009FAFB2 /* osxOpenGLDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = osxOpenGLDevice.h; sourceTree = "<group>"; };
 		86854E321663AAE6009FAFB2 /* osxOpenGLDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = osxOpenGLDevice.h; sourceTree = "<group>"; };
 		86854E331663AAE6009FAFB2 /* osxOpenGLDevice.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = osxOpenGLDevice.mm; sourceTree = "<group>"; };
 		86854E331663AAE6009FAFB2 /* osxOpenGLDevice.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = osxOpenGLDevice.mm; sourceTree = "<group>"; };
 		8694ADC41656B06B0080ABAC /* osxEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = osxEvents.h; sourceTree = "<group>"; };
 		8694ADC41656B06B0080ABAC /* osxEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = osxEvents.h; sourceTree = "<group>"; };
@@ -1437,6 +1537,7 @@
 			isa = PBXFrameworksBuildPhase;
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			buildActionMask = 2147483647;
 			files = (
 			files = (
+				867492F5188727FF00CF0136 /* libLeap.dylib in Frameworks */,
 				865A20CA16515B1E00527C44 /* AppKit.framework in Frameworks */,
 				865A20CA16515B1E00527C44 /* AppKit.framework in Frameworks */,
 				865A20CC16515B1E00527C44 /* Cocoa.framework in Frameworks */,
 				865A20CC16515B1E00527C44 /* Cocoa.framework in Frameworks */,
 				865A20CD16515B1E00527C44 /* CoreData.framework in Frameworks */,
 				865A20CD16515B1E00527C44 /* CoreData.framework in Frameworks */,
@@ -1450,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 = (
@@ -1586,6 +1739,19 @@
 			name = composites;
 			name = composites;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
 		};
 		};
+		8645C96D18872339004ED987 /* leapMotion */ = {
+			isa = PBXGroup;
+			children = (
+				8645C96E18872339004ED987 /* leapMotionConstants.h */,
+				8645C96F18872339004ED987 /* leapMotionManager.cc */,
+				8645C97018872339004ED987 /* leapMotionManager.h */,
+				8645C97118872339004ED987 /* LeapMotionManager_ScriptBinding.h */,
+				8645C97218872339004ED987 /* leapMotionUtil.cpp */,
+				8645C97318872339004ED987 /* leapMotionUtil.h */,
+			);
+			path = leapMotion;
+			sourceTree = "<group>";
+		};
 		865A20BD1651589F00527C44 /* Resources */ = {
 		865A20BD1651589F00527C44 /* Resources */ = {
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
@@ -1901,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 */,
@@ -1924,6 +2089,7 @@
 		869FF8BB1651518C002FE082 /* Frameworks */ = {
 		869FF8BB1651518C002FE082 /* Frameworks */ = {
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
+				867492F4188727FF00CF0136 /* libLeap.dylib */,
 				865A20C916515AEF00527C44 /* System */,
 				865A20C916515AEF00527C44 /* System */,
 				869FF8BE1651518C002FE082 /* Other Frameworks */,
 				869FF8BE1651518C002FE082 /* Other Frameworks */,
 			);
 			);
@@ -1940,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 */,
@@ -1963,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>";
@@ -1984,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 */,
@@ -1999,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 */,
@@ -2103,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 */,
@@ -2600,6 +2773,7 @@
 		86BC805816518D4600D96ADF /* input */ = {
 		86BC805816518D4600D96ADF /* input */ = {
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
+				8645C96D18872339004ED987 /* leapMotion */,
 				B350D153174EF5F200033EBB /* actionMap_ScriptBinding.h */,
 				B350D153174EF5F200033EBB /* actionMap_ScriptBinding.h */,
 				86BC805916518D4600D96ADF /* actionMap.cc */,
 				86BC805916518D4600D96ADF /* actionMap.cc */,
 				86BC805A16518D4600D96ADF /* actionMap.h */,
 				86BC805A16518D4600D96ADF /* actionMap.h */,
@@ -2685,6 +2859,7 @@
 		86BC809116518D4600D96ADF /* math */ = {
 		86BC809116518D4600D96ADF /* math */ = {
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
+				8645C96B1887231C004ED987 /* mPoint.cpp */,
 				B350D12B174ED1FE00033EBB /* box_ScriptBinding.h */,
 				B350D12B174ED1FE00033EBB /* box_ScriptBinding.h */,
 				B350D12C174ED1FE00033EBB /* math_ScriptBinding.cc */,
 				B350D12C174ED1FE00033EBB /* math_ScriptBinding.cc */,
 				B350D12D174ED1FE00033EBB /* matrix_ScriptBinding.h */,
 				B350D12D174ED1FE00033EBB /* matrix_ScriptBinding.h */,
@@ -3036,6 +3211,7 @@
 				869FF8B61651518C002FE082 /* Resources */,
 				869FF8B61651518C002FE082 /* Resources */,
 				869FF8B41651518C002FE082 /* Sources */,
 				869FF8B41651518C002FE082 /* Sources */,
 				869FF8B51651518C002FE082 /* Frameworks */,
 				869FF8B51651518C002FE082 /* Frameworks */,
+				8645C978188723B7004ED987 /* CopyFiles */,
 			);
 			);
 			buildRules = (
 			buildRules = (
 			);
 			);
@@ -3131,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 */,
@@ -3138,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 */,
@@ -3147,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 */,
@@ -3154,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 */,
@@ -3177,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 */,
@@ -3189,6 +3371,7 @@
 				86D77052165687220046D71F /* zipArchive.cc in Sources */,
 				86D77052165687220046D71F /* zipArchive.cc in Sources */,
 				86D77053165687220046D71F /* zipCryptStream.cc in Sources */,
 				86D77053165687220046D71F /* zipCryptStream.cc in Sources */,
 				86D77054165687220046D71F /* zipObject.cc in Sources */,
 				86D77054165687220046D71F /* zipObject.cc in Sources */,
+				8645C97418872339004ED987 /* leapMotionManager.cc in Sources */,
 				86D77055165687220046D71F /* zipSubStream.cc in Sources */,
 				86D77055165687220046D71F /* zipSubStream.cc in Sources */,
 				86D77056165687220046D71F /* zipTempStream.cc in Sources */,
 				86D77056165687220046D71F /* zipTempStream.cc in Sources */,
 				86D76FAF165687060046D71F /* crc.cc in Sources */,
 				86D76FAF165687060046D71F /* crc.cc in Sources */,
@@ -3203,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 */,
@@ -3219,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 */,
@@ -3253,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 */,
@@ -3263,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 */,
@@ -3276,11 +3464,13 @@
 				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 */,
 				86D77023165687060046D71F /* guiConsole.cc in Sources */,
 				86D77023165687060046D71F /* guiConsole.cc in Sources */,
 				86D77024165687060046D71F /* guiConsoleEditCtrl.cc in Sources */,
 				86D77024165687060046D71F /* guiConsoleEditCtrl.cc in Sources */,
+				8645C97518872339004ED987 /* leapMotionUtil.cpp in Sources */,
 				86D77025165687060046D71F /* guiConsoleTextCtrl.cc in Sources */,
 				86D77025165687060046D71F /* guiConsoleTextCtrl.cc in Sources */,
 				86D77026165687060046D71F /* guiControl.cc in Sources */,
 				86D77026165687060046D71F /* guiControl.cc in Sources */,
 				86D77027165687060046D71F /* guiDefaultControlRender.cc in Sources */,
 				86D77027165687060046D71F /* guiDefaultControlRender.cc in Sources */,
@@ -3310,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 */,
@@ -3371,11 +3563,13 @@
 				865A228E165187B600527C44 /* b2ContactManager.cpp in Sources */,
 				865A228E165187B600527C44 /* b2ContactManager.cpp in Sources */,
 				865A228F165187B600527C44 /* b2Fixture.cpp in Sources */,
 				865A228F165187B600527C44 /* b2Fixture.cpp in Sources */,
 				865A2290165187B600527C44 /* b2Island.cpp in Sources */,
 				865A2290165187B600527C44 /* b2Island.cpp in Sources */,
+				8645C96C1887231C004ED987 /* mPoint.cpp in Sources */,
 				865A2291165187B600527C44 /* b2World.cpp in Sources */,
 				865A2291165187B600527C44 /* b2World.cpp in Sources */,
 				865A2292165187B600527C44 /* b2WorldCallbacks.cpp in Sources */,
 				865A2292165187B600527C44 /* b2WorldCallbacks.cpp in Sources */,
 				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 */,
@@ -3383,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 */,
@@ -3391,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 */,
@@ -3410,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 */,
@@ -3428,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 */,
@@ -3440,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 */,
@@ -3460,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 */,
@@ -3474,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 */,
@@ -3572,7 +3775,7 @@
 		865A20AF165152EA00527C44 /* Shipping */ = {
 		865A20AF165152EA00527C44 /* Shipping */ = {
 			isa = XCBuildConfiguration;
 			isa = XCBuildConfiguration;
 			buildSettings = {
 			buildSettings = {
-				ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+				CLANG_CXX_LIBRARY = "libc++";
 				COMBINE_HIDPI_IMAGES = YES;
 				COMBINE_HIDPI_IMAGES = YES;
 				CONFIGURATION_BUILD_DIR = ../../..;
 				CONFIGURATION_BUILD_DIR = ../../..;
 				GCC_PRECOMPILE_PREFIX_HEADER = YES;
 				GCC_PRECOMPILE_PREFIX_HEADER = YES;
@@ -3592,13 +3795,16 @@
 					../../source/persistence/rapidjson/include,
 					../../source/persistence/rapidjson/include,
 					"$(SYSTEM_LIBRARY_DIR)/Frameworks/ApplicationServices.framework/Versions/Current/Frameworks/QD.framework/Headers",
 					"$(SYSTEM_LIBRARY_DIR)/Frameworks/ApplicationServices.framework/Versions/Current/Frameworks/QD.framework/Headers",
 					"$(SYSTEM_LIBRARY_DIR)/Frameworks/OpenGL.framework/Headers",
 					"$(SYSTEM_LIBRARY_DIR)/Frameworks/OpenGL.framework/Headers",
+					../../lib/LeapSDK/include,
 				);
 				);
 				INFOPLIST_FILE = "Torque2D/Torque2D-Info.plist";
 				INFOPLIST_FILE = "Torque2D/Torque2D-Info.plist";
-				LIBRARY_SEARCH_PATHS = "$(inherited)";
-				MACOSX_DEPLOYMENT_TARGET = 10.6;
+				LIBRARY_SEARCH_PATHS = (
+					"$(inherited)",
+					"../../lib/LeapSDK/lib/libc++",
+				);
+				MACOSX_DEPLOYMENT_TARGET = 10.7;
 				OTHER_LDFLAGS = /usr/lib/libz.dylib;
 				OTHER_LDFLAGS = /usr/lib/libz.dylib;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				VALID_ARCHS = i386;
 				WRAPPER_EXTENSION = app;
 				WRAPPER_EXTENSION = app;
 			};
 			};
 			name = Shipping;
 			name = Shipping;
@@ -3673,7 +3879,7 @@
 		869FF8D71651518C002FE082 /* Debug */ = {
 		869FF8D71651518C002FE082 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			isa = XCBuildConfiguration;
 			buildSettings = {
 			buildSettings = {
-				ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+				CLANG_CXX_LIBRARY = "libc++";
 				COMBINE_HIDPI_IMAGES = YES;
 				COMBINE_HIDPI_IMAGES = YES;
 				CONFIGURATION_BUILD_DIR = ../../..;
 				CONFIGURATION_BUILD_DIR = ../../..;
 				GCC_PRECOMPILE_PREFIX_HEADER = YES;
 				GCC_PRECOMPILE_PREFIX_HEADER = YES;
@@ -3695,13 +3901,16 @@
 					../../source/testing/googleTest/include,
 					../../source/testing/googleTest/include,
 					"$(SYSTEM_LIBRARY_DIR)/Frameworks/ApplicationServices.framework/Versions/Current/Frameworks/QD.framework/Headers",
 					"$(SYSTEM_LIBRARY_DIR)/Frameworks/ApplicationServices.framework/Versions/Current/Frameworks/QD.framework/Headers",
 					"$(SYSTEM_LIBRARY_DIR)/Frameworks/OpenGL.framework/Headers",
 					"$(SYSTEM_LIBRARY_DIR)/Frameworks/OpenGL.framework/Headers",
+					../../lib/LeapSDK/include,
 				);
 				);
 				INFOPLIST_FILE = "Torque2D/Torque2D-Info.plist";
 				INFOPLIST_FILE = "Torque2D/Torque2D-Info.plist";
-				LIBRARY_SEARCH_PATHS = "$(inherited)";
-				MACOSX_DEPLOYMENT_TARGET = 10.6;
+				LIBRARY_SEARCH_PATHS = (
+					"$(inherited)",
+					"../../lib/LeapSDK/lib/libc++",
+				);
+				MACOSX_DEPLOYMENT_TARGET = 10.7;
 				OTHER_LDFLAGS = /usr/lib/libz.dylib;
 				OTHER_LDFLAGS = /usr/lib/libz.dylib;
 				PRODUCT_NAME = "$(TARGET_NAME)_DEBUG";
 				PRODUCT_NAME = "$(TARGET_NAME)_DEBUG";
-				VALID_ARCHS = i386;
 				WRAPPER_EXTENSION = app;
 				WRAPPER_EXTENSION = app;
 			};
 			};
 			name = Debug;
 			name = Debug;
@@ -3709,7 +3918,7 @@
 		869FF8D81651518C002FE082 /* Release */ = {
 		869FF8D81651518C002FE082 /* Release */ = {
 			isa = XCBuildConfiguration;
 			isa = XCBuildConfiguration;
 			buildSettings = {
 			buildSettings = {
-				ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+				CLANG_CXX_LIBRARY = "libc++";
 				COMBINE_HIDPI_IMAGES = YES;
 				COMBINE_HIDPI_IMAGES = YES;
 				CONFIGURATION_BUILD_DIR = ../../..;
 				CONFIGURATION_BUILD_DIR = ../../..;
 				GCC_PRECOMPILE_PREFIX_HEADER = YES;
 				GCC_PRECOMPILE_PREFIX_HEADER = YES;
@@ -3732,13 +3941,30 @@
 					../../source/testing/googleTest/include,
 					../../source/testing/googleTest/include,
 					"$(SYSTEM_LIBRARY_DIR)/Frameworks/ApplicationServices.framework/Versions/Current/Frameworks/QD.framework/Headers",
 					"$(SYSTEM_LIBRARY_DIR)/Frameworks/ApplicationServices.framework/Versions/Current/Frameworks/QD.framework/Headers",
 					"$(SYSTEM_LIBRARY_DIR)/Frameworks/OpenGL.framework/Headers",
 					"$(SYSTEM_LIBRARY_DIR)/Frameworks/OpenGL.framework/Headers",
+					../../lib/LeapSDK/include,
+				);
+				"HEADER_SEARCH_PATHS[arch=*]" = (
+					../../source,
+					../../lib,
+					../../lib/vorbis/include,
+					../../lib/lpng,
+					../../lib/ljpeg,
+					../../lib/lungif,
+					../../source/persistence/rapidjson/include,
+					../../source/testing/googleTest,
+					../../source/testing/googleTest/include,
+					"$(SYSTEM_LIBRARY_DIR)/Frameworks/ApplicationServices.framework/Versions/Current/Frameworks/QD.framework/Headers",
+					"$(SYSTEM_LIBRARY_DIR)/Frameworks/OpenGL.framework/Headers",
+					../../lib/LeapSDK/include,
 				);
 				);
 				INFOPLIST_FILE = "Torque2D/Torque2D-Info.plist";
 				INFOPLIST_FILE = "Torque2D/Torque2D-Info.plist";
-				LIBRARY_SEARCH_PATHS = "$(inherited)";
-				MACOSX_DEPLOYMENT_TARGET = 10.6;
+				LIBRARY_SEARCH_PATHS = (
+					"$(inherited)",
+					"../../lib/LeapSDK/lib/libc++",
+				);
+				MACOSX_DEPLOYMENT_TARGET = 10.7;
 				OTHER_LDFLAGS = /usr/lib/libz.dylib;
 				OTHER_LDFLAGS = /usr/lib/libz.dylib;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				VALID_ARCHS = i386;
 				WRAPPER_EXTENSION = app;
 				WRAPPER_EXTENSION = app;
 			};
 			};
 			name = Release;
 			name = Release;

+ 162 - 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 */; };
@@ -453,6 +477,7 @@
 		867BB28116AECA110033868F /* jquant2.c in Sources */ = {isa = PBXBuildFile; fileRef = 867BB25316AECA110033868F /* jquant2.c */; };
 		867BB28116AECA110033868F /* jquant2.c in Sources */ = {isa = PBXBuildFile; fileRef = 867BB25316AECA110033868F /* jquant2.c */; };
 		867BB28216AECA110033868F /* jutils.c in Sources */ = {isa = PBXBuildFile; fileRef = 867BB25416AECA110033868F /* jutils.c */; };
 		867BB28216AECA110033868F /* jutils.c in Sources */ = {isa = PBXBuildFile; fileRef = 867BB25416AECA110033868F /* jutils.c */; };
 		867BB44416AED2850033868F /* iOSGL2ES.mm in Sources */ = {isa = PBXBuildFile; fileRef = 867BB44316AED2840033868F /* iOSGL2ES.mm */; };
 		867BB44416AED2850033868F /* iOSGL2ES.mm in Sources */ = {isa = PBXBuildFile; fileRef = 867BB44316AED2840033868F /* iOSGL2ES.mm */; };
+		8698388618872BF500D370A0 /* mPoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8698388518872BF500D370A0 /* mPoint.cpp */; };
 		869A879216C15A3A00AE96AE /* Default-Portrait~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 869A879116C15A3A00AE96AE /* Default-Portrait~ipad.png */; };
 		869A879216C15A3A00AE96AE /* Default-Portrait~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 869A879116C15A3A00AE96AE /* Default-Portrait~ipad.png */; };
 		869A879416C15A4300AE96AE /* Default-Portrait@2x~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 869A879316C15A4300AE96AE /* Default-Portrait@2x~ipad.png */; };
 		869A879416C15A4300AE96AE /* Default-Portrait@2x~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 869A879316C15A4300AE96AE /* Default-Portrait@2x~ipad.png */; };
 		869A879616C15A4900AE96AE /* Default-Landscape~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 869A879516C15A4900AE96AE /* Default-Landscape~ipad.png */; };
 		869A879616C15A4900AE96AE /* Default-Landscape~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 869A879516C15A4900AE96AE /* Default-Landscape~ipad.png */; };
@@ -486,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>"; };
@@ -1413,6 +1489,7 @@
 		867BB25416AECA110033868F /* jutils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = jutils.c; path = ../../lib/ljpeg/jutils.c; sourceTree = "<group>"; };
 		867BB25416AECA110033868F /* jutils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = jutils.c; path = ../../lib/ljpeg/jutils.c; sourceTree = "<group>"; };
 		867BB44216AED2840033868F /* iOSGL2ES.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iOSGL2ES.h; sourceTree = "<group>"; };
 		867BB44216AED2840033868F /* iOSGL2ES.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iOSGL2ES.h; sourceTree = "<group>"; };
 		867BB44316AED2840033868F /* iOSGL2ES.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = iOSGL2ES.mm; sourceTree = "<group>"; };
 		867BB44316AED2840033868F /* iOSGL2ES.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = iOSGL2ES.mm; sourceTree = "<group>"; };
+		8698388518872BF500D370A0 /* mPoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mPoint.cpp; sourceTree = "<group>"; };
 		869A879116C15A3A00AE96AE /* Default-Portrait~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait~ipad.png"; sourceTree = "<group>"; };
 		869A879116C15A3A00AE96AE /* Default-Portrait~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait~ipad.png"; sourceTree = "<group>"; };
 		869A879316C15A4300AE96AE /* Default-Portrait@2x~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait@2x~ipad.png"; sourceTree = "<group>"; };
 		869A879316C15A4300AE96AE /* Default-Portrait@2x~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait@2x~ipad.png"; sourceTree = "<group>"; };
 		869A879516C15A4900AE96AE /* Default-Landscape~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape~ipad.png"; sourceTree = "<group>"; };
 		869A879516C15A4900AE96AE /* Default-Landscape~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape~ipad.png"; sourceTree = "<group>"; };
@@ -1540,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 = (
@@ -1750,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 */,
@@ -1854,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 */,
@@ -2433,6 +2568,7 @@
 		867BAEF316AEC9050033868F /* math */ = {
 		867BAEF316AEC9050033868F /* math */ = {
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
+				8698388518872BF500D370A0 /* mPoint.cpp */,
 				B350D19E174F063200033EBB /* box_ScriptBinding.h */,
 				B350D19E174F063200033EBB /* box_ScriptBinding.h */,
 				B350D19F174F063200033EBB /* math_ScriptBinding.cc */,
 				B350D19F174F063200033EBB /* math_ScriptBinding.cc */,
 				B350D1A0174F063200033EBB /* matrix_ScriptBinding.h */,
 				B350D1A0174F063200033EBB /* matrix_ScriptBinding.h */,
@@ -3110,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 */,
 			);
 			);
@@ -3221,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 */,
@@ -3272,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 */,
@@ -3279,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 */,
@@ -3289,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 */,
@@ -3329,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 */,
@@ -3359,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 */,
@@ -3376,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 */,
@@ -3401,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 */,
@@ -3424,7 +3572,9 @@
 				867BB0A516AEC9050033868F /* resourceDictionary.cc in Sources */,
 				867BB0A516AEC9050033868F /* resourceDictionary.cc in Sources */,
 				867BB0A616AEC9050033868F /* resourceManager.cc in Sources */,
 				867BB0A616AEC9050033868F /* resourceManager.cc in Sources */,
 				867BB0A716AEC9050033868F /* streamObject.cc in Sources */,
 				867BB0A716AEC9050033868F /* streamObject.cc 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 */,
@@ -3433,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 */,
@@ -3449,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 */,
@@ -3481,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 */,
@@ -3514,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 */,
@@ -3535,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 */,
@@ -3562,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 */,
@@ -3579,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 */,
@@ -3599,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 */,
@@ -3629,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 */,

+ 4 - 0
engine/compilers/android/jni/Android.mk

@@ -83,6 +83,7 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH) \
     				$(LOCAL_PATH)/../../../source/gui/editor \
     				$(LOCAL_PATH)/../../../source/gui/editor \
     				$(LOCAL_PATH)/../../../source/gui/language \
     				$(LOCAL_PATH)/../../../source/gui/language \
     				$(LOCAL_PATH)/../../../source/input \
     				$(LOCAL_PATH)/../../../source/input \
+					$(LOCAL_PATH)/../../../source/input/leapMotion \
     				$(LOCAL_PATH)/../../../source/io \
     				$(LOCAL_PATH)/../../../source/io \
     				$(LOCAL_PATH)/../../../source/io/resource \
     				$(LOCAL_PATH)/../../../source/io/resource \
     				$(LOCAL_PATH)/../../../source/io/zip \
     				$(LOCAL_PATH)/../../../source/io/zip \
@@ -337,6 +338,8 @@ LOCAL_SRC_FILES :=  ../../../lib/ljpeg/jcapimin.c \
 					../../../source/gui/language/lang.cc \
 					../../../source/gui/language/lang.cc \
 					../../../source/gui/messageVector.cc \
 					../../../source/gui/messageVector.cc \
 					../../../source/input/actionMap.cc \
 					../../../source/input/actionMap.cc \
+					../../../source/input/leapMotion/leapMotionManager.cc \
+					../../../source/input/leapMotion/leapMotionUtil.cpp \
 					../../../source/io/bitStream.cc \
 					../../../source/io/bitStream.cc \
 					../../../source/io/bufferStream.cc \
 					../../../source/io/bufferStream.cc \
 					../../../source/io/fileObject.cc \
 					../../../source/io/fileObject.cc \
@@ -446,6 +449,7 @@ LOCAL_SRC_FILES :=  ../../../lib/ljpeg/jcapimin.c \
 					../../../source/math/mRandom.cc \
 					../../../source/math/mRandom.cc \
 					../../../source/math/mSolver.cc \
 					../../../source/math/mSolver.cc \
 					../../../source/math/mSplinePatch.cc \
 					../../../source/math/mSplinePatch.cc \
+					../../../source/math/mPoint.cpp \
 					../../../source/persistence/SimXMLDocument.cpp \
 					../../../source/persistence/SimXMLDocument.cpp \
 					../../../source/platform/CursorManager.cc \
 					../../../source/platform/CursorManager.cc \
 					../../../source/platform/nativeDialogs/fileDialog.cc \
 					../../../source/platform/nativeDialogs/fileDialog.cc \

+ 4566 - 0
engine/lib/LeapSDK/include/Leap.h

@@ -0,0 +1,4566 @@
+/******************************************************************************\
+* Copyright (C) 2012-2013 Leap Motion, Inc. All rights reserved.               *
+* Leap Motion proprietary and confidential. Not for distribution.              *
+* Use subject to the terms of the Leap Motion SDK Agreement available at       *
+* https://developer.leapmotion.com/sdk_agreement, or another agreement         *
+* between Leap Motion and you, your company or other organization.             *
+\******************************************************************************/
+
+#if !defined(__Leap_h__)
+#define __Leap_h__
+
+#include "LeapMath.h"
+#include <string>
+#include <vector>
+
+// Define integer types for Visual Studio 2008 and earlier
+#if defined(_MSC_VER) && (_MSC_VER < 1600)
+typedef __int32 int32_t;
+typedef __int64 int64_t;
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+#else
+#include <stdint.h>
+#endif
+
+// Define Leap export macros
+#if defined(_WIN32) // Windows
+#if LEAP_API_INTERNAL
+#define LEAP_EXPORT
+#elif LEAP_API_IMPLEMENTATION
+#define LEAP_EXPORT __declspec(dllexport)
+#else
+#define LEAP_EXPORT __declspec(dllimport)
+#endif
+#define LEAP_EXPORT_CLASS
+#define LEAP_EXPORT_PLUGIN __declspec(dllexport)
+#elif !defined(SWIG)
+#define LEAP_EXPORT __attribute__((visibility("default")))
+#define LEAP_EXPORT_CLASS __attribute__((visibility("default")))
+#define LEAP_EXPORT_PLUGIN __attribute__((visibility("default")))
+#else
+#define LEAP_EXPORT
+#define LEAP_EXPORT_CLASS
+#define LEAP_EXPORT_PLUGIN
+#endif
+
+namespace Leap {
+
+  // Interface for internal use only
+  class LEAP_EXPORT_CLASS Interface {
+  public:
+    struct Implementation {
+      LEAP_EXPORT virtual ~Implementation() {}
+    };
+  protected:
+    LEAP_EXPORT Interface(void* owner);
+    LEAP_EXPORT Interface(Implementation* reference, void* owner);
+    LEAP_EXPORT Interface(const Interface& rhs);
+    Interface(class SharedObject* object);
+    LEAP_EXPORT Interface& operator=(const Interface& rhs);
+    LEAP_EXPORT virtual ~Interface();
+    template<typename T> T* get() const { return static_cast<T*>(reference()); }
+    class SharedObject* m_object;
+  private:
+    LEAP_EXPORT Implementation* reference() const;
+  };
+
+  // Forward declarations for internal use only
+  class PointableImplementation;
+  class FingerImplementation;
+  class ToolImplementation;
+  class HandImplementation;
+  class GestureImplementation;
+  class ScreenImplementation;
+  class DeviceImplementation;
+  class InteractionBoxImplementation;
+  class FrameImplementation;
+  class ControllerImplementation;
+  template<typename T> class ListBaseImplementation;
+
+  // Forward declarations
+  class PointableList;
+  class FingerList;
+  class ToolList;
+  class HandList;
+  class GestureList;
+  class Hand;
+  class Gesture;
+  class Screen;
+  class InteractionBox;
+  class Frame;
+  class Listener;
+
+  /**
+   * The Pointable class reports the physical characteristics of a detected finger or tool.
+   *
+   * Both fingers and tools are classified as Pointable objects. Use the Pointable::isFinger()
+   * function to determine whether a Pointable object represents a finger. Use the
+   * Pointable::isTool() function to determine whether a Pointable object represents a tool.
+   * The Leap Motion software classifies a detected entity as a tool when it is thinner, straighter, and longer
+   * than a typical finger.
+   *
+   * \include Pointable_Get_Basic.txt
+   *
+   * To provide touch emulation, the Leap Motion software associates a floating touch
+   * plane that adapts to the user's finger movement and hand posture. The Leap Motion
+   * interprets purposeful movements toward this plane as potential touch points.
+   * The Pointable class reports
+   * touch state with the touchZone and touchDistance values.
+   *
+   * Note that Pointable objects can be invalid, which means that they do not contain
+   * valid tracking data and do not correspond to a physical entity. Invalid Pointable
+   * objects can be the result of asking for a Pointable object using an ID from an
+   * earlier frame when no Pointable objects with that ID exist in the current frame.
+   * A Pointable object created from the Pointable constructor is also invalid.
+   * Test for validity with the Pointable::isValid() function.
+   *
+   * @since 1.0
+   */
+  class Pointable : public Interface {
+  public:
+
+    /**
+     * Defines the values for reporting the state of a Pointable object in relation to
+     * an adaptive touch plane.
+     * @since 1.0
+     */
+    enum Zone {
+      /**
+       * The Pointable object is too far from the plane to be
+       * considered hovering or touching.
+       * @since 1.0
+       */
+      ZONE_NONE       = 0,
+      /**
+       * The Pointable object is close to, but not touching
+       * the plane.
+       * @since 1.0
+       */
+      ZONE_HOVERING   = 1,
+      /**
+       * The Pointable has penetrated the plane.
+       * @since 1.0
+       */
+      ZONE_TOUCHING   = 2,
+    };
+
+    // For internal use only.
+    Pointable(PointableImplementation*);
+    // For internal use only.
+    Pointable(FingerImplementation*);
+    // For internal use only.
+    Pointable(ToolImplementation*);
+
+    /**
+     * Constructs a Pointable object.
+     *
+     * An uninitialized pointable is considered invalid.
+     * Get valid Pointable objects from a Frame or a Hand object.
+     *
+     * \include Pointable_Pointable.txt
+     *
+     * @since 1.0
+     */
+    LEAP_EXPORT Pointable();
+
+    /**
+     * A unique ID assigned to this Pointable object, whose value remains the
+     * same across consecutive frames while the tracked finger or tool remains
+     * visible. If tracking is lost (for example, when a finger is occluded by
+     * another finger or when it is withdrawn from the Leap Motion Controller field of view), the
+     * Leap Motion software may assign a new ID when it detects the entity in a future frame.
+     *
+     * \include Pointable_id.txt
+     *
+     * Use the ID value with the Frame::pointable() function to find this
+     * Pointable object in future frames.
+     *
+     * IDs should be from 1 to 100 (inclusive). If more than 100 objects are tracked
+     * an IDs of -1 will be used until an ID in the defined range is available.
+     *
+     * @returns The ID assigned to this Pointable object.
+     * @since 1.0
+     */
+    LEAP_EXPORT int32_t id() const;
+
+    /**
+     * The Frame associated with this Pointable object.
+     *
+     * \include Pointable_frame.txt
+     *
+     * @returns The associated Frame object, if available; otherwise,
+     * an invalid Frame object is returned.
+     * @since 1.0
+     */
+    LEAP_EXPORT Frame frame() const;
+
+    /**
+     * The Hand associated with this finger or tool.
+     *
+     * \include Pointable_hand.txt
+     *
+     * @returns The associated Hand object, if available; otherwise,
+     * an invalid Hand object is returned.
+     * @since 1.0
+     */
+    LEAP_EXPORT Hand hand() const;
+
+    /**
+     * The tip position in millimeters from the Leap Motion origin.
+     *
+     * \include Pointable_tipPosition.txt
+     *
+     * @returns The Vector containing the coordinates of the tip position.
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector tipPosition() const;
+
+    /**
+     * The rate of change of the tip position in millimeters/second.
+     *
+     * \include Pointable_tipVelocity.txt
+     *
+     * @returns The Vector containing the coordinates of the tip velocity.
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector tipVelocity() const;
+
+    /**
+     * The direction in which this finger or tool is pointing.
+     *
+     * \include Pointable_direction.txt
+     *
+     * The direction is expressed as a unit vector pointing in the same
+     * direction as the tip.
+     *
+     * \image html images/Leap_Finger_Model.png
+     *
+     * @returns The Vector pointing in the same direction as the tip of this
+     * Pointable object.
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector direction() const;
+
+    /**
+     * The estimated width of the finger or tool in millimeters.
+     *
+     * \include Pointable_width.txt
+     *
+     * The reported width is the average width of the visible portion of the
+     * finger or tool from the hand to the tip. If the width isn't known,
+     * then a value of 0 is returned.
+     *
+     * @returns The estimated width of this Pointable object.
+     * @since 1.0
+     */
+    LEAP_EXPORT float width() const;
+
+    /**
+     * The estimated length of the finger or tool in millimeters.
+     *
+     * The reported length is the visible length of the finger or tool from the
+     * hand to tip. If the length isn't known, then a value of 0 is returned.
+     *
+     * \include Pointable_length.txt
+     *
+     * @returns The estimated length of this Pointable object.
+     * @since 1.0
+     */
+    LEAP_EXPORT float length() const;
+
+    /**
+     * Whether or not the Pointable is believed to be a finger.
+     * Fingers are generally shorter, thicker, and less straight than tools.
+     *
+     * \include Pointable_Conversion.txt
+     *
+     * @returns True, if this Pointable is classified as a finger.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool isFinger() const;
+
+    /**
+     * Whether or not the Pointable is believed to be a tool.
+     * Tools are generally longer, thinner, and straighter than fingers.
+     *
+     * \include Pointable_Conversion.txt
+     *
+     * @returns True, if this Pointable is classified as a tool.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool isTool() const;
+
+    /**
+     * Reports whether this is a valid Pointable object.
+     *
+     * \include Pointable_isValid.txt
+     *
+     * @returns True, if this Pointable object contains valid tracking data.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool isValid() const;
+
+    /**
+     * The current touch zone of this Pointable object.
+     *
+     * The Leap Motion software computes the touch zone based on a floating touch
+     * plane that adapts to the user's finger movement and hand posture. The Leap
+     * Motion software interprets purposeful movements toward this plane as potential touch
+     * points. When a Pointable moves close to the adaptive touch plane, it enters the
+     * "hovering" zone. When a Pointable reaches or passes through the plane, it enters
+     * the "touching" zone.
+     *
+     * The possible states are present in the Zone enum of this class:
+     *
+     * * Zone.NONE -- The Pointable is outside the hovering zone.
+     * * Zone.HOVERING -- The Pointable is close to, but not touching the touch plane.
+     * * Zone.TOUCHING -- The Pointable has penetrated the touch plane.
+     *
+     * The touchDistance value provides a normalized indication of the distance to
+     * the touch plane when the Pointable is in the hovering or touching zones.
+     *
+     * \include Pointable_touchZone.txt
+     *
+     * @returns The touch zone of this Pointable
+     * @since 1.0
+     */
+    LEAP_EXPORT Zone touchZone() const;
+
+    /**
+     * A value proportional to the distance between this Pointable object and the
+     * adaptive touch plane.
+     *
+     * \image html images/Leap_Touch_Plane.png
+     *
+     * The touch distance is a value in the range [-1, 1]. The value 1.0 indicates the
+     * Pointable is at the far edge of the hovering zone. The value 0 indicates the
+     * Pointable is just entering the touching zone. A value of -1.0 indicates the
+     * Pointable is firmly within the touching zone. Values in between are
+     * proportional to the distance from the plane. Thus, the touchDistance of 0.5
+     * indicates that the Pointable is halfway into the hovering zone.
+     *
+     * \include Pointable_touchDistance.txt
+     *
+     * You can use the touchDistance value to modulate visual feedback given to the
+     * user as their fingers close in on a touch target, such as a button.
+     *
+     * @returns The normalized touch distance of this Pointable object.
+     * @since 1.0
+     */
+    LEAP_EXPORT float touchDistance() const;
+
+    /**
+     * The stabilized tip position of this Pointable.
+     *
+     * Smoothing and stabilization is performed in order to make
+     * this value more suitable for interaction with 2D content. The stabilized
+     * position lags behind the tip position by a variable amount, depending
+     * primarily on the speed of movement.
+     *
+     * \include Pointable_stabilizedTipPosition.txt
+     *
+     * @returns A modified tip position of this Pointable object
+     * with some additional smoothing and stabilization applied.
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector stabilizedTipPosition() const;
+
+    /**
+     * The duration of time this Pointable has been visible to the Leap Motion Controller.
+     *
+     * \include Pointable_timeVisible.txt
+     *
+     * @returns The duration (in seconds) that this Pointable has been tracked.
+     * @since 1.0
+     */
+    LEAP_EXPORT float timeVisible() const;
+
+    /**
+     * Returns an invalid Pointable object.
+     *
+     * You can use the instance returned by this function in comparisons testing
+     * whether a given Pointable instance is valid or invalid. (You can also use the
+     * Pointable::isValid() function.)
+     *
+     * \include Pointable_invalid.txt
+     *
+     * @returns The invalid Pointable instance.
+     * @since 1.0
+     */
+    LEAP_EXPORT static const Pointable& invalid();
+
+    /**
+     * Compare Pointable object equality.
+     *
+     * \include Pointable_operator_equals.txt
+     *
+     * Two Pointable objects are equal if and only if both Pointable objects represent the
+     * exact same physical entities in the same frame and both Pointable objects are valid.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool operator==(const Pointable&) const;
+
+    /**
+     * Compare Pointable object inequality.
+     *
+     * \include Pointable_operator_not_equal.txt
+     *
+     * Two Pointable objects are equal if and only if both Pointable objects represent the
+     * exact same physical entities in the same frame and both Pointable objects are valid.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool operator!=(const Pointable&) const;
+
+    /**
+     * Writes a brief, human readable description of the Pointable object to an output stream.
+     *
+     * \include Pointable_operator_stream.txt
+     *
+     * @since 1.0
+     */
+    LEAP_EXPORT friend std::ostream& operator<<(std::ostream&, const Pointable&);
+
+    /**
+     * A string containing a brief, human readable description of the Pointable object.
+     *
+     * @returns A description of the Pointable object as a string.
+     * @since 1.0
+     */
+    LEAP_EXPORT std::string toString() const;
+  };
+
+  /**
+   * The Finger class represents a tracked finger.
+   *
+   * Fingers are Pointable objects that the Leap Motion software has classified as a finger.
+   * Get valid Finger objects from a Frame or a Hand object.
+   *
+   * Fingers may be permanently associated to a hand. In this case the angular order of the finger IDs
+   * will be invariant. As fingers move in and out of view it is possible for the guessed ID
+   * of a finger to be incorrect. Consequently, it may be necessary for finger IDs to be
+   * exchanged. All tracked properties, such as velocity, will remain continuous in the API.
+   * However, quantities that are derived from the API output (such as a history of positions)
+   * will be discontinuous unless they have a corresponding ID exchange.
+   *
+   * Note that Finger objects can be invalid, which means that they do not contain
+   * valid tracking data and do not correspond to a physical finger. Invalid Finger
+   * objects can be the result of asking for a Finger object using an ID from an
+   * earlier frame when no Finger objects with that ID exist in the current frame.
+   * A Finger object created from the Finger constructor is also invalid.
+   * Test for validity with the Finger::isValid() function.
+   * @since 1.0
+   */
+  class Finger : public Pointable {
+  public:
+    // For internal use only.
+    Finger(FingerImplementation*);
+
+    /**
+     * Constructs a Finger object.
+     *
+     * An uninitialized finger is considered invalid.
+     * Get valid Finger objects from a Frame or a Hand object.
+     * @since 1.0
+     */
+    LEAP_EXPORT Finger();
+
+    /**
+     * If the specified Pointable object represents a finger, creates a copy
+     * of it as a Finger object; otherwise, creates an invalid Finger object.
+     *
+     * \include Finger_Finger.txt
+     *
+     * @since 1.0
+     */
+    LEAP_EXPORT explicit Finger(const Pointable&);
+
+    /**
+     * Returns an invalid Finger object.
+     *
+     * You can use the instance returned by this function in comparisons testing
+     * whether a given Finger instance is valid or invalid. (You can also use the
+     * Finger::isValid() function.)
+     *
+     * \include Finger_invalid.txt
+     *
+     * @returns The invalid Finger instance.
+     * @since 1.0
+     */
+    LEAP_EXPORT static const Finger& invalid();
+
+    /**
+     * A string containing a brief, human readable description of the Finger object.
+     *
+     * @returns A description of the Finger object as a string.
+     * @since 1.0
+     */
+    LEAP_EXPORT std::string toString() const;
+  };
+
+  /**
+   * The Tool class represents a tracked tool.
+   *
+   * Tools are Pointable objects that the Leap Motion software has classified as a tool.
+   * Tools are longer, thinner, and straighter than a typical finger.
+   * Get valid Tool objects from a Frame or a Hand object.
+   *
+   * Tools may reference a hand, but unlike fingers they are not permanently associated.
+   * Instead, a tool can be transferred between hands while keeping the same ID.
+   *
+   * \image html images/Leap_Tool.png
+   *
+   * Note that Tool objects can be invalid, which means that they do not contain
+   * valid tracking data and do not correspond to a physical tool. Invalid Tool
+   * objects can be the result of asking for a Tool object using an ID from an
+   * earlier frame when no Tool objects with that ID exist in the current frame.
+   * A Tool object created from the Tool constructor is also invalid.
+   * Test for validity with the Tool::isValid() function.
+   * @since 1.0
+   */
+  class Tool : public Pointable {
+  public:
+    // For internal use only.
+    Tool(ToolImplementation*);
+
+    /**
+     * Constructs a Tool object.
+     *
+     * An uninitialized tool is considered invalid.
+     * Get valid Tool objects from a Frame or a Hand object.
+     *
+     * \include Tool_Tool.txt
+     *
+     * @since 1.0
+     */
+    LEAP_EXPORT Tool();
+
+    /**
+     * If the specified Pointable object represents a tool, creates a copy
+     * of it as a Tool object; otherwise, creates an invalid Tool object.
+     *
+     * \include Tool_Tool_copy.txt
+     *
+     * @since 1.0
+     */
+    LEAP_EXPORT explicit Tool(const Pointable&);
+
+    /**
+     * Returns an invalid Tool object.
+     *
+     * You can use the instance returned by this function in comparisons testing
+     * whether a given Tool instance is valid or invalid. (You can also use the
+     * Tool::isValid() function.)
+     *
+     * \include Tool_invalid.txt
+     *
+     * @returns The invalid Tool instance.
+     * @since 1.0
+     */
+    LEAP_EXPORT static const Tool& invalid();
+
+    /**
+     * A string containing a brief, human readable description of the Tool object.
+     *
+     * @returns A description of the Tool object as a string.
+     * @since 1.0
+     */
+    LEAP_EXPORT std::string toString() const;
+  };
+
+  /**
+   * The Hand class reports the physical characteristics of a detected hand.
+   *
+   * Hand tracking data includes a palm position and velocity; vectors for
+   * the palm normal and direction to the fingers; properties of a sphere fit
+   * to the hand; and lists of the attached fingers and tools.
+   *
+   * Get Hand objects from a Frame object:
+   *
+   * \include Hand_Get_First.txt
+   *
+   * Note that Hand objects can be invalid, which means that they do not contain
+   * valid tracking data and do not correspond to a physical entity. Invalid Hand
+   * objects can be the result of asking for a Hand object using an ID from an
+   * earlier frame when no Hand objects with that ID exist in the current frame.
+   * A Hand object created from the Hand constructor is also invalid.
+   * Test for validity with the Hand::isValid() function.
+   * @since 1.0
+   */
+  class Hand : public Interface {
+  public:
+    // For internal use only.
+    Hand(HandImplementation*);
+
+    /**
+     * Constructs a Hand object.
+     *
+     * An uninitialized hand is considered invalid.
+     * Get valid Hand objects from a Frame object.
+     *
+     * \include Hand_Hand.txt
+     *
+     * @since 1.0
+     */
+    LEAP_EXPORT Hand();
+
+    /**
+     * A unique ID assigned to this Hand object, whose value remains the same
+     * across consecutive frames while the tracked hand remains visible. If
+     * tracking is lost (for example, when a hand is occluded by another hand
+     * or when it is withdrawn from or reaches the edge of the Leap Motion Controller field of view),
+     * the Leap Motion software may assign a new ID when it detects the hand in a future frame.
+     *
+     * Use the ID value with the Frame::hand() function to find this Hand object
+     * in future frames:
+     *
+     * \include Hand_Get_ID.txt
+     *
+     * @returns The ID of this hand.
+     * @since 1.0
+     */
+    LEAP_EXPORT int32_t id() const;
+
+    /**
+     * The Frame associated with this Hand.
+     *
+     * \include Hand_frame.txt
+     *
+     * @returns The associated Frame object, if available; otherwise,
+     * an invalid Frame object is returned.
+     * @since 1.0
+     */
+    LEAP_EXPORT Frame frame() const;
+
+    /**
+     * The list of Pointable objects (fingers and tools) detected in this frame
+     * that are associated with this hand, given in arbitrary order. The list
+     * can be empty if no fingers or tools associated with this hand are detected.
+     *
+     * Use the Pointable::isFinger() function to determine whether or not an
+     * item in the list represents a finger. Use the Pointable::isTool() function
+     * to determine whether or not an item in the list represents a tool.
+     * You can also get only fingers using the Hand::fingers() function or
+     * only tools using the Hand::tools() function.
+     *
+     * \include Hand_Get_Fingers.txt
+     *
+     * @returns The PointableList containing all Pointable objects associated with this hand.
+     * @since 1.0
+     */
+    LEAP_EXPORT PointableList pointables() const;
+
+    /**
+     * The Pointable object with the specified ID associated with this hand.
+     *
+     * Use the Hand::pointable() function to retrieve a Pointable object
+     * associated with this hand using an ID value obtained from a previous frame.
+     * This function always returns a Pointable object, but if no finger or tool
+     * with the specified ID is present, an invalid Pointable object is returned.
+     *
+     * \include Hand_Get_Pointable_ByID.txt
+     *
+     * Note that the ID values assigned to objects persist across frames, but only until 
+     * tracking of that object is lost. If tracking of a finger or tool is lost and subsequently
+     * regained, the new Pointable object representing that finger or tool may have a
+     * different ID than that representing the finger or tool in an earlier frame.
+     *
+     * @param id The ID value of a Pointable object from a previous frame.
+     * @returns The Pointable object with the matching ID if one exists for this
+     * hand in this frame; otherwise, an invalid Pointable object is returned.
+     * @since 1.0
+     */
+    LEAP_EXPORT Pointable pointable(int32_t id) const;
+
+    /**
+     * The list of Finger objects detected in this frame that are attached to
+     * this hand, given in arbitrary order.
+     * The list can be empty if no fingers attached to this hand are detected.
+     *
+     * \include Hand_Get_Fingers.txt
+     *
+     * @returns The FingerList containing all Finger objects attached to this hand.
+     * @since 1.0
+     */
+    LEAP_EXPORT FingerList fingers() const;
+
+    /**
+     * The Finger object with the specified ID attached to this hand.
+     *
+     * Use the Hand::finger() function to retrieve a Finger object attached to
+     * this hand using an ID value obtained from a previous frame.
+     * This function always returns a Finger object, but if no finger
+     * with the specified ID is present, an invalid Finger object is returned.
+     *
+     * \include Hand_finger.txt
+     *
+     * Note that ID values persist across frames, but only until tracking of a
+     * particular object is lost. If tracking of a finger is lost and subsequently
+     * regained, the new Finger object representing that finger may have a
+     * different ID than that representing the finger in an earlier frame.
+     *
+     * @param id The ID value of a Finger object from a previous frame.
+     * @returns The Finger object with the matching ID if one exists for this
+     * hand in this frame; otherwise, an invalid Finger object is returned.
+     * @since 1.0
+     */
+    LEAP_EXPORT Finger finger(int32_t id) const;
+
+    /**
+     * The list of Tool objects detected in this frame that are held by this
+     * hand, given in arbitrary order.
+     * The list can be empty if no tools held by this hand are detected.
+     *
+     * \include Hand_tools.txt
+     *
+     * @returns The ToolList containing all Tool objects held by this hand.
+     * @since 1.0
+     */
+    LEAP_EXPORT ToolList tools() const;
+
+    /**
+     * The Tool object with the specified ID held by this hand.
+     *
+     * Use the Hand::tool() function to retrieve a Tool object held by
+     * this hand using an ID value obtained from a previous frame.
+     * This function always returns a Tool object, but if no tool
+     * with the specified ID is present, an invalid Tool object is returned.
+     *
+     * \include Hand_tool.txt
+     *
+     * Note that ID values persist across frames, but only until tracking of a
+     * particular object is lost. If tracking of a tool is lost and subsequently
+     * regained, the new Tool object representing that tool may have a
+     * different ID than that representing the tool in an earlier frame.
+     *
+     * @param id The ID value of a Tool object from a previous frame.
+     * @returns The Tool object with the matching ID if one exists for this
+     * hand in this frame; otherwise, an invalid Tool object is returned.
+     * @since 1.0
+     */
+    LEAP_EXPORT Tool tool(int32_t id) const;
+
+    /**
+     * The center position of the palm in millimeters from the Leap Motion Controller origin.
+     *
+     * \include Hand_palmPosition.txt
+     *
+     * @returns The Vector representing the coordinates of the palm position.
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector palmPosition() const;
+
+    /**
+     * The stabilized palm position of this Hand.
+     *
+     * Smoothing and stabilization is performed in order to make
+     * this value more suitable for interaction with 2D content. The stabilized
+     * position lags behind the palm position by a variable amount, depending
+     * primarily on the speed of movement.
+     * 
+     * \include Hand_stabilizedPalmPosition.txt
+     *
+     * @returns A modified palm position of this Hand object
+     * with some additional smoothing and stabilization applied.
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector stabilizedPalmPosition() const;
+
+    /**
+     * The rate of change of the palm position in millimeters/second.
+     *
+     * \include Hand_palmVelocity.txt
+     *
+     * @returns The Vector representing the coordinates of the palm velocity.
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector palmVelocity() const;
+
+    /**
+     * The normal vector to the palm. If your hand is flat, this vector will
+     * point downward, or "out" of the front surface of your palm.
+     *
+     * \image html images/Leap_Palm_Vectors.png
+     *
+     * The direction is expressed as a unit vector pointing in the same
+     * direction as the palm normal (that is, a vector orthogonal to the palm).
+     *
+     * You can use the palm normal vector to compute the roll angle of the palm with
+     * respect to the horizontal plane:
+     *
+     * \include Hand_Get_Angles.txt
+     *
+     * @returns The Vector normal to the plane formed by the palm.
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector palmNormal() const;
+
+    /**
+     * The direction from the palm position toward the fingers.
+     *
+     * The direction is expressed as a unit vector pointing in the same
+     * direction as the directed line from the palm position to the fingers.
+     *
+     * You can use the palm direction vector to compute the pitch and yaw angles of the palm with
+     * respect to the horizontal plane:
+     *
+     * \include Hand_Get_Angles.txt
+     *
+     * @returns The Vector pointing from the palm position toward the fingers.
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector direction() const;
+
+    /**
+     * The center of a sphere fit to the curvature of this hand.
+     *
+     * \include Hand_sphereCenter.txt
+     *
+     * This sphere is placed roughly as if the hand were holding a ball.
+     *
+     * \image html images/Leap_Hand_Ball.png
+     *
+     * @returns The Vector representing the center position of the sphere.
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector sphereCenter() const;
+
+    /**
+     * The radius of a sphere fit to the curvature of this hand.
+     *
+     * This sphere is placed roughly as if the hand were holding a ball. Thus the
+     * size of the sphere decreases as the fingers are curled into a fist.
+     *
+     * \include Hand_sphereRadius.txt
+     *
+     * @returns The radius of the sphere in millimeters.
+     * @since 1.0
+     */
+    LEAP_EXPORT float sphereRadius() const;
+
+    /**
+     * The change of position of this hand between the current frame and
+     * the specified frame.
+     *
+     * The returned translation vector provides the magnitude and direction of
+     * the movement in millimeters.
+     *
+     * \include Hand_translation.txt
+     *
+     * If a corresponding Hand object is not found in sinceFrame, or if either
+     * this frame or sinceFrame are invalid Frame objects, then this method
+     * returns a zero vector.
+     *
+     * @param sinceFrame The starting frame for computing the translation.
+     * @returns A Vector representing the heuristically determined change in
+     * hand position between the current frame and that specified in the
+     * sinceFrame parameter.
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector translation(const Frame& sinceFrame) const;
+
+    /**
+     * The estimated probability that the hand motion between the current
+     * frame and the specified frame is intended to be a translating motion.
+     *
+     * \include Hand_translationProbability.txt
+     *
+     * If a corresponding Hand object is not found in sinceFrame, or if either
+     * this frame or sinceFrame are invalid Frame objects, then this method
+     * returns zero.
+     *
+     * @param sinceFrame The starting frame for computing the translation.
+     * @returns A value between 0 and 1 representing the estimated probability
+     * that the hand motion between the current frame and the specified frame
+     * is intended to be a translating motion.
+     * @since 1.0
+     */
+    LEAP_EXPORT float translationProbability(const Frame& sinceFrame) const;
+
+    /**
+     * The axis of rotation derived from the change in orientation of this
+     * hand, and any associated fingers and tools, between the current frame
+     * and the specified frame.
+     *
+     * \include Hand_rotationAxis.txt
+     *
+     * The returned direction vector is normalized.
+     *
+     * If a corresponding Hand object is not found in sinceFrame, or if either
+     * this frame or sinceFrame are invalid Frame objects, then this method
+     * returns a zero vector.
+     *
+     * @param sinceFrame The starting frame for computing the relative rotation.
+     * @returns A normalized direction Vector representing the heuristically
+     * determined axis of rotational change of the hand between the current
+     * frame and that specified in the sinceFrame parameter.
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector rotationAxis(const Frame& sinceFrame) const;
+
+    /**
+     * The angle of rotation around the rotation axis derived from the change
+     * in orientation of this hand, and any associated fingers and tools,
+     * between the current frame and the specified frame.
+     *
+     * \include Hand_rotationAngle.txt
+     *
+     * The returned angle is expressed in radians measured clockwise around the
+     * rotation axis (using the right-hand rule) between the start and end frames.
+     * The value is always between 0 and pi radians (0 and 180 degrees).
+     *
+     * If a corresponding Hand object is not found in sinceFrame, or if either
+     * this frame or sinceFrame are invalid Frame objects, then the angle of
+     * rotation is zero.
+     *
+     * @param sinceFrame The starting frame for computing the relative rotation.
+     * @returns A positive value representing the heuristically determined
+     * rotational change of the hand between the current frame and that
+     * specified in the sinceFrame parameter.
+     * @since 1.0
+     */
+    LEAP_EXPORT float rotationAngle(const Frame& sinceFrame) const;
+
+    /**
+     * The angle of rotation around the specified axis derived from the change
+     * in orientation of this hand, and any associated fingers and tools,
+     * between the current frame and the specified frame.
+     *
+     * \include Hand_rotationAngle_axis.txt
+     *
+     * The returned angle is expressed in radians measured clockwise around the
+     * rotation axis (using the right-hand rule) between the start and end frames.
+     * The value is always between -pi and pi radians (-180 and 180 degrees).
+     *
+     * If a corresponding Hand object is not found in sinceFrame, or if either
+     * this frame or sinceFrame are invalid Frame objects, then the angle of
+     * rotation is zero.
+     *
+     * @param sinceFrame The starting frame for computing the relative rotation.
+     * @param axis The axis to measure rotation around.
+     * @returns A value representing the heuristically determined rotational
+     * change of the hand between the current frame and that specified in the
+     * sinceFrame parameter around the specified axis.
+     * @since 1.0
+     */
+    LEAP_EXPORT float rotationAngle(const Frame& sinceFrame, const Vector& axis) const;
+
+    /**
+     * The transform matrix expressing the rotation derived from the change
+     * in orientation of this hand, and any associated fingers and tools,
+     * between the current frame and the specified frame.
+     *
+     * \include Hand_rotationMatrix.txt
+     *
+     * If a corresponding Hand object is not found in sinceFrame, or if either
+     * this frame or sinceFrame are invalid Frame objects, then this method
+     * returns an identity matrix.
+     *
+     * @param sinceFrame The starting frame for computing the relative rotation.
+     * @returns A transformation Matrix representing the heuristically determined
+     * rotational change of the hand between the current frame and that specified
+     * in the sinceFrame parameter.
+     * @since 1.0
+     */
+    LEAP_EXPORT Matrix rotationMatrix(const Frame& sinceFrame) const;
+
+    /**
+     * The estimated probability that the hand motion between the current
+     * frame and the specified frame is intended to be a rotating motion.
+     *
+     * \include Hand_rotationProbability.txt
+     *
+     * If a corresponding Hand object is not found in sinceFrame, or if either
+     * this frame or sinceFrame are invalid Frame objects, then this method
+     * returns zero.
+     *
+     * @param sinceFrame The starting frame for computing the relative rotation.
+     * @returns A value between 0 and 1 representing the estimated probability
+     * that the hand motion between the current frame and the specified frame
+     * is intended to be a rotating motion.
+     * @since 1.0
+     */
+    LEAP_EXPORT float rotationProbability(const Frame& sinceFrame) const;
+
+    /**
+     * The scale factor derived from this hand's motion between the current frame
+     * and the specified frame.
+     *
+     * The scale factor is always positive. A value of 1.0 indicates no
+     * scaling took place. Values between 0.0 and 1.0 indicate contraction
+     * and values greater than 1.0 indicate expansion.
+     *
+     * \include Hand_scaleFactor.txt
+     *
+     * The Leap Motion software derives scaling from the relative inward or outward motion of
+     * a hand and its associated fingers and tools (independent of translation
+     * and rotation).
+     *
+     * If a corresponding Hand object is not found in sinceFrame, or if either
+     * this frame or sinceFrame are invalid Frame objects, then this method
+     * returns 1.0.
+     *
+     * @param sinceFrame The starting frame for computing the relative scaling.
+     * @returns A positive value representing the heuristically determined
+     * scaling change ratio of the hand between the current frame and that
+     * specified in the sinceFrame parameter.
+     * @since 1.0
+     */
+    LEAP_EXPORT float scaleFactor(const Frame& sinceFrame) const;
+
+    /**
+     * The estimated probability that the hand motion between the current
+     * frame and the specified frame is intended to be a scaling motion.
+     *
+     * \include Hand_scaleProbability.txt
+     *
+     * If a corresponding Hand object is not found in sinceFrame, or if either
+     * this frame or sinceFrame are invalid Frame objects, then this method
+     * returns zero.
+     *
+     * @param sinceFrame The starting frame for computing the relative scaling.
+     * @returns A value between 0 and 1 representing the estimated probability
+     * that the hand motion between the current frame and the specified frame
+     * is intended to be a scaling motion.
+     * @since 1.0
+     */
+    LEAP_EXPORT float scaleProbability(const Frame& sinceFrame) const;
+
+    /**
+     * The duration of time this Hand has been visible to the Leap Motion Controller.
+     *
+     * \include Hand_timeVisible.txt
+     *
+     * @returns The duration (in seconds) that this Hand has been tracked.
+     * @since 1.0
+     */
+    LEAP_EXPORT float timeVisible() const;
+
+    /**
+     * Reports whether this is a valid Hand object.
+     *
+     * \include Hand_isValid.txt
+     *
+     * @returns True, if this Hand object contains valid tracking data.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool isValid() const;
+
+    /**
+     * Returns an invalid Hand object.
+     *
+     * \include Hand_invalid.txt
+     *
+     * You can use the instance returned by this function in comparisons testing
+     * whether a given Hand instance is valid or invalid. (You can also use the
+     * Hand::isValid() function.)
+     *
+     * @returns The invalid Hand instance.
+     * @since 1.0
+     */
+    LEAP_EXPORT static const Hand& invalid();
+
+    /**
+     * Compare Hand object equality.
+     *
+     * \include Hand_operator_equals.txt
+     *
+     * Two Hand objects are equal if and only if both Hand objects represent the
+     * exact same physical hand in the same frame and both Hand objects are valid.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool operator==(const Hand&) const;
+
+    /**
+     * Compare Hand object inequality.
+     *
+     * \include Hand_operator_not_equals.txt
+     *
+     * Two Hand objects are equal if and only if both Hand objects represent the
+     * exact same physical hand in the same frame and both Hand objects are valid.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool operator!=(const Hand&) const;
+
+    /**
+     * Writes a brief, human readable description of the Hand object to an output stream.
+     *
+     * \include Hand_operator_stream.txt
+     *
+     * @since 1.0
+     */
+    LEAP_EXPORT friend std::ostream& operator<<(std::ostream&, const Hand&);
+
+    /**
+     * A string containing a brief, human readable description of the Hand object.
+     *
+     * @returns A description of the Hand as a string.
+     * @since 1.0
+     */
+    LEAP_EXPORT std::string toString() const;
+  };
+
+  /**
+   * The Gesture class represents a recognized movement by the user.
+   *
+   * The Leap Motion Controller watches the activity within its field of view for certain movement
+   * patterns typical of a user gesture or command. For example, a movement from side to
+   * side with the hand can indicate a swipe gesture, while a finger poking forward
+   * can indicate a screen tap gesture.
+   *
+   * When the Leap Motion software recognizes a gesture, it assigns an ID and adds a
+   * Gesture object to the frame gesture list. For continuous gestures, which
+   * occur over many frames, the Leap Motion software updates the gesture by adding
+   * a Gesture object having the same ID and updated properties in each
+   * subsequent frame.
+   *
+   * **Important:** Recognition for each type of gesture must be enabled using the
+   * Controller::enableGesture() function; otherwise **no gestures are recognized or
+   * reported**.
+   *
+   * \include Gesture_Feature_enable.txt
+   *
+   * Subclasses of Gesture define the properties for the specific movement patterns
+   * recognized by the Leap Motion software.
+   *
+   * The Gesture subclasses for include:
+   *
+   * * CircleGesture -- A circular movement by a finger.
+   * * SwipeGesture -- A straight line movement by the hand with fingers extended.
+   * * ScreenTapGesture -- A forward tapping movement by a finger.
+   * * KeyTapGesture -- A downward tapping movement by a finger.
+   *
+   * Circle and swipe gestures are continuous and these objects can have a
+   * state of start, update, and stop.
+   *
+   * The screen tap gesture is a discrete gesture. The Leap Motion software only creates a single
+   * ScreenTapGesture object for each tap and it always has a stop state.
+   *
+   * Get valid Gesture instances from a Frame object. You can get a list of gestures
+   * with the Frame::gestures() method. You can get a list of gestures since a
+   * specified frame with the `Frame::gestures(const Frame&)` method. You can also
+   * use the `Frame::gesture()` method to find a gesture in the current frame using
+   * an ID value obtained in a previous frame.
+   *
+   * Gesture objects can be invalid. For example, when you get a gesture by ID
+   * using `Frame::gesture()`, and there is no gesture with that ID in the current
+   * frame, then `gesture()` returns an Invalid Gesture object (rather than a null
+   * value). Always check object validity in situations where a gesture might be
+   * invalid.
+   *
+   * The following keys can be used with the Config class to configure the gesture
+   * recognizer:
+   *
+   * Key string | Value type | Default value | Units
+   * -----------|------------|---------------|------
+   * Gesture.Circle.MinRadius | float | 5.0 | mm
+   * Gesture.Circle.MinArc | float | 1.5*pi | radians
+   * Gesture.Swipe.MinLength | float | 150 | mm
+   * Gesture.Swipe.MinVelocity | float | 1000 | mm/s
+   * Gesture.KeyTap.MinDownVelocity | float | 50 | mm/s
+   * Gesture.KeyTap.HistorySeconds | float | 0.1 | s
+   * Gesture.KeyTap.MinDistance | float | 5.0 | mm
+   * Gesture.ScreenTap.MinForwardVelocity  | float | 50 | mm/s
+   * Gesture.ScreenTap.HistorySeconds | float | 0.1 | s
+   * Gesture.ScreenTap.MinDistance | float | 3.0 | mm
+   * @since 1.0
+   */
+  class Gesture : public Interface {
+  public:
+    // For internal use only.
+    Gesture(GestureImplementation*);
+
+    /**
+     * The supported types of gestures.
+     * @since 1.0
+     */
+    enum Type {
+      /**
+       * An invalid type.
+       * @since 1.0
+       */
+      TYPE_INVALID    = -1,
+      /**
+       * A straight line movement by the hand with fingers extended.
+       * @since 1.0
+       */
+      TYPE_SWIPE      = 1,
+      /**
+       * A circular movement by a finger.
+       * @since 1.0
+       */
+      TYPE_CIRCLE     = 4,
+      /**
+       * A forward tapping movement by a finger.
+       * @since 1.0
+       */
+      TYPE_SCREEN_TAP = 5,
+      /**
+       * A downward tapping movement by a finger.
+       * @since 1.0
+       */
+      TYPE_KEY_TAP    = 6
+    };
+
+    /**
+     * The possible gesture states.
+     * @since 1.0
+     */
+    enum State {
+      /**
+       * An invalid state
+       * @since 1.0
+       */
+      STATE_INVALID = -1,
+      /**
+       * The gesture is starting. Just enough has happened to recognize it.
+       * @since 1.0
+       */
+      STATE_START   = 1,
+      /**
+       * The gesture is in progress. (Note: not all gestures have updates).
+       * @since 1.0
+       */
+      STATE_UPDATE  = 2,
+      /**
+       * The gesture has completed or stopped.
+       * @since 1.0
+       */
+      STATE_STOP    = 3,
+    };
+
+    /**
+     * Constructs a new Gesture object.
+     *
+     * An uninitialized Gesture object is considered invalid. Get valid instances
+     * of the Gesture class, which will be one of the Gesture subclasses, from a
+     * Frame object.
+     * @since 1.0
+     */
+    LEAP_EXPORT Gesture();
+
+    /**
+     * Constructs a new copy of an Gesture object.
+     *
+     * \include Gesture_Gesture_copy.txt
+     *
+     * @since 1.0
+     */
+    LEAP_EXPORT Gesture(const Gesture& rhs);
+
+    /**
+     * The gesture type.
+     *
+     * \include Gesture_type.txt
+     *
+     * @returns Gesture::Type A value from the Gesture::Type enumeration.
+     * @since 1.0
+     */
+    LEAP_EXPORT Type type() const;
+
+    /**
+     * The gesture state.
+     *
+     * Recognized movements occur over time and have a beginning, a middle,
+     * and an end. The 'state()' attribute reports where in that sequence this
+     * Gesture object falls.
+     *
+     * \include Gesture_state.txt
+     *
+     * @returns Gesture::State A value from the Gesture::State enumeration.
+     * @since 1.0
+     */
+    LEAP_EXPORT State state() const;
+
+    /**
+     * The gesture ID.
+     *
+     * All Gesture objects belonging to the same recognized movement share the
+     * same ID value. Use the ID value with the Frame::gesture() method to
+     * find updates related to this Gesture object in subsequent frames.
+     *
+     * \include Gesture_id.txt
+     *
+     * @returns int32_t the ID of this Gesture.
+     * @since 1.0
+     */
+    LEAP_EXPORT int32_t id() const;
+
+    /**
+     * The elapsed duration of the recognized movement up to the
+     * frame containing this Gesture object, in microseconds.
+     *
+     * \include Gesture_duration.txt
+     *
+     * The duration reported for the first Gesture in the sequence (with the
+     * STATE_START state) will typically be a small positive number since
+     * the movement must progress far enough for the Leap Motion software to recognize it as
+     * an intentional gesture.
+     *
+     * @return int64_t the elapsed duration in microseconds.
+     * @since 1.0
+     */
+    LEAP_EXPORT int64_t duration() const;
+
+    /**
+     * The elapsed duration in seconds.
+     *
+     * \include Gesture_durationSeconds.txt
+     *
+     * @see duration()
+     * @return float the elapsed duration in seconds.
+     * @since 1.0
+     */
+    LEAP_EXPORT float durationSeconds() const;
+
+    /**
+     * The Frame containing this Gesture instance.
+     *
+     * \include Gesture_frame.txt
+     _
+     * @return Frame The parent Frame object.
+     * @since 1.0
+     */
+    LEAP_EXPORT Frame frame() const;
+
+    /**
+     * The list of hands associated with this Gesture, if any.
+     *
+     * \include Gesture_hands.txt
+     *
+     * If no hands are related to this gesture, the list is empty.
+     *
+     * @return HandList the list of related Hand objects.
+     * @since 1.0
+     */
+    LEAP_EXPORT HandList hands() const;
+
+    /**
+     * The list of fingers and tools associated with this Gesture, if any.
+     *
+     * If no Pointable objects are related to this gesture, the list is empty.
+     *
+     * \include Gesture_pointables.txt
+     *
+     * @return PointableList the list of related Pointable objects.
+     * @since 1.0
+     */
+    LEAP_EXPORT PointableList pointables() const;
+
+    /**
+     * Reports whether this Gesture instance represents a valid Gesture.
+     *
+     * An invalid Gesture object does not represent a snapshot of a recognized
+     * movement. Invalid Gesture objects are returned when a valid object cannot
+     * be provided. For example, when you get an gesture by ID
+     * using Frame::gesture(), and there is no gesture with that ID in the current
+     * frame, then gesture() returns an Invalid Gesture object (rather than a null
+     * value). Always check object validity in situations where an gesture might be
+     * invalid.
+     *
+     * \include Gesture_isValid.txt
+     *
+     * @returns bool True, if this is a valid Gesture instance; false, otherwise.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool isValid() const;
+
+    /**
+     * Compare Gesture object equality.
+     *
+     * \include Gesture_operator_equals.txt
+     *
+     * Two Gestures are equal if they represent the same snapshot of the same
+     * recognized movement.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool operator==(const Gesture& rhs) const;
+
+    /**
+     * Compare Gesture object inequality.
+     *
+     * \include Gesture_operator_not_equals.txt
+     *
+     * Two Gestures are equal only if they represent the same snapshot of the same
+     * recognized movement.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool operator!=(const Gesture& rhs) const;
+
+    /**
+     * A string containing a brief, human-readable description of this
+     * Gesture.
+     *
+     * \include Gesture_toString.txt
+     *
+     * @since 1.0
+     */
+    LEAP_EXPORT std::string toString() const;
+
+    /**
+     * Returns an invalid Gesture object.
+     *
+     * You can use the instance returned by this function in comparisons testing
+     * whether a given Gesture instance is valid or invalid. (You can also use the
+     * Gesture::isValid() function.)
+     *
+     * \include Gesture_invalid.txt
+     *
+     * @returns The invalid Gesture instance.
+     * @since 1.0
+     */
+    LEAP_EXPORT static const Gesture& invalid();
+  };
+
+  /**
+   * The SwipeGesture class represents a swiping motion of a finger or tool.
+   *
+   * \image html images/Leap_Gesture_Swipe.png
+   *
+   * **Important:** To use swipe gestures in your application, you must enable
+   * recognition of the swipe gesture. You can enable recognition with:
+   *
+   * \include Gesture_Swipe_Enable.txt
+   *
+   * Swipe gestures are continuous.
+   *
+   * You can set the minimum length and velocity required for a movement
+   * to be recognized as a swipe using the config attribute of a connected
+   * Controller object. Use the following keys to configure swipe recognition:
+   *
+   * Key string | Value type | Default value | Units
+   * -----------|------------|---------------|------
+   * Gesture.Swipe.MinLength | float | 150 | mm
+   * Gesture.Swipe.MinVelocity | float | 1000 | mm/s
+   *
+   * The following example demonstrates how to set the swipe configuration
+   * parameters:
+   *
+   * \include Gesture_Swipe_Params.txt
+   * @since 1.0
+   */
+  class SwipeGesture : public Gesture
+  {
+  public:
+    /**
+     * The swipe gesture type.
+     *
+     * \include SwipeGesture_classType.txt
+     *
+     * @returns Type The type value designating a swipe gesture.
+     * @since 1.0
+     */
+    static Type classType() { return TYPE_SWIPE; }
+
+    LEAP_EXPORT SwipeGesture();
+
+    /**
+     * Constructs a SwipeGesture object from an instance of the Gesture class.
+     *
+     * \include SwipeGesture_SwipeGesture.txt
+     *
+     * @param rhs The Gesture instance to specialize. This Gesture instance must
+     * be a SwipeGesture object.
+     * @since 1.0
+     */
+    LEAP_EXPORT SwipeGesture(const Gesture& rhs);
+
+    /**
+     * The position where the swipe began.
+     *
+     * \include SwipeGesture_startPosition.txt
+     *
+     * @returns Vector The starting position within the Leap Motion frame of
+     * reference, in mm.
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector startPosition() const;
+
+    /**
+     * The current position of the swipe.
+     *
+     * \include SwipeGesture_position.txt
+     *
+     * @returns Vector The current swipe position within the Leap Motion frame of
+     * reference, in mm.
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector position() const;
+
+    /**
+     * The unit direction vector parallel to the swipe motion.
+     *
+     * \include SwipeGesture_direction.txt
+     *
+     * You can compare the components of the vector to classify the swipe as
+     * appropriate for your application. For example, if you are using swipes
+     * for two dimensional scrolling, you can compare the x and y values to
+     * determine if the swipe is primarily horizontal or vertical.
+     *
+     * @returns Vector The unit direction vector representing the swipe
+     * motion.
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector direction() const;
+
+    /**
+     * The swipe speed in mm/second.
+     *
+     * \include SwipeGesture_speed.txt
+     *
+     * @returns float The speed of the finger performing the swipe gesture in
+     * millimeters per second.
+     * @since 1.0
+     */
+    LEAP_EXPORT float speed() const;
+
+    /**
+     * The finger performing the swipe gesture.
+     *
+     * \include SwipeGesture_pointable.txt
+     *
+     * @returns Pointable A Pointable object representing the swiping finger.
+     * @since 1.0
+     */
+    LEAP_EXPORT Pointable pointable() const;
+  };
+
+  /**
+   * The CircleGesture classes represents a circular finger movement.
+   *
+   * A circle movement is recognized when the tip of a finger draws a circle
+   * within the Leap Motion Controller field of view.
+   *
+   * \image html images/Leap_Gesture_Circle.png
+   *
+   * **Important:** To use circle gestures in your application, you must enable
+   * recognition of the circle gesture. You can enable recognition with:
+   *
+   * \include Gesture_Circle_Enable.txt
+   *
+   * Circle gestures are continuous. The CircleGesture objects for the gesture have
+   * three possible states:
+   *
+   * * State::STATE_START -- The circle gesture has just started. The movement has
+   *   progressed far enough for the recognizer to classify it as a circle.
+   * * State::STATE_UPDATE -- The circle gesture is continuing.
+   * * State::STATE_STOP -- The circle gesture is finished.
+   *
+   * You can set the minimum radius and minimum arc length required for a movement
+   * to be recognized as a circle using the config attribute of a connected
+   * Controller object. Use the following keys to configure circle recognition:
+   *
+   * Key string | Value type | Default value | Units
+   * -----------|------------|---------------|------
+   * Gesture.Circle.MinRadius | float | 5.0 | mm
+   * Gesture.Circle.MinArc | float | 1.5*pi | radians
+   *
+   * The following example demonstrates how to set the circle configuration
+   * parameters:
+   *
+   * \include Gesture_Circle_Params.txt
+   *
+   * @since 1.0
+   */
+  class CircleGesture : public Gesture
+  {
+  public:
+    /**
+     * The circle gesture type.
+     *
+     * \include CircleGesture_classType.txt
+     *
+     * @returns Type The type value designating a circle gesture.
+     * @since 1.0
+     */
+    static Type classType() { return TYPE_CIRCLE; }
+
+    /**
+     * Constructs a new CircleGesture object.
+     *
+     * An uninitialized CircleGesture object is considered invalid. Get valid instances
+     * of the CircleGesture class from a Frame object.
+     * @since 1.0
+     */
+    LEAP_EXPORT CircleGesture();
+
+    /**
+     * Constructs a CircleGesture object from an instance of the Gesture class.
+     *
+     * \include CircleGesture_CircleGesture.txt
+     *
+     * @param rhs The Gesture instance to specialize. This Gesture instance must
+     * be a CircleGesture object.
+     * @since 1.0
+     */
+    LEAP_EXPORT CircleGesture(const Gesture& rhs);
+
+    /**
+     * The center point of the circle within the Leap Motion frame of reference.
+     *
+     * \include CircleGesture_center.txt
+     * @returns Vector The center of the circle in mm from the Leap Motion origin.
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector center() const;
+
+    /**
+     * Returns the normal vector for the circle being traced.
+     *
+     * If you draw the circle clockwise, the normal vector points in the same
+     * general direction as the pointable object drawing the circle. If you draw
+     * the circle counterclockwise, the normal points back toward the
+     * pointable. If the angle between the normal and the pointable object
+     * drawing the circle is less than 90 degrees, then the circle is clockwise.
+     *
+     * \include Gesture_Circle_Direction.txt
+     *
+     * @return Vector the normal vector for the circle being traced
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector normal() const;
+
+    /**
+     * The number of times the finger tip has traversed the circle.
+     *
+     * Progress is reported as a positive number of the number. For example,
+     * a progress value of .5 indicates that the finger has gone halfway
+     * around, while a value of 3 indicates that the finger has gone around
+     * the the circle three times.
+     *
+     * \include CircleGesture_progress.txt
+     *
+     * Progress starts where the circle gesture began. Since the circle
+     * must be partially formed before the Leap Motion software can recognize it, progress
+     * will be greater than zero when a circle gesture first appears in the
+     * frame.
+     *
+     * @returns float A positive number indicating the gesture progress.
+     * @since 1.0
+     */
+    LEAP_EXPORT float progress() const;
+
+    /**
+     * The radius of the circle.
+     *
+     * \include CircleGesture_radius.txt
+     *
+     * @returns The circle radius in mm.
+     * @since 1.0
+     */
+    LEAP_EXPORT float radius() const;
+
+    /**
+     * The finger performing the circle gesture.
+     *
+     * \include CircleGesture_pointable.txt
+     *
+     * @returns Pointable A Pointable object representing the circling finger.
+     * @since 1.0
+     */
+    LEAP_EXPORT Pointable pointable() const;
+  };
+
+  /**
+   * The ScreenTapGesture class represents a tapping gesture by a finger or tool.
+   *
+   * A screen tap gesture is recognized when the tip of a finger pokes forward
+   * and then springs back to approximately the original postion, as if
+   * tapping a vertical screen. The tapping finger must pause briefly before beginning the tap.
+   *
+   * \image html images/Leap_Gesture_Tap2.png
+   *
+   * **Important:** To use screen tap gestures in your application, you must enable
+   * recognition of the screen tap gesture. You can enable recognition with:
+   *
+   * \include Gesture_ScreenTap_Enable.txt
+   *
+   * ScreenTap gestures are discrete. The ScreenTapGesture object representing a tap always
+   * has the state, STATE_STOP. Only one ScreenTapGesture object is created for each
+   * screen tap gesture recognized.
+   *
+   * You can set the minimum finger movement and velocity required for a movement
+   * to be recognized as a screen tap as well as adjust the detection window for
+   * evaluating the movement using the config attribute of a connected
+   * Controller object. Use the following keys to configure screen tap recognition:
+   *
+   * Key string | Value type | Default value | Units
+   * -----------|------------|---------------|------
+   * Gesture.ScreenTap.MinForwardVelocity  | float | 50 | mm/s
+   * Gesture.ScreenTap.HistorySeconds | float | 0.1 | s
+   * Gesture.ScreenTap.MinDistance | float | 3.0 | mm
+   *
+   * The following example demonstrates how to set the screen tap configuration
+   * parameters:
+   *
+   * \include Gesture_ScreenTap_Params.txt
+   * @since 1.0
+   */
+  class ScreenTapGesture : public Gesture
+  {
+  public:
+    /**
+     * The screen tap gesture type.
+     *
+     * \include ScreenTapGesture_classType.txt
+     *
+     * @returns Type The type value designating a screen tap gesture.
+     * @since 1.0
+     */
+    static Type classType() { return TYPE_SCREEN_TAP; }
+
+    /**
+     * Constructs a new ScreenTapGesture object.
+     *
+     * An uninitialized ScreenTapGesture object is considered invalid. Get valid instances
+     * of the ScreenTapGesture class from a Frame object.
+     * @since 1.0
+     */
+    LEAP_EXPORT ScreenTapGesture();
+
+    /**
+     * Constructs a ScreenTapGesture object from an instance of the Gesture class.
+     *
+     * \include ScreenTapGesture_ScreenTapGesture.txt
+     *
+     * @param rhs The Gesture instance to specialize. This Gesture instance must
+     * be a ScreenTapGesture object.
+     * @since 1.0
+     */
+    LEAP_EXPORT ScreenTapGesture(const Gesture& rhs);
+
+    /**
+     * The position where the screen tap is registered.
+     *
+     * \include ScreenTapGesture_position.txt
+     *
+     * @return Vector A Vector containing the coordinates of screen tap location.
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector position() const;
+
+    /**
+     * The direction of finger tip motion.
+     *
+     * \include ScreenTapGesture_direction.txt
+     *
+     * @returns Vector A unit direction vector.
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector direction() const;
+
+    /**
+     * The progess value is always 1.0 for a screen tap gesture.
+     *
+     * @returns float The value 1.0.
+     * @since 1.0
+     */
+    LEAP_EXPORT float progress() const;
+
+    /**
+     * The finger performing the screen tap gesture.
+     *
+     * \include ScreenTapGesture_pointable.txt
+     *
+     * @returns Pointable A Pointable object representing the tapping finger.
+     * @since 1.0
+     */
+    LEAP_EXPORT Pointable pointable() const;
+  };
+
+  /**
+   * The KeyTapGesture class represents a tapping gesture by a finger or tool.
+   *
+   * A key tap gesture is recognized when the tip of a finger rotates down toward the
+   * palm and then springs back to approximately the original postion, as if
+   * tapping. The tapping finger must pause briefly before beginning the tap.
+   *
+   * \image html images/Leap_Gesture_Tap.png
+   *
+   * **Important:** To use key tap gestures in your application, you must enable
+   * recognition of the key tap gesture. You can enable recognition with:
+   *
+   * \include Gesture_KeyTap_Enable.txt
+   *
+   * Key tap gestures are discrete. The KeyTapGesture object representing a tap always
+   * has the state, STATE_STOP. Only one KeyTapGesture object is created for each
+   * key tap gesture recognized.
+   *
+   * You can set the minimum finger movement and velocity required for a movement
+   * to be recognized as a key tap as well as adjust the detection window for
+   * evaluating the movement using the config attribute of a connected
+   * Controller object. Use the following configuration keys to configure key tap
+   * recognition:
+   *
+   * Key string | Value type | Default value | Units
+   * -----------|------------|---------------|------
+   * Gesture.KeyTap.MinDownVelocity | float | 50 | mm/s
+   * Gesture.KeyTap.HistorySeconds | float | 0.1 | s
+   * Gesture.KeyTap.MinDistance | float | 5.0 | mm
+   *
+   * The following example demonstrates how to set the key tap configuration
+   * parameters:
+   *
+   * \include Gesture_KeyTap_Params.txt
+   *
+   * @since 1.0
+   */
+  class KeyTapGesture : public Gesture
+  {
+  public:
+    /**
+     * The key tap gesture type.
+     *
+     * \include KeyTapGesture_classType.txt
+     *
+     * @returns Type The type value designating a key tap gesture.
+     * @since 1.0
+     */
+    static Type classType() { return TYPE_KEY_TAP; }
+
+    /**
+     * Constructs a new KeyTapGesture object.
+     *
+     * An uninitialized KeyTapGesture object is considered invalid. Get valid instances
+     * of the KeyTapGesture class from a Frame object.
+     * @since 1.0
+     */
+    LEAP_EXPORT KeyTapGesture();
+
+    /**
+     * Constructs a KeyTapGesture object from an instance of the Gesture class.
+     *
+     * \include KeyTapGesture_KeyTapGesture.txt
+     *
+     * @param rhs The Gesture instance to specialize. This Gesture instance must
+     * be a KeyTapGesture object.
+     * @since 1.0
+     */
+    LEAP_EXPORT KeyTapGesture(const Gesture& rhs);
+
+    /**
+     * The position where the key tap is registered.
+     *
+     * \include KeyTapGesture_position.txt
+     *
+     * @return Vector A Vector containing the coordinates of tap location.
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector position() const;
+
+    /**
+     * The direction of finger tip motion.
+     *
+     * \include KeyTapGesture_direction.txt
+     *
+     * @returns Vector A unit direction vector if the finger tip is moving;
+     * otherwise, a zero-vector.
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector direction() const;
+
+    /**
+     * The progess value is always 1.0 for a key tap gesture.
+     *
+     * @returns float The value 1.0.
+     * @since 1.0
+     */
+    LEAP_EXPORT float progress() const;
+
+    /**
+     * The finger performing the key tap gesture.
+     *
+     * \include KeyTapGesture_pointable.txt
+     *
+     * @returns Pointable A Pointable object representing the tapping finger.
+     * @since 1.0
+     */
+    LEAP_EXPORT Pointable pointable() const;
+  };
+
+  /*
+   * The Screen class is currently unsupported.
+   *
+   * We are re-evaluating this feature due to the cumbersome location process
+   * required to use it and the amount of confusion about the feature's purpose.
+   *
+   * The Screen class represents a computer monitor screen.
+   *
+   * The Screen class reports characteristics describing the position and
+   * orientation of the monitor screen within the Leap Motion coordinate system. These
+   * characteristics include the bottom-left corner position of the screen,
+   * direction vectors for the horizontal and vertical axes of the screen, and
+   * the screen's normal vector. The screen must be properly registered with the
+   * Screen Locator for the Leap Motion software to report these characteristics accurately.
+   * The Screen class also reports the size of the screen in pixels, using
+   * information obtained from the operating system. (Run the Screen Locator
+   * from the Leap Motion Settings dialog, on the Screen page.)
+   *
+   * You can get the point of intersection between the screen and a ray
+   * projected from a Pointable object using the Screen::intersect() function.
+   * Likewise, you can get the closest point on the screen to a point in space
+   * using the Screen::project() function. Again, the screen location
+   * must be registered with the Screen Locator for these functions to
+   * return accurate values.
+   *
+   * Note that Screen objects can be invalid, which means that they do not contain
+   * valid screen coordinate data and do not correspond to a physical entity.
+   * Test for validity with the Screen::isValid() function.
+   * @since 1.0
+   */
+  class Screen : public Interface {
+  public:
+    // For internal use only.
+    Screen(ScreenImplementation*);
+
+    /**
+     * Constructs a Screen object.
+     *
+     * An uninitialized screen is considered invalid.
+     * Get valid Screen objects from a ScreenList object obtained using the
+     * Controller::locatedScreens() method.
+     * @since 1.0
+     */
+    LEAP_EXPORT Screen();
+
+    /**
+     * A unique identifier for this screen based on the screen
+     * information in the configuration. A default screen with ID, *0*,
+     * always exists and contains default characteristics, even if no screens
+     * have been located.
+     * @since 1.0
+     */
+    LEAP_EXPORT int32_t id() const;
+
+    /**
+     * Returns the intersection between this screen and a ray projecting from a
+     * Pointable object.
+     *
+     * The projected ray emanates from the Pointable tipPosition along the
+     * Pointable's direction vector.
+     *
+     * Set the normalize parameter to true to request the intersection point in
+     * normalized screen coordinates. Normalized screen coordinates are usually
+     * values between 0 and 1, where 0 represents the screen's origin at the
+     * bottom-left corner and 1 represents the opposite edge (either top or
+     * right). When you request normalized coordinates, the z-component of the
+     * returned vector is zero. Multiply a normalized coordinate by the values
+     * returned by Screen::widthPixels() or Screen::heightPixels() to calculate
+     * the screen position in pixels (remembering that many other computer
+     * graphics coordinate systems place the origin in the top-left corner).
+     *
+     * Set the normalize parameter to false to request the intersection point
+     * in Leap Motion coordinates (millimeters from the Leap Motion origin).
+     *
+     * If the Pointable object points outside the screen's border (but still
+     * intersects the plane in which the screen lies), the returned intersection
+     * point is clamped to the nearest point on the edge of the screen.
+     *
+     * You can use the clampRatio parameter to contract or expand the area in
+     * which you can point. For example, if you set the clampRatio parameter to
+     * 0.5, then the positions reported for intersection points outside the
+     * central 50% of the screen are moved to the border of this smaller area.
+     * If, on the other hand, you expanded the area by setting clampRatio to
+     * a value such as 3.0, then you could point well outside screen's physical
+     * boundary before the intersection points would be clamped. The positions
+     * for any points clamped would also be placed on this larger outer border.
+     * The positions reported for any intersection points inside the clamping
+     * border are unaffected by clamping.
+     *
+     * \include Screen_Normalized_2.txt
+     *
+     * If the Pointable object does not point toward the plane of the screen
+     * (i.e. it is pointing parallel to or away from the screen), then the
+     * components of the returned vector are all set to NaN (not-a-number).
+     *
+     * @param pointable The Pointable object to check for screen intersection.
+     *
+     * @param normalize If true, return normalized coordinates representing
+     * the intersection point as a percentage of the screen's width and height.
+     * If false, return Leap Motion coordinates (millimeters from the Leap Motion origin,
+     * which is located at the center of the top surface of the Leap Motion Controller).
+     * If true and the clampRatio parameter is set to 1.0, coordinates will be
+     * of the form (0..1, 0..1, 0). Setting the clampRatio to a different value
+     * changes the range for normalized coordinates. For example, a clampRatio
+     * of 5.0 changes the range of values to be of the form (-2..3, -2..3, 0).
+     *
+     * @param clampRatio Adjusts the clamping border around this screen.
+     * By default this ratio is 1.0, and the border corresponds to the actual
+     * boundaries of the screen. Setting clampRatio to 0.5 would reduce the
+     * interaction area. Likewise, setting the ratio to 2.0 would increase the
+     * interaction area, adding 50% around each edge of the physical monitor.
+     * Intersection points outside the interaction area are repositioned to
+     * the closest point on the clamping border before the vector is returned.
+     *
+     * @returns A Vector containing the coordinates of the intersection between
+     * this screen and a ray projecting from the specified Pointable object.
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector intersect(const Pointable& pointable, bool normalize, float clampRatio = 1.0f) const;
+
+    /**
+     * Returns the intersection between this screen and a ray projecting from
+     * the specified position along the specified direction.
+     *
+     * Set the normalize parameter to true to request the intersection point in
+     * normalized screen coordinates. Normalized screen coordinates are usually
+     * values between 0 and 1, where 0 represents the screen's origin at the
+     * bottom-left corner and 1 represents the opposite edge (either top or
+     * right). When you request normalized coordinates, the z-component of the
+     * returned vector is zero. Multiply a normalized coordinate by the values
+     * returned by Screen::widthPixels() or Screen::heightPixels() to calculate
+     * the screen position in pixels (remembering that many other computer
+     * graphics coordinate systems place the origin in the top-left corner).
+     *
+     * Set the normalize parameter to false to request the intersection point
+     * in Leap Motion coordinates (millimeters from the Leap Motion origin).
+     *
+     * If the specified ray points outside the screen's border (but still
+     * intersects the plane in which the screen lies), the returned intersection
+     * point is clamped to the nearest point on the edge of the screen.
+     *
+     * You can use the clampRatio parameter to contract or expand the area in
+     * which you can point. For example, if you set the clampRatio parameter to
+     * 0.5, then the positions reported for intersection points outside the
+     * central 50% of the screen are moved to the border of this smaller area.
+     * If, on the other hand, you expanded the area by setting clampRatio to
+     * a value such as 3.0, then you could point well outside screen's physical
+     * boundary before the intersection points would be clamped. The positions
+     * for any points clamped would also be placed on this larger outer border.
+     * The positions reported for any intersection points inside the clamping
+     * border are unaffected by clamping.
+     *
+     * If the specified ray does not point toward the plane of the screen
+     * (i.e. it is pointing parallel to or away from the screen), then the
+     * components of the returned vector are all set to NaN (not-a-number).
+     *
+     * @param position The position from which to check for screen intersection.
+     * @param direction The direction in which to check for screen intersection.
+     *
+     * @param normalize If true, return normalized coordinates representing
+     * the intersection point as a percentage of the screen's width and height.
+     * If false, return Leap Motion coordinates (millimeters from the Leap Motion origin,
+     * which is located at the center of the top surface of the Leap Motion Controller).
+     * If true and the clampRatio parameter is set to 1.0, coordinates will be
+     * of the form (0..1, 0..1, 0). Setting the clampRatio to a different value
+     * changes the range for normalized coordinates. For example, a clampRatio
+     * of 5.0 changes the range of values to be of the form (-2..3, -2..3, 0).
+     *
+     * @param clampRatio Adjusts the clamping border around this screen.
+     * By default this ratio is 1.0, and the border corresponds to the actual
+     * boundaries of the screen. Setting clampRatio to 0.5 would reduce the
+     * interaction area. Likewise, setting the ratio to 2.0 would increase the
+     * interaction area, adding 50% around each edge of the physical monitor.
+     * Intersection points outside the interaction area are repositioned to
+     * the closest point on the clamping border before the vector is returned.
+     *
+     * @returns A Vector containing the coordinates of the intersection between
+     * this screen and a ray projecting from the specified position in the
+     * specified direction.
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector intersect(const Vector& position, const Vector& direction, bool normalize, float clampRatio = 1.0f) const;
+
+    /**
+     * Returns the projection from the specified position onto this screen.
+     *
+     * Set the normalize parameter to true to request the projection point in
+     * normalized screen coordinates. Normalized screen coordinates are usually
+     * values between 0 and 1, where 0 represents the screen's origin at the
+     * bottom-left corner and 1 represents the opposite edge (either top or
+     * right). When you request normalized coordinates, the z-component of the
+     * returned vector is zero. Multiply a normalized coordinate by the values
+     * returned by Screen::widthPixels() or Screen::heightPixels() to calculate
+     * the screen position in pixels (remembering that many other computer
+     * graphics coordinate systems place the origin in the top-left corner).
+     *
+     * Set the normalize parameter to false to request the projection point
+     * in Leap Motion coordinates (millimeters from the Leap Motion origin).
+     *
+     * If the specified point projects outside the screen's border, the returned
+     * projection point is clamped to the nearest point on the edge of the screen.
+     *
+     * You can use the clampRatio parameter to contract or expand the area in
+     * which you can point. For example, if you set the clampRatio parameter to
+     * 0.5, then the positions reported for projection points outside the
+     * central 50% of the screen are moved to the border of this smaller area.
+     * If, on the other hand, you expanded the area by setting clampRatio to
+     * a value such as 3.0, then you could point well outside screen's physical
+     * boundary before the projection points would be clamped. The positions
+     * for any points clamped would also be placed on this larger outer border.
+     * The positions reported for any projection points inside the clamping
+     * border are unaffected by clamping.
+     *
+     * @param position The position from which to project onto this screen.
+     *
+     * @param normalize If true, return normalized coordinates representing
+     * the projection point as a percentage of the screen's width and height.
+     * If false, return Leap Motion coordinates (millimeters from the Leap Motion origin,
+     * which is located at the center of the top surface of the Leap Motion Controller).
+     * If true and the clampRatio parameter is set to 1.0, coordinates will be
+     * of the form (0..1, 0..1, 0). Setting the clampRatio to a different value
+     * changes the range for normalized coordinates. For example, a clampRatio
+     * of 5.0 changes the range of values to be of the form (-2..3, -2..3, 0).
+     *
+     * @param clampRatio Adjusts the clamping border around this screen.
+     * By default this ratio is 1.0, and the border corresponds to the actual
+     * boundaries of the screen. Setting clampRatio to 0.5 would reduce the
+     * interaction area. Likewise, setting the ratio to 2.0 would increase the
+     * interaction area, adding 50% around each edge of the physical monitor.
+     * Projection points outside the interaction area are repositioned to
+     * the closest point on the clamping border before the vector is returned.
+     *
+     * @returns A Vector containing the coordinates of the projection between
+     * this screen and a ray projecting from the specified position onto the
+     * screen along its normal vector.
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector project(const Vector& position, bool normalize, float clampRatio = 1.0f) const;
+
+    /**
+     * A Vector representing the horizontal axis of this Screen within the
+     * Leap Motion coordinate system.
+     *
+     * The magnitude of this vector estimates the physical width of this Screen
+     * in millimeters. The direction of this vector is parallel to the bottom
+     * edge of the screen and points toward the right edge of the screen.
+     *
+     * Together, horizontalAxis(), verticalAxis(), and bottomLeftCorner()
+     * describe the physical position, size and orientation of this Screen.
+     *
+     * @returns A Vector representing the bottom, horizontal edge of this Screen.
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector horizontalAxis() const;
+
+    /**
+     * A Vector representing the vertical axis of this Screen within the
+     * Leap Motion coordinate system.
+     *
+     * The magnitude of this vector estimates the physical height of this Screen
+     * in millimeters. The direction of this vector is parallel to the left
+     * edge of the screen and points toward the top edge of the screen.
+     *
+     * Together, horizontalAxis(), verticalAxis(), and bottomLeftCorner()
+     * describe the physical position, size and orientation of this screen.
+     *
+     * @returns A Vector representing the left, vertical edge of this Screen.
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector verticalAxis() const;
+
+    /**
+     * A Vector representing the bottom left corner of this Screen within the
+     * Leap Motion coordinate system.
+     *
+     * The point represented by this vector defines the origin of the screen
+     * in the Leap Motion coordinate system.
+     *
+     * Together, horizontalAxis(), verticalAxis(), and bottomLeftCorner()
+     * describe the physical position, size and orientation of this Screen.
+     *
+     * @returns A Vector containing the coordinates of the bottom-left corner
+     * of this Screen.
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector bottomLeftCorner() const;
+
+    /**
+     * A Vector normal to the plane in which this Screen lies.
+     *
+     * The normal vector is a unit direction vector orthogonal to the screen's
+     * surface plane. It points toward a viewer positioned for typical use of
+     * the monitor.
+     *
+     * @returns A Vector representing this Screen's normal vector.
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector normal() const;
+
+    /**
+     * The horizontal resolution of this screen, in pixels.
+     *
+     * @returns The width of this Screen in pixels.
+     * @since 1.0
+     */
+    LEAP_EXPORT int widthPixels() const;
+
+    /**
+     * The vertical resolution of this screen, in pixels.
+     *
+     * @returns The height of this Screen in pixels.
+     * @since 1.0
+     */
+    LEAP_EXPORT int heightPixels() const;
+
+    /**
+     * The shortest distance from the specified point to the plane in which this
+     * Screen lies.
+     *
+     * @returns The length of the perpendicular line segment extending from
+     * the plane this Screen lies in to the specified point.
+     * @since 1.0
+     */
+    LEAP_EXPORT float distanceToPoint(const Vector& point) const;
+
+    /**
+     * Reports whether this is a valid Screen object.
+     *
+     * **Important:** A valid Screen object does not necessarily contain
+     * up-to-date screen location information. Location information is only
+     * accurate until the Leap Motion Controller or the monitor are moved. In addition, the
+     * primary screen always contains default location information even if the
+     * user has never run the screen location utility. This default location
+     * information will not return accurate results.
+     *
+     * @returns True, if this Screen object contains valid data.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool isValid() const;
+
+    /**
+     * Returns an invalid Screen object.
+     *
+     * You can use the instance returned by this function in comparisons testing
+     * whether a given Screen instance is valid or invalid. (You can also use the
+     * Screen::isValid() function.)
+     *
+     * @returns The invalid Screen instance.
+     * @since 1.0
+     */
+    LEAP_EXPORT static const Screen& invalid();
+
+    /**
+     * Compare Screen object equality.
+     * Two Screen objects are equal if and only if both Screen objects represent the
+     * exact same Screens and both Screens are valid.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool operator==(const Screen&) const;
+
+    /**
+     * Compare Screen object inequality.
+     * Two Screen objects are equal if and only if both Screen objects represent the
+     * exact same Screens and both Screens are valid.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool operator!=(const Screen&) const;
+
+    /**
+     * Writes a brief, human readable description of the Screen object.
+     * @since 1.0
+     */
+    LEAP_EXPORT friend std::ostream& operator<<(std::ostream&, const Screen&);
+
+    /**
+     * A string containing a brief, human readable description of the Screen object.
+     *
+     * @returns A description of the Screen as a string.
+     * @since 1.0
+     */
+    LEAP_EXPORT std::string toString() const;
+  };
+
+  /**
+   * The Device class represents a physically connected device.
+   *
+   * The Device class contains information related to a particular connected
+   * device such as field of view, device id, and calibrated positions.
+   *
+   * Note that Device objects can be invalid, which means that they do not contain
+   * valid device information and do not correspond to a physical device.
+   * Test for validity with the Device::isValid() function.
+   * @since 1.0
+   */
+  class Device : public Interface {
+  public:
+    // For internal use only.
+    Device(DeviceImplementation*);
+
+    /**
+     * Constructs a Device object.
+     *
+     * An uninitialized device is considered invalid.
+     * Get valid Device objects from a DeviceList object obtained using the
+     * Controller::devices() method.
+     *
+     * \include Device_Device.txt
+     *
+     * @since 1.0
+     */
+    LEAP_EXPORT Device();
+
+    /**
+     * The angle of view along the x axis of this device.
+     *
+     * \image html images/Leap_horizontalViewAngle.png
+     *
+     * The Leap Motion controller scans a region in the shape of an inverted pyramid
+     * centered at the device's center and extending upwards. The horizontalViewAngle
+     * reports the view angle along the long dimension of the device.
+     *
+     * \include Device_horizontalViewAngle.txt
+     *
+     * @returns The horizontal angle of view in radians.
+     * @since 1.0
+     */
+    LEAP_EXPORT float horizontalViewAngle() const;
+
+    /**
+     * The angle of view along the z axis of this device.
+     *
+     * \image html images/Leap_verticalViewAngle.png
+     *
+     * The Leap Motion controller scans a region in the shape of an inverted pyramid
+     * centered at the device's center and extending upwards. The verticalViewAngle
+     * reports the view angle along the short dimension of the device.
+     *
+     * \include Device_verticalViewAngle.txt
+     *
+     * @returns The vertical angle of view in radians.
+     * @since 1.0
+     */
+    LEAP_EXPORT float verticalViewAngle() const;
+
+    /**
+     * The maximum reliable tracking range.
+     *
+     * The range reports the maximum recommended distance from the device center
+     * for which tracking is expected to be reliable. This distance is not a hard limit.
+     * Tracking may be still be functional above this distance or begin to degrade slightly
+     * before this distance depending on calibration and extreme environmental conditions.
+     *
+     * \include Device_range.txt
+     *
+     * @returns The recommended maximum range of the device in mm.
+     * @since 1.0
+     */
+    LEAP_EXPORT float range() const;
+
+    /**
+     * The distance to the nearest edge of the Leap Motion controller's view volume.
+     *
+     * The view volume is an axis-aligned, inverted pyramid centered on the device origin
+     * and extending upward to the range limit. The walls of the pyramid are described
+     * by the horizontalViewAngle and verticalViewAngle and the roof by the range.
+     * This function estimates the distance between the specified input position and the
+     * nearest wall or roof of the view volume.
+     *
+     * \include Device_distanceToBoundary.txt
+     *
+     * @param position The point to use for the distance calculation.
+     * @returns The distance in millimeters from the input position to the nearest boundary.
+     * @since 1.0
+     */
+    LEAP_EXPORT float distanceToBoundary(const Vector& position) const;
+
+    /**
+     * Reports whether this is a valid Device object.
+     *
+     * \include Device_isValid.txt
+     *
+     * @returns True, if this Device object contains valid data.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool isValid() const;
+
+    /**
+     * Returns an invalid Device object.
+     *
+     * You can use the instance returned by this function in comparisons testing
+     * whether a given Device instance is valid or invalid. (You can also use the
+     * Device::isValid() function.)
+     *
+     * \include Device_invalid.txt
+     *
+     * @returns The invalid Device instance.
+     * @since 1.0
+     */
+    LEAP_EXPORT static const Device& invalid();
+
+    /**
+     * Compare Device object equality.
+     *
+     * \include Device_operator_equals.txt
+     *
+     * Two Device objects are equal if and only if both Device objects represent the
+     * exact same Device and both Devices are valid.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool operator==(const Device&) const;
+
+    /**
+     * Compare Device object inequality.
+     *
+     * \include Device_operator_not_equals.txt
+     *
+     * Two Device objects are equal if and only if both Device objects represent the
+     * exact same Device and both Devices are valid.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool operator!=(const Device&) const;
+
+    /**
+     * Writes a brief, human readable description of the Device object.
+     *
+     * \include Device_operator_stream.txt
+     *
+     * @since 1.0
+     */
+    LEAP_EXPORT friend std::ostream& operator<<(std::ostream&, const Device&);
+
+    /**
+     * A string containing a brief, human readable description of the Device object.
+     *
+     * @returns A description of the Device as a string.
+     * @since 1.0
+     */
+    LEAP_EXPORT std::string toString() const;
+  };
+
+  // For internal use only.
+  template<typename L, typename T>
+  class ConstListIterator {
+  public:
+    ConstListIterator<L,T>(const L& list, int index) : m_list(list), m_index(index) {}
+
+    const T operator*() const { return m_list[m_index]; }
+    void operator++(int) {++m_index;}
+    const ConstListIterator<L,T>& operator++() { ++m_index; return *this; }
+    bool operator!=(const ConstListIterator<L,T>& rhs) const { return m_index != rhs.m_index; }
+    bool operator==(const ConstListIterator<L,T>& rhs) const { return m_index == rhs.m_index; }
+
+    typedef std::ptrdiff_t difference_type;
+    typedef T value_type;
+    typedef const T* pointer;
+    typedef const T& reference;
+    typedef std::forward_iterator_tag iterator_category;
+
+  private:
+    const L& m_list;
+    int m_index;
+  };
+
+  /**
+   * The PointableList class represents a list of Pointable objects.
+   *
+   * Pointable objects include entities that can be pointed, such as fingers and tools.
+   *
+   * Get a PointableList object by calling Frame::pointables() or Hand::pointables().
+   *
+   * \include PointableList_PointableList.txt
+   *
+   * @since 1.0
+   */
+  class PointableList : public Interface {
+  public:
+    // For internal use only.
+    PointableList(const ListBaseImplementation<Pointable>&);
+
+    /**
+     * Constructs an empty list of pointable entities.
+     * @since 1.0
+     */
+    LEAP_EXPORT PointableList();
+
+    /**
+     * Returns the number of pointable entities in this list.
+     *
+     * \include PointableList_count.txt
+     *
+     * @returns The number of pointable entities in this list.
+     * @since 1.0
+     */
+    LEAP_EXPORT int count() const;
+
+    /**
+     * Reports whether the list is empty.
+     *
+     * \include PointableList_isEmpty.txt
+     *
+     * @returns True, if the list has no members.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool isEmpty() const;
+
+    /**
+     * Access a list member by its position in the list.
+     *
+     * \include PointableList_operator_index.txt
+     *
+     * @param index The zero-based list position index.
+     * @returns The Pointable object at the specified index.
+     * @since 1.0
+     */
+    LEAP_EXPORT Pointable operator[](int index) const;
+
+    /**
+     * Appends the members of the specifed PointableList to this PointableList.
+     * @param other A PointableList object containing Pointable objects
+     * to append to the end of this PointableList.
+     * @since 1.0
+     */
+    LEAP_EXPORT PointableList& append(const PointableList& other);
+
+    /**
+     * Appends the members of the specifed FingerList to this PointableList.
+     * @param other A FingerList object containing Finger objects
+     * to append to the end of this PointableList.
+     * @since 1.0
+     */
+    LEAP_EXPORT PointableList& append(const FingerList& other);
+
+    /**
+     * Appends the members of the specifed ToolList to this PointableList.
+     * @param other A ToolList object containing Tool objects
+     * to append to the end of this PointableList.
+     * @since 1.0
+     */
+    LEAP_EXPORT PointableList& append(const ToolList& other);
+
+    /**
+     * The member of the list that is farthest to the left within the standard
+     * Leap Motion frame of reference (i.e has the smallest X coordinate).
+     *
+     * \include PointableList_leftmost.txt
+     *
+     * @returns The leftmost pointable, or invalid if list is empty.
+     * @since 1.0
+     */
+    LEAP_EXPORT Pointable leftmost() const;
+
+    /**
+     * The member of the list that is farthest to the right within the standard
+     * Leap Motion frame of reference (i.e has the largest X coordinate).
+     *
+     * \include PointableList_rightmost.txt
+     *
+     * @returns The rightmost pointable, or invalid if list is empty.
+     * @since 1.0
+     */
+    LEAP_EXPORT Pointable rightmost() const;
+
+
+    /**
+     * The member of the list that is farthest to the front within the standard
+     * Leap Motion frame of reference (i.e has the smallest Z coordinate).
+     *
+     * \include PointableList_frontmost.txt
+     *
+     * @returns The frontmost pointable, or invalid if list is empty.
+     * @since 1.0
+     */
+    LEAP_EXPORT Pointable frontmost() const;
+
+    /**
+     * A C++ iterator type for PointableList objects.
+     *
+     * \include PointableList_iterator.txt
+     *
+     * @since 1.0
+     */
+    typedef ConstListIterator<PointableList, Pointable> const_iterator;
+
+    /**
+     * The C++ iterator set to the beginning of this PointableList.
+     *
+     * \include PointableList_begin.txt
+     *
+     * @since 1.0
+     */
+    LEAP_EXPORT const_iterator begin() const;
+
+    /**
+     * The C++ iterator set to the end of this PointableList.
+     *
+     * \include PointableList_end.txt
+     *
+     * @since 1.0
+     */
+    LEAP_EXPORT const_iterator end() const;
+  };
+
+  /**
+   * The FingerList class represents a list of Finger objects.
+   *
+   * Get a FingerList object by calling Frame::fingers().
+   *
+   * \include FingerList_FingerList.txt
+   *
+   * @since 1.0
+   */
+  class FingerList : public Interface {
+  public:
+    // For internal use only.
+    FingerList(const ListBaseImplementation<Finger>&);
+
+    /**
+     * Constructs an empty list of fingers.
+     * @since 1.0
+     */
+    LEAP_EXPORT FingerList();
+
+    /**
+     * Returns the number of fingers in this list.
+     *
+     * \include FingerList_count.txt
+     *
+     * @returns The number of fingers in this list.
+     * @since 1.0
+     */
+    LEAP_EXPORT int count() const;
+
+    /**
+     * Reports whether the list is empty.
+     *
+     * \include FingerList_isEmpty.txt
+     *
+     * @returns True, if the list has no members.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool isEmpty() const;
+
+    /**
+     * Access a list member by its position in the list.
+     *
+     * \include FingerList_index.txt
+     *
+     * @param index The zero-based list position index.
+     * @returns The Finger object at the specified index.
+     * @since 1.0
+     */
+    LEAP_EXPORT Finger operator[](int index) const;
+
+    /**
+     * Appends the members of the specifed FingerList to this FingerList.
+     * @param other A FingerList object containing Finger objects
+     * to append to the end of this FingerList.
+     * @since 1.0
+     */
+    LEAP_EXPORT FingerList& append(const FingerList& other);
+
+    /**
+     * The member of the list that is farthest to the left within the standard
+     * Leap Motion frame of reference (i.e has the smallest X coordinate).
+     *
+     * \include FingerList_leftmost.txt
+     *
+     * @returns The leftmost finger, or invalid if list is empty.
+     * @since 1.0
+     */
+    LEAP_EXPORT Finger leftmost() const;
+
+    /**
+     * The member of the list that is farthest to the right within the standard
+     * Leap Motion frame of reference (i.e has the largest X coordinate).
+     *
+     * \include FingerList_rightmost.txt
+     *
+     * @returns The rightmost finger, or invalid if list is empty.
+     * @since 1.0
+     */
+    LEAP_EXPORT Finger rightmost() const;
+
+    /**
+     * The member of the list that is farthest to the front within the standard
+     * Leap Motion frame of reference (i.e has the smallest Z coordinate).
+     *
+     * \include FingerList_frontmost.txt
+     *
+     * @returns The frontmost finger, or invalid if list is empty.
+     * @since 1.0
+     */
+    LEAP_EXPORT Finger frontmost() const;
+
+    /**
+     * A C++ iterator type for FingerList objects.
+     *
+     * \include FingerList_iterator.txt
+     *
+     * @since 1.0
+     */
+    typedef ConstListIterator<FingerList, Finger> const_iterator;
+
+    /**
+     * The C++ iterator set to the beginning of this FingerList.
+     *
+     * \include FingerList_begin.txt
+     *
+     * @since 1.0
+     */
+    LEAP_EXPORT const_iterator begin() const;
+
+    /**
+     * The C++ iterator set to the end of this FingerList.
+     *
+     * \include FingerList_end.txt
+     *
+     * @since 1.0
+     */
+    LEAP_EXPORT const_iterator end() const;
+  };
+
+  /**
+   * The ToolList class represents a list of Tool objects.
+   *
+   * Get a ToolList object by calling Frame::tools() or Hand::tools().
+   *
+   * \include ToolList_ToolList.txt
+   *
+   * @since 1.0
+   */
+  class ToolList : public Interface {
+  public:
+    // For internal use only.
+    ToolList(const ListBaseImplementation<Tool>&);
+
+    /**
+     * Constructs an empty list of tools.
+     * @since 1.0
+     */
+    LEAP_EXPORT ToolList();
+
+    /**
+     * Returns the number of tools in this list.
+     *
+     * \include ToolList_count.txt
+     *
+     * @returns The number of tools in this list.
+     * @since 1.0
+     */
+    LEAP_EXPORT int count() const;
+
+    /**
+     * Reports whether the list is empty.
+     *
+     * \include ToolList_isEmpty.txt
+     *
+     * @returns True, if the list has no members.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool isEmpty() const;
+
+    /**
+     * Access a list member by its position in the list.
+     *
+     * \include ToolList_operator_index.txt
+     *
+     * @param index The zero-based list position index.
+     * @returns The Tool object at the specified index.
+     * @since 1.0
+     */
+    LEAP_EXPORT Tool operator[](int index) const;
+
+    /**
+     * Appends the members of the specifed ToolList to this ToolList.
+     * @param other A ToolList object containing Tool objects
+     * to append to the end of this ToolList.
+     * @since 1.0
+     */
+    LEAP_EXPORT ToolList& append(const ToolList& other);
+
+    /**
+     * The member of the list that is farthest to the left within the standard
+     * Leap Motion frame of reference (i.e has the smallest X coordinate).
+     *
+     * \include ToolList_leftmost.txt
+     *
+     * @returns The leftmost tool, or invalid if list is empty.
+     * @since 1.0
+     */
+    LEAP_EXPORT Tool leftmost() const;
+
+    /**
+     * The member of the list that is farthest to the right within the standard
+     * Leap Motion frame of reference (i.e has the largest X coordinate).
+     *
+     * \include ToolList_rightmost.txt
+     *
+     * @returns The rightmost tool, or invalid if list is empty.
+     * @since 1.0
+     */
+    LEAP_EXPORT Tool rightmost() const;
+
+    /**
+     * The member of the list that is farthest to the front within the standard
+     * Leap Motion frame of reference (i.e has the smallest Z coordinate).
+     *
+     * \include ToolList_frontmost.txt
+     *
+     * @returns The frontmost tool, or invalid if list is empty.
+     * @since 1.0
+     */
+    LEAP_EXPORT Tool frontmost() const;
+
+    /**
+     * A C++ iterator type for ToolList objects.
+     *
+     * \include ToolList_iterator.txt
+     *
+     * @since 1.0
+     */
+    typedef ConstListIterator<ToolList, Tool> const_iterator;
+
+    /**
+     * The C++ iterator set to the beginning of this ToolList.
+     *
+     * \include ToolList_begin.txt
+     * @since 1.0
+     */
+    LEAP_EXPORT const_iterator begin() const;
+
+    /**
+     * The C++ iterator set to the end of this ToolList.
+     *
+     * \include ToolList_end.txt
+     *
+     * @since 1.0
+     */
+    LEAP_EXPORT const_iterator end() const;
+  };
+
+  /**
+   * The HandList class represents a list of Hand objects.
+   *
+   * Get a HandList object by calling Frame::hands().
+   *
+   * \include HandList_HandList.txt
+   *
+   * @since 1.0
+   */
+  class HandList : public Interface {
+  public:
+    // For internal use only.
+    HandList(const ListBaseImplementation<Hand>&);
+
+    /**
+     * Constructs an empty list of hands.
+     * @since 1.0
+     */
+    LEAP_EXPORT HandList();
+
+    /**
+     * Returns the number of hands in this list.
+     *
+     * \include HandList_count.txt
+     * @returns The number of hands in this list.
+     * @since 1.0
+     */
+    LEAP_EXPORT int count() const;
+
+    /**
+     * Reports whether the list is empty.
+     *
+     * \include HandList_isEmpty.txt
+     *
+     * @returns True, if the list has no members.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool isEmpty() const;
+
+    /**
+     * Access a list member by its position in the list.
+     *
+     * \include HandList_operator_index.txt
+     *
+     * @param index The zero-based list position index.
+     * @returns The Hand object at the specified index.
+     * @since 1.0
+     */
+    LEAP_EXPORT Hand operator[](int index) const;
+
+    /**
+     * Appends the members of the specifed HandList to this HandList.
+     * @param other A HandList object containing Hand objects
+     * to append to the end of this HandList.
+     */
+    LEAP_EXPORT HandList& append(const HandList& other);
+
+    /**
+     * The member of the list that is farthest to the left within the standard
+     * Leap Motion frame of reference (i.e has the smallest X coordinate).
+     *
+     * \include HandList_leftmost.txt
+     *
+     * @returns The leftmost hand, or invalid if list is empty.
+     * @since 1.0
+     */
+    LEAP_EXPORT Hand leftmost() const;
+
+    /**
+     * The member of the list that is farthest to the right within the standard
+     * Leap Motion frame of reference (i.e has the largest X coordinate).
+     *
+     * \include HandList_rightmost.txt
+     *
+     * @returns The rightmost hand, or invalid if list is empty.
+     * @since 1.0
+     */
+    LEAP_EXPORT Hand rightmost() const;
+
+    /**
+     * The member of the list that is farthest to the front within the standard
+     * Leap Motion frame of reference (i.e has the smallest Z coordinate).
+     *
+     * \include HandList_frontmost.txt
+     *
+     * @returns The frontmost hand, or invalid if list is empty.
+     * @since 1.0
+     */
+    LEAP_EXPORT Hand frontmost() const;
+
+    /**
+     * A C++ iterator type for this HandList objects.
+     *
+     * \include HandList_iterator.txt
+     *
+     * @since 1.0
+     */
+    typedef ConstListIterator<HandList, Hand> const_iterator;
+
+    /**
+     * The C++ iterator set to the beginning of this HandList.
+     *
+     * \include HandList_begin.txt
+     *
+     * @since 1.0
+     */
+    LEAP_EXPORT const_iterator begin() const;
+
+    /**
+     * The C++ iterator set to the end of this HandList.
+     *
+     * \include HandList_end.txt
+     *
+     * @since 1.0
+     */
+    LEAP_EXPORT const_iterator end() const;
+  };
+
+  /**
+   * The GestureList class represents a list of Gesture objects.
+   *
+   * Get a GestureList object from a Frame object.
+   * @since 1.0
+   */
+  class GestureList : public Interface {
+  public:
+    // For internal use only.
+    GestureList(const ListBaseImplementation<Gesture>&);
+
+    /**
+     * Constructs an empty gesture list.
+     * @since 1.0
+     */
+    LEAP_EXPORT GestureList();
+
+    /**
+     * The length of this list.
+     *
+     * \include GestureList_count.txt
+     *
+     * @returns The number of gestures in this list.
+     * @since 1.0
+     */
+    LEAP_EXPORT int count() const;
+
+    /**
+     * Reports whether the list is empty.
+     *
+     * \include GestureList_isEmpty.txt
+     *
+     * @returns True, if the list has no members.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool isEmpty() const;
+
+    /**
+     * Access a list member by its position in the list.
+     *
+     * \include GestureList_operator_index.txt
+     *
+     * @param index The zero-based list position index.
+     * @returns The Gesture object at the specified index.
+     * @since 1.0
+     */
+    LEAP_EXPORT Gesture operator[](int index) const;
+
+    /**
+     * Appends the members of the specified GestureList to this GestureList.
+     * @param other A GestureList object containing Gesture objects
+     * to append to the end of this GestureList.
+     * @since 1.0
+     */
+    LEAP_EXPORT GestureList& append(const GestureList& other);
+
+    /**
+     * A C++ iterator type for GestureList objects.
+     *
+     * \include GestureList_iterator.txt
+     * @since 1.0
+     */
+    typedef ConstListIterator<GestureList, Gesture> const_iterator;
+
+    /**
+     * The C++ iterator set to the beginning of this GestureList.
+     *
+     * \include GestureList_begin.txt
+     *
+     * @since 1.0
+     */
+    LEAP_EXPORT const_iterator begin() const;
+
+    /**
+     * The C++ iterator set to the end of this GestureList.
+     *
+     * \include GestureList_end.txt
+     *
+     * @since 1.0
+     */
+    LEAP_EXPORT const_iterator end() const;
+  };
+
+  /*
+   * The ScreenList and Screen classes are currently unsupported.
+   *
+   * We are re-evaluating this feature due to the cumbersome location process
+   * required to use it and the amount of confusion about the feature's purpose.
+   *
+   * The ScreenList class represents a list of Screen objects.
+   *
+   * The list always contains at least one entry representing the default
+   * screen. If the user has not registered the location of this default
+   * screen, then the coordinates, directions, and other values reported by
+   * the functions in its Screen object will not be accurate. Other monitor
+   * screens only appear in the list if their positions have been registered
+   * using the Leap Motion Screen Locator.
+   *
+   * Get a ScreenList object by calling Controller::locatedScreens().
+   *
+   * \include Screen_Closest_1.txt
+   * @since 1.0
+   */
+  class ScreenList : public Interface {
+  public:
+    // For internal use only.
+    ScreenList(const ListBaseImplementation<Screen>&);
+
+    /**
+     * Constructs an empty list of screens.
+     * @since 1.0
+     */
+    LEAP_EXPORT ScreenList();
+
+    /**
+     * Returns the number of screens in this list.
+     * @returns The number of screens in this list.
+     * @since 1.0
+     */
+    LEAP_EXPORT int count() const;
+
+    /**
+     * Reports whether the list is empty.
+     * @returns True, if the list has no members.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool isEmpty() const;
+
+    /**
+     * Access a list member by its position in the list.
+     * @param index The zero-based list position index.
+     * @returns The Screen object at the specified index.
+     * @since 1.0
+     */
+    LEAP_EXPORT Screen operator[](int index) const;
+
+    /**
+     * A C++ iterator type for this ScreenList objects.
+     * @since 1.0
+     */
+    typedef ConstListIterator<ScreenList, Screen> const_iterator;
+
+    /**
+     * The C++ iterator set to the beginning of this ScreenList.
+     * @since 1.0
+     */
+    LEAP_EXPORT const_iterator begin() const;
+
+    /**
+     * The C++ iterator set to the end of this ScreenList.
+     * @since 1.0
+     */
+    LEAP_EXPORT const_iterator end() const;
+
+    /**
+     * Gets the closest Screen intercepting a ray projecting from the specified
+     * Pointable object.
+     *
+     * The projected ray emanates from the Pointable tipPosition along the
+     * Pointable's direction vector. If the projected ray does not intersect
+     * any screen surface directly, then the Leap Motion software checks for intersection with
+     * the planes extending from the surfaces of the known screens
+     * and returns the Screen with the closest intersection.
+     *
+     * \include Screen_Closest_2.txt
+     *
+     * If no intersections are found (i.e. the ray is directed parallel to or
+     * away from all known screens), then an invalid Screen object is returned.
+     *
+     * *Note:* Be sure to test whether the Screen object returned by this method
+     * is valid. Attempting to use an invalid Screen object will lead to
+     * incorrect results.
+     *
+     * @param pointable The Pointable object to check for screen intersection.
+     * @returns The closest Screen toward which the specified Pointable object
+     * is pointing, or, if the pointable is not pointing in the direction of
+     * any known screen, an invalid Screen object.
+     * @since 1.0
+     */
+    LEAP_EXPORT Screen closestScreenHit(const Pointable& pointable) const;
+
+    /**
+     * Gets the closest Screen intercepting a ray projecting from the specified
+     * position in the specified direction.
+     *
+     * The projected ray emanates from the position along the direction vector.
+     * If the projected ray does not intersect any screen surface directly,
+     * then the Leap Motion software checks for intersection with the planes extending from the
+     * surfaces of the known screens and returns the Screen with the closest
+     * intersection.
+     *
+     * \include Screen_Closest_3.txt
+     *
+     * If no intersections are found (i.e. the ray is directed parallel to or
+     * away from all known screens), then an invalid Screen object is returned.
+     *
+     * *Note:* Be sure to test whether the Screen object returned by this method
+     * is valid. Attempting to use an invalid Screen object will lead to
+     * incorrect results.
+     *
+     * @param position The position from which to check for screen intersection.
+     * @param direction The direction in which to check for screen intersection.
+     * @returns The closest Screen toward which the specified ray is pointing,
+     * or, if the ray is not pointing in the direction of any known screen,
+     * an invalid Screen object.
+     * @since 1.0
+     */
+    LEAP_EXPORT Screen closestScreenHit(const Vector& position, const Vector& direction) const;
+
+    /**
+     * Gets the Screen closest to the specified position.
+     *
+     * The specified position is projected along each screen's normal vector
+     * onto the screen's plane. The screen whose projected point is closest to
+     * the specified position is returned. Call Screen::project(position)
+     * on the returned Screen object to find the projected point.
+     *
+     * \include Screen_Closest_3.txt
+     *
+     * @param position The position from which to check for screen projection.
+     * @returns The closest Screen onto which the specified position is projected.
+     * @since 1.0
+     */
+    LEAP_EXPORT Screen closestScreen(const Vector& position) const;
+  };
+
+  /**
+   * The DeviceList class represents a list of Device objects.
+   *
+   * Get a DeviceList object by calling Controller::devices().
+   * @since 1.0
+   */
+  class DeviceList : public Interface {
+  public:
+    // For internal use only.
+    DeviceList(const ListBaseImplementation<Device>&);
+
+    /**
+     * Constructs an empty list of devices.
+     * @since 1.0
+     */
+    LEAP_EXPORT DeviceList();
+
+    /**
+     * Returns the number of devices in this list.
+     * @returns The number of devices in this list.
+     * @since 1.0
+     */
+    LEAP_EXPORT int count() const;
+
+    /**
+     * Reports whether the list is empty.
+     *
+     * \include DeviceList_isEmpty.txt
+     *
+     * @returns True, if the list has no members.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool isEmpty() const;
+
+    /**
+     * Access a list member by its position in the list.
+     * @param index The zero-based list position index.
+     * @returns The Device object at the specified index.
+     * @since 1.0
+     */
+    LEAP_EXPORT Device operator[](int index) const;
+
+    /**
+     * Appends the members of the specifed DeviceList to this DeviceList.
+     * @param other A DeviceList object containing Device objects
+     * to append to the end of this DeviceList.
+     * @since 1.0
+     */
+    LEAP_EXPORT DeviceList& append(const DeviceList& other);
+
+    /**
+     * A C++ iterator type for this DeviceList objects.
+     * @since 1.0
+     */
+    typedef ConstListIterator<DeviceList, Device> const_iterator;
+
+    /**
+     * The C++ iterator set to the beginning of this DeviceList.
+     * @since 1.0
+     */
+    LEAP_EXPORT const_iterator begin() const;
+
+    /**
+     * The C++ iterator set to the end of this DeviceList.
+     * @since 1.0
+     */
+    LEAP_EXPORT const_iterator end() const;
+  };
+
+  /**
+   * The InteractionBox class represents a box-shaped region completely
+   * within the field of view of the Leap Motion controller.
+   *
+   * The interaction box is an axis-aligned rectangular prism and provides normalized
+   * coordinates for hands, fingers, and tools within this box. The InteractionBox class
+   * can make it easier to map positions in the Leap Motion coordinate system to 2D or
+   * 3D coordinate systems used for application drawing.
+   *
+   * \image html images/Leap_InteractionBox.png
+   *
+   * The InteractionBox region is defined by a center and dimensions along the x, y,
+   * and z axes.
+   *
+   * Get an InteractionBox object from a Frame object.
+   * @since 1.0
+   */
+  class InteractionBox : public Interface {
+  public:
+    // For internal use only.
+    InteractionBox(InteractionBoxImplementation*);
+
+    LEAP_EXPORT InteractionBox();
+
+    /**
+     * Normalizes the coordinates of a point using the interaction box.
+     *
+     * \include InteractionBox_normalizePoint.txt
+     *
+     * Coordinates from the Leap Motion frame of reference (millimeters) are converted
+     * to a range of [0..1] such that the minimum value of the InteractionBox maps to 0
+     * and the maximum value of the InteractionBox maps to 1.
+     *
+     * @param position The input position in device coordinates.
+     * @param clamp Whether or not to limit the output value to the range [0,1] when the
+     * input position is outside the InteractionBox. Defaults to true.
+     * @returns The normalized position.
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector normalizePoint(const Vector& position, bool clamp = true) const;
+
+    /**
+     * Converts a position defined by normalized InteractionBox coordinates into device
+     * coordinates in millimeters.
+     *
+     * \include InteractionBox_denormalizePoint.txt
+     *
+     * This function performs the inverse of normalizePoint().
+     *
+     * @param normalizedPosition The input position in InteractionBox coordinates.
+     * @returns The corresponding denormalized position in device coordinates.
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector denormalizePoint(const Vector& normalizedPosition) const;
+
+    /**
+     * The center of the InteractionBox in device coordinates (millimeters). This point
+     * is equidistant from all sides of the box.
+     *
+     * \include InteractionBox_center.txt
+     *
+     * @returns The InteractionBox center in device coordinates.
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector center() const;
+
+    /**
+     * The width of the InteractionBox in millimeters, measured along the x-axis.
+     *
+     * \include InteractionBox_width.txt
+     *
+     * @returns The InteractionBox width in millimeters.
+     * @since 1.0
+     */
+    LEAP_EXPORT float width() const;
+
+    /**
+     * The height of the InteractionBox in millimeters, measured along the y-axis.
+     *
+     * \include InteractionBox_height.txt
+     *
+     * @returns The InteractionBox height in millimeters.
+     * @since 1.0
+     */
+    LEAP_EXPORT float height() const;
+
+    /**
+     * The depth of the InteractionBox in millimeters, measured along the z-axis.
+     *
+     * \include InteractionBox_depth.txt
+     *
+     * @returns The InteractionBox depth in millimeters.
+     * @since 1.0
+     */
+    LEAP_EXPORT float depth() const;
+
+    /**
+     * Reports whether this is a valid InteractionBox object.
+     *
+     * \include InteractionBox_isValid.txt
+     *
+     * @returns True, if this InteractionBox object contains valid data.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool isValid() const;
+
+    /**
+     * Returns an invalid InteractionBox object.
+     *
+     * You can use the instance returned by this function in comparisons testing
+     * whether a given InteractionBox instance is valid or invalid. (You can also use the
+     * InteractionBox::isValid() function.)
+     *
+     * \include InteractionBox_invalid.txt
+     *
+     * @returns The invalid InteractionBox instance.
+     * @since 1.0
+     */
+    LEAP_EXPORT static const InteractionBox& invalid();
+
+    /**
+     * Compare InteractionBox object equality.
+     *
+     * \include InteractionBox_operator_equals.txt
+     *
+     * Two InteractionBox objects are equal if and only if both InteractionBox objects represent the
+     * exact same InteractionBox and both InteractionBoxes are valid.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool operator==(const InteractionBox&) const;
+
+    /**
+     * Compare InteractionBox object inequality.
+     *
+     * \include InteractionBox_operator_not_equals.txt
+     *
+     * Two InteractionBox objects are equal if and only if both InteractionBox objects represent the
+     * exact same InteractionBox and both InteractionBoxes are valid.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool operator!=(const InteractionBox&) const;
+
+    /**
+     * Writes a brief, human readable description of the InteractionBox object.
+     *
+     * \include InteractionBox_operator_stream.txt
+     *
+     * @since 1.0
+     */
+    LEAP_EXPORT friend std::ostream& operator<<(std::ostream&, const InteractionBox&);
+
+    /**
+     * A string containing a brief, human readable description of the InteractionBox object.
+     *
+     * @returns A description of the InteractionBox as a string.
+     * @since 1.0
+     */
+    LEAP_EXPORT std::string toString() const;
+  };
+
+  /**
+   * The Frame class represents a set of hand and finger tracking data detected
+   * in a single frame.
+   *
+   * The Leap Motion software detects hands, fingers and tools within the tracking area, reporting
+   * their positions, orientations, gestures, and motions in frames at the Leap Motion frame rate.
+   *
+   * Access Frame objects through an instance of the Controller class: 
+   *
+   * \include Controller_Frame_1.txt
+   *
+   * Implement a Listener subclass to receive a callback event when a new Frame is available.
+   * @since 1.0
+   */
+  class Frame : public Interface {
+  public:
+    // For internal use only.
+    Frame(FrameImplementation*);
+
+    /**
+     * Constructs a Frame object.
+     *
+     * Frame instances created with this constructor are invalid.
+     * Get valid Frame objects by calling the Controller::frame() function.
+     *
+     * \include Frame_Frame.txt
+     *
+     * @since 1.0
+     */
+    LEAP_EXPORT Frame();
+
+    /**
+     * A unique ID for this Frame. 
+     *
+     * Consecutive frames processed by the Leap Motion software have consecutive 
+     * increasing values. You can use the frame ID to avoid processing the same
+     * Frame object twice:
+     *
+     * \include Frame_Duplicate.txt
+     *
+     * As well as to make sure that your application processes every frame:
+     *
+     * \include Frame_Skipped.txt
+     *
+     * @returns The frame ID.
+     * @since 1.0
+     */
+    LEAP_EXPORT int64_t id() const;
+
+    /**
+     * The frame capture time in microseconds elapsed since the Leap started.
+     *
+     * \include Frame_timestamp.txt
+     *
+     * @returns The timestamp in microseconds.
+     * @since 1.0
+     */
+    LEAP_EXPORT int64_t timestamp() const;
+
+    /**
+     * The list of Hand objects detected in this frame, given in arbitrary order.
+     * The list can be empty if no hands are detected.
+     *
+     * \include Frame_hands.txt
+     *
+     * @returns The HandList containing all Hand objects detected in this frame.
+     * @since 1.0
+     */
+    LEAP_EXPORT HandList hands() const;
+
+    /**
+     * The Hand object with the specified ID in this frame.
+     *
+     * Use the Frame::hand() function to retrieve the Hand object from
+     * this frame using an ID value obtained from a previous frame.
+     * This function always returns a Hand object, but if no hand
+     * with the specified ID is present, an invalid Hand object is returned.
+     *
+     * \include Frame_hand.txt
+     *
+     * Note that ID values persist across frames, but only until tracking of a
+     * particular object is lost. If tracking of a hand is lost and subsequently
+     * regained, the new Hand object representing that physical hand may have
+     * a different ID than that representing the physical hand in an earlier frame.
+     *
+     * @param id The ID value of a Hand object from a previous frame.
+     * @returns The Hand object with the matching ID if one exists in this frame;
+     * otherwise, an invalid Hand object is returned.
+     * @since 1.0
+     */
+    LEAP_EXPORT Hand hand(int32_t id) const;
+
+    /**
+     * The list of Pointable objects (fingers and tools) detected in this frame,
+     * given in arbitrary order. The list can be empty if no fingers or tools are detected.
+     *
+     * \include Frame_pointables.txt
+     *
+     * @returns The PointableList containing all Pointable objects detected in this frame.
+     * @since 1.0
+     */
+    LEAP_EXPORT PointableList pointables() const;
+
+    /**
+     * The Pointable object with the specified ID in this frame.
+     *
+     * Use the Frame::pointable() function to retrieve the Pointable object from
+     * this frame using an ID value obtained from a previous frame.
+     * This function always returns a Pointable object, but if no finger or tool
+     * with the specified ID is present, an invalid Pointable object is returned.
+     *
+     * \include Frame_pointable.txt
+     *
+     * Note that ID values persist across frames, but only until tracking of a
+     * particular object is lost. If tracking of a finger or tool is lost and subsequently
+     * regained, the new Pointable object representing that finger or tool may have
+     * a different ID than that representing the finger or tool in an earlier frame.
+     *
+     * @param id The ID value of a Pointable object from a previous frame.
+     * @returns The Pointable object with the matching ID if one exists in this frame;
+     * otherwise, an invalid Pointable object is returned.
+     * @since 1.0
+     */
+    LEAP_EXPORT Pointable pointable(int32_t id) const;
+
+    /**
+     * The list of Finger objects detected in this frame, given in arbitrary order.
+     * The list can be empty if no fingers are detected.
+     *
+     * \include Frame_fingers.txt
+     *
+     * @returns The FingerList containing all Finger objects detected in this frame.
+     * @since 1.0
+     */
+    LEAP_EXPORT FingerList fingers() const;
+
+    /**
+     * The Finger object with the specified ID in this frame.
+     *
+     * Use the Frame::finger() function to retrieve the Finger object from
+     * this frame using an ID value obtained from a previous frame.
+     * This function always returns a Finger object, but if no finger
+     * with the specified ID is present, an invalid Finger object is returned.
+     *
+     * \include Frame_finger.txt
+     *
+     * Note that ID values persist across frames, but only until tracking of a
+     * particular object is lost. If tracking of a finger is lost and subsequently
+     * regained, the new Finger object representing that physical finger may have
+     * a different ID than that representing the finger in an earlier frame.
+     *
+     * @param id The ID value of a Finger object from a previous frame.
+     * @returns The Finger object with the matching ID if one exists in this frame;
+     * otherwise, an invalid Finger object is returned.
+     * @since 1.0
+     */
+    LEAP_EXPORT Finger finger(int32_t id) const;
+
+    /**
+     * The list of Tool objects detected in this frame, given in arbitrary order.
+     * The list can be empty if no tools are detected.
+     *
+     * \include Frame_tools.txt
+     *
+     * @returns The ToolList containing all Tool objects detected in this frame.
+     * @since 1.0
+     */
+    LEAP_EXPORT ToolList tools() const;
+
+    /**
+     * The Tool object with the specified ID in this frame.
+     *
+     * Use the Frame::tool() function to retrieve the Tool object from
+     * this frame using an ID value obtained from a previous frame.
+     * This function always returns a Tool object, but if no tool
+     * with the specified ID is present, an invalid Tool object is returned.
+     *
+     * \include Frame_tool.txt
+     *
+     * Note that ID values persist across frames, but only until tracking of a
+     * particular object is lost. If tracking of a tool is lost and subsequently
+     * regained, the new Tool object representing that tool may have a
+     * different ID than that representing the tool in an earlier frame.
+     *
+     * @param id The ID value of a Tool object from a previous frame.
+     * @returns The Tool object with the matching ID if one exists in this frame;
+     * otherwise, an invalid Tool object is returned.
+     * @since 1.0
+     */
+    LEAP_EXPORT Tool tool(int32_t id) const;
+
+    /**
+     * The Gesture object with the specified ID in this frame.
+     *
+     * Use the Frame::gesture() function to return a Gesture object in this
+     * frame using an ID obtained in an earlier frame. The function always
+     * returns a Gesture object, but if there was no update for the gesture in
+     * this frame, then an invalid Gesture object is returned.
+     *
+     * \include Frame_gesture.txt
+     *
+     * All Gesture objects representing the same recognized movement share the
+     * same ID.
+     * @param id The ID of an Gesture object from a previous frame.
+     * @returns The Gesture object in the frame with the specified ID if one
+     * exists; Otherwise, an Invalid Gesture object.
+     * @since 1.0
+     */
+    LEAP_EXPORT Gesture gesture(int32_t id) const;
+
+    /**
+     * The gestures recognized or continuing in this frame.
+     *
+     * \include Frame_gestures_now.txt
+     *
+     * Circle and swipe gestures are updated every frame. Tap gestures
+     * only appear in the list  for a single frame.
+     *
+     * @return GestureList the list of gestures.
+     * @since 1.0
+     */
+    LEAP_EXPORT GestureList gestures() const;
+
+    /**
+     * Returns a GestureList containing all gestures that have occured since
+     * the specified frame.
+     *
+     * \include Frame_gestures_since.txt
+     *
+     * @param sinceFrame An earlier Frame object. The starting frame must
+     * still be in the frame history cache, which has a default length of
+     * 60 frames.
+     * @return GestureList The list of the Gesture objects that have occured
+     * since the specified frame.
+     * @since 1.0
+     */
+    LEAP_EXPORT GestureList gestures(const Frame& sinceFrame) const;
+
+    /**
+     * The change of position derived from the overall linear motion between
+     * the current frame and the specified frame.
+     *
+     * The returned translation vector provides the magnitude and direction of
+     * the movement in millimeters.
+     *
+     * \include Frame_translation.txt
+     *
+     * The Leap Motion software derives frame translation from the linear motion of
+     * all objects detected in the field of view.
+     *
+     * If either this frame or sinceFrame is an invalid Frame object, then this
+     * method returns a zero vector.
+     *
+     * @param sinceFrame The starting frame for computing the relative translation.
+     * @returns A Vector representing the heuristically determined change in
+     * position of all objects between the current frame and that specified
+     * in the sinceFrame parameter.
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector translation(const Frame& sinceFrame) const;
+
+    /**
+     * The estimated probability that the overall motion between the current
+     * frame and the specified frame is intended to be a translating motion.
+     *
+     * \include Frame_translationProbability.txt
+     *
+     * If either this frame or sinceFrame is an invalid Frame object, then this
+     * method returns zero.
+     *
+     * @param sinceFrame The starting frame for computing the translation.
+     * @returns A value between 0 and 1 representing the estimated probability
+     * that the overall motion between the current frame and the specified frame
+     * is intended to be a translating motion.
+     * @since 1.0
+     */
+    LEAP_EXPORT float translationProbability(const Frame& sinceFrame) const;
+
+    /**
+     * The axis of rotation derived from the overall rotational motion between
+     * the current frame and the specified frame.
+     *
+     * The returned direction vector is normalized.
+     *
+     * \include Frame_rotationAxis.txt
+     *
+     * The Leap Motion software derives frame rotation from the relative change in position and
+     * orientation of all objects detected in the field of view.
+     *
+     * If either this frame or sinceFrame is an invalid Frame object, or if no
+     * rotation is detected between the two frames, a zero vector is returned.
+     *
+     * @param sinceFrame The starting frame for computing the relative rotation.
+     * @returns A normalized direction Vector representing the axis of the
+     * heuristically determined rotational change between the current frame
+     * and that specified in the sinceFrame parameter.
+     * @since 1.0
+     */
+    LEAP_EXPORT Vector rotationAxis(const Frame& sinceFrame) const;
+
+    /**
+     * The angle of rotation around the rotation axis derived from the overall
+     * rotational motion between the current frame and the specified frame.
+     *
+     * The returned angle is expressed in radians measured clockwise around the
+     * rotation axis (using the right-hand rule) between the start and end frames.
+     * The value is always between 0 and pi radians (0 and 180 degrees).
+     *
+     * \include Frame_rotationAngle.txt
+     *
+     * The Leap Motion software derives frame rotation from the relative change in position and
+     * orientation of all objects detected in the field of view.
+     *
+     * If either this frame or sinceFrame is an invalid Frame object, then the
+     * angle of rotation is zero.
+     *
+     * @param sinceFrame The starting frame for computing the relative rotation.
+     * @returns A positive value containing the heuristically determined
+     * rotational change between the current frame and that specified in the
+     * sinceFrame parameter.
+     * @since 1.0
+     */
+    LEAP_EXPORT float rotationAngle(const Frame& sinceFrame) const;
+
+    /**
+     * The angle of rotation around the specified axis derived from the overall
+     * rotational motion between the current frame and the specified frame.
+     *
+     * The returned angle is expressed in radians measured clockwise around the
+     * rotation axis (using the right-hand rule) between the start and end frames.
+     * The value is always between -pi and pi radians (-180 and 180 degrees).
+     *
+     * \include Frame_rotationAngle_axis.txt
+     *
+     * The Leap Motion software derives frame rotation from the relative change in position and
+     * orientation of all objects detected in the field of view.
+     *
+     * If either this frame or sinceFrame is an invalid Frame object, then the
+     * angle of rotation is zero.
+     *
+     * @param sinceFrame The starting frame for computing the relative rotation.
+     * @param axis The axis to measure rotation around.
+     * @returns A value containing the heuristically determined rotational
+     * change between the current frame and that specified in the sinceFrame
+     * parameter around the given axis.
+     * @since 1.0
+     */
+    LEAP_EXPORT float rotationAngle(const Frame& sinceFrame, const Vector& axis) const;
+
+    /**
+     * The transform matrix expressing the rotation derived from the overall
+     * rotational motion between the current frame and the specified frame.
+     *
+     * \include Frame_rotationMatrix.txt
+     *
+     * The Leap Motion software derives frame rotation from the relative change in position and
+     * orientation of all objects detected in the field of view.
+     *
+     * If either this frame or sinceFrame is an invalid Frame object, then this
+     * method returns an identity matrix.
+     *
+     * @param sinceFrame The starting frame for computing the relative rotation.
+     * @returns A transformation Matrix containing the heuristically determined
+     * rotational change between the current frame and that specified in the
+     * sinceFrame parameter.
+     * @since 1.0
+     */
+    LEAP_EXPORT Matrix rotationMatrix(const Frame& sinceFrame) const;
+
+    /**
+     * The estimated probability that the overall motion between the current
+     * frame and the specified frame is intended to be a rotating motion.
+     *
+     * \include Frame_rotationProbability.txt
+     *
+     * If either this frame or sinceFrame is an invalid Frame object, then this
+     * method returns zero.
+     *
+     * @param sinceFrame The starting frame for computing the relative rotation.
+     * @returns A value between 0 and 1 representing the estimated probability
+     * that the overall motion between the current frame and the specified frame
+     * is intended to be a rotating motion.
+     * @since 1.0
+     */
+    LEAP_EXPORT float rotationProbability(const Frame& sinceFrame) const;
+
+    /**
+     * The scale factor derived from the overall motion between the current frame
+     * and the specified frame.
+     *
+     * The scale factor is always positive. A value of 1.0 indicates no
+     * scaling took place. Values between 0.0 and 1.0 indicate contraction
+     * and values greater than 1.0 indicate expansion.
+     *
+     * \include Frame_scaleFactor.txt
+     *
+     * The Leap Motion software derives scaling from the relative inward or outward motion of
+     * all objects detected in the field of view (independent of translation
+     * and rotation).
+     *
+     * If either this frame or sinceFrame is an invalid Frame object, then this
+     * method returns 1.0.
+     *
+     * @param sinceFrame The starting frame for computing the relative scaling.
+     * @returns A positive value representing the heuristically determined
+     * scaling change ratio between the current frame and that specified in the
+     * sinceFrame parameter.
+     * @since 1.0
+     */
+    LEAP_EXPORT float scaleFactor(const Frame& sinceFrame) const;
+
+    /**
+     * The estimated probability that the overall motion between the current
+     * frame and the specified frame is intended to be a scaling motion.
+     *
+     * \include Frame_scaleProbability.txt
+     *
+     * If either this frame or sinceFrame is an invalid Frame object, then this
+     * method returns zero.
+     *
+     * @param sinceFrame The starting frame for computing the relative scaling.
+     * @returns A value between 0 and 1 representing the estimated probability
+     * that the overall motion between the current frame and the specified frame
+     * is intended to be a scaling motion.
+     * @since 1.0
+     */
+    LEAP_EXPORT float scaleProbability(const Frame& sinceFrame) const;
+
+    /**
+     * The current InteractionBox for the frame. See the InteractionBox class
+     * documentation for more details on how this class should be used.
+     *
+     * \include Frame_interactionBox.txt
+     *
+     * @returns The current InteractionBox object.
+     * @since 1.0
+     */
+    LEAP_EXPORT InteractionBox interactionBox() const;
+
+    /**
+     * The instantaneous framerate.
+     *
+     * The rate at which the Leap Motion software is providing frames of data
+     * (in frames per second). The framerate can fluctuate depending on available computing
+     * resources, activity within the device field of view, software tracking settings,
+     * and other factors.
+     *
+     * \include Frame_currentFramesPerSecond.txt
+     *
+     * @returns An estimate of frames per second of the Leap Motion Controller.
+     * @since 1.0
+     */
+    LEAP_EXPORT float currentFramesPerSecond() const;
+
+    /**
+     * Reports whether this Frame instance is valid.
+     *
+     * A valid Frame is one generated by the Leap::Controller object that contains
+     * tracking data for all detected entities. An invalid Frame contains no
+     * actual tracking data, but you can call its functions without risk of a
+     * null pointer exception. The invalid Frame mechanism makes it more
+     * convenient to track individual data across the frame history. For example,
+     * you can invoke:
+     *
+     * \include Frame_Valid_Chain.txt
+     *
+     * for an arbitrary Frame history value, "n", without first checking whether
+     * frame(n) returned a null object. (You should still check that the
+     * returned Finger instance is valid.)
+     *
+     * @returns True, if this is a valid Frame object; false otherwise.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool isValid() const;
+
+    /**
+     * Returns an invalid Frame object.
+     *
+     * You can use the instance returned by this function in comparisons testing
+     * whether a given Frame instance is valid or invalid. (You can also use the
+     * Frame::isValid() function.)
+     *
+     * \include Frame_Invalid_Demo.txt
+     *
+     * @returns The invalid Frame instance.
+     * @since 1.0
+     */
+    LEAP_EXPORT static const Frame& invalid();
+
+    /**
+     * Compare Frame object equality.
+     *
+     * \include Frame_operator_equals.txt
+     *
+     * Two Frame objects are equal if and only if both Frame objects represent
+     * the exact same frame of tracking data and both Frame objects are valid.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool operator==(const Frame&) const;
+
+    /**
+     * Compare Frame object inequality.
+     *
+     * \include Frame_operator_not_equals.txt
+     *
+     * Two Frame objects are equal if and only if both Frame objects represent
+     * the exact same frame of tracking data and both Frame objects are valid.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool operator!=(const Frame&) const;
+
+    /**
+     * Writes a brief, human readable description of the Frame object to an output stream.
+     *
+     * \include Frame_operator_stream.txt
+     *
+     * @since 1.0
+     */
+    LEAP_EXPORT friend std::ostream& operator<<(std::ostream&, const Frame&);
+
+    /**
+     * A string containing a brief, human readable description of the Frame object.
+     *
+     * @returns A description of the Frame as a string.
+     * @since 1.0
+     */
+    LEAP_EXPORT std::string toString() const;
+  };
+
+  /**
+   * The Config class provides access to Leap Motion system configuration information.
+   *
+   * You can get and set gesture configuration parameters using the Config object
+   * obtained from a connected Controller object. The key strings required to
+   * identify a configuration parameter include:
+   *
+   * Key string | Value type | Default value | Units
+   * -----------|------------|---------------|------
+   * Gesture.Circle.MinRadius | float | 5.0 | mm
+   * Gesture.Circle.MinArc | float | 1.5*pi | radians
+   * Gesture.Swipe.MinLength | float | 150 | mm
+   * Gesture.Swipe.MinVelocity | float | 1000 | mm/s
+   * Gesture.KeyTap.MinDownVelocity | float | 50 | mm/s
+   * Gesture.KeyTap.HistorySeconds | float | 0.1 | s
+   * Gesture.KeyTap.MinDistance | float | 3.0 | mm
+   * Gesture.ScreenTap.MinForwardVelocity  | float | 50 | mm/s
+   * Gesture.ScreenTap.HistorySeconds | float | 0.1 | s
+   * Gesture.ScreenTap.MinDistance | float | 5.0 | mm
+   *
+   * After setting a configuration value, you must call the Config::save method
+   * to commit the changes. The configuration value changes are not persistent;
+   * your application needs to set the values everytime it runs.
+   *
+   * @see CircleGesture
+   * @see KeyTapGesture
+   * @see ScreenTapGesture
+   * @see SwipeGesture
+   * @since 1.0
+   */
+  class Config : public Interface {
+  public:
+    /**
+     * Constructs a Config object. 
+     * Do not create your own Config objects. Get a Config object using
+     * the Controller::config() function.
+     *
+     * \include Config_Constructor.txt
+     *
+     * @since 1.0
+     */
+    LEAP_EXPORT Config();
+
+    /**
+     * Enumerates the possible data types for configuration values.
+     *
+     * The Config::type() function returns an item from the ValueType enumeration.
+     * @since 1.0
+     */
+    enum ValueType {
+      /**
+       * The data type is unknown.
+       * @since 1.0
+       */
+      TYPE_UNKNOWN = 0,
+      /**
+       * A boolean value.
+       * @since 1.0
+       */
+      TYPE_BOOLEAN = 1,
+      /**
+       * A 32-bit integer.
+       * @since 1.0
+       */
+      TYPE_INT32   = 2,
+      /**
+       * A floating-point number.
+       * @since 1.0
+       */
+      TYPE_FLOAT   = 6,
+      /**
+       * A string of characters.
+       * @since 1.0
+       */
+      TYPE_STRING  = 8
+    };
+
+    /**
+     * Reports the natural data type for the value related to the specified key.
+     *
+     * \include Config_type.txt
+     *
+     * @param key The key for the looking up the value in the configuration dictionary.
+     * @returns The native data type of the value, that is, the type that does not
+     * require a data conversion.
+     * @since 1.0
+     */
+    LEAP_EXPORT ValueType type(const std::string& key) const;
+
+    /**
+     * Gets the boolean representation for the specified key.
+     *
+     * \include Config_getBool.txt
+     *
+     * @since 1.0
+     */
+    LEAP_EXPORT bool getBool(const std::string& key) const;
+
+    /** Sets the boolean representation for the specified key.
+     *
+     * \include Config_setBool.txt
+     *
+     * @returns true on success, false on failure.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool setBool(const std::string& key, bool value);
+
+    /**
+     * Gets the 32-bit integer representation for the specified key.
+     *
+     * \include Config_getInt32.txt
+     *
+     * @since 1.0
+     */
+    LEAP_EXPORT int32_t getInt32(const std::string& key) const;
+
+    /** Sets the 32-bit integer representation for the specified key.
+     *
+     * \include Config_setInt32.txt
+     *
+     * @returns true on success, false on failure.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool setInt32(const std::string& key, int32_t value);
+
+    /**
+     * Gets the floating point representation for the specified key.
+     *
+     * \include Config_getFloat.txt
+     *
+     * @since 1.0
+     */
+    LEAP_EXPORT float getFloat(const std::string& key) const;
+
+    /** Sets the floating point representation for the specified key.
+     *
+     * \include Config_setFloat.txt
+     *
+     * @returns true on success, false on failure.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool setFloat(const std::string& key, float value);
+
+    /**
+     * Gets the string representation for the specified key.
+     *
+     * \include Config_getString.txt
+     *
+     * @since 1.0
+     */
+    LEAP_EXPORT std::string getString(const std::string& key) const;
+
+    /** Sets the string representation for the specified key.
+     *
+     * \include Config_setString.txt
+     *
+     * @returns true on success, false on failure.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool setString(const std::string& key, const std::string& value);
+
+    /**
+     * Saves the current state of the config.
+     *
+     * Call ``save()`` after making a set of configuration changes. The
+     * ``save()`` function transfers the configuration changes to the Leap Motion
+     * service. The configuration value changes are not persistent; your
+     * application must set the values everytime it runs.
+     *
+     * \include Config_save.txt
+     *
+     * @returns true on success, false on failure.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool save();
+  };
+
+  /**
+   * The Controller class is your main interface to the Leap Motion Controller.
+   *
+   * Create an instance of this Controller class to access frames of tracking
+   * data and configuration information. Frame data can be polled at any time
+   * using the Controller::frame() function. Call frame() or frame(0) to get the
+   * most recent frame. Set the history parameter to a positive integer to access
+   * previous frames. A controller stores up to 60 frames in its frame history.
+   *
+   * Polling is an appropriate strategy for applications which already have an
+   * intrinsic update loop, such as a game. You can also add an instance of a
+   * subclass of Leap::Listener to the controller to handle events as they occur.
+   * The Controller dispatches events to the listener upon initialization and exiting,
+   * on connection changes, when the application gains and loses the OS input focus,
+   * and when a new frame of tracking data is available.
+   * When these events occur, the controller object invokes the appropriate
+   * callback function defined in your subclass of Listener.
+   *
+   * To access frames of tracking data as they become available:
+   *
+   * 1. Implement a subclass of the Listener class and override the
+   *    Listener::onFrame() function.
+   * 2. In your Listener::onFrame() function, call the Controller::frame()
+   *    function to access the newest frame of tracking data.
+   * 3. To start receiving frames, create a Controller object and add an instance
+   *    of the Listener subclass to the Controller::addListener() function.
+   *
+   * When an instance of a Listener subclass is added to a Controller object,
+   * it calls the Listener::onInit() function when the listener is ready for use.
+   * When a connection is established between the controller and the Leap Motion software,
+   * the controller calls the Listener::onConnect() function. At this point, your
+   * application will start receiving frames of data. The controller calls the
+   * Listener::onFrame() function each time a new frame is available. If the
+   * controller loses its connection with the Leap Motion software or device for any
+   * reason, it calls the Listener::onDisconnect() function. If the listener is
+   * removed from the controller or the controller is destroyed, it calls the
+   * Listener::onExit() function. At that point, unless the listener is added to
+   * another controller again, it will no longer receive frames of tracking data.
+   *
+   * The Controller object is multithreaded and calls the Listener functions on
+   * its own thread, not on an application thread.
+   * @since 1.0
+   */
+  class Controller : public Interface {
+  public:
+    // For internal use only.
+    Controller(ControllerImplementation*);
+
+    /**
+     * Constructs a Controller object.
+     *
+     * When creating a Controller object, you may optionally pass in a
+     * reference to an instance of a subclass of Leap::Listener. Alternatively,
+     * you may add a listener using the Controller::addListener() function.
+     *
+     * @since 1.0
+     */
+    LEAP_EXPORT Controller();
+    LEAP_EXPORT virtual ~Controller();
+    /**
+     * Constructs a Controller object.
+     *
+     * When creating a Controller object, you may optionally pass in a
+     * reference to an instance of a subclass of Leap::Listener. Alternatively,
+     * you may add a listener using the Controller::addListener() function.
+     *
+     * \include Controller_Controller.txt
+     *
+     * @param listener An instance of Leap::Listener implementing the callback
+     * functions for the Leap Motion events you want to handle in your application.
+     * @since 1.0
+     */
+    LEAP_EXPORT Controller(Listener& listener);
+
+    /**
+     * Reports whether this Controller is connected to the Leap Motion service and 
+     * the Leap Motion hardware is plugged in.
+     *
+     * When you first create a Controller object, isConnected() returns false.
+     * After the controller finishes initializing and connects to the Leap Motion 
+     * software and if the Leap Motion hardware is plugged in, isConnected() returns true.
+     *
+     * You can either handle the onConnect event using a Listener instance or
+     * poll the isConnected() function if you need to wait for your
+     * application to be connected to the Leap Motion software before performing some other
+     * operation.
+     *
+     * \include Controller_isConnected.txt
+     * @returns True, if connected; false otherwise.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool isConnected() const;
+
+    /**
+     * Reports whether this application is the focused, foreground application.
+     *
+     * By default, your application only receives tracking information from
+     * the Leap Motion controller when it has the operating system input focus.
+     * To receive tracking data when your application is in the background,
+     * the background frames policy flag must be set.
+     *
+     * \include Controller_hasFocus.txt
+     *
+     * @returns True, if application has focus; false otherwise.
+     *
+     * @see Controller::setPolicyFlags()
+     * @since 1.0
+     */
+    LEAP_EXPORT bool hasFocus() const;
+
+    /**
+     * The supported controller policies.
+     *
+     * Currently, the only supported policy is the background frames policy,
+     * which determines whether your application receives frames of tracking
+     * data when it is not the focused, foreground application.
+     * @since 1.0
+     */
+    enum PolicyFlag {
+      /**
+       * The default policy.
+       * @since 1.0
+       */
+      POLICY_DEFAULT = 0,
+      /**
+       * Receive background frames.
+       * @since 1.0
+       */
+      POLICY_BACKGROUND_FRAMES = (1 << 0)
+    };
+
+    /**
+     * Gets the active policy settings.
+     *
+     * Use this function to determine the current policy state.
+     * Keep in mind that setting a policy flag is asynchronous, so changes are
+     * not effective immediately after calling setPolicyFlag(). In addition, a
+     * policy request can be declined by the user. You should always set the
+     * policy flags required by your application at startup and check that the
+     * policy change request was successful after an appropriate interval.
+     *
+     * If the controller object is not connected to the Leap Motion software, then the default
+     * policy state is returned.
+     *
+     * \include Controller_policyFlags.txt
+     *
+     * @returns The current policy flags.
+     * @since 1.0
+     */
+    LEAP_EXPORT PolicyFlag policyFlags() const;
+
+    /**
+     * Requests a change in policy.
+     *
+     * A request to change a policy is subject to user approval and a policy
+     * can be changed by the user at any time (using the Leap Motion settings dialog).
+     * The desired policy flags must be set every time an application runs.
+     *
+     * Policy changes are completed asynchronously and, because they are subject
+     * to user approval, may not complete successfully. Call
+     * Controller::policyFlags() after a suitable interval to test whether
+     * the change was accepted.
+     *
+     * Currently, the background frames policy is the only policy supported.
+     * The background frames policy determines whether an application
+     * receives frames of tracking data while in the background. By
+     * default, the Leap Motion  software only sends tracking data to the foreground application.
+     * Only applications that need this ability should request the background
+     * frames policy.
+     *
+     * At this time, you can use the Leap Motion Settings dialog to
+     * globally enable or disable the background frames policy. However,
+     * each application that needs tracking data while in the background
+     * must also set the policy flag using this function.
+     *
+     * This function can be called before the Controller object is connected,
+     * but the request will be sent to the Leap Motion software after the Controller connects.
+     *
+     * \include Controller_setPolicyFlags.txt
+     *
+     * @param flags A PolicyFlag value indicating the policies to request.
+     * @since 1.0
+     */
+    LEAP_EXPORT void setPolicyFlags(PolicyFlag flags) const;
+
+    /**
+     * Adds a listener to this Controller.
+     *
+     * The Controller dispatches Leap Motion events to each associated listener. The
+     * order in which listener callback functions are invoked is arbitrary. If
+     * you pass a listener to the Controller's constructor function, it is
+     * automatically added to the list and can be removed with the
+     * Controller::removeListener() function.
+     *
+     * \include Controller_addListener.txt
+     *
+     * @param listener A subclass of Leap::Listener implementing the callback
+     * functions for the Leap Motion events you want to handle in your application.
+     * @returns Whether or not the listener was successfully added to the list
+     * of listeners.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool addListener(Listener& listener);
+
+    /**
+     * Remove a listener from the list of listeners that will receive Leap Motion
+     * events. A listener must be removed if its lifetime is shorter than the
+     * controller to which it is listening.
+     *
+     * \include Controller_removeListener.txt
+     *
+     * @param listener The listener to remove.
+     * @returns Whether or not the listener was successfully removed from the
+     * list of listeners.
+     * @since 1.0
+     */
+    LEAP_EXPORT bool removeListener(Listener& listener);
+
+    /**
+     * Returns a frame of tracking data from the Leap Motion software. Use the optional
+     * history parameter to specify which frame to retrieve. Call frame() or
+     * frame(0) to access the most recent frame; call frame(1) to access the
+     * previous frame, and so on. If you use a history value greater than the
+     * number of stored frames, then the controller returns an invalid frame.
+     *
+     * \include Controller_Frame_1.txt
+     *
+     * You can call this function in your Listener implementation to get frames at the
+     * Leap Motion frame rate:
+     *
+     * \include Controller_Listener_onFrame.txt
+     
+     * @param history The age of the frame to return, counting backwards from
+     * the most recent frame (0) into the past and up to the maximum age (59).
+     * @returns The specified frame; or, if no history parameter is specified,
+     * the newest frame. If a frame is not available at the specified history
+     * position, an invalid Frame is returned.
+     * @since 1.0
+     */
+    LEAP_EXPORT Frame frame(int history = 0) const;
+
+    /**
+     * Returns a Config object, which you can use to query the Leap Motion system for
+     * configuration information.
+     *
+     * \include Controller_config.txt
+     *
+     * @since 1.0
+     */
+    LEAP_EXPORT Config config() const;
+
+    /**
+     * The list of currently attached and recognized Leap Motion controller devices.
+     *
+     * The Device objects in the list describe information such as the range and
+     * tracking volume.
+     *
+     * \include Controller_devices.txt
+     *
+     * Currently, the Leap Motion Controller only recognizes a single device at a time.
+     * @since 1.0
+     */
+    LEAP_EXPORT DeviceList devices() const;
+
+    /*
+     * The Screen and ScreenList classes are currently unsupported.
+     *
+     * We are re-evaluating this feature due to the cumbersome location process
+     * required to use it and the amount of confusion about the feature's purpose.
+     *
+     * The list of screens whose positions have been identified by using the
+     * Leap Motion Screen Locator.
+     *
+     * The list always contains at least one entry representing the default
+     * screen. If the user has not registered the location of this default
+     * screen, then the coordinates, directions, and other values reported by
+     * the functions in its Screen object will not be accurate. Other monitor
+     * screens only appear in the list if their positions have been registered
+     * using the Leap Motion Screen Locator.
+     *
+     * A Screen object represents the position and orientation of a display
+     * monitor screen within the Leap Motion coordinate system.
+     * For example, if the screen location is known, you can get Leap Motion coordinates
+     * for the bottom-left corner of the screen. Registering the screen
+     * location also allows the Leap Motion software to calculate the point on the screen at
+     * which a finger or tool is pointing.
+     *
+     * A user can run the Screen Locator tool from the Leap Motion
+     * Settings dialog. Avoid assuming that a screen location is known or that
+     * an existing position is still correct. The registered position is only
+     * valid as long as the relative position of the Leap Motion Controller and the
+     * monitor screen remain constant.
+     *
+     * \include Screen_Closest_1.txt
+     *
+     * @returns ScreenList A list containing the screens whose positions have
+     * been registered by the user using the Screen Locator tool.
+     * The list always contains at least one entry representing the default
+     * monitor. If the user has not run the Screen Locator or has moved the Leap
+     * Motion device or screen since running it, the Screen object for this entry
+     * only contains default values.
+     * @since 1.0
+     */
+    LEAP_EXPORT ScreenList locatedScreens() const;
+
+    /**
+     * Enables or disables reporting of a specified gesture type.
+     *
+     * By default, all gesture types are disabled. When disabled, gestures of the
+     * disabled type are never reported and will not appear in the frame
+     * gesture list.
+     *
+     * \include Controller_enableGesture.txt
+     *
+     * As a performance optimization, only enable recognition for the types
+     * of movements that you use in your application.
+     *
+     * @param type The type of gesture to enable or disable. Must be a
+     * member of the Gesture::Type enumeration.
+     * @param enable True, to enable the specified gesture type; False,
+     * to disable.
+     * @see Controller::isGestureEnabled()
+     * @since 1.0
+     */
+    LEAP_EXPORT void enableGesture(Gesture::Type type, bool enable = true) const;
+
+    /**
+     * Reports whether the specified gesture type is enabled.
+     *
+     * \include Controller_isGestureEnabled.txt
+     *
+     * @return True, if the specified type is enabled; false, otherwise.
+     * @see Controller::enableGesture()
+     * @since 1.0
+     */
+    LEAP_EXPORT bool isGestureEnabled(Gesture::Type type) const;
+  };
+
+  /**
+   * The Listener class defines a set of callback functions that you can
+   * override in a subclass to respond to events dispatched by the Controller object.
+   *
+   * To handle Leap Motion events, create an instance of a Listener subclass and assign
+   * it to the Controller instance. The Controller calls the relevant Listener
+   * callback function when an event occurs, passing in a reference to itself.
+   * You do not have to implement callbacks for events you do not want to handle.
+   *
+   * The Controller object calls these Listener functions from a thread created
+   * by the Leap Motion library, not the thread used to create or set the Listener instance.
+   * @since 1.0
+   */
+  class Listener {
+  public:
+    /**
+     * Constructs a Listener object.
+     * @since 1.0
+     */
+    LEAP_EXPORT Listener() {}
+
+    /**
+     * Destroys this Listener object.
+     */
+    LEAP_EXPORT virtual ~Listener() {}
+
+    /**
+     * Called once, when this Listener object is newly added to a Controller.
+     *
+     * \include Listener_onInit.txt
+     *
+     * @param controller The Controller object invoking this callback function.
+     * @since 1.0
+     */
+    LEAP_EXPORT virtual void onInit(const Controller&) {}
+
+    /**
+     * Called when the Controller object connects to the Leap Motion software and 
+     * the Leap Motion hardware device is plugged in,
+     * or when this Listener object is added to a Controller that is already connected.
+     *
+     * \include Listener_onConnect.txt
+     *
+     * @param controller The Controller object invoking this callback function.
+     * @since 1.0
+     */
+    LEAP_EXPORT virtual void onConnect(const Controller&) {}
+
+    /**
+     * Called when the Controller object disconnects from the Leap Motion software or
+     * the Leap Motion hardware is unplugged.
+     * The controller can disconnect when the Leap Motion device is unplugged, the
+     * user shuts the Leap Motion software down, or the Leap Motion software encounters an
+     * unrecoverable error.
+     *
+     * \include Listener_onDisconnect.txt
+     *
+     * Note: When you launch a Leap-enabled application in a debugger, the
+     * Leap Motion library does not disconnect from the application. This is to allow
+     * you to step through code without losing the connection because of time outs.
+     *
+     * @param controller The Controller object invoking this callback function.
+     * @since 1.0
+     */
+    LEAP_EXPORT virtual void onDisconnect(const Controller&) {}
+
+    /**
+     * Called when this Listener object is removed from the Controller
+     * or the Controller instance is destroyed.
+     *
+     * \include Listener_onExit.txt
+     *
+     * @param controller The Controller object invoking this callback function.
+     * @since 1.0
+     */
+    LEAP_EXPORT virtual void onExit(const Controller&) {}
+
+    /**
+     * Called when a new frame of hand and finger tracking data is available.
+     * Access the new frame data using the Controller::frame() function.
+     *
+     * \include Listener_onFrame.txt
+     *
+     * Note, the Controller skips any pending onFrame events while your
+     * onFrame handler executes. If your implementation takes too long to return,
+     * one or more frames can be skipped. The Controller still inserts the skipped
+     * frames into the frame history. You can access recent frames by setting
+     * the history parameter when calling the Controller::frame() function.
+     * You can determine if any pending onFrame events were skipped by comparing
+     * the ID of the most recent frame with the ID of the last received frame.
+     *
+     * @param controller The Controller object invoking this callback function.
+     * @since 1.0
+     */
+    LEAP_EXPORT virtual void onFrame(const Controller&) {}
+
+    /**
+     * Called when this application becomes the foreground application.
+     *
+     * Only the foreground application receives tracking data from the Leap
+     * Motion Controller. This function is only called when the controller
+     * object is in a connected state.
+     *
+     * \include Listener_onFocusGained.txt
+     *
+     * @param controller The Controller object invoking this callback function.
+     * @since 1.0
+     */
+    LEAP_EXPORT virtual void onFocusGained(const Controller&) {}
+
+    /**
+     * Called when this application loses the foreground focus.
+     *
+     * Only the foreground application receives tracking data from the Leap
+     * Motion Controller. This function is only called when the controller
+     * object is in a connected state.
+     *
+     * \include Listener_onFocusLost.txt
+     *
+     * @param controller The Controller object invoking this callback function.
+     * @since 1.0
+     */
+    LEAP_EXPORT virtual void onFocusLost(const Controller&) {}
+  };
+
+}
+
+#endif // __Leap_h__

+ 1036 - 0
engine/lib/LeapSDK/include/LeapMath.h

@@ -0,0 +1,1036 @@
+/******************************************************************************\
+* Copyright (C) 2012-2013 Leap Motion, Inc. All rights reserved.               *
+* Leap Motion proprietary and confidential. Not for distribution.              *
+* Use subject to the terms of the Leap Motion SDK Agreement available at       *
+* https://developer.leapmotion.com/sdk_agreement, or another agreement         *
+* between Leap Motion and you, your company or other organization.             *
+\******************************************************************************/
+
+#if !defined(__LeapMath_h__)
+#define __LeapMath_h__
+
+#include <cmath>
+#include <iostream>
+#include <sstream>
+#include <float.h>
+
+namespace Leap {
+
+/**
+ * The constant pi as a single precision floating point number.
+ * @since 1.0
+ */
+static const float PI          = 3.1415926536f;
+/**
+ * The constant ratio to convert an angle measure from degrees to radians.
+ * Multiply a value in degrees by this constant to convert to radians.
+ * @since 1.0
+ */
+static const float DEG_TO_RAD  = 0.0174532925f;
+/**
+ * The constant ratio to convert an angle measure from radians to degrees.
+ * Multiply a value in radians by this constant to convert to degrees.
+ * @since 1.0
+ */
+static const float RAD_TO_DEG  = 57.295779513f;
+
+/**
+ * The Vector struct represents a three-component mathematical vector or point
+ * such as a direction or position in three-dimensional space.
+ *
+ * The Leap Motion software employs a right-handed Cartesian coordinate system.
+ * Values given are in units of real-world millimeters. The origin is centered
+ * at the center of the Leap Motion Controller. The x- and z-axes lie in the horizontal
+ * plane, with the x-axis running parallel to the long edge of the device.
+ * The y-axis is vertical, with positive values increasing upwards (in contrast
+ * to the downward orientation of most computer graphics coordinate systems).
+ * The z-axis has positive values increasing away from the computer screen.
+ *
+ * \image html images/Leap_Axes.png
+ * @since 1.0
+ */
+struct Vector {
+  /**
+   * Creates a new Vector with all components set to zero.
+   * @since 1.0
+   */
+  Vector() :
+    x(0), y(0), z(0) {}
+
+  /**
+   * Creates a new Vector with the specified component values.
+   *
+   * \include Vector_Constructor_1.txt
+   * @since 1.0
+   */
+  Vector(float _x, float _y, float _z) :
+    x(_x), y(_y), z(_z) {}
+
+  /**
+   * Copies the specified Vector.
+   *
+   * \include Vector_Constructor_2.txt
+   * @since 1.0
+   */
+  Vector(const Vector& vector) :
+    x(vector.x), y(vector.y), z(vector.z) {}
+
+  /**
+   * The zero vector: (0, 0, 0)
+   *
+   * \include Vector_Zero.txt
+   * @since 1.0
+   */
+  static const Vector& zero() {
+    static Vector s_zero(0, 0, 0);
+    return s_zero;
+  }
+
+  /**
+   * The x-axis unit vector: (1, 0, 0)
+   *
+   * \include Vector_XAxis.txt
+   * @since 1.0
+   */
+  static const Vector& xAxis() {
+    static Vector s_xAxis(1, 0, 0);
+    return s_xAxis;
+  }
+  /**
+   * The y-axis unit vector: (0, 1, 0)
+   *
+   * \include Vector_YAxis.txt
+   * @since 1.0
+   */
+  static const Vector& yAxis() {
+    static Vector s_yAxis(0, 1, 0);
+    return s_yAxis;
+  }
+  /**
+   * The z-axis unit vector: (0, 0, 1)
+   *
+   * \include Vector_ZAxis.txt
+   * @since 1.0
+   */
+  static const Vector& zAxis() {
+    static Vector s_zAxis(0, 0, 1);
+    return s_zAxis;
+  }
+
+  /**
+   * The unit vector pointing left along the negative x-axis: (-1, 0, 0)
+   *
+   * \include Vector_Left.txt
+   * @since 1.0
+   */
+  static const Vector& left() {
+    static Vector s_left(-1, 0, 0);
+    return s_left;
+  }
+  /**
+   * The unit vector pointing right along the positive x-axis: (1, 0, 0)
+   *
+   * \include Vector_Right.txt
+   * @since 1.0
+   */
+  static const Vector& right() {
+    return xAxis();
+  }
+  /**
+   * The unit vector pointing down along the negative y-axis: (0, -1, 0)
+   *
+   * \include Vector_Down.txt
+   * @since 1.0
+   */
+  static const Vector& down() {
+    static Vector s_down(0, -1, 0);
+    return s_down;
+  }
+  /**
+   * The unit vector pointing up along the positive y-axis: (0, 1, 0)
+   *
+   * \include Vector_Up.txt
+   * @since 1.0
+   */
+  static const Vector& up() {
+    return yAxis();
+  }
+  /**
+   * The unit vector pointing forward along the negative z-axis: (0, 0, -1)
+   *
+   * \include Vector_Forward.txt
+   * @since 1.0
+   */
+  static const Vector& forward() {
+    static Vector s_forward(0, 0, -1);
+    return s_forward;
+  }
+  /**
+   * The unit vector pointing backward along the positive z-axis: (0, 0, 1)
+   *
+   * \include Vector_Backward.txt
+   * @since 1.0
+   */
+  static const Vector& backward() {
+    return zAxis();
+  }
+
+  /**
+   * The magnitude, or length, of this vector.
+   *
+   * The magnitude is the L2 norm, or Euclidean distance between the origin and
+   * the point represented by the (x, y, z) components of this Vector object.
+   *
+   * \include Vector_Magnitude.txt
+   *
+   * @returns The length of this vector.
+   * @since 1.0
+   */
+  float magnitude() const {
+    return std::sqrt(x*x + y*y + z*z);
+  }
+
+  /**
+   * The square of the magnitude, or length, of this vector.
+   *
+   * \include Vector_Magnitude_Squared.txt
+   *
+   * @returns The square of the length of this vector.
+   * @since 1.0
+   */
+  float magnitudeSquared() const {
+    return x*x + y*y + z*z;
+  }
+
+  /**
+   * The distance between the point represented by this Vector
+   * object and a point represented by the specified Vector object.
+   *
+   * \include Vector_DistanceTo.txt
+   *
+   * @param other A Vector object.
+   * @returns The distance from this point to the specified point.
+   * @since 1.0
+   */
+  float distanceTo(const Vector& other) const {
+    return std::sqrt((x - other.x)*(x - other.x) +
+                     (y - other.y)*(y - other.y) +
+                     (z - other.z)*(z - other.z));
+  }
+
+  /**
+   * The angle between this vector and the specified vector in radians.
+   *
+   * The angle is measured in the plane formed by the two vectors. The
+   * angle returned is always the smaller of the two conjugate angles.
+   * Thus <tt>A.angleTo(B) == B.angleTo(A)</tt> and is always a positive
+   * value less than or equal to pi radians (180 degrees).
+   *
+   * If either vector has zero length, then this function returns zero.
+   *
+   * \image html images/Math_AngleTo.png
+   *
+   * \include Vector_AngleTo.txt
+   *
+   * @param other A Vector object.
+   * @returns The angle between this vector and the specified vector in radians.
+   * @since 1.0
+   */
+  float angleTo(const Vector& other) const {
+    float denom = this->magnitudeSquared() * other.magnitudeSquared();
+    if (denom <= 0.0f) {
+      return 0.0f;
+    }
+    return std::acos(this->dot(other) / std::sqrt(denom));
+  }
+
+  /**
+   * The pitch angle in radians.
+   *
+   * Pitch is the angle between the negative z-axis and the projection of
+   * the vector onto the y-z plane. In other words, pitch represents rotation
+   * around the x-axis.
+   * If the vector points upward, the returned angle is between 0 and pi radians
+   * (180 degrees); if it points downward, the angle is between 0 and -pi radians.
+   *
+   * \image html images/Math_Pitch_Angle.png
+   *
+   * \include Vector_Pitch.txt
+   *
+   * @returns The angle of this vector above or below the horizon (x-z plane).
+   * @since 1.0
+   */
+  float pitch() const {
+    return std::atan2(y, -z);
+  }
+
+  /**
+   * The yaw angle in radians.
+   *
+   * Yaw is the angle between the negative z-axis and the projection of
+   * the vector onto the x-z plane. In other words, yaw represents rotation
+   * around the y-axis. If the vector points to the right of the negative z-axis,
+   * then the returned angle is between 0 and pi radians (180 degrees);
+   * if it points to the left, the angle is between 0 and -pi radians.
+   *
+   * \image html images/Math_Yaw_Angle.png
+   *
+   * \include Vector_Yaw.txt
+   *
+   * @returns The angle of this vector to the right or left of the negative z-axis.
+   * @since 1.0
+   */
+  float yaw() const {
+    return std::atan2(x, -z);
+  }
+
+  /**
+   * The roll angle in radians.
+   *
+   * Roll is the angle between the y-axis and the projection of
+   * the vector onto the x-y plane. In other words, roll represents rotation
+   * around the z-axis. If the vector points to the left of the y-axis,
+   * then the returned angle is between 0 and pi radians (180 degrees);
+   * if it points to the right, the angle is between 0 and -pi radians.
+   *
+   * \image html images/Math_Roll_Angle.png
+   *
+   * Use this function to get roll angle of the plane to which this vector is a
+   * normal. For example, if this vector represents the normal to the palm,
+   * then this function returns the tilt or roll of the palm plane compared
+   * to the horizontal (x-z) plane.
+   *
+   * \include Vector_Roll.txt
+   *
+   * @returns The angle of this vector to the right or left of the y-axis.
+   * @since 1.0
+   */
+  float roll() const {
+    return std::atan2(x, -y);
+  }
+
+  /**
+   * The dot product of this vector with another vector.
+   *
+   * The dot product is the magnitude of the projection of this vector
+   * onto the specified vector.
+   *
+   * \image html images/Math_Dot.png
+   *
+   * \include Vector_Dot.txt
+   *
+   * @param other A Vector object.
+   * @returns The dot product of this vector and the specified vector.
+   * @since 1.0
+   */
+  float dot(const Vector& other) const {
+    return (x * other.x) + (y * other.y) + (z * other.z);
+  }
+
+  /**
+   * The cross product of this vector and the specified vector.
+   *
+   * The cross product is a vector orthogonal to both original vectors.
+   * It has a magnitude equal to the area of a parallelogram having the
+   * two vectors as sides. The direction of the returned vector is
+   * determined by the right-hand rule. Thus <tt>A.cross(B) == -B.cross(A).</tt>
+   *
+   * \image html images/Math_Cross.png
+   *
+   * \include Vector_Cross.txt
+   *
+   * @param other A Vector object.
+   * @returns The cross product of this vector and the specified vector.
+   * @since 1.0
+   */
+  Vector cross(const Vector& other) const {
+    return Vector((y * other.z) - (z * other.y),
+                  (z * other.x) - (x * other.z),
+                  (x * other.y) - (y * other.x));
+  }
+
+  /**
+   * A normalized copy of this vector.
+   *
+   * A normalized vector has the same direction as the original vector,
+   * but with a length of one.
+   *
+   * \include Vector_Normalized.txt
+   *
+   * @returns A Vector object with a length of one, pointing in the same
+   * direction as this Vector object.
+   * @since 1.0
+   */
+  Vector normalized() const {
+    float denom = this->magnitudeSquared();
+    if (denom <= 0.0f) {
+      return Vector::zero();
+    }
+    denom = 1.0f / std::sqrt(denom);
+    return Vector(x * denom, y * denom, z * denom);
+  }
+
+  /**
+   * A copy of this vector pointing in the opposite direction.
+   *
+   * \include Vector_Negate.txt
+   *
+   * @returns A Vector object with all components negated.
+   * @since 1.0
+   */
+  Vector operator-() const {
+    return Vector(-x, -y, -z);
+  }
+
+  /**
+   * Add vectors component-wise.
+   *
+   * \include Vector_Plus.txt
+   * @since 1.0
+   */
+  Vector operator+(const Vector& other) const {
+    return Vector(x + other.x, y + other.y, z + other.z);
+  }
+
+  /**
+   * Subtract vectors component-wise.
+   *
+   * \include Vector_Minus.txt
+   * @since 1.0
+   */
+  Vector operator-(const Vector& other) const {
+    return Vector(x - other.x, y - other.y, z - other.z);
+  }
+
+  /**
+   * Multiply vector by a scalar.
+   *
+   * \include Vector_Times.txt
+   * @since 1.0
+   */
+  Vector operator*(float scalar) const {
+    return Vector(x * scalar, y * scalar, z * scalar);
+  }
+
+  /**
+   * Divide vector by a scalar.
+   *
+   * \include Vector_Divide.txt
+   * @since 1.0
+   */
+  Vector operator/(float scalar) const {
+    return Vector(x / scalar, y / scalar, z / scalar);
+  }
+
+#if !defined(SWIG)
+  /**
+   * Multiply vector by a scalar on the left-hand side (C++ only).
+   *
+   * \include Vector_Left_Times.txt
+   * @since 1.0
+   */
+  friend Vector operator*(float scalar, const Vector& vector) {
+    return Vector(vector.x * scalar, vector.y * scalar, vector.z * scalar);
+  }
+#endif
+
+  /**
+   * Add vectors component-wise and assign the sum.
+   * @since 1.0
+   */
+  Vector& operator+=(const Vector& other) {
+    x += other.x;
+    y += other.y;
+    z += other.z;
+    return *this;
+  }
+
+  /**
+   * Subtract vectors component-wise and assign the difference.
+   * @since 1.0
+   */
+  Vector& operator-=(const Vector& other) {
+    x -= other.x;
+    y -= other.y;
+    z -= other.z;
+    return *this;
+  }
+
+  /**
+   * Multiply vector by a scalar and assign the product.
+   * @since 1.0
+   */
+  Vector& operator*=(float scalar) {
+    x *= scalar;
+    y *= scalar;
+    z *= scalar;
+    return *this;
+  }
+
+  /**
+   * Divide vector by a scalar and assign the quotient.
+   * @since 1.0
+   */
+  Vector& operator/=(float scalar) {
+    x /= scalar;
+    y /= scalar;
+    z /= scalar;
+    return *this;
+  }
+
+  /**
+   * Returns a string containing this vector in a human readable format: (x, y, z).
+   * @since 1.0
+   */
+  std::string toString() const {
+    std::stringstream result;
+    result << "(" << x << ", " << y << ", " << z << ")";
+    return result.str();
+  }
+  /**
+   * Writes the vector to the output stream using a human readable format: (x, y, z).
+   * @since 1.0
+   */
+  friend std::ostream& operator<<(std::ostream& out, const Vector& vector) {
+    return out << vector.toString();
+  }
+
+  /**
+   * Compare Vector equality component-wise.
+   *
+   * \include Vector_Equals.txt
+   * @since 1.0
+   */
+  bool operator==(const Vector& other) const {
+    return x == other.x && y == other.y && z == other.z;
+  }
+  /**
+   * Compare Vector inequality component-wise.
+   *
+   * \include Vector_NotEqual.txt
+   * @since 1.0
+   */
+  bool operator!=(const Vector& other) const {
+    return x != other.x || y != other.y || z != other.z;
+  }
+
+  /**
+   * Returns true if all of the vector's components are finite.  If any
+   * component is NaN or infinite, then this returns false.
+   *
+   * \include Vector_IsValid.txt
+   * @since 1.0
+   */
+  bool isValid() const {
+    return (x <= FLT_MAX && x >= -FLT_MAX) &&
+           (y <= FLT_MAX && y >= -FLT_MAX) &&
+           (z <= FLT_MAX && z >= -FLT_MAX);
+  }
+
+  /**
+   * Index vector components numerically.
+   * Index 0 is x, index 1 is y, and index 2 is z.
+   * @returns The x, y, or z component of this Vector, if the specified index
+   * value is at least 0 and at most 2; otherwise, returns zero.
+   *
+   * \include Vector_Index.txt
+   * @since 1.0
+   */
+  float operator[](unsigned int index) const {
+    return index < 3 ? (&x)[index] : 0.0f;
+  }
+
+  /**
+   * Cast the vector to a float array.
+   *
+   * \include Vector_ToFloatPointer.txt
+   * @since 1.0
+   */
+  const float* toFloatPointer() const {
+    return &x; /* Note: Assumes x, y, z are aligned in memory. */
+  }
+
+  /**
+   * Convert a Leap::Vector to another 3-component Vector type.
+   *
+   * The specified type must define a constructor that takes the x, y, and z
+   * components as separate parameters.
+   * @since 1.0
+   */
+  template<typename Vector3Type>
+  const Vector3Type toVector3() const {
+    return Vector3Type(x, y, z);
+  }
+
+  /**
+   * Convert a Leap::Vector to another 4-component Vector type.
+   *
+   * The specified type must define a constructor that takes the x, y, z, and w
+   * components as separate parameters. (The homogeneous coordinate, w, is set
+   * to zero by default, but you should typically set it to one for vectors
+   * representing a position.)
+   * @since 1.0
+   */
+  template<typename Vector4Type>
+  const Vector4Type toVector4(float w=0.0f) const {
+    return Vector4Type(x, y, z, w);
+  }
+
+  /**
+   * The horizontal component.
+   * @since 1.0
+   */
+  float x;
+  /**
+   * The vertical component.
+   * @since 1.0
+   */
+  float y;
+  /**
+   * The depth component.
+   * @since 1.0
+   */
+  float z;
+};
+
+
+/**
+ * The FloatArray struct is used to allow the returning of native float arrays
+ * without requiring dynamic memory allocation.  It represents a matrix
+ * with a size up to 4x4.
+ * @since 1.0
+ */
+struct FloatArray {
+  /**
+   * Access the elements of the float array exactly like a native array.
+   * @since 1.0
+   */
+  float& operator[] (unsigned int index) {
+    return m_array[index];
+  }
+
+  /**
+   * Use the Float Array anywhere a float pointer can be used.
+   * @since 1.0
+   */
+  operator float* () {
+    return m_array;
+  }
+
+  /**
+   * Use the Float Array anywhere a const float pointer can be used.
+   * @since 1.0
+   */
+  operator const float* () const {
+    return m_array;
+  }
+
+  /**
+   * An array containing up to 16 entries of the matrix.
+   * @since 1.0
+   */
+  float m_array[16];
+};
+
+/**
+ * The Matrix struct represents a transformation matrix.
+ *
+ * To use this struct to transform a Vector, construct a matrix containing the
+ * desired transformation and then use the Matrix::transformPoint() or
+ * Matrix::transformDirection() functions to apply the transform.
+ *
+ * Transforms can be combined by multiplying two or more transform matrices using
+ * the * operator.
+ * @since 1.0
+ */
+struct Matrix
+{
+  /**
+   * Constructs an identity transformation matrix.
+   *
+   * \include Matrix_Matrix.txt
+   *
+   * @since 1.0
+   */
+  Matrix() :
+    xBasis(1, 0, 0),
+    yBasis(0, 1, 0),
+    zBasis(0, 0, 1),
+    origin(0, 0, 0) {
+  }
+
+  /**
+   * Constructs a copy of the specified Matrix object.
+   *
+   * \include Matrix_Matrix_copy.txt
+   *
+   * @since 1.0
+   */
+  Matrix(const Matrix& other) :
+    xBasis(other.xBasis),
+    yBasis(other.yBasis),
+    zBasis(other.zBasis),
+    origin(other.origin) {
+  }
+
+  /**
+   * Constructs a transformation matrix from the specified basis vectors.
+   *
+   * \include Matrix_Matrix_basis.txt
+   *
+   * @param _xBasis A Vector specifying rotation and scale factors for the x-axis.
+   * @param _yBasis A Vector specifying rotation and scale factors for the y-axis.
+   * @param _zBasis A Vector specifying rotation and scale factors for the z-axis.
+   * @since 1.0
+   */
+  Matrix(const Vector& _xBasis, const Vector& _yBasis, const Vector& _zBasis) :
+    xBasis(_xBasis),
+    yBasis(_yBasis),
+    zBasis(_zBasis),
+    origin(0, 0, 0) {
+  }
+
+  /**
+   * Constructs a transformation matrix from the specified basis and translation vectors.
+   *
+   * \include Matrix_Matrix_basis_origin.txt
+   *
+   * @param _xBasis A Vector specifying rotation and scale factors for the x-axis.
+   * @param _yBasis A Vector specifying rotation and scale factors for the y-axis.
+   * @param _zBasis A Vector specifying rotation and scale factors for the z-axis.
+   * @param _origin A Vector specifying translation factors on all three axes.
+   * @since 1.0
+   */
+  Matrix(const Vector& _xBasis, const Vector& _yBasis, const Vector& _zBasis, const Vector& _origin) :
+    xBasis(_xBasis),
+    yBasis(_yBasis),
+    zBasis(_zBasis),
+    origin(_origin) {
+  }
+
+  /**
+   * Constructs a transformation matrix specifying a rotation around the specified vector.
+   *
+   * \include Matrix_Matrix_rotation.txt
+   *
+   * @param axis A Vector specifying the axis of rotation.
+   * @param angleRadians The amount of rotation in radians.
+   * @since 1.0
+   */
+  Matrix(const Vector& axis, float angleRadians) :
+    origin(0, 0, 0) {
+    setRotation(axis, angleRadians);
+  }
+
+  /**
+   * Constructs a transformation matrix specifying a rotation around the specified vector
+   * and a translation by the specified vector.
+   *
+   * \include Matrix_Matrix_rotation_translation.txt
+   *
+   * @param axis A Vector specifying the axis of rotation.
+   * @param angleRadians The angle of rotation in radians.
+   * @param translation A Vector representing the translation part of the transform.
+   * @since 1.0
+   */
+  Matrix(const Vector& axis, float angleRadians, const Vector& translation)
+    : origin(translation) {
+    setRotation(axis, angleRadians);
+  }
+
+  /**
+   * Returns the identity matrix specifying no translation, rotation, and scale.
+   *
+   * \include Matrix_identity.txt
+   *
+   * @returns The identity matrix.
+   * @since 1.0
+   */
+  static const Matrix& identity() {
+    static Matrix s_identity;
+    return s_identity;
+  }
+
+  /**
+   * Sets this transformation matrix to represent a rotation around the specified vector.
+   *
+   * \include Matrix_setRotation.txt
+   *
+   * This function erases any previous rotation and scale transforms applied
+   * to this matrix, but does not affect translation.
+   *
+   * @param axis A Vector specifying the axis of rotation.
+   * @param angleRadians The amount of rotation in radians.
+   * @since 1.0
+   */
+  void setRotation(const Vector& axis, float angleRadians) {
+    const Vector n = axis.normalized();
+    const float s = std::sin(angleRadians);
+    const float c = std::cos(angleRadians);
+    const float C = (1-c);
+
+    xBasis = Vector(n[0]*n[0]*C + c,      n[0]*n[1]*C - n[2]*s, n[0]*n[2]*C + n[1]*s);
+    yBasis = Vector(n[1]*n[0]*C + n[2]*s, n[1]*n[1]*C + c,      n[1]*n[2]*C - n[0]*s);
+    zBasis = Vector(n[2]*n[0]*C - n[1]*s, n[2]*n[1]*C + n[0]*s, n[2]*n[2]*C + c     );
+  }
+
+  /**
+   * Transforms a vector with this matrix by transforming its rotation,
+   * scale, and translation.
+   *
+   * \include Matrix_transformPoint.txt
+   *
+   * Translation is applied after rotation and scale.
+   *
+   * @param in The Vector to transform.
+   * @returns A new Vector representing the transformed original.
+   * @since 1.0
+   */
+  Vector transformPoint(const Vector& in) const {
+    return xBasis*in.x + yBasis*in.y + zBasis*in.z + origin;
+  }
+
+  /**
+   * Transforms a vector with this matrix by transforming its rotation and
+   * scale only.
+   *
+   * \include Matrix_transformDirection.txt
+   *
+   * @param in The Vector to transform.
+   * @returns A new Vector representing the transformed original.
+   * @since 1.0
+   */
+  Vector transformDirection(const Vector& in) const {
+    return xBasis*in.x + yBasis*in.y + zBasis*in.z;
+  }
+
+  /**
+   * Performs a matrix inverse if the matrix consists entirely of rigid
+   * transformations (translations and rotations).  If the matrix is not rigid,
+   * this operation will not represent an inverse.
+   *
+   * \include Matrix_rigidInverse.txt
+   *
+   * Note that all matricies that are directly returned by the API are rigid.
+   *
+   * @returns The rigid inverse of the matrix.
+   * @since 1.0
+   */
+  Matrix rigidInverse() const {
+    Matrix rotInverse = Matrix(Vector(xBasis[0], yBasis[0], zBasis[0]),
+                               Vector(xBasis[1], yBasis[1], zBasis[1]),
+                               Vector(xBasis[2], yBasis[2], zBasis[2]));
+    rotInverse.origin = rotInverse.transformDirection( -origin );
+    return rotInverse;
+  }
+
+  /**
+   * Multiply transform matrices.
+   *
+   * Combines two transformations into a single equivalent transformation.
+   *
+   * \include Matrix_operator_times.txt
+   *
+   * @param other A Matrix to multiply on the right hand side.
+   * @returns A new Matrix representing the transformation equivalent to
+   * applying the other transformation followed by this transformation.
+   * @since 1.0
+   */
+  Matrix operator*(const Matrix& other) const {
+    return Matrix(transformDirection(other.xBasis),
+                  transformDirection(other.yBasis),
+                  transformDirection(other.zBasis),
+                  transformPoint(other.origin));
+  }
+
+  /**
+   * Multiply transform matrices and assign the product.
+   *
+   * \include Matrix_operator_times_equal.txt
+   *
+   * @since 1.0
+   */
+  Matrix& operator*=(const Matrix& other) {
+    return (*this) = (*this) * other;
+  }
+
+  /**
+   * Compare Matrix equality component-wise.
+   *
+   * \include Matrix_operator_equals.txt
+   *
+   * @since 1.0
+   */
+  bool operator==(const Matrix& other) const {
+    return xBasis == other.xBasis &&
+           yBasis == other.yBasis &&
+           zBasis == other.zBasis &&
+           origin == other.origin;
+  }
+  /**
+   * Compare Matrix inequality component-wise.
+   *
+   * \include Matrix_operator_not_equals.txt
+   *
+   * @since 1.0
+   */
+  bool operator!=(const Matrix& other) const {
+    return xBasis != other.xBasis ||
+           yBasis != other.yBasis ||
+           zBasis != other.zBasis ||
+           origin != other.origin;
+  }
+
+  /**
+   * Convert a Leap::Matrix object to another 3x3 matrix type.
+   *
+   * The new type must define a constructor function that takes each matrix
+   * element as a parameter in row-major order.
+   *
+   * Translation factors are discarded.
+   * @since 1.0
+   */
+  template<typename Matrix3x3Type>
+  const Matrix3x3Type toMatrix3x3() const {
+    return Matrix3x3Type(xBasis.x, xBasis.y, xBasis.z,
+                         yBasis.x, yBasis.y, yBasis.z,
+                         zBasis.x, zBasis.y, zBasis.z);
+  }
+
+  /**
+   * Convert a Leap::Matrix object to another 4x4 matrix type.
+   *
+   * The new type must define a constructor function that takes each matrix
+   * element as a parameter in row-major order.
+   * @since 1.0
+   */
+  template<typename Matrix4x4Type>
+  const Matrix4x4Type toMatrix4x4() const {
+    return Matrix4x4Type(xBasis.x, xBasis.y, xBasis.z, 0.0f,
+                         yBasis.x, yBasis.y, yBasis.z, 0.0f,
+                         zBasis.x, zBasis.y, zBasis.z, 0.0f,
+                         origin.x, origin.y, origin.z, 1.0f);
+  }
+
+  /**
+   * Writes the 3x3 Matrix object to a 9 element row-major float or
+   * double array.
+   *
+   * Translation factors are discarded.
+   *
+   * Returns a pointer to the same data.
+   * @since 1.0
+   */
+  template<typename T>
+  T* toArray3x3(T* output) const {
+    output[0] = xBasis.x; output[1] = xBasis.y; output[2] = xBasis.z;
+    output[3] = yBasis.x; output[4] = yBasis.y; output[5] = yBasis.z;
+    output[6] = zBasis.x; output[7] = zBasis.y; output[8] = zBasis.z;
+    return output;
+  }
+
+  /**
+   * Convert a 3x3 Matrix object to a 9 element row-major float array.
+   *
+   * Translation factors are discarded.
+   *
+   * \include Matrix_toArray3x3.txt
+   *
+   * Returns a FloatArray struct to avoid dynamic memory allocation.
+   * @since 1.0
+   */
+  FloatArray toArray3x3() const {
+    FloatArray output;
+    toArray3x3((float*)output);
+    return output;
+  }
+
+  /**
+   * Writes the 4x4 Matrix object to a 16 element row-major float
+   * or double array.
+   *
+   * Returns a pointer to the same data.
+   * @since 1.0
+   */
+  template<typename T>
+  T* toArray4x4(T* output) const {
+    output[0]  = xBasis.x; output[1]  = xBasis.y; output[2]  = xBasis.z; output[3]  = 0.0f;
+    output[4]  = yBasis.x; output[5]  = yBasis.y; output[6]  = yBasis.z; output[7]  = 0.0f;
+    output[8]  = zBasis.x; output[9]  = zBasis.y; output[10] = zBasis.z; output[11] = 0.0f;
+    output[12] = origin.x; output[13] = origin.y; output[14] = origin.z; output[15] = 1.0f;
+    return output;
+  }
+
+  /**
+   * Convert a 4x4 Matrix object to a 16 element row-major float array.
+   *
+   * \include Matrix_toArray4x4.txt
+   *
+   * Returns a FloatArray struct to avoid dynamic memory allocation.
+   * @since 1.0
+   */
+  FloatArray toArray4x4() const {
+    FloatArray output;
+    toArray4x4((float*)output);
+    return output;
+  }
+
+  /**
+   * Write the matrix to a string in a human readable format.
+   * @since 1.0
+   */
+  std::string toString() const {
+    std::stringstream result;
+    result << "xBasis:" << xBasis.toString() << " yBasis:" << yBasis.toString()
+           << " zBasis:" << zBasis.toString() << " origin:" << origin.toString();
+    return result.str();
+  }
+
+  /**
+   * Write the matrix to an output stream in a human readable format.
+   *
+   * \include Matrix_operator_stream.txt
+   *
+   * @since 1.0
+   */
+  friend std::ostream& operator<<(std::ostream& out, const Matrix& matrix) {
+    return out << matrix.toString();
+  }
+
+  /**
+   * The rotation and scale factors for the x-axis.
+   *
+   * \include Matrix_xBasis.txt
+   *
+   * @since 1.0
+   */
+  Vector xBasis;
+  /**
+   * The rotation and scale factors for the y-axis.
+   *
+   * \include Matrix_yBasis.txt
+   *
+   * @since 1.0
+   */
+  Vector yBasis;
+  /**
+   * The rotation and scale factors for the z-axis.
+   *
+   * \include Matrix_zBasis.txt
+   *
+   * @since 1.0
+   */
+  Vector zBasis;
+  /**
+   * The translation factors for all three axes.
+   *
+   * \include Matrix_origin.txt
+   *
+   * @since 1.0
+   */
+  Vector origin;
+};
+
+}; // namespace Leap
+
+#endif // __LeapMath_h__

BIN
engine/lib/LeapSDK/lib/libc++/libLeap.dylib


BIN
engine/lib/LeapSDK/lib/x64/Leap.dll


BIN
engine/lib/LeapSDK/lib/x64/Leapd.dll


BIN
engine/lib/LeapSDK/lib/x64/msvcp100.dll


BIN
engine/lib/LeapSDK/lib/x64/msvcp100d.dll


BIN
engine/lib/LeapSDK/lib/x64/msvcr100.dll


BIN
engine/lib/LeapSDK/lib/x64/msvcr100d.dll


BIN
engine/lib/LeapSDK/lib/x86/Leap.dll


BIN
engine/lib/LeapSDK/lib/x86/Leap.lib


BIN
engine/lib/LeapSDK/lib/x86/Leapd.dll


BIN
engine/lib/LeapSDK/lib/x86/Leapd.lib


BIN
engine/lib/LeapSDK/lib/x86/msvcp100.dll


BIN
engine/lib/LeapSDK/lib/x86/msvcp100d.dll


BIN
engine/lib/LeapSDK/lib/x86/msvcr100.dll


BIN
engine/lib/LeapSDK/lib/x86/msvcr100d.dll


+ 124 - 14
engine/source/2d/assets/AnimationAsset.cc

@@ -79,13 +79,15 @@ IMPLEMENT_CONOBJECT(AnimationAsset);
 
 
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 
 
-AnimationAsset::AnimationAsset() :    mAnimationTime(1.0f),
-                                                    mAnimationCycle(true),
-                                                    mRandomStart(false),
-                                                    mAnimationIntegration(0.0f)
+AnimationAsset::AnimationAsset() :  mAnimationTime(1.0f),
+                                    mAnimationCycle(true),
+                                    mRandomStart(false),
+                                    mAnimationIntegration(0.0f),
+                                    mNamedCellsMode(false)
 {
 {
     // Set Vector Associations.
     // Set Vector Associations.
     VECTOR_SET_ASSOCIATION( mAnimationFrames );
     VECTOR_SET_ASSOCIATION( mAnimationFrames );
+    VECTOR_SET_ASSOCIATION( mNamedAnimationFrames );
     VECTOR_SET_ASSOCIATION( mValidatedFrames );    
     VECTOR_SET_ASSOCIATION( mValidatedFrames );    
 }
 }
 
 
@@ -104,9 +106,11 @@ void AnimationAsset::initPersistFields()
 
 
     addProtectedField("Image", TypeImageAssetPtr, Offset(mImageAsset, AnimationAsset), &setImage, &defaultProtectedGetFn, &writeImage, "");
     addProtectedField("Image", TypeImageAssetPtr, Offset(mImageAsset, AnimationAsset), &setImage, &defaultProtectedGetFn, &writeImage, "");
     addProtectedField("AnimationFrames", TypeS32Vector, Offset(mAnimationFrames, AnimationAsset), &setAnimationFrames, &defaultProtectedGetFn, &writeAnimationFrames, "");
     addProtectedField("AnimationFrames", TypeS32Vector, Offset(mAnimationFrames, AnimationAsset), &setAnimationFrames, &defaultProtectedGetFn, &writeAnimationFrames, "");
+    addProtectedField("NamedAnimationFrames", TypeStringTableEntryVector, Offset(mNamedAnimationFrames, AnimationAsset), &setNamedAnimationFrames, &defaultProtectedGetFn, &writeNamedAnimationFrames, "");
     addProtectedField("AnimationTime", TypeF32, Offset(mAnimationTime, AnimationAsset), &setAnimationTime, &defaultProtectedGetFn, &defaultProtectedWriteFn, "");
     addProtectedField("AnimationTime", TypeF32, Offset(mAnimationTime, AnimationAsset), &setAnimationTime, &defaultProtectedGetFn, &defaultProtectedWriteFn, "");
     addProtectedField("AnimationCycle", TypeBool, Offset(mAnimationCycle, AnimationAsset), &setAnimationCycle, &defaultProtectedGetFn, &writeAnimationCycle, "");
     addProtectedField("AnimationCycle", TypeBool, Offset(mAnimationCycle, AnimationAsset), &setAnimationCycle, &defaultProtectedGetFn, &writeAnimationCycle, "");
     addProtectedField("RandomStart", TypeBool, Offset(mRandomStart, AnimationAsset), &setRandomStart, &defaultProtectedGetFn, &writeRandomStart, "");
     addProtectedField("RandomStart", TypeBool, Offset(mRandomStart, AnimationAsset), &setRandomStart, &defaultProtectedGetFn, &writeRandomStart, "");
+    addProtectedField("NamedCellsMode", TypeBool, Offset(mNamedCellsMode, AnimationAsset), &setNamedCellsMode, &defaultProtectedGetFn, &writeNamedCellsMode, "");
 }
 }
 
 
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
@@ -156,10 +160,17 @@ void AnimationAsset::copyTo(SimObject* object)
 
 
     // Copy state.
     // Copy state.
     pAsset->setImage( getImage().getAssetId() );
     pAsset->setImage( getImage().getAssetId() );
-    pAsset->setAnimationFrames( Con::getData( TypeS32Vector, (void*)&getSpecifiedAnimationFrames(), 0 ) );
+
+    // Are we in named cells mode?
+    if ( !pAsset->getNamedCellsMode() )
+        pAsset->setAnimationFrames( Con::getData( TypeS32Vector, (void*)&getSpecifiedAnimationFrames(), 0 ) );
+    else
+        pAsset->setNamedAnimationFrames( Con::getData( TypeStringTableEntryVector, (void*)&getSpecifiedNamedAnimationFrames(), 0 ) );
+
     pAsset->setAnimationTime( getAnimationTime() );
     pAsset->setAnimationTime( getAnimationTime() );
     pAsset->setAnimationCycle( getAnimationCycle() );
     pAsset->setAnimationCycle( getAnimationCycle() );
     pAsset->setRandomStart( getRandomStart() );
     pAsset->setRandomStart( getRandomStart() );
+    pAsset->setNamedCellsMode( getNamedCellsMode() );
 }
 }
 
 
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
@@ -200,6 +211,34 @@ void AnimationAsset::setAnimationFrames( const char* pAnimationFrames )
         mAnimationFrames.push_back( dAtoi( StringUnit::getUnit( pAnimationFrames, frameIndex, " \t\n" ) ) );
         mAnimationFrames.push_back( dAtoi( StringUnit::getUnit( pAnimationFrames, frameIndex, " \t\n" ) ) );
     }
     }
 
 
+    mNamedCellsMode = false;
+
+    // Validate frames.
+    validateFrames();
+
+    // Refresh the asset.
+    refreshAsset();
+}
+
+//------------------------------------------------------------------------------
+
+void AnimationAsset::setNamedAnimationFrames( const char* pAnimationFrames )
+{
+    // Clear any existing frames.
+    mNamedAnimationFrames.clear();
+
+    // Fetch frame count.
+    const U32 frameCount = StringUnit::getUnitCount( pAnimationFrames, " \t\n" );
+
+    // Iterate frames.
+    for( U32 frameIndex = 0; frameIndex < frameCount; ++frameIndex )
+    {
+        // Store frame.
+        mNamedAnimationFrames.push_back( StringTable->insert( StringUnit::getUnit( pAnimationFrames, frameIndex, " \t\n" ) ) );
+    }
+
+    mNamedCellsMode = true;
+
     // Validate frames.
     // Validate frames.
     validateFrames();
     validateFrames();
 
 
@@ -254,18 +293,26 @@ void AnimationAsset::setRandomStart( const bool randomStart )
 
 
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 
 
-void AnimationAsset::validateFrames( void )
+void AnimationAsset::setNamedCellsMode( const bool namedCellsMode )
 {
 {
-    // Debug Profiling.
-    PROFILE_SCOPE(AnimationAsset_ValidateFrames);
+    // Ignore no change.
+    if ( mNamedCellsMode == mNamedCellsMode)
+        return;
+
+    // Update.
+    mNamedCellsMode = namedCellsMode;
+
+    // Refresh the asset.
+    refreshAsset();
+}
 
 
+//------------------------------------------------------------------------------
+
+void AnimationAsset::validateNumericalFrames( void )
+{
     // Clear validated frames.
     // Clear validated frames.
     mValidatedFrames.clear();
     mValidatedFrames.clear();
 
 
-    // Finish if we don't have a valid image asset.
-    if ( mImageAsset.isNull() )
-        return;
-
     // Fetch Animation Frame Count.
     // Fetch Animation Frame Count.
     const U32 animationFrameCount = (U32)mAnimationFrames.size();
     const U32 animationFrameCount = (U32)mAnimationFrames.size();
 
 
@@ -290,12 +337,13 @@ void AnimationAsset::validateFrames( void )
         if ( frame < 0 || frame >= imageAssetFrameCount )
         if ( frame < 0 || frame >= imageAssetFrameCount )
         {
         {
             // No, warn.
             // No, warn.
-            Con::warnf( "AnimationAsset::validateFrames() - Animation asset '%s' specifies an out-of-bound frame of '%d' (key-index:'%d') against image asset Id '%s'.",
+            Con::warnf( "AnimationAsset::validateFrames() - Animation asset '%s' specifies an out-of-bound frame of '%d' (key-index:'%d') against image asset Id '%s'.", 
                 getAssetName(),
                 getAssetName(),
                 frame,
                 frame,
                 frameIndex,
                 frameIndex,
                 mImageAsset.getAssetId() );
                 mImageAsset.getAssetId() );
 
 
+            // Set the frame to a valid one.
             if ( frame < 0 )
             if ( frame < 0 )
                 frame = 0;
                 frame = 0;
             else if ( frame >= imageAssetFrameCount )
             else if ( frame >= imageAssetFrameCount )
@@ -309,9 +357,71 @@ void AnimationAsset::validateFrames( void )
 
 
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 
 
+void AnimationAsset::validateNamedFrames( void )
+{
+    mValidatedNameFrames.clear();
+
+    // Fetch Animation Frame Count.
+    const U32 animationFrameCount = (U32)mNamedAnimationFrames.size();
+
+    // Finish if no animation frames are specified.
+    if ( animationFrameCount == 0 )
+        return;
+
+    // Fetch image asset frame count.
+    const S32 imageAssetFrameCount = (S32)mImageAsset->getFrameCount();
+
+    // Finish if the image has no frames.
+    if ( imageAssetFrameCount == 0 )
+        return;
+
+    // Validate each specified frame.
+    for ( U32 frameIndex = 0; frameIndex < animationFrameCount; ++frameIndex )
+    {
+        // Fetch frame.
+        const char* frame = mNamedAnimationFrames[frameIndex];
+
+        // Valid Frame?
+        if ( frame ==  StringTable->EmptyString || !mImageAsset->containsFrame(frame) )
+        {
+            // No, warn.
+            Con::warnf( "AnimationAsset::validateNamedFrames() - Animation asset '%s' specifies a bad frame '%s' against image asset Id '%s'.",
+                getAssetName(),
+                frame,
+                mImageAsset.getAssetId() );
+        }
+
+        // Use frame.
+        mValidatedNameFrames.push_back( StringTable->insert(frame) );
+    }
+}
+
+//------------------------------------------------------------------------------
+
+void AnimationAsset::validateFrames( void )
+{
+    // Debug Profiling.
+    PROFILE_SCOPE(AnimationAsset_ValidateFrames);
+
+    // Finish if we don't have a valid image asset.
+    if ( mImageAsset.isNull() )
+        return;
+
+    if (mNamedCellsMode)
+    {
+        validateNamedFrames();
+    }
+    else
+    {
+        validateNumericalFrames();
+    }
+}
+
+//------------------------------------------------------------------------------
+
 bool AnimationAsset::isAssetValid( void ) const
 bool AnimationAsset::isAssetValid( void ) const
 {
 {
-    return mImageAsset.notNull() && mImageAsset->isAssetValid() && mValidatedFrames.size() > 0;
+    return mImageAsset.notNull() && mImageAsset->isAssetValid() && (mValidatedFrames.size() > 0 || mValidatedNameFrames.size() > 0);
 }
 }
 
 
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------

+ 32 - 16
engine/source/2d/assets/AnimationAsset.h

@@ -42,14 +42,18 @@ class AnimationAsset : public AssetBase
 private:
 private:
     typedef AssetBase  Parent;
     typedef AssetBase  Parent;
 
 
-    AssetPtr<ImageAsset>    mImageAsset;
-    Vector<S32>             mAnimationFrames;
-    Vector<S32>             mValidatedFrames;
-    F32                     mAnimationTime;
-    bool                    mAnimationCycle;
-    bool                    mRandomStart;
+    AssetPtr<ImageAsset>     mImageAsset;
+    Vector<S32>              mAnimationFrames;
+    Vector<StringTableEntry> mNamedAnimationFrames;
+    Vector<S32>              mValidatedFrames;
+    Vector<StringTableEntry> mValidatedNameFrames;
+    F32                      mAnimationTime;
+    bool                     mAnimationCycle;
+    bool                     mRandomStart;
 
 
-    F32                     mAnimationIntegration;
+    F32                      mAnimationIntegration;
+
+    bool                     mNamedCellsMode;
 
 
 public:
 public:
     AnimationAsset();
     AnimationAsset();
@@ -67,15 +71,23 @@ public:
     inline const Vector<S32>& getSpecifiedAnimationFrames( void ) const { return mAnimationFrames; }
     inline const Vector<S32>& getSpecifiedAnimationFrames( void ) const { return mAnimationFrames; }
     inline const Vector<S32>& getValidatedAnimationFrames( void ) const { return mValidatedFrames; }
     inline const Vector<S32>& getValidatedAnimationFrames( void ) const { return mValidatedFrames; }
 
 
+    void            setNamedAnimationFrames( const char* pAnimationFrames );
+    inline const Vector<StringTableEntry>& getSpecifiedNamedAnimationFrames( void ) const { return mNamedAnimationFrames; }
+    inline const Vector<StringTableEntry>& getValidatedNamedAnimationFrames( void ) const { return mValidatedNameFrames; }
+
     void            setAnimationTime( const F32 animationTime );
     void            setAnimationTime( const F32 animationTime );
     inline F32      getAnimationTime( void ) const                      { return mAnimationTime; }
     inline F32      getAnimationTime( void ) const                      { return mAnimationTime; }
     void            setAnimationCycle( const bool animationCycle );
     void            setAnimationCycle( const bool animationCycle );
     inline bool     getAnimationCycle( void ) const                     { return mAnimationCycle; }
     inline bool     getAnimationCycle( void ) const                     { return mAnimationCycle; }
     void            setRandomStart( const bool randomStart );
     void            setRandomStart( const bool randomStart );
     inline bool     getRandomStart( void ) const                        { return mRandomStart; }
     inline bool     getRandomStart( void ) const                        { return mRandomStart; }
+    void            setNamedCellsMode( const bool namedCellsMode );
+    inline bool     getNamedCellsMode( void ) const                     { return mNamedCellsMode; }
 
 
     // Frame validation.
     // Frame validation.
     void            validateFrames( void );
     void            validateFrames( void );
+    void            validateNumericalFrames( void );
+    void            validateNamedFrames( void );
 
 
     // Asset validation.
     // Asset validation.
     virtual bool    isAssetValid( void ) const;
     virtual bool    isAssetValid( void ) const;
@@ -88,15 +100,19 @@ protected:
     virtual void onAssetRefresh( void );
     virtual void onAssetRefresh( void );
 
 
 protected:
 protected:
-    static bool setImage( void* obj, const char* data )                         { static_cast<AnimationAsset*>(obj)->setImage( data ); return false; }
-    static bool writeImage( void* obj, StringTableEntry pFieldName )            { return static_cast<AnimationAsset*>(obj)->mImageAsset.notNull(); }
-    static bool setAnimationFrames( void* obj, const char* data )               { static_cast<AnimationAsset*>(obj)->setAnimationFrames( data ); return false; }    
-    static bool writeAnimationFrames( void* obj, StringTableEntry pFieldName )  { return static_cast<AnimationAsset*>(obj)->mAnimationFrames.size() > 0; }
-    static bool setAnimationTime( void* obj, const char* data )                 { static_cast<AnimationAsset*>(obj)->setAnimationTime( dAtof(data) ); return false; }
-    static bool setAnimationCycle( void* obj, const char* data )                { static_cast<AnimationAsset*>(obj)->setAnimationCycle( dAtob(data) ); return false; }
-    static bool writeAnimationCycle( void* obj, StringTableEntry pFieldName )   { return static_cast<AnimationAsset*>(obj)->getAnimationCycle() == false; }
-    static bool setRandomStart( void* obj, const char* data )                   { static_cast<AnimationAsset*>(obj)->setRandomStart( dAtob(data) ); return false; }
-    static bool writeRandomStart( void* obj, StringTableEntry pFieldName )      { return static_cast<AnimationAsset*>(obj)->getRandomStart() == true; }
+    static bool setImage( void* obj, const char* data )                             { static_cast<AnimationAsset*>(obj)->setImage( data ); return false; }
+    static bool writeImage( void* obj, StringTableEntry pFieldName )                { return static_cast<AnimationAsset*>(obj)->mImageAsset.notNull(); }
+    static bool setAnimationFrames( void* obj, const char* data )                   { static_cast<AnimationAsset*>(obj)->setAnimationFrames( data ); return false; }
+    static bool writeAnimationFrames( void* obj, StringTableEntry pFieldName )      { return static_cast<AnimationAsset*>(obj)->mAnimationFrames.size() > 0; }
+    static bool setNamedAnimationFrames( void* obj, const char* data )              { static_cast<AnimationAsset*>(obj)->setNamedAnimationFrames( data ); return false; }    
+    static bool writeNamedAnimationFrames( void* obj, StringTableEntry pFieldName ) { return static_cast<AnimationAsset*>(obj)->mNamedAnimationFrames.size() > 0; }
+    static bool setAnimationTime( void* obj, const char* data )                     { static_cast<AnimationAsset*>(obj)->setAnimationTime( dAtof(data) ); return false; }
+    static bool setAnimationCycle( void* obj, const char* data )                    { static_cast<AnimationAsset*>(obj)->setAnimationCycle( dAtob(data) ); return false; }
+    static bool writeAnimationCycle( void* obj, StringTableEntry pFieldName )       { return static_cast<AnimationAsset*>(obj)->getAnimationCycle() == false; }
+    static bool setRandomStart( void* obj, const char* data )                       { static_cast<AnimationAsset*>(obj)->setRandomStart( dAtob(data) ); return false; }
+    static bool writeRandomStart( void* obj, StringTableEntry pFieldName )          { return static_cast<AnimationAsset*>(obj)->getRandomStart() == true; }
+    static bool setNamedCellsMode( void* obj, const char* data )                    { static_cast<AnimationAsset*>(obj)->setNamedCellsMode( dAtob(data) ); return false; }
+    static bool writeNamedCellsMode( void* obj, StringTableEntry pFieldName )       { return static_cast<AnimationAsset*>(obj)->getNamedCellsMode() == true; }
 };
 };
 
 
 #endif // _ANIMATION_ASSET_H_
 #endif // _ANIMATION_ASSET_H_

+ 131 - 0
engine/source/2d/assets/AnimationAsset_ScriptBinding.h

@@ -48,6 +48,14 @@ ConsoleMethodWithDocs(AnimationAsset, getImage, ConsoleString, 2, 2, ())
 */
 */
 ConsoleMethodWithDocs(AnimationAsset, setAnimationFrames, ConsoleVoid, 3, 3, (animationFrames))
 ConsoleMethodWithDocs(AnimationAsset, setAnimationFrames, ConsoleVoid, 3, 3, (animationFrames))
 {
 {
+    // Are we in named cells mode?
+    if ( object->getNamedCellsMode() )
+    {
+        // Yes, so warn.
+        Con::warnf( "AnimationAsset::setAnimationFrames() - Method invalid, in named cells mode." );
+        return;
+    }
+
     object->setAnimationFrames( argv[2] );
     object->setAnimationFrames( argv[2] );
 }
 }
 
 
@@ -59,6 +67,14 @@ ConsoleMethodWithDocs(AnimationAsset, setAnimationFrames, ConsoleVoid, 3, 3, (an
 */
 */
 ConsoleMethodWithDocs(AnimationAsset, getAnimationFrames, ConsoleString, 2, 3, ([bool validatedFrames]))
 ConsoleMethodWithDocs(AnimationAsset, getAnimationFrames, ConsoleString, 2, 3, ([bool validatedFrames]))
 {
 {
+    // Are we in named cells mode?
+    if ( object->getNamedCellsMode() )
+    {
+        // Yes, so warn.
+        Con::warnf( "AnimationAsset::getAnimationFrames() - Method invalid, in named cells mode." );
+        return StringTable->EmptyString;
+    }
+
     // Fetch a return buffer.
     // Fetch a return buffer.
     S32 bufferSize = 4096;
     S32 bufferSize = 4096;
     char* pBuffer = Con::getReturnBuffer( bufferSize );
     char* pBuffer = Con::getReturnBuffer( bufferSize );
@@ -93,6 +109,14 @@ ConsoleMethodWithDocs(AnimationAsset, getAnimationFrames, ConsoleString, 2, 3, (
 */
 */
 ConsoleMethodWithDocs(AnimationAsset, getAnimationFrameCount, ConsoleInt, 2, 3, ([bool validatedFrames]))
 ConsoleMethodWithDocs(AnimationAsset, getAnimationFrameCount, ConsoleInt, 2, 3, ([bool validatedFrames]))
 {
 {
+    // Are we in named cells mode?
+    if ( object->getNamedCellsMode() )
+    {
+        // Yes, so warn.
+        Con::warnf( "AnimationAsset::getAnimationFrameCount() - Method invalid, in named cells mode." );
+        return -1;
+    }
+
     // Fetch validated frames flag.
     // Fetch validated frames flag.
     const bool validatedFrames = argc >= 3 ? dAtob( argv[2] ) : false;
     const bool validatedFrames = argc >= 3 ? dAtob( argv[2] ) : false;
 
 
@@ -104,6 +128,92 @@ ConsoleMethodWithDocs(AnimationAsset, getAnimationFrameCount, ConsoleInt, 2, 3,
 
 
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
 
 
+/*! Sets the named image frames that compose the animation.
+    @param animationFrames A set of named image frames that compose the animation.
+    @return No return value.
+*/
+ConsoleMethodWithDocs(AnimationAsset, setNamedAnimationFrames, ConsoleVoid, 3, 3, (animationFrames))
+{
+    // Are we in named cells mode?
+    if ( !object->getNamedCellsMode() )
+    {
+        // No, so warn.
+        Con::warnf( "AnimationAsset::setNamedAnimationFrames() - Method invalid, not in named cells mode." );
+        return;
+    }
+
+    object->setNamedAnimationFrames( argv[2] );
+}
+
+//-----------------------------------------------------------------------------
+
+/*! Gets the named frames that compose the animation or optionally only the ones validated against the image asset.
+    @param validatedFrames - Whether to return only the validated frames or not.  Optional: Default is false.
+    @return The named image frames that compose the animation or optionally only the ones validated against the image asset.
+*/
+ConsoleMethodWithDocs(AnimationAsset, getNamedAnimationFrames, ConsoleString, 2, 3, ([bool validatedFrames]))
+{
+    // Are we in named cells mode?
+    if ( !object->getNamedCellsMode() )
+    {
+        // No, so warn.
+        Con::warnf( "AnimationAsset::getNamedAnimationFrames() - Method invalid, not in named cells mode." );
+        return StringTable->EmptyString;
+    }
+
+    // Fetch a return buffer.
+    S32 bufferSize = 4096;
+    char* pBuffer = Con::getReturnBuffer( bufferSize );
+    char* pReturnBuffer = pBuffer;    
+
+    // Fetch validated frames flag.
+    const bool validatedFrames = argc >= 3 ? dAtob( argv[2] ) : false;
+
+    // Fetch specified frames.
+    const Vector<StringTableEntry>& frames = validatedFrames ? object->getValidatedNamedAnimationFrames() : object->getSpecifiedNamedAnimationFrames();
+
+    // Fetch frame count.
+    const U32 frameCount = (U32)frames.size();
+
+    // Format frames.
+    for ( U32 frameIndex = 0; frameIndex < frameCount; ++frameIndex )
+    {
+        const S32 offset = dSprintf( pBuffer, bufferSize, "%d ", frames[frameIndex] );
+        pBuffer += offset;
+        bufferSize -= offset;
+    }
+
+    // Return frames.
+    return pReturnBuffer;
+}
+
+//-----------------------------------------------------------------------------
+
+/*! Gets the count of named frames that compose the animation or optionally only the ones validated against the image asset.
+    @param validatedFrames - Whether to return only the validated frames or not.  Optional: Default is false.
+    @return The named image frames that compose the animation or optionally only the ones validated against the image asset.
+*/
+ConsoleMethodWithDocs(AnimationAsset, getNamedAnimationFrameCount, ConsoleInt, 2, 3, ([bool validatedFrames]))
+{
+    // Are we in named cells mode?
+    if ( !object->getNamedCellsMode() )
+    {
+        // No, so warn.
+        Con::warnf( "AnimationAsset::getNamedAnimationFrameCount() - Method invalid, not in named cells mode." );
+        return -1;
+    }
+
+    // Fetch validated frames flag.
+    const bool validatedFrames = argc >= 3 ? dAtob( argv[2] ) : false;
+
+    // Fetch specified frames.
+    const Vector<StringTableEntry>& frames = validatedFrames ? object->getValidatedNamedAnimationFrames() : object->getSpecifiedNamedAnimationFrames();
+
+    return frames.size();
+}
+
+//-----------------------------------------------------------------------------
+
 /*! Sets the total time to cycle through all animation frames.
 /*! Sets the total time to cycle through all animation frames.
     @param animationTime The total time to cycle through all animation frames.
     @param animationTime The total time to cycle through all animation frames.
     @return No return value.
     @return No return value.
@@ -145,4 +255,25 @@ ConsoleMethodWithDocs(AnimationAsset, getAnimationCycle, ConsoleBool, 2, 2, ())
     return object->getAnimationCycle();
     return object->getAnimationCycle();
 }
 }
 
 
+//-----------------------------------------------------------------------------
+
+/*! Sets whether the animation uses names for cells, instead of numerical index.
+    @param namedCellsMode True if it should be using named cells.
+    @return No return value.
+*/
+ConsoleMethodWithDocs(AnimationAsset, setNamedCellsMode, ConsoleVoid, 3, 3, ())
+{
+    object->setNamedCellsMode( dAtob(argv[2] ) );
+}
+
+//-----------------------------------------------------------------------------
+
+/*! Gets whether the animation is using names for its cells.
+    @return True if the animation is using named cells.
+*/
+ConsoleMethodWithDocs(AnimationAsset, getNamedCellsMode, ConsoleBool, 2, 2, ())
+{
+    return object->getNamedCellsMode();
+}
+
 ConsoleMethodGroupEndWithDocs(AnimationAsset)
 ConsoleMethodGroupEndWithDocs(AnimationAsset)

+ 290 - 140
engine/source/2d/assets/ImageAsset.cc

@@ -107,11 +107,15 @@ ConsoleSetType( TypeImageAssetPtr )
 
 
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 
 
-static StringTableEntry cellCustomNodeName          = StringTable->insert( "Cells" );
+static StringTableEntry cellCustomNodeCellsName     = StringTable->insert( "Cells" );
 static StringTableEntry cellNodeName                = StringTable->insert( "Cell" );
 static StringTableEntry cellNodeName                = StringTable->insert( "Cell" );
+static StringTableEntry cellRegionName              = StringTable->insert( "RegionName" );
 static StringTableEntry cellOffsetName              = StringTable->insert( "Offset" );
 static StringTableEntry cellOffsetName              = StringTable->insert( "Offset" );
+static StringTableEntry cellOffsetXName             = StringTable->insert( "OffsetX" );
+static StringTableEntry cellOffsetYName             = StringTable->insert( "OffsetY" );
 static StringTableEntry cellWidthName               = StringTable->insert( "Width" );
 static StringTableEntry cellWidthName               = StringTable->insert( "Width" );
 static StringTableEntry cellHeightName              = StringTable->insert( "Height" );
 static StringTableEntry cellHeightName              = StringTable->insert( "Height" );
+static StringTableEntry cellNameEntryName           = StringTable->insert( "Name" );
 
 
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 
 
@@ -133,7 +137,7 @@ ImageAsset::TextureFilterMode ImageAsset::getFilterModeEnum(const char* label)
             return((ImageAsset::TextureFilterMode)textureFilterLookup[i].index);
             return((ImageAsset::TextureFilterMode)textureFilterLookup[i].index);
 
 
     // Warn.
     // Warn.
-    Con::warnf("ImageAsset::getFilterModeEnum() - Invalid filter-mode '%s'", label );
+    Con::warnf( "ImageAsset::getFilterModeEnum() - Invalid filter-mode '%s'", label );
 
 
     return ImageAsset::FILTER_INVALID;
     return ImageAsset::FILTER_INVALID;
 }
 }
@@ -148,7 +152,7 @@ const char* ImageAsset::getFilterModeDescription( ImageAsset::TextureFilterMode
             return textureFilterLookup[i].label;
             return textureFilterLookup[i].label;
 
 
     // Warn.
     // Warn.
-    Con::warnf("ImageAsset::getFilterModeDescription() - Invalid filter-mode." );
+    Con::warnf( "ImageAsset::getFilterModeDescription() - Invalid filter-mode." );
 
 
     return StringTable->EmptyString;
     return StringTable->EmptyString;
 }
 }
@@ -159,7 +163,6 @@ ImageAsset::ImageAsset() :  mImageFile(StringTable->EmptyString),
                             mForce16Bit(false),
                             mForce16Bit(false),
                             mLocalFilterMode(FILTER_INVALID),
                             mLocalFilterMode(FILTER_INVALID),
                             mExplicitMode(false),
                             mExplicitMode(false),
-
                             mCellRowOrder(true),
                             mCellRowOrder(true),
                             mCellOffsetX(0),
                             mCellOffsetX(0),
                             mCellOffsetY(0),
                             mCellOffsetY(0),
@@ -294,7 +297,7 @@ void ImageAsset::copyTo(SimObject* object)
         const FrameArea::PixelArea& pixelArea = getImageFrameArea( index ).mPixelArea;
         const FrameArea::PixelArea& pixelArea = getImageFrameArea( index ).mPixelArea;
 
 
         // Add the explicit cell.
         // Add the explicit cell.
-        pAsset->addExplicitCell( pixelArea.mPixelOffset.x, pixelArea.mPixelOffset.y, pixelArea.mPixelWidth, pixelArea.mPixelHeight );
+        pAsset->addExplicitCell( pixelArea.mPixelOffset.x, pixelArea.mPixelOffset.y, pixelArea.mPixelWidth, pixelArea.mPixelHeight, pixelArea.mRegionName );
     }
     }
 }
 }
 
 
@@ -550,6 +553,8 @@ void ImageAsset::setCellHeight( const S32 cellheight )
     refreshAsset();
     refreshAsset();
 }
 }
 
 
+//------------------------------------------------------------------------------
+
 S32 ImageAsset::getExplicitCellWidth(const S32 cellIndex)
 S32 ImageAsset::getExplicitCellWidth(const S32 cellIndex)
 {
 {
 	if ( !getExplicitMode() )
 	if ( !getExplicitMode() )
@@ -564,6 +569,8 @@ S32 ImageAsset::getExplicitCellWidth(const S32 cellIndex)
 
 
 }
 }
 
 
+//------------------------------------------------------------------------------
+
 S32 ImageAsset::getExplicitCellHeight(const S32 cellIndex)
 S32 ImageAsset::getExplicitCellHeight(const S32 cellIndex)
 {
 {
 	if ( !getExplicitMode() )
 	if ( !getExplicitMode() )
@@ -577,6 +584,27 @@ S32 ImageAsset::getExplicitCellHeight(const S32 cellIndex)
     return(thisCell.mPixelHeight);
     return(thisCell.mPixelHeight);
 
 
 }
 }
+
+//------------------------------------------------------------------------------
+
+bool ImageAsset::containsNamedRegion(const char* regionName)
+{
+    for( typeFrameAreaVector::iterator frameItr = mFrames.begin(); frameItr != mFrames.end(); ++frameItr )
+    {
+        // Grab the current pixelArea
+        const FrameArea::PixelArea& pixelArea = frameItr->mPixelArea;
+            
+        // Check to see if the name matches the argument
+        if (!dStrcmp(pixelArea.mRegionName, regionName))
+        {
+            // Found it, so erase it and return success
+            return true;
+        }
+    }
+    
+    return false;
+}
+
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 
 
 bool ImageAsset::clearExplicitCells( void )
 bool ImageAsset::clearExplicitCells( void )
@@ -600,33 +628,42 @@ bool ImageAsset::clearExplicitCells( void )
 
 
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 
 
-bool ImageAsset::addExplicitCell( const S32 cellOffsetX, const S32 cellOffsetY, const S32 cellWidth, const S32 cellHeight )
+bool ImageAsset::addExplicitCell( const S32 cellOffsetX, const S32 cellOffsetY, const S32 cellWidth, const S32 cellHeight, const char* regionName )
 {
 {
     // Are we in explicit mode?
     // Are we in explicit mode?
     if ( !getExplicitMode() )
     if ( !getExplicitMode() )
     {
     {
         // No, so warn.
         // No, so warn.
-        Con::warnf( "ImageAsset::addCell() - Cannot perform explicit cell operation when not in explicit mode." );
+        Con::warnf( "ImageAsset::addExplicitCell() - Cannot perform explicit cell operation when not in explicit mode." );
         return false;
         return false;
     }
     }
 
 
     // Fetch the original image dimensions.
     // Fetch the original image dimensions.
     const S32 imageWidth = getImageWidth();
     const S32 imageWidth = getImageWidth();
     const S32 imageHeight = getImageHeight();
     const S32 imageHeight = getImageHeight();
+    
+    // The region name cannot be empty
+    if ( regionName == StringTable->EmptyString )
+    {
+        Con::warnf( "ImageAsset::addExplicitCell() - Cell name of '%s' is invalid or was not set.", regionName );
+        U32 currentIndex = mExplicitFrames.size();
+        Con::warnf( "- Setting to the next index in the frame list: '%i'", currentIndex );
+        dSscanf(regionName, "%i", currentIndex);
+    }
 
 
     // The Cell Offset X needs to be within the image.
     // The Cell Offset X needs to be within the image.
     if ( cellOffsetX < 0 || cellOffsetX >= imageWidth )
     if ( cellOffsetX < 0 || cellOffsetX >= imageWidth )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::addCell() - Invalid Cell OffsetX of %d.", cellOffsetX );
+        Con::warnf( "ImageAsset::addExplicitCell() - Invalid Cell OffsetX of %d.", cellOffsetX );
         return false;
         return false;
     }
     }
 
 
     // The Cell Offset Y needs to be within the image.
     // The Cell Offset Y needs to be within the image.
-    if ( cellOffsetY < 0 || cellOffsetY >= imageWidth )
+    if ( cellOffsetY < 0 || cellOffsetY >= imageHeight )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::addCell() - Invalid Cell OffsetY of %d.", cellOffsetY );
+        Con::warnf( "ImageAsset::addExplicitCell() - Invalid Cell OffsetY of %d.", cellOffsetY );
         return false;
         return false;
     }
     }
 
 
@@ -634,7 +671,7 @@ bool ImageAsset::addExplicitCell( const S32 cellOffsetX, const S32 cellOffsetY,
     if ( cellWidth <= 0 || (cellOffsetX+cellWidth) > imageWidth )
     if ( cellWidth <= 0 || (cellOffsetX+cellWidth) > imageWidth )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::addCell() - Invalid Cell Width of %d.", cellWidth );
+        Con::warnf( "ImageAsset::addExplicitCell() - Invalid Cell Width of %d.", cellWidth );
         return false;
         return false;
     }
     }
 
 
@@ -642,12 +679,12 @@ bool ImageAsset::addExplicitCell( const S32 cellOffsetX, const S32 cellOffsetY,
     if ( cellHeight <= 0 || (cellOffsetY+cellHeight) > imageHeight )
     if ( cellHeight <= 0 || (cellOffsetY+cellHeight) > imageHeight )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::addCell() - Invalid Cell Width of %d.", cellHeight );
+        Con::warnf( "ImageAsset::addExplicitCell() - Invalid Cell Width of %d.", cellHeight );
         return false;
         return false;
     }
     }
 
 
     // Store frame.
     // Store frame.
-    FrameArea::PixelArea pixelArea( cellOffsetX, cellOffsetY, cellWidth, cellHeight );
+    FrameArea::PixelArea pixelArea( cellOffsetX, cellOffsetY, cellWidth, cellHeight, regionName );
     mExplicitFrames.push_back( pixelArea );
     mExplicitFrames.push_back( pixelArea );
 
 
     // Refresh the asset.
     // Refresh the asset.
@@ -658,13 +695,13 @@ bool ImageAsset::addExplicitCell( const S32 cellOffsetX, const S32 cellOffsetY,
 
 
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 
 
-bool ImageAsset::insertExplicitCell( const S32 cellIndex, const S32 cellOffsetX, const S32 cellOffsetY, const S32 cellWidth, const S32 cellHeight )
+bool ImageAsset::insertExplicitCell( const S32 cellIndex, const S32 cellOffsetX, const S32 cellOffsetY, const S32 cellWidth, const S32 cellHeight, const char* regionName )
 {
 {
     // Are we in explicit mode?
     // Are we in explicit mode?
     if ( !getExplicitMode() )
     if ( !getExplicitMode() )
     {
     {
         // No, so warn.
         // No, so warn.
-        Con::warnf( "ImageAsset::insertCell() - Cannot perform explicit cell operation when not in explicit mode." );
+        Con::warnf( "ImageAsset::insertExplicitCell() - Cannot perform explicit cell operation when not in explicit mode." );
         return false;
         return false;
     }
     }
 
 
@@ -674,12 +711,20 @@ bool ImageAsset::insertExplicitCell( const S32 cellIndex, const S32 cellOffsetX,
 
 
     // Fetch the explicit frame count.
     // Fetch the explicit frame count.
     const S32 explicitFramelCount = mExplicitFrames.size();
     const S32 explicitFramelCount = mExplicitFrames.size();
+    
+    // Region cannot be empty
+    if ( regionName == StringTable->EmptyString )
+    {
+        Con::warnf( "ImageAsset::insertExplicitCell() - Cell name of '%s' is invalid or was not set.", regionName );
+        Con::warnf( "- Setting to the next index in the frame list: '%i'", explicitFramelCount );
+        dSscanf(regionName, "%i", explicitFramelCount);
+    }
 
 
     // The cell index needs to be in range.
     // The cell index needs to be in range.
     if ( cellIndex < 0 )
     if ( cellIndex < 0 )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::insertCell() - Invalid Cell Index of %d.", cellIndex );
+        Con::warnf( "ImageAsset::insertExplicitCell() - Invalid Cell Index of %d.", cellIndex );
         return false;
         return false;
     }
     }
 
 
@@ -687,15 +732,15 @@ bool ImageAsset::insertExplicitCell( const S32 cellIndex, const S32 cellOffsetX,
     if ( cellOffsetX < 0 || cellOffsetX >= imageWidth )
     if ( cellOffsetX < 0 || cellOffsetX >= imageWidth )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::insertCell() - Invalid Cell OffsetX of %d.", cellOffsetX );
+        Con::warnf( "ImageAsset::insertExplicitCell() - Invalid Cell OffsetX of %d.", cellOffsetX );
         return false;
         return false;
     }
     }
 
 
     // The Cell Offset Y needs to be within the image.
     // The Cell Offset Y needs to be within the image.
-    if ( cellOffsetY < 0 || cellOffsetY >= imageWidth )
+    if ( cellOffsetY < 0 || cellOffsetY >= imageHeight )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::insertCell() - Invalid Cell OffsetY of %d.", cellOffsetY );
+        Con::warnf( "ImageAsset::insertExplicitCell() - Invalid Cell OffsetY of %d.", cellOffsetY );
         return false;
         return false;
     }
     }
 
 
@@ -703,7 +748,7 @@ bool ImageAsset::insertExplicitCell( const S32 cellIndex, const S32 cellOffsetX,
     if ( cellWidth <= 0 || (cellOffsetX+cellWidth) > imageWidth )
     if ( cellWidth <= 0 || (cellOffsetX+cellWidth) > imageWidth )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::insertCell() - Invalid Cell Width of %d.", cellWidth );
+        Con::warnf( "ImageAsset::insertExplicitCell() - Invalid Cell Width of %d.", cellWidth );
         return false;
         return false;
     }
     }
 
 
@@ -711,12 +756,12 @@ bool ImageAsset::insertExplicitCell( const S32 cellIndex, const S32 cellOffsetX,
     if ( cellHeight <= 0 || (cellOffsetY+cellHeight) > imageHeight )
     if ( cellHeight <= 0 || (cellOffsetY+cellHeight) > imageHeight )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::insertCell() - Invalid Cell Width of %d.", cellHeight );
+        Con::warnf( "ImageAsset::insertExplicitCell() - Invalid Cell Width of %d.", cellHeight );
         return false;
         return false;
     }
     }
 
 
     // Configure frame.
     // Configure frame.
-    FrameArea::PixelArea pixelArea( cellOffsetX, cellOffsetY, cellWidth, cellHeight );
+    FrameArea::PixelArea pixelArea( cellOffsetX, cellOffsetY, cellWidth, cellHeight, regionName );
 
 
     // Insert frame appropriately.
     // Insert frame appropriately.
     if ( cellIndex >= explicitFramelCount )
     if ( cellIndex >= explicitFramelCount )
@@ -737,13 +782,13 @@ bool ImageAsset::insertExplicitCell( const S32 cellIndex, const S32 cellOffsetX,
 
 
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 
 
-bool ImageAsset::setExplicitCell( const S32 cellIndex, const S32 cellOffsetX, const S32 cellOffsetY, const S32 cellWidth, const S32 cellHeight )
+bool ImageAsset::setExplicitCell( const S32 cellIndex, const S32 cellOffsetX, const S32 cellOffsetY, const S32 cellWidth, const S32 cellHeight, const char* regionName )
 {
 {
     // Are we in explicit mode?
     // Are we in explicit mode?
     if ( !getExplicitMode() )
     if ( !getExplicitMode() )
     {
     {
         // No, so warn.
         // No, so warn.
-        Con::warnf( "ImageAsset::setCell() - Cannot perform explicit cell operation when not in explicit mode." );
+        Con::warnf( "ImageAsset::setExplicitCell() - Cannot perform explicit cell operation when not in explicit mode." );
         return false;
         return false;
     }
     }
 
 
@@ -753,12 +798,20 @@ bool ImageAsset::setExplicitCell( const S32 cellIndex, const S32 cellOffsetX, co
 
 
     // Fetch the explicit frame count.
     // Fetch the explicit frame count.
     const S32 explicitFrameCount = mExplicitFrames.size();
     const S32 explicitFrameCount = mExplicitFrames.size();
+    
+    // Region cannot be empty
+    if ( regionName == StringTable->EmptyString )
+    {
+        Con::warnf( "ImageAsset::setExplicitCell() - Cell name of '%s' is invalid or was not set.", regionName );
+        Con::warnf( "- Setting to the next index in the frame list: '%i'", explicitFrameCount );
+        dSscanf(regionName, "%i", explicitFrameCount);
+    }
 
 
     // The cell index needs to be in range.
     // The cell index needs to be in range.
     if ( cellIndex < 0 || cellIndex >= explicitFrameCount )
     if ( cellIndex < 0 || cellIndex >= explicitFrameCount )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::setCell() - Invalid Cell Index of %d.", cellIndex );
+        Con::warnf( "ImageAsset::setExplicitCell() - Invalid Cell Index of %d.", cellIndex );
         return false;
         return false;
     }
     }
 
 
@@ -766,15 +819,15 @@ bool ImageAsset::setExplicitCell( const S32 cellIndex, const S32 cellOffsetX, co
     if ( cellOffsetX < 0 || cellOffsetX >= imageWidth )
     if ( cellOffsetX < 0 || cellOffsetX >= imageWidth )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::setCell() - Invalid Cell OffsetX of %d.", cellOffsetX );
+        Con::warnf( "ImageAsset::setExplicitCell() - Invalid Cell OffsetX of %d.", cellOffsetX );
         return false;
         return false;
     }
     }
 
 
     // The Cell Offset Y needs to be within the image.
     // The Cell Offset Y needs to be within the image.
-    if ( cellOffsetY < 0 || cellOffsetY >= imageWidth )
+    if ( cellOffsetY < 0 || cellOffsetY >= imageHeight )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::setCell() - Invalid Cell OffsetY of %d.", cellOffsetY );
+        Con::warnf( "ImageAsset::setExplicitCell() - Invalid Cell OffsetY of %d.", cellOffsetY );
         return false;
         return false;
     }
     }
 
 
@@ -782,7 +835,7 @@ bool ImageAsset::setExplicitCell( const S32 cellIndex, const S32 cellOffsetX, co
     if ( cellWidth <= 0 || (cellOffsetX+cellWidth) > imageWidth )
     if ( cellWidth <= 0 || (cellOffsetX+cellWidth) > imageWidth )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::setCell() - Invalid Cell Width of %d.", cellWidth );
+        Con::warnf( "ImageAsset::setExplicitCell() - Invalid Cell Width of %d.", cellWidth );
         return false;
         return false;
     }
     }
 
 
@@ -790,12 +843,12 @@ bool ImageAsset::setExplicitCell( const S32 cellIndex, const S32 cellOffsetX, co
     if ( cellHeight <= 0 || (cellOffsetY+cellHeight) > imageHeight )
     if ( cellHeight <= 0 || (cellOffsetY+cellHeight) > imageHeight )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::setCell() - Invalid Cell Width of %d.", cellHeight );
+        Con::warnf( "ImageAsset::setExplicitCell() - Invalid Cell Width of %d.", cellHeight );
         return false;
         return false;
     }
     }
 
 
     // Configure frame.
     // Configure frame.
-    FrameArea::PixelArea pixelArea( cellOffsetX, cellOffsetY, cellWidth, cellHeight );
+    FrameArea::PixelArea pixelArea( cellOffsetX, cellOffsetY, cellWidth, cellHeight, regionName );
 
 
     // Set cell.
     // Set cell.
     mExplicitFrames[cellIndex] = pixelArea;
     mExplicitFrames[cellIndex] = pixelArea;
@@ -814,7 +867,7 @@ bool ImageAsset::removeExplicitCell( const S32 cellIndex )
     if ( !getExplicitMode() )
     if ( !getExplicitMode() )
     {
     {
         // No, so warn.
         // No, so warn.
-        Con::warnf( "ImageAsset::removeCell() - Cannot perform explicit cell operation when not in explicit mode." );
+        Con::warnf( "ImageAsset::removeExplicitCell() - Cannot perform explicit cell operation when not in explicit mode." );
         return false;
         return false;
     }
     }
 
 
@@ -825,7 +878,7 @@ bool ImageAsset::removeExplicitCell( const S32 cellIndex )
     if ( cellIndex < 0 || cellIndex >= explicitFrameCount )
     if ( cellIndex < 0 || cellIndex >= explicitFrameCount )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::removeCell() - Invalid Cell Index of %d.", cellIndex );
+        Con::warnf( "ImageAsset::removeExplicitCell() - Invalid Cell Index of %d.", cellIndex );
         return false;
         return false;
     }
     }
 
 
@@ -840,6 +893,69 @@ bool ImageAsset::removeExplicitCell( const S32 cellIndex )
 
 
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 
 
+bool ImageAsset::removeExplicitCell( const char* regionName )
+{
+    // Are we in explicit mode?
+    if ( !getExplicitMode() )
+    {
+        // No, so warn.
+        Con::warnf( "ImageAsset::removeExplicitCell() - Cannot perform explicit cell operation when not in explicit mode." );
+        return false;
+    }
+
+    // Interate through the vector
+    for( typeExplicitFrameAreaVector::iterator frameItr = mExplicitFrames.begin(); frameItr != mExplicitFrames.end(); ++frameItr )
+    {
+        // Grab the current pixelArea
+        const FrameArea::PixelArea& pixelArea = *frameItr;
+        
+        // Check to see if the name matches the argument
+        if (pixelArea.mRegionName == regionName)
+        {
+            // Found it, so erase it and return success
+            mExplicitFrames.erase(frameItr);
+            return true;
+        }
+    }
+
+    // Didn't find it, so warn
+    Con::warnf( "ImageAsset::removeExplicitCell() - Cannot find %s cell to remove.", regionName );
+    return false;
+}
+
+//------------------------------------------------------------------------------
+
+ImageAsset::FrameArea& ImageAsset::getCellByName( const char* cellName)
+{
+    // If the cellName was empty
+    if (cellName == StringTable->EmptyString)
+    {
+        // Warn and return a bad frame
+        Con::warnf( "ImageAsset::getCellByName() - Empty cell name was passed." );
+        return BadFrameArea;
+    }
+    
+    // Interate through the vector
+    for( typeFrameAreaVector::iterator frameItr = mFrames.begin(); frameItr != mFrames.end(); ++frameItr )
+    {
+        // Grab the current pixelArea
+        const FrameArea::PixelArea& pixelArea = frameItr->mPixelArea;
+        
+        // Check to see if the name matches the argument
+        if (pixelArea.mRegionName == cellName)
+        {
+            // Found it, so erase it and return success
+            return *frameItr;
+        }
+    }
+
+    // Didn't find it, so warn and return a bad frame
+    Con::warnf( "ImageAsset::getCellByName() - Cannot find %s cell.", cellName );
+    return BadFrameArea;
+}
+
+//------------------------------------------------------------------------------
+
 void ImageAsset::setTextureFilter( const TextureFilterMode filterMode )
 void ImageAsset::setTextureFilter( const TextureFilterMode filterMode )
 {
 {
     // Finish if no texture.
     // Finish if no texture.
@@ -1021,7 +1137,7 @@ void ImageAsset::calculateImplicitMode( void )
     if ( mCellWidth < 1 || mCellWidth > imageWidth )
     if ( mCellWidth < 1 || mCellWidth > imageWidth )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::calculateImage() - Invalid Cell Width of %d.", mCellWidth );
+        Con::warnf( "ImageAsset::calculateImage() - Invalid Cell Width of %d.", mCellWidth );
         return;
         return;
     }
     }
 
 
@@ -1029,7 +1145,7 @@ void ImageAsset::calculateImplicitMode( void )
     if ( mCellHeight < 1 || mCellHeight > imageHeight )
     if ( mCellHeight < 1 || mCellHeight > imageHeight )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::calculateImage() - Invalid Cell Height of %d.", mCellHeight );
+        Con::warnf( "ImageAsset::calculateImage() - Invalid Cell Height of %d.", mCellHeight );
         return;
         return;
     }
     }
 
 
@@ -1037,7 +1153,7 @@ void ImageAsset::calculateImplicitMode( void )
     if ( mCellOffsetX < 0 || mCellOffsetX >= imageWidth )
     if ( mCellOffsetX < 0 || mCellOffsetX >= imageWidth )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::calculateImage() - Invalid Cell OffsetX of %d.", mCellOffsetX );
+        Con::warnf( "ImageAsset::calculateImage() - Invalid Cell OffsetX of %d.", mCellOffsetX );
         return;
         return;
     }
     }
 
 
@@ -1045,11 +1161,10 @@ void ImageAsset::calculateImplicitMode( void )
     if ( mCellOffsetY < 0 || mCellOffsetY >= imageHeight )
     if ( mCellOffsetY < 0 || mCellOffsetY >= imageHeight )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::calculateImage() - Invalid Cell OffsetY of %d.", mCellOffsetY );
+        Con::warnf( "ImageAsset::calculateImage() - Invalid Cell OffsetY of %d.", mCellOffsetY );
         return;
         return;
     }
     }
 
 
-
     // Are we using Cell-StrideX?
     // Are we using Cell-StrideX?
     S32 cellStepX;
     S32 cellStepX;
     if ( mCellStrideX != 0 )
     if ( mCellStrideX != 0 )
@@ -1082,14 +1197,14 @@ void ImageAsset::calculateImplicitMode( void )
     if ( cellFinalPositionX < 0 )
     if ( cellFinalPositionX < 0 )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::calculateImage() - Invalid Cell OffsetX(%d)/Width(%d)/CountX(%d); off image left-hand-side.", mCellOffsetX, mCellWidth, mCellCountX );
+        Con::warnf( "ImageAsset::calculateImage() - Invalid Cell OffsetX(%d)/Width(%d)/CountX(%d); off image left-hand-side.", mCellOffsetX, mCellWidth, mCellCountX );
         return;
         return;
     }
     }
     // Off Right?
     // Off Right?
     else if ( cellFinalPositionX > imageWidth )
     else if ( cellFinalPositionX > imageWidth )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::calculateImage() - Invalid Cell OffsetX(%d)/Width(%d)/CountX(%d); off image right-hand-side.", mCellOffsetX, mCellWidth, mCellCountX );
+        Con::warnf( "ImageAsset::calculateImage() - Invalid Cell OffsetX(%d)/Width(%d)/CountX(%d); off image right-hand-side.", mCellOffsetX, mCellWidth, mCellCountX );
         return;
         return;
     }
     }
 
 
@@ -1099,14 +1214,14 @@ void ImageAsset::calculateImplicitMode( void )
     if ( cellFinalPositionY < 0 )
     if ( cellFinalPositionY < 0 )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::calculateImage() - Invalid Cell OffsetY(%d)/Height(%d)/CountY(%d); off image top-side.", mCellOffsetY, mCellHeight, mCellCountY );
+        Con::warnf( "ImageAsset::calculateImage() - Invalid Cell OffsetY(%d)/Height(%d)/CountY(%d); off image top-side.", mCellOffsetY, mCellHeight, mCellCountY );
         return;
         return;
     }
     }
     // Off Bottom?
     // Off Bottom?
     else if ( cellFinalPositionY > imageHeight )
     else if ( cellFinalPositionY > imageHeight )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::calculateImage() - Invalid Cell OffsetY(%d)/Height(%d)/CountY(%d); off image bottom-side.", mCellOffsetY, mCellHeight, mCellCountY );
+        Con::warnf( "ImageAsset::calculateImage() - Invalid Cell OffsetY(%d)/Height(%d)/CountY(%d); off image bottom-side.", mCellOffsetY, mCellHeight, mCellCountY );
         return;
         return;
     }
     }
 
 
@@ -1187,7 +1302,7 @@ void ImageAsset::calculateExplicitMode( void )
         const FrameArea::PixelArea& pixelArea = *frameItr;
         const FrameArea::PixelArea& pixelArea = *frameItr;
 
 
         // Set frame area.
         // Set frame area.
-        FrameArea frameArea( pixelArea.mPixelOffset.x, pixelArea.mPixelOffset.y, pixelArea.mPixelWidth, pixelArea.mPixelHeight, texelWidthScale, texelHeightScale );
+        FrameArea frameArea( pixelArea.mPixelOffset.x, pixelArea.mPixelOffset.y, pixelArea.mPixelWidth, pixelArea.mPixelHeight, texelWidthScale, texelHeightScale, pixelArea.mRegionName );
 
 
         // Store frame.
         // Store frame.
         mFrames.push_back( frameArea );
         mFrames.push_back( frameArea );
@@ -1216,22 +1331,26 @@ void ImageAsset::onTamlCustomWrite( TamlCustomNodes& customNodes )
     if ( !mExplicitMode )
     if ( !mExplicitMode )
         return;
         return;
 
 
-    // Add cell custom node.
-    TamlCustomNode* pCustomCellNodes = customNodes.addNode( cellCustomNodeName );
-
-    // Iterate explicit frames.
-    for( typeExplicitFrameAreaVector::iterator frameItr = mExplicitFrames.begin(); frameItr != mExplicitFrames.end(); ++frameItr )
+    if (mExplicitFrames.size() > 0)
     {
     {
-        // Fetch pixel area.
-        const FrameArea::PixelArea& pixelArea = *frameItr;
+        // Add cell custom node.
+        TamlCustomNode* pCustomCellNodes = customNodes.addNode( cellCustomNodeCellsName );
+
+        // Iterate explicit frames.
+        for( typeExplicitFrameAreaVector::iterator frameItr = mExplicitFrames.begin(); frameItr != mExplicitFrames.end(); ++frameItr )
+        {
+            // Fetch pixel area.
+            const FrameArea::PixelArea& pixelArea = *frameItr;
 
 
-        // Add cell alias.
-        TamlCustomNode* pCellNode = pCustomCellNodes->addNode( cellNodeName );
+            // Add cell alias.
+            TamlCustomNode* pCellNode = pCustomCellNodes->addNode( cellNodeName );
 
 
-        // Add cell properties.
-        pCellNode->addField( cellOffsetName, pixelArea.mPixelOffset );
-        pCellNode->addField( cellWidthName, pixelArea.mPixelWidth );
-        pCellNode->addField( cellHeightName, pixelArea.mPixelHeight );
+            // Add cell properties.
+            pCellNode->addField( cellRegionName, pixelArea.mRegionName );
+            pCellNode->addField( cellOffsetName, pixelArea.mPixelOffset );
+            pCellNode->addField( cellWidthName, pixelArea.mPixelWidth );
+            pCellNode->addField( cellHeightName, pixelArea.mPixelHeight );
+        }
     }
     }
 }
 }
 
 
@@ -1241,104 +1360,129 @@ void ImageAsset::onTamlCustomRead( const TamlCustomNodes& customNodes )
 {
 {
     // Debug Profiling.
     // Debug Profiling.
     PROFILE_SCOPE(ImageAsset_OnTamlCustomRead);
     PROFILE_SCOPE(ImageAsset_OnTamlCustomRead);
-
+    
     // Call parent.
     // Call parent.
     Parent::onTamlCustomRead( customNodes );
     Parent::onTamlCustomRead( customNodes );
-
+    
     // Find cell custom node.
     // Find cell custom node.
-    const TamlCustomNode* pCustomCellNodes = customNodes.findNode( cellCustomNodeName );
-
-    // Finish if we don't have explicit cells.
-    if ( pCustomCellNodes == NULL )
-        return;
-
-    // Set explicit mode.
-    mExplicitMode = true;
-
-    // Fetch children cell nodes.
-    const TamlCustomNodeVector& cellNodes = pCustomCellNodes->getChildren();
-
-    // Iterate cells.
-    for( TamlCustomNodeVector::const_iterator cellNodeItr = cellNodes.begin(); cellNodeItr != cellNodes.end(); ++cellNodeItr )
+    const TamlCustomNode* pCustomCellNodes = customNodes.findNode( cellCustomNodeCellsName );
+    
+    // Continue if we have explicit cells.
+    if ( pCustomCellNodes != NULL )
     {
     {
-        // Fetch cell node.
-        TamlCustomNode* pCellNode = *cellNodeItr;
-
-        // Fetch node name.
-        StringTableEntry nodeName = pCellNode->getNodeName();
-
-        // Is this a valid alias?
-        if ( nodeName != cellNodeName )
-        {
-            // No, so warn.
-            Con::warnf( "ImageAsset::onTamlCustomRead() - Encountered an unknown custom name of '%s'.  Only '%s' is valid.", nodeName, cellNodeName );
-            continue;
-        }
-
-        Point2I cellOffset(-1, -1);
-        S32 cellWidth = 0;
-        S32 cellHeight = 0;
-
-        // Fetch fields.
-        const TamlCustomFieldVector& fields = pCellNode->getFields();
-
-        // Iterate property fields.
-        for ( TamlCustomFieldVector::const_iterator fieldItr = fields.begin(); fieldItr != fields.end(); ++fieldItr )
+        // Set explicit mode.
+        mExplicitMode = true;
+        
+        // Fetch children cell nodes.
+        const TamlCustomNodeVector& cellNodes = pCustomCellNodes->getChildren();
+        
+        // Iterate cells.
+        for( TamlCustomNodeVector::const_iterator cellNodeItr = cellNodes.begin(); cellNodeItr != cellNodes.end(); ++cellNodeItr )
         {
         {
-            // Fetch field.
-            const TamlCustomField* pField = *fieldItr;
-
-            // Fetch field name.
-            StringTableEntry fieldName = pField->getFieldName();
-
-            // Check common fields.
-            if ( fieldName == cellOffsetName )
+            // Fetch cell node.
+            TamlCustomNode* pCellNode = *cellNodeItr;
+            
+            // Fetch node name.
+            StringTableEntry nodeName = pCellNode->getNodeName();
+            
+            // Is this a valid alias?
+            if ( nodeName != cellNodeName )
             {
             {
-                pField->getFieldValue( cellOffset );
+                // No, so warn.
+                Con::warnf( "ImageAsset::onTamlCustomRead() - Encountered an unknown custom name of '%s'.  Only '%s' is valid.", nodeName, cellNodeName );
+                continue;
             }
             }
-            else if ( fieldName == cellWidthName )
+            
+            Point2I cellOffset(-1, -1);
+            S32 cellWidth = 0;
+            S32 cellHeight = 0;
+            const char* regionName = StringTable->EmptyString;
+            
+            // Fetch fields.
+            const TamlCustomFieldVector& fields = pCellNode->getFields();
+            
+            // Iterate property fields.
+            for ( TamlCustomFieldVector::const_iterator fieldItr = fields.begin(); fieldItr != fields.end(); ++fieldItr )
             {
             {
-                pField->getFieldValue( cellWidth );
+                // Fetch field.
+                const TamlCustomField* pField = *fieldItr;
+                
+                // Fetch field name.
+                StringTableEntry fieldName = pField->getFieldName();
+                
+                // Check common fields.
+                if ( fieldName == cellRegionName )
+                {
+                    regionName = pField->getFieldValue();
+                }
+                else if ( fieldName == cellOffsetName )
+                {
+                    pField->getFieldValue( cellOffset );
+                }
+                else if ( fieldName == cellOffsetXName )
+                {
+                    pField->getFieldValue( cellOffset.x );
+                }
+                else if ( fieldName == cellOffsetYName )
+                {
+                    pField->getFieldValue( cellOffset.y );
+                }
+                else if ( fieldName == cellWidthName )
+                {
+                    pField->getFieldValue( cellWidth );
+                }
+                else if ( fieldName == cellHeightName )
+                {
+                    pField->getFieldValue( cellHeight );
+                }
+                else
+                {
+                    // Unknown name so warn.
+                    Con::warnf( "ImageAsset::onTamlCustomRead() - Encountered an unknown custom field name of '%s'.", fieldName );
+                    continue;
+                }
             }
             }
-            else if ( fieldName == cellHeightName )
+            
+            // Does the region have a name
+            if ( regionName == StringTable->EmptyString )
             {
             {
-                pField->getFieldValue( cellHeight );
+                // No, so warn and set it to the next index
+                Con::warnf( "ImageAsset::onTamlCustomRead() - Cell name of '%s' is invalid or was not set.", regionName );
+                
+                U32 currentIndex = mExplicitFrames.size();
+                Con::warnf( "- Setting to the next index in the frame list: '%i'", currentIndex );
+                
+                dSscanf(regionName, "%i", currentIndex);
             }
             }
-            else
+            
+            // Is cell offset valid?
+            if ( cellOffset.x < 0 || cellOffset.y < 0 )
             {
             {
-                // Unknown name so warn.
-                Con::warnf( "ImageAsset::onTamlCustomRead() - Encountered an unknown custom field name of '%s'.", fieldName );
+                // No, so warn.
+                Con::warnf( "ImageAsset::onTamlCustomRead() - Cell offset of '(%d,%d)' is invalid or was not set.", cellOffset.x, cellOffset.y );
                 continue;
                 continue;
             }
             }
+            
+            // Is cell width valid?
+            if ( cellWidth <= 0 )
+            {
+                // No, so warn.
+                Con::warnf( "ImageAsset::onTamlCustomRead() - Cell width of '%d' is invalid or was not set.", cellWidth );
+                continue;
+            }
+            
+            // Is cell height valid?
+            if ( cellHeight <= 0 )
+            {
+                // No, so warn.
+                Con::warnf( "ImageAsset::onTamlCustomRead() - Cell height of '%d' is invalid or was not set.", cellHeight );
+                continue;
+            }
+            
+            // Add explicit frame.
+            FrameArea::PixelArea pixelArea( cellOffset.x, cellOffset.y, cellWidth, cellHeight, regionName );
+            mExplicitFrames.push_back( pixelArea );
         }
         }
-
-        // Is cell offset valid?
-        if ( cellOffset.x < 0 || cellOffset.y < 0 )
-        {
-            // No, so warn.
-            Con::warnf( "ImageAsset::onTamlCustomRead() - Cell offset of '(%d,%d)' is invalid or was not set.", cellOffset.x, cellOffset.y );
-            continue;
-        }
-
-        // Is cell width valid?
-        if ( cellWidth <= 0 )
-        {
-            // No, so warn.
-            Con::warnf( "ImageAsset::onTamlCustomRead() - Cell width of '%d' is invalid or was not set.", cellWidth );
-            continue;
-        }
-
-        // Is cell height valid?
-        if ( cellHeight <= 0 )
-        {
-            // No, so warn.
-            Con::warnf( "ImageAsset::onTamlCustomRead() - Cell height of '%d' is invalid or was not set.", cellHeight );
-            continue;
-        }
-
-        // Add explicit frame.
-        FrameArea::PixelArea pixelArea( cellOffset.x, cellOffset.y, cellWidth, cellHeight );
-        mExplicitFrames.push_back( pixelArea );
     }
     }
 }
 }
 
 
@@ -1354,7 +1498,7 @@ static void WriteCustomTamlSchema( const AbstractClassRep* pClassRep, TiXmlEleme
 
 
     // Create ImageAsset node element.
     // Create ImageAsset node element.
     TiXmlElement* pImageAssetNodeElement = new TiXmlElement( "xs:element" );
     TiXmlElement* pImageAssetNodeElement = new TiXmlElement( "xs:element" );
-    dSprintf( buffer, sizeof(buffer), "%s.%s", pClassRep->getClassName(), cellCustomNodeName );
+    dSprintf( buffer, sizeof(buffer), "%s.%s", pClassRep->getClassName(), cellCustomNodeCellsName );
     pImageAssetNodeElement->SetAttribute( "name", buffer );
     pImageAssetNodeElement->SetAttribute( "name", buffer );
     pImageAssetNodeElement->SetAttribute( "minOccurs", 0 );
     pImageAssetNodeElement->SetAttribute( "minOccurs", 0 );
     pImageAssetNodeElement->SetAttribute( "maxOccurs", 1 );
     pImageAssetNodeElement->SetAttribute( "maxOccurs", 1 );
@@ -1381,6 +1525,12 @@ static void WriteCustomTamlSchema( const AbstractClassRep* pClassRep, TiXmlEleme
     TiXmlElement* pImageAssetComplexTypeElement = new TiXmlElement( "xs:complexType" );
     TiXmlElement* pImageAssetComplexTypeElement = new TiXmlElement( "xs:complexType" );
     pImageAssetElement->LinkEndChild( pImageAssetComplexTypeElement );
     pImageAssetElement->LinkEndChild( pImageAssetComplexTypeElement );
 
 
+    // Create "RegionName" attribute.
+    TiXmlElement* pImageRegionName = new TiXmlElement( "xs:attribute" );
+    pImageRegionName->SetAttribute( "name", cellRegionName );
+    pImageRegionName->SetAttribute( "type", "xs:string" );
+    pImageAssetComplexTypeElement->LinkEndChild( pImageRegionName );
+    
     // Create "Offset" attribute.
     // Create "Offset" attribute.
     TiXmlElement* pImageAssetOffset = new TiXmlElement( "xs:attribute" );
     TiXmlElement* pImageAssetOffset = new TiXmlElement( "xs:attribute" );
     pImageAssetOffset->SetAttribute( "name", cellOffsetName );
     pImageAssetOffset->SetAttribute( "name", cellOffsetName );

+ 38 - 5
engine/source/2d/assets/ImageAsset.h

@@ -73,17 +73,28 @@ public:
             {
             {
                 setArea( pixelFrameOffsetX, pixelFrameOffsetY, pixelFrameWidth, pixelFrameHeight );
                 setArea( pixelFrameOffsetX, pixelFrameOffsetY, pixelFrameWidth, pixelFrameHeight );
             }
             }
-
+            PixelArea( const S32 pixelFrameOffsetX, const S32 pixelFrameOffsetY, const U32 pixelFrameWidth, const U32 pixelFrameHeight, const char* regionName )
+            {
+                setArea( pixelFrameOffsetX, pixelFrameOffsetY, pixelFrameWidth, pixelFrameHeight, regionName );
+            }
             inline void setArea( const S32 pixelFrameOffsetX, const S32 pixelFrameOffsetY, const U32 pixelFrameWidth, const U32 pixelFrameHeight )
             inline void setArea( const S32 pixelFrameOffsetX, const S32 pixelFrameOffsetY, const U32 pixelFrameWidth, const U32 pixelFrameHeight )
             {
             {
                 mPixelOffset.set( pixelFrameOffsetX, pixelFrameOffsetY );
                 mPixelOffset.set( pixelFrameOffsetX, pixelFrameOffsetY );
                 mPixelWidth = pixelFrameWidth;
                 mPixelWidth = pixelFrameWidth;
                 mPixelHeight = pixelFrameHeight;
                 mPixelHeight = pixelFrameHeight;
             };
             };
+            inline void setArea( const S32 pixelFrameOffsetX, const S32 pixelFrameOffsetY, const U32 pixelFrameWidth, const U32 pixelFrameHeight, const char* regionName )
+            {
+                mPixelOffset.set( pixelFrameOffsetX, pixelFrameOffsetY );
+                mPixelWidth = pixelFrameWidth;
+                mPixelHeight = pixelFrameHeight;
+                mRegionName = StringTable->insert(regionName);
+            };
 
 
             Point2I mPixelOffset;
             Point2I mPixelOffset;
             U32 mPixelWidth;
             U32 mPixelWidth;
             U32 mPixelHeight;
             U32 mPixelHeight;
+            StringTableEntry mRegionName;
         };
         };
 
 
 
 
@@ -122,12 +133,25 @@ public:
         {
         {
             setArea( pixelFrameOffsetX, pixelFrameOffsetY, pixelFrameWidth, pixelFrameHeight, texelWidthScale, texelHeightScale );
             setArea( pixelFrameOffsetX, pixelFrameOffsetY, pixelFrameWidth, pixelFrameHeight, texelWidthScale, texelHeightScale );
         }
         }
+        FrameArea( const S32 pixelFrameOffsetX, const S32 pixelFrameOffsetY, const U32 pixelFrameWidth, const U32 pixelFrameHeight, const F32 texelWidthScale, const F32 texelHeightScale, const char* regionName )
+        {
+            setArea( pixelFrameOffsetX, pixelFrameOffsetY, pixelFrameWidth, pixelFrameHeight, texelWidthScale, texelHeightScale, regionName);
+        }
+        FrameArea()
+        {
+            setArea(0, 0, 0, 0, 0.0f, 0.0f);
+        }
 
 
         void setArea( const S32 pixelFrameOffsetX, const S32 pixelFrameOffsetY, const U32 pixelFrameWidth, const U32 pixelFrameHeight, const F32 texelWidthScale, const F32 texelHeightScale )
         void setArea( const S32 pixelFrameOffsetX, const S32 pixelFrameOffsetY, const U32 pixelFrameWidth, const U32 pixelFrameHeight, const F32 texelWidthScale, const F32 texelHeightScale )
         {
         {
             mPixelArea.setArea( pixelFrameOffsetX, pixelFrameOffsetY, pixelFrameWidth, pixelFrameHeight );
             mPixelArea.setArea( pixelFrameOffsetX, pixelFrameOffsetY, pixelFrameWidth, pixelFrameHeight );
             mTexelArea.setArea( mPixelArea, texelWidthScale, texelHeightScale );
             mTexelArea.setArea( mPixelArea, texelWidthScale, texelHeightScale );
         }
         }
+        void setArea( const S32 pixelFrameOffsetX, const S32 pixelFrameOffsetY, const U32 pixelFrameWidth, const U32 pixelFrameHeight, const F32 texelWidthScale, const F32 texelHeightScale, const char* regionName )
+        {
+            mPixelArea.setArea( pixelFrameOffsetX, pixelFrameOffsetY, pixelFrameWidth, pixelFrameHeight, regionName );
+            mTexelArea.setArea( mPixelArea, texelWidthScale, texelHeightScale );
+        }
 
 
         PixelArea mPixelArea;
         PixelArea mPixelArea;
         TexelArea mTexelArea;
         TexelArea mTexelArea;
@@ -207,27 +231,36 @@ public:
     void                    setCellHeight( const S32 cellheight );
     void                    setCellHeight( const S32 cellheight );
     S32                     getCellHeight( void) const						{ return mCellHeight; }
     S32                     getCellHeight( void) const						{ return mCellHeight; }
     S32                     getExplicitCellHeight(const S32 cellIndex);
     S32                     getExplicitCellHeight(const S32 cellIndex);
+    
+    bool                    containsNamedRegion(const char* regionName);
 
 
     inline TextureHandle&   getImageTexture( void )                         { return mImageTextureHandle; }
     inline TextureHandle&   getImageTexture( void )                         { return mImageTextureHandle; }
     inline S32              getImageWidth( void ) const                     { return mImageTextureHandle.getWidth(); }
     inline S32              getImageWidth( void ) const                     { return mImageTextureHandle.getWidth(); }
     inline S32              getImageHeight( void ) const                    { return mImageTextureHandle.getHeight(); }
     inline S32              getImageHeight( void ) const                    { return mImageTextureHandle.getHeight(); }
     inline U32              getFrameCount( void ) const                     { return (U32)mFrames.size(); };
     inline U32              getFrameCount( void ) const                     { return (U32)mFrames.size(); };
-
+    inline bool             containsFrame( const char* namedFrame )         { return containsNamedRegion(namedFrame); };
+    
+    FrameArea&              getCellByName(const char* cellName);
+    
     inline const FrameArea& getImageFrameArea( U32 frame ) const            { clampFrame(frame); return mFrames[frame]; };
     inline const FrameArea& getImageFrameArea( U32 frame ) const            { clampFrame(frame); return mFrames[frame]; };
+    inline const FrameArea& getImageFrameArea( const char* namedFrame)      { return getCellByName(namedFrame); };
     inline const void       bindImageTexture( void)                         { glBindTexture( GL_TEXTURE_2D, getImageTexture().getGLName() ); };
     inline const void       bindImageTexture( void)                         { glBindTexture( GL_TEXTURE_2D, getImageTexture().getGLName() ); };
     
     
     virtual bool            isAssetValid( void ) const                      { return !mImageTextureHandle.IsNull(); }
     virtual bool            isAssetValid( void ) const                      { return !mImageTextureHandle.IsNull(); }
 
 
     /// Explicit cell control.
     /// Explicit cell control.
     bool                    clearExplicitCells( void );
     bool                    clearExplicitCells( void );
-    bool                    addExplicitCell( const S32 cellOffsetX, const S32 cellOffsetY, const S32 cellWidth, const S32 cellHeight );
-    bool                    insertExplicitCell( const S32 cellIndex, const S32 cellOffsetX, const S32 cellOffsetY, const S32 cellWidth, const S32 cellHeight );
+    bool                    addExplicitCell( const S32 cellOffsetX, const S32 cellOffsetY, const S32 cellWidth, const S32 cellHeight, const char* regionName );
+    bool                    insertExplicitCell( const S32 cellIndex, const S32 cellOffsetX, const S32 cellOffsetY, const S32 cellWidth, const S32 cellHeight, const char* regionName );
     bool                    removeExplicitCell( const S32 cellIndex );
     bool                    removeExplicitCell( const S32 cellIndex );
-    bool                    setExplicitCell( const S32 cellIndex, const S32 cellOffsetX, const S32 cellOffsetY, const S32 cellWidth, const S32 cellHeight );
+    bool                    removeExplicitCell( const char* regionName );
+    bool                    setExplicitCell( const S32 cellIndex, const S32 cellOffsetX, const S32 cellOffsetY, const S32 cellWidth, const S32 cellHeight, const char* regionName );
     inline S32              getExplicitCellCount( void ) const              { return mExplicitFrames.size(); }
     inline S32              getExplicitCellCount( void ) const              { return mExplicitFrames.size(); }
     
     
     static TextureFilterMode getFilterModeEnum(const char* label);
     static TextureFilterMode getFilterModeEnum(const char* label);
     static const char* getFilterModeDescription( TextureFilterMode filterMode );
     static const char* getFilterModeDescription( TextureFilterMode filterMode );
+    
+    inline void forceCalculation( void ) { calculateImage(); }
 
 
     /// Declare Console Object.
     /// Declare Console Object.
     DECLARE_CONOBJECT(ImageAsset);
     DECLARE_CONOBJECT(ImageAsset);

+ 24 - 13
engine/source/2d/assets/ImageAsset_ScriptBinding.h

@@ -253,24 +253,32 @@ ConsoleMethodWithDocs(ImageAsset, getCellWidth, ConsoleInt, 2, 2, ())
     return object->getCellWidth();
     return object->getCellWidth();
 }
 }
 
 
-ConsoleMethod(ImageAsset, getExplicitCellWidth, S32, 3,3, "(CellIndex) Gets the CELL width in Explicit Mode.\n"
-                                                                        "@return the specified CELL width.")
+//-----------------------------------------------------------------------------
+
+/*! Gets the CELL width in Explicit Mode.
+    @return the specified CELL width.
+*/
+ConsoleMethodWithDocs(ImageAsset, getExplicitCellWidth, ConsoleInt, 3,3, (CellIndex))
 {
 {
-	    // Fetch cell index.
+    // Fetch cell index.
     const S32 cellIndex = dAtoi( argv[2] );
     const S32 cellIndex = dAtoi( argv[2] );
 
 
     return(object->getExplicitCellWidth(cellIndex));
     return(object->getExplicitCellWidth(cellIndex));
 }
 }
 
 
-ConsoleMethod(ImageAsset, getExplicitCellHeight, S32, 3,3, "(CellIndex) Gets the CELL height in Explicit Mode.\n"
-                                                                        "@return the specified CELL height.")
+//-----------------------------------------------------------------------------
+
+/*! Gets the CELL height in Explicit Mode.
+    @return the specified CELL height.
+*/
+ConsoleMethodWithDocs(ImageAsset, getExplicitCellHeight, ConsoleInt, 3,3, (CellIndex))
 {
 {
-	    // Fetch cell index.
+    // Fetch cell index.
     const S32 cellIndex = dAtoi( argv[2] );
     const S32 cellIndex = dAtoi( argv[2] );
 
 
     return(object->getExplicitCellHeight(cellIndex));
     return(object->getExplicitCellHeight(cellIndex));
-
 }
 }
+
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
 
 
 /*! Sets the CELL height.
 /*! Sets the CELL height.
@@ -399,10 +407,11 @@ ConsoleMethodWithDocs(ImageAsset, clearExplicitCells, ConsoleBool, 2, 2, ())
     @param cellOffsetY The offset in the Y axis to the top-left of the cell.
     @param cellOffsetY The offset in the Y axis to the top-left of the cell.
     @param cellWidth The width of the cell.
     @param cellWidth The width of the cell.
     @param cellHeight The height of the cell.
     @param cellHeight The height of the cell.
+    @param cellName The name of the cell's region.
     The image asset must be in explicit mode for this operation to succeed.
     The image asset must be in explicit mode for this operation to succeed.
     @return Whether the operation was successful or not.
     @return Whether the operation was successful or not.
 */
 */
-ConsoleMethodWithDocs(ImageAsset, addExplicitCell, ConsoleBool, 6, 6, (int cellOffsetX, int cellOffsetY, int cellWidth, int cellHeight))
+ConsoleMethodWithDocs(ImageAsset, addExplicitCell, ConsoleBool, 7, 7, (int cellOffsetX, int cellOffsetY, int cellWidth, int cellHeight, string cellName))
 {
 {
     // Fetch offsets.
     // Fetch offsets.
     const S32 cellOffsetX = dAtoi( argv[2] );
     const S32 cellOffsetX = dAtoi( argv[2] );
@@ -412,7 +421,7 @@ ConsoleMethodWithDocs(ImageAsset, addExplicitCell, ConsoleBool, 6, 6, (int cellO
     const S32 cellWidth = dAtoi( argv[4] );
     const S32 cellWidth = dAtoi( argv[4] );
     const S32 cellHeight = dAtoi (argv[5] );
     const S32 cellHeight = dAtoi (argv[5] );
 
 
-    return object->addExplicitCell( cellOffsetX, cellOffsetY, cellWidth, cellHeight );
+    return object->addExplicitCell( cellOffsetX, cellOffsetY, cellWidth, cellHeight, argv[6] );
 }
 }
 
 
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
@@ -423,10 +432,11 @@ ConsoleMethodWithDocs(ImageAsset, addExplicitCell, ConsoleBool, 6, 6, (int cellO
     @param cellOffsetY The offset in the Y axis to the top-left of the cell.
     @param cellOffsetY The offset in the Y axis to the top-left of the cell.
     @param cellWidth The width of the cell.
     @param cellWidth The width of the cell.
     @param cellHeight The height of the cell.
     @param cellHeight The height of the cell.
+    @param cellName The name of the cell's region.
     The image asset must be in explicit mode for this operation to succeed.
     The image asset must be in explicit mode for this operation to succeed.
     @return Whether the operation was successful or not.
     @return Whether the operation was successful or not.
 */
 */
-ConsoleMethodWithDocs(ImageAsset, insertExplicitCell, ConsoleBool, 7, 7, (int cellIndex, int cellOffsetX, int cellOffsetY, int cellWidth, int cellHeight))
+ConsoleMethodWithDocs(ImageAsset, insertExplicitCell, ConsoleBool, 8, 8, (int cellIndex, int cellOffsetX, int cellOffsetY, int cellWidth, int cellHeight, string cellName))
 {
 {
     // Fetch cell index.
     // Fetch cell index.
     const S32 cellIndex = dAtoi( argv[2] );
     const S32 cellIndex = dAtoi( argv[2] );
@@ -439,7 +449,7 @@ ConsoleMethodWithDocs(ImageAsset, insertExplicitCell, ConsoleBool, 7, 7, (int ce
     const S32 cellWidth = dAtoi( argv[5] );
     const S32 cellWidth = dAtoi( argv[5] );
     const S32 cellHeight = dAtoi (argv[6] );
     const S32 cellHeight = dAtoi (argv[6] );
 
 
-    return object->insertExplicitCell( cellIndex, cellOffsetX, cellOffsetY, cellWidth, cellHeight );
+    return object->insertExplicitCell( cellIndex, cellOffsetX, cellOffsetY, cellWidth, cellHeight, argv[7] );
 }
 }
 
 
 
 
@@ -465,10 +475,11 @@ ConsoleMethodWithDocs(ImageAsset, removeExplicitCell, ConsoleBool, 7, 7, (int ce
     @param cellOffsetY The offset in the Y axis to the top-left of the cell.
     @param cellOffsetY The offset in the Y axis to the top-left of the cell.
     @param cellWidth The width of the cell.
     @param cellWidth The width of the cell.
     @param cellHeight The height of the cell.
     @param cellHeight The height of the cell.
+    @param cellName The name of the cell's region.
     The image asset must be in explicit mode for this operation to succeed.
     The image asset must be in explicit mode for this operation to succeed.
     @return Whether the operation was successful or not.
     @return Whether the operation was successful or not.
 */
 */
-ConsoleMethodWithDocs(ImageAsset, setExplicitCell, ConsoleBool, 7, 7, (int cellIndex, int cellOffsetX, int cellOffsetY, int cellWidth, int cellHeight))
+ConsoleMethodWithDocs(ImageAsset, setExplicitCell, ConsoleBool, 8, 8, (int cellIndex, int cellOffsetX, int cellOffsetY, int cellWidth, int cellHeight, string cellName))
 {
 {
     // Fetch cell index.
     // Fetch cell index.
     const S32 cellIndex = dAtoi( argv[2] );
     const S32 cellIndex = dAtoi( argv[2] );
@@ -481,7 +492,7 @@ ConsoleMethodWithDocs(ImageAsset, setExplicitCell, ConsoleBool, 7, 7, (int cellI
     const S32 cellWidth = dAtoi( argv[5] );
     const S32 cellWidth = dAtoi( argv[5] );
     const S32 cellHeight = dAtoi (argv[6] );
     const S32 cellHeight = dAtoi (argv[6] );
 
 
-    return object->setExplicitCell( cellIndex, cellOffsetX, cellOffsetY, cellWidth, cellHeight );
+    return object->setExplicitCell( cellIndex, cellOffsetX, cellOffsetY, cellWidth, cellHeight, argv[7] );
 }
 }
 
 
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------

+ 100 - 2
engine/source/2d/assets/ParticleAssetEmitter.cc

@@ -203,6 +203,8 @@ ParticleAssetEmitter::ParticleAssetEmitter() :
     // Register for refresh notifications.
     // Register for refresh notifications.
     mImageAsset.registerRefreshNotify( this );
     mImageAsset.registerRefreshNotify( this );
     mAnimationAsset.registerRefreshNotify( this );
     mAnimationAsset.registerRefreshNotify( this );
+    
+    mImageFrameName = "";
 }
 }
 
 
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
@@ -246,6 +248,7 @@ void ParticleAssetEmitter::initPersistFields()
 
 
     addProtectedField("Image", TypeImageAssetPtr, Offset(mImageAsset, ParticleAssetEmitter), &setImage, &getImage, &writeImage, "");
     addProtectedField("Image", TypeImageAssetPtr, Offset(mImageAsset, ParticleAssetEmitter), &setImage, &getImage, &writeImage, "");
     addProtectedField("Frame", TypeS32, Offset(mImageFrame, ParticleAssetEmitter), &setImageFrame, &defaultProtectedGetFn, &writeImageFrame, "");
     addProtectedField("Frame", TypeS32, Offset(mImageFrame, ParticleAssetEmitter), &setImageFrame, &defaultProtectedGetFn, &writeImageFrame, "");
+    addProtectedField("FrameName", TypeString, Offset(mImageFrameName, ParticleAssetEmitter), &setImageFrameName, &defaultProtectedGetFn, &writeImageFrameName, "");
     addProtectedField("RandomImageFrame", TypeBool, Offset(mRandomImageFrame, ParticleAssetEmitter), &setRandomImageFrame, &defaultProtectedGetFn, &writeRandomImageFrame, "");
     addProtectedField("RandomImageFrame", TypeBool, Offset(mRandomImageFrame, ParticleAssetEmitter), &setRandomImageFrame, &defaultProtectedGetFn, &writeRandomImageFrame, "");
     addProtectedField("Animation", TypeAnimationAssetPtr, Offset(mAnimationAsset, ParticleAssetEmitter), &setAnimation, &getAnimation, &writeAnimation, "");
     addProtectedField("Animation", TypeAnimationAssetPtr, Offset(mAnimationAsset, ParticleAssetEmitter), &setAnimation, &getAnimation, &writeAnimation, "");
 }
 }
@@ -291,10 +294,15 @@ void ParticleAssetEmitter::copyTo(SimObject* object)
    pParticleAssetEmitter->setAlphaTest( getAlphaTest() );
    pParticleAssetEmitter->setAlphaTest( getAlphaTest() );
 
 
    pParticleAssetEmitter->setRandomImageFrame( getRandomImageFrame() );
    pParticleAssetEmitter->setRandomImageFrame( getRandomImageFrame() );
+
+   // Static provider?
    if ( pParticleAssetEmitter->isStaticFrameProvider() )
    if ( pParticleAssetEmitter->isStaticFrameProvider() )
    {
    {
-       pParticleAssetEmitter->setImage( getImage() );
-       pParticleAssetEmitter->setImageFrame( getImageFrame() );
+        // Named image frame?
+        if ( pParticleAssetEmitter->isUsingNamedImageFrame() )
+            pParticleAssetEmitter->setImage( getImage(), getImageFrameName() );
+        else
+            pParticleAssetEmitter->setImage( getImage(), getImageFrame() );
    }
    }
    else
    else
    {
    {
@@ -400,6 +408,9 @@ bool ParticleAssetEmitter::setImage( const char* pAssetId, U32 frame )
         mImageFrame = 0;
         mImageFrame = 0;
     }
     }
 
 
+    // Using a numerical frame index
+    mUsingFrameName = false;
+
     // Refresh the asset.
     // Refresh the asset.
     refreshAsset();
     refreshAsset();
 
 
@@ -409,6 +420,53 @@ bool ParticleAssetEmitter::setImage( const char* pAssetId, U32 frame )
 
 
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 
 
+bool ParticleAssetEmitter::setImage( const char* pAssetId, const char* frameName )
+{
+    // Sanity!
+    AssertFatal( pAssetId != NULL, "ParticleAssetEmitter::setImage() - Cannot use a NULL asset Id." );
+    
+    // Set static mode.
+    mStaticMode = true;
+    
+    // Clear animation asset.
+    mAnimationAsset.clear();
+    
+    // Set asset Id.
+    mImageAsset = pAssetId;
+    
+    // Is there an asset?
+    if ( mImageAsset.notNull() )
+    {
+        // Yes, so is the frame valid?
+        if ( !mImageAsset->containsFrame(frameName) )
+        {
+            // No, so warn.
+            Con::warnf( "ParticleAssetEmitter::setImage() - Invalid frame '%s' for ImageAsset '%s'.", frameName, mImageAsset.getAssetId() );
+        }
+        else
+        {
+        // Yes, so set the frame.
+        mImageFrameName = StringTable->insert(frameName);
+        }
+    }
+    else
+    {
+        // No, so reset the image frame.
+        mImageFrameName = StringTable->insert(StringTable->EmptyString);
+    }
+
+    // Using a named frame index
+    mUsingFrameName = true;
+    
+    // Refresh the asset.
+    refreshAsset();
+    
+    // Return Okay.
+    return true;
+}
+
+//------------------------------------------------------------------------------
+
 bool ParticleAssetEmitter::setImageFrame( const U32 frame )
 bool ParticleAssetEmitter::setImageFrame( const U32 frame )
 {
 {
     // Check Existing Image.
     // Check Existing Image.
@@ -433,6 +491,9 @@ bool ParticleAssetEmitter::setImageFrame( const U32 frame )
     // Set Frame.
     // Set Frame.
     mImageFrame = frame;
     mImageFrame = frame;
 
 
+    // Using a numerical frame index.
+    mUsingFrameName = false;
+
     // Refresh the asset.
     // Refresh the asset.
     refreshAsset();
     refreshAsset();
 
 
@@ -442,6 +503,43 @@ bool ParticleAssetEmitter::setImageFrame( const U32 frame )
 
 
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 
 
+bool ParticleAssetEmitter::setImageFrameName( const char* nameFrame )
+{
+    // Check Existing Image.
+    if ( mImageAsset.isNull() )
+    {
+        // Warn.
+        Con::warnf("ParticleAssetEmitter::setImageNameFrame() - Cannot set Frame without existing asset Id.");
+        
+        // Return Here.
+        return false;
+    }
+    
+    // Check Frame Validity.
+    if ( !mImageAsset->containsFrame(nameFrame) )
+    {
+        // Warn.
+        Con::warnf( "ParticleAssetEmitter::setImageFrameName() - Invalid Frame %s for asset Id '%s'.", nameFrame, mImageAsset.getAssetId() );
+        
+        // Return Here.
+        return false;
+    }
+    
+    // Set frame.
+    mImageFrameName = StringTable->insert(nameFrame);
+
+    // Using a named frame index
+    mUsingFrameName = true;
+    
+    // Refresh the asset.
+    refreshAsset();
+    
+    // Return Okay.
+    return true;
+}
+
+//------------------------------------------------------------------------------
+
 bool ParticleAssetEmitter::setAnimation( const char* pAnimationAssetId )
 bool ParticleAssetEmitter::setAnimation( const char* pAnimationAssetId )
 {
 {
     // Sanity!
     // Sanity!

+ 8 - 0
engine/source/2d/assets/ParticleAssetEmitter.h

@@ -107,8 +107,10 @@ private:
     bool                                    mStaticMode;
     bool                                    mStaticMode;
     AssetPtr<ImageAsset>                    mImageAsset;
     AssetPtr<ImageAsset>                    mImageAsset;
     U32                                     mImageFrame;
     U32                                     mImageFrame;
+    StringTableEntry                        mImageFrameName;
     bool                                    mRandomImageFrame;
     bool                                    mRandomImageFrame;
     AssetPtr<AnimationAsset>                mAnimationAsset;
     AssetPtr<AnimationAsset>                mAnimationAsset;
+    bool                                    mUsingFrameName;
 
 
     /// Particle fields.
     /// Particle fields.
     ParticleAssetFieldCollection            mParticleFields;
     ParticleAssetFieldCollection            mParticleFields;
@@ -182,10 +184,14 @@ public:
     inline bool getOldestInFront( void ) const { return mOldestInFront; }
     inline bool getOldestInFront( void ) const { return mOldestInFront; }
    
    
     inline bool isStaticFrameProvider( void ) const { return mStaticMode; }
     inline bool isStaticFrameProvider( void ) const { return mStaticMode; }
+    inline bool isUsingNamedImageFrame( void ) const { return mUsingFrameName; }
     bool setImage( const char* pAssetId, const U32 frame = 0 );
     bool setImage( const char* pAssetId, const U32 frame = 0 );
+    bool setImage( const char* pAssetId, const char* frameName);
     inline StringTableEntry getImage( void ) const { return mImageAsset.getAssetId(); }
     inline StringTableEntry getImage( void ) const { return mImageAsset.getAssetId(); }
     bool setImageFrame( const U32 frame );
     bool setImageFrame( const U32 frame );
     inline U32 getImageFrame( void ) const { return mImageFrame; }
     inline U32 getImageFrame( void ) const { return mImageFrame; }
+    bool setImageFrameName( const char* frameName);
+    inline const char* getImageFrameName( void ) const { return mImageFrameName; }
     inline void setRandomImageFrame( const bool randomImageFrame ) { mRandomImageFrame = randomImageFrame; }
     inline void setRandomImageFrame( const bool randomImageFrame ) { mRandomImageFrame = randomImageFrame; }
     inline bool getRandomImageFrame( void ) const { return mRandomImageFrame; }
     inline bool getRandomImageFrame( void ) const { return mRandomImageFrame; }
     bool setAnimation( const char* animationName );
     bool setAnimation( const char* animationName );
@@ -304,6 +310,8 @@ protected:
     static bool     writeImage( void* obj, StringTableEntry pFieldName )                { ParticleAssetEmitter* pCastObject = static_cast<ParticleAssetEmitter*>(obj); if ( !pCastObject->isStaticFrameProvider() ) return false; return pCastObject->mImageAsset.notNull(); }
     static bool     writeImage( void* obj, StringTableEntry pFieldName )                { ParticleAssetEmitter* pCastObject = static_cast<ParticleAssetEmitter*>(obj); if ( !pCastObject->isStaticFrameProvider() ) return false; return pCastObject->mImageAsset.notNull(); }
     static bool     setImageFrame(void* obj, const char* data)                          { static_cast<ParticleAssetEmitter*>(obj)->setImageFrame(dAtoi(data)); return false; };
     static bool     setImageFrame(void* obj, const char* data)                          { static_cast<ParticleAssetEmitter*>(obj)->setImageFrame(dAtoi(data)); return false; };
     static bool     writeImageFrame( void* obj, StringTableEntry pFieldName )           { ParticleAssetEmitter* pCastObject = static_cast<ParticleAssetEmitter*>(obj); if ( !pCastObject->isStaticFrameProvider() ) return false; return pCastObject->mImageAsset.notNull() && !pCastObject->getRandomImageFrame(); }
     static bool     writeImageFrame( void* obj, StringTableEntry pFieldName )           { ParticleAssetEmitter* pCastObject = static_cast<ParticleAssetEmitter*>(obj); if ( !pCastObject->isStaticFrameProvider() ) return false; return pCastObject->mImageAsset.notNull() && !pCastObject->getRandomImageFrame(); }
+    static bool     setImageFrameName(void* obj, const char* data)                      { static_cast<ParticleAssetEmitter*>(obj)->setImageFrameName(data); return false; };
+    static bool     writeImageFrameName( void* obj, StringTableEntry pFieldName )       { ParticleAssetEmitter* pCastObject = static_cast<ParticleAssetEmitter*>(obj); if ( !pCastObject->isStaticFrameProvider() ) return false; return pCastObject->mImageAsset.notNull() && !pCastObject->getRandomImageFrame(); }
     static bool     setRandomImageFrame(void* obj, const char* data)                    { static_cast<ParticleAssetEmitter*>(obj)->setRandomImageFrame(dAtob(data)); return false; };
     static bool     setRandomImageFrame(void* obj, const char* data)                    { static_cast<ParticleAssetEmitter*>(obj)->setRandomImageFrame(dAtob(data)); return false; };
     static bool     writeRandomImageFrame( void* obj, StringTableEntry pFieldName )     { ParticleAssetEmitter* pCastObject = static_cast<ParticleAssetEmitter*>(obj); if ( !pCastObject->isStaticFrameProvider() ) return false; return pCastObject->getRandomImageFrame(); }
     static bool     writeRandomImageFrame( void* obj, StringTableEntry pFieldName )     { ParticleAssetEmitter* pCastObject = static_cast<ParticleAssetEmitter*>(obj); if ( !pCastObject->isStaticFrameProvider() ) return false; return pCastObject->getRandomImageFrame(); }
     static bool     setAnimation(void* obj, const char* data)                           { static_cast<ParticleAssetEmitter*>(obj)->setAnimation(data); return false; };
     static bool     setAnimation(void* obj, const char* data)                           { static_cast<ParticleAssetEmitter*>(obj)->setAnimation(data); return false; };

+ 43 - 4
engine/source/2d/assets/ParticleAssetEmitter_ScriptBinding.h

@@ -517,10 +517,28 @@ ConsoleMethodWithDocs(ParticleAssetEmitter, getOldestInFront, ConsoleBool, 2, 2,
 */
 */
 ConsoleMethodWithDocs(ParticleAssetEmitter, setImage, ConsoleBool, 3, 4, (imageAssetId, [frame]))
 ConsoleMethodWithDocs(ParticleAssetEmitter, setImage, ConsoleBool, 3, 4, (imageAssetId, [frame]))
 {
 {
-    // Fetch the frame.
-    const U32 frame = argc >= 4 ? dAtoi(argv[3]) : 0;
-
-    return object->setImage( argv[2], frame );
+    // Was a frame specified?
+    if (argc >= 4)
+    {
+        // Was it a number or a string?
+        if (!dIsalpha(*argv[3]))
+        {
+            // Fetch the numerical frame and set the image
+            const U32 frame = argc >= 4 ? dAtoi(argv[3]) : 0;
+            return object->setImage(argv[2], frame);
+        }
+        else
+        {
+            // Set the image and pass the named frame string
+            return object->setImage(argv[2], argv[3]);
+        }
+    }
+    else
+    {
+        // Frame was not specified, use default 0 and set the image
+        const U32 frame = 0;
+        return object->setImage( argv[2], frame);
+    }
 }
 }
 
 
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
@@ -577,6 +595,27 @@ ConsoleMethodWithDocs(ParticleAssetEmitter, getRandomImageFrame, ConsoleBool, 2,
 
 
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 
 
+/*! Sets the emitter to use the specified image frame by name.
+    @param frame String containing the name of the frame in the image to use.
+    @return Whether the operation was successful or not.
+*/
+ConsoleMethodWithDocs(ParticleAssetEmitter, setImageFrameName, ConsoleBool, 3, 3,  (frame))
+{
+    return object->setImageFrameName( argv[2] );
+}
+
+//------------------------------------------------------------------------------
+
+/*! Gets the asset Id of the image asset assigned to the emitter.
+    @return The asset Id of the image asset assigned to the emitter or nothing if no image is assigned.
+*/
+ConsoleMethodWithDocs(ParticleAssetEmitter, getImageFrameName, ConsoleString, 2, 2, ())
+{
+    return object->getImageFrameName();
+}
+
+//------------------------------------------------------------------------------
+
 /*! Sets the emitter to use the specified animation asset Id.
 /*! Sets the emitter to use the specified animation asset Id.
     @param animationAssetId The animation asset Id to use.
     @param animationAssetId The animation asset Id to use.
     @return Whether the operation was successful or not.
     @return Whether the operation was successful or not.

+ 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)

+ 169 - 33
engine/source/2d/core/ImageFrameProviderCore.cc

@@ -114,7 +114,13 @@ void ImageFrameProviderCore::copyTo( ImageFrameProviderCore* pImageFrameProvider
     {
     {
         // Yes, so use the image/frame if we have an asset.
         // Yes, so use the image/frame if we have an asset.
         if ( mpImageAsset->notNull() )
         if ( mpImageAsset->notNull() )
-            pImageFrameProviderCore->setImage( getImage(), getImageFrame() );
+        {
+            // Named image frame?
+            if ( !isUsingNamedImageFrame() )
+                pImageFrameProviderCore->setImage( getImage(), getImageFrame() );
+            else
+                pImageFrameProviderCore->setImage( getImage(), getImageFrameByName() );
+        }
     }
     }
     else if ( mpAnimationAsset->notNull() )
     else if ( mpAnimationAsset->notNull() )
     {
     {
@@ -178,7 +184,10 @@ bool ImageFrameProviderCore::validRender( void ) const
     if ( isStaticFrameProvider() )
     if ( isStaticFrameProvider() )
     {
     {
         // Yes, so we must have an image asset and the frame must be in bounds.
         // Yes, so we must have an image asset and the frame must be in bounds.
-        return mpImageAsset->notNull() && ( getImageFrame() < (*mpImageAsset)->getFrameCount() );
+        if (!isUsingNamedImageFrame())
+            return mpImageAsset->notNull() && ( getImageFrame() < (*mpImageAsset)->getFrameCount() );
+        else
+            return mpImageAsset->notNull() && getImageFrameByName() != StringTable->EmptyString && ( (*mpImageAsset)->containsFrame(getImageFrameByName()) );
     }
     }
 
 
     // No, so if the animation must be valid.
     // No, so if the animation must be valid.
@@ -187,6 +196,27 @@ bool ImageFrameProviderCore::validRender( void ) const
 
 
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 
 
+const ImageAsset::FrameArea& ImageFrameProviderCore::getProviderImageFrameArea( void ) const
+{
+    // If this does not have a valid render state, return a bad frame
+    if (!validRender())
+        return BadFrameArea;
+    
+    // If it is a static frame and it's not using named frames, get the image area based mImageFrame
+    // If it is a static frame and it's using named frames, get the image area based on mImageNameFrame
+    // Otherwise, get the current animation frame
+    if (isStaticFrameProvider())
+        return !isUsingNamedImageFrame() ? (*mpImageAsset)->getImageFrameArea(mImageFrame) : (*mpImageAsset)->getImageFrameArea(mImageNameFrame);
+    else
+        return !(*mpAnimationAsset)->getNamedCellsMode() ? (*mpAnimationAsset)->getImage()->getImageFrameArea(getCurrentAnimationFrame()) :
+                                                           (*mpAnimationAsset)->getImage()->getImageFrameArea(getCurrentAnimationFrameName());
+    
+    // If we got here for some reason, that's bad. So return a bad area frame
+    return BadFrameArea;
+}
+
+//------------------------------------------------------------------------------
+
 void ImageFrameProviderCore::render(
 void ImageFrameProviderCore::render(
     const bool flipX,
     const bool flipX,
     const bool flipY,
     const bool flipY,
@@ -254,7 +284,7 @@ void ImageFrameProviderCore::renderGui( GuiControl& owner, Point2I offset, const
         RectI destinationRegion(offset, owner.mBounds.extent);
         RectI destinationRegion(offset, owner.mBounds.extent);
 
 
         // Render image.
         // Render image.
-		dglSetBitmapModulation( owner.mProfile->mFillColor );
+        dglSetBitmapModulation( owner.mProfile->mFillColor );
         dglDrawBitmapStretchSR( getProviderTexture(), destinationRegion, sourceRegion );
         dglDrawBitmapStretchSR( getProviderTexture(), destinationRegion, sourceRegion );
         dglClearBitmapModulation();
         dglClearBitmapModulation();
     }
     }
@@ -283,6 +313,9 @@ bool ImageFrameProviderCore::setImage( const char* pImageAssetId, const U32 fram
 
 
     // Set as static provider.
     // Set as static provider.
     mStaticProvider = true;
     mStaticProvider = true;
+    
+    // Using a numerical frame index.
+    mUsingNameFrame = false;
 
 
     // Turn-off tick processing.
     // Turn-off tick processing.
     setProcessTicks( false );
     setProcessTicks( false );
@@ -293,6 +326,37 @@ bool ImageFrameProviderCore::setImage( const char* pImageAssetId, const U32 fram
 
 
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 
 
+bool ImageFrameProviderCore::setImage( const char* pImageAssetId, const char* pNameFrame )
+{
+    // Finish if invalid image asset.
+    if ( pImageAssetId == NULL )
+        return false;
+    
+    // Set asset.
+    mpImageAsset->setAssetId( pImageAssetId );
+    
+    // Set the image frame if the image asset was set.
+    if ( mpImageAsset->notNull() )
+        setImageFrameByName( pNameFrame );
+    
+    // Set Frame.
+    mImageNameFrame = StringTable->insert(pNameFrame);
+    
+    // Set as static provider.
+    mStaticProvider = true;
+    
+    // Using a named frame index.
+    mUsingNameFrame = true;
+    
+    // Turn-off tick processing.
+    setProcessTicks( false );
+    
+    // Return Okay.
+    return true;
+}
+
+//------------------------------------------------------------------------------
+
 bool ImageFrameProviderCore::setImageFrame( const U32 frame )
 bool ImageFrameProviderCore::setImageFrame( const U32 frame )
 {
 {
     // Check Existing Image.
     // Check Existing Image.
@@ -309,7 +373,7 @@ bool ImageFrameProviderCore::setImageFrame( const U32 frame )
     if ( frame >= (*mpImageAsset)->getFrameCount() )
     if ( frame >= (*mpImageAsset)->getFrameCount() )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf( "ImageFrameProviderCore::setImageFrame() - Invalid Frame #%d for asset Id '%s'.", frame, mpImageAsset->getAssetId() );
+        Con::warnf("ImageFrameProviderCore::setImageFrame() - Invalid Frame #%d for asset Id '%s'.", frame, mpImageAsset->getAssetId());
         // Return Here.
         // Return Here.
         return false;
         return false;
     }
     }
@@ -317,6 +381,40 @@ bool ImageFrameProviderCore::setImageFrame( const U32 frame )
     // Set Frame.
     // Set Frame.
     mImageFrame = frame;
     mImageFrame = frame;
 
 
+    // Using a numerical frame index.
+    mUsingNameFrame = false;
+
+    // Return Okay.
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+bool ImageFrameProviderCore::setImageFrameByName(const char* frame)
+{
+    // Check Existing Image.
+    if ( mpImageAsset->isNull() )
+    {
+        // Warn.
+        Con::warnf("ImageFrameProviderCore::setImageNameFrame() - Cannot set Frame without existing asset Id.");
+        
+        // Return Here.
+        return false;
+    }
+    
+    // Check Frame Validity.
+    if ( frame == StringTable->EmptyString )
+    {
+        // Warn.
+        Con::warnf( "ImageFrameProviderCore::setImageNameFrame() - Invalid Frame %s for asset Id '%s'.", frame, mpImageAsset->getAssetId() );
+        // Return Here.
+        return false;
+    }
+    
+    // Set Frame.
+    mImageNameFrame = StringTable->insert(frame);
+    mUsingNameFrame = true;
+    
     // Return Okay.
     // Return Okay.
     return true;
     return true;
 }
 }
@@ -339,17 +437,37 @@ const U32 ImageFrameProviderCore::getCurrentAnimationFrame( void ) const
 
 
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
 
 
+const char* ImageFrameProviderCore::getCurrentAnimationFrameName( void ) const
+{
+    // Sanity!
+    AssertFatal( mpAnimationAsset->notNull(), "Animation controller requested current image frame but no animation asset assigned." );
+
+    // Fetch validated frames.
+    const Vector<StringTableEntry>& validatedFrames = (*mpAnimationAsset)->getValidatedNamedAnimationFrames();
+
+    // Sanity!
+    AssertFatal( mCurrentFrameIndex < validatedFrames.size(), "Animation controller requested the current frame but it is out of bounds of the validated frames." );
+
+    return validatedFrames[mCurrentFrameIndex];
+}
+
+//-----------------------------------------------------------------------------
+
 bool ImageFrameProviderCore::isAnimationValid( void ) const
 bool ImageFrameProviderCore::isAnimationValid( void ) const
 {
 {
     // Not valid if no animation asset.
     // Not valid if no animation asset.
     if ( mpAnimationAsset->isNull() )
     if ( mpAnimationAsset->isNull() )
         return false;
         return false;
 
 
-    // Fetch validated frames.
-    const Vector<S32>& validatedFrames = (*mpAnimationAsset)->getValidatedAnimationFrames();
+    S32 validatedFrameSize = 0;
+
+    if ((*mpAnimationAsset)->getNamedCellsMode())
+        validatedFrameSize = (*mpAnimationAsset)->getValidatedNamedAnimationFrames().size();
+    else
+        validatedFrameSize = (*mpAnimationAsset)->getValidatedAnimationFrames().size();
 
 
     // Not valid if current frame index is out of bounds of the validated frames.
     // Not valid if current frame index is out of bounds of the validated frames.
-    if ( mCurrentFrameIndex >= validatedFrames.size() )
+    if ( mCurrentFrameIndex >= validatedFrameSize )
         return false;
         return false;
 
 
     // Fetch image asset.
     // Fetch image asset.
@@ -359,12 +477,23 @@ bool ImageFrameProviderCore::isAnimationValid( void ) const
     if ( imageAsset.isNull() )
     if ( imageAsset.isNull() )
         return false;
         return false;
 
 
-    // Fetch current frame.
-    const U32 currentFrame = getCurrentAnimationFrame();
+    if (!(*mpAnimationAsset)->getNamedCellsMode())
+    {
+        // Fetch current frame.
+        const U32 currentFrame = getCurrentAnimationFrame();
 
 
-    // Not valid if current frame is out of bounds of the image asset.
-    if ( currentFrame >= imageAsset->getFrameCount() )
-        return false;
+        // Not valid if current frame is out of bounds of the image asset.
+        if ( currentFrame >= imageAsset->getFrameCount() )
+            return false;
+    }
+    else
+    {
+        // Fetch the current name frame.
+        const char* frameName = getCurrentAnimationFrameName();
+
+        if (!imageAsset->containsFrame(frameName))
+            return false;
+    }
 
 
     // Valid.
     // Valid.
     return true;
     return true;
@@ -419,10 +548,15 @@ bool ImageFrameProviderCore::playAnimation( const AssetPtr<AnimationAsset>& anim
     mStaticProvider = false;
     mStaticProvider = false;
 
 
     // Fetch validated frames.
     // Fetch validated frames.
-    const Vector<S32>& validatedFrames = animationAsset->getValidatedAnimationFrames();
+    U32 validatedFrameSize = 0;
+
+    if (animationAsset->getNamedCellsMode())
+        validatedFrameSize = animationAsset->getValidatedNamedAnimationFrames().size();
+    else
+        validatedFrameSize = animationAsset->getValidatedAnimationFrames().size();
 
 
     // Check we've got some frames.
     // Check we've got some frames.
-    if ( validatedFrames.size() == 0 )
+    if ( validatedFrameSize == 0 )
     {
     {
         Con::warnf( "ImageFrameProviderCore::playAnimation() - Cannot play AnimationAsset '%s' - Animation has no validated frames!", mpAnimationAsset->getAssetId() );
         Con::warnf( "ImageFrameProviderCore::playAnimation() - Cannot play AnimationAsset '%s' - Animation has no validated frames!", mpAnimationAsset->getAssetId() );
         return false;
         return false;
@@ -432,13 +566,13 @@ bool ImageFrameProviderCore::playAnimation( const AssetPtr<AnimationAsset>& anim
     mpAnimationAsset->setAssetId( animationAsset.getAssetId() );
     mpAnimationAsset->setAssetId( animationAsset.getAssetId() );
 
 
     // Set Maximum Frame Index.
     // Set Maximum Frame Index.
-    mMaxFrameIndex = validatedFrames.size()-1;
+    mMaxFrameIndex = validatedFrameSize-1;
 
 
     // Calculate Total Integration Time.
     // Calculate Total Integration Time.
     mTotalIntegrationTime = (*mpAnimationAsset)->getAnimationTime();
     mTotalIntegrationTime = (*mpAnimationAsset)->getAnimationTime();
 
 
     // Calculate Frame Integration Time.
     // Calculate Frame Integration Time.
-    mFrameIntegrationTime = mTotalIntegrationTime / validatedFrames.size();
+    mFrameIntegrationTime = mTotalIntegrationTime / validatedFrameSize;
 
 
     // No, so random Start?
     // No, so random Start?
     if ( (*mpAnimationAsset)->getRandomStart() )
     if ( (*mpAnimationAsset)->getRandomStart() )
@@ -477,12 +611,25 @@ bool ImageFrameProviderCore::updateAnimation( const F32 elapsedTime )
     if ( mAnimationFinished )
     if ( mAnimationFinished )
         return false;
         return false;
 
 
-    // Fetch validated frames.
-    const Vector<S32>& validatedFrames = (*mpAnimationAsset)->getValidatedAnimationFrames();
+    // Check for validity in the different frame lists
+    if ((*mpAnimationAsset)->getNamedCellsMode())
+    {
+        // Fetch the validated name frames.
+        const Vector<StringTableEntry>& validatedFrames = (*mpAnimationAsset)->getValidatedNamedAnimationFrames();
 
 
-    // Finish if there are no validated frames.
-    if ( validatedFrames.size() == 0 )
-        return false;
+        // Finish if there are no validated frames.
+        if ( validatedFrames.size() == 0 )
+            return false;
+    }
+    else
+    {
+        // Fetch validated frames.
+        const Vector<S32>& validatedFrames = (*mpAnimationAsset)->getValidatedAnimationFrames();
+
+        // Finish if there are no validated frames.
+        if ( validatedFrames.size() == 0 )
+            return false;
+    }
 
 
     // Calculate scaled time.
     // Calculate scaled time.
     const F32 scaledTime = elapsedTime * mAnimationTimeScale;
     const F32 scaledTime = elapsedTime * mAnimationTimeScale;
@@ -506,18 +653,6 @@ bool ImageFrameProviderCore::updateAnimation( const F32 elapsedTime )
     // Calculate Current Frame.
     // Calculate Current Frame.
     mCurrentFrameIndex = (S32)(mCurrentModTime / mFrameIntegrationTime);
     mCurrentFrameIndex = (S32)(mCurrentModTime / mFrameIntegrationTime);
 
 
-    // Fetch frame.
-    S32 frame = validatedFrames[mCurrentFrameIndex];
-
-    // Fetch image frame count.
-    const S32 imageFrameCount = (*mpAnimationAsset)->getImage()->getFrameCount();
-
-    // Clamp frames.
-    if ( frame < 0 )
-        frame = 0;
-    else if (frame >= imageFrameCount )
-        frame = imageFrameCount-1;
-
     // Calculate if frame has changed.
     // Calculate if frame has changed.
     bool frameChanged = (mCurrentFrameIndex != mLastFrameIndex);
     bool frameChanged = (mCurrentFrameIndex != mLastFrameIndex);
 
 
@@ -569,6 +704,7 @@ void ImageFrameProviderCore::clearAssets( void )
 
 
     // Reset remaining state.
     // Reset remaining state.
     mImageFrame = 0;
     mImageFrame = 0;
+    mImageNameFrame = StringTable->EmptyString;
     mStaticProvider = true;
     mStaticProvider = true;
     setProcessTicks( false );
     setProcessTicks( false );
 }
 }

+ 9 - 2
engine/source/2d/core/ImageFrameProviderCore.h

@@ -62,8 +62,11 @@ protected:
     bool                                    mSelfTick;
     bool                                    mSelfTick;
 
 
     bool                                    mStaticProvider;
     bool                                    mStaticProvider;
+    
+    bool                                    mUsingNameFrame;
 
 
     U32                                     mImageFrame;
     U32                                     mImageFrame;
+    StringTableEntry                        mImageNameFrame;
     AssetPtr<ImageAsset>*                   mpImageAsset;
     AssetPtr<ImageAsset>*                   mpImageAsset;
     AssetPtr<AnimationAsset>*               mpAnimationAsset;
     AssetPtr<AnimationAsset>*               mpAnimationAsset;
 
 
@@ -112,9 +115,12 @@ public:
     /// Static-Image Frame.
     /// Static-Image Frame.
     inline bool setImage( const char* pImageAssetId ) { return setImage( pImageAssetId, mImageFrame ); }
     inline bool setImage( const char* pImageAssetId ) { return setImage( pImageAssetId, mImageFrame ); }
     virtual bool setImage( const char* pImageAssetId, const U32 frame );
     virtual bool setImage( const char* pImageAssetId, const U32 frame );
+    virtual bool setImage( const char* pImageAssetId, const char* pNameFrame );
     inline StringTableEntry getImage( void ) const{ return mpImageAsset->getAssetId(); }
     inline StringTableEntry getImage( void ) const{ return mpImageAsset->getAssetId(); }
     virtual bool setImageFrame( const U32 frame );
     virtual bool setImageFrame( const U32 frame );
     inline U32 getImageFrame( void ) const { return mImageFrame; }
     inline U32 getImageFrame( void ) const { return mImageFrame; }
+    virtual bool setImageFrameByName( const char* frame );
+    inline StringTableEntry getImageFrameByName( void ) const { return mImageNameFrame; }
 
 
     /// Animated-Image Frame.
     /// Animated-Image Frame.
     virtual bool setAnimation( const char* pAnimationAssetId );
     virtual bool setAnimation( const char* pAnimationAssetId );
@@ -132,12 +138,13 @@ public:
 
 
     /// Frame provision.
     /// Frame provision.
     inline bool isStaticFrameProvider( void ) const { return mStaticProvider; }
     inline bool isStaticFrameProvider( void ) const { return mStaticProvider; }
+    inline bool isUsingNamedImageFrame( void ) const { return mUsingNameFrame; }
     inline TextureHandle& getProviderTexture( void ) const { return !validRender() ? BadTextureHandle : isStaticFrameProvider() ? (*mpImageAsset)->getImageTexture() : (*mpAnimationAsset)->getImage()->getImageTexture(); };
     inline TextureHandle& getProviderTexture( void ) const { return !validRender() ? BadTextureHandle : isStaticFrameProvider() ? (*mpImageAsset)->getImageTexture() : (*mpAnimationAsset)->getImage()->getImageTexture(); };
-    inline const ImageAsset::FrameArea& getProviderImageFrameArea( void ) const  { return !validRender() ? BadFrameArea : isStaticFrameProvider() ? (*mpImageAsset)->getImageFrameArea(mImageFrame) : (*mpAnimationAsset)->getImage()->getImageFrameArea(getCurrentAnimationFrame()); };
-
+    const ImageAsset::FrameArea& getProviderImageFrameArea( void ) const;
     inline const AnimationAsset* getCurrentAnimation( void ) const { return mpAnimationAsset->notNull() ? *mpAnimationAsset : NULL; };
     inline const AnimationAsset* getCurrentAnimation( void ) const { return mpAnimationAsset->notNull() ? *mpAnimationAsset : NULL; };
     inline const StringTableEntry getCurrentAnimationAssetId( void ) const { return mpAnimationAsset->getAssetId(); };
     inline const StringTableEntry getCurrentAnimationAssetId( void ) const { return mpAnimationAsset->getAssetId(); };
     const U32 getCurrentAnimationFrame( void ) const;
     const U32 getCurrentAnimationFrame( void ) const;
+    const char* getCurrentAnimationFrameName( void ) const;
     inline const F32 getCurrentAnimationTime( void ) const { return mCurrentTime; };
     inline const F32 getCurrentAnimationTime( void ) const { return mCurrentTime; };
 
 
     void clearAssets( void );
     void clearAssets( void );

+ 1 - 0
engine/source/2d/core/SpriteBase.cc

@@ -56,6 +56,7 @@ void SpriteBase::initPersistFields()
 
 
     addProtectedField("Image", TypeImageAssetPtr, Offset(mImageAsset, SpriteBase), &setImage, &getImage, &writeImage, "");
     addProtectedField("Image", TypeImageAssetPtr, Offset(mImageAsset, SpriteBase), &setImage, &getImage, &writeImage, "");
     addProtectedField("Frame", TypeS32, Offset(mImageFrame, SpriteBase), &setImageFrame, &defaultProtectedGetFn, &writeImageFrame, "");
     addProtectedField("Frame", TypeS32, Offset(mImageFrame, SpriteBase), &setImageFrame, &defaultProtectedGetFn, &writeImageFrame, "");
+    addProtectedField("FrameName", TypeString, Offset(mImageFrame, SpriteBase), &setImageNameFrame, &defaultProtectedGetFn, &writeImageNameFrame, "");
     addProtectedField("Animation", TypeAnimationAssetPtr, Offset(mAnimationAsset, SpriteBase), &setAnimation, &getAnimation, &writeAnimation, "");
     addProtectedField("Animation", TypeAnimationAssetPtr, Offset(mAnimationAsset, SpriteBase), &setAnimation, &getAnimation, &writeAnimation, "");
 }
 }
 
 

+ 10 - 8
engine/source/2d/core/SpriteBase.h

@@ -57,14 +57,16 @@ protected:
     virtual void onAnimationEnd( void );
     virtual void onAnimationEnd( void );
 
 
 protected:
 protected:
-    static bool setImage(void* obj, const char* data)                       { DYNAMIC_VOID_CAST_TO(SpriteBase, ImageFrameProvider, obj)->setImage(data); return false; };
-    static const char* getImage(void* obj, const char* data)                { return DYNAMIC_VOID_CAST_TO(SpriteBase, ImageFrameProvider, obj)->getImage(); }
-    static bool writeImage( void* obj, StringTableEntry pFieldName )        { SpriteBase* pCastObject = static_cast<SpriteBase*>(obj); if ( !pCastObject->isStaticFrameProvider() ) return false; return pCastObject->mImageAsset.notNull(); }
-    static bool setImageFrame(void* obj, const char* data)                  { DYNAMIC_VOID_CAST_TO(SpriteBase, ImageFrameProvider, obj)->setImageFrame(dAtoi(data)); return false; };
-    static bool writeImageFrame( void* obj, StringTableEntry pFieldName )   { SpriteBase* pCastObject = static_cast<SpriteBase*>(obj); if ( !pCastObject->isStaticFrameProvider() ) return false; return pCastObject->mImageAsset.notNull(); }
-    static bool setAnimation(void* obj, const char* data)                   { DYNAMIC_VOID_CAST_TO(SpriteBase, ImageFrameProvider, obj)->setAnimation(data); return false; };
-    static const char* getAnimation(void* obj, const char* data)            { return DYNAMIC_VOID_CAST_TO(SpriteBase, ImageFrameProvider, obj)->getAnimation(); }
-    static bool writeAnimation( void* obj, StringTableEntry pFieldName )    { SpriteBase* pCastObject = static_cast<SpriteBase*>(obj); if ( pCastObject->isStaticFrameProvider() ) return false; return pCastObject->mAnimationAsset.notNull(); }
+    static bool setImage(void* obj, const char* data)                           { DYNAMIC_VOID_CAST_TO(SpriteBase, ImageFrameProvider, obj)->setImage(data); return false; };
+    static const char* getImage(void* obj, const char* data)                    { return DYNAMIC_VOID_CAST_TO(SpriteBase, ImageFrameProvider, obj)->getImage(); }
+    static bool writeImage( void* obj, StringTableEntry pFieldName )            { SpriteBase* pCastObject = static_cast<SpriteBase*>(obj); if ( !pCastObject->isStaticFrameProvider() ) return false; return pCastObject->mImageAsset.notNull(); }
+    static bool setImageFrame(void* obj, const char* data)                      { DYNAMIC_VOID_CAST_TO(SpriteBase, ImageFrameProvider, obj)->setImageFrame(dAtoi(data)); return false; };
+    static bool setImageNameFrame(void* obj, const char* data)                  { DYNAMIC_VOID_CAST_TO(SpriteBase, ImageFrameProvider, obj)->setImageFrameByName(data); return false; };
+    static bool writeImageFrame( void* obj, StringTableEntry pFieldName )       { SpriteBase* pCastObject = static_cast<SpriteBase*>(obj); if ( !pCastObject->isStaticFrameProvider() ) return false; return pCastObject->mImageAsset.notNull(); }
+    static bool writeImageNameFrame( void* obj, StringTableEntry pFieldName )   { SpriteBase* pCastObject = static_cast<SpriteBase*>(obj); if ( !pCastObject->isUsingNamedImageFrame() ) return false; return pCastObject->mImageAsset.notNull(); }
+    static bool setAnimation(void* obj, const char* data)                       { DYNAMIC_VOID_CAST_TO(SpriteBase, ImageFrameProvider, obj)->setAnimation(data); return false; };
+    static const char* getAnimation(void* obj, const char* data)                { return DYNAMIC_VOID_CAST_TO(SpriteBase, ImageFrameProvider, obj)->getAnimation(); }
+    static bool writeAnimation( void* obj, StringTableEntry pFieldName )        { SpriteBase* pCastObject = static_cast<SpriteBase*>(obj); if ( pCastObject->isStaticFrameProvider() ) return false; return pCastObject->mAnimationAsset.notNull(); }
 };
 };
 
 
 #endif // _SPRITE_BASE_H_
 #endif // _SPRITE_BASE_H_

+ 63 - 7
engine/source/2d/core/SpriteBase_ScriptBinding.h

@@ -34,16 +34,33 @@ ConsoleMethodWithDocs(SpriteBase, isStaticFrameProvider, ConsoleBool, 2, 2, ())
 
 
 /*! Sets the sprite image and optionally frame.
 /*! Sets the sprite image and optionally frame.
     @param imageAssetId The image asset Id to display
     @param imageAssetId The image asset Id to display
-    @param frame The frame of the image to display
+    @param frame The numerical or named frame of the image to display
     @return Returns true on success.
     @return Returns true on success.
 */
 */
-ConsoleMethodWithDocs(SpriteBase, setImage, ConsoleBool, 3, 4, (string imageAssetId, [int frame]))
+ConsoleMethodWithDocs(SpriteBase, setImage, ConsoleBool, 3, 4, (imageAssetId, [frame]))
 {
 {
-    // Calculate Frame.
-    U32 frame = argc >= 4 ? dAtoi(argv[3]) : 0;
-
-    // Set image.
-    return static_cast<ImageFrameProvider*>(object)->setImage( argv[2], frame );
+    // Was a frame specified?
+    if (argc >= 4)
+    {
+        // Was it a number or a string?
+        if (!dIsalpha(*argv[3]))
+        {
+            // Fetch the numerical frame and set the image
+            const U32 frame = argc >= 4 ? dAtoi(argv[3]) : 0;
+            return static_cast<ImageFrameProvider*>(object)->setImage( argv[2], frame );
+        }
+        else
+        {
+            // Set the image and pass the named frame string
+            return static_cast<ImageFrameProvider*>(object)->setImage( argv[2], argv[3] );
+        }
+    }
+    else
+    {
+        // Frame was not specified, use default 0 and set the image
+        const U32 frame = 0;
+        return static_cast<ImageFrameProvider*>(object)->setImage( argv[2], frame );
+    }
 }
 }
 
 
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
@@ -106,6 +123,45 @@ ConsoleMethodWithDocs(SpriteBase, getImageFrame, ConsoleInt, 2, 2, ())
 
 
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 
 
+/*! Sets the image frame using a string.
+    @param frame - The name of the frame.
+    @return True on success.
+*/
+ConsoleMethodWithDocs(SpriteBase, setImageFrameName, ConsoleBool, 3, 3,  (frame))
+{
+    // Are we in static mode?
+    if ( !object->isStaticFrameProvider() )
+    {
+        // No, so warn.
+        Con::warnf( "SpriteBase::setImageFrameName() - Method invalid, not in static mode." );
+        return false;
+    }
+    
+    // Set image Frame.
+    return static_cast<ImageFrameProvider*>(object)->setImageFrameByName( argv[2] );
+}
+
+//------------------------------------------------------------------------------
+
+/*! Gets the current image frame name.
+    @return The current image frame name.
+*/
+ConsoleMethodWithDocs(SpriteBase, getImageFrameName, ConsoleString, 2, 2, ())
+{
+    // Are we in static mode?
+    if ( !object->isStaticFrameProvider() )
+    {
+        // No, so warn.
+        Con::warnf( "SpriteBase::getImageFrameName() - Method invalid, not in static mode." );
+        return false;
+    }
+    
+    // Get image Frame.
+    return static_cast<ImageFrameProvider*>(object)->getImageFrameByName();
+}
+
+//------------------------------------------------------------------------------
+
 /*! Plays an animation.
 /*! Plays an animation.
     @param animationAssetId The animation asset Id to play.
     @param animationAssetId The animation asset Id to play.
     @return Returns true on success.
     @return Returns true on success.

+ 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; }
 
 

+ 1 - 1
engine/source/2d/gui/guiSpriteCtrl.cc

@@ -125,7 +125,7 @@ bool GuiSpriteCtrl::setImage( const char* pImageAssetId )
 		return true;
 		return true;
 
 
 	// Call parent.
 	// Call parent.
-	if ( !ImageFrameProvider::setImage( pImageAssetId, 0 ) )
+	if ( !ImageFrameProvider::setImage( pImageAssetId, mImageFrame ) )
 		return false;
 		return false;
 
 
     // Update control.
     // Update control.

+ 6 - 3
engine/source/2d/scene/Scene.cc

@@ -4615,20 +4615,23 @@ void Scene::onTamlPostRead( const TamlCustomNodes& customNodes )
     }
     }
 
 
     // Find controller custom node.
     // Find controller custom node.
-    const TamlCustomNode* pControllerNode = customNodes.findNode( controllerCustomNodeName );
+    const TamlCustomNode* pControllerCustomNode = customNodes.findNode( controllerCustomNodeName );
 
 
     // Do we have any controllers?
     // Do we have any controllers?
-    if ( pControllerNode != NULL )
+    if ( pControllerCustomNode != NULL )
     {
     {
         // Yes, so fetch the scene controllers.
         // Yes, so fetch the scene controllers.
         SimSet* pControllerSet = getControllers();
         SimSet* pControllerSet = getControllers();
 
 
         // Fetch children controller nodes.
         // Fetch children controller nodes.
-        const TamlCustomNodeVector& controllerChildren = pControllerNode->getChildren();
+        const TamlCustomNodeVector& controllerChildren = pControllerCustomNode->getChildren();
 
 
         // Iterate controllers.
         // Iterate controllers.
         for( TamlCustomNodeVector::const_iterator controllerNodeItr = controllerChildren.begin(); controllerNodeItr != controllerChildren.end(); ++controllerNodeItr )
         for( TamlCustomNodeVector::const_iterator controllerNodeItr = controllerChildren.begin(); controllerNodeItr != controllerChildren.end(); ++controllerNodeItr )
         {
         {
+            // Fetch controller node.
+            TamlCustomNode* pControllerNode = *controllerNodeItr;
+            
             // Is the node a proxy object?
             // Is the node a proxy object?
             if ( !pControllerNode->isProxyObject() )
             if ( !pControllerNode->isProxyObject() )
             {
             {

+ 6 - 2
engine/source/2d/sceneobject/ParticlePlayer.cc

@@ -794,6 +794,7 @@ bool ParticlePlayer::play( const bool resetParticles )
         // Fetch the emitter node.
         // Fetch the emitter node.
         EmitterNode* pEmitterNode = *emitterItr;
         EmitterNode* pEmitterNode = *emitterItr;
 		pEmitterNode->setPaused(false);
 		pEmitterNode->setPaused(false);
+        
         // Reset the time since last generation.
         // Reset the time since last generation.
         pEmitterNode->setTimeSinceLastGeneration( 0.0f );
         pEmitterNode->setTimeSinceLastGeneration( 0.0f );
     }
     }
@@ -1272,7 +1273,10 @@ void ParticlePlayer::configureParticle( EmitterNode* pEmitterNode, ParticleSyste
         else
         else
         {
         {
             // No, so set the emitter image frame.
             // No, so set the emitter image frame.
-            frameProvider.setImageFrame( pParticleAssetEmitter->getImageFrame() );
+            if (pParticleAssetEmitter->isUsingNamedImageFrame())
+                frameProvider.setImageFrameByName( pParticleAssetEmitter->getImageFrameName() );
+            else
+                frameProvider.setImageFrame( pParticleAssetEmitter->getImageFrame() );
         }
         }
     }
     }
     else
     else
@@ -1534,7 +1538,7 @@ void ParticlePlayer::initializeParticleAsset( void )
 
 
         // Skip if the emitter does not have a valid assigned asset to render.
         // Skip if the emitter does not have a valid assigned asset to render.
         if (( pParticleAssetEmitter->isStaticFrameProvider() && (imageAsset.isNull() || imageAsset->getFrameCount() == 0 ) ) ||
         if (( pParticleAssetEmitter->isStaticFrameProvider() && (imageAsset.isNull() || imageAsset->getFrameCount() == 0 ) ) ||
-            ( !pParticleAssetEmitter->isStaticFrameProvider() && (animationAsset.isNull() || animationAsset->getValidatedAnimationFrames().size() == 0 ) ) )
+            ( !pParticleAssetEmitter->isStaticFrameProvider() && (animationAsset.isNull() || (animationAsset->getValidatedAnimationFrames().size() == 0 && animationAsset->getValidatedNamedAnimationFrames().size() == 0)) ) )
             continue;
             continue;
 
 
         // Create a new emitter node.
         // Create a new emitter node.

+ 3 - 3
engine/source/2d/sceneobject/SceneObject_ScriptBinding.h

@@ -1156,7 +1156,7 @@ ConsoleMethodWithDocs(SceneObject, getContact, ConsoleString, 3, 3, (contactInde
     {
     {
         dSprintf(pReturnBuffer, 128,
         dSprintf(pReturnBuffer, 128,
             "%d %d %d %0.4f %0.4f %0.4f %0.4f %0.4f %0.4f %0.4f %0.4f %0.4f %0.4f",
             "%d %d %d %0.4f %0.4f %0.4f %0.4f %0.4f %0.4f %0.4f %0.4f %0.4f %0.4f",
-            pSceneObjectCollider,
+            pSceneObjectCollider->getId(),
             shapeIndexThis, shapeIndexCollider,
             shapeIndexThis, shapeIndexCollider,
             normal.x, normal.y,
             normal.x, normal.y,
             point1.x, point1.y,
             point1.x, point1.y,
@@ -1170,7 +1170,7 @@ ConsoleMethodWithDocs(SceneObject, getContact, ConsoleString, 3, 3, (contactInde
     {
     {
         dSprintf(pReturnBuffer, 128,
         dSprintf(pReturnBuffer, 128,
             "%d %d %d %0.4f %0.4f %0.4f %0.4f %0.4f %0.4f",
             "%d %d %d %0.4f %0.4f %0.4f %0.4f %0.4f %0.4f",
-            pSceneObjectCollider,
+            pSceneObjectCollider->getId(),
             shapeIndexThis, shapeIndexCollider,
             shapeIndexThis, shapeIndexCollider,
             normal.x, normal.y,
             normal.x, normal.y,
             point1.x, point1.y,
             point1.x, point1.y,
@@ -1181,7 +1181,7 @@ ConsoleMethodWithDocs(SceneObject, getContact, ConsoleString, 3, 3, (contactInde
 	{
 	{
         dSprintf(pReturnBuffer, 64,
         dSprintf(pReturnBuffer, 64,
             "%d %d %d",
             "%d %d %d",
-            pSceneObjectCollider,
+            pSceneObjectCollider->getId(),
             shapeIndexThis, shapeIndexCollider );
             shapeIndexThis, shapeIndexCollider );
 	}
 	}
 
 

+ 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)

+ 1 - 1
engine/source/collection/nameTags_ScriptBinding.h

@@ -295,7 +295,7 @@ ConsoleMethodWithDocs(NameTags, queryTags, ConsoleString, 3, 4, (tagIds, [exclud
     // Format results.
     // Format results.
     U32 bufferSize = 8192;
     U32 bufferSize = 8192;
     char* pReturnBuffer = Con::getReturnBuffer( bufferSize );
     char* pReturnBuffer = Con::getReturnBuffer( bufferSize );
-    dSprintf(pReturnBuffer, sizeof(pReturnBuffer), "%s", "");
+    dSprintf(pReturnBuffer, bufferSize * sizeof(char), "%s", "");
     char* pBuffer = pReturnBuffer;
     char* pBuffer = pReturnBuffer;
 
 
     for( NameTags::queryType::iterator itr = results.begin(); itr != results.end(); ++itr )
     for( NameTags::queryType::iterator itr = results.begin(); itr != results.end(); ++itr )

+ 1 - 1
engine/source/gui/editor/guiDebugger.cc

@@ -79,7 +79,7 @@ ConsoleMethod(DbgFileView, getCurrentLine, const char *, 2, 2, "()"
 	S32 lineNum;
 	S32 lineNum;
    const char *file = object->getCurrentLine(lineNum);
    const char *file = object->getCurrentLine(lineNum);
    char* ret = Con::getReturnBuffer(256);
    char* ret = Con::getReturnBuffer(256);
-	dSprintf(ret, sizeof(ret), "%s\t%d", file, lineNum);
+	dSprintf(ret, 256 * sizeof(char), "%s\t%d", file, lineNum);
 	return ret;
 	return ret;
 }
 }
 
 

+ 7 - 7
engine/source/gui/guiCanvas.cc

@@ -31,17 +31,17 @@
 #include "gui/guiControl.h"
 #include "gui/guiControl.h"
 #include "gui/guiCanvas.h"
 #include "gui/guiCanvas.h"
 #include "game/gameInterface.h"
 #include "game/gameInterface.h"
-
+
 #include "guiCanvas_ScriptBinding.h"
 #include "guiCanvas_ScriptBinding.h"
-
+
 extern int _AndroidGetScreenWidth();
 extern int _AndroidGetScreenWidth();
 extern int _AndroidGetScreenHeight();
 extern int _AndroidGetScreenHeight();
-
+
 
 
 IMPLEMENT_CONOBJECT(GuiCanvas);
 IMPLEMENT_CONOBJECT(GuiCanvas);
 
 
 GuiCanvas *Canvas = NULL;
 GuiCanvas *Canvas = NULL;
-
+
 GuiCanvas::GuiCanvas()
 GuiCanvas::GuiCanvas()
 {
 {
 #ifdef TORQUE_OS_IOS
 #ifdef TORQUE_OS_IOS
@@ -384,14 +384,14 @@ bool GuiCanvas::processInputEvent(const InputEvent *event)
 
 
          if (event->objType == SI_XAXIS)
          if (event->objType == SI_XAXIS)
          {
          {
-            pt.x += (event->fValue * mPixelsPerMickey);
+            pt.x += (event->fValues[0] * mPixelsPerMickey);
             cursorPt.x = (F32)getMax(0, getMin((S32)pt.x, mBounds.extent.x - 1));
             cursorPt.x = (F32)getMax(0, getMin((S32)pt.x, mBounds.extent.x - 1));
             if (oldpt.x != S32(cursorPt.x))
             if (oldpt.x != S32(cursorPt.x))
                moved = true;
                moved = true;
          }
          }
          else
          else
          {
          {
-            pt.y += (event->fValue * mPixelsPerMickey);
+            pt.y += (event->fValues[0] * mPixelsPerMickey);
             cursorPt.y = (F32)getMax(0, getMin((S32)pt.y, mBounds.extent.y - 1));
             cursorPt.y = (F32)getMax(0, getMin((S32)pt.y, mBounds.extent.y - 1));
             if (oldpt.y != S32(cursorPt.y))
             if (oldpt.y != S32(cursorPt.y))
                moved = true;
                moved = true;
@@ -425,7 +425,7 @@ bool GuiCanvas::processInputEvent(const InputEvent *event)
          mLastEvent.mousePoint.y = S32( cursorPt.y );
          mLastEvent.mousePoint.y = S32( cursorPt.y );
          mLastEvent.eventID = 0;
          mLastEvent.eventID = 0;
 
 
-            if ( event->fValue < 0.0f )
+            if ( event->fValues[0] < 0.0f )
             rootMouseWheelDown( mLastEvent );
             rootMouseWheelDown( mLastEvent );
             else
             else
             rootMouseWheelUp( mLastEvent );
             rootMouseWheelUp( mLastEvent );

+ 2292 - 1853
engine/source/input/actionMap.cc

@@ -1,1855 +1,2294 @@
-//-----------------------------------------------------------------------------
-// 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.
-//-----------------------------------------------------------------------------
-
-#include "input/actionMap.h"
-#include "platform/event.h"
-#include "console/console.h"
-#include "platform/platform.h"
-#include "platform/platformInput.h"
-#include "platform/platformAssert.h"
-#include "io/fileStream.h"
-#include "io/resource/resourceManager.h"
-
+//-----------------------------------------------------------------------------
+// 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.
+//-----------------------------------------------------------------------------
+
+#include "input/actionMap.h"
+#include "platform/event.h"
+#include "console/console.h"
+#include "platform/platform.h"
+#include "platform/platformInput.h"
+#include "platform/platformAssert.h"
+#include "io/fileStream.h"
+#include "io/resource/resourceManager.h"
+
 // Script bindings.
 // Script bindings.
 #include "actionMap_ScriptBinding.h"
 #include "actionMap_ScriptBinding.h"
-
-IMPLEMENT_CONOBJECT(ActionMap);
-
-// This is used for determing keys that have ascii codes for the foreign keyboards. IsAlpha doesn't work on foreign keys.
-#define dIsDecentChar(c) (((char(0xa0) <= (c)) && ((c) <= char(0xff))) || (( char(0x21) <= (c)) && ((c) <= char(0x7e))) || (( char(0x91) <= (c)) && ((c) <= char(0x92))))
-
-struct CodeMapping
-{
-   const char* pDescription;
-   U8  type;
-   U32 code;
-};
-
-struct AsciiMapping
-{
-   const char* pDescription;
-   U16         asciiCode;
-};
-
-extern CodeMapping gVirtualMap[];
-extern AsciiMapping gAsciiMap[];
-
-//------------------------------------------------------------------------------
-//-------------------------------------- Action maps
-//
-Vector<ActionMap::BreakEntry> ActionMap::smBreakTable(__FILE__, __LINE__);
-
-
-//------------------------------------------------------------------------------
-ActionMap::ActionMap()
-{
-   VECTOR_SET_ASSOCIATION(mDeviceMaps);
-}
-
-//------------------------------------------------------------------------------
-ActionMap::~ActionMap()
-{
-   for (U32 i = 0; i < (U32)mDeviceMaps.size(); i++)
-      delete mDeviceMaps[i];
-   mDeviceMaps.clear();
-}
-
-//------------------------------------------------------------------------------
-ActionMap::DeviceMap::~DeviceMap()
-{
-   for(U32 i = 0; i < (U32)nodeMap.size(); i++)
-   {
-      dFree(nodeMap[i].makeConsoleCommand);
-      dFree(nodeMap[i].breakConsoleCommand);
-   }
-}
-
-//------------------------------------------------------------------------------
-bool ActionMap::onAdd()
-{
-   if (Parent::onAdd() == false)
-      return false;
-
-   Sim::getActionMapGroup()->addObject(this);
-
-   return true;
-}
-
-//--------------------------------------------------------------------------
-void ActionMap::dumpActionMap(const char* fileName, const bool append) const
-{
-   if (fileName != NULL) {
-      // Dump the deletion, and creation script commands, followed by all the binds
-      //  to a script.
-
-      FileStream iostrm;
-      if ( !ResourceManager->openFileForWrite( iostrm, fileName, append ? FileStream::WriteAppend : FileStream::Write ) )
-      {
-         Con::errorf( "Unable to open file '%s' for writing.", fileName );
-         return;
-      }
-
-      char lineBuffer[1024];
-      if ( append )
-         iostrm.setPosition( iostrm.getStreamSize() );
-      else
-      {
-         // IMPORTANT -- do NOT change the following line, it identifies the file as an input map file
-         dStrcpy( lineBuffer, "// Torque Input Map File\n" );
-         iostrm.write( dStrlen( lineBuffer ), lineBuffer );
-      }
-
-      dSprintf(lineBuffer, 1023, "if (isObject(%s)) %s.delete();\n"
-                                 "new ActionMap(%s);\n", getName(), getName(), getName());
-      iostrm.write(dStrlen(lineBuffer), lineBuffer);
-
-      // Dump all the binds to the console...
-      for (S32 i = 0; i < mDeviceMaps.size(); i++) {
-         const DeviceMap* pDevMap = mDeviceMaps[i];
-
-         char devbuffer[32];
-         getDeviceName(pDevMap->deviceType, pDevMap->deviceInst, devbuffer);
-
-         for (S32 j = 0; j < pDevMap->nodeMap.size(); j++) {
-            const Node& rNode = pDevMap->nodeMap[j];
-
-            const char* pModifierString = getModifierString(rNode.modifiers);
-
-            char objectbuffer[64];
-            if (getKeyString(rNode.action, objectbuffer) == false)
-               continue;
-
-            const char* command = (rNode.flags & Node::BindCmd) ? "bindCmd" : "bind";
-
-            dSprintf(lineBuffer, 1023, "%s.%s(%s, \"%s%s\"",
-                                        getName(),
-                                        command,
-                                        devbuffer,
-                                        pModifierString, objectbuffer);
-
-            if (rNode.flags & (Node::HasScale|Node::HasDeadZone|Node::Ranged|Node::Inverted)) {
-               char buff[10];
-               U32 curr = 0;
-               buff[curr++] = ',';
-               buff[curr++] = ' ';
-               if (rNode.flags & Node::HasScale)
-                  buff[curr++] = 'S';
-               if (rNode.flags & Node::Ranged)
-                  buff[curr++] = 'R';
-               if (rNode.flags & Node::HasDeadZone)
-                  buff[curr++] = 'D';
-               if (rNode.flags & Node::Inverted)
-                  buff[curr++] = 'I';
-               buff[curr] = '\0';
-
-               dStrcat(lineBuffer, buff);
-            }
-
-            if (rNode.flags & Node::HasDeadZone) {
-               char buff[64];
-               dSprintf(buff, 63, ", \"%g %g\"", rNode.deadZoneBegin, rNode.deadZoneEnd);
-               dStrcat(lineBuffer, buff);
-            }
-
-            if (rNode.flags & Node::HasScale) {
-               char buff[64];
-               dSprintf(buff, 63, ", %g", rNode.scaleFactor);
-               dStrcat(lineBuffer, buff);
-            }
-
-            if (rNode.flags & Node::BindCmd) {
-               if (rNode.makeConsoleCommand) {
-                  dStrcat(lineBuffer, ", \"");
-                  U32 pos = dStrlen(lineBuffer);
-                  expandEscape(lineBuffer + pos, rNode.makeConsoleCommand);
-                  dStrcat(lineBuffer, "\"");
-               } else {
-                  dStrcat(lineBuffer, ", \"\"");
-               }
-               if (rNode.breakConsoleCommand) {
-                  dStrcat(lineBuffer, ", \"");
-                  U32 pos = dStrlen(lineBuffer);
-                  expandEscape(lineBuffer + pos, rNode.breakConsoleCommand);
-                  dStrcat(lineBuffer, "\"");
-               }
-               else
-                  dStrcat(lineBuffer, ", \"\"");
-            } else {
-               dStrcat(lineBuffer, ", ");
-               dStrcat(lineBuffer, rNode.consoleFunction);
-            }
-
-            dStrcat(lineBuffer, ");\n");
-            iostrm.write(dStrlen(lineBuffer), lineBuffer);
-         }
-      }
-
-      iostrm.close();
-   }
-   else {
-      // Dump all the binds to the console...
-      for (S32 i = 0; i < mDeviceMaps.size(); i++) {
-         const DeviceMap* pDevMap = mDeviceMaps[i];
-
-         char devbuffer[32];
-         getDeviceName(pDevMap->deviceType, pDevMap->deviceInst, devbuffer);
-
-         for (S32 j = 0; j < pDevMap->nodeMap.size(); j++) {
-            const Node& rNode = pDevMap->nodeMap[j];
-
-            const char* pModifierString = getModifierString(rNode.modifiers);
-
-            char keybuffer[64];
-            if (getKeyString(rNode.action, keybuffer) == false)
-               continue;
-
-            const char* command = (rNode.flags & Node::BindCmd) ? "bindCmd" : "bind";
-
-            char finalBuffer[1024];
-            dSprintf(finalBuffer, 1023, "%s.%s(%s, \"%s%s\"",
-                                        getName(),
-                                        command,
-                                        devbuffer,
-                                        pModifierString, keybuffer);
-
-            if (rNode.flags & (Node::HasScale|Node::HasDeadZone|Node::Ranged|Node::Inverted)) {
-               char buff[10];
-               U32 curr = 0;
-               buff[curr++] = ',';
-               buff[curr++] = ' ';
-               if (rNode.flags & Node::HasScale)
-                  buff[curr++] = 'S';
-               if (rNode.flags & Node::Ranged)
-                  buff[curr++] = 'R';
-               if (rNode.flags & Node::HasDeadZone)
-                  buff[curr++] = 'D';
-               if (rNode.flags & Node::Inverted)
-                  buff[curr++] = 'I';
-               buff[curr] = '\0';
-
-               dStrcat(finalBuffer, buff);
-            }
-
-            if (rNode.flags & Node::HasDeadZone) {
-               char buff[64];
-               dSprintf(buff, 63, ", \"%g %g\"", rNode.deadZoneBegin, rNode.deadZoneEnd);
-               dStrcat(finalBuffer, buff);
-            }
-
-            if (rNode.flags & Node::HasScale) {
-               char buff[64];
-               dSprintf(buff, 63, ", %g", rNode.scaleFactor);
-               dStrcat(finalBuffer, buff);
-            }
-
-            if (rNode.flags & Node::BindCmd) {
-               if (rNode.makeConsoleCommand) {
-                  dStrcat(finalBuffer, ", \"");
-                  dStrcat(finalBuffer, rNode.makeConsoleCommand);
-                  dStrcat(finalBuffer, "\"");
-               } else {
-                  dStrcat(finalBuffer, ", \"\"");
-               }
-               if (rNode.breakConsoleCommand) {
-                  dStrcat(finalBuffer, ", \"");
-                  dStrcat(finalBuffer, rNode.breakConsoleCommand);
-                  dStrcat(finalBuffer, "\"");
-               }
-               else
-                  dStrcat(finalBuffer, ", \"\"");
-            } else {
-               dStrcat(finalBuffer, ", ");
-               dStrcat(finalBuffer, rNode.consoleFunction);
-            }
-
-            dStrcat(finalBuffer, ");");
-            Con::printf(finalBuffer);
-         }
-      }
-   }
-}
-
-//--------------------------------------------------------------------------
-bool ActionMap::createEventDescriptor(const char* pEventString, EventDescriptor* pDescriptor)
-{
-   char copyBuffer[256];
-   dStrcpy(copyBuffer, pEventString);
-
-   // Do we have modifiers?
-   char* pSpace = dStrchr(copyBuffer, ' ');
-   char* pObjectString;
-   if (pSpace != NULL) {
-      // Yes.  Parse them out...
-      //
-      pDescriptor->flags = 0;
-      pObjectString      = pSpace + 1;
-      pSpace[0]          = '\0';
-
-      char* pModifier = dStrtok(copyBuffer, "-");
-      while (pModifier != NULL) {
-         if (dStricmp(pModifier, "shift") == 0) {
-            pDescriptor->flags |= SI_SHIFT;
-         } else if (dStricmp(pModifier, "ctrl") == 0) {
-            pDescriptor->flags |= SI_CTRL;
-         } else if (dStricmp(pModifier, "alt") == 0) {
-            pDescriptor->flags |= SI_ALT;
-         } else if (dStricmp(pModifier, "cmd") == 0) {
-            pDescriptor->flags |= SI_ALT;
-         } else if (dStricmp(pModifier, "opt") == 0) {
-            pDescriptor->flags |= SI_MAC_OPT;
-         }
-
-         pModifier = dStrtok(NULL, "-");
-      }
-   } else {
-      // No.
-      pDescriptor->flags = 0;
-      pObjectString      = copyBuffer;
-   }
-
-   // Now we need to map the key string to the proper KEY code from event.h
-   //
-   AssertFatal(dStrlen(pObjectString) != 0, "Error, no key was specified!");
-
-   if (dStrlen(pObjectString) == 1)
-   {
-      if (dIsDecentChar(*pObjectString)) // includes foreign chars
-      {
-         U16 asciiCode = (*pObjectString);
-         // clear out the FF in upper 8bits for foreign keys??
-         asciiCode &= 0xFF;
-         U16 keyCode = Input::getKeyCode(asciiCode);
-         if ( keyCode >= KEY_0 )
-         {
-            pDescriptor->eventType = SI_KEY;
-            pDescriptor->eventCode = keyCode;
-            return true;
-         }
-         else if (dIsalpha(*pObjectString) == true)
-         {
-            pDescriptor->eventType = SI_KEY;
-            pDescriptor->eventCode = KEY_A+dTolower(*pObjectString)-'a';
-            return true;
-         }
-         else if (dIsdigit(*pObjectString) == true)
-         {
-            pDescriptor->eventType = SI_KEY;
-            pDescriptor->eventCode = KEY_0+(*pObjectString)-'0';
-            return true;
-         }
-      }
-      return false;
-   }
-   else
-   {
-      pDescriptor->eventCode = 0;
-      // Gotta search through the Ascii table...
-      for (U16 i = 0; gAsciiMap[i].asciiCode != 0xFFFF; i++)
-      {
-         if (dStricmp(pObjectString, gAsciiMap[i].pDescription) == 0)
-         {
-            U16 asciiCode = gAsciiMap[i].asciiCode;
-            U16 keyCode   = Input::getKeyCode(asciiCode);
-            if ( keyCode >= KEY_0 )
-            {
-               pDescriptor->eventType = SI_KEY;
-               pDescriptor->eventCode = keyCode;
-               return(true);
-
-            }
-            else
-            {
-               break;
-            }
-         }
-      }
-      // Didn't find an ascii match. Check the virtual map table
-      for (U32 j = 0; gVirtualMap[j].code != 0xFFFFFFFF; j++)
-      {
-         if (dStricmp(pObjectString, gVirtualMap[j].pDescription) == 0)
-         {
-            pDescriptor->eventType = gVirtualMap[j].type;
-            pDescriptor->eventCode = gVirtualMap[j].code;
-            return true;
-         }
-      }
-   }
-   return false;
-}
-
-//------------------------------------------------------------------------------
-ActionMap::Node* ActionMap::getNode(const U32 inDeviceType, const U32 inDeviceInst,
-                   const U32 inModifiers,  const U32 inAction,SimObject* object /*= NULL*/)
-{
-   // DMMTODO - Slow INITIAL implementation.  Replace with a faster version...
-   //
-   DeviceMap* pDeviceMap = NULL;
-   U32 i;
-   for (i = 0; i < (U32)mDeviceMaps.size(); i++) 
-   {
-      if (mDeviceMaps[i]->deviceType == inDeviceType &&
-          mDeviceMaps[i]->deviceInst == inDeviceInst) {
-         pDeviceMap = mDeviceMaps[i];
-         break;
-      }
-   }
-   if (pDeviceMap == NULL) 
-   {
-      mDeviceMaps.increment();
-      mDeviceMaps.last() = new DeviceMap;
-      pDeviceMap = mDeviceMaps.last();
-
-      pDeviceMap->deviceInst = inDeviceInst;
-      pDeviceMap->deviceType = inDeviceType;
-   }
-
-   for (i = 0; i < (U32)pDeviceMap->nodeMap.size(); i++) 
-   {
-      if (pDeviceMap->nodeMap[i].modifiers == inModifiers &&
-          pDeviceMap->nodeMap[i].action    == inAction &&
-          ( (object != NULL) ? object == pDeviceMap->nodeMap[i].object : true )) // Check for an object match if the object exists 
-      {
-         return &pDeviceMap->nodeMap[i];
-      }
-   }
-
-   // If we're here, the node doesn't exist.  create it.
-   pDeviceMap->nodeMap.increment();
-
-   Node* pRetNode = &pDeviceMap->nodeMap.last();
-   pRetNode->modifiers = inModifiers;
-   pRetNode->action    = inAction;
-
-   pRetNode->flags         = 0;
-   pRetNode->deadZoneBegin = 0.0;
-   pRetNode->deadZoneEnd   = 0.0;
-   pRetNode->scaleFactor   = 1.0;
-
-   pRetNode->consoleFunction = NULL;
-   pRetNode->makeConsoleCommand = NULL;
-   pRetNode->breakConsoleCommand = NULL;
-
-   //[neob, 5/7/2007 - #2975]
-   pRetNode->object = 0;
-
-   return pRetNode;
-}
-
-//------------------------------------------------------------------------------
-void ActionMap::removeNode(const U32 inDeviceType, const U32 inDeviceInst, const U32 inModifiers, const U32 inAction, SimObject* object /*= NULL*/)
-{
-   // DMMTODO - Slow INITIAL implementation.  Replace with a faster version...
-   //
-   DeviceMap* pDeviceMap = NULL;
-   U32 i;
-   for (i = 0; i < (U32)mDeviceMaps.size(); i++) {
-      if (mDeviceMaps[i]->deviceType == inDeviceType &&
-          mDeviceMaps[i]->deviceInst == inDeviceInst) {
-         pDeviceMap = mDeviceMaps[i];
-         break;
-      }
-   }
-
-   if (pDeviceMap == NULL)
-      return;
-
-   U32 realMods = inModifiers;
-   if (realMods & SI_SHIFT)
-      realMods |= SI_SHIFT;
-   if (realMods & SI_CTRL)
-      realMods |= SI_CTRL;
-   if (realMods & SI_ALT)
-      realMods |= SI_ALT;
-   if (realMods & SI_MAC_OPT)
-      realMods |= SI_MAC_OPT;
-
-   for (i = 0; i < (U32)pDeviceMap->nodeMap.size(); i++) {
-      if (pDeviceMap->nodeMap[i].modifiers == realMods &&
-          pDeviceMap->nodeMap[i].action    == inAction &&
-          ( (object != NULL) ? object == pDeviceMap->nodeMap[i].object : true )) 
-      {
-          dFree(pDeviceMap->nodeMap[i].makeConsoleCommand);
-          dFree(pDeviceMap->nodeMap[i].breakConsoleCommand);
-          pDeviceMap->nodeMap.erase(i);
-      }
-   }
-}
-
-//------------------------------------------------------------------------------
-const ActionMap::Node* ActionMap::findNode(const U32 inDeviceType, const U32 inDeviceInst,
-                    const U32 inModifiers,  const U32 inAction)
-{
-   // DMMTODO - Slow INITIAL implementation.  Replace with a faster version...
-   //
-   DeviceMap* pDeviceMap = NULL;
-   U32 i;
-   for (i = 0; i < (U32)mDeviceMaps.size(); i++)
-   {
-      if (mDeviceMaps[i]->deviceType == inDeviceType && mDeviceMaps[i]->deviceInst == inDeviceInst)
-      {
-         pDeviceMap = mDeviceMaps[i];
-         break;
-      }
-   }
-
-   if (pDeviceMap == NULL)
-      return NULL;
-
-   U32 realMods = inModifiers;
-   if (realMods & SI_SHIFT)
-      realMods |= SI_SHIFT;
-   if (realMods & SI_CTRL)
-      realMods |= SI_CTRL;
-   if (realMods & SI_ALT)
-      realMods |= SI_ALT;
-   if (realMods & SI_MAC_OPT)
-      realMods |= SI_MAC_OPT;
-
-   for (i = 0; i < (U32)pDeviceMap->nodeMap.size(); i++)
-   {
-      if (pDeviceMap->nodeMap[i].action == KEY_ANYKEY && pDeviceMap->nodeMap[i].modifiers == realMods && dIsDecentChar(inAction))
-         return &pDeviceMap->nodeMap[i];
-      else if (pDeviceMap->nodeMap[i].modifiers == realMods && pDeviceMap->nodeMap[i].action    == inAction)
-         return &pDeviceMap->nodeMap[i];
-   }
-
-   return NULL;
-}
-
-//------------------------------------------------------------------------------
-bool ActionMap::findBoundNode( const char* function, U32 &devMapIndex, U32 &nodeIndex )
-{
-   devMapIndex = 0;
-   nodeIndex = 0;
-   return nextBoundNode( function, devMapIndex, nodeIndex );
-}
-
-bool ActionMap::nextBoundNode( const char* function, U32 &devMapIndex, U32 &nodeIndex )
-{
-   // Loop through all of the existing nodes to find the one mapped to the
-   // given function:
-   for ( U32 i = devMapIndex; i < (U32)mDeviceMaps.size(); i++ )
-   {
-      const DeviceMap* dvcMap = mDeviceMaps[i];
-
-      for ( U32 j = nodeIndex; j < (U32)dvcMap->nodeMap.size(); j++ )
-      {
-         const Node* node = &dvcMap->nodeMap[j];
-         if ( !( node->flags & Node::BindCmd ) && ( dStricmp( function, node->consoleFunction ) == 0 ) )
-         {
-            devMapIndex = i;
-            nodeIndex = j;
-            return( true );
-         }
-      }
-
-      nodeIndex = 0;
-   }
-
-   return( false );
-}
-
-//------------------------------------------------------------------------------
-bool ActionMap::processUnbind(const char *device, const char *action, SimObject* object /*= NULL*/)
-{
-   U32 deviceType;
-   U32 deviceInst;
-
-   if(!getDeviceTypeAndInstance(device, deviceType, deviceInst))
-      return false;
-   EventDescriptor eventDescriptor;
-   if (!createEventDescriptor(action, &eventDescriptor))
-      return false;
-
-   removeNode(deviceType, deviceInst, eventDescriptor.flags,eventDescriptor.eventCode, object);
-   return true;
-}
-
-//------------------------------------------------------------------------------
-// This function is for the use of the control remapper.
-// It will only check against the console function (since all remappable commands are
-// bound using bind and not bindCmd).
-//
-const char* ActionMap::getBinding( const char* command )
-{
-   char* returnString = Con::getReturnBuffer( 1024 );
-   returnString[0] = 0;
-
-   char buffer[256];
-   char deviceBuffer[32];
-   char keyBuffer[64];
- 
-   U32 devMapIndex = 0, nodeIndex = 0;
-   while ( nextBoundNode( command, devMapIndex, nodeIndex ) )
-   {
-      const DeviceMap* deviceMap = mDeviceMaps[devMapIndex];
-
-      if ( getDeviceName( deviceMap->deviceType, deviceMap->deviceInst, deviceBuffer ) )
-      {
-         const Node* node = &deviceMap->nodeMap[nodeIndex];
-         const char* modifierString = getModifierString( node->modifiers );
-
-         if ( getKeyString( node->action, keyBuffer ) )
-         {
-            dSprintf( buffer, sizeof( buffer ), "%s\t%s%s", deviceBuffer, modifierString, keyBuffer );
-            if ( returnString[0] )
-               dStrcat( returnString, "\t" );
-            dStrcat( returnString, buffer );
-         }
-      }
-
-      ++nodeIndex;
-   }
-
-   return returnString;
-}
-
-//------------------------------------------------------------------------------
-// This function is for the use of the control remapper.
-// The intent of this function is to determine if the given event descriptor is already
-// bound in this action map.  If so, this function returns the command it is bound to.
-// If not, it returns NULL.
-//
-const char* ActionMap::getCommand( const char* device, const char* action )
-{
-    U32 deviceType;
-    U32 deviceInst;
-    if ( getDeviceTypeAndInstance( device, deviceType, deviceInst ) )
-    {
-        EventDescriptor eventDescriptor;
-        if ( createEventDescriptor( action, &eventDescriptor ) )
-        {
-            const ActionMap::Node* mapNode = findNode( deviceType, deviceInst, eventDescriptor.flags, eventDescriptor.eventCode );
-            if ( mapNode )
-            {
-                if ( mapNode->flags & Node::BindCmd )
-                {
-                    S32 bufferLen = dStrlen( mapNode->makeConsoleCommand ) + dStrlen( mapNode->breakConsoleCommand ) + 2;
-                    char* returnString = Con::getReturnBuffer( bufferLen );
-                    dSprintf( returnString, bufferLen, "%s\t%s",
-                            ( mapNode->makeConsoleCommand ? mapNode->makeConsoleCommand : "" ),
-                            ( mapNode->breakConsoleCommand ? mapNode->breakConsoleCommand : "" ) );					
-                    return( returnString );
-                }					
-                else
-                    return( mapNode->consoleFunction );					
-            }
-        }
-    }
-
-    return( "" );
-}
-
-//------------------------------------------------------------------------------
-// This function returns whether or not the mapping specified is inverted.
-// Obviously, this should only be used for axes.
-bool ActionMap::isInverted( const char* device, const char* action )
-{
-    U32 deviceType;
-    U32 deviceInst;
-    if ( getDeviceTypeAndInstance( device, deviceType, deviceInst ) )
-    {
-        EventDescriptor eventDescriptor;
-        if ( createEventDescriptor( action, &eventDescriptor ) )
-        {
-            const ActionMap::Node* mapNode = findNode( deviceType, deviceInst, eventDescriptor.flags, eventDescriptor.eventCode );
-            if ( mapNode )
-                return( mapNode->flags & Node::Inverted );
-        }
-    }
-
-    Con::errorf( "The input event specified by %s %s is not in this action map!", device, action );
-    return( false );
-}
-
-//------------------------------------------------------------------------------
-F32 ActionMap::getScale( const char* device, const char* action )
-{
-    U32 deviceType;
-    U32 deviceInst;
-    if ( getDeviceTypeAndInstance( device, deviceType, deviceInst ) )
-    {
-        EventDescriptor eventDescriptor;
-        if ( createEventDescriptor( action, &eventDescriptor ) )
-        {
-            const ActionMap::Node* mapNode = findNode( deviceType, deviceInst, eventDescriptor.flags, eventDescriptor.eventCode );
-            if ( mapNode )
-            {
-               if ( mapNode->flags & Node::HasScale )
-                   return( mapNode->scaleFactor );
-            else
-               return( 1.0f );
-         }
-        }
-    }
-
-    Con::errorf( "The input event specified by %s %s is not in this action map!", device, action );
-    return( 1.0f );
-}
-
-//------------------------------------------------------------------------------
-const char* ActionMap::getDeadZone( const char* device, const char* action )
-{
-    U32 deviceType;
-    U32 deviceInst;
-    if ( getDeviceTypeAndInstance( device, deviceType, deviceInst ) )
-    {
-        EventDescriptor eventDescriptor;
-        if ( createEventDescriptor( action, &eventDescriptor ) )
-        {
-            const ActionMap::Node* mapNode = findNode( deviceType, deviceInst, eventDescriptor.flags, eventDescriptor.eventCode );
-            if ( mapNode )
-            {
-               if ( mapNode->flags & Node::HasDeadZone )
-               {
-                   char buf[64];
-                   dSprintf( buf, sizeof( buf ), "%g %g", mapNode->deadZoneBegin, mapNode->deadZoneEnd );
-                   char* returnString = Con::getReturnBuffer( dStrlen( buf ) + 1 );
-                   dStrcpy( returnString, buf );
-                   return( returnString );
-                }
-                else
-                   return( "0 0" );				   		
-            }
-        }
-    }
-
-    Con::errorf( "The input event specified by %s %s is not in this action map!", device, action );
-    return( "" );
-}
-
-//------------------------------------------------------------------------------
-const char* ActionMap::buildActionString( const InputEvent* event )
-{
-    const char* modifierString = getModifierString( event->modifier );
-
-    char objectBuffer[64];
-    if ( !getKeyString( event->objInst, objectBuffer ) )
-        return( "" );
-
-    U32 returnLen = dStrlen( modifierString ) + dStrlen( objectBuffer ) + 2;	
-    char* returnString = Con::getReturnBuffer( returnLen );
-    dSprintf( returnString, returnLen - 1, "%s%s", modifierString, objectBuffer );
-    return( returnString );
-}
-
-//------------------------------------------------------------------------------
-bool ActionMap::getDeviceTypeAndInstance(const char *pDeviceName, U32 &deviceType, U32 &deviceInstance)
-{
-   U32 offset = 0;
-    
-   if (dStrnicmp(pDeviceName, "keyboard", dStrlen("keyboard")) == 0)
-   {
-      deviceType = KeyboardDeviceType;
-      offset = dStrlen("keyboard");
-   } 
-   else if (dStrnicmp(pDeviceName, "mouse", dStrlen("mouse")) == 0) 
-   {
-      deviceType = MouseDeviceType;
-      offset = dStrlen("mouse");
-   } 
-   else if (dStrnicmp(pDeviceName, "joystick", dStrlen("joystick")) == 0) 
-   {
-      deviceType = JoystickDeviceType;
-      offset = dStrlen("joystick");
-   } 
-   else if (dStrnicmp(pDeviceName, "accelerometer", dStrlen("accelerometer")) == 0) 
-   {
-      deviceType = AccelerometerDeviceType;
-      offset = dStrlen("accelerometer");
-   }
-   else if (dStrnicmp(pDeviceName, "gyroscope", dStrlen("gyroscope")) == 0)
-   {
-       deviceType = GyroscopeDeviceType;
-       offset = dStrlen("gyroscope");
-   }
-   else if (dStrnicmp(pDeviceName, "touchdevice", dStrlen("touchdevice")) == 0)
-   {
-       deviceType = ScreenTouchDeviceType;
-       offset = dStrlen("touchdevice");
-   }
-   else
-      return false;
-    
-   if (dStrlen(pDeviceName) > offset) 
-   {
-      const char* pInst = pDeviceName + offset;
-      S32 instNum = dAtoi(pInst);
-      
-      if (instNum < 0)
-         deviceInstance = 0;
-      else
-         deviceInstance = instNum;
-   } 
-   else 
-       deviceInstance = 0;
-    
-   return true;
-}
-
-//------------------------------------------------------------------------------
-bool ActionMap::getDeviceName(const U32 deviceType, const U32 deviceInstance, char* buffer)
-{
-   switch (deviceType)
-   {
-     case KeyboardDeviceType:
-      dStrcpy(buffer, "keyboard");
-      break;
-
-     case MouseDeviceType:
-      dSprintf(buffer, 16, "mouse%d", deviceInstance);
-      break;
-
-     case JoystickDeviceType:
-      dSprintf(buffer, 16, "joystick%d", deviceInstance);
-      break;
-    
-     case AccelerometerDeviceType:
-     dStrcpy(buffer, "accelerometer");
-     break;
-    
-     case GyroscopeDeviceType:
-     dStrcpy(buffer, "gyroscope");
-     break;
-     
-     case ScreenTouchDeviceType:
-     dStrcpy(buffer, "touchdevice");
-     break;
-           
-     default:
-      Con::errorf( "ActionMap::getDeviceName: unknown device type specified, %d (inst: %d)", deviceType, deviceInstance);
-      return false;
-   }
-
-   return true;
-}
-
-//------------------------------------------------------------------------------
-const char* ActionMap::getModifierString(const U32 modifiers)
-{
-    U32 realModifiers = modifiers;
-    if ( modifiers & SI_LSHIFT || modifiers & SI_RSHIFT )
-        realModifiers |= SI_SHIFT;
-    if ( modifiers & SI_LCTRL || modifiers & SI_RCTRL )
-        realModifiers |= SI_CTRL;
-    if ( modifiers & SI_LALT || modifiers & SI_RALT )
-        realModifiers |= SI_ALT;
-    if ( modifiers & SI_MAC_LOPT || modifiers & SI_MAC_ROPT )
-        realModifiers |= SI_MAC_OPT;
-
-   switch (realModifiers & (SI_SHIFT|SI_CTRL|SI_ALT|SI_MAC_OPT)) {
-#if defined(TORQUE_OS_OSX)
-      // optional code, to output alt as cmd on mac.
-      // interpreter sees them as the same...
-     case (SI_SHIFT|SI_CTRL|SI_ALT):
-      return "cmd-shift-ctrl ";
-
-     case (SI_SHIFT|SI_ALT):
-      return "cmd-shift ";
-
-     case (SI_CTRL|SI_ALT):
-      return "cmd-ctrl ";
-
-     case (SI_ALT):
-      return "cmd ";
-#else
-     case (SI_SHIFT|SI_CTRL|SI_ALT):
-      return "shift-ctrl-alt ";
-
-     case (SI_SHIFT|SI_ALT):
-      return "shift-alt ";
-
-     case (SI_CTRL|SI_ALT):
-      return "ctrl-alt ";
-
-     case (SI_ALT):
-      return "alt ";
-#endif
-     case (SI_SHIFT|SI_CTRL):
-      return "shift-ctrl ";
-
-     case (SI_SHIFT):
-      return "shift ";
-
-     case (SI_CTRL):
-      return "ctrl ";
-
-// plus new mac cases:
-     case (SI_ALT|SI_SHIFT|SI_CTRL|SI_MAC_OPT):
-      return "cmd-shift-ctrl-opt ";
-
-     case (SI_ALT|SI_SHIFT|SI_MAC_OPT):
-      return "cmd-shift-opt ";
-
-     case (SI_ALT|SI_CTRL|SI_MAC_OPT):
-      return "cmd-ctrl-opt ";
-
-     case (SI_ALT|SI_MAC_OPT):
-      return "cmd-opt ";
-
-     case (SI_SHIFT|SI_CTRL|SI_MAC_OPT):
-      return "shift-ctrl-opt ";
-
-     case (SI_SHIFT|SI_MAC_OPT):
-      return "shift-opt ";
-
-     case (SI_CTRL|SI_MAC_OPT):
-      return "ctrl-opt ";
-
-     case (SI_MAC_OPT):
-      return "opt ";
-      
-     case 0:
-      return "";
-
-     default:
-      AssertFatal(false, "Error, should never reach the default case in getModifierString");
-      return "";
-   }
-}
-
-//------------------------------------------------------------------------------
-bool ActionMap::getKeyString(const U32 action, char* buffer)
-{
-   U16 asciiCode = Input::getAscii(action, STATE_LOWER);
-
-//   if (action >= KEY_A && action <= KEY_Z) {
-//      buffer[0] = char(action - KEY_A + 'a');
-//      buffer[1] = '\0';
-//      return true;
-//   } else if (action >= KEY_0 && action <= KEY_9) {
-//      buffer[0] = char(action - KEY_0 + '0');
-//      buffer[1] = '\0';
-   if ( (asciiCode != 0) && dIsDecentChar((char)asciiCode))
-   {
-      for (U32 i = 0; gAsciiMap[i].asciiCode != 0xFFFF; i++) {
-         if (gAsciiMap[i].asciiCode == asciiCode)
-         {
-            dStrcpy(buffer, gAsciiMap[i].pDescription);
-            return true;
-         }
-      }
-      // Must not have found a string for that ascii code just record the char
-      buffer[0] = char(asciiCode);
-      buffer[1] = '\0';
-      return true;
-   }
-   else
-   {
-      if (action >= KEY_A && action <= KEY_Z)
-      {
-         buffer[0] = char(action - KEY_A + 'a');
-         buffer[1] = '\0';
-         return true;
-      }
-      else if (action >= KEY_0 && action <= KEY_9) {
-         buffer[0] = char(action - KEY_0 + '0');
-         buffer[1] = '\0';
-         return true;
-      }
-      for (U32 i = 0; gVirtualMap[i].code != 0xFFFFFFFF; i++) {
-         if (gVirtualMap[i].code == action) {
-            dStrcpy(buffer, gVirtualMap[i].pDescription);
-            return true;
-         }
-      }
-   }
-
-   Con::errorf( "ActionMap::getKeyString: no string for action %d", action );
-   return false;
-}
-
-//--------------------------------------------------------------------------
-bool ActionMap::processBindCmd(const char *device, const char *action, const char *makeCmd, const char *breakCmd)
-{
-   U32 deviceType;
-   U32 deviceInst;
-
-   if(!getDeviceTypeAndInstance(device, deviceType, deviceInst))
-   {
-      Con::printf("processBindCmd: unknown device: %s", device);
-      return false;
-   }
-
-   // Ok, we now have the deviceType and instance.  Create an event descriptor
-   //  for the bind...
-   //
-   EventDescriptor eventDescriptor;
-   if (createEventDescriptor(action, &eventDescriptor) == false) {
-      Con::printf("Could not create a description for binding: %s", action);
-      return false;
-   }
-
-   // SI_POV == SI_MOVE, and the POV works fine with bindCmd, so we have to add these manually.
-   if( ( eventDescriptor.eventCode == SI_XAXIS )    ||
-       ( eventDescriptor.eventCode == SI_YAXIS )    ||
-       ( eventDescriptor.eventCode == SI_ZAXIS )    ||
-       ( eventDescriptor.eventCode == SI_RXAXIS )   ||
-       ( eventDescriptor.eventCode == SI_RYAXIS )   ||
-       ( eventDescriptor.eventCode == SI_RZAXIS )   ||
-       ( eventDescriptor.eventCode == SI_SLIDER )   ||
-       ( eventDescriptor.eventCode == SI_XPOV )     ||
-       ( eventDescriptor.eventCode == SI_YPOV )     ||
-       ( eventDescriptor.eventCode == SI_XPOV2 )    ||
-       ( eventDescriptor.eventCode == SI_YPOV2 )    ||
-       ( eventDescriptor.eventCode == SI_ACCELX )   ||
-       ( eventDescriptor.eventCode == SI_ACCELY )   ||
-       ( eventDescriptor.eventCode == SI_ACCELZ )   ||
-       ( eventDescriptor.eventCode == SI_GRAVX )    ||
-       ( eventDescriptor.eventCode == SI_GRAVY )    ||
-       ( eventDescriptor.eventCode == SI_GRAVZ )    ||
-       ( eventDescriptor.eventCode == SI_GYROX )    ||
-       ( eventDescriptor.eventCode == SI_GYROY )    ||
-       ( eventDescriptor.eventCode == SI_GYROZ )    ||
-       ( eventDescriptor.eventCode == SI_YAW )      ||
-       ( eventDescriptor.eventCode == SI_PITCH )    ||
-       ( eventDescriptor.eventCode == SI_ROLL ) )
-   {
-      Con::warnf( "ActionMap::processBindCmd - Cannot use 'bindCmd' with a move event type. Use 'bind' instead." );
-      return false;
-   }
-
-   // Create the full bind entry, and place it in the map
-   //
-   // DMMTODO
-   Node* pBindNode = getNode(deviceType, deviceInst,
-                             eventDescriptor.flags,
-                             eventDescriptor.eventCode);
-
-   pBindNode->flags           = Node::BindCmd;
-   pBindNode->deadZoneBegin   = 0;
-   pBindNode->deadZoneEnd     = 0;
-   pBindNode->scaleFactor     = 1;
-   if(makeCmd[0])
-      pBindNode->makeConsoleCommand = dStrdup(makeCmd);
-   else
-      pBindNode->makeConsoleCommand = dStrdup("");
-
-   if(breakCmd[0])
-      pBindNode->breakConsoleCommand = dStrdup(breakCmd);
-   else
-      pBindNode->breakConsoleCommand = dStrdup("");
-   return true;
-}
-
-//------------------------------------------------------------------------------
-bool ActionMap::processBind(const U32 argc, const char** argv, SimObject* object)
-{
-   // Ok, the bind will come in the following format:
-   //  [device] [key or button] <[param spec] [param] ...> [fnName]
-   //
-   const char* pDeviceName = argv[0];
-   const char* pEvent      = argv[1];
-   const char* pFnName     = argv[argc - 1];
-
-   // Determine the device
-   U32 deviceType;
-   U32 deviceInst;
-
-   if(!getDeviceTypeAndInstance(argv[0], deviceType, deviceInst))
-   {
-      Con::printf("processBind: unknown device: %s", pDeviceName);
-      return false;
-   }
-
-   // Ok, we now have the deviceType and instance.  Create an event descriptor
-   //  for the bind...
-   //
-   EventDescriptor eventDescriptor;
-   if (createEventDescriptor(pEvent, &eventDescriptor) == false) {
-      Con::printf("Could not create a description for binding: %s", pEvent);
-      return false;
-   }
-
-   // Event has now been described, and device determined.  we need now to extract
-   //  any modifiers that the action map will apply to incoming events before
-   //  calling the bound function...
-   //
-   // DMMTODO
-   U32 assignedFlags = 0;
-   F32 deadZoneBegin = 0.0f;
-   F32 deadZoneEnd   = 0.0f;
-   F32 scaleFactor   = 1.0f;
-
-   if (argc != 3) {
-      // We have the following: "[DSIR]" [deadZone] [scale]
-      //
-      const char* pSpec = argv[2];
-
-      for (U32 i = 0; pSpec[i] != '\0'; i++) {
-         switch (pSpec[i]) {
-           case 'r': case 'R':
-            assignedFlags |= Node::HasScale;
-            break;
-           case 's': case 'S':
-            assignedFlags |= Node::HasScale;
-            break;
-           case 'd': case 'D':
-            assignedFlags |= Node::HasDeadZone;
-            break;
-           case 'i': case 'I':
-            assignedFlags |= Node::Inverted;
-            break;
-
-           default:
-            AssertFatal(false, avar("Misunderstood specifier in bind (spec string: %s)",
-                                    pSpec));
-         }
-      }
-
-      // Ok, we have the flags.  Scan the dead zone and scale, if any.
-      //
-      U32 curArg = 3;
-      if (assignedFlags & Node::HasDeadZone) {
-         dSscanf(argv[curArg], "%g %g", &deadZoneBegin, &deadZoneEnd);
-         curArg++;
-      }
-      if (assignedFlags & Node::HasScale) {
-         scaleFactor = dAtof(argv[curArg]);
-         curArg++;
-      }
-
-      if (curArg != (argc - 1)) {
-         AssertFatal(curArg == (argc - 1), "error in bind spec somewhere...");
-         Con::printf("Improperly specified bind for key: %s", argv[2]);
-         return false;
-      }
-   }
-
-   // Ensure that the console function is properly specified?
-   //
-   // DMMTODO
-
-   // Create the full bind entry, and place it in the map
-   //
-   // DMMTODO
-   Node* pBindNode = getNode(deviceType, deviceInst,
-                             eventDescriptor.flags,
-                             eventDescriptor.eventCode, object);
-
-   pBindNode->flags           = assignedFlags;
-   pBindNode->deadZoneBegin   = deadZoneBegin;
-   pBindNode->deadZoneEnd     = deadZoneEnd;
-   pBindNode->scaleFactor     = scaleFactor;
-   pBindNode->object          = object;
-   pBindNode->consoleFunction = StringTable->insert(pFnName);
-
-   return true;
-}
-
-//------------------------------------------------------------------------------
-bool ActionMap::processAction(const InputEvent* pEvent)
-{
-   static const char *argv[4];
-
-   if (pEvent->action == SI_TOUCH) // Touches
-   {
-       static const char *args[4];
-       
-       const Node* pNode = findNode(pEvent->deviceType, pEvent->deviceInst, pEvent->modifier, pEvent->objInst);
-       
-       if (pNode == NULL) 
-       {
-           // Check to see if we clear the modifiers, do we find an action?
-           if (pEvent->modifier != 0)
-               pNode = findNode(pEvent->deviceType, pEvent->deviceInst, 0, pEvent->objInst);
-           
-           if (pNode == NULL)
-               return false;
-       }
-       
-       // "Do nothing" bind:
-       if ( !pNode->consoleFunction[0] )
-           return( true );
-       
-       // Ok, we're all set up, call the function.
-       args[0] = pNode->consoleFunction;
-       args[1] = pEvent->touchIDs; //Con::getReturnBuffer(pEvent->touchIDs);
-       args[2] = pEvent->touchesX; //Con::getReturnBuffer(pEvent->touchesX);
-       args[3] = pEvent->touchesY; //Con::getReturnBuffer(pEvent->touchesY);
-       
-       if (pNode->object)
-           Con::executef(pNode->object, 2, args[0], args[1], args[2], args[3]);
-       else
-           Con::execute(4, args);
-       
-       return true;
-   }
-   else if (pEvent->action == SI_MAKE) // KEYBOARD/BUTTON DOWN EVENT
-   {
-      const Node* pNode = findNode(pEvent->deviceType, pEvent->deviceInst, pEvent->modifier, pEvent->objInst);
-
-      if (pNode == NULL) 
-      {
-         // Check to see if we clear the modifiers, do we find an action?
-         if (pEvent->modifier != 0)
-            pNode = findNode(pEvent->deviceType, pEvent->deviceInst, 0, pEvent->objInst);
-
-         if (pNode == NULL)
-            return false;
-      }
-
-      // Whadda ya know, we have this bound.  Set up, and call the console
-      //  function associated with it...
-      //
-      F32 value = pEvent->fValue;
-       
-      if (pNode->flags & Node::Ranged)
-      {
-          value = (value * 2.0f) - 1.0f;
-         
-          if (pNode->flags & Node::Inverted)
-              value *= -1.0f;
-      }
-      else
-      {
-          if (pNode->flags & Node::Inverted)
-              value = 1.0f - value;
-      }
-
-      if (pNode->flags & Node::HasScale)
-          value *= pNode->scaleFactor;
-
-      if (pNode->flags & Node::HasDeadZone)
-         if (value >= pNode->deadZoneBegin && value <= pNode->deadZoneEnd)
-             value = 0.0f;
-
-      // Ok, we're all set up, call the function.
-      if(pNode->flags & Node::BindCmd)
-      {
-         // it's a bind command
-         if(pNode->makeConsoleCommand)
-            Con::evaluate(pNode->makeConsoleCommand);
-      }
-      else if ( pNode->consoleFunction[0] )
-      {
-         argv[0] = pNode->consoleFunction;
-         argv[1] = Con::getFloatArg(value);
-         if (pNode->object)
-            Con::executef(pNode->object, 2, argv[0], argv[1]);
-         else
-            Con::execute(2, argv);
-      }
-
-      // [neo, 5/13/2007 - #3109]
-      // The execs/evaluate above could have called reentrant script code which made calls to
-      // bindCmd() etc, channging the node map underneath us. If enough nodes were added then
-      // the node map vector would realloc, with the result that pNode would then be pointing 
-      // at garbage and cause a crash when passed to enterBreakEvent() below. So we just look
-      // it up again to be safe. This is not needed in the other cases below as we return right
-      // after the execs and don't use pNode again.
-      pNode = findNode( pEvent->deviceType, pEvent->deviceInst, pEvent->modifier, pEvent->objInst );
-
-      if( pNode == NULL )
-         return true; // We already called any bound methods/functions so our job is done
-
-      //
-      // And enter the break into the table if this is a make event...
-      enterBreakEvent(pEvent, pNode);
-
-      return true;
-   } 
-   else if (pEvent->action == SI_MOVE) // MOUSE/JOYSTICK MOVE EVENT
-   {
-      if (pEvent->deviceType == MouseDeviceType)
-      {
-         const Node* pNode = findNode(pEvent->deviceType, pEvent->deviceInst, pEvent->modifier, pEvent->objType);
-
-         if (pNode == NULL)
-         {
-            // Check to see if we clear the modifiers, do we find an action?
-            if (pEvent->modifier != 0)
-               pNode = findNode(pEvent->deviceType, pEvent->deviceInst,
-                                0,                  pEvent->objType);
-
-            if (pNode == NULL)
-               return false;
-         }
-
-         // "Do nothing" bind:
-         if ( !pNode->consoleFunction[0] )
-            return( true );
-
-         // Whadda ya know, we have this bound.  Set up, and call the console
-         //  function associated with it.  Mouse events ignore range and dead
-         //  zone params.
-         //
-         F32 value = pEvent->fValue;
-         
-         if (pNode->flags & Node::Inverted)
-            value *= -1.0f;
-         if (pNode->flags & Node::HasScale)
-            value *= pNode->scaleFactor;
-
-         // Ok, we're all set up, call the function.
-         argv[0] = pNode->consoleFunction;
-         argv[1] = Con::getFloatArg(value);
-          
-         if (pNode->object)
-            Con::executef(pNode->object, 2, argv[0], argv[1]);
-         else
-            Con::execute(2, argv);
-
-         return true;
-      } 
-      else
-      {
-         // Joystick events...
-         const Node* pNode = findNode( pEvent->deviceType, pEvent->deviceInst,
-                                       pEvent->modifier,   pEvent->objType );
-
-         if ( pNode == NULL )
-         {
-            // Check to see if we clear the modifiers, do we find an action?
-            if (pEvent->modifier != 0)
-               pNode = findNode( pEvent->deviceType, pEvent->deviceInst, 0, pEvent->objType );
-
-               if ( pNode == NULL )
-                  return false;
-         }
-
-         // "Do nothing" bind:
-         if ( !pNode->consoleFunction[0] )
-            return( true );
-
-         // Whadda ya know, we have this bound.  Set up, and call the console
-         //  function associated with it.  Joystick move events are the same as mouse
-         //  move events except that they don't ignore dead zone.
-         //
-         F32 value = pEvent->fValue;
-          
-         if ( pNode->flags & Node::Inverted )
-            value *= -1.0f;
-
-         if ( pNode->flags & Node::HasScale )
-            value *= pNode->scaleFactor;
-
-         if ( pNode->flags & Node::HasDeadZone )
-            if ( value >= pNode->deadZoneBegin && value <= pNode->deadZoneEnd )
-               value = 0.0f;
-
-         // Ok, we're all set up, call the function.
-         argv[0] = pNode->consoleFunction;
-         argv[1] = Con::getFloatArg( value );
-          
-         if (pNode->object)
-            Con::executef(pNode->object, 2, argv[0], argv[1]);
-         else
-            Con::execute(2, argv);
-
-         return true;
-      }
-   }
-   else if (pEvent->action == SI_MOTION) // iOS MOTION EVENT
-   {
-       // iOS Accelerometer, Gyroscope and DeviceMotion processing
-       // Currently, this is identical to the joystick handling.
-       
-       // This was copied over into its own section because this will
-       // give us a dedicated section to tweak processing based on iOS specific
-       // devices. No point in trying to mangle joystick code any further
-       const Node* pNode = findNode( pEvent->deviceType, pEvent->deviceInst, pEvent->modifier,   pEvent->objType );
-       
-       if ( pNode == NULL )
-       {
-           // Check to see if we clear the modifiers, do we find an action?
-           if (pEvent->modifier != 0)
-               pNode = findNode( pEvent->deviceType, pEvent->deviceInst, 0, pEvent->objType );
-           
-           if ( pNode == NULL )
-               return false;
-       }
-       
-       // "Do nothing" bind:
-       if ( !pNode->consoleFunction[0] )
-           return( true );
-       
-       F32 value = pEvent->fValue;
-       
-       if ( pNode->flags & Node::Inverted )
-           value *= -1.0f;
-       
-       if ( pNode->flags & Node::HasScale )
-           value *= pNode->scaleFactor;
-       
-       if ( pNode->flags & Node::HasDeadZone )
-           if ( value >= pNode->deadZoneBegin && value <= pNode->deadZoneEnd )
-               value = 0.0f;
-       
-       // Ok, we're all set up, call the function.
-       argv[0] = pNode->consoleFunction;
-       argv[1] = Con::getFloatArg( value );
-       
-       if (pNode->object)
-           Con::executef(pNode->object, 2, argv[0], argv[1]);
-       else
-           Con::execute(2, argv);
-       
-       return true;
-   }
-   else if (pEvent->action == SI_BREAK) // UP EVENT
-   {
-      return checkBreakTable(pEvent);
-   }
-
-   return false;
-}
-
-//------------------------------------------------------------------------------
-void ActionMap::enterBreakEvent(const InputEvent* pEvent, const Node* pNode)
-{
-   // There aren't likely to be many breaks outstanding at any one given time,
-   //  so a simple linear search is probably sufficient.  Note that the break table
-   //  is static to the class, all breaks are directed to the action map that received
-   //  the make.
-   //
-   S32 entry = -1;
-   for (U32 i = 0; i < (U32)smBreakTable.size(); i++) {
-      if (smBreakTable[i].deviceType == U32(pEvent->deviceType) &&
-          smBreakTable[i].deviceInst == U32(pEvent->deviceInst) &&
-          smBreakTable[i].objInst    == U32(pEvent->objInst)) {
-         // Match.
-         entry = i;
-         break;
-      }
-   }
-   if (entry == -1) {
-      smBreakTable.increment();
-      entry = smBreakTable.size() - 1;
-
-      smBreakTable[entry].deviceType = pEvent->deviceType;
-      smBreakTable[entry].deviceInst = pEvent->deviceInst;
-      smBreakTable[entry].objInst    = pEvent->objInst;
-   }
-
-   // Ok, we now have the entry, and know that the device desc. and the objInst match.
-   //  Copy out the node information...
-   //
-   smBreakTable[entry].object = pNode->object;
-   // [neo, 5/7/2007 - #2975]
-   // object above can be deleted in between a make/break and so object will point
-   // to turfed memory and crash. To keep things simple we just store id as well so
-   // we can look it up to validate object ref.
-   smBreakTable[entry].objectId = pNode->object ? pNode->object->getId() : 0;
-
-   smBreakTable[entry].consoleFunction = pNode->consoleFunction;
-
-   if(pNode->breakConsoleCommand)
-      smBreakTable[entry].breakConsoleCommand = dStrdup(pNode->breakConsoleCommand);
-   else
-      smBreakTable[entry].breakConsoleCommand = NULL;
-
-   smBreakTable[entry].flags         = pNode->flags;
-   smBreakTable[entry].deadZoneBegin = pNode->deadZoneBegin;
-   smBreakTable[entry].deadZoneEnd   = pNode->deadZoneEnd;
-   smBreakTable[entry].scaleFactor   = pNode->scaleFactor;
-}
-
-//------------------------------------------------------------------------------
-bool ActionMap::checkBreakTable(const InputEvent* pEvent)
-{
-   for (U32 i = 0; i < (U32)smBreakTable.size(); i++) {
-      if (smBreakTable[i].deviceType == U32(pEvent->deviceType) &&
-          smBreakTable[i].deviceInst == U32(pEvent->deviceInst) &&
-          smBreakTable[i].objInst    == U32(pEvent->objInst)) {
-         // Match.  Issue the break event...
-         //
-         F32 value = pEvent->fValue;
-         if (smBreakTable[i].flags & Node::Ranged) {
-            value = (value * 2.0f) - 1.0f;
-            if (smBreakTable[i].flags & Node::Inverted)
-               value *= -1.0f;
-         } else {
-            if (smBreakTable[i].flags & Node::Inverted)
-               value = 1.0f - value;
-         }
-
-         if (smBreakTable[i].flags & Node::HasScale)
-            value *= smBreakTable[i].scaleFactor;
-
-         if (smBreakTable[i].flags & Node::HasDeadZone)
-            if (value >= smBreakTable[i].deadZoneBegin &&
-                value <= smBreakTable[i].deadZoneEnd)
-               value = 0.0f;
-
-         // Ok, we're all set up, call the function.
-         if(smBreakTable[i].consoleFunction)
-         {
-            if ( smBreakTable[i].consoleFunction[0] )
-            {
-               static const char *argv[2];
-               argv[0] = smBreakTable[i].consoleFunction;
-               argv[1] = Con::getFloatArg(value);
-                              
-               if( smBreakTable[i].object )
-               {
-                  // [neo, 5/7/2007 - #2975]
-                  // object above can be deleted in between a make/break and so object will point
-                  // to turfed memory and crash. To keep things simple we just store id as well so
-                  // we can look it up to validate object ref.
-                  if( smBreakTable[i].objectId > 0 && Sim::findObject( smBreakTable[i].objectId ) )
-                     Con::executef(smBreakTable[i].object, 2, argv[0], argv[1]);
-               }
-               else
-                  Con::execute(2, argv);
-            }
-         }
-         else if(smBreakTable[i].breakConsoleCommand)
-         {
-            Con::evaluate(smBreakTable[i].breakConsoleCommand);
-            dFree(smBreakTable[i].breakConsoleCommand);
-         }
-         smBreakTable.erase(i);
-         return true;
-      }
-   }
-
-   return false;
-}
-
-//------------------------------------------------------------------------------
-bool ActionMap::handleEvent(const InputEvent* pEvent)
-{
-   // Interate through the ActionMapSet until we get a map that
-   //  handles the event or we run out of maps...
-   //
-   SimSet* pActionMapSet = Sim::getActiveActionMapSet();
-   AssertFatal(pActionMapSet && pActionMapSet->size() != 0,
-               "error, no ActiveMapSet or no global action map...");
-
-   for (SimSet::iterator itr = pActionMapSet->end() - 1;
-        itr > pActionMapSet->begin(); itr--) {
-      ActionMap* pMap = static_cast<ActionMap*>(*itr);
-      if (pMap->processAction(pEvent) == true)
-         return true;
-   }
-
-   return false;
-}
-
-//------------------------------------------------------------------------------
-bool ActionMap::handleEventGlobal(const InputEvent* pEvent)
-{
-   // Interate through the ActionMapSet until we get a map that
-   //  handles the event or we run out of maps...
-   //
-   SimSet* pActionMapSet = Sim::getActiveActionMapSet();
-   AssertFatal(pActionMapSet && pActionMapSet->size() != 0,
-               "error, no ActiveMapSet or no global action map...");
-
-   return ((ActionMap*)pActionMapSet->first())->processAction(pEvent);
-}
-
-//------------------------------------------------------------------------------
-//-------------------------------------- Key code to string mapping
-//                                        TODO: Add most obvious aliases...
-//
-CodeMapping gVirtualMap[] =
-{
-   //-------------------------------------- KEYBOARD EVENTS
-   //
-   { "backspace",     SI_KEY,    KEY_BACKSPACE   },
-   { "tab",           SI_KEY,    KEY_TAB         },
-
-   { "return",        SI_KEY,    KEY_RETURN      },
-   { "enter",         SI_KEY,    KEY_RETURN      },
-
-   { "shift",         SI_KEY,    KEY_SHIFT       },
-   { "ctrl",          SI_KEY,    KEY_CONTROL     },
-   { "alt",           SI_KEY,    KEY_ALT         },
-   { "pause",         SI_KEY,    KEY_PAUSE       },
-   { "capslock",      SI_KEY,    KEY_CAPSLOCK    },
-
-   { "escape",        SI_KEY,    KEY_ESCAPE      },
-
-   { "space",         SI_KEY,    KEY_SPACE       },
-   { "pagedown",      SI_KEY,    KEY_PAGE_DOWN   },
-   { "pageup",        SI_KEY,    KEY_PAGE_UP     },
-   { "end",           SI_KEY,    KEY_END         },
-   { "home",          SI_KEY,    KEY_HOME        },
-   { "left",          SI_KEY,    KEY_LEFT        },
-   { "up",            SI_KEY,    KEY_UP          },
-   { "right",         SI_KEY,    KEY_RIGHT       },
-   { "down",          SI_KEY,    KEY_DOWN        },
-   { "print",         SI_KEY,    KEY_PRINT       },
-   { "insert",        SI_KEY,    KEY_INSERT      },
-   { "delete",        SI_KEY,    KEY_DELETE      },
-   { "help",          SI_KEY,    KEY_HELP        },
-
-   { "win_lwindow",   SI_KEY,    KEY_WIN_LWINDOW },
-   { "win_rwindow",   SI_KEY,    KEY_WIN_RWINDOW },
-   { "win_apps",      SI_KEY,    KEY_WIN_APPS    },
-
-   { "cmd",           SI_KEY,    KEY_ALT         },
-   { "opt",           SI_KEY,    KEY_MAC_OPT     },
-   { "lopt",          SI_KEY,    KEY_MAC_LOPT    },
-   { "ropt",          SI_KEY,    KEY_MAC_ROPT    },
-
-   { "numpad0",       SI_KEY,    KEY_NUMPAD0     },
-   { "numpad1",       SI_KEY,    KEY_NUMPAD1     },
-   { "numpad2",       SI_KEY,    KEY_NUMPAD2     },
-   { "numpad3",       SI_KEY,    KEY_NUMPAD3     },
-   { "numpad4",       SI_KEY,    KEY_NUMPAD4     },
-   { "numpad5",       SI_KEY,    KEY_NUMPAD5     },
-   { "numpad6",       SI_KEY,    KEY_NUMPAD6     },
-   { "numpad7",       SI_KEY,    KEY_NUMPAD7     },
-   { "numpad8",       SI_KEY,    KEY_NUMPAD8     },
-   { "numpad9",       SI_KEY,    KEY_NUMPAD9     },
-   { "numpadmult",    SI_KEY,    KEY_MULTIPLY    },
-   { "numpadadd",     SI_KEY,    KEY_ADD         },
-   { "numpadsep",     SI_KEY,    KEY_SEPARATOR   },
-   { "numpadminus",   SI_KEY,    KEY_SUBTRACT    },
-   { "numpaddecimal", SI_KEY,    KEY_DECIMAL     },
-   { "numpaddivide",  SI_KEY,    KEY_DIVIDE      },
-   { "numpadenter",   SI_KEY,    KEY_NUMPADENTER },
-
-   { "f1",            SI_KEY,    KEY_F1          },
-   { "f2",            SI_KEY,    KEY_F2          },
-   { "f3",            SI_KEY,    KEY_F3          },
-   { "f4",            SI_KEY,    KEY_F4          },
-   { "f5",            SI_KEY,    KEY_F5          },
-   { "f6",            SI_KEY,    KEY_F6          },
-   { "f7",            SI_KEY,    KEY_F7          },
-   { "f8",            SI_KEY,    KEY_F8          },
-   { "f9",            SI_KEY,    KEY_F9          },
-   { "f10",           SI_KEY,    KEY_F10         },
-   { "f11",           SI_KEY,    KEY_F11         },
-   { "f12",           SI_KEY,    KEY_F12         },
-   { "f13",           SI_KEY,    KEY_F13         },
-   { "f14",           SI_KEY,    KEY_F14         },
-   { "f15",           SI_KEY,    KEY_F15         },
-   { "f16",           SI_KEY,    KEY_F16         },
-   { "f17",           SI_KEY,    KEY_F17         },
-   { "f18",           SI_KEY,    KEY_F18         },
-   { "f19",           SI_KEY,    KEY_F19         },
-   { "f20",           SI_KEY,    KEY_F20         },
-   { "f21",           SI_KEY,    KEY_F21         },
-   { "f22",           SI_KEY,    KEY_F22         },
-   { "f23",           SI_KEY,    KEY_F23         },
-   { "f24",           SI_KEY,    KEY_F24         },
-
-   { "numlock",       SI_KEY,    KEY_NUMLOCK     },
-   { "scrolllock",    SI_KEY,    KEY_SCROLLLOCK  },
-
-   { "lshift",        SI_KEY,    KEY_LSHIFT      },
-   { "rshift",        SI_KEY,    KEY_RSHIFT      },
-   { "lcontrol",      SI_KEY,    KEY_LCONTROL    },
-   { "rcontrol",      SI_KEY,    KEY_RCONTROL    },
-   { "lalt",          SI_KEY,    KEY_LALT        },
-   { "ralt",          SI_KEY,    KEY_RALT        },
-   { "tilde",         SI_KEY,    KEY_TILDE       },
-
-   { "minus",         SI_KEY,    KEY_MINUS       },
-   { "equals",        SI_KEY,    KEY_EQUALS      },
-   { "lbracket",      SI_KEY,    KEY_LBRACKET    },
-   { "rbracket",      SI_KEY,    KEY_RBRACKET    },
-   { "backslash",     SI_KEY,    KEY_BACKSLASH   },
-   { "semicolon",     SI_KEY,    KEY_SEMICOLON   },
-   { "apostrophe",    SI_KEY,    KEY_APOSTROPHE  },
-   { "comma",         SI_KEY,    KEY_COMMA       },
-   { "period",        SI_KEY,    KEY_PERIOD      },
-   { "slash",         SI_KEY,    KEY_SLASH       },
-   { "lessthan",      SI_KEY,    KEY_OEM_102     },
-
-   //-------------------------------------- BUTTON EVENTS
-   // Joystick/Mouse buttons
-   { "button0",       SI_BUTTON, KEY_BUTTON0    },
-   { "button1",       SI_BUTTON, KEY_BUTTON1    },
-   { "button2",       SI_BUTTON, KEY_BUTTON2    },
-   { "button3",       SI_BUTTON, KEY_BUTTON3    },
-   { "button4",       SI_BUTTON, KEY_BUTTON4    },
-   { "button5",       SI_BUTTON, KEY_BUTTON5    },
-   { "button6",       SI_BUTTON, KEY_BUTTON6    },
-   { "button7",       SI_BUTTON, KEY_BUTTON7    },
-   { "button8",       SI_BUTTON, KEY_BUTTON8    },
-   { "button9",       SI_BUTTON, KEY_BUTTON9    },
-   { "button10",      SI_BUTTON, KEY_BUTTON10   },
-   { "button11",      SI_BUTTON, KEY_BUTTON11   },
-   { "button12",      SI_BUTTON, KEY_BUTTON12   },
-   { "button13",      SI_BUTTON, KEY_BUTTON13   },
-   { "button14",      SI_BUTTON, KEY_BUTTON14   },
-   { "button15",      SI_BUTTON, KEY_BUTTON15   },
-   { "button16",      SI_BUTTON, KEY_BUTTON16   },
-   { "button17",      SI_BUTTON, KEY_BUTTON17   },
-   { "button18",      SI_BUTTON, KEY_BUTTON18   },
-   { "button19",      SI_BUTTON, KEY_BUTTON19   },
-   { "button20",      SI_BUTTON, KEY_BUTTON20   },
-   { "button21",      SI_BUTTON, KEY_BUTTON21   },
-   { "button22",      SI_BUTTON, KEY_BUTTON22   },
-   { "button23",      SI_BUTTON, KEY_BUTTON23   },
-   { "button24",      SI_BUTTON, KEY_BUTTON24   },
-   { "button25",      SI_BUTTON, KEY_BUTTON25   },
-   { "button26",      SI_BUTTON, KEY_BUTTON26   },
-   { "button27",      SI_BUTTON, KEY_BUTTON27   },
-   { "button28",      SI_BUTTON, KEY_BUTTON28   },
-   { "button29",      SI_BUTTON, KEY_BUTTON29   },
-   { "button30",      SI_BUTTON, KEY_BUTTON30   },
-   { "button31",      SI_BUTTON, KEY_BUTTON31   },
-
-   //-------------------------------------- MOVE EVENTS
-   // Mouse/Joystick axes:
-   { "xaxis",         SI_MOVE,   SI_XAXIS       },
-   { "yaxis",         SI_MOVE,   SI_YAXIS       },
-   { "zaxis",         SI_MOVE,   SI_ZAXIS       },
-   { "rxaxis",        SI_MOVE,   SI_RXAXIS      },
-   { "ryaxis",        SI_MOVE,   SI_RYAXIS      },
-   { "rzaxis",        SI_MOVE,   SI_RZAXIS      },
-   { "slider",        SI_MOVE,   SI_SLIDER      },
-
-   //-------------------------------------- POV EVENTS
-   // Joystick POV:
-   { "xpov",          SI_POV,    SI_XPOV         },
-   { "ypov",          SI_POV,    SI_YPOV         },
-   { "upov",          SI_POV,    SI_UPOV         },
-   { "dpov",          SI_POV,    SI_DPOV         },
-   { "lpov",          SI_POV,    SI_LPOV         },
-   { "rpov",          SI_POV,    SI_RPOV         },
-   { "xpov2",         SI_POV,    SI_XPOV2        },
-   { "ypov2",         SI_POV,    SI_YPOV2        },
-   { "upov2",         SI_POV,    SI_UPOV2        },
-   { "dpov2",         SI_POV,    SI_DPOV2        },
-   { "lpov2",         SI_POV,    SI_LPOV2        },
-   { "rpov2",         SI_POV,    SI_RPOV2        },
-
-   //-------------------------------------- MOTION EVENTS
-   // Accelerometer/Gyroscope axes:
-   { "accelx",        SI_MOTION,    SI_ACCELX    },
-   { "accely",        SI_MOTION,    SI_ACCELY    },
-   { "accelz",        SI_MOTION,    SI_ACCELZ    },
-   { "gravityx",      SI_MOTION,    SI_GRAVX     },
-   { "gravityy",      SI_MOTION,    SI_GRAVY     },
-   { "gravityz",      SI_MOTION,    SI_GRAVZ     },
-   { "gyrox",         SI_MOTION,    SI_GYROX     },
-   { "gyroy",         SI_MOTION,    SI_GYROY     },
-   { "gyroz",         SI_MOTION,    SI_GYROZ     },
-   { "yaw",           SI_MOTION,    SI_YAW       },
-   { "pitch",         SI_MOTION,    SI_PITCH     },
-   { "roll",          SI_MOTION,    SI_ROLL      },
-
-   { "touchdown",     SI_TOUCH,    SI_TOUCHDOWN  },
-   { "touchup",       SI_TOUCH,    SI_TOUCHUP    },
-   { "touchmove",     SI_TOUCH,    SI_TOUCHMOVE  },
-   { "gesture",       SI_TOUCH,    SI_GESTURE    },
-
-   //-------------------------------------- MISCELLANEOUS EVENTS
-   //
-
-   { "anykey",        SI_KEY,      KEY_ANYKEY },
-   { "nomatch",       SI_UNKNOWN,  0xFFFFFFFF }
-};
-
-AsciiMapping gAsciiMap[] =
-{
-   //--- KEYBOARD EVENTS
-   //
-   { "space",           0x0020 },
-   //{ "exclamation",     0x0021 },
-   { "doublequote",     0x0022 },
-   //{ "pound",           0x0023 },
-   //{ "ampersand",       0x0026 },
-   { "apostrophe",      0x0027 },
-   //{ "lparen",          0x0028 },
-   //{ "rparen",          0x0029 },
-   { "comma",           0x002c },
-   { "minus",           0x002d },
-   { "period",          0x002e },
-   //{ "slash",           0x002f },
-   //{ "colon",           0x003a },
-   //{ "semicolon",       0x003b },
-   //{ "lessthan",        0x003c },
-   //{ "equals",          0x003d },
-   //{ "morethan",        0x003e },
-   //{ "lbracket",        0x005b },
-   { "backslash",       0x005c },
-   //{ "rbracket",        0x005d },
-   //{ "circumflex",      0x005e },
-   //{ "underscore",      0x005f },
-   { "grave",           0x0060 },
-   //{ "tilde",           0x007e },
-   //{ "vertbar",         0x007c },
-   //{ "exclamdown",      0x00a1 },
-   //{ "cent",            0x00a2 },
-   //{ "sterling",        0x00a3 },
-   //{ "currency",        0x00a4 },
-   //{ "brokenbar",       0x00a6 },
-   //{ "ring",            0x00b0 },
-   //{ "plusminus",       0x00b1 },
-   { "super2",          0x00b2 },
-   { "super3",          0x00b3 },
-   { "acute",           0x00b4 },
-   //{ "mu",              0x00b5 },
-   //{ "ordmasculine",    0x00ba },
-   //{ "questiondown",    0x00bf },
-   //{ "gemandbls",       0x00df },
-   //{ "agrave",          0x00e0 },
-   //{ "aacute",          0x00e1 },
-   //{ "acircumflex",     0x00e2 },
-   //{ "atilde",          0x00e3 },
-   //{ "adieresis",       0x00e4 },
-   //{ "aring",           0x00e5 },
-   //{ "ae",              0x00e6 },
-   //{ "ccedille",        0x00e7 },
-   //{ "egrave",          0x00e8 },
-   //{ "eacute",          0x00e9 },
-   //{ "ecircumflex",     0x00ea },
-   //{ "edieresis",       0x00eb },
-   //{ "igrave",          0x00ec },
-   //{ "iacute",          0x00ed },
-   //{ "icircumflex",     0x00ee },
-   //{ "idieresis",       0x00ef },
-   //{ "ntilde",          0x00f1 },
-   //{ "ograve",          0x00f2 },
-   //{ "oacute",          0x00f3 },
-   //{ "ocircumflex",     0x00f4 },
-   //{ "otilde",          0x00f5 },
-   //{ "odieresis",       0x00f6 },
-   //{ "divide",          0x00f7 },
-   //{ "oslash",          0x00f8 },
-   //{ "ugrave",          0x00f9 },
-   //{ "uacute",          0x00fa },
-   //{ "ucircumflex",     0x00fb },
-   //{ "udieresis",       0x00fc },
-   //{ "ygrave",          0x00fd },
-   //{ "thorn",           0x00fe },
-   //{ "ydieresis",       0x00ff },
-   { "nomatch",         0xFFFF }
-};
-
-
-////Device Event Types
-//#define SI_UNKNOWN   0x01
-//#define SI_BUTTON    0x02
-//#define SI_POV       0x03
-//#define SI_XPOV      0x04
-//#define SI_YPOV      0x05
-//#define SI_UPOV      0x06
-//#define SI_DPOV      0x07
-//#define SI_LPOV      0x08
-//#define SI_RPOV      0x09
-//#define SI_KEY       0x0A
-//#define SI_XAXIS     0x0B
-//#define SI_YAXIS     0x0C
-//#define SI_ZAXIS     0x0D
-//#define SI_RXAXIS    0x0E
-//#define SI_RYAXIS    0x0F
-//#define SI_RZAXIS    0x10
-//#define SI_SLIDER    0x11
+
+#define CONST_E 2.7182818284590452353602874f
+
+IMPLEMENT_CONOBJECT(ActionMap);
+
+// This is used for determing keys that have ascii codes for the foreign keyboards. IsAlpha doesn't work on foreign keys.
+#define dIsDecentChar(c) (((char(0xa0) <= (c)) && ((c) <= char(0xff))) || (( char(0x21) <= (c)) && ((c) <= char(0x7e))) || (( char(0x91) <= (c)) && ((c) <= char(0x92))))
+
+struct CodeMapping
+{
+   const char* pDescription;
+   U8  type;
+   U32 code;
+};
+
+struct AsciiMapping
+{
+   const char* pDescription;
+   U16         asciiCode;
+};
+
+extern CodeMapping gVirtualMap[];
+extern AsciiMapping gAsciiMap[];
+
+//------------------------------------------------------------------------------
+//-------------------------------------- Action maps
+//
+Vector<ActionMap::BreakEntry> ActionMap::smBreakTable(__FILE__, __LINE__);
+
+
+//------------------------------------------------------------------------------
+ActionMap::ActionMap()
+{
+   VECTOR_SET_ASSOCIATION(mDeviceMaps);
+}
+
+//------------------------------------------------------------------------------
+ActionMap::~ActionMap()
+{
+   for (U32 i = 0; i < (U32)mDeviceMaps.size(); i++)
+      delete mDeviceMaps[i];
+   mDeviceMaps.clear();
+}
+
+//------------------------------------------------------------------------------
+ActionMap::DeviceMap::~DeviceMap()
+{
+   for(U32 i = 0; i < (U32)nodeMap.size(); i++)
+   {
+      dFree(nodeMap[i].makeConsoleCommand);
+      dFree(nodeMap[i].breakConsoleCommand);
+   }
+}
+
+//------------------------------------------------------------------------------
+bool ActionMap::onAdd()
+{
+   if (Parent::onAdd() == false)
+      return false;
+
+   Sim::getActionMapGroup()->addObject(this);
+
+   return true;
+}
+
+//--------------------------------------------------------------------------
+void ActionMap::dumpActionMap(const char* fileName, const bool append) const
+{
+   if (fileName != NULL) {
+      // Dump the deletion, and creation script commands, followed by all the binds
+      //  to a script.
+
+      FileStream iostrm;
+      if ( !ResourceManager->openFileForWrite( iostrm, fileName, append ? FileStream::WriteAppend : FileStream::Write ) )
+      {
+         Con::errorf( "Unable to open file '%s' for writing.", fileName );
+         return;
+      }
+
+      char lineBuffer[1024];
+      if ( append )
+         iostrm.setPosition( iostrm.getStreamSize() );
+      else
+      {
+         // IMPORTANT -- do NOT change the following line, it identifies the file as an input map file
+         dStrcpy( lineBuffer, "// Torque Input Map File\n" );
+         iostrm.write( dStrlen( lineBuffer ), lineBuffer );
+      }
+
+      dSprintf(lineBuffer, 1023, "if (isObject(%s)) %s.delete();\n"
+                                 "new ActionMap(%s);\n", getName(), getName(), getName());
+      iostrm.write(dStrlen(lineBuffer), lineBuffer);
+
+      // Dump all the binds to the console...
+      for (S32 i = 0; i < mDeviceMaps.size(); i++) {
+         const DeviceMap* pDevMap = mDeviceMaps[i];
+
+         char devbuffer[32];
+         getDeviceName(pDevMap->deviceType, pDevMap->deviceInst, devbuffer);
+
+         for (S32 j = 0; j < pDevMap->nodeMap.size(); j++) {
+            const Node& rNode = pDevMap->nodeMap[j];
+
+            const char* pModifierString = getModifierString(rNode.modifiers);
+
+            char objectbuffer[64];
+            if (getKeyString(rNode.action, objectbuffer) == false)
+               continue;
+
+            const char* command = (rNode.flags & Node::BindCmd) ? "bindCmd" : "bind";
+
+            dSprintf(lineBuffer, 1023, "%s.%s(%s, \"%s%s\"",
+                                        getName(),
+                                        command,
+                                        devbuffer,
+                                        pModifierString, objectbuffer);
+
+            if (rNode.flags & (Node::HasScale|Node::HasDeadZone|Node::Ranged|Node::Inverted)) {
+               char buff[10];
+               U32 curr = 0;
+               buff[curr++] = ',';
+               buff[curr++] = ' ';
+               if (rNode.flags & Node::HasScale)
+                  buff[curr++] = 'S';
+               if (rNode.flags & Node::Ranged)
+                  buff[curr++] = 'R';
+               if (rNode.flags & Node::HasDeadZone)
+                  buff[curr++] = 'D';
+               if (rNode.flags & Node::Inverted)
+                  buff[curr++] = 'I';
+               buff[curr] = '\0';
+
+               dStrcat(lineBuffer, buff);
+            }
+
+            if (rNode.flags & Node::HasDeadZone) {
+               char buff[64];
+               dSprintf(buff, 63, ", \"%g %g\"", rNode.deadZoneBegin, rNode.deadZoneEnd);
+               dStrcat(lineBuffer, buff);
+            }
+
+            if (rNode.flags & Node::HasScale) {
+               char buff[64];
+               dSprintf(buff, 63, ", %g", rNode.scaleFactor);
+               dStrcat(lineBuffer, buff);
+            }
+
+            if (rNode.flags & Node::BindCmd) {
+               if (rNode.makeConsoleCommand) {
+                  dStrcat(lineBuffer, ", \"");
+                  U32 pos = dStrlen(lineBuffer);
+                  expandEscape(lineBuffer + pos, rNode.makeConsoleCommand);
+                  dStrcat(lineBuffer, "\"");
+               } else {
+                  dStrcat(lineBuffer, ", \"\"");
+               }
+               if (rNode.breakConsoleCommand) {
+                  dStrcat(lineBuffer, ", \"");
+                  U32 pos = dStrlen(lineBuffer);
+                  expandEscape(lineBuffer + pos, rNode.breakConsoleCommand);
+                  dStrcat(lineBuffer, "\"");
+               }
+               else
+                  dStrcat(lineBuffer, ", \"\"");
+            } else {
+               dStrcat(lineBuffer, ", ");
+               dStrcat(lineBuffer, rNode.consoleFunction);
+            }
+
+            dStrcat(lineBuffer, ");\n");
+            iostrm.write(dStrlen(lineBuffer), lineBuffer);
+         }
+      }
+
+      iostrm.close();
+   }
+   else {
+      // Dump all the binds to the console...
+      for (S32 i = 0; i < mDeviceMaps.size(); i++) {
+         const DeviceMap* pDevMap = mDeviceMaps[i];
+
+         char devbuffer[32];
+         getDeviceName(pDevMap->deviceType, pDevMap->deviceInst, devbuffer);
+
+         for (S32 j = 0; j < pDevMap->nodeMap.size(); j++) {
+            const Node& rNode = pDevMap->nodeMap[j];
+
+            const char* pModifierString = getModifierString(rNode.modifiers);
+
+            char keybuffer[64];
+            if (getKeyString(rNode.action, keybuffer) == false)
+               continue;
+
+            const char* command = (rNode.flags & Node::BindCmd) ? "bindCmd" : "bind";
+
+            char finalBuffer[1024];
+            dSprintf(finalBuffer, 1023, "%s.%s(%s, \"%s%s\"",
+                                        getName(),
+                                        command,
+                                        devbuffer,
+                                        pModifierString, keybuffer);
+
+            if (rNode.flags & (Node::HasScale|Node::HasDeadZone|Node::Ranged|Node::Inverted)) {
+               char buff[10];
+               U32 curr = 0;
+               buff[curr++] = ',';
+               buff[curr++] = ' ';
+               if (rNode.flags & Node::HasScale)
+                  buff[curr++] = 'S';
+               if (rNode.flags & Node::Ranged)
+                  buff[curr++] = 'R';
+               if (rNode.flags & Node::HasDeadZone)
+                  buff[curr++] = 'D';
+               if (rNode.flags & Node::Inverted)
+                  buff[curr++] = 'I';
+               buff[curr] = '\0';
+
+               dStrcat(finalBuffer, buff);
+            }
+
+            if (rNode.flags & Node::HasDeadZone) {
+               char buff[64];
+               dSprintf(buff, 63, ", \"%g %g\"", rNode.deadZoneBegin, rNode.deadZoneEnd);
+               dStrcat(finalBuffer, buff);
+            }
+
+            if (rNode.flags & Node::HasScale) {
+               char buff[64];
+               dSprintf(buff, 63, ", %g", rNode.scaleFactor);
+               dStrcat(finalBuffer, buff);
+            }
+
+            if (rNode.flags & Node::BindCmd) {
+               if (rNode.makeConsoleCommand) {
+                  dStrcat(finalBuffer, ", \"");
+                  dStrcat(finalBuffer, rNode.makeConsoleCommand);
+                  dStrcat(finalBuffer, "\"");
+               } else {
+                  dStrcat(finalBuffer, ", \"\"");
+               }
+               if (rNode.breakConsoleCommand) {
+                  dStrcat(finalBuffer, ", \"");
+                  dStrcat(finalBuffer, rNode.breakConsoleCommand);
+                  dStrcat(finalBuffer, "\"");
+               }
+               else
+                  dStrcat(finalBuffer, ", \"\"");
+            } else {
+               dStrcat(finalBuffer, ", ");
+               dStrcat(finalBuffer, rNode.consoleFunction);
+            }
+
+            dStrcat(finalBuffer, ");");
+            Con::printf(finalBuffer);
+         }
+      }
+   }
+}
+
+//--------------------------------------------------------------------------
+bool ActionMap::createEventDescriptor(const char* pEventString, EventDescriptor* pDescriptor)
+{
+   char copyBuffer[256];
+   dStrcpy(copyBuffer, pEventString);
+
+   // Do we have modifiers?
+   char* pSpace = dStrchr(copyBuffer, ' ');
+   char* pObjectString;
+   if (pSpace != NULL) {
+      // Yes.  Parse them out...
+      //
+      pDescriptor->flags = 0;
+      pObjectString      = pSpace + 1;
+      pSpace[0]          = '\0';
+
+      char* pModifier = dStrtok(copyBuffer, "-");
+      while (pModifier != NULL) {
+         if (dStricmp(pModifier, "shift") == 0) {
+            pDescriptor->flags |= SI_SHIFT;
+         } else if (dStricmp(pModifier, "ctrl") == 0) {
+            pDescriptor->flags |= SI_CTRL;
+         } else if (dStricmp(pModifier, "alt") == 0) {
+            pDescriptor->flags |= SI_ALT;
+         } else if (dStricmp(pModifier, "cmd") == 0) {
+            pDescriptor->flags |= SI_ALT;
+         } else if (dStricmp(pModifier, "opt") == 0) {
+            pDescriptor->flags |= SI_MAC_OPT;
+         }
+
+         pModifier = dStrtok(NULL, "-");
+      }
+   } else {
+      // No.
+      pDescriptor->flags = 0;
+      pObjectString      = copyBuffer;
+   }
+
+   // Now we need to map the key string to the proper KEY code from event.h
+   //
+   AssertFatal(dStrlen(pObjectString) != 0, "Error, no key was specified!");
+
+   if (dStrlen(pObjectString) == 1)
+   {
+      if (dIsDecentChar(*pObjectString)) // includes foreign chars
+      {
+         U16 asciiCode = (*pObjectString);
+         // clear out the FF in upper 8bits for foreign keys??
+         asciiCode &= 0xFF;
+         U16 keyCode = Input::getKeyCode(asciiCode);
+         if ( keyCode >= KEY_0 )
+         {
+            pDescriptor->eventType = SI_KEY;
+            pDescriptor->eventCode = keyCode;
+            return true;
+         }
+         else if (dIsalpha(*pObjectString) == true)
+         {
+            pDescriptor->eventType = SI_KEY;
+            pDescriptor->eventCode = KEY_A+dTolower(*pObjectString)-'a';
+            return true;
+         }
+         else if (dIsdigit(*pObjectString) == true)
+         {
+            pDescriptor->eventType = SI_KEY;
+            pDescriptor->eventCode = KEY_0+(*pObjectString)-'0';
+            return true;
+         }
+      }
+      return false;
+   }
+   else
+   {
+      pDescriptor->eventCode = 0;
+      // Gotta search through the Ascii table...
+      for (U16 i = 0; gAsciiMap[i].asciiCode != 0xFFFF; i++)
+      {
+         if (dStricmp(pObjectString, gAsciiMap[i].pDescription) == 0)
+         {
+            U16 asciiCode = gAsciiMap[i].asciiCode;
+            U16 keyCode   = Input::getKeyCode(asciiCode);
+            if ( keyCode >= KEY_0 )
+            {
+               pDescriptor->eventType = SI_KEY;
+               pDescriptor->eventCode = keyCode;
+               return(true);
+
+            }
+            else
+            {
+               break;
+            }
+         }
+      }
+      // Didn't find an ascii match. Check the virtual map table
+      for (U32 j = 0; gVirtualMap[j].code != 0xFFFFFFFF; j++)
+      {
+         if (dStricmp(pObjectString, gVirtualMap[j].pDescription) == 0)
+         {
+            pDescriptor->eventType = gVirtualMap[j].type;
+            pDescriptor->eventCode = gVirtualMap[j].code;
+            return true;
+         }
+      }
+   }
+   return false;
+}
+
+//------------------------------------------------------------------------------
+ActionMap::Node* ActionMap::getNode(const U32 inDeviceType, const U32 inDeviceInst,
+                   const U32 inModifiers,  const U32 inAction,SimObject* object /*= NULL*/)
+{
+   // DMMTODO - Slow INITIAL implementation.  Replace with a faster version...
+   //
+   DeviceMap* pDeviceMap = NULL;
+   U32 i;
+   for (i = 0; i < (U32)mDeviceMaps.size(); i++) 
+   {
+      if (mDeviceMaps[i]->deviceType == inDeviceType &&
+          mDeviceMaps[i]->deviceInst == inDeviceInst) {
+         pDeviceMap = mDeviceMaps[i];
+         break;
+      }
+   }
+   if (pDeviceMap == NULL) 
+   {
+      mDeviceMaps.increment();
+      mDeviceMaps.last() = new DeviceMap;
+      pDeviceMap = mDeviceMaps.last();
+
+      pDeviceMap->deviceInst = inDeviceInst;
+      pDeviceMap->deviceType = inDeviceType;
+   }
+
+   for (i = 0; i < (U32)pDeviceMap->nodeMap.size(); i++) 
+   {
+      if (pDeviceMap->nodeMap[i].modifiers == inModifiers &&
+          pDeviceMap->nodeMap[i].action    == inAction &&
+          ( (object != NULL) ? object == pDeviceMap->nodeMap[i].object : true )) // Check for an object match if the object exists 
+      {
+         return &pDeviceMap->nodeMap[i];
+      }
+   }
+
+   // If we're here, the node doesn't exist.  create it.
+   pDeviceMap->nodeMap.increment();
+
+   Node* pRetNode = &pDeviceMap->nodeMap.last();
+   pRetNode->modifiers = inModifiers;
+   pRetNode->action    = inAction;
+
+   pRetNode->flags         = 0;
+   pRetNode->deadZoneBegin = 0.0;
+   pRetNode->deadZoneEnd   = 0.0;
+   pRetNode->scaleFactor   = 1.0;
+
+   pRetNode->consoleFunction = NULL;
+   pRetNode->makeConsoleCommand = NULL;
+   pRetNode->breakConsoleCommand = NULL;
+
+   //[neob, 5/7/2007 - #2975]
+   pRetNode->object = 0;
+
+   return pRetNode;
+}
+
+//------------------------------------------------------------------------------
+void ActionMap::removeNode(const U32 inDeviceType, const U32 inDeviceInst, const U32 inModifiers, const U32 inAction, SimObject* object /*= NULL*/)
+{
+   // DMMTODO - Slow INITIAL implementation.  Replace with a faster version...
+   //
+   DeviceMap* pDeviceMap = NULL;
+   U32 i;
+   for (i = 0; i < (U32)mDeviceMaps.size(); i++) {
+      if (mDeviceMaps[i]->deviceType == inDeviceType &&
+          mDeviceMaps[i]->deviceInst == inDeviceInst) {
+         pDeviceMap = mDeviceMaps[i];
+         break;
+      }
+   }
+
+   if (pDeviceMap == NULL)
+      return;
+
+   U32 realMods = inModifiers;
+   if (realMods & SI_SHIFT)
+      realMods |= SI_SHIFT;
+   if (realMods & SI_CTRL)
+      realMods |= SI_CTRL;
+   if (realMods & SI_ALT)
+      realMods |= SI_ALT;
+   if (realMods & SI_MAC_OPT)
+      realMods |= SI_MAC_OPT;
+
+   for (i = 0; i < (U32)pDeviceMap->nodeMap.size(); i++) {
+      if (pDeviceMap->nodeMap[i].modifiers == realMods &&
+          pDeviceMap->nodeMap[i].action    == inAction &&
+          ( (object != NULL) ? object == pDeviceMap->nodeMap[i].object : true )) 
+      {
+          dFree(pDeviceMap->nodeMap[i].makeConsoleCommand);
+          dFree(pDeviceMap->nodeMap[i].breakConsoleCommand);
+          pDeviceMap->nodeMap.erase(i);
+      }
+   }
+}
+
+//------------------------------------------------------------------------------
+const ActionMap::Node* ActionMap::findNode(const U32 inDeviceType, const U32 inDeviceInst,
+                    const U32 inModifiers,  const U32 inAction)
+{
+   // DMMTODO - Slow INITIAL implementation.  Replace with a faster version...
+   //
+   DeviceMap* pDeviceMap = NULL;
+   U32 i;
+   for (i = 0; i < (U32)mDeviceMaps.size(); i++)
+   {
+      if (mDeviceMaps[i]->deviceType == inDeviceType && mDeviceMaps[i]->deviceInst == inDeviceInst)
+      {
+         pDeviceMap = mDeviceMaps[i];
+         break;
+      }
+   }
+
+   if (pDeviceMap == NULL)
+      return NULL;
+
+   U32 realMods = inModifiers;
+   if (realMods & SI_SHIFT)
+      realMods |= SI_SHIFT;
+   if (realMods & SI_CTRL)
+      realMods |= SI_CTRL;
+   if (realMods & SI_ALT)
+      realMods |= SI_ALT;
+   if (realMods & SI_MAC_OPT)
+      realMods |= SI_MAC_OPT;
+
+   for (i = 0; i < (U32)pDeviceMap->nodeMap.size(); i++)
+   {
+      if (pDeviceMap->nodeMap[i].action == KEY_ANYKEY && pDeviceMap->nodeMap[i].modifiers == realMods && dIsDecentChar(inAction))
+         return &pDeviceMap->nodeMap[i];
+      else if (pDeviceMap->nodeMap[i].modifiers == realMods && pDeviceMap->nodeMap[i].action    == inAction)
+         return &pDeviceMap->nodeMap[i];
+   }
+
+   return NULL;
+}
+
+//------------------------------------------------------------------------------
+bool ActionMap::findBoundNode( const char* function, U32 &devMapIndex, U32 &nodeIndex )
+{
+   devMapIndex = 0;
+   nodeIndex = 0;
+   return nextBoundNode( function, devMapIndex, nodeIndex );
+}
+
+bool ActionMap::nextBoundNode( const char* function, U32 &devMapIndex, U32 &nodeIndex )
+{
+   // Loop through all of the existing nodes to find the one mapped to the
+   // given function:
+   for ( U32 i = devMapIndex; i < (U32)mDeviceMaps.size(); i++ )
+   {
+      const DeviceMap* dvcMap = mDeviceMaps[i];
+
+      for ( U32 j = nodeIndex; j < (U32)dvcMap->nodeMap.size(); j++ )
+      {
+         const Node* node = &dvcMap->nodeMap[j];
+         if ( !( node->flags & Node::BindCmd ) && ( dStricmp( function, node->consoleFunction ) == 0 ) )
+         {
+            devMapIndex = i;
+            nodeIndex = j;
+            return( true );
+         }
+      }
+
+      nodeIndex = 0;
+   }
+
+   return( false );
+}
+
+//------------------------------------------------------------------------------
+bool ActionMap::processUnbind(const char *device, const char *action, SimObject* object /*= NULL*/)
+{
+   U32 deviceType;
+   U32 deviceInst;
+
+   if(!getDeviceTypeAndInstance(device, deviceType, deviceInst))
+      return false;
+   EventDescriptor eventDescriptor;
+   if (!createEventDescriptor(action, &eventDescriptor))
+      return false;
+
+   removeNode(deviceType, deviceInst, eventDescriptor.flags,eventDescriptor.eventCode, object);
+   return true;
+}
+
+//------------------------------------------------------------------------------
+// This function is for the use of the control remapper.
+// It will only check against the console function (since all remappable commands are
+// bound using bind and not bindCmd).
+//
+const char* ActionMap::getBinding( const char* command )
+{
+   char* returnString = Con::getReturnBuffer( 1024 );
+   returnString[0] = 0;
+
+   char buffer[256];
+   char deviceBuffer[32];
+   char keyBuffer[64];
+ 
+   U32 devMapIndex = 0, nodeIndex = 0;
+   while ( nextBoundNode( command, devMapIndex, nodeIndex ) )
+   {
+      const DeviceMap* deviceMap = mDeviceMaps[devMapIndex];
+
+      if ( getDeviceName( deviceMap->deviceType, deviceMap->deviceInst, deviceBuffer ) )
+      {
+         const Node* node = &deviceMap->nodeMap[nodeIndex];
+         const char* modifierString = getModifierString( node->modifiers );
+
+         if ( getKeyString( node->action, keyBuffer ) )
+         {
+            dSprintf( buffer, sizeof( buffer ), "%s\t%s%s", deviceBuffer, modifierString, keyBuffer );
+            if ( returnString[0] )
+               dStrcat( returnString, "\t" );
+            dStrcat( returnString, buffer );
+         }
+      }
+
+      ++nodeIndex;
+   }
+
+   return returnString;
+}
+
+//------------------------------------------------------------------------------
+// This function is for the use of the control remapper.
+// The intent of this function is to determine if the given event descriptor is already
+// bound in this action map.  If so, this function returns the command it is bound to.
+// If not, it returns NULL.
+//
+const char* ActionMap::getCommand( const char* device, const char* action )
+{
+    U32 deviceType;
+    U32 deviceInst;
+    if ( getDeviceTypeAndInstance( device, deviceType, deviceInst ) )
+    {
+        EventDescriptor eventDescriptor;
+        if ( createEventDescriptor( action, &eventDescriptor ) )
+        {
+            const ActionMap::Node* mapNode = findNode( deviceType, deviceInst, eventDescriptor.flags, eventDescriptor.eventCode );
+            if ( mapNode )
+            {
+                if ( mapNode->flags & Node::BindCmd )
+                {
+                    S32 bufferLen = dStrlen( mapNode->makeConsoleCommand ) + dStrlen( mapNode->breakConsoleCommand ) + 2;
+                    char* returnString = Con::getReturnBuffer( bufferLen );
+                    dSprintf( returnString, bufferLen, "%s\t%s",
+                            ( mapNode->makeConsoleCommand ? mapNode->makeConsoleCommand : "" ),
+                            ( mapNode->breakConsoleCommand ? mapNode->breakConsoleCommand : "" ) );					
+                    return( returnString );
+                }					
+                else
+                    return( mapNode->consoleFunction );					
+            }
+        }
+    }
+
+    return( "" );
+}
+
+//------------------------------------------------------------------------------
+// This function returns whether or not the mapping specified is inverted.
+// Obviously, this should only be used for axes.
+bool ActionMap::isInverted( const char* device, const char* action )
+{
+    U32 deviceType;
+    U32 deviceInst;
+    if ( getDeviceTypeAndInstance( device, deviceType, deviceInst ) )
+    {
+        EventDescriptor eventDescriptor;
+        if ( createEventDescriptor( action, &eventDescriptor ) )
+        {
+            const ActionMap::Node* mapNode = findNode( deviceType, deviceInst, eventDescriptor.flags, eventDescriptor.eventCode );
+            if ( mapNode )
+                return( mapNode->flags & Node::Inverted );
+        }
+    }
+
+    Con::errorf( "The input event specified by %s %s is not in this action map!", device, action );
+    return( false );
+}
+
+//------------------------------------------------------------------------------
+F32 ActionMap::getScale( const char* device, const char* action )
+{
+    U32 deviceType;
+    U32 deviceInst;
+    if ( getDeviceTypeAndInstance( device, deviceType, deviceInst ) )
+    {
+        EventDescriptor eventDescriptor;
+        if ( createEventDescriptor( action, &eventDescriptor ) )
+        {
+            const ActionMap::Node* mapNode = findNode( deviceType, deviceInst, eventDescriptor.flags, eventDescriptor.eventCode );
+            if ( mapNode )
+            {
+               if ( mapNode->flags & Node::HasScale )
+                   return( mapNode->scaleFactor );
+            else
+               return( 1.0f );
+         }
+        }
+    }
+
+    Con::errorf( "The input event specified by %s %s is not in this action map!", device, action );
+    return( 1.0f );
+}
+
+//------------------------------------------------------------------------------
+const char* ActionMap::getDeadZone( const char* device, const char* action )
+{
+    U32 deviceType;
+    U32 deviceInst;
+    if ( getDeviceTypeAndInstance( device, deviceType, deviceInst ) )
+    {
+        EventDescriptor eventDescriptor;
+        if ( createEventDescriptor( action, &eventDescriptor ) )
+        {
+            const ActionMap::Node* mapNode = findNode( deviceType, deviceInst, eventDescriptor.flags, eventDescriptor.eventCode );
+            if ( mapNode )
+            {
+               if ( mapNode->flags & Node::HasDeadZone )
+               {
+                   char buf[64];
+                   dSprintf( buf, sizeof( buf ), "%g %g", mapNode->deadZoneBegin, mapNode->deadZoneEnd );
+                   char* returnString = Con::getReturnBuffer( dStrlen( buf ) + 1 );
+                   dStrcpy( returnString, buf );
+                   return( returnString );
+                }
+                else
+                   return( "0 0" );				   		
+            }
+        }
+    }
+
+    Con::errorf( "The input event specified by %s %s is not in this action map!", device, action );
+    return( "" );
+}
+
+//------------------------------------------------------------------------------
+const char* ActionMap::buildActionString( const InputEvent* event )
+{
+    const char* modifierString = getModifierString( event->modifier );
+
+    char objectBuffer[64];
+    if ( !getKeyString( event->objInst, objectBuffer ) )
+        return( "" );
+
+    U32 returnLen = dStrlen( modifierString ) + dStrlen( objectBuffer ) + 2;	
+    char* returnString = Con::getReturnBuffer( returnLen );
+    dSprintf( returnString, returnLen - 1, "%s%s", modifierString, objectBuffer );
+    return( returnString );
+}
+
+//------------------------------------------------------------------------------
+bool ActionMap::getDeviceTypeAndInstance(const char *pDeviceName, U32 &deviceType, U32 &deviceInstance)
+{
+   U32 offset = 0;
+    
+   if (dStrnicmp(pDeviceName, "keyboard", dStrlen("keyboard")) == 0)
+   {
+      deviceType = KeyboardDeviceType;
+      offset = dStrlen("keyboard");
+   } 
+   else if (dStrnicmp(pDeviceName, "mouse", dStrlen("mouse")) == 0) 
+   {
+      deviceType = MouseDeviceType;
+      offset = dStrlen("mouse");
+   } 
+   else if (dStrnicmp(pDeviceName, "joystick", dStrlen("joystick")) == 0) 
+   {
+      deviceType = JoystickDeviceType;
+      offset = dStrlen("joystick");
+   } 
+   else if (dStrnicmp(pDeviceName, "accelerometer", dStrlen("accelerometer")) == 0) 
+   {
+      deviceType = AccelerometerDeviceType;
+      offset = dStrlen("accelerometer");
+   }
+   else if (dStrnicmp(pDeviceName, "gyroscope", dStrlen("gyroscope")) == 0)
+   {
+       deviceType = GyroscopeDeviceType;
+       offset = dStrlen("gyroscope");
+   }
+   else if (dStrnicmp(pDeviceName, "touchdevice", dStrlen("touchdevice")) == 0)
+   {
+       deviceType = ScreenTouchDeviceType;
+       offset = dStrlen("touchdevice");
+   }
+    else if (dStrnicmp(pDeviceName, "gamepad", dStrlen("gamepad")) == 0)
+   {
+      deviceType = GamepadDeviceType;
+      offset     = dStrlen("gamepad");
+   }
+   else if (dStrnicmp(pDeviceName, "leapdevice", dStrlen("leapdevice")) == 0)
+   {
+       deviceType = LeapMotionDeviceType;
+       offset = dStrlen("leapdevice");
+   }
+   else
+      return false;
+    
+   if (dStrlen(pDeviceName) > offset) 
+   {
+      const char* pInst = pDeviceName + offset;
+      S32 instNum = dAtoi(pInst);
+      
+      if (instNum < 0)
+         deviceInstance = 0;
+      else
+         deviceInstance = instNum;
+   } 
+   else 
+       deviceInstance = 0;
+    
+   return true;
+}
+
+//------------------------------------------------------------------------------
+bool ActionMap::getDeviceName(const U32 deviceType, const U32 deviceInstance, char* buffer)
+{
+   switch (deviceType)
+   {
+     case KeyboardDeviceType:
+      dStrcpy(buffer, "keyboard");
+      break;
+
+     case MouseDeviceType:
+      dSprintf(buffer, 16, "mouse%d", deviceInstance);
+      break;
+
+     case JoystickDeviceType:
+      dSprintf(buffer, 16, "joystick%d", deviceInstance);
+      break;
+    
+     case AccelerometerDeviceType:
+     dStrcpy(buffer, "accelerometer");
+     break;
+    
+     case GyroscopeDeviceType:
+     dStrcpy(buffer, "gyroscope");
+     break;
+     
+     case ScreenTouchDeviceType:
+     dStrcpy(buffer, "touchdevice");
+     break;
+
+	  case GamepadDeviceType:
+      dSprintf(buffer, 16, "gamepad%d", deviceInstance);
+      break;
+
+     case LeapMotionDeviceType:
+      dStrcpy(buffer, "leapdevice");
+      break;
+
+     default:
+      Con::errorf( "ActionMap::getDeviceName: unknown device type specified, %d (inst: %d)", deviceType, deviceInstance);
+      return false;
+   }
+
+   return true;
+}
+
+//------------------------------------------------------------------------------
+const char* ActionMap::getModifierString(const U32 modifiers)
+{
+    U32 realModifiers = modifiers;
+    if ( modifiers & SI_LSHIFT || modifiers & SI_RSHIFT )
+        realModifiers |= SI_SHIFT;
+    if ( modifiers & SI_LCTRL || modifiers & SI_RCTRL )
+        realModifiers |= SI_CTRL;
+    if ( modifiers & SI_LALT || modifiers & SI_RALT )
+        realModifiers |= SI_ALT;
+    if ( modifiers & SI_MAC_LOPT || modifiers & SI_MAC_ROPT )
+        realModifiers |= SI_MAC_OPT;
+
+   switch (realModifiers & (SI_SHIFT|SI_CTRL|SI_ALT|SI_MAC_OPT)) {
+#if defined(TORQUE_OS_OSX)
+      // optional code, to output alt as cmd on mac.
+      // interpreter sees them as the same...
+     case (SI_SHIFT|SI_CTRL|SI_ALT):
+      return "cmd-shift-ctrl ";
+
+     case (SI_SHIFT|SI_ALT):
+      return "cmd-shift ";
+
+     case (SI_CTRL|SI_ALT):
+      return "cmd-ctrl ";
+
+     case (SI_ALT):
+      return "cmd ";
+#else
+     case (SI_SHIFT|SI_CTRL|SI_ALT):
+      return "shift-ctrl-alt ";
+
+     case (SI_SHIFT|SI_ALT):
+      return "shift-alt ";
+
+     case (SI_CTRL|SI_ALT):
+      return "ctrl-alt ";
+
+     case (SI_ALT):
+      return "alt ";
+#endif
+     case (SI_SHIFT|SI_CTRL):
+      return "shift-ctrl ";
+
+     case (SI_SHIFT):
+      return "shift ";
+
+     case (SI_CTRL):
+      return "ctrl ";
+
+// plus new mac cases:
+     case (SI_ALT|SI_SHIFT|SI_CTRL|SI_MAC_OPT):
+      return "cmd-shift-ctrl-opt ";
+
+     case (SI_ALT|SI_SHIFT|SI_MAC_OPT):
+      return "cmd-shift-opt ";
+
+     case (SI_ALT|SI_CTRL|SI_MAC_OPT):
+      return "cmd-ctrl-opt ";
+
+     case (SI_ALT|SI_MAC_OPT):
+      return "cmd-opt ";
+
+     case (SI_SHIFT|SI_CTRL|SI_MAC_OPT):
+      return "shift-ctrl-opt ";
+
+     case (SI_SHIFT|SI_MAC_OPT):
+      return "shift-opt ";
+
+     case (SI_CTRL|SI_MAC_OPT):
+      return "ctrl-opt ";
+
+     case (SI_MAC_OPT):
+      return "opt ";
+      
+     case 0:
+      return "";
+
+     default:
+      AssertFatal(false, "Error, should never reach the default case in getModifierString");
+      return "";
+   }
+}
+
+//------------------------------------------------------------------------------
+bool ActionMap::getKeyString(const U32 action, char* buffer)
+{
+   U16 asciiCode = Input::getAscii(action, STATE_LOWER);
+
+//   if (action >= KEY_A && action <= KEY_Z) {
+//      buffer[0] = char(action - KEY_A + 'a');
+//      buffer[1] = '\0';
+//      return true;
+//   } else if (action >= KEY_0 && action <= KEY_9) {
+//      buffer[0] = char(action - KEY_0 + '0');
+//      buffer[1] = '\0';
+   if ( (asciiCode != 0) && dIsDecentChar((char)asciiCode))
+   {
+      for (U32 i = 0; gAsciiMap[i].asciiCode != 0xFFFF; i++) {
+         if (gAsciiMap[i].asciiCode == asciiCode)
+         {
+            dStrcpy(buffer, gAsciiMap[i].pDescription);
+            return true;
+         }
+      }
+      // Must not have found a string for that ascii code just record the char
+      buffer[0] = char(asciiCode);
+      buffer[1] = '\0';
+      return true;
+   }
+   else
+   {
+      if (action >= KEY_A && action <= KEY_Z)
+      {
+         buffer[0] = char(action - KEY_A + 'a');
+         buffer[1] = '\0';
+         return true;
+      }
+      else if (action >= KEY_0 && action <= KEY_9) {
+         buffer[0] = char(action - KEY_0 + '0');
+         buffer[1] = '\0';
+         return true;
+      }
+      for (U32 i = 0; gVirtualMap[i].code != 0xFFFFFFFF; i++) {
+         if (gVirtualMap[i].code == action) {
+            dStrcpy(buffer, gVirtualMap[i].pDescription);
+            return true;
+         }
+      }
+   }
+
+   Con::errorf( "ActionMap::getKeyString: no string for action %d", action );
+   return false;
+}
+
+//--------------------------------------------------------------------------
+bool ActionMap::processBindCmd(const char *device, const char *action, const char *makeCmd, const char *breakCmd)
+{
+   U32 deviceType;
+   U32 deviceInst;
+
+   if(!getDeviceTypeAndInstance(device, deviceType, deviceInst))
+   {
+      Con::printf("processBindCmd: unknown device: %s", device);
+      return false;
+   }
+
+   // Ok, we now have the deviceType and instance.  Create an event descriptor
+   //  for the bind...
+   //
+   EventDescriptor eventDescriptor;
+   if (createEventDescriptor(action, &eventDescriptor) == false) {
+      Con::printf("Could not create a description for binding: %s", action);
+      return false;
+   }
+
+   // SI_POV == SI_MOVE, and the POV works fine with bindCmd, so we have to add these manually.
+   if( ( eventDescriptor.eventCode == SI_XAXIS )    ||
+       ( eventDescriptor.eventCode == SI_YAXIS )    ||
+       ( eventDescriptor.eventCode == SI_ZAXIS )    ||
+       ( eventDescriptor.eventCode == SI_RXAXIS )   ||
+       ( eventDescriptor.eventCode == SI_RYAXIS )   ||
+       ( eventDescriptor.eventCode == SI_RZAXIS )   ||
+       ( eventDescriptor.eventCode == SI_SLIDER )   ||
+       ( eventDescriptor.eventCode == SI_XPOV )     ||
+       ( eventDescriptor.eventCode == SI_YPOV )     ||
+       ( eventDescriptor.eventCode == SI_XPOV2 )    ||
+       ( eventDescriptor.eventCode == SI_YPOV2 )    ||
+       ( eventDescriptor.eventCode == SI_ACCELX )   ||
+       ( eventDescriptor.eventCode == SI_ACCELY )   ||
+       ( eventDescriptor.eventCode == SI_ACCELZ )   ||
+       ( eventDescriptor.eventCode == SI_GRAVX )    ||
+       ( eventDescriptor.eventCode == SI_GRAVY )    ||
+       ( eventDescriptor.eventCode == SI_GRAVZ )    ||
+       ( eventDescriptor.eventCode == SI_GYROX )    ||
+       ( eventDescriptor.eventCode == SI_GYROY )    ||
+       ( eventDescriptor.eventCode == SI_GYROZ )    ||
+       ( eventDescriptor.eventCode == SI_YAW )      ||
+       ( eventDescriptor.eventCode == SI_PITCH )    ||
+       ( eventDescriptor.eventCode == SI_ROLL ) )	
+   {
+      Con::warnf( "ActionMap::processBindCmd - Cannot use 'bindCmd' with a move event type. Use 'bind' instead." );
+      return false;
+   }
+
+   // Create the full bind entry, and place it in the map
+   //
+   // DMMTODO
+   Node* pBindNode = getNode(deviceType, deviceInst,
+                             eventDescriptor.flags,
+                             eventDescriptor.eventCode);
+
+   pBindNode->flags           = Node::BindCmd;
+   pBindNode->deadZoneBegin   = 0;
+   pBindNode->deadZoneEnd     = 0;
+   pBindNode->scaleFactor     = 1;
+   if(makeCmd[0])
+      pBindNode->makeConsoleCommand = dStrdup(makeCmd);
+   else
+      pBindNode->makeConsoleCommand = dStrdup("");
+
+   if(breakCmd[0])
+      pBindNode->breakConsoleCommand = dStrdup(breakCmd);
+   else
+      pBindNode->breakConsoleCommand = dStrdup("");
+   return true;
+}
+
+//------------------------------------------------------------------------------
+bool ActionMap::processBind(const U32 argc, const char** argv, SimObject* object)
+{
+   // Ok, the bind will come in the following format:
+   //  [device] [key or button] <[param spec] [param] ...> [fnName]
+   //
+   const char* pDeviceName = argv[0];
+   const char* pEvent      = argv[1];
+   const char* pFnName     = argv[argc - 1];
+
+   // Determine the device
+   U32 deviceType;
+   U32 deviceInst;
+
+   if(!getDeviceTypeAndInstance(argv[0], deviceType, deviceInst))
+   {
+      Con::printf("processBind: unknown device: %s", pDeviceName);
+      return false;
+   }
+
+   // Ok, we now have the deviceType and instance.  Create an event descriptor
+   //  for the bind...
+   //
+   EventDescriptor eventDescriptor;
+   if (createEventDescriptor(pEvent, &eventDescriptor) == false) {
+      Con::printf("Could not create a description for binding: %s", pEvent);
+      return false;
+   }
+
+   // Event has now been described, and device determined.  we need now to extract
+   //  any modifiers that the action map will apply to incoming events before
+   //  calling the bound function...
+   //
+   // DMMTODO
+   U32 assignedFlags = 0;
+   F32 deadZoneBegin = 0.0f;
+   F32 deadZoneEnd   = 0.0f;
+   F32 scaleFactor   = 1.0f;
+
+   if (argc != 3) {
+      // We have the following: "[DSIR]" [deadZone] [scale]
+      //
+      const char* pSpec = argv[2];
+
+      for (U32 i = 0; pSpec[i] != '\0'; i++) {
+         switch (pSpec[i]) {
+           case 'r': case 'R':
+            assignedFlags |= Node::HasScale;
+            break;
+           case 's': case 'S':
+            assignedFlags |= Node::HasScale;
+            break;
+           case 'd': case 'D':
+            assignedFlags |= Node::HasDeadZone;
+            break;
+           case 'i': case 'I':
+            assignedFlags |= Node::Inverted;
+            break;
+			case 'n': case 'N':
+            assignedFlags |= Node::NonLinear;
+            break;
+
+           default:
+            AssertFatal(false, avar("Misunderstood specifier in bind (spec string: %s)",
+                                    pSpec));
+         }
+      }
+
+      // Ok, we have the flags.  Scan the dead zone and scale, if any.
+      //
+      U32 curArg = 3;
+      if (assignedFlags & Node::HasDeadZone) {
+         dSscanf(argv[curArg], "%g %g", &deadZoneBegin, &deadZoneEnd);
+         curArg++;
+      }
+      if (assignedFlags & Node::HasScale) {
+         scaleFactor = dAtof(argv[curArg]);
+         curArg++;
+      }
+
+      if (curArg != (argc - 1)) {
+         AssertFatal(curArg == (argc - 1), "error in bind spec somewhere...");
+         Con::printf("Improperly specified bind for key: %s", argv[2]);
+         return false;
+      }
+   }
+
+   // Ensure that the console function is properly specified?
+   //
+   // DMMTODO
+
+   // Create the full bind entry, and place it in the map
+   //
+   // DMMTODO
+   Node* pBindNode = getNode(deviceType, deviceInst,
+                             eventDescriptor.flags,
+                             eventDescriptor.eventCode, object);
+
+   pBindNode->flags           = assignedFlags;
+   pBindNode->deadZoneBegin   = deadZoneBegin;
+   pBindNode->deadZoneEnd     = deadZoneEnd;
+   pBindNode->scaleFactor     = scaleFactor;
+   pBindNode->object          = object;
+   pBindNode->consoleFunction = StringTable->insert(pFnName);
+
+   return true;
+}
+
+//------------------------------------------------------------------------------
+
+bool ActionMap::processLeap(const InputEvent* pEvent)
+{
+    static const char *argv[5];
+    char buffer[64];
+
+    const Node* pNode = findNode( pEvent->deviceType, pEvent->deviceInst, pEvent->modifier, pEvent->objType );
+
+    if (pNode == NULL)
+    {
+        // Check to see if we clear the modifiers, do we find an action?
+        if (pEvent->modifier != 0)
+            pNode = findNode(pEvent->deviceType, pEvent->deviceInst, 0, pEvent->objInst);
+
+        if (pNode == NULL)
+            return false;
+    }
+
+    // "Do nothing" bind:
+    if ( !pNode->consoleFunction[0] )
+        return( true );
+
+    argv[0] = pNode->consoleFunction;
+
+    float values[3];
+    values[0] = pEvent->fValues[0];
+    values[1] = pEvent->fValues[1];
+    values[2] = pEvent->fValues[2];
+
+    if ( pNode->flags & Node::HasDeadZone )
+    {
+        if ( pEvent->fValues[0] >= pNode->deadZoneBegin && pEvent->fValues[0] <= pNode->deadZoneEnd )
+            values[0] = 0.0f;
+        if ( pEvent->fValues[1] >= pNode->deadZoneBegin && pEvent->fValues[1] <= pNode->deadZoneEnd )
+            values[1] = 0.0f;
+        if ( pEvent->fValues[2] >= pNode->deadZoneBegin && pEvent->fValues[2] <= pNode->deadZoneEnd )
+            values[2] = 0.0f;
+
+        // All values are all null, so don't bother executing the function
+        if (!values[0] && !values[1] && !values[2])
+            return true;
+    }
+
+    switch(pEvent->objType)
+    {
+        case LM_HANDPOS:
+
+            // ID
+            argv[1] = Con::getIntArg(pEvent->iValue);
+
+            // Position
+            dSprintf(buffer, sizeof(buffer), "%f %f %f", values[0], values[1], values[2]);
+
+            argv[2] = buffer;
+
+            if (pNode->object)
+                Con::executef(pNode->object, 3, argv[0], argv[1], argv[2]);
+            else
+                Con::execute(3, argv);
+            break;
+
+        case LM_HANDROT:
+            
+            // ID
+            argv[1] = Con::getIntArg(pEvent->iValue);
+
+            // Rotation
+            dSprintf(buffer, sizeof(buffer), "%f %f %f", values[0], values[1], values[2]);
+
+            argv[2] = buffer;
+
+            if (pNode->object)
+                Con::executef(pNode->object, 3, argv[0], argv[1], argv[2]);
+            else
+                Con::execute(3, argv);
+            break;
+
+        case LM_FINGERPOS:
+            
+            // IDs
+            argv[1] = pEvent->fingerIDs;
+
+            // X-coordinates
+            argv[2] = pEvent->fingersX;
+
+            // Y-coordinates
+            argv[3] = pEvent->fingersY;
+
+            // Z-coordinates
+            argv[4] = pEvent->fingersZ;
+
+            if (pNode->object)
+                Con::executef(pNode->object, 5, argv[0], argv[1], argv[2], argv[3], argv[4]);
+            else
+                Con::execute(5, argv);
+            break;
+
+        case LM_HANDAXIS:
+        default:
+            return false;
+    }
+
+    return true;
+}
+
+//------------------------------------------------------------------------------
+
+bool ActionMap::processGesture(const InputEvent* pEvent)
+{
+    static const char *argv[6];
+    char buffer[64];
+
+    const Node* pNode = findNode( pEvent->deviceType, pEvent->deviceInst, pEvent->modifier, pEvent->objType );
+
+    if (pNode == NULL) 
+    {
+        // Check to see if we clear the modifiers, do we find an action?
+        if (pEvent->modifier != 0)
+            pNode = findNode(pEvent->deviceType, pEvent->deviceInst, 0, pEvent->objInst);
+           
+        if (pNode == NULL)
+            return false;
+    }
+
+    // "Do nothing" bind:
+    if ( !pNode->consoleFunction[0] )
+        return( true );
+
+    // Function
+    argv[0] = pNode->consoleFunction;
+    
+    switch(pEvent->objType)
+    {
+        case SI_CIRCLE_GESTURE:
+
+            // ID
+            argv[1] = Con::getIntArg(pEvent->iValue);
+
+            // Progress
+            argv[2] = Con::getFloatArg(pEvent->fValues[0]);
+
+            // Radius
+            argv[3] = Con::getFloatArg(pEvent->fValues[1]);
+
+            // Direction (1 clockwise, 0 counter-clockwise)
+            argv[4] = Con::getFloatArg(pEvent->fValues[2]);
+
+            // State
+            argv[5] = Con::getFloatArg(pEvent->fValues[3]);
+
+            if (pNode->object)
+                Con::executef(pNode->object, 6, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
+            else
+                Con::execute(6, argv);
+            break;
+
+        case SI_SWIPE_GESTURE:
+
+            // ID
+            argv[1] = Con::getIntArg(pEvent->iValue);
+
+            // State
+            argv[2] = Con::getFloatArg(pEvent->fValues[0]);
+
+            // Direction
+            dSprintf(buffer, sizeof(buffer), "%f %f %f", pEvent->fValues[1], pEvent->fValues[2], pEvent->fValues[3]);
+
+            argv[3] = buffer;
+            // Speed
+            argv[4] = Con::getFloatArg(pEvent->fValues[4]);
+
+            if (pNode->object)
+                Con::executef(pNode->object, 5, argv[0], argv[1], argv[2], argv[3], argv[4]);
+            else
+                Con::execute(5, argv);
+            break;
+
+        case SI_KEYTAP_GESTURE:
+        case SI_SCREENTAP_GESTURE:
+
+            // ID
+            argv[1] = Con::getIntArg(pEvent->iValue);
+        
+            // Position
+            dSprintf(buffer, sizeof(buffer), "%f %f %f", pEvent->fValues[0], pEvent->fValues[1], pEvent->fValues[2]);
+
+            argv[2] = buffer;
+
+            // Direction
+            dSprintf(buffer, sizeof(buffer), "%f %f %f", pEvent->fValues[3], pEvent->fValues[4], pEvent->fValues[5]);
+
+            argv[3] = buffer;
+
+            if (pNode->object)
+                Con::executef(pNode->object, 4, argv[0], argv[1], argv[2], argv[3]);
+            else
+                Con::execute(5, argv);
+
+            break;
+
+        case SI_PINCH_GESTURE:
+        case SI_SCALE_GESTURE:
+        default:
+            return true;
+    }
+       
+    return true;
+}
+
+//------------------------------------------------------------------------------
+
+bool ActionMap::processTouch(const InputEvent* pEvent)
+{
+    static const char *argv[4];
+    const Node* pNode = findNode(pEvent->deviceType, pEvent->deviceInst, pEvent->modifier, pEvent->objInst);
+       
+    if (pNode == NULL) 
+    {
+        // Check to see if we clear the modifiers, do we find an action?
+        if (pEvent->modifier != 0)
+            pNode = findNode(pEvent->deviceType, pEvent->deviceInst, 0, pEvent->objInst);
+           
+        if (pNode == NULL)
+            return false;
+    }
+       
+    // "Do nothing" bind:
+    if ( !pNode->consoleFunction[0] )
+        return( true );
+       
+    // Ok, we're all set up, call the function.
+    argv[0] = pNode->consoleFunction;
+    argv[1] = pEvent->fingerIDs;
+    argv[2] = pEvent->fingersX;
+    argv[3] = pEvent->fingersY;
+       
+    if (pNode->object)
+        Con::executef(pNode->object, 4, argv[0], argv[1], argv[2], argv[3]);
+    else
+        Con::execute(4, argv);
+       
+    return true;
+}
+
+//------------------------------------------------------------------------------
+
+bool ActionMap::processButton(const InputEvent* pEvent)
+{
+    static const char *argv[2];
+    const Node* pNode = findNode(pEvent->deviceType, pEvent->deviceInst, pEvent->modifier, pEvent->objInst);
+
+    if (pNode == NULL) 
+    {
+        // Check to see if we clear the modifiers, do we find an action?
+        if (pEvent->modifier != 0)
+            pNode = findNode(pEvent->deviceType, pEvent->deviceInst, 0, pEvent->objInst);
+
+        if (pNode == NULL)
+            return false;
+    }
+
+    // Whadda ya know, we have this bound.  Set up, and call the console
+    //  function associated with it...
+    //
+    F32 value = pEvent->fValues[0];
+       
+    if (pNode->flags & Node::Ranged)
+    {
+        value = (value * 2.0f) - 1.0f;
+         
+        if (pNode->flags & Node::Inverted)
+            value *= -1.0f;
+    }
+    else
+    {
+        if (pNode->flags & Node::Inverted)
+            value = 1.0f - value;
+    }
+
+    if (pNode->flags & Node::HasScale)
+        value *= pNode->scaleFactor;
+
+    if (pNode->flags & Node::HasDeadZone)
+    {
+        if (value >= pNode->deadZoneBegin && value <= pNode->deadZoneEnd)
+            value = 0.0f;
+        else			
+        {
+            if( value > 0 )
+                value = ( value - pNode->deadZoneBegin ) * ( 1.f / ( 1.f - pNode->deadZoneBegin ) );
+            else
+                value = ( value + pNode->deadZoneBegin ) * ( 1.f / ( 1.f - pNode->deadZoneBegin ) );
+        }
+    }
+
+    if( pNode->flags & Node::NonLinear )
+        value = ( value < 0.f ? -1.f : 1.f ) * mPow( mFabs( value ), CONST_E );
+
+    // Ok, we're all set up, call the function.
+    if(pNode->flags & Node::BindCmd)
+    {
+        // it's a bind command
+        if(pNode->makeConsoleCommand)
+            Con::evaluate(pNode->makeConsoleCommand);
+    }
+    else if ( pNode->consoleFunction[0] )
+    {
+        argv[0] = pNode->consoleFunction;
+        argv[1] = Con::getFloatArg(value);
+        
+        if (pNode->object)
+            Con::executef(pNode->object, 2, argv[0], argv[1]);
+        else
+            Con::execute(2, argv);
+    }
+
+    // [neo, 5/13/2007 - #3109]
+    // The execs/evaluate above could have called reentrant script code which made calls to
+    // bindCmd() etc, channging the node map underneath us. If enough nodes were added then
+    // the node map vector would realloc, with the result that pNode would then be pointing 
+    // at garbage and cause a crash when passed to enterBreakEvent() below. So we just look
+    // it up again to be safe. This is not needed in the other cases below as we return right
+    // after the execs and don't use pNode again.
+    pNode = findNode( pEvent->deviceType, pEvent->deviceInst, pEvent->modifier, pEvent->objInst );
+
+    if( pNode == NULL )
+        return true; // We already called any bound methods/functions so our job is done
+
+    //
+    // And enter the break into the table if this is a make event...
+    enterBreakEvent(pEvent, pNode);
+
+    return true;
+}
+
+//------------------------------------------------------------------------------
+
+bool ActionMap::processMove(const InputEvent* pEvent)
+{
+    static const char *argv[4];
+
+    if (pEvent->deviceType == MouseDeviceType)
+    {
+        const Node* pNode = findNode(pEvent->deviceType, pEvent->deviceInst, pEvent->modifier, pEvent->objType);
+
+        if (pNode == NULL)
+        {
+            // Check to see if we clear the modifiers, do we find an action?
+            if (pEvent->modifier != 0)
+                pNode = findNode(pEvent->deviceType, pEvent->deviceInst, 0, pEvent->objType);
+
+            if (pNode == NULL)
+                return false;
+        }
+
+        // "Do nothing" bind:
+        if ( !pNode->consoleFunction[0] )
+            return( true );
+
+        // Whadda ya know, we have this bound.  Set up, and call the console
+        //  function associated with it.  Mouse events ignore range and dead
+        //  zone params.
+        //
+        F32 value = pEvent->fValues[0];
+         
+        if (pNode->flags & Node::Inverted)
+            value *= -1.0f;
+        if (pNode->flags & Node::HasScale)
+            value *= pNode->scaleFactor;
+
+        // Ok, we're all set up, call the function.
+        argv[0] = pNode->consoleFunction;
+        argv[1] = Con::getFloatArg(value);
+          
+        if (pNode->object)
+            Con::executef(pNode->object, 2, argv[0], argv[1]);
+        else
+            Con::execute(2, argv);
+
+            return true;
+    } 
+    else if ( (pEvent->objType == XI_POS || pEvent->objType == XI_FLOAT || pEvent->objType == XI_ROT || pEvent->objType == XI_INT) )
+    {
+        const Node* pNode = findNode(pEvent->deviceType, pEvent->deviceInst, pEvent->modifier,   pEvent->objInst);
+
+        if( pNode == NULL )
+            return false;
+
+        // Ok, we're all set up, call the function.
+        argv[0] = pNode->consoleFunction;
+        S32 argc = 1;
+
+        if (pEvent->objType == XI_INT)
+        {
+            // Handle the integer as some sort of motion such as a
+            // single component to an absolute position
+            argv[1] = Con::getIntArg( pEvent->iValue );
+            argc += 1;
+        }
+        else if (pEvent->objType == XI_FLOAT)
+        {
+            // Handle float as some sort of motion such as a
+            // single component to an absolute position
+            argv[1] = Con::getFloatArg( pEvent->fValues[0] );
+            argc += 1;
+        }
+        else if (pEvent->objType == XI_POS)
+        {
+            // Handle Point3F type position
+            argv[1] = Con::getFloatArg( pEvent->fValues[0] );
+            argv[2] = Con::getFloatArg( pEvent->fValues[1] );
+            argv[3] = Con::getFloatArg( pEvent->fValues[2] );
+
+            argc += 3;
+        }
+
+        if (pNode->object)
+        {
+            Con::execute(pNode->object, argc, argv);
+        }
+        else
+        {
+            Con::execute(argc, argv);
+        }
+
+        return true;
+    }
+    else if ( pEvent->deviceType == JoystickDeviceType || pEvent->deviceType == GamepadDeviceType )
+    {
+        // Joystick events...
+        const Node* pNode = findNode( pEvent->deviceType, pEvent->deviceInst, pEvent->modifier,   pEvent->objInst );
+
+        if( pNode == NULL )
+            return false;
+
+        // "Do nothing" bind:
+        if ( !pNode->consoleFunction[0] )
+            return( true );
+
+        // Whadda ya know, we have this bound.  Set up, and call the console
+        //  function associated with it.  Joystick move events are the same as mouse
+        //  move events except that they don't ignore dead zone.
+        //
+        F32 value = pEvent->fValues[0];
+        if ( pNode->flags & Node::Inverted )
+            value *= -1.0f;
+
+        if ( pNode->flags & Node::HasScale )
+            value *= pNode->scaleFactor;
+
+        if ( pNode->flags & Node::HasDeadZone )
+        {
+            if ( value >= pNode->deadZoneBegin && value <= pNode->deadZoneEnd )
+            {
+                value = 0.0f;
+            }
+            else
+            {
+                if( value > 0 )
+                    value = ( value - pNode->deadZoneBegin ) * ( 1.f / ( 1.f - pNode->deadZoneBegin ) );
+                else
+                    value = ( value + pNode->deadZoneBegin ) * ( 1.f / ( 1.f - pNode->deadZoneBegin ) );
+            }
+        }
+
+        if( pNode->flags & Node::NonLinear )
+            value = ( value < 0.f ? -1.f : 1.f ) * mPow( mFabs( value ), CONST_E );
+
+        // Ok, we're all set up, call the function.
+        argv[0] = pNode->consoleFunction;
+        argv[1] = Con::getFloatArg( value );
+            
+        if (pNode->object)
+            Con::executef(pNode->object, S32(argv[0]), argv[1]);
+        else
+            Con::execute(2, argv);
+
+        return true;
+    }
+
+    return false;
+}
+
+//------------------------------------------------------------------------------
+
+bool ActionMap::processMotion(const InputEvent* pEvent)
+{
+    static const char *argv[2];
+
+    // iOS Accelerometer, Gyroscope and DeviceMotion processing
+    // Currently, this is identical to the joystick handling.
+       
+    // This was copied over into its own section because this will
+    // give us a dedicated section to tweak processing based on iOS specific
+    // devices. No point in trying to mangle joystick code any further
+    const Node* pNode = findNode( pEvent->deviceType, pEvent->deviceInst, pEvent->modifier,   pEvent->objType );
+       
+    if ( pNode == NULL )
+    {
+        // Check to see if we clear the modifiers, do we find an action?
+        if (pEvent->modifier != 0)
+            pNode = findNode( pEvent->deviceType, pEvent->deviceInst, 0, pEvent->objType );
+           
+        if ( pNode == NULL )
+            return false;
+    }
+       
+    // "Do nothing" bind:
+    if ( !pNode->consoleFunction[0] )
+        return( true );
+       
+    F32 value = pEvent->fValues[0];
+       
+    if ( pNode->flags & Node::Inverted )
+        value *= -1.0f;
+       
+    if ( pNode->flags & Node::HasScale )
+        value *= pNode->scaleFactor;
+       
+    if ( pNode->flags & Node::HasDeadZone )
+    {
+        if ( value >= pNode->deadZoneBegin && value <= pNode->deadZoneEnd )
+            value = 0.0f;
+    }
+       
+    // Ok, we're all set up, call the function.
+    argv[0] = pNode->consoleFunction;
+    argv[1] = Con::getFloatArg( value );
+       
+    if (pNode->object)
+        Con::executef(pNode->object, 2, argv[0], argv[1]);
+    else
+        Con::execute(2, argv);
+       
+    return true;
+}
+
+//------------------------------------------------------------------------------
+
+bool ActionMap::processXInput(const InputEvent* pEvent)
+{
+    static const char *argv[2];
+
+    if ((pEvent->objType == XI_FLOAT || pEvent->objType == XI_INT))
+    {
+        const Node* pNode = findNode( pEvent->deviceType, pEvent->deviceInst, pEvent->modifier, pEvent->objType );
+
+        if (pNode == NULL )
+            return false;
+
+        // Ok, we're all set up, call the function.
+        argv[0] = pNode->consoleFunction;
+        S32 argc = 1;
+
+        if (pEvent->objType == XI_INT)
+        {
+            // Handle the integer as some sort of motion such as a
+            // single component to an absolute position
+            argv[1] = Con::getIntArg( pEvent->iValue );
+            argc += 1;
+        }
+        else if (pEvent->objType == XI_FLOAT)
+        {
+            // Handle float as some sort of motion such as a
+            // single component to an absolute position
+            argv[1] = Con::getFloatArg( pEvent->fValues[0] );
+            argc += 1;
+        }
+
+        if (pNode->object)
+        {
+            Con::execute(pNode->object, argc, argv);
+        }
+        else
+        {
+            Con::execute(argc, argv);
+        }
+    }
+    
+    return true;
+}
+
+//------------------------------------------------------------------------------
+
+bool ActionMap::processAction(const InputEvent* pEvent)
+{
+    switch(pEvent->action)
+    {
+    case SI_LEAP:
+        return processLeap(pEvent);
+        break;
+    case SI_GESTURE:
+        return processGesture(pEvent);
+        break;
+    case SI_TOUCH:
+        return processTouch(pEvent);
+        break;
+    case SI_MAKE:
+        return processButton(pEvent);
+        break;
+    case SI_MOVE:
+        return processMove(pEvent);
+        break;
+    case SI_MOTION:
+        return processMotion(pEvent);
+        break;
+    case SI_BREAK:
+        return checkBreakTable(pEvent);
+        break;
+    case SI_VALUE:
+        return processXInput(pEvent);
+    }
+
+    return false;
+}
+
+//------------------------------------------------------------------------------
+void ActionMap::enterBreakEvent(const InputEvent* pEvent, const Node* pNode)
+{
+   // There aren't likely to be many breaks outstanding at any one given time,
+   //  so a simple linear search is probably sufficient.  Note that the break table
+   //  is static to the class, all breaks are directed to the action map that received
+   //  the make.
+   //
+   S32 entry = -1;
+   for (U32 i = 0; i < (U32)smBreakTable.size(); i++) {
+      if (smBreakTable[i].deviceType == U32(pEvent->deviceType) &&
+          smBreakTable[i].deviceInst == U32(pEvent->deviceInst) &&
+          smBreakTable[i].objInst    == U32(pEvent->objInst)) {
+         // Match.
+         entry = i;
+         break;
+      }
+   }
+   if (entry == -1) {
+      smBreakTable.increment();
+      entry = smBreakTable.size() - 1;
+
+      smBreakTable[entry].deviceType = pEvent->deviceType;
+      smBreakTable[entry].deviceInst = pEvent->deviceInst;
+      smBreakTable[entry].objInst    = pEvent->objInst;
+   }
+
+   // Ok, we now have the entry, and know that the device desc. and the objInst match.
+   //  Copy out the node information...
+   //
+   smBreakTable[entry].object = pNode->object;
+   // [neo, 5/7/2007 - #2975]
+   // object above can be deleted in between a make/break and so object will point
+   // to turfed memory and crash. To keep things simple we just store id as well so
+   // we can look it up to validate object ref.
+   smBreakTable[entry].objectId = pNode->object ? pNode->object->getId() : 0;
+
+   smBreakTable[entry].consoleFunction = pNode->consoleFunction;
+
+   if(pNode->breakConsoleCommand)
+      smBreakTable[entry].breakConsoleCommand = dStrdup(pNode->breakConsoleCommand);
+   else
+      smBreakTable[entry].breakConsoleCommand = NULL;
+
+   smBreakTable[entry].flags         = pNode->flags;
+   smBreakTable[entry].deadZoneBegin = pNode->deadZoneBegin;
+   smBreakTable[entry].deadZoneEnd   = pNode->deadZoneEnd;
+   smBreakTable[entry].scaleFactor   = pNode->scaleFactor;
+}
+
+//------------------------------------------------------------------------------
+bool ActionMap::checkBreakTable(const InputEvent* pEvent)
+{
+   for (U32 i = 0; i < (U32)smBreakTable.size(); i++) {
+      if (smBreakTable[i].deviceType == U32(pEvent->deviceType) &&
+          smBreakTable[i].deviceInst == U32(pEvent->deviceInst) &&
+          smBreakTable[i].objInst    == U32(pEvent->objInst)) {
+         // Match.  Issue the break event...
+         //
+         F32 value = pEvent->fValues[0];
+         if (smBreakTable[i].flags & Node::Ranged) {
+            value = (value * 2.0f) - 1.0f;
+            if (smBreakTable[i].flags & Node::Inverted)
+               value *= -1.0f;
+         } else {
+            if (smBreakTable[i].flags & Node::Inverted)
+               value = 1.0f - value;
+         }
+
+         if (smBreakTable[i].flags & Node::HasScale)
+            value *= smBreakTable[i].scaleFactor;
+
+         if (smBreakTable[i].flags & Node::HasDeadZone)
+            if (value >= smBreakTable[i].deadZoneBegin &&
+                value <= smBreakTable[i].deadZoneEnd)
+               value = 0.0f;
+
+         // Ok, we're all set up, call the function.
+         if(smBreakTable[i].consoleFunction)
+         {
+            if ( smBreakTable[i].consoleFunction[0] )
+            {
+               static const char *argv[2];
+               argv[0] = smBreakTable[i].consoleFunction;
+               argv[1] = Con::getFloatArg(value);
+                              
+               if( smBreakTable[i].object )
+               {
+                  // [neo, 5/7/2007 - #2975]
+                  // object above can be deleted in between a make/break and so object will point
+                  // to turfed memory and crash. To keep things simple we just store id as well so
+                  // we can look it up to validate object ref.
+                  if( smBreakTable[i].objectId > 0 && Sim::findObject( smBreakTable[i].objectId ) )
+                     Con::executef(smBreakTable[i].object, 2, argv[0], argv[1]);
+               }
+               else
+                  Con::execute(2, argv);
+            }
+         }
+         else if(smBreakTable[i].breakConsoleCommand)
+         {
+            Con::evaluate(smBreakTable[i].breakConsoleCommand);
+            dFree(smBreakTable[i].breakConsoleCommand);
+         }
+         smBreakTable.erase(i);
+         return true;
+      }
+   }
+
+   return false;
+}
+
+//------------------------------------------------------------------------------
+bool ActionMap::handleEvent(const InputEvent* pEvent)
+{
+   // Interate through the ActionMapSet until we get a map that
+   //  handles the event or we run out of maps...
+   //
+   SimSet* pActionMapSet = Sim::getActiveActionMapSet();
+   AssertFatal(pActionMapSet && pActionMapSet->size() != 0,
+               "error, no ActiveMapSet or no global action map...");
+
+   for (SimSet::iterator itr = pActionMapSet->end() - 1;
+        itr > pActionMapSet->begin(); itr--) {
+      ActionMap* pMap = static_cast<ActionMap*>(*itr);
+      if (pMap->processAction(pEvent) == true)
+         return true;
+   }
+
+   return false;
+}
+
+//------------------------------------------------------------------------------
+bool ActionMap::handleEventGlobal(const InputEvent* pEvent)
+{
+   // Interate through the ActionMapSet until we get a map that
+   //  handles the event or we run out of maps...
+   //
+   SimSet* pActionMapSet = Sim::getActiveActionMapSet();
+   AssertFatal(pActionMapSet && pActionMapSet->size() != 0,
+               "error, no ActiveMapSet or no global action map...");
+
+   return ((ActionMap*)pActionMapSet->first())->processAction(pEvent);
+}
+
+//------------------------------------------------------------------------------
+//-------------------------------------- Key code to string mapping
+//                                        TODO: Add most obvious aliases...
+//
+CodeMapping gVirtualMap[] =
+{
+   //-------------------------------------- KEYBOARD EVENTS
+   //
+   { "backspace",     SI_KEY,    KEY_BACKSPACE   },
+   { "tab",           SI_KEY,    KEY_TAB         },
+
+   { "return",        SI_KEY,    KEY_RETURN      },
+   { "enter",         SI_KEY,    KEY_RETURN      },
+
+   { "shift",         SI_KEY,    KEY_SHIFT       },
+   { "ctrl",          SI_KEY,    KEY_CONTROL     },
+   { "alt",           SI_KEY,    KEY_ALT         },
+   { "pause",         SI_KEY,    KEY_PAUSE       },
+   { "capslock",      SI_KEY,    KEY_CAPSLOCK    },
+
+   { "escape",        SI_KEY,    KEY_ESCAPE      },
+
+   { "space",         SI_KEY,    KEY_SPACE       },
+   { "pagedown",      SI_KEY,    KEY_PAGE_DOWN   },
+   { "pageup",        SI_KEY,    KEY_PAGE_UP     },
+   { "end",           SI_KEY,    KEY_END         },
+   { "home",          SI_KEY,    KEY_HOME        },
+   { "left",          SI_KEY,    KEY_LEFT        },
+   { "up",            SI_KEY,    KEY_UP          },
+   { "right",         SI_KEY,    KEY_RIGHT       },
+   { "down",          SI_KEY,    KEY_DOWN        },
+   { "print",         SI_KEY,    KEY_PRINT       },
+   { "insert",        SI_KEY,    KEY_INSERT      },
+   { "delete",        SI_KEY,    KEY_DELETE      },
+   { "help",          SI_KEY,    KEY_HELP        },
+
+   { "win_lwindow",   SI_KEY,    KEY_WIN_LWINDOW },
+   { "win_rwindow",   SI_KEY,    KEY_WIN_RWINDOW },
+   { "win_apps",      SI_KEY,    KEY_WIN_APPS    },
+
+   { "cmd",           SI_KEY,    KEY_ALT         },
+   { "opt",           SI_KEY,    KEY_MAC_OPT     },
+   { "lopt",          SI_KEY,    KEY_MAC_LOPT    },
+   { "ropt",          SI_KEY,    KEY_MAC_ROPT    },
+
+   { "numpad0",       SI_KEY,    KEY_NUMPAD0     },
+   { "numpad1",       SI_KEY,    KEY_NUMPAD1     },
+   { "numpad2",       SI_KEY,    KEY_NUMPAD2     },
+   { "numpad3",       SI_KEY,    KEY_NUMPAD3     },
+   { "numpad4",       SI_KEY,    KEY_NUMPAD4     },
+   { "numpad5",       SI_KEY,    KEY_NUMPAD5     },
+   { "numpad6",       SI_KEY,    KEY_NUMPAD6     },
+   { "numpad7",       SI_KEY,    KEY_NUMPAD7     },
+   { "numpad8",       SI_KEY,    KEY_NUMPAD8     },
+   { "numpad9",       SI_KEY,    KEY_NUMPAD9     },
+   { "numpadmult",    SI_KEY,    KEY_MULTIPLY    },
+   { "numpadadd",     SI_KEY,    KEY_ADD         },
+   { "numpadsep",     SI_KEY,    KEY_SEPARATOR   },
+   { "numpadminus",   SI_KEY,    KEY_SUBTRACT    },
+   { "numpaddecimal", SI_KEY,    KEY_DECIMAL     },
+   { "numpaddivide",  SI_KEY,    KEY_DIVIDE      },
+   { "numpadenter",   SI_KEY,    KEY_NUMPADENTER },
+
+   { "f1",            SI_KEY,    KEY_F1          },
+   { "f2",            SI_KEY,    KEY_F2          },
+   { "f3",            SI_KEY,    KEY_F3          },
+   { "f4",            SI_KEY,    KEY_F4          },
+   { "f5",            SI_KEY,    KEY_F5          },
+   { "f6",            SI_KEY,    KEY_F6          },
+   { "f7",            SI_KEY,    KEY_F7          },
+   { "f8",            SI_KEY,    KEY_F8          },
+   { "f9",            SI_KEY,    KEY_F9          },
+   { "f10",           SI_KEY,    KEY_F10         },
+   { "f11",           SI_KEY,    KEY_F11         },
+   { "f12",           SI_KEY,    KEY_F12         },
+   { "f13",           SI_KEY,    KEY_F13         },
+   { "f14",           SI_KEY,    KEY_F14         },
+   { "f15",           SI_KEY,    KEY_F15         },
+   { "f16",           SI_KEY,    KEY_F16         },
+   { "f17",           SI_KEY,    KEY_F17         },
+   { "f18",           SI_KEY,    KEY_F18         },
+   { "f19",           SI_KEY,    KEY_F19         },
+   { "f20",           SI_KEY,    KEY_F20         },
+   { "f21",           SI_KEY,    KEY_F21         },
+   { "f22",           SI_KEY,    KEY_F22         },
+   { "f23",           SI_KEY,    KEY_F23         },
+   { "f24",           SI_KEY,    KEY_F24         },
+
+   { "numlock",       SI_KEY,    KEY_NUMLOCK     },
+   { "scrolllock",    SI_KEY,    KEY_SCROLLLOCK  },
+
+   { "lshift",        SI_KEY,    KEY_LSHIFT      },
+   { "rshift",        SI_KEY,    KEY_RSHIFT      },
+   { "lcontrol",      SI_KEY,    KEY_LCONTROL    },
+   { "rcontrol",      SI_KEY,    KEY_RCONTROL    },
+   { "lalt",          SI_KEY,    KEY_LALT        },
+   { "ralt",          SI_KEY,    KEY_RALT        },
+   { "tilde",         SI_KEY,    KEY_TILDE       },
+
+   { "minus",         SI_KEY,    KEY_MINUS       },
+   { "equals",        SI_KEY,    KEY_EQUALS      },
+   { "lbracket",      SI_KEY,    KEY_LBRACKET    },
+   { "rbracket",      SI_KEY,    KEY_RBRACKET    },
+   { "backslash",     SI_KEY,    KEY_BACKSLASH   },
+   { "semicolon",     SI_KEY,    KEY_SEMICOLON   },
+   { "apostrophe",    SI_KEY,    KEY_APOSTROPHE  },
+   { "comma",         SI_KEY,    KEY_COMMA       },
+   { "period",        SI_KEY,    KEY_PERIOD      },
+   { "slash",         SI_KEY,    KEY_SLASH       },
+   { "lessthan",      SI_KEY,    KEY_OEM_102     },
+
+   //-------------------------------------- BUTTON EVENTS
+   // Joystick/Mouse buttons
+   { "button0",       SI_BUTTON, KEY_BUTTON0    },
+   { "button1",       SI_BUTTON, KEY_BUTTON1    },
+   { "button2",       SI_BUTTON, KEY_BUTTON2    },
+   { "button3",       SI_BUTTON, KEY_BUTTON3    },
+   { "button4",       SI_BUTTON, KEY_BUTTON4    },
+   { "button5",       SI_BUTTON, KEY_BUTTON5    },
+   { "button6",       SI_BUTTON, KEY_BUTTON6    },
+   { "button7",       SI_BUTTON, KEY_BUTTON7    },
+   { "button8",       SI_BUTTON, KEY_BUTTON8    },
+   { "button9",       SI_BUTTON, KEY_BUTTON9    },
+   { "button10",      SI_BUTTON, KEY_BUTTON10   },
+   { "button11",      SI_BUTTON, KEY_BUTTON11   },
+   { "button12",      SI_BUTTON, KEY_BUTTON12   },
+   { "button13",      SI_BUTTON, KEY_BUTTON13   },
+   { "button14",      SI_BUTTON, KEY_BUTTON14   },
+   { "button15",      SI_BUTTON, KEY_BUTTON15   },
+   { "button16",      SI_BUTTON, KEY_BUTTON16   },
+   { "button17",      SI_BUTTON, KEY_BUTTON17   },
+   { "button18",      SI_BUTTON, KEY_BUTTON18   },
+   { "button19",      SI_BUTTON, KEY_BUTTON19   },
+   { "button20",      SI_BUTTON, KEY_BUTTON20   },
+   { "button21",      SI_BUTTON, KEY_BUTTON21   },
+   { "button22",      SI_BUTTON, KEY_BUTTON22   },
+   { "button23",      SI_BUTTON, KEY_BUTTON23   },
+   { "button24",      SI_BUTTON, KEY_BUTTON24   },
+   { "button25",      SI_BUTTON, KEY_BUTTON25   },
+   { "button26",      SI_BUTTON, KEY_BUTTON26   },
+   { "button27",      SI_BUTTON, KEY_BUTTON27   },
+   { "button28",      SI_BUTTON, KEY_BUTTON28   },
+   { "button29",      SI_BUTTON, KEY_BUTTON29   },
+   { "button30",      SI_BUTTON, KEY_BUTTON30   },
+   { "button31",      SI_BUTTON, KEY_BUTTON31   },
+
+   //-------------------------------------- MOVE EVENTS
+   // Mouse/Joystick axes:
+   { "xaxis",         SI_MOVE,   SI_XAXIS       },
+   { "yaxis",         SI_MOVE,   SI_YAXIS       },
+   { "zaxis",         SI_MOVE,   SI_ZAXIS       },
+   { "rxaxis",        SI_MOVE,   SI_RXAXIS      },
+   { "ryaxis",        SI_MOVE,   SI_RYAXIS      },
+   { "rzaxis",        SI_MOVE,   SI_RZAXIS      },
+   { "slider",        SI_MOVE,   SI_SLIDER      },
+
+   //-------------------------------------- POV EVENTS
+   // Joystick POV:
+   { "xpov",          SI_POV,    SI_XPOV         },
+   { "ypov",          SI_POV,    SI_YPOV         },
+   { "upov",          SI_POV,    SI_UPOV         },
+   { "dpov",          SI_POV,    SI_DPOV         },
+   { "lpov",          SI_POV,    SI_LPOV         },
+   { "rpov",          SI_POV,    SI_RPOV         },
+   { "xpov2",         SI_POV,    SI_XPOV2        },
+   { "ypov2",         SI_POV,    SI_YPOV2        },
+   { "upov2",         SI_POV,    SI_UPOV2        },
+   { "dpov2",         SI_POV,    SI_DPOV2        },
+   { "lpov2",         SI_POV,    SI_LPOV2        },
+   { "rpov2",         SI_POV,    SI_RPOV2        },
+
+   #if defined( TORQUE_OS_WIN32 ) || defined( TORQUE_OS_XENON )
+   //-------------------------------------- XINPUT EVENTS
+   // Controller connect / disconnect:
+   { "connect",       XI_BUTTON, XI_CONNECT     },
+   
+   // L & R Thumbsticks:
+   { "thumblx",       XI_AXIS,   XI_THUMBLX     },
+   { "thumbly",       XI_AXIS,   XI_THUMBLY     },
+   { "thumbrx",       XI_AXIS,   XI_THUMBRX     },
+   { "thumbry",       XI_AXIS,   XI_THUMBRY     },
+
+   // L & R Triggers:
+   { "triggerl",      XI_AXIS,   XI_LEFT_TRIGGER  },
+   { "triggerr",      XI_AXIS,   XI_RIGHT_TRIGGER },
+
+   // DPAD Buttons:
+   { "dpadu",         XI_BUTTON, SI_UPOV     },
+   { "dpadd",         XI_BUTTON, SI_DPOV   },
+   { "dpadl",         XI_BUTTON, SI_LPOV   },
+   { "dpadr",         XI_BUTTON, SI_RPOV  },
+
+   // START & BACK Buttons:
+   { "btn_start",     XI_BUTTON, XI_START       },
+   { "btn_back",      XI_BUTTON, XI_BACK        },
+
+   // L & R Thumbstick Buttons:
+   { "btn_lt",        XI_BUTTON, XI_LEFT_THUMB  },
+   { "btn_rt",        XI_BUTTON, XI_RIGHT_THUMB },
+
+   // L & R Shoulder Buttons:
+   { "btn_l",         XI_BUTTON, XI_LEFT_SHOULDER  },
+   { "btn_r",         XI_BUTTON, XI_RIGHT_SHOULDER },
+
+   // Primary buttons:
+   { "btn_a",         XI_BUTTON, XI_A           },
+   { "btn_b",         XI_BUTTON, XI_B           },
+   { "btn_x",         XI_BUTTON, XI_X           },
+   { "btn_y",         XI_BUTTON, XI_Y           },
+#endif
+
+
+   //-------------------------------------- MOTION EVENTS
+   // Accelerometer/Gyroscope axes:
+   { "accelx",        SI_MOTION,    SI_ACCELX    },
+   { "accely",        SI_MOTION,    SI_ACCELY    },
+   { "accelz",        SI_MOTION,    SI_ACCELZ    },
+   { "gravityx",      SI_MOTION,    SI_GRAVX     },
+   { "gravityy",      SI_MOTION,    SI_GRAVY     },
+   { "gravityz",      SI_MOTION,    SI_GRAVZ     },
+   { "gyrox",         SI_MOTION,    SI_GYROX     },
+   { "gyroy",         SI_MOTION,    SI_GYROY     },
+   { "gyroz",         SI_MOTION,    SI_GYROZ     },
+   { "yaw",           SI_MOTION,    SI_YAW       },
+   { "pitch",         SI_MOTION,    SI_PITCH     },
+   { "roll",          SI_MOTION,    SI_ROLL      },
+
+   //-------------------------------------- TOUCH EVENTS
+   // Touch events:
+   { "touchdown",     SI_TOUCH,    SI_TOUCHDOWN  },
+   { "touchup",       SI_TOUCH,    SI_TOUCHUP    },
+   { "touchmove",     SI_TOUCH,    SI_TOUCHMOVE  },
+
+   //-------------------------------------- GESTURE EVENTS
+   // Preset gesture events:
+   { "circleGesture",      SI_GESTURE,  SI_CIRCLE_GESTURE    },
+   { "swipeGesture",       SI_GESTURE,  SI_SWIPE_GESTURE     },
+   { "screenTapGesture",   SI_GESTURE,  SI_SCREENTAP_GESTURE },
+   { "keyTapGesture",      SI_GESTURE,  SI_KEYTAP_GESTURE    },
+   { "pinchGesture",       SI_GESTURE,  SI_PINCH_GESTURE     },
+   { "scaleGesture",       SI_GESTURE,  SI_SCALE_GESTURE     },
+
+   //-------------------------------------- GESTURE EVENTS
+   // Preset gesture events:
+   { "leapHandAxis",    SI_LEAP,     LM_HANDAXIS    },
+   { "leapHandPos",     SI_LEAP,     LM_HANDPOS     },
+   { "leapHandRot",     SI_LEAP,     LM_HANDROT     },
+   { "leapFingerPos",   SI_LEAP,     LM_FINGERPOS   },
+   
+   //-------------------------------------- MISCELLANEOUS EVENTS
+   //
+   { "anykey",        SI_KEY,      KEY_ANYKEY },
+   { "nomatch",       SI_UNKNOWN,  0xFFFFFFFF }
+};
+
+AsciiMapping gAsciiMap[] =
+{
+   //--- KEYBOARD EVENTS
+   //
+   { "space",           0x0020 },
+   //{ "exclamation",     0x0021 },
+   { "doublequote",     0x0022 },
+   //{ "pound",           0x0023 },
+   //{ "ampersand",       0x0026 },
+   { "apostrophe",      0x0027 },
+   //{ "lparen",          0x0028 },
+   //{ "rparen",          0x0029 },
+   { "comma",           0x002c },
+   { "minus",           0x002d },
+   { "period",          0x002e },
+   //{ "slash",           0x002f },
+   //{ "colon",           0x003a },
+   //{ "semicolon",       0x003b },
+   //{ "lessthan",        0x003c },
+   //{ "equals",          0x003d },
+   //{ "morethan",        0x003e },
+   //{ "lbracket",        0x005b },
+   { "backslash",       0x005c },
+   //{ "rbracket",        0x005d },
+   //{ "circumflex",      0x005e },
+   //{ "underscore",      0x005f },
+   { "grave",           0x0060 },
+   //{ "tilde",           0x007e },
+   //{ "vertbar",         0x007c },
+   //{ "exclamdown",      0x00a1 },
+   //{ "cent",            0x00a2 },
+   //{ "sterling",        0x00a3 },
+   //{ "currency",        0x00a4 },
+   //{ "brokenbar",       0x00a6 },
+   //{ "ring",            0x00b0 },
+   //{ "plusminus",       0x00b1 },
+   { "super2",          0x00b2 },
+   { "super3",          0x00b3 },
+   { "acute",           0x00b4 },
+   //{ "mu",              0x00b5 },
+   //{ "ordmasculine",    0x00ba },
+   //{ "questiondown",    0x00bf },
+   //{ "gemandbls",       0x00df },
+   //{ "agrave",          0x00e0 },
+   //{ "aacute",          0x00e1 },
+   //{ "acircumflex",     0x00e2 },
+   //{ "atilde",          0x00e3 },
+   //{ "adieresis",       0x00e4 },
+   //{ "aring",           0x00e5 },
+   //{ "ae",              0x00e6 },
+   //{ "ccedille",        0x00e7 },
+   //{ "egrave",          0x00e8 },
+   //{ "eacute",          0x00e9 },
+   //{ "ecircumflex",     0x00ea },
+   //{ "edieresis",       0x00eb },
+   //{ "igrave",          0x00ec },
+   //{ "iacute",          0x00ed },
+   //{ "icircumflex",     0x00ee },
+   //{ "idieresis",       0x00ef },
+   //{ "ntilde",          0x00f1 },
+   //{ "ograve",          0x00f2 },
+   //{ "oacute",          0x00f3 },
+   //{ "ocircumflex",     0x00f4 },
+   //{ "otilde",          0x00f5 },
+   //{ "odieresis",       0x00f6 },
+   //{ "divide",          0x00f7 },
+   //{ "oslash",          0x00f8 },
+   //{ "ugrave",          0x00f9 },
+   //{ "uacute",          0x00fa },
+   //{ "ucircumflex",     0x00fb },
+   //{ "udieresis",       0x00fc },
+   //{ "ygrave",          0x00fd },
+   //{ "thorn",           0x00fe },
+   //{ "ydieresis",       0x00ff },
+   { "nomatch",         0xFFFF }
+};
+
+
+////Device Event Types
+//#define SI_UNKNOWN   0x01
+//#define SI_BUTTON    0x02
+//#define SI_POV       0x03
+//#define SI_XPOV      0x04
+//#define SI_YPOV      0x05
+//#define SI_UPOV      0x06
+//#define SI_DPOV      0x07
+//#define SI_LPOV      0x08
+//#define SI_RPOV      0x09
+//#define SI_KEY       0x0A
+//#define SI_XAXIS     0x0B
+//#define SI_YAXIS     0x0C
+//#define SI_ZAXIS     0x0D
+//#define SI_RXAXIS    0x0E
+//#define SI_RYAXIS    0x0F
+//#define SI_RZAXIS    0x10
+//#define SI_SLIDER    0x11

+ 14 - 3
engine/source/input/actionMap.h

@@ -23,6 +23,7 @@
 #ifndef _ACTIONMAP_H_
 #ifndef _ACTIONMAP_H_
 #define _ACTIONMAP_H_
 #define _ACTIONMAP_H_
 
 
+
 #ifndef _PLATFORM_H_
 #ifndef _PLATFORM_H_
 #include "platform/platform.h"
 #include "platform/platform.h"
 #endif
 #endif
@@ -61,7 +62,8 @@ class ActionMap : public SimObject
          HasScale    = BIT(1),   ///< Scaled input.
          HasScale    = BIT(1),   ///< Scaled input.
          HasDeadZone = BIT(2),   ///< Dead zone is present.
          HasDeadZone = BIT(2),   ///< Dead zone is present.
          Inverted    = BIT(3),   ///< Input is inverted.
          Inverted    = BIT(3),   ///< Input is inverted.
-         BindCmd     = BIT(4)    ///< Bind a console command to this.
+		 NonLinear   = BIT(4),
+         BindCmd     = BIT(5)    ///< Bind a console command to this.
       };
       };
 
 
       U32 flags;           /// @see Node::Flags
       U32 flags;           /// @see Node::Flags
@@ -134,6 +136,9 @@ class ActionMap : public SimObject
 
 
    static const char* getModifierString(const U32 modifiers);
    static const char* getModifierString(const U32 modifiers);
 
 
+   /// Pass index to a break entry, and this function will fire it off.
+   static void fireBreakEvent(U32 idx, F32 value = 0.f);
+
   public:
   public:
    ActionMap();
    ActionMap();
    ~ActionMap();
    ~ActionMap();
@@ -161,8 +166,14 @@ class ActionMap : public SimObject
    static const char* buildActionString( const InputEvent* event );
    static const char* buildActionString( const InputEvent* event );
 
 
    bool processAction(const InputEvent*);
    bool processAction(const InputEvent*);
-
-   static bool checkBreakTable(const InputEvent*);
+   bool processLeap(const InputEvent*);
+   bool processGesture(const InputEvent*);
+   bool processTouch(const InputEvent*);
+   bool processButton(const InputEvent*);
+   bool processMove(const InputEvent*);
+   bool processMotion(const InputEvent*);
+   bool processXInput(const InputEvent*);
+   static bool checkBreakTable(const InputEvent*);   
    static bool handleEvent(const InputEvent*);
    static bool handleEvent(const InputEvent*);
    static bool handleEventGlobal(const InputEvent*);
    static bool handleEventGlobal(const InputEvent*);
 
 

+ 176 - 0
engine/source/input/leapMotion/LeapMotionManager_ScriptBinding.h

@@ -0,0 +1,176 @@
+//-----------------------------------------------------------------------------
+// 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.
+//-----------------------------------------------------------------------------
+
+ConsoleFunction(initLeapMotionManager, void, 1, 1, "() Initialize the LeapMotionManager")
+{
+    if (gLeapMotionManager != NULL)
+    {
+        Con::printf("LeapMotionManager already initialized");
+    }
+    else
+    {
+        gLeapMotionManager = new LeapMotionManager();
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleFunction(enableLeapMotionManager, void, 2, 2, "(bool enabledState) Run or pause the LeapMotionManager.\n"
+                                                     "@param enabledState True to turn it on, false otherwise")
+{
+    if (gLeapMotionManager == NULL)
+    {
+        Con::printf("LeapMotionManager not initialized. Call initLeapMotionManager() first");
+    }
+    else
+    {
+        gLeapMotionManager->enable(dAtob(argv[1]));
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleFunction(isLeapMotionManagerEnabled, bool, 1, 1, "() Checks the LeapMotionManager to see if it is enabled.\n"
+                                                        "@return True if it's running, false otherwise")
+{
+    if (gLeapMotionManager == NULL)
+    {
+        Con::printf("LeapMotionManager not initialized. Call initLeapMotionManager() first");
+        return false;
+    }
+    else
+    {
+        return gLeapMotionManager->getEnabled();
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleFunction(enableLeapCursorControl, void, 2, 2, "(bool enabledState) Toggles the manager to act like a mouse.\n"
+                                                        "@param enabledState True to act like a mouse, false otherwise")
+{
+    if (gLeapMotionManager == NULL)
+    {
+        Con::printf("LeapMotionManager not initialized. Call initLeapMotionManager() first");
+    }
+    else
+    {
+        return gLeapMotionManager->toggleMouseControl(dAtob(argv[1]));
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleFunction(isLeapCursorControlled, bool, 1, 1, "() Checks the LeapMotionManager to see if it is controlling the mouse.\n"
+        "@return True if it's acting as a mouse, false otherwise")
+{
+    if (gLeapMotionManager == NULL)
+    {
+        Con::printf("LeapMotionManager not initialized. Call initLeapMotionManager() first");
+        return false;
+    }
+    else
+    {
+        return gLeapMotionManager->getMouseControlToggle();
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+ConsoleFunction(configureLeapGesture, bool, 3, 3,   "(gestureString, value) Modified a Config string on the main "
+                                                    "Controller, via the LeapMotionManager. The following strings are allowed:\n"
+                                                    "Gesture.Circle.MinProgress\n"
+                                                    "Gesture.Circle.MinRadius\n"
+                                                    "Gesture.Circle.MinArc\n"
+                                                    "Gesture.Swipe.MinLength\n"
+                                                    "Gesture.Swipe.MinVelocity\n"
+                                                    "Gesture.KeyTap.MinDownVelocity\n"
+                                                    "Gesture.KeyTap.HistorySeconds\n"
+                                                    "Gesture.KeyTap.MinDistance\n"
+                                                    "Gesture.ScreenTap.MinForwardVelocity\n"
+                                                    "Gesture.ScreenTap.HistorySeconds\n"
+                                                    "Gesture.ScreenTap.MinDistance\n"
+                                                    "@param gestureString The Config string to be set\n"
+                                                    "@param value The new value for the Config string\n"
+                                                    "@return True if string was successfully set, false otherwise")
+{
+    if (gLeapMotionManager == NULL)
+    {
+        Con::printf("LeapMotionManager not initialized. Call initLeapMotionManager() first");
+        return false;
+    }
+    else
+    {
+        if (!dStrcmp("Gesture.Circle.MinProgress", argv[1]))
+        {
+            return gLeapMotionManager->setMinCircleProgress(dAtof(argv[2]));
+        }
+        else
+        {
+            return gLeapMotionManager->configureLeapGesture(argv[1], dAtof(argv[2]));
+        }
+    }
+}
+
+ConsoleFunction(getPointFromProjection, const char*, 2, 4, "(x, y, z) - Gets the closest point on the screen to a point in space using Leap::Screen::project().\n"
+                                                             "@param x The x component of the finger position.\n"
+                                                             "@param y The y component of the finger position.\n"
+                                                             "@param z The z component of the finger position.\n\n"
+                                                             "@return An \"x y\" position of where the finger intersects with the screen.")
+{
+    // The new position.
+    Point3F pos;
+
+    if(argc == 2)
+    {
+        dSscanf(argv[1], "%g %g %g", &pos.x, &pos.y, &pos.z);
+    }
+    else if (argc == 4)
+    {
+        pos.x = dAtof(argv[1]);
+        pos.y = dAtof(argv[2]);
+        pos.z = dAtof(argv[3]);
+    }
+    else
+    {
+        Con::warnf("getPointFromProjection() - Invalid number of parameters!");
+        return "";
+    }
+
+   return gLeapMotionManager->getPointFromProjection(pos).scriptThis();
+   
+   
+}
+
+ConsoleFunction(getPointFromIntersection, const char*, 2, 2, "(fingerID) - Gets the point of intersection between the screen and a ray "
+                                                             "projected from a Pointable object using the Screen::intersect() function\n"
+                                                             "@param fingerID The finger ID, which will be grabbed from the last frame.\n\n"
+                                                             "@return An \"x y\" position of where the finger intersects with the screen.")
+{
+    if(argc < 2)
+    {
+        Con::warnf("getPointFromIntersection() - Invalid number of parameters!");
+        return "";
+    }
+
+    return gLeapMotionManager->getPointFromIntersection(dAtoi(argv[1])).scriptThis();
+}

+ 35 - 0
engine/source/input/leapMotion/leapMotionConstants.h

@@ -0,0 +1,35 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 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 _LEAPMOTIONCONSTANTS_H_
+#define _LEAPMOTIONCONSTANTS_H_
+
+namespace LeapMotionConstants
+{
+    enum Constants
+    {
+        MaxHands             = 2,
+        MaxPointablesPerHand = 5,
+    };
+}
+
+#endif   // _LEAPMOTIONCONSTANTS_H_

+ 674 - 0
engine/source/input/leapMotion/leapMotionManager.cc

@@ -0,0 +1,674 @@
+//-----------------------------------------------------------------------------
+// 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 _LEAPMOTIONMANAGER_H_
+#include "input/leapMotion/leapMotionManager.h"
+#endif
+
+#ifndef _PLATFORM_MEMORY_H_
+#include "platform/platformMemory.h"
+#endif 
+
+#ifndef _CONSOLETYPES_H_
+#include "console/consoleTypes.h"
+#endif
+
+#ifndef _EVENT_H_
+#include "platform/event.h"
+#endif
+
+#ifndef _GAMEINTERFACE_H_
+#include "game/gameInterface.h"
+#endif
+
+#ifndef _GUICANVAS_H_
+#include "gui/guiCanvas.h"
+#endif
+
+#ifndef _LEAPMOTIONUTIL_H_
+#include "leapMotionUtil.h"
+#endif
+
+#ifndef _VECTOR2_H_
+#include "2d/core/Vector2.h"
+#endif
+
+#include "input/leapMotion/LeapMotionManager_ScriptBinding.h"
+
+//-----------------------------------------------------------------------------
+
+bool LeapMotionManager::smEnableDevice = true;
+
+bool LeapMotionManager::smGenerateIndividualEvents = true;
+bool LeapMotionManager::smKeepHandIndexPersistent = false;
+bool LeapMotionManager::smKeepPointableIndexPersistent = false;
+
+bool LeapMotionManager::smGenerateSingleHandRotationAsAxisEvents = false;
+
+F32 LeapMotionManager::smMaximumHandAxisAngle = 25.0f;
+
+bool LeapMotionManager::smGenerateWholeFrameEvents = false;
+
+U32 LeapMotionManager::LM_FRAMEVALIDDATA = 0;
+U32 LeapMotionManager::LM_HAND[LeapMotionConstants::MaxHands] = {0};
+//U32 LeapMotionManager::LM_HANDROT[LeapMotionConstants::MaxHands] = {0};
+U32 LeapMotionManager::LM_HANDAXISX = 0;
+U32 LeapMotionManager::LM_HANDAXISY = 0;
+U32 LeapMotionManager::LM_HANDPOINTABLE[LeapMotionConstants::MaxHands][LeapMotionConstants::MaxPointablesPerHand] = {0};
+U32 LeapMotionManager::LM_HANDPOINTABLEROT[LeapMotionConstants::MaxHands][LeapMotionConstants::MaxPointablesPerHand] = {0};
+U32 LeapMotionManager::LM_FRAME = 0;
+
+//-----------------------------------------------------------------------------
+
+LeapMotionManager::LeapMotionManager()
+{
+    // Initialize the console variables
+    staticInit();
+
+    // Create our controller and listener
+    mListener = new MotionListener();
+    mController = new Leap::Controller();
+    mController->addListener(*mListener);
+
+    // Allocate a mutex to use later
+    mActiveMutex = Mutex::createMutex();
+
+    // Nothing is ready yet
+    mEnabled = false;
+    mActive = false;
+    mMouseControl = false;
+    mMinCircleProgress = 0.0;
+}
+
+//-----------------------------------------------------------------------------
+
+LeapMotionManager::~LeapMotionManager()
+{
+    // Disable and delete internal members
+    disable();
+
+    // Get rid of the mutex
+    Mutex::destroyMutex(mActiveMutex);
+}
+
+//-----------------------------------------------------------------------------
+
+void LeapMotionManager::staticInit()
+{
+    // If true, the Leap Motion device will be enabled, if present
+    Con::addVariable("pref::LeapMotion::EnableDevice", TypeBool, &smEnableDevice);
+   
+    // Indicates that events for each hand and pointable will be created.
+    Con::addVariable("LeapMotion::GenerateIndividualEvents", TypeBool, &smGenerateIndividualEvents);
+      
+	// Indicates that we track hand IDs and will ensure that the same hand will remain at the same index between frames.   
+    Con::addVariable("LeapMotion::KeepHandIndexPersistent", TypeBool, &smKeepHandIndexPersistent);
+    
+	// Indicates that we track pointable IDs and will ensure that the same pointable will remain at the same index between frames.   
+    Con::addVariable("LeapMotion::KeepPointableIndexPersistent", TypeBool, &smKeepPointableIndexPersistent);
+    
+	// If true, broadcast single hand rotation as axis events.
+    Con::addVariable("LeapMotion::GenerateSingleHandRotationAsAxisEvents", TypeBool, &smGenerateSingleHandRotationAsAxisEvents);
+    
+	// The maximum hand angle when used as an axis event as measured from a vector pointing straight up (in degrees).
+    // Shoud range from 0 to 90 degrees.   
+    Con::addVariable("LeapMotion::MaximumHandAxisAngle", TypeF32, &smMaximumHandAxisAngle);
+    
+    // Indicates that a whole frame event should be generated and frames should be buffered.
+    Con::addVariable("LeapMotion::GenerateWholeFrameEvents", TypeBool, &smGenerateWholeFrameEvents);   
+}
+
+//-----------------------------------------------------------------------------
+
+void LeapMotionManager::enable(bool enabledState)
+{
+    Mutex::lockMutex(mActiveMutex);
+    mEnabled = enabledState;
+    Mutex::unlockMutex(mActiveMutex);
+}
+
+//-----------------------------------------------------------------------------
+
+void LeapMotionManager::disable()
+{
+    if (mController)
+    {
+        delete mController;
+        mController = NULL;
+
+        if (mListener)
+        {
+            delete mListener;
+            mListener = NULL;
+        }
+    }
+
+    setActive(false);
+    mEnabled = false;
+}
+
+//-----------------------------------------------------------------------------
+
+bool LeapMotionManager::getActive()
+{
+    Mutex::lockMutex(mActiveMutex);
+    bool active = mActive;
+    Mutex::unlockMutex(mActiveMutex);
+
+    return active;
+}
+
+//-----------------------------------------------------------------------------
+
+void LeapMotionManager::setActive(bool state)
+{
+    Mutex::lockMutex(mActiveMutex);
+    mActive = state;
+    Mutex::unlockMutex(mActiveMutex);
+}
+
+//-----------------------------------------------------------------------------
+
+void LeapMotionManager::toggleMouseControl(bool enabledState)
+{
+    Mutex::lockMutex(mActiveMutex);
+    mMouseControl = enabledState;
+    Mutex::unlockMutex(mActiveMutex);
+}
+
+//-----------------------------------------------------------------------------
+
+bool LeapMotionManager::getMouseControlToggle()
+{
+    Mutex::lockMutex(mActiveMutex);
+    bool mouseControlled = mMouseControl;
+    Mutex::unlockMutex(mActiveMutex);
+
+    return mouseControlled;
+}
+
+//-----------------------------------------------------------------------------
+// You can get and set gesture configuration parameters using the Config object
+// obtained from a connected Controller object. The key strings required to
+// * identify a configuration parameter include:
+//*
+//* Key string | Value type | Default value | Units
+//* -----------|------------|---------------|------
+//* Gesture.Circle.MinRadius | float | 5.0 | mm
+//* Gesture.Circle.MinArc | float | 1.5 | radians
+//* Gesture.Swipe.MinLength | float | 150 | mm
+//* Gesture.Swipe.MinVelocity | float | 1000 | mm/s
+//* Gesture.KeyTap.MinDownVelocity | float | 50 | mm/s
+//* Gesture.KeyTap.HistorySeconds | float | 0.1 | s
+//* Gesture.KeyTap.MinDistance | float | 3.0 | mm
+//* Gesture.ScreenTap.MinForwardVelocity  | float | 50 | mm/s
+//* Gesture.ScreenTap.HistorySeconds | float | 0.1 | s
+//* Gesture.ScreenTap.MinDistance | float | 5.0 | mm
+bool LeapMotionManager::configureLeapGesture(const char* configString, const F32 value)
+{
+    // Get this controller's config.
+    Leap::Config config = mController->config();
+    
+    // Convert and pass the key, along with the value.
+    std::string *keyString = new std::string(configString);
+    bool success = config.setFloat(*keyString, value);
+    
+    // Free memory and return the result.
+    delete keyString;
+    return success;
+}
+
+//-----------------------------------------------------------------------------
+
+bool LeapMotionManager::setMinCircleProgress(const F32 value)
+{
+    mMinCircleProgress = value;
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+void LeapMotionManager::processHand(const Leap::Hand& hand, S32 id)
+{
+    // Get hand (palm) position
+    /*Point3F rawHandPosition;
+    Point3I convertedHandPosition;
+    
+    LeapMotionUtil::convertPosition(hand.palmPosition(), rawHandPosition);
+    convertedHandPosition.x = (S32)mFloor(rawHandPosition.x);
+    convertedHandPosition.y = (S32)mFloor(rawHandPosition.y);
+    convertedHandPosition.z = (S32)mFloor(rawHandPosition.z);*/
+
+    // Get the hand's normal vector and direction
+    const Leap::Vector normal = hand.palmNormal();
+    const Leap::Vector direction = hand.direction();
+
+    F32 pitch = direction.pitch() * Leap::RAD_TO_DEG;
+    F32 roll = normal.roll() * Leap::RAD_TO_DEG;
+    F32 yaw = direction.yaw() * Leap::RAD_TO_DEG;
+
+    // Position Event
+    InputEvent handPosEvent;
+
+    handPosEvent.deviceInst = 0;
+    handPosEvent.iValue = id;
+    handPosEvent.fValues[0] = hand.palmPosition().x;
+    handPosEvent.fValues[1] = hand.palmPosition().y;
+    handPosEvent.fValues[2] = hand.palmPosition().z;
+    handPosEvent.deviceType = LeapMotionDeviceType;
+    handPosEvent.objType = LM_HANDPOS;
+    handPosEvent.objInst = 0;
+    handPosEvent.action = SI_LEAP;
+    handPosEvent.modifier = 0;
+
+    InputEvent handRotEvent;
+
+    handRotEvent.deviceInst = 0;
+    handRotEvent.iValue = id;
+    handRotEvent.fValues[0] = yaw;
+    handRotEvent.fValues[1] = pitch;
+    handRotEvent.fValues[2] = roll;
+    handRotEvent.objType = LM_HANDROT;
+    handRotEvent.deviceType = LeapMotionDeviceType;
+    handRotEvent.objInst = 0;
+    handRotEvent.action = SI_LEAP;
+    handRotEvent.modifier = 0;
+
+    Game->postEvent(handPosEvent);
+    Game->postEvent(handRotEvent);
+}
+
+//-----------------------------------------------------------------------------
+
+void LeapMotionManager::processHandPointables(const Leap::PointableList& pointables)
+{
+    InputEvent pointablePositionEvent;
+    pointablePositionEvent.deviceInst = 0;
+    pointablePositionEvent.objInst = 0;
+    pointablePositionEvent.modifier = 0;
+    pointablePositionEvent.deviceType = LeapMotionDeviceType;
+    pointablePositionEvent.objType = LM_FINGERPOS;    
+    pointablePositionEvent.action = SI_LEAP;
+    
+    for (int f = 0; f < pointables.count(); ++f)
+    {
+        Leap::Pointable pointable = pointables[f];
+        
+        char charHolder[10];
+        Leap::Vector tipPosition = pointables[f].tipPosition();
+
+        dItoa((S32)tipPosition.x, charHolder);
+        dStrcat(pointablePositionEvent.fingersX, charHolder);
+        dStrcat(pointablePositionEvent.fingersX, " ");
+
+        dItoa((S32)tipPosition.y, charHolder);
+        dStrcat(pointablePositionEvent.fingersY, charHolder);
+        dStrcat(pointablePositionEvent.fingersY, " ");
+
+        dItoa((S32)tipPosition.z, charHolder);
+        dStrcat(pointablePositionEvent.fingersZ, charHolder);
+        dStrcat(pointablePositionEvent.fingersZ, " ");
+
+        dItoa(pointables[f].id(), charHolder);
+        dStrcat(pointablePositionEvent.fingerIDs, charHolder);
+        dStrcat(pointablePositionEvent.fingerIDs, " ");        
+    }
+    
+    // Post
+    Game->postEvent(pointablePositionEvent);
+}
+
+//-----------------------------------------------------------------------------
+
+void LeapMotionManager::processGestures(const Leap::GestureList& gestures)
+{
+    for (int g = 0; g < gestures.count(); ++g)
+    {
+        Leap::Gesture gesture = gestures[g];
+
+        switch (gesture.type())
+        {
+            case Leap::Gesture::TYPE_CIRCLE:
+            {
+                Leap::CircleGesture circle = gesture;
+
+                if (circle.progress() < mMinCircleProgress)
+                    break;
+
+                bool clockWise;
+
+                if (circle.pointable().direction().angleTo(circle.normal()) <= Leap::PI/4) 
+                    clockWise = true;
+                else 
+                    clockWise = false;
+
+                InputEvent event;
+            
+                event.deviceInst = 0;
+                event.iValue = g;
+                event.fValues[0] = circle.progress();
+                event.fValues[1] = circle.radius();
+                event.fValues[2] = clockWise;
+                event.fValues[3] = (F32)circle.state();
+                event.deviceType = LeapMotionDeviceType;
+                event.objType = SI_CIRCLE_GESTURE;
+                event.objInst = 0;
+                event.action = SI_GESTURE;
+                event.modifier = 0;
+            
+                Game->postEvent(event);
+                break;
+            }
+            case Leap::Gesture::TYPE_SWIPE:
+            {
+                Leap::SwipeGesture swipe = gesture;
+
+                // Comment to post begin and update swipes
+                if (swipe.state() != Leap::Gesture::STATE_STOP)
+                {
+                    break;
+                }
+
+                InputEvent event;
+            
+                event.deviceInst = 0;
+                event.iValue = g;
+                event.fValues[0] = (F32)swipe.state();
+                event.fValues[1] = swipe.direction().x;
+                event.fValues[2] = swipe.direction().y;
+                event.fValues[3] = swipe.direction().z;
+                event.fValues[4] = swipe.speed();
+                event.deviceType = LeapMotionDeviceType;
+                event.objType = SI_SWIPE_GESTURE;
+                event.objInst = 0;
+                event.action = SI_GESTURE;
+                event.modifier = 0;
+            
+                Game->postEvent(event);
+                break;
+            }
+            case Leap::Gesture::TYPE_KEY_TAP:
+            {
+                Leap::KeyTapGesture tap = gesture;
+
+                InputEvent event;
+            
+                event.deviceInst = 0;
+                event.iValue = g;
+                event.fValues[0] = tap.position().x;
+                event.fValues[1] = tap.position().y;
+                event.fValues[2] = tap.position().z;
+                event.fValues[3] = tap.direction().x;
+                event.fValues[4] = tap.direction().y;
+                event.fValues[5] = tap.direction().z;
+                event.deviceType = LeapMotionDeviceType;
+                event.objType = SI_KEYTAP_GESTURE;
+                event.objInst = 0;
+                event.action = SI_GESTURE;
+                event.modifier = 0;
+            
+                Game->postEvent(event);
+                break;
+            }
+            case Leap::Gesture::TYPE_SCREEN_TAP:
+            {
+                Leap::ScreenTapGesture screentap = gesture;
+
+                InputEvent event;
+            
+                event.deviceInst = 0;
+                event.iValue = g;
+                event.fValues[0] = screentap.position().x;
+                event.fValues[1] = screentap.position().y;
+                event.fValues[2] = screentap.position().z;
+                event.fValues[3] = screentap.direction().x;
+                event.fValues[4] = screentap.direction().y;
+                event.fValues[5] = screentap.direction().z;
+                event.deviceType = LeapMotionDeviceType;
+                event.objType = SI_SCREENTAP_GESTURE;
+                event.objInst = 0;
+                event.action = SI_GESTURE;
+                event.modifier = 0;
+            
+                Game->postEvent(event);
+                break;
+            }
+            default:
+                Con::warnf("LeapMotionManager::process() - Unknown gesture detected");
+                break;
+        }
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+void LeapMotionManager::process(const Leap::Controller& controller)
+{
+    // Is the manager enabled?
+    if (!mEnabled)
+        return;
+
+    // Was the leap device activated
+    if (!getActive())
+        return;
+
+    // Get the current frame
+    const Leap::Frame frame = controller.frame();
+
+    if (!frame.isValid())
+        return;
+
+    mLastFrame = frame;
+
+    // Get gestures
+    const Leap::GestureList gestures = frame.gestures();
+
+    if (!gestures.isEmpty())
+        processGestures(gestures);
+
+    if (getMouseControlToggle())
+    {
+        generateMouseEvent(controller);
+        return;
+    }
+
+    // Is a hand present?
+    if ( !frame.hands().isEmpty() ) 
+    {
+        for (int h = 0; h < frame.hands().count(); ++h)
+        {
+            const Leap::Hand hand = frame.hands()[h];
+
+            processHand(hand, h);
+            
+        }
+    }
+    
+    if (frame.pointables().count())
+    {
+        const Leap::PointableList pointables = frame.pointables();
+        
+        if (pointables.count())
+            processHandPointables(pointables);
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+void LeapMotionManager::generateMouseEvent(Leap::Controller const & controller)
+{
+    
+    const Leap::ScreenList screens = controller.locatedScreens();
+
+    // make sure we have a detected screen
+    if (screens.isEmpty())
+        return;
+
+    const Leap::Screen screen = screens[0];
+
+    // find the first finger or tool
+    const Leap::Frame frame = controller.frame();
+    const Leap::HandList hands = frame.hands();
+
+    if (hands.isEmpty())
+        return;
+
+    const Leap::PointableList pointables = hands[0].pointables();
+
+    if (pointables.isEmpty())
+        return;
+
+    const Leap::Pointable firstPointable = pointables[0];
+
+    // get x, y coordinates on the first screen
+    const Leap::Vector intersection = screen.intersect( firstPointable, true, 1.0f );
+
+    // if the user is not pointing at the screen all components of
+    // the returned vector will be Not A Number (NaN)
+    // isValid() returns true only if all components are finite
+    if (!intersection.isValid())
+        return;
+
+    F32 x = screen.widthPixels() * intersection.x;
+
+    // flip y coordinate to standard top-left origin
+    F32 y = screen.heightPixels() * (1.0f - intersection.y);
+
+    // Move the cursor
+    Point2I location((S32)x, (S32)y);
+    Canvas->setCursorPos(location);
+
+    // Build and postthe mouse event
+    MouseMoveEvent TorqueEvent;
+    TorqueEvent.xPos = (S32) location.x;
+    TorqueEvent.yPos = (S32) location.y;
+    Game->postEvent(TorqueEvent);
+}
+
+//-----------------------------------------------------------------------------
+
+Vector2 LeapMotionManager::getPointFromProjection(Point3F position)
+{
+    // Get the screen and projection
+    const Leap::Vector pointablePosition(position.x, position.y, position.z);
+    const Leap::Screen screen = mController->locatedScreens()[0];
+    const Leap::Vector projectedPosition = screen.project(pointablePosition, true, 1.0f);    
+
+    // if the user is not pointing at the screen all components of
+    // the returned vector will be Not A Number (NaN)
+    // isValid() returns true only if all components are finite
+    if (!projectedPosition.isValid())
+        return Vector2("");
+        
+    // Get the screen coordinates
+    F32 x = screen.widthPixels() * projectedPosition.x;
+        
+    // flip y coordinate to standard top-left origin
+    F32 y = screen.heightPixels() * (1.0f - projectedPosition.y);
+    
+    // Build the screenPosition and return it
+    Vector2 screenPosition;
+    screenPosition.x = x;
+    screenPosition.y = y;
+
+    return screenPosition;
+}
+
+//-----------------------------------------------------------------------------
+
+Vector2 LeapMotionManager::getPointFromIntersection(S32 pointableID)
+{
+    // Get the finger via ID and check for validity
+    Leap::Pointable lastPointable = mLastFrame.pointable(pointableID);
+
+    if (!lastPointable.isValid())
+        return Vector2("");
+
+    // Get the screen and intersection
+    const Leap::Screen screen = mController->locatedScreens()[0];
+    const Leap::Vector intersection = screen.intersect( lastPointable, true, 1.0f );
+    
+    // if the user is not pointing at the screen all components of
+    // the returned vector will be Not A Number (NaN)
+    // isValid() returns true only if all components are finite
+    if (!intersection.isValid())
+        return Vector2("");
+        
+    // Get the screen coordinates
+    F32 x = screen.widthPixels() * intersection.x;
+        
+    // flip y coordinate to standard top-left origin
+    F32 y = screen.heightPixels() * (1.0f - intersection.y);
+    
+    // Build the screenPosition and return it
+    Vector2 screenPosition;
+    screenPosition.x = x;
+    screenPosition.y = y;
+
+    return screenPosition;
+}
+
+//-----------------------------------------------------------------------------
+
+void LeapMotionManager::MotionListener::onInit(const Leap::Controller& controller)
+{
+    //Con::printf("MotionListener::onInit()");
+}
+
+//-----------------------------------------------------------------------------
+
+void LeapMotionManager::MotionListener::onFrame(const Leap::Controller& controller)
+{
+    gLeapMotionManager->process(controller);
+}
+
+//-----------------------------------------------------------------------------
+
+void LeapMotionManager::MotionListener::onConnect(const Leap::Controller& controller)
+{
+    gLeapMotionManager->setActive(true);
+    controller.enableGesture(Leap::Gesture::TYPE_CIRCLE);
+    controller.enableGesture(Leap::Gesture::TYPE_KEY_TAP);
+    controller.enableGesture(Leap::Gesture::TYPE_SCREEN_TAP);
+    controller.enableGesture(Leap::Gesture::TYPE_SWIPE);
+}
+
+//-----------------------------------------------------------------------------
+
+void LeapMotionManager::MotionListener::onDisconnect (const Leap::Controller& controller)
+{
+    gLeapMotionManager->setActive(false);
+}
+
+//-----------------------------------------------------------------------------
+
+void LeapMotionManager::MotionListener::onFocusGained(const Leap::Controller& controller)
+{
+    gLeapMotionManager->setActive(true);
+}
+
+//-----------------------------------------------------------------------------
+
+void LeapMotionManager::MotionListener::onFocusLost(const Leap::Controller& controller)
+{
+    gLeapMotionManager->setActive(false);
+}

+ 162 - 0
engine/source/input/leapMotion/leapMotionManager.h

@@ -0,0 +1,162 @@
+//-----------------------------------------------------------------------------
+// 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 _LEAPMOTIONMANAGER_H_
+#define _LEAPMOTIONMANAGER_H_
+
+#ifndef _UTILITY_H_
+#include "2d/core/Utility.h"
+#endif
+
+#ifndef _LEAPMOTIONCONSTANTS_H_
+#include "input/leapMotion/leapMotionConstants.h"
+#endif
+
+#ifndef __Leap_h__
+#include "Leap.h"
+#endif
+
+
+class LeapMotionManager
+{
+protected:
+    
+    bool mEnabled;
+    F32 mMinCircleProgress;
+
+    struct FingerEvent
+    {
+        S32 id;
+        F32 x;
+        F32 y;
+        F32 z;
+
+        FingerEvent( S32 ID, F32 X, F32 Y, F32 Z)
+        {
+            id = ID;
+            x = X;
+            y = Y;
+            z = Z;
+        }
+    };
+
+    class MotionListener : public Leap::Listener
+    {
+    public:
+        MotionListener() {}
+        virtual ~MotionListener() {}
+
+        virtual void onConnect (const Leap::Controller& controller);
+        virtual void onDisconnect (const Leap::Controller& controller);
+        virtual void onInit(const Leap::Controller& controller);
+        virtual void onFrame(const Leap::Controller& controller);
+        virtual void onFocusGained(const Leap::Controller& controller);
+        virtual void onFocusLost(const Leap::Controller& controller);
+   };
+
+    /// The connection to the Leap Motion
+    Leap::Controller* mController;
+
+    /// Our Leap Motion listener class
+    MotionListener* mListener;
+
+    /// Used with the LM listener object
+    void* mActiveMutex;
+
+    /// Is the Leap Motion active
+    bool mActive;
+
+    /// Is the Manager acting like a mouse
+    bool mMouseControl;
+
+    /// Last stored frame
+    Leap::Frame mLastFrame;
+
+public:
+    static bool smEnableDevice;
+
+    // Indicates that events for each hand and pointable will be created
+    static bool smGenerateIndividualEvents;
+
+    // Indicates that we track hand IDs and will ensure that the same hand
+    // will remain at the same index between frames.
+    static bool smKeepHandIndexPersistent;
+
+    // Indicates that we track pointable IDs and will ensure that the same
+    // pointable will remain at the same index between frames.
+    static bool smKeepPointableIndexPersistent;
+
+    // Broadcast single hand rotation as axis
+    static bool smGenerateSingleHandRotationAsAxisEvents;
+
+    // The maximum hand angle when used as an axis event
+    // as measured from a vector pointing straight up (in degrees)
+    static F32 smMaximumHandAxisAngle;
+
+    // Indicates that a whole frame event should be generated and frames
+    // should be buffered.
+    static bool smGenerateWholeFrameEvents;
+
+    // Frame action codes
+    static U32 LM_FRAMEVALIDDATA;    // SI_BUTTON
+
+    // Hand action codes
+    static U32 LM_HAND[LeapMotionConstants::MaxHands];    // SI_POS
+    //static U32 LM_HANDROT[LeapMotionConstants::MaxHands]; // SI_ROT
+
+    static U32 LM_HANDAXISX;   // SI_AXIS
+    static U32 LM_HANDAXISY;
+
+    // Pointables action codes
+    static U32 LM_HANDPOINTABLE[LeapMotionConstants::MaxHands][LeapMotionConstants::MaxPointablesPerHand];    // SI_POS
+    static U32 LM_HANDPOINTABLEROT[LeapMotionConstants::MaxHands][LeapMotionConstants::MaxPointablesPerHand]; // SI_ROT
+
+    // Whole frame
+    static U32 LM_FRAME;    // SI_INT
+
+public:
+    LeapMotionManager();
+    ~LeapMotionManager();
+
+    static void staticInit();
+
+    void enable(bool enabledState);
+    void disable();
+    bool getEnabled() { return mEnabled; }
+    bool getActive();
+    void setActive(bool enabledState);
+    void toggleMouseControl(bool enabledState);
+    bool getMouseControlToggle();
+    bool setMinCircleProgress(const F32 value);
+    F32 getMinCircleProgress() { return mMinCircleProgress; }
+    bool configureLeapGesture(const char* configString, const F32 value);
+    void process(const Leap::Controller& controller);
+    void processHand(const Leap::Hand& hand, S32 id);
+    void processHandPointables(const Leap::PointableList& pointables);
+    void processGestures(const Leap::GestureList& gestures);
+    void generateMouseEvent(const Leap::Controller& controller);
+    Vector2 getPointFromProjection(Point3F position);
+    Vector2 getPointFromIntersection(S32 pointableID);
+};
+
+static LeapMotionManager* gLeapMotionManager;
+
+#endif

+ 109 - 0
engine/source/input/leapMotion/leapMotionUtil.cpp

@@ -0,0 +1,109 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 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.
+//-----------------------------------------------------------------------------
+
+#include "input/leapMotion/leapMotionUtil.h"
+
+namespace LeapMotionUtil
+{
+
+void convertPosition(const Leap::Vector& inPosition, F32& x, F32& y, F32& z)
+{
+    // Convert to Torque coordinates.  The conversion is:
+    //
+    // Motion       Torque
+    // x y z   -->  x -z y
+    x = inPosition.x;    // x = x
+    y = -inPosition.z;   // y = -z
+    z = inPosition.y;    // z = y;
+}
+
+void convertPosition(const Leap::Vector& inPosition, Point3F& outPosition)
+{
+    // Convert to Torque coordinates.  The conversion is:
+    //
+    // Motion       Torque
+    // x y z   -->  x -z y
+    outPosition.x = inPosition.x;    // x = x
+    outPosition.y = -inPosition.z;   // y = -z
+    outPosition.z = inPosition.y;    // z = y;
+}
+
+void convertHandRotation(const Leap::Hand& hand, MatrixF& outRotation)
+{
+    // We need to convert from Motion coordinates to
+    // Torque coordinates.  The conversion is:
+    //
+    // Motion                       Torque
+    // a b c         a  b  c        a -c  b
+    // d e f   -->  -g -h -i  -->  -g  i -h
+    // g h i         d  e  f        d -f  e
+    const Leap::Vector& handToFingers = hand.direction();
+    Leap::Vector handFront = -handToFingers;
+    const Leap::Vector& handDown = hand.palmNormal();
+    Leap::Vector handUp = -handDown;
+    Leap::Vector handRight = handUp.cross(handFront);
+
+    outRotation.setColumn(0, Point4F(  handRight.x, -handRight.z,  handRight.y,  0.0f));
+    outRotation.setColumn(1, Point4F( -handFront.x,  handFront.z, -handFront.y,  0.0f));
+    outRotation.setColumn(2, Point4F(  handUp.x,    -handUp.z,     handUp.y,     0.0f));
+    outRotation.setPosition(Point3F::Zero);
+}
+
+void calculateHandAxisRotation(const MatrixF& handRotation, const F32& maxHandAxisRadius, Point2F& outRotation)
+{
+    const VectorF& controllerUp = handRotation.getUpVector();
+    outRotation.x = controllerUp.x;
+    outRotation.y = controllerUp.y;
+
+    // Limit the axis angle to that given to us
+    if (outRotation.len() > maxHandAxisRadius)
+    {
+        outRotation.normalize(maxHandAxisRadius);
+    }
+
+    // Renormalize to the range of 0..1
+    if (maxHandAxisRadius != 0.0f)
+    {
+        outRotation /= maxHandAxisRadius;
+    }
+}
+
+void convertPointableRotation(const Leap::Pointable& pointable, MatrixF& outRotation)
+{
+    // We need to convert from Motion coordinates to
+    // Torque coordinates.  The conversion is:
+    //
+    // Motion                       Torque
+    // a b c         a  b  c        a -c  b
+    // d e f   -->  -g -h -i  -->  -g  i -h
+    // g h i         d  e  f        d -f  e
+    Leap::Vector pointableFront = -pointable.direction();
+    Leap::Vector pointableRight = Leap::Vector::up().cross(pointableFront);
+    Leap::Vector pointableUp = pointableFront.cross(pointableRight);
+
+    outRotation.setColumn(0, Point4F(  pointableRight.x, -pointableRight.z,  pointableRight.y,  0.0f));
+    outRotation.setColumn(1, Point4F( -pointableFront.x,  pointableFront.z, -pointableFront.y,  0.0f));
+    outRotation.setColumn(2, Point4F(  pointableUp.x,    -pointableUp.z,     pointableUp.y,     0.0f));
+    outRotation.setPosition(Point3F::Zero);
+}
+
+}

+ 48 - 0
engine/source/input/leapMotion/leapMotionUtil.h

@@ -0,0 +1,48 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 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 _LEAPMOTIONUTIL_H_
+#define _LEAPMOTIONUTIL_H_
+
+#include "math/mPoint.h"
+#include "math/mMatrix.h"
+#include "Leap.h"
+
+namespace LeapMotionUtil
+{
+    /// Convert from a Leap Motion position to a Torque 3D position
+    void convertPosition(const Leap::Vector& inPosition, F32& x, F32& y, F32& z);
+
+    /// Convert from a Leap Motion position to a Torque 3D Point3F
+    void convertPosition(const Leap::Vector& inPosition, Point3F& outPosition);
+
+    /// Convert a Leap Motion hand's rotation to a Torque 3D matrix
+    void convertHandRotation(const Leap::Hand& hand, MatrixF& outRotation);
+
+    /// Calcualte a hand's rotation as if it were a thumb stick axis
+    void calculateHandAxisRotation(const MatrixF& handRotation, const F32& maxHandAxisRadius, Point2F& outRotation);
+
+    /// Convert a Leap Motion pointable's rotation to a Torque 3D matrix
+    void convertPointableRotation(const Leap::Pointable& pointable, MatrixF& outRotation);
+}
+
+#endif

+ 13 - 0
engine/source/math/mMatrix.h

@@ -149,6 +149,12 @@ public:
    /// This is the 4th column of the matrix.
    /// This is the 4th column of the matrix.
    void      setPosition( const Point3F &pos ){ setColumn( 3, pos ); }
    void      setPosition( const Point3F &pos ){ setColumn( 3, pos ); }
 
 
+   /// Get the z axis of the matrix.
+   ///
+   /// This is the 3rd column of the matrix and is
+   /// normally considered the up vector.   
+   VectorF getUpVector() const;
+
    MatrixF&  mul(const MatrixF &a);                    ///< M * a -> M
    MatrixF&  mul(const MatrixF &a);                    ///< M * a -> M
    MatrixF&  mul(const MatrixF &a, const MatrixF &b);  ///< a * b -> M
    MatrixF&  mul(const MatrixF &a, const MatrixF &b);  ///< a * b -> M
 
 
@@ -452,4 +458,11 @@ inline Point3F MatrixF::getPosition() const
    return pos;
    return pos;
 }
 }
 
 
+inline VectorF MatrixF::getUpVector() const
+{
+   VectorF vec;
+   getColumn( 2, &vec );
+   return vec;
+}
+
 #endif //_MMATRIX_H_
 #endif //_MMATRIX_H_

+ 55 - 0
engine/source/math/mPoint.cpp

@@ -0,0 +1,55 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 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.
+//-----------------------------------------------------------------------------
+
+#include "platform/platform.h"
+#include "math/mPoint.h"
+
+
+const Point2I Point2I::One(1, 1);
+const Point2I Point2I::Zero(0, 0);
+const Point2I Point2I::Min(S32_MIN, S32_MIN);
+const Point2I Point2I::Max(S32_MAX, S32_MAX);
+
+const Point2F Point2F::One(1.0f, 1.0f);
+const Point2F Point2F::Zero(0.0f, 0.0f);
+const Point2F Point2F::Min(F32_MIN, F32_MIN);
+const Point2F Point2F::Max(F32_MAX, F32_MAX);
+
+const Point2D Point2D::One(1.0, 1.0);
+const Point2D Point2D::Zero(0.0, 0.0);
+
+const Point3I Point3I::One(1, 1, 1);
+const Point3I Point3I::Zero(0, 0, 0);
+
+const Point3F Point3F::One(1.0f, 1.0f, 1.0f);
+const Point3F Point3F::Zero(0.0f, 0.0f, 0.0f);
+const Point3F Point3F::Min(F32_MIN, F32_MIN, F32_MIN);
+const Point3F Point3F::Max(F32_MAX, F32_MAX, F32_MAX);
+const Point3F Point3F::UnitX(1.0f, 0.0f, 0.0f);
+const Point3F Point3F::UnitY(0.0f, 1.0f, 0.0f);
+const Point3F Point3F::UnitZ(0.0f, 0.0f, 1.0f);
+
+const Point3D Point3D::One(1.0, 1.0, 1.0);
+const Point3D Point3D::Zero(0.0, 0.0, 0.0);
+
+const Point4F Point4F::One(1.0f, 1.0f, 1.0f, 1.0f);
+const Point4F Point4F::Zero(0.0f, 0.0f, 0.0f, 0.0f);

+ 45 - 0
engine/source/math/mPoint.h

@@ -82,6 +82,13 @@ class Point2I
 
 
    // Unary operators
    // Unary operators
    Point2I operator-() const;
    Point2I operator-() const;
+
+   //-------------------------------------- Public static constants
+  public:
+	const static Point2I One;
+	const static Point2I Zero;
+	const static Point2I Min;
+	const static Point2I Max;
 };
 };
 
 
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
@@ -135,6 +142,11 @@ class Point3I
 
 
    // Unary operators
    // Unary operators
    Point3I operator-() const;
    Point3I operator-() const;
+
+   //-------------------------------------- Public static constants
+public:
+   const static Point3I One;
+   const static Point3I Zero;
 };
 };
 
 
 
 
@@ -207,6 +219,14 @@ class Point2F
 
 
    // Unary operators
    // Unary operators
    Point2F operator-() const;
    Point2F operator-() const;
+
+   //-------------------------------------- Public static constants
+  public:
+	const static Point2F One;
+	const static Point2F Zero;
+	const static Point2F Min;
+	const static Point2F Max;
+
 };
 };
 
 
 
 
@@ -277,6 +297,11 @@ class Point2D
 
 
    // Unary operators
    // Unary operators
    Point2D operator-() const;
    Point2D operator-() const;
+
+//-------------------------------------- Public static constants
+  public:
+    const static Point2D One;
+    const static Point2D Zero;
 };
 };
 
 
 
 
@@ -349,6 +374,16 @@ class Point3F
 
 
    // Unary operators
    // Unary operators
    Point3F operator-() const;
    Point3F operator-() const;
+
+   //-------------------------------------- Public static constants
+public:
+   const static Point3F One;
+   const static Point3F Zero;
+   const static Point3F Max;
+   const static Point3F Min;
+   const static Point3F UnitX;
+   const static Point3F UnitY;
+   const static Point3F UnitZ;
 };
 };
 
 
 
 
@@ -416,6 +451,11 @@ class Point3D
 
 
    // Unary operators
    // Unary operators
    Point3D operator-() const;
    Point3D operator-() const;
+
+   //-------------------------------------- Public static constants
+public:
+   const static Point3D One;
+   const static Point3D Zero;
 };
 };
 
 
 
 
@@ -453,6 +493,11 @@ class Point4F
 
 
    operator F32*() { return (&x); }
    operator F32*() { return (&x); }
    operator F32*() const { return (F32*)(&x); }
    operator F32*() const { return (F32*)(&x); }
+
+   	//-------------------------------------- Public static constants
+  public:
+	const static Point4F One;
+	const static Point4F Zero;
 };
 };
 
 
 
 

+ 120 - 20
engine/source/platform/event.h

@@ -177,7 +177,8 @@ struct ScreenTouchEvent : public Event
 struct InputEvent : public Event
 struct InputEvent : public Event
 {
 {
    U32   deviceInst;  ///< Device instance: joystick0, joystick1, etc
    U32   deviceInst;  ///< Device instance: joystick0, joystick1, etc
-   float fValue;      ///< Value ranges from -1.0 to 1.0
+   S32 iValue;        ///< Handy for tracking IDs of things like fingers, hands, etc
+   float fValues[7];  ///< Stores the evemt data. Sometimes only one with a range of -1.0 - 1.0 is needed, other times it might be multiple vectors
    U16   deviceType;  ///< One of mouse, keyboard, joystick, unknown
    U16   deviceType;  ///< One of mouse, keyboard, joystick, unknown
    U16   objType;     ///< One of SI_XAXIS, SI_BUTTON, SI_KEY ...
    U16   objType;     ///< One of SI_XAXIS, SI_BUTTON, SI_KEY ...
    U16   ascii;       ///< ASCII character code if this is a keyboard event.
    U16   ascii;       ///< ASCII character code if this is a keyboard event.
@@ -186,13 +187,30 @@ struct InputEvent : public Event
    U8    modifier;    ///< Modifier to action: SI_LSHIFT, SI_LCTRL, etc.
    U8    modifier;    ///< Modifier to action: SI_LSHIFT, SI_LCTRL, etc.
 
 
    // iOS specific
    // iOS specific
-   char touchesX[256];    ///< Collection of x-coordinates for touches
-   char touchesY[256];    ///< Collection of y-coordinates for touches
-   char touchIDs[256];    ///< Collection of touch IDs
+   char fingersX[256];    ///< Collection of x-coordinates for fingers
+   char fingersY[256];    ///< Collection of y-coordinates for fingers
+   char fingersZ[256];    ///< Collection of Z-coordinates for fingers
+
+   char fingerIDs[256];    ///< Collection of touch IDs
     
     
-   InputEvent() { type = InputEventType; size = sizeof(InputEvent); dMemset(touchesX, 0, sizeof(touchesX)); 
-                                                                    dMemset(touchesY, 0, sizeof(touchesY));
-                                                                    dMemset(touchIDs, 0, sizeof(touchIDs));}
+    InputEvent()
+    { 
+        type = InputEventType; 
+        size = sizeof(InputEvent); 
+        deviceInst = 0;
+        iValue     = -1;
+        objType    = 0;
+        ascii      = 0;
+        objInst    = 0;
+        action     = 0;
+        modifier   = 0;
+        dMemset(fValues, 0, sizeof(fValues));
+        dMemset(fingersX, 0, sizeof(fingersX));
+        dMemset(fingersY, 0, sizeof(fingersY));
+        dMemset(fingersZ, 0, sizeof(fingersZ));
+        dMemset(fingerIDs, 0, sizeof(fingerIDs));
+    }
+
 };
 };
 
 
 /// @defgroup input_constants Input system constants
 /// @defgroup input_constants Input system constants
@@ -388,7 +406,34 @@ enum JoystickCodes {
    SI_UPOV2          = 0x214,
    SI_UPOV2          = 0x214,
    SI_DPOV2          = 0x215,
    SI_DPOV2          = 0x215,
    SI_LPOV2          = 0x216,
    SI_LPOV2          = 0x216,
-   SI_RPOV2          = 0x217
+   SI_RPOV2          = 0x217,
+
+//Xinput specific
+
+   XI_CONNECT        = 0x300,
+   XI_THUMBLX        = 0x301,
+   XI_THUMBLY        = 0x302,
+   XI_THUMBRX        = 0x303,
+   XI_THUMBRY        = 0x304,
+   XI_LEFT_TRIGGER   = 0x305,
+   XI_RIGHT_TRIGGER  = 0x306,
+
+   XI_DPAD_UP        = 0x206,
+   XI_DPAD_DOWN      = 0x207,
+   XI_DPAD_LEFT      = 0x208,
+   XI_DPAD_RIGHT     = 0x209,
+   
+   XI_START          = 0x311,
+   XI_BACK           = 0x312,
+   XI_LEFT_THUMB     = 0x313,
+   XI_RIGHT_THUMB    = 0x314,
+   XI_LEFT_SHOULDER  = 0x315,
+   XI_RIGHT_SHOULDER = 0x316,
+
+   XI_A              = 0x317,
+   XI_B              = 0x318,
+   XI_X              = 0x319,
+   XI_Y              = 0x320
 };
 };
 
 
 enum AccelerometerCodes
 enum AccelerometerCodes
@@ -416,9 +461,24 @@ enum TouchCodes
    SI_TOUCHDOWN   = 0x30C,
    SI_TOUCHDOWN   = 0x30C,
    SI_TOUCHUP     = 0x30D,
    SI_TOUCHUP     = 0x30D,
    SI_TOUCHMOVE   = 0x30E,
    SI_TOUCHMOVE   = 0x30E,
-   SI_PINCH       = 0x30F,
-   SI_SCALE       = 0x401,
-   SI_TAP         = 0x402
+};
+
+enum GestureCodes
+{
+    SI_CIRCLE_GESTURE    = 0x403,
+    SI_SWIPE_GESTURE     = 0x404,
+    SI_KEYTAP_GESTURE    = 0x405,
+    SI_SCREENTAP_GESTURE = 0x406,
+    SI_PINCH_GESTURE     = 0x407,
+    SI_SCALE_GESTURE     = 0x408
+};
+
+enum LeapMotionCodes
+{
+    LM_HANDAXIS          = 0x409,
+    LM_HANDROT           = 0x40A,
+    LM_HANDPOS           = 0x40B,
+    LM_FINGERPOS         = 0x40C,
 };
 };
 
 
 /// Input device types
 /// Input device types
@@ -428,9 +488,12 @@ enum InputDeviceTypes
    MouseDeviceType,
    MouseDeviceType,
    KeyboardDeviceType,
    KeyboardDeviceType,
    JoystickDeviceType,
    JoystickDeviceType,
+   GamepadDeviceType,
+   XInputDeviceType,
    ScreenTouchDeviceType,
    ScreenTouchDeviceType,
    AccelerometerDeviceType,
    AccelerometerDeviceType,
-   GyroscopeDeviceType
+   GyroscopeDeviceType,
+   LeapMotionDeviceType
 };
 };
 
 
 /// Device Event Action Types
 /// Device Event Action Types
@@ -438,16 +501,18 @@ enum InputDeviceTypes
 #define SI_BREAK     0x02
 #define SI_BREAK     0x02
 #define SI_MOVE      0x03
 #define SI_MOVE      0x03
 #define SI_REPEAT    0x04
 #define SI_REPEAT    0x04
+#define SI_VALUE	 0x05
 
 
 ///Device Event Types
 ///Device Event Types
-#define SI_UNKNOWN   0x01
-#define SI_BUTTON    0x02
-#define SI_POV       0x03
-#define SI_KEY       0x0A
-#define SI_TEXT      0x0B
-#define SI_TOUCH     0x0C
-#define SI_GESTURE   0x0D
-#define SI_MOTION    0x0F
+#define SI_UNKNOWN           0x01
+#define SI_BUTTON            0x02
+#define SI_POV               0x03
+#define SI_KEY               0x0A
+#define SI_TEXT              0x0B
+#define SI_TOUCH             0x0C
+#define SI_GESTURE           0x0D
+#define SI_MOTION            0x0F
+#define SI_LEAP              0x11
 
 
 /// Event SubTypes
 /// Event SubTypes
 #define SI_ANY       0xff
 #define SI_ANY       0xff
@@ -471,4 +536,39 @@ enum InputDeviceTypes
 
 
 /// @}
 /// @}
 
 
+//Xinput structs
+
+typedef U32 InputObjectInstances;
+
+enum XInputEventType
+{
+   XI_UNKNOWN = 0x01,
+   XI_BUTTON  = 0x02,   // Button press/release
+   XI_POV     = 0x03,   // Point of View hat
+   XI_AXIS    = 0x04,   // Axis in range -1.0..1.0
+   XI_POS     = 0x05,   // Absolute position value (Point3F)
+   XI_ROT     = 0x06,   // Absolute rotation value (QuatF)
+   XI_INT     = 0x07,   // Integer value (S32)
+   XI_FLOAT   = 0x08,   // Float value (F32)
+   XI_KEY     = 0x0A,   // Keyboard key
+};
+
+enum InputActionType
+{
+   /// Button was depressed.
+   XI_MAKE    = 0x01,
+
+   /// Button was released.
+   XI_BREAK   = 0x02,
+
+   /// An axis moved.
+   XI_MOVE    = 0x03,
+
+   /// A key repeat occurred. Happens in between a SI_MAKE and SI_BREAK.
+   XI_REPEAT  = 0x04,
+
+   /// A value of some type.  Matched with SI_FLOAT or SI_INT.
+   XI_VALUE   = 0x05,
+};
+
 #endif
 #endif

+ 123 - 0
engine/source/platform/platformInput_ScriptBinding.h

@@ -92,3 +92,126 @@ ConsoleFunctionWithDocs( echoInputState, ConsoleVoid, 1, 1, ())
 {
 {
 	Input::echoInputState();
 	Input::echoInputState();
 }
 }
+
+/*!	Enables XInput for Xbox 360 controllers.
+	@return No return value.
+*/
+ConsoleFunctionWithDocs( enableXInput, ConsoleBool, 1, 1, ())
+            
+{
+   return( DInputManager::enableXInput() );
+}
+
+/*! Disables XInput for Xbox 360 controllesr
+	@return No return value.
+*/
+
+ConsoleFunctionWithDocs( disableXInput, ConsoleVoid, 1, 1, ())
+{
+   DInputManager::disableXInput();
+}
+
+/*!	Rebuilds the XInput section of the InputManager.
+     @return No return value.
+*/
+ConsoleFunctionWithDocs( resetXInput, ConsoleVoid, 1, 1, ())
+{
+   // This function requests a full "refresh" of events for all controllers the
+   // next time we go through the input processing loop. This is useful to call
+   // at the beginning of your game code after your actionMap is set up to hook
+   // all of the appropriate events
+   
+   DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
+   if ( mgr && mgr->isEnabled() ) 
+      mgr->resetXInput();
+}
+
+/*!	Checks to see if an Xbox 360 controller is connected
+	@param controllerID Zero-based index of the controller to check.
+	@return 1 if the controller is connected, 0 if it isn't, and 205 if XInput
+	hasn't been initialized.
+*/
+
+ConsoleFunctionWithDocs( isXInputConnected, ConsoleBool, 2, 2, ( int controllerID ))
+{
+   DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
+   if ( mgr && mgr->isEnabled() ) return mgr->isXInputConnected( atoi( argv[1] ) );
+   return false;
+}
+
+/*!	Queries the current state of a connected Xbox 360 controller.
+	@param controllerID Zero-based index of the controller to return information about.
+    @param property Name of input action being queried, such as \"XI_THUMBLX\".
+    @param current True checks current device in action.
+    @return Button queried - 1 if the button is pressed, 0 if it's not.
+    @return Thumbstick queried - Int representing displacement from rest position.
+    @return %Trigger queried - Int from 0 to 255 representing how far the trigger is displaced.
+*/
+
+//------------------------------------------------------------------------------
+ConsoleFunctionWithDocs( getXInputState, ConsoleInt, 3, 4, ( int controllerID, string property, bool current ) )
+{
+   DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
+
+   if ( !mgr || !mgr->isEnabled() ) 
+      return -1;
+
+   // Use a little bit of macro magic to simplify this otherwise monolothic
+   // block of code.
+#define GET_XI_STATE(constName) \
+   if (!dStricmp(argv[2], #constName)) \
+      return mgr->getXInputState( dAtoi( argv[1] ), constName, ( dAtoi ( argv[3] ) == 1) );
+   
+   GET_XI_STATE(XI_THUMBLX);
+   GET_XI_STATE(XI_THUMBLY);
+   GET_XI_STATE(XI_THUMBRX);
+   GET_XI_STATE(XI_THUMBRY);
+   GET_XI_STATE(XI_LEFT_TRIGGER);
+   GET_XI_STATE(XI_RIGHT_TRIGGER);
+   GET_XI_STATE(SI_UPOV);
+   GET_XI_STATE(SI_DPOV);
+   GET_XI_STATE(SI_LPOV);
+   GET_XI_STATE(SI_RPOV);
+   GET_XI_STATE(XI_START);
+   GET_XI_STATE(XI_BACK);
+   GET_XI_STATE(XI_LEFT_THUMB);
+   GET_XI_STATE(XI_RIGHT_THUMB);
+   GET_XI_STATE(XI_LEFT_SHOULDER);
+   GET_XI_STATE(XI_RIGHT_SHOULDER);
+   GET_XI_STATE(XI_A);
+   GET_XI_STATE(XI_B);
+   GET_XI_STATE(XI_X);
+   GET_XI_STATE(XI_Y);
+#undef GET_XI_STATE
+
+   return -1;
+}
+
+
+/*!	Activates the vibration motors in the specified controller.
+    The controller will constantly at it's xRumble and yRumble intensities until
+    changed or told to stop.
+     
+	 Valid inputs for xRumble/yRumble are [0 - 1].
+
+	 @param device Name of the device to rumble.
+      @param xRumble Intensity to apply to the left motor.
+      @param yRumble Intensity to apply to the right motor.
+
+	 @note in an Xbox 360 controller, the left motor is low-frequency,
+     while the right motor is high-frequency.
+
+*/
+
+ConsoleFunctionWithDocs( rumble, ConsoleVoid, 4, 4, (string device, float xRumble, float yRumble) )
+{
+   DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
+   if ( mgr && mgr->isEnabled() )
+   {
+      mgr->rumble(argv[1], dAtof(argv[2]), dAtof(argv[3]));
+   }
+   else
+   {
+      Con::printf( "DirectInput/XInput is not enabled." );
+   }
+}

+ 4 - 4
engine/source/platformOSX/osxTorqueView.mm

@@ -237,9 +237,9 @@
     torqueEvent.ascii = 0;
     torqueEvent.ascii = 0;
     torqueEvent.action = action;
     torqueEvent.action = action;
     if (action == SI_BREAK)
     if (action == SI_BREAK)
-        torqueEvent.fValue = 0.0;
+        torqueEvent.fValues[0] = 0.0;
     else
     else
-        torqueEvent.fValue = 1.0;
+        torqueEvent.fValues[0] = 1.0;
     
     
     // Post the input event
     // Post the input event
     Game->postEvent(torqueEvent);
     Game->postEvent(torqueEvent);
@@ -287,7 +287,7 @@
     torqueEvent.modifier = modifiers;
     torqueEvent.modifier = modifiers;
     torqueEvent.ascii = 0;
     torqueEvent.ascii = 0;
     torqueEvent.action = action;
     torqueEvent.action = action;
-    torqueEvent.fValue = fValue;
+    torqueEvent.fValues[0] = fValue;
     torqueEvent.ascii = chars;
     torqueEvent.ascii = chars;
     
     
     // Post the input event
     // Post the input event
@@ -441,7 +441,7 @@
     torqueEvent.modifier = modifiers;
     torqueEvent.modifier = modifiers;
     torqueEvent.ascii = 0;
     torqueEvent.ascii = 0;
     torqueEvent.action = SI_MOVE;
     torqueEvent.action = SI_MOVE;
-    torqueEvent.fValue = deltaY;
+    torqueEvent.fValues[0] = deltaY;
     Game->postEvent(torqueEvent);
     Game->postEvent(torqueEvent);
 }
 }
 
 

+ 102 - 9
engine/source/platformWin32/winDInputDevice.cc

@@ -786,7 +786,7 @@ void DInputDevice::syncKeyboardState()
 
 
 
 
    U8* keyBuffer = new U8[mObjBufferSize];
    U8* keyBuffer = new U8[mObjBufferSize];
-   dMemset( keyBuffer, 0, sizeof( keyBuffer ) );
+   dMemset( keyBuffer, 0, sizeof( U8 ) * mObjBufferSize );
    HRESULT result = mDevice->GetDeviceState( mObjBufferSize, keyBuffer );
    HRESULT result = mDevice->GetDeviceState( mObjBufferSize, keyBuffer );
    if ( SUCCEEDED( result ) )
    if ( SUCCEEDED( result ) )
    {
    {
@@ -917,7 +917,7 @@ bool DInputDevice::buildEvent( DWORD offset, S32 newData, S32 oldData )
          newEvent.action = SI_MOVE;
          newEvent.action = SI_MOVE;
          if ( newEvent.deviceType == MouseDeviceType )
          if ( newEvent.deviceType == MouseDeviceType )
          {
          {
-            newEvent.fValue = float( newData );
+            newEvent.fValues[0] = float( newData );
 
 
          }
          }
          else  // Joystick or other device:
          else  // Joystick or other device:
@@ -926,11 +926,11 @@ bool DInputDevice::buildEvent( DWORD offset, S32 newData, S32 oldData )
             if ( objInfo.mMin != DIPROPRANGE_NOMIN && objInfo.mMax != DIPROPRANGE_NOMAX )
             if ( objInfo.mMin != DIPROPRANGE_NOMIN && objInfo.mMax != DIPROPRANGE_NOMAX )
             {
             {
                float range = float( objInfo.mMax - objInfo.mMin );
                float range = float( objInfo.mMax - objInfo.mMin );
-               //newEvent.fValue = float( newData - objInfo.mMin ) / range;
-               newEvent.fValue = float( ( 2 * newData ) - objInfo.mMax - objInfo.mMin ) / range;
+               //newEvent.fValues[0] = float( newData - objInfo.mMin ) / range;
+               newEvent.fValues[0] = float( ( 2 * newData ) - objInfo.mMax - objInfo.mMin ) / range;
             }
             }
             else
             else
-               newEvent.fValue = (F32)newData;
+               newEvent.fValues[0] = (F32)newData;
          }
          }
 
 
          Game->postEvent( newEvent );
          Game->postEvent( newEvent );
@@ -938,14 +938,14 @@ bool DInputDevice::buildEvent( DWORD offset, S32 newData, S32 oldData )
 
 
       case SI_BUTTON:
       case SI_BUTTON:
          newEvent.action   = ( newData & 0x80 ) ? SI_MAKE : SI_BREAK;
          newEvent.action   = ( newData & 0x80 ) ? SI_MAKE : SI_BREAK;
-         newEvent.fValue   = ( newEvent.action == SI_MAKE ) ? 1.0f : 0.0f;
+         newEvent.fValues[0]   = ( newEvent.action == SI_MAKE ) ? 1.0f : 0.0f;
 
 
          Game->postEvent( newEvent );
          Game->postEvent( newEvent );
          break;
          break;
 
 
       case SI_KEY:
       case SI_KEY:
          newEvent.action   = ( newData & 0x80 ) ? SI_MAKE : SI_BREAK;
          newEvent.action   = ( newData & 0x80 ) ? SI_MAKE : SI_BREAK;
-         newEvent.fValue   = ( newEvent.action == SI_MAKE ) ? 1.0f : 0.0f;
+         newEvent.fValues[0]   = ( newEvent.action == SI_MAKE ) ? 1.0f : 0.0f;
          processKeyEvent( newEvent );
          processKeyEvent( newEvent );
 
 
          Game->postEvent( newEvent );
          Game->postEvent( newEvent );
@@ -975,7 +975,7 @@ bool DInputDevice::buildEvent( DWORD offset, S32 newData, S32 oldData )
             if ( clearkeys )
             if ( clearkeys )
             {
             {
                newEvent.action = SI_BREAK;
                newEvent.action = SI_BREAK;
-               newEvent.fValue = 0.0f;
+               newEvent.fValues[0] = 0.0f;
                // post events for all buttons that need to be cleared.
                // post events for all buttons that need to be cleared.
                if( clearkeys & POV_up)
                if( clearkeys & POV_up)
                {
                {
@@ -1006,7 +1006,7 @@ bool DInputDevice::buildEvent( DWORD offset, S32 newData, S32 oldData )
             if ( setkeys )
             if ( setkeys )
             {
             {
                newEvent.action = SI_MAKE;
                newEvent.action = SI_MAKE;
-               newEvent.fValue = 1.0f;
+               newEvent.fValues[0] = 1.0f;
                // post events for all buttons that need to be set.
                // post events for all buttons that need to be set.
                if( setkeys & POV_up)
                if( setkeys & POV_up)
                {
                {
@@ -1040,6 +1040,99 @@ bool DInputDevice::buildEvent( DWORD offset, S32 newData, S32 oldData )
    return true;
    return true;
 }
 }
 
 
+
+void DInputDevice::rumble(float x, float y)
+{
+   LONG            rglDirection[2] = { 0, 0 };
+   DICONSTANTFORCE cf              = { 0 };
+   HRESULT         result;
+
+   // Now set the new parameters and start the effect immediately.
+   if (!mForceFeedbackEffect)
+   {
+      DIEFFECT eff;
+      ZeroMemory( &eff, sizeof(eff) );
+      eff.dwSize                  = sizeof(DIEFFECT);
+      eff.dwFlags                 = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
+      eff.dwDuration              = INFINITE;
+      eff.dwSamplePeriod          = 0;
+      eff.dwGain                  = DI_FFNOMINALMAX;
+      eff.dwTriggerButton         = DIEB_NOTRIGGER;
+      eff.dwTriggerRepeatInterval = 0;
+      eff.cAxes                   = mNumForceFeedbackAxes;
+      eff.rgdwAxes                = mForceFeedbackAxes;
+      eff.rglDirection            = rglDirection;
+      eff.lpEnvelope              = 0;
+      eff.cbTypeSpecificParams    = sizeof(DICONSTANTFORCE);
+      eff.lpvTypeSpecificParams   = &cf;
+      eff.dwStartDelay            = 0;
+
+      // Create the prepared effect
+      if ( FAILED( result = mDevice->CreateEffect( GUID_ConstantForce, &eff, &mForceFeedbackEffect, NULL ) ) )
+      {
+	      Con::errorf( "DInputDevice::rumbleJoystick - %s does not support force feedback.\n", mName );
+	      return;
+      }
+      else
+      {
+	      Con::printf( "DInputDevice::rumbleJoystick - %s supports force feedback.\n", mName );
+      }
+   }
+
+   // Clamp the input floats to [0 - 1]
+   x = max(0, min(1, x));
+   y = max(0, min(1, y));
+
+   if ( 1 == mNumForceFeedbackAxes )
+   {
+      cf.lMagnitude = (DWORD)( x * DI_FFNOMINALMAX );
+   }
+   else
+   {
+      rglDirection[0] = (DWORD)( x * DI_FFNOMINALMAX );
+      rglDirection[1] = (DWORD)( y * DI_FFNOMINALMAX );
+      cf.lMagnitude = (DWORD)sqrt( (double)(x * x * DI_FFNOMINALMAX * DI_FFNOMINALMAX + y * y * DI_FFNOMINALMAX * DI_FFNOMINALMAX) );
+   }
+
+   DIEFFECT eff;
+   ZeroMemory( &eff, sizeof(eff) );
+   eff.dwSize                  = sizeof(DIEFFECT);
+   eff.dwFlags                 = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
+   eff.dwDuration              = INFINITE;
+   eff.dwSamplePeriod          = 0;
+   eff.dwGain                  = DI_FFNOMINALMAX;
+   eff.dwTriggerButton         = DIEB_NOTRIGGER;
+   eff.dwTriggerRepeatInterval = 0;
+   eff.cAxes                   = mNumForceFeedbackAxes;
+   eff.rglDirection            = rglDirection;
+   eff.lpEnvelope              = 0;
+   eff.cbTypeSpecificParams    = sizeof(DICONSTANTFORCE);
+   eff.lpvTypeSpecificParams   = &cf;
+   eff.dwStartDelay            = 0;
+
+   if ( FAILED( result = mForceFeedbackEffect->SetParameters( &eff, DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_START ) ) )
+   {
+      const char* errorString = NULL;
+      switch ( result )
+      {
+         case DIERR_INPUTLOST:
+            errorString = "DIERR_INPUTLOST";
+            break;
+
+         case DIERR_INVALIDPARAM:
+            errorString = "DIERR_INVALIDPARAM";
+            break;
+
+         case DIERR_NOTACQUIRED:
+            errorString = "DIERR_NOTACQUIRED";
+            break;
+
+         default:
+            errorString = "Unknown Error";
+      }
+      Con::errorf( "DInputDevice::rumbleJoystick - %s - Failed to start rumble effect\n", errorString );
+   }
+}
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 //
 //
 // This function translates the DirectInput scan code to the associated
 // This function translates the DirectInput scan code to the associated

+ 9 - 1
engine/source/platformWin32/winDInputDevice.h

@@ -36,9 +36,9 @@
 
 
 //Luma: Adding this for direct input header
 //Luma: Adding this for direct input header
 #define DIRECTINPUT_VERSION  0x0800
 #define DIRECTINPUT_VERSION  0x0800
-
 #include <dinput.h>
 #include <dinput.h>
 
 
+
 class DInputDevice : public InputDevice
 class DInputDevice : public InputDevice
 {
 {
    public:
    public:
@@ -74,6 +74,11 @@ class DInputDevice : public InputDevice
       bool                 mAcquired;
       bool                 mAcquired;
       bool                 mNeedSync;
       bool                 mNeedSync;
 
 
+	  LPDIRECTINPUTEFFECT  mForceFeedbackEffect;   ///< Holds our DirectInput FF Effect
+      DWORD                mNumForceFeedbackAxes;  ///< # axes (we only support 0, 1, or 2
+      DWORD                mForceFeedbackAxes[2];  ///< Force Feedback axes offsets into DIOBJECTFORMAT
+
+
       //--------------------------------------
       //--------------------------------------
       DIDEVICEOBJECTINSTANCE* mObjInstance;
       DIDEVICEOBJECTINSTANCE* mObjInstance;
       DIOBJECTDATAFORMAT*     mObjFormat;
       DIOBJECTDATAFORMAT*     mObjFormat;
@@ -124,6 +129,9 @@ class DInputDevice : public InputDevice
       const char* getName();
       const char* getName();
       const char* getProductName();
       const char* getProductName();
 
 
+	   // Constant Effect Force Feedback
+      void rumble( float x, float y );
+
       // Console interface functions:
       // Console interface functions:
       const char* getJoystickAxesString();
       const char* getJoystickAxesString();
       static bool joystickDetected();
       static bool joystickDetected();

+ 426 - 1
engine/source/platformWin32/winDirectInput.cc

@@ -27,12 +27,16 @@
 #include "platform/event.h"
 #include "platform/event.h"
 #include "console/console.h"
 #include "console/console.h"
 #include "console/consoleTypes.h"
 #include "console/consoleTypes.h"
+#include "input/actionMap.h"
+#include "game/gameInterface.h"
+#include <Xinput.h>
 
 
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 // Static class variables:
 // Static class variables:
 bool DInputManager::smKeyboardEnabled = true;
 bool DInputManager::smKeyboardEnabled = true;
 bool DInputManager::smMouseEnabled = false;
 bool DInputManager::smMouseEnabled = false;
 bool DInputManager::smJoystickEnabled = false;
 bool DInputManager::smJoystickEnabled = false;
+bool DInputManager::smXInputEnabled = false;
 
 
 // Type definitions:
 // Type definitions:
 typedef HRESULT (WINAPI* FN_DirectInputCreate)(HINSTANCE hinst, DWORD dwVersion, REFIID riidltf, LPVOID *ppvOut, LPUNKNOWN punkOuter);
 typedef HRESULT (WINAPI* FN_DirectInputCreate)(HINSTANCE hinst, DWORD dwVersion, REFIID riidltf, LPVOID *ppvOut, LPUNKNOWN punkOuter);
@@ -44,6 +48,10 @@ DInputManager::DInputManager()
    mDInputLib        = NULL;
    mDInputLib        = NULL;
    mDInputInterface  = NULL;
    mDInputInterface  = NULL;
    mKeyboardActive   = mMouseActive = mJoystickActive = false;
    mKeyboardActive   = mMouseActive = mJoystickActive = false;
+   mXInputActive = true;
+
+   for(S32 i=0; i<4; i++)
+	   mLastDisconnectTime[i] = -1;
 }
 }
 
 
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
@@ -52,6 +60,7 @@ void DInputManager::init()
    Con::addVariable( "pref::Input::KeyboardEnabled",  TypeBool, &smKeyboardEnabled );
    Con::addVariable( "pref::Input::KeyboardEnabled",  TypeBool, &smKeyboardEnabled );
    Con::addVariable( "pref::Input::MouseEnabled",     TypeBool, &smMouseEnabled );
    Con::addVariable( "pref::Input::MouseEnabled",     TypeBool, &smMouseEnabled );
    Con::addVariable( "pref::Input::JoystickEnabled",  TypeBool, &smJoystickEnabled );
    Con::addVariable( "pref::Input::JoystickEnabled",  TypeBool, &smJoystickEnabled );
+   Con::addVariable( "pref::Input::XInputEnabled", TypeBool, &smXInputEnabled );
 }
 }
 
 
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
@@ -60,6 +69,27 @@ bool DInputManager::enable()
    FN_DirectInputCreate fnDInputCreate;
    FN_DirectInputCreate fnDInputCreate;
 
 
    disable();
    disable();
+
+   mXInputLib = LoadLibrary( dT("xinput9_1_0.dll") );
+
+   if( mXInputLib )
+   {
+	   mfnXInputGetState = (FN_XInputGetState) GetProcAddress( mXInputLib, "XInputGetState" );
+      mfnXInputSetState = (FN_XInputSetState) GetProcAddress( mXInputLib, "XInputSetState" );
+      
+	  if ( mfnXInputGetState && mfnXInputSetState )
+      {
+         mXInputStateReset = true;
+         mXInputDeadZoneOn = true;
+         smXInputEnabled = true;
+      }
+   }
+   else
+   {
+	   Con::warnf("Unable to enable XInput.");
+   }
+
+
    mDInputLib = LoadLibrary( dT("DInput8.dll") );
    mDInputLib = LoadLibrary( dT("DInput8.dll") );
    if ( mDInputLib )
    if ( mDInputLib )
    {
    {
@@ -117,6 +147,20 @@ void DInputManager::disable()
       mDInputLib = NULL;
       mDInputLib = NULL;
    }
    }
 
 
+   if ( mfnXInputGetState )
+   {
+      mXInputStateReset = true;
+      mfnXInputGetState = NULL;
+      mfnXInputSetState = NULL;
+   }
+
+   if ( mXInputLib )
+   {
+      FreeLibrary( mXInputLib );
+      mXInputLib = NULL;
+   }
+
+
    mEnabled = false;
    mEnabled = false;
 }
 }
 
 
@@ -180,6 +224,9 @@ void DInputManager::unacquire( U8 deviceType, U8 deviceID )
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 void DInputManager::process()
 void DInputManager::process()
 {
 {
+	if ( isXInputActive() )
+		processXInput();
+
    DInputDevice* dptr;
    DInputDevice* dptr;
    for ( iterator ptr = begin(); ptr != end(); ptr++ )
    for ( iterator ptr = begin(); ptr != end(); ptr++ )
    {
    {
@@ -194,7 +241,6 @@ void DInputManager::enumerateDevices()
 {
 {
    if ( mDInputInterface )
    if ( mDInputInterface )
    {
    {
-
       DInputDevice::init();
       DInputDevice::init();
       DInputDevice::smDInputInterface = mDInputInterface;
       DInputDevice::smDInputInterface = mDInputInterface;
       mDInputInterface->EnumDevices( DI8DEVTYPE_KEYBOARD, EnumDevicesProc, this, DIEDFL_ATTACHEDONLY );
       mDInputInterface->EnumDevices( DI8DEVTYPE_KEYBOARD, EnumDevicesProc, this, DIEDFL_ATTACHEDONLY );
@@ -424,4 +470,383 @@ const char* DInputManager::getJoystickAxesString( U32 deviceID )
    }
    }
 
 
    return( "" );
    return( "" );
+}
+
+bool DInputManager::enableXInput()
+{
+   // Largely, this series of functions is identical to the Joystick versions,
+   // except that XInput cannot be "activated" or "deactivated". You either have
+   // the DLL or you don't. Beyond that, you have up to four controllers
+   // connected at any given time
+
+   DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
+   if ( !mgr || !mgr->isEnabled() )
+      return( false );
+
+   if ( mgr->isXInputActive() )
+      return( true );
+
+   if ( Input::isActive() )
+      mgr->activateXInput();
+
+   if ( smXInputEnabled )
+   {
+      Con::printf( "XInput enabled." );
+   }
+   else
+   {
+      Con::warnf( "XInput failed to enable!" );
+   }
+
+   return( smXInputEnabled );
+}
+
+//------------------------------------------------------------------------------
+void DInputManager::disableXInput()
+{
+   DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
+   if ( !mgr || !mgr->isEnabled())
+      return;
+
+   mgr->deactivateXInput();
+   Con::printf( "XInput disabled." );
+}
+
+//------------------------------------------------------------------------------
+bool DInputManager::isXInputEnabled()
+{
+   return( smXInputEnabled );
+}
+
+//------------------------------------------------------------------------------
+bool DInputManager::isXInputConnected(int controllerID)
+{
+	return( mXInputStateNew[controllerID].bConnected );
+}
+
+int DInputManager::getXInputState(int controllerID, int property, bool current)
+{
+   int retVal;
+
+   switch(property)
+   {
+#define CHECK_PROP_ANALOG(prop, stateTest) \
+   case prop:        (current) ? retVal = mXInputStateNew[controllerID].state.Gamepad.##stateTest : retVal = mXInputStateOld[controllerID].state.Gamepad.##stateTest; return retVal;
+
+      CHECK_PROP_ANALOG(XI_THUMBLX, sThumbLX)
+      CHECK_PROP_ANALOG(XI_THUMBLY, sThumbLY)
+      CHECK_PROP_ANALOG(XI_THUMBRX, sThumbRX)
+      CHECK_PROP_ANALOG(XI_THUMBRY, sThumbRY)
+      CHECK_PROP_ANALOG(XI_LEFT_TRIGGER, bLeftTrigger)
+      CHECK_PROP_ANALOG(XI_RIGHT_TRIGGER, bRightTrigger)
+
+#undef CHECK_PROP_ANALOG
+
+#define CHECK_PROP_DIGITAL(prop, stateTest) \
+   case prop:           (current) ? retVal = (( mXInputStateNew[controllerID].state.Gamepad.wButtons & stateTest ) != 0 ) : retVal = (( mXInputStateOld[controllerID].state.Gamepad.wButtons & stateTest ) != 0 ); return retVal;
+
+      CHECK_PROP_DIGITAL(SI_UPOV, XINPUT_GAMEPAD_DPAD_UP)
+      CHECK_PROP_DIGITAL(SI_DPOV, XINPUT_GAMEPAD_DPAD_DOWN)
+      CHECK_PROP_DIGITAL(SI_LPOV, XINPUT_GAMEPAD_DPAD_LEFT)
+      CHECK_PROP_DIGITAL(SI_RPOV, XINPUT_GAMEPAD_DPAD_RIGHT)
+      CHECK_PROP_DIGITAL(XI_START, XINPUT_GAMEPAD_START)
+      CHECK_PROP_DIGITAL(XI_BACK, XINPUT_GAMEPAD_BACK)
+      CHECK_PROP_DIGITAL(XI_LEFT_THUMB, XINPUT_GAMEPAD_LEFT_THUMB)
+      CHECK_PROP_DIGITAL(XI_RIGHT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB)
+      CHECK_PROP_DIGITAL(XI_LEFT_SHOULDER, XINPUT_GAMEPAD_LEFT_SHOULDER)
+      CHECK_PROP_DIGITAL(XI_RIGHT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER)
+      CHECK_PROP_DIGITAL(XI_A, XINPUT_GAMEPAD_A)
+      CHECK_PROP_DIGITAL(XI_B, XINPUT_GAMEPAD_B)
+      CHECK_PROP_DIGITAL(XI_X, XINPUT_GAMEPAD_X)
+      CHECK_PROP_DIGITAL(XI_Y, XINPUT_GAMEPAD_Y)
+
+#undef CHECK_PROP_DIGITAL
+   }
+
+   return -1;
+}
+
+//------------------------------------------------------------------------------
+bool DInputManager::activateXInput()
+{
+   if ( !mEnabled || !Input::isActive())
+      return( false );
+
+   mXInputActive = true; //acquire( GamepadDeviceType, SI_ANY );
+
+   return( mXInputActive );
+}
+
+//------------------------------------------------------------------------------
+void DInputManager::deactivateXInput()
+{
+   if ( mEnabled && mXInputActive )
+   {
+      unacquire( GamepadDeviceType, SI_ANY );
+      mXInputActive = false;
+   }
+}
+
+//------------------------------------------------------------------------------
+bool DInputManager::rumble( const char *pDeviceName, float x, float y )
+{
+   // Determine the device
+   U32 deviceType;
+   U32 deviceInst;
+
+   // Find the requested device
+   if ( !ActionMap::getDeviceTypeAndInstance(pDeviceName, deviceType, deviceInst) )
+   {
+      Con::printf("DInputManager::rumble: unknown device: %s", pDeviceName);
+      return false;
+   }
+
+   // clamp (x, y) to the range of [0 ... 1] each
+   x = mClampF(x, 0.f, 1.f);
+   y = mClampF(y, 0.f, 1.f);
+
+   //  Easy path for xinput devices.
+   if(deviceType == GamepadDeviceType)
+   {
+      XINPUT_VIBRATION vibration;
+      vibration.wLeftMotorSpeed = static_cast<int>(x * 65535);
+      vibration.wRightMotorSpeed = static_cast<int>(y * 65535);
+      return ( mfnXInputSetState(deviceInst, &vibration) == ERROR_SUCCESS );
+   }
+
+   switch ( deviceType )
+   {
+   case JoystickDeviceType:
+
+      // Find the device and shake it!
+      DInputDevice* dptr;
+      for ( iterator ptr = begin(); ptr != end(); ptr++ )
+      {
+         dptr = dynamic_cast<DInputDevice*>( *ptr );
+         if ( dptr )
+         {
+            if (deviceType == dptr->getDeviceType() && deviceInst == dptr->getDeviceID())
+            {
+               dptr->rumble(x, y);
+               return true;
+            }
+         }
+      }
+
+      // We should never get here... something's really messed up
+      Con::errorf( "DInputManager::rumbleJoystick - Couldn't find device to rumble! This should never happen!\n" );
+      return false;
+
+   default:
+      Con::printf("DInputManager::rumble - only supports joystick and xinput/gamepad devices");
+      return false;
+   }
+}
+
+void DInputManager::buildXInputEvent( U32 deviceInst, XInputEventType objType, InputObjectInstances objInst, InputActionType action, float fValue )
+{
+InputEvent newEvent;
+
+   newEvent.deviceType = GamepadDeviceType;
+   newEvent.deviceInst = deviceInst;
+   newEvent.objType = objType;
+   newEvent.objInst = objInst;
+   newEvent.action = action;
+   newEvent.fValues[0] = fValue;
+   //we need to find the gameinterface object from here
+
+   Game->postEvent(newEvent);
+   
+}
+
+// The next three functions: fireXInputConnectEvent, fireXInputMoveEvent, and fireXInputButtonEvent
+// determine whether a "delta" has occurred between the last captured controller state and the
+// currently captured controller state and only if so, do we fire an event. The shortcutter
+// "mXInputStateReset" is the exception and is true whenever DirectInput gets reset (because 
+// the user ALT-TABBED away, for example). That means that after every context switch,
+// you will get a full set of updates on the "true" state of the controller.
+
+inline void DInputManager::fireXInputConnectEvent( int controllerID, bool condition, bool connected )
+{
+   if ( mXInputStateReset || condition )
+   {
+      buildXInputEvent( controllerID, XI_BUTTON, XI_CONNECT, connected ? XI_MAKE : XI_BREAK, 0);
+   }
+}
+
+inline void DInputManager::fireXInputMoveEvent( int controllerID, bool condition, InputObjectInstances objInst, float fValue )
+{
+   if ( mXInputStateReset || condition )
+   {
+	   /*
+	  //Uncomment if you want real-time Input displayed to the console
+
+	  const char *objName;
+      switch (objInst)
+      {
+      case XI_THUMBLX:       objName = "THUMBLX"; break;
+      case XI_THUMBLY:       objName = "THUMBLY"; break;
+      case XI_THUMBRX:       objName = "THUMBRX"; break;
+      case XI_THUMBRY:       objName = "THUMBRY"; break;
+      case XI_LEFT_TRIGGER:  objName = "LEFT_TRIGGER"; break;
+      case XI_RIGHT_TRIGGER: objName = "RIGHT_TRIGGER"; break;
+      default:               objName = "UNKNOWN"; break;
+      }
+	  Con::printf("%s  %.1f", objName, fValue);
+	  */
+	   buildXInputEvent( controllerID, XI_AXIS, objInst, XI_MOVE, fValue );
+   }
+}
+
+inline void DInputManager::fireXInputButtonEvent( int controllerID, bool forceFire, int button, InputObjectInstances objInst )
+{
+   if ( mXInputStateReset || forceFire || ((mXInputStateNew[controllerID].state.Gamepad.wButtons & button) != (mXInputStateOld[controllerID].state.Gamepad.wButtons & button)) )
+   {
+      InputActionType action = ((mXInputStateNew[controllerID].state.Gamepad.wButtons & button) != 0) ? XI_MAKE : XI_BREAK;
+
+	  /*
+	  //Uncomment if you want real-time Input displayed to the console
+
+	  char *objName;
+	  objName = "";
+	  switch (objInst)
+      {      
+      
+      case XI_DPAD_UP:        objName = "DPAD_UP"; break;
+      case XI_DPAD_DOWN:      objName = "DPAD_DOWN"; break;
+      case XI_DPAD_LEFT:      objName = "DPAD_LEFT"; break;
+      case XI_DPAD_RIGHT:     objName = "DPAD_RIGHT"; break;
+      
+      case XI_START:          objName = "START"; break;
+      case XI_BACK:           objName = "BACK"; break;
+      case XI_LEFT_THUMB:     objName = "LEFT_THUMB"; break;
+      case XI_RIGHT_THUMB:    objName = "RIGHT_THUMB"; break;
+      case XI_LEFT_SHOULDER:  objName = "LEFT_SHOULDER"; break;
+      case XI_RIGHT_SHOULDER: objName = "RIGHT_SHOULDER"; break;
+      case XI_A:              objName = "A"; break;
+      case XI_B:              objName = "B"; break;
+      case XI_X:              objName = "X"; break;
+      case XI_Y:              objName = "Y"; break;
+	  default:                Con::printf("%u", objInst); break;
+      }
+	  Con::printf("%s", objName);
+	  */
+
+      buildXInputEvent( controllerID, XI_BUTTON, objInst, action, ( action == XI_MAKE ? 1 : 0 ) );
+   }
+}
+
+void DInputManager::processXInput( void )
+{
+   const U32 curTime = Platform::getRealMilliseconds();
+
+   // We only want to check one disconnected device per frame.
+   bool foundDisconnected = false;
+
+   if ( mfnXInputGetState )
+   {
+      for ( int i=0; i<4; i++ )
+      {
+         // Calling XInputGetState on a disconnected controller takes a fair 
+         // amount of time (probably because it tries to locate it), so we 
+         // add a delay - only check every 250ms or so.
+         if(mLastDisconnectTime[i] != -1)
+         {
+            // If it's not -1, then it was disconnected list time we checked.
+            // So skip until it's time.
+            if((curTime-mLastDisconnectTime[i]) < csmDisconnectedSkipDelay)
+            {
+               continue;
+            }
+
+            // If we already checked a disconnected controller, don't try any
+            // further potentially disconnected devices.
+            if(foundDisconnected)
+            {
+               // If we're skipping this, defer it out by the skip delay
+               // so we don't get clumped checks.
+               mLastDisconnectTime[i] += csmDisconnectedSkipDelay;
+               continue;
+            }
+         }
+
+         mXInputStateOld[i] = mXInputStateNew[i];
+         mXInputStateNew[i].bConnected = ( mfnXInputGetState( i, &mXInputStateNew[i].state ) == ERROR_SUCCESS );
+
+         // Update the last connected time.
+         if(mXInputStateNew[i].bConnected)
+            mLastDisconnectTime[i] = -1;
+         else
+         {
+            foundDisconnected = true;
+            mLastDisconnectTime[i] = curTime;
+         }
+
+         // trim the controller's thumbsticks to zero if they are within the deadzone
+         if( mXInputDeadZoneOn )
+         {
+            // Zero value if thumbsticks are within the dead zone 
+            if( (mXInputStateNew[i].state.Gamepad.sThumbLX < XINPUT_DEADZONE && mXInputStateNew[i].state.Gamepad.sThumbLX > -XINPUT_DEADZONE) && 
+                (mXInputStateNew[i].state.Gamepad.sThumbLY < XINPUT_DEADZONE && mXInputStateNew[i].state.Gamepad.sThumbLY > -XINPUT_DEADZONE) ) 
+            {
+               mXInputStateNew[i].state.Gamepad.sThumbLX = 0;
+               mXInputStateNew[i].state.Gamepad.sThumbLY = 0;
+            }
+
+            if( (mXInputStateNew[i].state.Gamepad.sThumbRX < XINPUT_DEADZONE && mXInputStateNew[i].state.Gamepad.sThumbRX > -XINPUT_DEADZONE) && 
+                (mXInputStateNew[i].state.Gamepad.sThumbRY < XINPUT_DEADZONE && mXInputStateNew[i].state.Gamepad.sThumbRY > -XINPUT_DEADZONE) ) 
+            {
+               mXInputStateNew[i].state.Gamepad.sThumbRX = 0;
+               mXInputStateNew[i].state.Gamepad.sThumbRY = 0;
+            }
+         }
+
+         // this controller was connected or disconnected
+         bool bJustConnected = ( ( mXInputStateOld[i].bConnected != mXInputStateNew[i].bConnected ) && ( mXInputStateNew[i].bConnected ) );
+         fireXInputConnectEvent( i, (mXInputStateOld[i].bConnected != mXInputStateNew[i].bConnected), mXInputStateNew[i].bConnected );
+
+         // If this controller is disconnected, stop reporting events for it
+         if ( !mXInputStateNew[i].bConnected )
+            continue;
+
+         // == LEFT THUMBSTICK ==
+         fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.sThumbLX != mXInputStateOld[i].state.Gamepad.sThumbLX), XI_THUMBLX, (mXInputStateNew[i].state.Gamepad.sThumbLX / 32768.0f) );
+         fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.sThumbLY != mXInputStateOld[i].state.Gamepad.sThumbLY), XI_THUMBLY, (mXInputStateNew[i].state.Gamepad.sThumbLY / 32768.0f) );
+
+         // == RIGHT THUMBSTICK ==
+         fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.sThumbRX != mXInputStateOld[i].state.Gamepad.sThumbRX), XI_THUMBRX, (mXInputStateNew[i].state.Gamepad.sThumbRX / 32768.0f) );
+         fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.sThumbRY != mXInputStateOld[i].state.Gamepad.sThumbRY), XI_THUMBRY, (mXInputStateNew[i].state.Gamepad.sThumbRY / 32768.0f) );
+
+         // == LEFT & RIGHT REAR TRIGGERS ==
+         fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.bLeftTrigger != mXInputStateOld[i].state.Gamepad.bLeftTrigger), XI_LEFT_TRIGGER, (mXInputStateNew[i].state.Gamepad.bLeftTrigger / 255.0f) );
+         fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.bRightTrigger != mXInputStateOld[i].state.Gamepad.bRightTrigger), XI_RIGHT_TRIGGER, (mXInputStateNew[i].state.Gamepad.bRightTrigger / 255.0f) );
+
+         // == BUTTONS: DPAD ==
+         fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_DPAD_UP, SI_UPOV );
+         fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_DPAD_DOWN, SI_DPOV );
+         fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_DPAD_LEFT, SI_LPOV );
+         fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_DPAD_RIGHT, SI_RPOV );
+
+         // == BUTTONS: START & BACK ==
+         fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_START, XI_START );
+         fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_BACK, XI_BACK );
+
+         // == BUTTONS: LEFT AND RIGHT THUMBSTICK ==
+         fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_LEFT_THUMB, XI_LEFT_THUMB );
+         fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_RIGHT_THUMB, XI_RIGHT_THUMB );
+
+         // == BUTTONS: LEFT AND RIGHT SHOULDERS (formerly WHITE and BLACK on Xbox 1) ==
+         fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_LEFT_SHOULDER, XI_LEFT_SHOULDER );
+         fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_RIGHT_SHOULDER, XI_RIGHT_SHOULDER );
+
+         // == BUTTONS: A, B, X, and Y ==
+         fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_A, XI_A );
+         fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_B, XI_B );
+         fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_X, XI_X );
+         fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_Y, XI_Y );
+      }
+
+      if ( mXInputStateReset ) 
+         mXInputStateReset = false;
+   }
 }
 }

+ 50 - 0
engine/source/platformWin32/winDirectInput.h

@@ -35,6 +35,21 @@
 
 
 #define DIRECTINPUT_VERSION 0x0800
 #define DIRECTINPUT_VERSION 0x0800
 #include <dinput.h>
 #include <dinput.h>
+#include <Xinput.h>
+
+// XInput related definitions
+typedef DWORD (WINAPI* FN_XInputGetState)(DWORD dwUserIndex, XINPUT_STATE* pState);
+typedef DWORD (WINAPI* FN_XInputSetState)(DWORD dwUserIndex, XINPUT_VIBRATION* pVibration);
+#define XINPUT_MAX_CONTROLLERS 4  // XInput handles up to 4 controllers 
+#define XINPUT_DEADZONE  ( 0.24f * FLOAT(0x7FFF) )  // Default to 24% of the +/- 32767 range.   This is a reasonable default value but can be altered if needed.
+
+struct XINPUT_CONTROLLER_STATE
+{
+    XINPUT_STATE    state;
+    bool            bConnected;
+};
+
+//---------------------------------------------------------------------------
 
 
 struct InputEvent;
 struct InputEvent;
 
 
@@ -44,16 +59,32 @@ class DInputManager : public InputManager
    private:
    private:
       typedef SimGroup Parent;
       typedef SimGroup Parent;
 
 
+	    // XInput state
+      HMODULE                 mXInputLib;
+      FN_XInputGetState       mfnXInputGetState;
+      FN_XInputSetState       mfnXInputSetState;
+      XINPUT_CONTROLLER_STATE mXInputStateOld[XINPUT_MAX_CONTROLLERS];
+      XINPUT_CONTROLLER_STATE mXInputStateNew[XINPUT_MAX_CONTROLLERS];
+      U32                     mLastDisconnectTime[XINPUT_MAX_CONTROLLERS];
+      bool                    mXInputStateReset;
+      bool                    mXInputDeadZoneOn;
+
+      /// Number of milliseconds to skip checking an xinput device if it was
+      /// disconnected on last check.
+      const static U32 csmDisconnectedSkipDelay = 250;
+
       HMODULE        mDInputLib;
       HMODULE        mDInputLib;
       LPDIRECTINPUT8 mDInputInterface;
       LPDIRECTINPUT8 mDInputInterface;
 
 
       static bool smKeyboardEnabled;
       static bool smKeyboardEnabled;
       static bool smMouseEnabled;
       static bool smMouseEnabled;
       static bool smJoystickEnabled;
       static bool smJoystickEnabled;
+	  static bool smXInputEnabled;
 
 
       bool mKeyboardActive;
       bool mKeyboardActive;
       bool mMouseActive;
       bool mMouseActive;
       bool mJoystickActive;
       bool mJoystickActive;
+	  bool mXInputActive;
 
 
       void  enumerateDevices();
       void  enumerateDevices();
 
 
@@ -62,6 +93,13 @@ class DInputManager : public InputManager
       bool acquire( U8 deviceType, U8 deviceID );
       bool acquire( U8 deviceType, U8 deviceID );
       void unacquire( U8 deviceType, U8 deviceID );
       void unacquire( U8 deviceType, U8 deviceID );
 
 
+	  // XInput worker functions
+      void buildXInputEvent( U32 deviceInst, XInputEventType objType, InputObjectInstances objInst, InputActionType action, float fValue );
+      void fireXInputConnectEvent( int controllerID, bool condition, bool connected );
+      void fireXInputMoveEvent( int controllerID, bool condition, InputObjectInstances objInst, float fValue );
+      void fireXInputButtonEvent( int controllerID, bool forceFire, int button, InputObjectInstances objInst );
+      void processXInput();
+
    public:
    public:
       DInputManager();
       DInputManager();
 
 
@@ -98,8 +136,20 @@ class DInputManager : public InputManager
       void deactivateJoystick();
       void deactivateJoystick();
       bool isJoystickActive()       { return( mJoystickActive ); }
       bool isJoystickActive()       { return( mJoystickActive ); }
 
 
+	  static bool enableXInput();
+      static void disableXInput();
+      static bool isXInputEnabled();
+      bool activateXInput();
+      void deactivateXInput();
+      bool isXInputActive()         { return( mXInputActive ); }
+      void resetXInput()            { mXInputStateReset = true; }
+      bool isXInputConnected(int controllerID);
+      int getXInputState(int controllerID, int property, bool current);
+
       // Console interface:
       // Console interface:
       const char* getJoystickAxesString( U32 deviceID );
       const char* getJoystickAxesString( U32 deviceID );
+
+	  bool rumble( const char *pDeviceName, float x, float y );
 };
 };
 
 
 #endif  // _H_WINDIRECTINPUT_
 #endif  // _H_WINDIRECTINPUT_

+ 4 - 0
engine/source/platformWin32/winInput.cc

@@ -495,6 +495,10 @@ void Input::echoInputState()
 		Con::printf( "- Joystick is %sabled and %sactive.",
 		Con::printf( "- Joystick is %sabled and %sactive.",
             mgr->isJoystickEnabled() ? "en" : "dis",
             mgr->isJoystickEnabled() ? "en" : "dis",
             mgr->isJoystickActive() ? "" : "in" );
             mgr->isJoystickActive() ? "" : "in" );
+
+		Con::printf( "- Xinput is %sabled and %sactive.",
+			mgr->isXInputEnabled() ? "en" : "dis",
+			mgr->isXInputActive() ? "" : "in" );
    }
    }
    else
    else
     {
     {

+ 5 - 5
engine/source/platformWin32/winWindow.cc

@@ -480,7 +480,7 @@ static void processKeyMessage(UINT message, WPARAM wParam, LPARAM lParam)
    event.action     = make ? (repeat ? SI_REPEAT : SI_MAKE ) : SI_BREAK;
    event.action     = make ? (repeat ? SI_REPEAT : SI_MAKE ) : SI_BREAK;
    event.modifier   = modKey;
    event.modifier   = modKey;
    event.ascii      = asciiCode;
    event.ascii      = asciiCode;
-   event.fValue     = make ? 1.0f : 0.0f;
+   event.fValues[0]     = make ? 1.0f : 0.0f;
 
 
    // Store the current modifier keys
    // Store the current modifier keys
    Input::setModifierKeys(modifierKeys);
    Input::setModifierKeys(modifierKeys);
@@ -547,7 +547,7 @@ static void CheckCursorPos()
          event.action = SI_MOVE;
          event.action = SI_MOVE;
          event.modifier = modifierKeys;
          event.modifier = modifierKeys;
          event.ascii = 0;
          event.ascii = 0;
-         event.fValue = (F32)(mousePos.x - centerX);
+         event.fValues[0] = (F32)(mousePos.x - centerX);
          Game->postEvent(event);
          Game->postEvent(event);
       }
       }
 
 
@@ -562,7 +562,7 @@ static void CheckCursorPos()
          event.action = SI_MOVE;
          event.action = SI_MOVE;
          event.modifier = modifierKeys;
          event.modifier = modifierKeys;
          event.ascii = 0;
          event.ascii = 0;
-         event.fValue = (F32)(mousePos.y - centerY);
+         event.fValues[0] = (F32)(mousePos.y - centerY);
          Game->postEvent(event);
          Game->postEvent(event);
       }
       }
 
 
@@ -596,7 +596,7 @@ static void mouseButtonEvent(S32 action, S32 objInst)
    event.action = action;
    event.action = action;
    event.modifier = modifierKeys;
    event.modifier = modifierKeys;
    event.ascii = 0;
    event.ascii = 0;
-   event.fValue = action == SI_MAKE ? 1.0f : 0.0f;
+   event.fValues[0] = action == SI_MAKE ? 1.0f : 0.0f;
 
 
    Game->postEvent(event);
    Game->postEvent(event);
 }
 }
@@ -619,7 +619,7 @@ static void mouseWheelEvent( S32 delta )
       event.action = SI_MOVE;
       event.action = SI_MOVE;
       event.modifier = modifierKeys;
       event.modifier = modifierKeys;
       event.ascii = 0;
       event.ascii = 0;
-      event.fValue = (F32)delta;
+      event.fValues[0] = (F32)delta;
 
 
       Game->postEvent( event );
       Game->postEvent( event );
    }
    }

+ 1263 - 1263
engine/source/platformiOS/iOSInput.mm

@@ -1,1263 +1,1263 @@
-//-----------------------------------------------------------------------------
-// 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.
-//-----------------------------------------------------------------------------
-
-
-#include "platformiOS/platformiOS.h"
-#include "platformiOS/iOSUtil.h"
-#include "platform/platformInput.h"
-#include "platform/event.h"
-#include "console/console.h"
-#include "game/gameInterface.h"
-#include "string/Unicode.h"
-#include "gui/guiCanvas.h"
-
-
-// <Mat> just some random number 50, we'll get a proper value later
-#define IOS_DOUBLE_CLICK_TIME	( 50 * 60.0f * 1000.0f)
-
-// Static class variables:
-InputManager* Input::smManager;
-bool           Input::smActive;
-CursorManager* Input::smCursorManager = 0;
-
-
-bool gInputEnabled = false;
-bool gMouseEnabled = false;
-bool gKBEnabled = false;
-bool gMouseActive = false;
-bool gKBActive = false;
-
-//------------------------------------------------------------------------------
-// Helper functions.  Should migrate to an InputManager object at some point.
-bool enableKeyboard(void);
-void disableKeyboard(void);
-bool activateKeyboard(void);
-void deactivateKeyboard(void);
-bool enableMouse(void);
-void disableMouse(void);
-bool activateMouse(void);
-void deactivateMouse(void);
-
-
-
-static void fillAsciiTable();
-
-
-struct touchEvent {
-	S32 number;
-	S32 x;
-	S32 y;
-	touchEvent( S32 a, S32 b, S32 c ) {
-		number = a;
-		x = b;
-		y = c;
-	}
-};
-
-
-Vector<touchEvent> TouchMoveEvents;//<Mat> to make sure we don't have multiple events per frame
-Vector<touchEvent> TouchDownEvents;
-Vector<touchEvent> TouchUpEvents;
-
-// EFM - BEGIN TOUCH CHANGES
-#define MAX_TOUCH_EVENTS 5
-
-struct touchTracker {
-	S32 lastX;
-	S32 lastY;
-	Vector<touchEvent> downEvents;
-	Vector<touchEvent> moveEvents;
-	Vector<touchEvent> upEvents;
-};
-
-touchTracker CombinedTouchEvents[MAX_TOUCH_EVENTS];
-
-struct touchCorrelation {
-	S32 lastX;
-	S32 lastY;
-};
-
-touchCorrelation lastTouches[MAX_TOUCH_EVENTS];
-
-// EFM - END TOUCH CHANGES
-
-//Luma: Tap support
-Vector<touchEvent> TouchTapEvents;
-int processMultipleTouches();
-
-
-//------------------------------------------------------------------------------
-//
-// This function gets the standard ASCII code corresponding to our key code
-// and the existing modifier key state.
-//
-//------------------------------------------------------------------------------
-struct AsciiData
-{
-   struct KeyData
-   {
-      U16   ascii;
-      bool  isDeadChar;
-   };
-
-   KeyData upper;
-   KeyData lower;
-   KeyData goofy;
-};
-
-
-#define NUM_KEYS ( KEY_OEM_102 + 1 )
-#define KEY_FIRST KEY_ESCAPE
-static AsciiData AsciiTable[NUM_KEYS];
-
-void Input::enableMouse()
-{
-    // Do nothing on iOS
-}
-
-void Input::disableMouse()
-{
-    // Do nothing on iOS
-}
-
-void Input::enableKeyboard()
-{
-    // Do nothing on iOS
-}
-
-void Input::disableKeyboard()
-{
-    // Do nothing on iOS
-}
-
-bool Input::isMouseEnabled()
-{
-    return true;
-}
-
-bool Input::isKeyboardEnabled()
-{
-    return false;
-}
- 
-//--------------------------------------------------------------------------
-void Input::init()
-{
-    Con::printf( "Input Init:" );
-   destroy();
-
-   smManager = NULL;
-	//smManager = new iOSInputManager();
-   smActive = false;
-
-   // stop the double-cursor thing
-   Con::setBoolVariable("$pref::Gui::noClampTorqueCursorToWindow", true);
-   
-   // enable main input
-   Input::enable();
-
-   // Startup the Cursor Manager
-   if(!smCursorManager)
-   {
-      smCursorManager = new CursorManager();
-      if(smCursorManager)
-      {
-         // Add the arrow cursor to the stack
-         smCursorManager->pushCursor(CursorManager::curArrow);
-      }
-      else
-      {
-         Con::printf("   Cursor Manager not enabled.");
-      }
-   }
-	
-	
-	for(int i = 0 ; i < MAX_TOUCH_EVENTS; i++ )
-	{
-		lastTouches[i].lastX = -1;
-		lastTouches[i].lastY = -1;
-	}
-	
-	
-   
-   
-   Con::printf( "" );
-}
-
-//------------------------------------------------------------------------------
-ConsoleFunction( isJoystickDetected, bool, 1, 1, "Always false on the iOS." )
-{
-/*
-   argc; argv;
-   return( DInputDevice::joystickDetected() );
-*/
-   return(false);
-}
-
-//------------------------------------------------------------------------------
-ConsoleFunction( getJoystickAxes, const char*, 2, 2, "(handle instance)" )
-{
-
-   return( "" );
-}
-
-//------------------------------------------------------------------------------
-static void fillAsciiTable()
-{
-
-}
-
-//------------------------------------------------------------------------------
-U16 Input::getKeyCode( U16 asciiCode )
-{
-   U16 keyCode = 0;
-   U16 i;
-   
-   // This is done three times so the lowerkey will always
-   // be found first. Some foreign keyboards have duplicate
-   // chars on some keys.
-   for ( i = KEY_FIRST; i < NUM_KEYS && !keyCode; i++ )
-   {
-      if ( AsciiTable[i].lower.ascii == asciiCode )
-      {
-         keyCode = i;
-         break;
-      };
-   }
-
-   for ( i = KEY_FIRST; i < NUM_KEYS && !keyCode; i++ )
-   {
-      if ( AsciiTable[i].upper.ascii == asciiCode )
-      {
-         keyCode = i;
-         break;
-      };
-   }
-
-   for ( i = KEY_FIRST; i < NUM_KEYS && !keyCode; i++ )
-   {
-      if ( AsciiTable[i].goofy.ascii == asciiCode )
-      {
-         keyCode = i;
-         break;
-      };
-   }
-
-   return( keyCode );
-}
-
-//------------------------------------------------------------------------------
-U16 Input::getAscii( U16 keyCode, KEY_STATE keyState )
-{
-   if ( keyCode >= NUM_KEYS )
-      return 0;
-
-   switch ( keyState )
-   {
-      case STATE_LOWER:
-         return AsciiTable[keyCode].lower.ascii;
-      case STATE_UPPER:
-         return AsciiTable[keyCode].upper.ascii;
-      case STATE_GOOFY:
-         return AsciiTable[keyCode].goofy.ascii;
-      default:
-         return(0);
-            
-   }
-}
-
-//------------------------------------------------------------------------------
-void Input::destroy()
-{
-   // turn us off.
-   if (gInputEnabled)
-      disable();
-   
-   if ( smManager && smManager->isEnabled() )
-   {
-      smManager->disable();
-      delete smManager;
-      smManager = NULL;
-   }
-}
-
-//------------------------------------------------------------------------------
-bool Input::enable()
-{
-	Con::printf( "[]Input::enable." );
-
-   gInputEnabled = true;
-
-   if ( smManager && !smManager->isEnabled() )
-      return( smManager->enable() );
-
-   enableMouse();
-   //enableKeyboard();
-
-   return( gInputEnabled );
-}
-
-//------------------------------------------------------------------------------
-void Input::disable()
-{
-	Con::printf( "[]Input::disable." );
-
-   gInputEnabled = false;
-
-  if ( smManager && smManager->isEnabled() )
-      smManager->disable();
-
-   disableMouse();
-   //disableKeyboard();
-}
-
-//------------------------------------------------------------------------------
-void Input::activate()
-{
-   smActive = true;
-
-   enableMouse();
-// enableKeyboard();
-}
-
-//------------------------------------------------------------------------------
-void Input::deactivate()
-{
-//Con::printf( "[]Input::deactivate." );
-
-   deactivateMouse();
-   //deactivateKeyboard();
-
-   smActive = false;
-
-}
-
-//------------------------------------------------------------------------------
-void Input::reactivate()
-{
-   // don't think mac needs to do anything right now!!!!!! TBD
-   
-   // This is soo hacky...
-//   SetForegroundWindow( winState.appWindow );
-//   PostMessage( winState.appWindow, WM_ACTIVATE, WA_ACTIVE, NULL );
-}
-
-//------------------------------------------------------------------------------
-bool Input::isEnabled()
-{
-   if ( smManager )
-      return smManager->isEnabled();
-
-   return(gInputEnabled);
-}
-
-//------------------------------------------------------------------------------
-bool Input::isActive()
-{
-   return smActive;
-}
-
-//------------------------------------------------------------------------------
-void Input::process()
-{
-	//only gets called once per frame, create touches and accelerometer events here
-	//post, then pop each event
-
-	if(platState.multipleTouchesEnabled) processMultipleTouches();
-	
-   if (!smActive || !gInputEnabled)
-      return;
-
-   if (!gMouseEnabled || !gMouseActive)
-      return;
-      
- 
-   if ( smManager && smManager->isEnabled() && smActive )
-      smManager->process();
-}
-
-//------------------------------------------------------------------------------
-InputManager* Input::getManager()
-{
-   return( smManager );
-}
-
-
-//--------------------------------------------------------------------------
-//#pragma message("input remap table might need tweaking - rumors of ibooks having diff virt keycodes, might need intermediate remap...")
-static U8 VcodeRemap[256] =
-{
-KEY_A,                     // 0x00 
-KEY_S,                     // 0x01 
-KEY_D,                     // 0x02 
-KEY_F,                     // 0x03 
-KEY_H,                     // 0x04 
-KEY_G,                     // 0x05 
-KEY_Z,                     // 0x06 
-KEY_X,                     // 0x07 
-KEY_C,                     // 0x08 
-KEY_V,                     // 0x09 
-KEY_Y,                     // 0x0A       // this is questionable - not normal Y code
-KEY_B,                     // 0x0B 
-KEY_Q,                     // 0x0C 
-KEY_W,                     // 0x0D 
-KEY_E,                     // 0x0E 
-KEY_R,                     // 0x0F 
-KEY_Y,                     // 0x10 
-KEY_T,                     // 0x11 
-KEY_1,                     // 0x12 
-KEY_2,                     // 0x13 
-KEY_3,                     // 0x14 
-KEY_4,                     // 0x15 
-KEY_6,                     // 0x16 
-KEY_5,                     // 0x17 
-KEY_EQUALS,                // 0x18 
-KEY_9,                     // 0x19 
-KEY_7,                     // 0x1A 
-KEY_MINUS,                 // 0x1B 
-KEY_8,                     // 0x1C 
-KEY_0,                     // 0x1D 
-KEY_RBRACKET,              // 0x1E 
-KEY_O,                     // 0x1F 
-KEY_U,                     // 0x20 
-KEY_LBRACKET,              // 0x21 
-KEY_I,                     // 0x22 
-KEY_P,                     // 0x23 
-KEY_RETURN,                // 0x24 
-KEY_L,                     // 0x25 
-KEY_J,                     // 0x26 
-KEY_APOSTROPHE,            // 0x27 
-KEY_K,                     // 0x28 
-KEY_SEMICOLON,             // 0x29 
-KEY_BACKSLASH,             // 0x2A 
-KEY_COMMA,                 // 0x2B 
-KEY_SLASH,                 // 0x2C 
-KEY_N,                     // 0x2D 
-KEY_M,                     // 0x2E 
-KEY_PERIOD,                // 0x2F 
-KEY_TAB,                   // 0x30 
-KEY_SPACE,                 // 0x31 
-KEY_TILDE,                 // 0x32 
-KEY_BACKSPACE,             // 0x33 
-0,                         // 0x34 //?
-KEY_ESCAPE,                // 0x35 
-0,                         // 0x36 //?
-KEY_ALT,                   // 0x37 // best mapping for mac Cmd key
-KEY_LSHIFT,                // 0x38 
-KEY_CAPSLOCK,              // 0x39 
-KEY_MAC_OPT,               // 0x3A // direct map mac Option key -- better than KEY_WIN_WINDOWS
-KEY_CONTROL,               // 0x3B 
-KEY_RSHIFT,                // 0x3C 
-0,                         // 0x3D 
-0,                         // 0x3E 
-0,                         // 0x3F 
-0,                         // 0x40 
-KEY_DECIMAL,               // 0x41 
-0,                         // 0x42 
-KEY_MULTIPLY,              // 0x43 
-0,                         // 0x44 
-KEY_ADD,                   // 0x45 
-KEY_SUBTRACT,              // 0x46 // secondary code?
-KEY_NUMLOCK,               // 0x47 // also known as Clear on mac...
-KEY_SEPARATOR,             // 0x48 // secondary code? for KPEqual
-0,                         // 0x49 
-0,                         // 0x4A 
-KEY_DIVIDE,                // 0x4B 
-KEY_NUMPADENTER,           // 0x4C 
-KEY_DIVIDE,                // 0x4D // secondary code?
-KEY_SUBTRACT,              // 0x4E 
-0,                         // 0x4F 
-0,                         // 0x50 
-KEY_SEPARATOR,             // 0x51 // WHAT IS SEP?  This is KPEqual on mac.
-KEY_NUMPAD0,               // 0x52 
-KEY_NUMPAD1,               // 0x53 
-KEY_NUMPAD2,               // 0x54 
-KEY_NUMPAD3,               // 0x55 
-KEY_NUMPAD4,               // 0x56 
-KEY_NUMPAD5,               // 0x57 
-KEY_NUMPAD6,               // 0x58 
-KEY_NUMPAD7,               // 0x59 
-0,                         // 0x5A 
-KEY_NUMPAD8,               // 0x5B 
-KEY_NUMPAD9,               // 0x5C 
-0,                         // 0x5D 
-0,                         // 0x5E 
-0,                         // 0x5F 
-KEY_F5,                    // 0x60 
-KEY_F6,                    // 0x61 
-KEY_F7,                    // 0x62 
-KEY_F3,                    // 0x63 
-KEY_F8,                    // 0x64 
-KEY_F9,                    // 0x65 
-0,                         // 0x66 
-KEY_F11,                   // 0x67 
-0,                         // 0x68 
-KEY_PRINT,                 // 0x69 
-0,                         // 0x6A 
-KEY_SCROLLLOCK,            // 0x6B 
-0,                         // 0x6C 
-KEY_F10,                   // 0x6D 
-0,                         // 0x6E 
-KEY_F12,                   // 0x6F 
-0,                         // 0x70 
-KEY_PAUSE,                 // 0x71 
-KEY_INSERT,                // 0x72 // also known as mac Help
-KEY_HOME,                  // 0x73 
-KEY_PAGE_UP,               // 0x74 
-KEY_DELETE,                // 0x75 // FwdDel
-KEY_F4,                    // 0x76 
-KEY_END,                   // 0x77 
-KEY_F2,                    // 0x78 
-KEY_PAGE_DOWN,             // 0x79 
-KEY_F1,                    // 0x7A 
-KEY_LEFT,                  // 0x7B 
-KEY_RIGHT,                 // 0x7C 
-KEY_DOWN,                  // 0x7D 
-KEY_UP,                    // 0x7E 
-0,                         // 0x7F 
-0,                         // 0x80 
-0,                         // 0x81 
-0,                         // 0x82 
-0,                         // 0x83 
-0,                         // 0x84 
-0,                         // 0x85 
-0,                         // 0x86 
-0,                         // 0x87 
-0,                         // 0x88 
-0,                         // 0x89 
-0,                         // 0x8A 
-0,                         // 0x8B 
-0,                         // 0x8C 
-0,                         // 0x8D 
-0,                         // 0x8E 
-0,                         // 0x8F 
-
-0,                         // 0x90 
-0,                         // 0x91 
-0,                         // 0x92 
-0,                         // 0x93 
-0,                         // 0x94 
-0,                         // 0x95 
-0,                         // 0x96 
-0,                         // 0x97 
-0,                         // 0x98 
-0,                         // 0x99 
-0,                         // 0x9A 
-0,                         // 0x9B 
-0,                         // 0x9C 
-0,                         // 0x9D 
-0,                         // 0x9E 
-0,                         // 0x9F 
-
-0,                         // 0xA0 
-0,                         // 0xA1 
-0,                         // 0xA2 
-0,                         // 0xA3 
-0,                         // 0xA4 
-0,                         // 0xA5 
-0,                         // 0xA6 
-0,                         // 0xA7 
-0,                         // 0xA8 
-0,                         // 0xA9 
-0,                         // 0xAA 
-0,                         // 0xAB 
-0,                         // 0xAC 
-0,                         // 0xAD 
-0,                         // 0xAE 
-0,                         // 0xAF 
-0,                         // 0xB0 
-0,                         // 0xB1 
-0,                         // 0xB2 
-0,                         // 0xB3 
-0,                         // 0xB4 
-0,                         // 0xB5 
-0,                         // 0xB6 
-0,                         // 0xB7 
-0,                         // 0xB8 
-0,                         // 0xB9 
-0,                         // 0xBA 
-0,                         // 0xBB 
-0,                         // 0xBC 
-0,                         // 0xBD 
-0,                         // 0xBE 
-0,                         // 0xBF 
-0,                         // 0xC0 
-0,                         // 0xC1 
-0,                         // 0xC2 
-0,                         // 0xC3 
-0,                         // 0xC4 
-0,                         // 0xC5 
-0,                         // 0xC6 
-0,                         // 0xC7 
-0,                         // 0xC8 
-0,                         // 0xC9 
-0,                         // 0xCA 
-0,                         // 0xCB 
-0,                         // 0xCC 
-0,                         // 0xCD 
-0,                         // 0xCE 
-0,                         // 0xCF 
-0,                         // 0xD0 
-0,                         // 0xD1 
-0,                         // 0xD2 
-0,                         // 0xD3 
-0,                         // 0xD4 
-0,                         // 0xD5 
-0,                         // 0xD6 
-0,                         // 0xD7 
-0,                         // 0xD8 
-0,                         // 0xD9 
-0,                         // 0xDA 
-0,                         // 0xDB 
-0,                         // 0xDC 
-0,                         // 0xDD 
-0,                         // 0xDE 
-0,                         // 0xDF 
-0,                         // 0xE0 
-0,                         // 0xE1 
-0,                         // 0xE2 
-0,                         // 0xE3 
-0,                         // 0xE4 
-
-0,                         // 0xE5 
-
-0,                         // 0xE6 
-0,                         // 0xE7 
-0,                         // 0xE8 
-0,                         // 0xE9 
-0,                         // 0xEA 
-0,                         // 0xEB 
-0,                         // 0xEC 
-0,                         // 0xED 
-0,                         // 0xEE 
-0,                         // 0xEF 
-   
-0,                         // 0xF0 
-0,                         // 0xF1 
-0,                         // 0xF2 
-0,                         // 0xF3 
-0,                         // 0xF4 
-0,                         // 0xF5 
-   
-0,                         // 0xF6 
-0,                         // 0xF7 
-0,                         // 0xF8 
-0,                         // 0xF9 
-0,                         // 0xFA 
-0,                         // 0xFB 
-0,                         // 0xFC 
-0,                         // 0xFD 
-0,                         // 0xFE 
-0                          // 0xFF 
-};   
-
-
-U8 TranslateOSKeyCode(U8 vcode)
-{
-   return VcodeRemap[vcode];   
-}   
-
-#pragma mark ---- Clipboard functions ----
-//-----------------------------------------------------------------------------
-const char* Platform::getClipboard()
-{
-	return NULL;//no clipboard on iOS
-}
-
-//-----------------------------------------------------------------------------
-bool Platform::setClipboard(const char *text)
-{
-	return NULL;//no clipboard on iOS
-}
-
-#pragma mark ---- Cursor Functions ----
-//------------------------------------------------------------------------------
-void Input::pushCursor(S32 cursorID)
-{
-   CursorManager* cm = getCursorManager();
-   if(cm)
-      cm->pushCursor(cursorID);
-}
-
-//------------------------------------------------------------------------------
-void Input::popCursor()
-{
-   CursorManager* cm = getCursorManager();
-   if(cm)
-      cm->popCursor();
-}
-
-//------------------------------------------------------------------------------
-void Input::refreshCursor()
-{
-   CursorManager* cm = getCursorManager();
-   if(cm)
-      cm->refreshCursor();
-}
-
-#pragma mark ---- DoubleClick Functions ----
-//------------------------------------------------------------------------------
-U32 Input::getDoubleClickTime()
-{
-	return IOS_DOUBLE_CLICK_TIME;
-}
-
-//------------------------------------------------------------------------------
-S32 Input::getDoubleClickWidth()
-{
-   // this is an arbitrary value.
-   return 10;
-}
-
-//------------------------------------------------------------------------------
-S32 Input::getDoubleClickHeight()
-{
-   return getDoubleClickWidth();
-}
-
-#pragma mark -
-
-//------------------------------------------------------------------------------
-bool enableKeyboard()
-{
-   if ( !gInputEnabled )
-      return( false );
-
-   if ( gKBEnabled && gKBActive )
-      return( true );
-
-   gKBEnabled = true;
-   if ( Input::isActive() )
-      gKBEnabled = activateKeyboard();
-
-   if ( gKBEnabled )
-   {
-      Con::printf( "Hardware-direct keyboard enabled." );
-   }
-   else
-   {
-      Con::warnf( "Hardware-direct keyboard failed to enable!" );
-   }
-
-   return( gKBEnabled );
-}
-
-//------------------------------------------------------------------------------
-void disableKeyboard()
-{
-   if ( !gInputEnabled || !gKBEnabled )
-      return;
-
-   deactivateKeyboard();
-   gKBEnabled = false;
-
-   Con::printf( "Hardware-direct keyboard disabled." );
-}
-
-//------------------------------------------------------------------------------
-bool activateKeyboard()
-{
-   if ( !gInputEnabled || !Input::isActive() || !gKBEnabled )
-      return( false );
-
-   OSStatus status = noErr;
-   if (status==noErr)
-      gKBActive = true;
-
-   return( gKBActive );
-}
-
-//------------------------------------------------------------------------------
-void deactivateKeyboard()
-{
-   if ( gInputEnabled && gKBActive )
-   {
-      gKBActive = false;
-   }
-}
-
-//------------------------------------------------------------------------------
-bool enableMouse()
-{
-   if ( !gInputEnabled )
-      return( false );
-
-   if ( gMouseEnabled && gMouseActive )
-      return( true );
-
-   gMouseEnabled = activateMouse();
-
-   return( gMouseEnabled );
-}
-
-//------------------------------------------------------------------------------
-void disableMouse()
-{
-   if ( !gInputEnabled || !gMouseEnabled )
-      return;
-
-   deactivateMouse();
-   gMouseEnabled = false;
-
-   bool hwMouse = false;
-   Con::printf( "%s disabled", hwMouse?"Hardware-direct mouse":"Basic mouse capture");
-}
-
-//------------------------------------------------------------------------------
-bool activateMouse()
-{
-   if ( !gInputEnabled || !Input::isActive() || !gMouseEnabled )
-      return( false );
-   
-   if (gMouseActive)
-      return(true);
-
-   gMouseActive = true;
-
-   return( gMouseActive );
-}
-
-//------------------------------------------------------------------------------
-void deactivateMouse()
-{
-   if ( !gInputEnabled || !gMouseActive )
-       return;
-   
-   gMouseActive = false;
-}
-
-
-//------------------------------------------------------------------------------
-ConsoleFunction( enableMouse, bool, 1, 1, "enableMouse()" )
-{
-   return( enableMouse() );
-}
-
-//------------------------------------------------------------------------------
-ConsoleFunction( disableMouse, void, 1, 1, "disableMouse()" )
-{
-   disableMouse();
-}
-
-//------------------------------------------------------------------------------
-void printInputState(void)
-{
-   if ( gInputEnabled )
-   {
-         Con::printf( "Low-level input system is enabled." );
-      
-      Con::printf( "- Keyboard is %sabled and %sactive.", 
-            gKBEnabled ? "en" : "dis",
-            gKBActive ? "" : "in" );
-      Con::printf( "- Mouse is %sabled and %sactive.", 
-            gMouseEnabled ? "en" : "dis",
-            gMouseActive ? "" : "in" );
-/*
-      Con::printf( "- Joystick is %sabled and %sactive.", 
-            gJoystickEnabled() ? "en" : "dis",
-            gJoystickActive() ? "" : "in" );
-*/
-   }
-   else
-   {
-      Con::printf( "Low-level input system is disabled." );
-   }
-}
-
-//------------------------------------------------------------------------------
-ConsoleFunction( echoInputState, void, 1, 1, "echoInputState()" )
-{
-   printInputState();
-}
-
-//------------------------------------------------------------------------------
-ConsoleFunction( toggleInputState, void, 1, 1, "toggleInputState()" )
-{
-   if (gInputEnabled)
-      Input::disable();
-   else
-      Input::enable();
-
-   printInputState();
-}
-
-//------------------------------------------------------------------------------
-ConsoleFunction( deactivateKeyboard, void, 1, 1, "deactivateKeyboard();")
-{
-   // these are only useful on the windows side. They deal with some vagaries of win32 DirectInput.
-}
-
-ConsoleFunction( activateKeyboard, void, 1, 1, "activateKeyboard();")
-{
-   // these are only useful on the windows side. They deal with some vagaries of win32 DirectInput.
-}
-
-
-//------------------------------------------------------------------------------
-void Input::setCursorPos(S32 x, S32 y)
-{
-	//this gets called from GuiCanvas to set the game mouse cursor
-}
-
-int processMultipleTouches()
-{	
-	char posX[256], posY[256], temp[10], touchNums[256];
-    
-	dMemset(posX, 0, sizeof(posX));
-    dMemset(posY, 0, sizeof(posY));
-    dMemset(touchNums, 0, sizeof(touchNums));
-
-	touchEvent *currentEvent;
-	
-	//Down Events
-	int numTouchDownEvents = TouchDownEvents.size();
-	while( TouchDownEvents.size() > 0 )
-    {
-		currentEvent = &TouchDownEvents.last();
-		dItoa( currentEvent->x, temp );
-		dStrcat( posX, temp );
-		dStrcat( posX, " " );
-		
-		dItoa( currentEvent->y, temp );
-		dStrcat( posY, temp );
-		dStrcat( posY, " " );
-		
-		dItoa( currentEvent->number, temp );
-		dStrcat( touchNums, temp );
-		dStrcat( touchNums, " " );
-		
-		TouchDownEvents.pop_back();
-	}
-	dItoa( numTouchDownEvents, temp );
-    
-    if( numTouchDownEvents > 0 )
-    {
-        InputEvent touchEvent;
-        
-        touchEvent.deviceInst = 0;
-        touchEvent.objInst = SI_TOUCHDOWN;
-        
-        touchEvent.deviceType = ScreenTouchDeviceType;
-        touchEvent.action = SI_TOUCH;
-        touchEvent.objType = SI_TOUCHDOWN;
-        
-        dStrcpy(touchEvent.touchesX, posX);
-        dStrcpy(touchEvent.touchesY, posY);
-        dStrcpy(touchEvent.touchIDs, touchNums);
-        
-        touchEvent.modifier = 0;
-        
-        Game->postEvent(touchEvent);        
-    }
-    
-    // Deprecated in 1.5
-	//if( numTouchDownEvents > 0 )
-    //    Con::executef( 4, "oniOSTouchDown", touchNums , posX, posY );
-
-	//Move events
-	int numTouchMoveEvents = TouchMoveEvents.size();
-	while( TouchMoveEvents.size() > 0 ) 
-    {
-		currentEvent = &TouchMoveEvents.last();
-		dItoa( currentEvent->x, temp );
-		dStrcat( posX, temp );
-		dStrcat( posX, " " );
-		
-		dItoa( currentEvent->y, temp );
-		dStrcat( posY, temp );
-		dStrcat( posY, " " );
-		
-		dItoa( currentEvent->number, temp );
-		dStrcat( touchNums, temp );
-		dStrcat( touchNums, " " );		
-		
-		TouchMoveEvents.pop_back();
-	}
-	dItoa( numTouchMoveEvents, temp );
-    
-    if( numTouchMoveEvents > 0 )
-    {
-        InputEvent touchEvent;
-        
-        touchEvent.deviceInst = 0;
-        touchEvent.objInst = SI_TOUCHMOVE;
-        
-        touchEvent.deviceType = ScreenTouchDeviceType;
-        touchEvent.action = SI_TOUCH;
-        touchEvent.objType = SI_TOUCHMOVE;
-        
-        dStrcpy(touchEvent.touchesX, posX);
-        dStrcpy(touchEvent.touchesY, posY);
-        dStrcpy(touchEvent.touchIDs, touchNums);
-        
-        touchEvent.modifier = 0;
-        
-        Game->postEvent(touchEvent);        
-    }
-    
-    // Deprecated in 1.5 -MP
-	//if( numTouchMoveEvents > 0 )
-    //    Con::executef( 4, "oniOSTouchMove", touchNums , posX, posY );
-	
-	//Up events
-	int numTouchUpEvents = TouchUpEvents.size();
-    
-    dMemset(posX, 0, sizeof(posX));
-    dMemset(posY, 0, sizeof(posY));
-    dMemset(touchNums, 0, sizeof(touchNums));
-    
-	while( TouchUpEvents.size() > 0 ) 
-    {
-		currentEvent = &TouchUpEvents.last();
-		dItoa( currentEvent->x, temp );
-		dStrcat( posX, temp );
-		dStrcat( posX, " " );
-		
-		dItoa( currentEvent->y, temp );
-		dStrcat( posY, temp );
-		dStrcat( posY, " " );
-		
-		dItoa( currentEvent->number, temp );
-		dStrcat( touchNums, temp );
-		dStrcat( touchNums, " " );		
-		
-		lastTouches[currentEvent->number].lastX = -1;
-		lastTouches[currentEvent->number].lastY	 = -1;
-		
-        int x;
-        x = -1;
-        
-        lastTouches[currentEvent->number].lastX = -1;
-		lastTouches[currentEvent->number].lastY	 = -1;
-        
-		TouchUpEvents.pop_back();
-	}
-    
-	dItoa( numTouchUpEvents, temp );
-	
-    if( numTouchUpEvents > 0 )
-    {
-        InputEvent touchEvent;
-        
-        touchEvent.deviceInst = 0;
-        touchEvent.objInst = SI_TOUCHUP;
-        
-        touchEvent.deviceType = ScreenTouchDeviceType;
-        touchEvent.action = SI_TOUCH;
-        touchEvent.objType = SI_TOUCHUP;
-        
-        dStrcpy(touchEvent.touchesX, posX);
-        dStrcpy(touchEvent.touchesY, posY);
-        dStrcpy(touchEvent.touchIDs, touchNums);
-        
-        touchEvent.modifier = 0;
-        
-        Game->postEvent(touchEvent);        
-    }
-    
-    // Deprecated in 1.5 -MP
-    //if( numTouchUpEvents > 0 )
-    //    Con::executef( 4, "oniOSTouchUp", touchNums , posX, posY );
-	
-	//Luma: Tap support
-	posX[0] = '\0';
-	posY[0] = '\0';
-    
-	int numTapEvents = TouchTapEvents.size();
-	
-    while( TouchTapEvents.size() > 0 ) 
-    {
-		currentEvent = &TouchTapEvents.last();
-		sprintf( temp, "%d ", currentEvent->x );
-		dStrcat( posX, temp );
-		
-		sprintf( temp, "%d ", currentEvent->y );
-		dStrcat( posY, temp );
-		
-		TouchTapEvents.pop_back();
-	}
-    
-	sprintf( temp, "%d", numTapEvents );
-    
-	//if( numTapEvents > 0 )
-    //    Con::executef( 4, "oniOSTouchTap", temp , posX, posY );
-	
-	return numTouchDownEvents + numTouchMoveEvents + numTouchUpEvents;
-}
-
-
-
-//we want these to only be called once per frame!!
-bool createMouseMoveEvent( S32 touchNumber, S32 x, S32 y, S32 lastX, S32 lastY ) //EFM
-{	
-	S32 currentSlot = -1;
-	
-	for( int i = 0 ; (i < MAX_TOUCH_EVENTS) && (currentSlot == -1) ; i++ )
-	{
-		if( (lastX == lastTouches[i].lastX ) &&
-		   (lastY == lastTouches[i].lastY ) )
-		{
-			currentSlot = i;
-		}
-	}
-
-	if( currentSlot == -1 ) return false;
-	
-	ScreenTouchEvent event;
-	event.xPos = x;
-	event.yPos = y;
-	event.action = SI_MOVE;
-	event.touchID = currentSlot;
-    event.numTouches = 0;
-    
-	//Luma: Mouse not moving (no hover for mouse fingers!)
-	Canvas->setCursorPos( Point2I( x, y ) );  
-
-	if( currentSlot != -1 )
-	{
-		lastTouches[currentSlot].lastX = x;
-		lastTouches[currentSlot].lastY = y;
-	}		
-	
-	TouchMoveEvents.push_back( touchEvent( currentSlot, x, y ) );	
-	Game->postEvent(event);
-	
-	return true;//return false if we get bad values or something
-}
-
-
-bool createMouseDownEvent( S32 touchNumber, S32 x, S32 y, U32 numTouches ) 
-{
-	S32 vacantSlot = -1;
-	
-	for( int i = 0 ; (i < MAX_TOUCH_EVENTS) && (vacantSlot == -1) ; i++ )
-	{
-		if( lastTouches[i].lastX == -1 )
-		{
-			vacantSlot = i;
-		}
-	}
-	
-	if( vacantSlot == -1 ) 
-        return false;
-		
-	ScreenTouchEvent event;
-	event.xPos = x;
-	event.yPos = y;
-    event.touchID = vacantSlot;
-	event.action = SI_MAKE;
-	event.numTouches = numTouches;
-    
-	//Luma: Update position
-	Canvas->setCursorPos( Point2I( x, y ) );  		
-	
-	if( vacantSlot != -1 )
-	{
-		lastTouches[vacantSlot].lastX = x;
-		lastTouches[vacantSlot].lastY = y;
-	}	
-
-	TouchDownEvents.push_back( touchEvent( vacantSlot, x, y ) );
-	Game->postEvent(event);
-	
-	return true;//return false if we get bad values or something
-}
-
-bool createMouseUpEvent( S32 touchNumber, S32 x, S32 y, S32 lastX, S32 lastY, U32 numTouches ) //EFM
-{	
-	S32 currentSlot = -1;
-	
-	for( int i = 0 ; (i < MAX_TOUCH_EVENTS) && (currentSlot == -1) ; i++ )
-	{
-		if(( (x == lastTouches[i].lastX) && (y == lastTouches[i].lastY )) ||
-		   ( (lastX == lastTouches[i].lastX ) && (lastY == lastTouches[i].lastY )))
-		{
-			currentSlot = i;
-		}	
-	}
-	
-	if( currentSlot == -1 ) 
-        return false;
-
-	ScreenTouchEvent event;
-	event.xPos = x;
-	event.yPos = y;
-	event.action = SI_BREAK;
-	event.touchID = currentSlot;
-	event.numTouches = numTouches;
-    
-	TouchUpEvents.push_back( touchEvent( currentSlot, x, y ) );	
-	
-	Game->postEvent(event);
-	
-	return true;//return false if we get bad values or something
-}
-
-void clearPendingMultitouchEvents( void ) //EFM
-{	
-    for( int i = 0 ; i < MAX_TOUCH_EVENTS ; i++ )
-	{
-		lastTouches[i].lastX = -1;
-		lastTouches[i].lastY = -1;
-	}
-
-	TouchMoveEvents.clear();
-	TouchDownEvents.clear();
-	TouchUpEvents.clear();
-}
-
-
-//Luma: Tap support
-void createMouseTapEvent( S32 nbrTaps, S32 x, S32 y ) {
-
-	TouchTapEvents.push_back( touchEvent( nbrTaps, x, y ) );
-}
-
-
-// 0 = x, 1 = y, 2 = z
-UIAccelerationValue g_lastAccel[3];
-
-// PUAP -Mat this is polled at AccelerometerUpdateMS, which should be set at around 33 
-/*
-bool createAccelMoveEvent( UIAccelerationValue *accel ) {
-	bool returnVal = false;
-	
-	U32 axis[3] = { SI_XAXIS, SI_YAXIS, SI_ZAXIS }; 
-	
-	for( int i = 0; i < 3; i++ ) {
-		if(accel[i]  != g_lastAccel[i] ) {
-			InputEvent event;
-			event.deviceInst = 0;//joystick number
-			event.fValue = accel[i];
-			event.deviceType = JoystickDeviceType;
-			event.objType = axis[i];
-			event.objInst = i;//axis number
-			event.action = SI_MOVE;
-			event.modifier = 0;
-			Game->postEvent(event);
-			g_lastAccel[i]  = accel[i];
-			returnVal = true;
-		}
-	}
-	return false;
-}
-*/
+//-----------------------------------------------------------------------------
+// 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.
+//-----------------------------------------------------------------------------
+
+
+#include "platformiOS/platformiOS.h"
+#include "platformiOS/iOSUtil.h"
+#include "platform/platformInput.h"
+#include "platform/event.h"
+#include "console/console.h"
+#include "game/gameInterface.h"
+#include "string/Unicode.h"
+#include "gui/guiCanvas.h"
+
+
+// <Mat> just some random number 50, we'll get a proper value later
+#define IOS_DOUBLE_CLICK_TIME	( 50 * 60.0f * 1000.0f)
+
+// Static class variables:
+InputManager* Input::smManager;
+bool           Input::smActive;
+CursorManager* Input::smCursorManager = 0;
+
+
+bool gInputEnabled = false;
+bool gMouseEnabled = false;
+bool gKBEnabled = false;
+bool gMouseActive = false;
+bool gKBActive = false;
+
+//------------------------------------------------------------------------------
+// Helper functions.  Should migrate to an InputManager object at some point.
+bool enableKeyboard(void);
+void disableKeyboard(void);
+bool activateKeyboard(void);
+void deactivateKeyboard(void);
+bool enableMouse(void);
+void disableMouse(void);
+bool activateMouse(void);
+void deactivateMouse(void);
+
+
+
+static void fillAsciiTable();
+
+
+struct touchEvent {
+	S32 number;
+	S32 x;
+	S32 y;
+	touchEvent( S32 a, S32 b, S32 c ) {
+		number = a;
+		x = b;
+		y = c;
+	}
+};
+
+
+Vector<touchEvent> TouchMoveEvents;//<Mat> to make sure we don't have multiple events per frame
+Vector<touchEvent> TouchDownEvents;
+Vector<touchEvent> TouchUpEvents;
+
+// EFM - BEGIN TOUCH CHANGES
+#define MAX_TOUCH_EVENTS 5
+
+struct touchTracker {
+	S32 lastX;
+	S32 lastY;
+	Vector<touchEvent> downEvents;
+	Vector<touchEvent> moveEvents;
+	Vector<touchEvent> upEvents;
+};
+
+touchTracker CombinedTouchEvents[MAX_TOUCH_EVENTS];
+
+struct touchCorrelation {
+	S32 lastX;
+	S32 lastY;
+};
+
+touchCorrelation lastTouches[MAX_TOUCH_EVENTS];
+
+// EFM - END TOUCH CHANGES
+
+//Luma: Tap support
+Vector<touchEvent> TouchTapEvents;
+int processMultipleTouches();
+
+
+//------------------------------------------------------------------------------
+//
+// This function gets the standard ASCII code corresponding to our key code
+// and the existing modifier key state.
+//
+//------------------------------------------------------------------------------
+struct AsciiData
+{
+   struct KeyData
+   {
+      U16   ascii;
+      bool  isDeadChar;
+   };
+
+   KeyData upper;
+   KeyData lower;
+   KeyData goofy;
+};
+
+
+#define NUM_KEYS ( KEY_OEM_102 + 1 )
+#define KEY_FIRST KEY_ESCAPE
+static AsciiData AsciiTable[NUM_KEYS];
+
+void Input::enableMouse()
+{
+    // Do nothing on iOS
+}
+
+void Input::disableMouse()
+{
+    // Do nothing on iOS
+}
+
+void Input::enableKeyboard()
+{
+    // Do nothing on iOS
+}
+
+void Input::disableKeyboard()
+{
+    // Do nothing on iOS
+}
+
+bool Input::isMouseEnabled()
+{
+    return true;
+}
+
+bool Input::isKeyboardEnabled()
+{
+    return false;
+}
+ 
+//--------------------------------------------------------------------------
+void Input::init()
+{
+    Con::printf( "Input Init:" );
+   destroy();
+
+   smManager = NULL;
+	//smManager = new iOSInputManager();
+   smActive = false;
+
+   // stop the double-cursor thing
+   Con::setBoolVariable("$pref::Gui::noClampTorqueCursorToWindow", true);
+   
+   // enable main input
+   Input::enable();
+
+   // Startup the Cursor Manager
+   if(!smCursorManager)
+   {
+      smCursorManager = new CursorManager();
+      if(smCursorManager)
+      {
+         // Add the arrow cursor to the stack
+         smCursorManager->pushCursor(CursorManager::curArrow);
+      }
+      else
+      {
+         Con::printf("   Cursor Manager not enabled.");
+      }
+   }
+	
+	
+	for(int i = 0 ; i < MAX_TOUCH_EVENTS; i++ )
+	{
+		lastTouches[i].lastX = -1;
+		lastTouches[i].lastY = -1;
+	}
+	
+	
+   
+   
+   Con::printf( "" );
+}
+
+//------------------------------------------------------------------------------
+ConsoleFunction( isJoystickDetected, bool, 1, 1, "Always false on the iOS." )
+{
+/*
+   argc; argv;
+   return( DInputDevice::joystickDetected() );
+*/
+   return(false);
+}
+
+//------------------------------------------------------------------------------
+ConsoleFunction( getJoystickAxes, const char*, 2, 2, "(handle instance)" )
+{
+
+   return( "" );
+}
+
+//------------------------------------------------------------------------------
+static void fillAsciiTable()
+{
+
+}
+
+//------------------------------------------------------------------------------
+U16 Input::getKeyCode( U16 asciiCode )
+{
+   U16 keyCode = 0;
+   U16 i;
+   
+   // This is done three times so the lowerkey will always
+   // be found first. Some foreign keyboards have duplicate
+   // chars on some keys.
+   for ( i = KEY_FIRST; i < NUM_KEYS && !keyCode; i++ )
+   {
+      if ( AsciiTable[i].lower.ascii == asciiCode )
+      {
+         keyCode = i;
+         break;
+      };
+   }
+
+   for ( i = KEY_FIRST; i < NUM_KEYS && !keyCode; i++ )
+   {
+      if ( AsciiTable[i].upper.ascii == asciiCode )
+      {
+         keyCode = i;
+         break;
+      };
+   }
+
+   for ( i = KEY_FIRST; i < NUM_KEYS && !keyCode; i++ )
+   {
+      if ( AsciiTable[i].goofy.ascii == asciiCode )
+      {
+         keyCode = i;
+         break;
+      };
+   }
+
+   return( keyCode );
+}
+
+//------------------------------------------------------------------------------
+U16 Input::getAscii( U16 keyCode, KEY_STATE keyState )
+{
+   if ( keyCode >= NUM_KEYS )
+      return 0;
+
+   switch ( keyState )
+   {
+      case STATE_LOWER:
+         return AsciiTable[keyCode].lower.ascii;
+      case STATE_UPPER:
+         return AsciiTable[keyCode].upper.ascii;
+      case STATE_GOOFY:
+         return AsciiTable[keyCode].goofy.ascii;
+      default:
+         return(0);
+            
+   }
+}
+
+//------------------------------------------------------------------------------
+void Input::destroy()
+{
+   // turn us off.
+   if (gInputEnabled)
+      disable();
+   
+   if ( smManager && smManager->isEnabled() )
+   {
+      smManager->disable();
+      delete smManager;
+      smManager = NULL;
+   }
+}
+
+//------------------------------------------------------------------------------
+bool Input::enable()
+{
+	Con::printf( "[]Input::enable." );
+
+   gInputEnabled = true;
+
+   if ( smManager && !smManager->isEnabled() )
+      return( smManager->enable() );
+
+   enableMouse();
+   //enableKeyboard();
+
+   return( gInputEnabled );
+}
+
+//------------------------------------------------------------------------------
+void Input::disable()
+{
+	Con::printf( "[]Input::disable." );
+
+   gInputEnabled = false;
+
+  if ( smManager && smManager->isEnabled() )
+      smManager->disable();
+
+   disableMouse();
+   //disableKeyboard();
+}
+
+//------------------------------------------------------------------------------
+void Input::activate()
+{
+   smActive = true;
+
+   enableMouse();
+// enableKeyboard();
+}
+
+//------------------------------------------------------------------------------
+void Input::deactivate()
+{
+//Con::printf( "[]Input::deactivate." );
+
+   deactivateMouse();
+   //deactivateKeyboard();
+
+   smActive = false;
+
+}
+
+//------------------------------------------------------------------------------
+void Input::reactivate()
+{
+   // don't think mac needs to do anything right now!!!!!! TBD
+   
+   // This is soo hacky...
+//   SetForegroundWindow( winState.appWindow );
+//   PostMessage( winState.appWindow, WM_ACTIVATE, WA_ACTIVE, NULL );
+}
+
+//------------------------------------------------------------------------------
+bool Input::isEnabled()
+{
+   if ( smManager )
+      return smManager->isEnabled();
+
+   return(gInputEnabled);
+}
+
+//------------------------------------------------------------------------------
+bool Input::isActive()
+{
+   return smActive;
+}
+
+//------------------------------------------------------------------------------
+void Input::process()
+{
+	//only gets called once per frame, create touches and accelerometer events here
+	//post, then pop each event
+
+	if(platState.multipleTouchesEnabled) processMultipleTouches();
+	
+   if (!smActive || !gInputEnabled)
+      return;
+
+   if (!gMouseEnabled || !gMouseActive)
+      return;
+      
+ 
+   if ( smManager && smManager->isEnabled() && smActive )
+      smManager->process();
+}
+
+//------------------------------------------------------------------------------
+InputManager* Input::getManager()
+{
+   return( smManager );
+}
+
+
+//--------------------------------------------------------------------------
+//#pragma message("input remap table might need tweaking - rumors of ibooks having diff virt keycodes, might need intermediate remap...")
+static U8 VcodeRemap[256] =
+{
+KEY_A,                     // 0x00 
+KEY_S,                     // 0x01 
+KEY_D,                     // 0x02 
+KEY_F,                     // 0x03 
+KEY_H,                     // 0x04 
+KEY_G,                     // 0x05 
+KEY_Z,                     // 0x06 
+KEY_X,                     // 0x07 
+KEY_C,                     // 0x08 
+KEY_V,                     // 0x09 
+KEY_Y,                     // 0x0A       // this is questionable - not normal Y code
+KEY_B,                     // 0x0B 
+KEY_Q,                     // 0x0C 
+KEY_W,                     // 0x0D 
+KEY_E,                     // 0x0E 
+KEY_R,                     // 0x0F 
+KEY_Y,                     // 0x10 
+KEY_T,                     // 0x11 
+KEY_1,                     // 0x12 
+KEY_2,                     // 0x13 
+KEY_3,                     // 0x14 
+KEY_4,                     // 0x15 
+KEY_6,                     // 0x16 
+KEY_5,                     // 0x17 
+KEY_EQUALS,                // 0x18 
+KEY_9,                     // 0x19 
+KEY_7,                     // 0x1A 
+KEY_MINUS,                 // 0x1B 
+KEY_8,                     // 0x1C 
+KEY_0,                     // 0x1D 
+KEY_RBRACKET,              // 0x1E 
+KEY_O,                     // 0x1F 
+KEY_U,                     // 0x20 
+KEY_LBRACKET,              // 0x21 
+KEY_I,                     // 0x22 
+KEY_P,                     // 0x23 
+KEY_RETURN,                // 0x24 
+KEY_L,                     // 0x25 
+KEY_J,                     // 0x26 
+KEY_APOSTROPHE,            // 0x27 
+KEY_K,                     // 0x28 
+KEY_SEMICOLON,             // 0x29 
+KEY_BACKSLASH,             // 0x2A 
+KEY_COMMA,                 // 0x2B 
+KEY_SLASH,                 // 0x2C 
+KEY_N,                     // 0x2D 
+KEY_M,                     // 0x2E 
+KEY_PERIOD,                // 0x2F 
+KEY_TAB,                   // 0x30 
+KEY_SPACE,                 // 0x31 
+KEY_TILDE,                 // 0x32 
+KEY_BACKSPACE,             // 0x33 
+0,                         // 0x34 //?
+KEY_ESCAPE,                // 0x35 
+0,                         // 0x36 //?
+KEY_ALT,                   // 0x37 // best mapping for mac Cmd key
+KEY_LSHIFT,                // 0x38 
+KEY_CAPSLOCK,              // 0x39 
+KEY_MAC_OPT,               // 0x3A // direct map mac Option key -- better than KEY_WIN_WINDOWS
+KEY_CONTROL,               // 0x3B 
+KEY_RSHIFT,                // 0x3C 
+0,                         // 0x3D 
+0,                         // 0x3E 
+0,                         // 0x3F 
+0,                         // 0x40 
+KEY_DECIMAL,               // 0x41 
+0,                         // 0x42 
+KEY_MULTIPLY,              // 0x43 
+0,                         // 0x44 
+KEY_ADD,                   // 0x45 
+KEY_SUBTRACT,              // 0x46 // secondary code?
+KEY_NUMLOCK,               // 0x47 // also known as Clear on mac...
+KEY_SEPARATOR,             // 0x48 // secondary code? for KPEqual
+0,                         // 0x49 
+0,                         // 0x4A 
+KEY_DIVIDE,                // 0x4B 
+KEY_NUMPADENTER,           // 0x4C 
+KEY_DIVIDE,                // 0x4D // secondary code?
+KEY_SUBTRACT,              // 0x4E 
+0,                         // 0x4F 
+0,                         // 0x50 
+KEY_SEPARATOR,             // 0x51 // WHAT IS SEP?  This is KPEqual on mac.
+KEY_NUMPAD0,               // 0x52 
+KEY_NUMPAD1,               // 0x53 
+KEY_NUMPAD2,               // 0x54 
+KEY_NUMPAD3,               // 0x55 
+KEY_NUMPAD4,               // 0x56 
+KEY_NUMPAD5,               // 0x57 
+KEY_NUMPAD6,               // 0x58 
+KEY_NUMPAD7,               // 0x59 
+0,                         // 0x5A 
+KEY_NUMPAD8,               // 0x5B 
+KEY_NUMPAD9,               // 0x5C 
+0,                         // 0x5D 
+0,                         // 0x5E 
+0,                         // 0x5F 
+KEY_F5,                    // 0x60 
+KEY_F6,                    // 0x61 
+KEY_F7,                    // 0x62 
+KEY_F3,                    // 0x63 
+KEY_F8,                    // 0x64 
+KEY_F9,                    // 0x65 
+0,                         // 0x66 
+KEY_F11,                   // 0x67 
+0,                         // 0x68 
+KEY_PRINT,                 // 0x69 
+0,                         // 0x6A 
+KEY_SCROLLLOCK,            // 0x6B 
+0,                         // 0x6C 
+KEY_F10,                   // 0x6D 
+0,                         // 0x6E 
+KEY_F12,                   // 0x6F 
+0,                         // 0x70 
+KEY_PAUSE,                 // 0x71 
+KEY_INSERT,                // 0x72 // also known as mac Help
+KEY_HOME,                  // 0x73 
+KEY_PAGE_UP,               // 0x74 
+KEY_DELETE,                // 0x75 // FwdDel
+KEY_F4,                    // 0x76 
+KEY_END,                   // 0x77 
+KEY_F2,                    // 0x78 
+KEY_PAGE_DOWN,             // 0x79 
+KEY_F1,                    // 0x7A 
+KEY_LEFT,                  // 0x7B 
+KEY_RIGHT,                 // 0x7C 
+KEY_DOWN,                  // 0x7D 
+KEY_UP,                    // 0x7E 
+0,                         // 0x7F 
+0,                         // 0x80 
+0,                         // 0x81 
+0,                         // 0x82 
+0,                         // 0x83 
+0,                         // 0x84 
+0,                         // 0x85 
+0,                         // 0x86 
+0,                         // 0x87 
+0,                         // 0x88 
+0,                         // 0x89 
+0,                         // 0x8A 
+0,                         // 0x8B 
+0,                         // 0x8C 
+0,                         // 0x8D 
+0,                         // 0x8E 
+0,                         // 0x8F 
+
+0,                         // 0x90 
+0,                         // 0x91 
+0,                         // 0x92 
+0,                         // 0x93 
+0,                         // 0x94 
+0,                         // 0x95 
+0,                         // 0x96 
+0,                         // 0x97 
+0,                         // 0x98 
+0,                         // 0x99 
+0,                         // 0x9A 
+0,                         // 0x9B 
+0,                         // 0x9C 
+0,                         // 0x9D 
+0,                         // 0x9E 
+0,                         // 0x9F 
+
+0,                         // 0xA0 
+0,                         // 0xA1 
+0,                         // 0xA2 
+0,                         // 0xA3 
+0,                         // 0xA4 
+0,                         // 0xA5 
+0,                         // 0xA6 
+0,                         // 0xA7 
+0,                         // 0xA8 
+0,                         // 0xA9 
+0,                         // 0xAA 
+0,                         // 0xAB 
+0,                         // 0xAC 
+0,                         // 0xAD 
+0,                         // 0xAE 
+0,                         // 0xAF 
+0,                         // 0xB0 
+0,                         // 0xB1 
+0,                         // 0xB2 
+0,                         // 0xB3 
+0,                         // 0xB4 
+0,                         // 0xB5 
+0,                         // 0xB6 
+0,                         // 0xB7 
+0,                         // 0xB8 
+0,                         // 0xB9 
+0,                         // 0xBA 
+0,                         // 0xBB 
+0,                         // 0xBC 
+0,                         // 0xBD 
+0,                         // 0xBE 
+0,                         // 0xBF 
+0,                         // 0xC0 
+0,                         // 0xC1 
+0,                         // 0xC2 
+0,                         // 0xC3 
+0,                         // 0xC4 
+0,                         // 0xC5 
+0,                         // 0xC6 
+0,                         // 0xC7 
+0,                         // 0xC8 
+0,                         // 0xC9 
+0,                         // 0xCA 
+0,                         // 0xCB 
+0,                         // 0xCC 
+0,                         // 0xCD 
+0,                         // 0xCE 
+0,                         // 0xCF 
+0,                         // 0xD0 
+0,                         // 0xD1 
+0,                         // 0xD2 
+0,                         // 0xD3 
+0,                         // 0xD4 
+0,                         // 0xD5 
+0,                         // 0xD6 
+0,                         // 0xD7 
+0,                         // 0xD8 
+0,                         // 0xD9 
+0,                         // 0xDA 
+0,                         // 0xDB 
+0,                         // 0xDC 
+0,                         // 0xDD 
+0,                         // 0xDE 
+0,                         // 0xDF 
+0,                         // 0xE0 
+0,                         // 0xE1 
+0,                         // 0xE2 
+0,                         // 0xE3 
+0,                         // 0xE4 
+
+0,                         // 0xE5 
+
+0,                         // 0xE6 
+0,                         // 0xE7 
+0,                         // 0xE8 
+0,                         // 0xE9 
+0,                         // 0xEA 
+0,                         // 0xEB 
+0,                         // 0xEC 
+0,                         // 0xED 
+0,                         // 0xEE 
+0,                         // 0xEF 
+   
+0,                         // 0xF0 
+0,                         // 0xF1 
+0,                         // 0xF2 
+0,                         // 0xF3 
+0,                         // 0xF4 
+0,                         // 0xF5 
+   
+0,                         // 0xF6 
+0,                         // 0xF7 
+0,                         // 0xF8 
+0,                         // 0xF9 
+0,                         // 0xFA 
+0,                         // 0xFB 
+0,                         // 0xFC 
+0,                         // 0xFD 
+0,                         // 0xFE 
+0                          // 0xFF 
+};   
+
+
+U8 TranslateOSKeyCode(U8 vcode)
+{
+   return VcodeRemap[vcode];   
+}   
+
+#pragma mark ---- Clipboard functions ----
+//-----------------------------------------------------------------------------
+const char* Platform::getClipboard()
+{
+	return NULL;//no clipboard on iOS
+}
+
+//-----------------------------------------------------------------------------
+bool Platform::setClipboard(const char *text)
+{
+	return NULL;//no clipboard on iOS
+}
+
+#pragma mark ---- Cursor Functions ----
+//------------------------------------------------------------------------------
+void Input::pushCursor(S32 cursorID)
+{
+   CursorManager* cm = getCursorManager();
+   if(cm)
+      cm->pushCursor(cursorID);
+}
+
+//------------------------------------------------------------------------------
+void Input::popCursor()
+{
+   CursorManager* cm = getCursorManager();
+   if(cm)
+      cm->popCursor();
+}
+
+//------------------------------------------------------------------------------
+void Input::refreshCursor()
+{
+   CursorManager* cm = getCursorManager();
+   if(cm)
+      cm->refreshCursor();
+}
+
+#pragma mark ---- DoubleClick Functions ----
+//------------------------------------------------------------------------------
+U32 Input::getDoubleClickTime()
+{
+	return IOS_DOUBLE_CLICK_TIME;
+}
+
+//------------------------------------------------------------------------------
+S32 Input::getDoubleClickWidth()
+{
+   // this is an arbitrary value.
+   return 10;
+}
+
+//------------------------------------------------------------------------------
+S32 Input::getDoubleClickHeight()
+{
+   return getDoubleClickWidth();
+}
+
+#pragma mark -
+
+//------------------------------------------------------------------------------
+bool enableKeyboard()
+{
+   if ( !gInputEnabled )
+      return( false );
+
+   if ( gKBEnabled && gKBActive )
+      return( true );
+
+   gKBEnabled = true;
+   if ( Input::isActive() )
+      gKBEnabled = activateKeyboard();
+
+   if ( gKBEnabled )
+   {
+      Con::printf( "Hardware-direct keyboard enabled." );
+   }
+   else
+   {
+      Con::warnf( "Hardware-direct keyboard failed to enable!" );
+   }
+
+   return( gKBEnabled );
+}
+
+//------------------------------------------------------------------------------
+void disableKeyboard()
+{
+   if ( !gInputEnabled || !gKBEnabled )
+      return;
+
+   deactivateKeyboard();
+   gKBEnabled = false;
+
+   Con::printf( "Hardware-direct keyboard disabled." );
+}
+
+//------------------------------------------------------------------------------
+bool activateKeyboard()
+{
+   if ( !gInputEnabled || !Input::isActive() || !gKBEnabled )
+      return( false );
+
+   OSStatus status = noErr;
+   if (status==noErr)
+      gKBActive = true;
+
+   return( gKBActive );
+}
+
+//------------------------------------------------------------------------------
+void deactivateKeyboard()
+{
+   if ( gInputEnabled && gKBActive )
+   {
+      gKBActive = false;
+   }
+}
+
+//------------------------------------------------------------------------------
+bool enableMouse()
+{
+   if ( !gInputEnabled )
+      return( false );
+
+   if ( gMouseEnabled && gMouseActive )
+      return( true );
+
+   gMouseEnabled = activateMouse();
+
+   return( gMouseEnabled );
+}
+
+//------------------------------------------------------------------------------
+void disableMouse()
+{
+   if ( !gInputEnabled || !gMouseEnabled )
+      return;
+
+   deactivateMouse();
+   gMouseEnabled = false;
+
+   bool hwMouse = false;
+   Con::printf( "%s disabled", hwMouse?"Hardware-direct mouse":"Basic mouse capture");
+}
+
+//------------------------------------------------------------------------------
+bool activateMouse()
+{
+   if ( !gInputEnabled || !Input::isActive() || !gMouseEnabled )
+      return( false );
+   
+   if (gMouseActive)
+      return(true);
+
+   gMouseActive = true;
+
+   return( gMouseActive );
+}
+
+//------------------------------------------------------------------------------
+void deactivateMouse()
+{
+   if ( !gInputEnabled || !gMouseActive )
+       return;
+   
+   gMouseActive = false;
+}
+
+
+//------------------------------------------------------------------------------
+ConsoleFunction( enableMouse, bool, 1, 1, "enableMouse()" )
+{
+   return( enableMouse() );
+}
+
+//------------------------------------------------------------------------------
+ConsoleFunction( disableMouse, void, 1, 1, "disableMouse()" )
+{
+   disableMouse();
+}
+
+//------------------------------------------------------------------------------
+void printInputState(void)
+{
+   if ( gInputEnabled )
+   {
+         Con::printf( "Low-level input system is enabled." );
+      
+      Con::printf( "- Keyboard is %sabled and %sactive.", 
+            gKBEnabled ? "en" : "dis",
+            gKBActive ? "" : "in" );
+      Con::printf( "- Mouse is %sabled and %sactive.", 
+            gMouseEnabled ? "en" : "dis",
+            gMouseActive ? "" : "in" );
+/*
+      Con::printf( "- Joystick is %sabled and %sactive.", 
+            gJoystickEnabled() ? "en" : "dis",
+            gJoystickActive() ? "" : "in" );
+*/
+   }
+   else
+   {
+      Con::printf( "Low-level input system is disabled." );
+   }
+}
+
+//------------------------------------------------------------------------------
+ConsoleFunction( echoInputState, void, 1, 1, "echoInputState()" )
+{
+   printInputState();
+}
+
+//------------------------------------------------------------------------------
+ConsoleFunction( toggleInputState, void, 1, 1, "toggleInputState()" )
+{
+   if (gInputEnabled)
+      Input::disable();
+   else
+      Input::enable();
+
+   printInputState();
+}
+
+//------------------------------------------------------------------------------
+ConsoleFunction( deactivateKeyboard, void, 1, 1, "deactivateKeyboard();")
+{
+   // these are only useful on the windows side. They deal with some vagaries of win32 DirectInput.
+}
+
+ConsoleFunction( activateKeyboard, void, 1, 1, "activateKeyboard();")
+{
+   // these are only useful on the windows side. They deal with some vagaries of win32 DirectInput.
+}
+
+
+//------------------------------------------------------------------------------
+void Input::setCursorPos(S32 x, S32 y)
+{
+	//this gets called from GuiCanvas to set the game mouse cursor
+}
+
+int processMultipleTouches()
+{	
+	char posX[256], posY[256], temp[10], touchNums[256];
+    
+	dMemset(posX, 0, sizeof(posX));
+    dMemset(posY, 0, sizeof(posY));
+    dMemset(touchNums, 0, sizeof(touchNums));
+
+	touchEvent *currentEvent;
+	
+	//Down Events
+	int numTouchDownEvents = TouchDownEvents.size();
+	while( TouchDownEvents.size() > 0 )
+    {
+		currentEvent = &TouchDownEvents.last();
+		dItoa( currentEvent->x, temp );
+		dStrcat( posX, temp );
+		dStrcat( posX, " " );
+		
+		dItoa( currentEvent->y, temp );
+		dStrcat( posY, temp );
+		dStrcat( posY, " " );
+		
+		dItoa( currentEvent->number, temp );
+		dStrcat( touchNums, temp );
+		dStrcat( touchNums, " " );
+		
+		TouchDownEvents.pop_back();
+	}
+	dItoa( numTouchDownEvents, temp );
+    
+    if( numTouchDownEvents > 0 )
+    {
+        InputEvent touchEvent;
+        
+        touchEvent.deviceInst = 0;
+        touchEvent.objInst = SI_TOUCHDOWN;
+        
+        touchEvent.deviceType = ScreenTouchDeviceType;
+        touchEvent.action = SI_TOUCH;
+        touchEvent.objType = SI_TOUCHDOWN;
+        
+        dStrcpy(touchEvent.fingersX, posX);
+        dStrcpy(touchEvent.fingersY, posY);
+        dStrcpy(touchEvent.fingerIDs, touchNums);
+        
+        touchEvent.modifier = 0;
+        
+        Game->postEvent(touchEvent);        
+    }
+    
+    // Deprecated in 1.5
+	//if( numTouchDownEvents > 0 )
+    //    Con::executef( 4, "oniOSTouchDown", touchNums , posX, posY );
+
+	//Move events
+	int numTouchMoveEvents = TouchMoveEvents.size();
+	while( TouchMoveEvents.size() > 0 ) 
+    {
+		currentEvent = &TouchMoveEvents.last();
+		dItoa( currentEvent->x, temp );
+		dStrcat( posX, temp );
+		dStrcat( posX, " " );
+		
+		dItoa( currentEvent->y, temp );
+		dStrcat( posY, temp );
+		dStrcat( posY, " " );
+		
+		dItoa( currentEvent->number, temp );
+		dStrcat( touchNums, temp );
+		dStrcat( touchNums, " " );		
+		
+		TouchMoveEvents.pop_back();
+	}
+	dItoa( numTouchMoveEvents, temp );
+    
+    if( numTouchMoveEvents > 0 )
+    {
+        InputEvent touchEvent;
+        
+        touchEvent.deviceInst = 0;
+        touchEvent.objInst = SI_TOUCHMOVE;
+        
+        touchEvent.deviceType = ScreenTouchDeviceType;
+        touchEvent.action = SI_TOUCH;
+        touchEvent.objType = SI_TOUCHMOVE;
+        
+        dStrcpy(touchEvent.fingersX, posX);
+        dStrcpy(touchEvent.fingersY, posY);
+        dStrcpy(touchEvent.fingerIDs, touchNums);
+        
+        touchEvent.modifier = 0;
+        
+        Game->postEvent(touchEvent);        
+    }
+    
+    // Deprecated in 1.5 -MP
+	//if( numTouchMoveEvents > 0 )
+    //    Con::executef( 4, "oniOSTouchMove", touchNums , posX, posY );
+	
+	//Up events
+	int numTouchUpEvents = TouchUpEvents.size();
+    
+    dMemset(posX, 0, sizeof(posX));
+    dMemset(posY, 0, sizeof(posY));
+    dMemset(touchNums, 0, sizeof(touchNums));
+    
+	while( TouchUpEvents.size() > 0 ) 
+    {
+		currentEvent = &TouchUpEvents.last();
+		dItoa( currentEvent->x, temp );
+		dStrcat( posX, temp );
+		dStrcat( posX, " " );
+		
+		dItoa( currentEvent->y, temp );
+		dStrcat( posY, temp );
+		dStrcat( posY, " " );
+		
+		dItoa( currentEvent->number, temp );
+		dStrcat( touchNums, temp );
+		dStrcat( touchNums, " " );		
+		
+		lastTouches[currentEvent->number].lastX = -1;
+		lastTouches[currentEvent->number].lastY	 = -1;
+		
+        int x;
+        x = -1;
+        
+        lastTouches[currentEvent->number].lastX = -1;
+		lastTouches[currentEvent->number].lastY	 = -1;
+        
+		TouchUpEvents.pop_back();
+	}
+    
+	dItoa( numTouchUpEvents, temp );
+	
+    if( numTouchUpEvents > 0 )
+    {
+        InputEvent touchEvent;
+        
+        touchEvent.deviceInst = 0;
+        touchEvent.objInst = SI_TOUCHUP;
+        
+        touchEvent.deviceType = ScreenTouchDeviceType;
+        touchEvent.action = SI_TOUCH;
+        touchEvent.objType = SI_TOUCHUP;
+        
+        dStrcpy(touchEvent.fingersX, posX);
+        dStrcpy(touchEvent.fingersY, posY);
+        dStrcpy(touchEvent.fingerIDs, touchNums);
+        
+        touchEvent.modifier = 0;
+        
+        Game->postEvent(touchEvent);        
+    }
+    
+    // Deprecated in 1.5 -MP
+    //if( numTouchUpEvents > 0 )
+    //    Con::executef( 4, "oniOSTouchUp", touchNums , posX, posY );
+	
+	//Luma: Tap support
+	posX[0] = '\0';
+	posY[0] = '\0';
+    
+	int numTapEvents = TouchTapEvents.size();
+	
+    while( TouchTapEvents.size() > 0 ) 
+    {
+		currentEvent = &TouchTapEvents.last();
+		sprintf( temp, "%d ", currentEvent->x );
+		dStrcat( posX, temp );
+		
+		sprintf( temp, "%d ", currentEvent->y );
+		dStrcat( posY, temp );
+		
+		TouchTapEvents.pop_back();
+	}
+    
+	sprintf( temp, "%d", numTapEvents );
+    
+	//if( numTapEvents > 0 )
+    //    Con::executef( 4, "oniOSTouchTap", temp , posX, posY );
+	
+	return numTouchDownEvents + numTouchMoveEvents + numTouchUpEvents;
+}
+
+
+
+//we want these to only be called once per frame!!
+bool createMouseMoveEvent( S32 touchNumber, S32 x, S32 y, S32 lastX, S32 lastY ) //EFM
+{	
+	S32 currentSlot = -1;
+	
+	for( int i = 0 ; (i < MAX_TOUCH_EVENTS) && (currentSlot == -1) ; i++ )
+	{
+		if( (lastX == lastTouches[i].lastX ) &&
+		   (lastY == lastTouches[i].lastY ) )
+		{
+			currentSlot = i;
+		}
+	}
+
+	if( currentSlot == -1 ) return false;
+	
+	ScreenTouchEvent event;
+	event.xPos = x;
+	event.yPos = y;
+	event.action = SI_MOVE;
+	event.touchID = currentSlot;
+    event.numTouches = 0;
+    
+	//Luma: Mouse not moving (no hover for mouse fingers!)
+	Canvas->setCursorPos( Point2I( x, y ) );  
+
+	if( currentSlot != -1 )
+	{
+		lastTouches[currentSlot].lastX = x;
+		lastTouches[currentSlot].lastY = y;
+	}		
+	
+	TouchMoveEvents.push_back( touchEvent( currentSlot, x, y ) );	
+	Game->postEvent(event);
+	
+	return true;//return false if we get bad values or something
+}
+
+
+bool createMouseDownEvent( S32 touchNumber, S32 x, S32 y, U32 numTouches ) 
+{
+	S32 vacantSlot = -1;
+	
+	for( int i = 0 ; (i < MAX_TOUCH_EVENTS) && (vacantSlot == -1) ; i++ )
+	{
+		if( lastTouches[i].lastX == -1 )
+		{
+			vacantSlot = i;
+		}
+	}
+	
+	if( vacantSlot == -1 ) 
+        return false;
+		
+	ScreenTouchEvent event;
+	event.xPos = x;
+	event.yPos = y;
+    event.touchID = vacantSlot;
+	event.action = SI_MAKE;
+	event.numTouches = numTouches;
+    
+	//Luma: Update position
+	Canvas->setCursorPos( Point2I( x, y ) );  		
+	
+	if( vacantSlot != -1 )
+	{
+		lastTouches[vacantSlot].lastX = x;
+		lastTouches[vacantSlot].lastY = y;
+	}	
+
+	TouchDownEvents.push_back( touchEvent( vacantSlot, x, y ) );
+	Game->postEvent(event);
+	
+	return true;//return false if we get bad values or something
+}
+
+bool createMouseUpEvent( S32 touchNumber, S32 x, S32 y, S32 lastX, S32 lastY, U32 numTouches ) //EFM
+{	
+	S32 currentSlot = -1;
+	
+	for( int i = 0 ; (i < MAX_TOUCH_EVENTS) && (currentSlot == -1) ; i++ )
+	{
+		if(( (x == lastTouches[i].lastX) && (y == lastTouches[i].lastY )) ||
+		   ( (lastX == lastTouches[i].lastX ) && (lastY == lastTouches[i].lastY )))
+		{
+			currentSlot = i;
+		}	
+	}
+	
+	if( currentSlot == -1 ) 
+        return false;
+
+	ScreenTouchEvent event;
+	event.xPos = x;
+	event.yPos = y;
+	event.action = SI_BREAK;
+	event.touchID = currentSlot;
+	event.numTouches = numTouches;
+    
+	TouchUpEvents.push_back( touchEvent( currentSlot, x, y ) );	
+	
+	Game->postEvent(event);
+	
+	return true;//return false if we get bad values or something
+}
+
+void clearPendingMultitouchEvents( void ) //EFM
+{	
+    for( int i = 0 ; i < MAX_TOUCH_EVENTS ; i++ )
+	{
+		lastTouches[i].lastX = -1;
+		lastTouches[i].lastY = -1;
+	}
+
+	TouchMoveEvents.clear();
+	TouchDownEvents.clear();
+	TouchUpEvents.clear();
+}
+
+
+//Luma: Tap support
+void createMouseTapEvent( S32 nbrTaps, S32 x, S32 y ) {
+
+	TouchTapEvents.push_back( touchEvent( nbrTaps, x, y ) );
+}
+
+
+// 0 = x, 1 = y, 2 = z
+UIAccelerationValue g_lastAccel[3];
+
+// PUAP -Mat this is polled at AccelerometerUpdateMS, which should be set at around 33 
+/*
+bool createAccelMoveEvent( UIAccelerationValue *accel ) {
+	bool returnVal = false;
+	
+	U32 axis[3] = { SI_XAXIS, SI_YAXIS, SI_ZAXIS }; 
+	
+	for( int i = 0; i < 3; i++ ) {
+		if(accel[i]  != g_lastAccel[i] ) {
+			InputEvent event;
+			event.deviceInst = 0;//joystick number
+			event.fValue = accel[i];
+			event.deviceType = JoystickDeviceType;
+			event.objType = axis[i];
+			event.objInst = i;//axis number
+			event.action = SI_MOVE;
+			event.modifier = 0;
+			Game->postEvent(event);
+			g_lastAccel[i]  = accel[i];
+			returnVal = true;
+		}
+	}
+	return false;
+}
+*/

+ 460 - 460
engine/source/platformiOS/iOSMotionManager.mm

@@ -1,460 +1,460 @@
-//-----------------------------------------------------------------------------
-// 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.
-//-----------------------------------------------------------------------------
-
-#import "iOSMotionManager.h"
-
-#include "platformiOS.h"
-#include "string/stringBuffer.h"
-#include "sim/simBase.h"
-#include "game/gameInterface.h"
-
-extern iOSPlatState platState;
-
-static const double kFilterConst = 0.1;
-
-static const double kUpdateInterval = 0.2;
-
-@implementation iOSMotionManager
-
-
-@synthesize referenceAttitude;
-@synthesize accelerometerEnabled;
-@synthesize gyroscopeEnabled;
-
-- (id)init
-{
-    if (!(self = [super init])) return nil;
-    
-    if(self != NULL)
-    {
-        accelerometerEnabled = NO;
-        gyroscopeEnabled = NO;
-        
-        motionManager = [[CMMotionManager alloc] init];
-        
-        if(motionManager.deviceMotionAvailable)
-        {
-            [motionManager setDeviceMotionUpdateInterval:kUpdateInterval];
-        }
-        else
-        {
-            [motionManager setAccelerometerUpdateInterval:kUpdateInterval];
-        }
-    }
-    else
-    {
-        Con::printf("Could not initialized iOSMotionManager!");
-        return 0;
-    }
-        
-    return self;
-}
-
-
-
-double accelAxes[6];
-
-
-- (void)enableAccelerometer
-{
-    accelerometerEnabled = YES;
-}
-
-- (void)disableAccelerometer
-{
-    accelerometerEnabled = NO;
-}
-
-- (bool)isAccelerometerActive
-{
-    return motionManager.accelerometerActive;
-}
-
-- (bool)enableGyroscope
-{
-    if(motionManager.gyroAvailable)
-        gyroscopeEnabled = YES;
-    else
-    {
-        Con::errorf("Gyroscope not supported on this device");
-        return false;
-    }
-    
-    return true;    
-}
-
-- (bool)disableGyroscope
-{
-    if(motionManager.gyroAvailable)
-        gyroscopeEnabled = NO;
-    else
-    {
-        Con::errorf("Gyroscope not supported on this device");
-        return false;
-    }
-    
-    return true;
-}
-
-- (bool)isGyroAvailable
-{
-    return motionManager.gyroAvailable;
-}
-
-- (bool)isGyroActive
-{
-    return motionManager.gyroActive;
-}
-
-static double filteredAccel[3] = {0, 0, 0};
-
-void (^accelerometerHandler)(CMAccelerometerData*, NSError*) = ^(CMAccelerometerData *accelData, NSError *)
-{
-    if(gMotionManager.accelerometerEnabled)
-    {
-        U32 accelAxes[6] = { SI_ACCELX, SI_ACCELY, SI_ACCELZ, SI_GRAVX, SI_GRAVY, SI_GRAVZ };
-        
-        double userAcc[6];
-        
-        if(platState.portrait)
-        {
-        
-            filteredAccel[0] = (accelData.acceleration.x * kFilterConst) + (filteredAccel[0] * (1.0 - kFilterConst));
-            filteredAccel[1] = (accelData.acceleration.y * kFilterConst) + (filteredAccel[1] * (1.0 - kFilterConst));
-            filteredAccel[2] = (accelData.acceleration.z * kFilterConst) + (filteredAccel[2] * (1.0 - kFilterConst));
-            
-            userAcc[0] = accelData.acceleration.x - filteredAccel[0];
-            userAcc[1] = accelData.acceleration.y - filteredAccel[1];
-            userAcc[2] = accelData.acceleration.z - filteredAccel[2];
-            
-            // Assign the non-filtered data to gravity
-            userAcc[3] = accelData.acceleration.x;
-            userAcc[4] = accelData.acceleration.y;
-            userAcc[5] = accelData.acceleration.z;
-        }
-        else 
-        {
-            filteredAccel[0] = (accelData.acceleration.y * kFilterConst) + (filteredAccel[0] * (1.0 - kFilterConst));
-            filteredAccel[1] = (accelData.acceleration.x * kFilterConst) + (filteredAccel[1] * (1.0 - kFilterConst));
-            filteredAccel[2] = (accelData.acceleration.z * kFilterConst) + (filteredAccel[2] * (1.0 - kFilterConst));
-
-            userAcc[0] = accelData.acceleration.y - filteredAccel[0];
-            userAcc[1] = accelData.acceleration.x - filteredAccel[1];
-            userAcc[2] = accelData.acceleration.z - filteredAccel[2];
-            
-            // Assign the non-filtered data to gravity
-            userAcc[3] = accelData.acceleration.y;
-            userAcc[4] = accelData.acceleration.x;
-            userAcc[5] = accelData.acceleration.z;
-        }
-        
-        for( int i = 0; i < 6; i++)
-        {
-            InputEvent event;
-            
-            event.deviceInst = 0;
-            event.fValue = userAcc[i];
-            event.deviceType = AccelerometerDeviceType;
-            event.objType = accelAxes[i];
-            event.objInst = i;
-            event.action = SI_MOTION;
-            event.modifier = 0;
-            
-            Game->postEvent(event);
-        }
-    }
-};
-
-void (^motionHandler)(CMDeviceMotion*, NSError*) = ^(CMDeviceMotion *motionData, NSError *error)
-{
-    if(gMotionManager.referenceAttitude == NULL)
-        [gMotionManager resetDeviceMotionReference];
-    
-    CMAttitude* currentAttitude = motionData.attitude;
-  
-    [currentAttitude multiplyByInverseOfAttitude:gMotionManager.referenceAttitude];
-  
-    if(gMotionManager.accelerometerEnabled)
-    {
-        U32 accelAxes[6] = { SI_ACCELX, SI_ACCELY, SI_ACCELZ, SI_GRAVX, SI_GRAVY, SI_GRAVZ };
-        
-        double userAcc[6];
-        if(platState.portrait)
-        {
-            userAcc[0] = motionData.userAcceleration.x; 
-            userAcc[1] = motionData.userAcceleration.y;
-            userAcc[2] = motionData.userAcceleration.z;
-            userAcc[3] = motionData.gravity.x; 
-            userAcc[4] = motionData.gravity.y;
-            userAcc[5] = motionData.gravity.z;
-        }
-        else 
-        {
-            userAcc[0] = motionData.userAcceleration.y; 
-            userAcc[1] = motionData.userAcceleration.x;
-            userAcc[2] = motionData.userAcceleration.z;
-            userAcc[3] = motionData.gravity.y; 
-            userAcc[4] = motionData.gravity.x;
-            userAcc[5] = motionData.gravity.z;            
-        }
-
-        for( int i = 0; i < 6; i++)
-        {
-            InputEvent event;
-        
-            event.deviceInst = 0;
-            event.fValue = userAcc[i];
-            event.deviceType = AccelerometerDeviceType;
-            event.objType = accelAxes[i];
-            event.objInst = i;
-            event.action = SI_MOTION;
-            event.modifier = 0;
-        
-            Game->postEvent(event);
-        }
-    }
-    
-    if(gMotionManager.gyroscopeEnabled)
-    {
-        double gyroData[6] = { currentAttitude.pitch, 
-                               currentAttitude.yaw, 
-                               currentAttitude.roll, 
-                               motionData.rotationRate.x, 
-                               motionData.rotationRate.y, 
-                               motionData.rotationRate.z };
-        
-        U32 gyroAxes[6] = { SI_PITCH, SI_YAW, SI_ROLL, SI_GYROX, SI_GYROY, SI_GYROZ };
-        
-        for( int i = 0; i < 6; i++)
-        {
-            InputEvent event;
-            
-            event.deviceInst = 0;
-            event.fValue = gyroData[i];
-            event.deviceType = GyroscopeDeviceType;
-            event.objType = gyroAxes[i];
-            event.objInst = i;
-            event.action = SI_MOTION;
-            event.modifier = 0;
-            
-            Game->postEvent(event);
-        }
-    }
-    
-};
-
-- (bool)startDeviceMotion
-{
-    if(motionManager.deviceMotionAvailable)
-    {
-        if(referenceAttitude == NULL)
-            referenceAttitude = motionManager.deviceMotion.attitude;
-        
-        [motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue currentQueue] withHandler:motionHandler];
-    }
-    else
-    {
-        [motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue currentQueue] withHandler:accelerometerHandler];
-    }
-    
-    return true;
-}
-
-- (bool)stopDeviceMotion
-{
-    if(motionManager.deviceMotionAvailable)
-        [motionManager stopDeviceMotionUpdates];
-    else
-    {
-        [motionManager stopAccelerometerUpdates];
-    }
-    return true;
-}
-
-- (bool)resetDeviceMotionReference
-{
-    if(motionManager.deviceMotionAvailable)
-    {
-        referenceAttitude = motionManager.deviceMotion.attitude;
-        return true;
-    }
-    
-    Con::errorf("Device Motion not supported on this device (check OS)");
-    return false;
-}
-
-- (bool)isDeviceMotionAvailable
-{
-    return motionManager.deviceMotionAvailable;
-}
-
-- (bool)isDeviceMotionActive
-{
-    return motionManager.deviceMotionActive;
-}
-
-@end
-
-ConsoleFunction(initMotionManager, void, 1, 1, "() Initialize the iOSMotionManager")
-{
-    if(gMotionManager != NULL)
-    {
-        Con::printf("Motion Manager already initialized");
-    }
-    else
-        gMotionManager = [[iOSMotionManager alloc] init];
-}
-
-ConsoleFunction(enableAccelerometer, void, 1, 1, "() Allow accelerometer tracking during device motion updates")
-{
-    if(gMotionManager == NULL)
-        gMotionManager = [[iOSMotionManager alloc] init];
-    
-    gMotionManager.accelerometerEnabled = YES;
-    [gMotionManager startDeviceMotion];
-}
-
-ConsoleFunction(disableAccelerometer, void, 1, 1, "() Stop accelerometer tracking")
-{
-    if(gMotionManager != NULL)
-        gMotionManager.accelerometerEnabled = NO;
-    else
-    {
-        Con::warnf("Motion Manager was not initialized. Initializing now");
-        gMotionManager = [[iOSMotionManager alloc] init];
-    }
-}
-
-ConsoleFunction(isAccelerometerActive, bool, 1, 1, "() Check to see if Accelerometer is being polled\n"
-                "@return True if accelerometer is on, false otherwise")
-{
-    if(gMotionManager != NULL)
-        return [gMotionManager isAccelerometerActive];
-    else
-    {
-        Con::warnf("Motion Manager was not initialized. Initializing now");
-        gMotionManager = [[iOSMotionManager alloc] init];
-        return [gMotionManager isAccelerometerActive];
-    }
-}
-
-ConsoleFunction(isGyroAvailable, bool, 1, 1, "() Check to see if this iOS device has a gyroscope\n"
-                                             "@return True if gyro is on the device, false otherwise")
-{
-    if(gMotionManager != NULL)
-        return [gMotionManager isGyroAvailable];
-    else
-    {
-        Con::warnf("Motion Manager was not initialized. Initializing now");
-        gMotionManager = [[iOSMotionManager alloc] init];
-        return [gMotionManager isGyroAvailable];
-    }
-}
-
-ConsoleFunction(isGyroActive, bool, 1, 1, "() Check to see if this iOS device has a gyroscope\n"
-                "@return True if gyro is on the device, false otherwise")
-{
-    if(gMotionManager != NULL)
-        return [gMotionManager isGyroActive];
-    else
-    {
-        Con::warnf("Motion Manager was not initialized. Initializing now");
-        gMotionManager = [[iOSMotionManager alloc] init];
-        return [gMotionManager isGyroActive];
-    }
-}
-ConsoleFunction(enableGyroscope, void, 1, 1, "() Start the gyroscope tracking\n"
-                "@return True if gyroscope is supported, false otherwise")
-{
-    if(gMotionManager != NULL)
-        gMotionManager.gyroscopeEnabled = YES;
-    else
-    {
-        Con::warnf("Motion Manager was not initialized. Initializing now");
-        
-        gMotionManager = [[iOSMotionManager alloc] init];
-        gMotionManager.gyroscopeEnabled = YES;
-        [gMotionManager startDeviceMotion];
-    }
-}
-
-ConsoleFunction(stopGyroscope, void, 1, 1, "() Stop gyroscope tracking\n"
-                "@return True if gyroscope is supported, false otherwise")
-{
-    if(gMotionManager != NULL)
-        gMotionManager.gyroscopeEnabled = NO;
-    else
-    {
-        Con::warnf("Motion Manager was not initialized. Initializing now");
-        
-        gMotionManager = [[iOSMotionManager alloc] init];
-    }
-}
-
-ConsoleFunction(isDeviceMotionAvailable, bool, 1, 1, "() Check to see if this iOS device supports advanced device motion (requires gyroscope\n"
-                "@return True if Device Motion is supported, false otherwise")
-{
-    if(gMotionManager != NULL)
-        return [gMotionManager isDeviceMotionAvailable];
-    else
-    {
-        Con::warnf("Motion Manager was not initialized. Initializing now");
-        gMotionManager = [[iOSMotionManager alloc] init];
-        return [gMotionManager isDeviceMotionAvailable];
-    }
-}
-
-ConsoleFunction(isDeviceMotionActive, bool, 1, 1, "() Check to see if the device motion is running\n"
-                                                  "@return True if device motion is being tracked, false otherwise")
-{
-    if(gMotionManager != NULL)
-        return [gMotionManager isDeviceMotionActive];
-    
-    Con::warnf("Motion Manager was not initialized. Initializing now");
-    gMotionManager = [[iOSMotionManager alloc] init];
-    return [gMotionManager isDeviceMotionActive];
-}
-ConsoleFunction(startDeviceMotion, bool, 1, 1, "() Start Device motion tracking\n"
-                                               "@return True if device motion is supported, false otherwise")
-{
-    if(gMotionManager != NULL)
-        return [gMotionManager startDeviceMotion];
-    else
-    {
-        Con::warnf("Motion Manager was not initialized. Initializing now");
-        gMotionManager = [[iOSMotionManager alloc] init];
-        return [gMotionManager startDeviceMotion];
-    }
-}
-
-ConsoleFunction(stopDeviceMotion, bool, 1, 1, "() Stop Device Motion tracking\n"
-                                              "@return True if device motion is supported, false otherwise")
-{
-    if(gMotionManager != NULL)
-        return [gMotionManager stopDeviceMotion];
-    else
-    {
-        Con::warnf("Motion Manager was not initialized. Initializing now");
-        gMotionManager = [[iOSMotionManager alloc] init];
-        return [gMotionManager stopDeviceMotion];
-    }
-}
+//-----------------------------------------------------------------------------
+// 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.
+//-----------------------------------------------------------------------------
+
+#import "iOSMotionManager.h"
+
+#include "platformiOS.h"
+#include "string/stringBuffer.h"
+#include "sim/simBase.h"
+#include "game/gameInterface.h"
+
+extern iOSPlatState platState;
+
+static const double kFilterConst = 0.1;
+
+static const double kUpdateInterval = 0.2;
+
+@implementation iOSMotionManager
+
+
+@synthesize referenceAttitude;
+@synthesize accelerometerEnabled;
+@synthesize gyroscopeEnabled;
+
+- (id)init
+{
+    if (!(self = [super init])) return nil;
+    
+    if(self != NULL)
+    {
+        accelerometerEnabled = NO;
+        gyroscopeEnabled = NO;
+        
+        motionManager = [[CMMotionManager alloc] init];
+        
+        if(motionManager.deviceMotionAvailable)
+        {
+            [motionManager setDeviceMotionUpdateInterval:kUpdateInterval];
+        }
+        else
+        {
+            [motionManager setAccelerometerUpdateInterval:kUpdateInterval];
+        }
+    }
+    else
+    {
+        Con::printf("Could not initialized iOSMotionManager!");
+        return 0;
+    }
+        
+    return self;
+}
+
+
+
+double accelAxes[6];
+
+
+- (void)enableAccelerometer
+{
+    accelerometerEnabled = YES;
+}
+
+- (void)disableAccelerometer
+{
+    accelerometerEnabled = NO;
+}
+
+- (bool)isAccelerometerActive
+{
+    return motionManager.accelerometerActive;
+}
+
+- (bool)enableGyroscope
+{
+    if(motionManager.gyroAvailable)
+        gyroscopeEnabled = YES;
+    else
+    {
+        Con::errorf("Gyroscope not supported on this device");
+        return false;
+    }
+    
+    return true;    
+}
+
+- (bool)disableGyroscope
+{
+    if(motionManager.gyroAvailable)
+        gyroscopeEnabled = NO;
+    else
+    {
+        Con::errorf("Gyroscope not supported on this device");
+        return false;
+    }
+    
+    return true;
+}
+
+- (bool)isGyroAvailable
+{
+    return motionManager.gyroAvailable;
+}
+
+- (bool)isGyroActive
+{
+    return motionManager.gyroActive;
+}
+
+static double filteredAccel[3] = {0, 0, 0};
+
+void (^accelerometerHandler)(CMAccelerometerData*, NSError*) = ^(CMAccelerometerData *accelData, NSError *)
+{
+    if(gMotionManager.accelerometerEnabled)
+    {
+        U32 accelAxes[6] = { SI_ACCELX, SI_ACCELY, SI_ACCELZ, SI_GRAVX, SI_GRAVY, SI_GRAVZ };
+        
+        double userAcc[6];
+        
+        if(platState.portrait)
+        {
+        
+            filteredAccel[0] = (accelData.acceleration.x * kFilterConst) + (filteredAccel[0] * (1.0 - kFilterConst));
+            filteredAccel[1] = (accelData.acceleration.y * kFilterConst) + (filteredAccel[1] * (1.0 - kFilterConst));
+            filteredAccel[2] = (accelData.acceleration.z * kFilterConst) + (filteredAccel[2] * (1.0 - kFilterConst));
+            
+            userAcc[0] = accelData.acceleration.x - filteredAccel[0];
+            userAcc[1] = accelData.acceleration.y - filteredAccel[1];
+            userAcc[2] = accelData.acceleration.z - filteredAccel[2];
+            
+            // Assign the non-filtered data to gravity
+            userAcc[3] = accelData.acceleration.x;
+            userAcc[4] = accelData.acceleration.y;
+            userAcc[5] = accelData.acceleration.z;
+        }
+        else 
+        {
+            filteredAccel[0] = (accelData.acceleration.y * kFilterConst) + (filteredAccel[0] * (1.0 - kFilterConst));
+            filteredAccel[1] = (accelData.acceleration.x * kFilterConst) + (filteredAccel[1] * (1.0 - kFilterConst));
+            filteredAccel[2] = (accelData.acceleration.z * kFilterConst) + (filteredAccel[2] * (1.0 - kFilterConst));
+
+            userAcc[0] = accelData.acceleration.y - filteredAccel[0];
+            userAcc[1] = accelData.acceleration.x - filteredAccel[1];
+            userAcc[2] = accelData.acceleration.z - filteredAccel[2];
+            
+            // Assign the non-filtered data to gravity
+            userAcc[3] = accelData.acceleration.y;
+            userAcc[4] = accelData.acceleration.x;
+            userAcc[5] = accelData.acceleration.z;
+        }
+        
+        for( int i = 0; i < 6; i++)
+        {
+            InputEvent event;
+            
+            event.deviceInst = 0;
+            event.fValues[0] = userAcc[i];
+            event.deviceType = AccelerometerDeviceType;
+            event.objType = accelAxes[i];
+            event.objInst = i;
+            event.action = SI_MOTION;
+            event.modifier = 0;
+            
+            Game->postEvent(event);
+        }
+    }
+};
+
+void (^motionHandler)(CMDeviceMotion*, NSError*) = ^(CMDeviceMotion *motionData, NSError *error)
+{
+    if(gMotionManager.referenceAttitude == NULL)
+        [gMotionManager resetDeviceMotionReference];
+    
+    CMAttitude* currentAttitude = motionData.attitude;
+  
+    [currentAttitude multiplyByInverseOfAttitude:gMotionManager.referenceAttitude];
+  
+    if(gMotionManager.accelerometerEnabled)
+    {
+        U32 accelAxes[6] = { SI_ACCELX, SI_ACCELY, SI_ACCELZ, SI_GRAVX, SI_GRAVY, SI_GRAVZ };
+        
+        double userAcc[6];
+        if(platState.portrait)
+        {
+            userAcc[0] = motionData.userAcceleration.x; 
+            userAcc[1] = motionData.userAcceleration.y;
+            userAcc[2] = motionData.userAcceleration.z;
+            userAcc[3] = motionData.gravity.x; 
+            userAcc[4] = motionData.gravity.y;
+            userAcc[5] = motionData.gravity.z;
+        }
+        else 
+        {
+            userAcc[0] = motionData.userAcceleration.y; 
+            userAcc[1] = motionData.userAcceleration.x;
+            userAcc[2] = motionData.userAcceleration.z;
+            userAcc[3] = motionData.gravity.y; 
+            userAcc[4] = motionData.gravity.x;
+            userAcc[5] = motionData.gravity.z;            
+        }
+
+        for( int i = 0; i < 6; i++)
+        {
+            InputEvent event;
+        
+            event.deviceInst = 0;
+            event.fValues[0] = userAcc[i];
+            event.deviceType = AccelerometerDeviceType;
+            event.objType = accelAxes[i];
+            event.objInst = i;
+            event.action = SI_MOTION;
+            event.modifier = 0;
+        
+            Game->postEvent(event);
+        }
+    }
+    
+    if(gMotionManager.gyroscopeEnabled)
+    {
+        double gyroData[6] = { currentAttitude.pitch, 
+                               currentAttitude.yaw, 
+                               currentAttitude.roll, 
+                               motionData.rotationRate.x, 
+                               motionData.rotationRate.y, 
+                               motionData.rotationRate.z };
+        
+        U32 gyroAxes[6] = { SI_PITCH, SI_YAW, SI_ROLL, SI_GYROX, SI_GYROY, SI_GYROZ };
+        
+        for( int i = 0; i < 6; i++)
+        {
+            InputEvent event;
+            
+            event.deviceInst = 0;
+            event.fValues[0] = gyroData[i];
+            event.deviceType = GyroscopeDeviceType;
+            event.objType = gyroAxes[i];
+            event.objInst = i;
+            event.action = SI_MOTION;
+            event.modifier = 0;
+            
+            Game->postEvent(event);
+        }
+    }
+    
+};
+
+- (bool)startDeviceMotion
+{
+    if(motionManager.deviceMotionAvailable)
+    {
+        if(referenceAttitude == NULL)
+            referenceAttitude = motionManager.deviceMotion.attitude;
+        
+        [motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue currentQueue] withHandler:motionHandler];
+    }
+    else
+    {
+        [motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue currentQueue] withHandler:accelerometerHandler];
+    }
+    
+    return true;
+}
+
+- (bool)stopDeviceMotion
+{
+    if(motionManager.deviceMotionAvailable)
+        [motionManager stopDeviceMotionUpdates];
+    else
+    {
+        [motionManager stopAccelerometerUpdates];
+    }
+    return true;
+}
+
+- (bool)resetDeviceMotionReference
+{
+    if(motionManager.deviceMotionAvailable)
+    {
+        referenceAttitude = motionManager.deviceMotion.attitude;
+        return true;
+    }
+    
+    Con::errorf("Device Motion not supported on this device (check OS)");
+    return false;
+}
+
+- (bool)isDeviceMotionAvailable
+{
+    return motionManager.deviceMotionAvailable;
+}
+
+- (bool)isDeviceMotionActive
+{
+    return motionManager.deviceMotionActive;
+}
+
+@end
+
+ConsoleFunction(initMotionManager, void, 1, 1, "() Initialize the iOSMotionManager")
+{
+    if(gMotionManager != NULL)
+    {
+        Con::printf("Motion Manager already initialized");
+    }
+    else
+        gMotionManager = [[iOSMotionManager alloc] init];
+}
+
+ConsoleFunction(enableAccelerometer, void, 1, 1, "() Allow accelerometer tracking during device motion updates")
+{
+    if(gMotionManager == NULL)
+        gMotionManager = [[iOSMotionManager alloc] init];
+    
+    gMotionManager.accelerometerEnabled = YES;
+    [gMotionManager startDeviceMotion];
+}
+
+ConsoleFunction(disableAccelerometer, void, 1, 1, "() Stop accelerometer tracking")
+{
+    if(gMotionManager != NULL)
+        gMotionManager.accelerometerEnabled = NO;
+    else
+    {
+        Con::warnf("Motion Manager was not initialized. Initializing now");
+        gMotionManager = [[iOSMotionManager alloc] init];
+    }
+}
+
+ConsoleFunction(isAccelerometerActive, bool, 1, 1, "() Check to see if Accelerometer is being polled\n"
+                "@return True if accelerometer is on, false otherwise")
+{
+    if(gMotionManager != NULL)
+        return [gMotionManager isAccelerometerActive];
+    else
+    {
+        Con::warnf("Motion Manager was not initialized. Initializing now");
+        gMotionManager = [[iOSMotionManager alloc] init];
+        return [gMotionManager isAccelerometerActive];
+    }
+}
+
+ConsoleFunction(isGyroAvailable, bool, 1, 1, "() Check to see if this iOS device has a gyroscope\n"
+                                             "@return True if gyro is on the device, false otherwise")
+{
+    if(gMotionManager != NULL)
+        return [gMotionManager isGyroAvailable];
+    else
+    {
+        Con::warnf("Motion Manager was not initialized. Initializing now");
+        gMotionManager = [[iOSMotionManager alloc] init];
+        return [gMotionManager isGyroAvailable];
+    }
+}
+
+ConsoleFunction(isGyroActive, bool, 1, 1, "() Check to see if this iOS device has a gyroscope\n"
+                "@return True if gyro is on the device, false otherwise")
+{
+    if(gMotionManager != NULL)
+        return [gMotionManager isGyroActive];
+    else
+    {
+        Con::warnf("Motion Manager was not initialized. Initializing now");
+        gMotionManager = [[iOSMotionManager alloc] init];
+        return [gMotionManager isGyroActive];
+    }
+}
+ConsoleFunction(enableGyroscope, void, 1, 1, "() Start the gyroscope tracking\n"
+                "@return True if gyroscope is supported, false otherwise")
+{
+    if(gMotionManager != NULL)
+        gMotionManager.gyroscopeEnabled = YES;
+    else
+    {
+        Con::warnf("Motion Manager was not initialized. Initializing now");
+        
+        gMotionManager = [[iOSMotionManager alloc] init];
+        gMotionManager.gyroscopeEnabled = YES;
+        [gMotionManager startDeviceMotion];
+    }
+}
+
+ConsoleFunction(stopGyroscope, void, 1, 1, "() Stop gyroscope tracking\n"
+                "@return True if gyroscope is supported, false otherwise")
+{
+    if(gMotionManager != NULL)
+        gMotionManager.gyroscopeEnabled = NO;
+    else
+    {
+        Con::warnf("Motion Manager was not initialized. Initializing now");
+        
+        gMotionManager = [[iOSMotionManager alloc] init];
+    }
+}
+
+ConsoleFunction(isDeviceMotionAvailable, bool, 1, 1, "() Check to see if this iOS device supports advanced device motion (requires gyroscope\n"
+                "@return True if Device Motion is supported, false otherwise")
+{
+    if(gMotionManager != NULL)
+        return [gMotionManager isDeviceMotionAvailable];
+    else
+    {
+        Con::warnf("Motion Manager was not initialized. Initializing now");
+        gMotionManager = [[iOSMotionManager alloc] init];
+        return [gMotionManager isDeviceMotionAvailable];
+    }
+}
+
+ConsoleFunction(isDeviceMotionActive, bool, 1, 1, "() Check to see if the device motion is running\n"
+                                                  "@return True if device motion is being tracked, false otherwise")
+{
+    if(gMotionManager != NULL)
+        return [gMotionManager isDeviceMotionActive];
+    
+    Con::warnf("Motion Manager was not initialized. Initializing now");
+    gMotionManager = [[iOSMotionManager alloc] init];
+    return [gMotionManager isDeviceMotionActive];
+}
+ConsoleFunction(startDeviceMotion, bool, 1, 1, "() Start Device motion tracking\n"
+                                               "@return True if device motion is supported, false otherwise")
+{
+    if(gMotionManager != NULL)
+        return [gMotionManager startDeviceMotion];
+    else
+    {
+        Con::warnf("Motion Manager was not initialized. Initializing now");
+        gMotionManager = [[iOSMotionManager alloc] init];
+        return [gMotionManager startDeviceMotion];
+    }
+}
+
+ConsoleFunction(stopDeviceMotion, bool, 1, 1, "() Stop Device Motion tracking\n"
+                                              "@return True if device motion is supported, false otherwise")
+{
+    if(gMotionManager != NULL)
+        return [gMotionManager stopDeviceMotion];
+    else
+    {
+        Con::warnf("Motion Manager was not initialized. Initializing now");
+        gMotionManager = [[iOSMotionManager alloc] init];
+        return [gMotionManager stopDeviceMotion];
+    }
+}

+ 1 - 1
engine/source/sim/simObject.cc

@@ -1128,7 +1128,7 @@ void SimObject::copyTo(SimObject* object)
 {
 {
    object->mClassName = mClassName;
    object->mClassName = mClassName;
    object->mSuperClassName = mSuperClassName;
    object->mSuperClassName = mSuperClassName;
-
+   object->mNameSpace = NULL;
    object->linkNamespaces();
    object->linkNamespaces();
 }
 }
 
 

+ 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_ */

Some files were not shown because too many files changed in this diff