PageRenderTime 232ms CodeModel.GetById 60ms app.highlight 123ms RepoModel.GetById 38ms app.codeStats 1ms

/indra/newview/llviewerjoystick.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 1161 lines | 891 code | 145 blank | 125 comment | 184 complexity | 7525d3f8d89bc502c42653702c8f30db MD5 | raw file
   1/** 
   2 * @file llviewerjoystick.cpp
   3 * @brief Joystick / NDOF device functionality.
   4 *
   5 * $LicenseInfo:firstyear=2002&license=viewerlgpl$
   6 * Second Life Viewer Source Code
   7 * Copyright (C) 2010, Linden Research, Inc.
   8 * 
   9 * This library is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU Lesser General Public
  11 * License as published by the Free Software Foundation;
  12 * version 2.1 of the License only.
  13 * 
  14 * This library is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 * Lesser General Public License for more details.
  18 * 
  19 * You should have received a copy of the GNU Lesser General Public
  20 * License along with this library; if not, write to the Free Software
  21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  22 * 
  23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  24 * $/LicenseInfo$
  25 */
  26
  27#include "llviewerprecompiledheaders.h"
  28
  29#include "llviewerjoystick.h"
  30
  31#include "llviewercontrol.h"
  32#include "llviewerwindow.h"
  33#include "llviewercamera.h"
  34#include "llappviewer.h"
  35#include "llkeyboard.h"
  36#include "lltoolmgr.h"
  37#include "llselectmgr.h"
  38#include "llviewermenu.h"
  39#include "llagent.h"
  40#include "llagentcamera.h"
  41#include "llfocusmgr.h"
  42
  43
  44// ----------------------------------------------------------------------------
  45// Constants
  46
  47#define  X_I	1
  48#define  Y_I	2
  49#define  Z_I	0
  50#define RX_I	4
  51#define RY_I	5
  52#define RZ_I	3
  53
  54F32  LLViewerJoystick::sLastDelta[] = {0,0,0,0,0,0,0};
  55F32  LLViewerJoystick::sDelta[] = {0,0,0,0,0,0,0};
  56
  57// These constants specify the maximum absolute value coming in from the device.
  58// HACK ALERT! the value of MAX_JOYSTICK_INPUT_VALUE is not arbitrary as it 
  59// should be.  It has to be equal to 3000 because the SpaceNavigator on Windows
  60// refuses to respond to the DirectInput SetProperty call; it always returns 
  61// values in the [-3000, 3000] range.
  62#define MAX_SPACENAVIGATOR_INPUT  3000.0f
  63#define MAX_JOYSTICK_INPUT_VALUE  MAX_SPACENAVIGATOR_INPUT
  64
  65// -----------------------------------------------------------------------------
  66void LLViewerJoystick::updateEnabled(bool autoenable)
  67{
  68	if (mDriverState == JDS_UNINITIALIZED)
  69	{
  70		gSavedSettings.setBOOL("JoystickEnabled", FALSE );
  71	}
  72	else
  73	{
  74		if (isLikeSpaceNavigator() && autoenable)
  75		{
  76			gSavedSettings.setBOOL("JoystickEnabled", TRUE );
  77		}
  78	}
  79	if (!gSavedSettings.getBOOL("JoystickEnabled"))
  80	{
  81		mOverrideCamera = FALSE;
  82	}
  83}
  84
  85void LLViewerJoystick::setOverrideCamera(bool val)
  86{
  87	if (!gSavedSettings.getBOOL("JoystickEnabled"))
  88	{
  89		mOverrideCamera = FALSE;
  90	}
  91	else
  92	{
  93		mOverrideCamera = val;
  94	}
  95
  96	if (mOverrideCamera)
  97	{
  98		gAgentCamera.changeCameraToDefault();
  99	}
 100}
 101
 102// -----------------------------------------------------------------------------
 103#if LIB_NDOF
 104NDOF_HotPlugResult LLViewerJoystick::HotPlugAddCallback(NDOF_Device *dev)
 105{
 106	NDOF_HotPlugResult res = NDOF_DISCARD_HOTPLUGGED;
 107	LLViewerJoystick* joystick(LLViewerJoystick::getInstance());
 108	if (joystick->mDriverState == JDS_UNINITIALIZED)
 109	{
 110        llinfos << "HotPlugAddCallback: will use device:" << llendl;
 111		ndof_dump(dev);
 112		joystick->mNdofDev = dev;
 113        joystick->mDriverState = JDS_INITIALIZED;
 114        res = NDOF_KEEP_HOTPLUGGED;
 115	}
 116	joystick->updateEnabled(true);
 117    return res;
 118}
 119#endif
 120
 121// -----------------------------------------------------------------------------
 122#if LIB_NDOF
 123void LLViewerJoystick::HotPlugRemovalCallback(NDOF_Device *dev)
 124{
 125	LLViewerJoystick* joystick(LLViewerJoystick::getInstance());
 126	if (joystick->mNdofDev == dev)
 127	{
 128        llinfos << "HotPlugRemovalCallback: joystick->mNdofDev=" 
 129				<< joystick->mNdofDev << "; removed device:" << llendl;
 130		ndof_dump(dev);
 131		joystick->mDriverState = JDS_UNINITIALIZED;
 132	}
 133	joystick->updateEnabled(true);
 134}
 135#endif
 136
 137// -----------------------------------------------------------------------------
 138LLViewerJoystick::LLViewerJoystick()
 139:	mDriverState(JDS_UNINITIALIZED),
 140	mNdofDev(NULL),
 141	mResetFlag(false),
 142	mCameraUpdated(true),
 143	mOverrideCamera(false),
 144	mJoystickRun(0)
 145{
 146	for (int i = 0; i < 6; i++)
 147	{
 148		mAxes[i] = sDelta[i] = sLastDelta[i] = 0.0f;
 149	}
 150	
 151	memset(mBtn, 0, sizeof(mBtn));
 152
 153	// factor in bandwidth? bandwidth = gViewerStats->mKBitStat
 154	mPerfScale = 4000.f / gSysCPU.getMHz(); // hmm.  why?
 155}
 156
 157// -----------------------------------------------------------------------------
 158LLViewerJoystick::~LLViewerJoystick()
 159{
 160	if (mDriverState == JDS_INITIALIZED)
 161	{
 162		terminate();
 163	}
 164}
 165
 166// -----------------------------------------------------------------------------
 167void LLViewerJoystick::init(bool autoenable)
 168{
 169#if LIB_NDOF
 170	static bool libinit = false;
 171	mDriverState = JDS_INITIALIZING;
 172
 173	if (libinit == false)
 174	{
 175		// Note: The HotPlug callbacks are not actually getting called on Windows
 176		if (ndof_libinit(HotPlugAddCallback, 
 177						 HotPlugRemovalCallback, 
 178						 NULL))
 179		{
 180			mDriverState = JDS_UNINITIALIZED;
 181		}
 182		else
 183		{
 184			// NB: ndof_libinit succeeds when there's no device
 185			libinit = true;
 186
 187			// allocate memory once for an eventual device
 188			mNdofDev = ndof_create();
 189		}
 190	}
 191
 192	if (libinit)
 193	{
 194		if (mNdofDev)
 195		{
 196			// Different joysticks will return different ranges of raw values.
 197			// Since we want to handle every device in the same uniform way, 
 198			// we initialize the mNdofDev struct and we set the range 
 199			// of values we would like to receive. 
 200			// 
 201			// HACK: On Windows, libndofdev passes our range to DI with a 
 202			// SetProperty call. This works but with one notable exception, the
 203			// SpaceNavigator, who doesn't seem to care about the SetProperty
 204			// call. In theory, we should handle this case inside libndofdev. 
 205			// However, the range we're setting here is arbitrary anyway, 
 206			// so let's just use the SpaceNavigator range for our purposes. 
 207			mNdofDev->axes_min = (long)-MAX_JOYSTICK_INPUT_VALUE;
 208			mNdofDev->axes_max = (long)+MAX_JOYSTICK_INPUT_VALUE;
 209
 210			// libndofdev could be used to return deltas.  Here we choose to
 211			// just have the absolute values instead.
 212			mNdofDev->absolute = 1;
 213
 214			// init & use the first suitable NDOF device found on the USB chain
 215			if (ndof_init_first(mNdofDev, NULL))
 216			{
 217				mDriverState = JDS_UNINITIALIZED;
 218				llwarns << "ndof_init_first FAILED" << llendl;
 219			}
 220			else
 221			{
 222				mDriverState = JDS_INITIALIZED;
 223			}
 224		}
 225		else
 226		{
 227			mDriverState = JDS_UNINITIALIZED;
 228		}
 229	}
 230
 231	// Autoenable the joystick for recognized devices if nothing was connected previously
 232	if (!autoenable)
 233	{
 234		autoenable = gSavedSettings.getString("JoystickInitialized").empty() ? true : false;
 235	}
 236	updateEnabled(autoenable);
 237	
 238	if (mDriverState == JDS_INITIALIZED)
 239	{
 240		// A Joystick device is plugged in
 241		if (isLikeSpaceNavigator())
 242		{
 243			// It's a space navigator, we have defaults for it.
 244			if (gSavedSettings.getString("JoystickInitialized") != "SpaceNavigator")
 245			{
 246				// Only set the defaults if we haven't already (in case they were overridden)
 247				setSNDefaults();
 248				gSavedSettings.setString("JoystickInitialized", "SpaceNavigator");
 249			}
 250		}
 251		else
 252		{
 253			// It's not a Space Navigator
 254			gSavedSettings.setString("JoystickInitialized", "UnknownDevice");
 255		}
 256	}
 257	else
 258	{
 259		// No device connected, don't change any settings
 260	}
 261	
 262	llinfos << "ndof: mDriverState=" << mDriverState << "; mNdofDev=" 
 263			<< mNdofDev << "; libinit=" << libinit << llendl;
 264#endif
 265}
 266
 267// -----------------------------------------------------------------------------
 268void LLViewerJoystick::terminate()
 269{
 270#if LIB_NDOF
 271
 272	ndof_libcleanup();
 273	llinfos << "Terminated connection with NDOF device." << llendl;
 274	mDriverState = JDS_UNINITIALIZED;
 275#endif
 276}
 277
 278// -----------------------------------------------------------------------------
 279void LLViewerJoystick::updateStatus()
 280{
 281#if LIB_NDOF
 282
 283	ndof_update(mNdofDev);
 284
 285	for (int i=0; i<6; i++)
 286	{
 287		mAxes[i] = (F32) mNdofDev->axes[i] / mNdofDev->axes_max;
 288	}
 289
 290	for (int i=0; i<16; i++)
 291	{
 292		mBtn[i] = mNdofDev->buttons[i];
 293	}
 294	
 295#endif
 296}
 297
 298// -----------------------------------------------------------------------------
 299F32 LLViewerJoystick::getJoystickAxis(U32 axis) const
 300{
 301	if (axis < 6)
 302	{
 303		return mAxes[axis];
 304	}
 305	return 0.f;
 306}
 307
 308// -----------------------------------------------------------------------------
 309U32 LLViewerJoystick::getJoystickButton(U32 button) const
 310{
 311	if (button < 16)
 312	{
 313		return mBtn[button];
 314	}
 315	return 0;
 316}
 317
 318// -----------------------------------------------------------------------------
 319void LLViewerJoystick::handleRun(F32 inc)
 320{
 321	// Decide whether to walk or run by applying a threshold, with slight
 322	// hysteresis to avoid oscillating between the two with input spikes.
 323	// Analog speed control would be better, but not likely any time soon.
 324	if (inc > gSavedSettings.getF32("JoystickRunThreshold"))
 325	{
 326		if (1 == mJoystickRun)
 327		{
 328			++mJoystickRun;
 329			gAgent.setRunning();
 330			gAgent.sendWalkRun(gAgent.getRunning());
 331		}
 332		else if (0 == mJoystickRun)
 333		{
 334			// hysteresis - respond NEXT frame
 335			++mJoystickRun;
 336		}
 337	}
 338	else
 339	{
 340		if (mJoystickRun > 0)
 341		{
 342			--mJoystickRun;
 343			if (0 == mJoystickRun)
 344			{
 345				gAgent.clearRunning();
 346				gAgent.sendWalkRun(gAgent.getRunning());
 347			}
 348		}
 349	}
 350}
 351
 352// -----------------------------------------------------------------------------
 353void LLViewerJoystick::agentJump()
 354{
 355    gAgent.moveUp(1);
 356}
 357
 358// -----------------------------------------------------------------------------
 359void LLViewerJoystick::agentSlide(F32 inc)
 360{
 361	if (inc < 0.f)
 362	{
 363		gAgent.moveLeft(1);
 364	}
 365	else if (inc > 0.f)
 366	{
 367		gAgent.moveLeft(-1);
 368	}
 369}
 370
 371// -----------------------------------------------------------------------------
 372void LLViewerJoystick::agentPush(F32 inc)
 373{
 374	if (inc < 0.f)                            // forward
 375	{
 376		gAgent.moveAt(1, false);
 377	}
 378	else if (inc > 0.f)                       // backward
 379	{
 380		gAgent.moveAt(-1, false);
 381	}
 382}
 383
 384// -----------------------------------------------------------------------------
 385void LLViewerJoystick::agentFly(F32 inc)
 386{
 387	if (inc < 0.f)
 388	{
 389		if (! (gAgent.getFlying() ||
 390		       !gAgent.canFly() ||
 391		       gAgent.upGrabbed() ||
 392		       !gSavedSettings.getBOOL("AutomaticFly")) )
 393		{
 394			gAgent.setFlying(true);
 395		}
 396		gAgent.moveUp(1);
 397	}
 398	else if (inc > 0.f)
 399	{
 400		// crouch
 401		gAgent.moveUp(-1);
 402	}
 403}
 404
 405// -----------------------------------------------------------------------------
 406void LLViewerJoystick::agentPitch(F32 pitch_inc)
 407{
 408	if (pitch_inc < 0)
 409	{
 410		gAgent.setControlFlags(AGENT_CONTROL_PITCH_POS);
 411	}
 412	else if (pitch_inc > 0)
 413	{
 414		gAgent.setControlFlags(AGENT_CONTROL_PITCH_NEG);
 415	}
 416	
 417	gAgent.pitch(-pitch_inc);
 418}
 419
 420// -----------------------------------------------------------------------------
 421void LLViewerJoystick::agentYaw(F32 yaw_inc)
 422{	
 423	// Cannot steer some vehicles in mouselook if the script grabs the controls
 424	if (gAgentCamera.cameraMouselook() && !gSavedSettings.getBOOL("JoystickMouselookYaw"))
 425	{
 426		gAgent.rotate(-yaw_inc, gAgent.getReferenceUpVector());
 427	}
 428	else
 429	{
 430		if (yaw_inc < 0)
 431		{
 432			gAgent.setControlFlags(AGENT_CONTROL_YAW_POS);
 433		}
 434		else if (yaw_inc > 0)
 435		{
 436			gAgent.setControlFlags(AGENT_CONTROL_YAW_NEG);
 437		}
 438
 439		gAgent.yaw(-yaw_inc);
 440	}
 441}
 442
 443// -----------------------------------------------------------------------------
 444void LLViewerJoystick::resetDeltas(S32 axis[])
 445{
 446	for (U32 i = 0; i < 6; i++)
 447	{
 448		sLastDelta[i] = -mAxes[axis[i]];
 449		sDelta[i] = 0.f;
 450	}
 451
 452	sLastDelta[6] = sDelta[6] = 0.f;
 453	mResetFlag = false;
 454}
 455
 456// -----------------------------------------------------------------------------
 457void LLViewerJoystick::moveObjects(bool reset)
 458{
 459	static bool toggle_send_to_sim = false;
 460
 461	if (!gFocusMgr.getAppHasFocus() || mDriverState != JDS_INITIALIZED
 462		|| !gSavedSettings.getBOOL("JoystickEnabled") || !gSavedSettings.getBOOL("JoystickBuildEnabled"))
 463	{
 464		return;
 465	}
 466
 467	S32 axis[] = 
 468	{
 469		gSavedSettings.getS32("JoystickAxis0"),
 470		gSavedSettings.getS32("JoystickAxis1"),
 471		gSavedSettings.getS32("JoystickAxis2"),
 472		gSavedSettings.getS32("JoystickAxis3"),
 473		gSavedSettings.getS32("JoystickAxis4"),
 474		gSavedSettings.getS32("JoystickAxis5"),
 475	};
 476
 477	if (reset || mResetFlag)
 478	{
 479		resetDeltas(axis);
 480		return;
 481	}
 482
 483	F32 axis_scale[] =
 484	{
 485		gSavedSettings.getF32("BuildAxisScale0"),
 486		gSavedSettings.getF32("BuildAxisScale1"),
 487		gSavedSettings.getF32("BuildAxisScale2"),
 488		gSavedSettings.getF32("BuildAxisScale3"),
 489		gSavedSettings.getF32("BuildAxisScale4"),
 490		gSavedSettings.getF32("BuildAxisScale5"),
 491	};
 492
 493	F32 dead_zone[] =
 494	{
 495		gSavedSettings.getF32("BuildAxisDeadZone0"),
 496		gSavedSettings.getF32("BuildAxisDeadZone1"),
 497		gSavedSettings.getF32("BuildAxisDeadZone2"),
 498		gSavedSettings.getF32("BuildAxisDeadZone3"),
 499		gSavedSettings.getF32("BuildAxisDeadZone4"),
 500		gSavedSettings.getF32("BuildAxisDeadZone5"),
 501	};
 502
 503	F32 cur_delta[6];
 504	F32 time = gFrameIntervalSeconds;
 505
 506	// avoid making ridicously big movements if there's a big drop in fps 
 507	if (time > .2f)
 508	{
 509		time = .2f;
 510	}
 511
 512	// max feather is 32
 513	F32 feather = gSavedSettings.getF32("BuildFeathering"); 
 514	bool is_zero = true, absolute = gSavedSettings.getBOOL("Cursor3D");
 515	
 516	for (U32 i = 0; i < 6; i++)
 517	{
 518		cur_delta[i] = -mAxes[axis[i]];
 519		F32 tmp = cur_delta[i];
 520		if (absolute)
 521		{
 522			cur_delta[i] = cur_delta[i] - sLastDelta[i];
 523		}
 524		sLastDelta[i] = tmp;
 525		is_zero = is_zero && (cur_delta[i] == 0.f);
 526			
 527		if (cur_delta[i] > 0)
 528		{
 529			cur_delta[i] = llmax(cur_delta[i]-dead_zone[i], 0.f);
 530		}
 531		else
 532		{
 533			cur_delta[i] = llmin(cur_delta[i]+dead_zone[i], 0.f);
 534		}
 535		cur_delta[i] *= axis_scale[i];
 536		
 537		if (!absolute)
 538		{
 539			cur_delta[i] *= time;
 540		}
 541
 542		sDelta[i] = sDelta[i] + (cur_delta[i]-sDelta[i])*time*feather;
 543	}
 544
 545	U32 upd_type = UPD_NONE;
 546	LLVector3 v;
 547    
 548	if (!is_zero)
 549	{
 550		// Clear AFK state if moved beyond the deadzone
 551		if (gAwayTimer.getElapsedTimeF32() > LLAgent::MIN_AFK_TIME)
 552		{
 553			gAgent.clearAFK();
 554		}
 555		
 556		if (sDelta[0] || sDelta[1] || sDelta[2])
 557		{
 558			upd_type |= UPD_POSITION;
 559			v.setVec(sDelta[0], sDelta[1], sDelta[2]);
 560		}
 561		
 562		if (sDelta[3] || sDelta[4] || sDelta[5])
 563		{
 564			upd_type |= UPD_ROTATION;
 565		}
 566				
 567		// the selection update could fail, so we won't send 
 568		if (LLSelectMgr::getInstance()->selectionMove(v, sDelta[3],sDelta[4],sDelta[5], upd_type))
 569		{
 570			toggle_send_to_sim = true;
 571		}
 572	}
 573	else if (toggle_send_to_sim)
 574	{
 575		LLSelectMgr::getInstance()->sendSelectionMove();
 576		toggle_send_to_sim = false;
 577	}
 578}
 579
 580// -----------------------------------------------------------------------------
 581void LLViewerJoystick::moveAvatar(bool reset)
 582{
 583	if (!gFocusMgr.getAppHasFocus() || mDriverState != JDS_INITIALIZED
 584		|| !gSavedSettings.getBOOL("JoystickEnabled") || !gSavedSettings.getBOOL("JoystickAvatarEnabled"))
 585	{
 586		return;
 587	}
 588
 589	S32 axis[] = 
 590	{
 591		// [1 0 2 4  3  5]
 592		// [Z X Y RZ RX RY]
 593		gSavedSettings.getS32("JoystickAxis0"),
 594		gSavedSettings.getS32("JoystickAxis1"),
 595		gSavedSettings.getS32("JoystickAxis2"),
 596		gSavedSettings.getS32("JoystickAxis3"),
 597		gSavedSettings.getS32("JoystickAxis4"),
 598		gSavedSettings.getS32("JoystickAxis5")
 599	};
 600
 601	if (reset || mResetFlag)
 602	{
 603		resetDeltas(axis);
 604		if (reset)
 605		{
 606			// Note: moving the agent triggers agent camera mode;
 607			//  don't do this every time we set mResetFlag (e.g. because we gained focus)
 608			gAgent.moveAt(0, true);
 609		}
 610		return;
 611	}
 612
 613	bool is_zero = true;
 614	static bool button_held = false;
 615
 616	if (mBtn[1] == 1)
 617	{
 618		// If AutomaticFly is enabled, then button1 merely causes a
 619		// jump (as the up/down axis already controls flying) if on the
 620		// ground, or cease flight if already flying.
 621		// If AutomaticFly is disabled, then button1 toggles flying.
 622		if (gSavedSettings.getBOOL("AutomaticFly"))
 623		{
 624			if (!gAgent.getFlying())
 625			{
 626				gAgent.moveUp(1);
 627			}
 628			else if (!button_held)
 629			{
 630				button_held = true;
 631				gAgent.setFlying(FALSE);
 632			}
 633		}
 634		else if (!button_held)
 635		{
 636			button_held = true;
 637			gAgent.setFlying(!gAgent.getFlying());
 638		}
 639
 640		is_zero = false;
 641	}
 642	else
 643	{
 644		button_held = false;
 645	}
 646
 647	F32 axis_scale[] =
 648	{
 649		gSavedSettings.getF32("AvatarAxisScale0"),
 650		gSavedSettings.getF32("AvatarAxisScale1"),
 651		gSavedSettings.getF32("AvatarAxisScale2"),
 652		gSavedSettings.getF32("AvatarAxisScale3"),
 653		gSavedSettings.getF32("AvatarAxisScale4"),
 654		gSavedSettings.getF32("AvatarAxisScale5")
 655	};
 656
 657	F32 dead_zone[] =
 658	{
 659		gSavedSettings.getF32("AvatarAxisDeadZone0"),
 660		gSavedSettings.getF32("AvatarAxisDeadZone1"),
 661		gSavedSettings.getF32("AvatarAxisDeadZone2"),
 662		gSavedSettings.getF32("AvatarAxisDeadZone3"),
 663		gSavedSettings.getF32("AvatarAxisDeadZone4"),
 664		gSavedSettings.getF32("AvatarAxisDeadZone5")
 665	};
 666
 667	// time interval in seconds between this frame and the previous
 668	F32 time = gFrameIntervalSeconds;
 669
 670	// avoid making ridicously big movements if there's a big drop in fps 
 671	if (time > .2f)
 672	{
 673		time = .2f;
 674	}
 675
 676	// note: max feather is 32.0
 677	F32 feather = gSavedSettings.getF32("AvatarFeathering"); 
 678	
 679	F32 cur_delta[6];
 680	F32 val, dom_mov = 0.f;
 681	U32 dom_axis = Z_I;
 682#if LIB_NDOF
 683    bool absolute = (gSavedSettings.getBOOL("Cursor3D") && mNdofDev->absolute);
 684#else
 685    bool absolute = false;
 686#endif
 687	// remove dead zones and determine biggest movement on the joystick 
 688	for (U32 i = 0; i < 6; i++)
 689	{
 690		cur_delta[i] = -mAxes[axis[i]];
 691		if (absolute)
 692		{
 693			F32 tmp = cur_delta[i];
 694			cur_delta[i] = cur_delta[i] - sLastDelta[i];
 695			sLastDelta[i] = tmp;
 696		}
 697
 698		if (cur_delta[i] > 0)
 699		{
 700			cur_delta[i] = llmax(cur_delta[i]-dead_zone[i], 0.f);
 701		}
 702		else
 703		{
 704			cur_delta[i] = llmin(cur_delta[i]+dead_zone[i], 0.f);
 705		}
 706
 707		// we don't care about Roll (RZ) and Z is calculated after the loop
 708        if (i != Z_I && i != RZ_I)
 709		{
 710			// find out the axis with the biggest joystick motion
 711			val = fabs(cur_delta[i]);
 712			if (val > dom_mov)
 713			{
 714				dom_axis = i;
 715				dom_mov = val;
 716			}
 717		}
 718		
 719		is_zero = is_zero && (cur_delta[i] == 0.f);
 720	}
 721
 722	if (!is_zero)
 723	{
 724		// Clear AFK state if moved beyond the deadzone
 725		if (gAwayTimer.getElapsedTimeF32() > LLAgent::MIN_AFK_TIME)
 726		{
 727			gAgent.clearAFK();
 728		}
 729		
 730		setCameraNeedsUpdate(true);
 731	}
 732
 733	// forward|backward movements overrule the real dominant movement if 
 734	// they're bigger than its 20%. This is what you want 'cos moving forward
 735	// is what you do most. We also added a special (even more lenient) case 
 736	// for RX|RY to allow walking while pitching and turning
 737	if (fabs(cur_delta[Z_I]) > .2f * dom_mov
 738	    || ((dom_axis == RX_I || dom_axis == RY_I) 
 739		&& fabs(cur_delta[Z_I]) > .05f * dom_mov))
 740	{
 741		dom_axis = Z_I;
 742	}
 743
 744	sDelta[X_I] = -cur_delta[X_I] * axis_scale[X_I];
 745	sDelta[Y_I] = -cur_delta[Y_I] * axis_scale[Y_I];
 746	sDelta[Z_I] = -cur_delta[Z_I] * axis_scale[Z_I];
 747	cur_delta[RX_I] *= -axis_scale[RX_I] * mPerfScale;
 748	cur_delta[RY_I] *= -axis_scale[RY_I] * mPerfScale;
 749		
 750	if (!absolute)
 751	{
 752		cur_delta[RX_I] *= time;
 753		cur_delta[RY_I] *= time;
 754	}
 755	sDelta[RX_I] += (cur_delta[RX_I] - sDelta[RX_I]) * time * feather;
 756	sDelta[RY_I] += (cur_delta[RY_I] - sDelta[RY_I]) * time * feather;
 757	
 758	handleRun((F32) sqrt(sDelta[Z_I]*sDelta[Z_I] + sDelta[X_I]*sDelta[X_I]));
 759	
 760	// Allow forward/backward movement some priority
 761	if (dom_axis == Z_I)
 762	{
 763		agentPush(sDelta[Z_I]);			// forward/back
 764		
 765		if (fabs(sDelta[X_I])  > .1f)
 766		{
 767			agentSlide(sDelta[X_I]);	// move sideways
 768		}
 769		
 770		if (fabs(sDelta[Y_I])  > .1f)
 771		{
 772			agentFly(sDelta[Y_I]);		// up/down & crouch
 773		}
 774	
 775		// too many rotations during walking can be confusing, so apply
 776		// the deadzones one more time (quick & dirty), at 50%|30% power
 777		F32 eff_rx = .3f * dead_zone[RX_I];
 778		F32 eff_ry = .3f * dead_zone[RY_I];
 779	
 780		if (sDelta[RX_I] > 0)
 781		{
 782			eff_rx = llmax(sDelta[RX_I] - eff_rx, 0.f);
 783		}
 784		else
 785		{
 786			eff_rx = llmin(sDelta[RX_I] + eff_rx, 0.f);
 787		}
 788
 789		if (sDelta[RY_I] > 0)
 790		{
 791			eff_ry = llmax(sDelta[RY_I] - eff_ry, 0.f);
 792		}
 793		else
 794		{
 795			eff_ry = llmin(sDelta[RY_I] + eff_ry, 0.f);
 796		}
 797		
 798		
 799		if (fabs(eff_rx) > 0.f || fabs(eff_ry) > 0.f)
 800		{
 801			if (gAgent.getFlying())
 802			{
 803				agentPitch(eff_rx);
 804				agentYaw(eff_ry);
 805			}
 806			else
 807			{
 808				agentPitch(eff_rx);
 809				agentYaw(2.f * eff_ry);
 810			}
 811		}
 812	}
 813	else
 814	{
 815		agentSlide(sDelta[X_I]);		// move sideways
 816		agentFly(sDelta[Y_I]);			// up/down & crouch
 817		agentPush(sDelta[Z_I]);			// forward/back
 818		agentPitch(sDelta[RX_I]);		// pitch
 819		agentYaw(sDelta[RY_I]);			// turn
 820	}
 821}
 822
 823// -----------------------------------------------------------------------------
 824void LLViewerJoystick::moveFlycam(bool reset)
 825{
 826	static LLQuaternion 		sFlycamRotation;
 827	static LLVector3    		sFlycamPosition;
 828	static F32          		sFlycamZoom;
 829	
 830	if (!gFocusMgr.getAppHasFocus() || mDriverState != JDS_INITIALIZED
 831		|| !gSavedSettings.getBOOL("JoystickEnabled") || !gSavedSettings.getBOOL("JoystickFlycamEnabled"))
 832	{
 833		return;
 834	}
 835
 836	S32 axis[] = 
 837	{
 838		gSavedSettings.getS32("JoystickAxis0"),
 839		gSavedSettings.getS32("JoystickAxis1"),
 840		gSavedSettings.getS32("JoystickAxis2"),
 841		gSavedSettings.getS32("JoystickAxis3"),
 842		gSavedSettings.getS32("JoystickAxis4"),
 843		gSavedSettings.getS32("JoystickAxis5"),
 844		gSavedSettings.getS32("JoystickAxis6")
 845	};
 846
 847	bool in_build_mode = LLToolMgr::getInstance()->inBuildMode();
 848	if (reset || mResetFlag)
 849	{
 850		sFlycamPosition = LLViewerCamera::getInstance()->getOrigin();
 851		sFlycamRotation = LLViewerCamera::getInstance()->getQuaternion();
 852		sFlycamZoom = LLViewerCamera::getInstance()->getView();
 853		
 854		resetDeltas(axis);
 855
 856		return;
 857	}
 858
 859	F32 axis_scale[] =
 860	{
 861		gSavedSettings.getF32("FlycamAxisScale0"),
 862		gSavedSettings.getF32("FlycamAxisScale1"),
 863		gSavedSettings.getF32("FlycamAxisScale2"),
 864		gSavedSettings.getF32("FlycamAxisScale3"),
 865		gSavedSettings.getF32("FlycamAxisScale4"),
 866		gSavedSettings.getF32("FlycamAxisScale5"),
 867		gSavedSettings.getF32("FlycamAxisScale6")
 868	};
 869
 870	F32 dead_zone[] =
 871	{
 872		gSavedSettings.getF32("FlycamAxisDeadZone0"),
 873		gSavedSettings.getF32("FlycamAxisDeadZone1"),
 874		gSavedSettings.getF32("FlycamAxisDeadZone2"),
 875		gSavedSettings.getF32("FlycamAxisDeadZone3"),
 876		gSavedSettings.getF32("FlycamAxisDeadZone4"),
 877		gSavedSettings.getF32("FlycamAxisDeadZone5"),
 878		gSavedSettings.getF32("FlycamAxisDeadZone6")
 879	};
 880
 881	F32 time = gFrameIntervalSeconds;
 882
 883	// avoid making ridiculously big movements if there's a big drop in fps 
 884	if (time > .2f)
 885	{
 886		time = .2f;
 887	}
 888
 889	F32 cur_delta[7];
 890	F32 feather = gSavedSettings.getF32("FlycamFeathering");
 891	bool absolute = gSavedSettings.getBOOL("Cursor3D");
 892	bool is_zero = true;
 893
 894	for (U32 i = 0; i < 7; i++)
 895	{
 896		cur_delta[i] = -getJoystickAxis(axis[i]);
 897
 898
 899		F32 tmp = cur_delta[i];
 900		if (absolute)
 901		{
 902			cur_delta[i] = cur_delta[i] - sLastDelta[i];
 903		}
 904		sLastDelta[i] = tmp;
 905
 906		if (cur_delta[i] > 0)
 907		{
 908			cur_delta[i] = llmax(cur_delta[i]-dead_zone[i], 0.f);
 909		}
 910		else
 911		{
 912			cur_delta[i] = llmin(cur_delta[i]+dead_zone[i], 0.f);
 913		}
 914
 915		// We may want to scale camera movements up or down in build mode.
 916		// NOTE: this needs to remain after the deadzone calculation, otherwise
 917		// we have issues with flycam "jumping" when the build dialog is opened/closed  -Nyx
 918		if (in_build_mode)
 919		{
 920			if (i == X_I || i == Y_I || i == Z_I)
 921			{
 922				static LLCachedControl<F32> build_mode_scale(gSavedSettings,"FlycamBuildModeScale");
 923				cur_delta[i] *= build_mode_scale;
 924			}
 925		}
 926
 927		cur_delta[i] *= axis_scale[i];
 928		
 929		if (!absolute)
 930		{
 931			cur_delta[i] *= time;
 932		}
 933
 934		sDelta[i] = sDelta[i] + (cur_delta[i]-sDelta[i])*time*feather;
 935
 936		is_zero = is_zero && (cur_delta[i] == 0.f);
 937
 938	}
 939	
 940	// Clear AFK state if moved beyond the deadzone
 941	if (!is_zero && gAwayTimer.getElapsedTimeF32() > LLAgent::MIN_AFK_TIME)
 942	{
 943		gAgent.clearAFK();
 944	}
 945	
 946	sFlycamPosition += LLVector3(sDelta) * sFlycamRotation;
 947
 948	LLMatrix3 rot_mat(sDelta[3], sDelta[4], sDelta[5]);
 949	sFlycamRotation = LLQuaternion(rot_mat)*sFlycamRotation;
 950
 951	if (gSavedSettings.getBOOL("AutoLeveling"))
 952	{
 953		LLMatrix3 level(sFlycamRotation);
 954
 955		LLVector3 x = LLVector3(level.mMatrix[0]);
 956		LLVector3 y = LLVector3(level.mMatrix[1]);
 957		LLVector3 z = LLVector3(level.mMatrix[2]);
 958
 959		y.mV[2] = 0.f;
 960		y.normVec();
 961
 962		level.setRows(x,y,z);
 963		level.orthogonalize();
 964				
 965		LLQuaternion quat(level);
 966		sFlycamRotation = nlerp(llmin(feather*time,1.f), sFlycamRotation, quat);
 967	}
 968
 969	if (gSavedSettings.getBOOL("ZoomDirect"))
 970	{
 971		sFlycamZoom = sLastDelta[6]*axis_scale[6]+dead_zone[6];
 972	}
 973	else
 974	{
 975		sFlycamZoom += sDelta[6];
 976	}
 977
 978	LLMatrix3 mat(sFlycamRotation);
 979
 980	LLViewerCamera::getInstance()->setView(sFlycamZoom);
 981	LLViewerCamera::getInstance()->setOrigin(sFlycamPosition);
 982	LLViewerCamera::getInstance()->mXAxis = LLVector3(mat.mMatrix[0]);
 983	LLViewerCamera::getInstance()->mYAxis = LLVector3(mat.mMatrix[1]);
 984	LLViewerCamera::getInstance()->mZAxis = LLVector3(mat.mMatrix[2]);
 985}
 986
 987// -----------------------------------------------------------------------------
 988bool LLViewerJoystick::toggleFlycam()
 989{
 990	if (!gSavedSettings.getBOOL("JoystickEnabled") || !gSavedSettings.getBOOL("JoystickFlycamEnabled"))
 991	{
 992		mOverrideCamera = false;
 993		return false;
 994	}
 995
 996	if (!mOverrideCamera)
 997	{
 998		gAgentCamera.changeCameraToDefault();
 999	}
1000
1001	if (gAwayTimer.getElapsedTimeF32() > LLAgent::MIN_AFK_TIME)
1002	{
1003		gAgent.clearAFK();
1004	}
1005	
1006	mOverrideCamera = !mOverrideCamera;
1007	if (mOverrideCamera)
1008	{
1009		moveFlycam(true);
1010		
1011	}
1012	else 
1013	{
1014		// Exiting from the flycam mode: since we are going to keep the flycam POV for
1015		// the main camera until the avatar moves, we need to track this situation.
1016		setCameraNeedsUpdate(false);
1017		setNeedsReset(true);
1018	}
1019	return true;
1020}
1021
1022void LLViewerJoystick::scanJoystick()
1023{
1024	if (mDriverState != JDS_INITIALIZED || !gSavedSettings.getBOOL("JoystickEnabled"))
1025	{
1026		return;
1027	}
1028
1029#if LL_WINDOWS
1030	// On windows, the flycam is updated syncronously with a timer, so there is
1031	// no need to update the status of the joystick here.
1032	if (!mOverrideCamera)
1033#endif
1034	updateStatus();
1035
1036	// App focus check Needs to happen AFTER updateStatus in case the joystick
1037	// is not centred when the app loses focus.
1038	if (!gFocusMgr.getAppHasFocus())
1039	{
1040		return;
1041	}
1042
1043	static long toggle_flycam = 0;
1044
1045	if (mBtn[0] == 1)
1046    {
1047		if (mBtn[0] != toggle_flycam)
1048		{
1049			toggle_flycam = toggleFlycam() ? 1 : 0;
1050		}
1051	}
1052	else
1053	{
1054		toggle_flycam = 0;
1055	}
1056	
1057	if (!mOverrideCamera && !(LLToolMgr::getInstance()->inBuildMode() && gSavedSettings.getBOOL("JoystickBuildEnabled")))
1058	{
1059		moveAvatar();
1060	}
1061}
1062
1063// -----------------------------------------------------------------------------
1064std::string LLViewerJoystick::getDescription()
1065{
1066	std::string res;
1067#if LIB_NDOF
1068	if (mDriverState == JDS_INITIALIZED && mNdofDev)
1069	{
1070		res = ll_safe_string(mNdofDev->product);
1071	}
1072#endif
1073	return res;
1074}
1075
1076bool LLViewerJoystick::isLikeSpaceNavigator() const
1077{
1078#if LIB_NDOF	
1079	return (isJoystickInitialized() 
1080			&& (strncmp(mNdofDev->product, "SpaceNavigator", 14) == 0
1081				|| strncmp(mNdofDev->product, "SpaceExplorer", 13) == 0
1082				|| strncmp(mNdofDev->product, "SpaceTraveler", 13) == 0
1083				|| strncmp(mNdofDev->product, "SpacePilot", 10) == 0));
1084#else
1085	return false;
1086#endif
1087}
1088
1089// -----------------------------------------------------------------------------
1090void LLViewerJoystick::setSNDefaults()
1091{
1092#if LL_DARWIN || LL_LINUX
1093	const float platformScale = 20.f;
1094	const float platformScaleAvXZ = 1.f;
1095	// The SpaceNavigator doesn't act as a 3D cursor on OS X / Linux. 
1096	const bool is_3d_cursor = false;
1097#else
1098	const float platformScale = 1.f;
1099	const float platformScaleAvXZ = 2.f;
1100	const bool is_3d_cursor = true;
1101#endif
1102	
1103	//gViewerWindow->alertXml("CacheWillClear");
1104	llinfos << "restoring SpaceNavigator defaults..." << llendl;
1105	
1106	gSavedSettings.setS32("JoystickAxis0", 1); // z (at)
1107	gSavedSettings.setS32("JoystickAxis1", 0); // x (slide)
1108	gSavedSettings.setS32("JoystickAxis2", 2); // y (up)
1109	gSavedSettings.setS32("JoystickAxis3", 4); // pitch
1110	gSavedSettings.setS32("JoystickAxis4", 3); // roll 
1111	gSavedSettings.setS32("JoystickAxis5", 5); // yaw
1112	gSavedSettings.setS32("JoystickAxis6", -1);
1113	
1114	gSavedSettings.setBOOL("Cursor3D", is_3d_cursor);
1115	gSavedSettings.setBOOL("AutoLeveling", true);
1116	gSavedSettings.setBOOL("ZoomDirect", false);
1117	
1118	gSavedSettings.setF32("AvatarAxisScale0", 1.f * platformScaleAvXZ);
1119	gSavedSettings.setF32("AvatarAxisScale1", 1.f * platformScaleAvXZ);
1120	gSavedSettings.setF32("AvatarAxisScale2", 1.f);
1121	gSavedSettings.setF32("AvatarAxisScale4", .1f * platformScale);
1122	gSavedSettings.setF32("AvatarAxisScale5", .1f * platformScale);
1123	gSavedSettings.setF32("AvatarAxisScale3", 0.f * platformScale);
1124	gSavedSettings.setF32("BuildAxisScale1", .3f * platformScale);
1125	gSavedSettings.setF32("BuildAxisScale2", .3f * platformScale);
1126	gSavedSettings.setF32("BuildAxisScale0", .3f * platformScale);
1127	gSavedSettings.setF32("BuildAxisScale4", .3f * platformScale);
1128	gSavedSettings.setF32("BuildAxisScale5", .3f * platformScale);
1129	gSavedSettings.setF32("BuildAxisScale3", .3f * platformScale);
1130	gSavedSettings.setF32("FlycamAxisScale1", 2.f * platformScale);
1131	gSavedSettings.setF32("FlycamAxisScale2", 2.f * platformScale);
1132	gSavedSettings.setF32("FlycamAxisScale0", 2.1f * platformScale);
1133	gSavedSettings.setF32("FlycamAxisScale4", .1f * platformScale);
1134	gSavedSettings.setF32("FlycamAxisScale5", .15f * platformScale);
1135	gSavedSettings.setF32("FlycamAxisScale3", 0.f * platformScale);
1136	gSavedSettings.setF32("FlycamAxisScale6", 0.f * platformScale);
1137	
1138	gSavedSettings.setF32("AvatarAxisDeadZone0", .1f);
1139	gSavedSettings.setF32("AvatarAxisDeadZone1", .1f);
1140	gSavedSettings.setF32("AvatarAxisDeadZone2", .1f);
1141	gSavedSettings.setF32("AvatarAxisDeadZone3", 1.f);
1142	gSavedSettings.setF32("AvatarAxisDeadZone4", .02f);
1143	gSavedSettings.setF32("AvatarAxisDeadZone5", .01f);
1144	gSavedSettings.setF32("BuildAxisDeadZone0", .01f);
1145	gSavedSettings.setF32("BuildAxisDeadZone1", .01f);
1146	gSavedSettings.setF32("BuildAxisDeadZone2", .01f);
1147	gSavedSettings.setF32("BuildAxisDeadZone3", .01f);
1148	gSavedSettings.setF32("BuildAxisDeadZone4", .01f);
1149	gSavedSettings.setF32("BuildAxisDeadZone5", .01f);
1150	gSavedSettings.setF32("FlycamAxisDeadZone0", .01f);
1151	gSavedSettings.setF32("FlycamAxisDeadZone1", .01f);
1152	gSavedSettings.setF32("FlycamAxisDeadZone2", .01f);
1153	gSavedSettings.setF32("FlycamAxisDeadZone3", .01f);
1154	gSavedSettings.setF32("FlycamAxisDeadZone4", .01f);
1155	gSavedSettings.setF32("FlycamAxisDeadZone5", .01f);
1156	gSavedSettings.setF32("FlycamAxisDeadZone6", 1.f);
1157	
1158	gSavedSettings.setF32("AvatarFeathering", 6.f);
1159	gSavedSettings.setF32("BuildFeathering", 12.f);
1160	gSavedSettings.setF32("FlycamFeathering", 5.f);
1161}