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>
     <ClCompile>
       <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>
       <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
@@ -96,10 +96,10 @@
       <AdditionalIncludeDirectories>../../Lib/MSPlatformSDK/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ResourceCompile>
     <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>
       <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>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <ProgramDatabaseFile>$(IntDir)$(ProjectName).pdb</ProgramDatabaseFile>
@@ -119,7 +119,7 @@
     <ClCompile>
       <Optimization>MinSpace</Optimization>
       <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>
       <StringPooling>true</StringPooling>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
@@ -144,10 +144,10 @@
       <AdditionalIncludeDirectories>../../Lib/MSPlatformSDK/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ResourceCompile>
     <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>
       <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>
       <GenerateDebugInformation>false</GenerateDebugInformation>
       <ProgramDatabaseFile>$(IntDir)$(ProjectName).pdb</ProgramDatabaseFile>
@@ -178,7 +178,7 @@
     <ClCompile>
       <Optimization>MinSpace</Optimization>
       <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>
       <StringPooling>true</StringPooling>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
@@ -203,10 +203,10 @@
       <AdditionalIncludeDirectories>../../Lib/MSPlatformSDK/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ResourceCompile>
     <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>
       <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>
       <GenerateDebugInformation>false</GenerateDebugInformation>
       <ProgramDatabaseFile>$(IntDir)$(ProjectName).pdb</ProgramDatabaseFile>
@@ -404,6 +404,8 @@
     <ClCompile Include="..\..\source\gui\language\lang.cc" />
     <ClCompile Include="..\..\source\gui\messageVector.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\bufferStream.cc" />
     <ClCompile Include="..\..\source\io\fileObject.cc" />
@@ -429,6 +431,7 @@
     <ClCompile Include="..\..\source\io\zip\zipSubStream.cc" />
     <ClCompile Include="..\..\source\io\zip\zipTempStream.cc" />
     <ClCompile Include="..\..\source\math\math_ScriptBinding.cc" />
+    <ClCompile Include="..\..\source\math\mPoint.cpp" />
     <ClCompile Include="..\..\source\math\rectClipper.cpp" />
     <ClCompile Include="..\..\source\memory\dataChunker.cc" />
     <ClCompile Include="..\..\source\memory\frameAllocator_ScriptBinding.cc" />
@@ -878,6 +881,10 @@
     <ClInclude Include="..\..\source\gui\messageVector.h" />
     <ClInclude Include="..\..\source\input\actionMap.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\bufferStream.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">
       <UniqueIdentifier>{e8f11d4b-6a54-4467-ae13-794ed56063f9}</UniqueIdentifier>
     </Filter>
+    <Filter Include="input\leapMotion">
+      <UniqueIdentifier>{aa5d5692-e20a-4608-b3ee-390b3089fcb2}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\source\audio\audio.cc">
@@ -1338,6 +1341,15 @@
     <ClCompile Include="..\..\source\console\Package.cc">
       <Filter>console</Filter>
     </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>
     <ClInclude Include="..\..\source\audio\audio.h">
@@ -2979,6 +2991,18 @@
     <ClInclude Include="..\..\source\console\Package.h">
       <Filter>console</Filter>
     </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>
     <CustomBuild Include="..\..\source\math\mMath_ASM.asm">

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

@@ -75,7 +75,7 @@
     </Midl>
     <ClCompile>
       <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>
       <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
@@ -100,10 +100,10 @@
       <AdditionalIncludeDirectories>../../Lib/MSPlatformSDK/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ResourceCompile>
     <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>
       <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>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <ProgramDatabaseFile>$(IntDir)$(ProjectName).pdb</ProgramDatabaseFile>
@@ -124,7 +124,7 @@
     <ClCompile>
       <Optimization>MinSpace</Optimization>
       <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>
       <StringPooling>true</StringPooling>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
@@ -149,10 +149,10 @@
       <AdditionalIncludeDirectories>../../Lib/MSPlatformSDK/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ResourceCompile>
     <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>
       <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>
       <GenerateDebugInformation>false</GenerateDebugInformation>
       <ProgramDatabaseFile>$(IntDir)$(ProjectName).pdb</ProgramDatabaseFile>
@@ -184,7 +184,7 @@
     <ClCompile>
       <Optimization>MinSpace</Optimization>
       <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>
       <StringPooling>true</StringPooling>
       <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
@@ -209,10 +209,10 @@
       <AdditionalIncludeDirectories>../../Lib/MSPlatformSDK/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ResourceCompile>
     <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>
       <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>
       <GenerateDebugInformation>false</GenerateDebugInformation>
       <ProgramDatabaseFile>$(IntDir)$(ProjectName).pdb</ProgramDatabaseFile>
@@ -411,6 +411,8 @@
     <ClCompile Include="..\..\source\gui\language\lang.cc" />
     <ClCompile Include="..\..\source\gui\messageVector.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\bufferStream.cc" />
     <ClCompile Include="..\..\source\io\fileObject.cc" />
@@ -436,6 +438,7 @@
     <ClCompile Include="..\..\source\io\zip\zipSubStream.cc" />
     <ClCompile Include="..\..\source\io\zip\zipTempStream.cc" />
     <ClCompile Include="..\..\source\math\math_ScriptBinding.cc" />
+    <ClCompile Include="..\..\source\math\mPoint.cpp" />
     <ClCompile Include="..\..\source\math\rectClipper.cpp" />
     <ClCompile Include="..\..\source\memory\dataChunker.cc" />
     <ClCompile Include="..\..\source\memory\frameAllocator_ScriptBinding.cc" />
@@ -885,6 +888,10 @@
     <ClInclude Include="..\..\source\gui\messageVector.h" />
     <ClInclude Include="..\..\source\input\actionMap.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\bufferStream.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">
       <UniqueIdentifier>{cc1c1416-376b-4686-a4ac-21d1a35c9390}</UniqueIdentifier>
     </Filter>
+    <Filter Include="input\leapMotion">
+      <UniqueIdentifier>{addcb993-5059-42bf-a1ea-871f700c67c3}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\source\audio\audio.cc">
@@ -1338,6 +1341,15 @@
     <ClCompile Include="..\..\source\console\Package.cc">
       <Filter>console</Filter>
     </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>
     <ClInclude Include="..\..\source\audio\audio.h">
@@ -2979,6 +2991,18 @@
     <ClInclude Include="..\..\source\console\Package.h">
       <Filter>console</Filter>
     </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>
     <CustomBuild Include="..\..\source\math\mMath_ASM.asm">

+ 253 - 27
engine/compilers/Xcode/Torque2D.xcodeproj/project.pbxproj

@@ -7,6 +7,30 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		27908DFA18A3F8CB002D41BD /* Animation.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DCD18A3F8CB002D41BD /* Animation.c */; };
+		27908DFB18A3F8CB002D41BD /* AnimationState.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DCF18A3F8CB002D41BD /* AnimationState.c */; };
+		27908DFC18A3F8CB002D41BD /* AnimationStateData.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DD118A3F8CB002D41BD /* AnimationStateData.c */; };
+		27908DFD18A3F8CB002D41BD /* Atlas.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DD318A3F8CB002D41BD /* Atlas.c */; };
+		27908DFE18A3F8CB002D41BD /* AtlasAttachmentLoader.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DD518A3F8CB002D41BD /* AtlasAttachmentLoader.c */; };
+		27908DFF18A3F8CB002D41BD /* Attachment.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DD718A3F8CB002D41BD /* Attachment.c */; };
+		27908E0018A3F8CB002D41BD /* AttachmentLoader.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DD918A3F8CB002D41BD /* AttachmentLoader.c */; };
+		27908E0118A3F8CB002D41BD /* Bone.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DDB18A3F8CB002D41BD /* Bone.c */; };
+		27908E0218A3F8CB002D41BD /* BoneData.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DDD18A3F8CB002D41BD /* BoneData.c */; };
+		27908E0318A3F8CB002D41BD /* BoundingBoxAttachment.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DDF18A3F8CB002D41BD /* BoundingBoxAttachment.c */; };
+		27908E0418A3F8CB002D41BD /* Event.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DE118A3F8CB002D41BD /* Event.c */; };
+		27908E0518A3F8CB002D41BD /* EventData.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DE318A3F8CB002D41BD /* EventData.c */; };
+		27908E0618A3F8CB002D41BD /* extension.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DE518A3F8CB002D41BD /* extension.c */; };
+		27908E0718A3F8CB002D41BD /* Json.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DE718A3F8CB002D41BD /* Json.c */; };
+		27908E0818A3F8CB002D41BD /* RegionAttachment.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DE918A3F8CB002D41BD /* RegionAttachment.c */; };
+		27908E0918A3F8CB002D41BD /* Skeleton.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DEB18A3F8CB002D41BD /* Skeleton.c */; };
+		27908E0A18A3F8CB002D41BD /* SkeletonBounds.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DED18A3F8CB002D41BD /* SkeletonBounds.c */; };
+		27908E0B18A3F8CB002D41BD /* SkeletonData.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DEF18A3F8CB002D41BD /* SkeletonData.c */; };
+		27908E0C18A3F8CB002D41BD /* SkeletonJson.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DF118A3F8CB002D41BD /* SkeletonJson.c */; };
+		27908E0D18A3F8CB002D41BD /* Skin.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DF318A3F8CB002D41BD /* Skin.c */; };
+		27908E0E18A3F8CB002D41BD /* Slot.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DF518A3F8CB002D41BD /* Slot.c */; };
+		27908E0F18A3F8CB002D41BD /* SlotData.c in Sources */ = {isa = PBXBuildFile; fileRef = 27908DF718A3F8CB002D41BD /* SlotData.c */; };
+		27908E1318A3F904002D41BD /* SkeletonAsset.cc in Sources */ = {isa = PBXBuildFile; fileRef = 27908E1118A3F904002D41BD /* SkeletonAsset.cc */; };
+		27908E1718A3F91F002D41BD /* Skeleton.cc in Sources */ = {isa = PBXBuildFile; fileRef = 27908E1518A3F91F002D41BD /* Skeleton.cc */; };
 		2A03300D165D1D2100E9CD70 /* unitTesting.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2A03300B165D1D2100E9CD70 /* unitTesting.cc */; };
 		2A033011165D1D4100E9CD70 /* platformFileIoTests.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2A033010165D1D4100E9CD70 /* platformFileIoTests.cc */; };
 		2A25739016A48DAC00363C6F /* ParticlePlayer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2A25738E16A48DAC00363C6F /* ParticlePlayer.cc */; };
@@ -48,6 +72,10 @@
 		8609FE361655716E004662ED /* osxPopupMenu.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8609FE351655716E004662ED /* osxPopupMenu.mm */; };
 		8609FE38165572EC004662ED /* osxFont.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8609FE37165572EC004662ED /* osxFont.mm */; };
 		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 */; };
 		8652F2A016C146CF00639EFE /* torque2d.icns in Resources */ = {isa = PBXBuildFile; fileRef = 8652F29F16C146CF00639EFE /* torque2d.icns */; };
 		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 */; };
 		866381E51655615200C8C551 /* osxInput.mm in Sources */ = {isa = PBXBuildFile; fileRef = 866381E41655615200C8C551 /* osxInput.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 */; };
 		8694ADC81656B06B0080ABAC /* osxEvents.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8694ADC51656B06B0080ABAC /* osxEvents.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 */; };
 /* 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 */
