#include "MoveCenterTabController.h" #include #include "../overlaycontroller.h" #include "../utils/Matrix.h" #include "../quaternion/quaternion.h" void rotateCoordinates( double coordinates[3], double angle ) { if ( angle == 0 ) { return; } double s = sin( angle ); double c = cos( angle ); double newX = coordinates[0] * c - coordinates[2] * s; double newZ = coordinates[0] * s + coordinates[2] * c; coordinates[0] = newX; coordinates[2] = newZ; } // application namespace namespace advsettings { using std::chrono::duration_cast; using std::chrono::milliseconds; typedef std::chrono::system_clock clock; void MoveCenterTabController::initStage1() { setTrackingUniverse( vr::VRCompositor()->GetTrackingSpace() ); auto settings = OverlayController::appSettings(); settings->beginGroup( "playspaceSettings" ); auto value = settings->value( "adjustChaperone", m_adjustChaperone ); if ( value.isValid() && !value.isNull() ) { m_adjustChaperone = value.toBool(); } value = settings->value( "rotateHand", m_settingsHandTurningEnabled ); if ( value.isValid() && !value.isNull() ) { m_settingsHandTurningEnabled = value.toBool(); } value = settings->value( "moveShortcutRight", m_settingsRightHandDragEnabled ); if ( value.isValid() && !value.isNull() ) { m_settingsRightHandDragEnabled = value.toBool(); } value = settings->value( "moveShortcutLeft", m_settingsLeftHandDragEnabled ); if ( value.isValid() && !value.isNull() ) { m_settingsLeftHandDragEnabled = value.toBool(); } value = settings->value( "turnBindLeft", m_settingsLeftHandTurnEnabled ); if ( value.isValid() && !value.isNull() ) { m_settingsLeftHandTurnEnabled = value.toBool(); } value = settings->value( "turnBindRight", m_settingsRightHandTurnEnabled ); if ( value.isValid() && !value.isNull() ) { m_settingsRightHandTurnEnabled = value.toBool(); } value = settings->value( "dragComfortFactor", m_dragComfortFactor ); if ( value.isValid() && !value.isNull() ) { m_dragComfortFactor = value.toUInt(); } value = settings->value( "turnComfortFactor", m_turnComfortFactor ); if ( value.isValid() && !value.isNull() ) { m_turnComfortFactor = value.toUInt(); } value = settings->value( "lockXToggle", m_lockXToggle ); if ( value.isValid() && !value.isNull() ) { m_lockXToggle = value.toBool(); } value = settings->value( "lockYToggle", m_lockYToggle ); if ( value.isValid() && !value.isNull() ) { m_lockYToggle = value.toBool(); } value = settings->value( "lockZToggle", m_lockZToggle ); if ( value.isValid() && !value.isNull() ) { m_lockZToggle = value.toBool(); } settings->endGroup(); } void MoveCenterTabController::initStage2( OverlayController* var_parent, QQuickWindow* var_widget ) { this->parent = var_parent; this->widget = var_widget; } int MoveCenterTabController::trackingUniverse() const { return static_cast( m_trackingUniverse ); } void MoveCenterTabController::setTrackingUniverse( int value, bool notify ) { if ( m_trackingUniverse != value ) { reset(); m_trackingUniverse = value; if ( notify ) { emit trackingUniverseChanged( m_trackingUniverse ); } } } float MoveCenterTabController::offsetX() const { return m_offsetX; } void MoveCenterTabController::setOffsetX( float value, bool notify ) { if ( m_offsetX != value ) { modOffsetX( value - m_offsetX, notify ); } } float MoveCenterTabController::offsetY() const { return m_offsetY; } void MoveCenterTabController::setOffsetY( float value, bool notify ) { if ( m_offsetY != value ) { modOffsetY( value - m_offsetY, notify ); } } float MoveCenterTabController::offsetZ() const { return m_offsetZ; } void MoveCenterTabController::setOffsetZ( float value, bool notify ) { if ( m_offsetZ != value ) { modOffsetZ( value - m_offsetZ, notify ); } } int MoveCenterTabController::rotation() const { return m_rotation; } int MoveCenterTabController::tempRotation() const { return m_tempRotation; } void MoveCenterTabController::setRotation( int value, bool notify ) { if ( m_rotation != value ) { double angle = ( value - m_rotation ) * k_centidegreesToRadians; // Revert now because we don't commit in RotateUniverseCenter and // AddOffsetToUniverseCenter. We do this so rotation and offset can // happen in one go, avoiding positional judder. vr::VRChaperoneSetup()->RevertWorkingCopy(); // Get hmd pose matrix. vr::TrackedDevicePose_t devicePosesForRot[vr::k_unMaxTrackedDeviceCount]; vr::VRSystem()->GetDeviceToAbsoluteTrackingPose( vr::TrackingUniverseStanding, 0.0f, devicePosesForRot, vr::k_unMaxTrackedDeviceCount ); vr::HmdMatrix34_t oldHmdPos = devicePosesForRot[0].mDeviceToAbsoluteTracking; // 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 oldAngle = -m_rotation * k_centidegreesToRadians; rotateCoordinates( oldHmdXyz, oldAngle ); // Set newHmdXyz to have additional rotation from incoming angle change. rotateCoordinates( newHmdXyz, 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] }; // Rotate the tracking univese center without committing. parent->RotateUniverseCenter( vr::TrackingUniverseOrigin( m_trackingUniverse ), static_cast( angle ), m_adjustChaperone, false ); m_rotation = value; if ( notify ) { emit rotationChanged( m_rotation ); } // Get rotated offset to apply to universe center. // We use rotated coordinates here because we have already applied // RotateUniverseCenter. This will be the final offset ready to apply, // so it must match the current universe axis rotation. double finalAngle = m_rotation * k_centidegreesToRadians; double finalHmdRotDiff[3] = { hmdRotDiff[0], 0, hmdRotDiff[2] }; rotateCoordinates( finalHmdRotDiff, finalAngle ); // We're done with calculations now so we can down-cast the double // values to float for compatilibilty with openvr format float finalHmdRotDiffFloat[3] = { static_cast( finalHmdRotDiff[0] ), static_cast( finalHmdRotDiff[1] ), static_cast( finalHmdRotDiff[2] ) }; // Apply the offset (in rotated coordinates) without commit. // We still can't commit yet because it would call // vr::VRChaperoneSetup()->RevertWorkingCopy() and we'd lose our // uncommitted RotateUniverseCenter. parent->AddOffsetToUniverseCenter( vr::TrackingUniverseOrigin( m_trackingUniverse ), finalHmdRotDiffFloat, m_adjustChaperone, false ); // Commit here because we didn't in RotateUniverseCenter and // AddOffsetToUniverseCenter to combine into one go. vr::VRChaperoneSetup()->CommitWorkingCopy( vr::EChaperoneConfigFile_Live ); // Update UI offsets. m_offsetX += static_cast( hmdRotDiff[0] ); m_offsetZ += static_cast( hmdRotDiff[2] ); if ( notify ) { emit offsetXChanged( m_offsetX ); emit offsetZChanged( m_offsetZ ); } } } void MoveCenterTabController::setTempRotation( int value, bool notify ) { m_tempRotation = value; if ( notify ) { emit tempRotationChanged( m_tempRotation ); } } bool MoveCenterTabController::adjustChaperone() const { return m_adjustChaperone; } void MoveCenterTabController::setAdjustChaperone( bool value, bool notify ) { if ( m_adjustChaperone != value ) { m_adjustChaperone = value; if ( m_trackingUniverse == vr::TrackingUniverseStanding ) { double angle = m_rotation * k_centidegreesToRadians; double offsetdir = m_adjustChaperone ? -1.0 : 1.0; double offset[3] = { offsetdir * static_cast( m_offsetX ), offsetdir * static_cast( m_offsetY ), offsetdir * static_cast( m_offsetZ ) }; rotateCoordinates( offset, angle ); // We're done with calculations so down-cast to float for // compatibility with openvr format float offsetFloat[3] = { static_cast( offset[0] ), static_cast( offset[1] ), static_cast( offset[2] ) }; parent->AddOffsetToCollisionBounds( offsetFloat ); } auto settings = OverlayController::appSettings(); settings->beginGroup( "playspaceSettings" ); settings->setValue( "adjustChaperone", m_adjustChaperone ); settings->endGroup(); settings->sync(); if ( notify ) { emit adjustChaperoneChanged( m_adjustChaperone ); } } } bool MoveCenterTabController::rotateHand() const { return m_settingsHandTurningEnabled; } void MoveCenterTabController::setRotateHand( bool value, bool notify ) { m_settingsHandTurningEnabled = value; auto settings = OverlayController::appSettings(); settings->beginGroup( "playspaceSettings" ); settings->setValue( "rotateHand", m_settingsHandTurningEnabled ); settings->endGroup(); settings->sync(); if ( notify ) { emit rotateHandChanged( m_settingsHandTurningEnabled ); } } bool MoveCenterTabController::moveShortcutRight() const { return m_settingsRightHandDragEnabled; } void MoveCenterTabController::setMoveShortcutRight( bool value, bool notify ) { m_settingsRightHandDragEnabled = value; auto settings = OverlayController::appSettings(); settings->beginGroup( "playspaceSettings" ); settings->setValue( "moveShortcutRight", m_settingsRightHandDragEnabled ); settings->endGroup(); settings->sync(); if ( notify ) { emit moveShortcutRightChanged( m_settingsRightHandDragEnabled ); } } bool MoveCenterTabController::moveShortcutLeft() const { return m_settingsLeftHandDragEnabled; } void MoveCenterTabController::setMoveShortcutLeft( bool value, bool notify ) { m_settingsLeftHandDragEnabled = value; auto settings = OverlayController::appSettings(); settings->beginGroup( "playspaceSettings" ); settings->setValue( "moveShortcutLeft", m_settingsLeftHandDragEnabled ); settings->endGroup(); settings->sync(); if ( notify ) { emit moveShortcutLeftChanged( m_settingsLeftHandDragEnabled ); } } bool MoveCenterTabController::turnBindLeft() const { return m_settingsLeftHandTurnEnabled; } void MoveCenterTabController::setTurnBindLeft( bool value, bool notify ) { m_settingsLeftHandTurnEnabled = value; auto settings = OverlayController::appSettings(); settings->beginGroup( "playspaceSettings" ); settings->setValue( "turnBindLeft", m_settingsLeftHandTurnEnabled ); settings->endGroup(); settings->sync(); if ( notify ) { emit turnBindLeftChanged( m_settingsLeftHandTurnEnabled ); } } bool MoveCenterTabController::turnBindRight() const { return m_settingsRightHandTurnEnabled; } void MoveCenterTabController::setTurnBindRight( bool value, bool notify ) { m_settingsRightHandTurnEnabled = value; auto settings = OverlayController::appSettings(); settings->beginGroup( "playspaceSettings" ); settings->setValue( "turnBindRight", m_settingsRightHandTurnEnabled ); settings->endGroup(); settings->sync(); if ( notify ) { emit turnBindRightChanged( m_settingsRightHandTurnEnabled ); } } unsigned MoveCenterTabController::dragComfortFactor() const { return m_dragComfortFactor; } void MoveCenterTabController::setDragComfortFactor( unsigned value, bool notify ) { m_dragComfortFactor = value; auto settings = OverlayController::appSettings(); settings->beginGroup( "playspaceSettings" ); settings->setValue( "dragComfortFactor", m_dragComfortFactor ); settings->endGroup(); settings->sync(); if ( notify ) { emit dragComfortFactorChanged( m_dragComfortFactor ); } } unsigned MoveCenterTabController::turnComfortFactor() const { return m_turnComfortFactor; } void MoveCenterTabController::setTurnComfortFactor( unsigned value, bool notify ) { m_turnComfortFactor = value; auto settings = OverlayController::appSettings(); settings->beginGroup( "playspaceSettings" ); settings->setValue( "turnComfortFactor", m_turnComfortFactor ); settings->endGroup(); settings->sync(); if ( notify ) { emit turnComfortFactorChanged( m_turnComfortFactor ); } } bool MoveCenterTabController::lockXToggle() const { return m_lockXToggle; } void MoveCenterTabController::setLockX( bool value, bool notify ) { m_lockXToggle = value; auto settings = OverlayController::appSettings(); settings->beginGroup( "playspaceSettings" ); settings->setValue( "lockXToggle", m_lockXToggle ); settings->endGroup(); settings->sync(); if ( notify ) { emit requireLockXChanged( m_lockXToggle ); } } bool MoveCenterTabController::lockYToggle() const { return m_lockYToggle; } void MoveCenterTabController::setLockY( bool value, bool notify ) { m_lockYToggle = value; auto settings = OverlayController::appSettings(); settings->beginGroup( "playspaceSettings" ); settings->setValue( "lockYToggle", m_lockYToggle ); settings->endGroup(); settings->sync(); if ( notify ) { emit requireLockYChanged( m_lockYToggle ); } } bool MoveCenterTabController::lockZToggle() const { return m_lockZToggle; } void MoveCenterTabController::setLockZ( bool value, bool notify ) { m_lockZToggle = value; auto settings = OverlayController::appSettings(); settings->beginGroup( "playspaceSettings" ); settings->setValue( "lockZToggle", m_lockZToggle ); settings->endGroup(); settings->sync(); if ( notify ) { emit requireLockZChanged( m_lockZToggle ); } } void MoveCenterTabController::modOffsetX( float value, bool notify ) { // TODO ? possible issue with locking position this way if ( !m_lockXToggle ) { double angle = m_rotation * k_centidegreesToRadians; double offset[3] = { static_cast( value ), 0, 0 }; rotateCoordinates( offset, angle ); float offsetFloat[3] = { static_cast( offset[0] ), static_cast( offset[1] ), static_cast( offset[2] ) }; parent->AddOffsetToUniverseCenter( vr::TrackingUniverseOrigin( m_trackingUniverse ), offsetFloat, m_adjustChaperone ); m_offsetX += value; if ( notify ) { emit offsetXChanged( m_offsetX ); } } } void MoveCenterTabController::modOffsetY( float value, bool notify ) { if ( !m_lockYToggle ) { parent->AddOffsetToUniverseCenter( vr::TrackingUniverseOrigin( m_trackingUniverse ), 1, value, m_adjustChaperone ); m_offsetY += value; if ( notify ) { emit offsetYChanged( m_offsetY ); } } } void MoveCenterTabController::modOffsetZ( float value, bool notify ) { if ( !m_lockZToggle ) { double angle = m_rotation * k_centidegreesToRadians; double offset[3] = { 0, 0, static_cast( value ) }; rotateCoordinates( offset, angle ); float offsetFloat[3] = { static_cast( offset[0] ), static_cast( offset[1] ), static_cast( offset[2] ) }; parent->AddOffsetToUniverseCenter( vr::TrackingUniverseOrigin( m_trackingUniverse ), offsetFloat, m_adjustChaperone ); m_offsetZ += value; if ( notify ) { emit offsetZChanged( m_offsetZ ); } } } void MoveCenterTabController::reset() { vr::VRChaperoneSetup()->RevertWorkingCopy(); parent->RotateUniverseCenter( vr::TrackingUniverseOrigin( m_trackingUniverse ), static_cast( -m_rotation * k_centidegreesToRadians ), m_adjustChaperone, false ); float offset[3] = { -m_offsetX, -m_offsetY, -m_offsetZ }; parent->AddOffsetToUniverseCenter( vr::TrackingUniverseOrigin( m_trackingUniverse ), offset, m_adjustChaperone, false ); vr::VRChaperoneSetup()->CommitWorkingCopy( vr::EChaperoneConfigFile_Live ); m_offsetX = 0.0f; m_offsetY = 0.0f; m_offsetZ = 0.0f; m_rotation = 0; emit offsetXChanged( m_offsetX ); emit offsetYChanged( m_offsetY ); emit offsetZChanged( m_offsetZ ); emit rotationChanged( m_rotation ); } void MoveCenterTabController::zeroOffsets() { m_offsetX = 0.0f; m_offsetY = 0.0f; m_offsetZ = 0.0f; m_rotation = 0; emit offsetXChanged( m_offsetX ); emit offsetYChanged( m_offsetY ); emit offsetZChanged( m_offsetZ ); emit rotationChanged( m_rotation ); } double MoveCenterTabController::getHmdYawTotal() { return m_hmdYawTotal; } void MoveCenterTabController::resetHmdYawTotal() { m_hmdYawTotal = 0.0; } // START of drag bindings: void MoveCenterTabController::leftHandSpaceDrag( bool leftHandDragActive ) { if ( !m_settingsLeftHandDragEnabled ) { return; } // cancel if other hand is active in override if ( !m_overrideRightHandDragPressed ) { // detect new press if ( leftHandDragActive && !m_leftHandDragPressed ) { m_activeDragHand = vr::TrackedControllerRole_LeftHand; } // detect new release else if ( !leftHandDragActive && m_leftHandDragPressed ) { // check overrides // pass back to right hand? if ( m_overrideRightHandDragPressed ) { m_activeDragHand = vr::TrackedControllerRole_RightHand; } // otherwise check right hand for activated swap drag if ( m_swapDragToRightHandActivated ) { m_activeDragHand = vr::TrackedControllerRole_RightHand; } // override of left hand still active? else if ( m_overrideLeftHandDragPressed ) { m_activeDragHand = vr::TrackedControllerRole_LeftHand; } // swap activation of left hand still active? else if ( m_swapDragToLeftHandActivated ) { m_activeDragHand = vr::TrackedControllerRole_LeftHand; } // check non-override right hand action if ( m_rightHandDragPressed ) { m_activeDragHand = vr::TrackedControllerRole_RightHand; } // nothing else is pressed, deactivate drag else { m_activeDragHand = vr::TrackedControllerRole_Invalid; } } } m_leftHandDragPressed = leftHandDragActive; } void MoveCenterTabController::rightHandSpaceDrag( bool rightHandDragActive ) { if ( !m_settingsRightHandDragEnabled ) { return; } // cancel if other hand is active in override if ( !m_overrideLeftHandDragPressed ) { // detect new press if ( rightHandDragActive && !m_rightHandDragPressed ) { m_activeDragHand = vr::TrackedControllerRole_RightHand; } // detect new release else if ( !rightHandDragActive && m_rightHandDragPressed ) { // check overrides // pass back to left hand? if ( m_overrideLeftHandDragPressed ) { m_activeDragHand = vr::TrackedControllerRole_LeftHand; } // otherwise check left hand for activated swap drag if ( m_swapDragToLeftHandActivated ) { m_activeDragHand = vr::TrackedControllerRole_LeftHand; } // override of right hand still active? else if ( m_overrideRightHandDragPressed ) { m_activeDragHand = vr::TrackedControllerRole_RightHand; } // swap activation of right hand still active? else if ( m_swapDragToRightHandActivated ) { m_activeDragHand = vr::TrackedControllerRole_RightHand; } // check non-override left hand action if ( m_leftHandDragPressed ) { m_activeDragHand = vr::TrackedControllerRole_LeftHand; } // nothing else is pressed, deactivate drag else { m_activeDragHand = vr::TrackedControllerRole_Invalid; } } } m_rightHandDragPressed = rightHandDragActive; } void MoveCenterTabController::optionalOverrideLeftHandSpaceDrag( bool overrideLeftHandDragActive ) { if ( !m_settingsLeftHandDragEnabled ) { return; } // detect new press if ( overrideLeftHandDragActive && !m_overrideLeftHandDragPressed ) { m_activeDragHand = vr::TrackedControllerRole_LeftHand; // stop any active rotate because we're in override mode m_activeTurnHand = vr::TrackedControllerRole_Invalid; } // detect new release else if ( !overrideLeftHandDragActive && m_overrideLeftHandDragPressed ) { // check if we should pass back to other hand // give priority to override action if ( m_overrideRightHandDragPressed ) { m_activeDragHand = vr::TrackedControllerRole_RightHand; } // otherwise check right hand for activated swap drag if ( m_swapDragToRightHandActivated ) { m_activeDragHand = vr::TrackedControllerRole_RightHand; } // otherwise check normal action else if ( m_rightHandDragPressed ) { m_activeDragHand = vr::TrackedControllerRole_RightHand; } // swap activation of left hand still active? else if ( m_swapDragToLeftHandActivated ) { m_activeDragHand = vr::TrackedControllerRole_LeftHand; } // check if we should fall back to non-override left hand else if ( m_leftHandDragPressed ) { m_activeDragHand = vr::TrackedControllerRole_LeftHand; } // nothing else is pressed, deactivate drag else { m_activeDragHand = vr::TrackedControllerRole_Invalid; } } m_overrideLeftHandDragPressed = overrideLeftHandDragActive; } void MoveCenterTabController::optionalOverrideRightHandSpaceDrag( bool overrideRightHandDragActive ) { if ( !m_settingsRightHandDragEnabled ) { return; } // detect new press if ( overrideRightHandDragActive && !m_overrideRightHandDragPressed ) { m_activeDragHand = vr::TrackedControllerRole_RightHand; // stop any active rotate because we're in override mode m_activeTurnHand = vr::TrackedControllerRole_Invalid; } // detect new release else if ( !overrideRightHandDragActive && m_overrideRightHandDragPressed ) { // check if we should pass back to other hand // give priority to override action if ( m_overrideLeftHandDragPressed ) { m_activeDragHand = vr::TrackedControllerRole_LeftHand; } // otherwise check left hand for activated swap drag if ( m_swapDragToLeftHandActivated ) { m_activeDragHand = vr::TrackedControllerRole_LeftHand; } // otherwise check normal action else if ( m_leftHandDragPressed ) { m_activeDragHand = vr::TrackedControllerRole_LeftHand; } // swap activation of right hand still active? else if ( m_swapDragToRightHandActivated ) { m_activeDragHand = vr::TrackedControllerRole_RightHand; } // check if we should fall back to non-override right hand else if ( m_rightHandDragPressed ) { m_activeDragHand = vr::TrackedControllerRole_RightHand; } // nothing else is pressed, deactivate drag else { m_activeDragHand = vr::TrackedControllerRole_Invalid; } } m_overrideRightHandDragPressed = overrideRightHandDragActive; } void MoveCenterTabController::swapSpaceDragToLeftHandOverride( bool swapDragToLeftHand ) { if ( !m_settingsLeftHandDragEnabled ) { return; } // detect new press if ( swapDragToLeftHand && !m_swapDragToLeftHandPressed ) { // check for active right hand drag to swap from if ( m_activeDragHand == vr::TrackedControllerRole_RightHand ) { m_activeDragHand = vr::TrackedControllerRole_LeftHand; // stop any active rotate because we're in override mode m_activeTurnHand = vr::TrackedControllerRole_Invalid; // set this hand's swap action to successfully activated for // tracking if we should pass the drag back if another hand is // released m_swapDragToLeftHandActivated = true; } } // detect new release else if ( !swapDragToLeftHand && m_swapDragToLeftHandPressed ) { // check if the left hand swap had even activated if ( m_swapDragToLeftHandActivated && m_activeDragHand == vr::TrackedControllerRole_LeftHand ) { // check if we should pass back to other hand // give priority to override action if ( m_overrideRightHandDragPressed ) { m_activeDragHand = vr::TrackedControllerRole_RightHand; } // otherwise check if other hand's swap drag was active else if ( m_swapDragToRightHandActivated ) { m_activeDragHand = vr::TrackedControllerRole_RightHand; } // otherwise check if we retain left hand via its override // action else if ( m_overrideLeftHandDragPressed ) { m_activeDragHand = vr::TrackedControllerRole_LeftHand; } // otherwise check normal action on right hand else if ( m_rightHandDragPressed ) { m_activeDragHand = vr::TrackedControllerRole_RightHand; } // otherwise check if we retain left hand via its normal action else if ( m_leftHandDragPressed ) { m_activeDragHand = vr::TrackedControllerRole_LeftHand; } // nothing else is pressed, deactivate drag. else { m_activeDragHand = vr::TrackedControllerRole_Invalid; } } } m_swapDragToLeftHandPressed = swapDragToLeftHand; // we only set activated flag when the swap was successfull, but we always // deactivate when swap action isn't pressed. if ( !swapDragToLeftHand ) { m_swapDragToLeftHandActivated = false; } // deactivate turning even when not detecting a new press as long as a swap // is successfully activated now if ( m_swapDragToLeftHandActivated ) { m_activeTurnHand = vr::TrackedControllerRole_Invalid; } } void MoveCenterTabController::swapSpaceDragToRightHandOverride( bool swapDragToRightHand ) { if ( !m_settingsRightHandDragEnabled ) { return; } // detect new press if ( swapDragToRightHand && !m_swapDragToRightHandPressed ) { // check for active left hand drag to swap from if ( m_activeDragHand == vr::TrackedControllerRole_LeftHand ) { m_activeDragHand = vr::TrackedControllerRole_RightHand; // stop any active rotate because we're in override mode m_activeTurnHand = vr::TrackedControllerRole_Invalid; // set this hand's swap action to successfully activated for // tracking if we should pass the drag back if another hand is // released m_swapDragToRightHandActivated = true; } } // detect new release else if ( !swapDragToRightHand && m_swapDragToRightHandPressed ) { // check if the right hand swap had even activated if ( m_swapDragToRightHandActivated && m_activeDragHand == vr::TrackedControllerRole_RightHand ) { // check if we should pass back to other hand // give priority to override action if ( m_overrideLeftHandDragPressed ) { m_activeDragHand = vr::TrackedControllerRole_LeftHand; } // otherwise check if other hand's swap drag was active else if ( m_swapDragToLeftHandActivated ) { m_activeDragHand = vr::TrackedControllerRole_LeftHand; } // otherwise check if we retain right hand via its override // action else if ( m_overrideRightHandDragPressed ) { m_activeDragHand = vr::TrackedControllerRole_RightHand; } // otherwise check normal action on left hand else if ( m_leftHandDragPressed ) { m_activeDragHand = vr::TrackedControllerRole_LeftHand; } // otherwise check if we retain right hand via its normal action else if ( m_rightHandDragPressed ) { m_activeDragHand = vr::TrackedControllerRole_RightHand; } // nothing else is pressed, deactivate drag. else { m_activeDragHand = vr::TrackedControllerRole_Invalid; } } } m_swapDragToRightHandPressed = swapDragToRightHand; // we only set activated flag when the swap was successfull, but we always // deactivate when swap action isn't pressed. if ( !swapDragToRightHand ) { m_swapDragToRightHandActivated = false; } // deactivate turning even when not detecting a new press as long as a swap // is successfully activated now if ( m_swapDragToRightHandActivated ) { m_activeTurnHand = vr::TrackedControllerRole_Invalid; } } // END of drag bindings // START of turn bindgins void MoveCenterTabController::leftHandSpaceTurn( bool leftHandTurnActive ) { if ( !m_settingsLeftHandTurnEnabled ) { return; } // cancel if other hand is active in override if ( !m_overrideRightHandTurnPressed ) { // detect new press if ( leftHandTurnActive && !m_leftHandTurnPressed ) { m_activeTurnHand = vr::TrackedControllerRole_LeftHand; } // detect new release else if ( !leftHandTurnActive && m_leftHandTurnPressed ) { // check overrides // pass back to right hand? if ( m_overrideRightHandTurnPressed ) { m_activeTurnHand = vr::TrackedControllerRole_RightHand; } // override of left hand still active? else if ( m_overrideLeftHandTurnPressed ) { m_activeTurnHand = vr::TrackedControllerRole_LeftHand; } // check non-override right hand action if ( m_rightHandTurnPressed ) { m_activeTurnHand = vr::TrackedControllerRole_RightHand; } // nothing else is pressed, deactivate rotate else { m_activeTurnHand = vr::TrackedControllerRole_Invalid; } } } m_leftHandTurnPressed = leftHandTurnActive; } void MoveCenterTabController::rightHandSpaceTurn( bool rightHandTurnActive ) { if ( !m_settingsRightHandTurnEnabled ) { return; } // cancel if other hand is active in override if ( !m_overrideLeftHandTurnPressed ) { // detect new press if ( rightHandTurnActive && !m_rightHandTurnPressed ) { m_activeTurnHand = vr::TrackedControllerRole_RightHand; } // detect new release else if ( !rightHandTurnActive && m_rightHandTurnPressed ) { // check overrides // pass back to left hand? if ( m_overrideLeftHandTurnPressed ) { m_activeTurnHand = vr::TrackedControllerRole_LeftHand; } // override of right hand still active? else if ( m_overrideRightHandTurnPressed ) { m_activeTurnHand = vr::TrackedControllerRole_RightHand; } // check non-override left hand action if ( m_leftHandTurnPressed ) { m_activeTurnHand = vr::TrackedControllerRole_LeftHand; } // nothing else is pressed, deactivate rotate else { m_activeTurnHand = vr::TrackedControllerRole_Invalid; } } } m_rightHandTurnPressed = rightHandTurnActive; } void MoveCenterTabController::optionalOverrideLeftHandSpaceTurn( bool overrideLeftHandTurnActive ) { if ( !m_settingsLeftHandTurnEnabled ) { return; } // detect new press if ( overrideLeftHandTurnActive && !m_overrideLeftHandTurnPressed ) { m_activeTurnHand = vr::TrackedControllerRole_LeftHand; // stop any active move because we're in override mode m_activeDragHand = vr::TrackedControllerRole_Invalid; } // detect new release else if ( !overrideLeftHandTurnActive && m_overrideLeftHandTurnPressed ) { // check if we should pass back to other hand // give priority to override action if ( m_overrideRightHandTurnPressed ) { m_activeTurnHand = vr::TrackedControllerRole_RightHand; } // otherwise check normal action else if ( m_rightHandTurnPressed ) { m_activeTurnHand = vr::TrackedControllerRole_RightHand; } // check if we should fall back to non-override left hand else if ( m_leftHandTurnPressed ) { m_activeTurnHand = vr::TrackedControllerRole_LeftHand; } // nothing else is pressed, deactivate rotate else { m_activeTurnHand = vr::TrackedControllerRole_Invalid; } } m_overrideLeftHandTurnPressed = overrideLeftHandTurnActive; } void MoveCenterTabController::optionalOverrideRightHandSpaceTurn( bool overrideRightHandTurnActive ) { if ( !m_settingsRightHandTurnEnabled ) { return; } // detect new press if ( overrideRightHandTurnActive && !m_overrideRightHandTurnPressed ) { m_activeTurnHand = vr::TrackedControllerRole_RightHand; // stop any active move because we're in override mode m_activeDragHand = vr::TrackedControllerRole_Invalid; } // detect new release else if ( !overrideRightHandTurnActive && m_overrideRightHandTurnPressed ) { // check if we should pass back to other hand // give priority to override action if ( m_overrideLeftHandTurnPressed ) { m_activeTurnHand = vr::TrackedControllerRole_LeftHand; } // otherwise check normal action else if ( m_leftHandTurnPressed ) { m_activeTurnHand = vr::TrackedControllerRole_LeftHand; } // check if we should fall back to non-override right hand else if ( m_rightHandTurnPressed ) { m_activeTurnHand = vr::TrackedControllerRole_RightHand; } // nothing else is pressed, deactivate rotate else { m_activeTurnHand = vr::TrackedControllerRole_Invalid; } } m_overrideRightHandTurnPressed = overrideRightHandTurnActive; } // END of turn bindings. // START of other bindings. void MoveCenterTabController::gravityToggle( bool gravityToggleJustPressed ) { // temp stuff for compliner warnings if ( !gravityToggleJustPressed ) { return; } // TODO STUFF } void MoveCenterTabController::heightToggle( bool heightToggleJustPressed ) { // temp stuff for compliner warnings if ( !heightToggleJustPressed ) { return; } // TODO STUFF } void MoveCenterTabController::resetOffsets( bool resetOffsetsJustPressed ) { if ( resetOffsetsJustPressed ) { reset(); } } void MoveCenterTabController::snapTurnLeft( bool snapTurnLeftJustPressed ) { if ( !snapTurnLeftJustPressed ) { return; } // TODO add interface to configure snap angle. // temporarily hard coded to 45 degrees int newRotationAngleDeg = m_rotation - 4500; // Keep angle within -18000 ~ 18000 centidegrees if ( newRotationAngleDeg > 18000 ) { newRotationAngleDeg -= 36000; } else if ( newRotationAngleDeg < -18000 ) { newRotationAngleDeg += 36000; } setRotation( newRotationAngleDeg ); } void MoveCenterTabController::snapTurnRight( bool snapTurnRightJustPressed ) { if ( !snapTurnRightJustPressed ) { return; } // TODO add interface to configure snap angle. // temporarily hard coded to 45 degrees int newRotationAngleDeg = m_rotation + 4500; // Keep angle within -18000 ~ 18000 centidegrees if ( newRotationAngleDeg > 18000 ) { newRotationAngleDeg -= 36000; } else if ( newRotationAngleDeg < -18000 ) { newRotationAngleDeg += 36000; } setRotation( newRotationAngleDeg ); } // END of other bindings void MoveCenterTabController::updateHmdRotationCounter( vr::TrackedDevicePose_t hmdPose, double angle ) { // If hmd tracking is bad, set m_lastHmdQuaternion invalid if ( !hmdPose.bPoseIsValid || hmdPose.eTrackingResult != vr::TrackingResult_Running_OK ) { m_lastHmdQuaternion.w = k_quaternionInvalidValue; return; } // Get hmd pose matrix (in rotated coordinates) vr::HmdMatrix34_t hmdMatrix = hmdPose.mDeviceToAbsoluteTracking; // Set up (un)rotation matrix vr::HmdMatrix34_t hmdMatrixRotMat; vr::HmdMatrix34_t hmdMatrixAbsolute; utils::initRotationMatrix( hmdMatrixRotMat, 1, static_cast( angle ) ); // Get hmdMatrixAbsolute in un-rotated coordinates. utils::matMul33( hmdMatrixAbsolute, hmdMatrixRotMat, hmdMatrix ); // Convert pose matrix to quaternion m_hmdQuaternion = quaternion::fromHmdMatrix34( hmdMatrixAbsolute ); // Get rotation change of hmd // Checking for invalid quaternion using < because == isn't guaranteed // for doubles comparison. if ( m_lastHmdQuaternion.w < k_quaternionUnderIsInvalidValueThreshold ) { m_lastHmdQuaternion = m_hmdQuaternion; return; } // Construct a quaternion representing difference between old // hmd pose and new hmd pose. vr::HmdQuaternion_t hmdDiffQuaternion = quaternion::multiply( m_hmdQuaternion, quaternion::conjugate( m_lastHmdQuaternion ) ); // Calculate yaw from quaternion. double hmdYawDiff = quaternion::getYaw( hmdDiffQuaternion ); // Apply yaw difference to m_hmdYawTotal. m_hmdYawTotal += hmdYawDiff; m_lastHmdQuaternion = m_hmdQuaternion; } void MoveCenterTabController::updateHandDrag( vr::TrackedDevicePose_t* devicePoses, double angle ) { auto moveHandId = vr::VRSystem()->GetTrackedDeviceIndexForControllerRole( m_activeDragHand ); if ( m_activeDragHand == vr::TrackedControllerRole_Invalid || moveHandId == vr::k_unTrackedDeviceIndexInvalid || moveHandId >= vr::k_unMaxTrackedDeviceCount ) { if ( m_lastMoveHand != vr::TrackedControllerRole_Invalid ) { emit offsetXChanged( m_offsetX ); emit offsetYChanged( m_offsetY ); emit offsetZChanged( m_offsetZ ); } m_lastMoveHand = m_activeDragHand; return; } vr::TrackedDevicePose_t* movePose = devicePoses + moveHandId; if ( !movePose->bPoseIsValid || !movePose->bDeviceIsConnected || movePose->eTrackingResult != vr::TrackingResult_Running_OK ) { m_lastMoveHand = m_activeDragHand; 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, }; 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 // prevents UI from updating if axis movement is locked if ( !m_lockXToggle ) { m_offsetX += static_cast( diff[0] ); } if ( !m_lockYToggle ) { m_offsetY += static_cast( diff[1] ); } if ( !m_lockZToggle ) { m_offsetZ += static_cast( diff[2] ); } rotateCoordinates( diff, angle ); // Done calculating rotation so we down-cast double to float for // openvr format float diffFloat[3] = { static_cast( diff[0] ), static_cast( diff[1] ), static_cast( diff[2] ) }; // If locked removes movement if ( m_lockXToggle ) { diffFloat[0] = 0; } if ( m_lockYToggle ) { diffFloat[1] = 0; } if ( m_lockZToggle ) { diffFloat[2] = 0; } // Check if diffFloat is anything before comitting. if ( diffFloat[0] != 0 || diffFloat[1] != 0 || diffFloat[2] != 0 ) { parent->AddOffsetToUniverseCenter( vr::TrackingUniverseOrigin( m_trackingUniverse ), diffFloat, m_adjustChaperone ); } } m_lastControllerPosition[0] = absoluteControllerPosition[0]; m_lastControllerPosition[1] = absoluteControllerPosition[1]; m_lastControllerPosition[2] = absoluteControllerPosition[2]; m_lastMoveHand = m_activeDragHand; } void MoveCenterTabController::updateHandTurn( vr::TrackedDevicePose_t* devicePoses, double angle ) { auto rotateHandId = vr::VRSystem()->GetTrackedDeviceIndexForControllerRole( m_activeTurnHand ); if ( m_activeTurnHand == vr::TrackedControllerRole_Invalid || rotateHandId == vr::k_unTrackedDeviceIndexInvalid || rotateHandId >= vr::k_unMaxTrackedDeviceCount ) { if ( m_lastRotateHand != vr::TrackedControllerRole_Invalid ) { m_lastHandQuaternion.w = k_quaternionInvalidValue; } m_lastRotateHand = m_activeTurnHand; return; } vr::TrackedDevicePose_t* rotatePose = devicePoses + rotateHandId; if ( !rotatePose->bPoseIsValid || !rotatePose->bDeviceIsConnected || rotatePose->eTrackingResult != vr::TrackingResult_Running_OK ) { m_lastRotateHand = m_activeTurnHand; return; } // Get hand's rotation. // handMatrix is in rotated coordinates. vr::HmdMatrix34_t handMatrix = rotatePose->mDeviceToAbsoluteTracking; // We need un-rotated coordinates for valid comparison between // handQuaternion and lastHandQuaternion. Set up (un)rotation // matrix. vr::HmdMatrix34_t handMatrixRotMat; vr::HmdMatrix34_t handMatrixAbsolute; utils::initRotationMatrix( handMatrixRotMat, 1, static_cast( angle ) ); // Get handMatrixAbsolute in un-rotated coordinates. utils::matMul33( handMatrixAbsolute, handMatrixRotMat, handMatrix ); // Convert pose matrix to quaternion m_handQuaternion = quaternion::fromHmdMatrix34( handMatrixAbsolute ); if ( m_lastRotateHand == m_activeTurnHand ) { // Get rotation change of hand. // Checking for invalid quaternion using < because == isn't // guaranteed for doubles comparison. if ( m_lastHandQuaternion.w < k_quaternionUnderIsInvalidValueThreshold ) { m_lastHandQuaternion = m_handQuaternion; } else { // Construct a quaternion representing difference // between old hand and new hand. vr::HmdQuaternion_t handDiffQuaternion = quaternion::multiply( m_handQuaternion, quaternion::conjugate( m_lastHandQuaternion ) ); // Calculate yaw from quaternion. double handYawDiff = quaternion::getYaw( handDiffQuaternion ); int newRotationAngleDeg = static_cast( round( handYawDiff * k_radiansToCentidegrees ) + m_rotation ); // Keep angle within -18000 ~ 18000 centidegrees if ( newRotationAngleDeg > 18000 ) { newRotationAngleDeg -= 36000; } else if ( newRotationAngleDeg < -18000 ) { newRotationAngleDeg += 36000; } setRotation( newRotationAngleDeg ); } } m_lastHandQuaternion = m_handQuaternion; m_lastRotateHand = m_activeTurnHand; } void MoveCenterTabController::eventLoopTick( vr::ETrackingUniverseOrigin universe, vr::TrackedDevicePose_t* devicePoses ) { if ( settingsUpdateCounter >= k_moveCenterSettingsUpdateCounter ) { if ( parent->isDashboardVisible() ) { setTrackingUniverse( int( universe ) ); } settingsUpdateCounter = 0; } else { settingsUpdateCounter++; } // get current space rotation in radians double angle = m_rotation * k_centidegreesToRadians; // hmd rotations stats counting doesn't need to be smooth, so we skip some // frames for performance if ( m_hmdRotationStatsUpdateCounter >= k_hmdRotationCounterUpdateRate ) { // device pose index 0 is always the hmd updateHmdRotationCounter( devicePoses[0], angle ); m_hmdRotationStatsUpdateCounter = 0; } else { m_hmdRotationStatsUpdateCounter++; } // Smooth drag motion can cause sim-sickness so we check if the user wants // to skip frames to reduce vection. We use the factor squared because of // logarithmic human perception. if ( m_dragComfortFrameSkipCounter >= ( m_dragComfortFactor * m_dragComfortFactor ) ) { updateHandDrag( devicePoses, angle ); m_dragComfortFrameSkipCounter = 0; } else { m_dragComfortFrameSkipCounter++; } // Smooth turn motion can cause sim-sickness so we check if the user wants // to skip frames to reduce vection. We use the factor squared because of // logarithmic human perception. if ( m_turnComfortFrameSkipCounter >= ( m_turnComfortFactor * m_turnComfortFactor ) ) { updateHandTurn( devicePoses, angle ); m_turnComfortFrameSkipCounter = 0; } else { m_turnComfortFrameSkipCounter++; } } } // namespace advsettings