diff options
author | Leviathan Linden <leviathan@lindenlab.com> | 2023-11-16 13:53:37 -0800 |
---|---|---|
committer | Andrew Meadows <andrew.l.meadows@gmail.com> | 2024-10-03 09:01:12 -0700 |
commit | ed6ecca2a45e52d9be1d91107b9643b5ecdfb8bf (patch) | |
tree | 2b9d241bf5cb0520de13ee10f729d2b58278ce33 /indra/newview | |
parent | 13221f67c465017f44ca46aeca23b0d820935825 (diff) |
avatar_motion-->GameControl translation and flycam
Diffstat (limited to 'indra/newview')
24 files changed, 1586 insertions, 159 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 7fbf214dcf..627f1b4326 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -317,6 +317,7 @@ set(viewer_SOURCE_FILES llfollowcam.cpp llfriendcard.cpp llflyoutcombobtn.cpp + llflycam.cpp llgesturelistener.cpp llgesturemgr.cpp llgiveinventory.cpp @@ -991,6 +992,7 @@ set(viewer_HEADER_FILES llfollowcam.h llfriendcard.h llflyoutcombobtn.h + llflycam.h llgesturelistener.h llgesturemgr.h llgiveinventory.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 55f8f77383..ebe027689a 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -2612,10 +2612,10 @@ <key>Value</key> <integer>1</integer> </map> - <key>EnableGameControlInput</key> + <key>GameControlToServer</key> <map> <key>Comment</key> - <string>Transmit game controller input to server</string> + <string>Transmit game controller data to server</string> <key>Persist</key> <integer>1</integer> <key>Type</key> @@ -2623,10 +2623,21 @@ <key>Value</key> <integer>0</integer> </map> - <key>EnableGameControlKeyboardInput</key> + <key>GameControlToAgent</key> <map> <key>Comment</key> - <string>Send 'unhandled' keystrokes as GameInput to server</string> + <string>GameControl data moves avatar/flycam</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>AgentToGameControl</key> + <map> + <key>Comment</key> + <string>Avatar/flycam movement produces GameControl data</string> <key>Persist</key> <integer>1</integer> <key>Type</key> diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index c8b0adbaf8..406734ab61 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -1463,7 +1463,7 @@ LLVector3 LLAgent::getReferenceUpVector() } -// Radians, positive is forward into ground +// Radians, positive is downward toward ground //----------------------------------------------------------------------------- // pitch() //----------------------------------------------------------------------------- @@ -1477,27 +1477,23 @@ void LLAgent::pitch(F32 angle) LLVector3 skyward = getReferenceUpVector(); + // SL-19286 Avatar is upside down when viewed from below + // after left-clicking the mouse on the avatar and dragging down + // + // The issue is observed on angle below 10 degrees + const F32 look_down_limit = 179.f * DEG_TO_RAD; + const F32 look_up_limit = 10.f * DEG_TO_RAD; + + F32 angle_from_skyward = acos(mFrameAgent.getAtAxis() * skyward); + // clamp pitch to limits - if (angle >= 0.f) + if ((angle >= 0.f) && (angle_from_skyward + angle > look_down_limit)) { - const F32 look_down_limit = 179.f * DEG_TO_RAD; - F32 angle_from_skyward = acos(mFrameAgent.getAtAxis() * skyward); - if (angle_from_skyward + angle > look_down_limit) - { - angle = look_down_limit - angle_from_skyward; - } + angle = look_down_limit - angle_from_skyward; } - else if (angle < 0.f) + else if ((angle < 0.f) && (angle_from_skyward + angle < look_up_limit)) { - const F32 look_up_limit = 5.f * DEG_TO_RAD; - const LLVector3& viewer_camera_pos = LLViewerCamera::getInstance()->getOrigin(); - LLVector3 agent_focus_pos = getPosAgentFromGlobal(gAgentCamera.calcFocusPositionTargetGlobal()); - LLVector3 look_dir = agent_focus_pos - viewer_camera_pos; - F32 angle_from_skyward = angle_between(look_dir, skyward); - if (angle_from_skyward + angle < look_up_limit) - { - angle = look_up_limit - angle_from_skyward; - } + angle = look_up_limit - angle_from_skyward; } if (fabs(angle) > 1e-4) @@ -4986,6 +4982,245 @@ void LLAgent::renderAutoPilotTarget() } } +void LLAgent::setExternalActionFlags(U32 outer_flags) +{ + if (LLGameControl::willControlAvatar()) + { + // save these flags for later, for when we're ready + // to actually send an AgentUpdate packet + mExternalActionFlags = outer_flags; + mbFlagsDirty = TRUE; + } +} + +static U64 g_lastUpdateTime { 0 }; +static F32 g_deltaTime { 0.0f }; +static S32 g_lastUpdateFrame { 0 }; +static S32 g_deltaFrame { 0 }; + +void LLAgent::applyExternalActionFlags() +{ + if (! LLGameControl::willControlAvatar()) + { + return; + } + + // HACK: AGENT_CONTROL_NUDGE_AT_NEG is used to toggle Flycam + if ((mExternalActionFlags & AGENT_CONTROL_NUDGE_AT_NEG) > 0) + { + if (mToggleFlycam) + { + mUsingFlycam = !mUsingFlycam; + if (mUsingFlycam) + { + // copy main camera transform to flycam + LLViewerCamera* camera = LLViewerCamera::getInstance(); + mFlycam.setTransform(camera->getOrigin(), camera->getQuaternion()); + mLastFlycamUpdate = LLFrameTimer::getTotalTime(); + } + /* + else + { + // do we need to reset main camera? + } + */ + } + mToggleFlycam = false; + } + else + { + mToggleFlycam = true; + } + + // measure delta time and frame + // Note: it is possible for the deltas to be very large + // and it is the duty of the code that uses them to clamp as necessary + U64 now = LLFrameTimer::getTotalTime(); + g_deltaTime = F32(now - g_lastUpdateTime) / (F32)(USEC_PER_SEC); + g_lastUpdateTime = now; + + S32 frame_count = LLFrameTimer::getFrameCount(); + g_deltaFrame = frame_count - g_lastUpdateFrame; + g_lastUpdateFrame = frame_count; + + if (mUsingFlycam) + { + updateFlycam(); + return; + } + + S32 direction = (S32)(mExternalActionFlags & AGENT_CONTROL_AT_POS) + - (S32)((mExternalActionFlags & AGENT_CONTROL_AT_NEG) >> 1); + if (direction != 0) + { + moveAt(direction); + } + + static U32 last_non_fly_frame = 0; + static U64 last_non_fly_time = 0; + direction = (S32)(mExternalActionFlags & AGENT_CONTROL_UP_POS) + - (S32)((mExternalActionFlags & AGENT_CONTROL_UP_NEG) >> 1); + if (direction != 0) + { + // HACK: this auto-fly logic based on original code still extant in llviewerinput.cpp::agent_jump() + // but has been cleaned up. + // TODO?: DRY this logic + if (direction > 0) + { + if (!getFlying() + && !upGrabbed() + && gSavedSettings.getBOOL("AutomaticFly")) + { + constexpr F32 FLY_TIME = 0.5f; + constexpr U32 FLY_FRAMES = 4; + F32 delta_time = (F32)(now - last_non_fly_time) / (F32)(USEC_PER_SEC); + U32 delta_frames = frame_count - last_non_fly_frame; + if( delta_time > FLY_TIME + && delta_frames > FLY_FRAMES) + { + setFlying(TRUE); + } + } + } + else + { + last_non_fly_frame = frame_count; + last_non_fly_time = now; + } + + moveUp(direction); + } + else if (!getFlying()) + { + last_non_fly_frame = frame_count; + last_non_fly_time = now; + } + + direction = (S32)(mExternalActionFlags & AGENT_CONTROL_LEFT_POS) + - (S32)((mExternalActionFlags & AGENT_CONTROL_LEFT_NEG) >> 1); + if (direction != 0) + { + moveLeft(direction); + } + + direction = (S32)(mExternalActionFlags & AGENT_CONTROL_YAW_POS) + - (S32)((mExternalActionFlags & AGENT_CONTROL_YAW_NEG) >> 1); + if (direction != 0) + { + F32 sign = (direction < 0 ? -1.0f : 1.0f); + // HACK: hard-code 3.0 seconds for YawRate measure. It is simpler, + // and the missing variable yaw rate is unnoticeable. + moveYaw(sign * LLFloaterMove::getYawRate(3.0f)); + } + + { + F32 pitch = ((mExternalActionFlags & AGENT_CONTROL_PITCH_POS) > 0 ? 1.0f : 0.0f) + - ((mExternalActionFlags & AGENT_CONTROL_PITCH_NEG) > 0 ? 1.0f : 0.0f); + movePitch(pitch); + } + + if ((mExternalActionFlags & AGENT_CONTROL_FLY) > 0) + { + if (mToggleFly) + { + setFlying(!getFlying()); + } + mToggleFly = false; + } + else + { + mToggleFly = true; + } + + if (mExternalActionFlags & AGENT_CONTROL_STOP) + { + setControlFlags(AGENT_CONTROL_STOP); + } + + if ((mExternalActionFlags & AGENT_CONTROL_SIT_ON_GROUND) > 0) + { + if (mToggleSit) + { + if (isSitting()) + { + standUp(); + } + else + { + sitDown(); + } + } + mToggleSit = false; + } + else + { + mToggleSit = true; + } + + // HACK: AGENT_CONTROL_NUDGE_AT_POS is used to toggle running + if ((mExternalActionFlags & AGENT_CONTROL_NUDGE_AT_POS) > 0) + { + if (mToggleRun) + { + if (getRunning()) + { + clearRunning(); + sendWalkRun(false); + } + else + { + setRunning(); + sendWalkRun(true); + } + } + mToggleRun = false; + } + else + { + mToggleRun = true; + } +} + +void LLAgent::updateFlycam() +{ + // Note: no matter how camera_inputs are mapped to the controller + // they arrive in the following order: + enum FLYCAM_AXIS { + FLYCAM_FORWARD = 0, + FLYCAM_LEFT, + FLYCAM_UP, + FLYCAM_PITCH, + FLYCAM_YAW, + FLYCAM_ZOOM + }; + std::vector<F32> camera_inputs; + LLGameControl::getCameraInputs(camera_inputs); + + LLVector3 linear_velocity( + camera_inputs[FLYCAM_FORWARD], + camera_inputs[FLYCAM_LEFT], + camera_inputs[FLYCAM_UP]); + constexpr F32 MAX_FLYCAM_SPEED = 10.0f; + mFlycam.setLinearVelocity(MAX_FLYCAM_SPEED * linear_velocity); + + mFlycam.setPitchRate(camera_inputs[FLYCAM_PITCH]); + mFlycam.setYawRate(camera_inputs[FLYCAM_PITCH]); + + mFlycam.integrate(g_deltaTime); + + LLVector3 pos; + LLQuaternion rot; + mFlycam.getTransform(pos, rot); + + // copy flycam transform to main camera + LLMatrix3 mat(rot); + //LLViewerCamera::getInstance()->setView(sFlycamZoom); + LLViewerCamera::getInstance()->setOrigin(pos); + LLViewerCamera::getInstance()->mXAxis = LLVector3(mat.mMatrix[0]); + LLViewerCamera::getInstance()->mYAxis = LLVector3(mat.mMatrix[1]); + LLViewerCamera::getInstance()->mZAxis = LLVector3(mat.mMatrix[2]); +} + /********************************************************************************/ //----------------------------------------------------------------------------- diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h index afc34f747f..0394f9c76c 100644 --- a/indra/newview/llagent.h +++ b/indra/newview/llagent.h @@ -33,6 +33,8 @@ #include "llcharacter.h" #include "llcoordframe.h" // for mFrameAgent #include "llavatarappearancedefines.h" +#include "llflycam.h" +#include "llkeyboard.h" #include "llpermissionsflags.h" #include "llevents.h" #include "v3dmath.h" @@ -485,6 +487,7 @@ public: void resetControlFlags(); bool anyControlGrabbed() const; // True iff a script has taken over a control bool isControlGrabbed(S32 control_index) const; + bool isUsingFlycam() const { return mUsingFlycam; } // Send message to simulator to force grabbed controls to be // released, in case of a poorly written script. void forceReleaseControls(); @@ -492,9 +495,38 @@ public: private: S32 mControlsTakenCount[TOTAL_CONTROLS]; S32 mControlsTakenPassedOnCount[TOTAL_CONTROLS]; + // mControlFlags is a bitmask of behavior instructions for compact + // transmission to the server. It does NOT represent "input", rather + // the consequences of it, which will sometimes depend on "state". U32 mControlFlags; // Replacement for the mFooKey's //-------------------------------------------------------------------- + // GameControls + //-------------------------------------------------------------------- +public: + // ActionFlags are similar to, but not the same as, ControlFlags! + // An 'ActionFlags' bitmask stores 'simplified input' from key/button + // presses that are mapped to avatar/camera movement actions + // whereas 'mControlFlags' are a more complicated set of behavior bits + // computed as a function of input and state. + // + void setExternalActionFlags(U32 flags); + void applyExternalActionFlags(); + void applyExternalActionFlagsForFlycam(); + +private: + void updateFlycam(); + + U64 mLastFlycamUpdate { 0 }; + U32 mExternalActionFlags { 0 }; + LLFlycam mFlycam; + bool mToggleFly { true }; + bool mToggleSit { true }; + bool mToggleRun { true }; + bool mToggleFlycam { true }; + bool mUsingFlycam { false }; + + //-------------------------------------------------------------------- // Animations //-------------------------------------------------------------------- public: diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index ffa742d154..ed64dce109 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -215,6 +215,7 @@ #include "llvosurfacepatch.h" #include "llviewerfloaterreg.h" #include "llcommandlineparser.h" +#include "llfloaterpreference.h" #include "llfloatermemleak.h" #include "llfloaterreg.h" #include "llfloatersimplesnapshot.h" @@ -1418,27 +1419,76 @@ bool LLAppViewer::frame() return ret; } +// util for detecting most active input channel +LLGameControl::InputChannel get_active_input_channel(const LLGameControl::State& state) +{ + LLGameControl::InputChannel input; + if (state.mButtons > 0) + { + // check buttons + input.mType = LLGameControl::InputChannel::TYPE_BUTTON; + for (U8 i = 0; i < 32; ++i) + { + if ((0x1 << i) & state.mButtons) + { + input.mIndex = i; + break; + } + } + } + else + { + // scan axes + S16 threshold = std::numeric_limits<S16>::max() / 2; + for (U8 i = 0; i < 6; ++i) + { + if (abs(state.mAxes[i]) > threshold) + { + input.mType = LLGameControl::InputChannel::TYPE_AXIS; + // input.mIndex ultimately translates to a LLGameControl::KeyboardAxis + // which distinguishes between negative and positive directions + // so we must translate to axis index "i" according to the sign + // of the axis value. + input.mIndex = i; + input.mSign = state.mAxes[i] > 0 ? 1 : -1; + break; + } + } + } + return input; +} // static bool packGameControlInput(LLMessageSystem* msg) { - if (! LLGameControl::computeFinalInputAndCheckForChanges()) + if (! LLGameControl::computeFinalStateAndCheckForChanges()) { + // Note: LLGameControl manages some re-send logic + // so if we get here: nothing changed AND there is no need for a re-send return false; } - if (!gSavedSettings.getBOOL("EnableGameControlInput")) + if (!gSavedSettings.getBOOL("GameControlToServer")) { - LLGameControl::clearAllInput(); + LLGameControl::clearAllState(); return false; } + const LLGameControl::State& state = LLGameControl::getState(); + + if (LLPanelPreferenceGameControl::isWaitingForInputChannel()) + { + LLGameControl::InputChannel channel = get_active_input_channel(state); + if (channel.mType != LLGameControl::InputChannel::TYPE_NONE) + { + LLPanelPreferenceGameControl::applyGameControlInput(channel); + } + } + msg->newMessageFast(_PREHASH_GameControlInput); msg->nextBlock("AgentData"); msg->addUUID("AgentID", gAgentID); msg->addUUID("SessionID", gAgentSessionID); - const LLGameControl::State& state = LLGameControl::getState(); - size_t num_indices = state.mAxes.size(); for (U8 i = 0; i < num_indices; ++i) { @@ -1581,15 +1631,6 @@ bool LLAppViewer::doFrame() joystick->scanJoystick(); gKeyboard->scanKeyboard(); gViewerInput.scanMouse(); - - LLGameControl::setIncludeKeyboardButtons(gSavedSettings.getBOOL("EnableGameControlKeyboardInput")); - LLGameControl::processEvents(gFocusMgr.getAppHasFocus()); - // to help minimize lag we send GameInput packets immediately - // after getting the latest GameController input - if (packGameControlInput(gMessageSystem)) - { - gAgent.sendMessage(); - } } // Update state based on messages, user input, object idle. @@ -4823,11 +4864,37 @@ void LLAppViewer::idle() send_agent_update(false); - // After calling send_agent_update() in the mainloop we always clear - // the agent's ephemeral ControlFlags (whether an AgentUpdate was - // actually sent or not) because these will be recomputed based on - // real-time key/controller input and resubmitted next frame. - gAgent.resetControlFlags(); + // Note: we process game_control before sending AgentUpdate + // because it may translate to control flags that control avatar motion. + LLGameControl::processEvents(gFocusMgr.getAppHasFocus()); + + // trade flags between gAgent and LLGameControl + U32 control_flags = gAgent.getControlFlags(); + U32 action_flags = LLGameControl::computeInternalActionFlags(); + LLGameControl::setExternalActionFlags(control_flags); + if (packGameControlInput(gMessageSystem)) + { + // to help minimize lag we send GameInput packets ASAP + gAgent.sendMessage(); + } + gAgent.setExternalActionFlags(action_flags); + + // When appropriate, update agent location to the simulator. + F32 agent_update_time = agent_update_timer.getElapsedTimeF32(); + F32 agent_force_update_time = mLastAgentForceUpdate + agent_update_time; + bool force_update = gAgent.controlFlagsDirty() + || (mLastAgentControlFlags != gAgent.getControlFlags()) + || (agent_force_update_time > (1.0f / (F32) AGENT_FORCE_UPDATES_PER_SECOND)); + if (force_update || (agent_update_time > (1.0f / (F32) AGENT_UPDATES_PER_SECOND))) + { + gAgent.applyExternalActionFlags(); + LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; + // Send avatar and camera info + mLastAgentControlFlags = gAgent.getControlFlags(); + mLastAgentForceUpdate = force_update ? 0 : agent_force_update_time; + send_agent_update(force_update); + agent_update_timer.reset(); + } } ////////////////////////////////////// @@ -5078,6 +5145,10 @@ void LLAppViewer::idle() { LLViewerJoystick::getInstance()->moveFlycam(); } + else if (gAgent.isUsingFlycam()) + { + // TODO: implement this + } else { if (LLToolMgr::getInstance()->inBuildMode()) diff --git a/indra/newview/llfloaterjoystick.cpp b/indra/newview/llfloaterjoystick.cpp index 68b11ec92b..1f7f97aad5 100644 --- a/indra/newview/llfloaterjoystick.cpp +++ b/indra/newview/llfloaterjoystick.cpp @@ -79,14 +79,17 @@ BOOL CALLBACK di8_list_devices_callback(LPCDIDEVICEINSTANCE device_instance_ptr, if (device_instance_ptr && pvRef) { std::string product_name = utf16str_to_utf8str(llutf16string(device_instance_ptr->tszProductName)); - S32 size = sizeof(GUID); - LLSD::Binary data; //just an std::vector - data.resize(size); - memcpy(&data[0], &device_instance_ptr->guidInstance /*POD _GUID*/, size); - - LLFloaterJoystick * floater = (LLFloaterJoystick*)pvRef; - LLSD value = data; - floater->addDevice(product_name, value); + if (LLViewerJoystick::is3DConnexionDevice(product_name)) + { + S32 size = sizeof(GUID); + LLSD::Binary data; //just an std::vector + data.resize(size); + memcpy(&data[0], &device_instance_ptr->guidInstance /*POD _GUID*/, size); + + LLFloaterJoystick * floater = (LLFloaterJoystick*)pvRef; + LLSD value = data; + floater->addDevice(product_name, value); + } } return DIENUM_CONTINUE; } @@ -303,8 +306,11 @@ void LLFloaterJoystick::refreshListOfDevices() #if LL_WINDOWS && !LL_MESA_HEADLESS LL_WARNS() << "NDOF connected to device without using SL provided handle" << LL_ENDL; #endif - std::string desc = joystick->getDescription(); - if (!desc.empty()) + // This feature used to support various gamepad devices however + // going forward we will restrict it to 3DConnexion devices (SpaceMouse, etc) + // and will handle gamepads with the GameControl feature. + std::string desc = LLViewerJoystick::getInstance()->getDescription(); + if (LLViewerJoystick::is3DConnexionDevice(desc)) { LLSD value = LLSD::Integer(1); // value for selection addDevice(desc, value); diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index d60d41ae3c..17779a24f2 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -54,6 +54,7 @@ #include "llfloaterperformance.h" #include "llfloatersidepanelcontainer.h" #include "llfloaterimsession.h" +#include "llgamecontrol.h" #include "llkeyboard.h" #include "llmodaldialog.h" #include "llnavigationbar.h" @@ -2320,7 +2321,7 @@ public: mAccountIndependentSettings.push_back("AutoDisengageMic"); } - /*virtual*/ void saveSettings() + void saveSettings() override { LLPanelPreference::saveSettings(); @@ -3127,6 +3128,368 @@ void LLPanelPreferenceControls::onCancelKeyBind() pControlsTable->deselectAllItems(); } +//------------------------LLPanelPreferenceGameControl-------------------------------- + +// LLPanelPreferenceGameControl is effectively a singleton, so we track its instance +static LLPanelPreferenceGameControl* gGameControlPanel; + +LLPanelPreferenceGameControl::LLPanelPreferenceGameControl() +{ + gGameControlPanel = this; +} + +LLPanelPreferenceGameControl::~LLPanelPreferenceGameControl() +{ + gGameControlPanel = nullptr; +} + +static LLPanelInjector<LLPanelPreferenceGameControl> t_pref_game_control("panel_preference_game_control"); + +void LLPanelPreferenceGameControl::apply() +{ +} + +void LLPanelPreferenceGameControl::loadDefaults() +{ + // TODO: implement this +} + +void LLPanelPreferenceGameControl::loadSettings() +{ + // TODO: implement this +} + +void LLPanelPreferenceGameControl::saveSettings() +{ + // TODO: implement this +} + +void LLPanelPreferenceGameControl::updateEnabledState() +{ + // TODO?: implement this +} + +static LLScrollListItem* gSelectedItem { nullptr }; +static LLScrollListCell* gSelectedCell { nullptr }; + +void LLPanelPreferenceGameControl::onClickGameControlToServer(LLUICtrl* ctrl) +{ + BOOL checked = mCheckGameControlToServer->get(); + gSavedSettings.setBOOL( "GameControlToServer", checked ); + LLGameControl::enableSendToServer(checked); +} + +void LLPanelPreferenceGameControl::onClickGameControlToAgent(LLUICtrl* ctrl) +{ + BOOL checked = mCheckGameControlToAgent->get(); + gSavedSettings.setBOOL( "GameControlToAgent", checked ); + LLGameControl::enableControlAgent(checked); + + mActionTable->deselectAllItems(); + bool table_enabled = checked || mCheckAgentToGameControl->get(); + mActionTable->setEnabled(table_enabled); + mChannelSelector->setEnabled(table_enabled); + LLGameControl::enableTranslateAgentActions(checked); +} + +void LLPanelPreferenceGameControl::onClickAgentToGameControl(LLUICtrl* ctrl) +{ + BOOL checked = mCheckAgentToGameControl->get(); + gSavedSettings.setBOOL( "AgentToGameControl", checked ); + + mActionTable->deselectAllItems(); + bool table_enabled = checked || mCheckGameControlToAgent->get(); + mActionTable->setEnabled(table_enabled); + mChannelSelector->setEnabled(table_enabled); + LLGameControl::enableTranslateAgentActions(checked); + +} + +void LLPanelPreferenceGameControl::onActionSelect() +{ + clearSelectionState(); + + LLScrollListItem* item = mActionTable->getFirstSelected(); + if (item == NULL) + { + return; + } + + std::string action = item->getValue(); + + if (action.empty()) + { + mActionTable->deselectAllItems(); + return; + } + + S32 cell_index = item->getSelectedCell(); + if (cell_index != 1) + { + mActionTable->deselectAllItems(); + return; + } + + LLScrollListText* cell = dynamic_cast<LLScrollListText*>(item->getColumn(cell_index)); + if (cell) + { + gSelectedItem = item; + gSelectedCell = cell; + + // compute new rect for mChannelSelector + S32 row = mActionTable->getFirstSelectedIndex(); + S32 column = item->getSelectedCell(); + LLRect cell_rect = mActionTable->getCellRect(row, column); + + LLRect combo_rect = mChannelSelector->getRect(); + S32 width = combo_rect.getWidth(); + S32 height = combo_rect.getHeight(); + S32 left = cell_rect.mLeft + cell->getTextWidth(); + combo_rect.set(left, cell_rect.mTop, left + width, cell_rect.mTop - height); + mChannelSelector->setRect(combo_rect); + + std::string value = gSelectedCell->getValue(); + if (value == " ") + { + value = "NONE"; + } + mChannelSelector->setValue(value); + mChannelSelector->setVisible(TRUE); + mChannelSelector->showList(); + } + else + { + mActionTable->deselectAllItems(); + } +} + +void LLPanelPreferenceGameControl::onCommitInputChannel() +{ + if (gSelectedCell) + { + std::string channel_name = mChannelSelector->getSelectedItemLabel(); + LLGameControl::InputChannel channel = LLGameControl::getChannelByName(channel_name); + + std::string action_name = gSelectedItem->getValue(); + bool success = LLGameControl::updateActionMap(action_name, channel); + if (success) + { + if (channel_name == "NONE") + { + gSelectedCell->setValue(" "); + // TODO?: also clear cell to the right with script-relevant name + } + else + { + gSelectedCell->setValue(channel_name); + // TODO?: also update the cell to the right with script-relevant name + } + } + gGameControlPanel->updateTable(); + clearSelectionState(); + } +} + +bool LLPanelPreferenceGameControl::isWaitingForInputChannel() +{ + return gSelectedItem != nullptr; +} + +// static +void LLPanelPreferenceGameControl::applyGameControlInput(const LLGameControl::InputChannel& channel) +{ + if (gSelectedItem && channel.mType != (U8)(LLPanelPreferenceGameControl::TYPE_NONE)) + { + S32 cell_index = gSelectedItem->getSelectedCell(); + if (cell_index > 0) + { + LLScrollListCell* cell = gSelectedItem->getColumn(cell_index); + if (cell) + { + bool success = LLGameControl::updateActionMap(gSelectedItem->getValue(), channel); + if (success) + { + cell->setValue(channel.getLocalName()); + // TODO?: also update the cell to the right with script-relevant name + gGameControlPanel->updateTable(); + } + + } + } + gGameControlPanel->clearSelectionState(); + } +} + +BOOL LLPanelPreferenceGameControl::postBuild() +{ + mCheckGameControlToServer = getChild<LLCheckBoxCtrl>("game_control_to_server"); + mCheckGameControlToServer->setCommitCallback(boost::bind(&LLPanelPreferenceGameControl::onClickGameControlToServer, this, _1)); + //mCheckGameControlToServer->setEnabled(gSavedSettings.getBOOL( "GameControlToServer")); + + mCheckGameControlToAgent = getChild<LLCheckBoxCtrl>("game_control_to_agent"); + mCheckGameControlToAgent->setCommitCallback(boost::bind(&LLPanelPreferenceGameControl::onClickGameControlToAgent, this, _1)); + //mCheckGameControlToAgent->setEnabled(gSavedSettings.getBOOL( "GameControlToAgent")); + + mCheckAgentToGameControl= getChild<LLCheckBoxCtrl>("agent_to_game_control"); + mCheckAgentToGameControl->setCommitCallback(boost::bind(&LLPanelPreferenceGameControl::onClickAgentToGameControl, this, _1)); + //mCheckAgentToGameControl->setEnabled(gSavedSettings.getBOOL( "AgentToGameControl")); + + mActionTable = getChild<LLScrollListCtrl>("action_table"); + mActionTable->setCommitCallback(boost::bind(&LLPanelPreferenceGameControl::onActionSelect, this)); + + populateActionTable(); + + // enable the table if at least one of the GameControl<-->Avatar options is enabled + mActionTable->setEnabled(mCheckGameControlToAgent->get() || mCheckAgentToGameControl->get()); + + mChannelSelector = getChild<LLComboBox>("input_channel_combo"); + mChannelSelector->setVisible(FALSE); + mChannelSelector->setCommitCallback(boost::bind(&LLPanelPreferenceGameControl::onCommitInputChannel, this)); + + return TRUE; +} + +void LLPanelPreferenceGameControl::populateActionTable() +{ + loadSettings(); + populateColumns(); + populateRows("game_control_table_rows.xml"); + addTableSeparator(); + populateRows("game_control_table_camera_rows.xml"); +} + +void LLPanelPreferenceGameControl::populateColumns() +{ + // populate columns + std::string filename = "game_control_table_columns.xml"; + LLXMLNodePtr xmlNode; + LLScrollListCtrl::Contents contents; + if (!LLUICtrlFactory::getLayeredXMLNode(filename, xmlNode)) + { + LL_WARNS("Preferences") << "Failed to populate columns from '" << filename << "'" << LL_ENDL; + return; + } + LLXUIParser parser; + parser.readXUI(xmlNode, contents, filename); + if (!contents.validateBlock()) + { + LL_WARNS("Preferences") << "Failed to parse columns from '" << filename << "'" << LL_ENDL; + return; + } + for (LLInitParam::ParamIterator<LLScrollListColumn::Params>::const_iterator col_it = contents.columns.begin(); + col_it != contents.columns.end(); + ++col_it) + { + mActionTable->addColumn(*col_it); + } +} + +void LLPanelPreferenceGameControl::populateRows(const std::string& filename) +{ + LLXMLNodePtr xmlNode; + if (!LLUICtrlFactory::getLayeredXMLNode(filename, xmlNode)) + { + LL_WARNS("Preferences") << "Failed to populate rows from '" << filename << "'" << LL_ENDL; + return; + } + LLScrollListCtrl::Contents contents; + LLXUIParser parser; + parser.readXUI(xmlNode, contents, filename); + if (!contents.validateBlock()) + { + LL_WARNS("Preferences") << "Failed to parse rows from '" << filename << "'" << LL_ENDL; + return; + } + + // init basic cell params + LLScrollListCell::Params cell_params; + cell_params.font = LLFontGL::getFontSansSerif(); + cell_params.font_halign = LLFontGL::LEFT; + cell_params.column = ""; + cell_params.value = ""; + + // we expect the mActionTable to have at least three columns + if (mActionTable->getNumColumns() < 3) + { + LL_WARNS("Preferences") << "expected at least three columns in '" << filename << "'" << LL_ENDL; + return; + } + LLScrollListColumn* local_channel_column = mActionTable->getColumn(1); + + for (LLInitParam::ParamIterator<LLScrollListItem::Params>::const_iterator row_it = contents.rows.begin(); + row_it != contents.rows.end(); + ++row_it) + { + std::string name = row_it->value.getValue().asString(); + if (!name.empty() && name != "menu_separator") + { + LLScrollListItem::Params item_params(*row_it); + item_params.enabled.setValue(true); + size_t num_columns = item_params.columns.size(); + // item_params should already have one column that was defined + // in XUI config file, and now we want to add two more + if (num_columns > 0) + { + LLGameControl::InputChannel channel = LLGameControl::getChannelByActionName(name); + + cell_params.column = local_channel_column->mName; + cell_params.value = channel.getLocalName(); + item_params.columns.add(cell_params); + + // TODO?: add a column with more human readable name + //cell_params.column = remote_channel_column->mName; + //cell_params.value = channel.getRemoteName(); + //item_params.columns.add(cell_params); + } + mActionTable->addRow(item_params, EAddPosition::ADD_BOTTOM); + } + else + { + // Separator example: + // <rows + // enabled = "false"> + // <columns + // type = "icon" + // color = "0 0 0 0.7" + // halign = "center" + // value = "menu_separator" + // column = "action" / > + //</rows> + mActionTable->addRow(*row_it, EAddPosition::ADD_BOTTOM); + } + } +} + +void LLPanelPreferenceGameControl::clearSelectionState() +{ + if (gSelectedCell) + { + mChannelSelector->setVisible(FALSE); + gSelectedCell = nullptr; + } + gSelectedItem = nullptr; +} + +void LLPanelPreferenceGameControl::addTableSeparator() +{ + LLScrollListItem::Params separator_params; + separator_params.enabled(false); + LLScrollListCell::Params column_params; + column_params.type = "icon"; + column_params.value = "menu_separator"; + column_params.column = "action"; + column_params.color = LLColor4(0.f, 0.f, 0.f, 0.7f); + column_params.font_halign = LLFontGL::HCENTER; + separator_params.columns.add(column_params); + mActionTable->addRow(separator_params, EAddPosition::ADD_BOTTOM); +} + +void LLPanelPreferenceGameControl::updateTable() +{ + mActionTable->deselectAllItems(); +} + + LLFloaterPreferenceProxy::LLFloaterPreferenceProxy(const LLSD& key) : LLFloater(key), mSocksSettingsDirty(false) diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index 40806c22fc..22421f296c 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -36,6 +36,7 @@ #include "llfloater.h" #include "llavatarpropertiesprocessor.h" #include "llconversationlog.h" +#include "llgamecontroltranslator.h" #include "llsearcheditor.h" #include "llsetkeybinddialog.h" #include "llkeyconflict.h" @@ -51,6 +52,7 @@ class LLScrollListCell; class LLSliderCtrl; class LLSD; class LLTextBox; +class LLPanelPreferenceGameControl; namespace ll { @@ -80,12 +82,12 @@ public: void apply(); void cancel(const std::vector<std::string> settings_to_skip = {}); - /*virtual*/ void draw(); - /*virtual*/ bool postBuild(); - /*virtual*/ void onOpen(const LLSD& key); - /*virtual*/ void onClose(bool app_quitting); - /*virtual*/ void changed(); - /*virtual*/ void changed(const LLUUID& session_id, U32 mask) {}; + virtual void draw() override; + virtual bool postBuild() override; + virtual void onOpen(const LLSD& key) override; + virtual void onClose(bool app_quitting) override; + virtual void changed() override; + virtual void changed(const LLUUID& session_id, U32 mask) override {}; // static data update, called from message handler static void updateUserInfo(const std::string& visibility); @@ -99,8 +101,7 @@ public: // update Show Favorites checkbox static void updateShowFavoritesCheckbox(bool val); - void processProperties( void* pData, EAvatarProcessorType type ); - void saveAvatarProperties( void ); + void processProperties( void* pData, EAvatarProcessorType type ) override; static void saveAvatarPropertiesCoro(const std::string url, bool allow_publish); void selectPrivacyPanel(); void selectChatPanel(); @@ -251,7 +252,7 @@ class LLPanelPreference : public LLPanel { public: LLPanelPreference(); - /*virtual*/ bool postBuild(); + virtual bool postBuild() override; virtual ~LLPanelPreference(); @@ -297,14 +298,16 @@ private: class LLPanelPreferenceGraphics : public LLPanelPreference { public: - bool postBuild(); - void draw(); - void cancel(const std::vector<std::string> settings_to_skip = {}); - void saveSettings(); + bool postBuild() override; + void draw() override; + void cancel(const std::vector<std::string> settings_to_skip = {}) override; + void saveSettings() override; void resetDirtyChilds(); - void setHardwareDefaults(); + void setHardwareDefaults() override; void setPresetText(); + static const std::string getPresetsPath(); + protected: bool hasDirtyChilds(); @@ -320,11 +323,11 @@ public: LLPanelPreferenceControls(); virtual ~LLPanelPreferenceControls(); - bool postBuild(); + bool postBuild() override; - void apply(); - void cancel(const std::vector<std::string> settings_to_skip = {}); - void saveSettings(); + void apply() override; + void cancel(const std::vector<std::string> settings_to_skip = {}) override; + void saveSettings() override; void resetDirtyChilds(); void onListCommit(); @@ -340,9 +343,9 @@ public: void updateAndApply(); // from interface - /*virtual*/ bool onSetKeyBind(EMouseClickType click, KEY key, MASK mask, bool all_modes); - /*virtual*/ void onDefaultKeyBind(bool all_modes); - /*virtual*/ void onCancelKeyBind(); + bool onSetKeyBind(EMouseClickType click, KEY key, MASK mask, bool all_modes) override; + void onDefaultKeyBind(bool all_modes) override; + void onCancelKeyBind() override; private: // reloads settings, discards current changes, updates table @@ -367,6 +370,57 @@ private: S32 mEditingMode; }; +class LLPanelPreferenceGameControl : public LLPanelPreference +{ +public: + + enum InputType + { + TYPE_AXIS, + TYPE_BUTTON, + TYPE_NONE + }; + + LLPanelPreferenceGameControl(); + ~LLPanelPreferenceGameControl(); + + void apply() override; + void loadDefaults(); + void loadSettings(); + void saveSettings() override; + void updateEnabledState(); + + void onClickGameControlToServer(LLUICtrl* ctrl); + // "Agent" in this context means either Avatar or Flycam + void onClickGameControlToAgent(LLUICtrl* ctrl); + void onClickAgentToGameControl(LLUICtrl* ctrl); + void onActionSelect(); + void onCommitInputChannel(); + + static bool isWaitingForInputChannel(); + static void applyGameControlInput(const LLGameControl::InputChannel& channel); +protected: + bool postBuild() override; + + void populateActionTable(); + void populateColumns(); + void populateRows(const std::string& filename); + +private: + void clearSelectionState(); + void addTableSeparator(); + void updateTable(); + LOG_CLASS(LLPanelPreferenceGameControl); + + LLCheckBoxCtrl *mCheckGameControlToServer; // send game_control data to server + LLCheckBoxCtrl *mCheckGameControlToAgent; // use game_control data to move avatar + LLCheckBoxCtrl *mCheckAgentToGameControl; // translate external avatar actions to game_control data + + LLScrollListCtrl* mActionTable; + LLComboBox* mChannelSelector; + LLGameControlTranslator mActionTranslator; +}; + class LLAvatarComplexityControls { public: @@ -391,13 +445,13 @@ public: void cancel(); protected: - bool postBuild(); - void onOpen(const LLSD& key); - void onClose(bool app_quitting); + bool postBuild() override; + void onOpen(const LLSD& key) override; + void onClose(bool app_quitting) override; void saveSettings(); void onBtnOk(); void onBtnCancel(); - void onClickCloseBtn(bool app_quitting = false); + void onClickCloseBtn(bool app_quitting = false) override; void onChangeSocksSettings(); diff --git a/indra/newview/llflycam.cpp b/indra/newview/llflycam.cpp new file mode 100644 index 0000000000..53b8388f98 --- /dev/null +++ b/indra/newview/llflycam.cpp @@ -0,0 +1,117 @@ +/** + * @file llflycam.cpp + * @brief LLFlycam class implementation + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, 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 + * $/LicenseInfo$ + */ + +#include "llflycam.h" + +LLFlycam::LLFlycam() +{ +} + + +void LLFlycam::setTransform(const LLVector3& position, const LLQuaternion& rotation) +{ + mPosition = position; + mRotation = rotation; + mRotation.normalize(); +} + +void LLFlycam::getTransform(LLVector3& position_out, LLQuaternion& rotation_out) +{ + position_out = mPosition; + rotation_out = mRotation; +} + + +void LLFlycam::setLinearVelocity(const LLVector3& velocity) +{ + // TODO: cap input + mLinearVelocity = velocity; +} + + +void LLFlycam::setPitchRate(F32 pitch_rate) +{ + // TODO: cap input + mPitchRate = pitch_rate; +} + + +void LLFlycam::setYawRate(F32 yaw_rate) +{ + // TODO: cap input + mYawRate = yaw_rate; +} + + +void LLFlycam::integrate(F32 delta_time) +{ + // cap delta_time to slow camera motion when framerates are low + constexpr F32 MAX_DELTA_TIME = 0.2f; + if (delta_time > MAX_DELTA_TIME) + { + delta_time = MAX_DELTA_TIME; + } + + if (mLinearVelocity.lengthSquared() > 0.0f) + { + mPosition += delta_time * mLinearVelocity; + } + + F32 angle = mPitchRate * delta_time; + bool needs_renormalization = false; + if (fabsf(angle) > 0.0f) + { + LLQuaternion dQ; + dQ.setAngleAxis(angle, 0.0f, 1.0f, 0.0f); + mRotation = dQ * mRotation; + needs_renormalization = true; + } + + angle = mYawRate * delta_time; + if (fabsf(angle) > 0.0f) + { + LLQuaternion dQ; + dQ.setAngleAxis(angle, 0.0f, 0.0f, 1.0f); + mRotation = dQ * mRotation; + needs_renormalization = true; + } + + if (needs_renormalization) + { + mRotation.normalize(); + } + + + /* + // from llviewerjoystick.cpp + LLMatrix3 mat(sFlycamRotation); + LLViewerCamera::getInstance()->setView(sFlycamZoom); + LLViewerCamera::getInstance()->setOrigin(sFlycamPosition); + LLViewerCamera::getInstance()->mXAxis = LLVector3(mat.mMatrix[0]); + LLViewerCamera::getInstance()->mYAxis = LLVector3(mat.mMatrix[1]); + LLViewerCamera::getInstance()->mZAxis = LLVector3(mat.mMatrix[2]); + */ +} diff --git a/indra/newview/llflycam.h b/indra/newview/llflycam.h new file mode 100644 index 0000000000..1125767a19 --- /dev/null +++ b/indra/newview/llflycam.h @@ -0,0 +1,55 @@ +/** + * @file llflycam.h + * @brief LLFlycam class header file + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, 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 + * $/LicenseInfo$ + */ + +#pragma once + +#include "llcoordframe.h" +#include "v3math.h" +#include "llquaternion.h" + +class LLFlycam +{ +public: + + LLFlycam(); + + void setTransform(const LLVector3& position, const LLQuaternion& rotation); + void getTransform(LLVector3& position_out, LLQuaternion& rotation_out); + + void setLinearVelocity(const LLVector3& velocity); + void setPitchRate(F32 pitch_rate); + void setYawRate(F32 yaw_rate); + + void integrate(F32 delta_time); + + +protected: + LLVector3 mPosition; + LLVector3 mLinearVelocity; + LLQuaternion mRotation; + F32 mPitchRate { 0.0f }; + F32 mYawRate { 0.0f }; +}; diff --git a/indra/newview/llviewerinput.cpp b/indra/newview/llviewerinput.cpp index ea3088613f..4968df943f 100644 --- a/indra/newview/llviewerinput.cpp +++ b/indra/newview/llviewerinput.cpp @@ -35,6 +35,7 @@ #include "llagentcamera.h" #include "llfloaterimnearbychat.h" #include "llfocusmgr.h" +#include "llgamecontrol.h" #include "llkeybind.h" // LLKeyData #include "llmorphview.h" #include "llmoveview.h" @@ -157,9 +158,6 @@ static void agent_handle_doubletap_run(EKeystate s, LLAgent::EDoubleTapRunMode m static void agent_push_forwardbackward( EKeystate s, S32 direction, LLAgent::EDoubleTapRunMode mode ) { - agent_handle_doubletap_run(s, mode); - if (KEYSTATE_UP == s) return; - F32 time = gKeyboard->getCurKeyElapsedTime(); S32 frame_count = ll_round(gKeyboard->getCurKeyElapsedFrameCount()); @@ -668,6 +666,7 @@ bool start_gesture( EKeystate s ) bool run_forward(EKeystate s) { + // HACK: we use AGENT_CONTROL_NUDGE_AT_POS to signify "run forward" if (KEYSTATE_UP != s) { if (gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_FORWARD) @@ -693,6 +692,7 @@ bool run_forward(EKeystate s) bool run_backward(EKeystate s) { + // HACK: we use AGENT_CONTROL_NUDGE_AT_NEG to signify "run backward" if (KEYSTATE_UP != s) { if (gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_BACKWARD) @@ -718,6 +718,7 @@ bool run_backward(EKeystate s) bool run_left(EKeystate s) { + // HACK: we use AGENT_CONTROL_NUDGE_LEFT_POS to signify "run left" if (KEYSTATE_UP != s) { if (gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_SLIDELEFT) @@ -768,6 +769,7 @@ bool run_right(EKeystate s) bool toggle_run(EKeystate s) { + // HACK: we use AGENT_CONTROL_FAST_AT to signify "run button" if (KEYSTATE_DOWN != s) return true; bool run = gAgent.getAlwaysRun(); if (run) @@ -786,6 +788,7 @@ bool toggle_run(EKeystate s) bool toggle_sit(EKeystate s) { + // HACK: we use AGENT_CONTROL_SIT_ON_GROUND to signify "sit button" if (KEYSTATE_DOWN != s) return true; if (gAgent.isSitting()) { diff --git a/indra/newview/llviewerjoystick.cpp b/indra/newview/llviewerjoystick.cpp index 7543fb3743..42238da418 100644 --- a/indra/newview/llviewerjoystick.cpp +++ b/indra/newview/llviewerjoystick.cpp @@ -148,18 +148,12 @@ BOOL CALLBACK di8_devices_callback(LPCDIDEVICEINSTANCE device_instance_ptr, LPVO LLSD guid = LLViewerJoystick::getInstance()->getDeviceUUID(); - bool init_device = false; - if (guid.isBinary()) + bool init_device = LLViewerJoystick::is3DConnexionDevice(product_name); + if (init_device && 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; - } if (init_device) { @@ -1513,14 +1507,23 @@ std::string LLViewerJoystick::getDescription() return res; } +// static +bool LLViewerJoystick::is3DConnexionDevice(const std::string& device_name) +{ + bool answer = device_name.find("Space") == 0 + && ( (device_name.find("SpaceNavigator") == 0) + || (device_name.find("SpaceExplorer") == 0) + || (device_name.find("SpaceTraveler") == 0) + || (device_name.find("SpacePilot") == 0) + || (device_name.find("SpaceMouse") == 0)); + return answer; +} + bool LLViewerJoystick::isLikeSpaceNavigator() const { #if LIB_NDOF return (isJoystickInitialized() - && (strncmp(mNdofDev->product, "SpaceNavigator", 14) == 0 - || strncmp(mNdofDev->product, "SpaceExplorer", 13) == 0 - || strncmp(mNdofDev->product, "SpaceTraveler", 13) == 0 - || strncmp(mNdofDev->product, "SpacePilot", 10) == 0)); + && is3DConnexionDevice(mNdofDev->product)); #else return false; #endif diff --git a/indra/newview/llviewerjoystick.h b/indra/newview/llviewerjoystick.h index c989615653..b4fe3877f4 100644 --- a/indra/newview/llviewerjoystick.h +++ b/indra/newview/llviewerjoystick.h @@ -81,6 +81,8 @@ public: std::string getDescription(); void saveDeviceIdToSettings(); + static bool is3DConnexionDevice(const std::string& device_name); + protected: void updateEnabled(bool autoenable); void handleRun(F32 inc); diff --git a/indra/newview/skins/default/textures/bottomtray/Dpad.png b/indra/newview/skins/default/textures/bottomtray/Dpad.png Binary files differnew file mode 100644 index 0000000000..00fcb4beea --- /dev/null +++ b/indra/newview/skins/default/textures/bottomtray/Dpad.png diff --git a/indra/newview/skins/default/xui/en/floater_joystick.xml b/indra/newview/skins/default/xui/en/floater_joystick.xml index e6f0420698..597744238c 100644 --- a/indra/newview/skins/default/xui/en/floater_joystick.xml +++ b/indra/newview/skins/default/xui/en/floater_joystick.xml @@ -3,9 +3,9 @@ legacy_header_height="18" height="500" layout="topleft" - name="Joystick" + name="3Dconnexion Device" help_topic="Viewerhelp:Joystick_Configuration" - title="JOYSTICK CONFIGURATION" + title="3DCONNEXION DEVICE CONFIGURATION" width="569"> <floater.string name="JoystickDisabled"> @@ -22,7 +22,7 @@ width="50" mouse_opaque="false" name="joystick_lbl"> - Joystick: + Device: </text> <combo_box allow_text_entry="false" @@ -35,7 +35,7 @@ <spinner bottom="56" height="10" - control_name="JoystickAxis1" + control_name="Axis1" decimal_digits="0" increment="1" label="X Axis Mapping" @@ -44,12 +44,12 @@ left="20" max_val="5" min_val="-1" - name="JoystickAxis1" + name="Axis1" width="140" /> <spinner bottom_delta="0" height="10" - control_name="JoystickAxis2" + control_name="Axis2" decimal_digits="0" increment="1" label="Y Axis Mapping" @@ -58,12 +58,12 @@ left="190" max_val="5" min_val="-1" - name="JoystickAxis2" + name="Axis2" width="140" /> <spinner bottom_delta="0" height="10" - control_name="JoystickAxis0" + control_name="Axis0" decimal_digits="0" increment="1" label="Z Axis Mapping" @@ -72,12 +72,12 @@ left="360" max_val="5" min_val="-1" - name="JoystickAxis0" + name="Axis0" width="140" /> <spinner bottom="76" height="10" - control_name="JoystickAxis4" + control_name="Axis4" decimal_digits="0" increment="1" label="Pitch Mapping" @@ -86,12 +86,12 @@ left="20" max_val="5" min_val="-1" - name="JoystickAxis4" + name="Axis4" width="140" /> <spinner bottom_delta="0" height="10" - control_name="JoystickAxis5" + control_name="Axis5" decimal_digits="0" increment="1" label="Yaw Mapping" @@ -100,12 +100,12 @@ left="190" max_val="5" min_val="-1" - name="JoystickAxis5" + name="Axis5" width="140" /> <spinner bottom_delta="0" height="10" - control_name="JoystickAxis3" + control_name="Axis3" decimal_digits="0" increment="1" label="Roll Mapping" @@ -114,12 +114,12 @@ left="360" max_val="5" min_val="-1" - name="JoystickAxis3" + name="Axis3" width="140" /> <spinner bottom="96" height="10" - control_name="JoystickAxis6" + control_name="Axis6" decimal_digits="0" increment="1" label="Zoom Mapping" @@ -128,7 +128,7 @@ left="20" max_val="5" min_val="-1" - name="JoystickAxis6" + name="Axis6" width="140" /> <check_box bottom_delta="18" @@ -203,7 +203,7 @@ width="60" /> <stat_view height="270" - label="Joystick Monitor" + label="Axis Monitor" layout="topleft" left="359" name="axis_view" diff --git a/indra/newview/skins/default/xui/en/floater_preferences.xml b/indra/newview/skins/default/xui/en/floater_preferences.xml index 4614f2f06c..e0dc538af4 100644 --- a/indra/newview/skins/default/xui/en/floater_preferences.xml +++ b/indra/newview/skins/default/xui/en/floater_preferences.xml @@ -171,12 +171,12 @@ help_topic="preferences_controls_tab" name="controls" /> <panel - class="panel_preference_game_controls" - filename="panel_preferences_game_controls.xml" - label="Game Controls" + class="panel_preference_game_control" + filename="panel_preferences_game_control.xml" + label="Game Control" layout="topleft" - help_topic="preferences_game_controls_tab" - name="gamecontrols" /> + help_topic="preferences_game_control_tab" + name="game_control" /> </tab_container> </floater> diff --git a/indra/newview/skins/default/xui/en/game_control_table_camera_rows.xml b/indra/newview/skins/default/xui/en/game_control_table_camera_rows.xml new file mode 100644 index 0000000000..b2381c8493 --- /dev/null +++ b/indra/newview/skins/default/xui/en/game_control_table_camera_rows.xml @@ -0,0 +1,106 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<contents> + <rows + enabled="false" + name="camera_actions" + value=""> + <columns + type="icontext" + column="action" + font="SansSerif" + halign="left" + label="Camera" + name="action" + value="Cam_FreeCam_Off" /> + </rows> + <rows + name="move" + value="move"> + <columns + column="action" + font="SansSerif" + halign="left" + name="action" + tool_tip="Camera move forward/backward" + value="Move Forward" /> + </rows> + <rows + name="pan" + value="pan"> + <columns + column="action" + font="SansSerif" + halign="left" + name="action" + tool_tip="Camera pan left/right" + value="Pan Left" /> + </rows> + <rows + name="rise" + value="rise"> + <columns + column="action" + font="SansSerif" + halign="left" + name="action" + tool_tip="Camera rise/fall" + value="Rise Up" /> + </rows> + <rows + name="pitch" + value="pitch"> + <columns + column="action" + font="SansSerif" + halign="left" + name="action" + tool_tip="Camera adjust pitch" + value="Pitch Down" /> + </rows> + <rows + name="yaw" + value="yaw"> + <columns + column="action" + font="SansSerif" + halign="left" + name="action" + tool_tip="Camera turn left/right" + value="Yaw CCW" /> + </rows> + <rows + name="zoom" + value="zoom"> + <columns + column="action" + font="SansSerif" + halign="left" + name="action" + tool_tip="Camera zoom in/out" + value="Zoom In" /> + </rows> + <!-- + <rows + name="roll_ccw" + value="roll_ccw"> + <columns + column="action" + font="SansSerif" + halign="left" + name="action" + tool_tip="Camera roll counterclockwise" + value="Roll Counterclockwise" /> + </rows> + <rows + name="roll_cw" + value="roll_cw"> + <columns + column="action" + font="SansSerif" + halign="left" + name="action" + tool_tip="Camera roll clockwise" + value="Roll Clockwise" /> + </rows> + --> +</contents> diff --git a/indra/newview/skins/default/xui/en/game_control_table_columns.xml b/indra/newview/skins/default/xui/en/game_control_table_columns.xml new file mode 100644 index 0000000000..f88fc8305c --- /dev/null +++ b/indra/newview/skins/default/xui/en/game_control_table_columns.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<contents> + <columns + relative_width="0.25" + label="Action" + name="action" /> + <columns + relative_width="0.25" + label="GameControl" + name="index" /> + <columns + relative_width="0.50" + label=" " + name="foo" /> +</contents> diff --git a/indra/newview/skins/default/xui/en/game_control_table_rows.xml b/indra/newview/skins/default/xui/en/game_control_table_rows.xml new file mode 100644 index 0000000000..90e6990842 --- /dev/null +++ b/indra/newview/skins/default/xui/en/game_control_table_rows.xml @@ -0,0 +1,117 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<contents> + <rows + enabled="false" + name="move_actions" + value=""> + <columns + type="icontext" + column="action" + font="SansSerif" + halign="left" + label="Move Actions" + name="action" + value="Move_Walk_Off" /> + </rows> + <rows + name="push" + value="push"> + <columns + column="action" + font="SansSerif" + halign="left" + name="action" + value="Walk Forward" /> + </rows> + <rows + name="slide" + value="slide"> + <columns + column="action" + font="SansSerif" + halign="left" + name="action" + value="Strafe Left" /> + </rows> + <rows + name="jump" + value="jump"> + <columns + column="action" + font="SansSerif" + halign="left" + name="action" + value="Jump Up" /> + </rows> + <rows + name="turn" + value="turn"> + <columns + column="action" + font="SansSerif" + halign="left" + name="action" + value="Turn Left" /> + </rows> + <rows + name="look" + value="look"> + <columns + column="action" + font="SansSerif" + halign="left" + name="action" + value="Look Down" /> + </rows> + + <rows + name="stop" + value="stop"> + <columns + column="action" + font="SansSerif" + halign="left" + name="action" + value="Stop" /> + </rows> + <rows + name="toggle_run" + value="toggle_run"> + <columns + column="action" + font="SansSerif" + halign="left" + name="action" + value="Run" /> + </rows> + <rows + name="toggle_fly" + value="toggle_fly"> + <columns + column="action" + font="SansSerif" + halign="left" + name="action" + value="Fly" /> + </rows> + <rows + name="toggle_sit" + value="toggle_sit"> + <columns + column="action" + font="SansSerif" + halign="left" + name="action" + value="Sit" /> + </rows> + <rows + name="toggle_flycam" + value="toggle_flycam"> + <columns + column="action" + font="SansSerif" + halign="left" + name="action" + value="Flycam" /> + </rows> +</contents> diff --git a/indra/newview/skins/default/xui/en/panel_preferences_game_control.xml b/indra/newview/skins/default/xui/en/panel_preferences_game_control.xml new file mode 100644 index 0000000000..d48c0fe0d2 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_preferences_game_control.xml @@ -0,0 +1,244 @@ +<?xml version="1.0" encoding="utf-8"?> +<panel + border="true" + follows="all" + height="408" + label="Game Controls" + layout="topleft" + left="102" + name="gamecontrol" + top="1" + width="517"> + <check_box + control_name="GameControlToServer" + follows="top|left" + height="15" + label="Send GameControl Data to server" + layout="topleft" + left="10" + name="game_control_to_server" + top="6" + width="232"/> + <check_box + control_name="GameControlToAgent" + follows="top|left" + height="15" + label="GameControl moves avatar and flycam" + layout="topleft" + left="10" + name="game_control_to_agent" + top="27" + width="232"/> + <check_box + control_name="AgentToGameControl" + follows="top|left" + height="15" + label="Avatar and flycam actions interpreted as GameControl" + layout="topleft" + left="10" + name="agent_to_game_control" + top="48" + width="232"/> + <scroll_list + draw_heading="true" + follows="all" + layout="topleft" + column_padding="0" + selection_type="header" + top="66" + left="3" + bottom="-3" + right="-3" + can_sort="false" + multi_select="false" + name="action_table" + fg_disable_color="ScrollUnselectedColor"/> + <combo_box + height="23" + layout="topleft" + left="10" + name="input_channel_combo" + top_pad="5" + width="90"> + <combo_box.item + label="AXIS_0-" + name="AXIS_0-" + value="AXIS_0-"/> + <combo_box.item + label="AXIS_0+" + name="AXIS_0+" + value="AXIS_0+"/> + <combo_box.item + label="AXIS_1-" + name="AXIS_1-" + value="AXIS_1-"/> + <combo_box.item + label="AXIS_1+" + name="AXIS_1+" + value="AXIS_1+"/> + <combo_box.item + label="AXIS_2-" + name="AXIS_2-" + value="AXIS_2-"/> + <combo_box.item + label="AXIS_2+" + name="AXIS_2+" + value="AXIS_2+"/> + <combo_box.item + label="AXIS_3-" + name="AXIS_3-" + value="AXIS_3-"/> + <combo_box.item + label="AXIS_3+" + name="AXIS_3+" + value="AXIS_3+"/> + <combo_box.item + label="AXIS_4-" + name="AXIS_4-" + value="AXIS_4-"/> + <combo_box.item + label="AXIS_4+" + name="AXIS_4+" + value="AXIS_4+"/> + <combo_box.item + label="AXIS_5-" + name="AXIS_5-" + value="AXIS_5-"/> + <combo_box.item + label="AXIS_5+" + name="AXIS_5+" + value="AXIS_5+"/> + <combo_box.item + label="BUTTON_0" + name="BUTTON_0" + value="BUTTON_0"/> + <combo_box.item + label="BUTTON_1" + name="BUTTON_1" + value="BUTTON_1"/> + <combo_box.item + label="BUTTON_2" + name="BUTTON_2" + value="BUTTON_2"/> + <combo_box.item + label="BUTTON_3" + name="BUTTON_3" + value="BUTTON_3"/> + <combo_box.item + label="BUTTON_4" + name="BUTTON_4" + value="BUTTON_4"/> + <combo_box.item + label="BUTTON_5" + name="BUTTON_5" + value="BUTTON_5"/> + <combo_box.item + label="BUTTON_6" + name="BUTTON_6" + value="BUTTON_6"/> + <combo_box.item + label="BUTTON_7" + name="BUTTON_7" + value="BUTTON_7"/> + <combo_box.item + label="BUTTON_8" + name="BUTTON_8" + value="BUTTON_8"/> + <combo_box.item + label="BUTTON_9" + name="BUTTON_9" + value="BUTTON_9"/> + <combo_box.item + label="BUTTON_10" + name="BUTTON_10" + value="BUTTON_10"/> + <combo_box.item + label="BUTTON_11" + name="BUTTON_11" + value="BUTTON_11"/> + <combo_box.item + label="BUTTON_12" + name="BUTTON_12" + value="BUTTON_12"/> + <combo_box.item + label="BUTTON_13" + name="BUTTON_13" + value="BUTTON_13"/> + <combo_box.item + label="BUTTON_14" + name="BUTTON_14" + value="BUTTON_14"/> + <combo_box.item + label="BUTTON_15" + name="BUTTON_15" + value="BUTTON_15"/> + <combo_box.item + label="BUTTON_16" + name="BUTTON_16" + value="BUTTON_16"/> + <combo_box.item + label="BUTTON_17" + name="BUTTON_17" + value="BUTTON_17"/> + <combo_box.item + label="BUTTON_18" + name="BUTTON_18" + value="BUTTON_18"/> + <combo_box.item + label="BUTTON_19" + name="BUTTON_19" + value="BUTTON_19"/> + <combo_box.item + label="BUTTON_20" + name="BUTTON_20" + value="BUTTON_20"/> + <combo_box.item + label="BUTTON_21" + name="BUTTON_21" + value="BUTTON_21"/> + <combo_box.item + label="BUTTON_22" + name="BUTTON_22" + value="BUTTON_22"/> + <combo_box.item + label="BUTTON_23" + name="BUTTON_23" + value="BUTTON_23"/> + <combo_box.item + label="BUTTON_24" + name="BUTTON_24" + value="BUTTON_24"/> + <combo_box.item + label="BUTTON_25" + name="BUTTON_25" + value="BUTTON_25"/> + <combo_box.item + label="BUTTON_26" + name="BUTTON_26" + value="BUTTON_26"/> + <combo_box.item + label="BUTTON_27" + name="BUTTON_27" + value="BUTTON_27"/> + <combo_box.item + label="BUTTON_28" + name="BUTTON_28" + value="BUTTON_28"/> + <combo_box.item + label="BUTTON_29" + name="BUTTON_29" + value="BUTTON_29"/> + <combo_box.item + label="BUTTON_30" + name="BUTTON_30" + value="BUTTON_30"/> + <combo_box.item + label="BUTTON_31" + name="BUTTON_31" + value="BUTTON_31"/> + <combo_box.item + label="NONE" + name="NONE" + value="NONE"/> + </combo_box> +</panel> diff --git a/indra/newview/skins/default/xui/en/panel_preferences_game_controls.xml b/indra/newview/skins/default/xui/en/panel_preferences_game_controls.xml deleted file mode 100644 index 4b693e8955..0000000000 --- a/indra/newview/skins/default/xui/en/panel_preferences_game_controls.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<panel - border="true" - follows="all" - height="408" - label="Game Controls" - layout="topleft" - left="102" - name="gamecontrols" - top="1" - width="517"> - <check_box - control_name="EnableGameControlInput" - follows="top|left" - height="15" - label="Send GameControl Input to server" - layout="topleft" - left="10" - name="enable_game_control_input" - top="6" - width="232"/> - <check_box - control_name="EnableGameControlKeyboardInput" - follows="top|left" - height="15" - label="Include otherwise 'unhandled' Keyboard events in GameControl Input" - layout="topleft" - left="10" - name="game_control_keyboard_input" - top="27" - width="232"/> -</panel> diff --git a/indra/newview/skins/default/xui/en/panel_preferences_move.xml b/indra/newview/skins/default/xui/en/panel_preferences_move.xml index 0412466b4f..8a372256dc 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_move.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_move.xml @@ -270,7 +270,7 @@ width="200" /> <button height="23" - label="Other Devices" + label="3Dconnexion devices" left="30" name="joystick_setup_button" top="30" diff --git a/indra/newview/tests/llgamecontrol_stub.cpp b/indra/newview/tests/llgamecontrol_stub.cpp index 0872f647d7..b06a7fd132 100644 --- a/indra/newview/tests/llgamecontrol_stub.cpp +++ b/indra/newview/tests/llgamecontrol_stub.cpp @@ -24,53 +24,76 @@ * $/LicenseInfo$ */ +#pragma once + #include "llgamecontrol.h" #include "SDL2/SDL_events.h" +LLGameControl::State g_state; -void LLGameControl::addKeyButtonMap(U16 key, U8 button) +// static +bool LLGameControl::isInitialized() { + return false; } -void LLGameControl::removeKeyButtonMap(U16 key) +// static +void LLGameControl::init() { } -void LLGameControl::addKeyAxisMap(U16 key, U8 axis, bool positive) +// static +void LLGameControl::terminate() { } -void LLGameControl::removeKeyAxisMap(U16 key) +// static +bool LLGameControl::computeFinalStateAndCheckForChanges() { + return false; } -void LLGameControl::onKeyDown(U16 key, U32 mask) +// static +void LLGameControl::clearAllState() { } -void LLGameControl::onKeyUp(U16 key, U32 mask) +// static +void LLGameControl::processEvents(bool app_has_focus) { } // static -bool LLGameControl::isInitialized() +const LLGameControl::State& LLGameControl::getState() +{ + return g_state; +} + +// static +void LLGameControl::setIncludeKeyboard(bool include) +{ +} + +// static +bool LLGameControl::getIncludeKeyboard() { return false; } // static -void LLGameControl::init() +LLGameControl::InputChannel LLGameControl::getChannelByActionName(const std::string& name) { + return LLGameControl::InputChannel(); } // static -void LLGameControl::terminate() +void LLGameControl::addActionMapping(const std::string& name, LLGameControl::InputChannel channel) { } // static -void LLGameControl::processEvents(bool app_has_focus) +void LLGameControl::setActionFlags(U32 action_flags) { } diff --git a/indra/newview/tests/llversioninfo_test.cpp b/indra/newview/tests/llversioninfo_test.cpp index 9eb5146f2b..b07800cdcd 100644 --- a/indra/newview/tests/llversioninfo_test.cpp +++ b/indra/newview/tests/llversioninfo_test.cpp @@ -31,7 +31,7 @@ #include <iostream> -#include "llgamecontrol_stub.cpp" +//#include "llgamecontrol_stub.cpp" // LL_VIEWER_CHANNEL is a macro defined on the compiler command line. The |