leapMotionDevice.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platform/input/leapMotion/leapMotionDevice.h"
  23. #include "platform/input/leapMotion/leapMotionData.h"
  24. #include "platform/input/leapMotion/leapMotionFrameStore.h"
  25. #include "platform/platformInput.h"
  26. #include "core/module.h"
  27. #include "platform/threads/mutex.h"
  28. #include "console/engineAPI.h"
  29. MODULE_BEGIN( LeapMotionDevice )
  30. MODULE_INIT_AFTER( InputEventManager )
  31. MODULE_SHUTDOWN_BEFORE( InputEventManager )
  32. MODULE_INIT
  33. {
  34. LeapMotionDevice::staticInit();
  35. ManagedSingleton< LeapMotionDevice >::createSingleton();
  36. if(LeapMotionDevice::smEnableDevice)
  37. {
  38. LEAPMOTIONDEV->enable();
  39. }
  40. // Register the device with the Input Event Manager
  41. INPUTMGR->registerDevice(LEAPMOTIONDEV);
  42. }
  43. MODULE_SHUTDOWN
  44. {
  45. INPUTMGR->unregisterDevice(LEAPMOTIONDEV);
  46. ManagedSingleton< LeapMotionDevice >::deleteSingleton();
  47. }
  48. MODULE_END;
  49. //-----------------------------------------------------------------------------
  50. // LeapMotionDevice
  51. //-----------------------------------------------------------------------------
  52. bool LeapMotionDevice::smEnableDevice = true;
  53. bool LeapMotionDevice::smGenerateIndividualEvents = true;
  54. bool LeapMotionDevice::smKeepHandIndexPersistent = false;
  55. bool LeapMotionDevice::smKeepPointableIndexPersistent = false;
  56. bool LeapMotionDevice::smGenerateSingleHandRotationAsAxisEvents = false;
  57. F32 LeapMotionDevice::smMaximumHandAxisAngle = 25.0f;
  58. bool LeapMotionDevice::smGenerateWholeFrameEvents = false;
  59. U32 LeapMotionDevice::LM_FRAMEVALIDDATA = 0;
  60. U32 LeapMotionDevice::LM_HAND[LeapMotionConstants::MaxHands] = {0};
  61. U32 LeapMotionDevice::LM_HANDROT[LeapMotionConstants::MaxHands] = {0};
  62. U32 LeapMotionDevice::LM_HANDAXISX = 0;
  63. U32 LeapMotionDevice::LM_HANDAXISY = 0;
  64. U32 LeapMotionDevice::LM_HANDPOINTABLE[LeapMotionConstants::MaxHands][LeapMotionConstants::MaxPointablesPerHand] = {0};
  65. U32 LeapMotionDevice::LM_HANDPOINTABLEROT[LeapMotionConstants::MaxHands][LeapMotionConstants::MaxPointablesPerHand] = {0};
  66. U32 LeapMotionDevice::LM_FRAME = 0;
  67. LeapMotionDevice::LeapMotionDevice()
  68. {
  69. // From IInputDevice
  70. dStrcpy(mName, "leapmotion");
  71. mDeviceType = INPUTMGR->getNextDeviceType();
  72. mController = NULL;
  73. mListener = NULL;
  74. mActiveMutex = Mutex::createMutex();
  75. //
  76. mEnabled = false;
  77. mActive = false;
  78. for(U32 i=0; i<2; ++i)
  79. {
  80. mDataBuffer[i] = new LeapMotionDeviceData();
  81. }
  82. mPrevData = mDataBuffer[0];
  83. buildCodeTable();
  84. }
  85. LeapMotionDevice::~LeapMotionDevice()
  86. {
  87. disable();
  88. Mutex::destroyMutex(mActiveMutex);
  89. }
  90. void LeapMotionDevice::staticInit()
  91. {
  92. Con::addVariable("pref::LeapMotion::EnableDevice", TypeBool, &smEnableDevice,
  93. "@brief If true, the Leap Motion device will be enabled, if present.\n\n"
  94. "@ingroup Game");
  95. Con::addVariable("LeapMotion::GenerateIndividualEvents", TypeBool, &smGenerateIndividualEvents,
  96. "@brief Indicates that events for each hand and pointable will be created.\n\n"
  97. "@ingroup Game");
  98. Con::addVariable("LeapMotion::KeepHandIndexPersistent", TypeBool, &smKeepHandIndexPersistent,
  99. "@brief Indicates that we track hand IDs and will ensure that the same hand will remain at the same index between frames.\n\n"
  100. "@ingroup Game");
  101. Con::addVariable("LeapMotion::KeepPointableIndexPersistent", TypeBool, &smKeepPointableIndexPersistent,
  102. "@brief Indicates that we track pointable IDs and will ensure that the same pointable will remain at the same index between frames.\n\n"
  103. "@ingroup Game");
  104. Con::addVariable("LeapMotion::GenerateSingleHandRotationAsAxisEvents", TypeBool, &smGenerateSingleHandRotationAsAxisEvents,
  105. "@brief If true, broadcast single hand rotation as axis events.\n\n"
  106. "@ingroup Game");
  107. Con::addVariable("LeapMotion::MaximumHandAxisAngle", TypeF32, &smMaximumHandAxisAngle,
  108. "@brief The maximum hand angle when used as an axis event as measured from a vector pointing straight up (in degrees).\n\n"
  109. "Shoud range from 0 to 90 degrees.\n\n"
  110. "@ingroup Game");
  111. Con::addVariable("LeapMotion::GenerateWholeFrameEvents", TypeBool, &smGenerateWholeFrameEvents,
  112. "@brief Indicates that a whole frame event should be generated and frames should be buffered.\n\n"
  113. "@ingroup Game");
  114. }
  115. void LeapMotionDevice::buildCodeTable()
  116. {
  117. // Obtain all of the device codes
  118. LM_FRAMEVALIDDATA = INPUTMGR->getNextDeviceCode();
  119. for(U32 i=0; i<LeapMotionConstants::MaxHands; ++i)
  120. {
  121. // Hands
  122. LM_HAND[i] = INPUTMGR->getNextDeviceCode();
  123. LM_HANDROT[i] = INPUTMGR->getNextDeviceCode();
  124. // Pointables per hand
  125. for(U32 j=0; j<LeapMotionConstants::MaxPointablesPerHand; ++j)
  126. {
  127. LM_HANDPOINTABLE[i][j] = INPUTMGR->getNextDeviceCode();
  128. LM_HANDPOINTABLEROT[i][j] = INPUTMGR->getNextDeviceCode();
  129. }
  130. }
  131. LM_HANDAXISX = INPUTMGR->getNextDeviceCode();
  132. LM_HANDAXISY = INPUTMGR->getNextDeviceCode();
  133. LM_FRAME = INPUTMGR->getNextDeviceCode();
  134. // Build out the virtual map
  135. AddInputVirtualMap( lm_framevaliddata, SI_BUTTON, LM_FRAMEVALIDDATA );
  136. char buffer[64];
  137. for(U32 i=0; i<LeapMotionConstants::MaxHands; ++i)
  138. {
  139. // Hands
  140. dSprintf(buffer, 64, "lm_hand%d", i+1);
  141. INPUTMGR->addVirtualMap( buffer, SI_POS, LM_HAND[i] );
  142. dSprintf(buffer, 64, "lm_hand%drot", i+1);
  143. INPUTMGR->addVirtualMap( buffer, SI_ROT, LM_HANDROT[i] );
  144. // Pointables per hand
  145. for(U32 j=0; j<LeapMotionConstants::MaxPointablesPerHand; ++j)
  146. {
  147. dSprintf(buffer, 64, "lm_hand%dpoint%d", i+1, j+1);
  148. INPUTMGR->addVirtualMap( buffer, SI_POS, LM_HANDPOINTABLE[i][j] );
  149. dSprintf(buffer, 64, "lm_hand%dpoint%drot", i+1, j+1);
  150. INPUTMGR->addVirtualMap( buffer, SI_POS, LM_HANDPOINTABLEROT[i][j] );
  151. }
  152. }
  153. AddInputVirtualMap( lm_handaxisx, SI_AXIS, LM_HANDAXISX );
  154. AddInputVirtualMap( lm_handaxisy, SI_AXIS, LM_HANDAXISY );
  155. AddInputVirtualMap( lm_frame, SI_INT, LM_FRAME );
  156. }
  157. bool LeapMotionDevice::enable()
  158. {
  159. // Start off with disabling the device if it is already enabled
  160. disable();
  161. // Create the controller to talk with the Leap Motion along with the listener
  162. mListener = new MotionListener();
  163. mController = new Leap::Controller(*mListener);
  164. // The device is now enabled but not yet ready to be used
  165. mEnabled = true;
  166. return false;
  167. }
  168. void LeapMotionDevice::disable()
  169. {
  170. if(mController)
  171. {
  172. delete mController;
  173. mController = NULL;
  174. if(mListener)
  175. {
  176. delete mListener;
  177. mListener = NULL;
  178. }
  179. }
  180. setActive(false);
  181. mEnabled = false;
  182. }
  183. bool LeapMotionDevice::getActive()
  184. {
  185. Mutex::lockMutex(mActiveMutex);
  186. bool active = mActive;
  187. Mutex::unlockMutex(mActiveMutex);
  188. return active;
  189. }
  190. void LeapMotionDevice::setActive(bool state)
  191. {
  192. Mutex::lockMutex(mActiveMutex);
  193. mActive = state;
  194. Mutex::unlockMutex(mActiveMutex);
  195. }
  196. bool LeapMotionDevice::process()
  197. {
  198. if(!mEnabled)
  199. return false;
  200. if(!getActive())
  201. return false;
  202. //Con::printf("LeapMotionDevice::process()");
  203. //Build the maximum hand axis angle to be passed into the LeapMotionDeviceData::setData()
  204. F32 maxHandAxisRadius = mSin(mDegToRad(smMaximumHandAxisAngle));
  205. // Get a frame of data
  206. const Leap::Frame frame = mController->frame();
  207. //const Leap::HandList hands = frame.hands();
  208. //Con::printf("Frame: %lld Hands: %d Fingers: %d Tools: %d", (long long)frame.id(), hands.count(), frame.fingers().count(), frame.tools().count());
  209. // Store the current data
  210. LeapMotionDeviceData* currentBuffer = (mPrevData == mDataBuffer[0]) ? mDataBuffer[1] : mDataBuffer[0];
  211. currentBuffer->setData(frame, mPrevData, smKeepHandIndexPersistent, smKeepPointableIndexPersistent, maxHandAxisRadius);
  212. U32 diff = mPrevData->compare(currentBuffer);
  213. U32 metaDiff = mPrevData->compareMeta(currentBuffer);
  214. // Update the previous data pointers. We do this here in case someone calls our
  215. // console functions during one of the input events below.
  216. mPrevData = currentBuffer;
  217. // Send out any meta data
  218. if(metaDiff & LeapMotionDeviceData::METADIFF_FRAME_VALID_DATA)
  219. {
  220. // Frame valid change event
  221. INPUTMGR->buildInputEvent(mDeviceType, DEFAULT_MOTION_UNIT, SI_BUTTON, LM_FRAMEVALIDDATA, currentBuffer->mHasTrackingData ? SI_MAKE : SI_BREAK, currentBuffer->mHasTrackingData ? 1.0f : 0.0f);
  222. }
  223. // Send out any valid data
  224. if(currentBuffer->mDataSet && currentBuffer->mIsValid)
  225. {
  226. // Hands and their pointables
  227. if(smGenerateIndividualEvents)
  228. {
  229. for(U32 i=0; i<LeapMotionConstants::MaxHands; ++i)
  230. {
  231. if(currentBuffer->mHandValid[i])
  232. {
  233. // Send out position
  234. INPUTMGR->buildInputEvent(mDeviceType, DEFAULT_MOTION_UNIT, SI_POS, LM_HAND[i], SI_MOVE, currentBuffer->mHandPosPoint[i]);
  235. // Send out rotation
  236. INPUTMGR->buildInputEvent(mDeviceType, DEFAULT_MOTION_UNIT, SI_ROT, LM_HANDROT[i], SI_MOVE, currentBuffer->mHandRotQuat[i]);
  237. // Pointables for hand
  238. for(U32 j=0; j<LeapMotionConstants::MaxPointablesPerHand; ++j)
  239. {
  240. if(currentBuffer->mPointableValid[i][j])
  241. {
  242. // Send out position
  243. INPUTMGR->buildInputEvent(mDeviceType, DEFAULT_MOTION_UNIT, SI_POS, LM_HANDPOINTABLE[i][j], SI_MOVE, currentBuffer->mPointablePosPoint[i][j]);
  244. // Send out rotation
  245. INPUTMGR->buildInputEvent(mDeviceType, DEFAULT_MOTION_UNIT, SI_ROT, LM_HANDPOINTABLEROT[i][j], SI_MOVE, currentBuffer->mPointableRotQuat[i][j]);
  246. }
  247. }
  248. }
  249. }
  250. }
  251. // Single Hand as axis rotation
  252. if(smGenerateSingleHandRotationAsAxisEvents && diff & LeapMotionDeviceData::DIFF_HANDROTAXIS)
  253. {
  254. if(diff & LeapMotionDeviceData::DIFF_HANDROTAXISX)
  255. INPUTMGR->buildInputEvent(mDeviceType, DEFAULT_MOTION_UNIT, SI_AXIS, LM_HANDAXISX, SI_MOVE, currentBuffer->mHandRotAxis[0]);
  256. if(diff & LeapMotionDeviceData::DIFF_HANDROTAXISY)
  257. INPUTMGR->buildInputEvent(mDeviceType, DEFAULT_MOTION_UNIT, SI_AXIS, LM_HANDAXISY, SI_MOVE, currentBuffer->mHandRotAxis[1]);
  258. }
  259. }
  260. // Send out whole frame event, but only if the special frame group is defined
  261. if(smGenerateWholeFrameEvents && LeapMotionFrameStore::isFrameGroupDefined())
  262. {
  263. S32 id = LEAPMOTIONFS->generateNewFrame(frame, maxHandAxisRadius);
  264. if(id != 0)
  265. {
  266. INPUTMGR->buildInputEvent(mDeviceType, DEFAULT_MOTION_UNIT, SI_INT, LM_FRAME, SI_VALUE, id);
  267. }
  268. }
  269. return true;
  270. }
  271. //-----------------------------------------------------------------------------
  272. // LeapMotionDevice::MotionListener
  273. //-----------------------------------------------------------------------------
  274. void LeapMotionDevice::MotionListener::onConnect (const Leap::Controller &controller)
  275. {
  276. LEAPMOTIONDEV->setActive(true);
  277. }
  278. void LeapMotionDevice::MotionListener::onDisconnect (const Leap::Controller &controller)
  279. {
  280. LEAPMOTIONDEV->setActive(false);
  281. }
  282. //-----------------------------------------------------------------------------
  283. DefineEngineFunction(isLeapMotionActive, bool, (),,
  284. "@brief Used to determine if the Leap Motion input device is active\n\n"
  285. "The Leap Motion input device is considered active when the support library has been "
  286. "loaded and the device has been found.\n\n"
  287. "@return True if the Leap Motion input device is active.\n"
  288. "@ingroup Game")
  289. {
  290. if(!ManagedSingleton<LeapMotionDevice>::instanceOrNull())
  291. {
  292. return false;
  293. }
  294. return LEAPMOTIONDEV->getActive();
  295. }