Browse Source

Leap Motion input device support

DavidWyand-GG 12 years ago
parent
commit
6105849df2

+ 34 - 0
Engine/source/platform/input/leapMotion/leapMotionConstants.h

@@ -0,0 +1,34 @@
+//-----------------------------------------------------------------------------
+// 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_

+ 353 - 0
Engine/source/platform/input/leapMotion/leapMotionData.cpp

@@ -0,0 +1,353 @@
+//-----------------------------------------------------------------------------
+// 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/input/leapMotion/leapMotionData.h"
+#include "platform/input/leapMotion/leapMotionUtil.h"
+
+LeapMotionDeviceData::LeapMotionDeviceData()
+{
+   reset();
+}
+
+void LeapMotionDeviceData::reset()
+{
+   mDataSet = false;
+
+   mIsValid = false;
+
+   mHasTrackingData = false;
+
+   for(U32 i=0; i<LeapMotionConstants::MaxHands; ++i)
+   {
+      mHandValid[i] = false;
+
+      for(U32 j=0; j<LeapMotionConstants::MaxPointablesPerHand; ++j)
+      {
+         mPointableValid[i][j] = false;
+      }
+   }
+}
+
+void LeapMotionDeviceData::setData(const Leap::Frame& frame, LeapMotionDeviceData* prevData, bool keepHandIndexPersistent, bool keepPointableIndexPersistent, const F32& maxHandAxisRadius)
+{
+   mIsValid = frame.isValid();
+   if(!mIsValid)
+      return;
+
+   // Determine if there is any valid tracking data
+   mHasTrackingData = frame.hands().count() > 0 || frame.pointables().count() > 0;
+
+   const Leap::HandList hands = frame.hands();
+
+   // Check if the hand index needs to persist between frames, but only if the
+   // previous data is valid
+   if(keepHandIndexPersistent && prevData && prevData->mDataSet && prevData->mIsValid)
+   {
+      processPersistentHands(frame.hands(), keepPointableIndexPersistent, prevData);
+   }
+   else
+   {
+      processHands(frame.hands());
+   }
+
+   // Single hand rotation as axis
+   if(mHandValid[0])
+   {
+      Point2F axis;
+      LeapMotionUtil::calculateHandAxisRotation(mHandRot[0], maxHandAxisRadius, axis);
+
+      mHandRotAxis[0] = axis.x;
+      mHandRotAxis[1] = axis.y;
+   }
+   else
+   {
+      // The first hand is not valid so we reset the axis rotation to none
+      mHandRotAxis[0] = 0.0f;
+      mHandRotAxis[1] = 0.0f;
+   }
+
+   // Store the current sequence number
+   mSequenceNum = frame.id();
+
+   mDataSet = true;
+}
+
+void LeapMotionDeviceData::processPersistentHands(const Leap::HandList& hands, bool keepPointableIndexPersistent, LeapMotionDeviceData* prevData)
+{
+   S32 numHands = hands.count();
+
+   static S32 handDataIndex[LeapMotionConstants::MaxHands];
+   static bool handIndexUsed[LeapMotionConstants::MaxHands];
+   static Vector<S32> frameHandFound;
+
+   // Clear out our lookup arrays
+   for(U32 i=0; i<LeapMotionConstants::MaxHands; ++i)
+   {
+      handDataIndex[i] = -1;
+      handIndexUsed[i] = false;
+   }
+   frameHandFound.setSize(numHands);
+   for(U32 i=0; i<numHands; ++i)
+   {
+      frameHandFound[i] = -1;
+   }
+
+   // Check if any hands this frame were picked up last frame
+   for(U32 i=0; i<numHands; ++i)
+   {
+      const Leap::Hand& hand = hands[i];
+      for(U32 j=0; j<LeapMotionConstants::MaxHands; ++j)
+      {
+         if(prevData && prevData->mHandValid[j] && hand.id() == prevData->mHandID[j])
+         {
+            handDataIndex[j] = i;
+            frameHandFound[i] = j;
+         }
+      }
+   }
+
+   // Process all hands that were present in the last frame
+   for(U32 i=0; i<numHands; ++i)
+   {
+      if(frameHandFound[i] != -1)
+      {
+         processHand(hands[i], frameHandFound[i], keepPointableIndexPersistent, prevData);
+         handIndexUsed[frameHandFound[i]] = true;
+      }
+   }
+
+   // Process all hands that were not present in the last frame
+   for(U32 i=0; i<numHands; ++i)
+   {
+      if(frameHandFound[i] != -1)
+         continue;
+
+      // Find the first hand data that has not yet been used
+      for(U32 j=0; j<LeapMotionConstants::MaxHands; ++j)
+      {
+         if(!handIndexUsed[j])
+         {
+            // Process this hand
+            processHand(hands[i], j, keepPointableIndexPersistent, prevData);
+            handIndexUsed[j] = true;
+            break;
+         }
+      }
+   }
+
+   // Finally, mark all hand data that has not been processed this frame as invalid
+   for(U32 i=0; i<LeapMotionConstants::MaxHands; ++i)
+   {
+      if(!handIndexUsed[i])
+      {
+         mHandValid[i] = false;
+      }
+   }
+}
+
+void LeapMotionDeviceData::processHands(const Leap::HandList& hands)
+{
+   S32 numHands = hands.count();
+
+   // Process all valid hands
+   S32 handsToProcess = getMin(numHands, LeapMotionConstants::MaxHands);
+   for(U32 i=0; i<handsToProcess; ++i)
+   {
+      processHand(hands[i], i, false, NULL);
+   }
+
+   // Take care of any hands that do not exist this frame
+   for(U32 i=handsToProcess; i<LeapMotionConstants::MaxHands; ++i)
+   {
+      mHandValid[i] = false;
+   }
+}
+
+void LeapMotionDeviceData::processHand(const Leap::Hand& hand, U32 handIndex, bool keepPointableIndexPersistent, LeapMotionDeviceData* prevData)
+{
+   mHandValid[handIndex] = true;
+
+   mHandID[handIndex] = hand.id();
+
+   // Set the hand position
+   LeapMotionUtil::convertPosition(hand.palmPosition(), mHandRawPos[handIndex][0], mHandRawPos[handIndex][1], mHandRawPos[handIndex][2]);
+   mHandPos[handIndex][0] = (S32)mFloor(mHandRawPos[handIndex][0]);
+   mHandPos[handIndex][1] = (S32)mFloor(mHandRawPos[handIndex][1]);
+   mHandPos[handIndex][2] = (S32)mFloor(mHandRawPos[handIndex][2]);
+
+   mHandPosPoint[handIndex].set(mHandPos[handIndex][0], mHandPos[handIndex][1], mHandPos[handIndex][2]);
+
+   // Set the hand rotation
+   LeapMotionUtil::convertHandRotation(hand, mHandRot[handIndex]);
+   mHandRotQuat[handIndex].set(mHandRot[handIndex]);
+
+   // Process the pointables associated with this hand
+   if(keepPointableIndexPersistent)
+   {
+      processPersistentHandPointables(hand.pointables(), handIndex, prevData);
+   }
+   else
+   {
+      processHandPointables(hand.pointables(), handIndex);
+   }
+}
+
+void LeapMotionDeviceData::processPersistentHandPointables(const Leap::PointableList& pointables, U32 handIndex, LeapMotionDeviceData* prevData)
+{
+   S32 numPointables = pointables.count();
+
+   static S32 pointableDataIndex[LeapMotionConstants::MaxPointablesPerHand];
+   static bool pointableIndexUsed[LeapMotionConstants::MaxPointablesPerHand];
+   static Vector<S32> framePointableFound;
+
+   // Clear out our lookup arrays
+   for(U32 i=0; i<LeapMotionConstants::MaxPointablesPerHand; ++i)
+   {
+      pointableDataIndex[i] = -1;
+      pointableIndexUsed[i] = false;
+   }
+   framePointableFound.setSize(numPointables);
+   for(U32 i=0; i<numPointables; ++i)
+   {
+      framePointableFound[i] = -1;
+   }
+
+   // Check if any pointables for this hand during this frame were picked
+   // up last frame
+   for(U32 i=0; i<numPointables; ++i)
+   {
+      const Leap::Pointable& pointable = pointables[i];
+      for(U32 j=0; j<LeapMotionConstants::MaxPointablesPerHand; ++j)
+      {
+         if(prevData && prevData->mPointableValid[handIndex][j] && pointable.id() == prevData->mPointableID[handIndex][j])
+         {
+            pointableDataIndex[j] = i;
+            framePointableFound[i] = j;
+         }
+      }
+   }
+
+   // Process all hand pointables that were present in the last frame
+   for(U32 i=0; i<numPointables; ++i)
+   {
+      if(framePointableFound[i] != -1)
+      {
+         processHandPointable(pointables[i], handIndex, framePointableFound[i]);
+         pointableIndexUsed[framePointableFound[i]] = true;
+      }
+   }
+
+   // Process all hand pointables that were not present in the last frame
+   for(U32 i=0; i<numPointables; ++i)
+   {
+      if(framePointableFound[i] != -1)
+         continue;
+
+      // Find the first hand pointable data that has not yet been used
+      for(U32 j=0; j<LeapMotionConstants::MaxPointablesPerHand; ++j)
+      {
+         if(!pointableIndexUsed[j])
+         {
+            // Process the pointable
+            processHandPointable(pointables[i], handIndex, j);
+            pointableIndexUsed[j] = true;
+            break;
+         }
+      }
+   }
+
+   // Finally, mark all hand pointable data that has not been process this frame as invalid
+   for(U32 i=0; i<LeapMotionConstants::MaxPointablesPerHand; ++i)
+   {
+      if(!pointableIndexUsed[i])
+      {
+         mPointableValid[handIndex][i] = false;
+      }
+   }
+}
+
+void LeapMotionDeviceData::processHandPointables(const Leap::PointableList& pointables, U32 handIndex)
+{
+   // Process all pointables attached to the hand
+   S32 numPointables = pointables.count();
+   S32 pointablesToProcess = getMin(numPointables, LeapMotionConstants::MaxPointablesPerHand);
+   for(U32 i=0; i<pointablesToProcess; ++i)
+   {
+      processHandPointable(pointables[i], handIndex, i);
+   }
+
+   // Take care of any pointables that do not exist this frame
+   for(U32 i=pointablesToProcess; i<LeapMotionConstants::MaxPointablesPerHand; ++i)
+   {
+      mPointableValid[handIndex][i] = false;
+   }
+}
+
+void LeapMotionDeviceData::processHandPointable(const Leap::Pointable& pointable, U32 handIndex, U32 handPointableIndex)
+{
+   mPointableValid[handIndex][handPointableIndex] = true;
+
+   mPointableID[handIndex][handPointableIndex] = pointable.id();
+   mPointableLength[handIndex][handPointableIndex] = pointable.length();
+   mPointableWidth[handIndex][handPointableIndex] = pointable.width();
+
+   // Set the pointable position
+   LeapMotionUtil::convertPosition(pointable.tipPosition(), mPointableRawPos[handIndex][handPointableIndex][0], mPointableRawPos[handIndex][handPointableIndex][1], mPointableRawPos[handIndex][handPointableIndex][2]);
+   mPointablePos[handIndex][handPointableIndex][0] = (S32)mFloor(mPointableRawPos[handIndex][handPointableIndex][0]);
+   mPointablePos[handIndex][handPointableIndex][1] = (S32)mFloor(mPointableRawPos[handIndex][handPointableIndex][1]);
+   mPointablePos[handIndex][handPointableIndex][2] = (S32)mFloor(mPointableRawPos[handIndex][handPointableIndex][2]);
+
+   mPointablePosPoint[handIndex][handPointableIndex].set(mPointablePos[handIndex][handPointableIndex][0], mPointablePos[handIndex][handPointableIndex][1], mPointablePos[handIndex][handPointableIndex][2]);
+
+   // Set the pointable rotation
+   LeapMotionUtil::convertPointableRotation(pointable, mPointableRot[handIndex][handPointableIndex]);
+   mPointableRotQuat[handIndex][handPointableIndex].set(mPointableRot[handIndex][handPointableIndex]);
+}
+
+U32 LeapMotionDeviceData::compare(LeapMotionDeviceData* other)
+{
+   S32 result = DIFF_NONE;
+
+   // Check hand rotation as axis
+   if(mHandRotAxis[0] != other->mHandRotAxis[0] || !mDataSet)
+   {
+      result |= DIFF_HANDROTAXISX;
+   }
+   if(mHandRotAxis[1] != other->mHandRotAxis[1] || !mDataSet)
+   {
+      result |= DIFF_HANDROTAXISY;
+   }
+
+   return result;
+}
+
+U32 LeapMotionDeviceData::compareMeta(LeapMotionDeviceData* other)
+{
+   S32 result = DIFF_NONE;
+
+   if(mHasTrackingData != other->mHasTrackingData || !mDataSet)
+   {
+      result |= METADIFF_FRAME_VALID_DATA;
+   }
+
+   return result;
+}

