Example #1
0
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
	}
}