-		04CC626B1795512500F9C119 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = ../../../../../../../System/Library/Frameworks/AVFoundation.framework; sourceTree = "<group>"; };
+		27908DCD18A3F8CB002D41BD /* Animation.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Animation.c; path = ../../../source/spine/Animation.c; sourceTree = "<group>"; };
+		27908DCE18A3F8CB002D41BD /* Animation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Animation.h; path = ../../../source/spine/Animation.h; sourceTree = "<group>"; };
+		27908DCF18A3F8CB002D41BD /* AnimationState.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = AnimationState.c; path = ../../../source/spine/AnimationState.c; sourceTree = "<group>"; };
+		27908DD018A3F8CB002D41BD /* AnimationState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AnimationState.h; path = ../../../source/spine/AnimationState.h; sourceTree = "<group>"; };
+		27908DD118A3F8CB002D41BD /* AnimationStateData.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = AnimationStateData.c; path = ../../../source/spine/AnimationStateData.c; sourceTree = "<group>"; };
+		27908DD218A3F8CB002D41BD /* AnimationStateData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AnimationStateData.h; path = ../../../source/spine/AnimationStateData.h; sourceTree = "<group>"; };
+		27908DD318A3F8CB002D41BD /* Atlas.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Atlas.c; path = ../../../source/spine/Atlas.c; sourceTree = "<group>"; };
+		27908DD418A3F8CB002D41BD /* Atlas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Atlas.h; path = ../../../source/spine/Atlas.h; sourceTree = "<group>"; };
+		27908DD518A3F8CB002D41BD /* AtlasAttachmentLoader.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = AtlasAttachmentLoader.c; path = ../../../source/spine/AtlasAttachmentLoader.c; sourceTree = "<group>"; };
+		27908DD618A3F8CB002D41BD /* AtlasAttachmentLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AtlasAttachmentLoader.h; path = ../../../source/spine/AtlasAttachmentLoader.h; sourceTree = "<group>"; };
+		27908DD718A3F8CB002D41BD /* Attachment.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Attachment.c; path = ../../../source/spine/Attachment.c; sourceTree = "<group>"; };
+		27908DD818A3F8CB002D41BD /* Attachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Attachment.h; path = ../../../source/spine/Attachment.h; sourceTree = "<group>"; };
+		27908DD918A3F8CB002D41BD /* AttachmentLoader.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = AttachmentLoader.c; path = ../../../source/spine/AttachmentLoader.c; sourceTree = "<group>"; };
+		27908DDA18A3F8CB002D41BD /* AttachmentLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AttachmentLoader.h; path = ../../../source/spine/AttachmentLoader.h; sourceTree = "<group>"; };
+		27908DDB18A3F8CB002D41BD /* Bone.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Bone.c; path = ../../../source/spine/Bone.c; sourceTree = "<group>"; };
+		27908DDC18A3F8CB002D41BD /* Bone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Bone.h; path = ../../../source/spine/Bone.h; sourceTree = "<group>"; };
+		27908DDD18A3F8CB002D41BD /* BoneData.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = BoneData.c; path = ../../../source/spine/BoneData.c; sourceTree = "<group>"; };
+		27908DDE18A3F8CB002D41BD /* BoneData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BoneData.h; path = ../../../source/spine/BoneData.h; sourceTree = "<group>"; };
+		27908DDF18A3F8CB002D41BD /* BoundingBoxAttachment.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = BoundingBoxAttachment.c; path = ../../../source/spine/BoundingBoxAttachment.c; sourceTree = "<group>"; };
+		27908DE018A3F8CB002D41BD /* BoundingBoxAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BoundingBoxAttachment.h; path = ../../../source/spine/BoundingBoxAttachment.h; sourceTree = "<group>"; };
+		27908DE118A3F8CB002D41BD /* Event.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Event.c; path = ../../../source/spine/Event.c; sourceTree = "<group>"; };
+		27908DE218A3F8CB002D41BD /* Event.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Event.h; path = ../../../source/spine/Event.h; sourceTree = "<group>"; };
+		27908DE318A3F8CB002D41BD /* EventData.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = EventData.c; path = ../../../source/spine/EventData.c; sourceTree = "<group>"; };
+		27908DE418A3F8CB002D41BD /* EventData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EventData.h; path = ../../../source/spine/EventData.h; sourceTree = "<group>"; };
+		27908DE518A3F8CB002D41BD /* extension.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = extension.c; path = ../../../source/spine/extension.c; sourceTree = "<group>"; };
+		27908DE618A3F8CB002D41BD /* extension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = extension.h; path = ../../../source/spine/extension.h; sourceTree = "<group>"; };
+		27908DE718A3F8CB002D41BD /* Json.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Json.c; path = ../../../source/spine/Json.c; sourceTree = "<group>"; };
+		27908DE818A3F8CB002D41BD /* Json.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Json.h; path = ../../../source/spine/Json.h; sourceTree = "<group>"; };
+		27908DE918A3F8CB002D41BD /* RegionAttachment.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = RegionAttachment.c; path = ../../../source/spine/RegionAttachment.c; sourceTree = "<group>"; };
+		27908DEA18A3F8CB002D41BD /* RegionAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegionAttachment.h; path = ../../../source/spine/RegionAttachment.h; sourceTree = "<group>"; };
+		27908DEB18A3F8CB002D41BD /* Skeleton.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Skeleton.c; path = ../../../source/spine/Skeleton.c; sourceTree = "<group>"; };
+		27908DEC18A3F8CB002D41BD /* Skeleton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Skeleton.h; path = ../../../source/spine/Skeleton.h; sourceTree = "<group>"; };
+		27908DED18A3F8CB002D41BD /* SkeletonBounds.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SkeletonBounds.c; path = ../../../source/spine/SkeletonBounds.c; sourceTree = "<group>"; };
+		27908DEE18A3F8CB002D41BD /* SkeletonBounds.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkeletonBounds.h; path = ../../../source/spine/SkeletonBounds.h; sourceTree = "<group>"; };
+		27908DEF18A3F8CB002D41BD /* SkeletonData.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SkeletonData.c; path = ../../../source/spine/SkeletonData.c; sourceTree = "<group>"; };
+		27908DF018A3F8CB002D41BD /* SkeletonData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkeletonData.h; path = ../../../source/spine/SkeletonData.h; sourceTree = "<group>"; };
+		27908DF118A3F8CB002D41BD /* SkeletonJson.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SkeletonJson.c; path = ../../../source/spine/SkeletonJson.c; sourceTree = "<group>"; };
+		27908DF218A3F8CB002D41BD /* SkeletonJson.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkeletonJson.h; path = ../../../source/spine/SkeletonJson.h; sourceTree = "<group>"; };
+		27908DF318A3F8CB002D41BD /* Skin.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Skin.c; path = ../../../source/spine/Skin.c; sourceTree = "<group>"; };
+		27908DF418A3F8CB002D41BD /* Skin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Skin.h; path = ../../../source/spine/Skin.h; sourceTree = "<group>"; };
+		27908DF518A3F8CB002D41BD /* Slot.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Slot.c; path = ../../../source/spine/Slot.c; sourceTree = "<group>"; };
+		27908DF618A3F8CB002D41BD /* Slot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Slot.h; path = ../../../source/spine/Slot.h; sourceTree = "<group>"; };
+		27908DF718A3F8CB002D41BD /* SlotData.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SlotData.c; path = ../../../source/spine/SlotData.c; sourceTree = "<group>"; };
+		27908DF818A3F8CB002D41BD /* SlotData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SlotData.h; path = ../../../source/spine/SlotData.h; sourceTree = "<group>"; };
+		27908DF918A3F8CB002D41BD /* spine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = spine.h; path = ../../../source/spine/spine.h; sourceTree = "<group>"; };
+		27908E1018A3F904002D41BD /* SkeletonAsset_ScriptBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkeletonAsset_ScriptBinding.h; sourceTree = "<group>"; };
+		27908E1118A3F904002D41BD /* SkeletonAsset.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkeletonAsset.cc; sourceTree = "<group>"; };
+		27908E1218A3F904002D41BD /* SkeletonAsset.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkeletonAsset.h; sourceTree = "<group>"; };
+		27908E1418A3F91F002D41BD /* Skeleton_ScriptBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Skeleton_ScriptBinding.h; sourceTree = "<group>"; };
+		27908E1518A3F91F002D41BD /* Skeleton.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Skeleton.cc; sourceTree = "<group>"; };
+		27908E1618A3F91F002D41BD /* Skeleton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Skeleton.h; sourceTree = "<group>"; };
 		2797C9E117F4E12500625B51 /* eaxtypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = eaxtypes.h; sourceTree = "<group>"; };
 		2A03300B165D1D2100E9CD70 /* unitTesting.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = unitTesting.cc; path = ../../../source/testing/unitTesting.cc; sourceTree = "<group>"; };
 		2A03300C165D1D2100E9CD70 /* unitTesting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = unitTesting.h; path = ../../../source/testing/unitTesting.h; sourceTree = "<group>"; };
@@ -564,6 +656,13 @@
 		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>"; };
 		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>"; };
 		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>"; };
@@ -744,6 +843,7 @@
 		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>"; };
 		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>"; };
 		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>"; };
@@ -1437,6 +1537,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				867492F5188727FF00CF0136 /* libLeap.dylib in Frameworks */,
 				865A20CA16515B1E00527C44 /* AppKit.framework in Frameworks */,
 				865A20CC16515B1E00527C44 /* Cocoa.framework in Frameworks */,
 				865A20CD16515B1E00527C44 /* CoreData.framework in Frameworks */,