+ 113 - 0
Engine/source/platform/input/leapMotion/leapMotionData.h

@@ -0,0 +1,113 @@
+//-----------------------------------------------------------------------------
+// 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 _LEAPMOTIONDATA_H_
+#define _LEAPMOTIONDATA_H_
+
+#include "console/consoleTypes.h"
+#include "math/mMathFn.h"
+#include "math/mMatrix.h"
+#include "math/mQuat.h"
+#include "platform/input/leapMotion/leapMotionConstants.h"
+#include "Leap.h"
+
+struct LeapMotionDeviceData
+{
+   enum DataDifferences {
+      DIFF_NONE            = 0,
+      DIFF_HANDROTAXISX    = (1<<1),
+      DIFF_HANDROTAXISY    = (1<<2),
+
+      DIFF_HANDROTAXIS = (DIFF_HANDROTAXISX | DIFF_HANDROTAXISY),
+   };
+
+   enum MetaDataDifferences {
+      METADIFF_NONE              = 0,
+      METADIFF_FRAME_VALID_DATA  = (1<<0),
+   };
+
+protected:
+   void processPersistentHands(const Leap::HandList& hands, bool keepPointableIndexPersistent, LeapMotionDeviceData* prevData);
+   void processHands(const Leap::HandList& hands);
+   void processHand(const Leap::Hand& hand, U32 handIndex, bool keepPointableIndexPersistent, LeapMotionDeviceData* prevData);
+
+   void processPersistentHandPointables(const Leap::PointableList& pointables, U32 handIndex, LeapMotionDeviceData* prevData);
+   void processHandPointables(const Leap::PointableList& pointables, U32 handIndex);
+   void processHandPointable(const Leap::Pointable& pointable, U32 handIndex, U32 handPointableIndex);
+
+public:
+   bool mDataSet;
+
+   // Frame Data Set
+   bool mIsValid;
+   bool mHasTrackingData;
+
+   // Hand Data Set
+   bool mHandValid[LeapMotionConstants::MaxHands];
+   S32 mHandID[LeapMotionConstants::MaxHands];
+
+   // Hand Position
+   F32 mHandRawPos[LeapMotionConstants::MaxHands][3];
+   S32 mHandPos[LeapMotionConstants::MaxHands][3];
+   Point3F mHandPosPoint[LeapMotionConstants::MaxHands];
+
+   // Hand Rotation
+   MatrixF mHandRot[LeapMotionConstants::MaxHands];
+   QuatF mHandRotQuat[LeapMotionConstants::MaxHands];
+
+   // Hand rotation as axis x, y
+   F32 mHandRotAxis[2];
+
+   // Pointable Data Set
+   bool mPointableValid[LeapMotionConstants::MaxHands][LeapMotionConstants::MaxPointablesPerHand];
+   S32 mPointableID[LeapMotionConstants::MaxHands][LeapMotionConstants::MaxPointablesPerHand];
+   F32 mPointableLength[LeapMotionConstants::MaxHands][LeapMotionConstants::MaxPointablesPerHand];
+   F32 mPointableWidth[LeapMotionConstants::MaxHands][LeapMotionConstants::MaxPointablesPerHand];
+
+   // Pointable Position
+   F32 mPointableRawPos[LeapMotionConstants::MaxHands][LeapMotionConstants::MaxPointablesPerHand][3];
+   S32 mPointablePos[LeapMotionConstants::MaxHands][LeapMotionConstants::MaxPointablesPerHand][3];
+   Point3F mPointablePosPoint[LeapMotionConstants::MaxHands][LeapMotionConstants::MaxPointablesPerHand];
+
+   // Pointable Rotation
+   MatrixF mPointableRot[LeapMotionConstants::MaxHands][LeapMotionConstants::MaxPointablesPerHand];
+   QuatF mPointableRotQuat[LeapMotionConstants::MaxHands][LeapMotionConstants::MaxPointablesPerHand];
+
+   // Sequence number from device
+   U64 mSequenceNum;
+
+   LeapMotionDeviceData();
+
+   /// Reset device data
+   void reset();
+
+   /// Set data based on Leap Motion device data
+   void setData(const Leap::Frame& frame, LeapMotionDeviceData* prevData, bool keepHandIndexPersistent, bool keepPointableIndexPersistent, const F32& maxHandAxisRadius);
+
+   /// Compare this data and given and return differences
+   U32 compare(LeapMotionDeviceData* other);
+
+   /// Compare meta data between this and given and return differences
+   U32 compareMeta(LeapMotionDeviceData* other);
+};
+
+#endif   // _LEAPMOTIONDATA_H_

