Initial Leap Motion controller support, includes llleapmotioncontoller.* test code from Simon Linden

To build with Leap Motion support build with —leapmotion (Very unstable!)
Cinders 2013-11-27 20:09:34 -07:00
parent 8756acc1c5
commit 1f3cf9bce2
11 changed files with 862 additions and 50 deletions

View File

@ -1175,6 +1175,54 @@
</map>
</map>
</map>
<key>leap-motion</key>
<map>
<key>license</key>
<string>leap-motion</string>
<key>license_file</key>
<string>LICENSES/leap-motion.txt</string>
<key>name</key>
<string>leap-motion</string>
<key>platforms</key>
<map>
<key>darwin</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>94fa8329ff292e43a5f5527ed4a05291</string>
<key>url</key>
<string>file:///opt/firestorm/leap_motion-1.0.9+8391-darwin-20131127.tar.bz2</string>
</map>
<key>name</key>
<string>darwin</string>
</map>
<key>linux</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>c71a6149cda34b32103c17ed460cce66</string>
<key>url</key>
<string>file:///opt/firestorm/leap_motion-1.0.9+8391-linux-x64-20131128.tar.bz2</string>
</map>
<key>name</key>
<string>linux</string>
</map>
<key>windows</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>12f025c0b3d76dd3f83d963615b7964b</string>
<key>url</key>
<string>file:///opt/firestorm/leap_motion-1.0.9+8391-windows-20131128.tar.bz2</string>
</map>
<key>name</key>
<string>windows</string>
</map>
</map>
</map>
<key>libhunspell</key>
<map>
<key>license</key>
@ -1885,6 +1933,26 @@
</map>
</map>
</map>
<key>slplugin_x86</key>
<map>
<key>license</key>
<string>OpenSSL/LGPL</string>
<key>license_file</key>
<string>LICENSES/slplugin.txt</string>
<key>name</key>
<string>slplugin_x86</string>
<key>platforms</key>
<map>
<key>windows</key>
<map>
<key>archive</key>
<map>
</map>
<key>name</key>
<string>windows</string>
</map>
</map>
</map>
<key>slvoice</key>
<map>
<key>license</key>
@ -1957,6 +2025,26 @@
</map>
</map>
</map>
<key>wix</key>
<map>
<key>license</key>
<string>MS-RL</string>
<key>license_file</key>
<string>LICENSES/wix.txt</string>
<key>name</key>
<string>wix</string>
<key>platforms</key>
<map>
<key>windows</key>
<map>
<key>archive</key>
<map>
</map>
<key>name</key>
<string>windows</string>
</map>
</map>
</map>
<key>xmlrpc-epi</key>
<map>
<key>license</key>
@ -2053,54 +2141,6 @@
</map>
</map>
</map>
<key>slplugin_x86</key>
<map>
<key>license</key>
<string>OpenSSL/LGPL</string>
<key>license_file</key>
<string>LICENSES/slplugin.txt</string>
<key>name</key>
<string>slplugin_x86</string>
<key>platforms</key>
<map>
<key>windows</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string></string>
<key>url</key>
<string></string>
</map>
<key>name</key>
<string>windows</string>
</map>
</map>
</map>
<key>wix</key>
<map>
<key>license</key>
<string>MS-RL</string>
<key>license_file</key>
<string>LICENSES/wix.txt</string>
<key>name</key>
<string>wix</string>
<key>platforms</key>
<map>
<key>windows</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string></string>
<key>url</key>
<string></string>
</map>
<key>name</key>
<string>windows</string>
</map>
</map>
</map>
</map>
<key>package_description</key>
<map>

View File

