From f0218e92e4124d287104fdc0f064f72d1680e384 Mon Sep 17 00:00:00 2001 From: Chelsea Jaggi Date: Sat, 4 Apr 2020 17:48:57 -0700 Subject: [PATCH 1/8] wip wip --- src/openvr/ivrinput.cpp | 5 + src/openvr/ivrinput.h | 3 + src/overlaycontroller.cpp | 4 + src/package_files/action_manifest.json | 9 +- .../MoveCenterTabController.cpp | 178 ++++++++++-------- src/tabcontrollers/MoveCenterTabController.h | 4 +- src/tabcontrollers/RotationTabController.cpp | 73 +++++++ src/tabcontrollers/RotationTabController.h | 4 + 8 files changed, 202 insertions(+), 78 deletions(-) diff --git a/src/openvr/ivrinput.cpp b/src/openvr/ivrinput.cpp index 7a42b325..3ce64008 100644 --- a/src/openvr/ivrinput.cpp +++ b/src/openvr/ivrinput.cpp @@ -132,6 +132,7 @@ SteamIVRInput::SteamIVRInput() m_smoothTurnLeft( action_keys::smoothTurnLeft ), m_smoothTurnRight( action_keys::smoothTurnRight ), m_autoTurnToggle( action_keys::autoTurnToggle ), + m_addAutoAlignPoint( action_keys::addAutoAlignPoint ), m_xAxisLockToggle( action_keys::xAxisLockToggle ), m_yAxisLockToggle( action_keys::yAxisLockToggle ), m_zAxisLockToggle( action_keys::zAxisLockToggle ), @@ -300,6 +301,10 @@ bool SteamIVRInput::autoTurnToggle() { return isDigitalActionActivatedOnce( m_autoTurnToggle ); } +bool SteamIVRInput::addAutoAlignPoint() +{ + return isDigitalActionActivatedOnce( m_addAutoAlignPoint ); +} bool SteamIVRInput::xAxisLockToggle() { diff --git a/src/openvr/ivrinput.h b/src/openvr/ivrinput.h index 91289a84..a057dfb7 100644 --- a/src/openvr/ivrinput.h +++ b/src/openvr/ivrinput.h @@ -47,6 +47,7 @@ namespace action_keys constexpr auto smoothTurnLeft = "/actions/motion/in/SmoothTurnLeft"; constexpr auto smoothTurnRight = "/actions/motion/in/SmoothTurnRight"; constexpr auto autoTurnToggle = "/actions/motion/in/AutoTurnToggle"; + constexpr auto addAutoAlignPoint = "/actions/motion/in/AddAutoAlignPoint"; constexpr auto xAxisLockToggle = "/actions/misc/in/XAxisLockToggle"; constexpr auto yAxisLockToggle = "/actions/misc/in/YAxisLockToggle"; @@ -151,6 +152,7 @@ class SteamIVRInput bool smoothTurnLeft(); bool smoothTurnRight(); bool autoTurnToggle(); + bool addAutoAlignPoint(); bool xAxisLockToggle(); bool yAxisLockToggle(); bool zAxisLockToggle(); @@ -233,6 +235,7 @@ class SteamIVRInput DigitalAction m_smoothTurnLeft; DigitalAction m_smoothTurnRight; DigitalAction m_autoTurnToggle; + DigitalAction m_addAutoAlignPoint; DigitalAction m_xAxisLockToggle; DigitalAction m_yAxisLockToggle; DigitalAction m_zAxisLockToggle; diff --git a/src/overlaycontroller.cpp b/src/overlaycontroller.cpp index bec9952b..665ad6db 100644 --- a/src/overlaycontroller.cpp +++ b/src/overlaycontroller.cpp @@ -755,6 +755,10 @@ void OverlayController::processRotationBindings() m_rotationTabController.setAutoTurnEnabled( !( m_rotationTabController.autoTurnEnabled() ) ); } + if ( m_actions.addAutoAlignPoint() ) + { + m_rotationTabController.addAutoAlignPoint(); + } } /*! Checks if an action has been activated and dispatches the related action if diff --git a/src/package_files/action_manifest.json b/src/package_files/action_manifest.json index 43ffb181..3580b7ee 100644 --- a/src/package_files/action_manifest.json +++ b/src/package_files/action_manifest.json @@ -163,7 +163,11 @@ "requirement": "optional", "type": "boolean" }, - + { + "name": "/actions/motion/in/AddAutoAlignPoint", + "requirement": "optional", + "type": "boolean" + }, { "name": "/actions/misc/in/XAxisLockToggle", "requirement": "optional", @@ -288,6 +292,7 @@ "/actions/motion/in/SmoothTurnLeft" : "Smooth-Turn Left", "/actions/motion/in/SmoothTurnRight" : "Smooth-Turn Right", "/actions/motion/in/AutoTurnToggle" : "Auto-Turn Toggle", + "/actions/motion/in/AddAutoAlignPoint" : "Add Auto Align Point", "/actions/misc/in/XAxisLockToggle" : "X-Axis Lock Toggle", "/actions/misc/in/YAxisLockToggle" : "Y-Axis Lock Toggle", @@ -303,4 +308,4 @@ "/actions/system/in/KeyPressSystem": "Key Press System" } ] -} \ No newline at end of file +} diff --git a/src/tabcontrollers/MoveCenterTabController.cpp b/src/tabcontrollers/MoveCenterTabController.cpp index 1b52497b..c6174954 100644 --- a/src/tabcontrollers/MoveCenterTabController.cpp +++ b/src/tabcontrollers/MoveCenterTabController.cpp @@ -330,8 +330,6 @@ void MoveCenterTabController::setRotation( int value, bool notify ) return; } - double angle = ( value - m_rotation ) * k_centidegreesToRadians; - // Get hmd pose matrix. vr::TrackedDevicePose_t devicePosesForRot[vr::k_unMaxTrackedDeviceCount]; @@ -356,26 +354,35 @@ void MoveCenterTabController::setRotation( int value, bool notify ) vr::HmdMatrix34_t oldHmdPos = devicePosesForRot[0].mDeviceToAbsoluteTracking; + + setRotationAroundPivot(value, notify, oldHmdPos); + } +} +void MoveCenterTabController::setRotationAroundPivot( int value, bool notify, const vr::HmdMatrix34_t& pivot ) +{ + if ( m_rotation != value ) + { + double angle = ( value - m_rotation ) * k_centidegreesToRadians; // Set up xyz coordinate values from pose matrix. - double oldHmdXyz[3] = { static_cast( oldHmdPos.m[0][3] ), - static_cast( oldHmdPos.m[1][3] ), - static_cast( oldHmdPos.m[2][3] ) }; - double newHmdXyz[3] = { static_cast( oldHmdPos.m[0][3] ), - static_cast( oldHmdPos.m[1][3] ), - static_cast( oldHmdPos.m[2][3] ) }; - - // Convert oldHmdXyz into un-rotated coordinates. + double oldXyz[3] = { static_cast( pivot.m[0][3] ), + static_cast( pivot.m[1][3] ), + static_cast( pivot.m[2][3] ) }; + double newXyz[3] = { static_cast( pivot.m[0][3] ), + static_cast( pivot.m[1][3] ), + static_cast( pivot.m[2][3] ) }; + + // Convert oldXyz into un-rotated coordinates. double oldAngle = -m_rotation * k_centidegreesToRadians; - rotateCoordinates( oldHmdXyz, oldAngle ); + rotateCoordinates( oldXyz, oldAngle ); - // Set newHmdXyz to have additional rotation from incoming angle change. - rotateCoordinates( newHmdXyz, oldAngle - angle ); + // Set newXyz to have additional rotation from incoming angle change. + rotateCoordinates( newXyz, oldAngle - angle ); // find difference in x,z offset due to incoming angle change // (coordinates are in un-rotated axis). double hmdRotDiff[3] - = { oldHmdXyz[0] - newHmdXyz[0], 0, oldHmdXyz[2] - newHmdXyz[2] }; + = { oldXyz[0] - newXyz[0], 0, oldXyz[2] - newXyz[2] }; m_rotation = value; if ( notify ) @@ -999,12 +1006,10 @@ void MoveCenterTabController::reset() m_offsetY = 0.0f; m_offsetZ = 0.0f; m_rotation = 0; - m_lastControllerPosition[0] = 0.0f; - m_lastControllerPosition[1] = 0.0f; - m_lastControllerPosition[2] = 0.0f; m_lastMoveHand = vr::TrackedControllerRole_Invalid; m_lastRotateHand = vr::TrackedControllerRole_Invalid; applyChaperoneResetData(); + m_lastControllerPosition = {0.0f, 0.0f, 0.0f}; // For Center Marker // Needs to happen after apply chaperone @@ -2211,6 +2216,80 @@ void MoveCenterTabController::updateHmdRotationCounter( m_lastHmdQuaternion = m_hmdQuaternion; } +// Displace the entire universe by some difference. Both 'from' and 'to' are in relative coordinates, which get converted to absolute. +void MoveCenterTabController::displaceUniverseRelative(const vr::HmdVector3_t& from, const vr::HmdVector3_t& to, double angle) +{ + double relativeControllerPosition[] = { + static_cast( to.v[0] ), + static_cast( to.v[1] ), + static_cast( to.v[2] ) + }; + rotateCoordinates( relativeControllerPosition, -angle ); + float absoluteControllerPosition[] = { + static_cast( relativeControllerPosition[0] ) + m_offsetX, + static_cast( relativeControllerPosition[1] ) + m_offsetY, + static_cast( relativeControllerPosition[2] ) + m_offsetZ, + }; + double relativeFromPosition[] = { + static_cast( from.v[0] ), + static_cast( from.v[1] ), + static_cast( from.v[2] ) + }; + rotateCoordinates( relativeFromPosition, -angle ); + float absoluteFromPosition[] = { + static_cast( relativeFromPosition[0] ) + m_offsetX, + static_cast( relativeFromPosition[1] ) + m_offsetY, + static_cast( relativeFromPosition[2] ) + m_offsetZ, + }; + + double diff[3] = { + static_cast( absoluteControllerPosition[0] + - absoluteFromPosition[0] ), + static_cast( absoluteControllerPosition[1] + - absoluteFromPosition[1] ), + static_cast( absoluteControllerPosition[2] + - absoluteFromPosition[2] ), + }; + + // offset is un-rotated coordinates + + // prevent positional glitches from exceeding max openvr offset clamps. + // We do this by detecting a drag larger than 100m in a single frame. + if ( abs( diff[0] ) > 100.0 || abs( diff[1] ) > 100.0 + || abs( diff[2] ) > 100.0 ) + { + reset(); + } + + // prevents updating if axis movement is locked + if ( !lockXToggle() ) + { + m_offsetX += static_cast( diff[0] ); + } + if ( !lockYToggle() ) + { + m_offsetY += static_cast( diff[1] ); + } + if ( !lockZToggle() ) + { + m_offsetZ += static_cast( diff[2] ); + } + + double secondsSinceLastDragUpdate + = std::chrono::duration( std::chrono::steady_clock::now() + - m_lastDragUpdateTimePoint ) + .count(); + + // TODO: Add 'effects fling' boolean? or simply return diff[] as a HmdVect3_t + m_velocity[0] = ( diff[0] / secondsSinceLastDragUpdate ) + * static_cast( flingStrength() ); + m_velocity[1] = ( diff[1] / secondsSinceLastDragUpdate ) + * static_cast( flingStrength() ); + m_velocity[2] = ( diff[2] / secondsSinceLastDragUpdate ) + * static_cast( flingStrength() ); + +} + void MoveCenterTabController::updateHandDrag( vr::TrackedDevicePose_t* devicePoses, double angle ) @@ -2261,69 +2340,18 @@ void MoveCenterTabController::updateHandDrag( return; } - double relativeControllerPosition[] = { - static_cast( movePose->mDeviceToAbsoluteTracking.m[0][3] ), - static_cast( movePose->mDeviceToAbsoluteTracking.m[1][3] ), - static_cast( movePose->mDeviceToAbsoluteTracking.m[2][3] ) - }; - - rotateCoordinates( relativeControllerPosition, -angle ); - float absoluteControllerPosition[] = { - static_cast( relativeControllerPosition[0] ) + m_offsetX, - static_cast( relativeControllerPosition[1] ) + m_offsetY, - static_cast( relativeControllerPosition[2] ) + m_offsetZ, + vr::HmdVector3_t relativeControllerPosition = { + movePose->mDeviceToAbsoluteTracking.m[0][3], + movePose->mDeviceToAbsoluteTracking.m[1][3], + movePose->mDeviceToAbsoluteTracking.m[2][3] }; if ( m_lastMoveHand == m_activeDragHand ) { - double diff[3] = { - static_cast( absoluteControllerPosition[0] - - m_lastControllerPosition[0] ), - static_cast( absoluteControllerPosition[1] - - m_lastControllerPosition[1] ), - static_cast( absoluteControllerPosition[2] - - m_lastControllerPosition[2] ), - }; - - // offset is un-rotated coordinates - - // prevent positional glitches from exceeding max openvr offset clamps. - // We do this by detecting a drag larger than 100m in a single frame. - if ( abs( diff[0] ) > 100.0 || abs( diff[1] ) > 100.0 - || abs( diff[2] ) > 100.0 ) - { - reset(); - } - - // prevents updating if axis movement is locked - if ( !lockXToggle() ) - { - m_offsetX += static_cast( diff[0] ); - } - if ( !lockYToggle() ) - { - m_offsetY += static_cast( diff[1] ); - } - if ( !lockZToggle() ) - { - m_offsetZ += static_cast( diff[2] ); - } - - double secondsSinceLastDragUpdate - = std::chrono::duration( std::chrono::steady_clock::now() - - m_lastDragUpdateTimePoint ) - .count(); - - m_velocity[0] = ( diff[0] / secondsSinceLastDragUpdate ) - * static_cast( flingStrength() ); - m_velocity[1] = ( diff[1] / secondsSinceLastDragUpdate ) - * static_cast( flingStrength() ); - m_velocity[2] = ( diff[2] / secondsSinceLastDragUpdate ) - * static_cast( flingStrength() ); + // Displace from last controller position to current + displaceUniverseRelative(m_lastControllerPosition, relativeControllerPosition, angle); } - m_lastControllerPosition[0] = absoluteControllerPosition[0]; - m_lastControllerPosition[1] = absoluteControllerPosition[1]; - m_lastControllerPosition[2] = absoluteControllerPosition[2]; + m_lastControllerPosition = relativeControllerPosition; m_lastMoveHand = m_activeDragHand; } diff --git a/src/tabcontrollers/MoveCenterTabController.h b/src/tabcontrollers/MoveCenterTabController.h index 6759c552..5c8020cf 100644 --- a/src/tabcontrollers/MoveCenterTabController.h +++ b/src/tabcontrollers/MoveCenterTabController.h @@ -158,7 +158,7 @@ class MoveCenterTabController : public QObject bool m_moveShortcutRightPressed = false; bool m_moveShortcutLeftPressed = false; vr::TrackedDeviceIndex_t m_activeMoveController; - float m_lastControllerPosition[3]; + vr::HmdVector3_t m_lastControllerPosition; bool m_heightToggle = false; float m_gravityFloor = 0.0f; // Set lastHandQuaternion.w to -1000.0 when last hand is invalid. @@ -283,6 +283,8 @@ class MoveCenterTabController : public QObject void incomingSeatedReset(); void setBoundsBasisHeight( float newHeight ); float getBoundsBasisMaxY(); + void setRotationAroundPivot( int value, bool notify, const vr::HmdMatrix34_t& pivot); + void displaceUniverseRelative( const vr::HmdVector3_t& from, const vr::HmdVector3_t& to, double angle); void updateChaperoneResetData(); diff --git a/src/tabcontrollers/RotationTabController.cpp b/src/tabcontrollers/RotationTabController.cpp index 07301f9f..f686992f 100644 --- a/src/tabcontrollers/RotationTabController.cpp +++ b/src/tabcontrollers/RotationTabController.cpp @@ -74,11 +74,16 @@ void RotationTabController::initStage2( OverlayController* var_parent ) this->parent = var_parent; } +static vr::TrackedDevicePose_t lastHandPose; void RotationTabController::eventLoopTick( vr::TrackedDevicePose_t* devicePoses ) { if ( devicePoses ) { + auto moveHandId = vr::VRSystem()->GetTrackedDeviceIndexForControllerRole( + vr::TrackedControllerRole_RightHand); + lastHandPose = devicePoses[moveHandId]; + m_isHMDActive = false; std::lock_guard lock( parent->chaperoneUtils().mutex() ); @@ -578,6 +583,74 @@ void RotationTabController::doAutoTurn( } } +static std::vector autoAlignPoints; +void RotationTabController::addAutoAlignPoint() +{ + LOG(INFO) << "point added: " << autoAlignPoints.size(); + // get the location of right hand, push_back onto autoAlignPoints + autoAlignPoints.push_back(lastHandPose); + + // if we have exactly 4 points, go into main loop + if(autoAlignPoints.size() == 4) + { + vr::HmdVector3_t realFirstPoint = { + autoAlignPoints[0].mDeviceToAbsoluteTracking.m[0][3], + autoAlignPoints[0].mDeviceToAbsoluteTracking.m[1][3], + autoAlignPoints[0].mDeviceToAbsoluteTracking.m[2][3] + }; + + vr::HmdVector3_t realSecondPoint = { + autoAlignPoints[1].mDeviceToAbsoluteTracking.m[0][3], + autoAlignPoints[1].mDeviceToAbsoluteTracking.m[1][3], + autoAlignPoints[1].mDeviceToAbsoluteTracking.m[2][3] + }; + + vr::HmdVector3_t virtualFirstPoint = { + autoAlignPoints[2].mDeviceToAbsoluteTracking.m[0][3], + autoAlignPoints[2].mDeviceToAbsoluteTracking.m[1][3], + autoAlignPoints[2].mDeviceToAbsoluteTracking.m[2][3] + }; + + vr::HmdVector3_t virtualSecondPoint = { + autoAlignPoints[3].mDeviceToAbsoluteTracking.m[0][3], + autoAlignPoints[3].mDeviceToAbsoluteTracking.m[1][3], + autoAlignPoints[3].mDeviceToAbsoluteTracking.m[2][3] + }; + + + // Align the first of VR points (points[2]) with the first of the real points (points[0]) purely in position + parent->m_moveCenterTabController.displaceUniverseRelative(virtualFirstPoint, realFirstPoint, parent->m_moveCenterTabController.rotation()); + + + // TODO: if centered, use the center of both points as the pivot + + // Then rotate the universe to align, pivoting around the real point + double realEdgeAngle = static_cast( std::atan2( + realFirstPoint.v[0] - realSecondPoint.v[0], + realFirstPoint.v[2] - realSecondPoint.v[2] ) ); + double virtualEdgeAngle = static_cast( std::atan2( + virtualFirstPoint.v[0] - virtualSecondPoint.v[0], + virtualFirstPoint.v[2] - virtualSecondPoint.v[2] ) ); + double delta_degrees = (realEdgeAngle - virtualEdgeAngle) * k_radiansToCentidegrees; + int newRotationAngleDeg = static_cast( + parent->m_moveCenterTabController.rotation() + + delta_degrees ); + + // these need to be in the new, offset position. TODO: Doesn't currently work when already offset for some reason + vr::HmdMatrix34_t autoAlignPivot = autoAlignPoints[0].mDeviceToAbsoluteTracking; + autoAlignPivot.m[0][3] -= realFirstPoint.v[0] - virtualFirstPoint.v[0]; + autoAlignPivot.m[1][3] -= realFirstPoint.v[1] - virtualFirstPoint.v[1]; + autoAlignPivot.m[2][3] -= realFirstPoint.v[2] - virtualFirstPoint.v[2]; + + parent->m_moveCenterTabController.setRotationAroundPivot( + newRotationAngleDeg, true, autoAlignPivot); + + + // end of main loop, clear autoAlignPoints + autoAlignPoints.clear(); + } + +} // getters bool RotationTabController::autoTurnEnabled() const diff --git a/src/tabcontrollers/RotationTabController.h b/src/tabcontrollers/RotationTabController.h index de8253a5..6d8458e6 100644 --- a/src/tabcontrollers/RotationTabController.h +++ b/src/tabcontrollers/RotationTabController.h @@ -156,6 +156,7 @@ class RotationTabController : public QObject bool viewRatchettingEnabled() const; double viewRatchettingPercent() const; + public slots: void setAutoTurnEnabled( bool value, bool notify = true ); void setAutoTurnShowNotification( bool value, bool notify = true ); @@ -170,6 +171,9 @@ public slots: void setVestibularMotionRadius( double value, bool notify = true ); void setViewRatchettingEnabled( bool value, bool notify = true ); void setViewRatchettingPercent( double value, bool notify = true ); + + // Experimental - auto-align + void addAutoAlignPoint(); signals: From 21d7ea99f6ecfd41b3c4e0dcb3d8a86c4ad21187 Mon Sep 17 00:00:00 2001 From: Chelsea Jaggi Date: Sat, 18 Jul 2020 23:50:55 -0700 Subject: [PATCH 2/8] wip --- src/tabcontrollers/RotationTabController.cpp | 2 -- src/tabcontrollers/RotationTabController.h | 3 +++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tabcontrollers/RotationTabController.cpp b/src/tabcontrollers/RotationTabController.cpp index f686992f..a8cf97d5 100644 --- a/src/tabcontrollers/RotationTabController.cpp +++ b/src/tabcontrollers/RotationTabController.cpp @@ -74,7 +74,6 @@ void RotationTabController::initStage2( OverlayController* var_parent ) this->parent = var_parent; } -static vr::TrackedDevicePose_t lastHandPose; void RotationTabController::eventLoopTick( vr::TrackedDevicePose_t* devicePoses ) { @@ -583,7 +582,6 @@ void RotationTabController::doAutoTurn( } } -static std::vector autoAlignPoints; void RotationTabController::addAutoAlignPoint() { LOG(INFO) << "point added: " << autoAlignPoints.size(); diff --git a/src/tabcontrollers/RotationTabController.h b/src/tabcontrollers/RotationTabController.h index 6d8458e6..50ace646 100644 --- a/src/tabcontrollers/RotationTabController.h +++ b/src/tabcontrollers/RotationTabController.h @@ -121,6 +121,9 @@ class RotationTabController : public QObject std::optional m_autoTurnNotificationTimestamp; + std::vector autoAlignPoints; + vr::TrackedDevicePose_t lastHandPose; + bool m_isHMDActive = false; void doAutoTurn( From 64410ee814ee3d58c84b0491b6c05c5cd2a2d60a Mon Sep 17 00:00:00 2001 From: Chelsea Jaggi Date: Wed, 18 Nov 2020 20:33:14 -0800 Subject: [PATCH 3/8] Finally got the math right, correctly displaces regardless of current offset. More abstraction --- .../MoveCenterTabController.cpp | 90 +++++++++++-------- src/tabcontrollers/MoveCenterTabController.h | 9 +- src/tabcontrollers/RotationTabController.cpp | 66 ++++++-------- src/tabcontrollers/RotationTabController.h | 2 +- 4 files changed, 87 insertions(+), 80 deletions(-) diff --git a/src/tabcontrollers/MoveCenterTabController.cpp b/src/tabcontrollers/MoveCenterTabController.cpp index c6174954..b8189990 100644 --- a/src/tabcontrollers/MoveCenterTabController.cpp +++ b/src/tabcontrollers/MoveCenterTabController.cpp @@ -354,23 +354,27 @@ void MoveCenterTabController::setRotation( int value, bool notify ) vr::HmdMatrix34_t oldHmdPos = devicePosesForRot[0].mDeviceToAbsoluteTracking; - - setRotationAroundPivot(value, notify, oldHmdPos); + vr::HmdVector3_t oldHmdPos3 = { + oldHmdPos.m[0][3], + oldHmdPos.m[1][3], + oldHmdPos.m[2][3] + }; + setRotationAroundPivot(value, notify, oldHmdPos3); } } -void MoveCenterTabController::setRotationAroundPivot( int value, bool notify, const vr::HmdMatrix34_t& pivot ) +void MoveCenterTabController::setRotationAroundPivot( int value, bool notify, const vr::HmdVector3_t& pivot ) { if ( m_rotation != value ) { double angle = ( value - m_rotation ) * k_centidegreesToRadians; // Set up xyz coordinate values from pose matrix. - double oldXyz[3] = { static_cast( pivot.m[0][3] ), - static_cast( pivot.m[1][3] ), - static_cast( pivot.m[2][3] ) }; - double newXyz[3] = { static_cast( pivot.m[0][3] ), - static_cast( pivot.m[1][3] ), - static_cast( pivot.m[2][3] ) }; + double oldXyz[3] = { static_cast( pivot.v[0] ), + static_cast( pivot.v[1] ), + static_cast( pivot.v[2] ) }; + double newXyz[3] = { static_cast( pivot.v[0] ), + static_cast( pivot.v[1] ), + static_cast( pivot.v[2] ) }; // Convert oldXyz into un-rotated coordinates. double oldAngle = -m_rotation * k_centidegreesToRadians; @@ -2216,39 +2220,48 @@ void MoveCenterTabController::updateHmdRotationCounter( m_lastHmdQuaternion = m_hmdQuaternion; } -// Displace the entire universe by some difference. Both 'from' and 'to' are in relative coordinates, which get converted to absolute. -void MoveCenterTabController::displaceUniverseRelative(const vr::HmdVector3_t& from, const vr::HmdVector3_t& to, double angle) +vr::HmdVector3_t MoveCenterTabController::relativeToAbsolute(const vr::HmdVector3_t& relative) const { - double relativeControllerPosition[] = { - static_cast( to.v[0] ), - static_cast( to.v[1] ), - static_cast( to.v[2] ) + double relativeControllerPosition[] = { + static_cast( relative.v[0] ), + static_cast( relative.v[1] ), + static_cast( relative.v[2] ) }; - rotateCoordinates( relativeControllerPosition, -angle ); - float absoluteControllerPosition[] = { + rotateCoordinates( relativeControllerPosition, -m_rotation * k_centidegreesToRadians); + vr::HmdVector3_t absoluteControllerPosition = { static_cast( relativeControllerPosition[0] ) + m_offsetX, static_cast( relativeControllerPosition[1] ) + m_offsetY, static_cast( relativeControllerPosition[2] ) + m_offsetZ, }; - double relativeFromPosition[] = { - static_cast( from.v[0] ), - static_cast( from.v[1] ), - static_cast( from.v[2] ) + return absoluteControllerPosition; +} + +vr::HmdVector3_t MoveCenterTabController::absoluteToRelative(const vr::HmdVector3_t& absolute) const +{ + double absoluteControllerPosition[] = { + static_cast( absolute.v[0] - m_offsetX), + static_cast( absolute.v[1] - m_offsetY), + static_cast( absolute.v[2] - m_offsetZ) }; - rotateCoordinates( relativeFromPosition, -angle ); - float absoluteFromPosition[] = { - static_cast( relativeFromPosition[0] ) + m_offsetX, - static_cast( relativeFromPosition[1] ) + m_offsetY, - static_cast( relativeFromPosition[2] ) + m_offsetZ, + rotateCoordinates( absoluteControllerPosition, m_rotation * k_centidegreesToRadians); + vr::HmdVector3_t relativeControllerPosition = { + static_cast( absoluteControllerPosition[0] ), + static_cast( absoluteControllerPosition[1] ), + static_cast( absoluteControllerPosition[2] ) }; + return relativeControllerPosition; +} +// Displace the entire universe by some difference. Both 'from' and 'to' are in absolute coordinates. +void MoveCenterTabController::displaceUniverse(const vr::HmdVector3_t& from, const vr::HmdVector3_t& to) +{ double diff[3] = { - static_cast( absoluteControllerPosition[0] - - absoluteFromPosition[0] ), - static_cast( absoluteControllerPosition[1] - - absoluteFromPosition[1] ), - static_cast( absoluteControllerPosition[2] - - absoluteFromPosition[2] ), + static_cast( to.v[0] + - from.v[0] ), + static_cast( to.v[1] + - from.v[1] ), + static_cast( to.v[2] + - from.v[2] ), }; // offset is un-rotated coordinates @@ -2291,8 +2304,8 @@ void MoveCenterTabController::displaceUniverseRelative(const vr::HmdVector3_t& f } void MoveCenterTabController::updateHandDrag( - vr::TrackedDevicePose_t* devicePoses, - double angle ) + vr::TrackedDevicePose_t* devicePoses + ) { auto moveHandId = vr::VRSystem()->GetTrackedDeviceIndexForControllerRole( m_activeDragHand ); @@ -2345,13 +2358,14 @@ void MoveCenterTabController::updateHandDrag( movePose->mDeviceToAbsoluteTracking.m[1][3], movePose->mDeviceToAbsoluteTracking.m[2][3] }; + auto absoluteControllerPosition = relativeToAbsolute(relativeControllerPosition); - if ( m_lastMoveHand == m_activeDragHand ) + if ( m_lastMoveHand == m_activeDragHand ) { // Displace from last controller position to current - displaceUniverseRelative(m_lastControllerPosition, relativeControllerPosition, angle); + displaceUniverse(m_lastControllerPosition, absoluteControllerPosition); } - m_lastControllerPosition = relativeControllerPosition; + m_lastControllerPosition = absoluteControllerPosition; m_lastMoveHand = m_activeDragHand; } @@ -3069,7 +3083,7 @@ void MoveCenterTabController::eventLoopTick( if ( m_dragComfortFrameSkipCounter >= static_cast( ( dragComfortFactor() * dragComfortFactor() ) ) ) { - updateHandDrag( devicePoses, angle ); + updateHandDrag( devicePoses ); m_lastDragUpdateTimePoint = std::chrono::steady_clock::now(); m_dragComfortFrameSkipCounter = 0; } diff --git a/src/tabcontrollers/MoveCenterTabController.h b/src/tabcontrollers/MoveCenterTabController.h index 5c8020cf..2ce7051d 100644 --- a/src/tabcontrollers/MoveCenterTabController.h +++ b/src/tabcontrollers/MoveCenterTabController.h @@ -227,7 +227,7 @@ class MoveCenterTabController : public QObject void updateHmdRotationCounter( vr::TrackedDevicePose_t hmdPose, double angle ); - void updateHandDrag( vr::TrackedDevicePose_t* devicePoses, double angle ); + void updateHandDrag( vr::TrackedDevicePose_t* devicePoses ); void updateHandTurn( vr::TrackedDevicePose_t* devicePoses, double angle ); void updateGravity(); void updateSpace( bool forceUpdate = false ); @@ -283,8 +283,11 @@ class MoveCenterTabController : public QObject void incomingSeatedReset(); void setBoundsBasisHeight( float newHeight ); float getBoundsBasisMaxY(); - void setRotationAroundPivot( int value, bool notify, const vr::HmdMatrix34_t& pivot); - void displaceUniverseRelative( const vr::HmdVector3_t& from, const vr::HmdVector3_t& to, double angle); + void setRotationAroundPivot( int value, bool notify, const vr::HmdVector3_t& pivot ); + void displaceUniverse( const vr::HmdVector3_t& from, const vr::HmdVector3_t& to ); + + vr::HmdVector3_t relativeToAbsolute(const vr::HmdVector3_t& coordinates_in) const; + vr::HmdVector3_t absoluteToRelative(const vr::HmdVector3_t& absolute) const; void updateChaperoneResetData(); diff --git a/src/tabcontrollers/RotationTabController.cpp b/src/tabcontrollers/RotationTabController.cpp index a8cf97d5..fe09ed20 100644 --- a/src/tabcontrollers/RotationTabController.cpp +++ b/src/tabcontrollers/RotationTabController.cpp @@ -584,45 +584,31 @@ void RotationTabController::doAutoTurn( void RotationTabController::addAutoAlignPoint() { + // TODO: State machine: if we have >2 align points, freeze vestibular motion/ratchetting/etc + // remain frozen until after we move away from the aligned area + LOG(INFO) << "point added: " << autoAlignPoints.size(); // get the location of right hand, push_back onto autoAlignPoints - autoAlignPoints.push_back(lastHandPose); + // TODO: + vr::HmdVector3_t new_point = { + lastHandPose.mDeviceToAbsoluteTracking.m[0][3], + lastHandPose.mDeviceToAbsoluteTracking.m[1][3], + lastHandPose.mDeviceToAbsoluteTracking.m[2][3] + }; + // TODO: Probably smarter to actually just keep the virtual points as virtual until we use them. + // Then if they drift we don't care. + vr::HmdVector3_t absolute_point = parent->m_moveCenterTabController.relativeToAbsolute(new_point); + autoAlignPoints.push_back(absolute_point); // if we have exactly 4 points, go into main loop if(autoAlignPoints.size() == 4) { - vr::HmdVector3_t realFirstPoint = { - autoAlignPoints[0].mDeviceToAbsoluteTracking.m[0][3], - autoAlignPoints[0].mDeviceToAbsoluteTracking.m[1][3], - autoAlignPoints[0].mDeviceToAbsoluteTracking.m[2][3] - }; - - vr::HmdVector3_t realSecondPoint = { - autoAlignPoints[1].mDeviceToAbsoluteTracking.m[0][3], - autoAlignPoints[1].mDeviceToAbsoluteTracking.m[1][3], - autoAlignPoints[1].mDeviceToAbsoluteTracking.m[2][3] - }; - - vr::HmdVector3_t virtualFirstPoint = { - autoAlignPoints[2].mDeviceToAbsoluteTracking.m[0][3], - autoAlignPoints[2].mDeviceToAbsoluteTracking.m[1][3], - autoAlignPoints[2].mDeviceToAbsoluteTracking.m[2][3] - }; - - vr::HmdVector3_t virtualSecondPoint = { - autoAlignPoints[3].mDeviceToAbsoluteTracking.m[0][3], - autoAlignPoints[3].mDeviceToAbsoluteTracking.m[1][3], - autoAlignPoints[3].mDeviceToAbsoluteTracking.m[2][3] - }; - - - // Align the first of VR points (points[2]) with the first of the real points (points[0]) purely in position - parent->m_moveCenterTabController.displaceUniverseRelative(virtualFirstPoint, realFirstPoint, parent->m_moveCenterTabController.rotation()); - + vr::HmdVector3_t realFirstPoint = autoAlignPoints[0]; + vr::HmdVector3_t realSecondPoint = autoAlignPoints[1]; + vr::HmdVector3_t virtualFirstPoint = autoAlignPoints[2]; + vr::HmdVector3_t virtualSecondPoint = autoAlignPoints[3]; - // TODO: if centered, use the center of both points as the pivot - - // Then rotate the universe to align, pivoting around the real point + // Rotate the universe to align, pivoting around the real point double realEdgeAngle = static_cast( std::atan2( realFirstPoint.v[0] - realSecondPoint.v[0], realFirstPoint.v[2] - realSecondPoint.v[2] ) ); @@ -634,16 +620,20 @@ void RotationTabController::addAutoAlignPoint() parent->m_moveCenterTabController.rotation() + delta_degrees ); - // these need to be in the new, offset position. TODO: Doesn't currently work when already offset for some reason - vr::HmdMatrix34_t autoAlignPivot = autoAlignPoints[0].mDeviceToAbsoluteTracking; - autoAlignPivot.m[0][3] -= realFirstPoint.v[0] - virtualFirstPoint.v[0]; - autoAlignPivot.m[1][3] -= realFirstPoint.v[1] - virtualFirstPoint.v[1]; - autoAlignPivot.m[2][3] -= realFirstPoint.v[2] - virtualFirstPoint.v[2]; + // these need to be in the new, offset position. TODO: needs to be converted into new relative position? + vr::HmdVector3_t autoAlignPivot = realFirstPoint; + autoAlignPivot.v[0] -= realFirstPoint.v[0] - virtualFirstPoint.v[0]; + autoAlignPivot.v[1] -= realFirstPoint.v[1] - virtualFirstPoint.v[1]; + autoAlignPivot.v[2] -= realFirstPoint.v[2] - virtualFirstPoint.v[2]; + // TODO: if centered, use the center of both points as the pivot parent->m_moveCenterTabController.setRotationAroundPivot( - newRotationAngleDeg, true, autoAlignPivot); + newRotationAngleDeg, true, parent->m_moveCenterTabController.absoluteToRelative(virtualFirstPoint)); + // Align the first of VR points (points[2]) with the first of the real points (points[0]) purely in position + parent->m_moveCenterTabController.displaceUniverse(virtualFirstPoint, realFirstPoint); + // end of main loop, clear autoAlignPoints autoAlignPoints.clear(); } diff --git a/src/tabcontrollers/RotationTabController.h b/src/tabcontrollers/RotationTabController.h index 50ace646..bd11bebd 100644 --- a/src/tabcontrollers/RotationTabController.h +++ b/src/tabcontrollers/RotationTabController.h @@ -121,7 +121,7 @@ class RotationTabController : public QObject std::optional m_autoTurnNotificationTimestamp; - std::vector autoAlignPoints; + std::vector autoAlignPoints; vr::TrackedDevicePose_t lastHandPose; bool m_isHMDActive = false; From 57ac2a3094500ad3684daf1f077b4a22c7b3a1e2 Mon Sep 17 00:00:00 2001 From: Chelsea Jaggi Date: Wed, 18 Nov 2020 20:52:14 -0800 Subject: [PATCH 4/8] Formatting --- src/settings/internal/settings_internal.h | 2 +- .../MoveCenterTabController.cpp | 120 +++++++++--------- src/tabcontrollers/MoveCenterTabController.h | 15 ++- src/tabcontrollers/RotationTabController.cpp | 72 ++++++----- src/tabcontrollers/RotationTabController.h | 3 +- 5 files changed, 111 insertions(+), 101 deletions(-) diff --git a/src/settings/internal/settings_internal.h b/src/settings/internal/settings_internal.h index 5f13b079..1e38b4f0 100644 --- a/src/settings/internal/settings_internal.h +++ b/src/settings/internal/settings_internal.h @@ -81,7 +81,7 @@ void saveQtSetting( const SettingCategory category, getQSettings().endGroup(); } -template [[nodiscard]] bool isValidQVariant( const QVariant v ) +template [[nodiscard]] bool isValidQVariant( const QVariant v ) { auto savedSettingIsValid = v.isValid() && !v.isNull(); diff --git a/src/tabcontrollers/MoveCenterTabController.cpp b/src/tabcontrollers/MoveCenterTabController.cpp index b8189990..073e5422 100644 --- a/src/tabcontrollers/MoveCenterTabController.cpp +++ b/src/tabcontrollers/MoveCenterTabController.cpp @@ -354,27 +354,27 @@ void MoveCenterTabController::setRotation( int value, bool notify ) vr::HmdMatrix34_t oldHmdPos = devicePosesForRot[0].mDeviceToAbsoluteTracking; - vr::HmdVector3_t oldHmdPos3 = { - oldHmdPos.m[0][3], - oldHmdPos.m[1][3], - oldHmdPos.m[2][3] - }; - setRotationAroundPivot(value, notify, oldHmdPos3); + vr::HmdVector3_t oldHmdPos3 + = { oldHmdPos.m[0][3], oldHmdPos.m[1][3], oldHmdPos.m[2][3] }; + setRotationAroundPivot( value, notify, oldHmdPos3 ); } } -void MoveCenterTabController::setRotationAroundPivot( int value, bool notify, const vr::HmdVector3_t& pivot ) +void MoveCenterTabController::setRotationAroundPivot( + int value, + bool notify, + const vr::HmdVector3_t& pivot ) { if ( m_rotation != value ) { double angle = ( value - m_rotation ) * k_centidegreesToRadians; // Set up xyz coordinate values from pose matrix. double oldXyz[3] = { static_cast( pivot.v[0] ), - static_cast( pivot.v[1] ), - static_cast( pivot.v[2] ) }; + static_cast( pivot.v[1] ), + static_cast( pivot.v[2] ) }; double newXyz[3] = { static_cast( pivot.v[0] ), - static_cast( pivot.v[1] ), - static_cast( pivot.v[2] ) }; + static_cast( pivot.v[1] ), + static_cast( pivot.v[2] ) }; // Convert oldXyz into un-rotated coordinates. double oldAngle = -m_rotation * k_centidegreesToRadians; @@ -1013,7 +1013,7 @@ void MoveCenterTabController::reset() m_lastMoveHand = vr::TrackedControllerRole_Invalid; m_lastRotateHand = vr::TrackedControllerRole_Invalid; applyChaperoneResetData(); - m_lastControllerPosition = {0.0f, 0.0f, 0.0f}; + m_lastControllerPosition = { 0.0f, 0.0f, 0.0f }; // For Center Marker // Needs to happen after apply chaperone @@ -2220,48 +2220,48 @@ void MoveCenterTabController::updateHmdRotationCounter( m_lastHmdQuaternion = m_hmdQuaternion; } -vr::HmdVector3_t MoveCenterTabController::relativeToAbsolute(const vr::HmdVector3_t& relative) const +vr::HmdVector3_t MoveCenterTabController::relativeToAbsolute( + const vr::HmdVector3_t& relative ) const { - double relativeControllerPosition[] = { - static_cast( relative.v[0] ), - static_cast( relative.v[1] ), - static_cast( relative.v[2] ) - }; - rotateCoordinates( relativeControllerPosition, -m_rotation * k_centidegreesToRadians); - vr::HmdVector3_t absoluteControllerPosition = { + double relativeControllerPosition[] + = { static_cast( relative.v[0] ), + static_cast( relative.v[1] ), + static_cast( relative.v[2] ) }; + rotateCoordinates( relativeControllerPosition, + -m_rotation * k_centidegreesToRadians ); + vr::HmdVector3_t absoluteControllerPosition = { static_cast( relativeControllerPosition[0] ) + m_offsetX, static_cast( relativeControllerPosition[1] ) + m_offsetY, static_cast( relativeControllerPosition[2] ) + m_offsetZ, }; - return absoluteControllerPosition; + return absoluteControllerPosition; } -vr::HmdVector3_t MoveCenterTabController::absoluteToRelative(const vr::HmdVector3_t& absolute) const +vr::HmdVector3_t MoveCenterTabController::absoluteToRelative( + const vr::HmdVector3_t& absolute ) const { - double absoluteControllerPosition[] = { - static_cast( absolute.v[0] - m_offsetX), - static_cast( absolute.v[1] - m_offsetY), - static_cast( absolute.v[2] - m_offsetZ) - }; - rotateCoordinates( absoluteControllerPosition, m_rotation * k_centidegreesToRadians); - vr::HmdVector3_t relativeControllerPosition = { - static_cast( absoluteControllerPosition[0] ), - static_cast( absoluteControllerPosition[1] ), - static_cast( absoluteControllerPosition[2] ) - }; - return relativeControllerPosition; + double absoluteControllerPosition[] + = { static_cast( absolute.v[0] - m_offsetX ), + static_cast( absolute.v[1] - m_offsetY ), + static_cast( absolute.v[2] - m_offsetZ ) }; + rotateCoordinates( absoluteControllerPosition, + m_rotation * k_centidegreesToRadians ); + vr::HmdVector3_t relativeControllerPosition + = { static_cast( absoluteControllerPosition[0] ), + static_cast( absoluteControllerPosition[1] ), + static_cast( absoluteControllerPosition[2] ) }; + return relativeControllerPosition; } -// Displace the entire universe by some difference. Both 'from' and 'to' are in absolute coordinates. -void MoveCenterTabController::displaceUniverse(const vr::HmdVector3_t& from, const vr::HmdVector3_t& to) +// Displace the entire universe by some difference. Both 'from' and 'to' are in +// absolute coordinates. +void MoveCenterTabController::displaceUniverse( const vr::HmdVector3_t& from, + const vr::HmdVector3_t& to ) { double diff[3] = { - static_cast( to.v[0] - - from.v[0] ), - static_cast( to.v[1] - - from.v[1] ), - static_cast( to.v[2] - - from.v[2] ), + static_cast( to.v[0] - from.v[0] ), + static_cast( to.v[1] - from.v[1] ), + static_cast( to.v[2] - from.v[2] ), }; // offset is un-rotated coordinates @@ -2269,7 +2269,7 @@ void MoveCenterTabController::displaceUniverse(const vr::HmdVector3_t& from, con // prevent positional glitches from exceeding max openvr offset clamps. // We do this by detecting a drag larger than 100m in a single frame. if ( abs( diff[0] ) > 100.0 || abs( diff[1] ) > 100.0 - || abs( diff[2] ) > 100.0 ) + || abs( diff[2] ) > 100.0 ) { reset(); } @@ -2290,22 +2290,21 @@ void MoveCenterTabController::displaceUniverse(const vr::HmdVector3_t& from, con double secondsSinceLastDragUpdate = std::chrono::duration( std::chrono::steady_clock::now() - - m_lastDragUpdateTimePoint ) - .count(); + - m_lastDragUpdateTimePoint ) + .count(); - // TODO: Add 'effects fling' boolean? or simply return diff[] as a HmdVect3_t + // TODO: Add 'effects fling' boolean? or simply return diff[] as a + // HmdVect3_t m_velocity[0] = ( diff[0] / secondsSinceLastDragUpdate ) - * static_cast( flingStrength() ); + * static_cast( flingStrength() ); m_velocity[1] = ( diff[1] / secondsSinceLastDragUpdate ) - * static_cast( flingStrength() ); + * static_cast( flingStrength() ); m_velocity[2] = ( diff[2] / secondsSinceLastDragUpdate ) - * static_cast( flingStrength() ); - + * static_cast( flingStrength() ); } void MoveCenterTabController::updateHandDrag( - vr::TrackedDevicePose_t* devicePoses - ) + vr::TrackedDevicePose_t* devicePoses ) { auto moveHandId = vr::VRSystem()->GetTrackedDeviceIndexForControllerRole( m_activeDragHand ); @@ -2353,17 +2352,18 @@ void MoveCenterTabController::updateHandDrag( return; } - vr::HmdVector3_t relativeControllerPosition = { - movePose->mDeviceToAbsoluteTracking.m[0][3], - movePose->mDeviceToAbsoluteTracking.m[1][3], - movePose->mDeviceToAbsoluteTracking.m[2][3] - }; - auto absoluteControllerPosition = relativeToAbsolute(relativeControllerPosition); + vr::HmdVector3_t relativeControllerPosition + = { movePose->mDeviceToAbsoluteTracking.m[0][3], + movePose->mDeviceToAbsoluteTracking.m[1][3], + movePose->mDeviceToAbsoluteTracking.m[2][3] }; + auto absoluteControllerPosition + = relativeToAbsolute( relativeControllerPosition ); - if ( m_lastMoveHand == m_activeDragHand ) + if ( m_lastMoveHand == m_activeDragHand ) { // Displace from last controller position to current - displaceUniverse(m_lastControllerPosition, absoluteControllerPosition); + displaceUniverse( m_lastControllerPosition, + absoluteControllerPosition ); } m_lastControllerPosition = absoluteControllerPosition; m_lastMoveHand = m_activeDragHand; diff --git a/src/tabcontrollers/MoveCenterTabController.h b/src/tabcontrollers/MoveCenterTabController.h index 2ce7051d..104591ba 100644 --- a/src/tabcontrollers/MoveCenterTabController.h +++ b/src/tabcontrollers/MoveCenterTabController.h @@ -283,11 +283,16 @@ class MoveCenterTabController : public QObject void incomingSeatedReset(); void setBoundsBasisHeight( float newHeight ); float getBoundsBasisMaxY(); - void setRotationAroundPivot( int value, bool notify, const vr::HmdVector3_t& pivot ); - void displaceUniverse( const vr::HmdVector3_t& from, const vr::HmdVector3_t& to ); - - vr::HmdVector3_t relativeToAbsolute(const vr::HmdVector3_t& coordinates_in) const; - vr::HmdVector3_t absoluteToRelative(const vr::HmdVector3_t& absolute) const; + void setRotationAroundPivot( int value, + bool notify, + const vr::HmdVector3_t& pivot ); + void displaceUniverse( const vr::HmdVector3_t& from, + const vr::HmdVector3_t& to ); + + vr::HmdVector3_t + relativeToAbsolute( const vr::HmdVector3_t& coordinates_in ) const; + vr::HmdVector3_t + absoluteToRelative( const vr::HmdVector3_t& absolute ) const; void updateChaperoneResetData(); diff --git a/src/tabcontrollers/RotationTabController.cpp b/src/tabcontrollers/RotationTabController.cpp index fe09ed20..e35c6aba 100644 --- a/src/tabcontrollers/RotationTabController.cpp +++ b/src/tabcontrollers/RotationTabController.cpp @@ -79,8 +79,9 @@ void RotationTabController::eventLoopTick( { if ( devicePoses ) { - auto moveHandId = vr::VRSystem()->GetTrackedDeviceIndexForControllerRole( - vr::TrackedControllerRole_RightHand); + auto moveHandId + = vr::VRSystem()->GetTrackedDeviceIndexForControllerRole( + vr::TrackedControllerRole_RightHand ); lastHandPose = devicePoses[moveHandId]; m_isHMDActive = false; @@ -584,43 +585,45 @@ void RotationTabController::doAutoTurn( void RotationTabController::addAutoAlignPoint() { - // TODO: State machine: if we have >2 align points, freeze vestibular motion/ratchetting/etc - // remain frozen until after we move away from the aligned area + // TODO: State machine: if we have >2 align points, freeze vestibular + // motion/ratchetting/etc remain frozen until after we move away from the + // aligned area - LOG(INFO) << "point added: " << autoAlignPoints.size(); + LOG( INFO ) << "point added: " << autoAlignPoints.size(); // get the location of right hand, push_back onto autoAlignPoints - // TODO: - vr::HmdVector3_t new_point = { - lastHandPose.mDeviceToAbsoluteTracking.m[0][3], - lastHandPose.mDeviceToAbsoluteTracking.m[1][3], - lastHandPose.mDeviceToAbsoluteTracking.m[2][3] - }; - // TODO: Probably smarter to actually just keep the virtual points as virtual until we use them. - // Then if they drift we don't care. - vr::HmdVector3_t absolute_point = parent->m_moveCenterTabController.relativeToAbsolute(new_point); - autoAlignPoints.push_back(absolute_point); + // TODO: + vr::HmdVector3_t new_point + = { lastHandPose.mDeviceToAbsoluteTracking.m[0][3], + lastHandPose.mDeviceToAbsoluteTracking.m[1][3], + lastHandPose.mDeviceToAbsoluteTracking.m[2][3] }; + // TODO: Probably smarter to actually just keep the virtual points as + // virtual until we use them. Then if they drift we don't care. + vr::HmdVector3_t absolute_point + = parent->m_moveCenterTabController.relativeToAbsolute( new_point ); + autoAlignPoints.push_back( absolute_point ); // if we have exactly 4 points, go into main loop - if(autoAlignPoints.size() == 4) + if ( autoAlignPoints.size() == 4 ) { vr::HmdVector3_t realFirstPoint = autoAlignPoints[0]; - vr::HmdVector3_t realSecondPoint = autoAlignPoints[1]; + vr::HmdVector3_t realSecondPoint = autoAlignPoints[1]; vr::HmdVector3_t virtualFirstPoint = autoAlignPoints[2]; vr::HmdVector3_t virtualSecondPoint = autoAlignPoints[3]; - // Rotate the universe to align, pivoting around the real point - double realEdgeAngle = static_cast( std::atan2( - realFirstPoint.v[0] - realSecondPoint.v[0], - realFirstPoint.v[2] - realSecondPoint.v[2] ) ); - double virtualEdgeAngle = static_cast( std::atan2( - virtualFirstPoint.v[0] - virtualSecondPoint.v[0], - virtualFirstPoint.v[2] - virtualSecondPoint.v[2] ) ); - double delta_degrees = (realEdgeAngle - virtualEdgeAngle) * k_radiansToCentidegrees; + // Rotate the universe to align, pivoting around the real point + double realEdgeAngle = static_cast( + std::atan2( realFirstPoint.v[0] - realSecondPoint.v[0], + realFirstPoint.v[2] - realSecondPoint.v[2] ) ); + double virtualEdgeAngle = static_cast( + std::atan2( virtualFirstPoint.v[0] - virtualSecondPoint.v[0], + virtualFirstPoint.v[2] - virtualSecondPoint.v[2] ) ); + double delta_degrees + = ( realEdgeAngle - virtualEdgeAngle ) * k_radiansToCentidegrees; int newRotationAngleDeg = static_cast( - parent->m_moveCenterTabController.rotation() - + delta_degrees ); + parent->m_moveCenterTabController.rotation() + delta_degrees ); - // these need to be in the new, offset position. TODO: needs to be converted into new relative position? + // these need to be in the new, offset position. TODO: needs to be + // converted into new relative position? vr::HmdVector3_t autoAlignPivot = realFirstPoint; autoAlignPivot.v[0] -= realFirstPoint.v[0] - virtualFirstPoint.v[0]; autoAlignPivot.v[1] -= realFirstPoint.v[1] - virtualFirstPoint.v[1]; @@ -628,16 +631,19 @@ void RotationTabController::addAutoAlignPoint() // TODO: if centered, use the center of both points as the pivot parent->m_moveCenterTabController.setRotationAroundPivot( - newRotationAngleDeg, true, parent->m_moveCenterTabController.absoluteToRelative(virtualFirstPoint)); - + newRotationAngleDeg, + true, + parent->m_moveCenterTabController.absoluteToRelative( + virtualFirstPoint ) ); - // Align the first of VR points (points[2]) with the first of the real points (points[0]) purely in position - parent->m_moveCenterTabController.displaceUniverse(virtualFirstPoint, realFirstPoint); + // Align the first of VR points (points[2]) with the first of the real + // points (points[0]) purely in position + parent->m_moveCenterTabController.displaceUniverse( virtualFirstPoint, + realFirstPoint ); // end of main loop, clear autoAlignPoints autoAlignPoints.clear(); } - } // getters diff --git a/src/tabcontrollers/RotationTabController.h b/src/tabcontrollers/RotationTabController.h index bd11bebd..b70dd38a 100644 --- a/src/tabcontrollers/RotationTabController.h +++ b/src/tabcontrollers/RotationTabController.h @@ -159,7 +159,6 @@ class RotationTabController : public QObject bool viewRatchettingEnabled() const; double viewRatchettingPercent() const; - public slots: void setAutoTurnEnabled( bool value, bool notify = true ); void setAutoTurnShowNotification( bool value, bool notify = true ); @@ -174,7 +173,7 @@ public slots: void setVestibularMotionRadius( double value, bool notify = true ); void setViewRatchettingEnabled( bool value, bool notify = true ); void setViewRatchettingPercent( double value, bool notify = true ); - + // Experimental - auto-align void addAutoAlignPoint(); From 2546d824a4069a23eef8ab4a6471c9d540f5f056 Mon Sep 17 00:00:00 2001 From: Chelsea Jaggi Date: Wed, 18 Nov 2020 20:55:39 -0800 Subject: [PATCH 5/8] More formatting --- src/settings/internal/settings_internal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings/internal/settings_internal.h b/src/settings/internal/settings_internal.h index 1e38b4f0..5f13b079 100644 --- a/src/settings/internal/settings_internal.h +++ b/src/settings/internal/settings_internal.h @@ -81,7 +81,7 @@ void saveQtSetting( const SettingCategory category, getQSettings().endGroup(); } -template [[nodiscard]] bool isValidQVariant( const QVariant v ) +template [[nodiscard]] bool isValidQVariant( const QVariant v ) { auto savedSettingIsValid = v.isValid() && !v.isNull(); From de835a52efee182140aa4f395389b7a928e85e1f Mon Sep 17 00:00:00 2001 From: Chelsea Jaggi Date: Sun, 29 Nov 2020 17:31:54 -0800 Subject: [PATCH 6/8] Left and right handed auto-align, add HUD popup --- src/openvr/ivrinput.cpp | 12 ++- src/openvr/ivrinput.h | 9 +- src/overlaycontroller.cpp | 8 +- src/package_files/action_manifest.json | 10 +- src/res/img/rotation/autoalign.svg | 108 +++++++++++++++++++ src/res/img/rotation/autoalign1.png | Bin 0 -> 3565 bytes src/res/img/rotation/autoalign2.png | Bin 0 -> 5109 bytes src/res/img/rotation/autoalign3.png | Bin 0 -> 7141 bytes src/res/img/rotation/autoalign4.png | Bin 0 -> 9265 bytes src/tabcontrollers/RotationTabController.cpp | 68 ++++++++++-- src/tabcontrollers/RotationTabController.h | 8 +- 11 files changed, 205 insertions(+), 18 deletions(-) create mode 100644 src/res/img/rotation/autoalign.svg create mode 100644 src/res/img/rotation/autoalign1.png create mode 100644 src/res/img/rotation/autoalign2.png create mode 100644 src/res/img/rotation/autoalign3.png create mode 100644 src/res/img/rotation/autoalign4.png diff --git a/src/openvr/ivrinput.cpp b/src/openvr/ivrinput.cpp index 3ce64008..e4d5c4e2 100644 --- a/src/openvr/ivrinput.cpp +++ b/src/openvr/ivrinput.cpp @@ -132,7 +132,8 @@ SteamIVRInput::SteamIVRInput() m_smoothTurnLeft( action_keys::smoothTurnLeft ), m_smoothTurnRight( action_keys::smoothTurnRight ), m_autoTurnToggle( action_keys::autoTurnToggle ), - m_addAutoAlignPoint( action_keys::addAutoAlignPoint ), + m_addAutoAlignPointLeft( action_keys::addAutoAlignPointLeft ), + m_addAutoAlignPointRight( action_keys::addAutoAlignPointRight ), m_xAxisLockToggle( action_keys::xAxisLockToggle ), m_yAxisLockToggle( action_keys::yAxisLockToggle ), m_zAxisLockToggle( action_keys::zAxisLockToggle ), @@ -301,9 +302,14 @@ bool SteamIVRInput::autoTurnToggle() { return isDigitalActionActivatedOnce( m_autoTurnToggle ); } -bool SteamIVRInput::addAutoAlignPoint() +bool SteamIVRInput::addAutoAlignPointLeft() { - return isDigitalActionActivatedOnce( m_addAutoAlignPoint ); + return isDigitalActionActivatedOnce( m_addAutoAlignPointLeft ); +} + +bool SteamIVRInput::addAutoAlignPointRight() +{ + return isDigitalActionActivatedOnce( m_addAutoAlignPointRight ); } bool SteamIVRInput::xAxisLockToggle() diff --git a/src/openvr/ivrinput.h b/src/openvr/ivrinput.h index a057dfb7..be2cbe98 100644 --- a/src/openvr/ivrinput.h +++ b/src/openvr/ivrinput.h @@ -47,7 +47,8 @@ namespace action_keys constexpr auto smoothTurnLeft = "/actions/motion/in/SmoothTurnLeft"; constexpr auto smoothTurnRight = "/actions/motion/in/SmoothTurnRight"; constexpr auto autoTurnToggle = "/actions/motion/in/AutoTurnToggle"; - constexpr auto addAutoAlignPoint = "/actions/motion/in/AddAutoAlignPoint"; + constexpr auto addAutoAlignPointLeft = "/actions/motion/in/AddAutoAlignPointLeft"; + constexpr auto addAutoAlignPointRight = "/actions/motion/in/AddAutoAlignPointRight"; constexpr auto xAxisLockToggle = "/actions/misc/in/XAxisLockToggle"; constexpr auto yAxisLockToggle = "/actions/misc/in/YAxisLockToggle"; @@ -152,7 +153,8 @@ class SteamIVRInput bool smoothTurnLeft(); bool smoothTurnRight(); bool autoTurnToggle(); - bool addAutoAlignPoint(); + bool addAutoAlignPointLeft(); + bool addAutoAlignPointRight(); bool xAxisLockToggle(); bool yAxisLockToggle(); bool zAxisLockToggle(); @@ -235,7 +237,8 @@ class SteamIVRInput DigitalAction m_smoothTurnLeft; DigitalAction m_smoothTurnRight; DigitalAction m_autoTurnToggle; - DigitalAction m_addAutoAlignPoint; + DigitalAction m_addAutoAlignPointLeft; + DigitalAction m_addAutoAlignPointRight; DigitalAction m_xAxisLockToggle; DigitalAction m_yAxisLockToggle; DigitalAction m_zAxisLockToggle; diff --git a/src/overlaycontroller.cpp b/src/overlaycontroller.cpp index 665ad6db..a8f921e0 100644 --- a/src/overlaycontroller.cpp +++ b/src/overlaycontroller.cpp @@ -755,9 +755,13 @@ void OverlayController::processRotationBindings() m_rotationTabController.setAutoTurnEnabled( !( m_rotationTabController.autoTurnEnabled() ) ); } - if ( m_actions.addAutoAlignPoint() ) + if ( m_actions.addAutoAlignPointLeft() ) { - m_rotationTabController.addAutoAlignPoint(); + m_rotationTabController.addAutoAlignPoint( false ); + } + if ( m_actions.addAutoAlignPointRight() ) + { + m_rotationTabController.addAutoAlignPoint( true ); } } /*! diff --git a/src/package_files/action_manifest.json b/src/package_files/action_manifest.json index 3580b7ee..f43ef04a 100644 --- a/src/package_files/action_manifest.json +++ b/src/package_files/action_manifest.json @@ -164,7 +164,12 @@ "type": "boolean" }, { - "name": "/actions/motion/in/AddAutoAlignPoint", + "name": "/actions/motion/in/AddAutoAlignPointLeft", + "requirement": "optional", + "type": "boolean" + }, + { + "name": "/actions/motion/in/AddAutoAlignPointRight", "requirement": "optional", "type": "boolean" }, @@ -292,7 +297,8 @@ "/actions/motion/in/SmoothTurnLeft" : "Smooth-Turn Left", "/actions/motion/in/SmoothTurnRight" : "Smooth-Turn Right", "/actions/motion/in/AutoTurnToggle" : "Auto-Turn Toggle", - "/actions/motion/in/AddAutoAlignPoint" : "Add Auto Align Point", + "/actions/motion/in/AddAutoAlignPointLeft" : "Add Auto Align Point (Left Hand)", + "/actions/motion/in/AddAutoAlignPointRight" : "Add Auto Align Point (Right Hand)", "/actions/misc/in/XAxisLockToggle" : "X-Axis Lock Toggle", "/actions/misc/in/YAxisLockToggle" : "Y-Axis Lock Toggle", diff --git a/src/res/img/rotation/autoalign.svg b/src/res/img/rotation/autoalign.svg new file mode 100644 index 00000000..8b933753 --- /dev/null +++ b/src/res/img/rotation/autoalign.svg @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + image/svg+xml + + + + + + + A + + + + + + + diff --git a/src/res/img/rotation/autoalign1.png b/src/res/img/rotation/autoalign1.png new file mode 100644 index 0000000000000000000000000000000000000000..98a50304a06e2db78c905da69cb2573783b1a4a2 GIT binary patch literal 3565 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 Hx$FN1ETy0J literal 0 HcmV?d00001 diff --git a/src/res/img/rotation/autoalign2.png b/src/res/img/rotation/autoalign2.png new file mode 100644 index 0000000000000000000000000000000000000000..1e7d35dc8d2598041c2b3a97fc9753e9821b92f6 GIT binary patch literal 5109 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 R$(J3A;ceqvH8&lj{tqd|%Q^r6 literal 0 HcmV?d00001 diff --git a/src/res/img/rotation/autoalign3.png b/src/res/img/rotation/autoalign3.png new file mode 100644 index 0000000000000000000000000000000000000000..57367aca284a90bc2e39967b4c11646373a1ecb9 GIT binary patch literal 7141 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 + vr::TrackedControllerRole_LeftHand ); + lastLeftHandPose = devicePoses[leftHandId]; + auto rightHandId = vr::VRSystem()->GetTrackedDeviceIndexForControllerRole( vr::TrackedControllerRole_RightHand ); - lastHandPose = devicePoses[moveHandId]; + lastRightHandPose = devicePoses[rightHandId]; m_isHMDActive = false; std::lock_guard lock( @@ -583,15 +603,17 @@ void RotationTabController::doAutoTurn( } } -void RotationTabController::addAutoAlignPoint() +void RotationTabController::addAutoAlignPoint(bool rightHanded) { // TODO: State machine: if we have >2 align points, freeze vestibular // motion/ratchetting/etc remain frozen until after we move away from the // aligned area + const auto& lastHandPose = rightHanded ? + lastRightHandPose : + lastLeftHandPose; - LOG( INFO ) << "point added: " << autoAlignPoints.size(); - // get the location of right hand, push_back onto autoAlignPoints - // TODO: + LOG( DEBUG ) << "point added: " << autoAlignPoints.size(); + // get the location of hand, push_back onto autoAlignPoints vr::HmdVector3_t new_point = { lastHandPose.mDeviceToAbsoluteTracking.m[0][3], lastHandPose.mDeviceToAbsoluteTracking.m[1][3], @@ -644,6 +666,40 @@ void RotationTabController::addAutoAlignPoint() // end of main loop, clear autoAlignPoints autoAlignPoints.clear(); } + switch(autoAlignPoints.size()) + { + case 1: + vr::VROverlay()->SetOverlayFromFile( + m_autoturnValues.overlayHandle, + m_autoturnValues.alignPointOnePath.c_str() ); + break; + case 2: + vr::VROverlay()->SetOverlayFromFile( + m_autoturnValues.overlayHandle, + m_autoturnValues.alignPointTwoPath.c_str() ); + break; + case 3: + vr::VROverlay()->SetOverlayFromFile( + m_autoturnValues.overlayHandle, + m_autoturnValues.alignPointThreePath.c_str() ); + break; + case 4: + vr::VROverlay()->SetOverlayFromFile( + m_autoturnValues.overlayHandle, + m_autoturnValues.alignPointFourPath.c_str() ); + break; + } + + // TODO: configure whether auto-align has HUD popup independently + if ( autoTurnShowNotification() + && getNotificationOverlayHandle() != vr::k_ulOverlayHandleInvalid ) + { + vr::VROverlay()->SetOverlayAlpha( getNotificationOverlayHandle(), + 1.0f ); + vr::VROverlay()->ShowOverlay( getNotificationOverlayHandle() ); + m_autoTurnNotificationTimestamp.emplace( + std::chrono::steady_clock::now() ); + } } // getters diff --git a/src/tabcontrollers/RotationTabController.h b/src/tabcontrollers/RotationTabController.h index b70dd38a..3102ecaa 100644 --- a/src/tabcontrollers/RotationTabController.h +++ b/src/tabcontrollers/RotationTabController.h @@ -101,6 +101,10 @@ class RotationTabController : public QObject vr::VROverlayHandle_t overlayHandle = vr::k_ulOverlayHandleInvalid; std::string autoturnPath; std::string noautoturnPath; + std::string alignPointOnePath; + std::string alignPointTwoPath; + std::string alignPointThreePath; + std::string alignPointFourPath; } m_autoturnValues; virtual vr::VROverlayHandle_t getNotificationOverlayHandle() @@ -122,7 +126,7 @@ class RotationTabController : public QObject m_autoTurnNotificationTimestamp; std::vector autoAlignPoints; - vr::TrackedDevicePose_t lastHandPose; + vr::TrackedDevicePose_t lastLeftHandPose, lastRightHandPose; bool m_isHMDActive = false; @@ -175,7 +179,7 @@ public slots: void setViewRatchettingPercent( double value, bool notify = true ); // Experimental - auto-align - void addAutoAlignPoint(); + void addAutoAlignPoint( bool rightHanded ); signals: From 8bbd1709ebb6acfe7c845e67755cc5567751a6d5 Mon Sep 17 00:00:00 2001 From: Chelsea Jaggi Date: Sun, 29 Nov 2020 19:52:47 -0800 Subject: [PATCH 7/8] formatting --- src/openvr/ivrinput.h | 6 +- src/tabcontrollers/RotationTabController.cpp | 69 ++++++++++---------- src/tabcontrollers/RotationTabController.h | 8 +-- 3 files changed, 44 insertions(+), 39 deletions(-) diff --git a/src/openvr/ivrinput.h b/src/openvr/ivrinput.h index be2cbe98..84248a00 100644 --- a/src/openvr/ivrinput.h +++ b/src/openvr/ivrinput.h @@ -47,8 +47,10 @@ namespace action_keys constexpr auto smoothTurnLeft = "/actions/motion/in/SmoothTurnLeft"; constexpr auto smoothTurnRight = "/actions/motion/in/SmoothTurnRight"; constexpr auto autoTurnToggle = "/actions/motion/in/AutoTurnToggle"; - constexpr auto addAutoAlignPointLeft = "/actions/motion/in/AddAutoAlignPointLeft"; - constexpr auto addAutoAlignPointRight = "/actions/motion/in/AddAutoAlignPointRight"; + constexpr auto addAutoAlignPointLeft + = "/actions/motion/in/AddAutoAlignPointLeft"; + constexpr auto addAutoAlignPointRight + = "/actions/motion/in/AddAutoAlignPointRight"; constexpr auto xAxisLockToggle = "/actions/misc/in/XAxisLockToggle"; constexpr auto yAxisLockToggle = "/actions/misc/in/YAxisLockToggle"; diff --git a/src/tabcontrollers/RotationTabController.cpp b/src/tabcontrollers/RotationTabController.cpp index 24e37413..1052b2e0 100644 --- a/src/tabcontrollers/RotationTabController.cpp +++ b/src/tabcontrollers/RotationTabController.cpp @@ -38,10 +38,14 @@ void RotationTabController::initStage2( OverlayController* var_parent ) constexpr auto autoturnIconFilepath = "/res/img/rotation/autoturn.png"; constexpr auto noautoturnIconFilepath = "/res/img/rotation/noautoturn.png"; - constexpr auto alignPointOneIconFilepath = "/res/img/rotation/autoalign1.png"; - constexpr auto alignPointTwoIconFilepath = "/res/img/rotation/autoalign2.png"; - constexpr auto alignPointThreeIconFilepath = "/res/img/rotation/autoalign3.png"; - constexpr auto alignPointFourIconFilepath = "/res/img/rotation/autoalign4.png"; + constexpr auto alignPointOneIconFilepath + = "/res/img/rotation/autoalign1.png"; + constexpr auto alignPointTwoIconFilepath + = "/res/img/rotation/autoalign2.png"; + constexpr auto alignPointThreeIconFilepath + = "/res/img/rotation/autoalign3.png"; + constexpr auto alignPointFourIconFilepath + = "/res/img/rotation/autoalign4.png"; const auto autoturnIconFilePath = paths::verifyIconFilePath( autoturnIconFilepath ); @@ -603,14 +607,13 @@ void RotationTabController::doAutoTurn( } } -void RotationTabController::addAutoAlignPoint(bool rightHanded) +void RotationTabController::addAutoAlignPoint( bool rightHanded ) { // TODO: State machine: if we have >2 align points, freeze vestibular // motion/ratchetting/etc remain frozen until after we move away from the // aligned area - const auto& lastHandPose = rightHanded ? - lastRightHandPose : - lastLeftHandPose; + const auto& lastHandPose + = rightHanded ? lastRightHandPose : lastLeftHandPose; LOG( DEBUG ) << "point added: " << autoAlignPoints.size(); // get the location of hand, push_back onto autoAlignPoints @@ -666,31 +669,31 @@ void RotationTabController::addAutoAlignPoint(bool rightHanded) // end of main loop, clear autoAlignPoints autoAlignPoints.clear(); } - switch(autoAlignPoints.size()) - { - case 1: - vr::VROverlay()->SetOverlayFromFile( - m_autoturnValues.overlayHandle, - m_autoturnValues.alignPointOnePath.c_str() ); - break; - case 2: - vr::VROverlay()->SetOverlayFromFile( - m_autoturnValues.overlayHandle, - m_autoturnValues.alignPointTwoPath.c_str() ); - break; - case 3: - vr::VROverlay()->SetOverlayFromFile( - m_autoturnValues.overlayHandle, - m_autoturnValues.alignPointThreePath.c_str() ); - break; - case 4: - vr::VROverlay()->SetOverlayFromFile( - m_autoturnValues.overlayHandle, - m_autoturnValues.alignPointFourPath.c_str() ); - break; - } - - // TODO: configure whether auto-align has HUD popup independently + switch ( autoAlignPoints.size() ) + { + case 1: + vr::VROverlay()->SetOverlayFromFile( + m_autoturnValues.overlayHandle, + m_autoturnValues.alignPointOnePath.c_str() ); + break; + case 2: + vr::VROverlay()->SetOverlayFromFile( + m_autoturnValues.overlayHandle, + m_autoturnValues.alignPointTwoPath.c_str() ); + break; + case 3: + vr::VROverlay()->SetOverlayFromFile( + m_autoturnValues.overlayHandle, + m_autoturnValues.alignPointThreePath.c_str() ); + break; + case 4: + vr::VROverlay()->SetOverlayFromFile( + m_autoturnValues.overlayHandle, + m_autoturnValues.alignPointFourPath.c_str() ); + break; + } + + // TODO: configure whether auto-align has HUD popup independently if ( autoTurnShowNotification() && getNotificationOverlayHandle() != vr::k_ulOverlayHandleInvalid ) { diff --git a/src/tabcontrollers/RotationTabController.h b/src/tabcontrollers/RotationTabController.h index 3102ecaa..3113e06b 100644 --- a/src/tabcontrollers/RotationTabController.h +++ b/src/tabcontrollers/RotationTabController.h @@ -101,10 +101,10 @@ class RotationTabController : public QObject vr::VROverlayHandle_t overlayHandle = vr::k_ulOverlayHandleInvalid; std::string autoturnPath; std::string noautoturnPath; - std::string alignPointOnePath; - std::string alignPointTwoPath; - std::string alignPointThreePath; - std::string alignPointFourPath; + std::string alignPointOnePath; + std::string alignPointTwoPath; + std::string alignPointThreePath; + std::string alignPointFourPath; } m_autoturnValues; virtual vr::VROverlayHandle_t getNotificationOverlayHandle() From fc64b4238bd31d73ba78eb42212d864119d73ae3 Mon Sep 17 00:00:00 2001 From: Chelsea Jaggi Date: Sun, 29 Nov 2020 20:13:06 -0800 Subject: [PATCH 8/8] fix HUD, fix startup issue --- src/tabcontrollers/RotationTabController.cpp | 85 +++++++++++--------- 1 file changed, 49 insertions(+), 36 deletions(-) diff --git a/src/tabcontrollers/RotationTabController.cpp b/src/tabcontrollers/RotationTabController.cpp index 1052b2e0..d0b4c195 100644 --- a/src/tabcontrollers/RotationTabController.cpp +++ b/src/tabcontrollers/RotationTabController.cpp @@ -102,11 +102,23 @@ void RotationTabController::eventLoopTick( auto leftHandId = vr::VRSystem()->GetTrackedDeviceIndexForControllerRole( vr::TrackedControllerRole_LeftHand ); - lastLeftHandPose = devicePoses[leftHandId]; + if ( vr::k_unTrackedDeviceIndexInvalid + && devicePoses[leftHandId].bPoseIsValid + && devicePoses[leftHandId].eTrackingResult + == vr::TrackingResult_Running_OK ) + { + lastLeftHandPose = devicePoses[leftHandId]; + } auto rightHandId = vr::VRSystem()->GetTrackedDeviceIndexForControllerRole( vr::TrackedControllerRole_RightHand ); - lastRightHandPose = devicePoses[rightHandId]; + if ( vr::k_unTrackedDeviceIndexInvalid + && devicePoses[rightHandId].bPoseIsValid + && devicePoses[rightHandId].eTrackingResult + == vr::TrackingResult_Running_OK ) + { + lastRightHandPose = devicePoses[rightHandId]; + } m_isHMDActive = false; std::lock_guard lock( @@ -627,6 +639,41 @@ void RotationTabController::addAutoAlignPoint( bool rightHanded ) = parent->m_moveCenterTabController.relativeToAbsolute( new_point ); autoAlignPoints.push_back( absolute_point ); + switch ( autoAlignPoints.size() ) + { + case 1: + vr::VROverlay()->SetOverlayFromFile( + m_autoturnValues.overlayHandle, + m_autoturnValues.alignPointOnePath.c_str() ); + break; + case 2: + vr::VROverlay()->SetOverlayFromFile( + m_autoturnValues.overlayHandle, + m_autoturnValues.alignPointTwoPath.c_str() ); + break; + case 3: + vr::VROverlay()->SetOverlayFromFile( + m_autoturnValues.overlayHandle, + m_autoturnValues.alignPointThreePath.c_str() ); + break; + case 4: + vr::VROverlay()->SetOverlayFromFile( + m_autoturnValues.overlayHandle, + m_autoturnValues.alignPointFourPath.c_str() ); + break; + } + + // TODO: configure whether auto-align has HUD popup independently + if ( autoTurnShowNotification() + && getNotificationOverlayHandle() != vr::k_ulOverlayHandleInvalid ) + { + vr::VROverlay()->SetOverlayAlpha( getNotificationOverlayHandle(), + 1.0f ); + vr::VROverlay()->ShowOverlay( getNotificationOverlayHandle() ); + m_autoTurnNotificationTimestamp.emplace( + std::chrono::steady_clock::now() ); + } + // if we have exactly 4 points, go into main loop if ( autoAlignPoints.size() == 4 ) { @@ -669,40 +716,6 @@ void RotationTabController::addAutoAlignPoint( bool rightHanded ) // end of main loop, clear autoAlignPoints autoAlignPoints.clear(); } - switch ( autoAlignPoints.size() ) - { - case 1: - vr::VROverlay()->SetOverlayFromFile( - m_autoturnValues.overlayHandle, - m_autoturnValues.alignPointOnePath.c_str() ); - break; - case 2: - vr::VROverlay()->SetOverlayFromFile( - m_autoturnValues.overlayHandle, - m_autoturnValues.alignPointTwoPath.c_str() ); - break; - case 3: - vr::VROverlay()->SetOverlayFromFile( - m_autoturnValues.overlayHandle, - m_autoturnValues.alignPointThreePath.c_str() ); - break; - case 4: - vr::VROverlay()->SetOverlayFromFile( - m_autoturnValues.overlayHandle, - m_autoturnValues.alignPointFourPath.c_str() ); - break; - } - - // TODO: configure whether auto-align has HUD popup independently - if ( autoTurnShowNotification() - && getNotificationOverlayHandle() != vr::k_ulOverlayHandleInvalid ) - { - vr::VROverlay()->SetOverlayAlpha( getNotificationOverlayHandle(), - 1.0f ); - vr::VROverlay()->ShowOverlay( getNotificationOverlayHandle() ); - m_autoTurnNotificationTimestamp.emplace( - std::chrono::steady_clock::now() ); - } } // getters