diff --git a/build_scripts/qt/resources.pri b/build_scripts/qt/resources.pri index 5b03921..49f5cd2 100644 --- a/build_scripts/qt/resources.pri +++ b/build_scripts/qt/resources.pri @@ -60,6 +60,7 @@ DISTFILES += \ src/res/qml/rotation_page/redirected/* \ src/res/qml/rotation_page/snap_turn/* \ src/res/qml/rotation_page/space_turn/* \ + src/res/qml/rotation_page/view_ratchet/* \ src/res/qml/common/* win32:RC_ICONS += src/res/img/icons/advicon256px.ico diff --git a/src/openvr/ovr_settings_wrapper.cpp b/src/openvr/ovr_settings_wrapper.cpp index fd971bb..589c510 100644 --- a/src/openvr/ovr_settings_wrapper.cpp +++ b/src/openvr/ovr_settings_wrapper.cpp @@ -2,15 +2,31 @@ namespace ovr_settings_wrapper { +// cludgey solution, long term solution we should apply defaults that make sense +// (most likely false) +bool unsetSettingErrorEnabled = true; + SettingsError handleErrors( std::string settingsKey, vr::EVRSettingsError error, std::string customErrorMsg ) { if ( error != vr::VRSettingsError_None ) { + if ( !unsetSettingErrorEnabled + && error + == vr::EVRSettingsError:: + VRSettingsError_UnsetSettingHasNoDefault ) + { + return SettingsError::UndefinedError; + } LOG( ERROR ) << "Could not access \"" << settingsKey << "\" setting: " << vr::VRSettings()->GetSettingsErrorNameFromEnum( error ) << " " << customErrorMsg; + if ( error + == vr::EVRSettingsError::VRSettingsError_UnsetSettingHasNoDefault ) + { + unsetSettingErrorEnabled = false; + } return SettingsError::UndefinedError; } diff --git a/src/openvr/ovr_settings_wrapper.h b/src/openvr/ovr_settings_wrapper.h index 14d1a2d..fc1bac3 100644 --- a/src/openvr/ovr_settings_wrapper.h +++ b/src/openvr/ovr_settings_wrapper.h @@ -64,5 +64,7 @@ SettingsError handleErrors( std::string settingsKey, vr::EVRSettingsError, std::string customErrorMsg ); +extern bool unsetSettingErrorEnabled; + void resetAllSettings(); } // namespace ovr_settings_wrapper diff --git a/src/res/qml/rotation_page/RotationPage.qml b/src/res/qml/rotation_page/RotationPage.qml index 9299309..f3fffb9 100644 --- a/src/res/qml/rotation_page/RotationPage.qml +++ b/src/res/qml/rotation_page/RotationPage.qml @@ -6,13 +6,14 @@ import "../common" import "redirected" import "space_turn" import "snap_turn" +import "view_ratchet" MyStackViewPage { width: 1200 headerText: "Rotation Settings" content: ColumnLayout { - spacing: 10 + spacing: 8 AutoTurnGroupBox{ id: autoTurnGroupBox } @@ -20,7 +21,9 @@ MyStackViewPage { RedirectedGroupBox{ id: redirectedGroupBox } - + ViewRatchetGroupBox{ + id: vgiiewRatchetGroupBox + } SpaceTurnGroupBox{ id:spaceTurnGroupBox } diff --git a/src/res/qml/rotation_page/redirected/AutoTurnGroupBox.qml b/src/res/qml/rotation_page/redirected/AutoTurnGroupBox.qml index 81ef06e..7884f55 100644 --- a/src/res/qml/rotation_page/redirected/AutoTurnGroupBox.qml +++ b/src/res/qml/rotation_page/redirected/AutoTurnGroupBox.qml @@ -11,7 +11,7 @@ GroupBox { label: MyText { leftPadding: 10 text: "Auto-Turn (for discrete redirected walking)" - bottomPadding: -10 + bottomPadding: -12 } background: Rectangle { color: "transparent" @@ -26,7 +26,7 @@ GroupBox { color: "#ffffff" height: 1 Layout.fillWidth: true - Layout.bottomMargin: 5 + Layout.bottomMargin: 3 } RowLayout { diff --git a/src/res/qml/rotation_page/redirected/RedirectedGroupBox.qml b/src/res/qml/rotation_page/redirected/RedirectedGroupBox.qml index 757f2c5..2f66fc3 100644 --- a/src/res/qml/rotation_page/redirected/RedirectedGroupBox.qml +++ b/src/res/qml/rotation_page/redirected/RedirectedGroupBox.qml @@ -11,7 +11,7 @@ GroupBox { label: MyText { leftPadding: 10 text: "Vestibular Motion (for continuous redirected walking)" - bottomPadding: -10 + bottomPadding: -12 } background: Rectangle { color: "transparent" @@ -26,13 +26,13 @@ GroupBox { color: "#ffffff" height: 1 Layout.fillWidth: true - Layout.bottomMargin: 5 + Layout.bottomMargin: 3 } RowLayout{ MyToggleButton { id: redirectedModeToggle - text: "On" + text: "Toggle On/Off" Layout.preferredWidth: 225 onCheckedChanged: { RotationTabController.setVestibularMotionEnabled(this.checked, true); @@ -106,6 +106,7 @@ GroupBox { } } + Component.onCompleted: { redirectedWalkingRadiusText.text = ((RotationTabController.vestibularMotionRadius).toFixed(2)) redirectedModeToggle.checked = RotationTabController.vestibularMotionEnabled diff --git a/src/res/qml/rotation_page/space_turn/SpaceTurnGroupBox.qml b/src/res/qml/rotation_page/space_turn/SpaceTurnGroupBox.qml index 8491220..348c956 100644 --- a/src/res/qml/rotation_page/space_turn/SpaceTurnGroupBox.qml +++ b/src/res/qml/rotation_page/space_turn/SpaceTurnGroupBox.qml @@ -11,7 +11,7 @@ GroupBox { label: MyText { leftPadding: 10 text: "Space Turn (for manual redirected walking)" - bottomPadding: -10 + bottomPadding: -12 } background: Rectangle { color: "transparent" @@ -26,7 +26,7 @@ GroupBox { color: "#ffffff" height: 1 Layout.fillWidth: true - Layout.bottomMargin: 5 + Layout.bottomMargin: 3 } RowLayout { diff --git a/src/res/qml/rotation_page/view_ratchet/ViewRatchetGroupBox.qml b/src/res/qml/rotation_page/view_ratchet/ViewRatchetGroupBox.qml new file mode 100644 index 0000000..683da3f --- /dev/null +++ b/src/res/qml/rotation_page/view_ratchet/ViewRatchetGroupBox.qml @@ -0,0 +1,88 @@ +import QtQuick 2.7 +import QtQuick.Controls 2.0 +import QtQuick.Layouts 1.3 +import ovras.advsettings 1.0 +import "../../common" + +GroupBox { + id: viewRatchetGroupBox + Layout.fillWidth: true + + ColumnLayout { + anchors.fill: parent + + RowLayout { + Layout.fillWidth: true + MyToggleButton{ + id: viewRatchetToggleBtn + text: "View Ratchetting" + Layout.preferredWidth: 275 + onCheckedChanged: { + RotationTabController.setViewRatchettingEnabled(this.checked, true); + } + } + Item{ + Layout.preferredWidth: 25 + } + + MySlider { + id: viewRatchetSlider + from: 0.00 + to: 0.1 + stepSize: 0.01 + Layout.fillWidth: true + onMoved: { + viewRatchetText.text = this.value.toFixed(2) + RotationTabController.setViewRatchettingPercent(this.value, true) + } + + } + + MyTextField { + id: viewRatchetText + text: "0.05" + keyBoardUID: 1007 + Layout.preferredWidth: 100 + Layout.leftMargin: 10 + horizontalAlignment: Text.AlignHCenter + function onInputEvent(input) { + var val = parseFloat(input) + if (!isNaN(val)) { + if (val < 0) { + val = 0 + } else if (val > 1) { + val = 1 + } + RotationTabController.setViewRatchettingPercent(val.toFixed(2), true) + if(val > 0.1){ + viewRatchetSlider.value = 0.1 + }else{ + viewRatchetSlider.value = val + } + } + text = RotationTabController.viewRatchettingPercent.toFixed(2); + } + } + } + } + + Component.onCompleted: { + viewRatchetToggleBtn.checked = RotationTabController.viewRatchettingEnabled + var ratchetPercVal = RotationTabController.viewRatchettingPercent + viewRatchetSlider.value = ratchetPercVal + viewRatchetText.text = ratchetPercVal.toFixed(2) + } + + Connections { + target: RotationTabController + + onViewRatchettingEnabledChanged:{ + viewRatchetToggleBtn.checked = RotationTabController.viewRatchettingEnabled + } + onViewRatchettingPercentChanged:{ + var ratchetPercVal = RotationTabController.viewRatchettingPercent + viewRatchetSlider.value = ratchetPercVal + viewRatchetText.text = ratchetPercVal.toFixed(2) + } + } +} diff --git a/src/res/qml/video_page/brightness/BrightnessGroupBox.qml b/src/res/qml/video_page/brightness/BrightnessGroupBox.qml index 7729f7f..11ed526 100644 --- a/src/res/qml/video_page/brightness/BrightnessGroupBox.qml +++ b/src/res/qml/video_page/brightness/BrightnessGroupBox.qml @@ -56,7 +56,7 @@ GroupBox { stepSize: 0.01 value: 1.0 Layout.fillWidth: true - onPositionChanged: { + onMoved: { var val = (this.value * 100) brightnessValueText.text = Math.round(val) + "%" VideoTabController.setBrightnessOpacityValue(value.toFixed(2), true) @@ -80,6 +80,9 @@ GroupBox { } var v = (val/100) VideoTabController.setBrightnessOpacityValue(v.toFixed(2), true) + if(v<.05){ + v = .05 + } brightnessSlider.value = v } text = Math.round(VideoTabController.brightnessOpacityValue * 100) + "%" diff --git a/src/settings/internal/settings_controller.h b/src/settings/internal/settings_controller.h index fced11c..a8f4b4c 100644 --- a/src/settings/internal/settings_controller.h +++ b/src/settings/internal/settings_controller.h @@ -396,6 +396,10 @@ private: SettingCategory::Rotation, QtInfo{ "autoturnVestibularMotionEnabled" }, false }, + BoolSettingValue{ BoolSetting::ROTATION_autoturnViewRatchettingEnabled, + SettingCategory::Rotation, + QtInfo{ "autoturnViewRatchettingEnabled" }, + false }, BoolSettingValue{ BoolSetting::ROTATION_autoturnShowNotification, SettingCategory::Rotation, QtInfo{ "autoturnShowNotification" }, @@ -488,7 +492,11 @@ private: SettingCategory::Rotation, QtInfo{ "autoturnVestibularMotionRadius" }, 22.0 }, - + DoubleSettingValue{ + DoubleSetting::ROTATION_autoturnViewRatchettingPercent, + SettingCategory::Rotation, + QtInfo{ "autoturnViewRatchettingPercent" }, + 0.05 }, }; constexpr static auto stringSettingsSize diff --git a/src/settings/settings.h b/src/settings/settings.h index f36b096..7418b0d 100644 --- a/src/settings/settings.h +++ b/src/settings/settings.h @@ -56,6 +56,7 @@ enum class BoolSetting ROTATION_autoturnEnabled, ROTATION_autoturnUseCornerAngle, ROTATION_autoturnVestibularMotionEnabled, + ROTATION_autoturnViewRatchettingEnabled, ROTATION_autoturnShowNotification, // LAST_ENUMERATOR must always be set to the last value LAST_ENUMERATOR = ROTATION_autoturnShowNotification, @@ -84,8 +85,9 @@ enum class DoubleSetting ROTATION_cordDetanglingAngle, ROTATION_autoturnMinCordTangle, ROTATION_autoturnVestibularMotionRadius, + ROTATION_autoturnViewRatchettingPercent, // LAST_ENUMERATOR must always be set to the last value - LAST_ENUMERATOR = ROTATION_autoturnVestibularMotionRadius, + LAST_ENUMERATOR = ROTATION_autoturnViewRatchettingPercent, }; enum class StringSetting diff --git a/src/tabcontrollers/RotationTabController.cpp b/src/tabcontrollers/RotationTabController.cpp index a810f50..07301f9 100644 --- a/src/tabcontrollers/RotationTabController.cpp +++ b/src/tabcontrollers/RotationTabController.cpp @@ -94,7 +94,6 @@ void RotationTabController::eventLoopTick( { m_isHMDActive = true; } - // TODO: this doesn't need to run if both are off if ( poseHmd.bPoseIsValid && poseHmd.bDeviceIsConnected && poseHmd.eTrackingResult == vr::TrackingResult_Running_OK ) { @@ -116,6 +115,12 @@ void RotationTabController::eventLoopTick( doVestibularMotion( poseHmd, chaperoneDistances ); } + if ( RotationTabController::viewRatchettingEnabled() ) + { + doViewRatchetting( poseHmd, chaperoneDistances ); + } + + m_autoTurnLastHmdUpdate = poseHmd.mDeviceToAbsoluteTracking; m_autoTurnChaperoneDistancesLast = std::move( chaperoneDistances ); } } @@ -144,6 +149,85 @@ void RotationTabController::eventLoopTick( } } +void RotationTabController::doViewRatchetting( + const vr::TrackedDevicePose_t& poseHmd, + const std::vector& chaperoneDistances ) +{ + if ( m_isHMDActive && poseHmd.bPoseIsValid && poseHmd.bDeviceIsConnected + && poseHmd.eTrackingResult == vr::TrackingResult_Running_OK + && !chaperoneDistances.empty() ) + { + if ( chaperoneDistances.size() + != m_autoTurnChaperoneDistancesLast.size() ) + { + m_autoTurnChaperoneDistancesLast = chaperoneDistances; + m_autoTurnLastHmdUpdate = poseHmd.mDeviceToAbsoluteTracking; + } + + // Find the (index of the) nearest wall + size_t nearestWallIdx = 0; + for ( size_t i = 0; i < chaperoneDistances.size(); i++ ) + { + if ( chaperoneDistances[i].distance + < chaperoneDistances[nearestWallIdx].distance ) + { + nearestWallIdx = i; + } + } + const auto& nearestWall = chaperoneDistances[nearestWallIdx]; + + // Convert pose matrix to quaternion + auto hmdQuaternion + = quaternion::fromHmdMatrix34( poseHmd.mDeviceToAbsoluteTracking ); + + // Get HMD raw yaw + double hmdYaw = quaternion::getYaw( hmdQuaternion ); + + // Get angle between HMD position and nearest point on + // wall + double hmdPositionToWallYaw = static_cast( + std::atan2( nearestWall.nearestPoint.v[0] + - poseHmd.mDeviceToAbsoluteTracking.m[0][3], + nearestWall.nearestPoint.v[2] + - poseHmd.mDeviceToAbsoluteTracking.m[2][3] ) ); + + // Get angle between HMD and wall + double hmdToWallYaw + = reduceAngle<>( hmdYaw - hmdPositionToWallYaw, -M_PI, M_PI ); + + do + { + // if we change walls, ignore + if ( nearestWallIdx != m_ratchettingLastWall ) + { + break; + } + + // Facing away from the wall is 0, |facing towards the wall| is M_PI + // Ignore turning towards the wall (e.g. change in magnitude is + // greater than 0) + if ( std::abs( hmdToWallYaw ) + - std::abs( m_ratchettingLastHmdRotation ) + > 0.0 ) + { + break; + } + // Magnify that change + double delta_degrees + = reduceAngle<>( + hmdToWallYaw - m_ratchettingLastHmdRotation, -M_PI, M_PI ) + * viewRatchettingPercent(); + + parent->m_moveCenterTabController.setRotation( + parent->m_moveCenterTabController.rotation() + + static_cast( delta_degrees * k_radiansToCentidegrees ) ); + } while ( false ); + + m_ratchettingLastHmdRotation = hmdToWallYaw; + m_ratchettingLastWall = nearestWallIdx; + } +} + void RotationTabController::doVestibularMotion( const vr::TrackedDevicePose_t& poseHmd, const std::vector& chaperoneDistances ) @@ -158,37 +242,20 @@ void RotationTabController::doVestibularMotion( m_autoTurnChaperoneDistancesLast = chaperoneDistances; m_autoTurnLastHmdUpdate = poseHmd.mDeviceToAbsoluteTracking; } - // Find the nearest wall we're moving TOWARDS and rotate - // away from it Rotate dist/(2*pi*r) where r is + // Rotate dist/(2*pi*r) where r is // m_autoTurnVestibularMotionRadius, as if we had walked // however many inches along a circle and the world was // turning to compensate do { - // find nearest wall we're moving towards - auto nearestWall = chaperoneDistances.end(); - auto itrLast = m_autoTurnChaperoneDistancesLast.begin(); - for ( auto itr = chaperoneDistances.begin(); - itr != chaperoneDistances.end(); - itr++ ) - { - itrLast++; - // if ( itr->distance > itrLast->distance ) - //{ - // continue; - //} - - if ( nearestWall == chaperoneDistances.end() - || itr->distance < nearestWall->distance ) - { - nearestWall = itr; - } - } - if ( nearestWall == chaperoneDistances.end() ) - { - break; - } + // Find the nearest wall + const auto& nearestWall + = std::min_element( chaperoneDistances.begin(), + chaperoneDistances.end(), + []( const auto& quadA, const auto& quadB ) { + return quadA.distance < quadB.distance; + } ); // Convert pose matrix to quaternion auto hmdQuaternion = quaternion::fromHmdMatrix34( @@ -210,6 +277,19 @@ void RotationTabController::doVestibularMotion( = reduceAngle<>( hmdYaw - hmdPositionToWallYaw, -M_PI, M_PI ); bool turnLeft = hmdToWallYaw > 0.0; + // Only turn if moving forward + double hmdMovementDirection = static_cast( + std::atan2( m_autoTurnLastHmdUpdate.m[0][3] + - poseHmd.mDeviceToAbsoluteTracking.m[0][3], + m_autoTurnLastHmdUpdate.m[2][3] + - poseHmd.mDeviceToAbsoluteTracking.m[2][3] ) ); + if ( std::abs( hmdYaw - hmdMovementDirection ) + * k_radiansToCentidegrees + >= 7000 ) + { + break; + } + // Get the distance between previous and current // position double distanceChange = static_cast( @@ -229,20 +309,16 @@ void RotationTabController::doVestibularMotion( } double rotationAmount = arcLength * ( turnLeft ? 1 : -1 ); + int newRotationAngle = reduceAngle<>( + parent->m_moveCenterTabController.rotation() + + static_cast( rotationAmount + * k_radiansToCentidegrees ), + 0, + 36000 ); - double newRotationAngleDeg - = std::fmod( parent->m_moveCenterTabController.rotation() - + ( rotationAmount * k_radiansToCentidegrees ) - + m_autoTurnRoundingError, - 360000.0 ); - int newRotationAngleInt = static_cast( newRotationAngleDeg ); - m_autoTurnRoundingError = newRotationAngleDeg - newRotationAngleInt; - - parent->m_moveCenterTabController.setRotation( - newRotationAngleInt ); + parent->m_moveCenterTabController.setRotation( newRotationAngle ); } while ( false ); - m_autoTurnLastHmdUpdate = poseHmd.mDeviceToAbsoluteTracking; } } @@ -272,7 +348,6 @@ void RotationTabController::doAutoTurn( == AutoTurnModes::LINEAR_SMOOTH_TURN && m_autoTurnLinearSmoothTurnRemaining != 0 ) { - // TODO: implement angular acceleration max? double deltaSeconds = FrameRates::toDoubleSeconds( m_estimatedFrameRate ); auto miniDeltaAngle = static_cast( @@ -284,11 +359,11 @@ void RotationTabController::doAutoTurn( { miniDeltaAngle = m_autoTurnLinearSmoothTurnRemaining; } - int newRotationAngleDeg = reduceAngle<>( - static_cast( parent->m_moveCenterTabController.rotation() - + miniDeltaAngle ), - 0, - 36000 ); + int newRotationAngleDeg + = reduceAngle<>( parent->m_moveCenterTabController.rotation() + + static_cast( miniDeltaAngle ), + 0, + 36000 ); parent->m_moveCenterTabController.setRotation( newRotationAngleDeg ); @@ -428,9 +503,8 @@ void RotationTabController::doAutoTurn( { case AutoTurnModes::SNAP: parent->m_moveCenterTabController.setRotation( - static_cast( - parent->m_moveCenterTabController.rotation() - + delta_degrees ) ); + parent->m_moveCenterTabController.rotation() + + static_cast( delta_degrees ) ); break; case AutoTurnModes::LINEAR_SMOOTH_TURN: m_autoTurnLinearSmoothTurnRemaining @@ -577,6 +651,18 @@ double RotationTabController::vestibularMotionRadius() const settings::DoubleSetting::ROTATION_autoturnVestibularMotionRadius ); } +bool RotationTabController::viewRatchettingEnabled() const +{ + return settings::getSetting( + settings::BoolSetting::ROTATION_autoturnViewRatchettingEnabled ); +} + +double RotationTabController::viewRatchettingPercent() const +{ + return settings::getSetting( + settings::DoubleSetting::ROTATION_autoturnViewRatchettingPercent ); +} + // Setters void RotationTabController::setAutoTurnEnabled( bool value, bool notify ) @@ -711,5 +797,25 @@ void RotationTabController::setVestibularMotionRadius( double value, emit vestibularMotionRadiusChanged( value ); } } +void RotationTabController::setViewRatchettingEnabled( bool value, bool notify ) +{ + settings::setSetting( + settings::BoolSetting::ROTATION_autoturnViewRatchettingEnabled, value ); + if ( notify ) + { + emit viewRatchettingEnabledChanged( value ); + } +} +void RotationTabController::setViewRatchettingPercent( double value, + bool notify ) +{ + settings::setSetting( + settings::DoubleSetting::ROTATION_autoturnViewRatchettingPercent, + value ); + if ( notify ) + { + emit viewRatchettingPercentChanged( value ); + } +} } // namespace advsettings diff --git a/src/tabcontrollers/RotationTabController.h b/src/tabcontrollers/RotationTabController.h index ad6fa80..de8253a 100644 --- a/src/tabcontrollers/RotationTabController.h +++ b/src/tabcontrollers/RotationTabController.h @@ -83,6 +83,12 @@ class RotationTabController : public QObject Q_PROPERTY( double vestibularMotionRadius READ vestibularMotionRadius WRITE setVestibularMotionRadius NOTIFY vestibularMotionRadiusChanged ) + Q_PROPERTY( + bool viewRatchettingEnabled READ viewRatchettingEnabled WRITE + setViewRatchettingEnabled NOTIFY viewRatchettingEnabledChanged ) + Q_PROPERTY( + double viewRatchettingPercent READ viewRatchettingPercent WRITE + setViewRatchettingPercent NOTIFY viewRatchettingPercentChanged ) Q_PROPERTY( bool autoTurnShowNotification READ autoTurnShowNotification WRITE setAutoTurnShowNotification NOTIFY autoTurnShowNotificationChanged ) @@ -108,8 +114,9 @@ private: std::vector m_autoTurnWallActive; vr::HmdMatrix34_t m_autoTurnLastHmdUpdate; std::vector m_autoTurnChaperoneDistancesLast; - double m_autoTurnRoundingError = 0.0; std::chrono::steady_clock::time_point::duration m_estimatedFrameRate; + double m_ratchettingLastHmdRotation = 0.0; + size_t m_ratchettingLastWall = 0; std::optional m_autoTurnNotificationTimestamp; @@ -122,6 +129,9 @@ private: void doVestibularMotion( const vr::TrackedDevicePose_t& poseHmd, const std::vector& chaperoneDistances ); + void doViewRatchetting( + const vr::TrackedDevicePose_t& poseHmd, + const std::vector& chaperoneDistances ); public: void initStage1(); @@ -143,6 +153,8 @@ public: int autoTurnMode() const; bool vestibularMotionEnabled() const; double vestibularMotionRadius() const; + bool viewRatchettingEnabled() const; + double viewRatchettingPercent() const; public slots: void setAutoTurnEnabled( bool value, bool notify = true ); @@ -156,6 +168,8 @@ public slots: void setAutoTurnMode( int value, bool notify = true ); void setVestibularMotionEnabled( bool value, bool notify = true ); void setVestibularMotionRadius( double value, bool notify = true ); + void setViewRatchettingEnabled( bool value, bool notify = true ); + void setViewRatchettingPercent( double value, bool notify = true ); signals: @@ -171,6 +185,8 @@ signals: void autoTurnModeChanged( int value ); void vestibularMotionEnabledChanged( bool value ); void vestibularMotionRadiusChanged( double value ); + void viewRatchettingEnabledChanged( bool value ); + void viewRatchettingPercentChanged( double value ); }; // Would be nice to do but the standard doesn't allow