@ -80,6 +80,7 @@ set(cmake_SOURCE_FILES
LLVFS.cmake
LLWindow.cmake
LLXML.cmake
LeapMotion.cmake
# <FS:CR> We'll be fine without you -> LScript.cmake
Linking.cmake
MediaPluginBase.cmake

View File

@ -74,6 +74,11 @@ if(WINDOWS)
endif( NOT ND_BUILD64BIT_ARCH )
endif (FMODEX)
if (LEAPMOTION)
set(debug_files ${debug_files} Leapd.dll)
set(release_files ${release_files} Leap.dll)
endif (LEAPMOTION)
# <FS:ND> Copy pdb files for symbol generation too
if( NOT ND_BUILD64BIT_ARCH )
set(debug_files ${debug_files} ssleay32.pdb libeay32.pdb apr-1.pdb aprutil-1.pdb growl.pdb growl++.pdb )
@ -248,6 +253,10 @@ elseif(DARWIN)
set(release_files ${release_files} libfmodex.dylib)
endif (FMODEX)
if (LEAPMOTION)
set(release_files ${release_files} libLeap.dylib)
endif (LEAPMOTION)
elseif(LINUX)
# linux is weird, multiple side by side configurations aren't supported
# and we don't seem to have any debug shared libs built yet anyways...
@ -321,6 +330,10 @@ elseif(LINUX)
set(release_file ${release_files} "libfmodex.so")
endif (FMODEX)
if (LEAPMOTION)
set(release_file ${release_files} "libLeap.so")
endif (LEAPMOTION)
else(WINDOWS)
message(STATUS "WARNING: unrecognized platform for staging 3rd party libs, skipping...")
set(vivox_src_dir "${CMAKE_SOURCE_DIR}/newview/vivox-runtime/i686-linux")

View File

@ -0,0 +1,24 @@
# -*- cmake -*-
include(Linking)
if (INSTALL_PROPRIETARY)
set(LEAPMOTION ON CACHE BOOL "Building with Leap Motion Controller support.")
endif (INSTALL_PROPRIETARY)
if (STANDALONE)
# *TODO: Standalone support
else (STANDALONE)
include(Prebuilt)
use_prebuilt_binary(leap-motion)
if (DARWIN)
set(LEAP_MOTION_LIBRARY libLeap.dylib)
elseif (WINDOWS)
set(LEAP_MOTION_LIBRARY
debug Leapd.dll
optimized Leap.dll)
elseif (LINUX)
set(LEAP_MOTION_LIBRARY libLeap.so)
endif()
set(LEAP_MOTION_INCLUDE_DIR ${LIBS_OPEN_DIR}/leap-motion)
endif (STANDALONE)

View File

@ -32,6 +32,7 @@ include(LLUI)
include(LLVFS)
include(LLWindow)
include(LLXML)
include(LeapMotion)
# <FS:CR> Nope -> include(LScript)
include(Linking)
include(NDOF)
@ -68,6 +69,11 @@ if(FMODEX)
include_directories(${FMODEX_INCLUDE_DIR})
endif(FMODEX)
if(LEAPMOTION)
add_definitions(-DUSE_LEAPMOTION=1)
include_directories(${LEAP_MOTION_INCLUDE_DIR})
endif(LEAPMOTION)
include_directories(
${DBUSGLIB_INCLUDE_DIRS}
${JSONCPP_INCLUDE_DIR}
@ -1529,6 +1535,11 @@ set_source_files_properties(
# COMPILE_DEFINITIONS "${VIEWER_CHANNEL_VERSION_DEFINES}" # see BuildVersion.cmake
)
if (LEAPMOTION)
LIST(APPEND viewer_SOURCE_FILES llleapmotioncontroller.cpp)
LIST(APPEND viewer_HEADER_FILES llleapmotioncontroller.h)
endif (LEAPMOTION)
if (DARWIN)
LIST(APPEND viewer_SOURCE_FILES llappviewermacosx.cpp)
LIST(APPEND viewer_SOURCE_FILES llfilepicker_mac.mm)
@ -2054,6 +2065,13 @@ if (WINDOWS)
${SHARED_LIB_STAGING_DIR}/Debug/fmodexL.dll
)
endif (FMODEX)
if (LEAPMOTION)
list(APPEND_COPY_INPUT_DEPENDENCIES
${SHARED_LIB_STAGING_DIR}/Release/Leap.dll
${SHARED_LIB_STAGING_DIR}/Debug/Leapd.dll
)
endif (LEAPMOTION)
add_custom_command(
OUTPUT ${CMAKE_CFG_INTDIR}/copy_touched.bat
@ -2228,6 +2246,12 @@ target_link_libraries(${VIEWER_BINARY_NAME}
${GROWL_LIBRARY}
)
if (LEAPMOTION)
target_link_libraries(${VIEWER_BINARY_NAME}
${LEAP_MOTION_LIBRARY}
)
endif (LEAPMOTION)
if (WINDOWS)
target_link_libraries(${VIEWER_BINARY_NAME}
${GROWL_LIBRARY}

View File

@ -7287,6 +7287,17 @@
<key>Value</key>
<integer>2</integer>
</map>
<key>LeapMotionTestMode</key>
<map>
<key>Comment</key>
<string>Test mode for Leap Motion gesture controller</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>S32</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>LeftClickShowMenu</key>
<map>
<key>Comment</key>

View File

@ -244,6 +244,10 @@
#include "llviewereventrecorder.h"
#ifdef USE_LEAPMOTION
#include "llleapmotioncontroller.h"
#endif
// *FIX: These extern globals should be cleaned up.
// The globals either represent state/config/resource-storage of either
@ -1479,6 +1483,13 @@ bool LLAppViewer::mainLoop()
mMainLoopInitialized = true;
#endif
}
// [FS:CR]
#ifdef USE_LEAPMOTION
LLLeapMotionController gestureController;
#endif // USE_LEAPMOTION
// [/FS:CR]
// As we do not (yet) send data on the mainloop LLEventPump that varies
// with each frame, no need to instantiate a new LLSD event object each
// time. Obviously, if that changes, just instantiate the LLSD at the
@ -1661,6 +1672,11 @@ bool LLAppViewer::mainLoop()
}
}
// [FS:CR] Run any LeapMotion devices
#ifdef USE_LEAPMOTION
gestureController.stepFrame();
#endif //USE_LEAPMOTION
pingMainloopTimeout("Main:Sleep");

