bool LLTrackingData::haveTrackingInfo() { LLViewerObject* object = gObjectList.findObject(mAvatarID); if(object && !object->isDead()) { mCoarseLocationTimer.checkExpirationAndReset(COARSE_FREQUENCY); mUpdateTimer.setTimerExpirySec(FIND_FREQUENCY); mAgentGone.setTimerExpirySec(OFFLINE_SECONDS); mHaveInfo = true; return true; } if(mHaveCoarseInfo && !mCoarseLocationTimer.checkExpirationAndReset(COARSE_FREQUENCY)) { // if we reach here, then we have a 'recent' coarse update mUpdateTimer.setTimerExpirySec(FIND_FREQUENCY); mAgentGone.setTimerExpirySec(OFFLINE_SECONDS); return true; } if(mUpdateTimer.checkExpirationAndReset(FIND_FREQUENCY)) { LLAvatarTracker::instance().findAgent(); mHaveCoarseInfo = false; } if(mAgentGone.checkExpirationAndReset(OFFLINE_SECONDS)) { mHaveInfo = false; mHaveCoarseInfo = false; } return mHaveInfo; }
// 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 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.4f) { // Go left fast gAgent.moveYaw(1.f); } else if (palm_normal.x < -0.4f) { // Go right fast gAgent.moveYaw(-1.f); } } } // end flying controls } }