+ 364 - 0
Engine/source/platform/input/leapMotion/leapMotionDevice.cpp

@@ -0,0 +1,364 @@
+//-----------------------------------------------------------------------------
+// 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/input/leapMotion/leapMotionDevice.h"
+#include "platform/input/leapMotion/leapMotionData.h"
+#include "platform/input/leapMotion/leapMotionFrameStore.h"
+#include "platform/platformInput.h"
+#include "core/module.h"
+#include "platform/threads/mutex.h"
+#include "console/engineAPI.h"
+
+MODULE_BEGIN( LeapMotionDevice )
+
+   MODULE_INIT_AFTER( InputEventManager )
+   MODULE_SHUTDOWN_BEFORE( InputEventManager )
+
+   MODULE_INIT
+   {
+      LeapMotionDevice::staticInit();
+      ManagedSingleton< LeapMotionDevice >::createSingleton();
+      if(LeapMotionDevice::smEnableDevice)
+      {
+         LEAPMOTIONDEV->enable();
+      }
+
+      // Register the device with the Input Event Manager
+      INPUTMGR->registerDevice(LEAPMOTIONDEV);
+   }
+   
+   MODULE_SHUTDOWN
+   {
+      INPUTMGR->unregisterDevice(LEAPMOTIONDEV);
+      ManagedSingleton< LeapMotionDevice >::deleteSingleton();
+   }
+
+MODULE_END;
+
+//-----------------------------------------------------------------------------
+// LeapMotionDevice
+//-----------------------------------------------------------------------------
+
+bool LeapMotionDevice::smEnableDevice = true;
+
+bool LeapMotionDevice::smGenerateIndividualEvents = true;
+bool LeapMotionDevice::smKeepHandIndexPersistent = false;
+bool LeapMotionDevice::smKeepPointableIndexPersistent = false;
+
+bool LeapMotionDevice::smGenerateSingleHandRotationAsAxisEvents = false;
+
+F32 LeapMotionDevice::smMaximumHandAxisAngle = 25.0f;
+
+bool LeapMotionDevice::smGenerateWholeFrameEvents = false;
+
+U32 LeapMotionDevice::LM_FRAMEVALIDDATA = 0;
+U32 LeapMotionDevice::LM_HAND[LeapMotionConstants::MaxHands] = {0};
+U32 LeapMotionDevice::LM_HANDROT[LeapMotionConstants::MaxHands] = {0};
+U32 LeapMotionDevice::LM_HANDAXISX = 0;
+U32 LeapMotionDevice::LM_HANDAXISY = 0;
+U32 LeapMotionDevice::LM_HANDPOINTABLE[LeapMotionConstants::MaxHands][LeapMotionConstants::MaxPointablesPerHand] = {0};
+U32 LeapMotionDevice::LM_HANDPOINTABLEROT[LeapMotionConstants::MaxHands][LeapMotionConstants::MaxPointablesPerHand] = {0};
+U32 LeapMotionDevice::LM_FRAME = 0;
+
+LeapMotionDevice::LeapMotionDevice()
+{
+   // From IInputDevice
+   dStrcpy(mName, "leapmotion");
+   mDeviceType = INPUTMGR->getNextDeviceType();
+
+   mController = NULL;
+   mListener = NULL;
+   mActiveMutex = Mutex::createMutex();
+
+   //
+   mEnabled = false;
+   mActive = false;
+
+   for(U32 i=0; i<2; ++i)
+   {
+      mDataBuffer[i] = new LeapMotionDeviceData();
+   }
+   mPrevData = mDataBuffer[0];
+
+   buildCodeTable();
+}
+
+LeapMotionDevice::~LeapMotionDevice()
+{
+   disable();
+
+   Mutex::destroyMutex(mActiveMutex);
+}
+
+void LeapMotionDevice::staticInit()
+{
+   Con::addVariable("pref::LeapMotion::EnableDevice", TypeBool, &smEnableDevice, 
+      "@brief If true, the Leap Motion device will be enabled, if present.\n\n"
+	   "@ingroup Game");
+
+   Con::addVariable("LeapMotion::GenerateIndividualEvents", TypeBool, &smGenerateIndividualEvents, 
+      "@brief Indicates that events for each hand and pointable will be created.\n\n"
+	   "@ingroup Game");
+   Con::addVariable("LeapMotion::KeepHandIndexPersistent", TypeBool, &smKeepHandIndexPersistent, 
+      "@brief Indicates that we track hand IDs and will ensure that the same hand will remain at the same index between frames.\n\n"
+	   "@ingroup Game");
+   Con::addVariable("LeapMotion::KeepPointableIndexPersistent", TypeBool, &smKeepPointableIndexPersistent, 
+      "@brief Indicates that we track pointable IDs and will ensure that the same pointable will remain at the same index between frames.\n\n"
+	   "@ingroup Game");
+
+   Con::addVariable("LeapMotion::GenerateSingleHandRotationAsAxisEvents", TypeBool, &smGenerateSingleHandRotationAsAxisEvents, 
+      "@brief If true, broadcast single hand rotation as axis events.\n\n"
+	   "@ingroup Game");
+   Con::addVariable("LeapMotion::MaximumHandAxisAngle", TypeF32, &smMaximumHandAxisAngle, 
+      "@brief The maximum hand angle when used as an axis event as measured from a vector pointing straight up (in degrees).\n\n"
+      "Shoud range from 0 to 90 degrees.\n\n"
+	   "@ingroup Game");
+
+   Con::addVariable("LeapMotion::GenerateWholeFrameEvents", TypeBool, &smGenerateWholeFrameEvents, 
+      "@brief Indicates that a whole frame event should be generated and frames should be buffered.\n\n"
+	   "@ingroup Game");
+}
+
+void LeapMotionDevice::buildCodeTable()
+{
+   // Obtain all of the device codes
+   LM_FRAMEVALIDDATA = INPUTMGR->getNextDeviceCode();
+
+   for(U32 i=0; i<LeapMotionConstants::MaxHands; ++i)
+   {
+      // Hands
+      LM_HAND[i] = INPUTMGR->getNextDeviceCode();
+      LM_HANDROT[i] = INPUTMGR->getNextDeviceCode();
+
+      // Pointables per hand
+      for(U32 j=0; j<LeapMotionConstants::MaxPointablesPerHand; ++j)
+      {
+         LM_HANDPOINTABLE[i][j] = INPUTMGR->getNextDeviceCode();
+         LM_HANDPOINTABLEROT[i][j] = INPUTMGR->getNextDeviceCode();
+      }
+   }
+
+   LM_HANDAXISX = INPUTMGR->getNextDeviceCode();
+   LM_HANDAXISY = INPUTMGR->getNextDeviceCode();
+
+   LM_FRAME = INPUTMGR->getNextDeviceCode();
+
+   // Build out the virtual map
+   AddInputVirtualMap(  lm_framevaliddata,      SI_BUTTON,     LM_FRAMEVALIDDATA );
+
+   char buffer[64];
+   for(U32 i=0; i<LeapMotionConstants::MaxHands; ++i)
+   {
+      // Hands
+      dSprintf(buffer, 64, "lm_hand%d", i+1);
+      INPUTMGR->addVirtualMap( buffer, SI_POS, LM_HAND[i] );
+      dSprintf(buffer, 64, "lm_hand%drot", i+1);
+      INPUTMGR->addVirtualMap( buffer, SI_ROT, LM_HANDROT[i] );
+
+      // Pointables per hand
+      for(U32 j=0; j<LeapMotionConstants::MaxPointablesPerHand; ++j)
+      {
+         dSprintf(buffer, 64, "lm_hand%dpoint%d", i+1, j+1);
+         INPUTMGR->addVirtualMap( buffer, SI_POS, LM_HANDPOINTABLE[i][j] );
+         dSprintf(buffer, 64, "lm_hand%dpoint%drot", i+1, j+1);
+         INPUTMGR->addVirtualMap( buffer, SI_POS, LM_HANDPOINTABLEROT[i][j] );
+      }
+   }
+
+   AddInputVirtualMap(  lm_handaxisx,  SI_AXIS,    LM_HANDAXISX );
+   AddInputVirtualMap(  lm_handaxisy,  SI_AXIS,    LM_HANDAXISY );
+
+   AddInputVirtualMap(  lm_frame,      SI_INT,     LM_FRAME );
+}
+
+bool LeapMotionDevice::enable()
+{
+   // Start off with disabling the device if it is already enabled
+   disable();
+
+   // Create the controller to talk with the Leap Motion along with the listener
+   mListener = new MotionListener();
+   mController = new Leap::Controller(*mListener);
+
+   // The device is now enabled but not yet ready to be used
+   mEnabled = true;
+
+   return false;
+}
+
+void LeapMotionDevice::disable()
+{
+   if(mController)
+   {
+      delete mController;
+      mController = NULL;
+
+      if(mListener)
+      {
+         delete mListener;
+         mListener = NULL;
+      }
+   }
+
+   setActive(false);
+   mEnabled = false;
+}
+
+bool LeapMotionDevice::getActive()
+{
+   Mutex::lockMutex(mActiveMutex);
+   bool active = mActive;
+   Mutex::unlockMutex(mActiveMutex);
+
+   return active;
+}
+
+void LeapMotionDevice::setActive(bool state)
+{
+   Mutex::lockMutex(mActiveMutex);
+   mActive = state;
+   Mutex::unlockMutex(mActiveMutex);
+}
+
+bool LeapMotionDevice::process()
+{
+   if(!mEnabled)
+      return false;
+
+   if(!getActive())
+      return false;
+
+   //Con::printf("LeapMotionDevice::process()");
+
+   //Build the maximum hand axis angle to be passed into the LeapMotionDeviceData::setData()
+   F32 maxHandAxisRadius = mSin(mDegToRad(smMaximumHandAxisAngle));
+
+   // Get a frame of data
+   const Leap::Frame frame = mController->frame();
+
+   //const Leap::HandList hands = frame.hands();
+   //Con::printf("Frame: %lld  Hands: %d  Fingers: %d  Tools: %d", (long long)frame.id(), hands.count(), frame.fingers().count(), frame.tools().count());
+
+   // Store the current data
+   LeapMotionDeviceData* currentBuffer = (mPrevData == mDataBuffer[0]) ? mDataBuffer[1] : mDataBuffer[0];
+   currentBuffer->setData(frame, mPrevData, smKeepHandIndexPersistent, smKeepPointableIndexPersistent, maxHandAxisRadius);
+   U32 diff = mPrevData->compare(currentBuffer);
+   U32 metaDiff = mPrevData->compareMeta(currentBuffer);
+
+   // Update the previous data pointers.  We do this here in case someone calls our
+   // console functions during one of the input events below.
+   mPrevData = currentBuffer;
+
+   // Send out any meta data
+   if(metaDiff & LeapMotionDeviceData::METADIFF_FRAME_VALID_DATA)
+   {
+      // Frame valid change event
+      INPUTMGR->buildInputEvent(mDeviceType, DEFAULT_MOTION_UNIT, SI_BUTTON, LM_FRAMEVALIDDATA, currentBuffer->mHasTrackingData ? SI_MAKE : SI_BREAK, currentBuffer->mHasTrackingData ? 1.0f : 0.0f);
+   }
+
+   // Send out any valid data
+   if(currentBuffer->mDataSet && currentBuffer->mIsValid)
+   {
+      // Hands and their pointables
+      if(smGenerateIndividualEvents)
+      {
+         for(U32 i=0; i<LeapMotionConstants::MaxHands; ++i)
+         {
+            if(currentBuffer->mHandValid[i])
+            {
+               // Send out position
+               INPUTMGR->buildInputEvent(mDeviceType, DEFAULT_MOTION_UNIT, SI_POS, LM_HAND[i], SI_MOVE, currentBuffer->mHandPosPoint[i]);
+
+               // Send out rotation
+               INPUTMGR->buildInputEvent(mDeviceType, DEFAULT_MOTION_UNIT, SI_ROT, LM_HANDROT[i], SI_MOVE, currentBuffer->mHandRotQuat[i]);
+
+               // Pointables for hand
+               for(U32 j=0; j<LeapMotionConstants::MaxPointablesPerHand; ++j)
+               {
+                  if(currentBuffer->mPointableValid[i][j])
+                  {
+                     // Send out position
+                     INPUTMGR->buildInputEvent(mDeviceType, DEFAULT_MOTION_UNIT, SI_POS, LM_HANDPOINTABLE[i][j], SI_MOVE, currentBuffer->mPointablePosPoint[i][j]);
+
+                     // Send out rotation
+                     INPUTMGR->buildInputEvent(mDeviceType, DEFAULT_MOTION_UNIT, SI_ROT, LM_HANDPOINTABLEROT[i][j], SI_MOVE, currentBuffer->mPointableRotQuat[i][j]);
+                  }
+               }
+            }
+         }
+      }
+
+      // Single Hand as axis rotation
+      if(smGenerateSingleHandRotationAsAxisEvents && diff & LeapMotionDeviceData::DIFF_HANDROTAXIS)
+      {
+         if(diff & LeapMotionDeviceData::DIFF_HANDROTAXISX)
+            INPUTMGR->buildInputEvent(mDeviceType, DEFAULT_MOTION_UNIT, SI_AXIS, LM_HANDAXISX, SI_MOVE, currentBuffer->mHandRotAxis[0]);
+         if(diff & LeapMotionDeviceData::DIFF_HANDROTAXISY)
+            INPUTMGR->buildInputEvent(mDeviceType, DEFAULT_MOTION_UNIT, SI_AXIS, LM_HANDAXISY, SI_MOVE, currentBuffer->mHandRotAxis[1]);
+      }
+   }
+
+   // Send out whole frame event, but only if the special frame group is defined
+   if(smGenerateWholeFrameEvents && LeapMotionFrameStore::isFrameGroupDefined())
+   {
+      S32 id = LEAPMOTIONFS->generateNewFrame(frame, maxHandAxisRadius);
+      if(id != 0)
+      {
+         INPUTMGR->buildInputEvent(mDeviceType, DEFAULT_MOTION_UNIT, SI_INT, LM_FRAME, SI_VALUE, id);
+      }
+   }
+
+   return true;
+}
+
+//-----------------------------------------------------------------------------
+// LeapMotionDevice::MotionListener
+//-----------------------------------------------------------------------------
+
+void LeapMotionDevice::MotionListener::onConnect (const Leap::Controller &controller)
+{
+   LEAPMOTIONDEV->setActive(true);
+}
+
+void LeapMotionDevice::MotionListener::onDisconnect (const Leap::Controller &controller)
+{
+   LEAPMOTIONDEV->setActive(false);
+}
+//-----------------------------------------------------------------------------
+
+DefineEngineFunction(isLeapMotionActive, bool, (),,
+   "@brief Used to determine if the Leap Motion input device is active\n\n"
+
+   "The Leap Motion input device is considered active when the support library has been "
+   "loaded and the device has been found.\n\n"
+
+   "@return True if the Leap Motion input device is active.\n"
+
+   "@ingroup Game")
+{
+   if(!ManagedSingleton<LeapMotionDevice>::instanceOrNull())
+   {
+      return false;
+   }
+
+   return LEAPMOTIONDEV->getActive();
+}

