Add chaperone autoprofiles, crash recovery, and space fix revert
This commit is contained in:
@@ -300,7 +300,8 @@ OverlayController::OverlayController( bool desktopMode,
|
||||
}
|
||||
appSettings()->endGroup();
|
||||
|
||||
// Keep the settings for vsyncDisabled here in main overlaycontroller
|
||||
// Keep the settings for vsyncDisabled and crash recovery here in main
|
||||
// overlaycontroller
|
||||
appSettings()->beginGroup( "applicationSettings" );
|
||||
auto value = appSettings()->value( "vsyncDisabled", m_vsyncDisabled );
|
||||
if ( value.isValid() && !value.isNull() )
|
||||
@@ -324,6 +325,18 @@ OverlayController::OverlayController( bool desktopMode,
|
||||
m_customTickRateMs = value.toInt();
|
||||
}
|
||||
}
|
||||
value = appSettings()->value( "previousShutdownSafe",
|
||||
m_previousShutdownSafe );
|
||||
if ( value.isValid() && !value.isNull() )
|
||||
{
|
||||
m_previousShutdownSafe = value.toBool();
|
||||
}
|
||||
value = appSettings()->value( "crashRecoveryDisabled",
|
||||
m_crashRecoveryDisabled );
|
||||
if ( value.isValid() && !value.isNull() )
|
||||
{
|
||||
m_crashRecoveryDisabled = value.toBool();
|
||||
}
|
||||
appSettings()->endGroup();
|
||||
}
|
||||
|
||||
@@ -365,6 +378,9 @@ void OverlayController::Shutdown()
|
||||
m_pFbo.reset();
|
||||
m_pOpenGLContext.reset();
|
||||
m_pOffscreenSurface.reset();
|
||||
|
||||
// save to settings that shutdown was safe
|
||||
setPreviousShutdownSafe( true );
|
||||
}
|
||||
|
||||
void OverlayController::SetWidget( QQuickItem* quickItem,
|
||||
@@ -716,6 +732,28 @@ void OverlayController::processInputBindings()
|
||||
processKeyboardBindings();
|
||||
}
|
||||
|
||||
bool OverlayController::crashRecoveryDisabled() const
|
||||
{
|
||||
return m_crashRecoveryDisabled;
|
||||
}
|
||||
|
||||
void OverlayController::setCrashRecoveryDisabled( bool value, bool notify )
|
||||
{
|
||||
if ( m_crashRecoveryDisabled == value )
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_crashRecoveryDisabled = value;
|
||||
appSettings()->beginGroup( "applicationSettings" );
|
||||
appSettings()->setValue( "crashRecoveryDisabled", m_crashRecoveryDisabled );
|
||||
appSettings()->endGroup();
|
||||
appSettings()->sync();
|
||||
if ( notify )
|
||||
{
|
||||
emit crashRecoveryDisabledChanged( m_crashRecoveryDisabled );
|
||||
}
|
||||
}
|
||||
|
||||
bool OverlayController::vsyncDisabled() const
|
||||
{
|
||||
return m_vsyncDisabled;
|
||||
@@ -738,6 +776,19 @@ void OverlayController::setVsyncDisabled( bool value, bool notify )
|
||||
}
|
||||
}
|
||||
|
||||
void OverlayController::setPreviousShutdownSafe( bool value )
|
||||
{
|
||||
if ( m_previousShutdownSafe == value )
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_previousShutdownSafe = value;
|
||||
appSettings()->beginGroup( "applicationSettings" );
|
||||
appSettings()->setValue( "previousShutdownSafe", m_previousShutdownSafe );
|
||||
appSettings()->endGroup();
|
||||
appSettings()->sync();
|
||||
}
|
||||
|
||||
int OverlayController::customTickRateMs() const
|
||||
{
|
||||
return m_customTickRateMs;
|
||||
|
||||
@@ -77,6 +77,9 @@ class OverlayController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY( bool m_desktopMode READ isDesktopMode )
|
||||
Q_PROPERTY(
|
||||
bool crashRecoveryDisabled READ crashRecoveryDisabled WRITE
|
||||
setCrashRecoveryDisabled NOTIFY crashRecoveryDisabledChanged )
|
||||
Q_PROPERTY( bool vsyncDisabled READ vsyncDisabled WRITE setVsyncDisabled
|
||||
NOTIFY vsyncDisabledChanged )
|
||||
Q_PROPERTY( int customTickRateMs READ customTickRateMs WRITE
|
||||
@@ -101,7 +104,9 @@ private:
|
||||
Qt::MouseButtons m_lastMouseButtons = nullptr;
|
||||
|
||||
bool m_desktopMode;
|
||||
bool m_previousShutdownSafe = true;
|
||||
bool m_noSound;
|
||||
bool m_crashRecoveryDisabled = false;
|
||||
bool m_vsyncDisabled = false;
|
||||
int m_customTickRateMs = 20;
|
||||
|
||||
@@ -182,6 +187,12 @@ public:
|
||||
return m_desktopMode;
|
||||
}
|
||||
|
||||
bool isPreviousShutdownSafe()
|
||||
{
|
||||
return m_previousShutdownSafe;
|
||||
}
|
||||
void setPreviousShutdownSafe( bool value );
|
||||
|
||||
utils::ChaperoneUtils& chaperoneUtils() noexcept
|
||||
{
|
||||
return m_chaperoneUtils;
|
||||
@@ -199,6 +210,7 @@ public:
|
||||
vr::VREvent_t* pEvent );
|
||||
void mainEventLoop();
|
||||
|
||||
bool crashRecoveryDisabled() const;
|
||||
bool vsyncDisabled() const;
|
||||
int customTickRateMs() const;
|
||||
|
||||
@@ -215,11 +227,13 @@ public slots:
|
||||
void setAlarm01SoundVolume( float vol );
|
||||
void cancelAlarm01Sound();
|
||||
|
||||
void setCrashRecoveryDisabled( bool value, bool notify = true );
|
||||
void setVsyncDisabled( bool value, bool notify = true );
|
||||
void setCustomTickRateMs( int value, bool notify = true );
|
||||
|
||||
signals:
|
||||
void keyBoardInputSignal( QString input, unsigned long userValue = 0 );
|
||||
void crashRecoveryDisabledChanged( bool value );
|
||||
void vsyncDisabledChanged( bool value );
|
||||
void customTickRateMsChanged( int value );
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ MyStackViewPage {
|
||||
id: fixButton
|
||||
Layout.fillWidth: true
|
||||
text: "Fix Floor"
|
||||
Layout.preferredHeight: 100
|
||||
Layout.preferredHeight: 80
|
||||
onClicked: {
|
||||
FixFloorTabController.fixFloorClicked()
|
||||
}
|
||||
@@ -69,7 +69,7 @@ MyStackViewPage {
|
||||
id: recenterButton
|
||||
Layout.fillWidth: true
|
||||
text: "Recenter Space"
|
||||
Layout.preferredHeight: 100
|
||||
Layout.preferredHeight: 80
|
||||
onClicked: {
|
||||
FixFloorTabController.recenterClicked()
|
||||
}
|
||||
@@ -91,7 +91,7 @@ MyStackViewPage {
|
||||
id: zeroSpaceButton
|
||||
Layout.fillWidth: true
|
||||
text: "Apply Space Settings Offsets as Center"
|
||||
Layout.preferredHeight: 100
|
||||
Layout.preferredHeight: 80
|
||||
onClicked: {
|
||||
MoveCenterTabController.zeroOffsets()
|
||||
}
|
||||
@@ -101,6 +101,20 @@ MyStackViewPage {
|
||||
Layout.preferredHeight: 32
|
||||
}
|
||||
|
||||
MyPushButton {
|
||||
id: revertButton
|
||||
Layout.fillWidth: true
|
||||
text: "Revert All Changes from This Session"
|
||||
Layout.preferredHeight: 80
|
||||
onClicked: {
|
||||
ChaperoneTabController.applyAutosavedProfile()
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.preferredHeight: 32
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
statusMessageText.text = ""
|
||||
//undoFixButton.enabled = FixFloorTabController.canUndo
|
||||
@@ -110,6 +124,7 @@ MyStackViewPage {
|
||||
fixButton.enabled = false
|
||||
recenterButton.enabled = false
|
||||
zeroSpaceButton.enabled = false
|
||||
revertButton.enabled = false
|
||||
undoFixButton.enabled = false
|
||||
seatedWarningText.visible = true
|
||||
}
|
||||
@@ -155,12 +170,14 @@ MyStackViewPage {
|
||||
fixButton.enabled = false
|
||||
recenterButton.enabled = false
|
||||
zeroSpaceButton.enabled = false
|
||||
revertButton.enabled = false
|
||||
undoFixButton.enabled = false
|
||||
seatedWarningText.visible = true
|
||||
} else if (MoveCenterTabController.trackingUniverse === 1) {
|
||||
fixButton.enabled = true
|
||||
recenterButton.enabled = true
|
||||
zeroSpaceButton.enabled = true
|
||||
revertButton.enabled = true
|
||||
// undoFixButton.enabled = true
|
||||
// TODO Fix Undo Feature -^
|
||||
seatedWarningText.visible = false
|
||||
@@ -168,6 +185,7 @@ MyStackViewPage {
|
||||
fixButton.enabled = false
|
||||
recenterButton.enabled = false
|
||||
zeroSpaceButton.enabled = false
|
||||
revertButton.enabled = false
|
||||
undoFixButton.enabled = false
|
||||
seatedWarningText.visible = false
|
||||
}
|
||||
|
||||
@@ -58,6 +58,13 @@ MyStackViewPage {
|
||||
}
|
||||
}
|
||||
|
||||
MyToggleButton {
|
||||
id: disableCrashRecoveryToggle
|
||||
text: "Disable Automatic Crash Recovery of Chaperone Config"
|
||||
onCheckedChanged: {
|
||||
OverlayController.setCrashRecoveryDisabled(checked, true)
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
@@ -126,6 +133,7 @@ MyStackViewPage {
|
||||
enableSeatedOffsetsRecenterToggle.checked = MoveCenterTabController.enableSeatedOffsetsRecenter
|
||||
disableSeatedMotionToggle.checked = MoveCenterTabController.disableSeatedMotion
|
||||
|
||||
disableCrashRecoveryToggle.checked = OverlayController.crashRecoveryDisabled
|
||||
customTickRateText.text = OverlayController.customTickRateMs
|
||||
vsyncDisabledToggle.checked = OverlayController.vsyncDisabled
|
||||
customTickRateText.visible = vsyncDisabledToggle.checked
|
||||
@@ -167,6 +175,10 @@ MyStackViewPage {
|
||||
customTickRateLabel.visible = vsyncDisabledToggle.checked
|
||||
customTickRateMsLabel.visible = vsyncDisabledToggle.checked
|
||||
}
|
||||
onCrashRecoveryDisabledChanged: {
|
||||
disableCrashRecoveryToggle.checked = OverlayController.crashRecoveryDisabled
|
||||
}
|
||||
|
||||
onCustomTickRateMsChanged: {
|
||||
customTickRateText.text = OverlayController.customTickRateMs
|
||||
}
|
||||
|
||||
@@ -644,60 +644,7 @@ void ChaperoneTabController::eventLoopTick(
|
||||
{
|
||||
if ( parent->isDashboardVisible() )
|
||||
{
|
||||
vr::EVRSettingsError vrSettingsError;
|
||||
float vis = static_cast<float>( vr::VRSettings()->GetInt32(
|
||||
vr::k_pch_CollisionBounds_Section,
|
||||
vr::k_pch_CollisionBounds_ColorGammaA_Int32,
|
||||
&vrSettingsError ) );
|
||||
if ( vrSettingsError != vr::VRSettingsError_None )
|
||||
{
|
||||
LOG( WARNING )
|
||||
<< "Could not read \""
|
||||
<< vr::k_pch_CollisionBounds_ColorGammaA_Int32
|
||||
<< "\" setting: "
|
||||
<< vr::VRSettings()->GetSettingsErrorNameFromEnum(
|
||||
vrSettingsError );
|
||||
}
|
||||
setBoundsVisibility( vis / 255.0f );
|
||||
if ( vrSettingsError != vr::VRSettingsError_None )
|
||||
{
|
||||
LOG( WARNING )
|
||||
<< "Could not read \""
|
||||
<< vr::k_pch_CollisionBounds_FadeDistance_Float
|
||||
<< "\" setting: "
|
||||
<< vr::VRSettings()->GetSettingsErrorNameFromEnum(
|
||||
vrSettingsError );
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
setCenterMarker( cm );
|
||||
auto ps = vr::VRSettings()->GetBool(
|
||||
vr::k_pch_CollisionBounds_Section,
|
||||
vr::k_pch_CollisionBounds_PlaySpaceOn_Bool,
|
||||
&vrSettingsError );
|
||||
if ( vrSettingsError != vr::VRSettingsError_None )
|
||||
{
|
||||
LOG( WARNING )
|
||||
<< "Could not read \""
|
||||
<< vr::k_pch_CollisionBounds_PlaySpaceOn_Bool
|
||||
<< "\" setting: "
|
||||
<< vr::VRSettings()->GetSettingsErrorNameFromEnum(
|
||||
vrSettingsError );
|
||||
}
|
||||
setPlaySpaceMarker( ps );
|
||||
updateHeight( getBoundsMaxY() );
|
||||
updateChaperoneSettings();
|
||||
}
|
||||
settingsUpdateCounter = 0;
|
||||
}
|
||||
@@ -707,6 +654,60 @@ void ChaperoneTabController::eventLoopTick(
|
||||
}
|
||||
}
|
||||
|
||||
void ChaperoneTabController::updateChaperoneSettings()
|
||||
{
|
||||
vr::EVRSettingsError vrSettingsError;
|
||||
float vis = static_cast<float>(
|
||||
vr::VRSettings()->GetInt32( vr::k_pch_CollisionBounds_Section,
|
||||
vr::k_pch_CollisionBounds_ColorGammaA_Int32,
|
||||
&vrSettingsError ) );
|
||||
if ( vrSettingsError != vr::VRSettingsError_None )
|
||||
{
|
||||
LOG( WARNING ) << "Could not read \""
|
||||
<< vr::k_pch_CollisionBounds_ColorGammaA_Int32
|
||||
<< "\" setting: "
|
||||
<< vr::VRSettings()->GetSettingsErrorNameFromEnum(
|
||||
vrSettingsError );
|
||||
}
|
||||
setBoundsVisibility( vis / 255.0f );
|
||||
if ( vrSettingsError != vr::VRSettingsError_None )
|
||||
{
|
||||
LOG( WARNING ) << "Could not read \""
|
||||
<< vr::k_pch_CollisionBounds_FadeDistance_Float
|
||||
<< "\" setting: "
|
||||
<< vr::VRSettings()->GetSettingsErrorNameFromEnum(
|
||||
vrSettingsError );
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
setCenterMarker( cm );
|
||||
auto ps
|
||||
= vr::VRSettings()->GetBool( vr::k_pch_CollisionBounds_Section,
|
||||
vr::k_pch_CollisionBounds_PlaySpaceOn_Bool,
|
||||
&vrSettingsError );
|
||||
if ( vrSettingsError != vr::VRSettingsError_None )
|
||||
{
|
||||
LOG( WARNING ) << "Could not read \""
|
||||
<< vr::k_pch_CollisionBounds_PlaySpaceOn_Bool
|
||||
<< "\" setting: "
|
||||
<< vr::VRSettings()->GetSettingsErrorNameFromEnum(
|
||||
vrSettingsError );
|
||||
}
|
||||
setPlaySpaceMarker( ps );
|
||||
updateHeight( getBoundsMaxY() );
|
||||
}
|
||||
|
||||
float ChaperoneTabController::boundsVisibility() const
|
||||
{
|
||||
return m_visibility;
|
||||
@@ -1520,6 +1521,91 @@ void ChaperoneTabController::deleteChaperoneProfile( unsigned index )
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<bool, unsigned>
|
||||
ChaperoneTabController::getChaperoneProfileIndexFromName( std::string name )
|
||||
{
|
||||
std::pair<bool, unsigned> result = { false, 0 };
|
||||
unsigned profileIndex = 0;
|
||||
for ( auto& p : chaperoneProfiles )
|
||||
{
|
||||
if ( name == p.profileName )
|
||||
{
|
||||
result.first = true;
|
||||
result.second = profileIndex;
|
||||
break;
|
||||
}
|
||||
profileIndex++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void ChaperoneTabController::createNewAutosaveProfile()
|
||||
{
|
||||
// update settings to live chaperone
|
||||
updateChaperoneSettings();
|
||||
|
||||
// lookup the index for old autosave and delete it
|
||||
std::pair<bool, unsigned> previousAutosaveIndexLookup
|
||||
= getChaperoneProfileIndexFromName( "«Autosaved Profile (previous)»" );
|
||||
if ( previousAutosaveIndexLookup.first )
|
||||
{
|
||||
deleteChaperoneProfile( previousAutosaveIndexLookup.second );
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG( INFO )
|
||||
<< "[Chaperone Autosave] «Autosaved Profile (previous)» not found";
|
||||
}
|
||||
|
||||
// lookup the index for current autosave and rename to old
|
||||
std::pair<bool, unsigned> currentAutosaveIndexLookup
|
||||
= getChaperoneProfileIndexFromName( "«Autosaved Profile»" );
|
||||
if ( currentAutosaveIndexLookup.first )
|
||||
{
|
||||
chaperoneProfiles[currentAutosaveIndexLookup.second].profileName
|
||||
= "«Autosaved Profile (previous)»";
|
||||
saveChaperoneProfiles();
|
||||
OverlayController::appSettings()->sync();
|
||||
emit chaperoneProfilesUpdated();
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG( INFO ) << "[Chaperone Autosave] «Autosaved Profile» not found";
|
||||
}
|
||||
|
||||
// create a new autosave from current chaperone (all options set true)
|
||||
addChaperoneProfile( "«Autosaved Profile»",
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true );
|
||||
}
|
||||
|
||||
void ChaperoneTabController::applyAutosavedProfile()
|
||||
{
|
||||
// lookup index of autosave
|
||||
std::pair<bool, unsigned> autosaveIndexLookup
|
||||
= getChaperoneProfileIndexFromName( "«Autosaved Profile»" );
|
||||
|
||||
// check if it exists
|
||||
if ( autosaveIndexLookup.first )
|
||||
{
|
||||
// apply if it exists
|
||||
applyChaperoneProfile( autosaveIndexLookup.second );
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG( WARNING ) << "Failed to apply chaperone Autosaved Profile "
|
||||
"(autosave doesn't exist)";
|
||||
}
|
||||
}
|
||||
|
||||
float ChaperoneTabController::getBoundsMaxY()
|
||||
{
|
||||
unsigned collisionBoundsCount = 0;
|
||||
|
||||
@@ -157,6 +157,7 @@ private:
|
||||
bool m_chaperoneShowDashboardActive = false;
|
||||
|
||||
unsigned settingsUpdateCounter = 0;
|
||||
void updateChaperoneSettings();
|
||||
|
||||
bool m_isHapticGood = true;
|
||||
bool m_isHMDActive = false;
|
||||
@@ -165,6 +166,8 @@ private:
|
||||
bool m_leftHapticClickActivated = false;
|
||||
bool m_rightHapticClickActivated = false;
|
||||
|
||||
bool m_autosaveComplete = false;
|
||||
|
||||
int m_updateTicksChaperoneReload = 0;
|
||||
|
||||
vr::VRActionHandle_t m_rightActionHandle;
|
||||
@@ -219,6 +222,9 @@ public:
|
||||
Q_INVOKABLE QString getChaperoneProfileName( unsigned index );
|
||||
|
||||
float getBoundsMaxY();
|
||||
std::pair<bool, unsigned>
|
||||
getChaperoneProfileIndexFromName( std::string name );
|
||||
void createNewAutosaveProfile();
|
||||
|
||||
// actions
|
||||
void addLeftHapticClick( bool leftHapticClickPressed );
|
||||
@@ -265,6 +271,7 @@ public slots:
|
||||
bool includesProximityWarningSettings );
|
||||
void applyChaperoneProfile( unsigned index );
|
||||
void deleteChaperoneProfile( unsigned index );
|
||||
void applyAutosavedProfile();
|
||||
|
||||
void reset();
|
||||
|
||||
|
||||
@@ -1215,6 +1215,11 @@ void MoveCenterTabController::setEnableSeatedOffsetsRecenter( bool value,
|
||||
<< m_enableSeatedOffsetsRecenter;
|
||||
}
|
||||
|
||||
bool MoveCenterTabController::isInitComplete() const
|
||||
{
|
||||
return m_initComplete;
|
||||
}
|
||||
|
||||
bool MoveCenterTabController::disableSeatedMotion() const
|
||||
{
|
||||
return m_disableSeatedMotion;
|
||||
@@ -1414,7 +1419,30 @@ void MoveCenterTabController::zeroOffsets()
|
||||
if ( !m_initComplete )
|
||||
{
|
||||
setTrackingUniverse( vr::VRCompositor()->GetTrackingSpace() );
|
||||
m_initComplete = true;
|
||||
if ( parent->isPreviousShutdownSafe() )
|
||||
{
|
||||
// all init complete, safe to autosave chaperone profile
|
||||
parent->m_chaperoneTabController.createNewAutosaveProfile();
|
||||
m_initComplete = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// shutdown was unsafe last session!
|
||||
LOG( WARNING )
|
||||
<< "DETECTED UNSAFE SHUTDOWN FROM LAST SESSION";
|
||||
m_initComplete = true;
|
||||
if ( !parent->crashRecoveryDisabled() )
|
||||
{
|
||||
parent->m_chaperoneTabController
|
||||
.applyAutosavedProfile();
|
||||
LOG( INFO )
|
||||
<< "Applying last good chaperone profile autosave";
|
||||
}
|
||||
}
|
||||
// Now mark previous shutdown as unsafe in case we crash some
|
||||
// time during this session. Previous shutdown will be marked as
|
||||
// being safe once more just before our app shuts down properly.
|
||||
parent->setPreviousShutdownSafe( false );
|
||||
}
|
||||
}
|
||||
if ( m_roomSetupModeDetected )
|
||||
|
||||
@@ -259,6 +259,7 @@ public:
|
||||
bool universeCenteredRotation() const;
|
||||
bool enableSeatedOffsetsRecenter() const;
|
||||
bool disableSeatedMotion() const;
|
||||
bool isInitComplete() const;
|
||||
double getHmdYawTotal();
|
||||
void resetHmdYawTotal();
|
||||
void incomingSeatedReset();
|
||||
|
||||
Reference in New Issue
Block a user