Newer
Older
Josh Bell
committed
/**
* @file llviewerjoystick.cpp
Josh Bell
committed
*
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
* 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
Josh Bell
committed
*/
#include "llviewerprecompiledheaders.h"
Josh Bell
committed
#include "llviewercontrol.h"
#include "llviewerwindow.h"
#include "llviewercamera.h"
Steven Bennetts
committed
#include "llappviewer.h"
Josh Bell
committed
#include "llkeyboard.h"
#include "lltoolmgr.h"
#include "llselectmgr.h"
#include "llviewermenu.h"
Andrey Kleshchev
committed
#include "llviewerwindow.h"
#include "llwindow.h"
#include "llagentcamera.h"
Andrey Kleshchev
committed
#if LL_WINDOWS && !LL_MESA_HEADLESS
// Require DirectInput version 8
#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>
#endif
// ----------------------------------------------------------------------------
// Constants
#define X_I 1
#define Y_I 2
#define Z_I 0
#define RX_I 4
#define RY_I 5
#define RZ_I 3
F32 LLViewerJoystick::sLastDelta[] = {0,0,0,0,0,0,0};
F32 LLViewerJoystick::sDelta[] = {0,0,0,0,0,0,0};
// These constants specify the maximum absolute value coming in from the device.
// HACK ALERT! the value of MAX_JOYSTICK_INPUT_VALUE is not arbitrary as it
// should be. It has to be equal to 3000 because the SpaceNavigator on Windows
// refuses to respond to the DirectInput SetProperty call; it always returns
// values in the [-3000, 3000] range.
#define MAX_SPACENAVIGATOR_INPUT 3000.0f
#define MAX_JOYSTICK_INPUT_VALUE MAX_SPACENAVIGATOR_INPUT
Andrey Kleshchev
committed
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#if LIB_NDOF
std::ostream& operator<<(std::ostream& out, NDOF_Device* ptr)
{
if (! ptr)
{
return out << "nullptr";
}
out << "NDOF_Device{ ";
out << "axes [";
const char* delim = "";
for (short axis = 0; axis < ptr->axes_count; ++axis)
{
out << delim << ptr->axes[axis];
delim = ", ";
}
out << "]";
out << ", buttons [";
delim = "";
for (short button = 0; button < ptr->btn_count; ++button)
{
out << delim << ptr->buttons[button];
delim = ", ";
}
out << "]";
out << ", range " << ptr->axes_min << ':' << ptr->axes_max;
// If we don't coerce these to unsigned, they're streamed as characters,
// e.g. ctrl-A or nul.
out << ", absolute " << unsigned(ptr->absolute);
out << ", valid " << unsigned(ptr->valid);
out << ", manufacturer '" << ptr->manufacturer << "'";
out << ", product '" << ptr->product << "'";
out << ", private " << ptr->private_data;
out << " }";
return out;
}
#endif // LIB_NDOF
#if LL_WINDOWS && !LL_MESA_HEADLESS
// this should reflect ndof and set axises, see ndofdev_win.cpp from ndof package
BOOL CALLBACK EnumObjectsCallback(const DIDEVICEOBJECTINSTANCE* inst, VOID* user_data)
Andrey Kleshchev
committed
{
if (inst->dwType & DIDFT_AXIS)
{
LPDIRECTINPUTDEVICE8 device = *((LPDIRECTINPUTDEVICE8 *)user_data);
DIPROPRANGE diprg;
diprg.diph.dwSize = sizeof(DIPROPRANGE);
diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER);
diprg.diph.dwHow = DIPH_BYID;
diprg.diph.dwObj = inst->dwType; // specify the enumerated axis
// Set the range for the axis
diprg.lMin = (long)-MAX_JOYSTICK_INPUT_VALUE;
diprg.lMax = (long)+MAX_JOYSTICK_INPUT_VALUE;
HRESULT hr = device->SetProperty(DIPROP_RANGE, &diprg.diph);
Andrey Kleshchev
committed
if (FAILED(hr))
{
return DIENUM_STOP;
Andrey Kleshchev
committed
}
}
return DIENUM_CONTINUE;
Andrey Kleshchev
committed
}
BOOL CALLBACK di8_devices_callback(LPCDIDEVICEINSTANCE device_instance_ptr, LPVOID pvRef)
{
// Note: If a single device can function as more than one DirectInput
// device type, it is enumerated as each device type that it supports.
// Capable of detecting devices like Oculus Rift
if (device_instance_ptr)
Andrey Kleshchev
committed
{
std::string product_name = ll_convert_wide_to_string(std::wstring(device_instance_ptr->tszProductName));
Andrey Kleshchev
committed
LLSD guid = LLViewerJoystick::getInstance()->getDeviceUUID();
Andrey Kleshchev
committed
bool init_device = false;
if (guid.isBinary())
{
std::vector<U8> bin_bucket = guid.asBinary();
init_device = memcmp(&bin_bucket[0], &device_instance_ptr->guidInstance, sizeof(GUID)) == 0;
}
else
{
// It might be better to init space navigator here, but if system doesn't has one,
// ndof will pick a random device, it is simpler to pick first device now to have an id
init_device = true;
}
Andrey Kleshchev
committed
if (init_device)
Andrey Kleshchev
committed
{
LL_DEBUGS("Joystick") << "Found and attempting to use device: " << product_name << LL_ENDL;
LPDIRECTINPUT8 di8_interface = *((LPDIRECTINPUT8 *)gViewerWindow->getWindow()->getDirectInput8());
LPDIRECTINPUTDEVICE8 device = NULL;
HRESULT status = di8_interface->CreateDevice(
device_instance_ptr->guidInstance, // REFGUID rguid,
&device, // LPDIRECTINPUTDEVICE * lplpDirectInputDevice,
NULL // LPUNKNOWN pUnkOuter
);
Andrey Kleshchev
committed
if (status == DI_OK)
Andrey Kleshchev
committed
{
// prerequisite for aquire()
LL_DEBUGS("Joystick") << "Device created" << LL_ENDL;
status = device->SetDataFormat(&c_dfDIJoystick); // c_dfDIJoystick2
Andrey Kleshchev
committed
}
if (status == DI_OK)
{
// set properties
LL_DEBUGS("Joystick") << "Format set" << LL_ENDL;
status = device->EnumObjects(EnumObjectsCallback, &device, DIDFT_ALL);
}
if (status == DI_OK)
{
LL_DEBUGS("Joystick") << "Properties updated" << LL_ENDL;
S32 size = sizeof(GUID);
LLSD::Binary binary_data; //just an std::vector
binary_data.resize(size);
memcpy(&binary_data[0], &device_instance_ptr->guidInstance /*POD _GUID*/, size);
LLSD data(binary_data);
LLViewerJoystick::getInstance()->initDevice(&device, product_name, data);
return DIENUM_STOP;
}
}
else
{
LL_DEBUGS("Joystick") << "Found device: " << product_name << LL_ENDL;
Andrey Kleshchev
committed
}
}
return DIENUM_CONTINUE;
}
// Windows guids
// This is GUID2 so teoretically it can be memcpy copied into LLUUID
void guid_from_string(GUID &guid, const std::string &input)
{
CLSIDFromString(ll_convert_string_to_wide(input).c_str(), &guid);
}
std::string string_from_guid(const GUID &guid)
{
OLECHAR* guidString; //wchat
StringFromCLSID(guid, &guidString);
// use guidString...
std::string res = ll_convert_wide_to_string(std::wstring(guidString));
// ensure memory is freed
::CoTaskMemFree(guidString);
return res;
}
Andrey Kleshchev
committed
#endif
// -----------------------------------------------------------------------------
void LLViewerJoystick::updateEnabled(bool autoenable)
{
if (mDriverState == JDS_UNINITIALIZED)
{
gSavedSettings.setBOOL("JoystickEnabled", FALSE );
}
else
{
// autoenable if user specifically chose this device
if (autoenable && (isLikeSpaceNavigator() || isDeviceUUIDSet()))
{
gSavedSettings.setBOOL("JoystickEnabled", TRUE );
}
}
if (!gSavedSettings.getBOOL("JoystickEnabled"))
{
mOverrideCamera = FALSE;
}
}
void LLViewerJoystick::setOverrideCamera(bool val)
{
if (!gSavedSettings.getBOOL("JoystickEnabled"))
{
mOverrideCamera = FALSE;
}
else
{
mOverrideCamera = val;
}
if (mOverrideCamera)
{
gAgentCamera.changeCameraToDefault();
// -----------------------------------------------------------------------------
#if LIB_NDOF
NDOF_HotPlugResult LLViewerJoystick::HotPlugAddCallback(NDOF_Device *dev)
{
NDOF_HotPlugResult res = NDOF_DISCARD_HOTPLUGGED;
LLViewerJoystick* joystick(LLViewerJoystick::getInstance());
if (joystick->mDriverState == JDS_UNINITIALIZED)
{
LL_INFOS("Joystick") << "HotPlugAddCallback: will use device:" << LL_ENDL;
ndof_dump(stderr, dev);
joystick->mDriverState = JDS_INITIALIZED;
res = NDOF_KEEP_HOTPLUGGED;
joystick->updateEnabled(true);
return res;
}
#endif
// -----------------------------------------------------------------------------
#if LIB_NDOF
void LLViewerJoystick::HotPlugRemovalCallback(NDOF_Device *dev)
{
LLViewerJoystick* joystick(LLViewerJoystick::getInstance());
if (joystick->mNdofDev == dev)
{
LL_INFOS("Joystick") << "HotPlugRemovalCallback: joystick->mNdofDev="
<< joystick->mNdofDev << "; removed device:" << LL_ENDL;
ndof_dump(stderr, dev);
}
#endif
// -----------------------------------------------------------------------------
LLViewerJoystick::LLViewerJoystick()
: mDriverState(JDS_UNINITIALIZED),
mNdofDev(NULL),
mResetFlag(false),
{
for (int i = 0; i < 6; i++)
{
mAxes[i] = sDelta[i] = sLastDelta[i] = 0.0f;
}
memset(mBtn, 0, sizeof(mBtn));
// factor in bandwidth? bandwidth = gViewerStats->mKBitStat
Tofu Linden
committed
mPerfScale = 4000.f / gSysCPU.getMHz(); // hmm. why?
mLastDeviceUUID = LLSD::Integer(1);
}
// -----------------------------------------------------------------------------
LLViewerJoystick::~LLViewerJoystick()
{
if (mDriverState == JDS_INITIALIZED)
{
terminate();
}
}
// -----------------------------------------------------------------------------
{
#if LIB_NDOF
static bool libinit = false;
mDriverState = JDS_INITIALIZING;
loadDeviceIdFromSettings();
// Note: The HotPlug callbacks are not actually getting called on Windows
if (ndof_libinit(HotPlugAddCallback,
HotPlugRemovalCallback,
Andrey Kleshchev
committed
gViewerWindow->getWindow()->getDirectInput8()))
{
mDriverState = JDS_UNINITIALIZED;
}
else
{
// NB: ndof_libinit succeeds when there's no device
libinit = true;
// allocate memory once for an eventual device
mNdofDev = ndof_create();
}
}
if (libinit)
{
if (mNdofDev)
Andrey Kleshchev
committed
{
// di8_devices_callback callback is immediate and happens in scope of getInputDevices()
#if LL_WINDOWS && !LL_MESA_HEADLESS
// space navigator is marked as DI8DEVCLASS_GAMECTRL in ndof lib
U32 device_type = DI8DEVCLASS_GAMECTRL;
void* callback = &di8_devices_callback;
Andrey Kleshchev
committed
// MAC doesn't support device search yet
// On MAC there is an ndof_idsearch and it is possible to specify product
// and manufacturer in NDOF_Device for ndof_init_first to pick specific one
U32 device_type = 0;
void* callback = NULL;
#endif
if (!gViewerWindow->getWindow()->getInputDevices(device_type, callback, NULL))
Andrey Kleshchev
committed
{
LL_INFOS("Joystick") << "Failed to gather devices from window. Falling back to ndof's init" << LL_ENDL;
Andrey Kleshchev
committed
// Failed to gather devices from windows, init first suitable one
mLastDeviceUUID = LLSD();
Andrey Kleshchev
committed
void *preffered_device = NULL;
initDevice(preffered_device);
}
if (mDriverState == JDS_INITIALIZING)
{
LL_INFOS("Joystick") << "Found no matching joystick devices." << LL_ENDL;
mDriverState = JDS_UNINITIALIZED;
Andrey Kleshchev
committed
}
}
else
{
mDriverState = JDS_UNINITIALIZED;
}
}
// Autoenable the joystick for recognized devices if nothing was connected previously
if (!autoenable)
{
autoenable = gSavedSettings.getString("JoystickInitialized").empty() ? true : false;
}
if (mDriverState == JDS_INITIALIZED)
{
// A Joystick device is plugged in
if (isLikeSpaceNavigator())
{
// It's a space navigator, we have defaults for it.
if (gSavedSettings.getString("JoystickInitialized") != "SpaceNavigator")
{
// Only set the defaults if we haven't already (in case they were overridden)
setSNDefaults();
gSavedSettings.setString("JoystickInitialized", "SpaceNavigator");
}
}
else
{
// It's not a Space Navigator
gSavedSettings.setString("JoystickInitialized", "UnknownDevice");
}
}
else
{
// No device connected, don't change any settings
}
LL_INFOS("Joystick") << "ndof: mDriverState=" << mDriverState << "; mNdofDev="
<< mNdofDev << "; libinit=" << libinit << LL_ENDL;
void LLViewerJoystick::initDevice(LLSD &guid)
{
#if LIB_NDOF
mLastDeviceUUID = guid;
#if LL_WINDOWS && !LL_MESA_HEADLESS
// space navigator is marked as DI8DEVCLASS_GAMECTRL in ndof lib
U32 device_type = DI8DEVCLASS_GAMECTRL;
void* callback = &di8_devices_callback;
// MAC doesn't support device search yet
// On MAC there is an ndof_idsearch and it is possible to specify product
// and manufacturer in NDOF_Device for ndof_init_first to pick specific one
U32 device_type = 0;
void* callback = NULL;
#endif
mDriverState = JDS_INITIALIZING;
if (!gViewerWindow->getWindow()->getInputDevices(device_type, callback, NULL))
{
LL_INFOS("Joystick") << "Failed to gather devices from window. Falling back to ndof's init" << LL_ENDL;
// Failed to gather devices from windows, init first suitable one
void *preffered_device = NULL;
mLastDeviceUUID = LLSD();
initDevice(preffered_device);
}
if (mDriverState == JDS_INITIALIZING)
{
LL_INFOS("Joystick") << "Found no matching joystick devices." << LL_ENDL;
mDriverState = JDS_UNINITIALIZED;
}
#endif
}
void LLViewerJoystick::initDevice(void * preffered_device /*LPDIRECTINPUTDEVICE8*/, std::string &name, LLSD &guid)
{
#if LIB_NDOF
mLastDeviceUUID = guid;
size_t dest_size = sizeof(mNdofDev->product);
strncpy(mNdofDev->product, name.c_str(), dest_size-1);
mNdofDev->manufacturer[0] = '\0';
initDevice(preffered_device);
#endif
}
Andrey Kleshchev
committed
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
void LLViewerJoystick::initDevice(void * preffered_device /* LPDIRECTINPUTDEVICE8* */)
{
#if LIB_NDOF
// Different joysticks will return different ranges of raw values.
// Since we want to handle every device in the same uniform way,
// we initialize the mNdofDev struct and we set the range
// of values we would like to receive.
//
// HACK: On Windows, libndofdev passes our range to DI with a
// SetProperty call. This works but with one notable exception, the
// SpaceNavigator, who doesn't seem to care about the SetProperty
// call. In theory, we should handle this case inside libndofdev.
// However, the range we're setting here is arbitrary anyway,
// so let's just use the SpaceNavigator range for our purposes.
mNdofDev->axes_min = (long)-MAX_JOYSTICK_INPUT_VALUE;
mNdofDev->axes_max = (long)+MAX_JOYSTICK_INPUT_VALUE;
// libndofdev could be used to return deltas. Here we choose to
// just have the absolute values instead.
mNdofDev->absolute = 1;
// init & use the first suitable NDOF device found on the USB chain
// On windows preffered_device needs to be a pointer to LPDIRECTINPUTDEVICE8
if (ndof_init_first(mNdofDev, preffered_device))
{
mDriverState = JDS_UNINITIALIZED;
LL_WARNS() << "ndof_init_first FAILED" << LL_ENDL;
}
else
{
mDriverState = JDS_INITIALIZED;
}
#endif
}
// -----------------------------------------------------------------------------
void LLViewerJoystick::terminate()
{
#if LIB_NDOF
if (mNdofDev != NULL)
{
ndof_libcleanup(); // frees alocated memory in mNdofDev
mDriverState = JDS_UNINITIALIZED;
mNdofDev = NULL;
LL_INFOS("Joystick") << "Terminated connection with NDOF device." << LL_ENDL;
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
#endif
}
// -----------------------------------------------------------------------------
void LLViewerJoystick::updateStatus()
{
#if LIB_NDOF
ndof_update(mNdofDev);
for (int i=0; i<6; i++)
{
mAxes[i] = (F32) mNdofDev->axes[i] / mNdofDev->axes_max;
}
for (int i=0; i<16; i++)
{
mBtn[i] = mNdofDev->buttons[i];
}
#endif
}
// -----------------------------------------------------------------------------
F32 LLViewerJoystick::getJoystickAxis(U32 axis) const
{
if (axis < 6)
{
return mAxes[axis];
}
return 0.f;
}
// -----------------------------------------------------------------------------
U32 LLViewerJoystick::getJoystickButton(U32 button) const
{
if (button < 16)
{
return mBtn[button];
}
return 0;
}
// -----------------------------------------------------------------------------
void LLViewerJoystick::handleRun(F32 inc)
{
// Decide whether to walk or run by applying a threshold, with slight
// hysteresis to avoid oscillating between the two with input spikes.
// Analog speed control would be better, but not likely any time soon.
if (inc > gSavedSettings.getF32("JoystickRunThreshold"))
{
if (1 == mJoystickRun)
{
++mJoystickRun;
// gAgent.setRunning();
// gAgent.sendWalkRun(gAgent.getRunning());
// [RLVa:KB] - Checked: 2011-05-11 (RLVa-1.3.0i) | Added: RLVa-1.3.0i
gAgent.setTempRun();
// [/RLVa:KB]
}
else if (0 == mJoystickRun)
{
// hysteresis - respond NEXT frame
++mJoystickRun;
}
}
else
{
if (mJoystickRun > 0)
{
--mJoystickRun;
if (0 == mJoystickRun)
{
// gAgent.clearRunning();
// gAgent.sendWalkRun(gAgent.getRunning());
// [RLVa:KB] - Checked: 2011-05-11 (RLVa-1.3.0i) | Added: RLVa-1.3.0i
gAgent.clearTempRun();
// [/RLVa:KB]
// -----------------------------------------------------------------------------
void LLViewerJoystick::agentJump()
{
gAgent.moveUp(1);
}
// -----------------------------------------------------------------------------
void LLViewerJoystick::agentSlide(F32 inc)
{
{
gAgent.moveLeft(-1);
}
}
// -----------------------------------------------------------------------------
void LLViewerJoystick::agentPush(F32 inc)
{
{
gAgent.moveAt(-1, false);
}
}
// -----------------------------------------------------------------------------
void LLViewerJoystick::agentFly(F32 inc)
{
if (! (gAgent.getFlying() ||
!gAgent.canFly() ||
gAgent.upGrabbed() ||
!ALControlCache::AutomaticFly) )
{
// crouch
gAgent.moveUp(-1);
}
}
// -----------------------------------------------------------------------------
void LLViewerJoystick::agentPitch(F32 pitch_inc)
if (pitch_inc < 0)
{
gAgent.setControlFlags(AGENT_CONTROL_PITCH_POS);
}
else if (pitch_inc > 0)
{
gAgent.setControlFlags(AGENT_CONTROL_PITCH_NEG);
}
gAgent.pitch(-pitch_inc);
}
// -----------------------------------------------------------------------------
void LLViewerJoystick::agentYaw(F32 yaw_inc)
{
// Cannot steer some vehicles in mouselook if the script grabs the controls
if (gAgentCamera.cameraMouselook() && !gSavedSettings.getBOOL("JoystickMouselookYaw"))
{
gAgent.rotate(-yaw_inc, gAgent.getReferenceUpVector());
}
else
{
if (yaw_inc < 0)
{
gAgent.setControlFlags(AGENT_CONTROL_YAW_POS);
}
else if (yaw_inc > 0)
{
gAgent.setControlFlags(AGENT_CONTROL_YAW_NEG);
}
gAgent.yaw(-yaw_inc);
}
}
// -----------------------------------------------------------------------------
Mark Palange
committed
void LLViewerJoystick::resetDeltas(S32 axis[])
{
for (U32 i = 0; i < 6; i++)
{
sLastDelta[i] = -mAxes[axis[i]];
sDelta[i] = 0.f;
}
sLastDelta[6] = sDelta[6] = 0.f;
mResetFlag = false;
}
// -----------------------------------------------------------------------------
void LLViewerJoystick::moveObjects(bool reset)
{
static bool toggle_send_to_sim = false;
if (!gFocusMgr.getAppHasFocus() || mDriverState != JDS_INITIALIZED
|| !gSavedSettings.getBOOL("JoystickEnabled") || !gSavedSettings.getBOOL("JoystickBuildEnabled"))
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
{
return;
}
S32 axis[] =
{
gSavedSettings.getS32("JoystickAxis0"),
gSavedSettings.getS32("JoystickAxis1"),
gSavedSettings.getS32("JoystickAxis2"),
gSavedSettings.getS32("JoystickAxis3"),
gSavedSettings.getS32("JoystickAxis4"),
gSavedSettings.getS32("JoystickAxis5"),
};
if (reset || mResetFlag)
{
resetDeltas(axis);
return;
}
F32 axis_scale[] =
{
gSavedSettings.getF32("BuildAxisScale0"),
gSavedSettings.getF32("BuildAxisScale1"),
gSavedSettings.getF32("BuildAxisScale2"),
gSavedSettings.getF32("BuildAxisScale3"),
gSavedSettings.getF32("BuildAxisScale4"),
gSavedSettings.getF32("BuildAxisScale5"),
};
F32 dead_zone[] =
{
gSavedSettings.getF32("BuildAxisDeadZone0"),
gSavedSettings.getF32("BuildAxisDeadZone1"),
gSavedSettings.getF32("BuildAxisDeadZone2"),
gSavedSettings.getF32("BuildAxisDeadZone3"),
gSavedSettings.getF32("BuildAxisDeadZone4"),
gSavedSettings.getF32("BuildAxisDeadZone5"),
};
F32 cur_delta[6];
F32 time = gFrameIntervalSeconds.value();
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
// avoid making ridicously big movements if there's a big drop in fps
if (time > .2f)
{
time = .2f;
}
// max feather is 32
F32 feather = gSavedSettings.getF32("BuildFeathering");
bool is_zero = true, absolute = gSavedSettings.getBOOL("Cursor3D");
for (U32 i = 0; i < 6; i++)
{
cur_delta[i] = -mAxes[axis[i]];
F32 tmp = cur_delta[i];
if (absolute)
{
cur_delta[i] = cur_delta[i] - sLastDelta[i];
}
sLastDelta[i] = tmp;
is_zero = is_zero && (cur_delta[i] == 0.f);
if (cur_delta[i] > 0)
{
cur_delta[i] = llmax(cur_delta[i]-dead_zone[i], 0.f);
}
else
{
cur_delta[i] = llmin(cur_delta[i]+dead_zone[i], 0.f);
}
cur_delta[i] *= axis_scale[i];
if (!absolute)
{
cur_delta[i] *= time;
}
Josh Bell
committed
sDelta[i] = sDelta[i] + (cur_delta[i]-sDelta[i])*time*feather;
}
Josh Bell
committed
U32 upd_type = UPD_NONE;
LLVector3 v;
if (!is_zero)
{
if (gAwayTimer.getElapsedTimeF32() > LLAgent::MIN_AFK_TIME)
if (sDelta[0] || sDelta[1] || sDelta[2])
{
upd_type |= UPD_POSITION;
v.setVec(sDelta[0], sDelta[1], sDelta[2]);
}
if (sDelta[3] || sDelta[4] || sDelta[5])
{
upd_type |= UPD_ROTATION;
}
// the selection update could fail, so we won't send
if (LLSelectMgr::getInstance()->selectionMove(v, sDelta[3],sDelta[4],sDelta[5], upd_type))
{
toggle_send_to_sim = true;
}
}
else if (toggle_send_to_sim)
{
LLSelectMgr::getInstance()->sendSelectionMove();
toggle_send_to_sim = false;
}
}
Josh Bell
committed
// -----------------------------------------------------------------------------
void LLViewerJoystick::moveAvatar(bool reset)
Josh Bell
committed
{
if (!gFocusMgr.getAppHasFocus() || mDriverState != JDS_INITIALIZED
|| !gSavedSettings.getBOOL("JoystickEnabled") || !gSavedSettings.getBOOL("JoystickAvatarEnabled"))
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
{
return;
}
S32 axis[] =
{
// [1 0 2 4 3 5]
// [Z X Y RZ RX RY]
gSavedSettings.getS32("JoystickAxis0"),
gSavedSettings.getS32("JoystickAxis1"),
gSavedSettings.getS32("JoystickAxis2"),
gSavedSettings.getS32("JoystickAxis3"),
gSavedSettings.getS32("JoystickAxis4"),
gSavedSettings.getS32("JoystickAxis5")
};
if (reset || mResetFlag)
{
resetDeltas(axis);
if (reset)
{
// Note: moving the agent triggers agent camera mode;
// don't do this every time we set mResetFlag (e.g. because we gained focus)
gAgent.moveAt(0, true);
}
return;
}
static bool button_held = false;
// If AutomaticFly is enabled, then button1 merely causes a
// jump (as the up/down axis already controls flying) if on the
// ground, or cease flight if already flying.
// If AutomaticFly is disabled, then button1 toggles flying.
if (ALControlCache::AutomaticFly)
{
if (!gAgent.getFlying())
{
gAgent.moveUp(1);
}
else if (!button_held)
{
button_held = true;
gAgent.setFlying(FALSE);
}
}
else if (!button_held)
{
button_held = true;
gAgent.setFlying(!gAgent.getFlying());
}
else
{
button_held = false;
}
F32 axis_scale[] =
{
gSavedSettings.getF32("AvatarAxisScale0"),
gSavedSettings.getF32("AvatarAxisScale1"),
gSavedSettings.getF32("AvatarAxisScale2"),
gSavedSettings.getF32("AvatarAxisScale3"),
gSavedSettings.getF32("AvatarAxisScale4"),
gSavedSettings.getF32("AvatarAxisScale5")
};
Josh Bell
committed
F32 dead_zone[] =
{
gSavedSettings.getF32("AvatarAxisDeadZone0"),
gSavedSettings.getF32("AvatarAxisDeadZone1"),
gSavedSettings.getF32("AvatarAxisDeadZone2"),
gSavedSettings.getF32("AvatarAxisDeadZone3"),
gSavedSettings.getF32("AvatarAxisDeadZone4"),
gSavedSettings.getF32("AvatarAxisDeadZone5")
};
Josh Bell
committed
// time interval in seconds between this frame and the previous
F32 time = gFrameIntervalSeconds.value();
Josh Bell
committed
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
// avoid making ridicously big movements if there's a big drop in fps
if (time > .2f)
{
time = .2f;
}
// note: max feather is 32.0
F32 feather = gSavedSettings.getF32("AvatarFeathering");
F32 cur_delta[6];
F32 val, dom_mov = 0.f;
U32 dom_axis = Z_I;
#if LIB_NDOF
bool absolute = (gSavedSettings.getBOOL("Cursor3D") && mNdofDev->absolute);
#else
bool absolute = false;
#endif
// remove dead zones and determine biggest movement on the joystick
for (U32 i = 0; i < 6; i++)
{
cur_delta[i] = -mAxes[axis[i]];
if (absolute)
{
F32 tmp = cur_delta[i];
cur_delta[i] = cur_delta[i] - sLastDelta[i];
sLastDelta[i] = tmp;
}
if (cur_delta[i] > 0)
{
cur_delta[i] = llmax(cur_delta[i]-dead_zone[i], 0.f);
}
else
{
cur_delta[i] = llmin(cur_delta[i]+dead_zone[i], 0.f);
}
// we don't care about Roll (RZ) and Z is calculated after the loop
if (i != Z_I && i != RZ_I)
{
// find out the axis with the biggest joystick motion
val = fabs(cur_delta[i]);
if (val > dom_mov)
{
dom_axis = i;
dom_mov = val;
}
}
is_zero = is_zero && (cur_delta[i] == 0.f);
}
if (!is_zero)
{
// Clear AFK state if moved beyond the deadzone
if (gAwayTimer.getElapsedTimeF32() > LLAgent::MIN_AFK_TIME)
{
gAgent.clearAFK();
}
setCameraNeedsUpdate(true);
}
// forward|backward movements overrule the real dominant movement if
// they're bigger than its 20%. This is what you want 'cos moving forward
// is what you do most. We also added a special (even more lenient) case
// for RX|RY to allow walking while pitching and turning