diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index 11c80a4dd2..a91151f4e5 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -3,9 +3,9 @@ "channels": [ { "from": "Keyboard.A", "when": ["Keyboard.RightMouseButton", "!Keyboard.Control"], "to": "Actions.LATERAL_LEFT" }, { "from": "Keyboard.D", "when": ["Keyboard.RightMouseButton", "!Keyboard.Control"], "to": "Actions.LATERAL_RIGHT" }, - { "from": "Keyboard.E", "when": ["!Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LATERAL_RIGHT" }, - { "from": "Keyboard.Q", "when": ["!Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LATERAL_LEFT" }, + { "from": "Keyboard.Q", "when": ["!Application.CameraSelfie", "!Keyboard.Control", "!Application.CaptureMouse"], "to": "Actions.LATERAL_LEFT" }, { "from": "Keyboard.Q", "when": ["Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LATERAL_RIGHT" }, + { "from": "Keyboard.E", "when": ["!Application.CameraSelfie", "!Keyboard.Control", "!Application.CaptureMouse"], "to": "Actions.LATERAL_RIGHT" }, { "from": "Keyboard.E", "when": ["Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LATERAL_LEFT" }, { "from": "Keyboard.T", "when": "!Keyboard.Control", "to": "Actions.TogglePushToTalk" }, @@ -72,46 +72,20 @@ { "from": { "makeAxis" : [ ["Keyboard.Left"], ["Keyboard.Right"] - ] + ] }, - "when": ["Application.CameraFirstPerson", "!Keyboard.Shift"], + "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "!Application.CaptureMouse", "!Keyboard.Shift"], "to": "Actions.Yaw" }, - { "from": { "makeAxis" : [ - ["Keyboard.Left"], - ["Keyboard.Right"] - ] - }, - "when": ["Application.CameraFirstPersonLookat", "!Keyboard.Shift"], - "to": "Actions.Yaw" + { "from": "Keyboard.Left", + "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "Application.CaptureMouse", "!Keyboard.Shift"], + "to": "Actions.LATERAL_LEFT" }, - { "from": { "makeAxis" : [ - ["Keyboard.Left"], - ["Keyboard.Right"] - ] - }, - "when": ["Application.CameraThirdPerson", "!Keyboard.Shift"], - "to": "Actions.Yaw" - }, - - { "from": { "makeAxis" : [ - ["Keyboard.Left"], - ["Keyboard.Right"] - ] - }, - "when": ["Application.CameraLookAt", "!Keyboard.Shift"], - "to": "Actions.Yaw" - }, - - { "from": { "makeAxis" : [ - ["Keyboard.Left"], - ["Keyboard.Right"] - ] - }, - "when": ["Application.CameraSelfie", "!Keyboard.Shift"], - "to": "Actions.Yaw" + { "from": "Keyboard.Right", + "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "Application.CaptureMouse", "!Keyboard.Shift"], + "to": "Actions.LATERAL_RIGHT" }, { "from": { "makeAxis" : [ @@ -119,53 +93,18 @@ ["Keyboard.D"] ] }, - "when": ["Application.CameraFirstPerson", "!Keyboard.Control"], + "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "!Application.CaptureMouse", "!Keyboard.Control"], "to": "Actions.Yaw" }, - { "from": { "makeAxis" : [ - ["Keyboard.A"], - ["Keyboard.D"] - ] - }, - "when": ["Application.CameraFirstPersonLookat", "!Keyboard.Control"], - "to": "Actions.Yaw" + { "from": "Keyboard.A", + "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "Application.CaptureMouse", "!Keyboard.Control"], + "to": "Actions.LATERAL_LEFT" }, - { "from": { "makeAxis" : [ - ["Keyboard.A"], - ["Keyboard.D"] - ] - }, - "when": ["Application.CameraThirdPerson", "!Keyboard.Control"], - "to": "Actions.Yaw" - }, - - { "from": { "makeAxis" : [ - ["Keyboard.A"], - ["Keyboard.D"] - ] - }, - "when": ["Application.CameraLookAt", "!Keyboard.Control"], - "to": "Actions.Yaw" - }, - - { "from": { "makeAxis" : [ - ["Keyboard.A"], - ["Keyboard.D"] - ] - }, - "when": ["Application.CameraSelfie", "!Keyboard.Control"], - "to": "Actions.Yaw" - }, - - { "from": { "makeAxis" : [ - ["Keyboard.TouchpadLeft"], - ["Keyboard.TouchpadRight"] - ] - }, - "when": "Application.CameraFirstPerson", - "to": "Actions.Yaw" + { "from": "Keyboard.D", + "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity", "Application.CaptureMouse", "!Keyboard.Control"], + "to": "Actions.LATERAL_RIGHT" }, { "from": { "makeAxis" : [ @@ -173,39 +112,12 @@ ["Keyboard.TouchpadRight"] ] }, - "when": "Application.CameraFirstPersonLookat", - "to": "Actions.Yaw" - }, - - { "from": { "makeAxis" : [ - ["Keyboard.TouchpadLeft"], - ["Keyboard.TouchpadRight"] - ] - }, - "when": "Application.CameraThirdPerson", - "to": "Actions.Yaw" - }, - - { "from": { "makeAxis" : [ - ["Keyboard.TouchpadLeft"], - ["Keyboard.TouchpadRight"] - ] - }, - "when": "Application.CameraLookAt", - "to": "Actions.Yaw" - }, - - { "from": { "makeAxis" : [ - ["Keyboard.TouchpadLeft"], - ["Keyboard.TouchpadRight"] - ] - }, - "when": "Application.CameraSelfie", + "when": ["!Application.CameraFSM", "!Application.CameraIndependent", "!Application.CameraEntity"], "to": "Actions.Yaw" }, { "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] }, - "when": "Keyboard.RightMouseButton", + "when": ["Keyboard.RightMouseButton", "!Application.CaptureMouse"], "to": "Actions.DeltaYaw", "filters": [ @@ -213,8 +125,17 @@ ] }, + { "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] }, + "to": "Actions.DeltaYaw", + "when": "Application.CaptureMouse", + "filters": + [ + { "type": "scale", "scale": 0.2 } + ] + }, + { "from": { "makeAxis" : ["Keyboard.MouseMoveUp", "Keyboard.MouseMoveDown"] }, - "when": ["!Application.CameraSelfie", "!Application.CameraLookAt", "Keyboard.RightMouseButton"], + "when": ["!Application.CameraSelfie", "!Application.CameraLookAt", "!Application.CaptureMouse", "Keyboard.RightMouseButton"], "to": "Actions.DeltaPitch", "filters": [ @@ -222,6 +143,15 @@ ] }, + { "from": { "makeAxis" : ["Keyboard.MouseMoveUp", "Keyboard.MouseMoveDown"] }, + "to": "Actions.DeltaPitch", + "when": "Application.CaptureMouse", + "filters": + [ + { "type": "scale", "scale": 0.2 } + ] + }, + { "from": { "makeAxis" : ["Keyboard.MouseMoveUp", "Keyboard.MouseMoveDown"] }, "when": ["Application.CameraLookAt", "Keyboard.RightMouseButton"], "to": "Actions.DeltaPitch", diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4e6ed35d2c..b76e3f8dca 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -681,6 +681,9 @@ private: * CameraIndependentnumbernumberThe camera is in independent mode. * CameraEntitynumbernumberThe camera is in entity mode. * InHMDnumbernumberThe user is in HMD mode. + * CaptureMousenumbernumberThe mouse is captured. In this mode, + * the mouse is invisible and cannot leave the bounds of Interface, as long as Interface is the active window and + * no menu item is selected. * AdvancedMovementnumbernumberAdvanced movement (walking) controls are * enabled. * StrafeEnablednumbernumberStrafing is enabled @@ -716,6 +719,7 @@ static const QString STATE_PLATFORM_ANDROID = "PlatformAndroid"; static const QString STATE_LEFT_HAND_DOMINANT = "LeftHandDominant"; static const QString STATE_RIGHT_HAND_DOMINANT = "RightHandDominant"; static const QString STATE_STRAFE_ENABLED = "StrafeEnabled"; +static const QString STATE_CAPTURE_MOUSE = "CaptureMouse"; // Statically provided display and input plugins extern DisplayPluginList getDisplayPlugins(); @@ -919,7 +923,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::set(); controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_CAMERA_FULL_SCREEN_MIRROR, STATE_CAMERA_FIRST_PERSON, STATE_CAMERA_FIRST_PERSON_LOOK_AT, STATE_CAMERA_THIRD_PERSON, - STATE_CAMERA_ENTITY, STATE_CAMERA_INDEPENDENT, STATE_CAMERA_LOOK_AT, STATE_CAMERA_SELFIE, + STATE_CAMERA_ENTITY, STATE_CAMERA_INDEPENDENT, STATE_CAMERA_LOOK_AT, STATE_CAMERA_SELFIE, STATE_CAPTURE_MOUSE, STATE_SNAP_TURN, STATE_ADVANCED_MOVEMENT_CONTROLS, STATE_GROUNDED, STATE_NAV_FOCUSED, STATE_PLATFORM_WINDOWS, STATE_PLATFORM_MAC, STATE_PLATFORM_ANDROID, STATE_LEFT_HAND_DOMINANT, STATE_RIGHT_HAND_DOMINANT, STATE_STRAFE_ENABLED } }); DependencyManager::set(); @@ -1892,6 +1896,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _applicationStateDevice->setInputVariant(STATE_CAMERA_INDEPENDENT, []() -> float { return qApp->getCamera().getMode() == CAMERA_MODE_INDEPENDENT ? 1 : 0; }); + _applicationStateDevice->setInputVariant(STATE_CAPTURE_MOUSE, []() -> float { + return qApp->getCamera().getCaptureMouse() ? 1 : 0; + }); _applicationStateDevice->setInputVariant(STATE_SNAP_TURN, []() -> float { return qApp->getMyAvatar()->getSnapTurn() ? 1 : 0; }); @@ -2407,6 +2414,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo updateSystemTabletMode(); connect(&_myCamera, &Camera::modeUpdated, this, &Application::cameraModeChanged); + connect(&_myCamera, &Camera::captureMouseChanged, this, &Application::captureMouseChanged); DependencyManager::get()->setShouldPickHUDOperator([]() { return DependencyManager::get()->isHMDMode(); }); DependencyManager::get()->setCalculatePos2DFromHUDOperator([this](const glm::vec3& intersection) { @@ -4704,6 +4712,11 @@ void Application::maybeToggleMenuVisible(QMouseEvent* event) const { void Application::mouseMoveEvent(QMouseEvent* event) { PROFILE_RANGE(app_input_mouse, __FUNCTION__); + if (_ignoreMouseMove) { + _ignoreMouseMove = false; + return; + } + maybeToggleMenuVisible(event); auto& compositor = getApplicationCompositor(); @@ -4749,7 +4762,7 @@ void Application::mouseMoveEvent(QMouseEvent* event) { } if (_keyboardMouseDevice->isActive()) { - _keyboardMouseDevice->mouseMoveEvent(event); + _keyboardMouseDevice->mouseMoveEvent(event, _captureMouse, _mouseCaptureTarget); } } @@ -5950,6 +5963,20 @@ void Application::cameraModeChanged() { cameraMenuChanged(); } +bool Application::shouldCaptureMouse() const { + return _captureMouse && _glWidget->isActiveWindow() && !ui::Menu::isSomeSubmenuShown(); +} + +void Application::captureMouseChanged(bool captureMouse) { + _captureMouse = captureMouse; + if (_captureMouse) { + _glWidget->setCursor(QCursor(Qt::BlankCursor)); + } else { + _mouseCaptureTarget = QPointF(NAN, NAN); + _glWidget->unsetCursor(); + } +} + void Application::changeViewAsNeeded(float boomLength) { // Switch between first and third person views as needed // This is called when the boom length has changed @@ -6300,6 +6327,15 @@ void Application::update(float deltaTime) { PROFILE_ASYNC_END(app, "Scene Loading", ""); } + if (shouldCaptureMouse()) { + QPoint point = _glWidget->mapToGlobal(_glWidget->geometry().center()); + if (QCursor::pos() != point) { + _mouseCaptureTarget = point; + _ignoreMouseMove = true; + QCursor::setPos(point); + } + } + auto myAvatar = getMyAvatar(); { PerformanceTimer perfTimer("devices"); @@ -6355,8 +6391,8 @@ void Application::update(float deltaTime) { if (deltaTime > FLT_EPSILON && userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z) == 0.0f) { myAvatar->setDriveKey(MyAvatar::PITCH, -1.0f * userInputMapper->getActionState(controller::Action::PITCH)); myAvatar->setDriveKey(MyAvatar::YAW, -1.0f * userInputMapper->getActionState(controller::Action::YAW)); - myAvatar->setDriveKey(MyAvatar::DELTA_PITCH, -1.0f * userInputMapper->getActionState(controller::Action::DELTA_PITCH)); - myAvatar->setDriveKey(MyAvatar::DELTA_YAW, -1.0f * userInputMapper->getActionState(controller::Action::DELTA_YAW)); + myAvatar->setDriveKey(MyAvatar::DELTA_PITCH, -_myCamera.getSensitivity() * userInputMapper->getActionState(controller::Action::DELTA_PITCH)); + myAvatar->setDriveKey(MyAvatar::DELTA_YAW, -_myCamera.getSensitivity() * userInputMapper->getActionState(controller::Action::DELTA_YAW)); myAvatar->setDriveKey(MyAvatar::STEP_YAW, -1.0f * userInputMapper->getActionState(controller::Action::STEP_YAW)); } } diff --git a/interface/src/Application.h b/interface/src/Application.h index 37d4b4540a..16dadc8bf0 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -431,6 +431,7 @@ public slots: void cycleCamera(); void cameraModeChanged(); void cameraMenuChanged(); + void captureMouseChanged(bool captureMouse); void toggleOverlays(); void setOverlaysVisible(bool visible); Q_INVOKABLE void centerUI(); @@ -603,6 +604,7 @@ private: void maybeToggleMenuVisible(QMouseEvent* event) const; void toggleTabletUI(bool shouldOpen = false) const; + bool shouldCaptureMouse() const; void userKickConfirmation(const QUuid& nodeID); @@ -756,7 +758,9 @@ private: bool _settingsLoaded { false }; - bool _fakedMouseEvent { false }; + bool _captureMouse { false }; + bool _ignoreMouseMove { false }; + QPointF _mouseCaptureTarget { NAN, NAN }; bool _isMissingSequenceNumbers { false }; diff --git a/interface/src/FancyCamera.h b/interface/src/FancyCamera.h index 2a131d991f..afcd5197ff 100644 --- a/interface/src/FancyCamera.h +++ b/interface/src/FancyCamera.h @@ -36,6 +36,10 @@ class FancyCamera : public Camera { * @property {ViewFrustum} frustum - The camera frustum. * @property {Uuid} cameraEntity - The ID of the entity that is used for the camera position and orientation when the * camera is in entity mode. + * @property {boolean} captureMouse - The mouse capture state. When true, the mouse is invisible and cannot leave the bounds of + * Interface, as long as Interface is the active window and no menu item is selected. When false, the mouse + * behaves normally. + * @property {number} sensitivity - The current camera sensitivity. Must be positive. */ Q_PROPERTY(QUuid cameraEntity READ getCameraEntity WRITE setCameraEntity) diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 9f8a8ec013..79d9ebaa5c 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -358,6 +358,16 @@ void setupPreferences() { preference->setItems(items); preferences->addPreference(preference); } + { + auto getter = [myAvatar]()->float { return qApp->getCamera().getSensitivity(); }; + auto setter = [myAvatar](float value) { qApp->getCamera().setSensitivity(value); }; + auto preference = new SpinnerSliderPreference(VR_MOVEMENT, "Camera Sensitivity", getter, setter); + preference->setMin(0.01f); + preference->setMax(5.0f); + preference->setStep(0.1); + preference->setDecimals(2); + preferences->addPreference(preference); + } { auto getter = [myAvatar]()->int { return myAvatar->getControlScheme(); }; auto setter = [myAvatar](int index) { myAvatar->setControlScheme(index); }; diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp index a1138b3018..9b6946bbcc 100644 --- a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp +++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp @@ -1,6 +1,7 @@ // // Created by Benjamin Arnold on 5/27/14. // Copyright 2014 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -213,7 +214,6 @@ void CompositorHelper::setAllowMouseCapture(bool capture) { _allowMouseCapture = capture; emit allowMouseCaptureChanged(); } - _allowMouseCapture = capture; } void CompositorHelper::handleLeaveEvent() { diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.h b/libraries/display-plugins/src/display-plugins/CompositorHelper.h index 1279050a57..c45119fd63 100644 --- a/libraries/display-plugins/src/display-plugins/CompositorHelper.h +++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.h @@ -1,6 +1,7 @@ // // Created by Bradley Austin Davis Arnold on 2015/06/13 // Copyright 2015 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index c436a5b510..c7c5543d44 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -4,6 +4,7 @@ // // Created by Sam Gateau on 4/27/15. // Copyright 2015 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -36,13 +37,12 @@ void KeyboardMouseDevice::pluginUpdate(float deltaTime, const controller::InputC _inputDevice->_axisStateMap[MOUSE_AXIS_X].value = _lastCursor.x(); _inputDevice->_axisStateMap[MOUSE_AXIS_Y].value = _lastCursor.y(); - QPoint currentMove = _lastCursor - _previousCursor; - updateDeltaAxisValue(MOUSE_AXIS_X_POS, currentMove.x() > 0 ? currentMove.x() : 0.0f); - updateDeltaAxisValue(MOUSE_AXIS_X_NEG, currentMove.x() < 0 ? -currentMove.x() : 0.0f); + updateDeltaAxisValue(MOUSE_AXIS_X_POS, _accumulatedMove.x() > 0 ? _accumulatedMove.x() : 0.0f); + updateDeltaAxisValue(MOUSE_AXIS_X_NEG, _accumulatedMove.x() < 0 ? -_accumulatedMove.x() : 0.0f); // Y mouse is inverted positive is pointing up the screen - updateDeltaAxisValue(MOUSE_AXIS_Y_POS, currentMove.y() < 0 ? -currentMove.y() : 0.0f); - updateDeltaAxisValue(MOUSE_AXIS_Y_NEG, currentMove.y() > 0 ? currentMove.y() : 0.0f); - _previousCursor = _lastCursor; + updateDeltaAxisValue(MOUSE_AXIS_Y_POS, _accumulatedMove.y() < 0 ? -_accumulatedMove.y() : 0.0f); + updateDeltaAxisValue(MOUSE_AXIS_Y_NEG, _accumulatedMove.y() > 0 ? _accumulatedMove.y() : 0.0f); + _accumulatedMove = QPoint(0.0f, 0.0f); }); // For touch event, we need to check that the last event is not too long ago @@ -113,9 +113,16 @@ void KeyboardMouseDevice::eraseMouseClicked() { _inputDevice->_buttonPressedMap.erase(_inputDevice->makeInput(Qt::RightButton, true).getChannel()); } -void KeyboardMouseDevice::mouseMoveEvent(QMouseEvent* event) { +void KeyboardMouseDevice::mouseMoveEvent(QMouseEvent* event, bool capture, QPointF captureTarget) { QPoint currentPos = event->pos(); + if (!capture) { + _accumulatedMove += currentPos - _lastCursor; + } else if (!isNaN(captureTarget.x())) { + QPointF change = event->globalPos() - captureTarget; + _accumulatedMove += QPoint(change.x(), change.y()); + } + // FIXME - this has the characteristic that it will show large jumps when you move the cursor // outside of the application window, because we don't get MouseEvents when the cursor is outside // of the application window. diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h index 4286ced477..216194a712 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h @@ -4,6 +4,7 @@ // // Created by Sam Gateau on 4/27/15. // Copyright 2015 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -79,7 +80,7 @@ public: void keyPressEvent(QKeyEvent* event); void keyReleaseEvent(QKeyEvent* event); - void mouseMoveEvent(QMouseEvent* event); + void mouseMoveEvent(QMouseEvent* event, bool capture, QPointF captureTarget); void mousePressEvent(QMouseEvent* event); void mouseReleaseEvent(QMouseEvent* event); void eraseMouseClicked(); @@ -123,7 +124,7 @@ public: protected: QPoint _lastCursor; - QPoint _previousCursor; + QPoint _accumulatedMove; QPoint _mousePressPos; quint64 _mousePressTime; qreal _lastTotalScaleFactor; diff --git a/libraries/shared/src/shared/Camera.h b/libraries/shared/src/shared/Camera.h index 48cd814d86..f494318c73 100644 --- a/libraries/shared/src/shared/Camera.h +++ b/libraries/shared/src/shared/Camera.h @@ -3,6 +3,7 @@ // interface/src // // Copyright 2013 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -43,6 +44,8 @@ class Camera : public QObject { Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation) Q_PROPERTY(QString mode READ getModeString WRITE setModeString NOTIFY modeUpdated) Q_PROPERTY(QVariantMap frustum READ getViewFrustum CONSTANT) + Q_PROPERTY(bool captureMouse READ getCaptureMouse WRITE setCaptureMouse NOTIFY captureMouseChanged) + Q_PROPERTY(float sensitivity READ getSensitivity WRITE setSensitivity) public: Camera(); @@ -110,6 +113,37 @@ public slots: */ void setOrientation(const glm::quat& orientation); + /**jsdoc + * Gets the current mouse capture state. + * @function Camera.getCaptureMouse + * @returns {boolean} true if the mouse is captured (is invisible and cannot leave the bounds of Interface, + * if Interface is the active window and no menu item is selected), false if the mouse is behaving normally. + */ + bool getCaptureMouse() const { return _captureMouse; } + + /**jsdoc + * Sets the mouse capture state. When true, the mouse is invisible and cannot leave the bounds of + * Interface, as long as Interface is the active window and no menu item is selected. When false, the mouse + * behaves normally. + * @function Camera.setCaptureMouse + * @param {boolean} captureMouse - true to capture the mouse, false to release the mouse. + */ + void setCaptureMouse(bool captureMouse) { _captureMouse = captureMouse; emit captureMouseChanged(captureMouse); } + + /**jsdoc + * Gets the current camera sensitivity. + * @function Camera.getSensitivity + * @returns {number} The current camera sensitivity. Must be positive. + */ + float getSensitivity() const { return _sensitivity; } + + /**jsdoc + * Sets the camera sensitivity. Higher values mean that the camera will be more sensitive to mouse movements. + * @function Camera.setSensitivity + * @param {number} sensitivity - The desired camera sensitivity. Must be positive. + */ + void setSensitivity(float sensitivity) { _sensitivity = glm::max(0.0f, sensitivity); } + /**jsdoc * Computes a {@link PickRay} based on the current camera configuration and the specified x, y position on the * screen. The {@link PickRay} can be used in functions such as {@link Entities.findRayIntersection} and @@ -181,6 +215,20 @@ signals: */ void modeUpdated(const QString& newMode); + /**jsdoc + * Triggered when the camera mouse capture state changes. + * @function Camera.captureMouseChanged + * @param {boolean} newCaptureMouse - The new mouse capture state. + * @returns {Signal} + * @example Report mouse capture state changes. + * function onCaptureMouseChanged(newCaptureMouse) { + * print("The mouse capture has changed to " + newCaptureMouse); + * } + * + * Camera.captureMouseChanged.connect(onCaptureMouseChanged); + */ + void captureMouseChanged(bool newCaptureMouse); + private: void recompose(); void decompose(); @@ -194,6 +242,9 @@ private: glm::quat _orientation; bool _isKeepLookingAt{ false }; glm::vec3 _lookingAt; + + bool _captureMouse { false }; + float _sensitivity { 1.0f }; }; #endif // hifi_Camera_h