View File

@ -0,0 +1,613 @@
/**
* @file llleapmotioncontroller.cpp
* @brief LLLeapMotionController class implementation
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
* Copyright (C) 2013, Cinder Roxley <cinder.roxley@phoenixviewer.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h"
#include "llleapmotioncontroller.h"
#include "llagent.h"
#include "llagentcamera.h"
#include "llgesturemgr.h"
#include "llmath.h"
#include "llstartup.h"
#include "lltimer.h"
#include "llviewercontrol.h"
#include "fsnearbychathub.h"
#include <leap-motion/Leap.h>
const F32 LM_DEAD_ZONE = 20.f; // Dead zone in the middle of the space
const F32 LM_ORBIT_RATE_FACTOR = 80.f; // Number for camera orbit magic factor
class LLLMImpl : public Leap::Listener
{
public:
LLLMImpl();
~LLLMImpl();
// LeapMotion callbacks
virtual void onInit(const Leap::Controller&);
virtual void onConnect(const Leap::Controller&);
virtual void onDisconnect(const Leap::Controller&);
virtual void onFrame(const Leap::Controller&);
// Called from viewer main loop
void stepFrame();
Leap::Controller * mLMController; // Leapmotion's object
bool mLMConnected; // true if device is connected
bool mFrameAvailable; // true if there is a new frame of data available
int64_t mCurrentFrameID; // Id of the most recent frame of data
LLTimer mYawTimer; // Avoid turning left / right too fast
// Hacky demo code - send controller data to in-world objects via chat
LLTimer mChatMsgTimer; // Throttle sending LM controller data to region local chat
LLTimer mGestureTimer; // Throttle invoking SL gestures
private:
// Various controller modes
void modeFlyingControlTest(Leap::HandList & hands);
void modeStreamDataToSL(Leap::HandList & hands);
void modeGestureDetection1(Leap::HandList & hands);
void modeMoveAndCamTest1(Leap::HandList & hands);
void modeDumpDebugInfo(Leap::HandList & hands);
};
const F32 LLLEAP_YAW_INTERVAL = 0.075f;
// Time between spamming chat messages. Server-side throttle is 200 msgs in 10 seconds
const F32 LLLEAP_CHAT_MSG_INTERVAL = 0.200f; // Send 5/second
const F32 LLLEAP_GESTURE_INTERVAL = 3.f; // 3 seconds in between SL gestures
LLLMImpl::LLLMImpl() : mLMController(NULL)
, mLMConnected(false)
, mFrameAvailable(false)
, mCurrentFrameID(0)
{
mLMController = new Leap::Controller(*this);
mYawTimer.setTimerExpirySec(LLLEAP_YAW_INTERVAL);
mChatMsgTimer.setTimerExpirySec(LLLEAP_CHAT_MSG_INTERVAL);
mGestureTimer.setTimerExpirySec(LLLEAP_GESTURE_INTERVAL);
}
LLLMImpl::~LLLMImpl()
{
delete mLMController;
}
void LLLMImpl::onInit(const Leap::Controller& controller)
{
LL_INFOS("LeapMotion") << "Initialized" << LL_ENDL;
}
void LLLMImpl::onConnect(const Leap::Controller& controller)
{
LL_INFOS("LeapMotion") << "Connected" << LL_ENDL;
mLMConnected = true;
mCurrentFrameID = 0;
}
void LLLMImpl::onDisconnect(const Leap::Controller& controller)
{
LL_INFOS("LeapMotion") << "Disconnected" << LL_ENDL;
mLMConnected = false;
}
// Callback from Leapmotion code when a new frame is available. It simply
// sets a flag so stepFrame() can pick up new controller data
void LLLMImpl::onFrame(const Leap::Controller& controller)
{
if (mLMConnected)
{
// Get the most recent frame and report some basic information
const Leap::Frame frame = controller.frame();
int64_t frame_id = frame.id();
if (frame_id != mCurrentFrameID)
{ // Just record the ID and set flag indicating data is available
mCurrentFrameID = frame_id;
mFrameAvailable = true;
}
}
}
// This is called every SL viewer frame
void LLLMImpl::stepFrame()
{
if (mLMController
&& mFrameAvailable
&& mLMConnected)
{
mFrameAvailable = false;
// Get the most recent frame and report some basic information
const Leap::Frame frame = mLMController->frame();
Leap::HandList hands = frame.hands();
static LLCachedControl<S32> sControllerMode(gSavedSettings, "LeapMotionTestMode");
switch (sControllerMode)
{
case 1:
modeFlyingControlTest(hands);
break;
case 2:
modeStreamDataToSL(hands);
break;
case 3:
modeGestureDetection1(hands);
break;
case 4:
modeMoveAndCamTest1(hands);
break;
case 411:
modeDumpDebugInfo(hands);
break;
default:
// Do nothing
break;
}
}
}
// This controller mode is used to fly the avatar, going up, down, forward and turning.
void LLLMImpl::modeFlyingControlTest(Leap::HandList & hands)
{
static S32 sLMFlyingHysteresis = 0;
S32 numHands = hands.count();
BOOL agent_is_flying = gAgent.getFlying();
if (numHands == 0
&& agent_is_flying
&& sLMFlyingHysteresis > 0)
{
sLMFlyingHysteresis--;
if (sLMFlyingHysteresis == 0)
{
LL_INFOS("LeapMotion") << "LM stop flying - look ma, no hands!" << LL_ENDL;
gAgent.setFlying(FALSE);
}
}
else if (numHands == 1)
{
// Get the first hand
Leap::Hand hand = hands[0];
// Check if the hand has any fingers
Leap::FingerList finger_list = hand.fingers();
S32 num_fingers = finger_list.count();
Leap::Vector palm_pos = hand.palmPosition();
Leap::Vector palm_normal = hand.palmNormal();
F32 ball_radius = (F32) hand.sphereRadius();
Leap::Vector ball_center = hand.sphereCenter();
// Number of fingers controls flying on / off
if (num_fingers == 0 && // To do - add hysteresis or data smoothing?
agent_is_flying)
{
if (sLMFlyingHysteresis > 0)
{
sLMFlyingHysteresis--;
}
else
{
LL_INFOS("LeapMotion") << "LM stop flying" << LL_ENDL;
gAgent.setFlying(FALSE);
}
}
else if (num_fingers > 2 &&
!agent_is_flying)
{
LL_INFOS("LeapMotion") << "LM start flying" << LL_ENDL;
gAgent.setFlying(TRUE);
sLMFlyingHysteresis = 5;
}
// Radius of ball controls forward motion
if (agent_is_flying)
{
if (ball_radius > 110.f)
{ // Open hand, move fast
gAgent.setControlFlags(AGENT_CONTROL_AT_POS | AGENT_CONTROL_FAST_AT);
}
else if (ball_radius > 85.f)
{ // Partially open, move slow
gAgent.setControlFlags(AGENT_CONTROL_AT_POS);
}
else
{ // Closed - stop
gAgent.clearControlFlags(AGENT_CONTROL_AT_POS);
}
// Height of palm controls moving up and down
if (palm_pos.y > 260.f)
{ // Go up fast
gAgent.setControlFlags(AGENT_CONTROL_UP_POS | AGENT_CONTROL_FAST_UP);
}
else if (palm_pos.y > 200.f)
{ // Go up
gAgent.setControlFlags(AGENT_CONTROL_UP_POS);
}
else if (palm_pos.y < 60.f)
{ // Go down fast
gAgent.setControlFlags(AGENT_CONTROL_FAST_UP | AGENT_CONTROL_UP_NEG);
}
else if (palm_pos.y < 120.f)
{ // Go down
gAgent.setControlFlags(AGENT_CONTROL_UP_NEG);
}
else
{ // Clear up / down
gAgent.clearControlFlags(AGENT_CONTROL_FAST_UP | AGENT_CONTROL_UP_POS | AGENT_CONTROL_UP_NEG);
}
// Palm normal going left / right controls direction
if (mYawTimer.checkExpirationAndReset(LLLEAP_YAW_INTERVAL))
{
if (palm_normal.x > 0.4)
{ // Go left fast
gAgent.moveYaw(1.f);
}
else if (palm_normal.x < -0.4)
{ // Go right fast
gAgent.moveYaw(-1.f);
}
}
} // end flying controls
}
}
// This experimental mode sends chat messages into SL on a back channel for LSL scripts
// to intercept with a listen() event. This is experimental and not sustainable for
// a production feature ... many avatars using this would flood the chat system and
// hurt server performance. Depending on how useful this proves to be, a better
// mechanism should be designed to stream data from the viewer into SL scripts.
void LLLMImpl::modeStreamDataToSL(Leap::HandList & hands)
{
S32 numHands = hands.count();
if (numHands == 1 &&
mChatMsgTimer.checkExpirationAndReset(LLLEAP_CHAT_MSG_INTERVAL))
{
// Get the first (and only) hand
Leap::Hand hand = hands[0];
Leap::Vector palm_pos = hand.palmPosition();
Leap::Vector palm_normal = hand.palmNormal();
F32 ball_radius = (F32) hand.sphereRadius();
Leap::Vector ball_center = hand.sphereCenter();
// Chat message looks like "/2343 LM1,<palm pos>,<palm normal>,<sphere center>,<sphere radius>"
LLVector3 vec;
std::stringstream status_chat_msg;
status_chat_msg << "/2343 LM,";
status_chat_msg << "<" << palm_pos.x << "," << palm_pos.y << "," << palm_pos.z << ">,";
status_chat_msg << "<" << palm_normal.x << "," << palm_normal.y << "," << palm_normal.z << ">,";
status_chat_msg << "<" << ball_center.x << "," << ball_center.y << "," << ball_center.z << ">," << ball_radius;
FSNearbyChat::instance().sendChatFromViewer(status_chat_msg.str(), CHAT_TYPE_SHOUT, FALSE);
}
}
// This mode tries to detect simple hand motion and either triggers an avatar gesture or
// sends a chat message into SL in response. It is very rough, hard-coded for detecting
// a hand wave (a SL gesture) or the wiggling-thumb gun trigger (a chat message sent to a
// special version of the popgun).
void LLLMImpl::modeGestureDetection1(Leap::HandList & hands)
{
static S32 trigger_direction = -1;
S32 numHands = hands.count();
if (numHands == 1)
{
// Get the first hand
Leap::Hand hand = hands[0];
// Check if the hand has any fingers
Leap::FingerList finger_list = hand.fingers();
S32 num_fingers = finger_list.count();
static S32 last_num_fingers = 0;
if (num_fingers == 1)
{ // One finger ... possibly reset the
Leap::Finger finger = finger_list[0];
Leap::Vector finger_dir = finger.direction();
// Negative Z is into the screen - check that it's the largest component
S32 abs_z_dir = llabs(finger_dir.z);
if (finger_dir.z < -0.5 &&
abs_z_dir > llabs(finger_dir.x) &&
abs_z_dir > llabs(finger_dir.y))
{
Leap::Vector finger_pos = finger.tipPosition();
Leap::Vector finger_vel = finger.tipVelocity();
LL_INFOS("LeapMotion") << "finger direction is " << finger_dir.x << ", " << finger_dir.y << ", " << finger_dir.z
<< ", position " << finger_pos.x << ", " << finger_pos.y << ", " << finger_pos.z
<< ", velocity " << finger_vel.x << ", " << finger_vel.y << ", " << finger_vel.z
<< LL_ENDL;
}
if (trigger_direction != -1)
{
LL_INFOS("LeapMotion") << "Reset trigger_direction - one finger" << LL_ENDL;
trigger_direction = -1;
}
}
else if (num_fingers == 2)
{
Leap::Finger barrel_finger = finger_list[0];
Leap::Vector barrel_finger_dir = barrel_finger.direction();
// Negative Z is into the screen - check that it's the largest component
F32 abs_z_dir = llabs(barrel_finger_dir.z);
if (barrel_finger_dir.z < -0.5f &&
abs_z_dir > llabs(barrel_finger_dir.x) &&
abs_z_dir > llabs(barrel_finger_dir.y))
{
Leap::Finger thumb_finger = finger_list[1];
Leap::Vector thumb_finger_dir = thumb_finger.direction();
Leap::Vector thumb_finger_pos = thumb_finger.tipPosition();
Leap::Vector thumb_finger_vel = thumb_finger.tipVelocity();
if ((thumb_finger_dir.x < barrel_finger_dir.x) )
{ // Trigger gunfire
if (trigger_direction < 0 && // Haven't fired
thumb_finger_vel.x > 50.f && // Moving into screen
thumb_finger_vel.z < -50.f &&
mChatMsgTimer.checkExpirationAndReset(LLLEAP_CHAT_MSG_INTERVAL))
{
// Chat message looks like "/2343 LM2 gunfire"
std::string gesture_chat_msg("/2343 LM2 gunfire");
//LLNearbyChatBar::sendChatFromViewer(gesture_chat_msg, CHAT_TYPE_SHOUT, FALSE);
trigger_direction = 1;
LL_INFOS("LeapMotion") << "Sent gunfire chat" << LL_ENDL;
}
else if (trigger_direction > 0 && // Have fired, need to pull thumb back
thumb_finger_vel.x < -50.f &&
thumb_finger_vel.z > 50.f) // Moving out of screen
{
trigger_direction = -1;
LL_INFOS("LeapMotion") << "Reset trigger_direction" << LL_ENDL;
}
}
}
else if (trigger_direction != -1)
{
LL_INFOS("LeapMotion") << "Reset trigger_direction - hand pos" << LL_ENDL;
trigger_direction = -1;
}
}
else if (num_fingers == 5 &&
num_fingers == last_num_fingers)
{
if (mGestureTimer.checkExpirationAndReset(LLLEAP_GESTURE_INTERVAL))
{
// figure out a gesture to trigger
std::string gestureString("/overhere");
LLGestureMgr::instance().triggerAndReviseString( gestureString );
}
}
last_num_fingers = num_fingers;
}
}
// This mode tries to move the avatar and camera in Second Life. It's pretty rough and needs a lot of work
void LLLMImpl::modeMoveAndCamTest1(Leap::HandList & hands)
{
S32 numHands = hands.count();
if (numHands == 1)
{
// Get the first hand
Leap::Hand hand = hands[0];
// Check if the hand has any fingers
Leap::FingerList finger_list = hand.fingers();
S32 num_fingers = finger_list.count();
F32 orbit_rate = 0.f;
Leap::Vector pos(0, 0, 0);
for (size_t i = 0; i < num_fingers; ++i)
{
Leap::Finger finger = finger_list[i];
pos += finger.tipPosition();
}
pos = Leap::Vector(pos.x/num_fingers, pos.y/num_fingers, pos.z/num_fingers);
if (num_fingers == 1)
{ // 1 finger - move avatar
if (pos.x < -LM_DEAD_ZONE)
{ // Move left
gAgent.moveLeftNudge(1.f);
}
else if (pos.x > LM_DEAD_ZONE)
{
gAgent.moveLeftNudge(-1.f);
}
/*
if (pos.z < -LM_DEAD_ZONE)
{
gAgent.moveAtNudge(1.f);
}
else if (pos.z > LM_DEAD_ZONE)
{
gAgent.moveAtNudge(-1.f);
} */
if (pos.y < -LM_DEAD_ZONE)
{
gAgent.moveYaw(-1.f);
}
else if (pos.y > LM_DEAD_ZONE)
{
gAgent.moveYaw(1.f);
}
} // end 1 finger
else if (num_fingers == 2)
{ // 2 fingers - move camera around
// X values run from about -170 to +170
if (pos.x < -LM_DEAD_ZONE)
{ // Camera rotate left
gAgentCamera.unlockView();
orbit_rate = (llabs(pos.x) - LM_DEAD_ZONE) / LM_ORBIT_RATE_FACTOR;
gAgentCamera.setOrbitLeftKey(orbit_rate);
}
else if (pos.x > LM_DEAD_ZONE)
{
gAgentCamera.unlockView();
orbit_rate = (pos.x - LM_DEAD_ZONE) / LM_ORBIT_RATE_FACTOR;
gAgentCamera.setOrbitRightKey(orbit_rate);
}
if (pos.z < -LM_DEAD_ZONE)
{ // Camera zoom in
gAgentCamera.unlockView();
orbit_rate = (llabs(pos.z) - LM_DEAD_ZONE) / LM_ORBIT_RATE_FACTOR;
gAgentCamera.setOrbitInKey(orbit_rate);
}
else if (pos.z > LM_DEAD_ZONE)
{ // Camera zoom out
gAgentCamera.unlockView();
orbit_rate = (pos.z - LM_DEAD_ZONE) / LM_ORBIT_RATE_FACTOR;
gAgentCamera.setOrbitOutKey(orbit_rate);
}
if (pos.y < -LM_DEAD_ZONE)
{ // Camera zoom in
gAgentCamera.unlockView();
orbit_rate = (llabs(pos.y) - LM_DEAD_ZONE) / LM_ORBIT_RATE_FACTOR;
gAgentCamera.setOrbitUpKey(orbit_rate);
}
else if (pos.y > LM_DEAD_ZONE)
{ // Camera zoom out
gAgentCamera.unlockView();
orbit_rate = (pos.y - LM_DEAD_ZONE) / LM_ORBIT_RATE_FACTOR;
gAgentCamera.setOrbitDownKey(orbit_rate);
}
} // end 2 finger
}
}
// This controller mode just dumps out a bunch of the Leap Motion device data, which can then be
// analyzed for other use.
void LLLMImpl::modeDumpDebugInfo(Leap::HandList & hands)
{
S32 numHands = hands.count();
if (numHands == 1)
{
// Get the first hand
Leap::Hand hand = hands[0];
// Check if the hand has any fingers
Leap::FingerList finger_list = hand.fingers();
S32 num_fingers = finger_list.count();
if (num_fingers >= 1)
{ // Calculate the hand's average finger tip position
Leap::Vector pos(0, 0, 0);
Leap::Vector direction(0, 0, 0);
for (size_t i = 0; i < num_fingers; ++i)
{
Leap::Finger finger = finger_list[i];
pos += finger.tipPosition();
direction += finger.direction();
// Lots of log spam
LL_INFOS("LeapMotion") << "Finger " << i << " string is " << finger.toString() << LL_ENDL;
}
pos = Leap::Vector(pos.x/num_fingers, pos.y/num_fingers, pos.z/num_fingers);
direction = Leap::Vector(direction.x/num_fingers, direction.y/num_fingers, direction.z/num_fingers);
LL_INFOS("LeapMotion") << "Hand has " << num_fingers << " fingers with average tip position"
<< " (" << pos.x << ", " << pos.y << ", " << pos.z << ")"
<< " direction (" << direction.x << ", " << direction.y << ", " << direction.z << ")"
<< LL_ENDL;
}
Leap::Vector palm_pos = hand.palmPosition();
Leap::Vector palm_normal = hand.palmNormal();
LL_INFOS("LeapMotion") << "Palm pos " << palm_pos.x
<< ", " << palm_pos.y
<< ", " << palm_pos.z
<< ". Normal: " << palm_normal.x
<< ", " << palm_normal.y
<< ", " << palm_normal.z
<< LL_ENDL;
F32 ball_radius = (F32) hand.sphereRadius();
Leap::Vector ball_center = hand.sphereCenter();
LL_INFOS("LeapMotion") << "Ball pos " << ball_center.x
<< ", " << ball_center.y
<< ", " << ball_center.z
<< ", radius " << ball_radius
<< LL_ENDL;
} // dump_out_data
}
// --------------------------------------------------------------------------------
// The LLLeapMotionController class is a thin public glue layer into the LLLMImpl
// class, which does all the interesting work.
// One controller instance to rule them all
LLLeapMotionController::LLLeapMotionController()
{
mController = new LLLMImpl();
}
LLLeapMotionController::~LLLeapMotionController()
{
delete mController;
mController = NULL;
}
// Called every viewer frame
void LLLeapMotionController::stepFrame()
{
if (mController &&
STATE_STARTED == LLStartUp::getStartupState())
{
mController->stepFrame();
}
}

