123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364 |
- //-----------------------------------------------------------------------------
- // 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();
- }
|