diff --git a/advancedSettings.pro b/advancedSettings.pro index 9bb821a..300fb0c 100644 --- a/advancedSettings.pro +++ b/advancedSettings.pro @@ -25,7 +25,8 @@ SOURCES += src/main.cpp\ src/tabcontrollers/UtilitiesTabController.cpp \ src/tabcontrollers/audiomanager/AudioManagerWindows.cpp \ src/tabcontrollers/PttController.cpp \ - src/tabcontrollers/AccessibilityTabController.cpp + src/tabcontrollers/AccessibilityTabController.cpp \ + src/utils/ChaperoneUtils.cpp HEADERS += src/overlaycontroller.h \ src/logging.h \ @@ -41,7 +42,9 @@ HEADERS += src/overlaycontroller.h \ src/tabcontrollers/AudioManager.h \ src/tabcontrollers/audiomanager/AudioManagerWindows.h \ src/tabcontrollers/PttController.h \ - src/tabcontrollers/AccessibilityTabController.h + src/tabcontrollers/AccessibilityTabController.h \ + src/utils/Matrix.h \ + src/utils/ChaperoneUtils.h INCLUDEPATH += third-party/openvr/include \ third-party/easylogging++ diff --git a/bin/win64/res/qml/ChaperonePage.qml b/bin/win64/res/qml/ChaperonePage.qml index 504b94a..6fbe5ef 100644 --- a/bin/win64/res/qml/ChaperonePage.qml +++ b/bin/win64/res/qml/ChaperonePage.qml @@ -3,6 +3,7 @@ import QtQuick.Controls 2.0 import QtQuick.Layouts 1.3 import QtQuick.Dialogs 1.2 import matzman666.advsettings 1.0 +import "." // QTBUG-34418, singletons require explicit import to load qmldir file MyStackViewPage { headerText: "Chaperone Settings" @@ -102,6 +103,11 @@ MyStackViewPage { Layout.leftMargin: 32 text: "Force Bounds Setting" } + MyToggleButton { + id: chaperoneNewProfileIncludeProximityWarnings + Layout.leftMargin: 32 + text: "Proximity Warning Settings" + } } onClosed: { if (okClicked) { @@ -115,7 +121,8 @@ MyStackViewPage { && !chaperoneNewProfileIncludeFloorBoundsMarker.checked && !chaperoneNewProfileIncludeBoundsColor.checked && !chaperoneNewProfileIncludeStyle.checked - && !chaperoneNewProfileIncludeForceBounds.checked) { + && !chaperoneNewProfileIncludeForceBounds.checked + && !chaperoneNewProfileIncludeProximityWarnings.checked) { chaperoneMessageDialog.showMessage("Create New Profile", "ERROR: Nothing included.") } else { ChaperoneTabController.addChaperoneProfile(chaperoneNewProfileName.text, @@ -127,7 +134,8 @@ MyStackViewPage { chaperoneNewProfileIncludeFloorBoundsMarker.checked, chaperoneNewProfileIncludeBoundsColor.checked, chaperoneNewProfileIncludeStyle.checked, - chaperoneNewProfileIncludeForceBounds.checked) + chaperoneNewProfileIncludeForceBounds.checked, + chaperoneNewProfileIncludeProximityWarnings.checked) } } @@ -143,6 +151,7 @@ MyStackViewPage { chaperoneNewProfileIncludeBoundsColor.checked = false chaperoneNewProfileIncludeStyle.checked = false chaperoneNewProfileIncludeForceBounds.checked = false + chaperoneNewProfileIncludeProximityWarnings.checked = false open() } } @@ -440,6 +449,18 @@ MyStackViewPage { ChaperoneTabController.setForceBounds(this.checked, false) } } + + Item { Layout.fillWidth: true } + + MyPushButton { + id: chaperoneWarningsConfigButton + text: "Proximity Warning Settings" + Layout.preferredWidth: 350 + onClicked: { + MyResources.playFocusChangedSound() + mainView.push(chaperoneWarningsPage) + } + } } MyPushButton { diff --git a/bin/win64/res/qml/ChaperoneWarningsPage.qml b/bin/win64/res/qml/ChaperoneWarningsPage.qml new file mode 100644 index 0000000..1064838 --- /dev/null +++ b/bin/win64/res/qml/ChaperoneWarningsPage.qml @@ -0,0 +1,567 @@ +import QtQuick 2.7 +import QtQuick.Controls 2.0 +import QtQuick.Layouts 1.3 +import QtQuick.Dialogs 1.2 +import matzman666.advsettings 1.0 +import "." // QTBUG-34418, singletons require explicit import to load qmldir file + +MyStackViewPage { + headerText: "Chaperone Proximity Warning Settings" + + ColorDialog { + id: colorDialog + title: "Please choose a color" + onAccepted: { + console.log("You chose: " + colorDialog.color) + Qt.quit() + } + onRejected: { + console.log("Canceled") + Qt.quit() + } + } + + content: ColumnLayout { + spacing: 36 + + ColumnLayout { + spacing: 0 + MyToggleButton { + id: switchBeginnerToggle + text: "Switch to Beginner Mode" + onCheckedChanged: { + ChaperoneTabController.chaperoneSwitchToBeginnerEnabled = checked + } + } + RowLayout { + MyText { + text: "Activation Distance: " + Layout.preferredWidth: 250 + } + MyPushButton2 { + id: switchBeginnerDistanceMinusButton + Layout.preferredWidth: 40 + text: "-" + onClicked: { + var val = ChaperoneTabController.chaperoneSwitchToBeginnerDistance - 0.1 + if (val <= 0) { + val = 0.1; + } + ChaperoneTabController.chaperoneSwitchToBeginnerDistance = val.toFixed(2) + } + } + + MySlider { + id: switchBeginnerDistanceSlider + from: 0 + to: 2.0 + stepSize: 0.01 + value: 0.5 + Layout.fillWidth: true + onPositionChanged: { + var val = this.from + ( this.position * (this.to - this.from)) + switchBeginnerDistanceText.text = val.toFixed(2) + } + onValueChanged: { + var val = switchBeginnerDistanceSlider.value.toFixed(2) + if (val < 0.01) { + val = 0.01 + } + ChaperoneTabController.chaperoneSwitchToBeginnerDistance = val + switchBeginnerDistanceText.text = val + } + } + + MyPushButton2 { + id: switchBeginnerDistancePlusButton + Layout.preferredWidth: 40 + text: "+" + onClicked: { + var val = ChaperoneTabController.chaperoneSwitchToBeginnerDistance + 0.1 + if (val > 2.0) { + val = 2.0; + } + ChaperoneTabController.chaperoneSwitchToBeginnerDistance = val.toFixed(2) + } + } + + MyTextField { + id: switchBeginnerDistanceText + text: "0.00" + keyBoardUID: 801 + Layout.preferredWidth: 100 + Layout.leftMargin: 10 + horizontalAlignment: Text.AlignHCenter + function onInputEvent(input) { + var val = parseFloat(input) + if (!isNaN(val)) { + if (val <= 0.0) { + val = 0.01 + } + val = val.toFixed(2) + ChaperoneTabController.chaperoneSwitchToBeginnerDistance = val + text = val + } else { + text = ChaperoneTabController.chaperoneSwitchToBeginnerDistance.toFixed(2) + } + } + } + } + } + + ColumnLayout { + spacing: 0 + MyToggleButton { + id: hapticFeedbackToggle + text: "Trigger Haptic Feedback" + onCheckedChanged: { + ChaperoneTabController.chaperoneHapticFeedbackEnabled = checked + } + } + RowLayout { + MyText { + text: "Activation Distance: " + Layout.preferredWidth: 250 + } + MyPushButton2 { + id: hapticFeedbackDistanceMinusButton + Layout.preferredWidth: 40 + text: "-" + onClicked: { + var val = ChaperoneTabController.chaperoneHapticFeedbackDistance - 0.1 + if (val <= 0) { + val = 0.1; + } + ChaperoneTabController.chaperoneHapticFeedbackDistance = val.toFixed(2) + } + } + + MySlider { + id: hapticFeedbackDistanceSlider + from: 0 + to: 2.0 + stepSize: 0.01 + value: 0.5 + Layout.fillWidth: true + onPositionChanged: { + var val = this.from + ( this.position * (this.to - this.from)) + hapticFeedbackDistanceText.text = val.toFixed(2) + } + onValueChanged: { + var val = hapticFeedbackDistanceSlider.value.toFixed(2) + if (val < 0.01) { + val = 0.01 + } + ChaperoneTabController.chaperoneHapticFeedbackDistance = val + hapticFeedbackDistanceText.text = val + } + } + + MyPushButton2 { + id: hapticFeedbackDistancePlusButton + Layout.preferredWidth: 40 + text: "+" + onClicked: { + var val = ChaperoneTabController.chaperoneHapticFeedbackDistance + 0.1 + if (val > 2.0) { + val = 2.0; + } + ChaperoneTabController.chaperoneHapticFeedbackDistance = val.toFixed(2) + } + } + + MyTextField { + id: hapticFeedbackDistanceText + text: "0.00" + keyBoardUID: 805 + Layout.preferredWidth: 100 + Layout.leftMargin: 10 + horizontalAlignment: Text.AlignHCenter + function onInputEvent(input) { + var val = parseFloat(input) + if (!isNaN(val)) { + if (val <= 0.0) { + val = 0.01 + } + val = val.toFixed(2) + ChaperoneTabController.chaperoneHapticFeedbackDistance = val + text = val + } else { + text = ChaperoneTabController.chaperoneHapticFeedbackDistance.toFixed(2) + } + } + } + } + } + + ColumnLayout { + spacing: 0 + RowLayout { + spacing: 32 + MyToggleButton { + id: audioWarningToggle + text: "Audio Warning" + onCheckedChanged: { + ChaperoneTabController.chaperoneAlarmSoundEnabled = checked + } + } + MyToggleButton { + id: audioWarningLoopingToggle + text: "Loop Audio" + onCheckedChanged: { + ChaperoneTabController.chaperoneAlarmSoundLooping = checked + audioWarningAdjustVolumeToggle.enabled = checked + } + } + MyToggleButton { + id: audioWarningAdjustVolumeToggle + text: "Adjust Volume" + enabled: false + onCheckedChanged: { + ChaperoneTabController.chaperoneAlarmSoundAdjustVolume = checked + } + } + } + RowLayout { + MyText { + text: "Activation Distance: " + Layout.preferredWidth: 250 + } + MyPushButton2 { + id: audioWarningDistanceMinusButton + Layout.preferredWidth: 40 + text: "-" + onClicked: { + var val = ChaperoneTabController.chaperoneAlarmSoundDistance - 0.1 + if (val <= 0.0) { + val = 0.01; + } + ChaperoneTabController.chaperoneAlarmSoundDistance = val.toFixed(2) + } + } + + MySlider { + id: audioWarningDistanceSlider + from: 0 + to: 2.0 + stepSize: 0.01 + value: 0.5 + Layout.fillWidth: true + onPositionChanged: { + var val = this.from + ( this.position * (this.to - this.from)) + audioWarningDistanceText.text = val.toFixed(2) + } + onValueChanged: { + var val = audioWarningDistanceSlider.value.toFixed(2) + if (val < 0.01) { + val = 0.01 + } + ChaperoneTabController.chaperoneAlarmSoundDistance = val + audioWarningDistanceText.text = val + } + } + + MyPushButton2 { + id: audioWarningDistancePlusButton + Layout.preferredWidth: 40 + text: "+" + onClicked: { + var val = ChaperoneTabController.chaperoneAlarmSoundDistance + 0.1 + if (val > 2.0) { + val = 2.0; + } + ChaperoneTabController.chaperoneAlarmSoundDistance = val.toFixed(2) + } + } + + MyTextField { + id: audioWarningDistanceText + text: "0.00" + keyBoardUID: 802 + Layout.preferredWidth: 100 + Layout.leftMargin: 10 + horizontalAlignment: Text.AlignHCenter + function onInputEvent(input) { + var val = parseFloat(input) + if (!isNaN(val)) { + if (val <= 0.0) { + val = 0.01 + } + val = val.toFixed(2) + ChaperoneTabController.chaperoneAlarmSoundDistance = val + text = val + } else { + text = ChaperoneTabController.chaperoneAlarmSoundDistance.toFixed(2) + } + } + } + } + } + + ColumnLayout { + spacing: 0 + MyToggleButton { + id: openDashboardToggle + text: "Open Dashboard" + onCheckedChanged: { + ChaperoneTabController.chaperoneShowDashboardEnabled = checked + } + } + RowLayout { + MyText { + text: "Activation Distance: " + Layout.preferredWidth: 250 + } + MyPushButton2 { + id: openDashboardDistanceMinusButton + Layout.preferredWidth: 40 + text: "-" + onClicked: { + var val = ChaperoneTabController.chaperoneShowDashboardDistance - 0.1 + if (val <= 0.0) { + val = 0.01; + } + ChaperoneTabController.chaperoneShowDashboardDistance = val.toFixed(2) + } + } + + MySlider { + id: openDashboardDistanceSlider + from: 0 + to: 2.0 + stepSize: 0.01 + value: 0.5 + Layout.fillWidth: true + onPositionChanged: { + var val = this.from + ( this.position * (this.to - this.from)) + openDashboardDistanceText.text = val.toFixed(2) + } + onValueChanged: { + var val = openDashboardDistanceSlider.value.toFixed(2) + if (val < 0.01) { + val = 0.01 + } + ChaperoneTabController.chaperoneShowDashboardDistance = val + openDashboardDistanceText.text = val + } + } + + MyPushButton2 { + id: openDashboardDistancePlusButton + Layout.preferredWidth: 40 + text: "+" + onClicked: { + var val = ChaperoneTabController.chaperoneShowDashboardDistance + 0.1 + if (val > 2.0) { + val = 2.0; + } + ChaperoneTabController.chaperoneShowDashboardDistance = val.toFixed(2) + } + } + + MyTextField { + id: openDashboardDistanceText + text: "0.00" + keyBoardUID: 803 + Layout.preferredWidth: 100 + Layout.leftMargin: 10 + horizontalAlignment: Text.AlignHCenter + function onInputEvent(input) { + var val = parseFloat(input) + if (!isNaN(val)) { + if (val <= 0.0) { + val = 0.01 + } + val = val.toFixed(2) + ChaperoneTabController.chaperoneShowDashboardDistance = val + text = val + } else { + text = ChaperoneTabController.chaperoneShowDashboardDistance.toFixed(2) + } + } + } + } + } + + + ColumnLayout { + spacing: 0 + MyToggleButton { + id: velModifierToggle + text: "Velocity Dependent Fade/Activation Distance" + onCheckedChanged: { + ChaperoneTabController.chaperoneVelocityModifierEnabled = checked + } + } + RowLayout { + MyText { + text: "Distance Modifier: " + Layout.preferredWidth: 250 + } + MyPushButton2 { + id: velModifierMinusButton + Layout.preferredWidth: 40 + text: "-" + onClicked: { + var val = ChaperoneTabController.chaperoneVelocityModifier - 0.01 + if (val < 0.0) { + val = 0.0; + } + ChaperoneTabController.chaperoneVelocityModifier = val.toFixed(2) + } + } + + MySlider { + id: velModifierSlider + from: 0 + to: 1.0 + stepSize: 0.01 + value: 0.3 + Layout.fillWidth: true + onPositionChanged: { + var val = this.from + ( this.position * (this.to - this.from)) + velModifierText.text = val.toFixed(2) + } + onValueChanged: { + var val = velModifierSlider.value.toFixed(2) + if (val < 0.0) { + val = 0.0 + } + ChaperoneTabController.chaperoneVelocityModifier = val + velModifierText.text = val + } + } + + MyPushButton2 { + id: velModifierPlusButton + Layout.preferredWidth: 40 + text: "+" + onClicked: { + var val = ChaperoneTabController.chaperoneVelocityModifier + 0.01 + if (val > 1.0) { + val = 1.0; + } + ChaperoneTabController.chaperoneVelocityModifier = val.toFixed(2) + } + } + + MyTextField { + id: velModifierText + text: "0.00" + keyBoardUID: 803 + Layout.preferredWidth: 100 + Layout.leftMargin: 10 + horizontalAlignment: Text.AlignHCenter + function onInputEvent(input) { + var val = parseFloat(input) + if (!isNaN(val)) { + if (val < 0.0) { + val = 0.0 + } + val = val.toFixed(2) + ChaperoneTabController.chaperoneVelocityModifier = val + text = val + } else { + text = ChaperoneTabController.chaperoneVelocityModifier.toFixed(2) + } + } + } + } + } + + + Item { Layout.fillHeight: true; Layout.fillWidth: true } + + Component.onCompleted: { + switchBeginnerToggle.checked = ChaperoneTabController.chaperoneSwitchToBeginnerEnabled + var d = ChaperoneTabController.chaperoneSwitchToBeginnerDistance.toFixed(2) + if (d <= switchBeginnerDistanceSlider.to) { + switchBeginnerDistanceSlider.value = d + } + switchBeginnerDistanceText.text = d + hapticFeedbackToggle.checked = ChaperoneTabController.chaperoneHapticFeedbackEnabled + var d = ChaperoneTabController.chaperoneHapticFeedbackDistance.toFixed(2) + if (d <= hapticFeedbackDistanceSlider.to) { + hapticFeedbackDistanceSlider.value = d + } + hapticFeedbackDistanceText.text = d + audioWarningToggle.checked = ChaperoneTabController.chaperoneAlarmSoundEnabled + audioWarningLoopingToggle.checked = ChaperoneTabController.chaperoneAlarmSoundLooping + audioWarningAdjustVolumeToggle.checked = ChaperoneTabController.chaperoneAlarmSoundAdjustVolume + d = ChaperoneTabController.chaperoneAlarmSoundDistance.toFixed(2) + if (d <= audioWarningDistanceSlider.to) { + audioWarningDistanceSlider.value = d + } + audioWarningDistanceText.text = d + openDashboardToggle.checked = ChaperoneTabController.chaperoneShowDashboardEnabled + d = ChaperoneTabController.chaperoneShowDashboardDistance.toFixed(2) + if (d <= openDashboardDistanceSlider.to) { + openDashboardDistanceSlider.value = d + } + openDashboardDistanceText.text = d + velModifierToggle.checked = ChaperoneTabController.chaperoneVelocityModifierEnabled + d = ChaperoneTabController.chaperoneVelocityModifier.toFixed(2) + if (d <= velModifierSlider.to) { + velModifierSlider.value = d + } + velModifierText.text = d + } + + Connections { + target: ChaperoneTabController + onChaperoneSwitchToBeginnerEnabledChanged: { + switchBeginnerToggle.checked = ChaperoneTabController.chaperoneSwitchToBeginnerEnabled + } + onChaperoneSwitchToBeginnerDistanceChanged: { + var d = ChaperoneTabController.chaperoneSwitchToBeginnerDistance.toFixed(2) + if (d <= switchBeginnerDistanceSlider.to && Math.abs(switchBeginnerDistanceSlider.value - d) > 0.0008) { + switchBeginnerDistanceSlider.value = d + } + switchBeginnerDistanceText.text = d + } + onChaperoneHapticFeedbackEnabledChanged: { + hapticFeedbackToggle.checked = ChaperoneTabController.chaperoneHapticFeedbackEnabled + } + onChaperoneHapticFeedbackDistanceChanged: { + var d = ChaperoneTabController.chaperoneHapticFeedbackDistance.toFixed(2) + if (d <= hapticFeedbackDistanceSlider.to && Math.abs(hapticFeedbackDistanceSlider.value - d) > 0.0008) { + hapticFeedbackDistanceSlider.value = d + } + hapticFeedbackDistanceText.text = d + } + onChaperoneAlarmSoundEnabledChanged: { + audioWarningToggle.checked = ChaperoneTabController.chaperoneAlarmSoundEnabled + } + onChaperoneAlarmSoundLoopingChanged: { + audioWarningLoopingToggle.checked = ChaperoneTabController.chaperoneAlarmSoundLooping + } + onChaperoneAlarmSoundDistanceChanged: { + var d = ChaperoneTabController.chaperoneAlarmSoundDistance.toFixed(2) + if (d <= audioWarningDistanceSlider.to && Math.abs(audioWarningDistanceSlider.value - d) > 0.0008) { + audioWarningDistanceSlider.value = d + } + audioWarningDistanceText.text = d + } + onChaperoneShowDashboardEnabledChanged: { + openDashboardToggle.checked = ChaperoneTabController.chaperoneShowDashboardEnabled + } + onChaperoneShowDashboardDistanceChanged: { + var d = ChaperoneTabController.chaperoneShowDashboardDistance.toFixed(2) + if (d <= openDashboardDistanceSlider.to && Math.abs(openDashboardDistanceSlider.value - d) > 0.0008) { + openDashboardDistanceSlider.value = d + } + openDashboardDistanceText.text = d + } + onChaperoneVelocityModifierEnabledChanged: { + velModifierToggle.checked = ChaperoneTabController.chaperoneVelocityModifierEnabled + } + onChaperoneVelocityModifierChanged: { + var d = ChaperoneTabController.chaperoneVelocityModifier.toFixed(2) + if (d <= velModifierSlider.to && Math.abs(velModifierSlider.value - d) > 0.0008) { + velModifierSlider.value = d + } + velModifierText.text = d + } + } + } +} diff --git a/bin/win64/res/qml/MyResources.qml b/bin/win64/res/qml/MyResources.qml index 2bc50e9..ee5aaba 100644 --- a/bin/win64/res/qml/MyResources.qml +++ b/bin/win64/res/qml/MyResources.qml @@ -5,19 +5,9 @@ import matzman666.advsettings 1.0 QtObject { function playActivationSound() { - /*if (!OverlayController.soundDisabled()) { - activationSound.play() - }*/ + OverlayController.playActivationSound() } function playFocusChangedSound() { - /*if (!OverlayController.soundDisabled()) { - focusChangedSound.play() - }*/ - } - /*property SoundEffect activationSound: SoundEffect { - source: vrRuntimePath + "content/panorama/sounds/activation.wav" - } - property SoundEffect focusChangedSound: SoundEffect { - source: vrRuntimePath + "content/panorama/sounds/focus_change.wav" - }*/ + OverlayController.playFocusChangedSound() + } } diff --git a/bin/win64/res/qml/RootPage.qml b/bin/win64/res/qml/RootPage.qml index 2c6d323..37d8e77 100644 --- a/bin/win64/res/qml/RootPage.qml +++ b/bin/win64/res/qml/RootPage.qml @@ -144,7 +144,7 @@ MyStackViewPage { Layout.fillHeight: true Layout.fillWidth: true Layout.leftMargin: 80 - spacing: 18 + spacing: 9 RowLayout { spacing: 18 diff --git a/bin/win64/res/qml/mainwidget.qml b/bin/win64/res/qml/mainwidget.qml index 867db87..01fd8f0 100644 --- a/bin/win64/res/qml/mainwidget.qml +++ b/bin/win64/res/qml/mainwidget.qml @@ -23,6 +23,11 @@ Rectangle { visible: false } + property ChaperoneWarningsPage chaperoneWarningsPage: ChaperoneWarningsPage { + stackView: mainView + visible: false + } + property PlayspacePage playspacePage: PlayspacePage { stackView: mainView visible: false diff --git a/bin/win64/res/sounds/alarm01.wav b/bin/win64/res/sounds/alarm01.wav new file mode 100644 index 0000000..d96fb23 Binary files /dev/null and b/bin/win64/res/sounds/alarm01.wav differ diff --git a/installer/uninstallFiles.list b/installer/uninstallFiles.list index fff91b4..d938687 100644 --- a/installer/uninstallFiles.list +++ b/installer/uninstallFiles.list @@ -389,6 +389,7 @@ Delete $INSTDIR\qtdata\Universal\Tumbler.qml Delete $INSTDIR\res\qml\AudioPage.qml Delete $INSTDIR\res\qml\backarrow.svg Delete $INSTDIR\res\qml\ChaperonePage.qml +Delete $INSTDIR\res\qml\ChaperoneWarningsPage.qml Delete $INSTDIR\res\qml\FixFloorPage.qml Delete $INSTDIR\res\qml\mainwidget.qml Delete $INSTDIR\res\qml\mic_off.svg @@ -419,11 +420,13 @@ Delete $INSTDIR\res\qml\SteamVRPage.qml Delete $INSTDIR\res\qml\UtilitiesPage.qml Delete $INSTDIR\res\qml\AccessibilityPage.qml Delete $INSTDIR\res\thumbicon.png +Delete $INSTDIR\res\sounds\alarm01.wav Delete $INSTDIR\restartvrserver.bat Delete $INSTDIR\startdesktopmode.bat Delete $INSTDIR\Uninstall.exe Delete $INSTDIR\vcredist_x64.exe RMdir $INSTDIR\res\qml +RMdir $INSTDIR\res\sounds RMdir $INSTDIR\res RMdir $INSTDIR\qtdata\Universal RMdir $INSTDIR\qtdata\translations diff --git a/src/main.cpp b/src/main.cpp index 435da62..646d59f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -95,6 +95,21 @@ void removeManifest() { } +class MyQApplication : public QApplication { +public: + using QApplication::QApplication; + + virtual bool notify(QObject * receiver, QEvent * event) override { + try { + return QApplication::notify(receiver, event); + } catch (std::exception& e) { + LOG(ERROR) << "Exception thrown from an event handler: " << e.what(); + } + return false; + } +}; + + int main(int argc, char *argv[]) { bool desktopMode = false; @@ -149,7 +164,7 @@ int main(int argc, char *argv[]) { } try { - QApplication a(argc, argv); + MyQApplication a(argc, argv); a.setOrganizationName("matzman666"); a.setApplicationName("OpenVRAdvancedSettings"); a.setApplicationDisplayName(advsettings::OverlayController::applicationName); @@ -174,7 +189,7 @@ int main(int argc, char *argv[]) { } conf.setRemainingToDefault(); el::Loggers::reconfigureAllLoggers(conf); - LOG(INFO) << "Application started"; + LOG(INFO) << "Application started (Version " << advsettings::OverlayController::applicationVersionString << ")"; LOG(INFO) << "Log Config: " << QDir::toNativeSeparators(logconfigfile).toStdString(); if (!logFilePath.isEmpty()) { LOG(INFO) << "Log File: " << logFilePath; diff --git a/src/overlaycontroller.cpp b/src/overlaycontroller.cpp index 6f9def5..3b09548 100644 --- a/src/overlaycontroller.cpp +++ b/src/overlaycontroller.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -49,6 +48,33 @@ void OverlayController::Init(QQmlEngine* qmlEngine) { m_runtimePathUrl = QUrl::fromLocalFile(vr::VR_RuntimePath()); LOG(INFO) << "VR Runtime Path: " << m_runtimePathUrl.toLocalFile(); + QString activationSoundFile = m_runtimePathUrl.toLocalFile().append("/content/panorama/sounds/activation.wav"); + QFileInfo activationSoundFileInfo(activationSoundFile); + if (activationSoundFileInfo.exists() && activationSoundFileInfo.isFile()) { + activationSoundEffect.setSource(QUrl::fromLocalFile(activationSoundFile)); + activationSoundEffect.setVolume(1.0); + } else { + LOG(ERROR) << "Could not find activation sound file " << activationSoundFile; + } + + QString focusChangedSoundFile = m_runtimePathUrl.toLocalFile().append("/content/panorama/sounds/focus_change.wav"); + QFileInfo focusChangedSoundFileInfo(focusChangedSoundFile); + if (focusChangedSoundFileInfo.exists() && focusChangedSoundFileInfo.isFile()) { + focusChangedSoundEffect.setSource(QUrl::fromLocalFile(focusChangedSoundFile)); + focusChangedSoundEffect.setVolume(1.0); + } else { + LOG(ERROR) << "Could not find focus changed sound file " << focusChangedSoundFile; + } + + QString alarm01SoundFile = QApplication::applicationDirPath().append("/res/sounds/alarm01.wav"); + QFileInfo alarm01SoundFileInfo(alarm01SoundFile); + if (alarm01SoundFileInfo.exists() && alarm01SoundFileInfo.isFile()) { + alarm01SoundEffect.setSource(QUrl::fromLocalFile(alarm01SoundFile)); + alarm01SoundEffect.setVolume(1.0); + } else { + LOG(ERROR) << "Could not find alarm01 sound file " << alarm01SoundFile; + } + // Check whether OpenVR is too outdated if (!vr::VR_IsInterfaceVersionValid(vr::IVRSystem_Version)) { QMessageBox::critical(nullptr, "OpenVR Advanced Settings Overlay", "OpenVR version is too outdated. Please update OpenVR."); @@ -294,13 +320,21 @@ void OverlayController::renderOverlay() { } } +bool OverlayController::pollNextEvent(vr::VROverlayHandle_t ulOverlayHandle, vr::VREvent_t* pEvent) { + if (isDesktopMode()) { + return vr::VRSystem()->PollNextEvent(pEvent, sizeof(vr::VREvent_t)); + } else { + return vr::VROverlay()->PollNextOverlayEvent(ulOverlayHandle, pEvent, sizeof(vr::VREvent_t)); + } +} void OverlayController::OnTimeoutPumpEvents() { if (!vr::VRSystem()) return; vr::VREvent_t vrEvent; - while (vr::VROverlay()->PollNextOverlayEvent(m_ulOverlayHandle, &vrEvent, sizeof(vrEvent))) { + bool chaperoneDataAlreadyUpdated = false; + while (pollNextEvent(m_ulOverlayHandle, &vrEvent)) { switch (vrEvent.eventType) { case vr::VREvent_MouseMove: { QPoint ptNewMouse(vrEvent.data.mouse.x, vrEvent.data.mouse.y); @@ -349,6 +383,7 @@ void OverlayController::OnTimeoutPumpEvents() { LOG(INFO) << "Received quit request."; vr::VRSystem()->AcknowledgeQuit_Exiting(); // Let us buy some time just in case moveCenterTabController.reset(); + chaperoneTabController.shutdown(); Shutdown(); QApplication::exit(); return; @@ -373,16 +408,54 @@ void OverlayController::OnTimeoutPumpEvents() { emit keyBoardInputSignal(QString(keyboardBuffer), vrEvent.data.keyboard.uUserValue); } break; + + // Multiple ChaperoneUniverseHasChanged are often emitted at the same time (some with a little bit of delay) + // There is no sure way to recognize redundant events, we can only exclude redundant events during the same call of OnTimeoutPumpEvents() + case vr::VREvent_ChaperoneUniverseHasChanged: + case vr::VREvent_ChaperoneDataHasChanged: { + if (!chaperoneDataAlreadyUpdated) { + LOG(INFO) << "Re-loading chaperone data ..."; + m_chaperoneUtils.loadChaperoneData(); + LOG(INFO) << "Found " << m_chaperoneUtils.quadsCount() << " chaperone quads."; + if (m_chaperoneUtils.isChaperoneWellFormed()) { + LOG(INFO) << "Chaperone data seems to be well-formed."; + } else { + LOG(INFO) << "Chaperone data is NOT well-formed."; + } + chaperoneDataAlreadyUpdated = true; + } + } + break; } } vr::TrackedDevicePose_t devicePoses[vr::k_unMaxTrackedDeviceCount]; vr::VRSystem()->GetDeviceToAbsoluteTrackingPose(vr::TrackingUniverseStanding, 0.0f, devicePoses, vr::k_unMaxTrackedDeviceCount); + + // HMD/Controller Velocities + auto leftId = vr::VRSystem()->GetTrackedDeviceIndexForControllerRole(vr::TrackedControllerRole_LeftHand); + float leftSpeed = 0.0f; + if (leftId != vr::k_unTrackedDeviceIndexInvalid && devicePoses[leftId].bPoseIsValid && devicePoses[leftId].eTrackingResult == vr::TrackingResult_Running_OK) { + auto& vel = devicePoses[leftId].vVelocity.v; + leftSpeed = std::sqrt(vel[0] * vel[0] + vel[1] * vel[1] + vel[2] * vel[2]); + } + auto rightId = vr::VRSystem()->GetTrackedDeviceIndexForControllerRole(vr::TrackedControllerRole_RightHand); + auto rightSpeed = 0.0f; + if (rightId != vr::k_unTrackedDeviceIndexInvalid && devicePoses[rightId].bPoseIsValid && devicePoses[rightId].eTrackingResult == vr::TrackingResult_Running_OK) { + auto& vel = devicePoses[rightId].vVelocity.v; + rightSpeed = std::sqrt(vel[0] * vel[0] + vel[1] * vel[1] + vel[2] * vel[2]); + } + auto hmdSpeed = 0.0f; + if (devicePoses[vr::k_unTrackedDeviceIndex_Hmd].bPoseIsValid && devicePoses[vr::k_unTrackedDeviceIndex_Hmd].eTrackingResult == vr::TrackingResult_Running_OK) { + auto& vel = devicePoses[vr::k_unTrackedDeviceIndex_Hmd].vVelocity.v; + hmdSpeed = std::sqrt(vel[0] * vel[0] + vel[1] * vel[1] + vel[2] * vel[2]); + } + fixFloorTabController.eventLoopTick(devicePoses); - statisticsTabController.eventLoopTick(devicePoses); + statisticsTabController.eventLoopTick(devicePoses, leftSpeed, rightSpeed); moveCenterTabController.eventLoopTick(vr::VRCompositor()->GetTrackingSpace()); steamVRTabController.eventLoopTick(); - chaperoneTabController.eventLoopTick(); + chaperoneTabController.eventLoopTick(devicePoses, leftSpeed, rightSpeed, hmdSpeed); settingsTabController.eventLoopTick(); reviveTabController.eventLoopTick(); audioTabController.eventLoopTick(); @@ -547,4 +620,37 @@ void OverlayController::showKeyboard(QString existingText, unsigned long userVal } +void OverlayController::playActivationSound() { + if (!noSound) { + activationSoundEffect.play(); + } +} + + +void OverlayController::playFocusChangedSound() { + if (!noSound) { + focusChangedSoundEffect.play(); + } +} + +void OverlayController::playAlarm01Sound(bool loop) { + if (!noSound && !alarm01SoundEffect.isPlaying()) { + if (loop) { + alarm01SoundEffect.setLoopCount(QSoundEffect::Infinite); + } else { + alarm01SoundEffect.setLoopCount(1); + } + alarm01SoundEffect.play(); + } +} + +void OverlayController::setAlarm01SoundVolume(float vol) { + alarm01SoundEffect.setVolume(vol); +} + +void OverlayController::cancelAlarm01Sound() { + alarm01SoundEffect.stop(); +} + + } // namespace advconfig diff --git a/src/overlaycontroller.h b/src/overlaycontroller.h index 3323081..793a7d7 100644 --- a/src/overlaycontroller.h +++ b/src/overlaycontroller.h @@ -19,8 +19,10 @@ #include #include #include +#include #include #include "logging.h" +#include "utils/ChaperoneUtils.h" #include "tabcontrollers/SteamVRTabController.h" #include "tabcontrollers/ChaperoneTabController.h" @@ -45,7 +47,7 @@ class OverlayController : public QObject { public: static constexpr const char* applicationKey = "matzman666.AdvancedSettings"; static constexpr const char* applicationName = "Advanced Settings"; - static constexpr const char* applicationVersionString = "v2.4.1"; + static constexpr const char* applicationVersionString = "v2.5"; private: vr::VROverlayHandle_t m_ulOverlayHandle = vr::k_ulOverlayHandleInvalid; @@ -69,6 +71,12 @@ private: QUrl m_runtimePathUrl; + utils::ChaperoneUtils m_chaperoneUtils; + + QSoundEffect activationSoundEffect; + QSoundEffect focusChangedSoundEffect; + QSoundEffect alarm01SoundEffect; + public: // I know it's an ugly hack to make them public to enable external access, but I am too lazy to implement getters. SteamVRTabController steamVRTabController; ChaperoneTabController chaperoneTabController; @@ -103,6 +111,8 @@ public: bool isDesktopMode() { return desktopMode; }; + utils::ChaperoneUtils& chaperoneUtils() noexcept { return m_chaperoneUtils; } + Q_INVOKABLE QString getVersionString(); Q_INVOKABLE QUrl getVRRuntimePathUrl(); @@ -111,6 +121,8 @@ public: const vr::VROverlayHandle_t& overlayHandle(); const vr::VROverlayHandle_t& overlayThumbnailHandle(); + bool pollNextEvent(vr::VROverlayHandle_t ulOverlayHandle, vr::VREvent_t* pEvent); + public slots: void renderOverlay(); void OnRenderRequest(); @@ -118,6 +130,12 @@ public slots: void showKeyboard(QString existingText, unsigned long userValue = 0); + void playActivationSound(); + void playFocusChangedSound(); + void playAlarm01Sound(bool loop = false); + void setAlarm01SoundVolume(float vol); + void cancelAlarm01Sound(); + signals: void keyBoardInputSignal(QString input, unsigned long userValue = 0); diff --git a/src/tabcontrollers/ChaperoneTabController.cpp b/src/tabcontrollers/ChaperoneTabController.cpp index 8da6eeb..9895d86 100644 --- a/src/tabcontrollers/ChaperoneTabController.cpp +++ b/src/tabcontrollers/ChaperoneTabController.cpp @@ -8,8 +8,26 @@ namespace advsettings { void ChaperoneTabController::initStage1() { m_height = getBoundsMaxY(); + + auto settings = OverlayController::appSettings(); + settings->beginGroup("chaperoneSettings"); + m_enableChaperoneSwitchToBeginner = settings->value("chaperoneSwitchToBeginnerEnabled", false).toBool(); + m_chaperoneSwitchToBeginnerDistance = settings->value("chaperoneSwitchToBeginnerDistance", 0.5f).toFloat(); + m_enableChaperoneHapticFeedback = settings->value("chaperoneHapticFeedbackEnabled", false).toBool(); + m_chaperoneHapticFeedbackDistance = settings->value("chaperoneHapticFeedbackDistance", 0.5f).toFloat(); + m_enableChaperoneAlarmSound = settings->value("chaperoneAlarmSoundEnabled", false).toBool(); + m_chaperoneAlarmSoundLooping = settings->value("chaperoneAlarmSoundLooping", true).toBool(); + m_chaperoneAlarmSoundAdjustVolume = settings->value("chaperoneAlarmSoundAdjustVolume", false).toBool(); + m_chaperoneAlarmSoundDistance = settings->value("chaperoneAlarmSoundDistance", 0.5f).toFloat(); + m_enableChaperoneShowDashboard = settings->value("chaperoneShowDashboardEnabled", false).toBool(); + m_chaperoneShowDashboardDistance = settings->value("chaperoneShowDashboardDistance", 0.5f).toFloat(); + m_enableChaperoneVelocityModifier = settings->value("chaperoneVelocityModifierEnabled", false).toBool(); + m_chaperoneVelocityModifier = settings->value("chaperoneVelocityModifier", 0.3f).toFloat(); + m_chaperoneVelocityModifierCurrent = 1.0f; + settings->endGroup(); + reloadChaperoneProfiles(); - eventLoopTick(); + eventLoopTick(nullptr, 0.0f, 0.0f, 0.0f); } @@ -20,6 +38,14 @@ void ChaperoneTabController::initStage2(OverlayController * parent, QQuickWindow +ChaperoneTabController::~ChaperoneTabController() { + m_chaperoneHapticFeedbackActive = false; + if (m_chaperoneHapticFeedbackThread.joinable()) { + m_chaperoneHapticFeedbackThread.join(); + } +} + + void ChaperoneTabController::reloadChaperoneProfiles() { chaperoneProfiles.clear(); auto settings = OverlayController::appSettings(); @@ -94,6 +120,21 @@ void ChaperoneTabController::reloadChaperoneProfiles() { if (entry.includesForceBounds) { entry.forceBounds = settings->value("forceBounds", false).toBool(); } + entry.includesProximityWarningSettings = settings->value("includesProximityWarningSettings", false).toBool(); + if (entry.includesProximityWarningSettings){ + entry.enableChaperoneSwitchToBeginner = settings->value("chaperoneSwitchToBeginnerEnabled", false).toBool(); + entry.chaperoneSwitchToBeginnerDistance = settings->value("chaperoneSwitchToBeginnerDistance", 0.5f).toFloat(); + entry.enableChaperoneHapticFeedback = settings->value("chaperoneHapticFeedbackEnabled", false).toBool(); + entry.chaperoneHapticFeedbackDistance = settings->value("chaperoneHapticFeedbackDistance", 0.5f).toFloat(); + entry.enableChaperoneAlarmSound = settings->value("chaperoneAlarmSoundEnabled", false).toBool(); + entry.chaperoneAlarmSoundLooping = settings->value("chaperoneAlarmSoundLooping", true).toBool(); + entry.chaperoneAlarmSoundAdjustVolume = settings->value("chaperoneAlarmSoundAdjustVolume", false).toBool(); + entry.chaperoneAlarmSoundDistance = settings->value("chaperoneAlarmSoundDistance", 0.5f).toFloat(); + entry.enableChaperoneShowDashboard = settings->value("chaperoneShowDashboardEnabled", false).toBool(); + entry.chaperoneShowDashboardDistance = settings->value("chaperoneShowDashboardDistance", 0.5f).toFloat(); + entry.enableChaperoneVelocityModifier = settings->value("chaperoneVelocityModifierEnabled", false).toBool(); + entry.chaperoneVelocityModifier = settings->value("chaperoneVelocityModifier", 0.3f).toFloat(); + } } settings->endArray(); settings->endGroup(); @@ -171,15 +212,191 @@ void ChaperoneTabController::saveChaperoneProfiles() { if (p.includesForceBounds) { settings->setValue("forceBounds", p.forceBounds); } + settings->setValue("includesProximityWarningSettings", p.includesProximityWarningSettings); + if (p.includesProximityWarningSettings) { + settings->setValue("chaperoneSwitchToBeginnerEnabled", p.enableChaperoneSwitchToBeginner); + settings->setValue("chaperoneSwitchToBeginnerDistance", p.chaperoneSwitchToBeginnerDistance); + settings->setValue("chaperoneHapticFeedbackEnabled", p.enableChaperoneHapticFeedback); + settings->setValue("chaperoneHapticFeedbackDistance", p.chaperoneHapticFeedbackDistance); + settings->setValue("chaperoneAlarmSoundEnabled", p.enableChaperoneAlarmSound); + settings->setValue("chaperoneAlarmSoundLooping", p.chaperoneAlarmSoundLooping); + settings->setValue("chaperoneAlarmSoundAdjustVolume", p.chaperoneAlarmSoundAdjustVolume); + settings->setValue("chaperoneAlarmSoundDistance", p.chaperoneAlarmSoundDistance); + settings->setValue("chaperoneShowDashboardEnabled", p.enableChaperoneShowDashboard); + settings->setValue("chaperoneShowDashboardDistance", p.chaperoneShowDashboardDistance); + settings->setValue("chaperoneVelocityModifierEnabled", p.enableChaperoneVelocityModifier); + settings->setValue("chaperoneVelocityModifier", p.chaperoneVelocityModifier); + } i++; } settings->endArray(); settings->endGroup(); - } -void ChaperoneTabController::eventLoopTick() { +void ChaperoneTabController::handleChaperoneWarnings(float distance) { + vr::VRControllerState_t hmdState; + vr::VRSystem()->GetControllerState(vr::k_unTrackedDeviceIndex_Hmd, &hmdState, sizeof(vr::VRControllerState_t)); + + // Switch to Beginner Mode + if (m_enableChaperoneSwitchToBeginner) { + float activationDistance = m_chaperoneSwitchToBeginnerDistance * m_chaperoneVelocityModifierCurrent; + if (distance <= activationDistance && (hmdState.ulButtonPressed & vr::ButtonMaskFromId(vr::k_EButton_ProximitySensor)) && !m_chaperoneSwitchToBeginnerActive) { + vr::EVRSettingsError vrSettingsError; + m_chaperoneSwitchToBeginnerLastStyle = vr::VRSettings()->GetInt32(vr::k_pch_CollisionBounds_Section, vr::k_pch_CollisionBounds_Style_Int32, &vrSettingsError); + if (vrSettingsError != vr::VRSettingsError_None) { + LOG(WARNING) << "Could not read \"" << vr::k_pch_CollisionBounds_Style_Int32 << "\" setting: " << vr::VRSettings()->GetSettingsErrorNameFromEnum(vrSettingsError); + } else { + vr::VRSettings()->SetInt32(vr::k_pch_CollisionBounds_Section, vr::k_pch_CollisionBounds_Style_Int32, vr::COLLISION_BOUNDS_STYLE_BEGINNER, &vrSettingsError); + if (vrSettingsError != vr::VRSettingsError_None) { + LOG(WARNING) << "Could not set \"" << vr::k_pch_CollisionBounds_Style_Int32 << "\" setting: " << vr::VRSettings()->GetSettingsErrorNameFromEnum(vrSettingsError); + } else { + vr::VRSettings()->Sync(true); + m_chaperoneSwitchToBeginnerActive = true; + } + } + } else if ((distance > activationDistance || !(hmdState.ulButtonPressed & vr::ButtonMaskFromId(vr::k_EButton_ProximitySensor))) && m_chaperoneSwitchToBeginnerActive) { + vr::EVRSettingsError vrSettingsError; + vr::VRSettings()->SetInt32(vr::k_pch_CollisionBounds_Section, vr::k_pch_CollisionBounds_Style_Int32, m_chaperoneSwitchToBeginnerLastStyle, &vrSettingsError); + if (vrSettingsError != vr::VRSettingsError_None) { + LOG(WARNING) << "Could not set \"" << vr::k_pch_CollisionBounds_Style_Int32 << "\" setting: " << vr::VRSettings()->GetSettingsErrorNameFromEnum(vrSettingsError); + } else { + vr::VRSettings()->Sync(true); + m_chaperoneSwitchToBeginnerActive = false; + } + } + } + + // Haptic Feedback + if (m_enableChaperoneHapticFeedback) { + float activationDistance = m_chaperoneHapticFeedbackDistance * m_chaperoneVelocityModifierCurrent; + if (distance <= activationDistance && (hmdState.ulButtonPressed & vr::ButtonMaskFromId(vr::k_EButton_ProximitySensor))) { + if (!m_chaperoneHapticFeedbackActive) { + if (m_chaperoneHapticFeedbackThread.joinable()) { + m_chaperoneHapticFeedbackActive = false; + m_chaperoneHapticFeedbackThread.join(); + } + m_chaperoneHapticFeedbackActive = true; + m_chaperoneHapticFeedbackThread = std::thread([](ChaperoneTabController* _this) { + auto leftIndex = vr::VRSystem()->GetTrackedDeviceIndexForControllerRole(vr::TrackedControllerRole_LeftHand); + auto rightIndex = vr::VRSystem()->GetTrackedDeviceIndexForControllerRole(vr::TrackedControllerRole_RightHand); + while (_this->m_chaperoneHapticFeedbackActive) { + if (leftIndex != vr::k_unTrackedDeviceIndexInvalid) { + vr::VRSystem()->TriggerHapticPulse(leftIndex, 0, 2000); + } + if (rightIndex != vr::k_unTrackedDeviceIndexInvalid) { + vr::VRSystem()->TriggerHapticPulse(rightIndex, 0, 2000); + } + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + } + }, this); + } + } else if ((distance > activationDistance || !(hmdState.ulButtonPressed & vr::ButtonMaskFromId(vr::k_EButton_ProximitySensor))) && m_chaperoneHapticFeedbackActive) { + m_chaperoneHapticFeedbackActive = false; + } + } + + // Alarm Sound + if (m_enableChaperoneAlarmSound) { + float activationDistance = m_chaperoneAlarmSoundDistance * m_chaperoneVelocityModifierCurrent; + if (distance <= activationDistance && (hmdState.ulButtonPressed & vr::ButtonMaskFromId(vr::k_EButton_ProximitySensor))) { + if (!m_chaperoneAlarmSoundActive) { + parent->playAlarm01Sound(m_chaperoneAlarmSoundLooping); + m_chaperoneAlarmSoundActive = true; + } + if (m_chaperoneAlarmSoundLooping && m_chaperoneAlarmSoundAdjustVolume) { + float vol = 1.1f - distance / activationDistance; + if (vol > 1.0f) { + vol = 1.0f; + } + parent->setAlarm01SoundVolume(vol); + } else { + parent->setAlarm01SoundVolume(1.0f); + } + } else if ((distance > activationDistance || !(hmdState.ulButtonPressed & vr::ButtonMaskFromId(vr::k_EButton_ProximitySensor))) && m_chaperoneAlarmSoundActive) { + parent->cancelAlarm01Sound(); + m_chaperoneAlarmSoundActive = false; + } + } + + // Show Dashboard + if (m_enableChaperoneShowDashboard) { + float activationDistance = m_chaperoneShowDashboardDistance * m_chaperoneVelocityModifierCurrent; + if (distance <= activationDistance && !m_chaperoneShowDashboardActive) { + if (!vr::VROverlay()->IsDashboardVisible()) { + vr::VROverlay()->ShowDashboard(OverlayController::applicationKey); + } + m_chaperoneShowDashboardActive = true; + } else if (distance > activationDistance && m_chaperoneShowDashboardActive) { + m_chaperoneShowDashboardActive = false; + } + } +} + + +void ChaperoneTabController::eventLoopTick(vr::TrackedDevicePose_t* devicePoses, float leftSpeed, float rightSpeed, float hmdSpeed) { + m_chaperoneVelocityModifierCurrent = 1.0f; + if (m_enableChaperoneVelocityModifier) { + float mod = m_chaperoneVelocityModifier * std::max({ leftSpeed, rightSpeed, hmdSpeed }); + if (mod > 0.02) { + m_chaperoneVelocityModifierCurrent += mod; + } + } + //LOG(INFO) << "m_chaperoneVelocityModifierCurrent: " << m_chaperoneVelocityModifierCurrent; + float newFadeDistance = m_fadeDistance * m_chaperoneVelocityModifierCurrent; + if (m_fadeDistanceModified != newFadeDistance) { + m_fadeDistanceModified = newFadeDistance; + vr::VRSettings()->SetFloat(vr::k_pch_CollisionBounds_Section, vr::k_pch_CollisionBounds_FadeDistance_Float, m_fadeDistanceModified); + vr::VRSettings()->Sync(); + } + + if (devicePoses) { + std::lock_guard lock(parent->chaperoneUtils().mutex()); + auto minDistance = NAN; + auto& poseHmd = devicePoses[vr::k_unTrackedDeviceIndex_Hmd]; + if (poseHmd.bPoseIsValid && poseHmd.bDeviceIsConnected && poseHmd.eTrackingResult == vr::TrackingResult_Running_OK) { + auto distanceHmd = parent->chaperoneUtils().getDistanceToChaperone({ + poseHmd.mDeviceToAbsoluteTracking.m[0][3], + poseHmd.mDeviceToAbsoluteTracking.m[1][3], + poseHmd.mDeviceToAbsoluteTracking.m[2][3] + }); + if (!isnan(distanceHmd)) { + minDistance = distanceHmd; + } + } + auto leftIndex = vr::VRSystem()->GetTrackedDeviceIndexForControllerRole(vr::TrackedControllerRole_LeftHand); + if (leftIndex != vr::k_unTrackedDeviceIndexInvalid) { + auto& poseLeft = devicePoses[leftIndex]; + if (poseLeft.bPoseIsValid && poseLeft.bDeviceIsConnected && poseLeft.eTrackingResult == vr::TrackingResult_Running_OK) { + auto distanceLeft = parent->chaperoneUtils().getDistanceToChaperone({ + poseLeft.mDeviceToAbsoluteTracking.m[0][3], + poseLeft.mDeviceToAbsoluteTracking.m[1][3], + poseLeft.mDeviceToAbsoluteTracking.m[2][3] + }); + if (!isnan(distanceLeft) && (isnan(minDistance) || distanceLeft < minDistance)) { + minDistance = distanceLeft; + } + } + } + auto rightIndex = vr::VRSystem()->GetTrackedDeviceIndexForControllerRole(vr::TrackedControllerRole_RightHand); + if (rightIndex != vr::k_unTrackedDeviceIndexInvalid) { + auto& poseRight = devicePoses[rightIndex]; + if (poseRight.bPoseIsValid && poseRight.bDeviceIsConnected && poseRight.eTrackingResult == vr::TrackingResult_Running_OK) { + auto distanceRight = parent->chaperoneUtils().getDistanceToChaperone({ + poseRight.mDeviceToAbsoluteTracking.m[0][3], + poseRight.mDeviceToAbsoluteTracking.m[1][3], + poseRight.mDeviceToAbsoluteTracking.m[2][3] + }); + if (!isnan(distanceRight) && (isnan(minDistance) || distanceRight < minDistance)) { + minDistance = distanceRight; + } + } + } + if (!isnan(minDistance)) { + handleChaperoneWarnings(minDistance); + } + } + if (settingsUpdateCounter >= 50) { vr::EVRSettingsError vrSettingsError; float vis = vr::VRSettings()->GetInt32(vr::k_pch_CollisionBounds_Section, vr::k_pch_CollisionBounds_ColorGammaA_Int32, &vrSettingsError); @@ -187,11 +404,13 @@ void ChaperoneTabController::eventLoopTick() { LOG(WARNING) << "Could not read \"" << vr::k_pch_CollisionBounds_ColorGammaA_Int32 << "\" setting: " << vr::VRSettings()->GetSettingsErrorNameFromEnum(vrSettingsError); } setBoundsVisibility(vis/255.0f); - auto fd = vr::VRSettings()->GetFloat(vr::k_pch_CollisionBounds_Section, vr::k_pch_CollisionBounds_FadeDistance_Float, &vrSettingsError); - if (vrSettingsError != vr::VRSettingsError_None) { - LOG(WARNING) << "Could not read \"" << vr::k_pch_CollisionBounds_FadeDistance_Float << "\" setting: " << vr::VRSettings()->GetSettingsErrorNameFromEnum(vrSettingsError); + if (m_chaperoneVelocityModifierCurrent == 1.0f) { + auto fd = vr::VRSettings()->GetFloat(vr::k_pch_CollisionBounds_Section, vr::k_pch_CollisionBounds_FadeDistance_Float, &vrSettingsError); + if (vrSettingsError != vr::VRSettingsError_None) { + LOG(WARNING) << "Could not read \"" << vr::k_pch_CollisionBounds_FadeDistance_Float << "\" setting: " << vr::VRSettings()->GetSettingsErrorNameFromEnum(vrSettingsError); + } + setFadeDistance(fd); } - setFadeDistance(fd); auto cm = vr::VRSettings()->GetBool(vr::k_pch_CollisionBounds_Section, vr::k_pch_CollisionBounds_CenterMarkerOn_Bool, &vrSettingsError); if (vrSettingsError != vr::VRSettingsError_None) { LOG(WARNING) << "Could not read \"" << vr::k_pch_CollisionBounds_CenterMarkerOn_Bool << "\" setting: " << vr::VRSettings()->GetSettingsErrorNameFromEnum(vrSettingsError); @@ -319,6 +538,62 @@ bool ChaperoneTabController::forceBounds() const { return m_forceBounds; } + +bool ChaperoneTabController::isChaperoneSwitchToBeginnerEnabled() const { + return m_enableChaperoneSwitchToBeginner; +} + +float ChaperoneTabController::chaperoneSwitchToBeginnerDistance() const { + return m_chaperoneSwitchToBeginnerDistance; +} + + +bool ChaperoneTabController::isChaperoneHapticFeedbackEnabled() const { + return m_enableChaperoneHapticFeedback; +} + +float ChaperoneTabController::chaperoneHapticFeedbackDistance() const { + return m_chaperoneHapticFeedbackDistance; +} + + +bool ChaperoneTabController::isChaperoneAlarmSoundEnabled() const { + return m_enableChaperoneAlarmSound; +} + +bool ChaperoneTabController::isChaperoneAlarmSoundLooping() const { + return m_chaperoneAlarmSoundLooping; +} + +bool ChaperoneTabController::isChaperoneAlarmSoundAdjustVolume() const { + return m_chaperoneAlarmSoundAdjustVolume; +} + +float ChaperoneTabController::chaperoneAlarmSoundDistance() const { + return m_chaperoneAlarmSoundDistance; +} + + +bool ChaperoneTabController::isChaperoneShowDashboardEnabled() const { + return m_enableChaperoneShowDashboard; +} + +float ChaperoneTabController::chaperoneShowDashboardDistance() const { + return m_chaperoneShowDashboardDistance; +} + + +bool ChaperoneTabController::isChaperoneVelocityModifierEnabled() const { + return m_enableChaperoneVelocityModifier; +} + +float ChaperoneTabController::chaperoneVelocityModifier() const { + return m_chaperoneVelocityModifier; +} + + + + Q_INVOKABLE unsigned ChaperoneTabController::getChaperoneProfileCount() { return (unsigned)chaperoneProfiles.size(); } @@ -341,6 +616,207 @@ void ChaperoneTabController::setForceBounds(bool value, bool notify) { } } + +void ChaperoneTabController::setChaperoneSwitchToBeginnerEnabled(bool value, bool notify) { + if (m_enableChaperoneSwitchToBeginner != value) { + if (!value && m_chaperoneSwitchToBeginnerActive) { + vr::EVRSettingsError vrSettingsError; + vr::VRSettings()->SetInt32(vr::k_pch_CollisionBounds_Section, vr::k_pch_CollisionBounds_Style_Int32, m_chaperoneSwitchToBeginnerLastStyle, &vrSettingsError); + if (vrSettingsError != vr::VRSettingsError_None) { + LOG(WARNING) << "Could not set \"" << vr::k_pch_CollisionBounds_Style_Int32 << "\" setting: " << vr::VRSettings()->GetSettingsErrorNameFromEnum(vrSettingsError); + } else { + vr::VRSettings()->Sync(true); + } + } + m_enableChaperoneSwitchToBeginner = value; + m_chaperoneSwitchToBeginnerActive = false; + auto settings = OverlayController::appSettings(); + settings->beginGroup("chaperoneSettings"); + settings->setValue("chaperoneSwitchToBeginnerEnabled", m_enableChaperoneSwitchToBeginner); + settings->endGroup(); + settings->sync(); + if (notify) { + emit chaperoneSwitchToBeginnerEnabledChanged(m_enableChaperoneSwitchToBeginner); + } + } +} + +void ChaperoneTabController::setChaperoneSwitchToBeginnerDistance(float value, bool notify) { + if (m_chaperoneSwitchToBeginnerDistance != value) { + m_chaperoneSwitchToBeginnerDistance = value; + auto settings = OverlayController::appSettings(); + settings->beginGroup("chaperoneSettings"); + settings->setValue("chaperoneSwitchToBeginnerDistance", m_chaperoneSwitchToBeginnerDistance); + settings->endGroup(); + settings->sync(); + if (notify) { + emit chaperoneSwitchToBeginnerDistanceChanged(m_chaperoneSwitchToBeginnerDistance); + } + } +} + + +void ChaperoneTabController::setChaperoneHapticFeedbackEnabled(bool value, bool notify) { + if (m_enableChaperoneHapticFeedback != value) { + m_chaperoneHapticFeedbackActive = false; + if (m_chaperoneHapticFeedbackThread.joinable()) { + m_chaperoneHapticFeedbackThread.join(); + } + m_enableChaperoneHapticFeedback = value; + auto settings = OverlayController::appSettings(); + settings->beginGroup("chaperoneSettings"); + settings->setValue("chaperoneHapticFeedbackEnabled", m_enableChaperoneHapticFeedback); + settings->endGroup(); + settings->sync(); + if (notify) { + emit chaperoneHapticFeedbackEnabledChanged(m_enableChaperoneHapticFeedback); + } + } +} + +void ChaperoneTabController::setChaperoneHapticFeedbackDistance(float value, bool notify) { + if (m_chaperoneHapticFeedbackDistance != value) { + m_chaperoneHapticFeedbackDistance = value; + auto settings = OverlayController::appSettings(); + settings->beginGroup("chaperoneSettings"); + settings->setValue("chaperoneHapticFeedbackDistance", m_chaperoneHapticFeedbackDistance); + settings->endGroup(); + settings->sync(); + if (notify) { + emit chaperoneHapticFeedbackDistanceChanged(m_chaperoneHapticFeedbackDistance); + } + } +} + + +void ChaperoneTabController::setChaperoneAlarmSoundEnabled(bool value, bool notify) { + if (m_enableChaperoneAlarmSound != value) { + if (!value && m_chaperoneAlarmSoundActive) { + parent->cancelAlarm01Sound(); + } + m_enableChaperoneAlarmSound = value; + m_chaperoneAlarmSoundActive = false; + auto settings = OverlayController::appSettings(); + settings->beginGroup("chaperoneSettings"); + settings->setValue("chaperoneAlarmSoundEnabled", m_enableChaperoneAlarmSound); + settings->endGroup(); + settings->sync(); + if (notify) { + emit chaperoneAlarmSoundEnabledChanged(m_enableChaperoneAlarmSound); + } + } +} + +void ChaperoneTabController::setChaperoneAlarmSoundLooping(bool value, bool notify) { + if (m_chaperoneAlarmSoundLooping != value) { + m_chaperoneAlarmSoundLooping = value; + if (m_enableChaperoneAlarmSound && m_chaperoneAlarmSoundActive) { + if (m_chaperoneAlarmSoundLooping) { + parent->playAlarm01Sound(m_chaperoneAlarmSoundLooping); + } else { + parent->cancelAlarm01Sound(); + } + } + auto settings = OverlayController::appSettings(); + settings->beginGroup("chaperoneSettings"); + settings->setValue("chaperoneAlarmSoundLooping", m_chaperoneAlarmSoundLooping); + settings->endGroup(); + settings->sync(); + if (notify) { + emit chaperoneAlarmSoundLoopingChanged(m_chaperoneAlarmSoundLooping); + } + } +} + +void ChaperoneTabController::setChaperoneAlarmSoundAdjustVolume(bool value, bool notify) { + if (m_chaperoneAlarmSoundAdjustVolume != value) { + m_chaperoneAlarmSoundAdjustVolume = value; + auto settings = OverlayController::appSettings(); + settings->beginGroup("chaperoneSettings"); + settings->setValue("chaperoneAlarmSoundAdjustVolume", m_chaperoneAlarmSoundAdjustVolume); + settings->endGroup(); + settings->sync(); + if (notify) { + emit chaperoneAlarmSoundAdjustVolumeChanged(m_chaperoneAlarmSoundAdjustVolume); + } + } +} + +void ChaperoneTabController::setChaperoneAlarmSoundDistance(float value, bool notify) { + if (m_chaperoneAlarmSoundDistance != value) { + m_chaperoneAlarmSoundDistance = value; + auto settings = OverlayController::appSettings(); + settings->beginGroup("chaperoneSettings"); + settings->setValue("chaperoneAlarmSoundDistance", m_chaperoneAlarmSoundDistance); + settings->endGroup(); + settings->sync(); + if (notify) { + emit chaperoneAlarmSoundDistanceChanged(m_chaperoneAlarmSoundDistance); + } + } +} + + +void ChaperoneTabController::setChaperoneShowDashboardEnabled(bool value, bool notify) { + if (m_enableChaperoneShowDashboard != value) { + m_enableChaperoneShowDashboard = value; + m_chaperoneShowDashboardActive = false; + auto settings = OverlayController::appSettings(); + settings->beginGroup("chaperoneSettings"); + settings->setValue("chaperoneShowDashboardEnabled", m_enableChaperoneShowDashboard); + settings->endGroup(); + settings->sync(); + if (notify) { + emit chaperoneShowDashboardEnabledChanged(m_enableChaperoneShowDashboard); + } + } +} + +void ChaperoneTabController::setChaperoneShowDashboardDistance(float value, bool notify) { + if (m_chaperoneShowDashboardDistance != value) { + m_chaperoneShowDashboardDistance = value; + auto settings = OverlayController::appSettings(); + settings->beginGroup("chaperoneSettings"); + settings->setValue("chaperoneShowDashboardDistance", m_chaperoneShowDashboardDistance); + settings->endGroup(); + settings->sync(); + if (notify) { + emit chaperoneShowDashboardDistanceChanged(m_chaperoneShowDashboardDistance); + } + } +} + + +void ChaperoneTabController::setChaperoneVelocityModifierEnabled(bool value, bool notify) { + if (m_enableChaperoneVelocityModifier != value) { + m_enableChaperoneVelocityModifier = value; + auto settings = OverlayController::appSettings(); + settings->beginGroup("chaperoneSettings"); + settings->setValue("chaperoneVelocityModifierEnabled", m_enableChaperoneVelocityModifier); + settings->endGroup(); + settings->sync(); + if (notify) { + emit chaperoneVelocityModifierEnabledChanged(m_enableChaperoneVelocityModifier); + } + } +} + +void ChaperoneTabController::setChaperoneVelocityModifier(float value, bool notify) { + if (m_chaperoneVelocityModifier != value) { + m_chaperoneVelocityModifier = value; + auto settings = OverlayController::appSettings(); + settings->beginGroup("chaperoneSettings"); + settings->setValue("chaperoneVelocityModifier", m_chaperoneVelocityModifier); + settings->endGroup(); + settings->sync(); + if (notify) { + emit chaperoneVelocityModifierChanged(m_chaperoneVelocityModifier); + } + } +} + + + void ChaperoneTabController::flipOrientation() { parent->RotateUniverseCenter(vr::TrackingUniverseStanding, (float)M_PI); } @@ -351,7 +827,7 @@ void ChaperoneTabController::reloadFromDisk() { } void ChaperoneTabController::addChaperoneProfile(QString name, bool includeGeometry, bool includeVisbility, bool includeFadeDistance, bool includeCenterMarker, - bool includePlaySpaceMarker, bool includeFloorBounds, bool includeBoundsColor, bool includeChaperoneStyle, bool includeForceBounds) { + bool includePlaySpaceMarker, bool includeFloorBounds, bool includeBoundsColor, bool includeChaperoneStyle, bool includeForceBounds, bool includesProximityWarningSettings) { vr::EVRSettingsError vrSettingsError; ChaperoneProfile* profile = nullptr; for (auto& p : chaperoneProfiles) { @@ -426,6 +902,21 @@ void ChaperoneTabController::addChaperoneProfile(QString name, bool includeGeome if (includeForceBounds) { profile->forceBounds = m_forceBounds; } + profile->includesProximityWarningSettings = includesProximityWarningSettings; + if (includesProximityWarningSettings) { + profile->enableChaperoneSwitchToBeginner = m_enableChaperoneSwitchToBeginner; + profile->chaperoneSwitchToBeginnerDistance = m_chaperoneSwitchToBeginnerDistance; + profile->enableChaperoneHapticFeedback = m_enableChaperoneHapticFeedback; + profile->chaperoneHapticFeedbackDistance = m_chaperoneHapticFeedbackDistance; + profile->enableChaperoneAlarmSound = m_enableChaperoneAlarmSound; + profile->chaperoneAlarmSoundLooping = m_chaperoneAlarmSoundLooping; + profile->chaperoneAlarmSoundAdjustVolume = m_chaperoneAlarmSoundAdjustVolume; + profile->chaperoneAlarmSoundDistance = m_chaperoneAlarmSoundDistance; + profile->enableChaperoneShowDashboard = m_enableChaperoneShowDashboard; + profile->chaperoneShowDashboardDistance = m_chaperoneShowDashboardDistance; + profile->enableChaperoneVelocityModifier = m_enableChaperoneVelocityModifier; + profile->chaperoneVelocityModifier = m_chaperoneVelocityModifier; + } saveChaperoneProfiles(); OverlayController::appSettings()->sync(); emit chaperoneProfilesUpdated(); @@ -467,7 +958,21 @@ void ChaperoneTabController::applyChaperoneProfile(unsigned index) { if (profile.includesForceBounds) { setForceBounds(profile.forceBounds); } - vr::VRSettings()->Sync(); + if (profile.includesProximityWarningSettings) { + setChaperoneSwitchToBeginnerDistance(profile.chaperoneSwitchToBeginnerDistance); + setChaperoneSwitchToBeginnerEnabled(profile.enableChaperoneSwitchToBeginner); + setChaperoneHapticFeedbackDistance(profile.chaperoneHapticFeedbackDistance); + setChaperoneHapticFeedbackEnabled(profile.enableChaperoneHapticFeedback); + setChaperoneAlarmSoundLooping(profile.chaperoneAlarmSoundLooping); + setChaperoneAlarmSoundAdjustVolume(profile.chaperoneAlarmSoundAdjustVolume); + setChaperoneAlarmSoundDistance(profile.chaperoneAlarmSoundDistance); + setChaperoneAlarmSoundEnabled(profile.enableChaperoneAlarmSound); + setChaperoneShowDashboardDistance(profile.chaperoneShowDashboardDistance); + setChaperoneShowDashboardEnabled(profile.enableChaperoneShowDashboard); + setChaperoneVelocityModifier(profile.chaperoneVelocityModifier); + setChaperoneVelocityModifierEnabled(profile.enableChaperoneVelocityModifier); + } + vr::VRSettings()->Sync(true); updateHeight(getBoundsMaxY()); } } @@ -534,4 +1039,18 @@ void ChaperoneTabController::reset() { settingsUpdateCounter = 999; // Easiest way to get default values } + +void ChaperoneTabController::shutdown() { + if (m_enableChaperoneSwitchToBeginner && m_chaperoneSwitchToBeginnerActive) { + vr::EVRSettingsError vrSettingsError; + vr::VRSettings()->SetInt32(vr::k_pch_CollisionBounds_Section, vr::k_pch_CollisionBounds_Style_Int32, m_chaperoneSwitchToBeginnerLastStyle, &vrSettingsError); + if (vrSettingsError != vr::VRSettingsError_None) { + LOG(WARNING) << "Could not set \"" << vr::k_pch_CollisionBounds_Style_Int32 << "\" setting: " << vr::VRSettings()->GetSettingsErrorNameFromEnum(vrSettingsError); + } else { + vr::VRSettings()->Sync(true); + } + } +} + + } // namespace advconfig diff --git a/src/tabcontrollers/ChaperoneTabController.h b/src/tabcontrollers/ChaperoneTabController.h index 51347f0..d4d3293 100644 --- a/src/tabcontrollers/ChaperoneTabController.h +++ b/src/tabcontrollers/ChaperoneTabController.h @@ -3,6 +3,8 @@ #include #include +#include +#include #include class QQuickWindow; @@ -46,6 +48,20 @@ struct ChaperoneProfile { bool includesForceBounds = false; bool forceBounds = false; + + bool includesProximityWarningSettings = false; + bool enableChaperoneSwitchToBeginner = false; + float chaperoneSwitchToBeginnerDistance = 0.0f; + bool enableChaperoneHapticFeedback = false; + float chaperoneHapticFeedbackDistance = 0.0f; + bool enableChaperoneAlarmSound = false; + bool chaperoneAlarmSoundLooping = true; + bool chaperoneAlarmSoundAdjustVolume = false; + float chaperoneAlarmSoundDistance = 0.0f; + bool enableChaperoneShowDashboard = false; + float chaperoneShowDashboardDistance = 0.0f; + bool enableChaperoneVelocityModifier = false; + float chaperoneVelocityModifier = 0.0f; }; @@ -58,26 +74,71 @@ class ChaperoneTabController : public QObject { Q_PROPERTY(bool playSpaceMarker READ playSpaceMarker WRITE setPlaySpaceMarker NOTIFY playSpaceMarkerChanged) Q_PROPERTY(bool forceBounds READ forceBounds WRITE setForceBounds NOTIFY forceBoundsChanged) + Q_PROPERTY(bool chaperoneSwitchToBeginnerEnabled READ isChaperoneSwitchToBeginnerEnabled WRITE setChaperoneSwitchToBeginnerEnabled NOTIFY chaperoneSwitchToBeginnerEnabledChanged) + Q_PROPERTY(float chaperoneSwitchToBeginnerDistance READ chaperoneSwitchToBeginnerDistance WRITE setChaperoneSwitchToBeginnerDistance NOTIFY chaperoneSwitchToBeginnerDistanceChanged) + + Q_PROPERTY(bool chaperoneHapticFeedbackEnabled READ isChaperoneHapticFeedbackEnabled WRITE setChaperoneHapticFeedbackEnabled NOTIFY chaperoneHapticFeedbackEnabledChanged) + Q_PROPERTY(float chaperoneHapticFeedbackDistance READ chaperoneHapticFeedbackDistance WRITE setChaperoneHapticFeedbackDistance NOTIFY chaperoneHapticFeedbackDistanceChanged) + + Q_PROPERTY(bool chaperoneAlarmSoundEnabled READ isChaperoneAlarmSoundEnabled WRITE setChaperoneAlarmSoundEnabled NOTIFY chaperoneAlarmSoundEnabledChanged) + Q_PROPERTY(bool chaperoneAlarmSoundLooping READ isChaperoneAlarmSoundLooping WRITE setChaperoneAlarmSoundLooping NOTIFY chaperoneAlarmSoundLoopingChanged) + Q_PROPERTY(bool chaperoneAlarmSoundAdjustVolume READ isChaperoneAlarmSoundAdjustVolume WRITE setChaperoneAlarmSoundAdjustVolume NOTIFY chaperoneAlarmSoundAdjustVolumeChanged) + Q_PROPERTY(float chaperoneAlarmSoundDistance READ chaperoneAlarmSoundDistance WRITE setChaperoneAlarmSoundDistance NOTIFY chaperoneAlarmSoundDistanceChanged) + + Q_PROPERTY(bool chaperoneShowDashboardEnabled READ isChaperoneShowDashboardEnabled WRITE setChaperoneShowDashboardEnabled NOTIFY chaperoneShowDashboardEnabledChanged) + Q_PROPERTY(float chaperoneShowDashboardDistance READ chaperoneShowDashboardDistance WRITE setChaperoneShowDashboardDistance NOTIFY chaperoneShowDashboardDistanceChanged) + + Q_PROPERTY(bool chaperoneVelocityModifierEnabled READ isChaperoneVelocityModifierEnabled WRITE setChaperoneVelocityModifierEnabled NOTIFY chaperoneVelocityModifierEnabledChanged) + Q_PROPERTY(float chaperoneVelocityModifier READ chaperoneVelocityModifier WRITE setChaperoneVelocityModifier NOTIFY chaperoneVelocityModifierChanged) + private: OverlayController* parent; QQuickWindow* widget; float m_visibility = 0.6f; float m_fadeDistance = 0.7f; + float m_fadeDistanceModified = 0.7f; float m_height = 2.0f; bool m_centerMarker = false; bool m_playSpaceMarker = false; bool m_forceBounds = false; + bool m_enableChaperoneSwitchToBeginner = false; + float m_chaperoneSwitchToBeginnerDistance = 0.5f; + bool m_chaperoneSwitchToBeginnerActive = false; + int32_t m_chaperoneSwitchToBeginnerLastStyle = 0; + + bool m_enableChaperoneHapticFeedback = true; + float m_chaperoneHapticFeedbackDistance = 0.5f; + bool m_chaperoneHapticFeedbackActive = false; + std::thread m_chaperoneHapticFeedbackThread; + + bool m_enableChaperoneAlarmSound = false; + bool m_chaperoneAlarmSoundLooping = true; + bool m_chaperoneAlarmSoundAdjustVolume = false; + float m_chaperoneAlarmSoundDistance = 0.5f; + bool m_chaperoneAlarmSoundActive = false; + + bool m_enableChaperoneShowDashboard = false; + float m_chaperoneShowDashboardDistance = 0.5f; + bool m_chaperoneShowDashboardActive = false; + + bool m_enableChaperoneVelocityModifier = false; + float m_chaperoneVelocityModifier = 0.0f; + float m_chaperoneVelocityModifierCurrent = 1.0f; + unsigned settingsUpdateCounter = 0; std::vector chaperoneProfiles; public: + ~ChaperoneTabController(); + void initStage1(); void initStage2(OverlayController* parent, QQuickWindow* widget); - void eventLoopTick(); + void eventLoopTick(vr::TrackedDevicePose_t* devicePoses, float leftSpeed, float rightSpeed, float hmdSpeed); + void handleChaperoneWarnings(float distance); float boundsVisibility() const; float fadeDistance() const; @@ -86,6 +147,23 @@ public: bool playSpaceMarker() const; bool forceBounds() const; + bool isChaperoneSwitchToBeginnerEnabled() const; + float chaperoneSwitchToBeginnerDistance() const; + + bool isChaperoneHapticFeedbackEnabled() const; + float chaperoneHapticFeedbackDistance() const; + + bool isChaperoneAlarmSoundEnabled() const; + bool isChaperoneAlarmSoundLooping() const; + bool isChaperoneAlarmSoundAdjustVolume() const; + float chaperoneAlarmSoundDistance() const; + + bool isChaperoneShowDashboardEnabled() const; + float chaperoneShowDashboardDistance() const; + + bool isChaperoneVelocityModifierEnabled() const; + float chaperoneVelocityModifier() const; + void reloadChaperoneProfiles(); void saveChaperoneProfiles(); @@ -103,15 +181,35 @@ public slots: void setPlaySpaceMarker(bool value, bool notify = true); void setForceBounds(bool value, bool notify = true); + void setChaperoneSwitchToBeginnerEnabled(bool value, bool notify = true); + void setChaperoneSwitchToBeginnerDistance(float value, bool notify = true); + + void setChaperoneHapticFeedbackEnabled(bool value, bool notify = true); + void setChaperoneHapticFeedbackDistance(float value, bool notify = true); + + void setChaperoneAlarmSoundEnabled(bool value, bool notify = true); + void setChaperoneAlarmSoundLooping(bool value, bool notify = true); + void setChaperoneAlarmSoundAdjustVolume(bool value, bool notify = true); + void setChaperoneAlarmSoundDistance(float value, bool notify = true); + + void setChaperoneShowDashboardEnabled(bool value, bool notify = true); + void setChaperoneShowDashboardDistance(float value, bool notify = true); + + void setChaperoneVelocityModifierEnabled(bool value, bool notify = true); + void setChaperoneVelocityModifier(float value, bool notify = true); + void flipOrientation(); void reloadFromDisk(); void addChaperoneProfile(QString name, bool includeGeometry, bool includeVisbility, bool includeFadeDistance, bool includeCenterMarker, - bool includePlaySpaceMarker, bool includeFloorBounds, bool includeBoundsColor, bool includeChaperoneStyle, bool includeForceBounds); + bool includePlaySpaceMarker, bool includeFloorBounds, bool includeBoundsColor, bool includeChaperoneStyle, bool includeForceBounds, + bool includesProximityWarningSettings); void applyChaperoneProfile(unsigned index); void deleteChaperoneProfile(unsigned index); void reset(); + + void shutdown(); signals: void boundsVisibilityChanged(float value); @@ -121,6 +219,23 @@ signals: void playSpaceMarkerChanged(bool value); void forceBoundsChanged(bool value); + void chaperoneSwitchToBeginnerEnabledChanged(bool value); + void chaperoneSwitchToBeginnerDistanceChanged(float value); + + void chaperoneHapticFeedbackEnabledChanged(bool value); + void chaperoneHapticFeedbackDistanceChanged(float value); + + void chaperoneAlarmSoundEnabledChanged(bool value); + void chaperoneAlarmSoundLoopingChanged(bool value); + void chaperoneAlarmSoundAdjustVolumeChanged(bool value); + void chaperoneAlarmSoundDistanceChanged(float value); + + void chaperoneShowDashboardEnabledChanged(bool value); + void chaperoneShowDashboardDistanceChanged(float value); + + void chaperoneVelocityModifierEnabledChanged(bool value); + void chaperoneVelocityModifierChanged(float value); + void chaperoneProfilesUpdated(); }; diff --git a/src/tabcontrollers/StatisticsTabController.cpp b/src/tabcontrollers/StatisticsTabController.cpp index 8d20bc8..2195af5 100644 --- a/src/tabcontrollers/StatisticsTabController.cpp +++ b/src/tabcontrollers/StatisticsTabController.cpp @@ -13,7 +13,7 @@ void StatisticsTabController::initStage2(OverlayController * parent, QQuickWindo this->widget = widget; } -void StatisticsTabController::eventLoopTick(vr::TrackedDevicePose_t* devicePoses) { +void StatisticsTabController::eventLoopTick(vr::TrackedDevicePose_t* devicePoses, float leftSpeed, float rightSpeed) { vr::Compositor_CumulativeStats pStats; vr::VRCompositor()->GetCumulativeStats(&pStats, sizeof(vr::Compositor_CumulativeStats)); if (pStats.m_nPid != m_cumStats.m_nPid) { @@ -48,26 +48,14 @@ void StatisticsTabController::eventLoopTick(vr::TrackedDevicePose_t* devicePoses } // Controller speeds // - auto leftId = vr::VRSystem()->GetTrackedDeviceIndexForControllerRole(vr::TrackedControllerRole_LeftHand); - if (leftId != vr::k_unTrackedDeviceIndexInvalid && devicePoses[leftId].bPoseIsValid && devicePoses[leftId].eTrackingResult == vr::TrackingResult_Running_OK) { - auto& vel = devicePoses[leftId].vVelocity.v; - auto lspeed = std::sqrt(std::pow(vel[0], 2) + std::pow(vel[1], 2) + std::pow(vel[2], 2)); - if (lspeed > m_leftControllerMaxSpeed) { - m_leftControllerMaxSpeed = lspeed; - } + if (leftSpeed > m_leftControllerMaxSpeed) { + m_leftControllerMaxSpeed = leftSpeed; } - - auto rightId = vr::VRSystem()->GetTrackedDeviceIndexForControllerRole(vr::TrackedControllerRole_RightHand); - if (rightId != vr::k_unTrackedDeviceIndexInvalid && devicePoses[rightId].bPoseIsValid && devicePoses[rightId].eTrackingResult == vr::TrackingResult_Running_OK) { - auto& vel = devicePoses[rightId].vVelocity.v; - auto rspeed = std::sqrt(std::pow(vel[0], 2) + std::pow(vel[1], 2) + std::pow(vel[2], 2)); - if (rspeed > m_rightControllerMaxSpeed) { - m_rightControllerMaxSpeed = rspeed; - } + if (rightSpeed > m_rightControllerMaxSpeed) { + m_rightControllerMaxSpeed = rightSpeed; } // Hmd Rotation // - /* | Intrinsic y-x'-z" rotation matrix: | cr*cy+sp*sr*sy | cr*sp*sy-cy*sr | cp*sy | diff --git a/src/tabcontrollers/StatisticsTabController.h b/src/tabcontrollers/StatisticsTabController.h index 58e5950..48f698a 100644 --- a/src/tabcontrollers/StatisticsTabController.h +++ b/src/tabcontrollers/StatisticsTabController.h @@ -55,7 +55,7 @@ public: void initStage1(); void initStage2(OverlayController* parent, QQuickWindow* widget); - void eventLoopTick(vr::TrackedDevicePose_t* devicePoses); + void eventLoopTick(vr::TrackedDevicePose_t* devicePoses, float leftSpeed, float rightSpeed); float hmdDistanceMoved() const; float hmdRotations() const; diff --git a/src/utils/ChaperoneUtils.cpp b/src/utils/ChaperoneUtils.cpp new file mode 100644 index 0000000..21476dd --- /dev/null +++ b/src/utils/ChaperoneUtils.cpp @@ -0,0 +1,81 @@ + +#include "ChaperoneUtils.h" +#include + +namespace utils { + + +float ChaperoneUtils::_getDistanceToChaperone(const vr::HmdVector3_t & x, vr::HmdVector3_t * projectedPoint) { + float distance = NAN; + vr::HmdVector3_t* _cornersPtr = _corners.get(); + for (uint32_t i = 0; i < _quadsCount; i++) { + uint32_t i2 = (i + 1) % _quadsCount; + vr::HmdVector3_t& r0 = _cornersPtr[i]; + vr::HmdVector3_t& r1 = _cornersPtr[i2]; + float u_x = r1.v[0] - r0.v[0]; + float u_z = r1.v[2] - r0.v[2]; + float r = ((x.v[0] - r0.v[0]) * u_x + (x.v[2] - r0.v[2]) * u_z) / (u_x*u_x + u_z*u_z); + //int mode = 0; // 0 .. projected point on segment, 1 .. projected point outside of segment (r0 closer than r1), 2 .. projected point outside of segment (r1 closer than r0) + float d; + float x1_x; + float x1_z; + if (r < 0.0f || r > 1.0f) { // projected point outside of segment + float d_x = r0.v[0] - x.v[0]; + float d_z = r0.v[2] - x.v[2]; + float d1 = sqrt(d_x*d_x + d_z*d_z); + d_x = r1.v[0] - x.v[0]; + d_z = r1.v[2] - x.v[2]; + float d2 = sqrt(d_x*d_x + d_z*d_z); + if (d1 < d2) { + d = d1; + x1_x = r0.v[0]; + x1_z = r0.v[2]; + } else { + d = d2; + x1_x = r1.v[0]; + x1_z = r1.v[2]; + } + } else { // projected point on segment + x1_x = r0.v[0] + r * u_x; + x1_z = r0.v[2] + r * u_z; + float d_x = x1_x - x.v[0]; + float d_z = x1_z - x.v[2]; + d = sqrt(d_x*d_x + d_z*d_z); + } + if (isnan(distance) || d < distance) { + distance = d; + if (projectedPoint) { + projectedPoint->v[0] = x1_x; + projectedPoint->v[1] = x.v[1]; + projectedPoint->v[2] = x1_z; + } + } + } + return distance; +} + + +void ChaperoneUtils::loadChaperoneData() { + std::lock_guard lock(_mutex); + vr::VRChaperoneSetup()->GetLiveCollisionBoundsInfo(nullptr, &_quadsCount); + if (_quadsCount > 0) { + std::unique_ptr quadsBuffer(new vr::HmdQuad_t[_quadsCount]); + vr::HmdQuad_t* quadsBufferPtr = quadsBuffer.get(); + _corners.reset((vr::HmdVector3_t*)new vr::HmdQuad_t[_quadsCount]); + vr::HmdVector3_t* _cornersPtr = _corners.get(); + vr::VRChaperoneSetup()->GetLiveCollisionBoundsInfo(quadsBufferPtr, &_quadsCount); + for (uint32_t i = 0; i < _quadsCount; i++) { + _cornersPtr[i] = quadsBufferPtr[i].vCorners[0]; + uint32_t i2 = (i + 1) % _quadsCount; + if (quadsBufferPtr[i].vCorners[3].v[0] != quadsBufferPtr[i2].vCorners[0].v[0] + || quadsBufferPtr[i].vCorners[3].v[1] != quadsBufferPtr[i2].vCorners[0].v[1] + || quadsBufferPtr[i].vCorners[3].v[2] != quadsBufferPtr[i2].vCorners[0].v[2] + || quadsBufferPtr[i].vCorners[0].v[1] != 0.0f) { + _chaperoneWellFormed = false; + } + } + } +} + + +} // end namespace utils diff --git a/src/utils/ChaperoneUtils.h b/src/utils/ChaperoneUtils.h new file mode 100644 index 0000000..873536b --- /dev/null +++ b/src/utils/ChaperoneUtils.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include + +namespace utils { + +class ChaperoneUtils { +private: + std::recursive_mutex _mutex; + uint32_t _quadsCount = 0; + std::unique_ptr _corners; + bool _chaperoneWellFormed = true; + + float _getDistanceToChaperone(const vr::HmdVector3_t& point, vr::HmdVector3_t* projectedPoint); + +public: + uint32_t quadsCount() const noexcept { return _quadsCount; } + bool isChaperoneWellFormed() const noexcept { return _chaperoneWellFormed; } + + std::recursive_mutex& mutex() noexcept { return _mutex; } + + void loadChaperoneData(); + + float getDistanceToChaperone(const vr::HmdVector3_t& point, vr::HmdVector3_t* projectedPoint = nullptr, bool doLock = false) { + if (doLock) { + std::lock_guard lock(_mutex); + return _getDistanceToChaperone(point, projectedPoint); + } else { + return _getDistanceToChaperone(point, projectedPoint); + } + } +}; + + +} // end namespace utils +