diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0dd394c459..e3fbbe12ad 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -168,6 +168,7 @@ MyAvatar::MyAvatar(QThread* thread) : _displayNameSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "displayName", ""), _collisionSoundURLSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "collisionSoundURL", QUrl(_collisionSoundURL)), _useSnapTurnSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "useSnapTurn", _useSnapTurn), + _hoverWhenUnsupportedSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "hoverWhenUnsupported", _hoverWhenUnsupported), _userHeightSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "userHeight", DEFAULT_AVATAR_HEIGHT), _flyingHMDSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "flyingHMD", _flyingPrefHMD), _movementReferenceSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "movementReference", _movementReference), @@ -948,6 +949,7 @@ void MyAvatar::simulate(float deltaTime, bool inView) { bool collisionlessAllowed = zoneInteractionProperties.second; _characterController.setZoneFlyingAllowed(zoneAllowsFlying || !isPhysicsEnabled); _characterController.setComfortFlyingAllowed(_enableFlying); + _characterController.setHoverWhenUnsupported(_hoverWhenUnsupported); _characterController.setCollisionlessAllowed(collisionlessAllowed); } @@ -1309,6 +1311,7 @@ void MyAvatar::saveData() { _displayNameSetting.set(_displayName); _collisionSoundURLSetting.set(_collisionSoundURL); _useSnapTurnSetting.set(_useSnapTurn); + _hoverWhenUnsupportedSetting.set(_hoverWhenUnsupported); _userHeightSetting.set(getUserHeight()); _flyingHMDSetting.set(getFlyingHMDPref()); _movementReferenceSetting.set(getMovementReference()); @@ -1913,6 +1916,7 @@ void MyAvatar::loadData() { setDisplayName(_displayNameSetting.get()); setCollisionSoundURL(_collisionSoundURLSetting.get(QUrl(DEFAULT_AVATAR_COLLISION_SOUND_URL)).toString()); setSnapTurn(_useSnapTurnSetting.get()); + setHoverWhenUnsupported(_hoverWhenUnsupportedSetting.get()); setDominantHand(_dominantHandSetting.get(DOMINANT_RIGHT_HAND).toLower()); setStrafeEnabled(_strafeEnabledSetting.get(DEFAULT_STRAFE_ENABLED)); setHmdAvatarAlignmentType(_hmdAvatarAlignmentTypeSetting.get(DEFAULT_HMD_AVATAR_ALIGNMENT_TYPE).toLower()); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index ef38318b0f..7179c31d91 100755 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -798,6 +798,18 @@ public: * @param {number} index */ Q_INVOKABLE void setControlScheme(int index) { _controlSchemeIndex = (index >= 0 && index <= 2) ? index : 0; } + + /**jsdoc + * @function MyAvatar.hoverWhenUnsupported + * @returns {boolean} + */ + Q_INVOKABLE bool hoverWhenUnsupported() const { return _hoverWhenUnsupported; } + /**jsdoc + * @function MyAvatar.setHoverWhenUnsupported + * @param {boolean} on + */ + Q_INVOKABLE void setHoverWhenUnsupported(bool on) { _hoverWhenUnsupported = on; } + /**jsdoc * Sets the avatar's dominant hand. * @function MyAvatar.setDominantHand @@ -2484,6 +2496,7 @@ private: ThreadSafeValueCache _prefOverrideAnimGraphUrl; QUrl _fstAnimGraphOverrideUrl; bool _useSnapTurn { true }; + bool _hoverWhenUnsupported{ true }; ThreadSafeValueCache _dominantHand { DOMINANT_RIGHT_HAND }; ThreadSafeValueCache _hmdAvatarAlignmentType { DEFAULT_HMD_AVATAR_ALIGNMENT_TYPE }; ThreadSafeValueCache _strafeEnabled{ DEFAULT_STRAFE_ENABLED }; @@ -2679,6 +2692,7 @@ private: Setting::Handle _displayNameSetting; Setting::Handle _collisionSoundURLSetting; Setting::Handle _useSnapTurnSetting; + Setting::Handle _hoverWhenUnsupportedSetting; Setting::Handle _userHeightSetting; Setting::Handle _flyingHMDSetting; Setting::Handle _movementReferenceSetting; diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index bb84b88a1e..6a2516115d 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -300,6 +300,12 @@ void setupPreferences() { preference->setIndented(true); preferences->addPreference(preference); } + { + auto getter = [myAvatar]() -> bool { return myAvatar->hoverWhenUnsupported(); }; + auto setter = [myAvatar](bool value) { myAvatar->setHoverWhenUnsupported(value); }; + auto preference = new CheckPreference(VR_MOVEMENT, "Hover When Unsupported", getter, setter); + preferences->addPreference(preference); + } { auto getter = [myAvatar]()->int { return myAvatar->getMovementReference(); }; auto setter = [myAvatar](int value) { myAvatar->setMovementReference(value); }; diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 247550a9d0..f0f1b9c837 100755 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -754,7 +754,11 @@ void CharacterController::updateState() { switch (_state) { case State::Ground: if (!rayHasHit && !_hasSupport) { - SET_STATE(State::Hover, "no ground detected"); + if (_hoverWhenUnsupported) { + SET_STATE(State::Hover, "no ground detected"); + } else { + SET_STATE(State::InAir, "falling"); + } } else if (_pendingFlags & PENDING_FLAG_JUMP && _jumpButtonDownCount != _takeoffJumpButtonID) { _takeoffJumpButtonID = _jumpButtonDownCount; _takeoffToInAirStartTime = now; @@ -764,7 +768,7 @@ void CharacterController::updateState() { } break; case State::Takeoff: - if (!rayHasHit && !_hasSupport) { + if (_hoverWhenUnsupported && (!rayHasHit && !_hasSupport)) { SET_STATE(State::Hover, "no ground"); } else if ((now - _takeoffToInAirStartTime) > TAKE_OFF_TO_IN_AIR_PERIOD) { SET_STATE(State::InAir, "takeoff done"); @@ -791,7 +795,7 @@ void CharacterController::updateState() { SET_STATE(State::Hover, "double jump button"); } else if (_comfortFlyingAllowed && (jumpButtonHeld || vertTargetSpeedIsNonZero) && (now - _jumpButtonDownStartTime) > JUMP_TO_HOVER_PERIOD) { SET_STATE(State::Hover, "jump button held"); - } else if ((!rayHasHit && !_hasSupport) || _floorDistance > _scaleFactor * DEFAULT_AVATAR_FALL_HEIGHT) { + } else if (_hoverWhenUnsupported && ((!rayHasHit && !_hasSupport) || _floorDistance > _scaleFactor * DEFAULT_AVATAR_FALL_HEIGHT)) { // Transition to hover if there's no ground beneath us or we are above the fall threshold, regardless of _comfortFlyingAllowed SET_STATE(State::Hover, "above fall threshold"); } diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index c46c9c8361..c825f3a10b 100755 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -131,6 +131,7 @@ public: void setZoneFlyingAllowed(bool value) { _zoneFlyingAllowed = value; } void setComfortFlyingAllowed(bool value) { _comfortFlyingAllowed = value; } + void setHoverWhenUnsupported(bool value) { _hoverWhenUnsupported = value; } void setCollisionlessAllowed(bool value); void setPendingFlagsUpdateCollisionMask(){ _pendingFlags |= PENDING_FLAG_UPDATE_COLLISION_MASK; } @@ -215,6 +216,7 @@ protected: bool _zoneFlyingAllowed { true }; bool _comfortFlyingAllowed { true }; + bool _hoverWhenUnsupported{ true }; bool _collisionlessAllowed { true }; bool _collisionless { false };