View File

@ -0,0 +1,45 @@
/**
* @file llleapmotioncontroller.h
* @brief LLLeapMotionController class definition
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_LLLEAPMOTIONCONTROLLER_H
#define LL_LLLEAPMOTIONCONTROLLER_H
class LLLMImpl;
class LLLeapMotionController
{
public:
LLLeapMotionController();
~LLLeapMotionController();
// Called every viewer frame
void stepFrame();
protected:
LLLMImpl * mController;
};
#endif // LL_LLLEAPMOTIONCONTROLLER_H

View File

@ -470,6 +470,15 @@ class WindowsManifest(ViewerManifest):
self.path("fmodex64.dll")
except:
print "Skipping fmodex audio library(assuming other audio engine)"
# Get Leap Motion SDK
try:
if self.args['configuration'].lower() == 'debug':
self.path("Leapd.dll")
else:
self.path("Leap.dll")
except:
print "Leap Motion library was not found"
# For textures
if self.args['configuration'].lower() == 'debug':
@ -945,6 +954,7 @@ class DarwinManifest(ViewerManifest):
"libfmodex.dylib",
"libfmodexL.dylib",
"libGLOD.dylib",
"libLeap.dylib",
):
dylibs += path_optional(os.path.join(libdir, libfile), libfile)
@ -1403,6 +1413,11 @@ class Linux_i686Manifest(LinuxManifest):
except:
print "Skipping libfmodex.so - not found"
pass
try:
self.path("libLeap.so")
except:
print "Leap Motion library not found"
self.end_prefix("lib")

View File

@ -17,6 +17,7 @@ FALSE=1
# <string>-DINSTALL_PROPRIETARY=FALSE</string>
# <string>-DUSE_KDU=TRUE</string>
# <string>-DFMODEX:BOOL=ON</string>
# <string>-DLEAPMOTION:BOOL=OFF</string>
# <string>-DOPENSIM:BOOL=ON</string>
# <string>-DUSE_AVX_OPTIMIZATION:BOOL=OFF</string>
# <string>-DLL_TESTS:BOOL=OFF</string>
@ -33,6 +34,7 @@ WANTS_PACKAGE=$FALSE
WANTS_VERSION=$FALSE
WANTS_KDU=$FALSE
WANTS_FMODEX=$FALSE
WANTS_LEAPMOTION=$FALSE
WANTS_OPENSIM=$TRUE
WANTS_AVX=$FALSE
WANTS_BUILD=$FALSE
@ -62,6 +64,7 @@ showUsage()
echo " --package : Build installer"
echo " --no-package : Build without installer (Overrides --package)"
echo " --fmodex : Build with FMOD Ex"
echo " --leapmotion : Build with Leap Motion Controller support"
echo " --opensim : Build with OpenSim support (Disables Havok features)"
echo " --no-opensim : Build without OpenSim support (Overrides --opensim)"
echo " --avx : Build with Advanced Vector Extensions (Windows only)"
@ -76,7 +79,7 @@ getArgs()
# $* = the options passed in from main
{
if [ $# -gt 0 ]; then
while getoptex "clean build config version package no-package fmodex jobs: platform: kdu opensim no-opensim avx help chan: btype:" "$@" ; do
while getoptex "clean build config version package no-package fmodex jobs: platform: kdu leapmotion opensim no-opensim avx help chan: btype:" "$@" ; do
#insure options are valid
if [ -z "$OPTOPT" ] ; then
@ -95,6 +98,7 @@ getArgs()
;;
kdu) WANTS_KDU=$TRUE;;
fmodex) WANTS_FMODEX=$TRUE;;
leapmotion) WANTS_LEAPMOTION=$TRUE;;
opensim) WANTS_OPENSIM=$TRUE;;
no-opensim) WANTS_OPENSIM=$FALSE;;
avx) WANTS_AVX=$TRUE;;
@ -271,6 +275,7 @@ echo -e "configure_firestorm.py" > $LOG
echo -e " PLATFORM: '$PLATFORM'" | tee -a $LOG
echo -e " KDU: `b2a $WANTS_KDU`" | tee -a $LOG
echo -e " FMODEX: `b2a $WANTS_FMODEX`" | tee -a $LOG
echo -e " LEAPMOTION: `b2a $WANTS_LEAPMOTION`" | tee -a $LOG
echo -e " OPENSIM: `b2a $WANTS_OPENSIM`" | tee -a $LOG
echo -e " AVX: `b2a $WANTS_AVX` " | tee -a $LOG
echo -e " PACKAGE: `b2a $WANTS_PACKAGE`" | tee -a $LOG
@ -356,6 +361,11 @@ if [ $WANTS_CONFIG -eq $TRUE ] ; then
else
FMODEX="-DFMODEX:BOOL=OFF"
fi
if [ $WANTS_LEAPMOTION -eq $TRUE ] ; then
LEAPMOTION="-DLEAPMOTION:BOOL=ON"
else
LEAPMOTION="-DLEAPMOTION:BOOL=OFF"
fi
if [ $WANTS_OPENSIM -eq $TRUE ] ; then
OPENSIM="-DOPENSIM:BOOL=ON"
else
@ -407,7 +417,7 @@ if [ $WANTS_CONFIG -eq $TRUE ] ; then
UNATTENDED="-DUNATTENDED=ON"
fi
cmake -G "$TARGET" ../indra $CHANNEL $FMODEX $KDU $OPENSIM $AVX_OPTIMIZATION $PACKAGE $UNATTENDED -DLL_TESTS:BOOL=OFF -DWORD_SIZE:STRING=32 -DCMAKE_BUILD_TYPE:STRING=$BTYPE \
cmake -G "$TARGET" ../indra $CHANNEL $FMODEX $KDU $LEAPMOTION $OPENSIM $AVX_OPTIMIZATION $PACKAGE $UNATTENDED -DLL_TESTS:BOOL=OFF -DWORD_SIZE:STRING=32 -DCMAKE_BUILD_TYPE:STRING=$BTYPE \
-DNDTARGET_ARCH="${TARGET_ARCH}" -DROOT_PROJECT_NAME:STRING=Firestorm $LL_ARGS_PASSTHRU | tee $LOG
if [ $PLATFORM == "win32" ] ; then