aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/newview/llviewerjoystick.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'linden/indra/newview/llviewerjoystick.cpp')
-rw-r--r--linden/indra/newview/llviewerjoystick.cpp993
1 files changed, 859 insertions, 134 deletions
diff --git a/linden/indra/newview/llviewerjoystick.cpp b/linden/indra/newview/llviewerjoystick.cpp
index e3db0f2..e9690f8 100644
--- a/linden/indra/newview/llviewerjoystick.cpp
+++ b/linden/indra/newview/llviewerjoystick.cpp
@@ -1,6 +1,6 @@
1/** 1/**
2 * @file llviewerjoystick.cpp 2 * @file llviewerjoystick.cpp
3 * @brief Joystick functionality. 3 * @brief Joystick / NDOF device functionality.
4 * 4 *
5 * $LicenseInfo:firstyear=2002&license=viewergpl$ 5 * $LicenseInfo:firstyear=2002&license=viewergpl$
6 * 6 *
@@ -30,39 +30,701 @@
30 */ 30 */
31 31
32#include "llviewerprecompiledheaders.h" 32#include "llviewerprecompiledheaders.h"
33
34#include "llviewerjoystick.h"
35
33#include "llviewercontrol.h" 36#include "llviewercontrol.h"
34#include "llviewerwindow.h" 37#include "llviewerwindow.h"
35#include "llviewercamera.h" 38#include "llviewercamera.h"
36#include "llviewerjoystick.h"
37#include "llappviewer.h" 39#include "llappviewer.h"
38#include "llkeyboard.h" 40#include "llkeyboard.h"
41#include "lltoolmgr.h"
42#include "llselectmgr.h"
43#include "llviewermenu.h"
44#include "llagent.h"
45#include "llfocusmgr.h"
46
47
48// ----------------------------------------------------------------------------
49// Constants
50
51#define X_I 1
52#define Y_I 2
53#define Z_I 0
54#define RX_I 4
55#define RY_I 5
56#define RZ_I 3
39 57
40static LLQuaternion sFlycamRotation; 58// flycam translations in build mode should be reduced
41static LLVector3 sFlycamPosition; 59const F32 BUILDMODE_FLYCAM_T_SCALE = 3.f;
42static F32 sFlycamZoom;
43 60
44BOOL LLViewerJoystick::sOverrideCamera = FALSE; 61F32 LLViewerJoystick::sLastDelta[] = {0,0,0,0,0,0,0};
62F32 LLViewerJoystick::sDelta[] = {0,0,0,0,0,0,0};
45 63
46void LLViewerJoystick::updateCamera(BOOL reset) 64// These constants specify the maximum absolute value coming in from the device.
65// HACK ALERT! the value of MAX_JOYSTICK_INPUT_VALUE is not arbitrary as it
66// should be. It has to be equal to 3000 because the SpaceNavigator on Windows
67// refuses to respond to the DirectInput SetProperty call; it always returns
68// values in the [-3000, 3000] range.
69#define MAX_SPACENAVIGATOR_INPUT 3000.0f
70#define MAX_JOYSTICK_INPUT_VALUE MAX_SPACENAVIGATOR_INPUT
71
72// -----------------------------------------------------------------------------
73void LLViewerJoystick::updateEnabled(bool autoenable)
47{ 74{
48 static F32 last_delta[] = {0,0,0,0,0,0,0}; 75 if (mDriverState == JDS_UNINITIALIZED)
49 static F32 delta[] = { 0,0,0,0,0,0,0 }; 76 {
77 gSavedSettings.setBOOL("JoystickEnabled", FALSE );
78 }
79 else
80 {
81 if (isLikeSpaceNavigator() && autoenable)
82 {
83 gSavedSettings.setBOOL("JoystickEnabled", TRUE );
84 }
85 }
86 if (!gSavedSettings.getBOOL("JoystickEnabled"))
87 {
88 mOverrideCamera = FALSE;
89 }
90}
50 91
51 LLWindow* window = gViewerWindow->getWindow(); 92void LLViewerJoystick::setOverrideCamera(bool val)
93{
94 if (!gSavedSettings.getBOOL("JoystickEnabled"))
95 {
96 mOverrideCamera = FALSE;
97 }
98 else
99 {
100 mOverrideCamera = val;
101 }
102}
103
104// -----------------------------------------------------------------------------
105#if LIB_NDOF
106NDOF_HotPlugResult LLViewerJoystick::HotPlugAddCallback(NDOF_Device *dev)
107{
108 NDOF_HotPlugResult res = NDOF_DISCARD_HOTPLUGGED;
109 LLViewerJoystick* joystick(LLViewerJoystick::getInstance());
110 if (joystick->mDriverState == JDS_UNINITIALIZED)
111 {
112 llinfos << "HotPlugAddCallback: will use device:" << llendl;
113 ndof_dump(dev);
114 joystick->mNdofDev = dev;
115 joystick->mDriverState = JDS_INITIALIZED;
116 res = NDOF_KEEP_HOTPLUGGED;
117 }
118 joystick->updateEnabled(true);
119 return res;
120}
121#endif
122
123// -----------------------------------------------------------------------------
124#if LIB_NDOF
125void LLViewerJoystick::HotPlugRemovalCallback(NDOF_Device *dev)
126{
127 LLViewerJoystick* joystick(LLViewerJoystick::getInstance());
128 if (joystick->mNdofDev == dev)
129 {
130 llinfos << "HotPlugRemovalCallback: joystick->mNdofDev="
131 << joystick->mNdofDev << "; removed device:" << llendl;
132 ndof_dump(dev);
133 joystick->mDriverState = JDS_UNINITIALIZED;
134 }
135 joystick->updateEnabled(true);
136}
137#endif
138
139// -----------------------------------------------------------------------------
140LLViewerJoystick::LLViewerJoystick()
141: mDriverState(JDS_UNINITIALIZED),
142 mNdofDev(NULL),
143 mResetFlag(false),
144 mCameraUpdated(true),
145 mOverrideCamera(false)
146{
147 for (int i = 0; i < 6; i++)
148 {
149 mAxes[i] = sDelta[i] = sLastDelta[i] = 0.0f;
150 }
151
152 memset(mBtn, 0, sizeof(mBtn));
153
154 // factor in bandwidth? bandwidth = gViewerStats->mKBitStat
155 mPerfScale = 4000.f / gSysCPU.getMhz();
156}
157
158// -----------------------------------------------------------------------------
159LLViewerJoystick::~LLViewerJoystick()
160{
161 if (mDriverState == JDS_INITIALIZED)
162 {
163 terminate();
164 }
165}
166
167// -----------------------------------------------------------------------------
168void LLViewerJoystick::init(bool autoenable)
169{
170#if LIB_NDOF
171 static bool libinit = false;
172 mDriverState = JDS_INITIALIZING;
52 173
174 if (libinit == false)
175 {
176 // Note: The HotPlug callbacks are not actually getting called on Windows
177 if (ndof_libinit(HotPlugAddCallback,
178 HotPlugRemovalCallback,
179 NULL))
180 {
181 mDriverState = JDS_UNINITIALIZED;
182 }
183 else
184 {
185 // NB: ndof_libinit succeeds when there's no device
186 libinit = true;
187
188 // allocate memory once for an eventual device
189 mNdofDev = ndof_create();
190 }
191 }
192
193 if (libinit)
194 {
195 if (mNdofDev)
196 {
197 // Different joysticks will return different ranges of raw values.
198 // Since we want to handle every device in the same uniform way,
199 // we initialize the mNdofDev struct and we set the range
200 // of values we would like to receive.
201 //
202 // HACK: On Windows, libndofdev passes our range to DI with a
203 // SetProperty call. This works but with one notable exception, the
204 // SpaceNavigator, who doesn't seem to care about the SetProperty
205 // call. In theory, we should handle this case inside libndofdev.
206 // However, the range we're setting here is arbitrary anyway,
207 // so let's just use the SpaceNavigator range for our purposes.
208 mNdofDev->axes_min = (long)-MAX_JOYSTICK_INPUT_VALUE;
209 mNdofDev->axes_max = (long)+MAX_JOYSTICK_INPUT_VALUE;
210
211 // libndofdev could be used to return deltas. Here we choose to
212 // just have the absolute values instead.
213 mNdofDev->absolute = 1;
214
215 // init & use the first suitable NDOF device found on the USB chain
216 if (ndof_init_first(mNdofDev, NULL))
217 {
218 mDriverState = JDS_UNINITIALIZED;
219 llwarns << "ndof_init_first FAILED" << llendl;
220 }
221 else
222 {
223 mDriverState = JDS_INITIALIZED;
224 }
225 }
226 else
227 {
228 mDriverState = JDS_UNINITIALIZED;
229 }
230 }
231 updateEnabled(autoenable);
232
233 llinfos << "ndof: mDriverState=" << mDriverState << "; mNdofDev="
234 << mNdofDev << "; libinit=" << libinit << llendl;
235#endif
236}
237
238// -----------------------------------------------------------------------------
239void LLViewerJoystick::terminate()
240{
241#if LIB_NDOF
242
243 ndof_libcleanup();
244 llinfos << "Terminated connection with NDOF device." << llendl;
245
246#endif
247}
248
249// -----------------------------------------------------------------------------
250void LLViewerJoystick::updateStatus()
251{
252#if LIB_NDOF
253
254 ndof_update(mNdofDev);
255
256 for (int i=0; i<6; i++)
257 {
258 mAxes[i] = (F32) mNdofDev->axes[i] / mNdofDev->axes_max;
259 }
260
261 for (int i=0; i<16; i++)
262 {
263 mBtn[i] = mNdofDev->buttons[i];
264 }
265
266#endif
267}
268
269// -----------------------------------------------------------------------------
270F32 LLViewerJoystick::getJoystickAxis(U32 axis) const
271{
272 if (axis < 6)
273 {
274 return mAxes[axis];
275 }
276 return 0.f;
277}
278
279// -----------------------------------------------------------------------------
280U32 LLViewerJoystick::getJoystickButton(U32 button) const
281{
282 if (button < 16)
283 {
284 return mBtn[button];
285 }
286 return 0;
287}
288
289// -----------------------------------------------------------------------------
290void LLViewerJoystick::agentJump()
291{
292 gAgent.moveUp(1);
293}
294
295// -----------------------------------------------------------------------------
296void LLViewerJoystick::agentSlide(F32 inc)
297{
298 if (inc < 0)
299 {
300 gAgent.moveLeft(1);
301 }
302 else if (inc > 0)
303 {
304 gAgent.moveLeft(-1);
305 }
306}
307
308// -----------------------------------------------------------------------------
309void LLViewerJoystick::agentPush(F32 inc)
310{
311 if (inc < 0) // forward
312 {
313 gAgent.moveAt(1, false);
314 }
315 else if (inc > 0) // backward
316 {
317 gAgent.moveAt(-1, false);
318 }
319}
320
321// -----------------------------------------------------------------------------
322void LLViewerJoystick::agentFly(F32 inc)
323{
324 if (inc < 0)
325 {
326 if (gAgent.getFlying())
327 {
328 gAgent.moveUp(1);
329 }
330 else
331 {
332 gAgent.setFlying(true);
333 }
334 }
335 else if (inc > 0)
336 {
337 // crouch
338 gAgent.moveUp(-1);
339 }
340}
341
342// -----------------------------------------------------------------------------
343void LLViewerJoystick::agentRotate(F32 pitch_inc, F32 yaw_inc)
344{
345 LLQuaternion new_rot;
346 pitch_inc = gAgent.clampPitchToLimits(-pitch_inc);
347 const LLQuaternion qx(pitch_inc, gAgent.getLeftAxis());
348 const LLQuaternion qy(-yaw_inc, gAgent.getReferenceUpVector());
349 new_rot.setQuat(qx * qy);
350 gAgent.rotate(new_rot);
351}
352
353// -----------------------------------------------------------------------------
354void LLViewerJoystick::resetDeltas(S32 axis[], bool flycam_and_build_mode)
355{
356 for (U32 i = 0; i < 6; i++)
357 {
358 sLastDelta[i] = -mAxes[axis[i]];
359 sDelta[i] = 0.f;
360 }
361
362 if (flycam_and_build_mode)
363 {
364 sLastDelta[X_I] /= BUILDMODE_FLYCAM_T_SCALE;
365 sLastDelta[Y_I] /= BUILDMODE_FLYCAM_T_SCALE;
366 sLastDelta[Z_I] /= BUILDMODE_FLYCAM_T_SCALE;
367 }
368
369 sLastDelta[6] = sDelta[6] = 0.f;
370 mResetFlag = false;
371}
372
373// -----------------------------------------------------------------------------
374void LLViewerJoystick::moveObjects(bool reset)
375{
376 static bool toggle_send_to_sim = false;
377
378 if (!gFocusMgr.getAppHasFocus() || mDriverState != JDS_INITIALIZED
379 || !gSavedSettings.getBOOL("JoystickEnabled") || !gSavedSettings.getBOOL("JoystickBuildEnabled"))
380 {
381 return;
382 }
383
384 S32 axis[] =
385 {
386 gSavedSettings.getS32("JoystickAxis0"),
387 gSavedSettings.getS32("JoystickAxis1"),
388 gSavedSettings.getS32("JoystickAxis2"),
389 gSavedSettings.getS32("JoystickAxis3"),
390 gSavedSettings.getS32("JoystickAxis4"),
391 gSavedSettings.getS32("JoystickAxis5"),
392 };
393
394 if (reset || mResetFlag)
395 {
396 resetDeltas(axis);
397 return;
398 }
399
400 F32 axis_scale[] =
401 {
402 gSavedSettings.getF32("BuildAxisScale0"),
403 gSavedSettings.getF32("BuildAxisScale1"),
404 gSavedSettings.getF32("BuildAxisScale2"),
405 gSavedSettings.getF32("BuildAxisScale3"),
406 gSavedSettings.getF32("BuildAxisScale4"),
407 gSavedSettings.getF32("BuildAxisScale5"),
408 };
409
410 F32 dead_zone[] =
411 {
412 gSavedSettings.getF32("BuildAxisDeadZone0"),
413 gSavedSettings.getF32("BuildAxisDeadZone1"),
414 gSavedSettings.getF32("BuildAxisDeadZone2"),
415 gSavedSettings.getF32("BuildAxisDeadZone3"),
416 gSavedSettings.getF32("BuildAxisDeadZone4"),
417 gSavedSettings.getF32("BuildAxisDeadZone5"),
418 };
419
420 F32 cur_delta[6];
53 F32 time = gFrameIntervalSeconds; 421 F32 time = gFrameIntervalSeconds;
54 422
423 // avoid making ridicously big movements if there's a big drop in fps
424 if (time > .2f)
425 {
426 time = .2f;
427 }
428
429 // max feather is 32
430 F32 feather = gSavedSettings.getF32("BuildFeathering");
431 bool is_zero = true, absolute = gSavedSettings.getBOOL("Cursor3D");
432
433 for (U32 i = 0; i < 6; i++)
434 {
435 cur_delta[i] = -mAxes[axis[i]];
436 F32 tmp = cur_delta[i];
437 if (absolute)
438 {
439 cur_delta[i] = cur_delta[i] - sLastDelta[i];
440 }
441 sLastDelta[i] = tmp;
442 is_zero = is_zero && (cur_delta[i] == 0.f);
443
444 if (cur_delta[i] > 0)
445 {
446 cur_delta[i] = llmax(cur_delta[i]-dead_zone[i], 0.f);
447 }
448 else
449 {
450 cur_delta[i] = llmin(cur_delta[i]+dead_zone[i], 0.f);
451 }
452 cur_delta[i] *= axis_scale[i];
453
454 if (!absolute)
455 {
456 cur_delta[i] *= time;
457 }
458
459 sDelta[i] = sDelta[i] + (cur_delta[i]-sDelta[i])*time*feather;
460 }
461
462 U32 upd_type = UPD_NONE;
463 LLVector3 v;
464
465 if (!is_zero)
466 {
467 if (sDelta[0] || sDelta[1] || sDelta[2])
468 {
469 upd_type |= UPD_POSITION;
470 v.setVec(sDelta[0], sDelta[1], sDelta[2]);
471 }
472
473 if (sDelta[3] || sDelta[4] || sDelta[5])
474 {
475 upd_type |= UPD_ROTATION;
476 }
477
478 // the selection update could fail, so we won't send
479 if (LLSelectMgr::getInstance()->selectionMove(v, sDelta[3],sDelta[4],sDelta[5], upd_type))
480 {
481 toggle_send_to_sim = true;
482 }
483 }
484 else if (toggle_send_to_sim)
485 {
486 LLSelectMgr::getInstance()->sendSelectionMove();
487 toggle_send_to_sim = false;
488 }
489}
490
491// -----------------------------------------------------------------------------
492void LLViewerJoystick::moveAvatar(bool reset)
493{
494 if (!gFocusMgr.getAppHasFocus() || mDriverState != JDS_INITIALIZED
495 || !gSavedSettings.getBOOL("JoystickEnabled") || !gSavedSettings.getBOOL("JoystickAvatarEnabled"))
496 {
497 return;
498 }
499
500 S32 axis[] =
501 {
502 // [1 0 2 4 3 5]
503 // [Z X Y RZ RX RY]
504 gSavedSettings.getS32("JoystickAxis0"),
505 gSavedSettings.getS32("JoystickAxis1"),
506 gSavedSettings.getS32("JoystickAxis2"),
507 gSavedSettings.getS32("JoystickAxis3"),
508 gSavedSettings.getS32("JoystickAxis4"),
509 gSavedSettings.getS32("JoystickAxis5")
510 };
511
512 if (reset || mResetFlag)
513 {
514 resetDeltas(axis);
515 if (reset)
516 {
517 // Note: moving the agent triggers agent camera mode;
518 // don't do this every time we set mResetFlag (e.g. because we gained focus)
519 gAgent.moveAt(0, true);
520 }
521 return;
522 }
523
524 if (mBtn[1] == 1)
525 {
526 agentJump();
527 return;
528 }
529
530 F32 axis_scale[] =
531 {
532 gSavedSettings.getF32("AvatarAxisScale0"),
533 gSavedSettings.getF32("AvatarAxisScale1"),
534 gSavedSettings.getF32("AvatarAxisScale2"),
535 gSavedSettings.getF32("AvatarAxisScale3"),
536 gSavedSettings.getF32("AvatarAxisScale4"),
537 gSavedSettings.getF32("AvatarAxisScale5")
538 };
539
540 F32 dead_zone[] =
541 {
542 gSavedSettings.getF32("AvatarAxisDeadZone0"),
543 gSavedSettings.getF32("AvatarAxisDeadZone1"),
544 gSavedSettings.getF32("AvatarAxisDeadZone2"),
545 gSavedSettings.getF32("AvatarAxisDeadZone3"),
546 gSavedSettings.getF32("AvatarAxisDeadZone4"),
547 gSavedSettings.getF32("AvatarAxisDeadZone5")
548 };
549
550 // time interval in seconds between this frame and the previous
551 F32 time = gFrameIntervalSeconds;
552
553 // avoid making ridicously big movements if there's a big drop in fps
554 if (time > .2f)
555 {
556 time = .2f;
557 }
558
559 // note: max feather is 32.0
560 F32 feather = gSavedSettings.getF32("AvatarFeathering");
561
562 F32 cur_delta[6];
563 F32 val, dom_mov = 0.f;
564 U32 dom_axis = Z_I;
565#if LIB_NDOF
566 bool absolute = (gSavedSettings.getBOOL("Cursor3D") && mNdofDev->absolute);
567#else
568 bool absolute = false;
569#endif
570 // remove dead zones and determine biggest movement on the joystick
571 for (U32 i = 0; i < 6; i++)
572 {
573 cur_delta[i] = -mAxes[axis[i]];
574 if (absolute)
575 {
576 F32 tmp = cur_delta[i];
577 cur_delta[i] = cur_delta[i] - sLastDelta[i];
578 sLastDelta[i] = tmp;
579 }
580
581 if (cur_delta[i] > 0)
582 {
583 cur_delta[i] = llmax(cur_delta[i]-dead_zone[i], 0.f);
584 }
585 else
586 {
587 cur_delta[i] = llmin(cur_delta[i]+dead_zone[i], 0.f);
588 }
589
590 // we don't care about Roll (RZ) and Z is calculated after the loop
591 if (i != Z_I && i != RZ_I)
592 {
593 // find out the axis with the biggest joystick motion
594 val = fabs(cur_delta[i]);
595 if (val > dom_mov)
596 {
597 dom_axis = i;
598 dom_mov = val;
599 }
600 }
601 }
602
603 // forward|backward movements overrule the real dominant movement if
604 // they're bigger than its 20%. This is what you want cos moving forward
605 // is what you do most. We also added a special (even more lenient) case
606 // for RX|RY to allow walking while pitching n' turning
607 if (fabs(cur_delta[Z_I]) > .2f * dom_mov
608 || ((dom_axis == RX_I || dom_axis == RY_I)
609 && fabs(cur_delta[Z_I]) > .05f * dom_mov))
610 {
611 dom_axis = Z_I;
612 }
613
614 sDelta[X_I] = -cur_delta[X_I] * axis_scale[X_I];
615 sDelta[Y_I] = -cur_delta[Y_I] * axis_scale[Y_I];
616 sDelta[Z_I] = -cur_delta[Z_I] * axis_scale[Z_I];
617 cur_delta[RX_I] *= -axis_scale[RX_I] * mPerfScale;
618 cur_delta[RY_I] *= -axis_scale[RY_I] * mPerfScale;
619
620 if (!absolute)
621 {
622 cur_delta[RX_I] *= time;
623 cur_delta[RY_I] *= time;
624 }
625 sDelta[RX_I] += (cur_delta[RX_I] - sDelta[RX_I]) * time * feather;
626 sDelta[RY_I] += (cur_delta[RY_I] - sDelta[RY_I]) * time * feather;
627
628 switch (dom_axis)
629 {
630 case X_I: // move sideways
631 agentSlide(sDelta[X_I]);
632 break;
633
634 case Z_I: // forward/back
635 {
636 agentPush(sDelta[Z_I]);
637
638 if (fabs(sDelta[Y_I]) > .1f)
639 {
640 agentFly(sDelta[Y_I]);
641 }
642
643 // too many rotations during walking can be confusing, so apply
644 // the deadzones one more time (quick & dirty), at 50%|30% power
645 F32 eff_rx = .3f * dead_zone[RX_I];
646 F32 eff_ry = .3f * dead_zone[RY_I];
647
648 if (sDelta[RX_I] > 0)
649 {
650 eff_rx = llmax(sDelta[RX_I] - eff_rx, 0.f);
651 }
652 else
653 {
654 eff_rx = llmin(sDelta[RX_I] + eff_rx, 0.f);
655 }
656
657 if (sDelta[RY_I] > 0)
658 {
659 eff_ry = llmax(sDelta[RY_I] - eff_ry, 0.f);
660 }
661 else
662 {
663 eff_ry = llmin(sDelta[RY_I] + eff_ry, 0.f);
664 }
665
666
667 if (fabs(eff_rx) > 0.f || fabs(eff_ry) > 0.f)
668 {
669 if (gAgent.getFlying())
670 {
671 agentRotate(eff_rx, eff_ry);
672 }
673 else
674 {
675 agentRotate(eff_rx, 2.f * eff_ry);
676 }
677 }
678 break;
679 }
680 case Y_I: // up/crouch
681 agentFly(sDelta[Y_I]);
682 break;
683
684 case RX_I: // pitch
685 case RY_I: // turn
686 agentRotate(sDelta[RX_I], sDelta[RY_I]);
687 break;
688 // case RZ_I: roll is unused in avatar mode
689 }// switch
690}
691
692// -----------------------------------------------------------------------------
693void LLViewerJoystick::moveFlycam(bool reset)
694{
695 static LLQuaternion sFlycamRotation;
696 static LLVector3 sFlycamPosition;
697 static F32 sFlycamZoom;
698
699 if (!gFocusMgr.getAppHasFocus() || mDriverState != JDS_INITIALIZED
700 || !gSavedSettings.getBOOL("JoystickEnabled") || !gSavedSettings.getBOOL("JoystickFlycamEnabled"))
701 {
702 return;
703 }
704
55 S32 axis[] = 705 S32 axis[] =
56 { 706 {
57 gSavedSettings.getS32("FlycamAxis0"), 707 gSavedSettings.getS32("JoystickAxis0"),
58 gSavedSettings.getS32("FlycamAxis1"), 708 gSavedSettings.getS32("JoystickAxis1"),
59 gSavedSettings.getS32("FlycamAxis2"), 709 gSavedSettings.getS32("JoystickAxis2"),
60 gSavedSettings.getS32("FlycamAxis3"), 710 gSavedSettings.getS32("JoystickAxis3"),
61 gSavedSettings.getS32("FlycamAxis4"), 711 gSavedSettings.getS32("JoystickAxis4"),
62 gSavedSettings.getS32("FlycamAxis5"), 712 gSavedSettings.getS32("JoystickAxis5"),
63 gSavedSettings.getS32("FlycamAxis6") 713 gSavedSettings.getS32("JoystickAxis6")
64 }; 714 };
65 715
716 bool in_build_mode = LLToolMgr::getInstance()->inBuildMode();
717 if (reset || mResetFlag)
718 {
719 sFlycamPosition = LLViewerCamera::getInstance()->getOrigin();
720 sFlycamRotation = LLViewerCamera::getInstance()->getQuaternion();
721 sFlycamZoom = LLViewerCamera::getInstance()->getView();
722
723 resetDeltas(axis, in_build_mode);
724
725 return;
726 }
727
66 F32 axis_scale[] = 728 F32 axis_scale[] =
67 { 729 {
68 gSavedSettings.getF32("FlycamAxisScale0"), 730 gSavedSettings.getF32("FlycamAxisScale0"),
@@ -85,33 +747,37 @@ void LLViewerJoystick::updateCamera(BOOL reset)
85 gSavedSettings.getF32("FlycamAxisDeadZone6") 747 gSavedSettings.getF32("FlycamAxisDeadZone6")
86 }; 748 };
87 749
88 if (reset) 750 F32 time = gFrameIntervalSeconds;
89 {
90 sFlycamPosition = gCamera->getOrigin();
91 sFlycamRotation = gCamera->getQuaternion();
92 sFlycamZoom = gCamera->getView();
93 751
94 for (U32 i = 0; i < 7; i++) 752 // avoid making ridicously big movements if there's a big drop in fps
95 { 753 if (time > .2f)
96 last_delta[i] = -window->getJoystickAxis(axis[i]); 754 {
97 delta[i] = 0.f; 755 time = .2f;
98 }
99 return;
100 } 756 }
101 757
102 F32 cur_delta[7]; 758 F32 cur_delta[7];
103 F32 feather = gSavedSettings.getF32("FlycamFeathering"); 759 F32 feather = gSavedSettings.getF32("FlycamFeathering");
104 BOOL absolute = gSavedSettings.getBOOL("FlycamAbsolute"); 760 bool absolute = gSavedSettings.getBOOL("Cursor3D");
105 761
106 for (U32 i = 0; i < 7; i++) 762 for (U32 i = 0; i < 7; i++)
107 { 763 {
108 cur_delta[i] = -window->getJoystickAxis(axis[i]); 764 cur_delta[i] = -getJoystickAxis(axis[i]);
765
766 // we need smaller camera movements in build mode
767 if (in_build_mode)
768 {
769 if (i == X_I || i == Y_I || i == Z_I)
770 {
771 cur_delta[i] /= BUILDMODE_FLYCAM_T_SCALE;
772 }
773 }
774
109 F32 tmp = cur_delta[i]; 775 F32 tmp = cur_delta[i];
110 if (absolute) 776 if (absolute)
111 { 777 {
112 cur_delta[i] = cur_delta[i] - last_delta[i]; 778 cur_delta[i] = cur_delta[i] - sLastDelta[i];
113 } 779 }
114 last_delta[i] = tmp; 780 sLastDelta[i] = tmp;
115 781
116 if (cur_delta[i] > 0) 782 if (cur_delta[i] > 0)
117 { 783 {
@@ -128,18 +794,15 @@ void LLViewerJoystick::updateCamera(BOOL reset)
128 cur_delta[i] *= time; 794 cur_delta[i] *= time;
129 } 795 }
130 796
131 delta[i] = delta[i] + (cur_delta[i]-delta[i])*time*feather; 797 sDelta[i] = sDelta[i] + (cur_delta[i]-sDelta[i])*time*feather;
132 } 798 }
133 799
134 sFlycamPosition += LLVector3(delta) * sFlycamRotation; 800 sFlycamPosition += LLVector3(sDelta) * sFlycamRotation;
135 801
136 LLMatrix3 rot_mat(delta[3], 802 LLMatrix3 rot_mat(sDelta[3], sDelta[4], sDelta[5]);
137 delta[4],
138 delta[5]);
139
140 sFlycamRotation = LLQuaternion(rot_mat)*sFlycamRotation; 803 sFlycamRotation = LLQuaternion(rot_mat)*sFlycamRotation;
141 804
142 if (gSavedSettings.getBOOL("FlycamAutoLeveling")) 805 if (gSavedSettings.getBOOL("AutoLeveling"))
143 { 806 {
144 LLMatrix3 level(sFlycamRotation); 807 LLMatrix3 level(sFlycamRotation);
145 808
@@ -153,123 +816,185 @@ void LLViewerJoystick::updateCamera(BOOL reset)
153 level.setRows(x,y,z); 816 level.setRows(x,y,z);
154 level.orthogonalize(); 817 level.orthogonalize();
155 818
156 LLQuaternion quat = LLQuaternion(level); 819 LLQuaternion quat(level);
157 sFlycamRotation = nlerp(llmin(feather*time,1.f), sFlycamRotation, quat); 820 sFlycamRotation = nlerp(llmin(feather*time,1.f), sFlycamRotation, quat);
158 } 821 }
159 822
160 if (gSavedSettings.getBOOL("FlycamZoomDirect")) 823 if (gSavedSettings.getBOOL("ZoomDirect"))
161 { 824 {
162 sFlycamZoom = last_delta[6]*axis_scale[6]+dead_zone[6]; 825 sFlycamZoom = sLastDelta[6]*axis_scale[6]+dead_zone[6];
163 } 826 }
164 else 827 else
165 { 828 {
166 sFlycamZoom += delta[6]; 829 sFlycamZoom += sDelta[6];
167 } 830 }
168 831
169 LLMatrix3 mat(sFlycamRotation); 832 LLMatrix3 mat(sFlycamRotation);
170 833
171 gCamera->setView(sFlycamZoom); 834 LLViewerCamera::getInstance()->setView(sFlycamZoom);
172 gCamera->setOrigin(sFlycamPosition); 835 LLViewerCamera::getInstance()->setOrigin(sFlycamPosition);
173 gCamera->mXAxis = LLVector3(mat.mMatrix[0]); 836 LLViewerCamera::getInstance()->mXAxis = LLVector3(mat.mMatrix[0]);
174 gCamera->mYAxis = LLVector3(mat.mMatrix[1]); 837 LLViewerCamera::getInstance()->mYAxis = LLVector3(mat.mMatrix[1]);
175 gCamera->mZAxis = LLVector3(mat.mMatrix[2]); 838 LLViewerCamera::getInstance()->mZAxis = LLVector3(mat.mMatrix[2]);
176} 839}
177 840
841// -----------------------------------------------------------------------------
842bool LLViewerJoystick::toggleFlycam()
843{
844 if (!gSavedSettings.getBOOL("JoystickEnabled") || !gSavedSettings.getBOOL("JoystickFlycamEnabled"))
845 {
846 return false;
847 }
848 mOverrideCamera = !mOverrideCamera;
849 if (mOverrideCamera)
850 {
851 moveFlycam(true);
852 }
853 else if (!LLToolMgr::getInstance()->inBuildMode())
854 {
855 moveAvatar(true);
856 }
857 else
858 {
859 // we are in build mode, exiting from the flycam mode: since we are
860 // going to keep the flycam POV for the main camera until the avatar
861 // moves, we need to track this situation.
862 setCameraNeedsUpdate(false);
863 setNeedsReset(true);
864 }
865 return true;
866}
178 867
179void LLViewerJoystick::scanJoystick() 868void LLViewerJoystick::scanJoystick()
180{ 869{
181 if (!sOverrideCamera) 870 if (mDriverState != JDS_INITIALIZED || !gSavedSettings.getBOOL("JoystickEnabled"))
182 { 871 {
183 static U32 joystick_state = 0; 872 return;
184 static U32 button_state = 0; 873 }
185 874
186 F32 xval = gViewerWindow->getWindow()->getJoystickAxis(0); 875#if LL_WINDOWS
187 F32 yval = gViewerWindow->getWindow()->getJoystickAxis(1); 876 // On windows, the flycam is updated syncronously with a timer, so there is
877 // no need to update the status of the joystick here.
878 if (!mOverrideCamera)
879#endif
880 updateStatus();
188 881
189 if (xval <= -0.5f) 882 static long toggle_flycam = 0;
190 {
191 if (!(joystick_state & 0x1))
192 {
193 gKeyboard->handleTranslatedKeyDown(KEY_PAD_LEFT, 0);
194 joystick_state |= 0x1;
195 }
196 }
197 else
198 {
199 if (joystick_state & 0x1)
200 {
201 gKeyboard->handleTranslatedKeyUp(KEY_PAD_LEFT, 0);
202 joystick_state &= ~0x1;
203 }
204 }
205 if (xval >= 0.5f)
206 {
207 if (!(joystick_state & 0x2))
208 {
209 gKeyboard->handleTranslatedKeyDown(KEY_PAD_RIGHT, 0);
210 joystick_state |= 0x2;
211 }
212 }
213 else
214 {
215 if (joystick_state & 0x2)
216 {
217 gKeyboard->handleTranslatedKeyUp(KEY_PAD_RIGHT, 0);
218 joystick_state &= ~0x2;
219 }
220 }
221 if (yval <= -0.5f)
222 {
223 if (!(joystick_state & 0x4))
224 {
225 gKeyboard->handleTranslatedKeyDown(KEY_PAD_UP, 0);
226 joystick_state |= 0x4;
227 }
228 }
229 else
230 {
231 if (joystick_state & 0x4)
232 {
233 gKeyboard->handleTranslatedKeyUp(KEY_PAD_UP, 0);
234 joystick_state &= ~0x4;
235 }
236 }
237 if (yval >= 0.5f)
238 {
239 if (!(joystick_state & 0x8))
240 {
241 gKeyboard->handleTranslatedKeyDown(KEY_PAD_DOWN, 0);
242 joystick_state |= 0x8;
243 }
244 }
245 else
246 {
247 if (joystick_state & 0x8)
248 {
249 gKeyboard->handleTranslatedKeyUp(KEY_PAD_DOWN, 0);
250 joystick_state &= ~0x8;
251 }
252 }
253 883
254 for( int i = 0; i < 15; i++ ) 884 if (mBtn[0] == 1)
885 {
886 if (mBtn[0] != toggle_flycam)
255 { 887 {
256 if ( gViewerWindow->getWindow()->getJoystickButton(i) & 0x80 ) 888 toggle_flycam = toggleFlycam() ? 1 : 0;
257 {
258 if (!(button_state & (1<<i)))
259 {
260 gKeyboard->handleTranslatedKeyDown(KEY_BUTTON1+i, 0);
261 button_state |= (1<<i);
262 }
263 }
264 else
265 {
266 if (button_state & (1<<i))
267 {
268 gKeyboard->handleTranslatedKeyUp(KEY_BUTTON1+i, 0);
269 button_state &= ~(1<<i);
270 }
271 }
272 } 889 }
273 } 890 }
891 else
892 {
893 toggle_flycam = 0;
894 }
895
896 if (!mOverrideCamera && !LLToolMgr::getInstance()->inBuildMode())
897 {
898 moveAvatar();
899 }
900}
901
902// -----------------------------------------------------------------------------
903std::string LLViewerJoystick::getDescription()
904{
905 std::string res;
906#if LIB_NDOF
907 if (mDriverState == JDS_INITIALIZED && mNdofDev)
908 {
909 res = ll_safe_string(mNdofDev->product);
910 }
911#endif
912 return res;
913}
914
915bool LLViewerJoystick::isLikeSpaceNavigator() const
916{
917#if LIB_NDOF
918 return (isJoystickInitialized()
919 && (strncmp(mNdofDev->product, "SpaceNavigator", 14) == 0
920 || strncmp(mNdofDev->product, "SpaceExplorer", 13) == 0
921 || strncmp(mNdofDev->product, "SpaceTraveler", 13) == 0
922 || strncmp(mNdofDev->product, "SpacePilot", 10) == 0));
923#else
924 return false;
925#endif
274} 926}
275 927
928// -----------------------------------------------------------------------------
929void LLViewerJoystick::setSNDefaults()
930{
931#if LL_DARWIN
932#define kPlatformScale 20.f
933#else
934#define kPlatformScale 1.f
935#endif
936
937 //gViewerWindow->alertXml("CacheWillClear");
938 llinfos << "restoring SpaceNavigator defaults..." << llendl;
939
940 gSavedSettings.setS32("JoystickAxis0", 1); // z (at)
941 gSavedSettings.setS32("JoystickAxis1", 0); // x (slide)
942 gSavedSettings.setS32("JoystickAxis2", 2); // y (up)
943 gSavedSettings.setS32("JoystickAxis3", 4); // pitch
944 gSavedSettings.setS32("JoystickAxis4", 3); // roll
945 gSavedSettings.setS32("JoystickAxis5", 5); // yaw
946 gSavedSettings.setS32("JoystickAxis6", -1);
947
948#if LL_DARWIN
949 // The SpaceNavigator doesn't act as a 3D cursor on OS X.
950 gSavedSettings.setBOOL("Cursor3D", false);
951#else
952 gSavedSettings.setBOOL("Cursor3D", true);
953#endif
954 gSavedSettings.setBOOL("AutoLeveling", true);
955 gSavedSettings.setBOOL("ZoomDirect", false);
956
957 gSavedSettings.setF32("AvatarAxisScale0", 1.f);
958 gSavedSettings.setF32("AvatarAxisScale1", 1.f);
959 gSavedSettings.setF32("AvatarAxisScale2", 1.f);
960 gSavedSettings.setF32("AvatarAxisScale4", .1f * kPlatformScale);
961 gSavedSettings.setF32("AvatarAxisScale5", .1f * kPlatformScale);
962 gSavedSettings.setF32("AvatarAxisScale3", 0.f * kPlatformScale);
963 gSavedSettings.setF32("BuildAxisScale1", .3f * kPlatformScale);
964 gSavedSettings.setF32("BuildAxisScale2", .3f * kPlatformScale);
965 gSavedSettings.setF32("BuildAxisScale0", .3f * kPlatformScale);
966 gSavedSettings.setF32("BuildAxisScale4", .3f * kPlatformScale);
967 gSavedSettings.setF32("BuildAxisScale5", .3f * kPlatformScale);
968 gSavedSettings.setF32("BuildAxisScale3", .3f * kPlatformScale);
969 gSavedSettings.setF32("FlycamAxisScale1", 2.f * kPlatformScale);
970 gSavedSettings.setF32("FlycamAxisScale2", 2.f * kPlatformScale);
971 gSavedSettings.setF32("FlycamAxisScale0", 2.1f * kPlatformScale);
972 gSavedSettings.setF32("FlycamAxisScale4", .1f * kPlatformScale);
973 gSavedSettings.setF32("FlycamAxisScale5", .15f * kPlatformScale);
974 gSavedSettings.setF32("FlycamAxisScale3", 0.f * kPlatformScale);
975 gSavedSettings.setF32("FlycamAxisScale6", 0.f * kPlatformScale);
976
977 gSavedSettings.setF32("AvatarAxisDeadZone0", .1f);
978 gSavedSettings.setF32("AvatarAxisDeadZone1", .1f);
979 gSavedSettings.setF32("AvatarAxisDeadZone2", .1f);
980 gSavedSettings.setF32("AvatarAxisDeadZone3", 1.f);
981 gSavedSettings.setF32("AvatarAxisDeadZone4", .02f);
982 gSavedSettings.setF32("AvatarAxisDeadZone5", .01f);
983 gSavedSettings.setF32("BuildAxisDeadZone0", .01f);
984 gSavedSettings.setF32("BuildAxisDeadZone1", .01f);
985 gSavedSettings.setF32("BuildAxisDeadZone2", .01f);
986 gSavedSettings.setF32("BuildAxisDeadZone3", .01f);
987 gSavedSettings.setF32("BuildAxisDeadZone4", .01f);
988 gSavedSettings.setF32("BuildAxisDeadZone5", .01f);
989 gSavedSettings.setF32("FlycamAxisDeadZone0", .01f);
990 gSavedSettings.setF32("FlycamAxisDeadZone1", .01f);
991 gSavedSettings.setF32("FlycamAxisDeadZone2", .01f);
992 gSavedSettings.setF32("FlycamAxisDeadZone3", .01f);
993 gSavedSettings.setF32("FlycamAxisDeadZone4", .01f);
994 gSavedSettings.setF32("FlycamAxisDeadZone5", .01f);
995 gSavedSettings.setF32("FlycamAxisDeadZone6", 1.f);
996
997 gSavedSettings.setF32("AvatarFeathering", 6.f);
998 gSavedSettings.setF32("BuildFeathering", 12.f);
999 gSavedSettings.setF32("FlycamFeathering", 5.f);
1000}