From f48b583acadef92aca966f2975ab807e3dcfc52e Mon Sep 17 00:00:00 2001 From: matzman666 Date: Tue, 15 Aug 2017 21:44:29 +0200 Subject: [PATCH] Reimplemented sound effects. Added chaperone proximity warnings. Other bugfixes and improvements. --- advancedSettings.pro | 7 +- bin/win64/res/qml/ChaperonePage.qml | 25 +- bin/win64/res/qml/ChaperoneWarningsPage.qml | 567 ++++++++++++++++++ bin/win64/res/qml/MyResources.qml | 16 +- bin/win64/res/qml/RootPage.qml | 2 +- bin/win64/res/qml/mainwidget.qml | 5 + bin/win64/res/sounds/alarm01.wav | Bin 0 -> 131192 bytes installer/uninstallFiles.list | 3 + src/main.cpp | 19 +- src/overlaycontroller.cpp | 114 +++- src/overlaycontroller.h | 20 +- src/tabcontrollers/ChaperoneTabController.cpp | 537 ++++++++++++++++- src/tabcontrollers/ChaperoneTabController.h | 119 +++- .../StatisticsTabController.cpp | 22 +- src/tabcontrollers/StatisticsTabController.h | 2 +- src/utils/ChaperoneUtils.cpp | 81 +++ src/utils/ChaperoneUtils.h | 38 ++ 17 files changed, 1523 insertions(+), 54 deletions(-) create mode 100644 bin/win64/res/qml/ChaperoneWarningsPage.qml create mode 100644 bin/win64/res/sounds/alarm01.wav create mode 100644 src/utils/ChaperoneUtils.cpp create mode 100644 src/utils/ChaperoneUtils.h 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 0000000000000000000000000000000000000000..d96fb23d52573444134953e7110a631654a67798 GIT binary patch literal 131192 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
  • jL=N z0?-=k2QqpXw83ToZLa`puLNz&gQ(Z2a=?gA0j*|LbONd~h}u?A;vMkY9Z>ojR3^~* zOjISvQCXnHHwLWd0EUl7;{eG!4wNqfTmJ=G9fv^f(nOm1-?~+J$9jBNCJ-j5p)|6yuSc%XF*_(Bkec zL4#ZF?)~)3cgbaaXJ&V2{+Z;6=NRc+?9o3Uy*iwALoxnBrVsMniAPL6p07(YDU1;N zV;JK@Bthdj&!pq2xamv{o)i58RGo|+)CZ^I2RuW%GiMdE6&l-NW;=5U*J0)-qXqpB zLoYF6WQ>9dWSsEyU^_fRx(jsQbUL19F0QMd@G$z`=L%>JgZsCgwR{@ zOyPDssrfQK(Ak6n~X+l+n5&m58VyMz27BDY*B?=WOUBYnTp5 zz&+G7U=A@|(e7(J&36k%dWCVuljm!3{xX{!OX!$GBzcV8-o?vDro-b?%U91>$IFEj& z;n~?EQT`!aLT|#DmoOsCK#6${Wi-(5cR&OGNk5`5;r$3LMD8Pc7{>h?I_XAw4t zJN+NM3D2q)(JFcwok$(VbF&+$MRE8iuq0n#41#m*UeG{Vl@t zt>@#}+h#P29*Q=Gq8vrVQ&NqNse-?FEadow`do;d#-$Xdk*ao~vy^ z$59q^2g;kG<|;}>55`lq{izYOJw8v;HIx~Bi#A8eY~1xiivf6^cTe=!M(v?m@DHn| zv@z{O|Do2Sg(~VgHJdWWGs9m{SE#|%L#i5DZ4NZ^Na_z2O?BW~B%aOvm^y*Hc6>TQ z*FFxtcLLp=Zl-#`mN-hy!c()aQH@jt^#D)4{z-YG^?THFY8c8#QyZypDva7frBf+X zIdy@$Of5s&NR-`-vdysYjp#S%u?RUnC^r>lmQkTpG4%^N_F;+ykLsuu)J?SW8ZEh? z?fuj{P+3fOp&Kb{JWKpP*k}1vDYcgJg>7_>8UwDp!H8<8dGs*ak6sR|a5Q-80W0(g zxVwtxK}IgoUi3xe`hurjV59a!zB{Pz343fMtpvwCX)b*YWeD05uO+}s9)ibLLDPPq z@G;245wykwA2-ogz}sW=8#;ncgthw$?eC*|F^llD^~0d}47!3k3MpDjJA;D(Xyq^E zLqDZPQ(QU+vJ(M%J)%y5Q(SQOFI7R&R4ShTo&gGO!PCxjsUt`;qr>qu@<<>IGjT0P ziUzdu1(inO`w?m>=(m&#q+BTzst;19!%o4Ii77{_R#&CNf860|<-K^_ zy{LiI5nVUfd3&izlpJ<9n=+&HC}&(6U6Rg;s@DZmYpC_q8MINPI{;i{A+-w}+=;Ph zArrSyb1t=x+6|7fuqs|tFR4UoER_t&oCsOlK&`|-WPG9$sL$ZIH8^{MwxyqgyL`GB zGXfSvjmLA?&wws3DG}Dp3$$lKccZUU&(LZFY(YD)@JmT1;g_x|LK<)HkDAbXmZ#mQhBK>Mhhkw097-7UMOAGDf*YR44W^43zr= zicF)LbUmqN-4v|2Owjfr)tlY~e)xcXe7Xl#)dEPsba+wPAVJ@;I!m#FDnO||*c~Qe zZ!o|faRidQ5pp&OQrCli2W~n*=I(;Ri|IqKxF6D6V0DKBi7JPLhr!10z;_k1fVN`3 z(_YMCCWrQ6KF}j@!er5XU}sq{8FUssojF9e({1!|W;)K8_p~cg(~;gnuVzZ=O4^zE z4=2-1*kRjH(+#MPJ1nzf%m^j|S2`VtQ*0X2tMN>DN9^897$sxK>|loAieR{?+Xt_^ zXxWf?Pj|)6orZCh(!umw+|U2_>W1BT0QPM)y`G*+@5P>dmkvd_(0}*k*ad!}c4yjH zlq$LDRFhFh80kQ@_QgL5R6E~ERS<{j*o@93L!1y-yR#*l=%tI*m`dM#S| zhW8zqvp(|{d-pA*vzcG?C!8OvkY>!VF@qBNHBhnLcs~rvY{ZGy1Lt-G(;FVoW_mO8 z5%Nr&xK?HOPJ4nu7jB6ogMl)>U9s2IfV=(?p$R9)mGkQ4tB}C1?@!Jr& zC`~gxh-jueXds3T=nboPGDhAFqs@Z78w~raj;MgengvYjFRaN4z%U-dauSl42nH6r z7jW-+(A)dNlBq%IMX=u&!N!P!W~>KmJRcT_7FIwcteQ9$O-^D30e@4oYM}4#hc-M9 z_TWX>hXYxQVKaOpb+|UN2C$B^!hk0aLOC`on_P;NPtdQgLBE!=E(4RuL(4;0QKT)f z!a7*tOJIK-!5E@oC47XPxe?mrA?T1KaFWBMEi~)}ur;_%hfp_OK|UfafJ4|0!`CcJ4Neq8@i**o2$N zCfJOD&`94vhi@ddz+%&b#%f1?LrDWztWKC~KCCu1nGEZ_H}dOYPd|sYI|8E|3S{F6 zbm7IY3^&4>C_)K6(hU|)DJ+&7Vr&w$&@xz@^ZO zmjnISiMbAk4X%Wx@&+xfLT(3gLt*ocVcj8H$?<4eN4kRU&9J|mf&Da+f5;N@AgcrD zQWcp6+o}ybJO#dau=uRetZKB!hqV<18;FKInStCNKo7ETh0Vrht%3CJ zg>07+>tLZhgfuy_`ay0lkmF(X8nEuerk)Ls?ImZy+SCviU_tLkz3D7HmJ{nSteiM} zzXX|#1@7XAv^>b1oOKbF&>_h470CX0lv$5<=AxZ&Sd=oL`T6(||e!K+Nd4>JFP7y_h;A_iiGlmQ#^09v#KMX*D zDWWSRcPmCKhMimu)PW*il9{MC88rvt?gsFb4B&B@#0%gF$v{tTpnL^EVU6A*zd*_t zvc6+&EP;RJ2iEv;NO|Y=139ly+KROqJ4pxDPzF|r4*17Q>=j>uQCP7ufJ=sA<=Vrp z&%nMEg0(Oet=X_n0J+@;g2o6Y(29lgf z%*IYJ0=a)6rGdZ~0@2l*kNTrf8y_yH~f4{3n(js}Xm7k--ISo?ig2Uw3;7lD=pvsM5nna(POPvR+j z1+FON%K8sjN(*a0P~Ww{Q{u31Kf!5GMAnh!z-EkC3f5joemIavL)ISb4lL}8S|Ghz ztkgBw*<@HfGNkXtPN4;QxduCAAMAgzSh?;PaTCs-Ly$`YG6yK3FUsr%`k)U4Wd)&+ zwsrxH*a_@mFXa9bv5b)8#0v-FahtKj{ZgD=OMvlu18-ak>$nV-v@Q0KUf4M#p!aE< zq@m0&CX?C8yu|5Qfy)-DLf{G+jFiY?R-*Jk&=4(L410fj;G`kSUkRR5sA28qo zwEuwkh*Rex(B>la*#H!pW=5dYaE!|bxXu7F9Xf#zQc29u37_wPQizFBK#SN!5OW9L z2;vvh6S~a=CJWx{H_#_TnA@P!PRxEVbk)V6*k9niBmv4v_LgZ zAh!ms_kw5SI&uPlf^~9pKd7`0T#(|lh{E?r*e#oZFrCLv*8@@$033f0u=*aTM}UtM z=t>dBSgT+I|a->?cP35Ez^-^h*cgCG#I~2s){n zsAJ?9?@Q>Zuc6r*{QKOD(H(;}7y_yqV0K3_W;SU10{Z)Wl(-2RZHDi)j^@BKGZ{F} zTlfSPGUJ&Apzt_gISS;b<9fveG0T8688W9}cjQAS=hGy3B4Z+GIdhL51j{52DHEAH zuwRx!x4%Q50V0zR>--MgfV&_dRDW>yAF~ry!7}LTr|5A^E}cs&7-Lu<@9A3F3^v9V z^l}nDySqrM2R?9`4uKwD`_I~W2g_&@ywe5LHhLjiK1ol64RjlQzeDOG)Ok%$fVc22 zzGcJWe}|rwv>}}f^dy%mfK8r5Yf;)A^-cgWSO{FFBx`DQwX*t~&rEb8=X90-`!QEK;2HtaFN9)rssZ3xU z89*J$bu+XUVGr-?G4Eo z3uKPXtVV4yL&BEyg>@ASJHrRIA<0PKnXW*s*}zVHn7K&ryhec!_CQE0AZc>|E^c71 zp%)3`$oMkPP>VtfeVEJiSfG=pOb7V2mdS+AIv6%yH84;qa}ao`19K2K*dcgvUm@)v zY{w4jB~m^EbJ0<~5T|gNzJj~n_^hC8f$uy6>N6Ano8OD>NsECM_MyvxuYE;+XN1CV zv@WI-s9;(R>*xzoM*=1Nz#N`HK0CL@dE~I-c^y zXDTf03dEi~14=ZS@rEs$jFF2#*QY2q3bd1;)N@+Eq~Ur7+qO4mBZjR_GUqU}11MPm zEA|;~pJ>SSPl@BtQ^j{PbFRMvwp;4n&L0QFsuO9-@A48Qjg zScb!av+iS_(z8&rHxmFm5&i<$-EUwQ`=G~lK&KOdPQO6w_6(2NgIEk}aMcfUi-1LK z2t39US12$#L!fT;z(+XrLh3ekfxZvqwgEVhG1gQeq7y8EamGNh7h*M?!|Dq`P8}j) z2GYCe1YjIz=vb_f0<6PjKmtCYk0xNSgRl!E;{H6^TaNn@MCG(0r3F}V0FwaeiK8DQ zT0jWIx`Tek%wkNKc$yFYvJlp~27847CAx#Ue0&aL)Y!>JGJ8;VI%d~G4+kZ#Vy0Ix z_s5+)gft(A4}3i+upVvhM@}GkvXUtS;&K@0RR)vFq#<^oidl;?FR_EY0Hvpcn@_;$ z>7dkV^k#+DpWw1W$!Ey##)M;(@jydnBfh2w6H7N?&XrUI_M*O+`(5mdHrPd5u%9|( zhRwihTafmFHeq5g3kCKm3wj=qpD|e9de|3DF!~_ugIv@K2O_G#w+SeD0$51{C`O~y zbBe%z77UbWE_%~ZlTcMTHxHIOzf@VN)Dw(agWWXYf(aV0cJqY}Ij2MF_%xCyxIB4NH^oGl5cQ-r@yTQ*c zOdfXsXuOWm8*oAu&=wfcZmgJRc)x|ca0n2t*TBeL17GtY4l+87vK}~_5zeg@Xk{QI z!V{xE418$-c)SQwRf~4A=|*}4J&HEKd@Ptoy0gq4tiyV`3&!#U^;bgcxq=h-InM7d z$khk6fLKB5_G11LM3Pis_S4}j;WC?`*PR6_qhrjV3&6LB5k7__NufQI5|5b#Xmjro z@1S8OAX;D-gZ~*N+hK(+BE}Jjyo1Ib4(n76U5=0RQNSZ|fjpQ%7Y>I0oCw=_9@1{Y z${PWH@=aJgn_y+lhc(my^kM@n?^|RTEc7sPD{Su);5WxmF9_Cd1+>A=_`VI$%EJ*q zmPABB)B1q%EQU@w5u*u5S`eZb?t+sKkj^muk=GX*voE3-CSauJq4Anxe{+S-Rf8SN z6Y~s$KROavkrdWRU+6=oh)VhakK8V3Q|_=ocf)7619P8#6aJ5C&uEUP(Km+{0_`!GAQc``+|o5 zjK;h_Larx)1~T#5Z=}9<)mte(xhTQp~t#jy!AY8DbI#W`iVLk)~szZGmr+DW| zeFEQPI+KWg`a@^_in|q%|8!`C{h?+1LXLg0a_?cq*<*DFp|%D&RnROd(Owe#%8M}a zWM(Nm(T_mk3&d&IZYhwL8c4_|@S1^d_$;XW5I))+nAdjD_aQK<6!7pI>>Wo~U*$l- zp1>a7LxQtFPs)gWh>;+HpG`)bV@UtV9&sN>&OGmSouL=V9fVq0JX$6#7J)7?VGubCaRfr(qoFgdEo7 zdEy&TkfpF6_u!?6*q~{UyuFaI-FPK~u4h3{Ip(nv^Ii-8{T19_BrcL)(CT?yk6|AS z2A1Q6e)eG2Pf*$nZSDqDE@MV!ux|&V^ab=@L%0LukmGYA^8L{7YM@YA#BP+xBr?cs zjQbL1RSm1G7Fg^-Socbl|Ajff0Uf#kzc2%~;(=NHK$L_((9vhWH?9K9F+z-l0qYv6 z4-5MWa=Ws+V1A82Ooy@#v%Vps%z(8Yv{?mI^9rzui-j+{`vVctONAym2 zV5fl?V^^f+pcM{kAHcYB$#ih@HKKUBvu3h5NV@_&#R%xgT8!xpMlcYVgC6RdA?jfj z?DO+L;|62iSwIG7kjt?9J%yG36g2Wg9X8M+B`o{%peKd({|$7j2PWl>lp)A9z!+3$ z&kyU1fy~Lluc?rHQ=n4cA!GZo*Iq^riT!6U_L8ZcYn@pEihhCB^B8hF4%W~|SgSH( zJN)i);uBW>E9^7lf!~`E_aOVbAje6N>Ik6bK}<5%T{-xD5~%qoNO}-fP8C+(Ae3E! zQJ%*RmyR*Mz`C9Wj)AsZSj_Hk=XJhwMVBA;GrW;|1n5LJQVP}dQX%UF} z@WW1HiXFxcD=QK^p&#}il30MdM9hW3T$1r#4J)xPtVjW33o1Z^&iyF~t9w3H_D$Fj zLMEEt2Hk`U-Chly{vA%#BKV=~;Au#PHu(vZ6rdiW@Q9C~&0|nQkJtpd&VznG8l~)z zdH^#Whq!_uSR0Apqy~2jv41?mzQOy~??ar|=a>>$>KzzUId($=?Tv=6RtbAG2`xy0 zs3e2R?nvK)@kKE|K)Ijr!R!N7Vhhf<&Xcc`=gH7X9x|@zg9as!L2f&vflP@z*vkWejl`qHk<1m8p9em$fF_uO zPi$O^;2-h z1B3@Ox5M#gZcuX#RmyH_(DDb+s(z8 zSHNFX0Hpd5a4iRLu(QWQpj`{--HKHjiq<_L@6i~?RP?hEQc{W8tiftu^DplXs1*d7y z1O~7&Hef8l==UW0W$;UluE3}gzl7los~{~&XyYha{{XAS z5UriW9&r3$ANR0BB!C9jF{cXDBOpcP%rMMB2MM|f=^`MfozY+fp~8rVK=&@geKJ}} zM!7qfcQ8J+v=V)Gg?&~{N1;>-TKNi_$Q$$P^t#H??j7_Qft|A&=tKZ$>O}-XPhP_e zC1SyqRQe?5F$X0Q@ooUk`#A27gU3fP-lOnb&&H@fFzYajWPC;<&z(378dspNNbtQ5 z7W8^>^FHXJ06pG;7H-65?C|E01~zcH2&}Pnkkwm|yurjQaIkasBqSjO^AAL77)eqx0{vAI zGwAKK^xqy;39Eg5Cqe`YnlV-M2lNmENr*?ucu3PU^n45Zh&T2k5BTM7V03G-4^78D zCjnyW3Rz2n^mhUE%TeB~Q!58O)`ABEp_#u&$#Cp7Cvl!fg3F$uS2*UVz}_H-4`kp! zEzONg1Q&+DQkaSTZZNLd;LmNeaujsCjkZW!L%`4D7)uiJEAYFlPHrnuW-w&u_P>@$ zv@iowDnpqyknBK=eK5v77<{K8o$`N@@E$wqT_7}(kpEe*N0XTWSkbF7<1p|>1Du;A z)`MQA%m(^B?mZa-)G39ehk#Clm`I>e_fb9q`g0&!m<}4dgVIN^l8!QN;A;$%1{|O( zs8Ip=E(3+uf(PrdhL53*4X9y`-LLaB9|**_QvIojE8`Bcfh;#;QLTw8rHx~;5%`kwL4_N6?}C4mlqCh-9k$me43)C&K=wwbne9Z z8Ax!a$KwRX8OelTHpLis1m-~E>Ks?++MI(usRDe8fE=#JY3YFr+=AqjxbG}kjF~Q|U{5hej*QrV5s}~`!)$~sv#m`!cjC!;Ja^QMe8JW!68cbgsTw zjHQ^dKnVtGzjK~LAq&Hiej982Fev$+(E&|b2M>?GYF@(tn3JN<1qg{vJ?8MTcI0uSw z7IuKDtFZ691H}cPs`EeZ*b&&>u^4?I(G@f83dy*Iu^wciu-*sbbXX7DaVF$(2&CN{ z`{D`EClypmhkbPddbJW-H5=aKYLprQDffogyE|l1jku5JzHCe=o=>11`v+=N|=z z@(22y_@Ay44qn^>uclyEi3P%W2zMWV09-@F)j(L;opfIXD%0RmJnn8ohj&5wmGH4N z)6;;vO$F`BkRt(3@CJUEB6=kx_APdiw@fj<2LdTA#&4%~(|*htL`pgWEiRy)(8_N_ zaoHdm>lyu*-jDi0bZ6@Wp`VWfqAi5|{~va#JYe>}fuf1gzB5KQ88x2)x4jBEI1lem z80>|vkQD*YR330xK1SCc<7=mR7{dxk*c!Zdw!p_4ECnvnA2pou&R|!(i8)5o4w&~l z{Ilah<^pLVI&JG>*$;8kiyiVc06x&#DSA2V!1JZT1^Q|*9~j)#BgBqA7} z0}nT(k0E-MP0Oi^xa*1EvZmuVY?}}z`%dSI-|dZtkLVuIZxyiB5%dtmA`U^G6)@Rj zh!hUO_$(2-`w?k7>A-*Sj3v4eh=yaKhr@`p{QwWr5lW|94?NcjqaO_%xbw2YEY9Qg z0~h?7Nb3e3Tw^ZM+4K}<6nq~(z~olN&%@MLwDt|YIu$%$w-9UE7rwSqY82W} zq*lX6cLWi&-ROfzXCXFmB~?k)BNlf8^$QW6iHOD=j&>7JP6?#k8|{SQ6#_q-9uV`9 z@GiMfw&;_ORwm&y6h6I7U5joXqH5n#T~T)q`q>9h2Gz+`Is~a<)KIjViQb071C@nW zCPrw&JjUmDcr;qzN$Jac!A#SE6U$I;Ha!WecqP)l0ki*#_tWq}4Z|;3Z@@x0i*j4x zxvZe-=rHi3FT62)W)S3Y3ABGTGY3-t8OZ*1;LTa!x-EKo2nAWi8%RYTjHChcKM8+rD0rQTF<);I@Vwn9>M22_%Qo0B2eiLD9?c%^BFuDk6L3vsgLl3 zMZq884=<(&p6+&(Jq};24_Z-URfl5sy}^&Mpj-{K>U89cXIv1ao{oAx=xskRsrOjV z@kloW!qOeQQDHr&pbQ6I2Ohp9LUPogl@I9b3;*ptlvNPE@PO?>`*FZ~HB1g>YYfEU zCh%ZAXe8Z1qaf%eoiyzR%xM^?bQY)mQ$&0{LwrLUA|9V1r41O|GsKQ;1Gb%um?S^) z955I^*eXuIO&#G;xJNeQH?=On#%KPsVifcKaTKvR zSYu}qv(l;8?*cc6cjKHl=0&Ca`i7AAw$3l5@jBgf@sxRbu#AD6C@9ekXm5-M_ ze&gE+Uy2Sr{2|ss-#rFI_B3opCm^MtfVtiWdYJ(%*PgYGRO?Md=@8Z-))%}V0cJX# zwH#4rtMI#FKYY7~-*JCIELl&zv3iGDlMsJ4PVW#ag|&b^iKW)NjTl%zpxWd0rsAIq z1o&lfHo2N*sFw{Nj{~CG0?9exN)Enl1nsk-^E3h}4@NB9X^dhfJVM72Mc0V$8-X+r z=|t5L|5pggwG+YQcFftGoDVzg6{rw|Z_mMvbI2>eT+N}w=->&FfC2@;xI2hJXyGZ) zNH0+5IwWi)?&gD^KY_pfM9qAl+}lupJJP%T`<#c|c_`Bj)a-`#AERf2KF~hDK~YL3 z9*i-scCg6lxun18fTG@;0ax>}U0G@R07bE-|&JenY*_?duRbQ8;e$h{r-xabdNe{HyPN>p6<`&3n@W8j9=O z)h1mx4>#?xlb&LEy1iLCMW`rH=lysiEA8`Z|A#LhSdZ6Lf6nb;ursGRl1d3kZRH&> z|DeBuGlr4cU3R-Ttij^Z*+%0Fvv z=>_Zivq#{mfmYH_;_0Iv59FB)XPwM?+n8I|MVe;rEwJ*Q-u|IUT(gUuCJb+$pzBrD ztD~$sAZ3|imgC~-UIY89d(ga^EWIlY9VYLMuIF4Y-1%;K!kE~Sil?prkq_BdKEI2e zP#o7B!*3UE;`I_t{T79&^Dt4)6!nZrI7rxoY{I2ya-DdJG=lkEe zsgpD+)ds7h(lX8i{ada<{P8zrA>o@w`|oN4u|2uw5j%Z=Jl<(N0kXh6ZUUz;-D z7li1=^Sg1+aY95b9{sJB*m@fGpx+3Gki)rg4qKgPI%NoN@RpVLq~nxrg4|~NydS?K zYh2rutmmrH)+@SZnHIVkbMESUNe5Q_tx3&NX_nT${u7Ya6uP&9%8zARHjMq*l0GuG z4|B$J55GlvTTFXwP@Ai1c{}AL*~XUYnK}rZzB#u^SIG7>JZyNcv=TAxCrft38I<~x z^II1wGGuqW7b$zWtP;vPx=4Q1xt1*~yqROwo)x_O78jIxc$$ixv0cY)n=_6RTo;?Ng&g{7<8?OAZv^=L`jM25EaeumGzAUErIYKwd zREbKx)4jXg&w|C48vQ4QH`@BQJKMv_-jp|yJEQuIBEni^;8H&{B``US3hFpwu9Ua9 zd#a?Wowg52iP)1kSs0VYkKNKTGkt2t(2w)KpDQaX_caovef~Q)tG$#Eeid=V_oW@S z`kq?RebdLRz51tx!KAlsSC4D1SJjP%9ODDETa?{x#Rjc)p7C>%w{=`17Ft*1NkXfo zddht^A%~JY&}z`?{DIwXXd!1i`es>b$9M>PDcGKn@9HU#{IRE;w+Lb>vmBB)nJ*OYmA^? z(P3Sy-y!#+dor`|hvOj5KmT}nAU(*XC$|tfvB;Je(%uFgZ5S=u>$lt>+vAgDR zMck&qgAny{jBhhL?4Nk*b&0d&8@m~M)N^bmICyiFb@vnQCdbgbsTzwA={lFml0oJX z_RR(_OhxqgJTl!n+P6)eqL=aVgYE}YJg;o6LBG_=G2^q-id2F&@oMXk$v zgVUy#tbp9n*@eGCSs(u$!N06lS)-eB|H* zeIqz4xa@`+-75Nt;){z^G`U+2>C$nN=w*Qa`qV7f#*~M)S7^=C`tgb+^G5{t{bmc_ zam{yPO3{V3U|qlb;!>AaAwS74wM7L@6B#x6?B~9;Csl1NTLq!~IFTN|Nt`J+SBbki zh-~?5>GO57^}@|pINVS;+ne*H6=&+-v@bJW+R|Gm&dYB*(=xs4u}ztK(D(yR6_)?i znN)EMzZY3C^0Ecl-nn}}2Bp}4EYrR$dPUl|o&B00mzrx}5NXj*){l3|h0#2|!r670^%92~lPN~= za%;W0e0#gs4raDV(h&YU%~aMRS%C47hC_va65JZ+Huvc`Eg7%ya!)n&u-&hVX+O*v zP+VCh$(~#up(**jFX_&E&x-DaQS75yP4vP{-~4pqqUiK{OSW(0+27V_^FwWORV5eO_A-m(PNiw`E;T1Avc;F=n?=7I-cvs2f^QpUTPIPe~iciQEOXF1uCM>Pnea>q2ftb8r#A zoXGHN+mR5Pb|^d|ah&F)b~F26QfQ({ZhF;makeN~nk-7NF>vOZ|sV) z6xw?_UAHXYuQSN65=yJ3{^ZpT7y94ZM( z_HUVyJ}Tqnmx4bxDiSMm47X=4OmfWER{Rv~6yKLTm8`OYGR$JV z<3;<~R{J=kjQUppklZ#qz?xE5m%1r6to>CxEqf*U;E*i8Y06bDrCYdCYHLwa{(lJv z8h@l8&)V{JdNfn|OjF3lKW`?U%zdiy;~x+a;y}qOn{n>JriteJ*>XcKp-fMrTI1Z! zq25x*`DWBq6DmEd+{Qkq6{T!Q`rc;VMoZF!_iWcoE=eO*D@cFAJceI9Ie%09>E^SU zvYY|m2SrDhMU}5MIF~I?T9tRIT*hC_w-NRh4X}3iI4i$kK2k4=ox{h!04aT49GtFL zUNd&&W@&dw$5?3$N7n5~(FK|BPTw+ag zAMm2JJp^7h+qjDwPGnz7_|+EK_SAT&sM1_zwOh2)@jUyP$!&wf1(%CV6SC`jmflPD zkF$)LP-#(emg8O75vNyRTzt&nym%PDOqOak&a;nMl9`CdXEut%X$wKQ(=Er|_D;sL z_*d$y4eD&Ba(gt_GRo%^8}}-?P`D#8wQ6v6O~Tit zdG9Bb4=6s)?p{|DTbAosFon4y{>gbFI>U9fTxaF|-)e8Rfxn3mzZPE3er2)B$wYL+ zIe-pm{=;6tjWF(BcD3qH{zgVp-L=S9kRm!U;Hz7xeQifu%M?8_e|$SjJ5>{*(Y&{b z>-FhQ&EmRTqaW=%e~a>ORhuyP@hl;IiK)pQafs-o%T&JvS)+JiTUui`x`=nfp-LR) zzKVCJT22Uk0-?(HOHzwyy{3sOWU}XD)Aa?Wk2`c*Uh7a`)Wt%uQN=o9?~=>F*m|^GXLM7dGB#3J~}!7rLojmzkY2+uJgfz25jk?U%;Nj2^XDvY42NA77&m zmv1kaqvu+ED!$#Mdt`t?pMAX={o+XD{kwa=`XKUD zWo3R}mcBOa@8GPvg%Na(n-H~RA*G`_EhtXh;asZAEUGy7UpPHy*# z>&-veii9qyVR?D=#`5!`#nLiCKXtJ~t<_i?D}#$d7m*{iQ?br=qN9m&p1>mGANLLd8;jyIK)&F0h})bGp43!m5iNb_#qm~lJ#(64>bO*OqL z(@iGi8zlPY|E@SEk}F(gRpJ`+05>1i2%GEN=SFt2EjnY~H5+T&zE*qquZ+8t`{>z= z=fZMO7hXx;-)!8h$MO{Q%RM< z*Gi*Ui=2f;ee_2w9*UEt>C!;gwU(z;8Nw*`9n)C7WP{V{6m=Jehr$Q^JGFN@PMc0M z+}Xkue@Wa{!*3r$|CQ{M-*G!*%5@Ow52ve*>5B0c{5;<>-}>J1f71;^ zv^&l>+pKW2ZiRfOP$@qqIpFf$D!@!6Ngy9ey^Wm><@QU|C5{6nclklB+gdlOhMIKI z%`cBmtgP5ct!M2vEs~kJZWnjAE8{p40o)rkg0iXE%7Up40~6!3?|vvOT35V|zSTM_ zL6JojI(O8_9fgPFBW1(gkJ|KBjF7A&Lq$WlgE-wCt!xiD%oZ;&>E7|R<*U^>evU4y zG&GJVKc-u&$5p(M@tj1wL$>F6ul0NLr`DY+>! zxaCDx@Sh+t@-X2a`x7Q&oBg6h^1R@sc6)I}=GdI0Ew_>u<&6pdmiM>#OxsDOJaJ)W zkHUneB*`A`JV~AKrSl=XPl_d`mGmByH=JF1gVeJeG>+cVmHe#)*_fzg%l8x8H5Zai z^Q(v%ZLcJ!c=h(@#Y?S@nkfjruuD@(zGGf=x=%-3^3t62k49;Q<${JhgYRjjY2JnU zHU8pfCTAoo#T)E~I;G3w%x91RoToe<9jlD7H+CvD4L0E#G}QJr4Obk}YpmXyk(zmw zc-1;obcNU3_KI1M1!HYZ3>5s-4J>$Hq)y$~o|9RZvFF>zKkq9ItIIi-x$#MUg$K)< zd9A#Wf^PVS|J@ElY$tgZ@|NmV^L*L^4b9DI=j#^sra#5Kt4rArL^FiD>gvl}GO31c z%$kaaf}6Y@T{~>QIL+a0ZDIJSMVl-9G`&jJwKRUOO|4Ibqan{Vg@5@mK)Lj0A!g|7~y0~$h;pCE8Wh3OrIGpP>M zb6KD`*f`d4m}9&BG;>pfI%x^tfaRskQ;)IFkUiqBt^Zbf-uNc>6#bx*PQH}gMm0Ct zmXp4!L@jH@Z^WwG3M6JR% zqCVU-HQTkf{aW=TgIoL?l1e5)df#@7!vf_W@e%TQDc9Io^q9T9Dl>~n%IY{)d!Cah zIb|2=v_ZAs%BQ)v!8pAhC6)E>(w;V!XD&}Z`={&gmYU0@DsFAAMQmtxTS+2UB3U9L zgeOg>yZo|ZIh61`jmDaeBj$1^s)hE4)OYZI+)HJzB8b2>NTfHtXw6wgcZ*)eA?!BlYsMuFu(DdeSZRWdzG~%V%8F8s} zyExzZv&~TJ*`_MJ$+FSGkXYPBVg$}VIrYBv58oc1&9W0q&xt!6ce2Mnep=iX{qYr0-yB_ANX zZL9BCVX?{Lq8=go%Ma`5Yc|iu*x`xdJ>Qr9ph*_CnoOrwRXV5pWF4Ry>Sqbk1U4>_ zHmhtlnw7L{G|q1CTbx+gJL6s3hLqvyE}w?P%c^Q?rWod?@)F1AZm2jXJS6UrJQRJk zY;~SyR%!7<{}opy%VX}E&a%(5&#-#LUuRTQ=_#z0(X1VHSF@(3?bBUr2^4M-EwW!= zU2ncoMYY))$(g{Sqvfg;v!?4=C27-scKbEDlB1bv)F-zo;Xz)SM!@GwpGbB~{jFH; zPfQP+WgA2pI!YI_)|xsvhO3LLCB^}ql~tbn1hea`9rZEk5lO?^Ubk^1??spG3N2FP zW>)){9$Y#3wPb4XyhOJKtDG~LXMXMdAuE4cny258^F85b&a=`C zR7yXi5;8Mn14z;owLt!yz%J9%Q?33%bN$aI~Wn-US^AI@`Vc= z`sv>{+G}J{%oIIMJYM6VsZP>Q3W#jb952c+*i*eX?n>U#!Z_AD@lf6s>0No0$3vx! z>JtAWJy%>z));@W@9A*FE|upi9M}*`jJ0y)?`Yu{bx-K3^`Z+Hjbw#b>zI!xQI~VG z^``MYli`r8`ORjH5-Ksha2~kYR$I0*$iz~ zJmNaC^O_zIJ@nF?>?OO@dgc#m#uG_xJp?5UM=N$#=2Z&m52+{G9|^~JnhrO&V@vBx zi-@r5#>#!{gC&JkR=<^zhJPlg(1=-wkt=t_fEFFsxQs| zYc!}Trv5{Ved^kZ$fzI5BR>V@AFH0NwKe_~v+0j_`tcTjpi_7@rO`3_dG4nzMAkZf zfh@-GaP>y+ChJ?y8DvoQL`N6oLP6;>sUte2U2@s>HGD#yypR6C`ExF6&@JSn?W`!T#< z`~b62j1f21T4Q5jTghL>>XtiBcTdn{u%OkfxH94k!Xu$wm&4UDa?C~Upjo~_uY@bQ|d?CQ9M-QE_@ zmsf?=9L{$u+^)_3Q=5M0y}0~JK|SN&{5JYjdR1<1yN$HBKqso^l{nhkr((~KV4s(b zGm(?I7WZuf9VeK*mlrfU)VvbW{Bqhsb0+;k?qb5ECPOelbimEh&eq0Xa#_pfW;Gu# zajp$XztFlO_FUSusJvuOrkHQeSWUuf81>cwOl z^@ir`Z2L*jjdwvKr`;qw*sB|v!pKCs`X#Ns86W9j^PnzAl_JNU$s^Z#>+htkQ3=YgV6i zjFJZN{5pm=_Eu?lrMi{nP4SkcD=1I0S+PsD!fiRUM>bDNc$pA&{Y!>tS7zU8cptwb z`#{+J+=2r0_K3DV@judR^DUZNWQPSuWQh_-H$D5y<|(pB;-KJ^NjF2beXQLFhbOYp zyg?MV@r|m#SVWhW-HHt=_>O;Qn`$~nobI$k;Hl0OT_AG>`3>eJ2ePfRw9W2`mAU#K znzDV1mbAR2sRVI)N#5gzkyk?(Sr$f@U*$&$){FN_YpgcA1)5G*PBzHZZxDM>KBik77dtJn4C5AZ$JF4dhDxr1 zR$H0^OqkM)=mmPq>C3V|7Y>({Gm3BK>*1jJ5*WTYrjry-j~vlf#n+kB$JA2j89i zT{@#Ove2CEUi6=~M4(l=3^sSXr#`8zWop}h7o1>Lm*>{zW=BMRjLrYHxOr~vTfyau z+CRa?*K4EMSHv%P{l!bT?_@8<0`E`VZAHIDe;O{dKBem9TI;jYFK(}>G1|8^3p)tT z!TMxfpW^lOp32J7E0&SYx9Dj`DJzBN#6#M)*2hS^?ZRySit+?2S|X~p>)Uaj z=+A1oUOFv*3?0%MZI);gI>5m#*KC!+U9G*zTe`fCtG$^lty=KqSDNbc!+f6R6MpKo zJ*`J-O^$w3w&gR0zNFaTsuktVwr#d zq~0iaR-=~QHk~4AGOjkybUmjgZ7*}g{8-spR-*8a%_G|YrJ2Bz9aW?xHu1vrpVuwQ zKbBzCn%Vf7bHTLPy1+Ty{HN8z)?4gFMk(co>$+ylt=AThNjVZP{OMG4yJ)4MQHdsQ zY3`EJEd7zdk9)+K-%?N3&u{t0tF3a?MwT5fPHVUq)1G}l%2G3;Y(A6M(Jk&? zX8(eu_IGlXaE(OAA89v0J;ZvSg@9cl*=anPo^HO`da+%sJXp4?eO=8n;c}CH%-pI! z*^+`Y#FV;$yu17f&ezmoDoW^E_roZlbx(<9?V?O|+x(=k44-eqQzupD*WKaNWh_lS zU$8*CR`y%gDy)=FuVIPFFmXfs7?HlUi>=h+m|&Uyp7Ls=d3*;}j<#n( zcDj{rU*m1V5@Ej00_RuC!IoQEceA(a1(ZnZ=4PE}T##RyB#Np3A*+ojH{jS74@nr6 z=T|n98zPSpuY#Ax$mOTST2)UIZ^M)F4pUVkyNOa|f4^upU?9w>g$Ztz~Db@8{g8mQ{r&+NYk2>RbJ!RI7ivCNlnXu352) zbwcJL%#w_ePIYOvm|%97-(7ExY>Ra|-?SkagOig53`I_Q^tULAV6KirV6>ngq z@>+qO>_6FJw}Yw=ipzo!a)$Vl(Kh3C_Q}?p?S2c!nCxiI?l`WxWZdt6iq0~;iR|Ix zakrU?yVfYSxVyWc8?p3(2q+cGUm7;)U9y zKNG(N=}0L0TacLR6!Li+03z%Vbq>~oUnZ}XwUBFQQ>hh^eX%Qo#kBcxq-9*W#I`2Q z!eul10d?{`@&;*7au(tVWq{jbKGCp5e?J(kny*>;ai8(G>2kVdQeM?ly~%)ze1J|8 z#ZX(|diDxQPtIY+(9|A^2{#|rQ6iVWRs0XUL0z1D7VN|S0WC=Fw8?5l8P~v5kr_}s zibc^8`XR1n+EY`hZ+wdk)jD6p=2*Y#E}CJVKiB_fn-wfW8)~=I{bgwO|))a`m5h(lx92by4ZM&+FK16M%xLLvE*)49M#VJA@3vY z(o#XnK-oy1h#0$pJu5Shy`G7uVV#Fi`=~D{D$i2eulkb#bz)Uhh*D3!-?q0*nE9TN z7urK68-tF8c8sNGaArYX)y5A_QA-0qjr*UbPK=1=-zo~iC}i5WF?(wMAKy#n*HuKe``b|k>Amc|#+vXv>LykdP(``Iz9=aL28gH9xgZv= z@N`BT6>{V#UKsk9_}>0sAcB~Hc^+LD3rl} zV@mg{-um5IW6YO%y}A!@w7YK!w`Pa-Ph7spM88hGML5`cf#Q^GI1@pB#p;(h6g|T^ zEbNoTpv*w`vi@imfxQSS^k1Ir+6mU7A$MvVA0@rn{vBsFzg@CL^fmw7~Q6?{D_r-7+FUTtqCLTfZMD~k* zgtDmliggCz8=*3Rcbuqhq4}Ly8r;V@!R*}XqoA*N8u?`4H~CWVkF>J&zj|3@Rym=5 zcU~WLnQ@5sIZRY>q6V#-?-c|4X@3ALVi&0|YpT3J{1{zK82}zgl|vrM5QUDr9c&L< zY4PJ5sWKGDDOP{3J{XU?^YG6>i_Dh!g0B`V3sj+pAX=C){(RN3K*#z>bwd%Qh~b8; zmk5(JxH4knEQ_4<7F5z$R1)jotX0B^avFIyt}ke}1V-X?(Kog~l~OY{ z`N>y7TSD8_a<8zb^f~c>-$V#`W?9}^x$4KkyG6yy)9-)!5Z@FY2Z8f?EwrO*@bZ|E<<{1)9= z|FU0VnlYCl7Q#r+lj9^iMJNIf&vmvT-C(1TnLcwJtXdoy5rF|3pi=sYcZqRSn3eM3 z>)}o2D$~%aMsH6|yzW}zqS5qx_@4AUXoQ2H3+KAn8c!t5}ErjzQs5MvRGC{nc@| zNl+E7=H%tuei^i=M)$R{dzyRtAF%I0lK2tYO^4@V_^o&(>e6IufQ6Wj^GcV>M#yr= zjg(P-d0Od?l81+T8ShpgUG0;T6W_pYv`-nU0CGkyxd0v}MQwOfQ~d{{%D=CSSl9C1 zD8m)g`{b0^`LcZtS9O`;KcFE*BwYdw%uI=RoB_<5#CS@3%*51w;SuR5#d6wWsxbD! zKc91xHYcvLN|m6A1V0p*L+e1=li8ltD0$5qm(Wvsd4;;2`o9`Z#v~PBo&Uqjnz&u) z=b{DGk84)wTn-|wHNGA7A^{Jo*`dtp0b_Ya0s}KJ0K#sc+pSIEBGbr+ zgMK^RS!VYpSEzmlN78QR%oN!3qYFkCDCJ^HC-3qwFpZ!;^AXxk;`+Lnid;*x8WU z_J`>{>JO6%{2{3t{H;?@!9pxmFUM_i)imD-@2DN>IQC_HP30HAW|?b+I}5+OELxPM zx*cf4UC8PV%CR4%?Q-tStEj*i9#o2}{N^Ug%jXObTPuLe}YkH*o z8ORP?1P1WCw|~(30=JI3t9dS}8}5W_vHxx*%CVuiz7U&#^4BhV(AW{7sc%_PuHMxY zgDgB4Bp_U)dvaY;gnTl|PddaJnDXGC3iCv(B(o`(5Q{aVV;1rp3^!bF>`~Dpt`2lITcTop)Y?__i=9bWDu^_eyeP);kiWg;q=$^Gqj#UPZmMg zOR+kMIvfwFi2Mp_XnwQnVw8 zh3BCD(Qon~>rORK(TymJ76l|%JJamZVu;G&wVBQDmvIGAmiUKw z+lF$j)D_z5-QtpQHTT}lwku70k*j@%Vsd?@Zc1_rYZK6)(HfkVtr!2x4?|CpYUWY= zP?T1(Pzq+s=}y|zpe^_h+d{Y&>tU;@K5j=O>tlx*ZRyo5PqW|3FA{#n@T6t#-`3yy zmAYBMg0j&Ku=jqw(Y!A4U))yit^1}g2^*OI00Wt0p~czmbTos)XpL9^-X$MK?UqWV zqcgTK4w7%g>%8-My(zO3f7@qP4mTMR-%{P#Z2IaJqoEs83UyN|gVe@1#@t_%->@q% zu<}hK>ciB=yM|-op7Gj>cS@$NSKuXmEA z8{_C0hv7S$zE>q1oT-HoHhn6EqBsIB61X_eQvTYcph;!s=cMFvvZp7 z04xieUQzX|ul?hh25F^lM7&$_l#ZU7=@{+Wt$*_|UiR)=q2KB5MQ--YE^J{aaQP6M z!0F@;AeHzR-3HugHLcTPpeuMd*gHf@BIr8FW~f*0Yxr8%IQOye9)iK!A%QaujuSmT zgOWckGi&URmT!2FyN}~OB4TA@Hv0DI#_2BQi)xPk=6YK>d!j!lCjaVD{m?oWTgl$d zOcH-nw`a~0^kvTyWCKf~ez76Vw}449etIvt6BUY_Y2u=v;Z|V~;lHgNH4hTVaC`P6 z@%TP%Tb<^7z=hrA1OQHM-t28rEp!h4cC#k&Ws~~0{jBp7{FQ2E%?tHfuUt^cQ9#46 zEW!9zccl{~ONeP79(W#FLm9=F%K8iD;_|~+HAgXz$(guuvB&zos-F>75TG}6I<&ek z@61C%3jz@CHhQc3yT7__WqMbBP!;>@xuVt9F4n!sHw}5!MY`M0_TV!1G-v}6$+5J! zFRhmiA#%tX=00R8Wwt~h8!pJ8{)1U-R3tx=FJVN!WqN)2%;2QpIP5sKO*p8<2(C1} zD}N4glTc=JG#{?t>A7nCUDc}U-uLaUKMWf&UhCb`Lycq2lTj-04i!Z?0!+_7oK{c% zgLYsQ`jlX@^tJ3PJxn{B-jxs1Jj9CdMN7DvWxo>}zMe*B&uwe>X0@W%3@Yhi@*Vt-qKgn1)>mt7wRl{ zmeez`h}}*cmAzz5VEmT|J4R87uPe^FuDJ^cw;E{GOLS{p`=Cqm|nK%+i*A9`W?r$ddh}s)|xT+uB=_)Yj8ayFB``v2$+=l7#6D&;wA1s@g&Iv zS!>!ea@gri*3)h1r@?BIsAh=oL?oS-f)=w6wiwRV%0?k>q-cc4c9LtVwz#RreX}B4 zwfWOj2h(y9<@4SuU)xx3IF`D?;LyI({|9Z&9wkV!Ptk55s^}eYtFX_c{l(oBb+jh( z@xZ|76s`*YC|YQ#sv^3Y;?ofq7^9g`_FC|`bUwzIQsQ5v%Wy2}JI4BEZq>nt-yh$a z?;9Pd{y|D*tifxT9)HUiLj6p?mUht&3$L=S(6lf(a1C$6zLJ6BO|nb0*Tl`?x8Xhf zr^F7iY3AAG{q5SA95IwJh5lRdkQ|g)2}INv(sIvj3$DJ@u-2!l{?d5iBg1sf@H3GW zI#DTBZ__`Ew1>8mmOrz#52E$MNAsrM4(K-Y8pND8Iipw$;#n3#e41Utzj?1K9fuWj|wX1bH zctf}jFp{$59{P3R3=St1r2Gy()}J)At0%{=)IMuefB9N#x3BQs#9pbJpwb%VxZ0(^ z_kb>-SaKu-U>0#c!AIaIl)n=h>Fh^ag^-m=(xacc7t`yQ9>i1E{Dx!oUsJSjnB0ZD zQ9OsI;`}WxPOc}_C$jY?%xLB7$SyU$?&r_Gg<~A$O|`i1+Idx<^+5}sc#!;yJb-F} zB(hw|m{w1zM-lDG6Qj3LAj737;H+n?hAumoz>knyQAhcX*ecYMJaYIJ%LUS0;=iq0 zWUv%}Qjhpb$rCJo+a1>|11y64^||t7UO)r)1fm$+s4{9D#{4RIa9{2r1oWRVATZ3%POm}^H+jW#+Zi|A!63Hj-;m~;> zgNSsMhQ8>xH4fI5f0d~od@6V6>$@h6zW1fsHLn|YA_s_X&+lJoSq;Q65LkRaU9&beX*=Pvi;z4e<=vDg&_Z17}UYv8M?+sg%Q4`?Kz2VxB+B9K<-?;-{#)@G@ZYp-97{d#$^g`l_JF zsH(|z*YfqX+b#Eic|K&QDHsCr&Va=te|G+sSWDbv)BQ6MCMhwHuH9z*Rs$b~sp?gx< zP=2m}>d7)sA)GfoE0s3pnIw2K6sGm1J*BVDiVAnj576#o5Uf8DCD=Qn#nOOe6oU$l zapuK~!IOjwq2AV#+UuT)aZ%_!IG_Ha^=bZV$sqXb;4flNd!uu-X@GI1_i@qvx<5aD zv(B=h5I1AgvcvVK^lK7JI0axOy))1)6Dd0++|Q{(Li8eB+e8L;ZJI1m&=bJou+sIC z){}55k!o68Z_wXMb@cV3xv4#JRT8aa37`lth`oZ1<_^w+`e@i!_FQ%3lS#R{>4EPq zx_?c54O6$Uxsi%)k^Rkmnks5XdM6nCwOOSjzEhlIEePB;Q)5L)Gszml zo9S+VNB_w^k^4XPUd~zkar8*~KvYlgmExiJWtwm16D95_wuXBMbvMwladYMEU?g&y z0zj8U$3%ZaJtSy&Z+r>rm36*FQ@zOZKVwR@y*%UFbi2>!L`O~j3T#@}*p7J&cBY;H z4QzHxG;am}7ha0I4-7`_1ZpyVN+(P9lCP4?p7oeU{%3q)pjNY>%;Fyy-G_YzPT}+t zKcdc%-9m1~3h?!o&ekq9JXah0?Ak6h?>@I_dT-c)nc$3-uh0lgFOf;$FsUVYh2hA( z&Mo2&AdQ7>qHRMON%It+Wrw8`$rq_b{`884AfL20+|Mws0^htlaS@&ZP0%R0hPq8r ziC>GNkg&Gb1qZ$4>xTbuRMqrR7_CarmYv~R!ngfVb-1lI1 zavp6DT7vdUmdn&~GPROw4=;}N=WnGEV}5g8)l}Ov*qUg6Xc7gHc^^#5!oc^`ampi? z%(z{1KnrPHG^j0-uU^ z=k1VwRE&YPQvukK=2uJ;_$&2y)8YDv29Ln|N0N=i?s7bTxUgDqG;Sj&!)LT%^BYyi z_&>Ehl!afmlwNT5bT7l7u3uVpT|dbdARi*0qHG{-re5Hzlg=3smHt8KO_&yV0n19Y zm!dgqI78*m=91*o@Bkt?G1UIRgRzZ*P0%P^5LhSOGh$SxFg^XHOJ7M83@`CsF^ZTV4!zWJQMFEIcR-?H^Ow9O8R&D zTs+KQ7#QVT7&(R#ro6!}E=Zq?A=&b%JtUU?+q?G?q9_jxHcY;MhHFUNQ!8{ie-u^E z|Itv_(%aebkl0YOwRo<|+dPI7W_P5Y#BC6bX#Fq$gV;eiM-P(z@GQfP5%M!#f^)d( z2%2dzb~1TA@t^n#3rUp~=ob8&dV*ij=5pKjyfVn|TaLsNRi2r_tlC#i?n+VNi0?+8xHnsZH` z01MpPYjT&U-;4t#)Dr3Myf% zfVuo}tr&cpI5Tn}DZt({JaWIUcRHh{VI>>NALhMq-ZN3~8EMrH*BCVJz>eT-gLcr* z&^ayoiuA%`KsU6MWr8n4m5A!a=Vd2oXDQjPZjljC4n`cxGG|qnIS)niqTeAN^Kky; z<$;Wch~M8eb9yTBXzTiQg%2C!G=9(gBLjDbQp zsXr)F5naVt>0$+q)tl5mMf14XM(VI+py_K>QYT9eNmC)MKxWPp=r8dQK#{Z%p9Ef- zOxju%Em&Q-t8wN>WL>@Sx*wNnU)8^MvUX;3dHU6Q7TO35&QL07oL8(x@D8L#az9j~ zXsA3YyT!5-;qXl!CHp3HCUvTbRe8N(T;g}+ApJHqH)Ay5=J#Xg&PSu)UJYyqV6oRBHgt~bR#^vXBKJAv!GV*sF4(FzvVa& zT3-=nTKE++^=-{Cycjq{eMIlZ<>pe^uef~)jqnVpM{+UskfNjFX4>t8Q(k&-C=-O{?tiw~0Um5u@!j3jSL!Wk+ zmbx?vohr2w=t@{dYftRRuMkY_HnmMNW-fVEJSV|HOy(4(9pQFa-q`NoE%-%TM;yw^ zcKxLvo{~8hm^M=Xg?#;Hw3s9N9L@JTBaq(4^l( zwX2=uQmw!hjoB(HmLn8P6%Mz<-Sa}gBZE*-Y+0ouZ3(O?udl3n) zDH0m#U#NM^ou}}Oj<>DmJ@kQ=xu{JR2#E~N)pRrT{=B1#`}3pI;iyUZ5uu+mE3O*% zqFk&stktAdqYS; z!5)kZ(XyT!+79F#A@*^{vHwHrT_^lAD-S!QKY|t7uk6|(jy1Lui05_4s#6UH7nwJL zHwoh6&T~ezCdwYj{vi3Gf1%9qY2yF*InpJCRD_?1PSz zu*`3{zob}2D{_j^g*6z3AT5zlWZgvLs1I=yOgEzfO1izlpJ!NIw%6M?l!c^o0DiZY zZMk--EHNi_mlU!eX-=Dv4yglOF|bCG-_z;RbwJ&8t}A)gAU0y)U!Yx7B=stFLUygp zp0SZR4tE&nM1V*pgdV~L8I_bVf3Zl2bJV@VwAi|I6<@q zy`A=iVvi*mXmNSkIIW_0fE#V=NUg}N5v^VMnyK~a_7_hVT>2&`M`b?e1(|!BccHY2 zhk96ZoiaPPMKz^5^z+Z61Fnpwlf<){#}y=PnY}A*KJyXO9soE`vx|9;MfHSNI5~Yn z?0MSTu!vQHqm(uHZ2X8M!$C{%-Tfhfv4Ls%L=SI1GC5hPjR0VdP`L_F! zx}B?&b$)4w3dW}kPLX*Q#?fRefzz!-jbmT4+{OA9uZo?u(Ctt}#~TPGtWh--&(}zd*1xt!sL&Ls(k) zI+QQ0d-36sb+efZZxcFShHu!b|2OuG(V4cBJ{a_7i9`q3-$66{F~lRxM4gtL6rWUl z1jkSohj~E;S4fP<2G|x>8EwZB2-pZ_Yq}!m40KQG#M=_D3Eeyv%MwkocBQ{xWrKR+ zhbQ_R)4@0=`n{rW{W<;Cu$vB1>*-4HWHv)Gigg04f_V8l*L~9 zXY+QFpU19S$mLs2Q{tr59R>*2W_%*&iT99y!dH`aHqSA?Z(wSOfvHtClb@u}Mz<0zXD5Gu2`YK^@)O2L(KhjP7CUgr)Z&^$E639gMCu;m3xIe&+ z$vL(Q6?Ixts#i3NPNG0^26dw_{Srv!Q3(FY#?hMVb+f`xs!nU}=Y6Y3SRMpgAvRR! zsMz``UM(<=cnN4hP09fUg1wdfZ)!7P0r56WCh*CgD9$nx7cZL>EWgLFOx({c65X8kx`)>6zi$VYe0OjE4MXubTjb&25ZeQ!;B_2 z^(Pq%CV{!4H(95cNZv=}Y|H?fH$IiQHLaewISdjTU+Ai${bqB~PWN{88RfjBIDD8` zOGXN(3IAqg$!M@v#Bp$i9%E6}ObB(?t*C7%w0y5|^t27f+l`$op6g0XTW}vKD$)mP zJ#a+ULUOdtIr>((i+n6r3_k~+Q$YN^^gzx-^OnOvVRlUko(7cE82WcAhf24~P zGd;{--q$1nshw~aJgt(m6F()3=wAeRlH2Z!5u-DOJsk^r3j8G7HW<6UYhV=zYAYW7 zQ~C{}r&YMWeFxnOa5rtwE&rC^`rM=9QrW}Ech^Cx$awE}4^6=9C7fbqfKD<)t$;q+G+<#J`DiC5#oBDGKVkQDwh&6h#MI5;2yF={8_d_ zxB+_gVUu zpRWPFAw1cpg*dV!ol8{jFg!qAq!&`nG#MDn3uj9mJ1 z&-ai398AoL&bPf&8k+7TSNq+-C16ggd_ikTHTt11KrD3vP4}%&wci5PqNVk7KRz*x zv|f*ofxRpj)R}bJFvNZU7STUbsdAS5yP$zL7c~X^MSKy{u$GEq(#MQ9!0UKr(=JLa zL6gKem#Dd#?y2$Kz2xrH*4Y?&Sagr^(*G6LCEDH8r6(v3(q2~*J(~?+&IObD9yNrp zG4d8igvJPNq(z`o$*$xxWFuN4 zK3_T%xGi0V?1e4HR@&cN>S_qizioGFyVt<-PuX@FcB7JZWyONVN5+xJ0}zcughn!3 zwCu*;%0EexB8CC4(EBJoGHlXb(h`c5%=70UN(D4hkMLUkuhN&E=`k6q0b((;q#2ae zvK5%K=q|(scECDB8MA$MU8?R;w=?guZKL5mGT|*LKh`+i=t(t$e&VHcSH#-f06&A9 zq%$13)6Fy$k{i5LIZ@s7qrk*5TaxKNr}9PPYQwMa4QM}6M;{8_$Q~)lKKf_EmLNcldO)E@^uZJc{1l_`~2q`~p(Pcu)2@ z>AZ}AOgpt3s?gmC4PfAqyPdBa*=isBo_{9+BoCIrB|G>RrLn|V(%599_OmT3%^w=n z`89ifzWg=K{kMZgRO{%K9Sn;s1mYZWIe9(BNFK`d3P<(J$@w3FMZO&D4?CAk3T%Rl z3`zF89pYy1qQd>UkDhkOgA6t8B1EJ5`R$p{#64wEk{859l|eZ5mh_F-${r_h zL=#N|qUVr4xHfp98Lu6V+2c?V84`A%crS&p30z_Nf=omnH$6{oRkn6<3&WK|f6uD! z;!o>2B%py?Y*NOZ7g+1Lix`veKJMhUHv~Bn3~dhdma1wlMHdTRE2c@NrwJyhsSx#q za+BCDm9pk5H~O9g_mjMQUCT(j$GqXpFM-YQd=l3?I<#N))wWjkzh94jUe3SbNa)8S zdm1}coN8QbbCDMdqtIgVJKFaa`(;Y`653pXgWVB*81qo{ujr8M1MLOQVKn;dDa&!v z=o(vdMTuu+#EvN7?Gq~7yyA3{^hmGH{g32z4Di@B7p=FPBT8OWUH@>@D$okx8uy@5 zO~V-DwbUJE3-CXhoHnI}K_SW5!W@8(Gwu+1$U?ynX`S2+(EzyjtY-=NZFHM0u^<64w7k5wN|W^M zhxSna2gXRpB$SLr(n7HxeFPW^EKN;g=1a~f=JKR0CH#|p1o;?k6sn!8vvz3R6`0Yp zA9o6vF29rw2$%Af`ktfT!@3%G`EYd`g8Dj9_0!**3bCFZ_BN!pT0%KaL$?JfgP7l- zUf?*9a@SeVoc3+W{Hgems zJ|cQRSjt&gOc0TL&uEn{n_(un%{&&C(k;2#F<#l)urYNl+=1R6nBC$a_o}2CxE480 zJQVobJjPJaP=> zCM_E=SxAyykRRqW5O*MMxWddNup=dMU9O>MMknXTA2as?H?qSFwQvw4m6%WZGa@$6 zG)z!-3$?3}XpVm#TT^O&(QJWttsPf8N;}$p1`yB?kPX-=Uzt_Hu47L_mo<|W1+_;%;u>Yb`3iD%(H&}XVyJ{elarE*lsyX0Q6FUH{pQ}xyG zcqLu)@yo0VxHaq?hAvSls|C6*j<3`?1h%$Lu5VSG5nT{?dQ9J|@kh^rjhs@o6dq$N3@{k1?QhG${3jep1mnV~5}hc>w9M z+d`htE5j8v>l(jSy^9@=P2%!-x@?mfeG^zsp)m%q*|`mD47ZVV8P*-rr_7|a3XOcS)JV*r&hU;zofZru zj13MmkkiT88S(Z=Dfpa`mGLL|JM$54A0~^4u(UN(RTmxmJ-HQaYG1v-Zp}2mM{@lS z${uS7`cVlHsKuWG<`7l9=YpGEPIV~6Z6#k!ZjUcPjACxiyg+-J)eGJ=@IM5A{fGF% zrgGgfbWe0@?y4hEX8^hV9Ie;Lp=8kKM!nE0Vtf3ftXmv`55;A!FMm6tUM;#VxvFqh zqsVzKk?w4vYzLDhJk*kIR^WQvXLN&lCuaKpO}Z(+6n{cXmUrqc7#1Ra|T;E|^og^ShxO?(AS*fXr#YSMI3a+Qer+ zkN_yICWI%fMp^Byrc!0?xV$M%n{*oZisp62?E2CAkBMBU3v~*0Bqfnq zAls7hgZ&OQj1eW^5Z8IDrH2)Bnf;(w-npKMv_9m&;?Jx#wQqFolOx;)fDhc_(artoeSj30Nr6QB6di>B zhy&=Kl%=!>F){s^c&+>z*GO-TYHcMFyHma*M!VQrifTn-Y4d9|jQT>N${N61$Jyn5 zgxCQ~=+=31lv{o8>n~K;e|rk7u0|_@I6?og45#U8?Msoc`amFfmo*?~JpYks8fhr@ zD!nZB6^9g+iV>o9ln`O6)sZC9ULxE2jvElwHXkX}E?EF9=R9ax#$6vZFurgSq&@$Sm5AJ#M#8MmW6u7|}>>Q%Zf@RqFqXfbdLa4Ku4kiqWh+UCk@pJJG zg%i3d0%q%5uF*e_2NFD{9f)M=DDT9lX#L<9$P@fa%+=~e4GV(ztBN%L`><8r z*T@a=6NIW$s-*U^uLX3SB!U!anlMwIJ^`3*QgTv0;Tmd|xSjm2{1tsKxgh260^Gmo z8L$aOWJ(jh1}Vu~Yc=I;M|W zHYIYq=RLZ9?JQtzo*6)z4C zBb=kecoOkX1~T1BnZl1l3iY+-5j9f-NaNn>1tmAXd@H z*28HH(nAtdyJpTq_%zD+c={eh8>pxfPNpy8J@>4R4<+8FVWPLKZhh4J87(x9L0=~7 zGvBw-Nsn;1dUV8yrm%uYx*3<*kgH`g&TDi^+MEI`V7s zRK@|0ulM@Q9O`bO*TYRtiH{KFvzj>bGkSR2N0zz-=ozW7XGX|xSq(d&x$X9+skn_} zDil_x-_$CyA+*M6#C>y?nupg?Kd-MUFZmkY?YcwW()7RIv(!4zaC}GBNboy(GgvC) z(1&p=IoGJ8sBQg6LM>UVWIqJU!7Q59d)HM0AB1_4Iug2Mylu`%m4?P}RWj-zcBY&w zhdp=oCT0Zaeu|@7`NX#POQ-sCpBGwrmUDrd@hO#}>TeBqBUDi%qZ5e4)X0bCw3OWA z&n1*GQf+`n&DDo$Pv{T%Ua-0|JA-w&r-D{lT1gjCUu=Y` z2Xo8Au%tE3_bC4T69UI$L!{V9#Rx|-~kD{*v&O66x6Hwp~NRD-e z=b5rq=$d+|a{2GJC45JtS%`hBK3jnC;LZKEKOe&Vh&Y7u%u zp-3WoF6s!1i4U#mB~n^8eztG6ad71%A3j(ae@DN??$^@55y&K|&+*5^`L_8!hi-J! zwsg$sbls*eLDyYVC3?SiQRxh|NuLFu%ved?3WTTwGQNmDN>$9CC^Yjj20~oq!zHU^ zzZugRNna2DdEhv4U|eH&)R`>lMKSLq8jChIHz|B4nn3924H0UaQ`T(`ZhFdFQ5vdO ze(tAvVBZ_ALEWx$E6a5E1A|#|=mh;fb+lxe;yypY&qmi#dr_=WHT|Vx|WduHEOQO>n zH^S?c=sh*>!WDsY=qPQp(2{kRyIV3b-U0a;4Yyd`L(~B;%L1>)SMa{|c9&VWxQo`g z<;OJ#O%ZezJx-emJ!TYSze|%8A<_`^B&HN~mKc#n#dl>(sof}z9vY&Vy%U=serS@` zSpCCdNK6a*W^RkDQ4Fp0FuX6~FI;E)$tH2be@z%?r|Nmi)Tcd7DbpWly2o9yMbpK+ z4w288K{L`fGF#>bc-`1plsd$BXf<{Nu}Ho{v_$41BS=Bth9rUC9XBzeFn+Fh;CU6x zz>ygXI7{TkR7lo@?t!_Aw>xfFXQ|fPwmY+GSJa*Qq__4nyhjZ6ysuo)Xfj++RYT9n zW1#MIPVObjciy<4j7R8Gx!@QDClCk8YY4fPlV~I!&zmf(|xGe=$Eo@6*AL3sjkUB%rf!0a7 z6=(~;PZ{dzWE|GGR9hSFS~XeoAP=M4U^x-(3!_xOX!xJutp7W>ff#{?0_!tE(s}Iv zSVL22GKw$~rWI654=55~BSipX_{!Nr`Xt!ECcMg_gAnp?2VfiNioA-66^-VOPoAMf zgN^zMIPB;@DI%=AY=qNb%Hg>iP+y{ zDE0wbD)~#HU{k3}u~XC9@f!vM?R1^h9jjBOwgmd1-;!VRDrIgqPjNQY5&sJ@Tq|p$ z)x8UGjd_)U;-lZXx#9LUgoD-k{2+|ty z2nmZlg8w14+g={2hwn$$2mAO(HQq(eb5~;Ch)#8@>zgAy%Zyq4sqM+3wxKDou7h)M z*{1S9iKnz(@R|8I(XF!+XFO3%-~i~QbSwE6{06VB^r3t&<8L4yxa<5) z=|S$2EVhBQJvEn-ZMJG;a; zA5&V@RFkc3@SwqQw3Xm-@)gmT%*~QNrOUAEsq3JbsoBta$tU@KzLcp!zq3sz9Hb4# z4E8AXP0Hrv1YZYq8}PeWl>3O!7S0XaL2yw6jII2W8v6OqYx`8%O1Bjp^IWzfNx#j{ z%eHCeSY8tWR#&JV%w>T&VF5>aj5-GUjd?%y5j$M8L-JD00ZVB$j%4CCFb?}O_|j5X z>+;fL|3p`VOlEoO_58N7N3dTJJn^h`n)8aDZyDs-R@6}$d!KIu%zq${hsKt@Z&+=Z z3?p;@hD40Fz>TaLNnGG#_ClRzRA4`()cgygrSdM&7pg39qIo~K3b!{}X*;H5TiD5| z!DpZoh_qx0m&<=ql;LS4zjuL+XP>J*9jPrVte^35iw0%)g^_S(rMY&m_Pg&PT}8V= zA4FR(!z%`IW9;on2)Ikjf}P~OmrPb{=5(j=;m=%3dQVy=?6eE0@2@qbc7|qyf6~Tf zAL73g5tw}<*9jLQJ(@LHT2`L(KgSChVZ3}%q(bnRw6#q$n43Vx=ygOt*} zSt8aq_EFRk(n+uc-j26k_MfaZzmn)dTbyN34!tG3#xqd0xnWOYbnFac4?xP=&o1GK zIizHqXpVn3$4pmiw}xs|KN@F$_m-=zxvuLdYW*KIFSTbKt*MV_4?rZiOakTroTXeh z@*qAyqoexso+viUcJaHDNLa4BJ2Z*yLjKRASI()$Bz}Zd0F&mBB3M^gLB2cnh#X24 zn1tqS)qjTL4KEsx{Zy39wdXYXuxA=SRR5zpX+KOZrmUg8rOgo1TV%5Cb0?zOVBj=A z9LpY?@lL*#zmgOuwDkyRNv;ZGZ~n6ZtnM3M5II49M&%3J@cJPhKr_Q zRrug?y|0d0vbtchLuyUolqO=8PCwL=N@t8aQeS~=?zh}R&Q-w*d>j!4+QG_cM8#P} zp(sIuC=pK%{xzRY-0%OQn_49dZ;!1MJ|B-Huy#!Z++sHTul6kGgNA3gmIclHO7;pi+AMS#Dvh0qm8;MON z1?HtT3a%5?A&f~Oj>rN+J4_-X5jx7XgB?er$*dw;Re{ZQ1tNAKgOy2kod$b7*6o==$n*1(}5ZK^iP zHYA|Ebo*LTug(}AIX!Sf{_=rt@pr#m{@gFq%3cVX3nk7mwtQQ_si;}Jeh`FYkj@b^Ub#@k+%` zIdb~@ewNnG3{^f(N&a-_$otVztFP9%H0a8+YgAHz%>TcjKHf z`EwLInEjWWcJfj2*?ZW>m*1y7`S(Ms7ZsxVT&-|CIo2a)r7G}t?#%^HFTHv1Sv|+H zETeq0`p+s-IQ(5iUjHz!mBC#+i+J8f!?p9(_FIyvS8Sed=Y8gTg(gpYGyldv?+>MZ zakb07Cs-G&owIKE1uMdpH?!}@kstHEX#eW@*Z*DL{cz5)+_9%#e04r2mATgcVc?6? zshP7B&0N;+LZ*R{e@0Y|OwD=S?QoX$KC?ud?Aby?BA)~Y1tumP`QZ45`UaYR-(PvS z?P<6P`Dpu=@-I=Sbl9T2D*|7CndY4_b@jXDUn)HsmYU)ArH7l(M&4ig;lrnb8C>_` zZnu4QKA}pMbpbiEB>C>kwLX90JZnNnd)4(>k!59CieKJ*9m3snuMRxv_2gZGXP3;| z-4=bW`f_IUy0qEfrl{I}6Y^9m@^em+``q^>rkmT^mpQ(KKl=Cc+82jzs2kPJ^!d2_ z?PTu?v2|{gc{btgOYeQzLbCjnWp&n+LYH&r4H=zzxVsGUH?!QoxpJ@Gjp)wu2jBnQnXJ}GC>@DuW;YqR$s@3STNzAW~x*5|HKWA2YS zf>rQlsEql3|JtfYpJK{7SArY(R|w4PpQAwad<}AC&H7dK&N{>EhPauhU`SlV`z)FL zb|rTGHa2T`hI-hw&F-y#`_=wCxk8rR{#%N;vR?>mlBsm+ri@oV=ZPKuX3X;w$q6@> zK3;V+Q_QG$S5wdG8@E0@colQx`>ntte!mA6^bbx%BM3f*lGeJiHc*VS2y`xOif4k!~jFQR_%sG!m^qmTCa#k!sM zbLge8)xn3n+lvaH#%3!Q@Jg47zjnLVv$eK=+KjAY{Fdjx<@Yf6t-x_&yl-;ytTzo~ z&OF$VeB)lnn0qH)M*sEZz}F(O@cq5f9b)=^8t#81)20C7S12MO^5<+XvQ-lUyfgTC zSxfTlkMNE-7CgY`t(EV~$>1%)zqt8+x%~9W`=UWTyoTe!PxIWEnAORUVT;`=&~}S^ zoA{*b)sjgUpC`R4b1CZPymuYHkIA$*zH{7Iaaq<6s^(qNJ1Jlw67yNc2YD*w?3sOF z$XoBi0hKeQW+~%+K=jGvmJ*t2yY7?N>D9BRp|Rym8Mm5=oj=ukAN=*Dd!a1Fy*Iix zB)5IO^UaII*BNGJSmt{5e#h(CpI$i6eL@0;xW(~bIp+Iq37GG9Jm6dQ<^G@CYNp)~ zy}aiHhGuG>wm)T3Qgg@HC1e$HcgKi7J|B9kU$_6>SM@W0B~6Qa8h19?&wHQmO0OKM zuWN7Op2X{x%6!>xxO@4Om)|-h78F75W0fOT>w~VIsZE?;-QzPxWIo|WekW1c^6|dq z+rqD;=R7ggDXgEmDUZQ&uj|0KYYD64qrQ*R8QpiQTxpAwnx^cQQ!@PPHO@qT-}G_V zC$GfO&L8TiysJB>-b^f>bS>>~=_eBHdue~z-lC@!>*49O+{0U@+5L60DB<4OtFf1_ zS7nd(?zP=ddi~-3!mFqII$bzrP}1qtWT%Yiu1mN|rQAsc-;^;j%zSWNOU|3LB5_>G z^VBkF8(r}!<&wiv8awH^Ki$n36E9y`$K4)Tm)vH!ue55*17sfl)jIBe$YYG>0*?#s zXWZ9$j`8yM^l@LNCW+t774blNC~bP$<5LT}HruPo4td#rb0~MPA8{|^U1ztXLf#ZX6n-B0olb~icNBedS;*~oP0CsbJCjR-%?(soJ{dfxtW|qY{Hqe zj&=__gR|ASsP~x`GN(1#EvLK7?Yi4(_t74CJ>Pg-@+j}I%e{-cuUi(Yt@=;8$s{sO z=GG^jWlnvku^pfGOWKySg=zEC_S!G(E%voE{-dNwS%leU}F+WE_g zb!s~+oJCG%r#X2PqZ~JTJ3dd^gG_O>Pya(Vq1v(nh{q?au8;OU&Uw~z_*k#^~BCW(BO zwaGQtN*<7f)dkha`l?2%eyXq9sNSexYpFV-2B^9!O zMcxtNVhPy?E0~*Pj{N4hkpr{4lWH%Z?XC76`yV^Z`Pr%KtaZ9M|2a9yDVmimg@Rn@ z{m4cgPKMmQ0c)alm{+RuvwpLBTAi(- z)_t{!{V$l%oCGQRHc1mvU9R$$qmv?#mMsNjy%R%_^B}} zPNvDd?5PKGJ$Z$D%f7M~S@$Akiii`P*ugKwLh|F)CoAQ3a)WmxQ{heW*l#zdpu7Yb z-=j<;@+AI3?!F}9A*aY?Wb|vP7PF)N;;aX&MXI;@D9fmd>Xv*(Hos0}tONJ{6cQXS!Cr~LB7QfjCrH*k^wSG+!WQw+^xw6c$>V) z>*PRrQ(oX|2pI+c;1y2GVPr^tK<3C5(6*O-IGl`u8JW2%qA&BI#7eRmUn585H8ODT zr$=ArD9L0H<;4)z;x#>Gm-oe2@mVaBi{)=}k-RF=Eo6SVTRakBazAr&m9{>S;WAJp zlRbJqC)|(B>Z|m1_UV4Ib^gZRS+-;SXSIFBH?o437G*^y=!_)Kd>?XBC$b9?;Yo>HA0!|4RB?b3`NcBxT2CZ* z=P)u!M)B@#?vNG!5ArKJwRb|67K=Ca)@croQv#?1<3`!hTP%X$ag+p{4Scp&ohaKW;Zi5 zow;ZMZR?S40r0RqCPiDE$8qF;Ze@~n5x8-*$t@O}hvu8f#JUHQm43ePmceq2ILzw1 z$Or1MTl$f?JAobaig_Bx41Qxw3+cNiJuN1CtTIb!)tBs&1EFsQXQ7OkNlx&$#)p;m zB@2CeF7}*cr}yFgxG5k8kcrz{?m?Pd6`y$B6K~nchCZT2IjO~1%14Vq^e~XzmMz3l z(*s^tn?Ap=OLy_N(L~bwTi(l4)-aBvtoK+(o)d}-aguzQ|CWrR4tt<1rN%Rk^xUJZ zezYva6>@v;F_)n|J6Tl?R3C+tlrak#%QSYGq<1AQF*ni7r7yW_1(b9Vg*icanD3Uf zxKuP3r)WD$+z`__TPMhxx=Y+*>}@%_@vO!&?slW@b;=Fm>XaBvJzuel79W_;W+*Kl zAb0vW&hrWO`UbI?vdQAHILMp~kU2SXZKS&lm2KfmkKx37MOL{64p0pqFim=JZmP;z zoYzzA=W)?{Fk}pqYWR_vGYzkZboePfl`b-cmm|xpH%}Bd?P6+rvbYO*~O|B2)8jeVm-U z3&`3$l044qbS$s+`U-iJOOoBVD0$6`kkdF;N9zr`5PM;c*})p#GYw4x#<-n)vcGZX zFcRerBX1yHqWeaQfFCd0G!*B}0&{^f(dH|oZ37oM3}ut~oJ^hVWZ2HdIn%WHndkGF z&v)n}J1ITK6lMRtVNd5K+iel%FSB@ME;6qbL?veJD_6sq;YpluY0}?U(NAtuo;J&q zQ}{pql`PQT=)D*E#2vjF`U~`eyiuF#dB%R)sh`piV0=ikf?=<_gzO?6uHM34Re?>?)W!^a}gtunx?_L0^4DgAHI zGj*`p$WimF6Fwm4=d^;7*tHRh~_TjyN61-e}H@%%H6Vc$PaOyQb62{t2g_i#=6< zzPposI>K~jR{FE%cgz=hY)U_!;eIc89iUVdX1WnAH6^Qklpa8xv5d(gE4{lp&K`UK zzg^2N+pV|hYdXYS*Y_A>G&ACEcCxFRLZd^z@BqeGo&3&Qx$__6D}o*sh~x?5+eWBU zTqSBZnQ*`~_aE!KassF((ro-QsQm&e~Wvarr zwK#{?aEE7A&}k$$H2vJ z!=3KX`av-RN=A!LJiEua>I7v?d6pSQIpc?B(vh;W=yMGGEu9J8) z33^9=%2tN!G=zh8q})o=2kAP4+08;`>21h{w`Q;L7cW@J)5a2eIO7H3+J&jpkCp@y zpeijjq=(8-kc$&noROD+rhC+wzzj-Gs2?Z4FFaP#Qa^S;L)IsT)AyW{TwI(sA+%JL zXL+GXq17EiM|6jtLCB_?<~8(LqP_T&mRG~WSC9kwzW9Uh=dc-aGeQsat8h6E3KG#- zOTvd|QD!A$nm|tF%aopgw)QLI9wgk+3B%EegRoKCNJ}0SrLYH*Xk|O)yvR{~2!1~r zE$|-F&r|+KAF)W_*|awsDzDPw4t7Ct#$F71D#On@it12aoOSTy?ml+N4bB=8n3?E= zOv%et7ixcjE1n~d_8O?)A%5b{MCfXPURg>cFdwJPOE}#p=IjM+mt?*dquCxXAK8sP zIiHI-Lw@Y24M>5K^wEd1>(NIhBNMxmyZi+u+rg#I!B=}DJ2%4jPIEU8G~T4`V$4to zk|acopxu(#H>Hp*XIP7_+}#g#d4)godW|)GX$CQ>iLA^-RySQ*4n(d+i8)BdYpm-K zIPN6obS-CLAN8I?_20DMFFu=}XmbIY_;WN-^W!y%_NAB%EjRgWM$ZzzH$KF4v+qDlX(19VYc8rx<)+Vn}{Jy$}G)CQ{MnDJ;^NY;_N(QrwwEWJYzoybdEEWon(H7BM+mk z;ys#0?dC&x+;ND}o_XNrJ7`=Hr*T?i${f9mb zFK?&Y^7Jre;`DpIU)Gt-6`j*~qtE|K-O|j9rzzsD4VLQAoDger@_qS zL0YY9k|1^#_|llk`G>^O&GiGGb3CDy{p;f*O|Ae?8+}@ zxCmo+KR46G0<7b|;VKI`qY{9$m=_EAKgY@zcoi!h) zttRiADG`bs=?8xa6eD=Lf-@H;>Z9ufaAGBVligfp_O7v0uJPFtJ^d>4wTl*O(?@OQsTNc?)bisjpMa`INSBfDY1^#g{tBdw zKYy)3U20J~ACmEd`31UKGny617eAhi!JdC2uA82;Jd?gc8AE9#l^^%Uu$No0$Gan? zrXm&cF}iTTBeI(>J>=Z{1j#Vq|UK8iN1R$2OXW9|a5Sf)}deHL5ORvq;3QlN|Tg|NY;o4z7uQJ=AKgKhI`%ftEC)%Q4wnv9|gA#nDX!_gH zE%R}fN3jlmTzzK$2JmzywU5B}ucEbo2OvhqfL2Cu5Gva4C3IVUMK zN1gy34OiWjK`(hGGpc8DnA)u-gZ*?+Il!&nNlOi)+-+*#mh-6JAN1oHyZspW#BSy; zke$AUx&8%dvJ*}(cui;DX=YNgd)%;e)9s+6%+NMQF`bbYL-RNeHyDEz7_WOW`V{Sl z9vUcmpuy#Vf8|Ak>;Xrb#+>ywOPJ*Z_)Z-5i;ua8Eprt6;V7ErC`vul1+hyUaMdY%?siT)jr1$WQxDR?y0Xr%yI}FS&^-qs2Pz^tT*!|5aQ`#ZTSpJY z;DSA&VJRb6VwTfGHFV9ZrYxG)Rnv}{ZVxWCj`da8=1GjZKiWYG`bH6Os5@e~{4Sb< zb#+JA@Q{CtAzXngW1*gBpZc&id6>gy@(nsnVY!cbHRMe06_ly8yPjE01=&B1MwE_& z8g#4IVlKU`qp!IjGxdMq8)q$S!nSm^&P!KH$$twg3M^jUlaOyXhx#vZ9)&Y^W!XQ zvkm|p^vQQ!KW*O>ISQOvWu#rZpeGmUG0&pXnQPrVryO@vN+tkxNHOh zP*Au`6{y$>51ax&B&o32f;G_?i$x&=mkLb~M#y^7M$sr5!l}7l{moyzYONyh z9bBc&jq+DDf%de_rxtQ`85}sXdQ3kCL^F#jt;+H2gS;Z+B_VcFsrB+d`B+X=`XHamcIq?VvVgoAc+Nmj*a*r@Qb8(OR*~)bevOgcpq!hkudd1`vI30QEa-+el4Qmss^k^&8*A@ZktNHc#+RTm|c$3QdJn2RTd*2GwmM{bd(s?+UnF zm@I(Ls~mi)b2`2+v&bo6JzbFeSK(J_XsxMOGwYGGgRqa{S^tA*QwPv7L(n+?)D2mU z1bFx(_OW5-YWD9WczIS4#~Hh5E^*a{y^?|+I-gR{(C@na&}=n4?=srlB@jR*`kVE7 z0vgE$^p^4TqTyPhA}eP$3n#Au9QqpPU@8{nHl$;AaPO0{682#wbdMeKcUctFx)MH$ zZrtY|y@JHQ;dB&Hd6>VmAj4dgxkc6YIhE=W;a%?IeOd77y3o$7mzOICN<*Y7N#0@$&5l^T|%>Ijnd;e6a@P z&7rjq$k_{~DY7IOEvX8c^;`C8B!3r?CH>gJ7Pes!81M=-EPrg`MwEKM890C^#$qg& z7=?zvHlkh?w5p%M3<|KPE~1I=6zdr0Bl8%3yBi$Tn4c*zi&JGITknFr`XU2o!uJNF zYY&F+EtX4UJ2?~-HdN-5^<;OnoBK%7P@Z+di*t^7J%c^86aCN!gryTQb{L3k2YHV< z-*38!5b%N&DEXa!BA~Dnn8tn1W<_@QMeOH!@cUNSoMX*lY{5x7m)?YItBnQo6gwu# zIf|e3n3L$7a5g(PoyATArxdRNcu_Yy-t5akc+=WBE<9}MKHHc0RhKynoP5qEO678P z*@*{)K{KKWez^lAMmV&IWp#Pl?*jGhTC@nw+ok`U)rNxpUCz&siDZ zJawwk&R3_GuH&?DYSG7MXM&Sm_i-jUm+)dA!s;EP`(m%gI2PyYx$_8p>7o7`&&6Ks z`EK-DZLS;<^HK)Xx;P3TDre__Xt){S^|1=k&g)Q&VG zX{U+yr|vtrRJt8pj_-c7(HIOk8vU~gwJTC%rat8~(YDit5tKu3dh5ialSaS+d+1bW z7PKr!I|zlgCRhc%%yLlOuI4_ibiopjMlYVlJdXq+Xo}{XA8$(&W_kuEyD@0W|IpGZ zb2W;)O|g>y#3!2-+}hDS%yu~9a8nsueg(ZO;r+X_@X${}t%}Hghu)S=fa7 z0o1<_uD%-I`fX5>+h~TzXzw5NqbWKB&GC0V7?1H?H1S~|DtFM*OX+D`)#v_DbgmFQ z9mDl_&RqsQ1Pv?6xvNV+Q<4tlZ06=Yo34P@!UriZl=e>O40;)O!FFCJwJ#&`(4E0z zR_i(-2pJh|nx1Axo0rh_N>{+{SPV70v76KA$%TZN&S-p)aox}WADS+FHUPJ20P2#D zS2v6^(K_Ps>8ZOJZ>9GQ-}~h-1E|k8CWJ(!y`V`vFitUt)T&;ob z7b>rSu|$FFM2UQI3?8@2&=iEFbOf(bRkSS!I;-MQ$|$RYaK6E=n#9vrKWJ_VI#GbS zNl3KjU?Jo1Ce9Hxh}xNmhj0P-tud{*a~xc!2C}ib_*eXfHC`NSv@Ozb5i+k3(xr2{ zozHc=DTH@BeaB>_af7 zK(S^KvMP`nZNXwF5dIxftiO@m%a*Tc>P43I6iSM z@F_*K8TVsM!@(f4iIdF7eQeBVbl5ZO`gpXvoah;sb#cm0B?{s{oe|V72RdvnG?lNQ zoR>goAG7b=Ohv37ANYJa$A95u)(v%vbAS_i+ex>FZ7=cwuRM9C_5w8H3!+ zpx+=TMB}1~ zJrry%q{A;HE%Hezstk543LFut70df*}(0y}UY$e9Va4PQd{U;Cx=ZD^P8$`5_+zft{ zA74mo_FfJ2;&!y!hCRHPJ$Xu;U=)!*(4S?ry_9+ri5Qqf>v!?Cq~lGK#7Qjt!qUc5 z?vM6d2rQ&9_)-nfj$nD4yAI{vi@sPh4p?goO5DX3F=7!mT2(2DMtP5a-4S=CmbbAf z2dKH~w0f;}{D`4Cg!Mn4NT)zJhLL^YB9qiF*p#ufcTY~ha@5oxAOpD9Le&KO%F4Y3 zSfS0~A-}5o@;FZ%bjiM0SBvq+ZH3$PQ}J?=tS)~OlZg-diFls=#6i`7ssXYnZA^o* z<=EXtxqlk!2Xp;E20_Ixo@7^xc=e;#1dxm0RaJPD1cUe}2Vk4J)9W;B?MZ5nDxl8E zLaL_9#;CGE#~?LTrAHyQQlHc^aEk;uqaqS<2T$*Vx@A*EiH9pjjcHKIyHeQKwTNme zq;jbs%frfMeFE{=g=HM1o~Z5E`b&v(oT66IUOh&hOSxIattY$>u?|>8tTl@Ne`IyB z+7hAG-dbbjvN~Axi684^eOCLd86Ybj#8@`4RuN&ik9%uqeV=+v45K@djP2D|^@aGu zl|;@>15emZjM#M5#rn&dW$oqJQ{`oqw!CO@HZ6|gn*WN5_5MXAt1s677-cytgLRR( ztd7|HUe+_No`Npyk@bknoTY{m30O}Jkza_W^5E)%yhx1TKKTeVeLA+lbY#N=u}6ND zxl|Wus1MeVAj_$TGDHniS3ow_s)OJTiOgLo=?VIgNc7rwIYM>7dfOxOAVn^Lem>*6 z6LP47{68C}(3m zO=VPW_%gD~r`Wt*h*3Q&@-qHC$kSzvygwAV%Z{AZ#*8;z;(8JzmWP^;X`?Pk=Q75* zA9Vj3mgsZJ3L;3SK>0}8NFS9XitVxZQ$Au0xnv$_yTIMtV9IB}jR&G7G?E|15u(q^ zL0d!G?*g_p8aeNR!r5Sijb(zUDCdJx`KTU@u>eTWY4skgW0A^cwNR%xW&Ua+xL^tE zj#^Iq;w@Q`Gf+w0lrO=GDswJ;;Tx}Mv5G1Kp0<(saOkD1d@C1mGAF70>L+-~GTL~@ zvua>bE^xz2Dl16XRFz7NIUu@KIrT5#O@D&{yam<$4Nmlimj0BR@0#SQeK+#sF}@gFtBgXG1CZ^~G3nIh_Mc^N#tJacgyo#-~WLPl0H zlN^Xe91Z?EOCA(uX?;37Dv`Z044gkZGoQ%L8q9tg%v`?1Q(YY-{ynz)W;`{U@KKN) zR`lZg9ilS|!^8J!MVV9P7P6xew)_}&WCb&X7>q4Ep8#^Z*@UBUjz{ksOk9w+xr>$` zf(LdSUa9+dcu!D95KY#LC&$3$H-crn%PoWa^!<&PK<#SmoCwn# ztm=^-kG-;2x7K%vzKzj&%}ONt7c8%*SgrHXiN7%qPtkdLp%YxfZmfy6y%G&*FV@O3 zbnL^(+|R_`G{nZ30xoniKaL9exBC>0qNviB+LA2 zCH{F#%9ekS7oSPk-X0L;dp`}>?Idz#YD$jb%Sf8>uzd7>IhdE*KpS{*GN|v*Ddb7Po0ozQtPI@rjOyS4X&YSYoH(tloX_g z8fhKUT0>`L#?ZoEY5UnJX#?$6b|iNO+n3Vz*~{(8Ja6tiwqxvj%zHWJxT!PQe#}*b z)5Q7B`3I~kH+abcR(u|a%Ly#@U(to15QE&%Avr)j z#$oT4F#li==Rq%a!`oFJ`=cIS#cWtA^+23+n385bUa4bnx1-p=4Y5W(IitaWx`TkZ zV;h`xa_DW&cxYMRJakSvxhRnXR3hu9i`L< zr;NS_rP)BR>pLOXA!YSuegia*Uvrey|2S@X0kQq}ojF|30RdVMEp<$H5RaVvUgkZP zN1QGNl}EuG?}Nvk#5385vD^b~+5{bL*yvlJA{$TMVzmTA$2@S3d5mbok4Qa>F~rkX zHn5I5AVx*ZLfsyms2L9rf zp-H?WvOFuA{eN5+!;`7x2x{KMVt5B?9*ad1$KAWw9(T~Eiy{l#Bk${A(;q_`bfHX9 zbP-LdIq-Uc=jIPA@Y+zI(1|`^=M)!RK_upa=1Z*ObVTJ2ku}+ncE_=ZO5$Pej09Q> z=YNGniUrx2g9qR(oHxBj9bzAja_=aX?qSffTZ}q8D0)eAomqNMTt$7+fu|d=rApBM zdiGLLW@Hm`YYu-laqWR30jlmmZ#TwO9JDzr(ZZd$GZ*}NK9OaOkot{eZFC+fN6C{Q z1f@YPl$=Yw`iwgsO)eWV`9Ey0c*Z-5*;2&#m*ieOyb7^cV(Uc@IU6}&n_fl;#sHmh zNXdDkCb-ZrZ1rNSsYD*m#dirZkL;bvDh7hYc4B?s61m$6`@Im-;BTUw1m1+YjIySl z&DerK?`|;0>U&Ct>NDJ4g`#1NAQ@}bkdY!qUM=*

    zZpGX-6w`@{?uaFMg8ecd|I{6#)Qh0Il$7(q_j95LW|ybX#wQTVms_!h(DEANy{xRZ;JF-2y$b4)JkPVA z)mDCo(hJKm1G;2i)dT;|V%Y>;GmqLvH24GTg%4=KJ-PmZRgxga$uPWY8C4@R)DZk| zO;tH`!o{p`E-c7}c<6eeYe%zxf5rL@k$_p#+0F(Wy9WE5Ec_jjqI5ogmR+2 zWpVI|Jy<0rh#vdID-YWJaddSr<~EF#C_#kMNihu0Vu;2O{3dA}tMX7BVt1Fsm!DVW zfQ#j0{kCA|T_;jQo73F?%->elWC10{(SjlVsT-@G{#=QFd3E9BH$c$Kbwk;M^DRMMU72$c(030KaE@VltlT873KTOkT8wJ;YKrLL=yfwr~{f zaWN4V6Nu$e=nv=6Cnh4Vck3fgHDb#OfNR%4Gp|mIjnOavLNY|cCw+MROYh?t?NGe* zk~rIJ{LZo%k!hdser~~Ads++x5ef$dS|y8v$QZ8H$hn}>Er}__erduwRf4pl^{oAFpzU9k_tSevu_En=VbW;exC zX9qhq6IS{|P=&3cjf{YrNczu&RXI@3!k0RfaZH!#zraakPYjWRc+#DbR{~4_DoUv; zvKstpDe~!WtACC%&Z*aX%kq4^^G( z_Qdb}O)05{QF0*BVpx-A@+kySKn>tHqc?aeES;zCxbsL=F zrfdaX@r6+yXMc=@mS|9@PyE_A0R*#&8byB(@TaUKt|me5<2R~JRW~?teRyzP>n#}G zc&m>pX7y89tu*!6$_Qfj23{KkqW2b#zDlKkURJ_7X)U7OIcq*#S%V8sQT46U{3^JZ zHI94r`HXbiZk@4$t#15gc&c*1;;w>YPN9{`V4|&&zt?E1FTD8<5-2zF*G)YDr}04I z=2df8;RrJly4#GI9CN&^PON(HQ58z!MVnPS1l5q*+0v`4QimVc12272Z`<^pE37- P{K6~!vjZ(OU #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 +