+ 138 - 0
Engine/source/platform/input/leapMotion/leapMotionDevice.h

@@ -0,0 +1,138 @@
+//-----------------------------------------------------------------------------
+// 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 _LEAPMOTIONDEVICE_H_
+#define _LEAPMOTIONDEVICE_H_
+
+#include "platform/input/IInputDevice.h"
+#include "platform/input/event.h"
+#include "platformWin32/platformWin32.h"
+#include "core/util/tSingleton.h"
+#include "math/mQuat.h"
+#include "platform/input/leapMotion/leapMotionConstants.h"
+#include "Leap.h"
+
+#define DEFAULT_MOTION_UNIT 0
+
+struct LeapMotionDeviceData;
+
+class LeapMotionDevice : public IInputDevice
+{
+protected:
+   class MotionListener : public Leap::Listener
+   {
+   public:
+      MotionListener() {}
+      virtual ~MotionListener() {}
+
+      virtual void onConnect (const Leap::Controller &controller);
+      virtual void onDisconnect (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;
+
+   /// Buffer to store data Leap Motion data in a Torque friendly way
+   LeapMotionDeviceData*  mDataBuffer[2];
+
+   /// Points to the buffers that holds the previously collected data
+   LeapMotionDeviceData*  mPrevData;
+
+protected:
+   /// Build out the codes used for controller actions with the
+   /// Input Event Manager
+   void buildCodeTable();
+
+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:
+   LeapMotionDevice();
+   ~LeapMotionDevice();
+
+   static void staticInit();
+
+   bool enable();
+   void disable();
+
+   bool getActive();
+   void setActive(bool state);
+
+   bool process();
+
+public:
+   // For ManagedSingleton.
+   static const char* getSingletonName() { return "LeapMotionDevice"; }   
+};
+
+/// Returns the LeapMotionDevice singleton.
+#define LEAPMOTIONDEV ManagedSingleton<LeapMotionDevice>::instance()
+
+#endif   // _LEAPMOTIONDEVICE_H_

+ 497 - 0
Engine/source/platform/input/leapMotion/leapMotionFrame.cpp

@@ -0,0 +1,497 @@
+//-----------------------------------------------------------------------------
+// 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/input/leapMotion/leapMotionFrame.h"
+#include "platform/input/leapMotion/leapMotionUtil.h"
+#include "console/engineAPI.h"
+#include "math/mAngAxis.h"
+#include "math/mTransform.h"
+
+U32 LeapMotionFrame::smNextInternalFrameId = 0;
+
+IMPLEMENT_CONOBJECT(LeapMotionFrame);
+
+ImplementEnumType( LeapMotionFramePointableType,
+   "Leap Motion pointable type.\n\n")
+   { LeapMotionFrame::PT_UNKNOWN,   "Unknown",  "Unknown pointable type.\n" },
+   { LeapMotionFrame::PT_FINGER,    "Finger",   "Finger pointable type.\n" },
+   { LeapMotionFrame::PT_TOOL,      "Tool",     "Tool pointable type.\n"  },
+EndImplementEnumType;
+
+LeapMotionFrame::LeapMotionFrame()
+{
+   clear();
+}
+
+LeapMotionFrame::~LeapMotionFrame()
+{
+   clear();
+}
+
+
+void LeapMotionFrame::initPersistFields()
+{
+   Parent::initPersistFields();
+}
+
+bool LeapMotionFrame::onAdd()
+{
+   if (!Parent::onAdd())
+      return false;
+
+   return true;
+}
+
+void LeapMotionFrame::onRemove()
+{
+   Parent::onRemove();
+}
+
+void LeapMotionFrame::clear()
+{
+   mFrameValid = false;
+
+   mHandCount = 0;
+   mHandValid.clear();
+   mHandId.clear();
+   mHandRawPos.clear();
+   mHandPos.clear();
+   mHandRot.clear();
+   mHandRotQuat.clear();
+   mHandRotAxis.clear();
+   mHandPointablesCount.clear();
+
+   mPointableCount = 0;
+   mPointableValid.clear();
+   mPointableId.clear();
+   mPointableHandIndex.clear();
+   mPointableType.clear();
+   mPointableRawPos.clear();
+   mPointablePos.clear();
+   mPointableRot.clear();
+   mPointableRotQuat.clear();
+   mPointableLength.clear();
+   mPointableWidth.clear();
+}
+
+void LeapMotionFrame::copyFromFrame(const Leap::Frame& frame, const F32& maxHandAxisRadius)
+{
+   // This also resets all counters
+   clear();
+
+   // Retrieve frame information
+   mFrameValid = frame.isValid();
+   mFrameId = frame.id();
+   mFrameTimeStamp = frame.timestamp();
+
+   mFrameInternalId = smNextInternalFrameId;
+   ++smNextInternalFrameId;
+   mFrameSimTime = Sim::getCurrentTime();
+   mFrameRealTime = Platform::getRealMilliseconds();
+
+   if(!mFrameValid)
+   {
+      return;
+   }
+
+   // Retrieve hand information
+   mHandCount = frame.hands().count();
+   if(mHandCount > 0)
+   {
+      copyFromFrameHands(frame.hands(), maxHandAxisRadius);
+   }
+
+   // Retrieve pointable information
+   mPointableCount = frame.pointables().count();
+   if(mPointableCount > 0)
+   {
+      copyFromFramePointables(frame.pointables());
+   }
+}
+
+void LeapMotionFrame::copyFromFrameHands(const Leap::HandList& hands, const F32& maxHandAxisRadius)
+{
+   // Set up Vectors
+   mHandValid.increment(mHandCount);
+   mHandId.increment(mHandCount);
+   mHandRawPos.increment(mHandCount);
+   mHandPos.increment(mHandCount);
+   mHandRot.increment(mHandCount);
+   mHandRotQuat.increment(mHandCount);
+   mHandRotAxis.increment(mHandCount);
+   mHandPointablesCount.increment(mHandCount);
+
+   // Copy data
+   for(U32 i=0; i<mHandCount; ++i)
+   {
+      const Leap::Hand& hand = hands[i];
+
+      mHandValid[i] = hand.isValid();
+      mHandId[i] = hand.id();
+      
+      // Position
+      LeapMotionUtil::convertPosition(hand.palmPosition(), mHandRawPos[i]);
+      mHandPos[i].x = (S32)mFloor(mHandRawPos[i].x);
+      mHandPos[i].y = (S32)mFloor(mHandRawPos[i].y);
+      mHandPos[i].z = (S32)mFloor(mHandRawPos[i].z);
+
+      // Rotation
+      LeapMotionUtil::convertHandRotation(hand, mHandRot[i]);
+      mHandRotQuat[i].set(mHandRot[i]);
+
+      // Thumb stick axis rotation
+      LeapMotionUtil::calculateHandAxisRotation(mHandRot[i], maxHandAxisRadius, mHandRotAxis[i]);
+
+      // Pointables
+      mHandPointablesCount[i] = hand.pointables().count();
+   }
+}
+
+void LeapMotionFrame::copyFromFramePointables(const Leap::PointableList& pointables)
+{
+   // Set up Vectors
+   mPointableValid.increment(mPointableCount);
+   mPointableId.increment(mPointableCount);
+   mPointableHandIndex.increment(mPointableCount);
+   mPointableType.increment(mPointableCount);
+   mPointableRawPos.increment(mPointableCount);
+   mPointablePos.increment(mPointableCount);
+   mPointableRot.increment(mPointableCount);
+   mPointableRotQuat.increment(mPointableCount);
+   mPointableLength.increment(mPointableCount);
+   mPointableWidth.increment(mPointableCount);
+
+   // Copy data
+   for(U32 i=0; i<mPointableCount; ++i)
+   {
+      const Leap::Pointable& pointable = pointables[i];
+
+      mPointableValid[i] = pointable.isValid();
+      mPointableId[i] = pointable.id();
+      mPointableLength[i] = pointable.length();
+      mPointableWidth[i] = pointable.width();
+
+      mPointableType[i] = PT_UNKNOWN;
+      if(pointable.isFinger())
+      {
+         mPointableType[i] = PT_FINGER;
+      }
+      else if(pointable.isTool())
+      {
+         mPointableType[i] = PT_TOOL;
+      }
+
+      // Which hand, if any
+      const Leap::Hand& hand = pointable.hand();
+      if(hand.isValid())
+      {
+         bool found = false;
+         S32 handId = hand.id();
+         for(U32 j=0; j<mHandCount; ++j)
+         {
+            if(mHandId[j] == handId)
+            {
+               mPointableHandIndex[i] = j;
+               found = true;
+               break;
+            }
+         }
+
+         if(!found)
+         {
+            mPointableHandIndex[i] = -1;
+         }
+      }
+      else
+      {
+         mPointableHandIndex[i] = -1;
+      }
+
+      // Position
+      LeapMotionUtil::convertPosition(pointable.tipPosition(), mPointableRawPos[i]);
+      mPointablePos[i].x = (S32)mFloor(mPointableRawPos[i].x);
+      mPointablePos[i].y = (S32)mFloor(mPointableRawPos[i].y);
+      mPointablePos[i].z = (S32)mFloor(mPointableRawPos[i].z);
+
+      // Rotation
+      LeapMotionUtil::convertPointableRotation(pointable, mPointableRot[i]);
+      mPointableRotQuat[i].set(mPointableRot[i]);
+   }
+}
+
+//-----------------------------------------------------------------------------
+
+DefineEngineMethod( LeapMotionFrame, isFrameValid, bool, ( ),,
+   "@brief Checks if this frame is valid.\n\n"
+   "@return True if the frame is valid.\n\n")
+{
+   return object->isFrameValid();
+}
+
+DefineEngineMethod( LeapMotionFrame, getFrameInternalId, S32, ( ),,
+   "@brief Provides the internal ID for this frame.\n\n"
+   "@return Internal ID of this frame.\n\n")
+{
+   return object->getFrameInternalId();
+}
+
+DefineEngineMethod( LeapMotionFrame, getFrameSimTime, S32, ( ),,
+   "@brief Get the sim time that this frame was generated.\n\n"
+   "@return Sim time of this frame in milliseconds.\n\n")
+{
+   return object->getFrameSimTime();
+}
+
+DefineEngineMethod( LeapMotionFrame, getFrameRealTime, S32, ( ),,
+   "@brief Get the real time that this frame was generated.\n\n"
+   "@return Real time of this frame in milliseconds.\n\n")
+{
+   return object->getFrameRealTime();
+}
+
+DefineEngineMethod( LeapMotionFrame, getHandCount, S32, ( ),,
+   "@brief Get the number of hands defined in this frame.\n\n"
+   "@return The number of defined hands.\n\n")
+{
+   return object->getHandCount();
+}
+
+DefineEngineMethod( LeapMotionFrame, getHandValid, bool, ( S32 index ),,
+   "@brief Check if the requested hand is valid.\n\n"
+   "@param index The hand index to check.\n"
+   "@return True if the hand is valid.\n\n")
+{
+   return object->getHandValid(index);
+}
+
+DefineEngineMethod( LeapMotionFrame, getHandId, S32, ( S32 index ),,
+   "@brief Get the ID of the requested hand.\n\n"
+   "@param index The hand index to check.\n"
+   "@return ID of the requested hand.\n\n")
+{
+   return object->getHandId(index);
+}
+
+DefineEngineMethod( LeapMotionFrame, getHandRawPos, Point3F, ( S32 index ),,
+   "@brief Get the raw position of the requested hand.\n\n"
+   "The raw position is the hand's floating point position converted to "
+   "Torque 3D coordinates (in millimeters).\n"
+   "@param index The hand index to check.\n"
+   "@return Raw position of the requested hand.\n\n")
+{
+   return object->getHandRawPos(index);
+}
+
+DefineEngineMethod( LeapMotionFrame, getHandPos, Point3I, ( S32 index ),,
+   "@brief Get the position of the requested hand.\n\n"
+   "The position is the hand's integer position converted to "
+   "Torque 3D coordinates (in millimeters).\n"
+   "@param index The hand index to check.\n"
+   "@return Integer position of the requested hand (in millimeters).\n\n")
+{
+   return object->getHandPos(index);
+}
+
+DefineEngineMethod( LeapMotionFrame, getHandRot, AngAxisF, ( S32 index ),,
+   "@brief Get the rotation of the requested hand.\n\n"
+   "The Leap Motion hand rotation as converted into the Torque 3D"
+   "coordinate system.\n"
+   "@param index The hand index to check.\n"
+   "@return Rotation of the requested hand.\n\n")
+{
+   AngAxisF aa(object->getHandRot(index));
+   return aa;
+}
+
+DefineEngineMethod( LeapMotionFrame, getHandRawTransform, TransformF, ( S32 index ),,
+   "@brief Get the raw transform of the requested hand.\n\n"
+   "@param index The hand index to check.\n"
+   "@return The raw position and rotation of the requested hand (in Torque 3D coordinates).\n\n")
+{
+   const Point3F& pos = object->getHandRawPos(index);
+   const QuatF& qa = object->getHandRotQuat(index);
+
+   AngAxisF aa(qa);
+   aa.axis.normalize();
+
+   TransformF trans(pos, aa);
+   return trans;
+}
+
+DefineEngineMethod( LeapMotionFrame, getHandTransform, TransformF, ( S32 index ),,
+   "@brief Get the transform of the requested hand.\n\n"
+   "@param index The hand index to check.\n"
+   "@return The position and rotation of the requested hand (in Torque 3D coordinates).\n\n")
+{
+   const Point3I& pos = object->getHandPos(index);
+   const QuatF& qa = object->getHandRotQuat(index);
+
+   AngAxisF aa(qa);
+   aa.axis.normalize();
+
+   TransformF trans;
+   trans.mPosition = Point3F(pos.x, pos.y, pos.z);
+   trans.mOrientation = aa;
+
+   return trans;
+}
+
+DefineEngineMethod( LeapMotionFrame, getHandRotAxis, Point2F, ( S32 index ),,
+   "@brief Get the axis rotation of the requested hand.\n\n"
+   "This is the axis rotation of the hand as if the hand were a gamepad thumb stick.  "
+   "Imagine a stick coming out the top of the hand and tilting the hand front, back, "
+   "left and right controls that stick.  The values returned along the x and y stick "
+   "axis are normalized from -1.0 to 1.0 with the maximum hand tilt angle for these "
+   "values as defined by $LeapMotion::MaximumHandAxisAngle.\n"
+   "@param index The hand index to check.\n"
+   "@return Axis rotation of the requested hand.\n\n"
+   "@see LeapMotion::MaximumHandAxisAngle\n")
+{
+   return object->getHandRotAxis(index);
+}
+
+DefineEngineMethod( LeapMotionFrame, getHandPointablesCount, S32, ( S32 index ),,
+   "@brief Get the number of pointables associated with this hand.\n\n"
+   "@param index The hand index to check.\n"
+   "@return Number of pointables that belong with this hand.\n\n")
+{
+   return object->getHandPointablesCount(index);
+}
+
+DefineEngineMethod( LeapMotionFrame, getPointablesCount, S32, ( ),,
+   "@brief Get the number of pointables defined in this frame.\n\n"
+   "@return The number of defined pointables.\n\n")
+{
+   return object->getPointablesCount();
+}
+
+DefineEngineMethod( LeapMotionFrame, getPointableValid, bool, ( S32 index ),,
+   "@brief Check if the requested pointable is valid.\n\n"
+   "@param index The pointable index to check.\n"
+   "@return True if the pointable is valid.\n\n")
+{
+   return object->getPointableValid(index);
+}
+
+DefineEngineMethod( LeapMotionFrame, getPointableId, S32, ( S32 index ),,
+   "@brief Get the ID of the requested pointable.\n\n"
+   "@param index The pointable index to check.\n"
+   "@return ID of the requested pointable.\n\n")
+{
+   return object->getPointableId(index);
+}
+
+DefineEngineMethod( LeapMotionFrame, getPointableHandIndex, S32, ( S32 index ),,
+   "@brief Get the index of the hand that this pointable belongs to, if any.\n\n"
+   "@param index The pointable index to check.\n"
+   "@return Index of the hand this pointable belongs to, or -1 if there is no associated hand.\n\n")
+{
+   return object->getPointableHandIndex(index);
+}
+
+DefineEngineMethod( LeapMotionFrame, getPointableType, LeapMotionFramePointableType, ( S32 index ),,
+   "@brief Get the type of the requested pointable.\n\n"
+   "@param index The pointable index to check.\n"
+   "@return Type of the requested pointable.\n\n")
+{
+   return object->getPointableType(index);
+}
+
+DefineEngineMethod( LeapMotionFrame, getPointableRawPos, Point3F, ( S32 index ),,
+   "@brief Get the raw position of the requested pointable.\n\n"
+   "The raw position is the pointable's floating point position converted to "
+   "Torque 3D coordinates (in millimeters).\n"
+   "@param index The pointable index to check.\n"
+   "@return Raw position of the requested pointable.\n\n")
+{
+   return object->getPointableRawPos(index);
+}
+
+DefineEngineMethod( LeapMotionFrame, getPointablePos, Point3I, ( S32 index ),,
+   "@brief Get the position of the requested pointable.\n\n"
+   "The position is the pointable's integer position converted to "
+   "Torque 3D coordinates (in millimeters).\n"
+   "@param index The pointable index to check.\n"
+   "@return Integer position of the requested pointable (in millimeters).\n\n")
+{
+   return object->getPointablePos(index);
+}
+
+DefineEngineMethod( LeapMotionFrame, getPointableRot, AngAxisF, ( S32 index ),,
+   "@brief Get the rotation of the requested pointable.\n\n"
+   "The Leap Motion pointable rotation as converted into the Torque 3D"
+   "coordinate system.\n"
+   "@param index The pointable index to check.\n"
+   "@return Rotation of the requested pointable.\n\n")
+{
+   AngAxisF aa(object->getPointableRot(index));
+   return aa;
+}
+
+DefineEngineMethod( LeapMotionFrame, getPointableRawTransform, TransformF, ( S32 index ),,
+   "@brief Get the raw transform of the requested pointable.\n\n"
+   "@param index The pointable index to check.\n"
+   "@return The raw position and rotation of the requested pointable (in Torque 3D coordinates).\n\n")
+{
+   const Point3F& pos = object->getPointableRawPos(index);
+   const QuatF& qa = object->getPointableRotQuat(index);
+
+   AngAxisF aa(qa);
+   aa.axis.normalize();
+
+   TransformF trans(pos, aa);
+   return trans;
+}
+
+DefineEngineMethod( LeapMotionFrame, getPointableTransform, TransformF, ( S32 index ),,
+   "@brief Get the transform of the requested pointable.\n\n"
+   "@param index The pointable index to check.\n"
+   "@return The position and rotation of the requested pointable (in Torque 3D coordinates).\n\n")
+{
+   const Point3I& pos = object->getPointablePos(index);
+   const QuatF& qa = object->getPointableRotQuat(index);
+
+   AngAxisF aa(qa);
+   aa.axis.normalize();
+
+   TransformF trans;
+   trans.mPosition = Point3F(pos.x, pos.y, pos.z);
+   trans.mOrientation = aa;
+
+   return trans;
+}
+
+DefineEngineMethod( LeapMotionFrame, getPointableLength, F32, ( S32 index ),,
+   "@brief Get the length of the requested pointable.\n\n"
+   "@param index The pointable index to check.\n"
+   "@return Length of the requested pointable (in millimeters).\n\n")
+{
+   return object->getPointableLength(index);
+}
+
+DefineEngineMethod( LeapMotionFrame, getPointableWidth, F32, ( S32 index ),,
+   "@brief Get the width of the requested pointable.\n\n"
+   "@param index The pointable index to check.\n"
+   "@return Width of the requested pointable (in millimeters).\n\n")
+{
+   return object->getPointableWidth(index);
+}

+ 227 - 0
Engine/source/platform/input/leapMotion/leapMotionFrame.h

@@ -0,0 +1,227 @@
+//-----------------------------------------------------------------------------
+// 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 _LEAPMOTIONFRAME_H_
+#define _LEAPMOTIONFRAME_H_
+
+#include "console/simObject.h"
+#include "math/mPoint3.h"
+#include "math/mMatrix.h"
+#include "math/mQuat.h"
+#include "Leap.h"
+
+class LeapMotionFrame : public SimObject
+{
+   typedef SimObject Parent;
+
+public:
+   enum PointableType
+   {
+      PT_UNKNOWN = -1,
+      PT_FINGER = 0,
+      PT_TOOL,
+   };
+
+protected:
+   static U32 smNextInternalFrameId;
+
+   // Frame
+   bool  mFrameValid;
+   U64   mFrameId;
+   U64   mFrameTimeStamp;
+
+   // Torque 3D frame information
+   U32   mFrameInternalId;
+   S32   mFrameSimTime;
+   S32   mFrameRealTime;
+
+   // Hands
+   U32 mHandCount;
+   Vector<bool>      mHandValid;
+   Vector<S32>       mHandId;
+   Vector<Point3F>   mHandRawPos;
+   Vector<Point3I>   mHandPos;
+   Vector<MatrixF>   mHandRot;
+   Vector<QuatF>     mHandRotQuat;
+   Vector<Point2F>   mHandRotAxis;
+   Vector<U32>       mHandPointablesCount;
+
+   // Pointables
+   U32 mPointableCount;
+   Vector<bool>      mPointableValid;
+   Vector<S32>       mPointableId;
+   Vector<S32>       mPointableHandIndex;
+   Vector<PointableType>   mPointableType;
+   Vector<Point3F>   mPointableRawPos;
+   Vector<Point3I>   mPointablePos;
+   Vector<MatrixF>   mPointableRot;
+   Vector<QuatF>     mPointableRotQuat;
+   Vector<F32>       mPointableLength;
+   Vector<F32>       mPointableWidth;
+
+protected:
+   void copyFromFrameHands(const Leap::HandList& hands, const F32& maxHandAxisRadius);
+   void copyFromFramePointables(const Leap::PointableList& pointables);
+
+public:
+   LeapMotionFrame();
+   virtual ~LeapMotionFrame();
+
+   static void initPersistFields();
+
+   virtual bool onAdd();
+   virtual void onRemove();
+
+   void clear();
+
+   /// Copy a Leap Frame into our data structures
+   void copyFromFrame(const Leap::Frame& frame, const F32& maxHandAxisRadius);
+
+   // Frame
+   bool isFrameValid() const { return mFrameValid; }
+   U32 getFrameInternalId() const { return mFrameInternalId; }
+   S32 getFrameSimTime() const { return mFrameSimTime; }
+   S32 getFrameRealTime() const { return mFrameRealTime; }
+
+   // Hands
+   U32 getHandCount() const { return mHandCount; }
+   bool getHandValid(U32 index) const;
+   S32 getHandId(U32 index) const;
+   const Point3F& getHandRawPos(U32 index) const;
+   const Point3I& getHandPos(U32 index) const;
+   const MatrixF& getHandRot(U32 index) const;
+   const QuatF& getHandRotQuat(U32 index) const;
+   const Point2F& getHandRotAxis(U32 index) const;
+   U32 getHandPointablesCount(U32 index) const;
+
+   // Pointables
+   U32 getPointablesCount() const { return mPointableCount; }
+   bool getPointableValid(U32 index) const;
+   S32 getPointableId(U32 index) const;
+   S32 getPointableHandIndex(U32 index) const;
+   PointableType getPointableType(U32 index) const;
+   const Point3F& getPointableRawPos(U32 index) const;
+   const Point3I& getPointablePos(U32 index) const;
+   const MatrixF& getPointableRot(U32 index) const;
+   const QuatF& getPointableRotQuat(U32 index) const;
+   F32 getPointableLength(U32 index) const;
+   F32 getPointableWidth(U32 index) const;
+
+   DECLARE_CONOBJECT(LeapMotionFrame);
+};
+
+typedef LeapMotionFrame::PointableType LeapMotionFramePointableType;
+DefineEnumType( LeapMotionFramePointableType );
+
+//-----------------------------------------------------------------------------
+
+inline bool LeapMotionFrame::getHandValid(U32 index) const
+{
+   return (index < mHandCount && mHandValid[index]);
+}
+
+inline S32 LeapMotionFrame::getHandId(U32 index) const
+{
+   return (index >= mHandCount) ? -1 : mHandId[index];
+}
+
+inline const Point3F& LeapMotionFrame::getHandRawPos(U32 index) const
+{
+   return (index >= mHandCount) ? Point3F::Zero : mHandRawPos[index];
+}
+
+inline const Point3I& LeapMotionFrame::getHandPos(U32 index) const
+{
+   return (index >= mHandCount) ? Point3I::Zero : mHandPos[index];
+}
+
+inline const MatrixF& LeapMotionFrame::getHandRot(U32 index) const
+{
+   return (index >= mHandCount) ? MatrixF::Identity : mHandRot[index];
+}
+
+inline const QuatF& LeapMotionFrame::getHandRotQuat(U32 index) const
+{
+   return (index >= mHandCount) ? QuatF::Identity : mHandRotQuat[index];
+}
+
+inline const Point2F& LeapMotionFrame::getHandRotAxis(U32 index) const
+{
+   return (index >= mHandCount) ? Point2F::Zero : mHandRotAxis[index];
+}
+
+inline U32 LeapMotionFrame::getHandPointablesCount(U32 index) const
+{
+   return (index >= mHandCount) ? 0 : mHandPointablesCount[index];
+}
+
+inline bool LeapMotionFrame::getPointableValid(U32 index) const
+{
+   return (index < mPointableCount && mPointableValid[index]);
+}
+
+inline S32 LeapMotionFrame::getPointableId(U32 index) const
+{
+   return (index >= mPointableCount) ? -1 : mPointableId[index];
+}
+
+inline S32 LeapMotionFrame::getPointableHandIndex(U32 index) const
+{
+   return (index >= mPointableCount) ? -1 : mPointableHandIndex[index];
+}
+
+inline LeapMotionFrame::PointableType LeapMotionFrame::getPointableType(U32 index) const
+{
+   return (index >= mPointableCount) ? PT_UNKNOWN : mPointableType[index];
+}
+
+inline const Point3F& LeapMotionFrame::getPointableRawPos(U32 index) const
+{
+   return (index >= mPointableCount) ? Point3F::Zero : mPointableRawPos[index];
+}
+
+inline const Point3I& LeapMotionFrame::getPointablePos(U32 index) const
+{
+   return (index >= mPointableCount) ? Point3I::Zero : mPointablePos[index];
+}
+
+inline const MatrixF& LeapMotionFrame::getPointableRot(U32 index) const
+{
+   return (index >= mPointableCount) ? MatrixF::Identity : mPointableRot[index];
+}
+
+inline const QuatF& LeapMotionFrame::getPointableRotQuat(U32 index) const
+{
+   return (index >= mPointableCount) ? QuatF::Identity : mPointableRotQuat[index];
+}
+
+inline F32 LeapMotionFrame::getPointableLength(U32 index) const
+{
+   return (index >= mPointableCount) ? 0.0f : mPointableLength[index];
+}
+
+inline F32 LeapMotionFrame::getPointableWidth(U32 index) const
+{
+   return (index >= mPointableCount) ? 0.0f : mPointableWidth[index];
+}
+
+#endif   // _LEAPMOTIONFRAME_H_

+ 106 - 0
Engine/source/platform/input/leapMotion/leapMotionFrameStore.cpp

@@ -0,0 +1,106 @@
+//-----------------------------------------------------------------------------
+// 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/input/leapMotion/leapMotionFrameStore.h"
+#include "platform/input/leapMotion/leapMotionFrame.h"
+#include "core/module.h"
+#include "console/simSet.h"
+#include "console/consoleTypes.h"
+
+MODULE_BEGIN( LeapMotionFrameStore )
+
+   MODULE_INIT_AFTER( LeapMotionDevice )
+   MODULE_INIT_AFTER( Sim )
+   MODULE_SHUTDOWN_BEFORE( Sim )
+   MODULE_SHUTDOWN_BEFORE( LeapMotionDevice )
+
+   MODULE_INIT
+   {
+      LeapMotionFrameStore::staticInit();
+      ManagedSingleton< LeapMotionFrameStore >::createSingleton();
+   }
+   
+   MODULE_SHUTDOWN
+   {
+      ManagedSingleton< LeapMotionFrameStore >::deleteSingleton();
+   }
+
+MODULE_END;
+
+S32 LeapMotionFrameStore::smMaximumFramesStored = 30;
+
+SimGroup* LeapMotionFrameStore::smFrameGroup = NULL;
+
+LeapMotionFrameStore::LeapMotionFrameStore()
+{
+   // Set up the SimGroup to store our frames
+   smFrameGroup = new SimGroup();
+   smFrameGroup->registerObject("LeapMotionFrameGroup");
+   smFrameGroup->setNameChangeAllowed(false);
+   Sim::getRootGroup()->addObject(smFrameGroup);
+}
+
+LeapMotionFrameStore::~LeapMotionFrameStore()
+{
+   if(smFrameGroup)
+   {
+      smFrameGroup->deleteObject();
+      smFrameGroup = NULL;
+   }
+}
+
+void LeapMotionFrameStore::staticInit()
+{
+   Con::addVariable("LeapMotion::MaximumFramesStored", TypeS32, &smMaximumFramesStored, 
+      "@brief The maximum number of frames to keep when $LeapMotion::GenerateWholeFrameEvents is true.\n\n"
+	   "@ingroup Game");
+}
+
+S32 LeapMotionFrameStore::generateNewFrame(const Leap::Frame& frame, const F32& maxHandAxisRadius)
+{
+   // Make sure our group has been created
+   if(!smFrameGroup)
+      return 0;
+
+   // Either create a new frame object or pull one off the end
+   S32 frameID = 0;
+   if(smFrameGroup->size() >= smMaximumFramesStored)
+   {
+      // Make the last frame the first and update
+      LeapMotionFrame* frameObj = static_cast<LeapMotionFrame*>(smFrameGroup->last());
+      smFrameGroup->bringObjectToFront(frameObj);
+      frameObj->copyFromFrame(frame, maxHandAxisRadius);
+      frameID = frameObj->getId();
+   }
+   else
+   {
+      // Create a new frame and add it to the front of the list
+      LeapMotionFrame* frameObj = new LeapMotionFrame();
+      frameObj->registerObject();
+      smFrameGroup->addObject(frameObj);
+      smFrameGroup->bringObjectToFront(frameObj);
+      frameObj->copyFromFrame(frame, maxHandAxisRadius);
+      frameID = frameObj->getId();
+   }
+
+   return frameID;
+}

+ 58 - 0
Engine/source/platform/input/leapMotion/leapMotionFrameStore.h

@@ -0,0 +1,58 @@
+//-----------------------------------------------------------------------------
+// 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 _LEAPMOTIONFRAMESTORE_H_
+#define _LEAPMOTIONFRAMESTORE_H_
+
+#include "platformWin32/platformWin32.h"
+#include "Leap.h"
+
+class SimGroup;
+
+class LeapMotionFrameStore
+{
+public:
+   // The maximum number of frames to keep
+   static S32 smMaximumFramesStored;
+
+   static SimGroup* smFrameGroup;
+
+public:
+   LeapMotionFrameStore();
+   virtual ~LeapMotionFrameStore();
+
+   static void staticInit();
+
+   static bool isFrameGroupDefined() { return smFrameGroup != NULL; }
+   static SimGroup* getFrameGroup() { return smFrameGroup; }
+
+   S32 generateNewFrame(const Leap::Frame& frame, const F32& maxHandAxisRadius);
+
+public:
+   // For ManagedSingleton.
+   static const char* getSingletonName() { return "LeapMotionFrameStore"; }   
+};
+
+/// Returns the LeapMotionFrameStore singleton.
+#define LEAPMOTIONFS ManagedSingleton<LeapMotionFrameStore>::instance()
+
+#endif   // _LEAPMOTIONFRAMESTORE_H_

+ 109 - 0
Engine/source/platform/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 "platform/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/platform/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/mPoint3.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

+ 79 - 0
Tools/projectGenerator/modules/leapMotion.inc

@@ -0,0 +1,79 @@
+<?php
+//-----------------------------------------------------------------------------
+// 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.
+//-----------------------------------------------------------------------------
+
+beginModule( 'leapMotion' );
+
+   // Look for the optional global from the project.conf.
+   global $LEAPMOTION_SDK_PATH;
+   if (!$LEAPMOTION_SDK_PATH)
+   {
+      // First look for an environment var.
+      $LEAPMOTION_SDK_PATH = getenv( "TORQUE_LEAPMOTION_PATH" );
+
+      if (strlen($LEAPMOTION_SDK_PATH) == 0 || !file_exists($LEAPMOTION_SDK_PATH))
+      {
+         // Sometimes users get confused and use this var.
+         $LEAPMOTION_SDK_PATH = getenv( "LEAPMOTION_SDK_PATH" );
+      }
+
+      // We need forward slashes for paths.
+      $LEAPMOTION_SDK_PATH = str_replace( "\\", "/", $LEAPMOTION_SDK_PATH);
+
+      // Remove trailing slashes.
+      $LEAPMOTION_SDK_PATH = rtrim($LEAPMOTION_SDK_PATH, " /");
+   }
+
+   // If we still don't have the SDK path then let the user know.
+   if (!file_exists($LEAPMOTION_SDK_PATH))
+   {
+      trigger_error( 
+            "\n*******************************************************************".
+            "\n".
+            "\n  We were not able to find a valid path to the Leap Motion SDK!".
+            "\n".
+            "\n  You must install the latest Sixense SDK and set the path via a".
+            "\n  \$LEAPMOTION_SDK_PATH variable in your buildFiles/project.conf file".
+            "\n  or by setting the TORQUE_LEAPMOTION_PATH system environment variable".
+            "\n  (may require a reboot).".
+            "\n".
+            "\n*******************************************************************".
+            "\n", E_USER_ERROR );
+   }
+
+   // Only Windows is supported at this time
+   if ( Generator::$platform == "win32" )
+   {
+      // Source
+      addEngineSrcDir( "platform/input/leapMotion" );
+
+      // Includes
+      addIncludePath( $LEAPMOTION_SDK_PATH . "/include" );
+
+      // Libs
+      addProjectLibDir( $LEAPMOTION_SDK_PATH . "/lib/x86" );
+      addProjectLibInput( "Leap.lib", "Leapd.lib" );
+   }
+
+endModule();
+
+?>