@@ -1450,6 +1551,58 @@
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
+		27908DCC18A3F895002D41BD /* spine */ = {
+			isa = PBXGroup;
+			children = (
+				27908DCD18A3F8CB002D41BD /* Animation.c */,
+				27908DCE18A3F8CB002D41BD /* Animation.h */,
+				27908DCF18A3F8CB002D41BD /* AnimationState.c */,
+				27908DD018A3F8CB002D41BD /* AnimationState.h */,
+				27908DD118A3F8CB002D41BD /* AnimationStateData.c */,
+				27908DD218A3F8CB002D41BD /* AnimationStateData.h */,
+				27908DD318A3F8CB002D41BD /* Atlas.c */,
+				27908DD418A3F8CB002D41BD /* Atlas.h */,
+				27908DD518A3F8CB002D41BD /* AtlasAttachmentLoader.c */,
+				27908DD618A3F8CB002D41BD /* AtlasAttachmentLoader.h */,
+				27908DD718A3F8CB002D41BD /* Attachment.c */,
+				27908DD818A3F8CB002D41BD /* Attachment.h */,
+				27908DD918A3F8CB002D41BD /* AttachmentLoader.c */,
+				27908DDA18A3F8CB002D41BD /* AttachmentLoader.h */,
+				27908DDB18A3F8CB002D41BD /* Bone.c */,
+				27908DDC18A3F8CB002D41BD /* Bone.h */,
+				27908DDD18A3F8CB002D41BD /* BoneData.c */,
+				27908DDE18A3F8CB002D41BD /* BoneData.h */,
+				27908DDF18A3F8CB002D41BD /* BoundingBoxAttachment.c */,
+				27908DE018A3F8CB002D41BD /* BoundingBoxAttachment.h */,
+				27908DE118A3F8CB002D41BD /* Event.c */,
+				27908DE218A3F8CB002D41BD /* Event.h */,
+				27908DE318A3F8CB002D41BD /* EventData.c */,
+				27908DE418A3F8CB002D41BD /* EventData.h */,
+				27908DE518A3F8CB002D41BD /* extension.c */,
+				27908DE618A3F8CB002D41BD /* extension.h */,
+				27908DE718A3F8CB002D41BD /* Json.c */,
+				27908DE818A3F8CB002D41BD /* Json.h */,
+				27908DE918A3F8CB002D41BD /* RegionAttachment.c */,
+				27908DEA18A3F8CB002D41BD /* RegionAttachment.h */,
+				27908DEB18A3F8CB002D41BD /* Skeleton.c */,
+				27908DEC18A3F8CB002D41BD /* Skeleton.h */,
+				27908DED18A3F8CB002D41BD /* SkeletonBounds.c */,
+				27908DEE18A3F8CB002D41BD /* SkeletonBounds.h */,
+				27908DEF18A3F8CB002D41BD /* SkeletonData.c */,
+				27908DF018A3F8CB002D41BD /* SkeletonData.h */,
+				27908DF118A3F8CB002D41BD /* SkeletonJson.c */,
+				27908DF218A3F8CB002D41BD /* SkeletonJson.h */,
+				27908DF318A3F8CB002D41BD /* Skin.c */,
+				27908DF418A3F8CB002D41BD /* Skin.h */,
+				27908DF518A3F8CB002D41BD /* Slot.c */,
+				27908DF618A3F8CB002D41BD /* Slot.h */,
+				27908DF718A3F8CB002D41BD /* SlotData.c */,
+				27908DF818A3F8CB002D41BD /* SlotData.h */,
+				27908DF918A3F8CB002D41BD /* spine.h */,
+			);
+			name = spine;
+			sourceTree = "<group>";
+		};
 		2A033005165D1CB100E9CD70 /* testing */ = {
 			isa = PBXGroup;
 			children = (
@@ -1586,6 +1739,19 @@
 			name = composites;
 			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 */ = {
 			isa = PBXGroup;
 			children = (
@@ -1901,7 +2067,6 @@
 		869FF8AD1651518C002FE082 = {
 			isa = PBXGroup;
 			children = (
-				04CC626B1795512500F9C119 /* AVFoundation.framework */,
 				8652F29F16C146CF00639EFE /* torque2d.icns */,
 				869FF8C21651518C002FE082 /* Torque2D */,
 				865A2351165188AF00527C44 /* platformOSX */,
@@ -1924,6 +2089,7 @@
 		869FF8BB1651518C002FE082 /* Frameworks */ = {
 			isa = PBXGroup;
 			children = (
+				867492F4188727FF00CF0136 /* libLeap.dylib */,
 				865A20C916515AEF00527C44 /* System */,
 				869FF8BE1651518C002FE082 /* Other Frameworks */,
 			);
@@ -1940,7 +2106,6 @@
 		869FF8C21651518C002FE082 /* Torque2D */ = {
 			isa = PBXGroup;
 			children = (
-				86BC815516518D4600D96ADF /* torqueConfig.h */,
 				86BC7E7516518D4600D96ADF /* 2d */,
 				86BC7EE016518D4600D96ADF /* algorithm */,
 				86BC7EE716518D4600D96ADF /* assets */,
@@ -1963,8 +2128,10 @@
 				86BC80EF16518D4600D96ADF /* persistence */,
 				86BC82ED16518F6800D96ADF /* platform */,
 				86BC812C16518D4600D96ADF /* sim */,
+				27908DCC18A3F895002D41BD /* spine */,
 				86BC814816518D4600D96ADF /* string */,
 				2A033005165D1CB100E9CD70 /* testing */,
+				86BC815516518D4600D96ADF /* torqueConfig.h */,
 			);
 			path = Torque2D;
 			sourceTree = "<group>";
@@ -1984,10 +2151,10 @@
 		86BC7E7516518D4600D96ADF /* 2d */ = {
 			isa = PBXGroup;
 			children = (
-				2AE2938016EF4BFA0015E200 /* experimental */,
-				2AB4F1CF16D55B7300C9A27B /* controllers */,
 				86BC7E7616518D4600D96ADF /* assets */,
+				2AB4F1CF16D55B7300C9A27B /* controllers */,
 				86BC7E8016518D4600D96ADF /* core */,
+				2AE2938016EF4BFA0015E200 /* experimental */,
 				86BC7E9616518D4600D96ADF /* gui */,
 				86BC7EA216518D4600D96ADF /* scene */,
 				86BC7EB716518D4600D96ADF /* sceneobject */,
@@ -1999,6 +2166,9 @@
 		86BC7E7616518D4600D96ADF /* assets */ = {
 			isa = PBXGroup;
 			children = (
+				27908E1018A3F904002D41BD /* SkeletonAsset_ScriptBinding.h */,
+				27908E1118A3F904002D41BD /* SkeletonAsset.cc */,
+				27908E1218A3F904002D41BD /* SkeletonAsset.h */,
 				2AF80CFF16A80CB400CE13F1 /* ParticleAssetEmitter_ScriptBinding.h */,
 				2AE5B54016A6D860006908D5 /* ParticleAssetFieldCollection.cc */,
 				2AE5B54116A6D860006908D5 /* ParticleAssetFieldCollection.h */,
@@ -2103,31 +2273,34 @@
 		86BC7EB716518D4600D96ADF /* sceneobject */ = {
 			isa = PBXGroup;
 			children = (
-				2AA6865A16D69943003CEF0A /* SceneObjectList.cc */,
-				2AA6865B16D69943003CEF0A /* SceneObjectList.h */,
-				2AA6865C16D69943003CEF0A /* SceneObjectSet_ScriptBinding.h */,
-				2AA6865D16D69943003CEF0A /* SceneObjectSet.cc */,
-				2AA6865E16D69943003CEF0A /* SceneObjectSet.h */,
-				2AC4404216B0142B00FC4091 /* ImageFont_ScriptBinding.h */,
+				86BC7EBB16518D4600D96ADF /* CompositeSprite.cc */,
+				86BC7EBC16518D4600D96ADF /* CompositeSprite.h */,
+				86BC7EBD16518D4600D96ADF /* CompositeSprite_ScriptBinding.h */,
 				2AC4404316B0142B00FC4091 /* ImageFont.cc */,
 				2AC4404416B0142B00FC4091 /* ImageFont.h */,
-				2A25738D16A48DAC00363C6F /* ParticlePlayer_ScriptBinding.h */,
+				2AC4404216B0142B00FC4091 /* ImageFont_ScriptBinding.h */,
 				2A25738E16A48DAC00363C6F /* ParticlePlayer.cc */,
 				2A25738F16A48DAC00363C6F /* ParticlePlayer.h */,
-				86BC7EBB16518D4600D96ADF /* CompositeSprite.cc */,
-				86BC7EBC16518D4600D96ADF /* CompositeSprite.h */,
-				86BC7EBD16518D4600D96ADF /* CompositeSprite_ScriptBinding.h */,
+				2A25738D16A48DAC00363C6F /* ParticlePlayer_ScriptBinding.h */,
 				86BC7EC316518D4600D96ADF /* SceneObject.cc */,
 				86BC7EC416518D4600D96ADF /* SceneObject.h */,
 				86BC7EC516518D4600D96ADF /* SceneObject_ScriptBinding.h */,
+				2AA6865A16D69943003CEF0A /* SceneObjectList.cc */,
+				2AA6865B16D69943003CEF0A /* SceneObjectList.h */,
 				86BC7EC816518D4600D96ADF /* SceneObjectMoveToEvent.h */,
 				86BC7EC916518D4600D96ADF /* SceneObjectRotateToEvent.h */,
+				2AA6865D16D69943003CEF0A /* SceneObjectSet.cc */,
+				2AA6865E16D69943003CEF0A /* SceneObjectSet.h */,
+				2AA6865C16D69943003CEF0A /* SceneObjectSet_ScriptBinding.h */,
 				86BC7ECE16518D4600D96ADF /* Scroller.cc */,
 				86BC7ECF16518D4600D96ADF /* Scroller.h */,
 				86BC7ED016518D4600D96ADF /* Scroller_ScriptBinding.h */,
 				86BC7ED116518D4600D96ADF /* ShapeVector.cc */,
 				86BC7ED216518D4600D96ADF /* ShapeVector.h */,
 				86BC7ED316518D4600D96ADF /* ShapeVector_ScriptBinding.h */,
+				27908E1518A3F91F002D41BD /* Skeleton.cc */,
+				27908E1618A3F91F002D41BD /* Skeleton.h */,
+				27908E1418A3F91F002D41BD /* Skeleton_ScriptBinding.h */,
 				86BC7ED416518D4600D96ADF /* Sprite.cc */,
 				86BC7ED516518D4600D96ADF /* Sprite.h */,
 				86BC7ED616518D4600D96ADF /* Sprite_ScriptBinding.h */,
@@ -2600,6 +2773,7 @@
 		86BC805816518D4600D96ADF /* input */ = {
 			isa = PBXGroup;
 			children = (
+				8645C96D18872339004ED987 /* leapMotion */,
 				B350D153174EF5F200033EBB /* actionMap_ScriptBinding.h */,
 				86BC805916518D4600D96ADF /* actionMap.cc */,
 				86BC805A16518D4600D96ADF /* actionMap.h */,
@@ -2685,6 +2859,7 @@
 		86BC809116518D4600D96ADF /* math */ = {
 			isa = PBXGroup;
 			children = (
+				8645C96B1887231C004ED987 /* mPoint.cpp */,
 				B350D12B174ED1FE00033EBB /* box_ScriptBinding.h */,
 				B350D12C174ED1FE00033EBB /* math_ScriptBinding.cc */,
 				B350D12D174ED1FE00033EBB /* matrix_ScriptBinding.h */,
@@ -3036,6 +3211,7 @@
 				869FF8B61651518C002FE082 /* Resources */,
 				869FF8B41651518C002FE082 /* Sources */,
 				869FF8B51651518C002FE082 /* Frameworks */,
+				8645C978188723B7004ED987 /* CopyFiles */,
 			);
 			buildRules = (
 			);
@@ -3131,6 +3307,7 @@
 				86D7707F1656873C0046D71F /* SimXMLDocument.cpp in Sources */,
 				86D770801656873C0046D71F /* taml.cc in Sources */,
 				86D770841656873C0046D71F /* tamlWriteNode.cc in Sources */,
+				27908E0618A3F8CB002D41BD /* extension.c in Sources */,
 				86D770881656873C0046D71F /* tinystr.cpp in Sources */,
 				86D770891656873C0046D71F /* tinyxml.cpp in Sources */,
 				86D7708A1656873C0046D71F /* tinyxmlerror.cpp in Sources */,
@@ -3138,6 +3315,7 @@
 				86D7708D1656873C0046D71F /* CursorManager.cc in Sources */,
 				86D7708E1656873C0046D71F /* platform.cc in Sources */,
 				86D7708F1656873C0046D71F /* platformAssert.cc in Sources */,
+				27908E1318A3F904002D41BD /* SkeletonAsset.cc in Sources */,
 				86D770901656873C0046D71F /* platformCPU.cc in Sources */,
 				86D770911656873C0046D71F /* platformFileIO.cc in Sources */,
 				86D770921656873C0046D71F /* platformFont.cc in Sources */,
@@ -3147,6 +3325,7 @@
 				86D770961656873C0046D71F /* platformVideo.cc in Sources */,
 				86D770971656873C0046D71F /* Tickable.cc in Sources */,
 				86D770981656873C0046D71F /* popupMenu.cc in Sources */,
+				27908DFB18A3F8CB002D41BD /* AnimationState.c in Sources */,
 				86D770991656873C0046D71F /* msgBox.cpp in Sources */,
 				86D770AA1656873C0046D71F /* scriptGroup.cc in Sources */,
 				86D770AB1656873C0046D71F /* scriptObject.cc in Sources */,
@@ -3154,6 +3333,7 @@
 				86D770AD1656873C0046D71F /* simConsoleEvent.cc in Sources */,
 				86D770AE1656873C0046D71F /* simConsoleThreadExecEvent.cc in Sources */,
 				86D770AF1656873C0046D71F /* simDatablock.cc in Sources */,
+				27908DFF18A3F8CB002D41BD /* Attachment.c in Sources */,
 				86D770B01656873C0046D71F /* simDictionary.cc in Sources */,
 				86D770B11656873C0046D71F /* simFieldDictionary.cc in Sources */,
 				86D770B21656873C0046D71F /* simManager.cc in Sources */,
@@ -3177,7 +3357,9 @@
 				86D77046165687220046D71F /* memStream.cc in Sources */,
 				86D77047165687220046D71F /* nStream.cc in Sources */,
 				86D77048165687220046D71F /* resizeStream.cc in Sources */,
+				27908E1718A3F91F002D41BD /* Skeleton.cc in Sources */,
 				86D77049165687220046D71F /* resourceDictionary.cc in Sources */,
+				27908E0018A3F8CB002D41BD /* AttachmentLoader.c in Sources */,
 				86D7704A165687220046D71F /* resourceManager.cc in Sources */,
 				86D7704B165687220046D71F /* streamObject.cc in Sources */,
 				86D7704C165687220046D71F /* centralDir.cc in Sources */,
@@ -3189,6 +3371,7 @@
 				86D77052165687220046D71F /* zipArchive.cc in Sources */,
 				86D77053165687220046D71F /* zipCryptStream.cc in Sources */,
 				86D77054165687220046D71F /* zipObject.cc in Sources */,
+				8645C97418872339004ED987 /* leapMotionManager.cc in Sources */,
 				86D77055165687220046D71F /* zipSubStream.cc in Sources */,
 				86D77056165687220046D71F /* zipTempStream.cc in Sources */,
 				86D76FAF165687060046D71F /* crc.cc in Sources */,
@@ -3203,6 +3386,7 @@
 				86D76FBF165687060046D71F /* consoleNamespace.cc in Sources */,
 				86D76FC0165687060046D71F /* consoleBaseType.cc in Sources */,
 				86D76FC1165687060046D71F /* ConsoleTypeValidators.cc in Sources */,
+				27908E0518A3F8CB002D41BD /* EventData.c in Sources */,
 				86D76FC2165687060046D71F /* Package.cc in Sources */,
 				86D76FC3165687060046D71F /* astAlloc.cc in Sources */,
 				86D76FC4165687060046D71F /* astNodes.cc in Sources */,
@@ -3219,6 +3403,7 @@
 				86D76FCF165687060046D71F /* consoleParser.cc in Sources */,
 				86D76FD0165687060046D71F /* consoleTypes.cc in Sources */,
 				86D76FD1165687060046D71F /* profiler.cc in Sources */,
+				27908E0A18A3F8CB002D41BD /* SkeletonBounds.c in Sources */,
 				86D76FD2165687060046D71F /* RemoteDebugger1.cc in Sources */,
 				86D76FD3165687060046D71F /* RemoteDebuggerBase.cc in Sources */,
 				86D76FD4165687060046D71F /* RemoteDebuggerBridge.cc in Sources */,
@@ -3253,7 +3438,9 @@
 				86D77006165687060046D71F /* guiAutoScrollCtrl.cc in Sources */,
 				86D77007165687060046D71F /* guiCtrlArrayCtrl.cc in Sources */,
 				86D77008165687060046D71F /* guiDragAndDropCtrl.cc in Sources */,
+				27908E0418A3F8CB002D41BD /* Event.c in Sources */,
 				86D77009165687060046D71F /* guiDynamicCtrlArrayCtrl.cc in Sources */,
+				27908DFA18A3F8CB002D41BD /* Animation.c in Sources */,
 				86D7700A165687060046D71F /* guiFormCtrl.cc in Sources */,
 				86D7700B165687060046D71F /* guiFrameCtrl.cc in Sources */,
 				86D7700C165687060046D71F /* guiPaneCtrl.cc in Sources */,
@@ -3263,6 +3450,7 @@
 				86D77010165687060046D71F /* guiTabBookCtrl.cc in Sources */,
 				86D77011165687060046D71F /* guiWindowCtrl.cc in Sources */,
 				86D77012165687060046D71F /* guiControlListPopup.cc in Sources */,
+				27908E0F18A3F8CB002D41BD /* SlotData.c in Sources */,
 				86D77013165687060046D71F /* guiDebugger.cc in Sources */,
 				86D77014165687060046D71F /* guiEditCtrl.cc in Sources */,
 				86D77015165687060046D71F /* guiFilterCtrl.cc in Sources */,
@@ -3276,11 +3464,13 @@
 				86D7701D165687060046D71F /* guiBackgroundCtrl.cc in Sources */,
 				86D7701E165687060046D71F /* guiBitmapBorderCtrl.cc in Sources */,
 				86D7701F165687060046D71F /* guiBitmapCtrl.cc in Sources */,
+				27908E0E18A3F8CB002D41BD /* Slot.c in Sources */,
 				86D77020165687060046D71F /* guiBubbleTextCtrl.cc in Sources */,
 				86D77021165687060046D71F /* guiCanvas.cc in Sources */,
 				86D77022165687060046D71F /* guiColorPicker.cc in Sources */,
 				86D77023165687060046D71F /* guiConsole.cc in Sources */,
 				86D77024165687060046D71F /* guiConsoleEditCtrl.cc in Sources */,
+				8645C97518872339004ED987 /* leapMotionUtil.cpp in Sources */,
 				86D77025165687060046D71F /* guiConsoleTextCtrl.cc in Sources */,
 				86D77026165687060046D71F /* guiControl.cc in Sources */,
 				86D77027165687060046D71F /* guiDefaultControlRender.cc in Sources */,
@@ -3310,6 +3500,8 @@
 				86D76F9C165686D80046D71F /* assetFieldTypes.cc in Sources */,
 				86D76F9D165686D80046D71F /* assetManager.cc in Sources */,
 				86D76F9F165686D80046D71F /* assetQuery.cc in Sources */,
+				27908E0918A3F8CB002D41BD /* Skeleton.c in Sources */,
+				27908E0118A3F8CB002D41BD /* Bone.c in Sources */,
 				86D76FA1165686D80046D71F /* assetTagsManifest.cc in Sources */,
 				86D76FA2165686D80046D71F /* audio.cc in Sources */,
 				86D76FA3165686D80046D71F /* AudioAsset.cc in Sources */,
@@ -3371,11 +3563,13 @@
 				865A228E165187B600527C44 /* b2ContactManager.cpp in Sources */,
 				865A228F165187B600527C44 /* b2Fixture.cpp in Sources */,
 				865A2290165187B600527C44 /* b2Island.cpp in Sources */,
+				8645C96C1887231C004ED987 /* mPoint.cpp in Sources */,
 				865A2291165187B600527C44 /* b2World.cpp in Sources */,
 				865A2292165187B600527C44 /* b2WorldCallbacks.cpp in Sources */,
 				865A2293165187B600527C44 /* b2ChainAndCircleContact.cpp in Sources */,
 				865A2294165187B600527C44 /* b2ChainAndPolygonContact.cpp in Sources */,
 				865A2295165187B600527C44 /* b2CircleContact.cpp in Sources */,
+				27908E0D18A3F8CB002D41BD /* Skin.c in Sources */,
 				865A2296165187B600527C44 /* b2Contact.cpp in Sources */,
 				865A2297165187B600527C44 /* b2ContactSolver.cpp in Sources */,
 				865A2298165187B600527C44 /* b2EdgeAndCircleContact.cpp in Sources */,
@@ -3383,6 +3577,7 @@
 				865A229A165187B600527C44 /* b2PolygonAndCircleContact.cpp in Sources */,
 				865A229B165187B600527C44 /* b2PolygonContact.cpp in Sources */,
 				865A229C165187B600527C44 /* b2DistanceJoint.cpp in Sources */,
+				27908DFD18A3F8CB002D41BD /* Atlas.c in Sources */,
 				865A229D165187B600527C44 /* b2FrictionJoint.cpp in Sources */,
 				865A229E165187B600527C44 /* b2GearJoint.cpp in Sources */,
 				865A229F165187B600527C44 /* b2Joint.cpp in Sources */,
@@ -3391,6 +3586,7 @@
 				865A22A2165187B600527C44 /* b2PrismaticJoint.cpp in Sources */,
 				865A22A3165187B600527C44 /* b2PulleyJoint.cpp in Sources */,
 				865A22A4165187B600527C44 /* b2RevoluteJoint.cpp in Sources */,
+				27908DFE18A3F8CB002D41BD /* AtlasAttachmentLoader.c in Sources */,
 				865A22A5165187B600527C44 /* b2RopeJoint.cpp in Sources */,
 				865A22A6165187B600527C44 /* b2WeldJoint.cpp in Sources */,
 				865A22A7165187B600527C44 /* b2WheelJoint.cpp in Sources */,
@@ -3410,6 +3606,7 @@
 				865A2311165187FF00527C44 /* jcphuff.c in Sources */,
 				865A2312165187FF00527C44 /* jcprepct.c in Sources */,
 				865A2313165187FF00527C44 /* jcsample.c in Sources */,
+				27908E0818A3F8CB002D41BD /* RegionAttachment.c in Sources */,
 				865A2314165187FF00527C44 /* jctrans.c in Sources */,
 				865A2315165187FF00527C44 /* jdapimin.c in Sources */,
 				865A2316165187FF00527C44 /* jdapistd.c in Sources */,
@@ -3428,6 +3625,7 @@
 				865A2323165187FF00527C44 /* jdpostct.c in Sources */,
 				865A2324165187FF00527C44 /* jdsample.c in Sources */,
 				865A2325165187FF00527C44 /* jdtrans.c in Sources */,
+				27908DFC18A3F8CB002D41BD /* AnimationStateData.c in Sources */,
 				865A2326165187FF00527C44 /* jerror.c in Sources */,
 				865A2327165187FF00527C44 /* jfdctflt.c in Sources */,
 				865A2328165187FF00527C44 /* jfdctfst.c in Sources */,
@@ -3440,9 +3638,11 @@
 				865A232F165187FF00527C44 /* jmemmgr.c in Sources */,
 				865A2330165187FF00527C44 /* jquant1.c in Sources */,
 				865A2331165187FF00527C44 /* jquant2.c in Sources */,
+				27908E0218A3F8CB002D41BD /* BoneData.c in Sources */,
 				865A2332165187FF00527C44 /* jutils.c in Sources */,
 				865A23421651881300527C44 /* png.c in Sources */,
 				865A23431651881300527C44 /* pngerror.c in Sources */,
+				27908E0718A3F8CB002D41BD /* Json.c in Sources */,
 				865A23441651881300527C44 /* pngget.c in Sources */,
 				865A23451651881300527C44 /* pngmem.c in Sources */,
 				865A23461651881300527C44 /* pngpread.c in Sources */,
@@ -3460,6 +3660,7 @@
 				865A235B16518AD300527C44 /* main.mm in Sources */,
 				86063A251654180000362D83 /* platformOSX.mm in Sources */,
 				866381D8165550FF00C8C551 /* osxFileIO.mm in Sources */,
+				27908E0B18A3F8CB002D41BD /* SkeletonData.c in Sources */,
 				866381DA1655562400C8C551 /* osxMemory.mm in Sources */,
 				866381DC165556AD00C8C551 /* osxMath.mm in Sources */,
 				866381E51655615200C8C551 /* osxInput.mm in Sources */,
@@ -3474,9 +3675,11 @@
 				8694ADC81656B06B0080ABAC /* osxEvents.mm in Sources */,
 				8694ADC91656B06B0080ABAC /* osxWindow.mm in Sources */,
 				8694ADD11656B7FC0080ABAC /* osxVideo.mm in Sources */,
+				27908E0C18A3F8CB002D41BD /* SkeletonJson.c in Sources */,
 				8694ADD51656BDE60080ABAC /* osxGL.mm in Sources */,
 				8658B174165A7BFB0087ABC1 /* osxCPU.mm in Sources */,
 				8658B175165A7BFB0087ABC1 /* osxNetwork.mm in Sources */,
+				27908E0318A3F8CB002D41BD /* BoundingBoxAttachment.c in Sources */,
 				8658B176165A7BFB0087ABC1 /* osxString.mm in Sources */,
 				86EC5AC7165C1E0100757872 /* osxTorqueView.mm in Sources */,
 				2A03300D165D1D2100E9CD70 /* unitTesting.cc in Sources */,
@@ -3572,7 +3775,7 @@
 		865A20AF165152EA00527C44 /* Shipping */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+				CLANG_CXX_LIBRARY = "libc++";
 				COMBINE_HIDPI_IMAGES = YES;
 				CONFIGURATION_BUILD_DIR = ../../..;
 				GCC_PRECOMPILE_PREFIX_HEADER = YES;
@@ -3592,13 +3795,16 @@
 					../../source/persistence/rapidjson/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";
-				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;
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				VALID_ARCHS = i386;
 				WRAPPER_EXTENSION = app;
 			};
 			name = Shipping;
@@ -3673,7 +3879,7 @@
 		869FF8D71651518C002FE082 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+				CLANG_CXX_LIBRARY = "libc++";
 				COMBINE_HIDPI_IMAGES = YES;
 				CONFIGURATION_BUILD_DIR = ../../..;
 				GCC_PRECOMPILE_PREFIX_HEADER = YES;
@@ -3695,13 +3901,16 @@
 					../../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";
-				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;
 				PRODUCT_NAME = "$(TARGET_NAME)_DEBUG";
-				VALID_ARCHS = i386;
 				WRAPPER_EXTENSION = app;
 			};
 			name = Debug;
@@ -3709,7 +3918,7 @@
 		869FF8D81651518C002FE082 /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+				CLANG_CXX_LIBRARY = "libc++";
 				COMBINE_HIDPI_IMAGES = YES;
 				CONFIGURATION_BUILD_DIR = ../../..;
 				GCC_PRECOMPILE_PREFIX_HEADER = YES;
@@ -3732,13 +3941,30 @@
 					../../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,
+				);
+				"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";
-				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;
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				VALID_ARCHS = i386;
 				WRAPPER_EXTENSION = app;
 			};
 			name = Release;

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

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

+ 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/language \
     				$(LOCAL_PATH)/../../../source/input \
+					$(LOCAL_PATH)/../../../source/input/leapMotion \
     				$(LOCAL_PATH)/../../../source/io \
     				$(LOCAL_PATH)/../../../source/io/resource \
     				$(LOCAL_PATH)/../../../source/io/zip \
@@ -337,6 +338,8 @@ LOCAL_SRC_FILES :=  ../../../lib/ljpeg/jcapimin.c \
 					../../../source/gui/language/lang.cc \
 					../../../source/gui/messageVector.cc \
 					../../../source/input/actionMap.cc \
+					../../../source/input/leapMotion/leapMotionManager.cc \
+					../../../source/input/leapMotion/leapMotionUtil.cpp \
 					../../../source/io/bitStream.cc \
 					../../../source/io/bufferStream.cc \
 					../../../source/io/fileObject.cc \
@@ -446,6 +449,7 @@ LOCAL_SRC_FILES :=  ../../../lib/ljpeg/jcapimin.c \
 					../../../source/math/mRandom.cc \
 					../../../source/math/mSolver.cc \
 					../../../source/math/mSplinePatch.cc \
+					../../../source/math/mPoint.cpp \
 					../../../source/persistence/SimXMLDocument.cpp \
 					../../../source/platform/CursorManager.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.
     VECTOR_SET_ASSOCIATION( mAnimationFrames );
+    VECTOR_SET_ASSOCIATION( mNamedAnimationFrames );
     VECTOR_SET_ASSOCIATION( mValidatedFrames );    
 }
 
@@ -104,9 +106,11 @@ void AnimationAsset::initPersistFields()
 
     addProtectedField("Image", TypeImageAssetPtr, Offset(mImageAsset, AnimationAsset), &setImage, &defaultProtectedGetFn, &writeImage, "");
     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("AnimationCycle", TypeBool, Offset(mAnimationCycle, AnimationAsset), &setAnimationCycle, &defaultProtectedGetFn, &writeAnimationCycle, "");
     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.
     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->setAnimationCycle( getAnimationCycle() );
     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" ) ) );
     }
 
+    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.
     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.
     mValidatedFrames.clear();
 
-    // Finish if we don't have a valid image asset.
-    if ( mImageAsset.isNull() )
-        return;
-
     // Fetch Animation Frame Count.
     const U32 animationFrameCount = (U32)mAnimationFrames.size();
 
@@ -290,12 +337,13 @@ void AnimationAsset::validateFrames( void )
         if ( frame < 0 || frame >= imageAssetFrameCount )
         {
             // 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(),
                 frame,
                 frameIndex,
                 mImageAsset.getAssetId() );
 
+            // Set the frame to a valid one.
             if ( frame < 0 )
                 frame = 0;
             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
 {
-    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:
     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:
     AnimationAsset();
@@ -67,15 +71,23 @@ public:
     inline const Vector<S32>& getSpecifiedAnimationFrames( void ) const { return mAnimationFrames; }
     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 );
     inline F32      getAnimationTime( void ) const                      { return mAnimationTime; }
     void            setAnimationCycle( const bool animationCycle );
     inline bool     getAnimationCycle( void ) const                     { return mAnimationCycle; }
     void            setRandomStart( const bool randomStart );
     inline bool     getRandomStart( void ) const                        { return mRandomStart; }
+    void            setNamedCellsMode( const bool namedCellsMode );
+    inline bool     getNamedCellsMode( void ) const                     { return mNamedCellsMode; }
 
     // Frame validation.
     void            validateFrames( void );
+    void            validateNumericalFrames( void );
+    void            validateNamedFrames( void );
 
     // Asset validation.
     virtual bool    isAssetValid( void ) const;
@@ -88,15 +100,19 @@ protected:
     virtual void onAssetRefresh( void );
 
 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_

+ 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))
 {
+    // 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] );
 }
 
@@ -59,6 +67,14 @@ ConsoleMethodWithDocs(AnimationAsset, setAnimationFrames, ConsoleVoid, 3, 3, (an
 */
 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.
     S32 bufferSize = 4096;
     char* pBuffer = Con::getReturnBuffer( bufferSize );
@@ -93,6 +109,14 @@ ConsoleMethodWithDocs(AnimationAsset, getAnimationFrames, ConsoleString, 2, 3, (
 */
 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.
     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.
     @param animationTime The total time to cycle through all animation frames.
     @return No return value.
@@ -145,4 +255,25 @@ ConsoleMethodWithDocs(AnimationAsset, getAnimationCycle, ConsoleBool, 2, 2, ())
     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)

+ 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 cellRegionName              = StringTable->insert( "RegionName" );
 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 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);
 
     // Warn.
-    Con::warnf("ImageAsset::getFilterModeEnum() - Invalid filter-mode '%s'", label );
+    Con::warnf( "ImageAsset::getFilterModeEnum() - Invalid filter-mode '%s'", label );
 
     return ImageAsset::FILTER_INVALID;
 }
@@ -148,7 +152,7 @@ const char* ImageAsset::getFilterModeDescription( ImageAsset::TextureFilterMode
             return textureFilterLookup[i].label;
 
     // Warn.
-    Con::warnf("ImageAsset::getFilterModeDescription() - Invalid filter-mode." );
+    Con::warnf( "ImageAsset::getFilterModeDescription() - Invalid filter-mode." );
 
     return StringTable->EmptyString;
 }
@@ -159,7 +163,6 @@ ImageAsset::ImageAsset() :  mImageFile(StringTable->EmptyString),
                             mForce16Bit(false),
                             mLocalFilterMode(FILTER_INVALID),
                             mExplicitMode(false),
-
                             mCellRowOrder(true),
                             mCellOffsetX(0),
                             mCellOffsetY(0),
@@ -294,7 +297,7 @@ void ImageAsset::copyTo(SimObject* object)
         const FrameArea::PixelArea& pixelArea = getImageFrameArea( index ).mPixelArea;
 
         // 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();
 }
 
+//------------------------------------------------------------------------------
+
 S32 ImageAsset::getExplicitCellWidth(const S32 cellIndex)
 {
 	if ( !getExplicitMode() )
@@ -564,6 +569,8 @@ S32 ImageAsset::getExplicitCellWidth(const S32 cellIndex)
 
 }
 
+//------------------------------------------------------------------------------
+
 S32 ImageAsset::getExplicitCellHeight(const S32 cellIndex)
 {
 	if ( !getExplicitMode() )
@@ -577,6 +584,27 @@ S32 ImageAsset::getExplicitCellHeight(const S32 cellIndex)
     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 )
@@ -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?
     if ( !getExplicitMode() )
     {
         // 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;
     }
 
     // Fetch the original image dimensions.
     const S32 imageWidth = getImageWidth();
     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.
     if ( cellOffsetX < 0 || cellOffsetX >= imageWidth )
     {
         // Warn.
-        Con::warnf("ImageAsset::addCell() - Invalid Cell OffsetX of %d.", cellOffsetX );
+        Con::warnf( "ImageAsset::addExplicitCell() - Invalid Cell OffsetX of %d.", cellOffsetX );
         return false;
     }
 
     // The Cell Offset Y needs to be within the image.
-    if ( cellOffsetY < 0 || cellOffsetY >= imageWidth )
+    if ( cellOffsetY < 0 || cellOffsetY >= imageHeight )
     {
         // Warn.
-        Con::warnf("ImageAsset::addCell() - Invalid Cell OffsetY of %d.", cellOffsetY );
+        Con::warnf( "ImageAsset::addExplicitCell() - Invalid Cell OffsetY of %d.", cellOffsetY );
         return false;
     }
 
@@ -634,7 +671,7 @@ bool ImageAsset::addExplicitCell( const S32 cellOffsetX, const S32 cellOffsetY,
     if ( cellWidth <= 0 || (cellOffsetX+cellWidth) > imageWidth )
     {
         // Warn.
-        Con::warnf("ImageAsset::addCell() - Invalid Cell Width of %d.", cellWidth );
+        Con::warnf( "ImageAsset::addExplicitCell() - Invalid Cell Width of %d.", cellWidth );
         return false;
     }
 
@@ -642,12 +679,12 @@ bool ImageAsset::addExplicitCell( const S32 cellOffsetX, const S32 cellOffsetY,
     if ( cellHeight <= 0 || (cellOffsetY+cellHeight) > imageHeight )
     {
         // Warn.
-        Con::warnf("ImageAsset::addCell() - Invalid Cell Width of %d.", cellHeight );
+        Con::warnf( "ImageAsset::addExplicitCell() - Invalid Cell Width of %d.", cellHeight );
         return false;
     }
 
     // Store frame.
-    FrameArea::PixelArea pixelArea( cellOffsetX, cellOffsetY, cellWidth, cellHeight );
+    FrameArea::PixelArea pixelArea( cellOffsetX, cellOffsetY, cellWidth, cellHeight, regionName );
     mExplicitFrames.push_back( pixelArea );
 
     // 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?
     if ( !getExplicitMode() )
     {
         // 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;
     }
 
@@ -674,12 +711,20 @@ bool ImageAsset::insertExplicitCell( const S32 cellIndex, const S32 cellOffsetX,
 
     // Fetch the explicit frame count.
     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.
     if ( cellIndex < 0 )
     {
         // Warn.
-        Con::warnf("ImageAsset::insertCell() - Invalid Cell Index of %d.", cellIndex );
+        Con::warnf( "ImageAsset::insertExplicitCell() - Invalid Cell Index of %d.", cellIndex );
         return false;
     }
 
@@ -687,15 +732,15 @@ bool ImageAsset::insertExplicitCell( const S32 cellIndex, const S32 cellOffsetX,
     if ( cellOffsetX < 0 || cellOffsetX >= imageWidth )
     {
         // Warn.
-        Con::warnf("ImageAsset::insertCell() - Invalid Cell OffsetX of %d.", cellOffsetX );
+        Con::warnf( "ImageAsset::insertExplicitCell() - Invalid Cell OffsetX of %d.", cellOffsetX );
         return false;
     }
 
     // The Cell Offset Y needs to be within the image.
-    if ( cellOffsetY < 0 || cellOffsetY >= imageWidth )
+    if ( cellOffsetY < 0 || cellOffsetY >= imageHeight )
     {
         // Warn.
-        Con::warnf("ImageAsset::insertCell() - Invalid Cell OffsetY of %d.", cellOffsetY );
+        Con::warnf( "ImageAsset::insertExplicitCell() - Invalid Cell OffsetY of %d.", cellOffsetY );
         return false;
     }
 
@@ -703,7 +748,7 @@ bool ImageAsset::insertExplicitCell( const S32 cellIndex, const S32 cellOffsetX,
     if ( cellWidth <= 0 || (cellOffsetX+cellWidth) > imageWidth )
     {
         // Warn.
-        Con::warnf("ImageAsset::insertCell() - Invalid Cell Width of %d.", cellWidth );
+        Con::warnf( "ImageAsset::insertExplicitCell() - Invalid Cell Width of %d.", cellWidth );
         return false;
     }
 
@@ -711,12 +756,12 @@ bool ImageAsset::insertExplicitCell( const S32 cellIndex, const S32 cellOffsetX,
     if ( cellHeight <= 0 || (cellOffsetY+cellHeight) > imageHeight )
     {
         // Warn.
-        Con::warnf("ImageAsset::insertCell() - Invalid Cell Width of %d.", cellHeight );
+        Con::warnf( "ImageAsset::insertExplicitCell() - Invalid Cell Width of %d.", cellHeight );
         return false;
     }
 
     // Configure frame.
-    FrameArea::PixelArea pixelArea( cellOffsetX, cellOffsetY, cellWidth, cellHeight );
+    FrameArea::PixelArea pixelArea( cellOffsetX, cellOffsetY, cellWidth, cellHeight, regionName );
 
     // Insert frame appropriately.
     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?
     if ( !getExplicitMode() )
     {
         // 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;
     }
 
@@ -753,12 +798,20 @@ bool ImageAsset::setExplicitCell( const S32 cellIndex, const S32 cellOffsetX, co
 
     // Fetch the explicit frame count.
     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.
     if ( cellIndex < 0 || cellIndex >= explicitFrameCount )
     {
         // Warn.
-        Con::warnf("ImageAsset::setCell() - Invalid Cell Index of %d.", cellIndex );
+        Con::warnf( "ImageAsset::setExplicitCell() - Invalid Cell Index of %d.", cellIndex );
         return false;
     }
 
@@ -766,15 +819,15 @@ bool ImageAsset::setExplicitCell( const S32 cellIndex, const S32 cellOffsetX, co
     if ( cellOffsetX < 0 || cellOffsetX >= imageWidth )
     {
         // Warn.
-        Con::warnf("ImageAsset::setCell() - Invalid Cell OffsetX of %d.", cellOffsetX );
+        Con::warnf( "ImageAsset::setExplicitCell() - Invalid Cell OffsetX of %d.", cellOffsetX );
         return false;
     }
 
     // The Cell Offset Y needs to be within the image.
-    if ( cellOffsetY < 0 || cellOffsetY >= imageWidth )
+    if ( cellOffsetY < 0 || cellOffsetY >= imageHeight )
     {
         // Warn.
-        Con::warnf("ImageAsset::setCell() - Invalid Cell OffsetY of %d.", cellOffsetY );
+        Con::warnf( "ImageAsset::setExplicitCell() - Invalid Cell OffsetY of %d.", cellOffsetY );
         return false;
     }
 
@@ -782,7 +835,7 @@ bool ImageAsset::setExplicitCell( const S32 cellIndex, const S32 cellOffsetX, co
     if ( cellWidth <= 0 || (cellOffsetX+cellWidth) > imageWidth )
     {
         // Warn.
-        Con::warnf("ImageAsset::setCell() - Invalid Cell Width of %d.", cellWidth );
+        Con::warnf( "ImageAsset::setExplicitCell() - Invalid Cell Width of %d.", cellWidth );
         return false;
     }
 
@@ -790,12 +843,12 @@ bool ImageAsset::setExplicitCell( const S32 cellIndex, const S32 cellOffsetX, co
     if ( cellHeight <= 0 || (cellOffsetY+cellHeight) > imageHeight )
     {
         // Warn.
-        Con::warnf("ImageAsset::setCell() - Invalid Cell Width of %d.", cellHeight );
+        Con::warnf( "ImageAsset::setExplicitCell() - Invalid Cell Width of %d.", cellHeight );
         return false;
     }
 
     // Configure frame.
-    FrameArea::PixelArea pixelArea( cellOffsetX, cellOffsetY, cellWidth, cellHeight );
+    FrameArea::PixelArea pixelArea( cellOffsetX, cellOffsetY, cellWidth, cellHeight, regionName );
 
     // Set cell.
     mExplicitFrames[cellIndex] = pixelArea;
@@ -814,7 +867,7 @@ bool ImageAsset::removeExplicitCell( const S32 cellIndex )
     if ( !getExplicitMode() )
     {
         // 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;
     }
 
@@ -825,7 +878,7 @@ bool ImageAsset::removeExplicitCell( const S32 cellIndex )
     if ( cellIndex < 0 || cellIndex >= explicitFrameCount )
     {
         // Warn.
-        Con::warnf("ImageAsset::removeCell() - Invalid Cell Index of %d.", cellIndex );
+        Con::warnf( "ImageAsset::removeExplicitCell() - Invalid Cell Index of %d.", cellIndex );
         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 )
 {
     // Finish if no texture.
@@ -1021,7 +1137,7 @@ void ImageAsset::calculateImplicitMode( void )
     if ( mCellWidth < 1 || mCellWidth > imageWidth )
     {
         // Warn.
-        Con::warnf("ImageAsset::calculateImage() - Invalid Cell Width of %d.", mCellWidth );
+        Con::warnf( "ImageAsset::calculateImage() - Invalid Cell Width of %d.", mCellWidth );
         return;
     }
 
@@ -1029,7 +1145,7 @@ void ImageAsset::calculateImplicitMode( void )
     if ( mCellHeight < 1 || mCellHeight > imageHeight )
     {
         // Warn.
-        Con::warnf("ImageAsset::calculateImage() - Invalid Cell Height of %d.", mCellHeight );
+        Con::warnf( "ImageAsset::calculateImage() - Invalid Cell Height of %d.", mCellHeight );
         return;
     }
 
@@ -1037,7 +1153,7 @@ void ImageAsset::calculateImplicitMode( void )
     if ( mCellOffsetX < 0 || mCellOffsetX >= imageWidth )
     {
         // Warn.
-        Con::warnf("ImageAsset::calculateImage() - Invalid Cell OffsetX of %d.", mCellOffsetX );
+        Con::warnf( "ImageAsset::calculateImage() - Invalid Cell OffsetX of %d.", mCellOffsetX );
         return;
     }
 
@@ -1045,11 +1161,10 @@ void ImageAsset::calculateImplicitMode( void )
     if ( mCellOffsetY < 0 || mCellOffsetY >= imageHeight )
     {
         // Warn.
-        Con::warnf("ImageAsset::calculateImage() - Invalid Cell OffsetY of %d.", mCellOffsetY );
+        Con::warnf( "ImageAsset::calculateImage() - Invalid Cell OffsetY of %d.", mCellOffsetY );
         return;
     }
 
-
     // Are we using Cell-StrideX?
     S32 cellStepX;
     if ( mCellStrideX != 0 )
@@ -1082,14 +1197,14 @@ void ImageAsset::calculateImplicitMode( void )
     if ( cellFinalPositionX < 0 )
     {
         // 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;
     }
     // Off Right?
     else if ( cellFinalPositionX > imageWidth )
     {
         // 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;
     }
 
@@ -1099,14 +1214,14 @@ void ImageAsset::calculateImplicitMode( void )
     if ( cellFinalPositionY < 0 )
     {
         // 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;
     }
     // Off Bottom?
     else if ( cellFinalPositionY > imageHeight )
     {
         // 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;
     }
 
@@ -1187,7 +1302,7 @@ void ImageAsset::calculateExplicitMode( void )
         const FrameArea::PixelArea& pixelArea = *frameItr;
 
         // 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.
         mFrames.push_back( frameArea );
@@ -1216,22 +1331,26 @@ void ImageAsset::onTamlCustomWrite( TamlCustomNodes& customNodes )
     if ( !mExplicitMode )
         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.
     PROFILE_SCOPE(ImageAsset_OnTamlCustomRead);
-
+    
     // Call parent.
     Parent::onTamlCustomRead( customNodes );
-
+    
     // 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;
             }
+            
+            // 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.
     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( "minOccurs", 0 );
     pImageAssetNodeElement->SetAttribute( "maxOccurs", 1 );
@@ -1381,6 +1525,12 @@ static void WriteCustomTamlSchema( const AbstractClassRep* pClassRep, TiXmlEleme
     TiXmlElement* pImageAssetComplexTypeElement = new TiXmlElement( "xs:complexType" );
     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.
     TiXmlElement* pImageAssetOffset = new TiXmlElement( "xs:attribute" );
     pImageAssetOffset->SetAttribute( "name", cellOffsetName );

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

@@ -73,17 +73,28 @@ public:
             {
                 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 )
             {
                 mPixelOffset.set( pixelFrameOffsetX, pixelFrameOffsetY );
                 mPixelWidth = pixelFrameWidth;
                 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;
             U32 mPixelWidth;
             U32 mPixelHeight;
+            StringTableEntry mRegionName;
         };
 
 
@@ -122,12 +133,25 @@ public:
         {
             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 )
         {
             mPixelArea.setArea( pixelFrameOffsetX, pixelFrameOffsetY, pixelFrameWidth, pixelFrameHeight );
             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;
         TexelArea mTexelArea;
@@ -207,27 +231,36 @@ public:
     void                    setCellHeight( const S32 cellheight );
     S32                     getCellHeight( void) const						{ return mCellHeight; }
     S32                     getExplicitCellHeight(const S32 cellIndex);
+    
+    bool                    containsNamedRegion(const char* regionName);
 
     inline TextureHandle&   getImageTexture( void )                         { return mImageTextureHandle; }
     inline S32              getImageWidth( void ) const                     { return mImageTextureHandle.getWidth(); }
     inline S32              getImageHeight( void ) const                    { return mImageTextureHandle.getHeight(); }
     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( const char* namedFrame)      { return getCellByName(namedFrame); };
     inline const void       bindImageTexture( void)                         { glBindTexture( GL_TEXTURE_2D, getImageTexture().getGLName() ); };
     
     virtual bool            isAssetValid( void ) const                      { return !mImageTextureHandle.IsNull(); }
 
     /// Explicit cell control.
     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                    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(); }
     
     static TextureFilterMode getFilterModeEnum(const char* label);
     static const char* getFilterModeDescription( TextureFilterMode filterMode );
+    
+    inline void forceCalculation( void ) { calculateImage(); }
 
     /// Declare Console Object.
     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();
 }
 
-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] );
 
     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] );
 
     return(object->getExplicitCellHeight(cellIndex));
-
 }
+
 //-----------------------------------------------------------------------------
 
 /*! 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 cellWidth The width 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.
     @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.
     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 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 cellWidth The width 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.
     @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.
     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 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 cellWidth The width 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.
     @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.
     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 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.
     mImageAsset.registerRefreshNotify( this );
     mAnimationAsset.registerRefreshNotify( this );
+    
+    mImageFrameName = "";
 }
 
 //------------------------------------------------------------------------------
@@ -246,6 +248,7 @@ void ParticleAssetEmitter::initPersistFields()
 
     addProtectedField("Image", TypeImageAssetPtr, Offset(mImageAsset, ParticleAssetEmitter), &setImage, &getImage, &writeImage, "");
     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("Animation", TypeAnimationAssetPtr, Offset(mAnimationAsset, ParticleAssetEmitter), &setAnimation, &getAnimation, &writeAnimation, "");
 }
@@ -291,10 +294,15 @@ void ParticleAssetEmitter::copyTo(SimObject* object)
    pParticleAssetEmitter->setAlphaTest( getAlphaTest() );
 
    pParticleAssetEmitter->setRandomImageFrame( getRandomImageFrame() );
+
+   // Static provider?
    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
    {
@@ -400,6 +408,9 @@ bool ParticleAssetEmitter::setImage( const char* pAssetId, U32 frame )
         mImageFrame = 0;
     }
 
+    // Using a numerical frame index
+    mUsingFrameName = false;
+
     // Refresh the asset.
     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 )
 {
     // Check Existing Image.
@@ -433,6 +491,9 @@ bool ParticleAssetEmitter::setImageFrame( const U32 frame )
     // Set Frame.
     mImageFrame = frame;
 
+    // Using a numerical frame index.
+    mUsingFrameName = false;
+
     // Refresh the asset.
     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 )
 {
     // Sanity!

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

@@ -107,8 +107,10 @@ private:
     bool                                    mStaticMode;
     AssetPtr<ImageAsset>                    mImageAsset;
     U32                                     mImageFrame;
+    StringTableEntry                        mImageFrameName;
     bool                                    mRandomImageFrame;
     AssetPtr<AnimationAsset>                mAnimationAsset;
+    bool                                    mUsingFrameName;
 
     /// Particle fields.
     ParticleAssetFieldCollection            mParticleFields;
@@ -182,10 +184,14 @@ public:
     inline bool getOldestInFront( void ) const { return mOldestInFront; }
    
     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 char* frameName);
     inline StringTableEntry getImage( void ) const { return mImageAsset.getAssetId(); }
     bool setImageFrame( const U32 frame );
     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 bool getRandomImageFrame( void ) const { return mRandomImageFrame; }
     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     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     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     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; };

+ 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]))
 {
-    // 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.
     @param animationAssetId The animation asset Id to use.
     @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.
         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() )
     {
@@ -178,7 +184,10 @@ bool ImageFrameProviderCore::validRender( void ) const
     if ( isStaticFrameProvider() )
     {
         // 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.
@@ -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(
     const bool flipX,
     const bool flipY,
@@ -254,7 +284,7 @@ void ImageFrameProviderCore::renderGui( GuiControl& owner, Point2I offset, const
         RectI destinationRegion(offset, owner.mBounds.extent);
 
         // Render image.
-		dglSetBitmapModulation( owner.mProfile->mFillColor );
+        dglSetBitmapModulation( owner.mProfile->mFillColor );
         dglDrawBitmapStretchSR( getProviderTexture(), destinationRegion, sourceRegion );
         dglClearBitmapModulation();
     }
@@ -283,6 +313,9 @@ bool ImageFrameProviderCore::setImage( const char* pImageAssetId, const U32 fram
 
     // Set as static provider.
     mStaticProvider = true;
+    
+    // Using a numerical frame index.
+    mUsingNameFrame = false;
 
     // Turn-off tick processing.
     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 )
 {
     // Check Existing Image.
@@ -309,7 +373,7 @@ bool ImageFrameProviderCore::setImageFrame( const U32 frame )
     if ( frame >= (*mpImageAsset)->getFrameCount() )
     {
         // 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 false;
     }
@@ -317,6 +381,40 @@ bool ImageFrameProviderCore::setImageFrame( const U32 frame )
     // Set 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 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
 {
     // Not valid if no animation asset.
     if ( mpAnimationAsset->isNull() )
         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.
-    if ( mCurrentFrameIndex >= validatedFrames.size() )
+    if ( mCurrentFrameIndex >= validatedFrameSize )
         return false;
 
     // Fetch image asset.
@@ -359,12 +477,23 @@ bool ImageFrameProviderCore::isAnimationValid( void ) const
     if ( imageAsset.isNull() )
         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.
     return true;
@@ -419,10 +548,15 @@ bool ImageFrameProviderCore::playAnimation( const AssetPtr<AnimationAsset>& anim
     mStaticProvider = false;
 
     // 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.
-    if ( validatedFrames.size() == 0 )
+    if ( validatedFrameSize == 0 )
     {
         Con::warnf( "ImageFrameProviderCore::playAnimation() - Cannot play AnimationAsset '%s' - Animation has no validated frames!", mpAnimationAsset->getAssetId() );
         return false;
@@ -432,13 +566,13 @@ bool ImageFrameProviderCore::playAnimation( const AssetPtr<AnimationAsset>& anim
     mpAnimationAsset->setAssetId( animationAsset.getAssetId() );
 
     // Set Maximum Frame Index.
-    mMaxFrameIndex = validatedFrames.size()-1;
+    mMaxFrameIndex = validatedFrameSize-1;
 
     // Calculate Total Integration Time.
     mTotalIntegrationTime = (*mpAnimationAsset)->getAnimationTime();
 
     // Calculate Frame Integration Time.
-    mFrameIntegrationTime = mTotalIntegrationTime / validatedFrames.size();
+    mFrameIntegrationTime = mTotalIntegrationTime / validatedFrameSize;
 
     // No, so random Start?
     if ( (*mpAnimationAsset)->getRandomStart() )
@@ -477,12 +611,25 @@ bool ImageFrameProviderCore::updateAnimation( const F32 elapsedTime )
     if ( mAnimationFinished )
         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.
     const F32 scaledTime = elapsedTime * mAnimationTimeScale;
@@ -506,18 +653,6 @@ bool ImageFrameProviderCore::updateAnimation( const F32 elapsedTime )
     // Calculate Current Frame.
     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.
     bool frameChanged = (mCurrentFrameIndex != mLastFrameIndex);
 
@@ -569,6 +704,7 @@ void ImageFrameProviderCore::clearAssets( void )
 
     // Reset remaining state.
     mImageFrame = 0;
+    mImageNameFrame = StringTable->EmptyString;
     mStaticProvider = true;
     setProcessTicks( false );
 }

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

@@ -62,8 +62,11 @@ protected:
     bool                                    mSelfTick;
 
     bool                                    mStaticProvider;
+    
+    bool                                    mUsingNameFrame;
 
     U32                                     mImageFrame;
+    StringTableEntry                        mImageNameFrame;
     AssetPtr<ImageAsset>*                   mpImageAsset;
     AssetPtr<AnimationAsset>*               mpAnimationAsset;
 
@@ -112,9 +115,12 @@ public:
     /// Static-Image Frame.
     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 char* pNameFrame );
     inline StringTableEntry getImage( void ) const{ return mpImageAsset->getAssetId(); }
     virtual bool setImageFrame( const U32 frame );
     inline U32 getImageFrame( void ) const { return mImageFrame; }
+    virtual bool setImageFrameByName( const char* frame );
+    inline StringTableEntry getImageFrameByName( void ) const { return mImageNameFrame; }
 
     /// Animated-Image Frame.
     virtual bool setAnimation( const char* pAnimationAssetId );
@@ -132,12 +138,13 @@ public:
 
     /// Frame provision.
     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 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 StringTableEntry getCurrentAnimationAssetId( void ) const { return mpAnimationAsset->getAssetId(); };
     const U32 getCurrentAnimationFrame( void ) const;
+    const char* getCurrentAnimationFrameName( void ) const;
     inline const F32 getCurrentAnimationTime( void ) const { return mCurrentTime; };
 
     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("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, "");
 }
 

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

@@ -57,14 +57,16 @@ protected:
     virtual void onAnimationEnd( void );
 
 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_

+ 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.
     @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.
 */
-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.
     @param animationAssetId The animation asset Id to play.
     @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 )
 {
     // Debug Profiling.

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

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

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

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

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

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

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

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

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

@@ -4615,20 +4615,23 @@ void Scene::onTamlPostRead( const TamlCustomNodes& customNodes )
     }
 
     // Find controller custom node.
-    const TamlCustomNode* pControllerNode = customNodes.findNode( controllerCustomNodeName );
+    const TamlCustomNode* pControllerCustomNode = customNodes.findNode( controllerCustomNodeName );
 
     // Do we have any controllers?
-    if ( pControllerNode != NULL )
+    if ( pControllerCustomNode != NULL )
     {
         // Yes, so fetch the scene controllers.
         SimSet* pControllerSet = getControllers();
 
         // Fetch children controller nodes.
-        const TamlCustomNodeVector& controllerChildren = pControllerNode->getChildren();
+        const TamlCustomNodeVector& controllerChildren = pControllerCustomNode->getChildren();
 
         // Iterate controllers.
         for( TamlCustomNodeVector::const_iterator controllerNodeItr = controllerChildren.begin(); controllerNodeItr != controllerChildren.end(); ++controllerNodeItr )
         {
+            // Fetch controller node.
+            TamlCustomNode* pControllerNode = *controllerNodeItr;
+            
             // Is the node a proxy object?
             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.
         EmitterNode* pEmitterNode = *emitterItr;
 		pEmitterNode->setPaused(false);
+        
         // Reset the time since last generation.
         pEmitterNode->setTimeSinceLastGeneration( 0.0f );
     }
@@ -1272,7 +1273,10 @@ void ParticlePlayer::configureParticle( EmitterNode* pEmitterNode, ParticleSyste
         else
         {
             // No, so set the emitter image frame.
-            frameProvider.setImageFrame( pParticleAssetEmitter->getImageFrame() );
+            if (pParticleAssetEmitter->isUsingNamedImageFrame())
+                frameProvider.setImageFrameByName( pParticleAssetEmitter->getImageFrameName() );
+            else
+                frameProvider.setImageFrame( pParticleAssetEmitter->getImageFrame() );
         }
     }
     else
@@ -1534,7 +1538,7 @@ void ParticlePlayer::initializeParticleAsset( void )
 
         // Skip if the emitter does not have a valid assigned asset to render.
         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;
 
         // 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,
             "%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,
             normal.x, normal.y,
             point1.x, point1.y,
@@ -1170,7 +1170,7 @@ ConsoleMethodWithDocs(SceneObject, getContact, ConsoleString, 3, 3, (contactInde
     {
         dSprintf(pReturnBuffer, 128,
             "%d %d %d %0.4f %0.4f %0.4f %0.4f %0.4f %0.4f",
-            pSceneObjectCollider,
+            pSceneObjectCollider->getId(),
             shapeIndexThis, shapeIndexCollider,
             normal.x, normal.y,
             point1.x, point1.y,
@@ -1181,7 +1181,7 @@ ConsoleMethodWithDocs(SceneObject, getContact, ConsoleString, 3, 3, (contactInde
 	{
         dSprintf(pReturnBuffer, 64,
             "%d %d %d",
-            pSceneObjectCollider,
+            pSceneObjectCollider->getId(),
             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.
     U32 bufferSize = 8192;
     char* pReturnBuffer = Con::getReturnBuffer( bufferSize );
-    dSprintf(pReturnBuffer, sizeof(pReturnBuffer), "%s", "");
+    dSprintf(pReturnBuffer, bufferSize * sizeof(char), "%s", "");
     char* pBuffer = pReturnBuffer;
 
     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;
    const char *file = object->getCurrentLine(lineNum);
    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;
 }
 

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

@@ -31,17 +31,17 @@
 #include "gui/guiControl.h"
 #include "gui/guiCanvas.h"
 #include "game/gameInterface.h"
-
+
 #include "guiCanvas_ScriptBinding.h"
-
+
 extern int _AndroidGetScreenWidth();
 extern int _AndroidGetScreenHeight();
-
+
 
 IMPLEMENT_CONOBJECT(GuiCanvas);
 
 GuiCanvas *Canvas = NULL;
-
+
 GuiCanvas::GuiCanvas()
 {
 #ifdef TORQUE_OS_IOS
@@ -384,14 +384,14 @@ bool GuiCanvas::processInputEvent(const InputEvent *event)
 
          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));
             if (oldpt.x != S32(cursorPt.x))
                moved = true;
          }
          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));
             if (oldpt.y != S32(cursorPt.y))
                moved = true;
@@ -425,7 +425,7 @@ bool GuiCanvas::processInputEvent(const InputEvent *event)
          mLastEvent.mousePoint.y = S32( cursorPt.y );
          mLastEvent.eventID = 0;
 
-            if ( event->fValue < 0.0f )
+            if ( event->fValues[0] < 0.0f )
             rootMouseWheelDown( mLastEvent );
             else
             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.
 #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_
 #define _ACTIONMAP_H_
 
+
 #ifndef _PLATFORM_H_
 #include "platform/platform.h"
 #endif
@@ -61,7 +62,8 @@ class ActionMap : public SimObject
          HasScale    = BIT(1),   ///< Scaled input.
          HasDeadZone = BIT(2),   ///< Dead zone is present.
          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
@@ -134,6 +136,9 @@ class ActionMap : public SimObject
 
    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:
    ActionMap();
    ~ActionMap();
@@ -161,8 +166,14 @@ class ActionMap : public SimObject
    static const char* buildActionString( const InputEvent* event );
 
    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 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.
    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, const MatrixF &b);  ///< a * b -> M
 
@@ -452,4 +458,11 @@ inline Point3F MatrixF::getPosition() const
    return pos;
 }
 
+inline VectorF MatrixF::getUpVector() const
+{
+   VectorF vec;
+   getColumn( 2, &vec );
+   return vec;
+}
+
 #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
    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
    Point3I operator-() const;
+
+   //-------------------------------------- Public static constants
+public:
+   const static Point3I One;
+   const static Point3I Zero;
 };
 
 
@@ -207,6 +219,14 @@ class Point2F
 
    // Unary operators
    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
    Point2D operator-() const;
+
+//-------------------------------------- Public static constants
+  public:
+    const static Point2D One;
+    const static Point2D Zero;
 };
 
 
@@ -349,6 +374,16 @@ class Point3F
 
    // Unary operators
    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
    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*() 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
 {
    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   objType;     ///< One of SI_XAXIS, SI_BUTTON, SI_KEY ...
    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.
 
    // 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
@@ -388,7 +406,34 @@ enum JoystickCodes {
    SI_UPOV2          = 0x214,
    SI_DPOV2          = 0x215,
    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
@@ -416,9 +461,24 @@ enum TouchCodes
    SI_TOUCHDOWN   = 0x30C,
    SI_TOUCHUP     = 0x30D,
    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
@@ -428,9 +488,12 @@ enum InputDeviceTypes
    MouseDeviceType,
    KeyboardDeviceType,
    JoystickDeviceType,
+   GamepadDeviceType,
+   XInputDeviceType,
    ScreenTouchDeviceType,
    AccelerometerDeviceType,
-   GyroscopeDeviceType
+   GyroscopeDeviceType,
+   LeapMotionDeviceType
 };
 
 /// Device Event Action Types
@@ -438,16 +501,18 @@ enum InputDeviceTypes
 #define SI_BREAK     0x02
 #define SI_MOVE      0x03
 #define SI_REPEAT    0x04
+#define SI_VALUE	 0x05
 
 ///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
 #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

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

@@ -92,3 +92,126 @@ ConsoleFunctionWithDocs( echoInputState, ConsoleVoid, 1, 1, ())
 {
 	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.action = action;
     if (action == SI_BREAK)
-        torqueEvent.fValue = 0.0;
+        torqueEvent.fValues[0] = 0.0;
     else
-        torqueEvent.fValue = 1.0;
+        torqueEvent.fValues[0] = 1.0;
     
     // Post the input event
     Game->postEvent(torqueEvent);
@@ -287,7 +287,7 @@
     torqueEvent.modifier = modifiers;
     torqueEvent.ascii = 0;
     torqueEvent.action = action;
-    torqueEvent.fValue = fValue;
+    torqueEvent.fValues[0] = fValue;
     torqueEvent.ascii = chars;
     
     // Post the input event
@@ -441,7 +441,7 @@
     torqueEvent.modifier = modifiers;
     torqueEvent.ascii = 0;
     torqueEvent.action = SI_MOVE;
-    torqueEvent.fValue = deltaY;
+    torqueEvent.fValues[0] = deltaY;
     Game->postEvent(torqueEvent);
 }
 

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

@@ -786,7 +786,7 @@ void DInputDevice::syncKeyboardState()
 
 
    U8* keyBuffer = new U8[mObjBufferSize];
-   dMemset( keyBuffer, 0, sizeof( keyBuffer ) );
+   dMemset( keyBuffer, 0, sizeof( U8 ) * mObjBufferSize );
    HRESULT result = mDevice->GetDeviceState( mObjBufferSize, keyBuffer );
    if ( SUCCEEDED( result ) )
    {
@@ -917,7 +917,7 @@ bool DInputDevice::buildEvent( DWORD offset, S32 newData, S32 oldData )
          newEvent.action = SI_MOVE;
          if ( newEvent.deviceType == MouseDeviceType )
          {
-            newEvent.fValue = float( newData );
+            newEvent.fValues[0] = float( newData );
 
          }
          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 )
             {
                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
-               newEvent.fValue = (F32)newData;
+               newEvent.fValues[0] = (F32)newData;
          }
 
          Game->postEvent( newEvent );
@@ -938,14 +938,14 @@ bool DInputDevice::buildEvent( DWORD offset, S32 newData, S32 oldData )
 
       case SI_BUTTON:
          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 );
          break;
 
       case SI_KEY:
          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 );
 
          Game->postEvent( newEvent );
@@ -975,7 +975,7 @@ bool DInputDevice::buildEvent( DWORD offset, S32 newData, S32 oldData )
             if ( clearkeys )
             {
                newEvent.action = SI_BREAK;
-               newEvent.fValue = 0.0f;
+               newEvent.fValues[0] = 0.0f;
                // post events for all buttons that need to be cleared.
                if( clearkeys & POV_up)
                {
@@ -1006,7 +1006,7 @@ bool DInputDevice::buildEvent( DWORD offset, S32 newData, S32 oldData )
             if ( setkeys )
             {
                newEvent.action = SI_MAKE;
-               newEvent.fValue = 1.0f;
+               newEvent.fValues[0] = 1.0f;
                // post events for all buttons that need to be set.
                if( setkeys & POV_up)
                {
@@ -1040,6 +1040,99 @@ bool DInputDevice::buildEvent( DWORD offset, S32 newData, S32 oldData )
    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

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

@@ -36,9 +36,9 @@
 
 //Luma: Adding this for direct input header
 #define DIRECTINPUT_VERSION  0x0800
-
 #include <dinput.h>
 
+
 class DInputDevice : public InputDevice
 {
    public:
@@ -74,6 +74,11 @@ class DInputDevice : public InputDevice
       bool                 mAcquired;
       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;
       DIOBJECTDATAFORMAT*     mObjFormat;
@@ -124,6 +129,9 @@ class DInputDevice : public InputDevice
       const char* getName();
       const char* getProductName();
 
+	   // Constant Effect Force Feedback
+      void rumble( float x, float y );
+
       // Console interface functions:
       const char* getJoystickAxesString();
       static bool joystickDetected();

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

@@ -27,12 +27,16 @@
 #include "platform/event.h"
 #include "console/console.h"
 #include "console/consoleTypes.h"
+#include "input/actionMap.h"
+#include "game/gameInterface.h"
+#include <Xinput.h>
 
 //------------------------------------------------------------------------------
 // Static class variables:
 bool DInputManager::smKeyboardEnabled = true;
 bool DInputManager::smMouseEnabled = false;
 bool DInputManager::smJoystickEnabled = false;
+bool DInputManager::smXInputEnabled = false;
 
 // Type definitions:
 typedef HRESULT (WINAPI* FN_DirectInputCreate)(HINSTANCE hinst, DWORD dwVersion, REFIID riidltf, LPVOID *ppvOut, LPUNKNOWN punkOuter);
@@ -44,6 +48,10 @@ DInputManager::DInputManager()
    mDInputLib        = NULL;
    mDInputInterface  = NULL;
    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::MouseEnabled",     TypeBool, &smMouseEnabled );
    Con::addVariable( "pref::Input::JoystickEnabled",  TypeBool, &smJoystickEnabled );
+   Con::addVariable( "pref::Input::XInputEnabled", TypeBool, &smXInputEnabled );
 }
 
 //------------------------------------------------------------------------------
@@ -60,6 +69,27 @@ bool DInputManager::enable()
    FN_DirectInputCreate fnDInputCreate;
 
    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") );
    if ( mDInputLib )
    {
@@ -117,6 +147,20 @@ void DInputManager::disable()
       mDInputLib = NULL;
    }
 
+   if ( mfnXInputGetState )
+   {
+      mXInputStateReset = true;
+      mfnXInputGetState = NULL;
+      mfnXInputSetState = NULL;
+   }
+
+   if ( mXInputLib )
+   {
+      FreeLibrary( mXInputLib );
+      mXInputLib = NULL;
+   }
+
+
    mEnabled = false;
 }
 
@@ -180,6 +224,9 @@ void DInputManager::unacquire( U8 deviceType, U8 deviceID )
 //------------------------------------------------------------------------------
 void DInputManager::process()
 {
+	if ( isXInputActive() )
+		processXInput();
+
    DInputDevice* dptr;
    for ( iterator ptr = begin(); ptr != end(); ptr++ )
    {
@@ -194,7 +241,6 @@ void DInputManager::enumerateDevices()
 {
    if ( mDInputInterface )
    {
-
       DInputDevice::init();
       DInputDevice::smDInputInterface = mDInputInterface;
       mDInputInterface->EnumDevices( DI8DEVTYPE_KEYBOARD, EnumDevicesProc, this, DIEDFL_ATTACHEDONLY );
@@ -424,4 +470,383 @@ const char* DInputManager::getJoystickAxesString( U32 deviceID )
    }
 
    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
 #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;
 
@@ -44,16 +59,32 @@ class DInputManager : public InputManager
    private:
       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;
       LPDIRECTINPUT8 mDInputInterface;
 
       static bool smKeyboardEnabled;
       static bool smMouseEnabled;
       static bool smJoystickEnabled;
+	  static bool smXInputEnabled;
 
       bool mKeyboardActive;
       bool mMouseActive;
       bool mJoystickActive;
+	  bool mXInputActive;
 
       void  enumerateDevices();
 
@@ -62,6 +93,13 @@ class DInputManager : public InputManager
       bool acquire( 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:
       DInputManager();
 
@@ -98,8 +136,20 @@ class DInputManager : public InputManager
       void deactivateJoystick();
       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:
       const char* getJoystickAxesString( U32 deviceID );
+
+	  bool rumble( const char *pDeviceName, float x, float y );
 };
 
 #endif  // _H_WINDIRECTINPUT_

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

@@ -495,6 +495,10 @@ void Input::echoInputState()
 		Con::printf( "- Joystick is %sabled and %sactive.",
             mgr->isJoystickEnabled() ? "en" : "dis",
             mgr->isJoystickActive() ? "" : "in" );
+
+		Con::printf( "- Xinput is %sabled and %sactive.",
+			mgr->isXInputEnabled() ? "en" : "dis",
+			mgr->isXInputActive() ? "" : "in" );
    }
    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.modifier   = modKey;
    event.ascii      = asciiCode;
-   event.fValue     = make ? 1.0f : 0.0f;
+   event.fValues[0]     = make ? 1.0f : 0.0f;
 
    // Store the current modifier keys
    Input::setModifierKeys(modifierKeys);
@@ -547,7 +547,7 @@ static void CheckCursorPos()
          event.action = SI_MOVE;
          event.modifier = modifierKeys;
          event.ascii = 0;
-         event.fValue = (F32)(mousePos.x - centerX);
+         event.fValues[0] = (F32)(mousePos.x - centerX);
          Game->postEvent(event);
       }
 
@@ -562,7 +562,7 @@ static void CheckCursorPos()
          event.action = SI_MOVE;
          event.modifier = modifierKeys;
          event.ascii = 0;
-         event.fValue = (F32)(mousePos.y - centerY);
+         event.fValues[0] = (F32)(mousePos.y - centerY);
          Game->postEvent(event);
       }
 
@@ -596,7 +596,7 @@ static void mouseButtonEvent(S32 action, S32 objInst)
    event.action = action;
    event.modifier = modifierKeys;
    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);
 }
@@ -619,7 +619,7 @@ static void mouseWheelEvent( S32 delta )
       event.action = SI_MOVE;
       event.modifier = modifierKeys;
       event.ascii = 0;
-      event.fValue = (F32)delta;
+      event.fValues[0] = (F32)delta;
 
       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->mSuperClassName = mSuperClassName;
-
+   object->mNameSpace = NULL;
    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