Initial implementation of Auto-Turn Auto-turn leverages Space Turn automatically whenever you reach a configured distance from your chaperone boundary. Turn direction is in order of precedence: 1. Away from a corner where you're already touching one wall. This is to avoid being turned to face a wall, which we pretty much never want. If 'use corner angle' is enabled, it'll turn the angle of the corner, otherwise it'll turn the angle of your headset to the wall you encountered. 2. In whichever direction will start you unwinding your cord. If your rotations is greater than 'cord detangling rotations' and your angle to the wall is less than 'min detangling angle' it'll turn you such that the thing you're walking towards is directly along a wall, in the direction which will begin unwinding your cord. 3. In the closest angle to the wall. This too will put whatever you're walking towards directly along a wall, so you can walk the maximum distance to it. - If Smooth turn is checked, it'll turn at a constant (configurable) rate until the world is aligned with the new angle, otherwise it'll snap to the new direction instantly. - Activation distance is the distance from your chaperone at which auto-turn starts, and deactivation distance is the minimum distance (+ the activation distance) at which it'll be able to be activated again, to avoid turning twice at once. Redirected walking tries to gently guide you away from your playspace boundaries by tweaking the world's angle at smaller increments than your brain is capable of noticing. While most playspaces won't be big enough to allow this to give you a true 'infinite walk' experience, it can give the perception of your playspace being ever so slightly bigger by making you walk in a slight arc in real life while walking forward in VR. - Redirected walking is independent from Auto-turn and can be activated on its own. At both the 'slight' and 'imperceptable' radii, the turning speed is incredibly minimal. When walking along one wall of your chaperone to another, you should notice the far corner of your playspace (relative to the world) drifting ever so slightly away from you. In normal circumstances at the lowest settings this shouldn't be noticable. Co-authored-by: feilen <feilen1000@gmail.com> * Initial implementation of Infinite Walk * Code cleanup and abstraction, use nearest corner instead of nearest wall to determine turn direction * Run clang-format * Turn relative to the last wall if we run into adjacent walls, otherwise turn at easiest angle * Limit maximum turns (just refuse to turn for now), only turn on shared corners if going the same direction * Improve same-corner * get rid of LastDirectionLeft * WIP: cord untangling hint * Try to automatically untangle cord, don't rotate if we encounter a wall behind us * Incorrect offset for walls * optimize if hmd inactive * Cleanup/clarify angles * (probably?) solve rare segfault * Adjust the defaults and fix a bug with blocking reverse turns * Implement smooth turn, fix a bug with shared-corners that would cause it to turn the wrong way * actually really fix shared corner bug vect[i + 1 % vect.size()] was somehow evaluating to vect[i + (1 % vect.size())] * Move configurables for autoturn to .h to make clearer what's configurable * WIP: true redirected walking * First pass optimization/cleanup * Second pass cleanup/optimize * Missing clang-format * Somewhat ugly: Optionally turn the exact angle of the corner when using shared corner turns * added rotation tab controller in progress * cleaned up chaperone controller/ finished settings/rotationtab * did first 4 settings qml, and main qml setup * initial working build, not all settings UI done * merging master into redirected walking. (#387) * Update OVR to 1.10.30 (#379) * updated to ovr 1.10.30 * fixed updated showoverlaykeyboard call * fixed keyboard location * Updated Manifest (#380) * updated to ovr 1.10.30 * fixed updated showoverlaykeyboard call * fixed keyboard location * removed un-necessary matrix, updated app key and manifest * fixed formatting * Fix push to mute icon not being used when PTM enabled * added touch default bindings (#385) travis CI down/broken? Co-authored-by: Vasily <vautkin@teknik.io> * added action, and additional settings * fixed various bugs added turn speed and mode * fixed conversion error for turn speed * added redirected walking options * formatting * formatting fixing * removed old comments * changed logs to debug and some wording changes * changed UI layout * added space turn to rotation page * added icon, fixed autoturn to autoTurn, fixed conversion to be in qml vs set * Update src/res/qml/RootPage.qml yep XD Co-Authored-By: Kung-vr <38249782+Kung-vr@users.noreply.github.com> * reverted build changes * removed un-needed app image build commands * Cleanup some functions, avoid an extra chaperone check. * Make redirected walking and autoturn independently enable-able Co-authored-by: Feilen <feilen1000@gmail.com> Co-authored-by: Vasily <vautkin@teknik.io> Co-authored-by: Kung-vr <38249782+Kung-vr@users.noreply.github.com>
404 lines
12 KiB
C++
404 lines
12 KiB
C++
#include "ivrinput.h"
|
|
#include "ivrinput_action.h"
|
|
#include <openvr.h>
|
|
#include <iostream>
|
|
#include <array>
|
|
#include <easylogging++.h>
|
|
|
|
namespace input
|
|
{
|
|
/*!
|
|
Wrapper around the IVRInput GetDigitalActionData with error handling.
|
|
|
|
The struct is created and zero initalized in this function instead of being
|
|
passed as reference since the size is currently only 28 bytes, and currently
|
|
only two bools are used from the entire struct.
|
|
MSVC on /O2 will likely completely optimize the function call away but even if
|
|
it isn't the struct will still need to be created somewhere, and with move
|
|
semantics it almost doesn't matter if the struct is created in the calling
|
|
function and passed as reference, or created in this function.
|
|
*/
|
|
vr::InputDigitalActionData_t getDigitalActionData( DigitalAction& action )
|
|
{
|
|
vr::InputDigitalActionData_t handleData = {};
|
|
|
|
const auto error = vr::VRInput()->GetDigitalActionData(
|
|
action.handle(),
|
|
&handleData,
|
|
sizeof( handleData ),
|
|
vr::k_ulInvalidInputValueHandle );
|
|
|
|
if ( error != vr::EVRInputError::VRInputError_None )
|
|
{
|
|
LOG( ERROR ) << "Error getting IVRInput Digital Action Data for handle "
|
|
<< action.name() << ". SteamVR Error: " << error;
|
|
}
|
|
|
|
return handleData;
|
|
}
|
|
|
|
/*!
|
|
Wrapper around the IVRInput GetAnalogActionData with error handling.
|
|
|
|
The struct is created and zero initalized in this function instead of being
|
|
passed as reference since the size is currently only 28 bytes, and currently
|
|
only two bools are used from the entire struct.
|
|
MSVC on /O2 will likely completely optimize the function call away but even if
|
|
it isn't the struct will still need to be created somewhere, and with move
|
|
semantics it almost doesn't matter if the struct is created in the calling
|
|
function and passed as reference, or created in this function.
|
|
*/
|
|
vr::InputAnalogActionData_t getAnalogActionData( AnalogAction& action )
|
|
{
|
|
vr::InputAnalogActionData_t handleData = {};
|
|
|
|
const auto error
|
|
= vr::VRInput()->GetAnalogActionData( action.handle(),
|
|
&handleData,
|
|
sizeof( handleData ),
|
|
vr::k_ulInvalidInputValueHandle );
|
|
|
|
if ( error != vr::EVRInputError::VRInputError_None )
|
|
{
|
|
LOG( ERROR ) << "Error getting IVRInput Digital Action Data for handle "
|
|
<< action.name() << ". SteamVR Error: " << error;
|
|
}
|
|
|
|
return handleData;
|
|
}
|
|
|
|
/*!
|
|
Gets the action state of an Action.
|
|
This will only return true the first time the button is released. It is
|
|
necessary to release the button and the push it again in order for this function
|
|
to return true again.
|
|
*/
|
|
bool isDigitalActionActivatedOnce( DigitalAction& action )
|
|
{
|
|
const auto handleData = getDigitalActionData( action );
|
|
|
|
return handleData.bState && handleData.bChanged;
|
|
}
|
|
|
|
/*!
|
|
Gets the action state of an Action.
|
|
This will continually return true while the button is held down.
|
|
*/
|
|
bool isDigitalActionActivatedConstant( DigitalAction& action )
|
|
{
|
|
const auto handleData = getDigitalActionData( action );
|
|
|
|
return handleData.bState;
|
|
}
|
|
|
|
/*!
|
|
Sets up the input systems.
|
|
Actions should be added to the initializer list before the body of the
|
|
constructor. Actions should be certain that the action name strings are
|
|
correctly input, and that the correct type is provided.
|
|
Wrong action names may not result in direct error messages, but just features
|
|
mystically not working.
|
|
*/
|
|
SteamIVRInput::SteamIVRInput()
|
|
: m_manifest(), m_mainSet( action_sets::haptic ),
|
|
m_music( action_sets::music ), m_motion( action_sets::motion ),
|
|
m_misc( action_sets::misc ), m_nextTrack( action_keys::nextTrack ),
|
|
m_previousTrack( action_keys::previousTrack ),
|
|
m_pausePlayTrack( action_keys::pausePlayTrack ),
|
|
m_stopTrack( action_keys::stopTrack ),
|
|
m_leftHandSpaceTurn( action_keys::leftHandSpaceTurn ),
|
|
m_rightHandSpaceTurn( action_keys::rightHandSpaceTurn ),
|
|
m_leftHandSpaceDrag( action_keys::leftHandSpaceDrag ),
|
|
m_rightHandSpaceDrag( action_keys::rightHandSpaceDrag ),
|
|
m_optionalOverrideLeftHandSpaceTurn(
|
|
action_keys::optionalOverrideLeftHandSpaceTurn ),
|
|
m_optionalOverrideRightHandSpaceTurn(
|
|
action_keys::optionalOverrideRightHandSpaceTurn ),
|
|
m_optionalOverrideLeftHandSpaceDrag(
|
|
action_keys::optionalOverrideLeftHandSpaceDrag ),
|
|
m_optionalOverrideRightHandSpaceDrag(
|
|
action_keys::optionalOverrideRightHandSpaceDrag ),
|
|
m_swapSpaceDragToLeftHandOverride(
|
|
action_keys::swapSpaceDragToLeftHandOverride ),
|
|
m_swapSpaceDragToRightHandOverride(
|
|
action_keys::swapSpaceDragToRightHandOverride ),
|
|
m_gravityToggle( action_keys::gravityToggle ),
|
|
m_gravityReverse( action_keys::gravityReverse ),
|
|
m_heightToggle( action_keys::heightToggle ),
|
|
m_resetOffsets( action_keys::resetOffsets ),
|
|
m_snapTurnLeft( action_keys::snapTurnLeft ),
|
|
m_snapTurnRight( action_keys::snapTurnRight ),
|
|
m_smoothTurnLeft( action_keys::smoothTurnLeft ),
|
|
m_smoothTurnRight( action_keys::smoothTurnRight ),
|
|
m_autoTurnToggle( action_keys::autoTurnToggle ),
|
|
m_xAxisLockToggle( action_keys::xAxisLockToggle ),
|
|
m_yAxisLockToggle( action_keys::yAxisLockToggle ),
|
|
m_zAxisLockToggle( action_keys::zAxisLockToggle ),
|
|
m_keyboardOne( action_keys::keyboardOne ),
|
|
m_keyboardTwo( action_keys::keyboardTwo ),
|
|
m_keyboardThree( action_keys::keyboardThree ),
|
|
m_chaperoneToggle( action_keys::chaperoneToggle ),
|
|
m_pushToTalk( action_keys::pushToTalk ),
|
|
m_leftHaptic( action_keys::hapticsLeft ),
|
|
m_rightHaptic( action_keys::hapticsRight ),
|
|
m_addLeftHapticClick( action_keys::addLeftHapticClick ),
|
|
m_addRightHapticClick( action_keys::addRightHapticClick ),
|
|
m_proxSensor( action_keys::proxSensor ),
|
|
m_leftHand( input_keys::leftHand ), m_rightHand( input_keys::rightHand ),
|
|
m_sets( { m_mainSet.activeActionSet(),
|
|
m_music.activeActionSet(),
|
|
m_motion.activeActionSet(),
|
|
m_misc.activeActionSet() } )
|
|
{
|
|
}
|
|
/*!
|
|
Returns true if the next media track should be played.
|
|
|
|
Will only return true the first time that the button is pressed. Holding the
|
|
button down will result in false until it has been released and pushed again.
|
|
*/
|
|
bool SteamIVRInput::nextSong()
|
|
{
|
|
return isDigitalActionActivatedOnce( m_nextTrack );
|
|
}
|
|
|
|
/*!
|
|
Returns true if the previous media track should be played.
|
|
|
|
Will only return true the first time that the button is pressed. Holding the
|
|
button down will result in false until it has been released and pushed again.
|
|
*/
|
|
bool SteamIVRInput::previousSong()
|
|
{
|
|
return isDigitalActionActivatedOnce( m_previousTrack );
|
|
}
|
|
|
|
/*!
|
|
Returns true if media playback should be paused/played.
|
|
|
|
Will only return true the first time that the button is pressed. Holding the
|
|
button down will result in false until it has been released and pushed again.
|
|
*/
|
|
bool SteamIVRInput::pausePlaySong()
|
|
{
|
|
return isDigitalActionActivatedOnce( m_pausePlayTrack );
|
|
}
|
|
|
|
/*!
|
|
Returns true if media playback should be stopped.
|
|
|
|
Will only return true the first time that the button is pressed. Holding the
|
|
button down will result in false until it has been released and pushed again.
|
|
*/
|
|
bool SteamIVRInput::stopSong()
|
|
{
|
|
return isDigitalActionActivatedOnce( m_stopTrack );
|
|
}
|
|
|
|
bool SteamIVRInput::leftHandSpaceTurn()
|
|
{
|
|
return isDigitalActionActivatedConstant( m_leftHandSpaceTurn );
|
|
}
|
|
|
|
bool SteamIVRInput::rightHandSpaceTurn()
|
|
{
|
|
return isDigitalActionActivatedConstant( m_rightHandSpaceTurn );
|
|
}
|
|
|
|
bool SteamIVRInput::leftHandSpaceDrag()
|
|
{
|
|
return isDigitalActionActivatedConstant( m_leftHandSpaceDrag );
|
|
}
|
|
|
|
bool SteamIVRInput::rightHandSpaceDrag()
|
|
{
|
|
return isDigitalActionActivatedConstant( m_rightHandSpaceDrag );
|
|
}
|
|
bool SteamIVRInput::optionalOverrideLeftHandSpaceTurn()
|
|
{
|
|
return isDigitalActionActivatedConstant(
|
|
m_optionalOverrideLeftHandSpaceTurn );
|
|
}
|
|
|
|
bool SteamIVRInput::optionalOverrideRightHandSpaceTurn()
|
|
{
|
|
return isDigitalActionActivatedConstant(
|
|
m_optionalOverrideRightHandSpaceTurn );
|
|
}
|
|
|
|
bool SteamIVRInput::optionalOverrideLeftHandSpaceDrag()
|
|
{
|
|
return isDigitalActionActivatedConstant(
|
|
m_optionalOverrideLeftHandSpaceDrag );
|
|
}
|
|
|
|
bool SteamIVRInput::optionalOverrideRightHandSpaceDrag()
|
|
{
|
|
return isDigitalActionActivatedConstant(
|
|
m_optionalOverrideRightHandSpaceDrag );
|
|
}
|
|
|
|
bool SteamIVRInput::swapSpaceDragToLeftHandOverride()
|
|
{
|
|
return isDigitalActionActivatedConstant(
|
|
m_swapSpaceDragToLeftHandOverride );
|
|
}
|
|
|
|
bool SteamIVRInput::swapSpaceDragToRightHandOverride()
|
|
{
|
|
return isDigitalActionActivatedConstant(
|
|
m_swapSpaceDragToRightHandOverride );
|
|
}
|
|
|
|
bool SteamIVRInput::gravityToggle()
|
|
{
|
|
return isDigitalActionActivatedOnce( m_gravityToggle );
|
|
}
|
|
|
|
bool SteamIVRInput::gravityReverse()
|
|
{
|
|
return isDigitalActionActivatedConstant( m_gravityReverse );
|
|
}
|
|
|
|
bool SteamIVRInput::heightToggle()
|
|
{
|
|
return isDigitalActionActivatedOnce( m_heightToggle );
|
|
}
|
|
|
|
bool SteamIVRInput::resetOffsets()
|
|
{
|
|
return isDigitalActionActivatedOnce( m_resetOffsets );
|
|
}
|
|
|
|
bool SteamIVRInput::snapTurnLeft()
|
|
{
|
|
return isDigitalActionActivatedOnce( m_snapTurnLeft );
|
|
}
|
|
|
|
bool SteamIVRInput::snapTurnRight()
|
|
{
|
|
return isDigitalActionActivatedOnce( m_snapTurnRight );
|
|
}
|
|
|
|
bool SteamIVRInput::smoothTurnLeft()
|
|
{
|
|
return isDigitalActionActivatedConstant( m_smoothTurnLeft );
|
|
}
|
|
|
|
bool SteamIVRInput::smoothTurnRight()
|
|
{
|
|
return isDigitalActionActivatedConstant( m_smoothTurnRight );
|
|
}
|
|
bool SteamIVRInput::autoTurnToggle()
|
|
{
|
|
return isDigitalActionActivatedOnce( m_autoTurnToggle );
|
|
}
|
|
|
|
bool SteamIVRInput::xAxisLockToggle()
|
|
{
|
|
return isDigitalActionActivatedOnce( m_xAxisLockToggle );
|
|
}
|
|
|
|
bool SteamIVRInput::yAxisLockToggle()
|
|
{
|
|
return isDigitalActionActivatedOnce( m_yAxisLockToggle );
|
|
}
|
|
|
|
bool SteamIVRInput::zAxisLockToggle()
|
|
{
|
|
return isDigitalActionActivatedOnce( m_zAxisLockToggle );
|
|
}
|
|
|
|
bool SteamIVRInput::pushToTalk()
|
|
{
|
|
return isDigitalActionActivatedConstant( m_pushToTalk );
|
|
}
|
|
|
|
bool SteamIVRInput::chaperoneToggle()
|
|
{
|
|
return isDigitalActionActivatedOnce( m_chaperoneToggle );
|
|
}
|
|
|
|
bool SteamIVRInput::proxState()
|
|
{
|
|
return isDigitalActionActivatedConstant( m_proxSensor );
|
|
}
|
|
|
|
bool SteamIVRInput::addLeftHapticClick()
|
|
{
|
|
return isDigitalActionActivatedConstant( m_addLeftHapticClick );
|
|
}
|
|
|
|
bool SteamIVRInput::addRightHapticClick()
|
|
{
|
|
return isDigitalActionActivatedConstant( m_addRightHapticClick );
|
|
}
|
|
|
|
bool SteamIVRInput::keyboardOne()
|
|
{
|
|
return isDigitalActionActivatedOnce( m_keyboardOne );
|
|
}
|
|
|
|
bool SteamIVRInput::keyboardTwo()
|
|
{
|
|
return isDigitalActionActivatedOnce( m_keyboardTwo );
|
|
}
|
|
bool SteamIVRInput::keyboardThree()
|
|
{
|
|
return isDigitalActionActivatedOnce( m_keyboardThree );
|
|
}
|
|
/*!
|
|
Returns the action handle of the Left Haptic Action
|
|
*/
|
|
|
|
vr::VRActionHandle_t SteamIVRInput::leftHapticActionHandle()
|
|
{
|
|
return m_leftHaptic.handle();
|
|
}
|
|
|
|
/*!
|
|
Returns the action handle of the Right Haptic Action
|
|
*/
|
|
vr::VRActionHandle_t SteamIVRInput::rightHapticActionHandle()
|
|
{
|
|
return m_rightHaptic.handle();
|
|
}
|
|
|
|
/*!
|
|
Returns the input handle of the Right hand
|
|
*/
|
|
vr::VRInputValueHandle_t SteamIVRInput::rightInputHandle()
|
|
{
|
|
return m_rightHand.handle();
|
|
}
|
|
|
|
/*!
|
|
Returns the input handle of the Left hand
|
|
*/
|
|
vr::VRInputValueHandle_t SteamIVRInput::leftInputHandle()
|
|
{
|
|
return m_leftHand.handle();
|
|
}
|
|
|
|
/*!
|
|
Updates the active action set(s).
|
|
Should be called every frame, or however often you want the input system to
|
|
update state.
|
|
*/
|
|
void SteamIVRInput::UpdateStates()
|
|
{
|
|
const auto error
|
|
= vr::VRInput()->UpdateActionState( m_sets.data(),
|
|
sizeof( vr::VRActiveActionSet_t ),
|
|
action_sets::numberOfSets );
|
|
|
|
if ( error != vr::EVRInputError::VRInputError_None )
|
|
{
|
|
LOG( ERROR )
|
|
<< "Error during IVRInput action state update. OpenVR Error: "
|
|
<< error;
|
|
}
|
|
}
|
|
|
|
} // namespace input
|